diff options
| -rw-r--r-- | .env.sample | 6 | ||||
| -rw-r--r-- | db/schema.sql | 72 | ||||
| -rw-r--r-- | docker-compose.yml | 6 | ||||
| -rw-r--r-- | index.js | 15 | ||||
| -rw-r--r-- | lib/webhooks.js | 28 | ||||
| -rw-r--r-- | package.json | 2 | ||||
| -rw-r--r-- | routes/bitcoin/receive.js | 21 | ||||
| -rw-r--r-- | routes/lightning/receive.js | 2 | ||||
| -rw-r--r-- | routes/liquid/receive.js | 136 | ||||
| -rw-r--r-- | routes/payments.js | 4 | ||||
| -rw-r--r-- | sampleconfig/liquid/elements.conf | 29 | ||||
| -rw-r--r-- | yarn.lock | 14 |
12 files changed, 191 insertions, 144 deletions
diff --git a/.env.sample b/.env.sample index 4d47d913..1208e287 100644 --- a/.env.sample +++ b/.env.sample @@ -1,7 +1,7 @@ BITCOIN_PEER_PORT=18442 BITCOIN_NODE_PORT=18443 -LIQUID_PEER_PORT=18886 -LIQUID_NODE_PORT=18884 +LIQUID_PEER_PORT=18444 +LIQUID_NODE_PORT=7040 BITCOIN_ELECTRS_RPC_PORT=51401 BITCOIN_ELECTRS_PORT=3002 LIQUID_ELECTRS_RPC_PORT=60401 @@ -10,5 +10,5 @@ LIQUID_BLOCK_PORT=18606 LIQUID_TX_PORT=18607 LIQUID_ESPLORA_URL=http://localhost:5005 LIQUID_ESPLORA_PORT=5005 -LIQUID_ELECTRS_URL=http://192.168.1.5:3012 +LIQUID_ELECTRS_URL=http://localhost:3012 ROOT_PASSWORD=password diff --git a/db/schema.sql b/db/schema.sql index 63e8d247..24add256 100644 --- a/db/schema.sql +++ b/db/schema.sql @@ -1,8 +1,8 @@ --- MySQL dump 10.19 Distrib 10.3.29-MariaDB, for debian-linux-gnu (x86_64) +-- MySQL dump 10.19 Distrib 10.3.30-MariaDB, for debian-linux-gnu (x86_64) -- --- Host: localhost Database: coinos +-- Host: 0.0.0.0 Database: coinos -- ------------------------------------------------------ --- Server version 10.3.29-MariaDB-1:10.3.29+maria~bionic +-- Server version 10.6.1-MariaDB-1:10.6.1+maria~focal /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; @@ -16,22 +16,28 @@ /*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; -- --- Table structure for table `SequelizeMeta` +-- Current Database: `coinos` -- -DROP DATABASE IF EXISTS coinos; -CREATE DATABASE coinos; -USE coinos; +/*!40000 DROP DATABASE IF EXISTS `coinos`*/; + +CREATE DATABASE /*!32312 IF NOT EXISTS*/ `coinos` /*!40100 DEFAULT CHARACTER SET utf8mb4 */; + +USE `coinos`; + +-- +-- Table structure for table `SequelizeMeta` +-- DROP TABLE IF EXISTS `SequelizeMeta`; /*!40101 SET @saved_cs_client = @@character_set_client */; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `SequelizeMeta` ( - `name` varchar(255) COLLATE utf8_unicode_ci NOT NULL, + `name` varchar(255) COLLATE utf8mb3_unicode_ci NOT NULL, PRIMARY KEY (`name`), UNIQUE KEY `name` (`name`), UNIQUE KEY `SequelizeMeta_name_unique` (`name`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -62,7 +68,7 @@ CREATE TABLE `accounts` ( `network` varchar(255) DEFAULT NULL, `privkey` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=4188 DEFAULT CHARSET=latin1; +) ENGINE=InnoDB AUTO_INCREMENT=4194 DEFAULT CHARSET=latin1; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -123,8 +129,9 @@ CREATE TABLE `invoices` ( `memo` text DEFAULT NULL, `account_id` int(11) DEFAULT NULL, `path` varchar(255) DEFAULT NULL, + `webhook` text DEFAULT NULL, PRIMARY KEY (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=7771 DEFAULT CHARSET=latin1; +) ENGINE=InnoDB AUTO_INCREMENT=7881 DEFAULT CHARSET=latin1; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -141,7 +148,37 @@ CREATE TABLE `linkingkeys` ( `createdAt` datetime NOT NULL, `updatedAt` datetime NOT NULL, PRIMARY KEY (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=4666 DEFAULT CHARSET=latin1; +) ENGINE=InnoDB AUTO_INCREMENT=4669 DEFAULT CHARSET=latin1; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `migrations` +-- + +DROP TABLE IF EXISTS `migrations`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `migrations` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(255) DEFAULT NULL, + `batch` int(11) DEFAULT NULL, + `migration_time` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(), + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8mb4; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `migrations_lock` +-- + +DROP TABLE IF EXISTS `migrations_lock`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `migrations_lock` ( + `index` int(10) unsigned NOT NULL AUTO_INCREMENT, + `is_locked` int(11) DEFAULT NULL, + PRIMARY KEY (`index`) +) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -195,8 +232,11 @@ CREATE TABLE `payments` ( `redeemcode` varchar(255) DEFAULT NULL, `redeemed` tinyint(1) NOT NULL DEFAULT 0, `path` varchar(255) DEFAULT NULL, - PRIMARY KEY (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=40254 DEFAULT CHARSET=latin1; + `invoice_id` int(11) DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `payments_invoice_id_foreign` (`invoice_id`), + CONSTRAINT `payments_invoice_id_foreign` FOREIGN KEY (`invoice_id`) REFERENCES `invoices` (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=40329 DEFAULT CHARSET=latin1; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -269,7 +309,7 @@ CREATE TABLE `users` ( `sms` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`), KEY `ip` (`ip`) -) ENGINE=InnoDB AUTO_INCREMENT=788 DEFAULT CHARSET=latin1; +) ENGINE=InnoDB AUTO_INCREMENT=794 DEFAULT CHARSET=latin1; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -325,4 +365,4 @@ CREATE TABLE `withdrawals` ( /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; --- Dump completed on 2021-06-02 20:32:20 +-- Dump completed on 2021-07-09 12:57:30 diff --git a/docker-compose.yml b/docker-compose.yml index 8e3f4ef8..3fdd1e06 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -35,8 +35,8 @@ services: image: asoltys/liquid:latest container_name: liquid ports: - - ${LIQUID_NODE_PORT}:18884 - - ${LIQUID_PEER_PORT}:18886 + - ${LIQUID_NODE_PORT}:7040 + - ${LIQUID_PEER_PORT}:18444 - ${LIQUID_BLOCK_PORT}:18602 - ${LIQUID_TX_PORT}:18603 volumes: @@ -64,7 +64,7 @@ services: - --daemon-dir - /config - --daemon-rpc-addr - - liquid:18884 + - liquid:7040 - --cookie - admin1:123 - --http-addr @@ -57,17 +57,18 @@ server = require("http").Server(app); require("./db"); require("./lib/utils"); require("./lib/sockets"); +require("./lib/webhooks"); require("./lib/passport"); require("./lib/register"); require("./lib/send"); require("./routes/assets"); require("./routes/info"); - + require("./routes/users"); // Exclude plugins for minimalist version -if (process.env.SCOPE !== 'MIN') { +if (process.env.SCOPE !== "MIN") { if (config.bitcoin) networks.push("bitcoin"); if (config.liquid) networks.push("liquid"); if (config.lna) networks.push("lightning"); @@ -86,11 +87,11 @@ if (process.env.SCOPE !== 'MIN') { // Scope based Route Handling -var referralsRouter = require('./routes/referrals.js'); -app.use('/referrals', referralsRouter) +var referralsRouter = require("./routes/referrals.js"); +app.use("/referrals", referralsRouter); -var adminRouter = require('./routes/admin.js'); -app.use('/admin', adminRouter) +var adminRouter = require("./routes/admin.js"); +app.use("/admin", adminRouter); app.use((err, req, res, next) => { const details = { @@ -116,6 +117,6 @@ server.listen(config.port, () => process.on("SIGINT", process.exit); -process.on('uncaughtException', function (exception) { +process.on("uncaughtException", function(exception) { console.log(exception); }); diff --git a/lib/webhooks.js b/lib/webhooks.js new file mode 100644 index 00000000..f21607fb --- /dev/null +++ b/lib/webhooks.js @@ -0,0 +1,28 @@ +const wretch = require("wretch"); +const fetch = require("node-fetch"); +wretch().polyfills({ fetch }); + +callWebhook = (invoice, payment) => { + if (!invoice || !payment) return; + + let { address, text, webhook } = invoice; + + if (webhook) { + let { amount, confirmed, hash, memo } = payment; + + l.info("calling webhook", webhook); + return wretch() + .url(webhook) + .post({ + address, + amount, + hash, + memo, + confirmed, + text, + webhook + }) + .json() + .catch(console.log); + } +}; diff --git a/package.json b/package.json index 1474238a..352f7bc1 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,7 @@ }, "dependencies": { "@asoltys/bitcoin-core": "^3.0.7", - "@asoltys/liquidjs-lib": "^5.1.36", + "@asoltys/liquidjs-lib": "^5.1.37", "@grpc/proto-loader": "^0.5.3", "async-mutex": "^0.2.4", "axios": "^0.21.1", diff --git a/routes/bitcoin/receive.js b/routes/bitcoin/receive.js index ba522aeb..dbc5801b 100644 --- a/routes/bitcoin/receive.js +++ b/routes/bitcoin/receive.js @@ -2,9 +2,6 @@ const reverse = require("buffer-reverse"); const zmq = require("zeromq"); const { Op } = require("sequelize"); const { fromBase58 } = require("bip32"); -const wretch = require("wretch"); -const fetch = require("node-fetch"); -wretch().polyfills({ fetch }); const bitcoin = require("bitcoinjs-lib"); @@ -117,6 +114,7 @@ zmqRawTx.on("message", async (topic, message, sequence) => { emit(user.username, "payment", payment); l.info("bitcoin detected", user.username, value); notify(user, `${value} SAT payment detected`); + await callWebhook(invoice, payment); } } catch (e) { console.log(e); @@ -155,26 +153,18 @@ setInterval(async () => { as: "account" }, { - model: db.User, - as: "user" - }, - { model: db.Invoice, as: "invoice" + }, + { + model: db.User, + as: "user" } ], lock: transaction.LOCK.UPDATE, transaction }); - if (p.invoice && p.invoice.webhook) { - l.info("calling webhook", p.invoice.webhook); - console.log(await wretch() - .url(p.invoice.webhook) - .post({ oh: "yeah" }) - .json()); - } - if (p && p.address) address = p.address; if (p && p.user) user = p.user; @@ -199,6 +189,7 @@ setInterval(async () => { emit(user.username, "payment", p); l.info("bitcoin confirmed", user.username, p.amount, p.tip); notify(user, `${total} SAT payment confirmed`); + await callWebhook(p.invoice, p); } else { l.warn("couldn't find bitcoin payment", hash); } diff --git a/routes/lightning/receive.js b/routes/lightning/receive.js index 55193d2d..32c54de6 100644 --- a/routes/lightning/receive.js +++ b/routes/lightning/receive.js @@ -69,6 +69,8 @@ const handlePayment = async msg => { payment = payment.get({ plain: true }); payment.account = account.get({ plain: true }); + await callWebhook(invoice, payment); + emit(user.username, "payment", payment); emit(user.username, "account", payment.account); notify(user, `Received ${total} SAT`); diff --git a/routes/liquid/receive.js b/routes/liquid/receive.js index fa7ff7d4..99bcae2f 100644 --- a/routes/liquid/receive.js +++ b/routes/liquid/receive.js @@ -101,7 +101,7 @@ zmqRawTx.on("message", async (topic, message, sequence) => { user_id: user.id, network: "liquid" }, - order: [["id", "DESC"]] + order: [["id", "DESC"]], }); if (!invoice) return; @@ -175,6 +175,7 @@ zmqRawTx.on("message", async (topic, message, sequence) => { emit(user.username, "account", payment.account); l.info("liquid detected", address, user.username, asset, value); notify(user, `${value} SAT payment detected`); + await callWebhook(invoice, payment); }); } } catch (e) { @@ -187,75 +188,44 @@ zmqRawTx.on("message", async (topic, message, sequence) => { let queue = {}; zmqRawBlock.on("message", async (topic, message, sequence) => { - const payments = await db.Payment.findAll({ - where: { confirmed: 0 } - }); + try { + const payments = await db.Payment.findAll({ + where: { confirmed: 0 } + }); - const block = Block.fromHex(message.toString("hex"), true); + const block = Block.fromHex(message.toString("hex"), true); - let hash, json; + let hash, json; - try { hash = await lq.getBlockHash(block.height); json = await lq.getBlock(hash, 2); - } catch (e) { - return console.log(e); - } - - json.tx.map(async tx => { - if (issuances[tx.txid]) { - await db.transaction(async transaction => { - const { - user_id, - asset, - asset_amount, - asset_payment_id, - token, - token_amount, - token_payment_id - } = issuances[tx.txid]; - - const user = await getUserById(user_id); - - let 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 }); - let payment = await db.Payment.findOne({ - where: { id: asset_payment_id }, - include: { - model: db.Account, - as: "account" - }, - lock: transaction.LOCK.UPDATE, - transaction - }); + json.tx.map(async tx => { + if (issuances[tx.txid]) { + await db.transaction(async transaction => { + const { + user_id, + asset, + asset_amount, + asset_payment_id, + token, + token_amount, + token_payment_id + } = issuances[tx.txid]; - payment.confirmed = true; - await payment.save({ transaction }); - payment = payment.get({ plain: true }); - payment.account = account.get({ plain: true }); + const user = await getUserById(user_id); - emit(user.username, "account", account); - emit(user.username, "payment", payment); - - if (token) { - account = await db.Account.findOne({ - where: { user_id, asset: token }, + let account = await db.Account.findOne({ + where: { user_id, asset }, lock: transaction.LOCK.UPDATE, transaction }); - account.balance = token_amount * SATS; + account.balance = asset_amount * SATS; account.pending = 0; await account.save({ transaction }); - payment = await db.Payment.findOne({ - where: { id: token_payment_id }, + let payment = await db.Payment.findOne({ + where: { id: asset_payment_id }, include: { model: db.Account, as: "account" @@ -266,20 +236,50 @@ zmqRawBlock.on("message", async (topic, message, sequence) => { payment.confirmed = true; await payment.save({ transaction }); - payment = payment.get({ plain: true }); payment.account = account.get({ plain: true }); emit(user.username, "account", account); emit(user.username, "payment", payment); - } - }); - } else if (payments.find(p => p.hash === tx.txid)) queue[tx.txid] = 1; - }); + + if (token) { + account = await db.Account.findOne({ + where: { user_id, asset: token }, + lock: transaction.LOCK.UPDATE, + transaction + }); + account.balance = token_amount * SATS; + account.pending = 0; + await account.save({ transaction }); + + payment = await db.Payment.findOne({ + where: { id: token_payment_id }, + include: { + model: db.Account, + as: "account" + }, + lock: transaction.LOCK.UPDATE, + transaction + }); + + payment.confirmed = true; + await payment.save({ transaction }); + + payment = payment.get({ plain: true }); + payment.account = account.get({ plain: true }); + + emit(user.username, "account", account); + emit(user.username, "payment", payment); + } + }); + } else if (payments.find(p => p.hash === tx.txid)) queue[tx.txid] = 1; + }); + } catch (e) { + return console.log(e); + } }); setInterval(async () => { - // throw new Error("boom"); try { const arr = Object.keys(queue); @@ -296,6 +296,10 @@ setInterval(async () => { as: "account" }, { + model: db.Invoice, + as: "invoice" + }, + { model: db.User, as: "user" } @@ -334,6 +338,7 @@ setInterval(async () => { ); notify(user, `${total} SAT payment confirmed`); + await callWebhook(p.invoice, p); } else { l.warn("couldn't find liquid payment", hash); } @@ -343,7 +348,12 @@ setInterval(async () => { let c = convert[address]; if (address && c) { - l.info("liquid detected for conversion request", address, c.address, user.username); + l.info( + "liquid detected for conversion request", + address, + c.address, + user.username + ); user.account = account; diff --git a/routes/payments.js b/routes/payments.js index 41dfce26..a4bae562 100644 --- a/routes/payments.js +++ b/routes/payments.js @@ -134,7 +134,6 @@ ah(async () => { } }, 50); } - /* if (config.liquid) { lq = new BitcoinCore(config.liquid); @@ -152,7 +151,7 @@ ah(async () => { const parts = hdkeypath.split("/"); app.set("lqAddressIndex", parts[parts.length - 1].slice(0, -1)); } catch (e) { - console.error(e); + console.error("Problem getting liquid address", e); } }, 50); } @@ -199,5 +198,4 @@ ah(async () => { } }) ); -*/ })(); diff --git a/sampleconfig/liquid/elements.conf b/sampleconfig/liquid/elements.conf index 06539981..30909090 100644 --- a/sampleconfig/liquid/elements.conf +++ b/sampleconfig/liquid/elements.conf @@ -2,37 +2,14 @@ chain=liquidregtest listen=1 txindex=1 - [liquidregtest] -port=18886 -rpcport=18884 rpcuser=admin1 rpcpassword=123 -rpcallowip=0.0.0.0/0 rpcbind=0.0.0.0 - -#mainchainrpcport=19001 -#mainchainrpchost=10.10.0.10 -#mainchainrpcuser=admin1 -#mainchainrpcpassword=123 - -# This is the script that controls pegged in funds in Bitcoin network -# Users will be pegging into a P2SH of this, and the "watchmen" -# can then recover these funds and send them to users who desire to peg out. -# This template is 1-of-1 checkmultisig -#fedpegscript=5121<pubkey>51ae - -# This is the script that controls how blocks are made -# We have to supply a signature that satisfies this to create -# a valid block. -#signblockscript=5121<pubkey2>51ae - -# We want to validate pegins by checking with bitcoind if header exists -# in best known chain, and how deep. We combine this with pegin -# proof included in the pegin to get full security. +rpcallowip=0.0.0.0/0 validatepegin=0 initialfreecoins=2100000000000000 -zmqpubrawblock=tcp://0.0.0.0:18606 -zmqpubrawtx=tcp://0.0.0.0:18607 +zmqpubrawblock=tcp://0.0.0.0:18602 +zmqpubrawtx=tcp://0.0.0.0:18603 @@ -18,10 +18,10 @@ resolved "https://registry.yarnpkg.com/@asoltys/blech32/-/blech32-1.0.4.tgz#10066fb173b9b426a465504d7418e3d7295d2d58" integrity sha512-yiiQNRSCqUMCDcFbPvG2QIyBNJsTbZvthq7inIF9RKVd6ei8yuMUXPdO85cwyANLeVvtdFEBpsKBzCETahSk0A== -"@asoltys/liquidjs-lib@^5.1.36": - version "5.1.36" - resolved "https://registry.yarnpkg.com/@asoltys/liquidjs-lib/-/liquidjs-lib-5.1.36.tgz#6a78253429f884f46e0760240e13f48073a27dd0" - integrity sha512-yMOwXGSEpYglgriIx+twG/Y0n4JGCcBPD9flWoldDplPD9beccdB85qOYI6Dx48lfsy0Ar2j5FfqqG8asMF6jA== +"@asoltys/liquidjs-lib@^5.1.37": + version "5.1.37" + resolved "https://registry.yarnpkg.com/@asoltys/liquidjs-lib/-/liquidjs-lib-5.1.37.tgz#8428b57a2e205236d56b5972dd69716e9e1ccd8b" + integrity sha512-F3Lz6A9K5ZXv5RgLynpQaf6rcKKYhaeXYocfVByJZI6O5sqaJXOGV7/OPtNRR8YLkWOreYfoWX0NiFoHauhvHg== dependencies: "@asoltys/blech32" "^1.0.4" "@asoltys/secp256k1-zkp" "^2.0.4" @@ -650,9 +650,9 @@ bip174@^2.0.1: resolved "https://registry.yarnpkg.com/bip174/-/bip174-2.0.1.tgz#39cf8ca99e50ce538fb762589832f4481d07c254" integrity sha512-i3X26uKJOkDTAalYAp0Er+qGMDhrbbh2o93/xiPyAN2s25KrClSpe3VXo/7mNJoqA5qfko8rLS2l3RWZgYmjKQ== -bip174@vulpemventures/bip174.git: - version "1.0.1" - resolved "https://codeload.github.com/vulpemventures/bip174/tar.gz/d6a71254fa66b2ba84de80c67b7255ab65979c3b" +"bip174@github:vulpemventures/bip174": + version "1.0.3" + resolved "https://codeload.github.com/vulpemventures/bip174/tar.gz/b6d0ffe54e0dfa72e971b68372f89b25f7602502" bip32@^2.0.4, bip32@^2.0.5: version "2.0.6" |
