Fixes and improvements in validation

This commit is contained in:
sairajzero 2023-07-23 06:13:10 +05:30
parent eb7513663f
commit b169ee7d61

View File

@ -116,10 +116,9 @@
return loanEquivalent * inverse_security_percent; return loanEquivalent * inverse_security_percent;
} }
function calcDueAmount(loan_amount, policy_id, open_time) { function calcDueAmount(loan_amount, policy_id, open_time, close_time = Date.now()) {
let policy = POLICIES[policy_id], let policy = POLICIES[policy_id],
current_time = Date.now(), duration = yearDiff(close_time, open_time);
duration = yearDiff(current_time, open_time);
let interest_amount = loan_amount * policy.interest * duration; let interest_amount = loan_amount * policy.interest * duration;
let due_amount = loan_amount + interest_amount; let due_amount = loan_amount + interest_amount;
return due_amount; return due_amount;
@ -257,11 +256,11 @@
*/ */
} }
function parseLoanOpenData(str, tx_time) { function parseLoanOpenData(str, txid, tx_time) {
let splits = str.split('|'); let splits = str.split('|');
if (splits[0] !== LOAN_DETAILS_IDENTIFIER) if (splits[0] !== LOAN_DETAILS_IDENTIFIER)
throw "Invalid Loan blockchain data"; throw "Invalid Loan blockchain data";
var details = { open_time: tx_time }; var details = { loan_id: txid, open_time: tx_time };
splits.forEach(s => { splits.forEach(s => {
let d = s.split(':'); let d = s.split(':');
switch (d[0]) { switch (d[0]) {
@ -285,7 +284,7 @@
const getLoanDetails = btcMortgage.getLoanDetails = function (loan_id) { const getLoanDetails = btcMortgage.getLoanDetails = function (loan_id) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
floBlockchainAPI.getTx(loan_id).then(tx => { floBlockchainAPI.getTx(loan_id).then(tx => {
let parsed_loan_details = parseLoanOpenData(tx.floData, tx.time); let parsed_loan_details = parseLoanOpenData(tx.floData, tx.txid, tx.time);
validateLoanDetails(parsed_loan_details) validateLoanDetails(parsed_loan_details)
.then(_ => resolve(parsed_loan_details)) .then(_ => resolve(parsed_loan_details))
.catch(error => reject(error)) .catch(error => reject(error))
@ -312,7 +311,45 @@
return reject("Invalid coborrower signature"); return reject("Invalid coborrower signature");
if (!verify_lenderSign(loan_details.lender_sign, loan_details.lender, loan_details.coborrower_sign, loan_details.loan_transfer_id)) if (!verify_lenderSign(loan_details.lender_sign, loan_details.lender, loan_details.coborrower_sign, loan_details.loan_transfer_id))
return reject("Invalid lender signature"); return reject("Invalid lender signature");
resolve(true) validateCollateralLock(loan_details.collateral_lock_id, extractPubKeyFromSign(loan_details.coborrower_sign), extractPubKeyFromSign(loan_details.lender_sign), loan_details.collateral_value).then(result => {
validateLoanOpenTokenTransfer(loan_details.loan_transfer_id, loan_details.borrower, loan_details.lender, loan_details.loan_amount)
.then(result => resolve(true))
.catch(error => reject(error))
}).catch(error => reject(error))
})
}
const validateCollateralLock = btcMortgage.validateCollateralLock = function (collateral_lock_id, coborrower_pubKey, lender_pubKey, collateral_value) {
return new Promise((resolve, reject) => {
btcOperator.getTx(collateral_lock_id).then(collateral_tx => {
if (!collateral_tx.confirmations)
return reject("Collateral lock transaction not confirmed yet");
let locker_id = findLocker(coborrower_pubKey, lender_pubKey).address;
let locked_amt = collateral_tx.outputs.filter(o => o.address == locker_id).reduce((a, o) => a += o.value, 0);
if (locked_amt < collateral_value)
return reject("Insufficient Collateral locked");
resolve(true);
}).catch(error => reject(error))
})
}
const validateLoanOpenTokenTransfer = btcMortgage.validateLoanOpenTokenTransfer = function (loan_transfer_id, borrower, lender, loan_amount) {
return new Promise((resolve, reject) => {
floTokenAPI.getTx(loan_transfer_id).then(token_tx => {
if (token_tx.parsedFloData.type != "transfer" || token_tx.parsedFloData.transferType != "token")
return reject("Transaction is not a token transfer");
if (token_tx.parsedFloData.tokenIdentification != CURRENCY)
return reject("Transfered token is not " + CURRENCY);
if (token_tx.transactionDetails.confirmations == 0)
return reject("Transaction not yet confirmed");
if (token_tx.transactionDetails.receiverAddress != floCrypto.toFloID(borrower))
return reject("Receiver is not borrower");
if (token_tx.transactionDetails.senderAddress != floCrypto.toFloID(lender))
return reject("Sender is not lender");
if (token_tx.parsedFloData.tokenAmount !== loan_amount)
return reject("Token amount doesnot match the loan amount");
resolve(true);
}).catch(error => reject(error))
}) })
} }
@ -326,11 +363,11 @@
].join('|'); ].join('|');
} }
function parseLoanCloseData(str, tx_time) { function parseLoanCloseData(str, txid, tx_time) {
let splits = str.split('|'); let splits = str.split('|');
if (splits[0] !== LOAN_CLOSING_IDENTIFIER) if (splits[1] !== LOAN_CLOSING_IDENTIFIER) //splits[0] will be token transfer
throw "Invalid Loan closing data"; throw "Invalid Loan closing data";
var details = { close_time: tx_time }; var details = { close_time: tx_time, close_id: txid };
splits.forEach(s => { splits.forEach(s => {
let d = s.split(':'); let d = s.split(':');
switch (d[0]) { switch (d[0]) {
@ -342,19 +379,54 @@
return details; return details;
} }
const getLoanClosing = btcMortgage.getLoanClosing = function (loan_id, closing_txid) { const getLoanClosing = btcMortgage.getLoanClosing = function (closing_txid) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
floBlockchainAPI.getTx(closing_txid).then(tx => { floBlockchainAPI.getTx(closing_txid).then(tx => {
let parsed_loan_closing = parseLoanCloseData(tx.floData, tx.time); let closing_details = parseLoanCloseData(tx.floData, tx.time);
validateLoanClosing(parsed_loan_closing) getLoanDetails(closing_details.loan_id).then(loan_details => {
.then(_ => resolve(parsed_loan_closing)) validateLoanClosing(loan_details, closing_details)
.catch(error => reject(error)) .then(result => resolve(Object.assign(loan_details, closing_details)))
.catch(error => reject(error))
}).catch(error => reject(error))
}).catch(error => reject(error)) }).catch(error => reject(error))
}) })
} }
const validateLoanClosing = btcMortgage.validateLoanClosing = function (loan_id, closing_txid) { const validateLoanClosing = btcMortgage.validateLoanClosing = function (loan_details, closing_details) {
//TODO return new Promise((resolve, reject) => {
if (closing_details.loan_id !== loan_details.loan_id)
return reject("Closing doesnot belong to this loan")
if (!floCrypto.validateFloID(closing_details.borrower))
return reject("Invalid borrower floID");
if (closing_details.borrower != loan_details.borrower)
return reject("Borrower ID is different");
if (verify_closingSign(closing_details.closing_sign, closing_details.borrower, closing_details.loan_id, loan_details.lender_sign))
return reject("Invalid closing signature");
validateLoanCloseTokenTransfer(closing_details.close_id, loan_details.borrower, loan_details.lender, loan_details.loan_amount, loan_details.policy_id, loan_details.open_time, closing_details.close_time)
.then(result => resolve(true))
.catch(error => reject(error))
})
}
const validateLoanCloseTokenTransfer = btcMortgage.validateLoanCloseTokenTransfer = function (close_id, borrower, lender, loan_amount, policy_id, open_time, close_time) {
return new Promise((resolve, reject) => {
floTokenAPI.getTx(close_id).then(token_tx => {
if (token_tx.parsedFloData.type != "transfer" || token_tx.parsedFloData.transferType != "token")
return reject("Transaction is not a token transfer");
if (token_tx.parsedFloData.tokenIdentification != CURRENCY)
return reject("Transfered token is not " + CURRENCY);
if (token_tx.transactionDetails.confirmations == 0)
return reject("Transaction not yet confirmed");
if (token_tx.transactionDetails.receiverAddress != floCrypto.toFloID(lender))
return reject("Receiver is not lender");
if (token_tx.transactionDetails.senderAddress != floCrypto.toFloID(borrower))
return reject("Sender is not borrower");
let repay_amount = calcDueAmount(loan_amount, policy_id, open_time, close_time);
if (token_tx.parsedFloData.tokenAmount < repay_amount)
return reject("Token amount is less than loan repayment amount");
resolve(true);
}).catch(error => reject(error))
})
} }
/*Signature and verification */ /*Signature and verification */
@ -877,7 +949,7 @@
//repay and close the loan //repay and close the loan
let closing_sign = sign_closing(privKey, loan_id, loan_details.lender_sign); let closing_sign = sign_closing(privKey, loan_id, loan_details.lender_sign);
var closing_data = stringifyLoanCloseData(loan_id, loan_details.borrower, closing_sign); var closing_data = stringifyLoanCloseData(loan_id, loan_details.borrower, closing_sign);
floTokenAPI.sendToken(privKey, due_amount, loan_details.lender, closing_data, CURRENCY).then(closing_txid => { floTokenAPI.sendToken(privKey, due_amount, loan_details.lender, "|" + closing_data, CURRENCY).then(closing_txid => {
//send message to coborrower as reminder to unlock collateral //send message to coborrower as reminder to unlock collateral
floCloudAPI.sendApplicationData({ loan_id, closing_txid }, TYPE_LOAN_CLOSED_ACK, { receiverID: loan_details.coborrower }) floCloudAPI.sendApplicationData({ loan_id, closing_txid }, TYPE_LOAN_CLOSED_ACK, { receiverID: loan_details.coborrower })
.then(result => { .then(result => {
@ -1312,7 +1384,10 @@
//check output //check output
let return_amount = total_collateral_value - due_amount; let return_amount = total_collateral_value - due_amount;
if (return_amount > 0) { if (return_amount > 0) {
//TODO: check if the return amount and receiver is correct let return_outpts_amount = tx.outs.filter(o => spendScriptToAddress(o.script) == coborrower_btcID).reduce((a, o) => a += o.value, 0)
return_outpts_amount = btcOperator.util.Sat_to_BTC(return_outpts_amount);
if (return_outpts_amount < return_amount)
return reject("Return value after liquidation is lower")
} }
//sign the tx hex //sign the tx hex
tx.sign(privKey, 1 /*sighashtype*/); tx.sign(privKey, 1 /*sighashtype*/);
@ -1322,6 +1397,21 @@
}) })
} }
function spendScriptToAddress(script) {
var address;
switch (script.chunks[0]) {
case 0: //bech32, multisig-bech32
address = btcOperator.util.encodeBech32(Crypto.util.bytesToHex(script.chunks[1]), coinjs.bech32.version, coinjs.bech32.hrp);
break;
case 169: //segwit, multisig-segwit
address = btcOperator.util.encodeLegacy(Crypto.util.bytesToHex(script.chunks[1]), coinjs.multisig);
break;
case 118: //legacy
address = btcOperator.util.encodeLegacy(Crypto.util.bytesToHex(script.chunks[2]), coinjs.pub);
}
return address;
}
function checkIfLoanClosed(loan_id, borrower, lender) { function checkIfLoanClosed(loan_id, borrower, lender) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
var query_options = { sentOnly: true, tx: true, receivers: [floCrypto.toFloID(lender)] }; var query_options = { sentOnly: true, tx: true, receivers: [floCrypto.toFloID(lender)] };