Fixes and improvements in validation
This commit is contained in:
parent
eb7513663f
commit
b169ee7d61
@ -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)] };
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user