aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2021-01-15 15:31:46 -0800
committerAdam Soltys <[email protected]>2021-01-15 15:31:46 -0800
commitd01159ce9eee956dca32fd69525c0edd5aae9477 (patch)
treee6a4d4a928daffb5bcedbc3411a0dfded6ff422f
parentd03dad4d85ccd107068719c4b68cbd6a255864f6 (diff)
downloadcoinos-server-d01159ce9eee956dca32fd69525c0edd5aae9477.tar.gz
coinos-server-d01159ce9eee956dca32fd69525c0edd5aae9477.zip
add more transactions and locks
-rw-r--r--lib/account.js7
-rw-r--r--lib/send.js1
-rw-r--r--routes/assets.js45
-rw-r--r--routes/bitcoin/broadcast.js56
-rw-r--r--routes/bitcoin/receive.js9
-rw-r--r--routes/bitcoin/send.js43
-rw-r--r--routes/lightning/receive.js4
-rw-r--r--routes/liquid/broadcast.js57
-rw-r--r--routes/liquid/receive.js158
-rw-r--r--routes/liquid/send.js22
-rw-r--r--routes/lnurl.js8
-rw-r--r--routes/send.js29
-rw-r--r--routes/users.js73
13 files changed, 305 insertions, 207 deletions
diff --git a/lib/account.js b/lib/account.js
index 5a8c3924..ac09a8e8 100644
--- a/lib/account.js
+++ b/lib/account.js
@@ -33,9 +33,10 @@ module.exports = async (asset, user, transaction) => {
asset
},
order: [["id", "ASC"]],
- limit: 1
+ limit: 1,
+ lock: transaction.LOCK.UPDATE,
+ transaction
},
- { transaction }
);
if (existing) {
@@ -46,5 +47,5 @@ module.exports = async (asset, user, transaction) => {
params = { ...params, ...{ domain, ticker, precision, name } };
params.balance = 0;
params.pending = 0;
- return db.Account.create(params);
+ return db.Account.create(params, { transaction });
};
diff --git a/lib/send.js b/lib/send.js
index 4e00f54a..a1052041 100644
--- a/lib/send.js
+++ b/lib/send.js
@@ -1,7 +1,6 @@
const bolt11 = require("bolt11");
send = async (amount, memo, hash, user) => {
- return res.status(500).send("Internal payments are temporarily disabled");
let payreq = bolt11.decode(hash);
if (!amount) amount = payreq.satoshis;
diff --git a/routes/assets.js b/routes/assets.js
index ea3e82ff..7d2cdfcf 100644
--- a/routes/assets.js
+++ b/routes/assets.js
@@ -29,15 +29,17 @@ app.get(
Object.keys(assets).map(a => {
assets[a].registered = true;
if (!assets[a].asset) assets[a].asset = assets[a].asset_id;
- if ((
- assets[a].ticker === 'BTC' && assets[a].asset !== config.liquid.btcasset
- ) || (
- assets[a].ticker === 'EUR' && assets[a].asset !== config.liquid.eurasset
- ) || (
- assets[a].ticker === 'CAD' && assets[a].asset !== config.liquid.cadasset
- ) || (
- assets[a].ticker === 'USDt' && assets[a].asset !== config.liquid.usdtasset
- )) delete assets[a];
+ if (
+ (assets[a].ticker === "BTC" &&
+ assets[a].asset !== config.liquid.btcasset) ||
+ (assets[a].ticker === "EUR" &&
+ assets[a].asset !== config.liquid.eurasset) ||
+ (assets[a].ticker === "CAD" &&
+ assets[a].asset !== config.liquid.cadasset) ||
+ (assets[a].ticker === "USDt" &&
+ assets[a].asset !== config.liquid.usdtasset)
+ )
+ delete assets[a];
});
accounts.map(({ asset, name, domain, ticker, precision }) => {
@@ -156,8 +158,11 @@ app.post(
);
}
- account.balance -= Math.round(funded.fee * SATS);
- await account.save({ transaction });
+ await account.decrement(
+ { balance: Math.round(funded.fee * SATS) },
+ { transaction }
+ );
+ await account.reload({ transaction });
emit(user.username, "account", account);
account = await db.Account.create(
@@ -294,7 +299,11 @@ app.post(
try {
await db.transaction(async transaction => {
- let { account } = user;
+ let account = await getAccount(
+ config.liquid.btcasset,
+ user,
+ transaction
+ );
if (amount < 0) throw new Error("Amount to load cannot be negative");
if (account.asset !== config.liquid.btcasset)
@@ -305,8 +314,8 @@ app.post(
let fee = 0;
- account.balance -= amount;
- await account.save({ transaction });
+ await account.decrement({ balance: amount }, { transaction });
+ await account.reload({ transaction });
let a2;
let acc = {
@@ -321,8 +330,8 @@ app.post(
});
if (a2) {
- a2.balance += amount;
- await a2.save({ transaction });
+ await a2.increment({ balance: amount }, { transaction });
+ await a2.reload({ transaction });
} else {
let name = asset.substr(0, 6);
let domain;
@@ -339,7 +348,9 @@ app.post(
asset
},
order: [["id", "ASC"]],
- limit: 1
+ limit: 1,
+ lock: transaction.LOCK.UPDATE,
+ transaction
});
if (existing) {
diff --git a/routes/bitcoin/broadcast.js b/routes/bitcoin/broadcast.js
index 77b487de..a32e9067 100644
--- a/routes/bitcoin/broadcast.js
+++ b/routes/bitcoin/broadcast.js
@@ -4,29 +4,43 @@ module.exports = ah(async (req, res) => {
let hash = await bc.sendRawTransaction(req.body.tx);
if (user) {
- let { account } = user;
- let payment = await db.Payment.create({
- ...req.body.payment,
- hash,
- amount: -req.body.payment.amount,
- tip: 0,
- account_id: account.id,
- user_id: user.id,
- rate: app.get("rates")[user.currency],
- currency: user.currency,
- confirmed: true,
- received: false,
- network: "bitcoin",
- });
+ await db.transaction(async transaction => {
+ let account = await db.Account.findOne({
+ where: {
+ id: user.account_id
+ },
+ lock: transaction.LOCK.UPDATE,
+ transaction
+ });
+ let payment = await db.Payment.create(
+ {
+ ...req.body.payment,
+ hash,
+ amount: -req.body.payment.amount,
+ tip: 0,
+ account_id: account.id,
+ user_id: user.id,
+ rate: app.get("rates")[user.currency],
+ currency: user.currency,
+ confirmed: true,
+ received: false,
+ network: "bitcoin"
+ },
+ { transaction }
+ );
- account.balance += payment.amount - payment.fee;
- await account.save();
+ await account.increment(
+ { balance: payment.amount - payment.fee },
+ { transaction }
+ );
+ await account.reload({ transaction });
- payment = payment.get({ plain: true });
- payment.account = account.get({ plain: true });
- emit(user.username, "payment", payment);
- emit(user.username, "account", payment.account);
- res.send(payment);
+ payment = payment.get({ plain: true });
+ payment.account = account.get({ plain: true });
+ emit(user.username, "payment", payment);
+ emit(user.username, "account", payment.account);
+ res.send(payment);
+ });
} else {
res.send(result);
}
diff --git a/routes/bitcoin/receive.js b/routes/bitcoin/receive.js
index e0e3828b..2a6a7954 100644
--- a/routes/bitcoin/receive.js
+++ b/routes/bitcoin/receive.js
@@ -159,10 +159,13 @@ setInterval(async () => {
let total = p.amount + p.tip;
p.confirmed = 1;
- p.account.balance += total;
- p.account.pending -= Math.min(p.account.pending, total);
-
await p.account.save({ transaction });
+ await p.account.increment({ balance: total }, { transaction });
+ await p.account.decrement(
+ { pending: Math.min(p.account.pending, total) },
+ { transaction }
+ );
+ await p.account.reload({ transaction });
await p.save({ transaction });
p = p.get({ plain: true });
diff --git a/routes/bitcoin/send.js b/routes/bitcoin/send.js
index 2089adfa..f1814b08 100644
--- a/routes/bitcoin/send.js
+++ b/routes/bitcoin/send.js
@@ -31,15 +31,23 @@ module.exports = ah(async (req, res) => {
total = total - change + fee;
let amount = total - fee;
- let { account } = user;
let params;
try {
await db.transaction(async transaction => {
+ let account = await db.Account.findOne({
+ where: {
+ id: user.account_id
+ },
+ lock: transaction.LOCK.UPDATE,
+ transaction
+ });
+
if (account.asset !== btc) {
account = await db.Account.findOne({
where: {
user_id: user.id,
asset: btc,
+ pubkey: null
},
lock: transaction.LOCK.UPDATE,
transaction
@@ -51,8 +59,8 @@ module.exports = ah(async (req, res) => {
throw new Error("Insufficient funds");
}
- account.balance -= total;
- await account.save({ transaction });
+ await account.decrement({ balance: total }, { transaction });
+ await account.reload({ transaction });
params = {
amount: -amount,
@@ -67,29 +75,24 @@ module.exports = ah(async (req, res) => {
received: false,
network: "bitcoin"
};
- });
- } catch (e) {
- l.error("problem creating bitcoin payment", user.username, e.message);
- return res.status(500).send(e.message);
- }
- try {
- if (config.bitcoin.walletpass)
- await bc.walletPassphrase(config.bitcoin.walletpass, 300);
+ if (config.bitcoin.walletpass)
+ await bc.walletPassphrase(config.bitcoin.walletpass, 300);
- hex = (await bc.signRawTransactionWithWallet(hex)).hex;
- params.hash = await bc.sendRawTransaction(hex);
+ hex = (await bc.signRawTransactionWithWallet(hex)).hex;
+ params.hash = await bc.sendRawTransaction(hex);
- let payment = await db.Payment.create(params);
+ let payment = await db.Payment.create(params, { transaction });
- payment = payment.get({ plain: true });
- payment.account = account.get({ plain: true });
+ payment = payment.get({ plain: true });
+ payment.account = account.get({ plain: true });
- emit(user.username, "payment", payment);
- res.send(payment);
+ emit(user.username, "payment", payment);
+ res.send(payment);
- payments.push(params.hash);
- l.info("sent bitcoin", user.username, total);
+ payments.push(params.hash);
+ l.info("sent bitcoin", user.username, total);
+ });
} catch (e) {
l.error("error sending bitcoin", e.message);
return res.status(500).send(e.message);
diff --git a/routes/lightning/receive.js b/routes/lightning/receive.js
index 5b0d1d77..0c8d6557 100644
--- a/routes/lightning/receive.js
+++ b/routes/lightning/receive.js
@@ -55,9 +55,9 @@ const handlePayment = async (msg) => {
let total = amount + tip;
invoice.received += total;
- account.balance += total;
- await account.save({ transaction });
+ await account.increment({ balance: total }, { transaction });
+ await account.reload({ transaction });
await invoice.save({ transaction });
await payment.save({ transaction });
payments.push(msg.payment_request);
diff --git a/routes/liquid/broadcast.js b/routes/liquid/broadcast.js
index a39349a1..1db166c5 100644
--- a/routes/liquid/broadcast.js
+++ b/routes/liquid/broadcast.js
@@ -4,29 +4,44 @@ module.exports = ah(async (req, res) => {
let hash = await lq.sendRawTransaction(req.body.tx);
if (user) {
- let { account } = user;
- let payment = await db.Payment.create({
- ...req.body.payment,
- hash,
- amount: -req.body.payment.amount,
- tip: 0,
- account_id: account.id,
- user_id: user.id,
- rate: app.get("rates")[user.currency],
- currency: user.currency,
- confirmed: true,
- received: false,
- network: "liquid",
- });
+ await db.transaction(async transaction => {
+ let account = await db.Account.findOne({
+ where: {
+ id: user.account_id
+ },
+ lock: transaction.LOCK.UPDATE,
+ transaction
+ });
+
+ let payment = await db.Payment.create(
+ {
+ ...req.body.payment,
+ hash,
+ amount: -req.body.payment.amount,
+ tip: 0,
+ account_id: account.id,
+ user_id: user.id,
+ rate: app.get("rates")[user.currency],
+ currency: user.currency,
+ confirmed: true,
+ received: false,
+ network: "liquid"
+ },
+ { transaction }
+ );
- account.balance += payment.amount - payment.fee;
- await account.save();
+ await account.increment(
+ { balance: payment.amount - payment.fee },
+ { transaction }
+ );
+ await account.reload({ transaction });
- payment = payment.get({ plain: true });
- payment.account = account.get({ plain: true });
- emit(user.username, "payment", payment);
- emit(user.username, "account", payment.account);
- res.send(payment);
+ payment = payment.get({ plain: true });
+ payment.account = account.get({ plain: true });
+ emit(user.username, "payment", payment);
+ emit(user.username, "account", payment.account);
+ res.send(payment);
+ });
} else {
res.send(result);
}
diff --git a/routes/liquid/receive.js b/routes/liquid/receive.js
index 2976bf57..ce1c6d4b 100644
--- a/routes/liquid/receive.js
+++ b/routes/liquid/receive.js
@@ -12,9 +12,11 @@ const network =
config.liquid.network === "mainnet" ? "liquid" : config.liquid.network
];
-const getAccount = async params => {
+const getAccount = async (params, transaction) => {
let account = await db.Account.findOne({
- where: params
+ where: params,
+ lock: transaction.LOCK.UPDATE,
+ transaction
});
if (account) {
@@ -39,7 +41,9 @@ const getAccount = async params => {
pubkey
},
order: [["id", "ASC"]],
- limit: 1
+ limit: 1,
+ lock: transaction.LOCK.UPDATE,
+ transaction
});
if (existing) {
@@ -51,7 +55,7 @@ const getAccount = async params => {
params = { ...params, ...{ domain, ticker, precision, name } };
params.balance = 0;
params.network = "liquid";
- return db.Account.create(params);
+ return db.Account.create(params, { transaction });
};
const zmqRawBlock = zmq.socket("sub");
@@ -82,78 +86,91 @@ zmqRawTx.on("message", async (topic, message, sequence) => {
Object.keys(addresses).includes(address) &&
!change.includes(address)
) {
- let user = await getUser(addresses[address]);
+ await db.transaction(async transaction => {
+ let user = await getUser(addresses[address], transaction);
- let invoice = await db.Invoice.findOne({
- where: {
- unconfidential: address,
- user_id: user.id,
- network: "liquid"
- },
- order: [["id", "DESC"]],
- include: {
- model: db.Account,
- as: "account"
- }
- });
-
- if (!invoice) return;
+ let invoice = await db.Invoice.findOne({
+ where: {
+ unconfidential: address,
+ user_id: user.id,
+ network: "liquid"
+ },
+ order: [["id", "DESC"]]
+ });
- let confirmed = 0;
+ if (!invoice) return;
- let { account } = invoice;
+ let confirmed = 0;
- if (
- account.asset === asset &&
- (!account.pubkey || account.network === "liquid")
- ) {
- account.pending += value;
- await account.save();
- } else {
- account = await getAccount({
- seed: account.seed,
- path: account.path,
- user_id: user.id,
- asset,
- pubkey: account.pubkey,
- pending: value,
- index: 0
+ let account = await db.Account.findOne({
+ where: {
+ id: invoice.account_id
+ },
+ lock: transaction.LOCK.UPDATE,
+ transaction
});
- }
- if (config.liquid.walletpass)
- await lq.walletPassphrase(config.liquid.walletpass, 300);
+ console.log(account);
+ if (
+ account.asset === asset &&
+ (!account.pubkey || account.network === "liquid")
+ ) {
+ await account.increment({ pending: value }, { transaction });
+ await account.reload({ transaction });
+ } else {
+ account = await getAccount(
+ {
+ seed: account.seed,
+ path: account.path,
+ user_id: user.id,
+ asset,
+ pubkey: account.pubkey,
+ pending: value,
+ index: 0
+ },
+ transaction
+ );
+ }
- await user.save();
+ if (config.liquid.walletpass)
+ await lq.walletPassphrase(config.liquid.walletpass, 300);
- const currency = invoice ? invoice.currency : user.currency;
- const rate = invoice ? invoice.rate : app.get("rates")[user.currency];
- const tip = invoice ? invoice.tip : 0;
- const memo = invoice ? invoice.memo : "";
+ await user.save({ transaction });
- let payment = await db.Payment.create({
- account_id: account.id,
- user_id: user.id,
- hash: blinded.txid,
- amount: value - tip,
- currency,
- memo,
- rate,
- received: true,
- tip,
- confirmed,
- address,
- network: "liquid"
- });
+ const currency = invoice ? invoice.currency : user.currency;
+ const rate = invoice
+ ? invoice.rate
+ : app.get("rates")[user.currency];
+ const tip = invoice ? invoice.tip : 0;
+ const memo = invoice ? invoice.memo : "";
- payments.push(blinded.txid);
- payment = payment.get({ plain: true });
- payment.account = account.get({ plain: true });
+ let payment = await db.Payment.create(
+ {
+ account_id: account.id,
+ user_id: user.id,
+ hash: blinded.txid,
+ amount: value - tip,
+ currency,
+ memo,
+ rate,
+ received: true,
+ tip,
+ confirmed,
+ address,
+ network: "liquid"
+ },
+ { transaction }
+ );
- emit(user.username, "payment", payment);
- emit(user.username, "account", payment.account);
- l.info("liquid detected", address, user.username, asset, value);
- notify(user, `${value} SAT payment detected`);
+ payments.push(blinded.txid);
+ payment = payment.get({ plain: true });
+ payment.account = account.get({ plain: true });
+
+ emit(user.username, "payment", payment);
+ emit(user.username, "account", payment.account);
+ l.info("liquid detected", address, user.username, asset, value);
+ notify(user, `${value} SAT payment detected`);
+ });
}
} catch (e) {
l.error("Problem processing transaction", e.message, e.stack);
@@ -279,10 +296,15 @@ setInterval(async () => {
if (p) {
let total = p.amount + p.tip;
p.confirmed = 1;
- p.account.balance += total;
- p.account.pending -= Math.min(p.account.pending, total);
-
await p.account.save({ transaction });
+
+ await p.account.increment({ balance: total }, { transaction });
+ await p.account.decrement(
+ { pending: Math.min(p.account.pending, total) },
+ { transaction }
+ );
+ await p.account.reload({ transaction });
+
await p.save({ transaction });
p = p.get({ plain: true });
diff --git a/routes/liquid/send.js b/routes/liquid/send.js
index a04bc007..04b90814 100644
--- a/routes/liquid/send.js
+++ b/routes/liquid/send.js
@@ -54,13 +54,16 @@ module.exports = ah(async (req, res) => {
where: {
asset: nonbtc[0],
user_id: null
- }
+ },
+ lock: transaction.LOCK.UPDATE,
+ transaction
});
if (faucet) {
covered = faucet.balance;
if (covered > fee) covered = fee;
- faucet.balance -= covered;
+ await faucet.decrement({ balance: covered }, { transaction });
+ await faucet.reload({ transaction });
await faucet.save({ transaction });
}
@@ -88,12 +91,14 @@ module.exports = ah(async (req, res) => {
balance: account.balance
});
throw new Error(
- `Insufficient funds, need ${total} ${account.ticker === 'BTC' ? 'SAT' : account.ticker}, have ${account.balance}`
+ `Insufficient funds, need ${total} ${
+ account.ticker === "BTC" ? "SAT" : account.ticker
+ }, have ${account.balance}`
);
}
- account.balance -= total;
- await account.save({ transaction });
+ await account.decrement({ balance: total }, { transaction });
+ await account.reload({ transaction });
let payment = {
amount: -amount,
@@ -113,14 +118,7 @@ module.exports = ah(async (req, res) => {
payments.push(payment);
}
}
- });
- } catch (e) {
- l.error("problem creating liquid payment", user.username, e.message);
- return res.status(500).send(e.message);
- }
- try {
- await db.transaction(async transaction => {
if (config.liquid.walletpass)
await lq.walletPassphrase(config.liquid.walletpass, 300);
diff --git a/routes/lnurl.js b/routes/lnurl.js
index f47cf6f4..f6b066ff 100644
--- a/routes/lnurl.js
+++ b/routes/lnurl.js
@@ -395,8 +395,8 @@ lnurlServer.bindToHook(
if (p) {
l.info("found payment", pr);
payment.fee = p.fee;
- account.balance -= p.fee;
- await account.save();
+ await account.decrement({ balance: p.fee }, { transaction });
+ await account.reload({ transaction });
await payment.save();
payment = payment.get({ plain: true });
payment.account = account.get({ plain: true });
@@ -410,8 +410,8 @@ lnurlServer.bindToHook(
}
}, 1000);
- account.balance -= amount;
- await account.save({ transaction });
+ await account.decrement({ balance: amount }, { transaction });
+ await account.reload({ transaction });
let p = payment.get({ plain: true });
p.account = account.get({ plain: true });
diff --git a/routes/send.js b/routes/send.js
index 8a183183..8e5e527b 100644
--- a/routes/send.js
+++ b/routes/send.js
@@ -2,7 +2,6 @@ const axios = require("axios");
const uuidv4 = require("uuid/v4");
module.exports = ah(async (req, res, next) => {
- return res.status(500).send("Internal payments are temporarily disabled");
let { amount, asset, memo, username } = req.body;
let { user } = req;
amount = parseInt(amount);
@@ -84,12 +83,11 @@ module.exports = ah(async (req, res, next) => {
if (recipient.account.asset === asset && !recipient.account.pubkey)
({ account: a2 } = recipient);
else {
- a2 = await db.Account.findOne(
- {
- where: acc
- },
- { transaction }
- );
+ a2 = await db.Account.findOne({
+ where: acc,
+ lock: transaction.LOCK.UPDATE,
+ transaction
+ });
}
if (a2) {
@@ -106,16 +104,15 @@ module.exports = ah(async (req, res, next) => {
if (assets[asset]) {
({ domain, ticker, precision, name } = assets[asset]);
} else {
- const existing = await db.Account.findOne(
- {
- where: {
- asset
- },
- order: [["id", "ASC"]],
- limit: 1
+ const existing = await db.Account.findOne({
+ where: {
+ asset
},
- { transaction }
- );
+ order: [["id", "ASC"]],
+ limit: 1,
+ lock: transaction.LOCK.UPDATE,
+ transaction
+ });
if (existing) {
({ domain, ticker, precision, name } = existing);
diff --git a/routes/users.js b/routes/users.js
index c9d60585..41fdb154 100644
--- a/routes/users.js
+++ b/routes/users.js
@@ -508,14 +508,14 @@ app.post(
} = req.body;
try {
- let account = await db.Account.findOne({
- where: { id, user_id: user.id }
- });
-
- if (!account || (account.pubkey && !pubkey)) throw new Error("Problem updating account");
+ await db.transaction(async transaction => {
+ let account = await db.Account.findOne({
+ where: { id, user_id: user.id },
+ lock: transaction.LOCK.UPDATE,
+ transaction
+ });
- await db.Account.update(
- {
+ let params = {
name,
ticker,
precision,
@@ -526,18 +526,19 @@ app.post(
hide,
index,
privkey
- },
- {
- where: { id, user_id: user.id }
- }
- );
+ };
- account = await db.Account.findOne({
- where: { id }
- });
+ if (!account || (account.pubkey && !pubkey)) delete params.pubkey;
- emit(user.username, "account", account);
- res.end();
+ await db.Account.update(params, {
+ where: { id, user_id: user.id },
+ transaction
+ });
+
+ await account.reload({ transaction });
+ emit(user.username, "account", account);
+ res.end();
+ });
} catch (e) {
l.error(e.message);
return res.status(500).send("There was a problem updating the account");
@@ -676,8 +677,8 @@ app.post(
{ transaction }
);
- account.balance -= source.amount;
- await account.save();
+ await account.decrement({ balance: source.amount }, { transaction });
+ await account.reload({ transaction });
payment = payment.get({ plain: true });
payment.account = account.get({ plain: true });
@@ -711,3 +712,37 @@ app.post(
res.send(await bcrypt.compare(password, user.password));
})
);
+
+app.get(
+ "/isInternal",
+ auth,
+ ah(async function(req, res) {
+ let { user } = req;
+ let { address } = req.query;
+ if (!address) throw new Error("Address not provided");
+
+ let invoice = await db.Invoice.findOne({
+ where: { address },
+ include: {
+ attributes: ["username"],
+ model: db.User,
+ as: "user"
+ }
+ });
+
+ if (invoice) {
+ let info;
+ try {
+ info = await bc.getAddressInfo(address);
+ } catch (e) {
+ info = await lq.getAddressInfo(address);
+ }
+ if (info.ismine) {
+ emit(user.username, "to", invoice.user);
+ return res.send(true);
+ }
+ }
+
+ res.send(false);
+ })
+);