aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2020-10-09 14:19:59 -0700
committerAdam Soltys <[email protected]>2020-10-09 14:19:59 -0700
commitb6f38f8a42a6bc308257888ec30c711a618bf58e (patch)
tree275f6ea99bade5cb866391fa9f48c7d60315df0b
parent70ea418f91c1a05a6631fe8acb16e68295d5d8f2 (diff)
downloadcoinos-server-b6f38f8a42a6bc308257888ec30c711a618bf58e.tar.gz
coinos-server-b6f38f8a42a6bc308257888ec30c711a618bf58e.zip
error messages
-rw-r--r--routes/swaps.js6
-rw-r--r--swaps.js876
2 files changed, 3 insertions, 879 deletions
diff --git a/routes/swaps.js b/routes/swaps.js
index 8872face..9e1fd1ea 100644
--- a/routes/swaps.js
+++ b/routes/swaps.js
@@ -135,16 +135,16 @@ const swap = async (user, { a1, a2, v1, v2 }) => {
assets["a0c358a0f6947864af3a06f3f6a2aeb304df7fd95c922f2f22d7412399ce7691"] =
"adamcoin";
- if (!assets[a1]) throw new Error("unsupported asset");
+ if (!assets[a1]) throw new Error("Asset not found on server");
if (v1 > Math.round(b[assets[a1]] * SATS))
- throw new Error(`insufficient server funds, ${v1} ${b[assets[a1]]}`);
+ throw new Error(`Insufficient server funds, ${v1} ${b[assets[a1]]}`);
await db.transaction(async transaction => {
let a1acc = await getAccount(a1, user, transaction);
let a2acc = await getAccount(a2, user, transaction);
if (v1 > a1acc.balance)
- throw new Error(`insufficient funds, ${v1} ${a1acc.balance}`);
+ throw new Error(`Insufficient funds, ${v1} ${a1acc.balance}`);
l.info(
`trade ${v1} ${a1.substr(0, 6)} for ${v2} ${a2.substr(0, 6)}`,
diff --git a/swaps.js b/swaps.js
deleted file mode 100644
index 89fc551a..00000000
--- a/swaps.js
+++ /dev/null
@@ -1,876 +0,0 @@
-const axios = require("axios");
-const crypto = require("crypto");
-const level = require("level");
-const fs = require("fs");
-const { spawn } = require("child_process");
-const leveldb = level("leveldb");
-const getAccount = require("../lib/account");
-const { Op, col } = require("sequelize");
-const shallow = a => {
- let b = {};
- for (x in a) {
- if (a[x] instanceof Date || typeof a[x] !== "object") b[x] = a[x];
- }
- return b;
-};
-
-const assets = {
- b2e15d0d7a0c94e4e2ce0fe6e8691b9e451377f6e46e8045a86f7c4b5d4f0f23: "bitcoin",
- "4eebe36eb0819e6daa5dd3c97733251ff4eb728c810d949365d6dacaad5ef6e8": "tether"
-};
-
-const swapsdir = "swaps/";
-if (!fs.existsSync(swapsdir)) {
- fs.mkdirSync(swapsdir);
-}
-
-const timeout = 20000;
-
-const cli = (...args) => {
- const params = ["--regtest", "-c", config.liquid.conf, ...args];
- if (config.liquid.network !== "regtest") params.shift();
- l.info("spawning liquidswap-cli with params:", params);
- return spawn("liquidswap-cli", params);
-};
-
-const getInfo = filename =>
- new Promise((resolve, reject) => {
- const proc = cli("info", swapsdir + filename);
-
- proc.stdout.on("data", data => {
- resolve(data.toString());
- });
-
- proc.stderr.on("data", err => {
- l.error("info error", err.toString());
- reject(err.toString());
- });
-
- proc.on("close", (code, signal) => {
- let msg = (code && code.toString()) || (signal && signal.toString());
- reject(new Error(`Liquid swap tool process closed unexpectedly: ${msg}`));
- });
-
- proc.on("exit", (code, signal) => {
- let msg = (code && code.toString()) || (signal && signal.toString());
- reject(new Error(`Liquid swap tool process exited unexpectedly: ${msg}`));
- });
-
- setTimeout(
- () => reject(new Error("Liquid swap tool timed out"), proc),
- timeout
- );
- });
-
-const swap = async (user, { a1, a2, v1, v2 }) => {
- let rate = v2 / v1;
-
- const b = await lq.getBalance();
-
- Object.keys(b).map(asset => {
- assets[asset] = asset;
- });
-
- assets[config.liquid.btcasset] = "bitcoin";
- assets["ce091c998b83c78bb71a632313ba3760f1763d9cfcffae02258ffa9865a37bd2"] =
- "tether";
- assets["a0c358a0f6947864af3a06f3f6a2aeb304df7fd95c922f2f22d7412399ce7691"] =
- "adamcoin";
-
- if (!assets[a1]) throw new Error("unsupported asset");
- if (v1 > Math.round(b[assets[a1]] * SATS))
- throw new Error(`insufficient server funds, ${v1} ${b[assets[a1]]}`);
-
- await db.transaction(async transaction => {
- let a1acc = await getAccount(a1, user, transaction);
- let a2acc = await getAccount(a2, user, transaction);
-
- if (v1 > a1acc.balance)
- throw new Error(`insufficient funds, ${v1} ${a1acc.balance}`);
-
- l.info(
- `trade ${v1} ${a1.substr(0, 6)} for ${v2} ${a2.substr(0, 6)}`,
- user.username
- );
-
- await a1acc.decrement({ balance: v1 }, { transaction });
- await a1acc.reload({ transaction });
-
- let payment = await db.Payment.create(
- {
- hash: "Trade Funding",
- amount: -v1,
- account_id: a1acc.id,
- user_id: user.id,
- currency: user.currency,
- rate: app.get("rates")[user.currency],
- confirmed: true,
- received: false,
- network: "COINOS"
- },
- { transaction }
- );
-
- emit(user.username, "payment", payment.get({ plain: true }));
- emit(user.username, "account", a1acc.get({ plain: true }));
-
- const orders = await db.Order.findAll(
- {
- where: {
- "$acc1.asset$": a2,
- "$acc2.asset$": a1,
- rate: { [Op.lte]: 1/rate },
- accepted: false
- },
- order: [
- ["rate", "ASC"],
- ["id", "ASC"]
- ],
- include: [
- {
- model: db.User,
- as: "user"
- },
- {
- model: db.Account,
- as: "acc1"
- },
- {
- model: db.Account,
- as: "acc2"
- }
- ]
- },
- { transaction }
- );
-
- for (let i = 0; i < orders.length; i++) {
- if (!v1) break;
- p = orders[i];
- if (p.v2 > v1) {
- p.v2 -= v1;
- await p.save({ transaction });
-
- let payment = await db.Payment.create(
- {
- hash: "Trade Fill",
- amount: v2,
- account_id: p.a1_id,
- user_id: p.user_id,
- currency: p.user.currency,
- rate: app.get("rates")[p.user.currency],
- confirmed: true,
- received: true,
- network: "COINOS"
- },
- { transaction }
- );
-
- await p.acc1.increment({ balance: v2 }, { transaction });
- await p.acc1.reload({ transaction });
-
- emit(p.user.username, "payment", payment.get({ plain: true }));
- emit(p.user.username, "account", p.acc1.get({ plain: true }));
-
- payment = await db.Payment.create(
- {
- hash: "Trade Fill",
- amount: v1,
- account_id: a1acc.id,
- user_id: user.id,
- currency: user.currency,
- rate: app.get("rates")[user.currency],
- confirmed: true,
- received: true,
- network: "COINOS"
- },
- { transaction }
- );
-
- a1acc.increment({ balance: v1 }, { transaction });
- await a1acc.reload({ transaction });
-
- emit(user.username, "payment", payment.get({ plain: true }));
- emit(user.username, "account", a1acc.get({ plain: true }));
-
- if (p.fee) {
- const btc = await getAccount(
- config.liquid.btcasset,
- p.user,
- transaction
- );
-
- payment = await db.Payment.create(
- {
- hash: "Swap Fee Refund",
- amount: p.fee,
- account_id: btc.id,
- user_id: p.user_id,
- currency: p.user.currency,
- rate: app.get("rates")[p.user.currency],
- confirmed: true,
- received: true,
- network: "COINOS"
- },
- { transaction }
- );
-
- await btc.increment({ balance: p.fee }, { transaction });
- await btc.reload({ transaction });
-
- emit(p.user.username, "account", btc.get({ plain: true }));
- }
-
- emit(p.user.username, "payment", payment.get({ plain: true }));
-
- p = p.get({ plain: true });
- p.a1 = p.acc1.asset;
- p.a2 = p.acc2.asset;
- broadcast("order", shallow(p));
- v1 = 0;
- } else {
- v1 -= p.v2;
- v2 = Math.round(v1 * rate);
-
- p.accepted = true;
- p.completedAt = new Date();
-
- await p.save({ transaction });
-
- let payment = await db.Payment.create(
- {
- hash: "Trade Fill",
- amount: p.v1,
- account_id: a2acc.id,
- user_id: user.id,
- currency: user.currency,
- rate: app.get("rates")[user.currency],
- confirmed: true,
- received: true,
- network: "COINOS"
- },
- { transaction }
- );
-
- await a2acc.increment({ balance: p.v1 }, { transaction });
- await a2acc.reload({ transaction });
-
- emit(user.username, "payment", payment.get({ plain: true }));
- emit(user.username, "account", a2acc.get({ plain: true }));
-
- payment = await db.Payment.create(
- {
- hash: "Trade Fill",
- amount: p.v2,
- account_id: p.a2_id,
- user_id: p.user_id,
- currency: p.user.currency,
- rate: app.get("rates")[p.user.currency],
- confirmed: true,
- received: true,
- network: "COINOS"
- },
- { transaction }
- );
-
- emit(p.user.username, "payment", payment.get({ plain: true }));
- await p.acc2.increment({ balance: p.v2 }, { transaction });
- await p.acc2.reload({ transaction });
-
- p = p.get({ plain: true });
- p.a1 = p.acc1.asset;
- p.a2 = p.acc2.asset;
- broadcast("order", shallow(p));
- }
- }
-
- let order;
- if (v1) {
- order = await db.Order.create(
- {
- v1,
- v2,
- user_id: user.id,
- a1_id: a1acc.id,
- a2_id: a2acc.id
- },
- { transaction }
- );
-
- const text = await new Promise((resolve, reject) => {
- l.info(
- "running liquid-cli tool %s %s %s %s %s",
- config.liquid.conf,
- a1,
- v1,
- a2,
- v2
- );
- const proc = cli(
- "propose",
- a1,
- (v1 / SATS).toFixed(8),
- a2,
- (v2 / SATS).toFixed(8)
- );
-
- proc.stdout.on("data", data => {
- resolve(data.toString());
- });
-
- proc.stderr.on("error", err => {
- l.error("order error", err.toString());
- reject(err.toString());
- });
-
- proc.on("close", (code, signal) => {
- let msg = (code && code.toString()) || (signal && signal.toString());
- reject(
- new Error(`Liquid swap tool process closed unexpectedly: ${msg}`)
- );
- });
-
- proc.on("exit", (code, signal) => {
- let msg = (code && code.toString()) || (signal && signal.toString());
- reject(
- new Error(`Liquid swap tool process exited unexpectedly: ${msg}`)
- );
- });
-
- setTimeout(
- () => reject(new Error("Liquid swap tool timed out"), proc),
- timeout
- );
- });
-
- const filename = `order-${order.id}.txt`;
-
- fs.writeFileSync(swapsdir + filename, text);
- let info = JSON.parse(await getInfo(filename));
- let [fee, rate, asset] = parse(info);
-
- const btc = await getAccount(config.liquid.btcasset, user, transaction);
- fee = Math.round(fee * SATS);
- await btc.decrement({ balance: fee }, { transaction });
- await btc.save({ transaction });
- emit(user.username, "account", btc.get({ plain: true }));
-
- let payment = await db.Payment.create(
- {
- hash: "Swap Fee Deposit",
- amount: -fee,
- account_id: btc.id,
- user_id: user.id,
- currency: user.currency,
- rate: app.get("rates")[user.currency],
- confirmed: true,
- received: false,
- network: "COINOS"
- },
- { transaction }
- );
-
- emit(user.username, "payment", payment.get({ plain: true }));
-
- order.fee = fee;
- order.text = text;
- await order.save({ transaction });
-
- order = order.get({ plain: true });
- order.rate = v2 / v1;
- order.a1 = a1;
- order.a2 = a2;
-
- broadcast("order", order);
- }
- });
-};
-
-const cancel = async (user, id) => {
- await db.transaction(async transaction => {
- let order = await db.Order.findOne(
- {
- where: {
- id,
- user_id: user.id
- },
- include: {
- model: db.Account,
- as: "acc1"
- }
- },
-
- { transaction }
- );
-
- let { acc1: account, v1, v2 } = order;
- await account.increment({ balance: v1 }, { transaction });
- await account.reload({ transaction });
-
- let rate = v2 / v1;
- let payment = await db.Payment.create(
- {
- hash: "Trade Cancelled",
- amount: v1,
- account_id: account.id,
- user_id: user.id,
- currency: user.currency,
- rate: app.get("rates")[user.currency],
- confirmed: true,
- received: true,
- network: "COINOS"
- },
- { transaction }
- );
-
- emit(user.username, "account", account.get({ plain: true }));
- emit(user.username, "payment", payment.get({ plain: true }));
-
- if (order.fee) {
- const btc = await getAccount(config.liquid.btcasset, user, transaction);
-
- payment = await db.Payment.create(
- {
- hash: "Swap Fee Refund",
- amount: order.fee,
- account_id: btc.id,
- user_id: user.id,
- currency: user.currency,
- rate: app.get("rates")[user.currency],
- confirmed: true,
- received: true,
- network: "COINOS"
- },
- { transaction }
- );
-
- await btc.increment({ balance: order.fee }, { transaction });
- await btc.reload({ transaction });
-
- emit(user.username, "account", btc.get({ plain: true }));
- emit(user.username, "payment", payment.get({ plain: true }));
- }
-
- broadcast("removeOrder", id);
- await order.destroy({ transaction });
- });
-};
-
-app.delete(
- "/order/:id",
- auth,
- ah(async (req, res) => {
- const { id } = req.params;
- const { user } = req;
-
- await cancel(user, id);
- res.end();
- })
-);
-
-app.post(
- "/orders",
- auth,
- ah(async (req, res) => {
- const { user } = req;
- try {
- await swap(user, req.body);
- res.end();
- } catch (e) {
- l.error(req.user.username, e.message, e.stack);
- res.status(500).send(e.message);
- }
- })
-);
-
-app.post(
- "/accept",
- optionalAuth,
- ah(async (req, res) => {
- try {
- const { id, text } = req.body;
-
- let order = await db.Order.findOne({
- where: {
- id
- },
- include: {
- model: db.User,
- as: "user"
- }
- });
-
- if (!order) throw new Error("Order not found");
-
- const { user } = req;
-
- if (text) {
- const filename = `acceptance-${id}.txt`;
- fs.writeFileSync(swapsdir + filename, text);
-
- let info = JSON.parse(await getInfo(filename));
- let [fee, rate, asset] = parse(info);
- let { tx, u_address_p, u_address_r } = JSON.parse(
- Buffer.from(text, "base64").toString()
- );
- const sha256 = crypto.createHash("sha256");
- sha256.update(tx);
- const hash = sha256.digest("hex");
-
- const leg1 = info.legs.find(leg => !leg.incoming);
- const leg2 = info.legs.find(leg => leg.incoming);
-
- await db.transaction(async transaction => {
- const l2a2 = await getAccount(leg2.asset, order.user, transaction);
-
- let amount = Math.round(leg1.amount * SATS);
- fee = Math.round(fee * SATS);
-
- amount = Math.round(leg2.amount * SATS);
-
- await db.Invoice.create({
- user_id: order.user_id,
- text: u_address_p,
- address: u_address_p,
- unconfidential: u_address_p,
- currency: order.user.currency,
- memo: "Atomic Swap",
- rate: app.get("rates")[order.user.currency],
- amount,
- tip: 0,
- network: "liquid",
- account_id: l2a2.id
- });
-
- addresses[u_address_p] = order.user.username;
-
- await finalize(filename);
-
- order.accepted = true;
- order.completedAt = new Date();
- await order.save({ transaction });
-
- order = order.get({ plain: true });
- order.a1 = leg1.asset;
- order.a2 = leg2.asset;
- broadcast("order", shallow(order));
- });
-
- res.send(info);
- } else {
- throw new Error("No acceptance provided");
- }
- } catch (e) {
- l.error(e.message);
- res.status(500).send(e.message);
- }
- })
-);
-
-app.post(
- "/acceptance",
- ah(async (req, res) => {
- const { acceptance: text } = req.body;
- l.info("acceptance received");
-
- fs.writeFile(swapsdir + "accepted.txt", text, async err => {
- if (err) {
- return res.status(500).send(err);
- }
-
- try {
- let info = JSON.parse(
- await new Promise((resolve, reject) => {
- const proc = cli("info", swapsdir + "accepted.txt");
-
- proc.stdout.on("data", data => {
- resolve(data.toString());
- });
-
- proc.stderr.on("data", err => {
- reject(err.toString());
- });
-
- setTimeout(
- () => reject(new Error("Liquid swap tool timed out"), proc),
- timeout
- );
- })
- );
-
- const time = Math.floor(new Date()).toString();
- const [fee, rate, asset] = parse(info);
- if (!asset) throw new Error("Unsupported asset");
- l.info("accepted", info, rate, asset);
-
- leveldb.put(time, JSON.stringify({ text, info, rate, asset }));
- l.info({ text, info, rate, asset });
-
- res.send({ info, rate });
- } catch (e) {
- l.error(e);
- res.status(500).send(e.message);
- }
- });
- })
-);
-
-checkQueue = async () => {
- const ws = app.get("ws");
- const txs = [];
-
- leveldb
- .createReadStream()
- .on("data", async function(data) {
- txs.push({ ...JSON.parse(data.value), key: data.key });
- })
- .on("end", async function(data) {
- const rate = (a, b) => a.rate - b.rate;
- const time = (a, b) => b.time - a.time;
-
- const pending = txs.filter(tx => !tx.id);
- const completed = txs.filter(tx => tx.id).sort(time);
-
- const bitcoin = pending
- .filter(tx => tx.asset === "bitcoin")
- .sort(rate)
- .reverse();
- const tether = pending
- .filter(tx => tx.asset === "tether")
- .sort(rate)
- .reverse();
-
- const strip = a =>
- a.map(({ id, key, info, rate, time }) => ({
- id,
- key,
- info,
- rate,
- time
- }));
-
- ws &&
- ws.send(
- JSON.stringify({
- completed: strip(completed.slice(0, 3)),
- bitcoin: strip(bitcoin.slice(0, 3)),
- tether: strip(tether.slice(0, 3))
- })
- );
-
- let b, t, tx;
- if (bitcoin[0]) b = bitcoin[0].rate - app.get("ask");
- if (tether[0]) t = tether[0].rate - 1 / app.get("bid");
-
- if (b > 0 || t > 0) {
- if ((b && !t) || b > t) tx = bitcoin[0];
- if ((t && !b) || t > b) tx = tether[0];
- }
-
- if (tx) {
- try {
- tx.id = JSON.parse(await finalize(tx.text)).txid;
- tx.time = Date.now();
- delete tx.text;
- leveldb.put(tx.key, JSON.stringify(tx));
- } catch (e) {
- l.info(e);
- if (e.includes("Unexpected fees")) leveldb.del(tx.key);
- if (e.includes("unsigned inputs")) leveldb.del(tx.key);
- if (e.includes("insufficient fee")) leveldb.del(tx.key);
- }
- }
- });
-};
-
-const parse = info => {
- const fee = info.legs[0].fee;
- return [
- fee,
- (info.legs[1].amount / (info.legs[0].amount + fee)).toFixed(8),
- assets[info.legs[0].asset]
- ];
-};
-
-const finalize = (filename = "finalized.txt", text) => {
- if (text) fs.writeFileSync(swapsdir + filename, text);
-
- return new Promise((resolve, reject) => {
- const proc = cli("finalize", swapsdir + filename, "-s");
-
- proc.stdout.on("data", data => {
- resolve(data.toString());
- });
-
- proc.stderr.on("data", err => {
- reject(new Error(err.toString()));
- });
-
- proc.on("close", (code, signal) => {
- let msg = (code && code.toString()) || (signal && signal.toString());
- reject(new Error(`Liquid swap tool process closed unexpectedly: ${msg}`));
- });
-
- proc.on("exit", (code, signal) => {
- let msg = (code && code.toString()) || (signal && signal.toString());
- reject(new Error(`Liquid swap tool process exited unexpectedly: ${msg}`));
- });
-
- setTimeout(
- () => reject(new Error("Liquid swap tool timed out"), proc),
- timeout
- );
- });
-};
-
-app.get(
- "/orders",
- optionalAuth,
- ah(async (req, res) => {
- try {
- let orders = await db.Order.findAll({
- attributes: {
- include: [
- [col("acc1.asset"), "a1"],
- [col("acc2.asset"), "a2"]
- ]
- },
- include: [
- {
- model: db.Account,
- as: "acc1",
- attributes: []
- },
- {
- model: db.Account,
- as: "acc2",
- attributes: []
- }
- ]
- });
-
- res.send(
- orders.map(p => {
- if (p.user_id !== req.user.id) p.user_id = null;
- return p;
- })
- );
- } catch (e) {
- res.status(500).send(e.message);
- }
- })
-);
-
-app.post(
- "/publish",
- auth,
- ah(async (req, res) => {
- try {
- const { id } = req.body;
- await db.Order.update(
- { public: true },
- { where: { id, user_id: req.user.id } }
- );
- res.end();
- } catch (e) {
- res.status(500).send(e.message);
- }
- })
-);
-
-setInterval(async () => {
- if (!app.get("rates")) return;
- const amount = 0.01;
- const btc = "5ac9f65c0efcc4775e0baec4ec03abdde22473cd3cf33c0419ca290e0751b225";
- const lcad = "e749f7326d0ba155ec1d878e23458bc3f740a38bf91bbce955945ee10d6ab523";
- const { CAD, USD } = app.get("rates");
- const user = await db.User.findOne({
- where: {
- username: "maker"
- }
- });
-
- await db.transaction(async transaction => {
- let p = await db.Order.findOne(
- {
- where: {
- user_id: user.id,
- "$acc1.asset$": btc,
- accepted: false
- },
- include: [
- {
- model: db.Account,
- as: "acc1"
- }
- ]
- },
- { transaction }
- );
-
- let params = {
- a1: btc,
- a2: lcad,
- v1: amount * SATS,
- v2: Math.round(amount * SATS * (((app.get("ask") * CAD) / USD) * 1.01))
- };
-
- if (p && p.acc1.balance > p.v1) {
- p.v1 = params.v1;
- p.v2 = params.v2;
- await p.save();
- p = p.get({ plain: true });
- p.a1 = params.a1;
- p.a2 = params.a2;
-
- broadcast("order", shallow(p));
- } else {
- try {
- await swap(user, params);
- } catch(e) {
- l.error("Failed to make ask", e.message);
- }
- }
-
- p = await db.Order.findOne(
- {
- where: {
- user_id: user.id,
- "$acc1.asset$": lcad,
- accepted: false
- },
- include: [
- {
- model: db.Account,
- as: "acc1"
- }
- ]
- },
- { transaction }
- );
-
- params = {
- a1: lcad,
- a2: btc,
- v1: Math.round(amount * SATS * ((app.get("bid") * CAD) / USD) * 0.99),
- v2: amount * SATS
- }
-
- if (p && p.acc1.balance > p.v1) {
- p.v1 = params.v1;
- p.v2 = params.v2;
- await p.save();
- p = p.get({ plain: true });
- p.a1 = params.a1;
- p.a2 = params.a2;
-
- broadcast("order", shallow(p));
- } else {
- try {
- await swap(user, params);
- } catch(e) {
- l.warn("Failed to make bid", e.message);
- }
- }
- });
-}, 6000);