diff options
| author | Adam Soltys <[email protected]> | 2021-09-24 02:19:30 -0400 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2021-09-24 02:19:30 -0400 |
| commit | 18814242ad055ecd6f9de1d23209958e12aee69c (patch) | |
| tree | f9abc8b8716b5587c8a71700df62ebfe2098f4f6 | |
| parent | fd44b77a4ddac5beef34f65be8e5558f4b4291f9 (diff) | |
| download | coinos-server-18814242ad055ecd6f9de1d23209958e12aee69c.tar.gz coinos-server-18814242ad055ecd6f9de1d23209958e12aee69c.zip | |
always create account on issuance
| -rw-r--r-- | db/migrations/20210814193811_fix_db_conflicts.js | 16 | ||||
| -rw-r--r-- | db/migrations/20210821013757_cull_usernames.js | 11 | ||||
| -rw-r--r-- | index.js | 2 | ||||
| -rw-r--r-- | lib/buildliquidtx.js | 31 | ||||
| -rw-r--r-- | lib/rates.js | 1 | ||||
| -rw-r--r-- | lib/send.js | 4 | ||||
| -rw-r--r-- | routes/assets.js | 88 | ||||
| -rw-r--r-- | routes/bitcoin/receive.js | 12 | ||||
| -rw-r--r-- | routes/lightning/receive.js | 2 | ||||
| -rw-r--r-- | routes/lightning/send.js | 2 | ||||
| -rw-r--r-- | routes/liquid/receive.js | 2 | ||||
| -rw-r--r-- | routes/liquid/send.js | 256 | ||||
| -rw-r--r-- | routes/lnurl.js | 4 | ||||
| -rw-r--r-- | routes/payments.js | 11 |
14 files changed, 230 insertions, 212 deletions
diff --git a/db/migrations/20210814193811_fix_db_conflicts.js b/db/migrations/20210814193811_fix_db_conflicts.js deleted file mode 100644 index 37ff2bbc..00000000 --- a/db/migrations/20210814193811_fix_db_conflicts.js +++ /dev/null @@ -1,16 +0,0 @@ - -exports.up = function(knex) { - return Promise.all([ - knex.raw("UPDATE waiting_list set created_at = '2021-01-01', updated_at='2021-01-01' WHERE updated_at IS NULL OR created_at IS NULL"), - knex.raw("ALTER TABLE waiting_list MODIFY created_at datetime NOT NULL DEFAULT CURRENT_TIMESTAMP"), - knex.raw("ALTER TABLE waiting_list MODIFY updated_at datetime NOT NULL DEFAULT CURRENT_TIMESTAMP"), - - knex.raw("UPDATE referrals set created_at = '2021-01-01', updated_at='2021-01-01' WHERE updated_at IS NULL OR created_at IS NULL"), - knex.raw("ALTER TABLE referrals MODIFY created_at datetime NOT NULL DEFAULT CURRENT_TIMESTAMP"), - knex.raw("ALTER TABLE referrals MODIFY updated_at datetime NOT NULL DEFAULT CURRENT_TIMESTAMP") - ]) -}; - -exports.down = function(knex) { - -}; diff --git a/db/migrations/20210821013757_cull_usernames.js b/db/migrations/20210821013757_cull_usernames.js deleted file mode 100644 index 5d2a4672..00000000 --- a/db/migrations/20210821013757_cull_usernames.js +++ /dev/null @@ -1,11 +0,0 @@ - -exports.up = function(knex) { - return Promise.all([ - knex.raw("UPDATE users SET username = REPLACE(username, ' ', '')"), // just in case valid users have trailing spaces in their username - knex.raw("DELETE FROM users WHERE !(username REGEXP '^[a-zA-Z0-9\.\_\@\-]{3,32}$')"), - ]) -}; - -exports.down = function(knex) { - -}; @@ -116,7 +116,7 @@ app.use((err, req, res, next) => { }); server.listen(config.port, () => - console.log(`CoinOS Server listening on port ${config.port}`) + l.info(`CoinOS Server listening on port ${config.port}`) ); process.on("SIGINT", process.exit); diff --git a/lib/buildliquidtx.js b/lib/buildliquidtx.js index 7bebb548..aab8e000 100644 --- a/lib/buildliquidtx.js +++ b/lib/buildliquidtx.js @@ -7,6 +7,7 @@ const api = prod : "http://localhost:3002"; const SATS = 100000000; const network = prod ? liquid.networks["liquid"] : liquid.networks["regtest"]; +const { Op } = require("sequelize"); module.exports = async ({ address, @@ -26,13 +27,29 @@ module.exports = async ({ feeRate = Math.round(feeRate / 100); if (asset !== btc) feeRate = 0; - const payments = await db.Payment.findAll({ - where: { - received: true, - account_id: user.account_id, - network: "liquid" - } - }); + let payments; + if (asset === btc) { + payments = await db.Payment.findAll({ + where: { + received: true, + account_id: user.account_id, + network: "liquid" + } + }); + } else { + payments = await db.Payment.findAll({ + where: { + "$account.pubkey": { [Op.ne]: null }, + received: true, + user_id: user.id, + network: "liquid" + }, + include: { + model: db.Account, + as: "account" + } + }); + } let utxos = []; for (let i = 0; i < payments.length; i++) { diff --git a/lib/rates.js b/lib/rates.js index 598fc041..55dc1ea5 100644 --- a/lib/rates.js +++ b/lib/rates.js @@ -59,6 +59,7 @@ setInterval(() => { }, 1000); setInterval(() => { + l.info("refreshing binance socket"); binance.terminate(); binance = require("./binance")(); }, 360000); diff --git a/lib/send.js b/lib/send.js index 293473d9..e08e9033 100644 --- a/lib/send.js +++ b/lib/send.js @@ -2,6 +2,8 @@ const bolt11 = require("bolt11"); const { Op } = require("sequelize"); send = async (amount, memo, hash, user) => { + l.info("attempting lightning payment", user.username, amount, hash); + let payreq = bolt11.decode(hash); let routingInfo = payreq.tags.find(t => t.tagName === "routing_info"); if ( @@ -59,7 +61,7 @@ send = async (amount, memo, hash, user) => { fee_limit: { fixed: Math.min(10000, account.balance) }, }); - if (!m) throw new Error("lightning payment failed"); + if (!m) throw new Error(`lightning payment failed ${amount} ${hash}`); if (m.payment_error) throw new Error(m.payment_error); ({ diff --git a/routes/assets.js b/routes/assets.js index 72d4c329..b33e43b4 100644 --- a/routes/assets.js +++ b/routes/assets.js @@ -204,7 +204,6 @@ app.post( await account.reload({ transaction }); emit(user.username, "account", account); - if (address) return; account = await db.Account.create( { asset, @@ -216,7 +215,7 @@ app.post( name, balance: 0, network: "liquid", - pending: params.asset_amount * SATS + pending: address ? 0 : params.asset_amount * SATS }, { transaction } ); @@ -227,30 +226,33 @@ app.post( user.username, params.asset_amount, ticker, - name + name, + account.id ); - const asset_payment = await db.Payment.create( - { - account_id: account.id, - user_id, - hash: txid, - amount: params.asset_amount * SATS, - received: true, - confirmed: false, - address: asset_address, - network: "liquid" - }, - { transaction } - ); - emit(user.username, "payment", asset_payment); + if (!address) { + const asset_payment = await db.Payment.create( + { + account_id: account.id, + user_id, + hash: txid, + amount: params.asset_amount * SATS, + received: true, + confirmed: false, + address: asset_address, + network: "liquid" + }, + { transaction } + ); + emit(user.username, "payment", asset_payment); - issuances[txid] = { - user_id, - asset, - asset_amount: params.asset_amount, - asset_payment_id: asset_payment.id - }; + issuances[txid] = { + user_id, + asset, + asset_amount: params.asset_amount, + asset_payment_id: asset_payment.id + }; + } if (token_amount) { account = await db.Account.create( @@ -262,30 +264,32 @@ app.post( precision: 8, name: `${name} Reissuance Token`, balance: 0, - pending: token_amount * SATS + pending: address ? 0 : token_amount * SATS }, { transaction } ); emit(user.username, "account", account); - const token_payment = await db.Payment.create( - { - account_id: account.id, - user_id, - hash: txid, - amount: token_amount * SATS, - received: true, - confirmed: false, - address: token_address, - network: "liquid" - }, - { transaction } - ); - emit(user.username, "payment", token_payment); + if (!address) { + const token_payment = await db.Payment.create( + { + account_id: account.id, + user_id, + hash: txid, + amount: token_amount * SATS, + received: true, + confirmed: false, + address: token_address, + network: "liquid" + }, + { transaction } + ); + emit(user.username, "payment", token_payment); - issuances[txid].token = token; - issuances[txid].token_amount = token_amount; - issuances[txid].token_payment_id = token_payment.id; + issuances[txid].token = token; + issuances[txid].token_amount = token_amount; + issuances[txid].token_payment_id = token_payment.id; + } } }); @@ -309,6 +313,8 @@ app.post( } }); + l.info("registering", asset, account.contract); + try { const { data: result } = await axios.post( "https://assets.blockstream.info/", diff --git a/routes/bitcoin/receive.js b/routes/bitcoin/receive.js index 327d1a0c..c14e636c 100644 --- a/routes/bitcoin/receive.js +++ b/routes/bitcoin/receive.js @@ -76,6 +76,16 @@ zmqRawTx.on("message", async (topic, message, sequence) => { let { account } = invoice; + if (account.asset !== config.liquid.btcasset) { + account = await db.Account.findOne({ + where: { + user_id: user.id, + asset: config.liquid.btcasset, + pubkey: null + } + }); + } + account.pending += value; await account.save(); @@ -208,7 +218,7 @@ setInterval(async () => { ); user.account = account; - await sendLiquid({ + sendLiquid({ address: c.address, amount: total - 100, user, diff --git a/routes/lightning/receive.js b/routes/lightning/receive.js index d974860e..ffc0ae03 100644 --- a/routes/lightning/receive.js +++ b/routes/lightning/receive.js @@ -93,7 +93,7 @@ const handlePayment = async (msg) => { user.account = account; try { - await sendLiquid({ + sendLiquid({ address: c.address, amount: total - 100, user, diff --git a/routes/lightning/send.js b/routes/lightning/send.js index f3c0da46..f6bd1b36 100644 --- a/routes/lightning/send.js +++ b/routes/lightning/send.js @@ -5,7 +5,7 @@ module.exports = ah(async (req, res) => { try { res.send(await send(amount, memo, payreq, user)); } catch (e) { - l.error("problem sending lightning payment", user.username, e.message, e.stack); + l.error("problem sending lightning payment", user.username, e.message); res.status(500).send(e.message); } }); diff --git a/routes/liquid/receive.js b/routes/liquid/receive.js index 31cddbef..bc4293d3 100644 --- a/routes/liquid/receive.js +++ b/routes/liquid/receive.js @@ -358,7 +358,7 @@ setInterval(async () => { user.account = p.account; - await sendLiquid({ + sendLiquid({ address: c.address, amount: total - 100, user, diff --git a/routes/liquid/send.js b/routes/liquid/send.js index 0c0b2012..f5c46a6f 100644 --- a/routes/liquid/send.js +++ b/routes/liquid/send.js @@ -3,165 +3,169 @@ const lcad = config.liquid.cadasset; const { Transaction } = require("@asoltys/liquidjs-lib"); sendLiquid = async ({ asset, amount, user, address, memo, tx, limit }) => { - l.info("sending liquid", amount, address); - if (!tx) { - ({ tx } = await liquidTx({ - address, - asset, - amount, - feeRate: 100, - replaceable: false, - user - })); - } + try { + l.info("sending liquid", amount, address); + if (!tx) { + ({ tx } = await liquidTx({ + address, + asset, + amount, + feeRate: 200, + replaceable: false, + user + })); + } - const isChange = async address => - (await lq.getAddressInfo(address)).ismine && - !Object.keys(addresses).includes(address); + const isChange = async address => + (await lq.getAddressInfo(address)).ismine && + !Object.keys(addresses).includes(address); - let totals = {}; - let change = {}; - let fee = 0; + let totals = {}; + let change = {}; + let fee = 0; - let { vout } = await lq.decodeRawTransaction(tx.hex); + let { vout } = await lq.decodeRawTransaction(tx.hex); - for (let i = 0; i < vout.length; i++) { - let { - asset, - value, - scriptPubKey: { type, addresses } - } = vout[i]; + for (let i = 0; i < vout.length; i++) { + let { + asset, + value, + scriptPubKey: { type, addresses } + } = vout[i]; - if (type === "fee") fee = toSats(value); - else { - if (!totals[asset]) totals[asset] = change[asset] = 0; - totals[asset] += toSats(value); + if (type === "fee") fee = toSats(value); + else { + if (!totals[asset]) totals[asset] = change[asset] = 0; + totals[asset] += toSats(value); - if (addresses) { - if (await isChange(addresses[0])) { - change[asset] += toSats(value); + if (addresses) { + if (await isChange(addresses[0])) { + change[asset] += toSats(value); + } } } } - } - const assets = Object.keys(totals); - const payments = []; + const assets = Object.keys(totals); + const payments = []; + + let main, signed; + 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 + }); - let main, signed; - 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 (faucet) { + covered = faucet.balance; + if (covered > fee) covered = fee; + await faucet.decrement({ balance: covered }, { transaction }); + await faucet.reload({ transaction }); + await faucet.save({ transaction }); + } + } + + total += fee - covered; + } + + if (limit && total > limit + fee) + throw new Error("Tx amount exceeds authorized amount"); - if (asset === btc) { - let covered = 0; - let nonbtc = assets.filter(a => a !== btc); - if (nonbtc.length === 1) { - let faucet = await db.Account.findOne({ + if (asset !== btc || total) { + l.info("creating liquid payment", user.username, asset, total, fee); + + let account = await db.Account.findOne({ where: { - asset: nonbtc[0], - user_id: null + user_id: user.id, + asset, + pubkey: null }, lock: transaction.LOCK.UPDATE, + order: [["balance", "DESC"]], 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 }); + 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}` + ); } - } - total += fee - covered; - } + await account.decrement({ balance: total }, { transaction }); + await account.reload({ transaction }); - if (limit && total > limit + fee) - throw new Error("Tx amount exceeds authorized amount"); - - if (asset !== btc || total) { - l.info("creating liquid payment", user.username, asset, total, fee); - - let account = await db.Account.findOne({ - where: { + let payment = { + amount: -amount, + account_id: account.id, + fee, + memo, user_id: user.id, - asset, - pubkey: null - }, - lock: transaction.LOCK.UPDATE, - order: [["balance", "DESC"]], - transaction - }); + rate: app.get("rates")[user.currency], + currency: user.currency, + address, + confirmed: true, + received: false, + network: "liquid" + }; - 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}` - ); + payment.account = account; + payments.push(payment); } - - 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); } - } - signed = await lq.signRawTransactionWithWallet( - await lq.blindRawTransaction(tx.hex) - ); - let txid = Transaction.fromHex(signed.hex).getId(); + signed = await lq.signRawTransactionWithWallet( + await lq.blindRawTransaction(tx.hex) + ); + let txid = Transaction.fromHex(signed.hex).getId(); - 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 }); + 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); - }); + emit(user.username, "account", main.account); + emit(user.username, "payment", main); + }); - if (config.liquid.walletpass) - await lq.walletPassphrase(config.liquid.walletpass, 300); + if (config.liquid.walletpass) + await lq.walletPassphrase(config.liquid.walletpass, 300); - let txid = await lq.sendRawTransaction(signed.hex); - l.info("sent liquid tx", txid, address); + let txid = await lq.sendRawTransaction(signed.hex); + l.info("sent liquid tx", txid, address); - return main; + return main; + } catch (e) { + l.error("problem sending liquid", e.message); + } }; module.exports = ah(async (req, res) => { diff --git a/routes/lnurl.js b/routes/lnurl.js index feceb913..b07aeeab 100644 --- a/routes/lnurl.js +++ b/routes/lnurl.js @@ -117,6 +117,7 @@ app.post( app.get( "/lnurlp/:username", ah(async (req, res, next) => { + throw new Error("LNURL payments temporarily disabled due to a server issue. Sep. 2, 2021. Join our Telegram channel https://t.me/coinoswallet for updates"); const { username } = req.params; let user = await db.User.findOne({ where: { @@ -152,6 +153,7 @@ app.get( app.get( "/pay/:username", ah(async (req, res, next) => { + throw new Error("LNURL payments temporarily disabled due to a server issue. Sep. 2, 2021. Join our Telegram channel https://t.me/coinoswallet for updates"); const { username } = req.params; let user = await db.User.findOne({ where: { @@ -185,6 +187,7 @@ app.post( "/pay", auth, ah(async (req, res, next) => { + throw new Error("LNURL payments temporarily disabled due to a server issue. Sep. 2, 2021. Join our Telegram channel https://t.me/coinoswallet for updates"); const { user } = req; const { amount, @@ -194,7 +197,6 @@ app.post( let url = `${callback}${callback.includes("?") ? "&" : "?"}amount=${amount * 1000}${comment ? "&comment=" + comment : ""}`; - console.log(url); try { const parts = callback.split("/"); diff --git a/routes/payments.js b/routes/payments.js index 58b176d0..7ecfb36f 100644 --- a/routes/payments.js +++ b/routes/payments.js @@ -71,7 +71,8 @@ ah(async () => { .map(i => ({ amount: parseInt(i.amt_paid_sat), preimage: i.r_preimage.toString("hex"), - pr: i.payment_request.toString("hex") + pr: i.payment_request.toString("hex"), + createdAt: new Date(i.settle_date * 1000) })); const twoDaysAgo = new Date(new Date().setDate(new Date().getDate() - 2)); @@ -84,13 +85,13 @@ ah(async () => { let missed = recent.filter(i => !settled.includes(i.preimage)); for (let i = 0; i < missed.length; i++) { - let { amount, preimage, pr: text } = missed[i]; + let { amount, preimage, pr: text, createdAt } = missed[i]; let invoice = await db.Invoice.findOne({ where: { text }, include: { model: db.User, as: "user" - }, + } }); if (invoice && invoice.user_id) { @@ -115,7 +116,9 @@ ah(async () => { confirmed: true, network: "lightning", tip: invoice.tip, - invoice_id: invoice.id + invoice_id: invoice.id, + createdAt, + updatedAt: createdAt }); await account.increment({ balance: amount }); |
