Removing need for user registration

- Users can directly login or request without the need for user registration.
- Users can sign the requests either directly with own privateKey or by using a proxy
- Request's timestamp will be checked for Sign expiry.
- Request's sign will be checked for duplication.
- Cash table update will try to insert if record is not there
- Request Log table will also store a boolean value for sign by proxy or not
- Updated the SQL schema for the above changes.
This commit is contained in:
sairajzero 2022-03-15 04:06:43 +05:30
parent 3296b16710
commit f59c6b3f2a
9 changed files with 177 additions and 161 deletions

View File

@ -33,31 +33,21 @@ CREATE TABLE TrustedList(
/* User Data */ /* 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 ( CREATE TABLE UserSession (
id INT NOT NULL AUTO_INCREMENT, id INT NOT NULL AUTO_INCREMENT,
floID CHAR(34) NOT NULL, floID CHAR(34) NOT NULL,
proxyKey CHAR(66) NOT NULL, proxyKey CHAR(66) NOT NULL,
session_time DATETIME DEFAULT CURRENT_TIMESTAMP, session_time DATETIME DEFAULT CURRENT_TIMESTAMP,
KEY (id), KEY (id),
PRIMARY KEY(floID), PRIMARY KEY(floID)
FOREIGN KEY (floID) REFERENCES Users(floID)
); );
CREATE TABLE Cash ( CREATE TABLE Cash (
id INT NOT NULL AUTO_INCREMENT, id INT NOT NULL AUTO_INCREMENT,
floID CHAR(34) NOT NULL UNIQUE, floID CHAR(34) NOT NULL UNIQUE,
balance DECIMAL(12, 2) DEFAULT 0.00, balance DECIMAL(12, 2) DEFAULT 0.00,
PRIMARY KEY(id), KEY(id),
FOREIGN KEY (floID) REFERENCES Users(floID) PRIMARY KEY(floID)
); );
CREATE TABLE Vault ( CREATE TABLE Vault (
@ -68,7 +58,6 @@ CREATE TABLE Vault (
base DECIMAL(10, 2), base DECIMAL(10, 2),
quantity FLOAT NOT NULL, quantity FLOAT NOT NULL,
PRIMARY KEY(id), PRIMARY KEY(id),
FOREIGN KEY (floID) REFERENCES Users(floID),
FOREIGN KEY (asset) REFERENCES AssetList(asset) FOREIGN KEY (asset) REFERENCES AssetList(asset)
); );
@ -78,7 +67,6 @@ CREATE TABLE UserTag (
tag VARCHAR(50) NOT NULL, tag VARCHAR(50) NOT NULL,
PRIMARY KEY(floID, tag), PRIMARY KEY(floID, tag),
KEY (id), KEY (id),
FOREIGN KEY (floID) REFERENCES Users(floID),
FOREIGN KEY (tag) REFERENCES TagList(tag) FOREIGN KEY (tag) REFERENCES TagList(tag)
); );
@ -89,9 +77,10 @@ CREATE TABLE RequestLog(
floID CHAR(34) NOT NULL, floID CHAR(34) NOT NULL,
request TEXT NOT NULL, request TEXT NOT NULL,
sign TEXT NOT NULL, sign TEXT NOT NULL,
proxy BOOLEAN NOT NULL,
request_time DATETIME DEFAULT CURRENT_TIMESTAMP, request_time DATETIME DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY(id), PRIMARY KEY(id),
FOREIGN KEY (floID) REFERENCES Users(floID) UNIQUE (sign)
); );
CREATE TABLE SellOrder ( CREATE TABLE SellOrder (
@ -102,7 +91,6 @@ CREATE TABLE SellOrder (
minPrice DECIMAL(10, 2) NOT NULL, minPrice DECIMAL(10, 2) NOT NULL,
time_placed DATETIME DEFAULT CURRENT_TIMESTAMP, time_placed DATETIME DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY(id), PRIMARY KEY(id),
FOREIGN KEY (floID) REFERENCES Users(floID),
FOREIGN KEY (asset) REFERENCES AssetList(asset) FOREIGN KEY (asset) REFERENCES AssetList(asset)
); );
@ -114,7 +102,6 @@ CREATE TABLE BuyOrder (
maxPrice DECIMAL(10, 2) NOT NULL, maxPrice DECIMAL(10, 2) NOT NULL,
time_placed DATETIME DEFAULT CURRENT_TIMESTAMP, time_placed DATETIME DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY(id), PRIMARY KEY(id),
FOREIGN KEY (floID) REFERENCES Users(floID),
FOREIGN KEY (asset) REFERENCES AssetList(asset) FOREIGN KEY (asset) REFERENCES AssetList(asset)
); );
@ -124,8 +111,7 @@ CREATE TABLE InputFLO (
floID CHAR(34) NOT NULL, floID CHAR(34) NOT NULL,
amount FLOAT, amount FLOAT,
status VARCHAR(50) NOT NULL, status VARCHAR(50) NOT NULL,
PRIMARY KEY(id), PRIMARY KEY(id)
FOREIGN KEY (floID) REFERENCES Users(floID)
); );
CREATE TABLE OutputFLO ( CREATE TABLE OutputFLO (
@ -134,8 +120,7 @@ CREATE TABLE OutputFLO (
floID CHAR(34) NOT NULL, floID CHAR(34) NOT NULL,
amount FLOAT NOT NULL, amount FLOAT NOT NULL,
status VARCHAR(50) NOT NULL, status VARCHAR(50) NOT NULL,
PRIMARY KEY(id), PRIMARY KEY(id)
FOREIGN KEY (floID) REFERENCES Users(floID)
); );
CREATE TABLE InputToken ( CREATE TABLE InputToken (
@ -145,8 +130,7 @@ CREATE TABLE InputToken (
token VARCHAR(64), token VARCHAR(64),
amount FLOAT, amount FLOAT,
status VARCHAR(50) NOT NULL, status VARCHAR(50) NOT NULL,
PRIMARY KEY(id), PRIMARY KEY(id)
FOREIGN KEY (floID) REFERENCES Users(floID)
); );
CREATE TABLE OutputToken ( CREATE TABLE OutputToken (
@ -156,8 +140,7 @@ CREATE TABLE OutputToken (
token VARCHAR(64), token VARCHAR(64),
amount FLOAT NOT NULL, amount FLOAT NOT NULL,
status VARCHAR(50) NOT NULL, status VARCHAR(50) NOT NULL,
PRIMARY KEY(id), PRIMARY KEY(id)
FOREIGN KEY (floID) REFERENCES Users(floID)
); );
/* Transaction Data */ /* Transaction Data */
@ -180,9 +163,7 @@ CREATE TABLE TransferTransactions (
tx_time DATETIME DEFAULT CURRENT_TIMESTAMP, tx_time DATETIME DEFAULT CURRENT_TIMESTAMP,
txid VARCHAR(66) NOT NULL, txid VARCHAR(66) NOT NULL,
KEY(id), KEY(id),
PRIMARY KEY(txid), PRIMARY KEY(txid)
FOREIGN KEY (sender) REFERENCES Users(floID),
FOREIGN KEY (receiver) REFERENCES Users(floID)
); );
CREATE TABLE TradeTransactions ( CREATE TABLE TradeTransactions (
@ -196,8 +177,6 @@ CREATE TABLE TradeTransactions (
txid VARCHAR(66) NOT NULL, txid VARCHAR(66) NOT NULL,
KEY(id), KEY(id),
PRIMARY KEY(txid), PRIMARY KEY(txid),
FOREIGN KEY (buyer) REFERENCES Users(floID),
FOREIGN KEY (seller) REFERENCES Users(floID),
FOREIGN KEY (asset) REFERENCES AssetList(asset) FOREIGN KEY (asset) REFERENCES AssetList(asset)
); );
@ -219,8 +198,6 @@ CREATE TABLE AuditTrade(
buyer_old_cash FLOAT NOT NULL, buyer_old_cash FLOAT NOT NULL,
buyer_new_cash FLOAT NOT NULL, buyer_new_cash FLOAT NOT NULL,
PRIMARY KEY(id), PRIMARY KEY(id),
FOREIGN KEY (sellerID) REFERENCES Users(floID),
FOREIGN KEY (buyerID) REFERENCES Users(floID),
FOREIGN KEY (asset) REFERENCES AssetList(asset) FOREIGN KEY (asset) REFERENCES AssetList(asset)
); );
@ -249,13 +226,6 @@ CREATE TABLE sinkShares(
PRIMARY KEY(floID) 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 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; 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 CREATE TRIGGER RequestLog_U AFTER UPDATE ON RequestLog

View File

@ -15,7 +15,6 @@ TRUNCATE UserTag;
TRUNCATE TransferTransactions; TRUNCATE TransferTransactions;
TRUNCATE TradeTransactions; TRUNCATE TradeTransactions;
TRUNCATE Vault; TRUNCATE Vault;
DELETE FROM Users;
/* Blockchain data */ /* Blockchain data */
TRUNCATE LastTx; TRUNCATE LastTx;

View File

@ -102,6 +102,8 @@ function getAccount(floID, proxySecret) {
floID: floID, floID: floID,
timestamp: Date.now() timestamp: Date.now()
}; };
if (floCrypto.getFloID(proxySecret) === floID) //Direct signing (without proxy)
request.pubKey = floCrypto.getPubKeyHex(proxySecret);
request.sign = signRequest({ request.sign = signRequest({
type: "get_account", type: "get_account",
timestamp: request.timestamp timestamp: request.timestamp
@ -173,11 +175,11 @@ function getTx(txid) {
}) })
} }
function signRequest(request, privKey) { function signRequest(request, signKey) {
if (typeof request !== "object") if (typeof request !== "object")
throw Error("Request is not an object"); throw Error("Request is not an object");
let req_str = Object.keys(request).sort().map(r => r + ":" + request[r]).join("|"); 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() { function getLoginCode() {
@ -190,6 +192,7 @@ function getLoginCode() {
}) })
} }
/*
function signUp(privKey, code, hash) { function signUp(privKey, code, hash) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
if (!code || !hash) if (!code || !hash)
@ -220,6 +223,7 @@ function signUp(privKey, code, hash) {
.catch(error => reject(error)); .catch(error => reject(error));
}); });
} }
*/
function login(privKey, proxyKey, code, hash) { function login(privKey, proxyKey, code, hash) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
@ -228,6 +232,7 @@ function login(privKey, proxyKey, code, hash) {
let request = { let request = {
proxyKey: proxyKey, proxyKey: proxyKey,
floID: floCrypto.getFloID(privKey), floID: floCrypto.getFloID(privKey),
pubKey: floCrypto.getPubKeyHex(privKey),
timestamp: Date.now(), timestamp: Date.now(),
code: code, code: code,
hash: hash hash: hash
@ -261,6 +266,8 @@ function logout(floID, proxySecret) {
floID: floID, floID: floID,
timestamp: Date.now() timestamp: Date.now()
}; };
if (floCrypto.getFloID(proxySecret) === floID) //Direct signing (without proxy)
request.pubKey = floCrypto.getPubKeyHex(proxySecret);
request.sign = signRequest({ request.sign = signRequest({
type: "logout", type: "logout",
timestamp: request.timestamp timestamp: request.timestamp
@ -293,6 +300,8 @@ function buy(asset, quantity, max_price, floID, proxySecret) {
max_price: max_price, max_price: max_price,
timestamp: Date.now() timestamp: Date.now()
}; };
if (floCrypto.getFloID(proxySecret) === floID) //Direct signing (without proxy)
request.pubKey = floCrypto.getPubKeyHex(proxySecret);
request.sign = signRequest({ request.sign = signRequest({
type: "buy_order", type: "buy_order",
asset: asset, asset: asset,
@ -329,6 +338,8 @@ function sell(asset, quantity, min_price, floID, proxySecret) {
min_price: min_price, min_price: min_price,
timestamp: Date.now() timestamp: Date.now()
}; };
if (floCrypto.getFloID(proxySecret) === floID) //Direct signing (without proxy)
request.pubKey = floCrypto.getPubKeyHex(proxySecret);
request.sign = signRequest({ request.sign = signRequest({
type: "sell_order", type: "sell_order",
quantity: quantity, quantity: quantity,
@ -362,6 +373,8 @@ function cancelOrder(type, id, floID, proxySecret) {
orderID: id, orderID: id,
timestamp: Date.now() timestamp: Date.now()
}; };
if (floCrypto.getFloID(proxySecret) === floID) //Direct signing (without proxy)
request.pubKey = floCrypto.getPubKeyHex(proxySecret);
request.sign = signRequest({ request.sign = signRequest({
type: "cancel_order", type: "cancel_order",
order: type, order: type,
@ -396,6 +409,8 @@ function transferToken(receiver, token, amount, floID, proxySecret) {
amount: amount, amount: amount,
timestamp: Date.now() timestamp: Date.now()
}; };
if (floCrypto.getFloID(proxySecret) === floID) //Direct signing (without proxy)
request.pubKey = floCrypto.getPubKeyHex(proxySecret);
request.sign = signRequest({ request.sign = signRequest({
type: "transfer_token", type: "transfer_token",
receiver: receiver, 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) => { return new Promise((resolve, reject) => {
if (typeof quantity !== "number" || quantity <= floGlobals.fee) if (typeof quantity !== "number" || quantity <= floGlobals.fee)
return reject(`Invalid quantity (${quantity})`); return reject(`Invalid quantity (${quantity})`);
@ -428,11 +443,13 @@ function depositFLO(quantity, floID, sinkID, privKey, proxySecret) {
txid: txid, txid: txid,
timestamp: Date.now() timestamp: Date.now()
}; };
if (!proxySecret) //Direct signing (without proxy)
request.pubKey = floCrypto.getPubKeyHex(privKey);
request.sign = signRequest({ request.sign = signRequest({
type: "deposit_flo", type: "deposit_flo",
txid: txid, txid: txid,
timestamp: request.timestamp timestamp: request.timestamp
}, proxySecret); }, proxySecret || privKey);
console.debug(request); console.debug(request);
exchangeAPI('/deposit-flo', { exchangeAPI('/deposit-flo', {
@ -456,6 +473,8 @@ function withdrawFLO(quantity, floID, proxySecret) {
amount: quantity, amount: quantity,
timestamp: Date.now() timestamp: Date.now()
}; };
if (floCrypto.getFloID(proxySecret) === floID) //Direct signing (without proxy)
request.pubKey = floCrypto.getPubKeyHex(proxySecret);
request.sign = signRequest({ request.sign = signRequest({
type: "withdraw_flo", type: "withdraw_flo",
amount: quantity, 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) => { return new Promise((resolve, reject) => {
if (!floCrypto.verifyPrivKey(privKey, floID)) if (!floCrypto.verifyPrivKey(privKey, floID))
return reject("Invalid Private Key"); return reject("Invalid Private Key");
@ -486,11 +505,13 @@ function depositToken(token, quantity, floID, sinkID, privKey, proxySecret) {
txid: txid, txid: txid,
timestamp: Date.now() timestamp: Date.now()
}; };
if (!proxySecret) //Direct signing (without proxy)
request.pubKey = floCrypto.getPubKeyHex(privKey);
request.sign = signRequest({ request.sign = signRequest({
type: "deposit_token", type: "deposit_token",
txid: txid, txid: txid,
timestamp: request.timestamp timestamp: request.timestamp
}, proxySecret); }, proxySecret || privKey);
console.debug(request); console.debug(request);
exchangeAPI('/deposit-token', { exchangeAPI('/deposit-token', {
@ -515,6 +536,8 @@ function withdrawToken(token, quantity, floID, proxySecret) {
amount: quantity, amount: quantity,
timestamp: Date.now() timestamp: Date.now()
}; };
if (floCrypto.getFloID(proxySecret) === floID) //Direct signing (without proxy)
request.pubKey = floCrypto.getPubKeyHex(proxySecret);
request.sign = signRequest({ request.sign = signRequest({
type: "withdraw_token", type: "withdraw_token",
token: token, token: token,
@ -544,6 +567,8 @@ function addUserTag(tag_user, tag, floID, proxySecret) {
tag: tag, tag: tag,
timestamp: Date.now() timestamp: Date.now()
}; };
if (floCrypto.getFloID(proxySecret) === floID) //Direct signing (without proxy)
request.pubKey = floCrypto.getPubKeyHex(proxySecret);
request.sign = signRequest({ request.sign = signRequest({
type: "add_tag", type: "add_tag",
user: tag_user, user: tag_user,
@ -573,6 +598,8 @@ function removeUserTag(tag_user, tag, floID, proxySecret) {
tag: tag, tag: tag,
timestamp: Date.now() timestamp: Date.now()
}; };
if (floCrypto.getFloID(proxySecret) === floID) //Direct signing (without proxy)
request.pubKey = floCrypto.getPubKeyHex(proxySecret);
request.sign = signRequest({ request.sign = signRequest({
type: "remove_tag", type: "remove_tag",
user: tag_user, user: tag_user,

View File

@ -4,7 +4,8 @@ module.exports = {
PERIOD_INTERVAL: 15 * 60 * 1000 // 15 min PERIOD_INTERVAL: 15 * 60 * 1000 // 15 min
}, },
request: { 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 INVALID_SERVER_MSG: "INCORRECT_SERVER_ERROR" //Should be reflected in public backend script
}, },
market: { market: {

View File

@ -53,7 +53,7 @@ module.exports = function App(secret, DB) {
app.get('/get-login-code', Request.getLoginCode); app.get('/get-login-code', Request.getLoginCode);
//signup request //signup request
app.post('/signup', Request.SignUp); //app.post('/signup', Request.SignUp); Removing need for signup
//login request //login request
app.post('/login', Request.Login); app.post('/login', Request.Login);

View File

@ -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) { function updateBalance(seller_best, buyer_best, txQueries, asset, cur_price, quantity) {
//Update cash balance for seller and buyer //Update cash balance for seller and buyer
let totalAmount = cur_price * quantity; 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]]); txQueries.push(["UPDATE Cash SET balance=balance-? WHERE floID=?", [totalAmount, buyer_best.floID]]);
//Add coins to Buyer //Add coins to Buyer
txQueries.push(["INSERT INTO Vault(floID, asset, base, quantity) VALUES (?, ?, ?, ?)", [buyer_best.floID, asset, cur_price, quantity]]) 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 => { 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) for (let i in result)
balance[result[i].floID].asset = result[i].asset_balance; 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); resolve(balance);
}).catch(error => reject(error)) }).catch(error => reject(error))
}).catch(error => reject(error)) }).catch(error => reject(error))

View File

@ -364,8 +364,8 @@ function getTopValidBuyOrder(orders, cur_price) {
function verifyBuyOrder(buyOrder, cur_price) { function verifyBuyOrder(buyOrder, cur_price) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
DB.query("SELECT balance AS bal FROM Cash WHERE floID=?", [buyOrder.floID]).then(result => { DB.query("SELECT balance FROM Cash WHERE floID=?", [buyOrder.floID]).then(result => {
if (result[0].bal < cur_price * buyOrder.quantity) { 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 //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`); console.warn(`Buy order ${buyOrder.id} is active, but Cash is insufficient`);
reject(false); reject(false);

View File

@ -10,18 +10,9 @@ const {
var DB, assetList; //container for database and allowed assets 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) => { const getAssetBalance = (floID, asset) => new Promise((resolve, reject) => {
let promises = (asset === floGlobals.currency) ? [ 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*maxPrice) AS locked FROM BuyOrder WHERE floID=?", [floID])
] : [ ] : [
DB.query("SELECT SUM(quantity) AS balance FROM Vault WHERE floID=? AND asset=?", [floID, asset]), DB.query("SELECT SUM(quantity) AS balance FROM Vault WHERE floID=? AND asset=?", [floID, asset]),
@ -164,7 +155,7 @@ function getAccountDetails(floID) {
else else
switch (i) { switch (i) {
case 0: case 0:
response.cash = a.value[0].balance; response.cash = a.value.length ? a.value[0].balance : 0;
break; break;
case 1: case 1:
response.vault = a.value; response.vault = a.value;
@ -216,22 +207,20 @@ function transferToken(sender, receiver, token, amount) {
else if (token !== floGlobals.currency && !assetList.includes(token)) else if (token !== floGlobals.currency && !assetList.includes(token))
return reject(INVALID(`Invalid token (${token})`)); return reject(INVALID(`Invalid token (${token})`));
getAssetBalance.check(senderID, token, amount).then(_ => { getAssetBalance.check(senderID, token, amount).then(_ => {
checkIfUserRegistered(receiver).then(_ => { consumeAsset(sender, token, amount).then(txQueries => {
consumeAsset(sender, token, amount).then(txQueries => { if (token === floGlobals.currency)
if (token === floGlobals.currency) txQueries.push(["INSERT INTO Cash (floID, balance) VALUE (?, ?) ON DUPLICATE KEY UPDATE balance=balance+?", [receiver, amount, amount]]);
txQueries.push(["UPDATE Cash SET balance=balance+? WHERE floID=?", [amount, receiver]]); else
else txQueries.push(["INSERT INTO Vault(floID, quantity) VALUES (?, ?)", [receiver, amount]]);
txQueries.push(["INSERT INTO Vault(floID, quantity) VALUES (?, ?)", [receiver, amount]]); let time = Date.now();
let time = Date.now(); let hash = TRANSFER_HASH_PREFIX + Crypto.SHA256([time, sender, receiver, token, amount].join("|"));
let hash = TRANSFER_HASH_PREFIX + Crypto.SHA256([time, sender, receiver, token, amount].join("|")); txQueries.push([
txQueries.push([ "INSERT INTO TransferTransactions (sender, receiver, token, amount, tx_time, txid)",
"INSERT INTO TransferTransactions (sender, receiver, token, amount, tx_time, txid)", [sender, receiver, token, amount, global.convertDateToString(time), hash]
[sender, receiver, token, amount, global.convertDateToString(time), hash] ]);
]); DB.transaction(txQueries)
DB.transaction(txQueries) .then(result => resolve(hash))
.then(result => resolve(hash)) .catch(error => reject(error))
.catch(error => reject(error))
}).catch(error => reject(error))
}).catch(error => reject(error)) }).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]]); txQueries.push(["UPDATE InputToken SET status=?, token=?, amount=? WHERE id=?", ["SUCCESS", token_name, amount_token, req.id]]);
if (token_name === floGlobals.currency) 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 else
txQueries.push(["INSERT INTO Vault(floID, asset, quantity) VALUES (?, ?, ?)", [req.floID, token_name, amount_token]]); txQueries.push(["INSERT INTO Vault(floID, asset, quantity) VALUES (?, ?, ?)", [req.floID, token_name, amount_token]]);
DB.transaction(txQueries) DB.transaction(txQueries)

View File

@ -3,6 +3,7 @@
const market = require("./market"); const market = require("./market");
const { const {
SIGN_EXPIRE_TIME,
MAX_SESSION_TIMEOUT, MAX_SESSION_TIMEOUT,
INVALID_SERVER_MSG INVALID_SERVER_MSG
} = require("./_constants")["request"]; } = require("./_constants")["request"];
@ -25,40 +26,61 @@ INTERNAL.e_code = 500;
var serving; var serving;
function validateRequestFromFloID(request, sign, floID, proxy = true) { function validateRequest(request, sign, floID, pubKey) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
if (!serving) 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)) else if (!floCrypto.validateAddr(floID))
return reject(INVALID("Invalid floID")); reject(INVALID("Invalid floID"));
DB.query("SELECT " + (proxy ? "session_time, proxyKey AS pubKey FROM UserSession" : "pubKey FROM Users") + " WHERE floID=?", [floID]).then(result => { else if (typeof request !== "object")
if (result.length < 1) reject(INVALID("Request is not an object"));
return reject(INVALID(proxy ? "Session not active" : "User not registered")); else validateRequest.getSignKey(floID, pubKey).then(signKey => {
if (proxy && result[0].session_time + MAX_SESSION_TIMEOUT < Date.now()) let req_str = Object.keys(request).sort().map(r => r + ":" + request[r]).join("|");
return reject(INVALID("Session Expired! Re-login required")); try {
let req_str = validateRequest(request, sign, result[0].pubKey); if (!floCrypto.verifySign(req_str, sign, signKey))
req_str instanceof INVALID ? reject(req_str) : resolve(req_str); 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)); }).catch(error => reject(error));
}); });
} }
function validateRequest(request, sign, pubKey) { validateRequest.getSignKey = (floID, pubKey) => new Promise((resolve, reject) => {
if (typeof request !== "object") if (!pubKey)
return INVALID("Request is not an object"); DB.query("SELECT session_time, proxyKey FROM UserSession WHERE floID=?", [floID]).then(result => {
let req_str = Object.keys(request).sort().map(r => r + ":" + request[r]).join("|"); if (result.length < 1)
try { reject(INVALID("Session not active"));
if (floCrypto.verifySign(req_str, sign, pubKey)) else if (proxy && result[0].session_time + MAX_SESSION_TIMEOUT < Date.now())
return req_str; reject(INVALID("Session Expired! Re-login required"));
else else
return INVALID("Invalid request signature! Re-login if this error occurs frequently"); resolve(result[0].proxyKey);
} catch { }).catch(error => reject(error));
return INVALID("Corrupted sign/key"); 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); //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)); .then(_ => null).catch(error => console.error(error));
} }
@ -73,6 +95,7 @@ function getLoginCode(req, res) {
}); });
} }
/*
function SignUp(req, res) { function SignUp(req, res) {
if (!serving) if (!serving)
return res.status(INVALID.e_code).send(INVALID_SERVER_MSG); 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"); return res.status(INVALID.e_code).send("Invalid Public Key");
if (!data.code || data.hash != Crypto.SHA1(data.code + secret)) if (!data.code || data.hash != Crypto.SHA1(data.code + secret))
return res.status(INVALID.e_code).send("Invalid Code"); return res.status(INVALID.e_code).send("Invalid Code");
let req_str = validateRequest({ let req_str = validateRequest_X({
type: "create_account", type: "create_account",
random: data.code, random: data.code,
timestamp: data.timestamp timestamp: data.timestamp
@ -103,17 +126,20 @@ function SignUp(req, res) {
} }
}); });
} }
*/
function Login(req, res) { function Login(req, res) {
let data = req.body; let data = req.body;
if (!data.code || data.hash != Crypto.SHA1(data.code + secret)) if (!data.code || data.hash != Crypto.SHA1(data.code + secret))
return res.status(INVALID.e_code).send("Invalid Code"); 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", type: "login",
random: data.code, random: data.code,
proxyKey: data.proxyKey, proxyKey: data.proxyKey,
timestamp: data.timestamp 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 (?, ?) " + DB.query("INSERT INTO UserSession (floID, proxyKey) VALUE (?, ?) " +
"ON DUPLICATE KEY UPDATE session_time=DEFAULT, proxyKey=?", "ON DUPLICATE KEY UPDATE session_time=DEFAULT, proxyKey=?",
[data.floID, data.proxyKey, data.proxyKey]).then(_ => { [data.floID, data.proxyKey, data.proxyKey]).then(_ => {
@ -135,12 +161,12 @@ function Login(req, res) {
function Logout(req, res) { function Logout(req, res) {
let data = req.body; let data = req.body;
validateRequestFromFloID({ validateRequest({
type: "logout", type: "logout",
timestamp: data.timestamp 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(_ => { 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'); res.send('Logout successful');
}).catch(error => { }).catch(error => {
console.error(error); console.error(error);
@ -158,15 +184,15 @@ function Logout(req, res) {
function PlaceSellOrder(req, res) { function PlaceSellOrder(req, res) {
let data = req.body; let data = req.body;
validateRequestFromFloID({ validateRequest({
type: "sell_order", type: "sell_order",
asset: data.asset, asset: data.asset,
quantity: data.quantity, quantity: data.quantity,
min_price: data.min_price, min_price: data.min_price,
timestamp: data.timestamp 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 => { 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); res.send(result);
}).catch(error => { }).catch(error => {
if (error instanceof INVALID) if (error instanceof INVALID)
@ -188,15 +214,15 @@ function PlaceSellOrder(req, res) {
function PlaceBuyOrder(req, res) { function PlaceBuyOrder(req, res) {
let data = req.body; let data = req.body;
validateRequestFromFloID({ validateRequest({
type: "buy_order", type: "buy_order",
asset: data.asset, asset: data.asset,
quantity: data.quantity, quantity: data.quantity,
max_price: data.max_price, max_price: data.max_price,
timestamp: data.timestamp 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 => { 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); res.send(result);
}).catch(error => { }).catch(error => {
if (error instanceof INVALID) if (error instanceof INVALID)
@ -218,14 +244,14 @@ function PlaceBuyOrder(req, res) {
function CancelOrder(req, res) { function CancelOrder(req, res) {
let data = req.body; let data = req.body;
validateRequestFromFloID({ validateRequest({
type: "cancel_order", type: "cancel_order",
order: data.orderType, order: data.orderType,
id: data.orderID, id: data.orderID,
timestamp: data.timestamp 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 => { 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); res.send(result);
}).catch(error => { }).catch(error => {
if (error instanceof INVALID) if (error instanceof INVALID)
@ -247,15 +273,15 @@ function CancelOrder(req, res) {
function TransferToken(req, res) { function TransferToken(req, res) {
let data = req.body; let data = req.body;
validateRequestFromFloID({ validateRequest({
type: "transfer_token", type: "transfer_token",
receiver: data.receiver, receiver: data.receiver,
token: data.token, token: data.token,
amount: data.amount, amount: data.amount,
timestamp: data.timestamp 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 => { 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); res.send(result);
}).catch(error => { }).catch(error => {
if (error instanceof INVALID) if (error instanceof INVALID)
@ -335,10 +361,10 @@ function getTransaction(req, res) {
function Account(req, res) { function Account(req, res) {
let data = req.body; let data = req.body;
validateRequestFromFloID({ validateRequest({
type: "get_account", type: "get_account",
timestamp: data.timestamp 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 => { market.getAccountDetails(data.floID).then(result => {
result.sinkID = global.sinkID; result.sinkID = global.sinkID;
if (trustedIDs.includes(data.floID)) if (trustedIDs.includes(data.floID))
@ -357,13 +383,13 @@ function Account(req, res) {
function DepositFLO(req, res) { function DepositFLO(req, res) {
let data = req.body; let data = req.body;
validateRequestFromFloID({ validateRequest({
type: "deposit_flo", type: "deposit_flo",
txid: data.txid, txid: data.txid,
timestamp: data.timestamp 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 => { 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); res.send(result);
}).catch(error => { }).catch(error => {
if (error instanceof INVALID) if (error instanceof INVALID)
@ -385,13 +411,13 @@ function DepositFLO(req, res) {
function WithdrawFLO(req, res) { function WithdrawFLO(req, res) {
let data = req.body; let data = req.body;
validateRequestFromFloID({ validateRequest({
type: "withdraw_flo", type: "withdraw_flo",
amount: data.amount, amount: data.amount,
timestamp: data.timestamp 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 => { 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); res.send(result);
}).catch(error => { }).catch(error => {
if (error instanceof INVALID) if (error instanceof INVALID)
@ -413,13 +439,13 @@ function WithdrawFLO(req, res) {
function DepositToken(req, res) { function DepositToken(req, res) {
let data = req.body; let data = req.body;
validateRequestFromFloID({ validateRequest({
type: "deposit_token", type: "deposit_token",
txid: data.txid, txid: data.txid,
timestamp: data.timestamp 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 => { 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); res.send(result);
}).catch(error => { }).catch(error => {
if (error instanceof INVALID) if (error instanceof INVALID)
@ -441,14 +467,14 @@ function DepositToken(req, res) {
function WithdrawToken(req, res) { function WithdrawToken(req, res) {
let data = req.body; let data = req.body;
validateRequestFromFloID({ validateRequest({
type: "withdraw_token", type: "withdraw_token",
token: data.token, token: data.token,
amount: data.amount, amount: data.amount,
timestamp: data.timestamp 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 => { 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); res.send(result);
}).catch(error => { }).catch(error => {
if (error instanceof INVALID) if (error instanceof INVALID)
@ -471,15 +497,15 @@ function WithdrawToken(req, res) {
function addUserTag(req, res) { function addUserTag(req, res) {
let data = req.body; let data = req.body;
if (!trustedIDs.includes(data.floID)) if (!trustedIDs.includes(data.floID))
return res.status(INVALID.e_code).send("Access Denied"); res.status(INVALID.e_code).send("Access Denied");
validateRequestFromFloID({ else validateRequest({
type: "add_tag", type: "add_tag",
user: data.user, user: data.user,
tag: data.tag, tag: data.tag,
timestamp: data.timestamp 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 => { 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); res.send(result);
}).catch(error => { }).catch(error => {
if (error instanceof INVALID) if (error instanceof INVALID)
@ -502,25 +528,16 @@ function addUserTag(req, res) {
function removeUserTag(req, res) { function removeUserTag(req, res) {
let data = req.body; let data = req.body;
if (!trustedIDs.includes(data.floID)) if (!trustedIDs.includes(data.floID))
return res.status(INVALID.e_code).send("Access Denied"); res.status(INVALID.e_code).send("Access Denied");
else else validateRequest({
validateRequestFromFloID({ type: "remove_tag",
type: "remove_tag", user: data.user,
user: data.user, tag: data.tag,
tag: data.tag, timestamp: data.timestamp
timestamp: data.timestamp }, data.sign, data.floID, data.pubKey).then(req_str => {
}, data.sign, data.floID).then(req_str => { market.group.removeTag(data.user, data.tag).then(result => {
market.group.removeTag(data.user, data.tag).then(result => { storeRequest(data.floID, req_str, data.sign, !data.pubKey);
storeRequest(data.floID, req_str, data.sign); res.send(result);
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!");
}
});
}).catch(error => { }).catch(error => {
if (error instanceof INVALID) if (error instanceof INVALID)
res.status(INVALID.e_code).send(error.message); 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!"); 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 = { module.exports = {
getLoginCode, getLoginCode,
SignUp, //SignUp,
Login, Login,
Logout, Logout,
PlaceBuyOrder, PlaceBuyOrder,