diff options
| author | Adam Soltys <[email protected]> | 2020-08-11 09:18:26 -0700 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2020-08-11 09:18:26 -0700 |
| commit | 3980fec05509a060ac41f98b4dd126a13e4bc9cd (patch) | |
| tree | eacb00d0f3cc42a5216baa654b8e23b96a2e64d3 | |
| parent | f89b0ffde25292909bd398430e84e6c693084582 (diff) | |
| download | coinos-server-3980fec05509a060ac41f98b4dd126a13e4bc9cd.tar.gz coinos-server-3980fec05509a060ac41f98b4dd126a13e4bc9cd.zip | |
non-custodial sends
| -rw-r--r-- | lib/buildtx.js | 76 | ||||
| -rw-r--r-- | routes/bitcoin/broadcast.js | 35 | ||||
| -rw-r--r-- | routes/bitcoin/fee.js | 10 | ||||
| -rw-r--r-- | routes/bitcoin/receive.js | 2 | ||||
| -rw-r--r-- | routes/bitcoin/send.js | 1 | ||||
| -rw-r--r-- | routes/bitcoin/sweep.js | 5 | ||||
| -rw-r--r-- | routes/liquid/receive.js | 1 | ||||
| -rw-r--r-- | routes/liquid/send.js | 2 | ||||
| -rw-r--r-- | routes/lnurl.js | 10 | ||||
| -rw-r--r-- | routes/payments.js | 2 |
10 files changed, 127 insertions, 17 deletions
diff --git a/lib/buildtx.js b/lib/buildtx.js new file mode 100644 index 00000000..351f8081 --- /dev/null +++ b/lib/buildtx.js @@ -0,0 +1,76 @@ +const axios = require("axios"); +const bitcoin = require("bitcoinjs-lib"); +const coinselect = require("coinselect"); +const split = require("coinselect/split"); +const api = prod ? "https://blockstream.info/api" : "http://localhost:3002"; +const SATS = 100000000; +const network = prod + ? bitcoin.networks["bitcoin"] + : bitcoin.networks["regtest"]; + +module.exports = async ({ address, amount, feeRate, user }) => { + if (!feeRate) feeRate = (await bc.estimateSmartFee(6)).feerate * SATS; + feeRate = Math.round(feeRate / 1000); + + const payments = await db.Payment.findAll({ + where: { + received: true, + account_id: user.account_id, + } + }); + + const utxos = []; + for (let i = 0; i < payments.length; i++) { + try { + utxos.push(...(await axios.get(`${api}/address/${payments[i].address}/utxo`)).data); + } catch (e) { + l.error("problem fetching utxos", e.message); + } + } + + let targets = [ + { + address, + value: amount + } + ]; + + let { inputs, outputs, fee } = coinselect(utxos, targets, feeRate); + + if (!inputs || !outputs) { + l.info("utxos", amount, feeRate, utxos, inputs, outputs); + throw new Error("Failed to create transaction"); + } + + let psbt = new bitcoin.Psbt({ network }); + let total = 0; + + outputs.map(({ address, value }) => { + total += value; + if (!address) { + address = payments[payments.length - 1].address; + change.push(address); + } + + psbt = psbt.addOutput({ + address, + value + }); + }); + + for (let i = 0; i < inputs.length; i++) { + let { txid, vout } = inputs[i]; + let nonWitnessUtxo = Buffer.from(await bc.getRawTransaction(txid), "hex"); + psbt = psbt.addInput({ + hash: txid, + index: vout, + nonWitnessUtxo + }); + } + + feeRate = Math.round(feeRate * 1000); + psbt = psbt.toBase64(); + + l.info("returning", { tx: psbt, feeRate }); + return { tx: psbt, feeRate }; +}; diff --git a/routes/bitcoin/broadcast.js b/routes/bitcoin/broadcast.js new file mode 100644 index 00000000..c7b134ac --- /dev/null +++ b/routes/bitcoin/broadcast.js @@ -0,0 +1,35 @@ +module.exports = ah(async (req, res) => { + try { + const { user } = req; + let hash = await bc.sendRawTransaction(req.body.tx); + + if (user) { + let { account } = user; + let payment = await db.Payment.create({ + ...req.body.payment, + hash, + amount: -req.body.payment.amount, + tip: 0, + account_id: account.id, + user_id: user.id, + rate: app.get("rates")[user.currency], + currency: user.currency, + confirmed: true, + received: false, + network: "BTC", + }); + + account.balance -= payment.amount + payment.fee; + await account.save(); + + payment = payment.get({ plain: true }); + payment.account = account.get({ plain: true }); + emit(user.username, "payment", payment); + res.send(payment); + } else { + res.send(result); + } + } catch (e) { + res.status(500).send(e.message); + } +}); diff --git a/routes/bitcoin/fee.js b/routes/bitcoin/fee.js index cd99d363..3249004c 100644 --- a/routes/bitcoin/fee.js +++ b/routes/bitcoin/fee.js @@ -1,4 +1,4 @@ -const buildTx = ({ address, amount, feeRate }) => {}; +const buildTx = require("../../lib/buildtx"); module.exports = ah(async (req, res) => { let { user } = req; @@ -6,8 +6,12 @@ module.exports = ah(async (req, res) => { let tx, fee; if (user.account.pubkey) { - tx = buildTx(req.body); - return res.end(); + try { + let psbt = await buildTx({ address, amount, feeRate, user }); + return res.send(psbt); + } catch(e) { + return res.status(500).send(e.message); + } } let invoice = await db.Invoice.findOne({ diff --git a/routes/bitcoin/receive.js b/routes/bitcoin/receive.js index 1c731118..ec120605 100644 --- a/routes/bitcoin/receive.js +++ b/routes/bitcoin/receive.js @@ -35,7 +35,7 @@ zmqRawTx.on("message", async (topic, message, sequence) => { return; } - if (Object.keys(addresses).includes(address)) { + if (Object.keys(addresses).includes(address) && !change.includes(address)) { payments.push(hash); let user = await db.User.findOne({ diff --git a/routes/bitcoin/send.js b/routes/bitcoin/send.js index 744f8f29..ddf07f3c 100644 --- a/routes/bitcoin/send.js +++ b/routes/bitcoin/send.js @@ -1,5 +1,4 @@ const bitcoin = require("bitcoinjs-lib"); -const reverse = require("buffer-reverse"); module.exports = ah(async (req, res) => { let { user } = req; diff --git a/routes/bitcoin/sweep.js b/routes/bitcoin/sweep.js index 7d596499..36bd11b5 100644 --- a/routes/bitcoin/sweep.js +++ b/routes/bitcoin/sweep.js @@ -9,11 +9,6 @@ const network = prod : bitcoin.networks["regtest"]; module.exports = ah(async (req, res) => { - let ecpair = bitcoin.ECPair.fromWIF( - "cVosbTBiXp7LYSiGVRZfZnrEjpao4X4CUTG7Ze21XM3zFTLPCMvk", - network - ); - let { address: from, amount, feeRate, target } = req.body; let utxos; if (!feeRate) feeRate = (await bc.estimateSmartFee(6)).feerate * SATS; diff --git a/routes/liquid/receive.js b/routes/liquid/receive.js index 4a00de78..3fe235a6 100644 --- a/routes/liquid/receive.js +++ b/routes/liquid/receive.js @@ -1,5 +1,4 @@ const axios = require("axios"); -const reverse = require("buffer-reverse"); const zmq = require("zeromq"); const { Op } = require("sequelize"); const elements = require("elementsjs-lib"); diff --git a/routes/liquid/send.js b/routes/liquid/send.js index 5dd7f04e..1d12db30 100644 --- a/routes/liquid/send.js +++ b/routes/liquid/send.js @@ -1,5 +1,3 @@ -const reverse = require("buffer-reverse"); - module.exports = ah(async (req, res) => { let { user } = req; let { address, memo, tx } = req.body; diff --git a/routes/lnurl.js b/routes/lnurl.js index 7da1d768..c6830ba1 100644 --- a/routes/lnurl.js +++ b/routes/lnurl.js @@ -65,10 +65,12 @@ app.post( ah(async (req, res, next) => { try { const { encoded } = req.body.lnurl; - const code = await db.Code.findOrCreate({ where: { - code: `lnurl:${encoded.substr(-32)}`, - text: encoded, - }}); + const code = await db.Code.findOrCreate({ + where: { + code: `lnurl:${encoded.substr(-32)}`, + text: encoded, + }, + }); res.send(code); } catch (e) { diff --git a/routes/payments.js b/routes/payments.js index c46af90e..ca479026 100644 --- a/routes/payments.js +++ b/routes/payments.js @@ -8,6 +8,7 @@ const read = require("../lib/read"); ah(async () => { seen = []; addresses = {}; + change = []; issuances = {}; const exceptions = []; @@ -112,6 +113,7 @@ ah(async () => { if (config.bitcoin) { bc = new BitcoinCore(config.bitcoin); + app.post("/bitcoin/broadcast", optionalAuth, require("./bitcoin/broadcast")); app.get("/bitcoin/generate", auth, require("./bitcoin/generate")); app.post("/bitcoin/sweep", auth, require("./bitcoin/sweep")); app.post("/bitcoin/fee", auth, require("./bitcoin/fee")); |
