From 9c2a909dc707c3f2baad8f86cbeea54606dbea41 Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Mon, 13 Jan 2020 16:06:56 -0800 Subject: liquid receiving support --- index.js | 3 +- liquidPayments.js | 134 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ models/users.js | 18 ++++++++ payments.js | 1 + users.js | 11 +++-- 5 files changed, 162 insertions(+), 5 deletions(-) create mode 100644 liquidPayments.js diff --git a/index.js b/index.js index f13e666a..aab88b93 100644 --- a/index.js +++ b/index.js @@ -35,9 +35,10 @@ const l = console.log; const seen = []; const addresses = {}; await db.User.findAll({ - attributes: ["username", "address"] + attributes: ["username", "address", "liquid"] }).map(u => { addresses[u.address] = u.username; + if (u.liquid) addresses[u.liquid] = u.username; }); const payments = (await db.Payment.findAll({ diff --git a/liquidPayments.js b/liquidPayments.js new file mode 100644 index 00000000..62be1243 --- /dev/null +++ b/liquidPayments.js @@ -0,0 +1,134 @@ +const reverse = require("buffer-reverse"); +const zmq = require("zeromq"); +const config = require("./config"); +const Sequelize = require("sequelize"); +const BitcoinCore = require("bitcoin-core"); +const bitcoin = require("bitcoinjs-lib"); +const l = console.log; + +const zmqRawBlock = zmq.socket("sub"); +zmqRawBlock.connect(config.liquid.zmqrawblock); +zmqRawBlock.subscribe("rawblock"); + +const zmqRawTx = zmq.socket("sub"); +zmqRawTx.connect("tcp://127.0.0.1:18603"); +zmqRawTx.subscribe("rawtx"); + +let NETWORK = bitcoin.networks[config.bitcoin.network === "mainnet" ? "bitcoin" : config.bitcoin.network]; +const bc = new BitcoinCore(config.liquid); + +module.exports = (app, db, addresses, payments, emit) => { + zmqRawTx.on("message", async (topic, message, sequence) => { + const tx = await bc.decodeRawTransaction(message.toString("hex")); + + Promise.all( + tx.vout.map(async o => { + if (!(o.scriptPubKey && o.scriptPubKey.addresses)) return; + const address = o.scriptPubKey.addresses[0]; + + if (Object.keys(addresses).includes(address)) { + payments.push(hash); + + let user = await db.User.findOne({ + where: { + username: addresses[address] + } + }); + + let invoices = await db.Payment.findAll({ + limit: 1, + where: { + address, + received: null, + amount: { + [Sequelize.Op.gt]: 0 + } + }, + order: [["id", "DESC"]] + }); + + let tip = null; + if (invoices.length) tip = invoices[0].tip; + + let confirmed = false; + + if (user.friend) { + user.balance += o.value; + confirmed = true; + } else { + user.pending += o.value; + } + + await user.save(); + emit(user.username, "user", user); + + const payment = await db.Payment.create({ + user_id: user.id, + hash, + amount: o.value, + currency: "CAD", + rate: app.get("ask"), + received: true, + tip, + confirmed, + address + }); + + emit(user.username, "payment", payment); + emit(user.username, "tx", message); + } + }) + ); + }); + + let queue = {}; + + zmqRawBlock.on("message", async (topic, message, sequence) => { + /* + const payments = await db.Payment.findAll({ + where: { confirmed: false } + }); + + const hashes = payments.map(p => p.hash); + + let block = bitcoin.Block.fromHex(message.toString("hex")); + block.transactions.map(tx => { + let hash = reverse(tx.getHash()).toString("hex"); + if (hashes.includes(hash)) queue[hash] = 1; + }); + */ + }); + + /* + setInterval(async () => { + let arr = Object.keys(queue); + for (let i = 0; i < arr.length; i++) { + let hash = arr[i]; + + let p = await db.Payment.findOne({ + include: [{ model: db.User, as: "user" }], + where: { hash, confirmed: 0 } + }) + + p.confirmed = 1; + + let user = await p.getUser(); + user.balance += p.amount; + user.pending -= p.amount; + emit(user.username, "user", user); + + await user.save(); + await p.save(); + + let payments = await db.Payment.findAll({ + where: { user_id: user.id }, + order: [['id', 'DESC']], + limit: 20 + }); + + emit(user.username, "payments", payments); + delete queue[hash]; + } + }, 1000); + */ +}; diff --git a/models/users.js b/models/users.js index 09ba8660..40eb00f5 100644 --- a/models/users.js +++ b/models/users.js @@ -40,6 +40,24 @@ module.exports = sequelize => { comment: null, field: "email" }, + liquid: { + type: DataTypes.STRING(255), + allowNull: true, + defaultValue: null, + primaryKey: false, + autoIncrement: false, + comment: null, + field: "liquid" + }, + confidential: { + type: DataTypes.STRING(255), + allowNull: true, + defaultValue: null, + primaryKey: false, + autoIncrement: false, + comment: null, + field: "confidential" + }, address: { type: DataTypes.STRING(255), allowNull: true, diff --git a/payments.js b/payments.js index b9b932dd..3f2a8d17 100644 --- a/payments.js +++ b/payments.js @@ -6,6 +6,7 @@ module.exports = async (app, auth, addresses, bc, db, emit, seen, payments) => { const lnb = await createLnrpc(config.lnb); require("./bitcoinPayments")(app, bc, db, addresses, payments, emit); + require("./liquidPayments")(app, bc, db, addresses, payments, emit); require("./lightningPayments")(app, db, lna, lnb, emit, payments); app.post( diff --git a/users.js b/users.js index 4442c785..68624b76 100644 --- a/users.js +++ b/users.js @@ -4,12 +4,13 @@ const bcrypt = require("bcrypt"); const jwt = require("jsonwebtoken"); const mailgun = require("mailgun-js"); const uuidv4 = require("uuid/v4"); - +const BitcoinCore = require("bitcoin-core"); const authyVerify = require("./authy"); const config = require("./config"); const authy = new Client({ key: config.authy.key }); const whitelist = require("./whitelist"); const fb = "https://graph.facebook.com/"; +const liquid = new BitcoinCore(config.liquid); const pick = (O, ...K) => K.reduce((o, k) => ((o[k] = O[k]), o), {}); @@ -24,6 +25,8 @@ module.exports = (addresses, auth, app, bc, db, emit) => { if (exists) return err("Username taken"); user.address = await bc.getNewAddress("", "bech32"); + user.confidential = await liquid.getNewAddress(); + user.liquid = (await liquid.getAddressInfo(user.confidential)).unconfidential; user.password = await bcrypt.hash(user.password, 1); user.name = user.username; user.currency = "USD"; @@ -91,7 +94,6 @@ module.exports = (addresses, auth, app, bc, db, emit) => { phone, twofa, pin, - pinconfirm, password, passconfirm } = req.body; @@ -112,13 +114,12 @@ module.exports = (addresses, auth, app, bc, db, emit) => { user.email = email; user.phone = phone; user.twofa = twofa; + user.pin = pin; if (password && password === passconfirm) { user.password = await bcrypt.hash(password, 1); } - if (pin && pin === pinconfirm) user.pin = await bcrypt.hash(pin, 1); - if (twofa && !user.authyId && user.phoneVerified) { try { let r = await authy.registerUser({ countryCode: "CA", email, phone }); @@ -244,6 +245,8 @@ module.exports = (addresses, auth, app, bc, db, emit) => { user.username = userID; user.name = (await axios.get(`${fb}/me?access_token=${accessToken}`)).data.name; user.address = await bc.getNewAddress("", "bech32"); + user.confidential = await liquid.getNewAddress(); + user.liquid = (await liquid.getAddressInfo(user.confidential)).unconfidential; user.password = await bcrypt.hash(accessToken, 1); user.balance = 0; user.pending = 0; -- cgit v1.2.3