aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2020-04-24 22:46:45 -0700
committerAdam Soltys <[email protected]>2020-04-28 22:33:02 -0700
commitfae6e8b55ea12989d688eb777c002222199c169b (patch)
tree3efed4974408fc1bc8911aa9fb9fc657456d69ee
parent306357c24d3014b55ae1cd25b954a695ea2e5cc6 (diff)
downloadcoinos-server-fae6e8b55ea12989d688eb777c002222199c169b.tar.gz
coinos-server-fae6e8b55ea12989d688eb777c002222199c169b.zip
improved assets support
-rw-r--r--db/index.js10
-rw-r--r--db/models/accounts.js27
-rw-r--r--db/models/payments.js13
-rw-r--r--db/models/users.js78
-rw-r--r--index.js1
-rw-r--r--lib/passport.js1
-rw-r--r--lib/sockets.js1
-rw-r--r--lib/utils.js85
-rw-r--r--lib/whitelist.js8
-rw-r--r--routes/balances.js13
-rw-r--r--routes/bitcoin/receive.js49
-rw-r--r--routes/bitcoin/send.js3
-rw-r--r--routes/lightning/receive.js32
-rw-r--r--routes/lightning/send.js82
-rw-r--r--routes/lightning/user.js2
-rw-r--r--routes/liquid/receive.js89
-rw-r--r--routes/liquid/send.js148
-rw-r--r--routes/users.js24
18 files changed, 353 insertions, 313 deletions
diff --git a/db/index.js b/db/index.js
index bb94fb98..215d34df 100644
--- a/db/index.js
+++ b/db/index.js
@@ -28,6 +28,11 @@ db["User"].hasMany(db["Payment"], {
foreignKey: "user_id"
});
+db["User"].belongsTo(db["Account"], {
+ as: "account",
+ foreignKey: "account_id"
+});
+
db["Account"].belongsTo(db["User"], {
as: "user",
foreignKey: "user_id"
@@ -38,6 +43,11 @@ db["Invoice"].belongsTo(db["User"], {
foreignKey: "user_id"
});
+db["Payment"].belongsTo(db["Account"], {
+ as: "account",
+ foreignKey: "account_id"
+});
+
db["Payment"].belongsTo(db["User"], {
as: "user",
foreignKey: "user_id"
diff --git a/db/models/accounts.js b/db/models/accounts.js
index aeaf8d5e..a4b38992 100644
--- a/db/models/accounts.js
+++ b/db/models/accounts.js
@@ -37,6 +37,24 @@ const attributes = {
comment: null,
field: "updatedAt"
},
+ name: {
+ type: DataTypes.STRING(255),
+ allowNull: true,
+ defaultValue: null,
+ primaryKey: false,
+ autoIncrement: false,
+ comment: null,
+ field: "name"
+ },
+ ticker: {
+ type: DataTypes.STRING(255),
+ allowNull: true,
+ defaultValue: null,
+ primaryKey: false,
+ autoIncrement: false,
+ comment: null,
+ field: "ticker"
+ },
asset: {
type: DataTypes.STRING(255),
allowNull: true,
@@ -64,6 +82,15 @@ const attributes = {
comment: null,
field: "pending"
},
+ precision: {
+ type: DataTypes.DOUBLE,
+ allowNull: true,
+ defaultValue: null,
+ primaryKey: false,
+ autoIncrement: false,
+ comment: null,
+ field: "precision"
+ },
};
const options = {
diff --git a/db/models/payments.js b/db/models/payments.js
index c6dd6e83..8c464293 100644
--- a/db/models/payments.js
+++ b/db/models/payments.js
@@ -10,6 +10,15 @@ const attributes = {
comment: null,
field: "id"
},
+ account_id: {
+ type: DataTypes.INTEGER(11),
+ allowNull: true,
+ defaultValue: null,
+ primaryKey: false,
+ autoIncrement: false,
+ comment: null,
+ field: "account_id"
+ },
user_id: {
type: DataTypes.INTEGER(11),
allowNull: true,
@@ -55,14 +64,14 @@ const attributes = {
comment: null,
field: "rate"
},
- asset: {
+ network: {
type: DataTypes.STRING(255),
allowNull: true,
defaultValue: null,
primaryKey: false,
autoIncrement: false,
comment: null,
- field: "asset"
+ field: "network"
},
currency: {
type: DataTypes.STRING(255),
diff --git a/db/models/users.js b/db/models/users.js
index 48e8c94f..439ddb63 100644
--- a/db/models/users.js
+++ b/db/models/users.js
@@ -64,14 +64,14 @@ const attributes = {
comment: null,
field: "address"
},
- symbol: {
- type: DataTypes.STRING(255),
+ account_id: {
+ type: DataTypes.INTEGER(11),
allowNull: true,
defaultValue: null,
primaryKey: false,
autoIncrement: false,
comment: null,
- field: "symbol"
+ field: "account_id"
},
otpsecret: {
type: DataTypes.STRING(255),
@@ -100,24 +100,6 @@ const attributes = {
comment: null,
field: "currencies"
},
- pubkey: {
- type: DataTypes.STRING(255),
- allowNull: true,
- defaultValue: null,
- primaryKey: false,
- autoIncrement: false,
- comment: null,
- field: "pubkey"
- },
- privkey: {
- type: DataTypes.STRING(255),
- allowNull: true,
- defaultValue: null,
- primaryKey: false,
- autoIncrement: false,
- comment: null,
- field: "privkey"
- },
createdAt: {
type: DataTypes.DATE,
allowNull: false,
@@ -145,42 +127,6 @@ const attributes = {
comment: null,
field: "index"
},
- balance: {
- type: DataTypes.INTEGER(11),
- allowNull: false,
- defaultValue: "0",
- primaryKey: false,
- autoIncrement: false,
- comment: null,
- field: "balance"
- },
- channel: {
- type: DataTypes.STRING(255),
- allowNull: true,
- defaultValue: null,
- primaryKey: false,
- autoIncrement: false,
- comment: null,
- field: "channel"
- },
- readonlypw: {
- type: DataTypes.STRING(255),
- allowNull: true,
- defaultValue: null,
- primaryKey: false,
- autoIncrement: false,
- comment: null,
- field: "readonlypw"
- },
- limit: {
- type: DataTypes.INTEGER(11),
- allowNull: true,
- defaultValue: "0",
- primaryKey: false,
- autoIncrement: false,
- comment: null,
- field: "limit"
- },
fbtoken: {
type: DataTypes.STRING(255),
allowNull: true,
@@ -190,24 +136,6 @@ const attributes = {
comment: null,
field: "fbtoken"
},
- friend: {
- type: DataTypes.INTEGER(1),
- allowNull: true,
- defaultValue: null,
- primaryKey: false,
- autoIncrement: false,
- comment: null,
- field: "friend"
- },
- pending: {
- type: DataTypes.INTEGER(11),
- allowNull: true,
- defaultValue: "0",
- primaryKey: false,
- autoIncrement: false,
- comment: null,
- field: "pending"
- },
name: {
type: DataTypes.STRING(255),
allowNull: true,
diff --git a/index.js b/index.js
index 0ff00cc5..376810b2 100644
--- a/index.js
+++ b/index.js
@@ -20,6 +20,7 @@ app.use(compression());
server = require("http").Server(app);
require("./db");
+require("./lib/utils");
require("./lib/sockets");
require("./lib/passport");
require("./lib/rates");
diff --git a/lib/passport.js b/lib/passport.js
index 81f4e948..e0c2dd62 100644
--- a/lib/passport.js
+++ b/lib/passport.js
@@ -1,6 +1,5 @@
const Passport = require("passport");
const jwt = require("passport-jwt");
-const { getUser } = require("./utils");
function cookieExtractor(req) {
if (req && req.cookies) return req.cookies["token"];
diff --git a/lib/sockets.js b/lib/sockets.js
index ad7295b0..9a5457c3 100644
--- a/lib/sockets.js
+++ b/lib/sockets.js
@@ -1,6 +1,5 @@
const jwt = require("jsonwebtoken");
const io = require("socket.io");
-const { getUser } = require("./utils");
/* eslint-disable-next-line */
const pick = (O, ...K) => K.reduce((o, k) => ((o[k] = O[k]), o), {});
diff --git a/lib/utils.js b/lib/utils.js
index 7784ae5c..2f0214d5 100644
--- a/lib/utils.js
+++ b/lib/utils.js
@@ -1,30 +1,69 @@
const { Op } = require("sequelize");
-module.exports = {
- async getUser(username) {
- return db.User.findOne({
- include: [
- {
- model: db.Payment,
- as: "payments",
- order: [["id", "DESC"]],
- where: {
- [Op.or]: {
- received: true,
- amount: {
- [Op.lt]: 0
- }
+getUser = async username => {
+ return db.User.findOne({
+ include: [
+ {
+ model: db.Payment,
+ as: "payments",
+ order: [["id", "DESC"]],
+ where: {
+ [Op.or]: {
+ received: true,
+ amount: {
+ [Op.lt]: 0
}
- },
- limit: 12
+ }
},
- {
+ limit: 12,
+ include: {
model: db.Account,
- as: "accounts"
+ as: "account"
}
- ],
- where:
- { [Op.or]: { username, id: username }}
- });
- }
+ },
+ {
+ model: db.Account,
+ as: "accounts"
+ },
+ {
+ model: db.Account,
+ as: "account"
+ }
+ ],
+ where: { username }
+ });
+};
+
+getUserById = async id => {
+ return db.User.findOne({
+ include: [
+ {
+ model: db.Payment,
+ as: "payments",
+ order: [["id", "DESC"]],
+ where: {
+ [Op.or]: {
+ received: true,
+ amount: {
+ [Op.lt]: 0
+ }
+ }
+ },
+ limit: 12,
+ include: {
+ model: db.Account,
+ as: "account"
+ }
+ },
+ {
+ model: db.Account,
+ as: "accounts"
+ },
+ {
+ model: db.Account,
+ as: "account"
+ }
+ ],
+ where: { id }
+ });
};
diff --git a/lib/whitelist.js b/lib/whitelist.js
index e5f672b2..8af95820 100644
--- a/lib/whitelist.js
+++ b/lib/whitelist.js
@@ -1,5 +1,7 @@
whitelist = [
'id',
+'account',
+'account_id',
'unit',
'fbtoken',
'username',
@@ -7,11 +9,13 @@ whitelist = [
'symbol',
'currency',
'currencies',
-'balance',
-'pending',
'name',
'pic',
'twofa',
'liquid',
'confidential',
+'accounts',
+'payments',
+'balance',
+'pending',
]
diff --git a/routes/balances.js b/routes/balances.js
index eff361ea..86658ed3 100644
--- a/routes/balances.js
+++ b/routes/balances.js
@@ -9,11 +9,6 @@ app.get("/balances", async (req, res) => {
[sequelize.fn("sum", sequelize.col("balance")), "total"]
],
group: ["asset"],
- where: {
- asset: {
- [Op.ne]: config.liquid.btcasset
- }
- }
});
const balances = {
@@ -23,13 +18,6 @@ app.get("/balances", async (req, res) => {
liquid: parseInt(assets.bitcoin * SATS),
lnchannel: parseInt((await lna.channelBalance({})).balance),
lnwallet: parseInt((await lna.walletBalance({})).total_balance),
- user: parseInt(
- (await db["User"].findAll({
- attributes: [[sequelize.fn("sum", sequelize.col("balance")), "total"]],
- raw: true,
- order: sequelize.literal("total DESC")
- }))[0].total
- )
};
const { bitcoin, liquid, lnchannel, user, lnwallet } = balances;
@@ -42,6 +30,5 @@ app.get("/balances", async (req, res) => {
balances.ratio = (balances.total / parseInt(user)).toFixed(2);
-
res.send(balances);
});
diff --git a/routes/bitcoin/receive.js b/routes/bitcoin/receive.js
index 3083c49c..74afb4fb 100644
--- a/routes/bitcoin/receive.js
+++ b/routes/bitcoin/receive.js
@@ -12,7 +12,6 @@ const zmqRawTx = zmq.socket("sub");
zmqRawTx.connect(config.bitcoin.zmqrawtx);
zmqRawTx.subscribe("rawtx");
-const asset = "BTC";
const network =
bitcoin.networks[
config.bitcoin.network === "mainnet" ? "bitcoin" : config.bitcoin.network
@@ -48,7 +47,7 @@ zmqRawTx.on("message", async (topic, message, sequence) => {
const invoice = await db.Invoice.findOne({
where: {
user_id: user.id,
- asset
+ network: 'BTC',
},
order: [["id", "DESC"]]
});
@@ -59,15 +58,25 @@ zmqRawTx.on("message", async (topic, message, sequence) => {
let confirmed = false;
- user.address = await bc.getNewAddress("", "bech32");
- user.pending += value;
+ const account = await db.Account.findOne({
+ where: {
+ user_id: user.id,
+ asset: config.liquid.btcasset
+ }
+ });
+
+ account.pending += value;
+ await account.save();
+ user.address = await bc.getNewAddress("", "bech32");
await user.save();
+ user = await getUser(user.username);
emit(user.username, "user", user);
addresses[user.address] = user.username;
const payment = await db.Payment.create({
+ account_id: account.id,
user_id: user.id,
hash,
amount: value - tip,
@@ -77,7 +86,7 @@ zmqRawTx.on("message", async (topic, message, sequence) => {
tip,
confirmed,
address,
- asset,
+ network: 'BTC'
});
l.info("bitcoin detected", user.username, o.value);
@@ -109,33 +118,23 @@ setInterval(async () => {
let hash = arr[i];
let p = await db.Payment.findOne({
- include: [{ model: db.User, as: "user" }],
- where: { hash, confirmed: 0 }
+ where: { hash, confirmed: 0, received: 1 },
+ include: {
+ model: db.Account,
+ as: "account"
+ }
});
p.confirmed = 1;
+ p.account.balance += p.amount + p.tip;
+ p.account.pending -= Math.min(p.account.pending, p.amount + p.tip);
- let user = await p.getUser();
- user.balance += p.amount + p.tip;
- user.pending -= Math.min(user.pending, p.amount + p.tip);
- emit(user.username, "user", user);
-
- await user.save();
+ await p.account.save();
await p.save();
- let payments = await db.Payment.findAll({
- where: {
- user_id: user.id,
- received: {
- [Op.ne]: null
- }
- },
- order: [["id", "DESC"]],
- limit: 12
- });
-
+ let user = await getUserById(p.user_id);
+ emit(user.username, "user", user);
l.info("bitcoin confirmed", user.username, p.amount, p.tip);
- emit(user.username, "payments", payments);
delete queue[hash];
}
}, 1000);
diff --git a/routes/bitcoin/send.js b/routes/bitcoin/send.js
index 948e5386..bb0cf97b 100644
--- a/routes/bitcoin/send.js
+++ b/routes/bitcoin/send.js
@@ -72,6 +72,7 @@ module.exports = async (req, res) => {
{
amount: -total,
fee,
+ account_id: user.account.id,
user_id: user.id,
hash: txid,
rate: app.get("rates")[user.currency],
@@ -79,7 +80,7 @@ module.exports = async (req, res) => {
address,
confirmed: true,
received: false,
- asset: "BTC"
+ network: "BTC"
},
{ transaction }
);
diff --git a/routes/lightning/receive.js b/routes/lightning/receive.js
index 6ac4c947..c300d8ea 100644
--- a/routes/lightning/receive.js
+++ b/routes/lightning/receive.js
@@ -4,19 +4,29 @@ const handlePayment = async msg => {
if (!msg.settled) return;
const invoice = await db.Invoice.findOne({
- include: { model: db.User, as: "user" },
where: {
text: msg.payment_request
}
});
+
if (!invoice) return;
- const { text: hash, currency, rate, tip, user } = invoice;
+ const { text: hash, currency, rate, tip, user_id } = invoice;
const amount = parseInt(msg.value) - tip;
+ let user = await getUserById(user_id);
+
+ const account = await db.Account.findOne({
+ where: {
+ user_id,
+ asset: config.liquid.btcasset
+ }
+ });
+
const payment = await db.Payment.create({
- user_id: user.id,
+ account_id: account.id,
+ user_id,
hash,
amount,
currency,
@@ -24,17 +34,27 @@ const handlePayment = async msg => {
received: true,
confirmed: true,
asset,
- tip,
+ tip
});
invoice.received += amount + tip;
- user.balance += amount + tip;
+ account.balance += amount + tip;
+ await account.save();
await invoice.save();
await payment.save();
await user.save();
+
payments.push(msg.payment_request);
- l.info("lightning payment received", user.username, payment.amount, payment.tip);
+ l.info(
+ "lightning payment received",
+ user.username,
+ payment.amount,
+ payment.tip
+ );
+
+ user = await getUser(user.username)
+ l.info(user.account.balance);
emit(user.username, "payment", payment);
emit(user.username, "user", user);
diff --git a/routes/lightning/send.js b/routes/lightning/send.js
index 822fe0e4..0d2025ad 100644
--- a/routes/lightning/send.js
+++ b/routes/lightning/send.js
@@ -32,66 +32,76 @@ module.exports = async (req, res) => {
await user.save({ transaction });
});
} catch (e) {
- l.warn("insufficient funds for lightning payment", user.username, user.balance);
+ l.warn(
+ "insufficient funds for lightning payment",
+ user.username,
+ user.balance
+ );
return res.status(500).send("Not enough satoshis");
}
let m;
try {
- let paymentHash = payreq.tags.find(t => t.tagName === 'payment_hash').data;
+ let paymentHash = payreq.tags.find(t => t.tagName === "payment_hash").data;
m = await lna.sendPaymentSync({
- "payment_request": hash,
+ payment_request: hash
});
-
+
if (m.payment_error) return res.status(500).send(m.payment_error);
if (seen.includes(m.payment_preimage)) {
l.warn("duplicate payment detected", m.payment_preimage);
return res.status(500).send("duplicate payment detected");
- }
+ }
let total = parseInt(m.payment_route.total_amt);
let fee = m.payment_route.total_fees;
user.balance -= total - payreq.satoshis;
- await db.transaction(async transaction => {
- await user.save({ transaction });
+ await user.save();
- const payment = await db.Payment.create(
- {
- amount: -total,
- fee,
- user_id: user.id,
- hash,
- rate: app.get("rates")[user.currency],
- currency: user.currency,
- confirmed: true,
- asset: 'LNBTC',
- },
- { transaction }
- );
+ const account = await db.Account.findOne({
+ where: {
+ user_id: user.id,
+ asset: config.liquid.btcasset
+ }
+ });
- seen.push(m.payment_preimage);
+ const payment = await db.Payment.create({
+ amount: -total,
+ account_id: account.id,
+ user_id: user.id,
+ hash,
+ rate: app.get("rates")[user.currency],
+ currency: user.currency,
+ confirmed: true,
+ network: "LNBTC"
+ });
- l.info("sent lightning", user.username, -payment.amount);
- emit(user.username, "payment", payment);
- emit(user.username, "user", user);
+ user = await getUser(user.username);
- if (payreq.payeeNodeKey === config.lnb.id) {
- lna.addInvoice({ value: payreq.satoshis }, (err, invoice) => {
- let payback = lnb.sendPayment(lnb.meta, {});
+ seen.push(m.payment_preimage);
- /* eslint-disable-next-line */
- let { payment_request } = invoice;
- /* eslint-disable-next-line */
- payback.write({ payment_request });
- });
- }
+ l.info("sent lightning", user.username, -payment.amount);
+ emit(user.username, "payment", payment);
- seen.push(hash);
- res.send(payment);
- });
+ l.info("user payments", user.payments);
+ emit(user.username, "user", user);
+
+ if (payreq.payeeNodeKey === config.lnb.id) {
+ lna.addInvoice({ value: payreq.satoshis }, (err, invoice) => {
+ let payback = lnb.sendPayment(lnb.meta, {});
+
+ /* eslint-disable-next-line */
+ let { payment_request } = invoice;
+ /* eslint-disable-next-line */
+ payback.write({ payment_request });
+ });
+ }
+
+ seen.push(hash);
+ res.send(payment);
} catch (e) {
l.info("error sending lightning", e);
return res.status(500).send(e);
diff --git a/routes/lightning/user.js b/routes/lightning/user.js
index 34659a6c..36af2718 100644
--- a/routes/lightning/user.js
+++ b/routes/lightning/user.js
@@ -34,7 +34,7 @@ module.exports = async (req, res) => {
tip: 0,
confirmed: true,
received: false,
- asset: "LNBTC"
+ network : "LNBTC"
});
req.url = "/lightning/send";
diff --git a/routes/liquid/receive.js b/routes/liquid/receive.js
index 24d17a35..5d7eb237 100644
--- a/routes/liquid/receive.js
+++ b/routes/liquid/receive.js
@@ -1,3 +1,4 @@
+const axios = require("axios");
const reverse = require("buffer-reverse");
const zmq = require("zeromq");
const { Op } = require("sequelize");
@@ -47,27 +48,29 @@ zmqRawTx.on("message", async (topic, message, sequence) => {
let confirmed = 0;
- if (asset === config.liquid.btcasset) {
- user.pending += value;
- } else {
- const params = {
- user_id: user.id,
- asset,
- };
+ let params = {
+ user_id: user.id,
+ asset
+ };
- const account = await db.Account.findOne({
- where: params
- });
+ const account = await db.Account.findOne({
+ where: params
+ });
- if (account) {
- account.pending += value;
- await account.save();
- } else {
- params.balance = 0;
- params.pending = value;
- l.info("creating", params);
- await db.Account.create(params);
+ if (account) {
+ account.pending += value;
+ await account.save();
+ } else {
+ let assets = await axios.get("https://assets.blockstream.info/");
+
+ if (assets[asset]) {
+ let { ticker, precision, name } = assets[asset];
+ params = { ...params, ...{ ticker, precision, name } };
}
+
+ params.balance = 0;
+ params.pending = value;
+ account = await db.Account.create(params);
}
user.confidential = await lq.getNewAddress();
@@ -76,11 +79,13 @@ zmqRawTx.on("message", async (topic, message, sequence) => {
)).unconfidential;
await user.save();
+ user = await getUser(user.username);
emit(user.username, "user", user);
addresses[user.liquid] = user.username;
const payment = await db.Payment.create({
+ account_id: account.id,
user_id: user.id,
hash: blinded.txid,
amount: value - tip,
@@ -90,7 +95,7 @@ zmqRawTx.on("message", async (topic, message, sequence) => {
tip,
confirmed,
address,
- asset
+ network: "LBTC"
});
payments.push(blinded.txid);
@@ -126,48 +131,24 @@ setInterval(async () => {
let hash = arr[i];
let p = await db.Payment.findOne({
- where: { hash, confirmed: 0, received: 1 }
- });
-
- const user = await getUser(p.user_id);
-
- p.confirmed = 1;
-
- const asset = await db.Account.findOne({
- where: {
- user_id: user.id,
- asset: p.asset
+ where: { hash, confirmed: 0, received: 1 },
+ include: {
+ model: db.Account,
+ as: "account"
}
});
- l.info("asset", p.asset);
- if (p.asset === config.liquid.btcasset) {
- user.balance += p.amount + p.tip;
- user.pending -= Math.min(user.pending, p.amount + p.tip);
- } else {
- asset.balance += p.amount + p.tip;
- asset.pending -= Math.min(user.pending, p.amount + p.tip);
- await asset.save();
- }
-
- l.info("liquid confirmed", user.username, p.asset, p.amount, p.tip);
- emit(user.username, "user", user);
+ p.confirmed = 1;
+ p.account.balance += p.amount + p.tip;
+ p.account.pending -= Math.min(p.account.pending, p.amount + p.tip);
- await user.save();
+ await p.account.save();
await p.save();
- let payments = await db.Payment.findAll({
- where: {
- user_id: user.id,
- received: {
- [Op.ne]: null
- }
- },
- order: [["id", "DESC"]],
- limit: 12
- });
+ let user = await getUserById(p.user_id);
+ emit(user.username, "user", user);
+ l.info("liquid confirmed", user.username, p.asset, p.amount, p.tip);
- emit(user.username, "payments", payments);
delete queue[hash];
}
}, 1000);
diff --git a/routes/liquid/send.js b/routes/liquid/send.js
index e1a7e473..461b0c0a 100644
--- a/routes/liquid/send.js
+++ b/routes/liquid/send.js
@@ -4,96 +4,104 @@ module.exports = async (req, res) => {
let { user } = req;
let {
address,
- asset,
tx: { hex }
} = req.body;
- const isChange = async address =>
- ((await lq.getAddressInfo(address)).ismine) &&
- (!Object.keys(addresses).includes(address) ||
- address === user.liquid);
+ const isChange = async address =>
+ (await lq.getAddressInfo(address)).ismine &&
+ (!Object.keys(addresses).includes(address) || address === user.liquid);
const unblinded = await lq.unblindRawTransaction(hex);
tx = await lq.decodeRawTransaction(unblinded.hex);
-
- let total = 0;
- let change = 0;
+
+ let totals = {};
+ let change = {};
let fee = 0;
for (let i = 0; i < tx.vout.length; i++) {
- let o = tx.vout[i];
- total += toSats(o.value);
- if (o.scriptPubKey.type === 'fee') fee = toSats(o.value);
-
- if (o.scriptPubKey.addresses) {
- if ((await isChange(o.scriptPubKey.addresses[0]))) {
- change += toSats(o.value);
+ let {
+ asset,
+ value,
+ scriptPubKey: { type, addresses }
+ } = tx.vout[i];
+
+ if (!totals[asset]) totals[asset] = change[asset] = 0;
+ totals[asset] += toSats(value);
+ if (type === "fee") fee = toSats(value);
+
+ if (addresses) {
+ if (await isChange(addresses[0])) {
+ change[asset] += toSats(value);
}
}
- }
+ }
- total = total - change;
- let amount = total - fee;
+ let assets = Object.keys(totals);
+ for (let i = 0; i < assets.length; i++) {
+ let asset = assets[i];
+ let total = totals[asset];
+ if (change[asset]) total -= change[asset];
- l.info("attempting liquid payment", user.username, amount, fee);
+ l.info("attempting liquid payment", user.username, asset, total, fee);
- try {
- await db.transaction(async transaction => {
- let { balance } = await db.User.findOne({
- where: {
- username: user.username
- },
- lock: transaction.LOCK.UPDATE,
- transaction
- });
+ try {
+ await db.transaction(async transaction => {
+ let account = await db.Account.findOne({
+ where: {
+ user_id: user.id,
+ asset
+ },
+ lock: transaction.LOCK.UPDATE,
+ transaction
+ });
- if (amount !== balance && total > balance) {
- l.warn("amount exceeds balance", { amount, fee, balance });
- throw new Error("insufficient funds");
- }
+ if (total > account.balance) {
+ l.warn("amount exceeds balance", {
+ total,
+ fee,
+ balance: account.balance
+ });
+ throw new Error("insufficient funds");
+ }
- req.user.balance -= total;
- await user.save({ transaction });
- });
- } catch (e) {
- l.warn("insufficient funds for liquid payment", user.username);
- return res.status(500).send("Not enough satoshis");
- }
+ account.balance -= total;
+ await account.save({ transaction });
- try {
- if (config.liquid.walletpass)
- await lq.walletPassphrase(config.liquid.walletpass, 300);
+ try {
+ if (config.liquid.walletpass)
+ await lq.walletPassphrase(config.liquid.walletpass, 300);
- hex = await lq.blindRawTransaction(hex);
- rawtx = (await lq.signRawTransactionWithWallet(hex)).hex;
- let txid = await lq.sendRawTransaction(rawtx);
+ hex = await lq.blindRawTransaction(hex);
+ rawtx = (await lq.signRawTransactionWithWallet(hex)).hex;
+ let txid = await lq.sendRawTransaction(rawtx);
- await db.transaction(async transaction => {
- await user.save({ transaction });
- emit(user.username, "user", user);
+ const payment = await db.Payment.create({
+ amount: -total,
+ account_id: account.id,
+ fee,
+ user_id: user.id,
+ hash: txid,
+ rate: app.get("rates")[user.currency],
+ currency: user.currency,
+ address,
+ confirmed: true,
+ received: false,
+ network: "LBTC"
+ }, { transaction });
- const payment = await db.Payment.create(
- {
- amount: -total,
- fee,
- user_id: user.id,
- hash: txid,
- rate: app.get("rates")[user.currency],
- currency: user.currency,
- address,
- confirmed: true,
- received: false,
- asset: 'LBTC'
- },
- { transaction }
- );
+ payment.account = account;
- l.info("sent liquid", user.username, total);
- emit(user.username, "payment", payment);
- res.send(payment);
- });
- } catch (e) {
- l.error("liquid send failed", e);
- return res.status(500).send(e.message);
+ user = await getUser(user.username);
+ emit(user.username, "user", user);
+ res.send(payment);
+ } catch (e) {
+ l.error("liquid send failed", e);
+ return res.status(500).send(e.message);
+ }
+ });
+ } catch (e) {
+ l.warn("insufficient funds for liquid payment", user.username, e.message);
+ return res.status(500).send("Not enough satoshis");
+ }
}
};
diff --git a/routes/users.js b/routes/users.js
index 1d9a4f3d..cc7b62cc 100644
--- a/routes/users.js
+++ b/routes/users.js
@@ -4,7 +4,6 @@ const jwt = require("jsonwebtoken");
const uuidv4 = require("uuid/v4");
const fb = "https://graph.facebook.com/";
const authenticator = require("otplib").authenticator;
-const { getUser } = require("../lib/utils");
const pick = (O, ...K) => K.reduce((o, k) => ((o[k] = O[k]), o), {});
let faucet = 1000;
@@ -55,10 +54,16 @@ app.get("/liquidate", async (req, res) => {
});
const gift = async user => {
+ const account = user.accounts[0];
+
if (faucet > 0) {
- user.balance = 100;
faucet -= 100;
+
+ account.balance = 100;
+ await account.save();
+
const payment = await db.Payment.create({
+ account_id: account.id,
user_id: user.id,
hash: "Welcome Gift",
amount: 100,
@@ -66,8 +71,9 @@ const gift = async user => {
rate: app.get("rates")[user.currency],
received: true,
confirmed: 1,
- asset: "GIFT"
+ network: "GIFT"
});
+
await user.save();
}
};
@@ -109,8 +115,20 @@ app.post("/register", async (req, res) => {
addresses[user.address] = user.username;
addresses[user.liquid] = user.username;
user = await db.User.create(user);
+
+ let account = await db.Account.create({
+ user_id: user.id,
+ asset: config.liquid.btcasset,
+ balance: 0,
+ pending: 0,
+ });
+
+ user.accounts = [account];
await gift(user);
+ user.account_id = account.id;
+ await user.save();
+
l.info("new user", user.username);
res.send(pick(user, ...whitelist));
});