diff options
| author | Adam Soltys <[email protected]> | 2020-10-12 00:39:45 -0700 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2020-10-12 00:39:45 -0700 |
| commit | a90d6cc97470696ade30f237b050b8b329858a17 (patch) | |
| tree | d2b0144a6076d799aa779a731f61e7c9c864f6a7 | |
| parent | d7e5d530bd37f611beb1884c0587593fb9fb5880 (diff) | |
| download | coinos-server-a90d6cc97470696ade30f237b050b8b329858a17.tar.gz coinos-server-a90d6cc97470696ade30f237b050b8b329858a17.zip | |
javascript address generation
| -rw-r--r-- | lib/register.js | 28 | ||||
| -rw-r--r-- | package.json | 1 | ||||
| -rw-r--r-- | routes/bitcoin/receive.js | 5 | ||||
| -rw-r--r-- | routes/invoices.js | 2 | ||||
| -rw-r--r-- | routes/payments.js | 14 | ||||
| -rw-r--r-- | routes/users.js | 113 | ||||
| -rw-r--r-- | yarn.lock | 12 |
7 files changed, 127 insertions, 48 deletions
diff --git a/lib/register.js b/lib/register.js index 7502c626..23e20a72 100644 --- a/lib/register.js +++ b/lib/register.js @@ -12,34 +12,6 @@ register = async (user, ip, requireChallenge) => { user.password = await bcrypt.hash(user.password, 1); } - if (config.bitcoin) { - if (config.bitcoin.walletpass) - await bc.walletPassphrase(config.bitcoin.walletpass, 300); - - user.address = await bc.getNewAddress("", "bech32"); - addresses[user.address] = user.username; - } - - if (config.liquid) { - if (config.liquid.walletpass) - await lq.walletPassphrase(config.liquid.walletpass, 300); - - user.confidential = await lq.getNewAddress(); - user.liquid = (await lq.getAddressInfo(user.confidential)).unconfidential; - addresses[user.liquid] = user.username; - } - - if ( - requireChallenge && - (!challenge[ip] || - user.response.toLowerCase() !== challenge[ip].toLowerCase()) - ) { - l.info("failed challenge", ip, user.response, challenge[ip]); - throw new Error("Failed challenge"); - } - - delete challenge[ip]; - let countries = { CA: "CAD", US: "USD", diff --git a/package.json b/package.json index 746e4c6a..53668ac7 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,7 @@ }, "dependencies": { "@grpc/proto-loader": "^0.5.3", + "async-mutex": "^0.2.4", "axios": "^0.19.0", "bcrypt": "^5.0.0", "bip32": "^2.0.5", diff --git a/routes/bitcoin/receive.js b/routes/bitcoin/receive.js index 12eb760e..e0e3828b 100644 --- a/routes/bitcoin/receive.js +++ b/routes/bitcoin/receive.js @@ -77,11 +77,6 @@ zmqRawTx.on("message", async (topic, message, sequence) => { if (config.bitcoin.walletpass) await bc.walletPassphrase(config.bitcoin.walletpass, 300); - user.address = await bc.getNewAddress("", "bech32"); - await user.save(); - - addresses[user.address] = user.username; - let totalOutputs = tx.outs.reduce((a, b) => a + b.value, 0); let totalInputs = 0; for (let i = 0; i < tx.ins.length; i++) { diff --git a/routes/invoices.js b/routes/invoices.js index f084b4c3..443c1565 100644 --- a/routes/invoices.js +++ b/routes/invoices.js @@ -62,7 +62,7 @@ app.post("/invoice", ah(async (req, res, next) => { addresses[invoice.address] = user.username; if (invoice.unconfidential) { addresses[invoice.unconfidential] = user.username; - await lq.importBlindingKey(invoice.address, blindkey); + if (blindkey) await lq.importBlindingKey(invoice.address, blindkey); } res.send(invoice); } catch(e) { diff --git a/routes/payments.js b/routes/payments.js index 11242667..cf9e1d00 100644 --- a/routes/payments.js +++ b/routes/payments.js @@ -113,6 +113,13 @@ ah(async () => { app.post("/bitcoin/fee", auth, require("./bitcoin/fee")); app.post("/bitcoin/send", auth, require("./bitcoin/send")); require("./bitcoin/receive"); + + setTimeout(async () => { + const address = await bc.getNewAddress(); + const { hdkeypath } = await bc.getAddressInfo(address); + const parts = hdkeypath.split('/'); + app.set("bcAddressIndex", parts[parts.length - 1].slice(0, -1)); + }, 50); } if (config.liquid) { @@ -126,6 +133,13 @@ ah(async () => { app.post("/liquid/fee", auth, require("./liquid/fee")); app.post("/liquid/send", auth, require("./liquid/send")); require("./liquid/receive"); + + setTimeout(async () => { + const address = await lq.getNewAddress(); + const { hdkeypath } = await lq.getAddressInfo(address); + const parts = hdkeypath.split('/'); + app.set("lqAddressIndex", parts[parts.length - 1].slice(0, -1)); + }, 50); } app.get( diff --git a/routes/users.js b/routes/users.js index d40ad5a8..6e9678fe 100644 --- a/routes/users.js +++ b/routes/users.js @@ -6,6 +6,11 @@ const textToImage = require("text-to-image"); const randomWord = require("random-words"); const getAccount = require("../lib/account"); const Sequelize = require("sequelize"); +const bitcoin = require("bitcoinjs-lib"); +const liquid = require("liquidjs-lib"); +const { fromBase58, fromPrivateKey } = require("bip32"); +const { Mutex } = require("async-mutex"); +const bip32 = require("bip32"); const pick = (O, ...K) => K.reduce((o, k) => ((o[k] = O[k]), o), {}); require("../lib/whitelist"); @@ -259,7 +264,16 @@ app.post( "/accounts", auth, ah(async (req, res) => { - const { name, seed, pubkey, ticker, precision, path, privkey, network } = req.body; + const { + name, + seed, + pubkey, + ticker, + precision, + path, + privkey, + network + } = req.body; const { user } = req; let account = await db.Account.create({ @@ -384,24 +398,84 @@ app.post( app.get( "/address", ah(async (req, res) => { - const { network, type } = req.query; + let { network, type } = req.query; let address; - if (network === "bitcoin") { - if (!config.bitcoin) - return res.status(500).send("Bitcoin not configured"); + type = { + bech32: "p2wpkh", + "p2sh-segwit": "p2sh", + legacy: "p2pkh" + }[type]; - if (config.bitcoin.walletpass) - await bc.walletPassphrase(config.bitcoin.walletpass, 300); + let hd, i, p, n; + const mutex = new Mutex(); + const release = await mutex.acquire(); + try { + if (network === "bitcoin") { + if (!config.bitcoin) + return res.status(500).send("Bitcoin not configured"); + p = bitcoin.payments; + n = prod ? bitcoin.networks["bitcoin"] : bitcoin.networks["regtest"]; - address = await bc.getNewAddress("", type || "bech32"); - } else if (network === "liquid") { - if (!config.liquid) return res.status(500).send("Liquid not configured"); + i = parseInt(app.get("bcAddressIndex")); + hd = fromBase58(config.bitcoin.masterkey, n).derivePath( + `m/0'/0'/${i}'` + ); + app.set("bcAddressIndex", i + 1); + } else if (network === "liquid") { + p = liquid.payments; + n = + liquid.networks[ + config.liquid.network === "mainnet" + ? "liquid" + : config.liquid.network + ]; + type = "p2sh"; - if (config.liquid.walletpass) - await lq.walletPassphrase(config.liquid.walletpass, 300); + i = parseInt(app.get("lqAddressIndex")); + hd = fromBase58(config.liquid.masterkey, n).derivePath(`m/0'/0'/${i}'`); + app.set("lqAddressIndex", i + 1); + } else { + throw new Error("Unsupported network"); + } + } finally { + release(); + } + + if (type !== "p2sh") { + ({ address } = p[type]({ + pubkey: hd.publicKey, + network: n + })); + } else { + if (network === "liquid") { + const p2wpkh = p.p2wpkh({ + pubkey: hd.publicKey, + network: n + }); + + const blindkey = fromPrivateKey( + Buffer.from(config.liquid.blindkey, "hex"), + hd.chainCode, + n + ); + + ({ confidentialAddress: address } = p[type]({ + redeem: p2wpkh, + network: n, + blindkey: blindkey.publicKey + })); - address = await lq.getNewAddress("", type || "bech32"); + lq.importBlindingKey(address, blindkey.privateKey.toString('hex')); + } else { + ({ address } = p[type]({ + redeem: p.p2wpkh({ + pubkey: hd.publicKey, + network: n + }), + network: n + })); + } } res.send(address); @@ -429,7 +503,18 @@ app.post( try { await db.Account.update( - { name, ticker, precision, domain, seed, pubkey, path, hide, index, privkey }, + { + name, + ticker, + precision, + domain, + seed, + pubkey, + path, + hide, + index, + privkey + }, { where: { id, user_id: user.id } } @@ -534,6 +534,13 @@ async-listener@^0.6.0: semver "^5.3.0" shimmer "^1.1.0" +async-mutex@^0.2.4: + version "0.2.4" + resolved "https://registry.yarnpkg.com/async-mutex/-/async-mutex-0.2.4.tgz#f6ea5f9cc73147f395f86fa573a2af039fe63082" + integrity sha512-fcQKOXUKMQc57JlmjBCHtkKNrfGpHyR7vu18RfuLfeTAf4hK9PgOadPR5cDrBQ682zasrLUhJFe7EKAHJOduDg== + dependencies: + tslib "^2.0.0" + version "1.5.2" resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" @@ -5548,6 +5555,11 @@ [email protected]: resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.3.tgz#d7e4dd79245d85428c4d7e4822a79917954ca286" integrity sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ== +tslib@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.0.3.tgz#8e0741ac45fc0c226e58a17bfc3e64b9bc6ca61c" + integrity sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ== + tsscmp@^1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/tsscmp/-/tsscmp-1.0.6.tgz#85b99583ac3589ec4bfef825b5000aa911d605eb" |
