diff options
| author | Adam Soltys <[email protected]> | 2020-05-18 15:37:50 -0700 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2020-05-18 17:19:43 -0700 |
| commit | ff4facd8960f605fa1ff9fa68d73ddd267cb0b8f (patch) | |
| tree | dbb5b8e76e7eb54cd5ba097b7734c785b058d8f5 /routes | |
| parent | 11724003fbbcd12b90b27dc370c9537eb1bbcbf2 (diff) | |
| download | coinos-server-ff4facd8960f605fa1ff9fa68d73ddd267cb0b8f.tar.gz coinos-server-ff4facd8960f605fa1ff9fa68d73ddd267cb0b8f.zip | |
asset issuance
Diffstat (limited to 'routes')
| -rw-r--r-- | routes/assets.js | 109 | ||||
| -rw-r--r-- | routes/liquid/receive.js | 53 | ||||
| -rw-r--r-- | routes/payments.js | 1 |
3 files changed, 147 insertions, 16 deletions
diff --git a/routes/assets.js b/routes/assets.js index 10eee1f5..926bdf74 100644 --- a/routes/assets.js +++ b/routes/assets.js @@ -1,4 +1,5 @@ const axios = require("axios"); +const crypto = require("crypto"); app.get("/assets", async (req, res) => { let assets; @@ -6,8 +7,112 @@ app.get("/assets", async (req, res) => { try { assets = await axios.get("https://assets.blockstream.info/"); res.send(assets.data); - } catch(e) { + } catch (e) { l.error("error fetching assets", e); res.status(500).send("error fetching assets"); - } + } +}); + +app.post("/assets", auth, async (req, res) => { + try { + const sha256 = crypto.createHash("sha256"); + const { id: user_id } = req.user; + const token_address = await lq.getNewAddress("", "legacy"); + const asset_address = await lq.getNewAddress("", "legacy"); + const blind = false; + const info = await lq.getAddressInfo(asset_address); + const { pubkey: issuer_pubkey } = info; + + const { name, asset_amount, token_amount, precision, ticker } = req.body; + const domain = "adamsoltys.com"; + const version = 0; + + const contract = { + entity: { domain }, + issuer_pubkey, + name, + precision, + ticker, + version + }; + + sha256.update(JSON.stringify(contract)); + const hash = sha256.digest("hex"); + const contract_hash = hash + .match(/[a-f0-9]{2}/g) + .reverse() + .join(""); + const rawtx = await lq.createRawTransaction([], { data: "00" }); + const funded = await lq.fundRawTransaction(rawtx, { feeRate: 0.00003 }); + const ria = await lq.rawIssueAsset(funded.hex, [ + { + asset_amount, + token_amount, + asset_address, + token_address, + blind, + contract_hash + } + ]); + const { asset, hex } = ria[0]; + const brt = await lq.blindRawTransaction(hex, true, [], false); + const srt = await lq.signRawTransactionWithWallet(brt); + const allowed = (await lq.testMempoolAccept([srt.hex]))[0].allowed; + if (allowed) { + const txid = await lq.sendRawTransaction(srt.hex); + + await db.transaction(async transaction => { + let account = await db.Account.findOne({ + where: { + user_id, + asset: config.liquid.btcasset + }, + lock: transaction.LOCK.UPDATE, + transaction + }); + + if (Math.round(funded.fee * SATS) > account.balance) { + l.error("amount exceeds balance", amount, fee, account.balance); + throw new Error("insufficient funds"); + } + + account.balance -= Math.round(funded.fee * SATS); + await account.save({ transaction }); + + account = await db.Account.create( + { + asset, + user_id, + ticker, + precision, + name, + balance: 0, + pending: asset_amount * SATS + }, + { transaction } + ); + + const payment = await db.Payment.create({ + account_id: account.id, + user_id, + hash: txid, + amount: asset_amount * SATS, + received: true, + confirmed: false, + address: asset_address, + network: "LBTC" + }); + + issuances[txid] = { user_id, asset, asset_amount, payment_id: payment.id }; + + let user = await getUserById(user_id, transaction); + emit(user.username, "user", user); + }); + + res.send(issuances[txid]); + } + } catch (e) { + l.error("asset issuance failed", e.message); + res.status(500).send("Failed to issue asset"); + } }); diff --git a/routes/liquid/receive.js b/routes/liquid/receive.js index 9ef67967..803caa90 100644 --- a/routes/liquid/receive.js +++ b/routes/liquid/receive.js @@ -20,7 +20,7 @@ zmqRawTx.on("message", async (topic, message, sequence) => { if (payments.includes(blinded.txid)) return; Promise.all( - tx.vout.map(async (o) => { + tx.vout.map(async o => { if (!(o.scriptPubKey && o.scriptPubKey.addresses)) return; const { asset } = o; @@ -30,19 +30,19 @@ zmqRawTx.on("message", async (topic, message, sequence) => { if (Object.keys(addresses).includes(address)) { let user = await db.User.findOne({ where: { - username: addresses[address], - }, + username: addresses[address] + } }); let confirmed = 0; let params = { user_id: user.id, - asset, + asset }; let account = await db.Account.findOne({ - where: params, + where: params }); if (account) { @@ -83,9 +83,9 @@ zmqRawTx.on("message", async (topic, message, sequence) => { invoice = await db.Invoice.findOne({ where: { user_id: user.id, - network: "LBTC", + network: "LBTC" }, - order: [["id", "DESC"]], + order: [["id", "DESC"]] }); } @@ -104,7 +104,7 @@ zmqRawTx.on("message", async (topic, message, sequence) => { tip, confirmed, address, - network: "LBTC", + network: "LBTC" }); payments.push(blinded.txid); @@ -124,17 +124,42 @@ let queue = {}; zmqRawBlock.on("message", async (topic, message, sequence) => { const payments = await db.Payment.findAll({ - where: { confirmed: 0 }, + where: { confirmed: 0 } }); - const hashes = payments.map((p) => p.hash); + const hashes = payments.map(p => p.hash); const block = elements.Block.fromHex(message.toString("hex"), true); const hash = await lq.getBlockHash(block.height); const json = await lq.getBlock(hash, 2); - json.tx.map((tx) => { - if (hashes.includes(tx.txid)) queue[tx.txid] = 1; + json.tx.map(async tx => { + if (issuances[tx.txid]) { + await db.transaction(async transaction => { + const { user_id, asset, asset_amount, payment_id } = issuances[tx.txid]; + const account = await db.Account.findOne({ + where: { user_id, asset }, + lock: transaction.LOCK.UPDATE, + transaction + }); + account.balance = asset_amount * SATS; + account.pending = 0; + await account.save({ transaction }); + + const payment = await db.Payment.findOne({ + where: { id: payment_id }, + lock: transaction.LOCK.UPDATE, + transaction + }); + + payment.confirmed = true; + await payment.save({ transaction }); + + const user = await getUserById(user_id, transaction); + emit(user.username, "user", user); + }); + } + else if (hashes.includes(tx.txid)) queue[tx.txid] = 1; }); }); @@ -150,8 +175,8 @@ setInterval(async () => { where: { hash, confirmed: 0, received: 1 }, include: { model: db.Account, - as: "account", - }, + as: "account" + } }); p.confirmed = 1; diff --git a/routes/payments.js b/routes/payments.js index 81d7540b..ab35c9a0 100644 --- a/routes/payments.js +++ b/routes/payments.js @@ -6,6 +6,7 @@ const { join } = require("path"); (async () => { seen = []; addresses = {}; + issuances = {}; await db.User.findAll({ attributes: ["username", "address", "liquid"] |
