diff options
| -rw-r--r-- | index.js | 2 | ||||
| -rw-r--r-- | lib/rates.js | 2 | ||||
| -rw-r--r-- | package.json | 3 | ||||
| -rw-r--r-- | routes/bitcoin/sweep.js | 59 | ||||
| -rw-r--r-- | routes/payments.js | 1 | ||||
| -rw-r--r-- | routes/users.js | 2 | ||||
| -rw-r--r-- | yarn.lock | 7 |
7 files changed, 72 insertions, 4 deletions
@@ -7,6 +7,8 @@ const express = require("express"); l = require("pino")(); config = require("./config"); networks = []; +prod = process.env.NODE_ENV === "production"; + if (config.bitcoin) networks.push('bitcoin'); if (config.liquid) networks.push('liquid'); if (config.lna) networks.push('lightning'); diff --git a/lib/rates.js b/lib/rates.js index b6c38a50..a8818375 100644 --- a/lib/rates.js +++ b/lib/rates.js @@ -6,7 +6,7 @@ let fetchRates; (fetchRates = async () => { let rates = require("./devrates"); - if (process.env.NODE_ENV === "production") { + if (prod) { try { let res = await axios.get( `http://data.fixer.io/api/latest?access_key=${config.fixer}` diff --git a/package.json b/package.json index 045b9fcb..9a582d46 100644 --- a/package.json +++ b/package.json @@ -11,11 +11,12 @@ "axios": "^0.19.0", "bcrypt": "^3.0.6", "bitcoin-core": "https://github.com/asoltys/bitcoin-core", - "bitcoinjs-lib": "^5.1.7", + "bitcoinjs-lib": "^5.1.8", "body-parser": "^1.19.0", "bolt11": "^1.2.5", "buffer-reverse": "^1.0.1", "clightning-client": "^0.1.2", + "coinselect": "^3.1.12", "compression": "^1.7.4", "connect-redis": "3.4.1", "cookie-parser": "^1.4.4", diff --git a/routes/bitcoin/sweep.js b/routes/bitcoin/sweep.js new file mode 100644 index 00000000..37efb73f --- /dev/null +++ b/routes/bitcoin/sweep.js @@ -0,0 +1,59 @@ +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 (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); + feeRate = Math.round(feeRate / 1000); + l.info("sweeping", req.user.username, from, amount, feeRate); + try { + ({ data: utxos } = await axios.get(`${api}/address/${from}/utxo`)); + } catch (e) { + l.error("problem fetching utxos", e.message); + } + + let targets = [ + { + address: target, + value: amount + } + ]; + + let { inputs, outputs, fee } = coinselect(utxos, targets, feeRate); + + if (!inputs || !outputs) return res.status(500).send("Unable to construct sweep transaction, try a lower fee rate?"); + + let psbt = new bitcoin.Psbt({ network }); + + outputs.map(({ address, value }) => { + psbt = psbt.addOutput({ + address: address || from, + 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(); + res.send({ feeRate, psbt }); +}; diff --git a/routes/payments.js b/routes/payments.js index ab35c9a0..9586a9a8 100644 --- a/routes/payments.js +++ b/routes/payments.js @@ -40,6 +40,7 @@ const { join } = require("path"); if (config.bitcoin) { bc = new BitcoinCore(config.bitcoin); + app.post("/bitcoin/sweep", auth, require("./bitcoin/sweep")); app.post("/bitcoin/fee", auth, require("./bitcoin/fee")); app.post("/bitcoin/send", auth, require("./bitcoin/send")); require("./bitcoin/receive"); diff --git a/routes/users.js b/routes/users.js index 61421edf..4c39ca07 100644 --- a/routes/users.js +++ b/routes/users.js @@ -110,7 +110,7 @@ app.post("/register", async (req, res) => { delete challenge[ip]; let success, score; - if (process.env.NODE_ENV === "production") { + if (prod) { if (!token) return res.status(500).send("Missing captcha token"); @@ -616,7 +616,7 @@ bitcoinjs-lib@^3.3.1: varuint-bitcoin "^1.0.4" wif "^2.0.1" -bitcoinjs-lib@^5.1.7: +bitcoinjs-lib@^5.1.8: version "5.1.8" resolved "https://registry.yarnpkg.com/bitcoinjs-lib/-/bitcoinjs-lib-5.1.8.tgz#b61be4fab7cf82142cea76f82e70a324c9a57984" integrity sha512-LQQ9uw33sqW3K1aSzYCy0Az8zbcsR1ttaNkjThFwApT/vbB7bibEesPIxf0zvzjf8V8It68v7D/fCkjd4bStMw== @@ -1005,6 +1005,11 @@ coininfo@^4.5.0: dependencies: safe-buffer "^5.1.1" +coinselect@^3.1.12: + version "3.1.12" + resolved "https://registry.yarnpkg.com/coinselect/-/coinselect-3.1.12.tgz#2255794c4002f7ecac9cf6d1cefe9f0adfd2fe7f" + integrity sha512-XKnm9wwZzJRGuvR1BGNbFZEfa4SEr38do8Z5riq0797QwMT836EqhdPuhtbn8uAX8vCMqJ7+gS4CHa1G9M5xhw== + color-convert@^1.9.0: version "1.9.3" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" |
