From d558f46110aa83745da8967e45e3aa9dc7c77c21 Mon Sep 17 00:00:00 2001 From: jouzo Date: Tue, 14 Dec 2021 14:57:37 +0100 Subject: [PATCH 01/13] Scenario where BTC increase and price and should be able to be withdrawn --- test/functional/feature_loan_vault.py | 45 +++++++++++++++++++++++---- 1 file changed, 39 insertions(+), 6 deletions(-) diff --git a/test/functional/feature_loan_vault.py b/test/functional/feature_loan_vault.py index e02821b503..0dea3cc479 100755 --- a/test/functional/feature_loan_vault.py +++ b/test/functional/feature_loan_vault.py @@ -18,8 +18,8 @@ def set_test_params(self): self.num_nodes = 2 self.setup_clean_chain = True self.extra_args = [ - ['-txnotokens=0', '-amkheight=1', '-bayfrontheight=1', '-eunosheight=1', '-txindex=1', '-fortcanningheight=1'], - ['-txnotokens=0', '-amkheight=1', '-bayfrontheight=1', '-eunosheight=1', '-txindex=1', '-fortcanningheight=1'] + ['-txnotokens=0', '-amkheight=1', '-bayfrontheight=1', '-bayfrontgardensheight=1', '-eunosheight=1', '-txindex=1', '-fortcanningheight=1'], + ['-txnotokens=0', '-amkheight=1', '-bayfrontheight=1', '-bayfrontgardensheight=1', '-eunosheight=1', '-txindex=1', '-fortcanningheight=1'] ] def run_test(self): @@ -424,28 +424,24 @@ def run_test(self): estimatevault = self.nodes[0].estimatevault('3.00000000@DFI', '3.00000000@TSLAA') except JSONRPCException as e: errorString = e.error['message'] - print("errorString", errorString) assert("Invalid Defi token: TSLAA" in errorString) # Invalid collateral token try: estimatevault = self.nodes[0].estimatevault('3.00000000@DFII', '3.00000000@TSLA') except JSONRPCException as e: errorString = e.error['message'] - print("errorString", errorString) assert("Invalid Defi token: DFII" in errorString) # Token not set as a collateral try: estimatevault = self.nodes[0].estimatevault('3.00000000@TSLA', '3.00000000@TSLA') except JSONRPCException as e: errorString = e.error['message'] - print("errorString", errorString) assert("Token with id (2) is not a valid collateral!" in errorString) # Token not set as loan token try: estimatevault = self.nodes[0].estimatevault('3.00000000@DFI', '3.00000000@DFI') except JSONRPCException as e: errorString = e.error['message'] - print("errorString", errorString) assert("Token with id (0) is not a loan token!" in errorString) vault = self.nodes[0].getvault(vaultId2) @@ -455,5 +451,42 @@ def run_test(self): assert_equal(estimatevault["informativeRatio"], vault["informativeRatio"]) assert_equal(estimatevault["collateralRatio"], vault["collateralRatio"]) + + # Test BTC price increase and remove some BTC from collateral + + # Reset price + oracle1_prices = [{"currency": "USD", "tokenAmount": "1@DFI"}, {"currency": "USD", "tokenAmount": "1@TSLA"}, {"currency": "USD", "tokenAmount": "1@BTC"}] + timestamp = calendar.timegm(time.gmtime()) + self.nodes[0].setoracledata(oracle_id1, timestamp, oracle1_prices) + self.nodes[0].generate(11) + + # Deposit collaterals. 50% of BTC + address = self.nodes[0].getnewaddress() + self.nodes[1].sendtokenstoaddress({}, { address: '1.25@BTC'}) + self.nodes[1].generate(1) + self.sync_all() + vaultId4 = self.nodes[0].createvault(address, 'LOAN000A') + self.nodes[0].generate(1) + self.nodes[0].deposittovault(vaultId4, address, '1.25@BTC') # 1.25@BTC as collateral factor 0.8 + self.nodes[0].deposittovault(vaultId4, accountDFI, '1@DFI') + self.nodes[0].generate(1) + + self.nodes[0].takeloan({ + 'vaultId': vaultId4, + 'amounts': "1@TSLA" + }) + self.nodes[0].generate(1) + + # BTC doubles in price + oracle1_prices = [{"currency": "USD", "tokenAmount": "1@DFI"}, {"currency": "USD", "tokenAmount": "1@TSLA"}, {"currency": "USD", "tokenAmount": "2@BTC"}] + timestamp = calendar.timegm(time.gmtime()) + self.nodes[0].setoracledata(oracle_id1, timestamp, oracle1_prices) + self.nodes[0].generate(11) + + # Should be able to withdraw part of BTC + self.nodes[0].withdrawfromvault(vaultId4, address, "0.1@BTC") + self.nodes[0].generate(1) + vault = self.nodes[0].getvault(vaultId4) + if __name__ == '__main__': VaultTest().main() From 866b94e79305dd0e33ee9fea35353d3c14cf9368 Mon Sep 17 00:00:00 2001 From: jouzo Date: Tue, 14 Dec 2021 15:06:10 +0100 Subject: [PATCH 02/13] Check that 50% of the collateralization comes from DFI only when DFI is withdrawn --- src/masternodes/mn_checks.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/masternodes/mn_checks.cpp b/src/masternodes/mn_checks.cpp index cf15e51063..d172409ae1 100644 --- a/src/masternodes/mn_checks.cpp +++ b/src/masternodes/mn_checks.cpp @@ -2505,7 +2505,7 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor if (col.nTokenId == DCT_ID{0}) totalDFI += col.nValue; - if (totalDFI < collateralsLoans.val->totalCollaterals / 2) + if (totalDFI < collateralsLoans.val->totalCollaterals / 2 && obj.amount.nTokenId == DCT_ID{0}) return Res::Err("At least 50%% of the vault must be in DFI"); if (collateralsLoans.val->ratio() < scheme->ratio) From dd3cc25b56791114724086e8be44db55cc3cad13 Mon Sep 17 00:00:00 2001 From: Jouzo Date: Tue, 14 Dec 2021 15:10:53 +0100 Subject: [PATCH 03/13] Remove unused var --- test/functional/feature_loan_vault.py | 1 - 1 file changed, 1 deletion(-) diff --git a/test/functional/feature_loan_vault.py b/test/functional/feature_loan_vault.py index 0dea3cc479..c933b98f33 100755 --- a/test/functional/feature_loan_vault.py +++ b/test/functional/feature_loan_vault.py @@ -486,7 +486,6 @@ def run_test(self): # Should be able to withdraw part of BTC self.nodes[0].withdrawfromvault(vaultId4, address, "0.1@BTC") self.nodes[0].generate(1) - vault = self.nodes[0].getvault(vaultId4) if __name__ == '__main__': VaultTest().main() From a513f260b0f962147d414b62d017e9987e2874a3 Mon Sep 17 00:00:00 2001 From: jouzo Date: Wed, 22 Dec 2021 12:11:54 +0100 Subject: [PATCH 04/13] Adds guard against fortcanninghill height --- src/chainparams.cpp | 9 +++++++-- src/consensus/params.h | 1 + src/init.cpp | 1 + src/masternodes/mn_checks.cpp | 16 +++++++++------- src/rpc/blockchain.cpp | 1 + test/functional/feature_loan_vault.py | 4 ++-- test/functional/rpc_blockchain.py | 1 + 7 files changed, 22 insertions(+), 11 deletions(-) diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 9f7f48bbe9..f32197ca7a 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -128,6 +128,7 @@ class CMainParams : public CChainParams { consensus.EunosPayaHeight = 1072000; // Aug 05, 2021. consensus.FortCanningHeight = 1367000; // Nov 15, 2021. consensus.FortCanningMuseumHeight = 1430640; + consensus.FortCanningHillHeight = std::numeric_limits::max(); consensus.pos.diffLimit = uint256S("00000fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); // consensus.pos.nTargetTimespan = 14 * 24 * 60 * 60; // two weeks @@ -348,6 +349,7 @@ class CTestNetParams : public CChainParams { consensus.EunosPayaHeight = 463300; consensus.FortCanningHeight = 686200; consensus.FortCanningMuseumHeight = 724000; + consensus.FortCanningHillHeight = std::numeric_limits::max(); consensus.pos.diffLimit = uint256S("00000fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); // consensus.pos.nTargetTimespan = 14 * 24 * 60 * 60; // two weeks @@ -530,6 +532,7 @@ class CDevNetParams : public CChainParams { consensus.EunosPayaHeight = 300; consensus.FortCanningHeight = std::numeric_limits::max(); consensus.FortCanningMuseumHeight = std::numeric_limits::max(); + consensus.FortCanningHillHeight = std::numeric_limits::max(); consensus.pos.diffLimit = uint256S("00000fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); consensus.pos.nTargetTimespan = 5 * 60; // 5 min == 10 blocks @@ -704,6 +707,7 @@ class CRegTestParams : public CChainParams { consensus.EunosPayaHeight = 10000000; consensus.FortCanningHeight = 10000000; consensus.FortCanningMuseumHeight = 10000000; + consensus.FortCanningHillHeight = 10000000; consensus.pos.diffLimit = uint256S("00000fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); consensus.pos.nTargetTimespan = 14 * 24 * 60 * 60; // two weeks @@ -915,8 +919,9 @@ void CRegTestParams::UpdateActivationParametersFromArgs(const ArgsManager& args) consensus.EunosKampungHeight = static_cast(eunosHeight.get()); } UpdateHeightValidation("Eunos Paya", "-eunospayaheight", consensus.EunosPayaHeight); - UpdateHeightValidation("Fork canning", "-fortcanningheight", consensus.FortCanningHeight); - UpdateHeightValidation("Fork canning museum", "-fortcanningmuseumheight", consensus.FortCanningMuseumHeight); + UpdateHeightValidation("Fort canning", "-fortcanningheight", consensus.FortCanningHeight); + UpdateHeightValidation("Fort canning museum", "-fortcanningmuseumheight", consensus.FortCanningMuseumHeight); + UpdateHeightValidation("Fort canning hill", "-fortcanninghillheight", consensus.FortCanningHillHeight); if (!args.IsArgSet("-vbparams")) return; diff --git a/src/consensus/params.h b/src/consensus/params.h index 28caa9fe43..f2da593335 100644 --- a/src/consensus/params.h +++ b/src/consensus/params.h @@ -91,6 +91,7 @@ struct Params { int EunosPayaHeight; int FortCanningHeight; int FortCanningMuseumHeight; + int FortCanningHillHeight; /** Foundation share after AMK, normalized to COIN = 100% */ CAmount foundationShareDFIP1; diff --git a/src/init.cpp b/src/init.cpp index 3660aedad7..5e404ceebc 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -473,6 +473,7 @@ void SetupServerArgs() gArgs.AddArg("-eunospayaheight", "EunosPaya fork activation height (regtest only)", ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS); gArgs.AddArg("-fortcanningheight", "Fort Canning fork activation height (regtest only)", ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS); gArgs.AddArg("-fortcanningmuseumheight", "Fort Canning Museum fork activation height (regtest only)", ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS); + gArgs.AddArg("-fortcanninghillheight", "Fort Canning Hill fork activation height (regtest only)", ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS); gArgs.AddArg("-jellyfish_regtest", "Configure the regtest network for jellyfish testing", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); #ifdef USE_UPNP #if USE_UPNP diff --git a/src/masternodes/mn_checks.cpp b/src/masternodes/mn_checks.cpp index d172409ae1..48e62e7dfc 100644 --- a/src/masternodes/mn_checks.cpp +++ b/src/masternodes/mn_checks.cpp @@ -2500,13 +2500,15 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor if (!collateralsLoans) return std::move(collateralsLoans); - uint64_t totalDFI = 0; - for (auto& col : collateralsLoans.val->collaterals) - if (col.nTokenId == DCT_ID{0}) - totalDFI += col.nValue; - - if (totalDFI < collateralsLoans.val->totalCollaterals / 2 && obj.amount.nTokenId == DCT_ID{0}) - return Res::Err("At least 50%% of the vault must be in DFI"); + if (obj.amount.nTokenId == DCT_ID{0} || static_cast(height) < consensus.FortCanningHillHeight) { + uint64_t totalDFI = 0; + for (auto& col : collateralsLoans.val->collaterals) + if (col.nTokenId == DCT_ID{0}) + totalDFI += col.nValue; + + if (totalDFI < collateralsLoans.val->totalCollaterals / 2) + return Res::Err("At least 50%% of the vault must be in DFI"); + } if (collateralsLoans.val->ratio() < scheme->ratio) return Res::Err("Vault does not have enough collateralization ratio defined by loan scheme - %d < %d", collateralsLoans.val->ratio(), scheme->ratio); diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index ca929e0c66..1c17234ce6 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1362,6 +1362,7 @@ UniValue getblockchaininfo(const JSONRPCRequest& request) BuriedForkDescPushBack(softforks, "eunospaya", consensusParams.EunosPayaHeight); BuriedForkDescPushBack(softforks, "fortcanning", consensusParams.FortCanningHeight); BuriedForkDescPushBack(softforks, "fortcanningmuseum", consensusParams.FortCanningMuseumHeight); + BuriedForkDescPushBack(softforks, "fortcanninghill", consensusParams.FortCanningHillHeight); BIP9SoftForkDescPushBack(softforks, "testdummy", consensusParams, Consensus::DEPLOYMENT_TESTDUMMY); obj.pushKV("softforks", softforks); diff --git a/test/functional/feature_loan_vault.py b/test/functional/feature_loan_vault.py index c933b98f33..7cf42b3b2e 100755 --- a/test/functional/feature_loan_vault.py +++ b/test/functional/feature_loan_vault.py @@ -18,8 +18,8 @@ def set_test_params(self): self.num_nodes = 2 self.setup_clean_chain = True self.extra_args = [ - ['-txnotokens=0', '-amkheight=1', '-bayfrontheight=1', '-bayfrontgardensheight=1', '-eunosheight=1', '-txindex=1', '-fortcanningheight=1'], - ['-txnotokens=0', '-amkheight=1', '-bayfrontheight=1', '-bayfrontgardensheight=1', '-eunosheight=1', '-txindex=1', '-fortcanningheight=1'] + ['-txnotokens=0', '-amkheight=1', '-bayfrontheight=1', '-bayfrontgardensheight=1', '-eunosheight=1', '-txindex=1', '-fortcanningheight=1', '-fortcanninghillheight=1'], + ['-txnotokens=0', '-amkheight=1', '-bayfrontheight=1', '-bayfrontgardensheight=1', '-eunosheight=1', '-txindex=1', '-fortcanningheight=1', '-fortcanninghillheight=1'] ] def run_test(self): diff --git a/test/functional/rpc_blockchain.py b/test/functional/rpc_blockchain.py index e0860f316a..7932038eef 100755 --- a/test/functional/rpc_blockchain.py +++ b/test/functional/rpc_blockchain.py @@ -133,6 +133,7 @@ def _test_getblockchaininfo(self): 'eunospaya': {'type': 'buried', 'active': False, 'height': 10000000}, 'fortcanning': {'type': 'buried', 'active': False, 'height': 10000000}, 'fortcanningmuseum': {'type': 'buried', 'active': False, 'height': 10000000}, + 'fortcanninghill': {'type': 'buried', 'active': False, 'height': 10000000}, 'testdummy': { 'type': 'bip9', 'bip9': { From cbc911df7784d8d695c14dd85823fce02b19a559 Mon Sep 17 00:00:00 2001 From: jouzo Date: Wed, 22 Dec 2021 15:30:20 +0100 Subject: [PATCH 05/13] Add other test scenario --- test/functional/feature_loan_vault.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/test/functional/feature_loan_vault.py b/test/functional/feature_loan_vault.py index 7cf42b3b2e..8bcbccbb8c 100755 --- a/test/functional/feature_loan_vault.py +++ b/test/functional/feature_loan_vault.py @@ -462,7 +462,7 @@ def run_test(self): # Deposit collaterals. 50% of BTC address = self.nodes[0].getnewaddress() - self.nodes[1].sendtokenstoaddress({}, { address: '1.25@BTC'}) + self.nodes[1].sendtokenstoaddress({}, { address: '1.50@BTC'}) self.nodes[1].generate(1) self.sync_all() vaultId4 = self.nodes[0].createvault(address, 'LOAN000A') @@ -477,13 +477,20 @@ def run_test(self): }) self.nodes[0].generate(1) + self.nodes[0].deposittovault(vaultId4, address, '0.2@BTC') + self.nodes[0].generate(1) + + # Should be able to withdraw extra BTC + self.nodes[0].withdrawfromvault(vaultId4, address, "0.1@BTC") + self.nodes[0].generate(1) + # BTC doubles in price oracle1_prices = [{"currency": "USD", "tokenAmount": "1@DFI"}, {"currency": "USD", "tokenAmount": "1@TSLA"}, {"currency": "USD", "tokenAmount": "2@BTC"}] timestamp = calendar.timegm(time.gmtime()) self.nodes[0].setoracledata(oracle_id1, timestamp, oracle1_prices) - self.nodes[0].generate(11) + self.nodes[0].generate(20) - # Should be able to withdraw part of BTC + # Should be able to withdraw part of BTC after BTC appreciation in price self.nodes[0].withdrawfromvault(vaultId4, address, "0.1@BTC") self.nodes[0].generate(1) From 24dc3a8cb99cdc58d3fe81e739f6b333149e27a2 Mon Sep 17 00:00:00 2001 From: jouzo Date: Thu, 23 Dec 2021 16:41:46 +0100 Subject: [PATCH 06/13] Check that DFI is 50% of collateralized value of loans in withdrawfromvault and takeloan --- src/masternodes/mn_checks.cpp | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/src/masternodes/mn_checks.cpp b/src/masternodes/mn_checks.cpp index 48e62e7dfc..687b6c0b44 100644 --- a/src/masternodes/mn_checks.cpp +++ b/src/masternodes/mn_checks.cpp @@ -2500,14 +2500,17 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor if (!collateralsLoans) return std::move(collateralsLoans); - if (obj.amount.nTokenId == DCT_ID{0} || static_cast(height) < consensus.FortCanningHillHeight) { - uint64_t totalDFI = 0; - for (auto& col : collateralsLoans.val->collaterals) - if (col.nTokenId == DCT_ID{0}) - totalDFI += col.nValue; + uint64_t totalDFI = 0; + for (auto& col : collateralsLoans.val->collaterals) + if (col.nTokenId == DCT_ID{0}) + totalDFI += col.nValue; + if (static_cast(height) < consensus.FortCanningHillHeight) { if (totalDFI < collateralsLoans.val->totalCollaterals / 2) return Res::Err("At least 50%% of the vault must be in DFI"); + } else { + if (totalDFI * 100 < collateralsLoans.val->totalLoans * scheme->ratio / 2) + return Res::Err("At least 50%% of the vault must be in DFI"); } if (collateralsLoans.val->ratio() < scheme->ratio) @@ -2613,8 +2616,13 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor if (col.nTokenId == DCT_ID{0}) totalDFI += col.nValue; - if (totalDFI < collateralsLoans.val->totalCollaterals / 2) - return Res::Err("At least 50%% of the vault must be in DFI when taking a loan"); + if (static_cast(height) < consensus.FortCanningHillHeight) { + if (totalDFI < collateralsLoans.val->totalCollaterals / 2) + return Res::Err("At least 50%% of the vault must be in DFI when taking a loan."); + } else { + if (totalDFI * 100 < collateralsLoans.val->totalLoans * scheme->ratio / 2) + return Res::Err("At least 50%% of the vault must be in DFI when taking a loan."); + } if (collateralsLoans.val->ratio() < scheme->ratio) return Res::Err("Vault does not have enough collateralization ratio defined by loan scheme - %d < %d", collateralsLoans.val->ratio(), scheme->ratio); From 819c8314961717b9cad7351165194eac4770399d Mon Sep 17 00:00:00 2001 From: jouzo Date: Thu, 23 Dec 2021 16:42:34 +0100 Subject: [PATCH 07/13] Add test for takeloan --- test/functional/feature_loan_vault.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/test/functional/feature_loan_vault.py b/test/functional/feature_loan_vault.py index 8bcbccbb8c..941700cc4c 100755 --- a/test/functional/feature_loan_vault.py +++ b/test/functional/feature_loan_vault.py @@ -491,8 +491,21 @@ def run_test(self): self.nodes[0].generate(20) # Should be able to withdraw part of BTC after BTC appreciation in price - self.nodes[0].withdrawfromvault(vaultId4, address, "0.1@BTC") + self.nodes[0].withdrawfromvault(vaultId4, address, "0.5@BTC") self.nodes[0].generate(1) + # Should not be able to withdraw if DFI lower than 50% of collateralized loan value + try: + self.nodes[0].withdrawfromvault(vaultId4, accountDFI, "0.25@DFI") + except JSONRPCException as e: + errorString = e.error['message'] + assert("At least 50% of the vault must be in DFI" in errorString) + + # Should be able to take 0.33@TSLA and respect 50% DFI ratio + self.nodes[0].takeloan({ + 'vaultId': vaultId4, + 'amounts': "0.33@TSLA" + }) + if __name__ == '__main__': VaultTest().main() From 151130db23f5b6358c250d376c6293b434be1ee5 Mon Sep 17 00:00:00 2001 From: jouzo Date: Thu, 23 Dec 2021 17:00:26 +0100 Subject: [PATCH 08/13] Use MultiplyAmounts to guard against overflow --- src/masternodes/mn_checks.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/masternodes/mn_checks.cpp b/src/masternodes/mn_checks.cpp index 687b6c0b44..e3c9e474b1 100644 --- a/src/masternodes/mn_checks.cpp +++ b/src/masternodes/mn_checks.cpp @@ -2509,7 +2509,7 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor if (totalDFI < collateralsLoans.val->totalCollaterals / 2) return Res::Err("At least 50%% of the vault must be in DFI"); } else { - if (totalDFI * 100 < collateralsLoans.val->totalLoans * scheme->ratio / 2) + if (MultiplyAmounts(totalDFI * 100) < MultiplyAmounts(collateralsLoans.val->totalLoans * scheme->ratio) / 2) return Res::Err("At least 50%% of the vault must be in DFI"); } @@ -2620,7 +2620,7 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor if (totalDFI < collateralsLoans.val->totalCollaterals / 2) return Res::Err("At least 50%% of the vault must be in DFI when taking a loan."); } else { - if (totalDFI * 100 < collateralsLoans.val->totalLoans * scheme->ratio / 2) + if (MultiplyAmounts(totalDFI * 100) < MultiplyAmounts(collateralsLoans.val->totalLoans * scheme->ratio) / 2) return Res::Err("At least 50%% of the vault must be in DFI when taking a loan."); } From 0e3d6b6cc79ac2005e4ceff0f722fdd0d8f0ebf9 Mon Sep 17 00:00:00 2001 From: jouzo Date: Thu, 23 Dec 2021 17:10:14 +0100 Subject: [PATCH 09/13] Fix MultiplyAmounts call --- src/masternodes/mn_checks.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/masternodes/mn_checks.cpp b/src/masternodes/mn_checks.cpp index e3c9e474b1..4f101a7cf7 100644 --- a/src/masternodes/mn_checks.cpp +++ b/src/masternodes/mn_checks.cpp @@ -2509,7 +2509,7 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor if (totalDFI < collateralsLoans.val->totalCollaterals / 2) return Res::Err("At least 50%% of the vault must be in DFI"); } else { - if (MultiplyAmounts(totalDFI * 100) < MultiplyAmounts(collateralsLoans.val->totalLoans * scheme->ratio) / 2) + if (MultiplyAmounts(totalDFI, 100) < MultiplyAmounts(collateralsLoans.val->totalLoans, scheme->ratio) / 2) return Res::Err("At least 50%% of the vault must be in DFI"); } @@ -2620,7 +2620,7 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor if (totalDFI < collateralsLoans.val->totalCollaterals / 2) return Res::Err("At least 50%% of the vault must be in DFI when taking a loan."); } else { - if (MultiplyAmounts(totalDFI * 100) < MultiplyAmounts(collateralsLoans.val->totalLoans * scheme->ratio) / 2) + if (MultiplyAmounts(totalDFI, 100) < MultiplyAmounts(collateralsLoans.val->totalLoans, scheme->ratio) / 2) return Res::Err("At least 50%% of the vault must be in DFI when taking a loan."); } From 41c6cc6480cbd4ecbe16f611027496bd12410180 Mon Sep 17 00:00:00 2001 From: jouzo Date: Thu, 23 Dec 2021 17:11:03 +0100 Subject: [PATCH 10/13] Withdraw more DFI in test --- test/functional/feature_loan_vault.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/functional/feature_loan_vault.py b/test/functional/feature_loan_vault.py index 941700cc4c..05817bfc8b 100755 --- a/test/functional/feature_loan_vault.py +++ b/test/functional/feature_loan_vault.py @@ -496,7 +496,7 @@ def run_test(self): # Should not be able to withdraw if DFI lower than 50% of collateralized loan value try: - self.nodes[0].withdrawfromvault(vaultId4, accountDFI, "0.25@DFI") + self.nodes[0].withdrawfromvault(vaultId4, accountDFI, "0.26@DFI") except JSONRPCException as e: errorString = e.error['message'] assert("At least 50% of the vault must be in DFI" in errorString) From c565aaad7d6ad76001877728d216a5ee845f6d04 Mon Sep 17 00:00:00 2001 From: jouzo Date: Wed, 12 Jan 2022 10:56:03 +0100 Subject: [PATCH 11/13] Add collateral value overflow test --- test/functional/feature_loan_vault.py | 90 ++++++++++++++++++++++++++- 1 file changed, 88 insertions(+), 2 deletions(-) diff --git a/test/functional/feature_loan_vault.py b/test/functional/feature_loan_vault.py index 05817bfc8b..9cb86c4902 100755 --- a/test/functional/feature_loan_vault.py +++ b/test/functional/feature_loan_vault.py @@ -18,8 +18,8 @@ def set_test_params(self): self.num_nodes = 2 self.setup_clean_chain = True self.extra_args = [ - ['-txnotokens=0', '-amkheight=1', '-bayfrontheight=1', '-bayfrontgardensheight=1', '-eunosheight=1', '-txindex=1', '-fortcanningheight=1', '-fortcanninghillheight=1'], - ['-txnotokens=0', '-amkheight=1', '-bayfrontheight=1', '-bayfrontgardensheight=1', '-eunosheight=1', '-txindex=1', '-fortcanningheight=1', '-fortcanninghillheight=1'] + ['-txnotokens=0', '-amkheight=1', '-bayfrontheight=1', '-bayfrontgardensheight=1', '-eunosheight=1', '-txindex=1', '-fortcanningheight=1', '-fortcanninghillheight=1', '-jellyfish_regtest=1'], + ['-txnotokens=0', '-amkheight=1', '-bayfrontheight=1', '-bayfrontgardensheight=1', '-eunosheight=1', '-txindex=1', '-fortcanningheight=1', '-fortcanninghillheight=1', '-jellyfish_regtest=1'] ] def run_test(self): @@ -506,6 +506,92 @@ def run_test(self): 'vaultId': vaultId4, 'amounts': "0.33@TSLA" }) + self.nodes[0].generate(1) + + # Collateral value overflow + + # Add token and poolpairs needed for paybackloan + self.nodes[0].setloantoken({ + 'symbol': "DUSD", + 'name': "DUSD stable token", + 'fixedIntervalPriceId': "DUSD/USD", + 'mintable': True, + 'interest': 1}) + self.nodes[0].generate(1) + + poolOwner = self.nodes[0].getnewaddress("", "legacy") + self.nodes[0].createpoolpair({ + "tokenA": "DUSD", + "tokenB": idDFI, + "commission": Decimal('0.002'), + "status": True, + "ownerAddress": poolOwner, + "pairSymbol": "DUSD-DFI", + }, []) + self.nodes[0].generate(1) + + accountDFI = self.nodes[0].get_genesis_keys().ownerAuthAddress + self.nodes[0].minttokens("300@DUSD") + self.nodes[0].generate(1) + self.nodes[0].utxostoaccount({accountDFI: "100@" + symbolDFI}) + self.nodes[0].generate(1) + self.nodes[0].addpoolliquidity({ + accountDFI: ["300@DUSD", "100@" + symbolDFI] + }, accountDFI, []) + self.nodes[0].generate(1) + + self.nodes[0].createpoolpair({ + "tokenA": "DUSD", + "tokenB": "TSLA", + "commission": Decimal('0.002'), + "status": True, + "ownerAddress": poolOwner, + "pairSymbol": "DUSD-TSLA", + }, []) + self.nodes[0].generate(1) + + self.nodes[0].minttokens("100@TSLA") + self.nodes[0].generate(1) + self.nodes[0].minttokens("100@DUSD") + self.nodes[0].generate(1) + self.nodes[0].addpoolliquidity({ + accountDFI: ["100@TSLA", "100@DUSD"] + }, accountDFI, []) + self.nodes[0].generate(1) + + vaultId5 = self.nodes[0].createvault(address, 'LOAN000A') + self.nodes[0].generate(1) + + self.nodes[0].utxostoaccount({accountDFI: "100000000@" + symbolDFI}) + self.nodes[0].generate(1) + self.nodes[0].deposittovault(vaultId5, accountDFI, "100000000@" + symbolDFI) + self.nodes[0].generate(1) + + self.nodes[0].takeloan({ + 'vaultId': vaultId5, + 'amounts': "0.5@TSLA" + }) + self.nodes[0].generate(1) + + oracle1_prices = [{"currency": "USD", "tokenAmount": "9999999999@DFI"}, {"currency": "USD", "tokenAmount": "1@TSLA"}, {"currency": "USD", "tokenAmount": "1@BTC"}] + timestamp = calendar.timegm(time.gmtime()) + self.nodes[0].setoracledata(oracle_id1, timestamp, oracle1_prices) + self.nodes[0].generate(20) + + vault5 = self.nodes[0].getvault(vaultId5) + assert_equal(vault5['collateralValue'], 0) # collateral value overflowed + + # Actions on vault should be blocked + assert_raises_rpc_error(-32600, 'Value/price too high', self.nodes[0].takeloan, {'vaultId': vaultId5,'amounts': "0.5@TSLA"}) + assert_raises_rpc_error(-32600, 'Value/price too high', self.nodes[0].deposittovault, vaultId5, accountDFI, "1@" + symbolDFI) + assert_raises_rpc_error(-32600, 'Value/price too high', self.nodes[0].withdrawfromvault, vaultId5, address, "1@DFI") + + # Should be able to close vault + self.nodes[0].paybackloan({'vaultId': vaultId5, 'from': address, 'amounts': ["1@TSLA"]}) + self.nodes[0].generate(1) + self.nodes[0].closevault(vaultId5, address) + self.nodes[0].generate(1) + if __name__ == '__main__': VaultTest().main() From a9ad0911441fcd508637942a45fcc6f9f6ddff8d Mon Sep 17 00:00:00 2001 From: jouzo Date: Tue, 18 Jan 2022 09:14:03 +0100 Subject: [PATCH 12/13] Update error message --- src/masternodes/mn_checks.cpp | 8 ++++---- test/functional/feature_loan_priceupdate.py | 2 +- test/functional/feature_loan_vault.py | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/masternodes/mn_checks.cpp b/src/masternodes/mn_checks.cpp index e0960572f4..58b9cbd786 100644 --- a/src/masternodes/mn_checks.cpp +++ b/src/masternodes/mn_checks.cpp @@ -2539,10 +2539,10 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor if (static_cast(height) < consensus.FortCanningHillHeight) { if (totalDFI < collateralsLoans.val->totalCollaterals / 2) - return Res::Err("At least 50%% of the vault must be in DFI"); + return Res::Err("At least 50%% of the collateral must be in DFI"); } else { if (MultiplyAmounts(totalDFI, 100) < MultiplyAmounts(collateralsLoans.val->totalLoans, scheme->ratio) / 2) - return Res::Err("At least 50%% of the vault must be in DFI"); + return Res::Err("At least 50%% of the collateral must be in DFI"); } if (collateralsLoans.val->ratio() < scheme->ratio) @@ -2650,10 +2650,10 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor if (static_cast(height) < consensus.FortCanningHillHeight) { if (totalDFI < collateralsLoans.val->totalCollaterals / 2) - return Res::Err("At least 50%% of the vault must be in DFI when taking a loan."); + return Res::Err("At least 50%% of the collateral must be in DFI when taking a loan."); } else { if (MultiplyAmounts(totalDFI, 100) < MultiplyAmounts(collateralsLoans.val->totalLoans, scheme->ratio) / 2) - return Res::Err("At least 50%% of the vault must be in DFI when taking a loan."); + return Res::Err("At least 50%% of the collateral must be in DFI when taking a loan."); } if (collateralsLoans.val->ratio() < scheme->ratio) diff --git a/test/functional/feature_loan_priceupdate.py b/test/functional/feature_loan_priceupdate.py index f157ab4484..ae6a452ba1 100755 --- a/test/functional/feature_loan_priceupdate.py +++ b/test/functional/feature_loan_priceupdate.py @@ -257,7 +257,7 @@ def run_test(self): self.nodes[0].withdrawfromvault(vaultId1, account, "900@DFI") except JSONRPCException as e: errorString = e.error['message'] - assert("At least 50% of the vault must be in DFI" in errorString) + assert("At least 50% of the collateral must be in DFI" in errorString) vault = self.nodes[0].getvault(vaultId1) self.nodes[0].withdrawfromvault(vaultId1, account, "100@BTC") diff --git a/test/functional/feature_loan_vault.py b/test/functional/feature_loan_vault.py index 9cb86c4902..90933f2921 100755 --- a/test/functional/feature_loan_vault.py +++ b/test/functional/feature_loan_vault.py @@ -292,7 +292,7 @@ def run_test(self): 'amounts': "0.1@TSLA"}) except JSONRPCException as e: errorString = e.error['message'] - assert("At least 50% of the vault must be in DFI when taking a loan" in errorString) + assert("At least 50% of the collateral must be in DFI when taking a loan" in errorString) self.nodes[0].deposittovault(vaultId1, accountDFI, '0.7@DFI') self.nodes[0].generate(1) @@ -347,7 +347,7 @@ def run_test(self): self.nodes[0].withdrawfromvault(vaultId1, accountDFI, "1@DFI") except JSONRPCException as e: errorString = e.error['message'] - assert("At least 50% of the vault must be in DFI" in errorString) + assert("At least 50% of the collateral must be in DFI" in errorString) params = {'loanSchemeId':'LOAN000A'} self.nodes[0].updatevault(vaultId1, params) @@ -499,7 +499,7 @@ def run_test(self): self.nodes[0].withdrawfromvault(vaultId4, accountDFI, "0.26@DFI") except JSONRPCException as e: errorString = e.error['message'] - assert("At least 50% of the vault must be in DFI" in errorString) + assert("At least 50% of the collateral must be in DFI" in errorString) # Should be able to take 0.33@TSLA and respect 50% DFI ratio self.nodes[0].takeloan({ From 621b8f889dba021f879046450ea3488ca69d48cf Mon Sep 17 00:00:00 2001 From: jouzo Date: Wed, 19 Jan 2022 15:36:35 +0100 Subject: [PATCH 13/13] Use arith_uint256 instead of MultiplyAmounts --- src/masternodes/mn_checks.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/masternodes/mn_checks.cpp b/src/masternodes/mn_checks.cpp index d22a717650..5c1b0c6c2b 100644 --- a/src/masternodes/mn_checks.cpp +++ b/src/masternodes/mn_checks.cpp @@ -2541,7 +2541,7 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor if (totalDFI < collateralsLoans.val->totalCollaterals / 2) return Res::Err("At least 50%% of the collateral must be in DFI"); } else { - if (MultiplyAmounts(totalDFI, 100) < MultiplyAmounts(collateralsLoans.val->totalLoans, scheme->ratio) / 2) + if (arith_uint256(totalDFI) * 100 < arith_uint256(collateralsLoans.val->totalLoans) * scheme->ratio / 2) return Res::Err("At least 50%% of the collateral must be in DFI"); } @@ -2652,7 +2652,7 @@ class CCustomTxApplyVisitor : public CCustomTxVisitor if (totalDFI < collateralsLoans.val->totalCollaterals / 2) return Res::Err("At least 50%% of the collateral must be in DFI when taking a loan."); } else { - if (MultiplyAmounts(totalDFI, 100) < MultiplyAmounts(collateralsLoans.val->totalLoans, scheme->ratio) / 2) + if (arith_uint256(totalDFI) * 100 < arith_uint256(collateralsLoans.val->totalLoans) * scheme->ratio / 2) return Res::Err("At least 50%% of the collateral must be in DFI when taking a loan."); }