Transfer transactions now support one-to-many

- receiver parameter now requires an object in the format
{floID1: amount1, floID2: amount2 ...}
- Transaction hash (id) for both trade and transfer transactions are now generated using SHA of JSON-string object
- Updated schema for one-to-many TransferTransactions
This commit is contained in:
sairajzero 2022-03-16 00:33:22 +05:30
parent f59c6b3f2a
commit 1823a46a98
5 changed files with 68 additions and 36 deletions

View File

@ -157,9 +157,9 @@ CREATE TABLE PriceHistory (
CREATE TABLE TransferTransactions ( CREATE TABLE TransferTransactions (
id INT NOT NULL AUTO_INCREMENT, id INT NOT NULL AUTO_INCREMENT,
sender CHAR(34) NOT NULL, sender CHAR(34) NOT NULL,
receiver CHAR(34) NOT NULL, receiver TEXT NOT NULL,
token VARCHAR(64) NOT NULL, token VARCHAR(64) NOT NULL,
amount FLOAT NOT NULL, totalAmount FLOAT NOT NULL,
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),

View File

@ -396,26 +396,35 @@ function cancelOrder(type, id, floID, proxySecret) {
}) })
} }
function transferToken(receiver, token, amount, floID, proxySecret) { //receiver should be object eg {floID1: amount1, floID2: amount2 ...}
function transferToken(receiver, token, floID, proxySecret) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
if (!floCrypto.validateAddr(receiver)) if (typeof receiver !== Object || receiver === null)
return reject(INVALID(`Invalid receiver (${receiver})`)); return reject("Invalid receiver: parameter is not an object");
else if (typeof amount !== "number" || amount <= 0) let invalidIDs = [],
return reject(`Invalid amount (${amount})`); invalidAmt = [];
for (let f in receiver) {
if (!floCrypto.validateAddr(f))
invalidIDs.push(f);
else if (typeof receiver[f] !== "number" || receiver[f] <= 0)
invalidAmt.push(receiver[f])
}
if (invalidIDs.length)
return reject(INVALID(`Invalid receiver (${invalidIDs})`));
else if (invalidAmt.length)
return reject(`Invalid amount (${invalidAmt})`);
let request = { let request = {
floID: floID, floID: floID,
token: token, token: token,
receiver: receiver, receiver: receiver,
amount: amount,
timestamp: Date.now() timestamp: Date.now()
}; };
if (floCrypto.getFloID(proxySecret) === floID) //Direct signing (without proxy) if (floCrypto.getFloID(proxySecret) === floID) //Direct signing (without proxy)
request.pubKey = floCrypto.getPubKeyHex(proxySecret); request.pubKey = floCrypto.getPubKeyHex(proxySecret);
request.sign = signRequest({ request.sign = signRequest({
type: "transfer_token", type: "transfer_token",
receiver: receiver, receiver: JSON.stringify(receiver),
token: token, token: token,
amount: amount,
timestamp: request.timestamp timestamp: request.timestamp
}, proxySecret); }, proxySecret);
console.debug(request); console.debug(request);

View File

@ -118,7 +118,14 @@ function updateBalance(seller_best, buyer_best, txQueries, asset, cur_price, qua
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
let time = Date.now(); let time = Date.now();
let hash = TRADE_HASH_PREFIX + Crypto.SHA256([time, seller_best.floID, buyer_best.floID, asset, quantity, cur_price].join("|")); let hash = TRADE_HASH_PREFIX + Crypto.SHA256(JSON.stringify({
seller: seller_best.floID,
buyer: buyer_best.floID,
asset: asset,
quantity: quantity,
unitValue: cur_price,
tx_time: time,
}));
txQueries.push([ txQueries.push([
"INSERT INTO TradeTransactions (seller, buyer, asset, quantity, unitValue, tx_time, txid) VALUES (?, ?, ?, ?, ?, ?, ?)", "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] [seller_best.floID, buyer_best.floID, asset, quantity, cur_price, global.convertDateToString(time), hash]

View File

@ -190,39 +190,56 @@ function getTransactionDetails(txid) {
if (result.length) { if (result.length) {
let details = result[0]; let details = result[0];
details.type = type; details.type = type;
if (tableName === 'TransferTransactions') //As json object is stored for receiver in transfer (to support one-to-many)
details.receiver = JSON.parse(details.receiver);
resolve(details); resolve(details);
} else } else
reject(INVALID("Transaction not found")); reject(INVALID("Transaction not found"));
}).catch(error => reject(error)) }).catch(error => reject(error))
} }
function transferToken(sender, receiver, token, amount) { function transferToken(sender, receivers, token) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
if (floCrypto.validateAddr(sender)) if (floCrypto.validateAddr(sender))
return reject(INVALID(`Invalid sender (${sender})`)); 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)) else if (token !== floGlobals.currency && !assetList.includes(token))
return reject(INVALID(`Invalid token (${token})`)); reject(INVALID(`Invalid token (${token})`));
getAssetBalance.check(senderID, token, amount).then(_ => { else {
consumeAsset(sender, token, amount).then(txQueries => { let invalidIDs = [],
if (token === floGlobals.currency) totalAmount = 0;
txQueries.push(["INSERT INTO Cash (floID, balance) VALUE (?, ?) ON DUPLICATE KEY UPDATE balance=balance+?", [receiver, amount, amount]]); for (let floID in receivers)
if (!floCrypto.validateAddr(floID))
invalidIDs.push(floID);
else else
txQueries.push(["INSERT INTO Vault(floID, quantity) VALUES (?, ?)", [receiver, amount]]); totalAmount += receivers[floID];
let time = Date.now(); if (invalidIDs.length)
let hash = TRANSFER_HASH_PREFIX + Crypto.SHA256([time, sender, receiver, token, amount].join("|")); reject(INVALID(`Invalid receiver (${invalidIDs})`));
txQueries.push([ else getAssetBalance.check(senderID, token, totalAmount).then(_ => {
"INSERT INTO TransferTransactions (sender, receiver, token, amount, tx_time, txid)", consumeAsset(sender, token, totalAmount).then(txQueries => {
[sender, receiver, token, amount, global.convertDateToString(time), hash] if (token === floGlobals.currency)
]); for (let floID in receivers)
DB.transaction(txQueries) txQueries.push(["INSERT INTO Cash (floID, balance) VALUE (?, ?) ON DUPLICATE KEY UPDATE balance=balance+?", [floID, receivers[floID], receivers[floID]]]);
.then(result => resolve(hash)) else
.catch(error => reject(error)) for (let floID in receivers)
txQueries.push(["INSERT INTO Vault(floID, quantity) VALUES (?, ?)", [floID, receivers[floID]]]);
let time = Date.now();
let hash = TRANSFER_HASH_PREFIX + Crypto.SHA256(JSON.stringify({
sender: sender,
receiver: receivers,
token: token,
totalAmount: totalAmount,
tx_time: time,
}));
txQueries.push([
"INSERT INTO TransferTransactions (sender, receiver, token, totalAmount, tx_time, txid)",
[sender, JSON.stringify(receiver), token, totalAmount, 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))
}).catch(error => reject(error)) }
}) })
} }

View File

@ -275,12 +275,11 @@ function TransferToken(req, res) {
let data = req.body; let data = req.body;
validateRequest({ validateRequest({
type: "transfer_token", type: "transfer_token",
receiver: data.receiver, receiver: JSON.stringify(data.receiver),
token: data.token, token: data.token,
amount: data.amount,
timestamp: data.timestamp timestamp: data.timestamp
}, data.sign, data.floID, data.pubKey).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).then(result => {
storeRequest(data.floID, req_str, data.sign, !data.pubKey); storeRequest(data.floID, req_str, data.sign, !data.pubKey);
res.send(result); res.send(result);
}).catch(error => { }).catch(error => {