aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2022-09-14 12:31:24 -0700
committerAdam Soltys <[email protected]>2022-09-14 12:31:24 -0700
commited261ce59cae16c71868224455a4f8ca522d3212 (patch)
tree4cb95d14ec9edb3f1cba7e3a59f3f42ece3022cd
parentaa6ce918b043f37bed35b6f5c3106e5251e626b7 (diff)
parent59d7dcd4f97a63c2d572f41c908b342114701dc4 (diff)
downloadcoinos-server-ed261ce59cae16c71868224455a4f8ca522d3212.tar.gz
coinos-server-ed261ce59cae16c71868224455a4f8ca522d3212.zip
Merge branch 'master' into prod
-rw-r--r--.gitignore1
-rw-r--r--README.md2
-rw-r--r--docker-compose.yml7
-rw-r--r--lib/sendInternal.js217
-rw-r--r--package.json4
-rw-r--r--prod.yml1
-rw-r--r--routes/liquid/receive.js6
-rw-r--r--routes/lnurl.js13
-rw-r--r--routes/send.js220
-rw-r--r--sampleconfig/lnurl.js46
10 files changed, 283 insertions, 234 deletions
diff --git a/.gitignore b/.gitignore
index bc8aef57..5f9aae93 100644
--- a/.gitignore
+++ b/.gitignore
@@ -19,3 +19,4 @@ build.json
build.sh
watcher.sh
logs
+docker-compose.mainnet.yml
diff --git a/README.md b/README.md
index 0e49ee3c..743d2db1 100644
--- a/README.md
+++ b/README.md
@@ -19,7 +19,7 @@ cp fx.sample fx
docker network create net --gateway 172.18.0.1 --subnet 172.18.0.0/16
docker run -it -v $(pwd):/app --entrypoint pnpm asoltys/coinos-server i
docker-compose up -d
-docker exec -i mariadb mysql -u root -ppassword < db/schema.sql
+docker exec -i maria mysql -u root -ppassword < db/schema.sql
docker exec -it bitcoin bitcoin-cli createwallet coinos
docker exec -it bitcoin bitcoin-cli rescanblockchain
docker exec -it bitcoin bitcoin-cli generatetoaddress 500 $(docker exec -it bitcoin bitcoin-cli getnewaddress "" "p2sh-segwit")
diff --git a/docker-compose.yml b/docker-compose.yml
index 49f143f0..6b430e35 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -9,7 +9,6 @@ services:
- '8085:8085'
environment:
NODE_ENV: development
- # NODE_OPTIONS: --openssl-legacy-provider
volumes:
- ../coinos-ui:/app
networks:
@@ -23,14 +22,18 @@ services:
- lnd
container_name: app
image: gcr.io/coinos-326717/github.com/coinos/coinos-server:prod
+ command: ["pnpm", "dev"]
ports:
- '3118:3118'
- '3119:3119'
environment:
+ NODE_OPTIONS: "--es-module-specifier-resolution=node --experimental-modules --loader ./loader.js --openssl-legacy-provider"
NODE_ENV: development
volumes:
- ./:/app
- ./logs:/logs
+ extra_hosts:
+ - host.docker.internal:host-gateway
networks:
default:
ipv4_address: 172.18.0.22
@@ -82,7 +85,7 @@ services:
default:
ipv4_address: 172.18.0.31
maria:
- container_name: mariadb
+ container_name: maria
image: mariadb:10.6.1
restart: always
environment:
diff --git a/lib/sendInternal.js b/lib/sendInternal.js
new file mode 100644
index 00000000..80701ca3
--- /dev/null
+++ b/lib/sendInternal.js
@@ -0,0 +1,217 @@
+import config from "$config";
+import db from "$db";
+import { emit } from "$lib/sockets";
+import store from "$lib/store";
+import { callWebhook } from "$lib/webhooks";
+import axios from "axios";
+import { v4 } from "uuid";
+import { notify } from "$lib/notifications";
+import { sendLiquid } from "$routes/liquid/send";
+import { l, err } from "$lib/logging";
+import { fail } from "$lib/utils";
+
+export default async ({
+ amount,
+ address,
+ payreq,
+ unconfidential,
+ asset,
+ memo,
+ username,
+}, hostname, user) => {
+ amount = parseInt(amount);
+
+ if (!asset) asset = config.liquid.btcasset;
+
+ if (!amount || amount < 0) fail("Amount must be greater than zero");
+
+ return db.transaction(async transaction => {
+ let account;
+ if (user.account.asset === asset && !user.account.pubkey) {
+ account = await db.Account.findOne({
+ where: {
+ id: user.account.id,
+ pubkey: null
+ },
+ lock: transaction.LOCK.UPDATE,
+ transaction
+ });
+ } else {
+ account = await db.Account.findOne({
+ where: {
+ user_id: user.id,
+ asset,
+ pubkey: null
+ },
+ lock: transaction.LOCK.UPDATE,
+ transaction
+ });
+ }
+
+ if (!account) throw new Error("Account not found");
+
+ if (account.balance < amount) {
+ throw new Error("Insufficient funds");
+ }
+
+ let fee = 0;
+
+ await account.decrement({ balance: amount }, { transaction });
+ await account.reload({ transaction });
+
+ let params = {
+ amount: -amount,
+ account_id: account.id,
+ memo,
+ user_id: user.id,
+ rate: store.rates[user.currency],
+ currency: user.currency,
+ confirmed: true,
+ hash: `#${v4().substr(0, 6)} ${
+ username ? `Payment to ${username}` : "Internal Transfer"
+ }`,
+ network: "COINOS"
+ };
+
+ if (!username) {
+ l("creating redeemable payment");
+ params.redeemcode = v4();
+ params.hash = `${hostname}/redeem/${params.redeemcode}`;
+ }
+
+ let payment = await db.Payment.create(params, { transaction });
+
+ payment = payment.get({ plain: true });
+ payment.account = account.get({ plain: true });
+
+ l("sent internal", user.username, -payment.amount);
+
+ emit(user.username, "payment", payment);
+ emit(user.username, "account", account);
+
+ if (username) {
+ let recipient = await db.User.findOne(
+ {
+ where: { username },
+ include: {
+ model: db.Account,
+ as: "account"
+ }
+ },
+ { transaction }
+ );
+
+ params = {
+ where: { user_id: recipient.id },
+ order: [["id", "DESC"]]
+ };
+ if (address) params.where.address = address;
+ else if (payreq) params.where.text = payreq;
+ let invoice = (address || payreq) && (await db.Invoice.findOne(params));
+
+ let a2;
+ let acc = {
+ user_id: recipient.id,
+ asset,
+ pubkey: null
+ };
+
+ if (recipient.account.asset === asset && !recipient.account.pubkey)
+ a2 = recipient.account;
+ else {
+ a2 = await db.Account.findOne({
+ where: acc,
+ lock: transaction.LOCK.UPDATE,
+ transaction
+ });
+ }
+
+ if (a2) {
+ await a2.increment({ balance: amount }, { transaction });
+ await a2.reload({ transaction });
+ } else {
+ let name = asset.substr(0, 6);
+ let domain;
+ let ticker = asset.substr(0, 3).toUpperCase();
+ let precision = 8;
+
+ if (store.assets[asset]) {
+ ({ domain, ticker, precision, name } = store.assets[asset]);
+ } else {
+ const existing = await db.Account.findOne({
+ where: {
+ asset
+ },
+ order: [["id", "ASC"]],
+ limit: 1,
+ lock: transaction.LOCK.UPDATE,
+ transaction
+ });
+
+ if (existing) {
+ ({ domain, ticker, precision, name } = existing);
+ }
+ }
+
+ acc = { ...acc, ...{ domain, ticker, precision, name } };
+ acc.balance = amount;
+ acc.pending = 0;
+ acc.network = "liquid";
+ a2 = await db.Account.create(acc, { transaction });
+ }
+
+ let spread = user.username === "coinos" ? 1.02 : 1;
+ amount = Math.round(amount / spread);
+
+ params = {
+ amount,
+ account_id: a2.id,
+ user_id: recipient.id,
+ rate: store.rates[recipient.currency] * spread,
+ currency: recipient.currency,
+ confirmed: true,
+ hash: `#${v4().substr(0, 6)} Payment from ${user.username}`,
+ memo,
+ network: "COINOS",
+ received: true
+ };
+
+ if (invoice) {
+ params.invoice_id = invoice.id;
+
+ let c = store.convert[invoice.text];
+ if (c) {
+ l(
+ "internal payment detected for conversion",
+ invoice.text,
+ c.address,
+ recipient.username
+ );
+
+ recipient.account = a2;
+
+ sendLiquid({
+ address: c.address,
+ amount: amount - 100,
+ user: recipient,
+ limit: amount
+ }).catch(console.log);
+ }
+ }
+
+ let p2 = await db.Payment.create(params, { transaction });
+
+ p2 = p2.get({ plain: true });
+ p2.account = a2.get({ plain: true });
+ emit(recipient.username, "payment", p2);
+ emit(recipient.username, "account", p2.account);
+
+ l("received internal", recipient.username, amount);
+ notify(recipient, `Received ${amount} ${a2.ticker} sats`);
+ callWebhook(invoice, p2);
+
+ }
+
+ return payment;
+ });
+};
diff --git a/package.json b/package.json
index 518c2c1a..4e7030fa 100644
--- a/package.json
+++ b/package.json
@@ -3,8 +3,8 @@
"version": "0.0.1",
"private": true,
"scripts": {
- "start": "NODE_OPTIONS=\"--es-module-specifier-resolution=node --experimental-modules --loader ./loader.js --openssl-legacy-provider\" node index.js",
- "dev": "NODE_OPTIONS=\"--es-module-specifier-resolution=node --experimental-modules --loader ./loader.js --openssl-legacy-provider\" nodemon index.js"
+ "start": "node index.js",
+ "dev": "nodemon index.js"
},
"dependencies": {
"@fastify/multipart": "^7.1.2",
diff --git a/prod.yml b/prod.yml
index dcd0c449..ea600a18 100644
--- a/prod.yml
+++ b/prod.yml
@@ -9,6 +9,7 @@ services:
- '3119:3119'
environment:
NODE_ENV: production
+ NODE_OPTIONS: "--es-module-specifier-resolution=node --experimental-modules --loader ./loader.js --openssl-legacy-provider"
volumes:
- ./:/app
- ./logs:/logs
diff --git a/routes/liquid/receive.js b/routes/liquid/receive.js
index 9c2b490b..480677de 100644
--- a/routes/liquid/receive.js
+++ b/routes/liquid/receive.js
@@ -11,18 +11,16 @@ import zmq from "zeromq/v5-compat";
import { Op } from "@sequelize/core";
import { fromBase58 } from "bip32";
import bitcoin from "bitcoinjs-lib";
-// import liquidJs from "liquidjs-lib";
+import liquidJs from "liquidjs-lib";
import { l, err, warn } from "$lib/logging";
import lq from "$lib/liquid";
-// const { Block, networks, Transaction } = liquidJs;
+const { Block, networks, Transaction } = liquidJs;
-/*
const network =
networks[
config.liquid.network === "mainnet" ? "liquid" : config.liquid.network
];
- */
import { computeConversionFee } from "./conversionFee";
diff --git a/routes/lnurl.js b/routes/lnurl.js
index e5f426a6..4335d566 100644
--- a/routes/lnurl.js
+++ b/routes/lnurl.js
@@ -15,7 +15,6 @@ import jwt from "jsonwebtoken";
import qs from "query-string";
import bolt11 from "bolt11";
-
import { createInvoice, decodePaymentRequest, getPayments } from "lightning";
import {
@@ -24,6 +23,7 @@ import {
} from "./lightning/conversionFee.js";
import send from "$lib/send";
+import sendInternal from "$lib/sendInternal";
const logins = persist("data/logins.json");
const recipients = persist("data/recipients.json");
@@ -197,19 +197,14 @@ app.post("/pay", auth, async (req, res, next) => {
lnurlPayments[secret] = user.id;
if (recipients[secret]) {
- url = `https://coinos.io/api/send`;
- const { data } = await axios.post(
- url,
+ let data = await sendInternal(
{
amount,
memo: comment,
username: recipients[secret].username
},
- {
- headers: {
- authorization: req.headers.authorization
- }
- }
+ req.hostname,
+ user
);
res.send(data);
diff --git a/routes/send.js b/routes/send.js
index 5d46f4de..360ad5b2 100644
--- a/routes/send.js
+++ b/routes/send.js
@@ -1,223 +1,11 @@
-import config from "$config";
-import db from "$db";
-import { emit } from "$lib/sockets";
-import store from "$lib/store";
-import { callWebhook } from "$lib/webhooks";
-import axios from "axios";
-import { v4 } from "uuid";
-import { notify } from "$lib/notifications";
-import { sendLiquid } from "$routes/liquid/send";
-import { l, err } from "$lib/logging";
+import sendInternal from "$lib/sendInternal";
export default async (req, res, next) => {
- let {
- amount,
- address,
- payreq,
- unconfidential,
- asset,
- memo,
- username
- } = req.body;
- let { user } = req;
- amount = parseInt(amount);
-
- if (!asset) asset = config.liquid.btcasset;
-
- if (!amount || amount < 0)
- return res.code(500).send("Amount must be greater than zero");
-
try {
- await db.transaction(async transaction => {
- let account;
- if (user.account.asset === asset && !user.account.pubkey) {
- account = await db.Account.findOne({
- where: {
- id: user.account.id,
- pubkey: null
- },
- lock: transaction.LOCK.UPDATE,
- transaction
- });
- } else {
- account = await db.Account.findOne({
- where: {
- user_id: user.id,
- asset,
- pubkey: null
- },
- lock: transaction.LOCK.UPDATE,
- transaction
- });
- }
-
- if (!account) throw new Error("Account not found");
-
- if (account.balance < amount) {
- throw new Error("Insufficient funds");
- }
-
- let fee = 0;
-
- await account.decrement({ balance: amount }, { transaction });
- await account.reload({ transaction });
-
- let params = {
- amount: -amount,
- account_id: account.id,
- memo,
- user_id: user.id,
- rate: store.rates[user.currency],
- currency: user.currency,
- confirmed: true,
- hash: `#${v4().substr(0, 6)} ${
- username ? `Payment to ${username}` : "Internal Transfer"
- }`,
- network: "COINOS"
- };
-
- if (!username) {
- l("creating redeemable payment");
- params.redeemcode = v4();
- params.hash = `${req.hostname}/redeem/${params.redeemcode}`;
- }
-
- let payment = await db.Payment.create(params, { transaction });
-
- payment = payment.get({ plain: true });
- payment.account = account.get({ plain: true });
-
- l("sent internal", user.username, -payment.amount);
-
- emit(user.username, "payment", payment);
- emit(user.username, "account", account);
-
- if (username) {
- let recipient = await db.User.findOne(
- {
- where: { username },
- include: {
- model: db.Account,
- as: "account"
- }
- },
- { transaction }
- );
-
- params = {
- where: { user_id: recipient.id },
- order: [["id", "DESC"]]
- };
- if (address) params.where.address = address;
- else if (payreq) params.where.text = payreq;
- let invoice = (address || payreq) && (await db.Invoice.findOne(params));
-
- let a2;
- let acc = {
- user_id: recipient.id,
- asset,
- pubkey: null
- };
-
- if (recipient.account.asset === asset && !recipient.account.pubkey)
- a2 = recipient.account;
- else {
- a2 = await db.Account.findOne({
- where: acc,
- lock: transaction.LOCK.UPDATE,
- transaction
- });
- }
-
- if (a2) {
- await a2.increment({ balance: amount }, { transaction });
- await a2.reload({ transaction });
- } else {
- let name = asset.substr(0, 6);
- let domain;
- let ticker = asset.substr(0, 3).toUpperCase();
- let precision = 8;
-
- if (store.assets[asset]) {
- ({ domain, ticker, precision, name } = store.assets[asset]);
- } else {
- const existing = await db.Account.findOne({
- where: {
- asset
- },
- order: [["id", "ASC"]],
- limit: 1,
- lock: transaction.LOCK.UPDATE,
- transaction
- });
-
- if (existing) {
- ({ domain, ticker, precision, name } = existing);
- }
- }
-
- acc = { ...acc, ...{ domain, ticker, precision, name } };
- acc.balance = amount;
- acc.pending = 0;
- acc.network = "liquid";
- a2 = await db.Account.create(acc, { transaction });
- }
-
- let spread = user.username === "coinos" ? 1.02 : 1;
- amount = Math.round(amount / spread);
-
- params = {
- amount,
- account_id: a2.id,
- user_id: recipient.id,
- rate: store.rates[recipient.currency] * spread,
- currency: recipient.currency,
- confirmed: true,
- hash: `#${v4().substr(0, 6)} Payment from ${user.username}`,
- memo,
- network: "COINOS",
- received: true
- };
-
- if (invoice) {
- params.invoice_id = invoice.id;
-
- let c = store.convert[invoice.text];
- if (c) {
- l(
- "internal payment detected for conversion",
- invoice.text,
- c.address,
- recipient.username
- );
-
- recipient.account = a2;
-
- sendLiquid({
- address: c.address,
- amount: amount - 100,
- user: recipient,
- limit: amount
- }).catch(console.log);
- }
- }
-
- let p2 = await db.Payment.create(params, { transaction });
-
- p2 = p2.get({ plain: true });
- p2.account = a2.get({ plain: true });
- emit(recipient.username, "payment", p2);
- emit(recipient.username, "account", p2.account);
-
- l("received internal", recipient.username, amount);
- notify(recipient, `Received ${amount} ${a2.ticker} sats`);
- callWebhook(invoice, p2);
- }
- res.send(payment);
- });
+ let payment = await sendInternal(req.body, req.hostname, req.user);
+ res.send(payment);
} catch (e) {
- console.log(e);
- err("problem sending internal payment", user.username, e.message);
+ err("problem sending internal payment", e.message);
return res.code(500).send(e.message);
}
};
diff --git a/sampleconfig/lnurl.js b/sampleconfig/lnurl.js
new file mode 100644
index 00000000..6f358983
--- /dev/null
+++ b/sampleconfig/lnurl.js
@@ -0,0 +1,46 @@
+import fs from "fs";
+
+export default {
+ host: "app",
+ port: 3118,
+ protocol: "http",
+ url: "https://desk.bobcat-liberty.ts.net",
+ endpoint: "/lnurl",
+ listen: true,
+ auth: {
+ apiKeys: [
+ {
+ id: "50bdeb634d",
+ key: "ac9bdd15a983072311c74c13877d122f16a27a8f40cb4dfa5014eece4da5cda2"
+ }
+ ]
+ },
+ lightning: {
+ backend: "lnd",
+ config: {
+ hostname: "lnd:9735",
+ cert: {
+ data: fs.readFileSync(
+ "/app/config/lnd/tls.cert",
+ "utf8"
+ )
+ },
+ macaroon: {
+ data:
+ "0201036c6e6402f801030a100a1edc729d77423ec23fa75b85b781391201301a160a0761646472657373120472656164120577726974651a130a04696e666f120472656164120577726974651a170a08696e766f69636573120472656164120577726974651a210a086d616361726f6f6e120867656e6572617465120472656164120577726974651a160a076d657373616765120472656164120577726974651a170a086f6666636861696e120472656164120577726974651a160a076f6e636861696e120472656164120577726974651a140a057065657273120472656164120577726974651a180a067369676e6572120867656e657261746512047265616400000620aea455def2702fb1ec9838f1300255147ed20e825a539a1ee2e99e7049b04c11"
+ }
+ }
+ },
+ store: {
+ backend: 'knex',
+ config: {
+ client: 'mysql',
+ connection: {
+ host: 'maria',
+ user: 'root',
+ password: 'password',
+ database: 'coinos',
+ },
+ },
+ },
+};