aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2020-03-09 10:59:04 -0700
committerAdam Soltys <[email protected]>2020-03-09 10:59:10 -0700
commit1ae4c03116ce3a650af88f78a4b5d19581238c71 (patch)
tree10fce06803f0a13c5ba990f08d687c4deced6186
parentddb61fe99e4103c1ba8a5d29e3aa66a580c63bf6 (diff)
downloadcoinos-server-1ae4c03116ce3a650af88f78a4b5d19581238c71.tar.gz
coinos-server-1ae4c03116ce3a650af88f78a4b5d19581238c71.zip
fee estimation
-rw-r--r--bitcoinPayments.js40
-rw-r--r--estimateBitcoinFee.js32
-rw-r--r--estimateLiquidFee.js35
-rw-r--r--liquidPayments.js18
-rw-r--r--payments.js6
-rw-r--r--sendCoins.js55
-rw-r--r--sendLiquid.js52
7 files changed, 154 insertions, 84 deletions
diff --git a/bitcoinPayments.js b/bitcoinPayments.js
index 64a50e58..5b916265 100644
--- a/bitcoinPayments.js
+++ b/bitcoinPayments.js
@@ -5,6 +5,7 @@ const Sequelize = require("sequelize");
const bitcoin = require("bitcoinjs-lib");
const l = require("pino")();
+const toSats = n => parseInt((n * 100000000).toFixed())
const zmqRawBlock = zmq.socket("sub");
zmqRawBlock.connect(config.bitcoin.zmqrawblock);
@@ -17,26 +18,19 @@ zmqRawTx.subscribe("rawtx");
let NETWORK = bitcoin.networks[config.bitcoin.network === "mainnet" ? "bitcoin" : config.bitcoin.network];
module.exports = (app, bc, db, addresses, payments, emit) => {
- zmqRawTx.on("message", (topic, message, sequence) => {
- message = message.toString("hex");
-
- let tx = bitcoin.Transaction.fromHex(message);
- let hash = reverse(tx.getHash()).toString("hex");
+ zmqRawTx.on("message", async (topic, message, sequence) => {
+ const hex = message.toString("hex");
+ const tx = await bc.decodeRawTransaction(hex);
+ let hash = tx.txid;
if (payments.includes(hash)) return;
Promise.all(
- tx.outs.map(async o => {
+ tx.vout.map(async o => {
+ if (!(o.scriptPubKey && o.scriptPubKey.addresses)) return;
- let address;
- try {
- address = bitcoin.address.fromOutputScript(
- o.script,
- NETWORK
- );
- } catch (e) {
- return;
- }
+ const value = toSats(o.value);
+ const address = o.scriptPubKey.addresses[0];
if (Object.keys(addresses).includes(address)) {
payments.push(hash);
@@ -64,22 +58,18 @@ module.exports = (app, bc, db, addresses, payments, emit) => {
let confirmed = false;
- if (user.friend) {
- user.balance += o.value;
- confirmed = true;
- } else {
- user.pending += o.value;
- }
-
user.address = await bc.getNewAddress("", "bech32");
- addresses[user.address] = user.username;
+ user.pending += value;
+
await user.save();
emit(user.username, "user", user);
+
+ addresses[user.address] = user.username;
const payment = await db.Payment.create({
user_id: user.id,
hash,
- amount: o.value,
+ amount: value,
currency: user.currency,
rate: app.get("rates")[user.currency],
received: true,
@@ -126,7 +116,7 @@ module.exports = (app, bc, db, addresses, payments, emit) => {
let user = await p.getUser();
user.balance += p.amount;
- user.pending -= p.amount;
+ user.pending -= Math.min(user.pending, p.amount);
emit(user.username, "user", user);
await user.save();
diff --git a/estimateBitcoinFee.js b/estimateBitcoinFee.js
new file mode 100644
index 00000000..215a180d
--- /dev/null
+++ b/estimateBitcoinFee.js
@@ -0,0 +1,32 @@
+const config = require("./config");
+
+const l = require("pino")();
+const SATS = 100000000;
+
+module.exports = (app, bc, db, emit) => async (req, res) => {
+ let { user } = req;
+ let { address, amount, confTarget, feeRate, mode } = req.body;
+ let tx, fee;
+
+ try {
+ amount = parseInt(amount);
+
+ tx = await bc.createRawTransaction([], {
+ [address]: (amount / SATS).toFixed(8)
+ });
+
+ let params = {
+ subtractFeeFromOutputs: amount === user.balance ? [0] : []
+ }
+
+ if (feeRate) params["feeRate"] = (parseInt(feeRate) / SATS * 1000).toFixed(8);
+ if (confTarget) params["conf_target"] = parseInt(confTarget);
+ if (mode) params["estimate_mode"] = mode;
+
+ tx = await bc.fundRawTransaction(tx, params);
+ res.send({ tx });
+ } catch (e) {
+ l.error(e);
+ return res.status(500).send(e.message);
+ }
+};
diff --git a/estimateLiquidFee.js b/estimateLiquidFee.js
new file mode 100644
index 00000000..fc1655b6
--- /dev/null
+++ b/estimateLiquidFee.js
@@ -0,0 +1,35 @@
+const config = require("./config");
+const BitcoinCore = require("bitcoin-core");
+
+const l = require("pino")();
+const SATS = 100000000;
+
+const bc = new BitcoinCore(config.liquid);
+
+module.exports = (app, db, emit) => async (req, res) => {
+ let { user } = req;
+ let { address, amount, confTarget, feeRate, mode } = req.body;
+ let tx, fee;
+
+ try {
+ amount = parseInt(amount);
+
+ tx = await bc.createRawTransaction([], {
+ [address]: (amount / SATS).toFixed(8)
+ });
+
+ let params = {
+ subtractFeeFromOutputs: amount === user.balance ? [0] : []
+ }
+
+ if (feeRate) params["feeRate"] = (parseInt(feeRate) / SATS * 1000).toFixed(8);
+ if (confTarget) params["conf_target"] = parseInt(confTarget);
+ if (mode) params["estimate_mode"] = mode;
+
+ tx = await bc.fundRawTransaction(tx, params);
+ res.send({ tx });
+ } catch (e) {
+ l.error(e);
+ return res.status(500).send(e.message);
+ }
+};
diff --git a/liquidPayments.js b/liquidPayments.js
index 8ed5507a..3c2a12f4 100644
--- a/liquidPayments.js
+++ b/liquidPayments.js
@@ -17,7 +17,7 @@ zmqRawTx.subscribe("rawtx");
let NETWORK = bitcoin.networks[config.bitcoin.network === "mainnet" ? "bitcoin" : config.bitcoin.network];
const bc = new BitcoinCore(config.liquid);
-const SATS = 100000000;
+const toSats = n => parseInt((n * 100000000).toFixed())
module.exports = (app, db, addresses, payments, emit) => {
zmqRawTx.on("message", async (topic, message, sequence) => {
@@ -30,8 +30,7 @@ module.exports = (app, db, addresses, payments, emit) => {
Promise.all(
tx.vout.map(async o => {
if (!(o.scriptPubKey && o.scriptPubKey.addresses)) return;
- let { value } = o;
- value *= SATS
+ const value = toSats(o.value);
const address = o.scriptPubKey.addresses[0];
if (Object.keys(addresses).includes(address)) {
@@ -58,20 +57,15 @@ module.exports = (app, db, addresses, payments, emit) => {
let confirmed = 0;
- if (user.friend) {
- user.balance += value;
- confirmed = 1;
- } else {
- user.pending += value;
- }
-
+ user.pending += value;
user.confidential = await bc.getNewAddress();
user.liquid = (await bc.getAddressInfo(user.confidential)).unconfidential;
- addresses[user.liquid] = user.username;
await user.save();
emit(user.username, "user", user);
+ addresses[user.liquid] = user.username;
+
const payment = await db.Payment.create({
user_id: user.id,
hash: blinded.txid,
@@ -126,7 +120,7 @@ module.exports = (app, db, addresses, payments, emit) => {
let user = await p.getUser();
user.balance += p.amount;
- user.pending -= p.amount;
+ user.pending -= Math.min(user.pending, p.amount);
l.info("liquid confirmed", user.username, p.amount);
emit(user.username, "user", user);
diff --git a/payments.js b/payments.js
index 10c3b541..c2d9d306 100644
--- a/payments.js
+++ b/payments.js
@@ -24,8 +24,10 @@ module.exports = async (app, auth, addresses, bc, db, emit, seen, payments) => {
require("./sendPayment")(app, db, emit, seen, lnaraw, lnb)
);
app.post("/payUser", auth, require("./payUser")(app, db, lna, lnb));
- app.post("/sendCoins", auth, require("./sendCoins")(app, bc, db, emit));
- app.post("/sendLiquid", auth, require("./sendLiquid")(app, db, emit));
+ app.post("/sendCoins", auth, require("./sendCoins")(addresses, app, bc, db, emit));
+ app.post("/estimateBitcoinFee", auth, require("./estimateBitcoinFee")(app, bc, db, emit));
+ app.post("/sendLiquid", auth, require("./sendLiquid")(addresses, app, db, emit));
+ app.post("/estimateLiquidFee", auth, require("./estimateLiquidFee")(app, db, emit));
app.post("/addInvoice", auth, require("./addInvoice")(app, db, lnb));
app.get("/payments", auth, async (req, res) => {
const payments = await db.Payment.findAll({
diff --git a/sendCoins.js b/sendCoins.js
index 00e2fa99..6d871ca4 100644
--- a/sendCoins.js
+++ b/sendCoins.js
@@ -3,31 +3,40 @@ const config = require("./config");
const reverse = require("buffer-reverse");
const l = require("pino")();
-const SATS = 100000000;
+const toSats = n => parseInt((n * 100000000).toFixed())
-module.exports = (app, bc, db, emit) => async (req, res) => {
+module.exports = (addresses, app, bc, db, emit) => async (req, res) => {
let { user } = req;
- let { address, amount } = req.body;
- let rawtx, hex, fee, total;
+ let { address, tx } = req.body;
+ let { hex } = tx;
- try {
- amount = parseInt(amount);
+ let fee = toSats(tx.fee);
- rawtx = await bc.createRawTransaction([], {
- [address]: (amount / SATS).toFixed(8)
- });
+ const isChange = async address =>
+ !((await bc.getAddressInfo(address)).ismine) ||
+ !Object.keys(addresses).includes(address) ||
+ address === user.address;
- rawtx = await bc.fundRawTransaction(rawtx, {
- subtractFeeFromOutputs: amount === user.balance ? [0] : []
- });
+ tx = await bc.decodeRawTransaction(hex);
- ({ hex, fee } = rawtx);
- fee = parseInt(fee * SATS);
- total = amount + fee;
- } catch (e) {
- l.error(e);
- return res.status(500).send(e.message);
- }
+ let total = 0;
+ let change = 0;
+
+ for (let i = 0; i < tx.vout.length; i++) {
+ let o = tx.vout[i];
+ total += toSats(o.value);
+
+ if (o.scriptPubKey.addresses) {
+ if ((await isChange(o.scriptPubKey.addresses[0]))) {
+ change += toSats(o.value);
+ }
+ }
+ }
+
+ l.info(total);
+ total = (total - change) + fee;
+ let amount = total - fee;
+ l.info(fee, total, amount, change);
try {
await db.transaction(async transaction => {
@@ -56,10 +65,8 @@ module.exports = (app, bc, db, emit) => async (req, res) => {
if (config.bitcoin.walletpass)
await bc.walletPassphrase(config.bitcoin.walletpass, 300);
- rawtx = (await bc.signRawTransactionWithWallet(hex)).hex;
- let txid = await bc.sendRawTransaction(rawtx);
-
- let tx = bitcoin.Transaction.fromHex(rawtx);
+ hex = (await bc.signRawTransactionWithWallet(hex)).hex;
+ let txid = await bc.sendRawTransaction(hex);
await db.transaction(async transaction => {
await user.save({ transaction });
@@ -76,7 +83,7 @@ module.exports = (app, bc, db, emit) => async (req, res) => {
address,
confirmed: true,
received: false,
- asset: 'BTC',
+ asset: "BTC"
},
{ transaction }
);
diff --git a/sendLiquid.js b/sendLiquid.js
index 855a2617..a86395a2 100644
--- a/sendLiquid.js
+++ b/sendLiquid.js
@@ -4,35 +4,43 @@ const reverse = require("buffer-reverse");
const BitcoinCore = require("bitcoin-core");
const l = require("pino")();
-const SATS = 100000000;
+const toSats = n => parseInt((n * 100000000).toFixed())
const bc = new BitcoinCore(config.liquid);
-module.exports = (app, db, emit) => async (req, res) => {
- let { address, amount } = req.body;
- let rawtx, hex, fee, total;
+module.exports = (addresses, app, db, emit) => async (req, res) => {
let { user } = req;
+ let {
+ address,
+ tx: { hex }
+ } = req.body;
- try {
- amount = parseInt(amount);
-
- rawtx = await bc.createRawTransaction([], {
- [address]: (amount / SATS).toFixed(8)
- });
+ const isChange = async address =>
+ !((await bc.getAddressInfo(address)).ismine) ||
+ !Object.keys(addresses).includes(address) ||
+ address === user.liquid;
- rawtx = await bc.fundRawTransaction(rawtx, {
- subtractFeeFromOutputs: amount === user.balance ? [0] : []
- });
+ const unblinded = await bc.unblindRawTransaction(hex);
+ tx = await bc.decodeRawTransaction(unblinded.hex);
+
+ let total = 0;
+ let change = 0;
+ let fee = 0;
- hex = await bc.blindRawTransaction(rawtx.hex);
+ for (let i = 0; i < tx.vout.length; i++) {
+ let o = tx.vout[i];
+ total += toSats(o.value);
+ if (o.scriptPubKey.type === 'fee') fee = toSats(o.value);
+
+ if (o.scriptPubKey.addresses) {
+ if ((await isChange(o.scriptPubKey.addresses[0]))) {
+ change += toSats(o.value);
+ }
+ }
+ }
- ({ fee } = rawtx);
- fee = parseInt(fee * SATS);
- total = amount + fee;
- } catch (e) {
- l.error("funding failed", e);
- return res.status(500).send(e.message);
- }
+ total = total - change;
+ let amount = total - fee;
try {
await db.transaction(async transaction => {
@@ -61,12 +69,14 @@ module.exports = (app, db, emit) => async (req, res) => {
if (config.liquid.walletpass)
await bc.walletPassphrase(config.liquid.walletpass, 300);
+ hex = await bc.blindRawTransaction(hex);
rawtx = (await bc.signRawTransactionWithWallet(hex)).hex;
let txid = await bc.sendRawTransaction(rawtx);
await db.transaction(async transaction => {
await user.save({ transaction });
emit(user.username, "user", user);
+ l.info("total", total);
const payment = await db.Payment.create(
{