From 7362d0d4b7d7b7ee8e2eb03b5c950c900715d73a Mon Sep 17 00:00:00 2001 From: sairajzero Date: Fri, 24 Sep 2021 03:35:04 +0530 Subject: [PATCH] Fixed various bugs in server scripts --- src/app.js | 12 +++-- src/market.js | 125 ++++++++++++++++++++++--------------------------- src/request.js | 22 +++++---- 3 files changed, 77 insertions(+), 82 deletions(-) diff --git a/src/app.js b/src/app.js index 7e3b8e4..f491ffd 100644 --- a/src/app.js +++ b/src/app.js @@ -3,6 +3,8 @@ const cookieParser = require("cookie-parser"); const sessions = require('express-session'); const Request = require('./request'); +const REFRESH_INTERVAL = 60 * 1000; //1 min + module.exports = function App(secret, DB) { const app = express(); @@ -55,11 +57,13 @@ module.exports = function App(secret, DB) { app.get('/account', Request.Account); //withdraw and deposit request - app.post('deposit-flo', Request.DepositFLO); - app.post('withdraw-flo', Request.WithdrawFLO); - app.post('deposit-rupee', Request.DepositRupee); - app.post('withdraw-rupee', Request.WithdrawRupee); + app.post('/deposit-flo', Request.DepositFLO); + app.post('/withdraw-flo', Request.WithdrawFLO); + app.post('/deposit-rupee', Request.DepositRupee); + app.post('/withdraw-rupee', Request.WithdrawRupee); Request.DB = DB; + Request.periodicProcess(); + let refresher = setInterval(Request.periodicProcess, REFRESH_INTERVAL); return app; } \ No newline at end of file diff --git a/src/market.js b/src/market.js index e132b2c..3209a88 100644 --- a/src/market.js +++ b/src/market.js @@ -2,7 +2,6 @@ const floGlobals = require("./floGlobals"); var net_FLO_price; //container for FLO price (from API or by model) var DB; //container for database -const REFRESH_INTERVAL = 60 * 1000; //1 min const tokenAPI = { fetch_api: function(apicall) { @@ -101,11 +100,11 @@ function addSellOrder(floID, quantity, min_price) { return reject(INVALID(`Invalid quantity (${quantity})`)); else if (typeof min_price !== "number" || min_price <= 0) return reject(INVALID(`Invalid min_price (${min_price})`)); - DB.query("SELECT SUM(quantity) as total FROM Vault WHERE floID=?", [floID]).then(result => { + DB.query("SELECT SUM(quantity) AS total FROM Vault WHERE floID=?", [floID]).then(result => { let total = result.pop()["total"] || 0; if (total < quantity) return reject(INVALID("Insufficient FLO")); - DB.query("SELECT SUM(quantity) as locked FROM SellOrder WHERE floID=?", [floID]).then(result => { + DB.query("SELECT SUM(quantity) AS locked FROM SellOrder WHERE floID=?", [floID]).then(result => { let locked = result.pop()["locked"] || 0; let available = total - locked; console.debug(total, locked, available); @@ -133,7 +132,7 @@ function addBuyOrder(floID, quantity, max_price) { let total = result.pop()["rupeeBalance"]; if (total < quantity * max_price) return reject(INVALID("Insufficient Rupee balance")); - DB.query("SELECT SUM(maxPrice * quantity) as locked FROM BuyOrder WHERE floID=?", [floID]).then(result => { + DB.query("SELECT SUM(maxPrice * quantity) AS locked FROM BuyOrder WHERE floID=?", [floID]).then(result => { let locked = result.pop()["locked"] || 0; let available = total - locked; console.debug(total, locked, available); @@ -206,7 +205,7 @@ function getBestBuyer(cur_price, n = 0) { let buyOrder = result.shift(); if (!buyOrder) return reject("No valid buyers available"); - DB.query("SELECT rupeeBalance as bal FROM Users WHERE floID=?", [buyOrder.floID]).then(result => { + DB.query("SELECT rupeeBalance AS bal FROM Users WHERE floID=?", [buyOrder.floID]).then(result => { if (result[0].bal < cur_price * buyOrder.quantity) { //This should not happen unless a buy order is placed when user doesnt have enough rupee balance console.warn(`Buy order ${buyOrder.id} is active, but rupee# is insufficient`); @@ -343,7 +342,7 @@ function getAccountDetails(floID) { }); } -function depositCoins(floID, txid) { +function depositFLO(floID, txid) { return new Promise((resolve, reject) => { DB.query("SELECT status FROM inputFLO WHERE txid=? AND floID=?", [txid, floID]).then(result => { if (result.length) { @@ -368,15 +367,14 @@ function confirmDepositFLO() { results.forEach(req => { confirmDepositFLO.checkTx(req.floID, req.txid).then(amount => { let txQueries = []; - txQueries.push("INSERT INTO Vault(floID, quantity) VALUES (?, ?)", [floID, amount]); - txQueries.push("UPDATE inputFLO SET status=?, amount=? WHERE id=?", ["SUCCESS", amount, req.id]); + txQueries.push(["INSERT INTO Vault(floID, quantity) VALUES (?, ?)", [req.floID, amount]]); + txQueries.push(["UPDATE inputFLO SET status=?, amount=? WHERE id=?", ["SUCCESS", amount, req.id]]); DB.transaction(txQueries) - .then(result => console.debug("FLO deposited for ", floID)) + .then(result => console.debug("FLO deposited for ", req.floID)) .catch(error => console.error(error)) }).catch(error => { - if (!Array.isArray(error)) - console.error(error); - else if (error[0]) + console.error(error); + if (error[0]) DB.query("UPDATE inputFLO SET status=? WHERE id=?", ["REJECTED", req.id]) .then(_ => null).catch(error => console.error(error)); }); @@ -386,9 +384,9 @@ function confirmDepositFLO() { confirmDepositFLO.checkTx = function(sender, txid) { return new Promise((resolve, reject) => { - const receiver = globals.myFloID; //receiver should be market's floID (ie, adminID) + const receiver = global.myFloID; //receiver should be market's floID (ie, adminID) floBlockchainAPI.getTx(txid).then(tx => { - let vin_sender = tx.vin.filter(v => v.addr === sender).length + let vin_sender = tx.vin.filter(v => v.addr === sender) if (!vin_sender.length) return reject([true, "Transaction not sent by the sender"]); if (vin_sender.length !== tx.vin.length) @@ -397,7 +395,7 @@ confirmDepositFLO.checkTx = function(sender, txid) { return reject([false, "Transaction not included in any block yet"]); if (!tx.confirmations) return reject([false, "Transaction not confirmed yet"]); - let amount = tx.vout.reduce(v => v.scriptPubKey.addresses[0] === receiver ? a + v.value : a, 0); + let amount = tx.vout.reduce((a, v) => v.scriptPubKey.addresses[0] === receiver ? a + v.value : a, 0); if (amount == 0) return reject([true, "Transaction receiver is not market ID"]); else @@ -406,17 +404,17 @@ confirmDepositFLO.checkTx = function(sender, txid) { }) } -function withdrawCoins(floID, amount) { +function withdrawFLO(floID, amount) { return new Promise((resolve, reject) => { if (!floID || !floCrypto.validateAddr(floID)) return reject(INVALID("Invalid FLO ID")); else if (typeof amount !== "number" || amount <= 0) return reject(INVALID(`Invalid amount (${amount})`)); - DB.query("SELECT SUM(quantity) as total FROM Vault WHERE floID=?", [floID]).then(result => { + DB.query("SELECT SUM(quantity) AS total FROM Vault WHERE floID=?", [floID]).then(result => { let total = result.pop()["total"] || 0; if (total < amount) return reject(INVALID("Insufficient FLO")); - DB.query("SELECT SUM(quantity) as locked FROM SellOrder WHERE floID=?", [floID]).then(result => { + DB.query("SELECT SUM(quantity) AS locked FROM SellOrder WHERE floID=?", [floID]).then(result => { let locked = result.pop()["locked"] || 0; let available = total - locked; if (available < amount) @@ -430,23 +428,23 @@ function withdrawCoins(floID, amount) { rem = 0; } else { txQueries.push(["DELETE FROM Vault WHERE id=?", [coins[i].id]]); - rem -= result[i].quantity; + rem -= coins[i].quantity; } } - if (rem > 0) //should not happen as the total and net is checked already + if (rem > 0) //should not happen AS the total and net is checked already return reject(INVALID("Insufficient FLO")); DB.transaction(txQueries).then(result => { //Send FLO to user via blockchain API - floBlockchainAPI.sendTx(global.myFloID, floID, amount, global.myPrivKey, 'Withdraw FLO Coins from Market').then(result => { - let txid = result.txid.result; - if (!txid || result.txid.error) + floBlockchainAPI.sendTx(global.myFloID, floID, amount, global.myPrivKey, 'Withdraw FLO Coins from Market').then(txid => { + if (!txid) throw Error("Transaction not successful"); //Transaction was successful, Add in DB DB.query("INSERT INTO outputFLO (floID, amount, txid, status) VALUES (?, ?, ?, ?)", [floID, amount, txid, "WAITING_CONFIRMATION"]) .then(_ => null).catch(error => console.error(error)) .finally(_ => resolve("Withdrawal was successful")); }).catch(error => { - DB.query("INSERT INTO outputFLO (floID, amount, txid, status) VALUES (?, ?, ?, ?)", [floID, amount, txid, "PENDING"]) + console.debug(error); + DB.query("INSERT INTO outputFLO (floID, amount, status) VALUES (?, ?, ?)", [floID, amount, "PENDING"]) .then(_ => null).catch(error => console.error(error)) .finally(_ => resolve("Withdrawal request is in process")); }); @@ -460,12 +458,9 @@ function withdrawCoins(floID, amount) { function retryWithdrawalFLO() { DB.query("SELECT id, floID, amount FROM outputFLO WHERE status=?", ["PENDING"]).then(results => { results.forEach(req => { - floBlockchainAPI.sendTx(global.myFloID, req.floID, req.amount, global.myPrivKey, 'Withdraw FLO Coins from Market').then(result => { - let txid = result.txid.result; - if (!txid || result.txid.error) { - console.error(result); - return; - } + floBlockchainAPI.sendTx(global.myFloID, req.floID, req.amount, global.myPrivKey, 'Withdraw FLO Coins from Market').then(txid => { + if (!txid) + throw Error("Transaction not successful"); //Transaction was successful, Add in DB DB.query("UPDATE outputFLO SET status=? WHERE id=?", ["WAITING_CONFIRMATION", req.id]) .then(_ => null).catch(error => console.error(error)); @@ -488,7 +483,7 @@ function confirmWithdrawalFLO() { }).catch(error => console.error(error)); } -function depositTokens(floID, txid) { +function depositRupee(floID, txid) { return new Promise((resolve, reject) => { DB.query("SELECT status FROM inputRupee WHERE txid=? AND floID=?", [txid, floID]).then(result => { if (result.length) { @@ -512,25 +507,24 @@ function confirmDepositRupee() { DB.query("SELECT id, floID, txid FROM inputRupee WHERE status=?", ["PENDING"]).then(results => { results.forEach(req => { confirmDepositRupee.checkTx(req.floID, req.txid).then(amounts => { - DB.query("SELECT id FROM inputFLO where floID=? AND txid=?").then(result => { + DB.query("SELECT id FROM inputFLO where floID=? AND txid=?", [req.floID, req.txid]).then(result => { let txQueries = [], amount_rupee = amounts[0]; //Add the FLO balance if necessary if (!result.length) { let amount_flo = amounts[1]; - txQueries.push("INSERT INTO Vault(floID, quantity) VALUES (?, ?)", [req.floID, amount_flo]); - txQueries.push("INSERT INTO inputFLO(txid, floID, status) VALUES (?, ?, ?)", [req.txid, req.floID, "SUCCESS"]); + txQueries.push(["INSERT INTO Vault(floID, quantity) VALUES (?, ?)", [req.floID, amount_flo]]); + txQueries.push(["INSERT INTO inputFLO(txid, floID, amount, status) VALUES (?, ?, ?)", [req.txid, req.floID, amount_flo, "SUCCESS"]]); } - txQueries.push("UPDATE inputRupee SET status=? WHERE id=?", ["SUCCESS", req.id]); - txQueries.push("UPDATE Users SET rupeeBalance=rupeeBalance+? WHERE floID=?", [amount_rupee, req.floID]); + txQueries.push(["UPDATE inputRupee SET status=? WHERE id=?", ["SUCCESS", req.id]]); + txQueries.push(["UPDATE Users SET rupeeBalance=rupeeBalance+? WHERE floID=?", [amount_rupee, req.floID]]); DB.transaction(txQueries) .then(result => console.debug("Rupee deposited for ", req.floID)) .catch(error => console.error(error)); - }).catch(error => reject(error)); + }).catch(error => console.error(error)); }).catch(error => { - if (!Array.isArray(error)) - console.error(error); - else if (error[0]) + console.error(error); + if (error[0]) DB.query("UPDATE inputRupee SET status=? WHERE id=?", ["REJECTED", req.id]) .then(_ => null).catch(error => console.error(error)); }); @@ -540,7 +534,7 @@ function confirmDepositRupee() { confirmDepositRupee.checkTx = function(sender, txid) { return new Promise((resolve, reject) => { - const receiver = globals.myFloID; //receiver should be market's floID (ie, adminID) + const receiver = global.myFloID; //receiver should be market's floID (ie, adminID) tokenAPI.getTx(txid).then(tx => { if (tx.parsedFloData.type !== "transfer") return reject([true, "Transaction type not 'transfer'"]); @@ -549,10 +543,10 @@ confirmDepositRupee.checkTx = function(sender, txid) { else if (tx.parsedFloData.tokenIdentification !== "rupee") return reject([true, "Transaction token is not 'rupee'"]); var amount_rupee = tx.parsedFloData.tokenAmount; - let vin_sender = tx.transactionDetails.vin.filter(v => v.addr === sender).length + let vin_sender = tx.transactionDetails.vin.filter(v => v.addr === sender) if (!vin_sender.length) return reject([true, "Transaction not sent by the sender"]); - let amount_flo = tx.transactionDetails.vout.reduce(v => v.scriptPubKey.addresses[0] === receiver ? a + v.value : a, 0); + let amount_flo = tx.transactionDetails.vout.reduce((a, v) => v.scriptPubKey.addresses[0] === receiver ? a + v.value : a, 0); if (amount_flo == 0) return reject([true, "Transaction receiver is not market ID"]); else @@ -561,21 +555,22 @@ confirmDepositRupee.checkTx = function(sender, txid) { }) } -function withdrawTokens(floID, amount) { +function withdrawRupee(floID, amount) { return new Promise((resolve, reject) => { if (!floID || !floCrypto.validateAddr(floID)) return reject(INVALID("Invalid FLO ID")); else if (typeof amount !== "number" || amount <= 0) return reject(INVALID(`Invalid amount (${amount})`)); - DB.query("SELECT SUM(quantity) as total FROM Vault WHERE floID=?", [floID]).then(result => { + DB.query("SELECT SUM(quantity) AS total FROM Vault WHERE floID=?", [floID]).then(result => { let required_flo = floGlobals.sendAmt + floGlobals.fee, total = result.pop()["total"] || 0; if (total < required_flo) return reject(INVALID(`Insufficient FLO! Required ${required_flo} FLO to withdraw tokens`)); - DB.query("SELECT SUM(quantity) as locked FROM SellOrder WHERE floID=?", [floID]).then(result => { + DB.query("SELECT SUM(quantity) AS locked FROM SellOrder WHERE floID=?", [floID]).then(result => { let locked = result.pop()["locked"] || 0; let available = total - locked; - if (available < amount) + console.debug(total, locked, available); + if (available < required_flo) return reject(INVALID(`Insufficient FLO (Some FLO are locked in sell orders)! Required ${required_flo} FLO to withdraw tokens`)); DB.query("SELECT rupeeBalance FROM Users WHERE floID=?", [floID]).then(result => { if (result.length < 1) @@ -594,22 +589,22 @@ function withdrawTokens(floID, amount) { rem -= result[i].quantity; } } - if (rem > 0) //should not happen as the total and net is checked already + if (rem > 0) //should not happen AS the total and net is checked already return reject(INVALID("Insufficient FLO")); txQueries.push(["UPDATE Users SET rupeeBalance=rupeeBalance-? WHERE floID=?", [amount, floID]]); DB.transaction(txQueries).then(result => { //Send FLO to user via blockchain API - tokenAPI.sendToken(global.myPrivKey, amount, '(withdrawal from market)', floID).then(result => { - let txid = result.txid.result; - if (!txid || result.txid.error) + tokenAPI.sendToken(global.myPrivKey, amount, '(withdrawal from market)', floID).then(txid => { + if (!txid) throw Error("Transaction not successful"); //Transaction was successful, Add in DB DB.query("INSERT INTO outputRupee (floID, amount, txid, status) VALUES (?, ?, ?, ?)", [floID, amount, txid, "WAITING_CONFIRMATION"]) .then(_ => null).catch(error => console.error(error)) .finally(_ => resolve("Withdrawal was successful")); }).catch(error => { - DB.query("INSERT INTO outputRupee (floID, amount, txid, status) VALUES (?, ?, ?, ?)", [floID, amount, txid, "PENDING"]) + console.debug(error); + DB.query("INSERT INTO outputRupee (floID, amount, status) VALUES (?, ?, ?)", [floID, amount, "PENDING"]) .then(_ => null).catch(error => console.error(error)) .finally(_ => resolve("Withdrawal request is in process")); }); @@ -624,14 +619,11 @@ function withdrawTokens(floID, amount) { function retryWithdrawalRupee() { DB.query("SELECT id, floID, amount FROM outputRupee WHERE status=?", ["PENDING"]).then(results => { results.forEach(req => { - tokenAPI.sendToken(global.myPrivKey, req.amount, '(withdrawal from market)', req.floID).then(result => { - let txid = result.txid.result; - if (!txid || result.txid.error) { - console.error(result); - return; - } + tokenAPI.sendToken(global.myPrivKey, req.amount, '(withdrawal from market)', req.floID).then(txid => { + if (!txid) + throw Error("Transaction not successful"); //Transaction was successful, Add in DB - DB.query("UPDATE outputRupee SET status=? WHERE id=?", ["WAITING_CONFIRMATION", req.id]) + DB.query("UPDATE outputRupee SET status=?, txid=? WHERE id=?", ["WAITING_CONFIRMATION", txid, req.id]) .then(_ => null).catch(error => console.error(error)); }).catch(error => console.error(error)); }); @@ -650,7 +642,7 @@ function confirmWithdrawalRupee() { }).catch(error => console.error(error)); } -function intervalFunction() { +function periodicProcess() { let old_rate = net_FLO_price; getRates().then(cur_rate => { transactionReCheck(); @@ -667,19 +659,16 @@ function transactionReCheck() { confirmWithdrawalRupee(); } -intervalFunction(); - -let refresher = setInterval(intervalFunction, REFRESH_INTERVAL); - module.exports = { addBuyOrder, addSellOrder, cancelOrder, getAccountDetails, - depositCoins, - withdrawCoins, - depositTokens, - withdrawTokens, + depositFLO, + withdrawFLO, + depositRupee, + withdrawRupee, + periodicProcess, set DB(db) { DB = db; } diff --git a/src/request.js b/src/request.js index a687ff8..e48fa04 100644 --- a/src/request.js +++ b/src/request.js @@ -21,10 +21,10 @@ const maxSessionTimeout = 60 * oneDay; function validateRequestFromFloID(request, sign, floID, proxy = true) { return new Promise((resolve, reject) => { - DB.query("SELECT " + (proxy ? "proxyKey as key FROM Session" : "pubKey as key FROM Users") + " WHERE floID=?", [floID]).then(result => { + DB.query("SELECT " + (proxy ? "proxyKey AS pubKey FROM Sessions" : "pubKey FROM Users") + " WHERE floID=?", [floID]).then(result => { if (result.length < 1) return reject(INVALID(proxy ? "Session not active" : "User not registered")); - let req_str = validateRequest(request, sign, result[0].key); + let req_str = validateRequest(request, sign, result[0].pubKey); req_str instanceof INVALID ? reject(req_str) : resolve(req_str); }).catch(error => reject(error)); }); @@ -87,12 +87,13 @@ function Login(req, res) { proxyKey: data.proxyKey, timestamp: data.timestamp }, data.sign, data.floID, false).then(req_str => { - DB.query("INSERT INTO Session (floID, session_id, proxyKey) VALUES (?, ?, ?) " + + DB.query("INSERT INTO Sessions (floID, session_id, proxyKey) VALUES (?, ?, ?) " + "ON DUPLICATE KEY UPDATE session_id=?, session_time=DEFAULT, proxyKey=?", [data.floID, req.sessionID, data.proxyKey, req.sessionID, data.proxyKey]).then(_ => { if (data.saveSession) session.cookie.maxAge = maxSessionTimeout; session.user_id = data.floID; + storeRequest(data.floID, req_str, data.sign); res.send("Login Successful"); }).catch(error => { console.error(error); @@ -103,7 +104,7 @@ function Login(req, res) { res.status(INVALID.e_code).send(error.message); else { console.error(error); - res.status(INTERNAL.e_code).send("Order placement failed! Try again later!"); + res.status(INTERNAL.e_code).send("Login failed! Try again later!"); } }) } @@ -112,7 +113,7 @@ function Logout(req, res) { let session = req.session; if (!session.user_id) return res.status(INVALID.e_code).send("No logged in user found in this session"); - DB.query("DELETE FROM Session WHERE floID=?", [session.user_id]).then(_ => { + DB.query("DELETE FROM Sessions WHERE floID=?", [session.user_id]).then(_ => { session.destroy(); res.send('Logout successful'); }).catch(error => { @@ -253,7 +254,7 @@ function Account(req, res) { if (!req.session.user_id) setLogin("Login required"); else { - DB.query("SELECT session_id, session_time FROM Session WHERE floID=?", [req.session.user_id]).then(result => { + DB.query("SELECT session_id, session_time FROM Sessions WHERE floID=?", [req.session.user_id]).then(result => { if (result.length < 1) { res.status(INVALID.e_code).send("floID not registered"); return; @@ -283,7 +284,7 @@ function DepositFLO(req, res) { txid: data.txid, timestamp: data.timestamp }, data.sign, session.user_id).then(req_str => { - market.depositCoins(session.user_id, data.txid).then(result => { + market.depositFLO(session.user_id, data.txid).then(result => { storeRequest(session.user_id, req_str, data.sign); res.send(result); }).catch(error => { @@ -314,7 +315,7 @@ function WithdrawFLO(req, res) { amount: data.amount, timestamp: data.timestamp }, data.sign, session.user_id).then(req_str => { - market.withdrawCoins(session.user_id, data.amount).then(result => { + market.withdrawFLO(session.user_id, data.amount).then(result => { storeRequest(session.user_id, req_str, data.sign); res.send(result); }).catch(error => { @@ -345,7 +346,7 @@ function DepositRupee(req, res) { txid: data.txid, timestamp: data.timestamp }, data.sign, session.user_id).then(req_str => { - market.depositTokens(session.user_id, data.txid).then(result => { + market.depositRupee(session.user_id, data.txid).then(result => { storeRequest(session.user_id, req_str, data.sign); res.send(result); }).catch(error => { @@ -376,7 +377,7 @@ function WithdrawRupee(req, res) { amount: data.amount, timestamp: data.timestamp }, data.sign, session.user_id).then(req_str => { - market.withdrawTokens(session.user_id, data.amount).then(result => { + market.withdrawRupee(session.user_id, data.amount).then(result => { storeRequest(session.user_id, req_str, data.sign); res.send(result); }).catch(error => { @@ -412,6 +413,7 @@ module.exports = { WithdrawFLO, DepositRupee, WithdrawRupee, + periodicProcess: market.periodicProcess, set DB(db) { DB = db; market.DB = db;