aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2020-08-11 09:18:26 -0700
committerAdam Soltys <[email protected]>2020-08-11 09:18:26 -0700
commit3980fec05509a060ac41f98b4dd126a13e4bc9cd (patch)
treeeacb00d0f3cc42a5216baa654b8e23b96a2e64d3
parentf89b0ffde25292909bd398430e84e6c693084582 (diff)
downloadcoinos-server-3980fec05509a060ac41f98b4dd126a13e4bc9cd.tar.gz
coinos-server-3980fec05509a060ac41f98b4dd126a13e4bc9cd.zip
non-custodial sends
-rw-r--r--lib/buildtx.js76
-rw-r--r--routes/bitcoin/broadcast.js35
-rw-r--r--routes/bitcoin/fee.js10
-rw-r--r--routes/bitcoin/receive.js2
-rw-r--r--routes/bitcoin/send.js1
-rw-r--r--routes/bitcoin/sweep.js5
-rw-r--r--routes/liquid/receive.js1
-rw-r--r--routes/liquid/send.js2
-rw-r--r--routes/lnurl.js10
-rw-r--r--routes/payments.js2
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"));