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:
parent
3296b16710
commit
f59c6b3f2a
@ -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
|
||||
|
||||
@ -15,7 +15,6 @@ TRUNCATE UserTag;
|
||||
TRUNCATE TransferTransactions;
|
||||
TRUNCATE TradeTransactions;
|
||||
TRUNCATE Vault;
|
||||
DELETE FROM Users;
|
||||
|
||||
/* Blockchain data */
|
||||
TRUNCATE LastTx;
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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: {
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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))
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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)
|
||||
|
||||
187
src/request.js
187
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,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user