diff --git a/args/schema.sql b/args/schema.sql index e1ab126..71f9057 100644 --- a/args/schema.sql +++ b/args/schema.sql @@ -33,31 +33,21 @@ CREATE TABLE TrustedList( /* User Data */ -CREATE TABLE Users ( - id INT NOT NULL AUTO_INCREMENT, - floID CHAR(34) NOT NULL, - pubKey CHAR(66) NOT NULL, - created DATETIME DEFAULT CURRENT_TIMESTAMP, - KEY(id), - PRIMARY KEY(floID) -); - CREATE TABLE UserSession ( id INT NOT NULL AUTO_INCREMENT, floID CHAR(34) NOT NULL, proxyKey CHAR(66) NOT NULL, session_time DATETIME DEFAULT CURRENT_TIMESTAMP, KEY (id), - PRIMARY KEY(floID), - FOREIGN KEY (floID) REFERENCES Users(floID) + PRIMARY KEY(floID) ); CREATE TABLE Cash ( id INT NOT NULL AUTO_INCREMENT, floID CHAR(34) NOT NULL UNIQUE, balance DECIMAL(12, 2) DEFAULT 0.00, - PRIMARY KEY(id), - FOREIGN KEY (floID) REFERENCES Users(floID) + KEY(id), + PRIMARY KEY(floID) ); CREATE TABLE Vault ( @@ -68,7 +58,6 @@ CREATE TABLE Vault ( base DECIMAL(10, 2), quantity FLOAT NOT NULL, PRIMARY KEY(id), - FOREIGN KEY (floID) REFERENCES Users(floID), FOREIGN KEY (asset) REFERENCES AssetList(asset) ); @@ -78,7 +67,6 @@ CREATE TABLE UserTag ( tag VARCHAR(50) NOT NULL, PRIMARY KEY(floID, tag), KEY (id), - FOREIGN KEY (floID) REFERENCES Users(floID), FOREIGN KEY (tag) REFERENCES TagList(tag) ); @@ -89,9 +77,10 @@ CREATE TABLE RequestLog( floID CHAR(34) NOT NULL, request TEXT NOT NULL, sign TEXT NOT NULL, + proxy BOOLEAN NOT NULL, request_time DATETIME DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY(id), - FOREIGN KEY (floID) REFERENCES Users(floID) + UNIQUE (sign) ); CREATE TABLE SellOrder ( @@ -102,7 +91,6 @@ CREATE TABLE SellOrder ( minPrice DECIMAL(10, 2) NOT NULL, time_placed DATETIME DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY(id), - FOREIGN KEY (floID) REFERENCES Users(floID), FOREIGN KEY (asset) REFERENCES AssetList(asset) ); @@ -114,7 +102,6 @@ CREATE TABLE BuyOrder ( maxPrice DECIMAL(10, 2) NOT NULL, time_placed DATETIME DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY(id), - FOREIGN KEY (floID) REFERENCES Users(floID), FOREIGN KEY (asset) REFERENCES AssetList(asset) ); @@ -124,8 +111,7 @@ CREATE TABLE InputFLO ( floID CHAR(34) NOT NULL, amount FLOAT, status VARCHAR(50) NOT NULL, - PRIMARY KEY(id), - FOREIGN KEY (floID) REFERENCES Users(floID) + PRIMARY KEY(id) ); CREATE TABLE OutputFLO ( @@ -134,8 +120,7 @@ CREATE TABLE OutputFLO ( floID CHAR(34) NOT NULL, amount FLOAT NOT NULL, status VARCHAR(50) NOT NULL, - PRIMARY KEY(id), - FOREIGN KEY (floID) REFERENCES Users(floID) + PRIMARY KEY(id) ); CREATE TABLE InputToken ( @@ -145,8 +130,7 @@ CREATE TABLE InputToken ( token VARCHAR(64), amount FLOAT, status VARCHAR(50) NOT NULL, - PRIMARY KEY(id), - FOREIGN KEY (floID) REFERENCES Users(floID) + PRIMARY KEY(id) ); CREATE TABLE OutputToken ( @@ -156,8 +140,7 @@ CREATE TABLE OutputToken ( token VARCHAR(64), amount FLOAT NOT NULL, status VARCHAR(50) NOT NULL, - PRIMARY KEY(id), - FOREIGN KEY (floID) REFERENCES Users(floID) + PRIMARY KEY(id) ); /* Transaction Data */ @@ -180,9 +163,7 @@ CREATE TABLE TransferTransactions ( tx_time DATETIME DEFAULT CURRENT_TIMESTAMP, txid VARCHAR(66) NOT NULL, KEY(id), - PRIMARY KEY(txid), - FOREIGN KEY (sender) REFERENCES Users(floID), - FOREIGN KEY (receiver) REFERENCES Users(floID) + PRIMARY KEY(txid) ); CREATE TABLE TradeTransactions ( @@ -196,8 +177,6 @@ CREATE TABLE TradeTransactions ( txid VARCHAR(66) NOT NULL, KEY(id), PRIMARY KEY(txid), - FOREIGN KEY (buyer) REFERENCES Users(floID), - FOREIGN KEY (seller) REFERENCES Users(floID), FOREIGN KEY (asset) REFERENCES AssetList(asset) ); @@ -219,8 +198,6 @@ CREATE TABLE AuditTrade( buyer_old_cash FLOAT NOT NULL, buyer_new_cash FLOAT NOT NULL, PRIMARY KEY(id), - FOREIGN KEY (sellerID) REFERENCES Users(floID), - FOREIGN KEY (buyerID) REFERENCES Users(floID), FOREIGN KEY (asset) REFERENCES AssetList(asset) ); @@ -249,13 +226,6 @@ CREATE TABLE sinkShares( PRIMARY KEY(floID) ); -CREATE TRIGGER Users_I AFTER INSERT ON Users -FOR EACH ROW INSERT INTO _backup (t_name, id) VALUES ('Users', NEW.id) ON DUPLICATE KEY UPDATE mode=TRUE, timestamp=DEFAULT; -CREATE TRIGGER Users_U AFTER UPDATE ON Users -FOR EACH ROW INSERT INTO _backup (t_name, id) VALUES ('Users', NEW.id) ON DUPLICATE KEY UPDATE mode=TRUE, timestamp=DEFAULT; -CREATE TRIGGER Users_D AFTER DELETE ON Users -FOR EACH ROW INSERT INTO _backup (t_name, id) VALUES ('Users', OLD.id) ON DUPLICATE KEY UPDATE mode=NULL, timestamp=DEFAULT; - CREATE TRIGGER RequestLog_I AFTER INSERT ON RequestLog FOR EACH ROW INSERT INTO _backup (t_name, id) VALUES ('RequestLog', NEW.id) ON DUPLICATE KEY UPDATE mode=TRUE, timestamp=DEFAULT; CREATE TRIGGER RequestLog_U AFTER UPDATE ON RequestLog diff --git a/args/truncateAll.sql b/args/truncateAll.sql index 4ded68d..a0a546a 100644 --- a/args/truncateAll.sql +++ b/args/truncateAll.sql @@ -15,7 +15,6 @@ TRUNCATE UserTag; TRUNCATE TransferTransactions; TRUNCATE TradeTransactions; TRUNCATE Vault; -DELETE FROM Users; /* Blockchain data */ TRUNCATE LastTx; diff --git a/public/api.js b/public/api.js index eb44495..1d0d388 100644 --- a/public/api.js +++ b/public/api.js @@ -102,6 +102,8 @@ function getAccount(floID, proxySecret) { floID: floID, timestamp: Date.now() }; + if (floCrypto.getFloID(proxySecret) === floID) //Direct signing (without proxy) + request.pubKey = floCrypto.getPubKeyHex(proxySecret); request.sign = signRequest({ type: "get_account", timestamp: request.timestamp @@ -173,11 +175,11 @@ function getTx(txid) { }) } -function signRequest(request, privKey) { +function signRequest(request, signKey) { if (typeof request !== "object") throw Error("Request is not an object"); let req_str = Object.keys(request).sort().map(r => r + ":" + request[r]).join("|"); - return floCrypto.signData(req_str, privKey); + return floCrypto.signData(req_str, signKey); } function getLoginCode() { @@ -190,6 +192,7 @@ function getLoginCode() { }) } +/* function signUp(privKey, code, hash) { return new Promise((resolve, reject) => { if (!code || !hash) @@ -220,6 +223,7 @@ function signUp(privKey, code, hash) { .catch(error => reject(error)); }); } +*/ function login(privKey, proxyKey, code, hash) { return new Promise((resolve, reject) => { @@ -228,6 +232,7 @@ function login(privKey, proxyKey, code, hash) { let request = { proxyKey: proxyKey, floID: floCrypto.getFloID(privKey), + pubKey: floCrypto.getPubKeyHex(privKey), timestamp: Date.now(), code: code, hash: hash @@ -261,6 +266,8 @@ function logout(floID, proxySecret) { floID: floID, timestamp: Date.now() }; + if (floCrypto.getFloID(proxySecret) === floID) //Direct signing (without proxy) + request.pubKey = floCrypto.getPubKeyHex(proxySecret); request.sign = signRequest({ type: "logout", timestamp: request.timestamp @@ -293,6 +300,8 @@ function buy(asset, quantity, max_price, floID, proxySecret) { max_price: max_price, timestamp: Date.now() }; + if (floCrypto.getFloID(proxySecret) === floID) //Direct signing (without proxy) + request.pubKey = floCrypto.getPubKeyHex(proxySecret); request.sign = signRequest({ type: "buy_order", asset: asset, @@ -329,6 +338,8 @@ function sell(asset, quantity, min_price, floID, proxySecret) { min_price: min_price, timestamp: Date.now() }; + if (floCrypto.getFloID(proxySecret) === floID) //Direct signing (without proxy) + request.pubKey = floCrypto.getPubKeyHex(proxySecret); request.sign = signRequest({ type: "sell_order", quantity: quantity, @@ -362,6 +373,8 @@ function cancelOrder(type, id, floID, proxySecret) { orderID: id, timestamp: Date.now() }; + if (floCrypto.getFloID(proxySecret) === floID) //Direct signing (without proxy) + request.pubKey = floCrypto.getPubKeyHex(proxySecret); request.sign = signRequest({ type: "cancel_order", order: type, @@ -396,6 +409,8 @@ function transferToken(receiver, token, amount, floID, proxySecret) { amount: amount, timestamp: Date.now() }; + if (floCrypto.getFloID(proxySecret) === floID) //Direct signing (without proxy) + request.pubKey = floCrypto.getPubKeyHex(proxySecret); request.sign = signRequest({ type: "transfer_token", receiver: receiver, @@ -418,7 +433,7 @@ function transferToken(receiver, token, amount, floID, proxySecret) { }) } -function depositFLO(quantity, floID, sinkID, privKey, proxySecret) { +function depositFLO(quantity, floID, sinkID, privKey, proxySecret = null) { return new Promise((resolve, reject) => { if (typeof quantity !== "number" || quantity <= floGlobals.fee) return reject(`Invalid quantity (${quantity})`); @@ -428,11 +443,13 @@ function depositFLO(quantity, floID, sinkID, privKey, proxySecret) { txid: txid, timestamp: Date.now() }; + if (!proxySecret) //Direct signing (without proxy) + request.pubKey = floCrypto.getPubKeyHex(privKey); request.sign = signRequest({ type: "deposit_flo", txid: txid, timestamp: request.timestamp - }, proxySecret); + }, proxySecret || privKey); console.debug(request); exchangeAPI('/deposit-flo', { @@ -456,6 +473,8 @@ function withdrawFLO(quantity, floID, proxySecret) { amount: quantity, timestamp: Date.now() }; + if (floCrypto.getFloID(proxySecret) === floID) //Direct signing (without proxy) + request.pubKey = floCrypto.getPubKeyHex(proxySecret); request.sign = signRequest({ type: "withdraw_flo", amount: quantity, @@ -476,7 +495,7 @@ function withdrawFLO(quantity, floID, proxySecret) { }) } -function depositToken(token, quantity, floID, sinkID, privKey, proxySecret) { +function depositToken(token, quantity, floID, sinkID, privKey, proxySecret = null) { return new Promise((resolve, reject) => { if (!floCrypto.verifyPrivKey(privKey, floID)) return reject("Invalid Private Key"); @@ -486,11 +505,13 @@ function depositToken(token, quantity, floID, sinkID, privKey, proxySecret) { txid: txid, timestamp: Date.now() }; + if (!proxySecret) //Direct signing (without proxy) + request.pubKey = floCrypto.getPubKeyHex(privKey); request.sign = signRequest({ type: "deposit_token", txid: txid, timestamp: request.timestamp - }, proxySecret); + }, proxySecret || privKey); console.debug(request); exchangeAPI('/deposit-token', { @@ -515,6 +536,8 @@ function withdrawToken(token, quantity, floID, proxySecret) { amount: quantity, timestamp: Date.now() }; + if (floCrypto.getFloID(proxySecret) === floID) //Direct signing (without proxy) + request.pubKey = floCrypto.getPubKeyHex(proxySecret); request.sign = signRequest({ type: "withdraw_token", token: token, @@ -544,6 +567,8 @@ function addUserTag(tag_user, tag, floID, proxySecret) { tag: tag, timestamp: Date.now() }; + if (floCrypto.getFloID(proxySecret) === floID) //Direct signing (without proxy) + request.pubKey = floCrypto.getPubKeyHex(proxySecret); request.sign = signRequest({ type: "add_tag", user: tag_user, @@ -573,6 +598,8 @@ function removeUserTag(tag_user, tag, floID, proxySecret) { tag: tag, timestamp: Date.now() }; + if (floCrypto.getFloID(proxySecret) === floID) //Direct signing (without proxy) + request.pubKey = floCrypto.getPubKeyHex(proxySecret); request.sign = signRequest({ type: "remove_tag", user: tag_user, diff --git a/src/_constants.js b/src/_constants.js index ea9eb8c..3fd31c2 100644 --- a/src/_constants.js +++ b/src/_constants.js @@ -4,7 +4,8 @@ module.exports = { PERIOD_INTERVAL: 15 * 60 * 1000 // 15 min }, request: { - MAX_SESSION_TIMEOUT: 60 * 24 * 60 * 60 * 1000, //60 days + SIGN_EXPIRE_TIME: 1 * 60 * 1000, //1 min + MAX_SESSION_TIMEOUT: 30 * 24 * 60 * 60 * 1000, //30 days INVALID_SERVER_MSG: "INCORRECT_SERVER_ERROR" //Should be reflected in public backend script }, market: { diff --git a/src/app.js b/src/app.js index 29adbf2..0d18032 100644 --- a/src/app.js +++ b/src/app.js @@ -53,7 +53,7 @@ module.exports = function App(secret, DB) { app.get('/get-login-code', Request.getLoginCode); //signup request - app.post('/signup', Request.SignUp); + //app.post('/signup', Request.SignUp); Removing need for signup //login request app.post('/login', Request.Login); diff --git a/src/coupling.js b/src/coupling.js index 152e517..1665acd 100644 --- a/src/coupling.js +++ b/src/coupling.js @@ -112,7 +112,7 @@ function processOrders(seller_best, buyer_best, txQueries, quantity, clear_sell) function updateBalance(seller_best, buyer_best, txQueries, asset, cur_price, quantity) { //Update cash balance for seller and buyer let totalAmount = cur_price * quantity; - txQueries.push(["UPDATE Cash SET balance=balance+? WHERE floID=?", [totalAmount, seller_best.floID]]); + txQueries.push(["INSERT INTO Cash (floID, balance) VALUE (?, ?) ON DUPLICATE KEY UPDATE balance=balance+?", [seller_best.floID, totalAmount, totalAmount]]); txQueries.push(["UPDATE Cash SET balance=balance-? WHERE floID=?", [totalAmount, buyer_best.floID]]); //Add coins to Buyer txQueries.push(["INSERT INTO Vault(floID, asset, base, quantity) VALUES (?, ?, ?, ?)", [buyer_best.floID, asset, cur_price, quantity]]) @@ -158,6 +158,11 @@ function auditBalance(sellerID, buyerID, asset) { DB.query("SELECT floID, SUM(quantity) as asset_balance FROM Vault WHERE asset=? AND floID IN (?, ?) GROUP BY floID", [asset, sellerID, buyerID]).then(result => { for (let i in result) balance[result[i].floID].asset = result[i].asset_balance; + //Set them as 0 if undefined or null + balance[sellerID].cash = balance[sellerID].cash || 0; + balance[sellerID].asset = balance[sellerID].asset || 0; + balance[buyerID].cash = balance[buyerID].cash || 0; + balance[buyerID].asset = balance[buyerID].asset || 0; resolve(balance); }).catch(error => reject(error)) }).catch(error => reject(error)) diff --git a/src/group.js b/src/group.js index 18d1087..bdbf5c4 100644 --- a/src/group.js +++ b/src/group.js @@ -364,8 +364,8 @@ function getTopValidBuyOrder(orders, cur_price) { function verifyBuyOrder(buyOrder, cur_price) { return new Promise((resolve, reject) => { - DB.query("SELECT balance AS bal FROM Cash WHERE floID=?", [buyOrder.floID]).then(result => { - if (result[0].bal < cur_price * buyOrder.quantity) { + DB.query("SELECT balance FROM Cash WHERE floID=?", [buyOrder.floID]).then(result => { + if (!result.length || result[0].balance < cur_price * buyOrder.quantity) { //This should not happen unless a buy order is placed when user doesnt have enough cash balance console.warn(`Buy order ${buyOrder.id} is active, but Cash is insufficient`); reject(false); diff --git a/src/market.js b/src/market.js index b616312..42b7da6 100644 --- a/src/market.js +++ b/src/market.js @@ -10,18 +10,9 @@ const { var DB, assetList; //container for database and allowed assets -const checkIfUserRegistered = floID => new Promise((resolve, reject) => { - DB.query("SELECT id FROM Users WHERE floID=?", [floID]).then(result => { - if (result.length) - resolve(result[0].id); - else - reject(INVALID(`User ${floID} not registered`)); - }).catch(error => reject(error)); -}); - const getAssetBalance = (floID, asset) => new Promise((resolve, reject) => { let promises = (asset === floGlobals.currency) ? [ - DB.query("SELECT balance FROM Cash WHERE floID=?", [floID]), + DB.query("SELECT SUM(balance) AS balance FROM Cash WHERE floID=?", [floID]), DB.query("SELECT SUM(quantity*maxPrice) AS locked FROM BuyOrder WHERE floID=?", [floID]) ] : [ DB.query("SELECT SUM(quantity) AS balance FROM Vault WHERE floID=? AND asset=?", [floID, asset]), @@ -164,7 +155,7 @@ function getAccountDetails(floID) { else switch (i) { case 0: - response.cash = a.value[0].balance; + response.cash = a.value.length ? a.value[0].balance : 0; break; case 1: response.vault = a.value; @@ -216,22 +207,20 @@ function transferToken(sender, receiver, token, amount) { else if (token !== floGlobals.currency && !assetList.includes(token)) return reject(INVALID(`Invalid token (${token})`)); getAssetBalance.check(senderID, token, amount).then(_ => { - checkIfUserRegistered(receiver).then(_ => { - consumeAsset(sender, token, amount).then(txQueries => { - if (token === floGlobals.currency) - txQueries.push(["UPDATE Cash SET balance=balance+? WHERE floID=?", [amount, receiver]]); - else - txQueries.push(["INSERT INTO Vault(floID, quantity) VALUES (?, ?)", [receiver, amount]]); - let time = Date.now(); - let hash = TRANSFER_HASH_PREFIX + Crypto.SHA256([time, sender, receiver, token, amount].join("|")); - txQueries.push([ - "INSERT INTO TransferTransactions (sender, receiver, token, amount, tx_time, txid)", - [sender, receiver, token, amount, global.convertDateToString(time), hash] - ]); - DB.transaction(txQueries) - .then(result => resolve(hash)) - .catch(error => reject(error)) - }).catch(error => reject(error)) + consumeAsset(sender, token, amount).then(txQueries => { + if (token === floGlobals.currency) + txQueries.push(["INSERT INTO Cash (floID, balance) VALUE (?, ?) ON DUPLICATE KEY UPDATE balance=balance+?", [receiver, amount, amount]]); + else + txQueries.push(["INSERT INTO Vault(floID, quantity) VALUES (?, ?)", [receiver, amount]]); + let time = Date.now(); + let hash = TRANSFER_HASH_PREFIX + Crypto.SHA256([time, sender, receiver, token, amount].join("|")); + txQueries.push([ + "INSERT INTO TransferTransactions (sender, receiver, token, amount, tx_time, txid)", + [sender, receiver, token, amount, global.convertDateToString(time), hash] + ]); + DB.transaction(txQueries) + .then(result => resolve(hash)) + .catch(error => reject(error)) }).catch(error => reject(error)) }).catch(error => reject(error)) }) @@ -392,7 +381,7 @@ function confirmDepositToken() { } txQueries.push(["UPDATE InputToken SET status=?, token=?, amount=? WHERE id=?", ["SUCCESS", token_name, amount_token, req.id]]); if (token_name === floGlobals.currency) - txQueries.push(["UPDATE Cash SET balance=balance+? WHERE floID=?", [amount_token, req.floID]]); + txQueries.push(["INSERT INTO Cash (floID, balance) VALUE (?, ?) ON DUPLICATE KEY UPDATE balance=balance+?", [req.floID, amount_token, amount_token]]); else txQueries.push(["INSERT INTO Vault(floID, asset, quantity) VALUES (?, ?, ?)", [req.floID, token_name, amount_token]]); DB.transaction(txQueries) diff --git a/src/request.js b/src/request.js index 97cc6cb..734ccdd 100644 --- a/src/request.js +++ b/src/request.js @@ -3,6 +3,7 @@ const market = require("./market"); const { + SIGN_EXPIRE_TIME, MAX_SESSION_TIMEOUT, INVALID_SERVER_MSG } = require("./_constants")["request"]; @@ -25,40 +26,61 @@ INTERNAL.e_code = 500; var serving; -function validateRequestFromFloID(request, sign, floID, proxy = true) { +function validateRequest(request, sign, floID, pubKey) { return new Promise((resolve, reject) => { if (!serving) - return reject(INVALID(INVALID_SERVER_MSG)); + reject(INVALID(INVALID_SERVER_MSG)); + else if (!request.timestamp) + reject(INVALID("Timestamp parameter missing")); + else if (Date.now() - SIGN_EXPIRE_TIME > request.timestamp) + reject(INVALID("Signature Expired")); else if (!floCrypto.validateAddr(floID)) - return reject(INVALID("Invalid floID")); - DB.query("SELECT " + (proxy ? "session_time, proxyKey AS pubKey FROM UserSession" : "pubKey FROM Users") + " WHERE floID=?", [floID]).then(result => { - if (result.length < 1) - return reject(INVALID(proxy ? "Session not active" : "User not registered")); - if (proxy && result[0].session_time + MAX_SESSION_TIMEOUT < Date.now()) - return reject(INVALID("Session Expired! Re-login required")); - let req_str = validateRequest(request, sign, result[0].pubKey); - req_str instanceof INVALID ? reject(req_str) : resolve(req_str); + reject(INVALID("Invalid floID")); + else if (typeof request !== "object") + reject(INVALID("Request is not an object")); + else validateRequest.getSignKey(floID, pubKey).then(signKey => { + let req_str = Object.keys(request).sort().map(r => r + ":" + request[r]).join("|"); + try { + if (!floCrypto.verifySign(req_str, sign, signKey)) + reject(INVALID("Invalid request signature! Re-login if this error occurs frequently")); + else validateRequest.checkIfSignUsed(sign) + .then(result => resolve(req_str)) + .catch(error => reject(error)) + } catch { + reject(INVALID("Corrupted sign/key")); + } }).catch(error => reject(error)); }); } -function validateRequest(request, sign, pubKey) { - if (typeof request !== "object") - return INVALID("Request is not an object"); - let req_str = Object.keys(request).sort().map(r => r + ":" + request[r]).join("|"); - try { - if (floCrypto.verifySign(req_str, sign, pubKey)) - return req_str; - else - return INVALID("Invalid request signature! Re-login if this error occurs frequently"); - } catch { - return INVALID("Corrupted sign/key"); - } -} +validateRequest.getSignKey = (floID, pubKey) => new Promise((resolve, reject) => { + if (!pubKey) + DB.query("SELECT session_time, proxyKey FROM UserSession WHERE floID=?", [floID]).then(result => { + if (result.length < 1) + reject(INVALID("Session not active")); + else if (proxy && result[0].session_time + MAX_SESSION_TIMEOUT < Date.now()) + reject(INVALID("Session Expired! Re-login required")); + else + resolve(result[0].proxyKey); + }).catch(error => reject(error)); + else if (floCrypto.getFloID(pubKey) === floID) + resolve(pubKey); + else + reject(INVALID("Invalid pubKey")); +}); -function storeRequest(floID, req_str, sign) { +validateRequest.checkIfSignUsed = sign => new Promise((resolve, reject) => { + DB.query("SELECT id FROM RequestLog WHERE sign=?", [sign]).then(result => { + if (result.length) + reject(INVALID("Duplicate signature")); + else + resolve(true); + }).catch(error => reject(error)) +}); + +function storeRequest(floID, req_str, sign, proxy = false) { //console.debug(floID, req_str); - DB.query("INSERT INTO RequestLog (floID, request, sign) VALUES (?,?,?)", [floID, req_str, sign]) + DB.query("INSERT INTO RequestLog (floID, request, sign, proxy) VALUES (?,?,?, ?)", [floID, req_str, sign, proxy]) .then(_ => null).catch(error => console.error(error)); } @@ -73,6 +95,7 @@ function getLoginCode(req, res) { }); } +/* function SignUp(req, res) { if (!serving) return res.status(INVALID.e_code).send(INVALID_SERVER_MSG); @@ -81,7 +104,7 @@ function SignUp(req, res) { return res.status(INVALID.e_code).send("Invalid Public Key"); if (!data.code || data.hash != Crypto.SHA1(data.code + secret)) return res.status(INVALID.e_code).send("Invalid Code"); - let req_str = validateRequest({ + let req_str = validateRequest_X({ type: "create_account", random: data.code, timestamp: data.timestamp @@ -103,17 +126,20 @@ function SignUp(req, res) { } }); } +*/ function Login(req, res) { let data = req.body; if (!data.code || data.hash != Crypto.SHA1(data.code + secret)) return res.status(INVALID.e_code).send("Invalid Code"); - validateRequestFromFloID({ + if (!data.pubKey) + return res.status(INVALID.e_code).send("Public key missing"); + validateRequest({ type: "login", random: data.code, proxyKey: data.proxyKey, timestamp: data.timestamp - }, data.sign, data.floID, false).then(req_str => { + }, data.sign, data.floID, data.pubKey).then(req_str => { DB.query("INSERT INTO UserSession (floID, proxyKey) VALUE (?, ?) " + "ON DUPLICATE KEY UPDATE session_time=DEFAULT, proxyKey=?", [data.floID, data.proxyKey, data.proxyKey]).then(_ => { @@ -135,12 +161,12 @@ function Login(req, res) { function Logout(req, res) { let data = req.body; - validateRequestFromFloID({ + validateRequest({ type: "logout", timestamp: data.timestamp - }, data.sign, data.floID).then(req_str => { + }, data.sign, data.floID, data.pubKey).then(req_str => { DB.query("DELETE FROM UserSession WHERE floID=?", [data.floID]).then(_ => { - storeRequest(data.floID, req_str, data.sign); + storeRequest(data.floID, req_str, data.sign, !data.pubKey); res.send('Logout successful'); }).catch(error => { console.error(error); @@ -158,15 +184,15 @@ function Logout(req, res) { function PlaceSellOrder(req, res) { let data = req.body; - validateRequestFromFloID({ + validateRequest({ type: "sell_order", asset: data.asset, quantity: data.quantity, min_price: data.min_price, timestamp: data.timestamp - }, data.sign, data.floID).then(req_str => { + }, data.sign, data.floID, data.pubKey).then(req_str => { market.addSellOrder(data.floID, data.asset, data.quantity, data.min_price).then(result => { - storeRequest(data.floID, req_str, data.sign); + storeRequest(data.floID, req_str, data.sign, !data.pubKey); res.send(result); }).catch(error => { if (error instanceof INVALID) @@ -188,15 +214,15 @@ function PlaceSellOrder(req, res) { function PlaceBuyOrder(req, res) { let data = req.body; - validateRequestFromFloID({ + validateRequest({ type: "buy_order", asset: data.asset, quantity: data.quantity, max_price: data.max_price, timestamp: data.timestamp - }, data.sign, data.floID).then(req_str => { + }, data.sign, data.floID, data.pubKey).then(req_str => { market.addBuyOrder(data.floID, data.asset, data.quantity, data.max_price).then(result => { - storeRequest(data.floID, req_str, data.sign); + storeRequest(data.floID, req_str, data.sign, !data.pubKey); res.send(result); }).catch(error => { if (error instanceof INVALID) @@ -218,14 +244,14 @@ function PlaceBuyOrder(req, res) { function CancelOrder(req, res) { let data = req.body; - validateRequestFromFloID({ + validateRequest({ type: "cancel_order", order: data.orderType, id: data.orderID, timestamp: data.timestamp - }, data.sign, data.floID).then(req_str => { + }, data.sign, data.floID, data.pubKey).then(req_str => { market.cancelOrder(data.orderType, data.orderID, data.floID).then(result => { - storeRequest(data.floID, req_str, data.sign); + storeRequest(data.floID, req_str, data.sign, !data.pubKey); res.send(result); }).catch(error => { if (error instanceof INVALID) @@ -247,15 +273,15 @@ function CancelOrder(req, res) { function TransferToken(req, res) { let data = req.body; - validateRequestFromFloID({ + validateRequest({ type: "transfer_token", receiver: data.receiver, token: data.token, amount: data.amount, timestamp: data.timestamp - }, data.sign, data.floID).then(req_str => { + }, data.sign, data.floID, data.pubKey).then(req_str => { market.transferToken(data.floID, data.receiver, data.token, data.amount).then(result => { - storeRequest(data.floID, req_str, data.sign); + storeRequest(data.floID, req_str, data.sign, !data.pubKey); res.send(result); }).catch(error => { if (error instanceof INVALID) @@ -335,10 +361,10 @@ function getTransaction(req, res) { function Account(req, res) { let data = req.body; - validateRequestFromFloID({ + validateRequest({ type: "get_account", timestamp: data.timestamp - }, data.sign, data.floID).then(req_str => { + }, data.sign, data.floID, data.pubKey).then(req_str => { market.getAccountDetails(data.floID).then(result => { result.sinkID = global.sinkID; if (trustedIDs.includes(data.floID)) @@ -357,13 +383,13 @@ function Account(req, res) { function DepositFLO(req, res) { let data = req.body; - validateRequestFromFloID({ + validateRequest({ type: "deposit_flo", txid: data.txid, timestamp: data.timestamp - }, data.sign, data.floID).then(req_str => { + }, data.sign, data.floID, data.pubKey).then(req_str => { market.depositFLO(data.floID, data.txid).then(result => { - storeRequest(data.floID, req_str, data.sign); + storeRequest(data.floID, req_str, data.sign, !data.pubKey); res.send(result); }).catch(error => { if (error instanceof INVALID) @@ -385,13 +411,13 @@ function DepositFLO(req, res) { function WithdrawFLO(req, res) { let data = req.body; - validateRequestFromFloID({ + validateRequest({ type: "withdraw_flo", amount: data.amount, timestamp: data.timestamp - }, data.sign, data.floID).then(req_str => { + }, data.sign, data.floID, data.pubKey).then(req_str => { market.withdrawFLO(data.floID, data.amount).then(result => { - storeRequest(data.floID, req_str, data.sign); + storeRequest(data.floID, req_str, data.sign, !data.pubKey); res.send(result); }).catch(error => { if (error instanceof INVALID) @@ -413,13 +439,13 @@ function WithdrawFLO(req, res) { function DepositToken(req, res) { let data = req.body; - validateRequestFromFloID({ + validateRequest({ type: "deposit_token", txid: data.txid, timestamp: data.timestamp - }, data.sign, data.floID).then(req_str => { + }, data.sign, data.floID, data.pubKey).then(req_str => { market.depositToken(data.floID, data.txid).then(result => { - storeRequest(data.floID, req_str, data.sign); + storeRequest(data.floID, req_str, data.sign, !data.pubKey); res.send(result); }).catch(error => { if (error instanceof INVALID) @@ -441,14 +467,14 @@ function DepositToken(req, res) { function WithdrawToken(req, res) { let data = req.body; - validateRequestFromFloID({ + validateRequest({ type: "withdraw_token", token: data.token, amount: data.amount, timestamp: data.timestamp - }, data.sign, data.floID).then(req_str => { + }, data.sign, data.floID, data.pubKey).then(req_str => { market.withdrawToken(data.floID, data.token, data.amount).then(result => { - storeRequest(data.floID, req_str, data.sign); + storeRequest(data.floID, req_str, data.sign, !data.pubKey); res.send(result); }).catch(error => { if (error instanceof INVALID) @@ -471,15 +497,15 @@ function WithdrawToken(req, res) { function addUserTag(req, res) { let data = req.body; if (!trustedIDs.includes(data.floID)) - return res.status(INVALID.e_code).send("Access Denied"); - validateRequestFromFloID({ + res.status(INVALID.e_code).send("Access Denied"); + else validateRequest({ type: "add_tag", user: data.user, tag: data.tag, timestamp: data.timestamp - }, data.sign, data.floID).then(req_str => { + }, data.sign, data.floID, data.pubKey).then(req_str => { market.group.addTag(data.user, data.tag).then(result => { - storeRequest(data.floID, req_str, data.sign); + storeRequest(data.floID, req_str, data.sign, !data.pubKey); res.send(result); }).catch(error => { if (error instanceof INVALID) @@ -502,25 +528,16 @@ function addUserTag(req, res) { function removeUserTag(req, res) { let data = req.body; if (!trustedIDs.includes(data.floID)) - return res.status(INVALID.e_code).send("Access Denied"); - else - validateRequestFromFloID({ - type: "remove_tag", - user: data.user, - tag: data.tag, - timestamp: data.timestamp - }, data.sign, data.floID).then(req_str => { - market.group.removeTag(data.user, data.tag).then(result => { - storeRequest(data.floID, req_str, data.sign); - res.send(result); - }).catch(error => { - if (error instanceof INVALID) - res.status(INVALID.e_code).send(error.message); - else { - console.error(error); - res.status(INTERNAL.e_code).send("Request processing failed! Try again later!"); - } - }); + res.status(INVALID.e_code).send("Access Denied"); + else validateRequest({ + type: "remove_tag", + user: data.user, + tag: data.tag, + timestamp: data.timestamp + }, data.sign, data.floID, data.pubKey).then(req_str => { + market.group.removeTag(data.user, data.tag).then(result => { + storeRequest(data.floID, req_str, data.sign, !data.pubKey); + res.send(result); }).catch(error => { if (error instanceof INVALID) res.status(INVALID.e_code).send(error.message); @@ -529,11 +546,19 @@ function removeUserTag(req, res) { res.status(INTERNAL.e_code).send("Request processing failed! Try again later!"); } }); + }).catch(error => { + if (error instanceof INVALID) + res.status(INVALID.e_code).send(error.message); + else { + console.error(error); + res.status(INTERNAL.e_code).send("Request processing failed! Try again later!"); + } + }); } module.exports = { getLoginCode, - SignUp, + //SignUp, Login, Logout, PlaceBuyOrder,