From dce07aa93b3c2f47aca0c24cea6adde9ec37f1a7 Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Sun, 8 Jan 2023 21:05:34 -0800 Subject: tech debt jubilee --- sampledata/conversions.json | 2 -- sampledata/emails.json | 4 ---- sampledata/logins.json | 1 - sampledata/payments.json | 2 -- sampledata/recipients.json | 2 -- sampledata/withdrawals.json | 1 - 6 files changed, 12 deletions(-) delete mode 100644 sampledata/conversions.json delete mode 100644 sampledata/emails.json delete mode 100644 sampledata/logins.json delete mode 100644 sampledata/payments.json delete mode 100644 sampledata/recipients.json delete mode 100644 sampledata/withdrawals.json (limited to 'sampledata') diff --git a/sampledata/conversions.json b/sampledata/conversions.json deleted file mode 100644 index 2c63c085..00000000 --- a/sampledata/conversions.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} diff --git a/sampledata/emails.json b/sampledata/emails.json deleted file mode 100644 index 5fbac846..00000000 --- a/sampledata/emails.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "emails": [ - ] -} diff --git a/sampledata/logins.json b/sampledata/logins.json deleted file mode 100644 index 0967ef42..00000000 --- a/sampledata/logins.json +++ /dev/null @@ -1 +0,0 @@ -{} diff --git a/sampledata/payments.json b/sampledata/payments.json deleted file mode 100644 index 2c63c085..00000000 --- a/sampledata/payments.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} diff --git a/sampledata/recipients.json b/sampledata/recipients.json deleted file mode 100644 index 2c63c085..00000000 --- a/sampledata/recipients.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} diff --git a/sampledata/withdrawals.json b/sampledata/withdrawals.json deleted file mode 100644 index 0967ef42..00000000 --- a/sampledata/withdrawals.json +++ /dev/null @@ -1 +0,0 @@ -{} -- cgit v1.2.3 From 94584ff57ba86cbb2a6affad29a1efe62dfede99 Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Tue, 24 Jan 2023 13:11:13 -0800 Subject: readme update --- README.md | 38 +++++--------------------------------- sampleconfig/bitcoin/bitcoin.conf | 21 --------------------- sampleconfig/index.js | 16 ---------------- sampleconfig/lightning/config | 8 -------- sampledata/bitcoin/bitcoin.conf | 21 +++++++++++++++++++++ sampledata/index.js | 16 ++++++++++++++++ sampledata/lightning/config | 8 ++++++++ 7 files changed, 50 insertions(+), 78 deletions(-) delete mode 100644 sampleconfig/bitcoin/bitcoin.conf delete mode 100644 sampleconfig/index.js delete mode 100644 sampleconfig/lightning/config create mode 100644 sampledata/bitcoin/bitcoin.conf create mode 100644 sampledata/index.js create mode 100644 sampledata/lightning/config (limited to 'sampledata') diff --git a/README.md b/README.md index 699bd928..ae2b09ec 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ # coinos-server -Coinos is a bitcoin wallet app that supports payments over the bitcoin, liquid and lightning networks. Try it out at coinos.io. +Coinos is a bitcoin wallet app that supports payments over the bitcoin and lightning networks. Try it out at coinos.io. -This repository contains the code for the backend API server which is implemented as a NodeJS application. The code for the frontend UI is tracked separately here. +This repository contains the code for the API server. The frontend code is at https://github.com/coinos/coinos-ui-v2 ## Install @@ -11,39 +11,11 @@ The follow commands will set up bitcoin, liquid and lightning nodes in regtest m ```bash git clone https://github.com/coinos/coinos-server cd coinos-server -cp -r sampleconfig config cp -r sampledata data -mkdir uploads -sudo chown $(id -u):$(id -g) config/liquid -sudo chown $(id -u):$(id -g) config/bitcoin -cp .env.sample .env -cp fx.sample fx -docker network create net --gateway 172.18.0.1 --subnet 172.18.0.0/16 docker run -it -v $(pwd):/app --entrypoint pnpm asoltys/coinos-server i docker-compose up -d -docker exec -i maria mysql -u root -ppassword < db/schema.sql -docker exec -it bitcoin bitcoin-cli createwallet coinos -docker exec -it bitcoin bitcoin-cli rescanblockchain -docker exec -it bitcoin bitcoin-cli generatetoaddress 500 $(docker exec -it bitcoin bitcoin-cli getnewaddress "" "p2sh-segwit") -docker exec -it liquid elements-cli createwallet coinos -docker exec -it liquid elements-cli rescanblockchain -docker exec -it liquid elements-cli sendtoaddress AzpsKhC6xE9FEK4aWAzMnbvueMLiSa5ym1xpuYogFkHzWgMHSt8B79aNNbFppQzCSQ2yZ9E4nL6RQJU7 1000000 -docker exec -it liquid elements-cli generatetoaddress 1 AzpsKhC6xE9FEK4aWAzMnbvueMLiSa5ym1xpuYogFkHzWgMHSt8B79aNNbFppQzCSQ2yZ9E4nL6RQJU7 -curl localhost:3119/register -H "content-type: application/json" -d '{"user": { "username": "coinosfees", "password": "password"}}' +docker exec -it bc bitcoin-cli createwallet coinos +docker exec -it bc bitcoin-cli rescanblockchain +docker exec -it bc bitcoin-cli generatetoaddress 500 $(docker exec -it bitcoin bitcoin-cli getnewaddress "" "p2sh-segwit") ``` -After successful creation of all docker containers coinos ui will be available at http://localhost:8085 and coinos server will be available at http://localhost:3119 - -### License - -This code is [licensed]. Coinos is free for personal use. Anyone can fork as long as it stays AGPLv3. - -To purchase a commercial license or to inquire about customized, managed instances - please reach out to us at [contact@coinos.io] - - -[Config changes]:(#config-changes) -[config/lnd/lnd.conf]:./sampleconfig/lnd/lnd.conf -[pwd file]:./sampleconfig/lnd/pwd -[licensed]:./LICENSE.md -[contact@coinos.io]:mailto:contact@coinos.io -[generate some BTC]:./doc/fund-server-bitcoin-cli.md diff --git a/sampleconfig/bitcoin/bitcoin.conf b/sampleconfig/bitcoin/bitcoin.conf deleted file mode 100644 index 5da37e5a..00000000 --- a/sampleconfig/bitcoin/bitcoin.conf +++ /dev/null @@ -1,21 +0,0 @@ -regtest=1 -testnet=0 -dnsseed=0 -upnp=0 - -[regtest] -fallbackfee=0.00002 -server=1 -txindex=1 - -rpcuser=admin1 -rpcpassword=123 -rpcallowip=0.0.0.0/0 -rpcbind=0.0.0.0 -rpcwallet=coinos - -wallet=coinos -wallet=external - -zmqpubrawblock=tcp://0.0.0.0:18506 -zmqpubrawtx=tcp://0.0.0.0:18507 diff --git a/sampleconfig/index.js b/sampleconfig/index.js deleted file mode 100644 index 8fcb0a6f..00000000 --- a/sampleconfig/index.js +++ /dev/null @@ -1,16 +0,0 @@ -export default { - port: 3119, - nostr: "ws://nostr:8080", - ipxapi: "4275|PqKjKaxIlzooToria2m4XFLzvHihSpsZMX4hvgxu", - postmark: "f64dffd2-84ea-47c7-ba9f-95e053a2d0ae", - jwt: "00d0ab4f7738a83feb37f661526512063c41e49278b7c32cba87314269a5788b", - bitcoin: { - host: "bitcoin", - wallet: "coinos", - username: "admin1", - password: "123", - network: "regtest", - port: 18443 - }, - lightning: "/app/config/lightning/regtest/lightning-rpc" -}; diff --git a/sampleconfig/lightning/config b/sampleconfig/lightning/config deleted file mode 100644 index 9ac922ba..00000000 --- a/sampleconfig/lightning/config +++ /dev/null @@ -1,8 +0,0 @@ -alias=coinos -rgb=006000 -bitcoin-rpcconnect=bitcoin -bitcoin-rpcport=18443 -bitcoin-rpcuser=admin1 -bitcoin-rpcpassword=123 -network=regtest -addr=172.18.0.31:9735 diff --git a/sampledata/bitcoin/bitcoin.conf b/sampledata/bitcoin/bitcoin.conf new file mode 100644 index 00000000..5da37e5a --- /dev/null +++ b/sampledata/bitcoin/bitcoin.conf @@ -0,0 +1,21 @@ +regtest=1 +testnet=0 +dnsseed=0 +upnp=0 + +[regtest] +fallbackfee=0.00002 +server=1 +txindex=1 + +rpcuser=admin1 +rpcpassword=123 +rpcallowip=0.0.0.0/0 +rpcbind=0.0.0.0 +rpcwallet=coinos + +wallet=coinos +wallet=external + +zmqpubrawblock=tcp://0.0.0.0:18506 +zmqpubrawtx=tcp://0.0.0.0:18507 diff --git a/sampledata/index.js b/sampledata/index.js new file mode 100644 index 00000000..8fcb0a6f --- /dev/null +++ b/sampledata/index.js @@ -0,0 +1,16 @@ +export default { + port: 3119, + nostr: "ws://nostr:8080", + ipxapi: "4275|PqKjKaxIlzooToria2m4XFLzvHihSpsZMX4hvgxu", + postmark: "f64dffd2-84ea-47c7-ba9f-95e053a2d0ae", + jwt: "00d0ab4f7738a83feb37f661526512063c41e49278b7c32cba87314269a5788b", + bitcoin: { + host: "bitcoin", + wallet: "coinos", + username: "admin1", + password: "123", + network: "regtest", + port: 18443 + }, + lightning: "/app/config/lightning/regtest/lightning-rpc" +}; diff --git a/sampledata/lightning/config b/sampledata/lightning/config new file mode 100644 index 00000000..9ac922ba --- /dev/null +++ b/sampledata/lightning/config @@ -0,0 +1,8 @@ +alias=coinos +rgb=006000 +bitcoin-rpcconnect=bitcoin +bitcoin-rpcport=18443 +bitcoin-rpcuser=admin1 +bitcoin-rpcpassword=123 +network=regtest +addr=172.18.0.31:9735 -- cgit v1.2.3 From f0543b6d94081f43a265bb9f754a654df9c4268a Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Thu, 26 Jan 2023 11:26:05 -0800 Subject: sample notify script --- lib/db.js | 12 ++---------- lib/payments.js | 31 ++++++++++++++++++++++++------- lib/upload.js | 20 +++++++++----------- lib/whitelist.js | 1 + routes/payments.js | 12 ++++++++---- sampledata/bitcoin/bitcoin.conf | 1 + sampledata/bitcoin/transaction.sh | 2 ++ 7 files changed, 47 insertions(+), 32 deletions(-) create mode 100755 sampledata/bitcoin/transaction.sh (limited to 'sampledata') diff --git a/lib/db.js b/lib/db.js index acc4bff1..516db236 100644 --- a/lib/db.js +++ b/lib/db.js @@ -11,16 +11,9 @@ export let s = (k, v) => db.set(k, JSON.stringify(v)); let retries = {}; export let t = async (k, f) => { - let result; try { - result = await db.executeIsolated(async db => { - await db.watch(k); - let v = await db.get(k); - await db - .multi() - .set(k, await f(v)) - .exec(); - }); + await db.watch(k); + await f(await db.get(k), db); } catch (err) { if (!err.message.includes("watch")) throw err; @@ -37,5 +30,4 @@ export let t = async (k, f) => { } delete retries[k]; - return result; }; diff --git a/lib/payments.js b/lib/payments.js index 386d84d8..d0593447 100644 --- a/lib/payments.js +++ b/lib/payments.js @@ -34,16 +34,30 @@ export let debit = async ( if (!amount || amount < 0) fail("Amount must be greater than zero"); - await t(`balance:${uid}`, async balance => { - await new Promise(r => setTimeout(r, 100)); - if (balance < amount) fail("Insufficient funds"); - return balance - amount; + let ourfee = Math.round(amount * config.fee); + await t(`balance:${uid}`, async (balance, db) => { + await t(`credit:${type}:${uid}`, async (credit, db) => { + let covered = Math.min(credit, ourfee) || 0; + ourfee -= covered; + + if (balance < amount + ourfee) fail("Insufficient funds"); + await db + .multi() + .decrBy(`credit:${type}:${uid}`, covered) + .exec(); + }); + + await db + .multi() + .decrBy(`balance:${uid}`, amount + (ourfee - credit)) + .exec(); }); let p = { id, amount: -amount, fee, + ourfee, memo, uid, confirmed: true, @@ -103,9 +117,12 @@ export let credit = async (hash, amount, memo, ref, type = types.internal) => { p.id = hash; } - await db - .multi() - .set(`invoice:${hash}`, JSON.stringify(invoice)) + let m = await db.multi(); + + if ([types.bitcoin, types.lightning].includes(type)) + m.incrBy(`credit:${type}:${uid}`, Math.round(amount * config.fee)); + + m.set(`invoice:${hash}`, JSON.stringify(invoice)) .set(`payment:${p.id}`, JSON.stringify(p)) .lPush(`${uid}:payments`, p.id) .incrBy(`${balance}:${uid}`, amount) diff --git a/lib/upload.js b/lib/upload.js index fbf9ce20..5f5aebed 100644 --- a/lib/upload.js +++ b/lib/upload.js @@ -5,12 +5,16 @@ import fs from "fs"; import path from "path"; import sharp from "sharp"; import { pipeline } from "stream"; -import { g, s } from "$lib/db"; +import { g, s, db } from "$lib/db"; -export default async ({ params: { type }, file, user }, res) => { +export default async (req, res) => { + let { + params: { type }, + user + } = req; if (!["banner", "profile"].includes(type)) throw new Error("invalid upload"); - let data = await file(); + let data = await req.file(); let s1 = new Clone(data.file); let s2 = new Clone(data.file); @@ -30,14 +34,8 @@ export default async ({ params: { type }, file, user }, res) => { await new Promise(r => pipeline(s2, t, fs.createWriteStream(path), r)); - let r = async () => { - await db.watch(`user:${user.id}`); - user = await g(`user:${user.id}`); - user[type] = true; - let m = await db.multi(); - await s(`user:${user.id}`, user); - }; - await r(); + user[type] = true; + await s(`user:${user.id}`, user); res.send(name); }; diff --git a/lib/whitelist.js b/lib/whitelist.js index 77243855..850dffc0 100644 --- a/lib/whitelist.js +++ b/lib/whitelist.js @@ -14,6 +14,7 @@ export default [ "haspin", "id", "index", + "linked", "keys", "locktime", "payments", diff --git a/routes/payments.js b/routes/payments.js index a060d90a..ca32e969 100644 --- a/routes/payments.js +++ b/routes/payments.js @@ -111,9 +111,12 @@ export default { async take({ body: { name, amount }, user }, res) { amount = parseInt(amount); - await t(`pot:${name}`, async balance => { + await t(`pot:${name}`, async (balance, db) => { if (balance < amount) fail("Insufficient funds"); - return balance - amount; + await db + .multi() + .decrBy(`pot:${name}`, amount) + .exec(); }); let hash = v4(); @@ -211,10 +214,11 @@ export default { total = total - change + fee; let amount = total - fee; + if (change && !amount) fail("Cannot send to unregistered coinos address"); + await debit(txid, amount, fee, null, user, types.bitcoin, txid); await bc.sendRawTransaction(hex); res.send({ txid }); - }, - + } }; diff --git a/sampledata/bitcoin/bitcoin.conf b/sampledata/bitcoin/bitcoin.conf index 5da37e5a..3aa44186 100644 --- a/sampledata/bitcoin/bitcoin.conf +++ b/sampledata/bitcoin/bitcoin.conf @@ -16,6 +16,7 @@ rpcwallet=coinos wallet=coinos wallet=external +walletnotify=/home/bitcoin/.bitcoin/transaction.sh %s %w zmqpubrawblock=tcp://0.0.0.0:18506 zmqpubrawtx=tcp://0.0.0.0:18507 diff --git a/sampledata/bitcoin/transaction.sh b/sampledata/bitcoin/transaction.sh new file mode 100755 index 00000000..e44789d1 --- /dev/null +++ b/sampledata/bitcoin/transaction.sh @@ -0,0 +1,2 @@ +#!/bin/sh +wget -O- --post-data='{"txid": "'$1'", "wallet": "'$2'"}' --header='Content-Type:application/json' 'http://app:3119/bitcoin' -- cgit v1.2.3