Adding transfer token feature

Transfer tokens
- Added transferToken: Users can directly transfer tokens (including FLO) to other registered users.
- Updated schema changes for the above requirement.
- Trade transactions and transfer transactions will generate a txid (using SHA256).

Other changes
- changed all signing type to lowercase
This commit is contained in:
sairajzero 2022-03-12 02:25:31 +05:30
parent 136ba68b42
commit 0520791696
10 changed files with 241 additions and 86 deletions

View File

@ -171,7 +171,21 @@ CREATE TABLE PriceHistory (
FOREIGN KEY (asset) REFERENCES AssetList(asset) FOREIGN KEY (asset) REFERENCES AssetList(asset)
); );
CREATE TABLE TransactionHistory ( CREATE TABLE TransferTransactions (
id INT NOT NULL AUTO_INCREMENT,
sender CHAR(34) NOT NULL,
receiver CHAR(34) NOT NULL,
token VARCHAR(64) NOT NULL,
amount FLOAT NOT NULL,
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)
);
CREATE TABLE TradeTransactions (
id INT NOT NULL AUTO_INCREMENT, id INT NOT NULL AUTO_INCREMENT,
seller CHAR(34) NOT NULL, seller CHAR(34) NOT NULL,
buyer CHAR(34) NOT NULL, buyer CHAR(34) NOT NULL,
@ -179,13 +193,15 @@ CREATE TABLE TransactionHistory (
quantity FLOAT NOT NULL, quantity FLOAT NOT NULL,
unitValue DECIMAL(10, 2) NOT NULL, unitValue DECIMAL(10, 2) NOT NULL,
tx_time DATETIME DEFAULT CURRENT_TIMESTAMP, tx_time DATETIME DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY(id), txid VARCHAR(66) NOT NULL,
KEY(id),
PRIMARY KEY(txid),
FOREIGN KEY (buyer) REFERENCES Users(floID), FOREIGN KEY (buyer) REFERENCES Users(floID),
FOREIGN KEY (seller) REFERENCES Users(floID), FOREIGN KEY (seller) REFERENCES Users(floID),
FOREIGN KEY (asset) REFERENCES AssetList(asset) FOREIGN KEY (asset) REFERENCES AssetList(asset)
); );
CREATE TABLE AuditTransaction( CREATE TABLE AuditTrade(
id INT NOT NULL AUTO_INCREMENT, id INT NOT NULL AUTO_INCREMENT,
rec_time DATETIME DEFAULT CURRENT_TIMESTAMP, rec_time DATETIME DEFAULT CURRENT_TIMESTAMP,
unit_price FLOAT NOT NULL, unit_price FLOAT NOT NULL,
@ -324,16 +340,23 @@ FOR EACH ROW INSERT INTO _backup (t_name, id) VALUES ('PriceHistory', NEW.id) ON
CREATE TRIGGER PriceHistory_D AFTER DELETE ON PriceHistory CREATE TRIGGER PriceHistory_D AFTER DELETE ON PriceHistory
FOR EACH ROW INSERT INTO _backup (t_name, id) VALUES ('PriceHistory', OLD.id) ON DUPLICATE KEY UPDATE mode=NULL, timestamp=DEFAULT; FOR EACH ROW INSERT INTO _backup (t_name, id) VALUES ('PriceHistory', OLD.id) ON DUPLICATE KEY UPDATE mode=NULL, timestamp=DEFAULT;
CREATE TRIGGER AuditTransaction_I AFTER INSERT ON AuditTransaction CREATE TRIGGER AuditTransaction_I AFTER INSERT ON AuditTrade
FOR EACH ROW INSERT INTO _backup (t_name, id) VALUES ('AuditTransaction', NEW.id) ON DUPLICATE KEY UPDATE mode=TRUE, timestamp=DEFAULT; FOR EACH ROW INSERT INTO _backup (t_name, id) VALUES ('AuditTrade', NEW.id) ON DUPLICATE KEY UPDATE mode=TRUE, timestamp=DEFAULT;
CREATE TRIGGER AuditTransaction_U AFTER UPDATE ON AuditTransaction CREATE TRIGGER AuditTransaction_U AFTER UPDATE ON AuditTrade
FOR EACH ROW INSERT INTO _backup (t_name, id) VALUES ('AuditTransaction', NEW.id) ON DUPLICATE KEY UPDATE mode=TRUE, timestamp=DEFAULT; FOR EACH ROW INSERT INTO _backup (t_name, id) VALUES ('AuditTrade', NEW.id) ON DUPLICATE KEY UPDATE mode=TRUE, timestamp=DEFAULT;
CREATE TRIGGER AuditTransaction_D AFTER DELETE ON AuditTransaction CREATE TRIGGER AuditTransaction_D AFTER DELETE ON AuditTrade
FOR EACH ROW INSERT INTO _backup (t_name, id) VALUES ('AuditTransaction', OLD.id) ON DUPLICATE KEY UPDATE mode=NULL, timestamp=DEFAULT; FOR EACH ROW INSERT INTO _backup (t_name, id) VALUES ('AuditTrade', OLD.id) ON DUPLICATE KEY UPDATE mode=NULL, timestamp=DEFAULT;
CREATE TRIGGER TransactionHistory_I AFTER INSERT ON TransactionHistory CREATE TRIGGER TransactionHistory_I AFTER INSERT ON TradeTransactions
FOR EACH ROW INSERT INTO _backup (t_name, id) VALUES ('TransactionHistory', NEW.id) ON DUPLICATE KEY UPDATE mode=TRUE, timestamp=DEFAULT; FOR EACH ROW INSERT INTO _backup (t_name, id) VALUES ('TradeTransactions', NEW.id) ON DUPLICATE KEY UPDATE mode=TRUE, timestamp=DEFAULT;
CREATE TRIGGER TransactionHistory_U AFTER UPDATE ON TransactionHistory CREATE TRIGGER TransactionHistory_U AFTER UPDATE ON TradeTransactions
FOR EACH ROW INSERT INTO _backup (t_name, id) VALUES ('TransactionHistory', NEW.id) ON DUPLICATE KEY UPDATE mode=TRUE, timestamp=DEFAULT; FOR EACH ROW INSERT INTO _backup (t_name, id) VALUES ('TradeTransactions', NEW.id) ON DUPLICATE KEY UPDATE mode=TRUE, timestamp=DEFAULT;
CREATE TRIGGER TransactionHistory_D AFTER DELETE ON TransactionHistory CREATE TRIGGER TransactionHistory_D AFTER DELETE ON TradeTransactions
FOR EACH ROW INSERT INTO _backup (t_name, id) VALUES ('TransactionHistory', OLD.id) ON DUPLICATE KEY UPDATE mode=NULL, timestamp=DEFAULT; FOR EACH ROW INSERT INTO _backup (t_name, id) VALUES ('TradeTransactions', OLD.id) ON DUPLICATE KEY UPDATE mode=NULL, timestamp=DEFAULT;
CREATE TRIGGER TransferTransactions_I AFTER INSERT ON TransferTransactions
FOR EACH ROW INSERT INTO _backup (t_name, id) VALUES ('TransferTransactions', NEW.id) ON DUPLICATE KEY UPDATE mode=TRUE, timestamp=DEFAULT;
CREATE TRIGGER TransferTransactions_U AFTER UPDATE ON TransferTransactions
FOR EACH ROW INSERT INTO _backup (t_name, id) VALUES ('TransferTransactions', NEW.id) ON DUPLICATE KEY UPDATE mode=TRUE, timestamp=DEFAULT;
CREATE TRIGGER TransferTransactions_D AFTER DELETE ON TransferTransactions
FOR EACH ROW INSERT INTO _backup (t_name, id) VALUES ('TransferTransactions', OLD.id) ON DUPLICATE KEY UPDATE mode=NULL, timestamp=DEFAULT;

View File

@ -1,6 +1,6 @@
/* Node data */ /* Node data */
TRUNCATE _backup; TRUNCATE _backup;
TRUNCATE AuditTransaction; TRUNCATE AuditTrade;
TRUNCATE BuyOrder; TRUNCATE BuyOrder;
TRUNCATE Cash; TRUNCATE Cash;
TRUNCATE InputFLO; TRUNCATE InputFLO;
@ -12,7 +12,8 @@ TRUNCATE RequestLog;
TRUNCATE SellOrder; TRUNCATE SellOrder;
TRUNCATE UserSession; TRUNCATE UserSession;
TRUNCATE UserTag; TRUNCATE UserTag;
TRUNCATE TransactionHistory; TRUNCATE TransferTransactions;
TRUNCATE TradeTransactions;
TRUNCATE Vault; TRUNCATE Vault;
DELETE FROM Users; DELETE FROM Users;

View File

@ -371,6 +371,41 @@ function cancelOrder(type, id, floID, proxySecret) {
}) })
} }
function transferToken(receiver, token, amount, floID, proxySecret) {
return new Promise((resolve, reject) => {
if (!floCrypto.validateAddr(receiver))
return reject(INVALID(`Invalid receiver (${receiver})`));
else if (typeof amount !== "number" || amount <= 0)
return reject(`Invalid amount (${amount})`);
let request = {
floID: floID,
token: token,
receiver: receiver,
amount: amount,
timestamp: Date.now()
};
request.sign = signRequest({
type: "transfer_token",
receiver: receiver,
token: token,
amount: amount,
timestamp: request.timestamp
}, proxySecret);
console.debug(request);
exchangeAPI('/transfer-token', {
method: "POST",
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(request)
}).then(result => responseParse(result, false)
.then(result => resolve(result))
.catch(error => reject(error)))
.catch(error => reject(error))
})
}
function depositFLO(quantity, floID, sinkID, privKey, proxySecret) { function depositFLO(quantity, floID, sinkID, privKey, proxySecret) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
if (typeof quantity !== "number" || quantity <= floGlobals.fee) if (typeof quantity !== "number" || quantity <= floGlobals.fee)
@ -382,7 +417,7 @@ function depositFLO(quantity, floID, sinkID, privKey, proxySecret) {
timestamp: Date.now() timestamp: Date.now()
}; };
request.sign = signRequest({ request.sign = signRequest({
type: "deposit_FLO", type: "deposit_flo",
txid: txid, txid: txid,
timestamp: request.timestamp timestamp: request.timestamp
}, proxySecret); }, proxySecret);
@ -410,7 +445,7 @@ function withdrawFLO(quantity, floID, proxySecret) {
timestamp: Date.now() timestamp: Date.now()
}; };
request.sign = signRequest({ request.sign = signRequest({
type: "withdraw_FLO", type: "withdraw_flo",
amount: quantity, amount: quantity,
timestamp: request.timestamp timestamp: request.timestamp
}, proxySecret); }, proxySecret);
@ -440,7 +475,7 @@ function depositToken(token, quantity, floID, sinkID, privKey, proxySecret) {
timestamp: Date.now() timestamp: Date.now()
}; };
request.sign = signRequest({ request.sign = signRequest({
type: "deposit_Token", type: "deposit_token",
txid: txid, txid: txid,
timestamp: request.timestamp timestamp: request.timestamp
}, proxySecret); }, proxySecret);
@ -469,7 +504,7 @@ function withdrawToken(token, quantity, floID, proxySecret) {
timestamp: Date.now() timestamp: Date.now()
}; };
request.sign = signRequest({ request.sign = signRequest({
type: "withdraw_Token", type: "withdraw_token",
token: token, token: token,
amount: quantity, amount: quantity,
timestamp: request.timestamp timestamp: request.timestamp
@ -498,7 +533,7 @@ function addUserTag(tag_user, tag, floID, proxySecret) {
timestamp: Date.now() timestamp: Date.now()
}; };
request.sign = signRequest({ request.sign = signRequest({
command: "add_Tag", type: "add_tag",
user: tag_user, user: tag_user,
tag: tag, tag: tag,
timestamp: request.timestamp timestamp: request.timestamp
@ -527,7 +562,7 @@ function removeUserTag(tag_user, tag, floID, proxySecret) {
timestamp: Date.now() timestamp: Date.now()
}; };
request.sign = signRequest({ request.sign = signRequest({
command: "remove_Tag", type: "remove_tag",
user: tag_user, user: tag_user,
tag: tag, tag: tag,
timestamp: request.timestamp timestamp: request.timestamp

View File

@ -8,7 +8,9 @@ module.exports = {
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: {
MINIMUM_BUY_REQUIREMENT: 0 MINIMUM_BUY_REQUIREMENT: 0,
TRADE_HASH_PREFIX: "z1",
TRANSFER_HASH_PREFIX: "z0"
}, },
price: { price: {
MIN_TIME: 1 * 60 * 60 * 1000, // 1 hr MIN_TIME: 1 * 60 * 60 * 1000, // 1 hr

View File

@ -68,6 +68,9 @@ module.exports = function App(secret, DB) {
//cancel sell or buy order //cancel sell or buy order
app.post('/cancel', Request.CancelOrder); app.post('/cancel', Request.CancelOrder);
//transfer amount to another user
app.post('/transfer-token', Request.TransferToken);
//list sell or buy order //list sell or buy order
app.get('/list-sellorders', Request.ListSellOrders); app.get('/list-sellorders', Request.ListSellOrders);
app.get('/list-buyorders', Request.ListBuyOrders); app.get('/list-buyorders', Request.ListBuyOrders);
@ -86,7 +89,6 @@ module.exports = function App(secret, DB) {
app.post('/withdraw-token', Request.WithdrawToken); app.post('/withdraw-token', Request.WithdrawToken);
//Manage user tags (Access to trusted IDs only) //Manage user tags (Access to trusted IDs only)
app.post('/add-tag', Request.addUserTag); app.post('/add-tag', Request.addUserTag);
app.post('/remove-tag', Request.removeUserTag); app.post('/remove-tag', Request.removeUserTag);

View File

@ -3,10 +3,15 @@
const group = require("./group"); const group = require("./group");
const price = require("./price"); const price = require("./price");
const {
TRADE_HASH_PREFIX
} = require("./_constants")["market"];
var DB; //container for database var DB; //container for database
function startCouplingForAsset(asset) { function startCouplingForAsset(asset) {
price.getRates(asset).then(cur_rate => { price.getRates(asset).then(cur_rate => {
cur_rate = cur_rate.toFixed(3);
group.getBestPairs(asset, cur_rate) group.getBestPairs(asset, cur_rate)
.then(bestPairQueue => processCoupling(bestPairQueue)) .then(bestPairQueue => processCoupling(bestPairQueue))
.catch(error => console.error("initiateCoupling", error)) .catch(error => console.error("initiateCoupling", error))
@ -112,7 +117,12 @@ function updateBalance(seller_best, buyer_best, txQueries, asset, cur_price, qua
//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]])
//Record transaction //Record transaction
txQueries.push(["INSERT INTO TransactionHistory (seller, buyer, asset, quantity, unitValue) VALUES (?, ?, ?, ?, ?)", [seller_best.floID, buyer_best.floID, asset, quantity, cur_price]]); let time = Date.now();
let hash = TRADE_HASH_PREFIX + Crypto.SHA256([time, seller_best.floID, buyer_best.floID, asset, quantity, cur_price].join("|"));
txQueries.push([
"INSERT INTO TradeTransactions (seller, buyer, asset, quantity, unitValue, tx_time, txid) VALUES (?, ?, ?, ?, ?, ?, ?)",
[seller_best.floID, buyer_best.floID, asset, quantity, cur_price, global.convertDateToString(time), hash]
]);
} }
function beginAudit(sellerID, buyerID, asset, unit_price, quantity) { function beginAudit(sellerID, buyerID, asset, unit_price, quantity) {
@ -125,7 +135,7 @@ function beginAudit(sellerID, buyerID, asset, unit_price, quantity) {
function endAudit(sellerID, buyerID, asset, old_bal, unit_price, quantity) { function endAudit(sellerID, buyerID, asset, old_bal, unit_price, quantity) {
auditBalance(sellerID, buyerID, asset).then(new_bal => { auditBalance(sellerID, buyerID, asset).then(new_bal => {
DB.query("INSERT INTO AuditTransaction (asset, quantity, unit_price, total_cost," + DB.query("INSERT INTO AuditTrade (asset, quantity, unit_price, total_cost," +
" sellerID, seller_old_cash, seller_old_asset, seller_new_cash, seller_new_asset," + " sellerID, seller_old_cash, seller_old_asset, seller_new_cash, seller_new_asset," +
" buyerID, buyer_old_cash, buyer_old_asset, buyer_new_cash, buyer_new_asset)" + " buyerID, buyer_old_cash, buyer_old_asset, buyer_new_cash, buyer_new_asset)" +
" Value (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", [ " Value (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", [

View File

@ -86,6 +86,7 @@ function Database(user, password, dbname, host = 'localhost') {
user: user, user: user,
password: password, password: password,
database: dbname, database: dbname,
//dateStrings : true,
timezone: 'UTC' timezone: 'UTC'
}); });
db.connect.then(conn => { db.connect.then(conn => {

View File

@ -3,11 +3,21 @@
const coupling = require('./coupling'); const coupling = require('./coupling');
const { const {
MINIMUM_BUY_REQUIREMENT MINIMUM_BUY_REQUIREMENT,
TRANSFER_HASH_PREFIX
} = require('./_constants')["market"]; } = require('./_constants')["market"];
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 balance FROM Cash WHERE floID=?", [floID]),
@ -32,7 +42,7 @@ getAssetBalance.check = (floID, asset, amount) => new Promise((resolve, reject)
else else
resolve(true); resolve(true);
}).catch(error => reject(error)) }).catch(error => reject(error))
}) });
const consumeAsset = (floID, asset, amount, txQueries = []) => new Promise((resolve, reject) => { const consumeAsset = (floID, asset, amount, txQueries = []) => new Promise((resolve, reject) => {
//If asset/token is currency update Cash else consume from Vault //If asset/token is currency update Cash else consume from Vault
@ -60,8 +70,8 @@ const consumeAsset = (floID, asset, amount, txQueries = []) => new Promise((reso
function addSellOrder(floID, asset, quantity, min_price) { function addSellOrder(floID, asset, quantity, min_price) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
if (!floID || !floCrypto.validateAddr(floID)) if (!floCrypto.validateAddr(floID))
return reject(INVALID("Invalid FLO ID")); return reject(INVALID(`Invalid floID (${floID})`));
else if (typeof quantity !== "number" || quantity <= 0) else if (typeof quantity !== "number" || quantity <= 0)
return reject(INVALID(`Invalid quantity (${quantity})`)); return reject(INVALID(`Invalid quantity (${quantity})`));
else if (typeof min_price !== "number" || min_price <= 0) else if (typeof min_price !== "number" || min_price <= 0)
@ -71,7 +81,7 @@ function addSellOrder(floID, asset, quantity, min_price) {
getAssetBalance.check(floID, asset, quantity).then(_ => { getAssetBalance.check(floID, asset, quantity).then(_ => {
checkSellRequirement(floID, asset).then(_ => { checkSellRequirement(floID, asset).then(_ => {
DB.query("INSERT INTO SellOrder(floID, asset, quantity, minPrice) VALUES (?, ?, ?, ?)", [floID, asset, quantity, min_price]) DB.query("INSERT INTO SellOrder(floID, asset, quantity, minPrice) VALUES (?, ?, ?, ?)", [floID, asset, quantity, min_price])
.then(result => resolve("Added SellOrder to DB")) .then(result => resolve('Sell Order placed successfully'))
.catch(error => reject(error)); .catch(error => reject(error));
}).catch(error => reject(error)) }).catch(error => reject(error))
}).catch(error => reject(error)); }).catch(error => reject(error));
@ -83,7 +93,7 @@ const checkSellRequirement = (floID, asset) => new Promise((resolve, reject) =>
if (result.length) if (result.length)
return resolve(true); return resolve(true);
//TODO: Should seller need to buy same type of asset before selling? //TODO: Should seller need to buy same type of asset before selling?
DB.query("SELECT SUM(quantity) AS brought FROM TransactionHistory WHERE buyer=? AND asset=?", [floID, asset]).then(result => { DB.query("SELECT SUM(quantity) AS brought FROM TradeTransactions WHERE buyer=? AND asset=?", [floID, asset]).then(result => {
if (result[0].brought >= MINIMUM_BUY_REQUIREMENT) if (result[0].brought >= MINIMUM_BUY_REQUIREMENT)
resolve(true); resolve(true);
else else
@ -94,8 +104,8 @@ const checkSellRequirement = (floID, asset) => new Promise((resolve, reject) =>
function addBuyOrder(floID, asset, quantity, max_price) { function addBuyOrder(floID, asset, quantity, max_price) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
if (!floID || !floCrypto.validateAddr(floID)) if (!floCrypto.validateAddr(floID))
return reject(INVALID("Invalid FLO ID")); return reject(INVALID(`Invalid floID (${floID})`));
else if (typeof quantity !== "number" || quantity <= 0) else if (typeof quantity !== "number" || quantity <= 0)
return reject(INVALID(`Invalid quantity (${quantity})`)); return reject(INVALID(`Invalid quantity (${quantity})`));
else if (typeof max_price !== "number" || max_price <= 0) else if (typeof max_price !== "number" || max_price <= 0)
@ -104,7 +114,7 @@ function addBuyOrder(floID, asset, quantity, max_price) {
return reject(INVALID(`Invalid asset (${asset})`)); return reject(INVALID(`Invalid asset (${asset})`));
getAssetBalance.check(floID, floGlobals.currency, quantity * max_price).then(_ => { getAssetBalance.check(floID, floGlobals.currency, quantity * max_price).then(_ => {
DB.query("INSERT INTO BuyOrder(floID, asset, quantity, maxPrice) VALUES (?, ?, ?, ?)", [floID, asset, quantity, max_price]) DB.query("INSERT INTO BuyOrder(floID, asset, quantity, maxPrice) VALUES (?, ?, ?, ?)", [floID, asset, quantity, max_price])
.then(result => resolve("Added BuyOrder to DB")) .then(result => resolve('Buy Order placed successfully'))
.catch(error => reject(error)); .catch(error => reject(error));
}).catch(error => reject(error)); }).catch(error => reject(error));
}); });
@ -112,8 +122,8 @@ function addBuyOrder(floID, asset, quantity, max_price) {
function cancelOrder(type, id, floID) { function cancelOrder(type, id, floID) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
if (!floID || !floCrypto.validateAddr(floID)) if (!floCrypto.validateAddr(floID))
return reject(INVALID("Invalid FLO ID")); return reject(INVALID(`Invalid floID (${floID})`));
let tableName; let tableName;
if (type === "buy") if (type === "buy")
tableName = "BuyOrder"; tableName = "BuyOrder";
@ -166,7 +176,7 @@ function getAccountDetails(floID) {
break; break;
} }
}); });
DB.query("SELECT * FROM TransactionHistory WHERE seller=? OR buyer=?", [floID, floID]) DB.query("SELECT * FROM TradeTransactions WHERE seller=? OR buyer=?", [floID, floID])
.then(result => response.transactions = result) .then(result => response.transactions = result)
.catch(error => console.error(error)) .catch(error => console.error(error))
.finally(_ => resolve(response)); .finally(_ => resolve(response));
@ -174,6 +184,38 @@ function getAccountDetails(floID) {
}); });
} }
function transferToken(sender, receiver, token, amount) {
return new Promise((resolve, reject) => {
if (floCrypto.validateAddr(sender))
return reject(INVALID(`Invalid sender (${sender})`));
else if (floCrypto.validateAddr(receiver))
return reject(INVALID(`Invalid receiver (${receiver})`));
else if (typeof amount !== "number" || amount <= 0)
return reject(INVALID(`Invalid amount (${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))
}).catch(error => reject(error))
}).catch(error => reject(error))
})
}
function depositFLO(floID, txid) { function depositFLO(floID, txid) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
DB.query("SELECT status FROM InputFLO WHERE txid=? AND floID=?", [txid, floID]).then(result => { DB.query("SELECT status FROM InputFLO WHERE txid=? AND floID=?", [txid, floID]).then(result => {
@ -238,8 +280,8 @@ confirmDepositFLO.checkTx = function(sender, txid) {
function withdrawFLO(floID, amount) { function withdrawFLO(floID, amount) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
if (!floID || !floCrypto.validateAddr(floID)) if (!floCrypto.validateAddr(floID))
return reject(INVALID("Invalid FLO ID")); return reject(INVALID(`Invalid floID (${floID})`));
else if (typeof amount !== "number" || amount <= 0) else if (typeof amount !== "number" || amount <= 0)
return reject(INVALID(`Invalid amount (${amount})`)); return reject(INVALID(`Invalid amount (${amount})`));
getAssetBalance.check(floID, "FLO", amount).then(_ => { getAssetBalance.check(floID, "FLO", amount).then(_ => {
@ -372,8 +414,8 @@ confirmDepositToken.checkTx = function(sender, txid) {
function withdrawToken(floID, token, amount) { function withdrawToken(floID, token, amount) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
if (!floID || !floCrypto.validateAddr(floID)) if (!floCrypto.validateAddr(floID))
return reject(INVALID("Invalid FLO ID")); return reject(INVALID(`Invalid floID (${floID})`));
else if (typeof amount !== "number" || amount <= 0) else if (typeof amount !== "number" || amount <= 0)
return reject(INVALID(`Invalid amount (${amount})`)); return reject(INVALID(`Invalid amount (${amount})`));
else if ((!assetList.includes(token) && token !== floGlobals.currency) || token === "FLO") else if ((!assetList.includes(token) && token !== floGlobals.currency) || token === "FLO")
@ -454,6 +496,7 @@ module.exports = {
addSellOrder, addSellOrder,
cancelOrder, cancelOrder,
getAccountDetails, getAccountDetails,
transferToken,
depositFLO, depositFLO,
withdrawFLO, withdrawFLO,
depositToken, depositToken,

View File

@ -165,18 +165,17 @@ function PlaceSellOrder(req, res) {
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).then(req_str => {
market.addSellOrder(data.floID, data.asset, data.quantity, data.min_price) market.addSellOrder(data.floID, data.asset, data.quantity, data.min_price).then(result => {
.then(result => { storeRequest(data.floID, req_str, data.sign);
storeRequest(data.floID, req_str, data.sign); res.send(result);
res.send('Sell Order placed successfully'); }).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); else {
else { console.error(error);
console.error(error); res.status(INTERNAL.e_code).send("Order placement failed! Try again later!");
res.status(INTERNAL.e_code).send("Order placement 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);
@ -196,18 +195,17 @@ function PlaceBuyOrder(req, res) {
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).then(req_str => {
market.addBuyOrder(data.floID, data.asset, data.quantity, data.max_price) market.addBuyOrder(data.floID, data.asset, data.quantity, data.max_price).then(result => {
.then(result => { storeRequest(data.floID, req_str, data.sign);
storeRequest(data.floID, req_str, data.sign); res.send(result);
res.send('Buy Order placed successfully'); }).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); else {
else { console.error(error);
console.error(error); res.status(INTERNAL.e_code).send("Order placement failed! Try again later!");
res.status(INTERNAL.e_code).send("Order placement 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);
@ -226,18 +224,47 @@ function CancelOrder(req, res) {
id: data.orderID, id: data.orderID,
timestamp: data.timestamp timestamp: data.timestamp
}, data.sign, data.floID).then(req_str => { }, data.sign, data.floID).then(req_str => {
market.cancelOrder(data.orderType, data.orderID, data.floID) market.cancelOrder(data.orderType, data.orderID, data.floID).then(result => {
.then(result => { storeRequest(data.floID, req_str, data.sign);
storeRequest(data.floID, req_str, data.sign); res.send(result);
res.send(result); }).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); else {
else { console.error(error);
console.error(error); res.status(INTERNAL.e_code).send("Order cancellation failed! Try again later!");
res.status(INTERNAL.e_code).send("Order cancellation 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!");
}
});
}
function TransferToken(req, res) {
let data = req.body;
validateRequestFromFloID({
type: "transfer_token",
receiver: data.receiver,
token: data.token,
amount: data.amount,
timestamp: data.timestamp
}, data.sign, data.floID).then(req_str => {
market.transferToken(data.floID, data.receiver, data.token, data.amount).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("Token Transfer 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);
@ -264,7 +291,7 @@ function ListBuyOrders(req, res) {
function ListTransactions(req, res) { function ListTransactions(req, res) {
//TODO: Limit size (recent) //TODO: Limit size (recent)
DB.query("SELECT * FROM TransactionHistory ORDER BY tx_time DESC") DB.query("SELECT * FROM TradeTransactions ORDER BY tx_time DESC")
.then(result => res.send(result)) .then(result => res.send(result))
.catch(error => res.status(INTERNAL.e_code).send("Try again later!")); .catch(error => res.status(INTERNAL.e_code).send("Try again later!"));
} }
@ -301,7 +328,7 @@ function Account(req, res) {
function DepositFLO(req, res) { function DepositFLO(req, res) {
let data = req.body; let data = req.body;
validateRequestFromFloID({ validateRequestFromFloID({
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).then(req_str => {
@ -329,7 +356,7 @@ function DepositFLO(req, res) {
function WithdrawFLO(req, res) { function WithdrawFLO(req, res) {
let data = req.body; let data = req.body;
validateRequestFromFloID({ validateRequestFromFloID({
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).then(req_str => {
@ -357,7 +384,7 @@ function WithdrawFLO(req, res) {
function DepositToken(req, res) { function DepositToken(req, res) {
let data = req.body; let data = req.body;
validateRequestFromFloID({ validateRequestFromFloID({
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).then(req_str => {
@ -385,7 +412,7 @@ function DepositToken(req, res) {
function WithdrawToken(req, res) { function WithdrawToken(req, res) {
let data = req.body; let data = req.body;
validateRequestFromFloID({ validateRequestFromFloID({
type: "withdraw_Token", type: "withdraw_token",
token: data.token, token: data.token,
amount: data.amount, amount: data.amount,
timestamp: data.timestamp timestamp: data.timestamp
@ -416,7 +443,7 @@ function addUserTag(req, res) {
if (!trustedIDs.includes(data.floID)) if (!trustedIDs.includes(data.floID))
return res.status(INVALID.e_code).send("Access Denied"); return res.status(INVALID.e_code).send("Access Denied");
validateRequestFromFloID({ validateRequestFromFloID({
command: "add_Tag", type: "add_tag",
user: data.user, user: data.user,
tag: data.tag, tag: data.tag,
timestamp: data.timestamp timestamp: data.timestamp
@ -448,7 +475,7 @@ function removeUserTag(req, res) {
return res.status(INVALID.e_code).send("Access Denied"); return res.status(INVALID.e_code).send("Access Denied");
else else
validateRequestFromFloID({ validateRequestFromFloID({
command: "remove_Tag", type: "remove_tag",
user: data.user, user: data.user,
tag: data.tag, tag: data.tag,
timestamp: data.timestamp timestamp: data.timestamp
@ -482,6 +509,7 @@ module.exports = {
PlaceBuyOrder, PlaceBuyOrder,
PlaceSellOrder, PlaceSellOrder,
CancelOrder, CancelOrder,
TransferToken,
ListSellOrders, ListSellOrders,
ListBuyOrders, ListBuyOrders,
ListTransactions, ListTransactions,

View File

@ -2,6 +2,16 @@
//fetch for node js (used in floBlockchainAPI.js) //fetch for node js (used in floBlockchainAPI.js)
global.fetch = require("node-fetch"); global.fetch = require("node-fetch");
global.convertDateToString = function(timestamp) {
let date = new Date(timestamp);
return date.getFullYear() + '-' +
('00' + (date.getMonth() + 1)).slice(-2) + '-' +
('00' + date.getDate()).slice(-2) + ' ' +
('00' + date.getHours()).slice(-2) + ':' +
('00' + date.getMinutes()).slice(-2) + ':' +
('00' + date.getSeconds()).slice(-2);
}
//Set browser paramaters from param.json (or param-default.json) //Set browser paramaters from param.json (or param-default.json)
var param; var param;
try { try {