From 9a5bbac84fe801c64945056fc489804f004d0bf2 Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Mon, 5 Mar 2018 17:32:22 -0800 Subject: reload user before updating balances and handle other edge cases --- index.js | 88 +++++++++++++++++++++--------- mutations.js | 5 ++ package.json | 2 +- yarn.lock | 176 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 4 files changed, 241 insertions(+), 30 deletions(-) diff --git a/index.js b/index.js index 8c0e92d9..499087be 100644 --- a/index.js +++ b/index.js @@ -2,6 +2,7 @@ import ba from 'bitcoinaverage' import bcrypt from 'bcrypt' import bitcoin from 'bitcoinjs-lib' import bodyParser from 'body-parser' +import bolt11 from 'bolt11' import compression from 'compression' import cookieParser from 'cookie-parser' import core from 'bitcoin-core' @@ -64,7 +65,6 @@ const l = console.log let user = jwt.decode(token).username socket.request.user = user sids[user] = socket.id - l(sids) } catch (e) { /* */ } next() }) @@ -88,7 +88,8 @@ const l = console.log if (!invoice) return - invoice.user.channelbalance += parseInt(msg.value) + l(msg) + invoice.user.channelbalance += msg.value await invoice.user.save() socket.to(sids[invoice.user.username]).emit('invoice', msg) @@ -100,6 +101,8 @@ const l = console.log const invoicesb = lnb.subscribeInvoices({}) invoicesb.on('data', handlePayment) + const payments = lna.sendPayment(meta, {}) + const zmqSock = zmq.socket('sub') zmqSock.connect('tcp://127.0.0.1:18503') zmqSock.subscribe('rawblock') @@ -161,24 +164,27 @@ const l = console.log }) app.post('/openchannel', auth, async (req, res) => { + if (req.user.balance < 200000) { + res.status(500).send('Need at least 200000 satoshis for channel opening') + } + let pending = await lna.pendingChannels({}, meta) let busypeers = pending.pending_open_channels.map(c => c.channel.remote_node_pub) let peer = channelpeers.find(p => !busypeers.includes(p)) if (!peer) { - return res.status(500).send( - { error: 'All peers have pending channel opens' } - ) - } + res.status(500).send('All peers have pending channel opens') + } try { + l('opening', req.user, peer) let channel = await lna.openChannel({ node_pubkey: new Buffer(peer, 'hex'), local_funding_amount: req.user.balance, }) channel.on('data', async data => { - l(data) + if (!data.chan_pending) return req.user.channelbalance += req.user.balance req.user.balance = 0 req.user.channel = reverse(data.chan_pending.txid).toString('hex') @@ -205,38 +211,70 @@ const l = console.log res.send('success') }) - app.post('/sendPayment', auth, (req, res) => { - const payments = lna.sendPayment(meta, {}) - payments.write({ payment_request: req.body.payreq }) + app.post('/sendPayment', auth, async (req, res) => { + await req.user.reload() + + if (req.user.channelbalance < bolt11.decode(req.body.payreq).satoshis) { + return res.status(500).send('Not enough satoshis') + } + + try { + payments.write({ payment_request: req.body.payreq }) + } catch (e) { l('here', e) } + payments.on('data', async m => { if (m.payment_error) { l(m) res.status(500).send({ error: m.payment_error }) } else { - l(m, req.user.channelbalance) req.user.channelbalance -= parseInt(m.payment_route.total_amt) await req.user.save() res.send(m) } + + payments.cancel() + }) + + payments.on('error', e => { + l(e) + l(e.message) + if (e.message === 'Cancelled') return + res.status(500).send(e.message) + payments.cancel() }) }) app.post('/sendCoins', auth, async (req, res) => { - let sumOuts = tx => tx.outs.reduce((a, b) => a + b.value, 0) + await req.user.reload() + const MINFEE = 180 + let { address, amount } = req.body - let txhash = (await lna.sendCoins({ addr: address, amount })).txid - let txhex = await bc.getRawTransaction(txhash) - let tx = bitcoin.Transaction.fromHex(txhex) - let input_total = await tx.ins.reduce(async (a, input) => { - let h = await bc.getRawTransaction(reverse(input.hash).toString('hex')) - return a + bitcoin.Transaction.fromHex(h).outs[input.index].value - }, 0) - let output_total = sumOuts(tx) - let fees = input_total - output_total - l(output_total, input_total, fees) - req.user.balance -= amount - await req.user.save() - res.send({ txid: txhash, tx: tx, total: output_total, fees: fees }) + + if (amount === user.balance) { + amount = user.balance - MINFEE + } + + if (user.balance < amount - MINFEE) { + res.status(500).send({ error: 'not enough funds' }) + } + + try { + let txhash = (await lna.sendCoins({ addr: address, amount })).txid + let txhex = await bc.getRawTransaction(txhash) + let tx = bitcoin.Transaction.fromHex(txhex) + let input_total = await tx.ins.reduce(async (a, input) => { + let h = await bc.getRawTransaction(reverse(input.hash).toString('hex')) + return a + bitcoin.Transaction.fromHex(h).outs[input.index].value + }, 0) + let output_total = tx.outs.reduce((a, b) => a + b.value, 0) + let fees = input_total - output_total + l(output_total, input_total, fees) + req.user.balance -= amount + await req.user.save() + res.send({ txid: txhash, tx: tx, total: output_total, fees: fees }) + } catch (e) { + res.status(500).send(e.message) + } }) app.post('/addInvoice', auth, async (req, res) => { diff --git a/mutations.js b/mutations.js index 0e8765d7..e8c26fb5 100644 --- a/mutations.js +++ b/mutations.js @@ -11,6 +11,8 @@ import { GraphQLList } from 'graphql' +const l = console.log + module.exports = (db, gqltypes, lnrpc) => { const UserInputType = new GraphQLInputObjectType({ name: 'UserInput', @@ -31,6 +33,9 @@ module.exports = (db, gqltypes, lnrpc) => { } }, resolve: async (root, { user }) => { + if (!user.username) throw new Error('Username required') + if (user.password.length < 2) throw new Error('Password too short') + let exists = await db['User'].count({ where: { username: user.username } }) if (exists) throw new Error('Username taken') diff --git a/package.json b/package.json index ce8804d2..d5202a88 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ "bitcoinjs-lib": "^3.3.2", "bluebird": "^3.4.6", "body-parser": "^1.5.0", - "bolt11": "^1.0.0", + "bolt11": "https://github.com/asoltys/bolt11#wip", "browser-sync": "^2.11.0", "buffer-reverse": "^1.0.1", "compression": "^1.7.1", diff --git a/yarn.lock b/yarn.lock index 71eb74fe..3bf918be 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1075,6 +1075,10 @@ binary-extensions@^1.0.0: version "1.8.0" resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.8.0.tgz#48ec8d16df4377eae5fa5884682480af4d95c774" +bindings@^1.2.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.3.0.tgz#b346f6ecf6a95f5a815c5839fc7cdb22502f1ed7" + bindings@~1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.2.1.tgz#14ad6113812d2d37d72e67b4cacb4bb726505f11" @@ -1092,6 +1096,12 @@ bip66@^1.1.0: version "1.1.4" resolved "https://registry.yarnpkg.com/bip66/-/bip66-1.1.4.tgz#8a59f8ae16eccb94681c3c2a7b224774605aadfb" +bip66@^1.1.3: + version "1.1.5" + resolved "https://registry.yarnpkg.com/bip66/-/bip66-1.1.5.tgz#01fa8748785ca70955d5011217d1b3139969ca22" + dependencies: + safe-buffer "^5.0.1" + bitcoin-core@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/bitcoin-core/-/bitcoin-core-2.0.0.tgz#defcd4b3bfb6738ea88051b5fe5a944e32c09df0" @@ -1117,7 +1127,7 @@ bitcoinaverage@^0.1.4: request "^2.81.0" ws "^3.0.0" -bitcoinjs-lib@^3.3.2: +bitcoinjs-lib@^3.3.1, bitcoinjs-lib@^3.3.2: version "3.3.2" resolved "https://registry.yarnpkg.com/bitcoinjs-lib/-/bitcoinjs-lib-3.3.2.tgz#780c9c53ecb1222adb463b58bef26386067b609a" dependencies: @@ -1173,6 +1183,10 @@ bluebird@^3.1.1, bluebird@^3.4.6: version "3.5.0" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.0.tgz#791420d7f551eea2897453a8a77653f96606d67c" +bn.js@^4.11.3, bn.js@^4.11.8, bn.js@^4.4.0: + version "4.11.8" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.8.tgz#2cde09eb5ee341f484746bb0309b3253b1b1442f" + body-parser@^1.5.0: version "1.17.1" resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.17.1.tgz#75b3bc98ddd6e7e0d8ffe750dfaca5c66993fa47" @@ -1188,9 +1202,16 @@ body-parser@^1.5.0: raw-body "~2.2.0" type-is "~1.6.14" -bolt11@^1.0.0: +"bolt11@https://github.com/asoltys/bolt11#wip": version "1.0.0" - resolved "https://registry.yarnpkg.com/bolt11/-/bolt11-1.0.0.tgz#8baca5fa717f335d50179d46fd5642252421bbb4" + resolved "https://github.com/asoltys/bolt11#21a1120248ba5229ef38bcd3d71b5a2641ff5b39" + dependencies: + bech32 "^1.1.2" + bitcoinjs-lib "^3.3.1" + bn.js "^4.11.8" + lodash "^4.17.4" + safe-buffer "^5.1.1" + secp256k1 "^3.4.0" boom@0.4.x: version "0.4.2" @@ -1242,6 +1263,10 @@ braces@^1.8.2: preserve "^0.2.0" repeat-element "^1.1.2" +brorand@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" + browser-sync-client@2.4.5: version "2.4.5" resolved "https://registry.yarnpkg.com/browser-sync-client/-/browser-sync-client-2.4.5.tgz#976afab1a54f255baa38fe22ae3c0d3753ad337b" @@ -1291,6 +1316,17 @@ browser-sync@^2.11.0: ua-parser-js "0.7.12" yargs "6.4.0" +browserify-aes@^1.0.6: + version "1.1.1" + resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.1.1.tgz#38b7ab55edb806ff2dcda1a7f1620773a477c49f" + dependencies: + buffer-xor "^1.0.3" + cipher-base "^1.0.0" + create-hash "^1.1.0" + evp_bytestokey "^1.0.3" + inherits "^2.0.1" + safe-buffer "^5.0.1" + browserslist@^2.1.2: version "2.11.0" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-2.11.0.tgz#50350d6873a82ebe0f3ae5483658c571ae5f9d7d" @@ -1363,6 +1399,10 @@ buffer-shims@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/buffer-shims/-/buffer-shims-1.0.0.tgz#9978ce317388c649ad8793028c3477ef044a8b51" +buffer-xor@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" + buffer@^3.0.1: version "3.6.0" resolved "https://registry.yarnpkg.com/buffer/-/buffer-3.6.0.tgz#a72c936f77b96bf52f5f7e7b467180628551defb" @@ -1527,6 +1567,13 @@ chownr@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.0.1.tgz#e2a75042a9551908bebd25b8523d5f9769d79181" +cipher-base@^1.0.0, cipher-base@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz#8760e4ecc272f4c363532f926d874aae2c1397de" + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + cipher-base@^1.0.1: version "1.0.3" resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.3.tgz#eeabf194419ce900da3018c207d212f2a6df0a07" @@ -1810,6 +1857,15 @@ create-hash@^1.1.0: ripemd160 "^1.0.0" sha.js "^2.3.6" +create-hash@^1.1.2: + version "1.1.3" + resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.1.3.tgz#606042ac8b9262750f483caddab0f5819172d8fd" + dependencies: + cipher-base "^1.0.1" + inherits "^2.0.1" + ripemd160 "^2.0.0" + sha.js "^2.4.0" + create-hmac@^1.1.2, create-hmac@^1.1.3: version "1.1.4" resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.4.tgz#d3fb4ba253eb8b3f56e39ea2fbcb8af747bd3170" @@ -1817,6 +1873,17 @@ create-hmac@^1.1.2, create-hmac@^1.1.3: create-hash "^1.1.0" inherits "^2.0.1" +create-hmac@^1.1.4: + version "1.1.6" + resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.6.tgz#acb9e221a4e17bdb076e90657c42b93e3726cf06" + dependencies: + cipher-base "^1.0.3" + create-hash "^1.1.0" + inherits "^2.0.1" + ripemd160 "^2.0.0" + safe-buffer "^5.0.1" + sha.js "^2.4.8" + cross-spawn@^5.0.1, cross-spawn@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449" @@ -2103,6 +2170,14 @@ download@^6.2.2: p-event "^1.0.0" pify "^3.0.0" +drbg.js@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/drbg.js/-/drbg.js-1.0.1.tgz#3e36b6c42b37043823cdbc332d58f31e2445480b" + dependencies: + browserify-aes "^1.0.6" + create-hash "^1.1.2" + create-hmac "^1.1.4" + dtrace-provider@~0.8: version "0.8.6" resolved "https://registry.yarnpkg.com/dtrace-provider/-/dtrace-provider-0.8.6.tgz#428a223afe03425d2cd6d6347fdf40c66903563d" @@ -2168,6 +2243,18 @@ electron-to-chromium@^1.3.30: dependencies: electron-releases "^2.1.0" +elliptic@^6.2.3: + version "6.4.0" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.4.0.tgz#cac9af8762c85836187003c8dfe193e5e2eae5df" + dependencies: + bn.js "^4.4.0" + brorand "^1.0.1" + hash.js "^1.0.0" + hmac-drbg "^1.0.0" + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + minimalistic-crypto-utils "^1.0.0" + emitter-steward@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/emitter-steward/-/emitter-steward-1.0.0.tgz#f3411ade9758a7565df848b2da0cbbd1b46cbd64" @@ -2442,6 +2529,13 @@ eventemitter3@1.x.x: version "1.2.0" resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-1.2.0.tgz#1c86991d816ad1e504750e73874224ecf3bec508" +evp_bytestokey@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz#7fcbdb198dc71959432efe13842684e0525acb02" + dependencies: + md5.js "^1.3.4" + safe-buffer "^5.1.1" + execa@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/execa/-/execa-0.7.0.tgz#944becd34cc41ee32a63a9faf27ad5a65fc59777" @@ -3136,6 +3230,26 @@ has-unicode@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" +hash-base@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-2.0.2.tgz#66ea1d856db4e8a5470cadf6fce23ae5244ef2e1" + dependencies: + inherits "^2.0.1" + +hash-base@^3.0.0: + version "3.0.4" + resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.0.4.tgz#5fc8686847ecd73499403319a6b0a3f3f6ae4918" + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + +hash.js@^1.0.0, hash.js@^1.0.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.3.tgz#340dedbe6290187151c1ea1d777a3448935df846" + dependencies: + inherits "^2.0.3" + minimalistic-assert "^1.0.0" + hawk@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/hawk/-/hawk-1.1.1.tgz#87cd491f9b46e4e2aeaca335416766885d2d1ed9" @@ -3167,6 +3281,14 @@ hike@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/hike/-/hike-1.0.2.tgz#59aa053e8939163b2b1faf891a4264290a64867a" +hmac-drbg@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" + dependencies: + hash.js "^1.0.3" + minimalistic-assert "^1.0.0" + minimalistic-crypto-utils "^1.0.1" + hoek@0.9.x: version "0.9.1" resolved "https://registry.yarnpkg.com/hoek/-/hoek-0.9.1.tgz#3d322462badf07716ea7eb85baf88079cddce505" @@ -4025,6 +4147,13 @@ make-dir@^1.0.0: dependencies: pify "^3.0.0" +md5.js@^1.3.4: + version "1.3.4" + resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.4.tgz#e9bdbde94a20a5ac18b04340fc5764d5b09d901d" + dependencies: + hash-base "^3.0.0" + inherits "^2.0.1" + media-typer@0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" @@ -4159,6 +4288,14 @@ mincer@1.5.0: shellwords "^0.1.0" source-map "^0.5.3" +minimalistic-assert@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz#702be2dda6b37f4836bcb3f5db56641b64a1d3d3" + +minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" + "minimatch@2 || 3", minimatch@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" @@ -4271,6 +4408,10 @@ nan@2.6.2: version "2.6.2" resolved "https://registry.yarnpkg.com/nan/-/nan-2.6.2.tgz#e4ff34e6c95fdfb5aecc08de6596f43605a7db45" +nan@^2.2.1: + version "2.9.2" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.9.2.tgz#f564d75f5f8f36a6d9456cca7a6c4fe488ab7866" + nan@^2.3.0: version "2.4.0" resolved "https://registry.yarnpkg.com/nan/-/nan-2.4.0.tgz#fb3c59d45fe4effe215f0b890f8adf6eb32d2232" @@ -5408,6 +5549,13 @@ ripemd160@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-1.0.1.tgz#93a4bbd4942bc574b69a8fa57c71de10ecca7d6e" +ripemd160@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.1.tgz#0f4584295c53a3628af7e6d79aca21ce57d1c6e7" + dependencies: + hash-base "^2.0.0" + inherits "^2.0.1" + run-async@^2.2.0: version "2.3.0" resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.3.0.tgz#0371ab4ae0bdd720d4166d7dfda64ff7a445a6c0" @@ -5428,7 +5576,7 @@ rx@4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/rx/-/rx-4.1.0.tgz#a5f13ff79ef3b740fe30aa803fb09f98805d4782" -safe-buffer@5.1.1, safe-buffer@^5.1.1, safe-buffer@~5.1.0, safe-buffer@~5.1.1: +safe-buffer@5.1.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853" @@ -5444,6 +5592,19 @@ scmp@0.0.3: version "0.0.3" resolved "https://registry.yarnpkg.com/scmp/-/scmp-0.0.3.tgz#3648df2d7294641e7f78673ffc29681d9bad9073" +secp256k1@^3.4.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/secp256k1/-/secp256k1-3.5.0.tgz#677d3b8a8e04e1a5fa381a1ae437c54207b738d0" + dependencies: + bindings "^1.2.1" + bip66 "^1.1.3" + bn.js "^4.11.3" + create-hash "^1.1.2" + drbg.js "^1.0.1" + elliptic "^6.2.3" + nan "^2.2.1" + safe-buffer "^5.1.0" + seek-bzip@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/seek-bzip/-/seek-bzip-1.0.5.tgz#cfe917cb3d274bcffac792758af53173eb1fabdc" @@ -5610,6 +5771,13 @@ sha.js@^2.3.6: dependencies: inherits "^2.0.1" +sha.js@^2.4.0, sha.js@^2.4.8: + version "2.4.10" + resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.10.tgz#b1fde5cd7d11a5626638a07c604ab909cfa31f9b" + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + shebang-command@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" -- cgit v1.2.3