aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2021-02-19 21:46:21 -0800
committerAdam Soltys <[email protected]>2021-02-19 21:46:21 -0800
commitfb2a1a9f7262c58636b62fe1eee1b95e88a481c8 (patch)
treefc05ad9ac87f402ec48e08d8792e44df72631d23
parent035afbbbc1c00f15d3956dbd21adb3a9c82c223e (diff)
downloadcoinos-server-fb2a1a9f7262c58636b62fe1eee1b95e88a481c8.tar.gz
coinos-server-fb2a1a9f7262c58636b62fe1eee1b95e88a481c8.zip
instant lightning->liquid conversion
-rw-r--r--index.js1
-rw-r--r--routes/invoices.js24
-rw-r--r--routes/lightning/invoice.js3
-rw-r--r--routes/lightning/receive.js45
-rw-r--r--routes/liquid/fee.js89
-rw-r--r--routes/liquid/send.js185
6 files changed, 188 insertions, 159 deletions
diff --git a/index.js b/index.js
index 908666e3..399cc9fe 100644
--- a/index.js
+++ b/index.js
@@ -17,6 +17,7 @@ fail = msg => {
};
challenge = {};
+convert = {};
logins = {};
sessions = {};
sockets = {};
diff --git a/routes/invoices.js b/routes/invoices.js
index 3ef0b4e3..9a24a8c0 100644
--- a/routes/invoices.js
+++ b/routes/invoices.js
@@ -21,11 +21,14 @@ app.get(
app.post(
"/invoice",
+ optionalAuth,
ah(async (req, res, next) => {
try {
- let { invoice, user } = req.body;
+ let { liquidAddress, invoice, user } = req.body;
let { blindkey } = invoice;
+ if (liquidAddress) convert[invoice.text] = liquidAddress;
+
if (!user) ({ user } = req);
else {
user = await db.User.findOne({
@@ -35,6 +38,7 @@ app.post(
});
}
if (!user) throw new Error("user not provided");
+ if (!invoice.currency) invoice.currency = user.currency;
invoice.user_id = user.id;
invoice.account_id = user.account_id;
@@ -49,15 +53,17 @@ app.post(
if (!invoice.tip) invoice.tip = 0;
- const exists = await db.Invoice.findOne({
- where: {
- [Op.or]: {
- address: invoice.address || "",
- unconfidential: invoice.unconfidential || "",
- text: invoice.text
+ const exists =
+ invoice.text &&
+ (await db.Invoice.findOne({
+ where: {
+ [Op.or]: {
+ address: invoice.address || "",
+ unconfidential: invoice.unconfidential || "",
+ text: invoice.text
+ }
}
- }
- });
+ }));
invoice = exists
? await exists.update(invoice)
diff --git a/routes/lightning/invoice.js b/routes/lightning/invoice.js
index 701ed3c2..140a3002 100644
--- a/routes/lightning/invoice.js
+++ b/routes/lightning/invoice.js
@@ -1,5 +1,6 @@
module.exports = ah(async (req, res) => {
- const { amount, memo, tip } = req.body;
+ let { amount, memo, tip } = req.body;
+ if (!tip) tip = 0;
try {
if (config.lna.clightning) {
diff --git a/routes/lightning/receive.js b/routes/lightning/receive.js
index 0c8d6557..2144470f 100644
--- a/routes/lightning/receive.js
+++ b/routes/lightning/receive.js
@@ -1,12 +1,12 @@
-const handlePayment = async (msg) => {
+const handlePayment = async msg => {
try {
- await db.transaction(async (transaction) => {
+ await db.transaction(async transaction => {
if (!msg.settled) return;
const invoice = await db.Invoice.findOne({
where: {
- text: msg.payment_request,
- },
+ text: msg.payment_request
+ }
});
if (!invoice)
@@ -22,17 +22,38 @@ const handlePayment = async (msg) => {
where: {
user_id,
asset: config.liquid.btcasset,
+ pubkey: null
},
include: {
model: db.User,
- as: "user",
+ as: "user"
},
lock: transaction.LOCK.UPDATE,
- transaction,
+ transaction
});
const { user } = account;
+ let address = convert[msg.payment_request];
+ if (address) {
+ user.account = account;
+
+ let { tx } = await liquidTx({
+ address,
+ asset: config.liquid.btcasset,
+ amount,
+ feeRate: 1000,
+ replaceable: true,
+ user
+ });
+
+ return sendLiquid({
+ address,
+ user,
+ tx
+ });
+ }
+
let preimage = msg.r_preimage.toString("hex");
let payment = await db.Payment.create(
@@ -48,7 +69,7 @@ const handlePayment = async (msg) => {
received: true,
confirmed: true,
network: "lightning",
- tip,
+ tip
},
{ transaction }
);
@@ -82,14 +103,14 @@ const handlePayment = async (msg) => {
};
if (config.lna.clightning) {
- const poll = async (ln) => {
- const wait = async (i) => {
+ const poll = async ln => {
+ const wait = async i => {
const {
bolt11: payment_request,
pay_index,
status,
msatoshi_received,
- payment_preimage: r_preimage,
+ payment_preimage: r_preimage
} = await ln.waitanyinvoice(i);
let settled = status === "paid";
@@ -99,13 +120,13 @@ if (config.lna.clightning) {
payment_request,
settled,
amt_paid_sat,
- r_preimage,
+ r_preimage
});
wait(pay_index);
};
const { invoices } = await ln.listinvoices();
- wait(Math.max(...invoices.map((i) => i.pay_index).filter((n) => n)));
+ wait(Math.max(...invoices.map(i => i.pay_index).filter(n => n)));
};
poll(lna);
diff --git a/routes/liquid/fee.js b/routes/liquid/fee.js
index 489c5c4b..82bb7604 100644
--- a/routes/liquid/fee.js
+++ b/routes/liquid/fee.js
@@ -1,25 +1,18 @@
const buildTx = require("../../lib/buildliquidtx");
-module.exports = ah(async (req, res) => {
- let { user } = req;
- let { address, asset, amount, feeRate, replaceable } = req.body;
+liquidTx = async ({ address, asset, amount, feeRate, replaceable, user }) => {
let tx, fee;
if (user.account.pubkey) {
- try {
- let psbt = await buildTx({
- address,
- asset,
- amount,
- feeRate,
- replaceable,
- user
- });
- return res.send(psbt);
- } catch (e) {
- l.error(e.message, e.stack);
- return res.status(500).send(e.message);
- }
+ let psbt = await buildTx({
+ address,
+ asset,
+ amount,
+ feeRate,
+ replaceable,
+ user
+ });
+ return psbt;
}
let invoice = await db.Invoice.findOne({
@@ -35,47 +28,51 @@ module.exports = ah(async (req, res) => {
let { ismine } = await lq.getAddressInfo(address);
if (ismine) {
emit(user.username, "to", invoice.user);
- return res.end();
+ return;
}
}
- try {
- if (config.liquid.walletpass)
- await lq.walletPassphrase(config.liquid.walletpass, 300);
+ if (config.liquid.walletpass)
+ await lq.walletPassphrase(config.liquid.walletpass, 300);
- amount = parseInt(amount);
+ amount = parseInt(amount);
- let params = {
- subtractFeeFromOutputs: amount === user.balance ? [0] : []
- };
+ let params = {
+ subtractFeeFromOutputs: amount === user.balance ? [0] : []
+ };
- let value = (amount / SATS).toFixed(8);
- if (feeRate) params.feeRate = (feeRate / SATS).toFixed(8);
+ let value = (amount / SATS).toFixed(8);
+ if (feeRate) params.feeRate = (feeRate / SATS).toFixed(8);
- let info = await lq.getAddressInfo(address);
+ let info = await lq.getAddressInfo(address);
- tx = await lq.createRawTransaction(
- [],
- {
- [address]: (amount / SATS).toFixed(8)
- },
- 0,
- replaceable,
- {
- [address]: asset
- }
- );
+ tx = await lq.createRawTransaction(
+ [],
+ {
+ [address]: (amount / SATS).toFixed(8)
+ },
+ 0,
+ replaceable,
+ {
+ [address]: asset
+ }
+ );
+
+ tx = await lq.fundRawTransaction(tx, params);
- tx = await lq.fundRawTransaction(tx, params);
+ let blinded = await lq.blindRawTransaction(tx.hex);
+ let signed = await lq.signRawTransactionWithWallet(blinded);
- let blinded = await lq.blindRawTransaction(tx.hex);
- let signed = await lq.signRawTransactionWithWallet(blinded);
+ decoded = await lq.decodeRawTransaction(signed.hex);
+ feeRate = Math.round((tx.fee * SATS * 1000) / decoded.vsize);
+ l.info("estimated", asset, feeRate);
- decoded = await lq.decodeRawTransaction(signed.hex);
- feeRate = Math.round((tx.fee * SATS * 1000) / decoded.vsize);
- l.info("estimated", asset, feeRate);
+ return { feeRate, tx };
+};
- res.send({ feeRate, tx });
+module.exports = ah(async (req, res) => {
+ try {
+ res.send(await liquidTx({ ...req.body, user: req.user }));
} catch (e) {
l.error("error estimating liquid fee", e.message);
return res.status(500).send(e.message);
diff --git a/routes/liquid/send.js b/routes/liquid/send.js
index 01b1a41d..ee62c1b8 100644
--- a/routes/liquid/send.js
+++ b/routes/liquid/send.js
@@ -1,10 +1,7 @@
const btc = config.liquid.btcasset;
const lcad = config.liquid.cadasset;
-module.exports = ah(async (req, res) => {
- let { user } = req;
- let { address, memo, tx } = req.body;
-
+sendLiquid = async ({ user, address, memo, tx }) => {
const isChange = async address =>
(await lq.getAddressInfo(address)).ismine &&
!Object.keys(addresses).includes(address);
@@ -38,113 +35,119 @@ module.exports = ah(async (req, res) => {
const assets = Object.keys(totals);
const payments = [];
- try {
- await db.transaction(async transaction => {
- for (let i = 0; i < assets.length; i++) {
- let asset = assets[i];
- let amount = totals[asset];
- if (change[asset]) amount -= change[asset];
- let total = amount;
-
- if (asset === btc) {
- let covered = 0;
- let nonbtc = assets.filter(a => a !== btc);
- if (nonbtc.length === 1) {
- let faucet = await db.Account.findOne({
- where: {
- asset: nonbtc[0],
- user_id: null
- },
- lock: transaction.LOCK.UPDATE,
- transaction
- });
-
- if (faucet) {
- covered = faucet.balance;
- if (covered > fee) covered = fee;
- await faucet.decrement({ balance: covered }, { transaction });
- await faucet.reload({ transaction });
- await faucet.save({ transaction });
- }
+ return db.transaction(async transaction => {
+ for (let i = 0; i < assets.length; i++) {
+ let asset = assets[i];
+ let amount = totals[asset];
+ if (change[asset]) amount -= change[asset];
+ let total = amount;
- total += fee - covered;
- }
- }
-
- if (asset !== btc || total) {
- l.info("creating liquid payment", user.username, asset, total, fee);
-
- let account = await db.Account.findOne({
+ if (asset === btc) {
+ let covered = 0;
+ let nonbtc = assets.filter(a => a !== btc);
+ if (nonbtc.length === 1) {
+ let faucet = await db.Account.findOne({
where: {
- user_id: user.id,
- asset,
- pubkey: null
+ asset: nonbtc[0],
+ user_id: null
},
lock: transaction.LOCK.UPDATE,
- order: [['balance', 'DESC']],
transaction
});
- if (total > account.balance) {
- l.warn("amount exceeds balance", {
- total,
- fee,
- balance: account.balance
- });
- throw new Error(
- `Insufficient funds, need ${total} ${
- account.ticker === "BTC" ? "SAT" : account.ticker
- }, have ${account.balance}`
- );
+ if (faucet) {
+ covered = faucet.balance;
+ if (covered > fee) covered = fee;
+ await faucet.decrement({ balance: covered }, { transaction });
+ await faucet.reload({ transaction });
+ await faucet.save({ transaction });
}
- await account.decrement({ balance: total }, { transaction });
- await account.reload({ transaction });
+ total += fee - covered;
+ }
+ }
- let payment = {
- amount: -amount,
- account_id: account.id,
- fee,
- memo,
+ if (asset !== btc || total) {
+ l.info("creating liquid payment", user.username, asset, total, fee);
+
+ let account = await db.Account.findOne({
+ where: {
user_id: user.id,
- rate: app.get("rates")[user.currency],
- currency: user.currency,
- address,
- confirmed: true,
- received: false,
- network: "liquid"
- };
+ asset,
+ pubkey: null
+ },
+ lock: transaction.LOCK.UPDATE,
+ order: [["balance", "DESC"]],
+ transaction
+ });
- payment.account = account;
- payments.push(payment);
+ if (total > account.balance) {
+ l.warn("amount exceeds balance", {
+ total,
+ fee,
+ balance: account.balance
+ });
+ throw new Error(
+ `Insufficient funds, need ${total} ${
+ account.ticker === "BTC" ? "SAT" : account.ticker
+ }, have ${account.balance}`
+ );
}
+
+ await account.decrement({ balance: total }, { transaction });
+ await account.reload({ transaction });
+
+ let payment = {
+ amount: -amount,
+ account_id: account.id,
+ fee,
+ memo,
+ user_id: user.id,
+ rate: app.get("rates")[user.currency],
+ currency: user.currency,
+ address,
+ confirmed: true,
+ received: false,
+ network: "liquid"
+ };
+
+ payment.account = account;
+ payments.push(payment);
}
+ }
- if (config.liquid.walletpass)
- await lq.walletPassphrase(config.liquid.walletpass, 300);
+ if (config.liquid.walletpass)
+ await lq.walletPassphrase(config.liquid.walletpass, 300);
- let blinded = await lq.blindRawTransaction(tx.hex);
- let signed = await lq.signRawTransactionWithWallet(blinded);
- let txid = await lq.sendRawTransaction(signed.hex);
+ let blinded = await lq.blindRawTransaction(tx.hex);
+ let signed = await lq.signRawTransactionWithWallet(blinded);
+ let txid = await lq.sendRawTransaction(signed.hex);
- let main;
- for (let i = 0; i < assets.length; i++) {
- p = payments[i];
- if (p) {
- let { account } = p;
- p.hash = txid;
- p = await db.Payment.create(p, { transaction });
- if (account.ticker !== "BTC" || !main) {
- main = p.get({ plain: true });
- main.account = account.get({ plain: true });
- }
+ let main;
+ for (let i = 0; i < assets.length; i++) {
+ p = payments[i];
+ if (p) {
+ let { account } = p;
+ p.hash = txid;
+ p = await db.Payment.create(p, { transaction });
+ if (account.ticker !== "BTC" || !main) {
+ main = p.get({ plain: true });
+ main.account = account.get({ plain: true });
}
}
+ }
- emit(user.username, "account", main.account);
- emit(user.username, "payment", main);
- res.send(main);
- });
+ emit(user.username, "account", main.account);
+ emit(user.username, "payment", main);
+ return main;
+ });
+};
+
+module.exports = ah(async (req, res) => {
+ let { user } = req;
+ let { address, memo, tx } = req.body;
+ try {
+ res.send(await sendLiquid({ user, address, memo, tx }));
} catch (e) {
l.error("problem sending liquid", user.username, e.message);
return res.status(500).send(e.message);