diff --git a/src/masternodes/rpc_vault.cpp b/src/masternodes/rpc_vault.cpp index ee3e43fea01..a6d5a8d1c82 100644 --- a/src/masternodes/rpc_vault.cpp +++ b/src/masternodes/rpc_vault.cpp @@ -524,15 +524,13 @@ UniValue listvaults(const JSONRPCRequest& request) { if (!including_start) { including_start = true; - return (true); - } - if (!ownerAddress.empty() && ownerAddress != data.ownerAddress) { - return false; + return true; } auto vaultState = GetVaultState(vaultId, data); if ((loanSchemeId.empty() || loanSchemeId == data.schemeId) - && (state == VaultState::Unknown || state == vaultState)) { + && (ownerAddress.empty() || ownerAddress == data.ownerAddress) + && (state == VaultState::Unknown || state == vaultState)) { UniValue vaultObj{UniValue::VOBJ}; if(!verbose){ vaultObj.pushKV("vaultId", vaultId.GetHex()); @@ -1156,7 +1154,7 @@ UniValue listauctionhistory(const JSONRPCRequest& request) { // parse pagination size_t limit = 100; - AuctionHistoryKey start = {~0u}; + AuctionHistoryKey start = {~0u, {}, {}, ~0u}; { if (request.params.size() > 1) { UniValue paginationObj = request.params[1].get_obj(); @@ -1208,6 +1206,10 @@ UniValue listauctionhistory(const JSONRPCRequest& request) { return true; } + if (start.index!=~0u && start.index != key.index){ + return true; + } + ret.push_back(auctionhistoryToJSON(key, valueLazy.get())); return --limit != 0; diff --git a/test/functional/feature_loan.py b/test/functional/feature_loan.py index aa689026c93..e904f192680 100755 --- a/test/functional/feature_loan.py +++ b/test/functional/feature_loan.py @@ -23,8 +23,7 @@ def set_test_params(self): ['-txnotokens=0', '-amkheight=1', '-bayfrontheight=1', '-bayfrontgardensheight=1', '-eunosheight=1', '-txindex=1', '-fortcanningheight=1'] ] - def run_test(self): - self.nodes[0].generate(300) + def create_tokens(self): self.nodes[0].createtoken({ "symbol": "BTC", "name": "BTC token", @@ -37,38 +36,42 @@ def run_test(self): "isDAT": True, "collateralAddress": self.nodes[0].get_genesis_keys().ownerAuthAddress }) - self.nodes[0].generate(1) + def mint_tokens(self): # mint BTC self.nodes[0].minttokens("2000@BTC") self.nodes[0].generate(1) + self.nodes[0].minttokens("1000@TSLA") + self.nodes[0].generate(1) - account = self.nodes[0].get_genesis_keys().ownerAuthAddress + self.account = self.nodes[0].get_genesis_keys().ownerAuthAddress # transfer DFI - self.nodes[0].utxostoaccount({account: "2000@DFI"}) + self.nodes[0].utxostoaccount({self.account: "2000@DFI"}) self.nodes[0].generate(1) + def setup_oracles(self): # setup oracle - oracle_address1 = self.nodes[0].getnewaddress("", "legacy") - price_feeds = [{"currency": "USD", "token": "DFI"}, {"currency": "USD", "token": "BTC"}, {"currency": "USD", "token": "TSLA"}] - oracle_id1 = self.nodes[0].appointoracle(oracle_address1, price_feeds, 10) + self.oracle_address1 = self.nodes[0].getnewaddress("", "legacy") + self.price_feeds = [{"currency": "USD", "token": "DFI"}, {"currency": "USD", "token": "BTC"}, {"currency": "USD", "token": "TSLA"}] + self.oracle_id1 = self.nodes[0].appointoracle(self.oracle_address1, self.price_feeds, 10) self.nodes[0].generate(1) - oracle_address2 = self.nodes[0].getnewaddress("", "legacy") - oracle_id2 = self.nodes[0].appointoracle(oracle_address2, price_feeds, 10) + self.oracle_address2 = self.nodes[0].getnewaddress("", "legacy") + self.oracle_id2 = self.nodes[0].appointoracle(self.oracle_address2, self.price_feeds, 10) self.nodes[0].generate(1) # feed oracle - oracle1_prices = [{"currency": "USD", "tokenAmount": "10@TSLA"}, {"currency": "USD", "tokenAmount": "10@DFI"}, {"currency": "USD", "tokenAmount": "10@BTC"}] + self.oracle1_prices = [{"currency": "USD", "tokenAmount": "10@TSLA"}, {"currency": "USD", "tokenAmount": "10@DFI"}, {"currency": "USD", "tokenAmount": "10@BTC"}] timestamp = calendar.timegm(time.gmtime()) - self.nodes[0].setoracledata(oracle_id1, timestamp, oracle1_prices) + self.nodes[0].setoracledata(self.oracle_id1, timestamp, self.oracle1_prices) self.nodes[0].generate(1) - oracle2_prices = [{"currency": "USD", "tokenAmount": "15@TSLA"}, {"currency": "USD", "tokenAmount": "15@DFI"}, {"currency": "USD", "tokenAmount": "15@BTC"}] + self.oracle2_prices = [{"currency": "USD", "tokenAmount": "15@TSLA"}, {"currency": "USD", "tokenAmount": "15@DFI"}, {"currency": "USD", "tokenAmount": "15@BTC"}] timestamp = calendar.timegm(time.gmtime()) - self.nodes[0].setoracledata(oracle_id2, timestamp, oracle2_prices) + self.nodes[0].setoracledata(self.oracle_id2, timestamp, self.oracle2_prices) self.nodes[0].generate(1) + def set_collateral_tokens(self): # set DFI an BTC as collateral tokens self.nodes[0].setcollateraltoken({ 'token': "DFI", @@ -81,22 +84,14 @@ def run_test(self): 'fixedIntervalPriceId': "BTC/USD"}) self.nodes[0].generate(1) + def create_loanschemes(self): # Create loan schemes self.nodes[0].createloanscheme(200, 1, 'LOAN0001') self.nodes[0].generate(1) self.nodes[0].createloanscheme(150, 0.5, 'LOAN0002') self.nodes[0].generate(1) - # Create vault - vaultId1 = self.nodes[0].createvault(account, '') # default loan scheme - self.nodes[0].generate(5) - - # deposit DFI and BTC to vault1 - self.nodes[0].deposittovault(vaultId1, account, '1000@DFI') - self.nodes[0].generate(1) - self.nodes[0].deposittovault(vaultId1, account, '1000@BTC') - self.nodes[0].generate(1) - + def set_loan_tokens(self): # set TSLA as loan token self.nodes[0].setloantoken({ 'symbol': "TSLA", @@ -106,15 +101,26 @@ def run_test(self): 'interest': 1}) self.nodes[0].generate(6) + def init_vault(self): + # Create vault + self.vaultId1 = self.nodes[0].createvault(self.account, '') # default loan scheme + self.nodes[0].generate(5) + + # deposit DFI and BTC to vault1 + self.nodes[0].deposittovault(self.vaultId1, self.account, '1000@DFI') + self.nodes[0].generate(1) + self.nodes[0].deposittovault(self.vaultId1, self.account, '1000@BTC') + self.nodes[0].generate(1) + # take loan self.nodes[0].takeloan({ - 'vaultId': vaultId1, + 'vaultId': self.vaultId1, 'amounts': "1000@TSLA"}) self.nodes[0].generate(1) - accountBal = self.nodes[0].getaccount(account) - assert_equal(accountBal, ['1000.00000000@DFI', '1000.00000000@BTC', '1000.00000000@TSLA']) + accountBal = self.nodes[0].getaccount(self.account) + assert_equal(accountBal, ['1000.00000000@DFI', '1000.00000000@BTC', '2000.00000000@TSLA']) self.nodes[0].generate(60) # ~10 hours - vault1 = self.nodes[0].getvault(vaultId1) + vault1 = self.nodes[0].getvault(self.vaultId1) interests = self.nodes[0].getinterest( vault1['loanSchemeId'], 'TSLA' @@ -122,175 +128,264 @@ def run_test(self): totalLoanAmount = (1000+interests[0]['totalInterest']) # Initial loan taken assert_equal(Decimal(vault1['loanAmounts'][0].split("@")[0]), totalLoanAmount) + def setup(self): + self.nodes[0].generate(300) + self.nodes[1].generate(110) + self.create_tokens() + self.setup_oracles() + self.set_collateral_tokens() + self.set_loan_tokens() + self.mint_tokens() + self.create_loanschemes() + self.init_vault() + + def trigger_liquidation(self): # Trigger liquidation updating price in oracle oracle1_prices = [{"currency": "USD", "tokenAmount": "20@TSLA"}] oracle2_prices = [{"currency": "USD", "tokenAmount": "30@TSLA"}] timestamp = calendar.timegm(time.gmtime()) - self.nodes[0].setoracledata(oracle_id1, timestamp, oracle1_prices) - self.nodes[0].setoracledata(oracle_id2, timestamp, oracle2_prices) + self.nodes[0].setoracledata(self.oracle_id1, timestamp, oracle1_prices) + self.nodes[0].setoracledata(self.oracle_id2, timestamp, oracle2_prices) self.nodes[0].generate(36) # Auction tests auctionlist = self.nodes[0].listauctions() assert_equal(len(auctionlist[0]['batches']), 3) - vault1 = self.nodes[0].getvault(vaultId1) getloaninfo = self.nodes[0].getloaninfo() assert_equal(getloaninfo['totals']['openAuctions'], 3) + def place_too_low_bid(self): # Fail auction bid try: - self.nodes[0].placeauctionbid(vaultId1, 0, account, "410@TSLA") + self.nodes[0].placeauctionbid(self.vaultId1, 0, self.account, "410@TSLA") except JSONRPCException as e: errorString = e.error['message'] assert("First bid should include liquidation penalty of 5%" in errorString) - self.nodes[0].minttokens("1000@TSLA") - self.nodes[0].generate(1) - - self.nodes[0].placeauctionbid(vaultId1, 0, account, "550@TSLA") + def place_auction_bids_batches_0_1(self): + self.nodes[0].placeauctionbid(self.vaultId1, 0, self.account, "550@TSLA") self.nodes[0].generate(1) batches = self.nodes[0].listauctions()[0]['batches'] highestBid = batches[0]['highestBid'] - assert_equal(highestBid['owner'], account) + assert_equal(highestBid['owner'], self.account) assert_equal(highestBid['amount'], '550.00000000@TSLA') - accountBal = self.nodes[0].getaccount(account) + accountBal = self.nodes[0].getaccount(self.account) assert_equal(accountBal, ['1000.00000000@DFI', '1000.00000000@BTC', '1450.00000000@TSLA']) - # new account for new bidder + self.nodes[0].placeauctionbid(self.vaultId1, 1, self.account, "450@TSLA") self.nodes[0].generate(1) - account2 = self.nodes[0].getnewaddress() - self.nodes[0].accounttoaccount(account, {account2: "1000@TSLA"} ) + + batches = self.nodes[0].listauctions()[0]['batches'] + highestBid = batches[1]['highestBid'] + assert_equal(highestBid['owner'], self.account) + assert_equal(highestBid['amount'], '450.00000000@TSLA') + accountBal = self.nodes[0].getaccount(self.account) + assert_equal(accountBal, ['1000.00000000@DFI', '1000.00000000@BTC', '1000.00000000@TSLA']) + + def bid_with_new_account_in_different_node(self): + # new account for new bidder + self.sync_blocks() + self.account2 = self.nodes[1].getnewaddress() + self.nodes[0].accounttoaccount(self.account, {self.account2: "1000@TSLA"} ) self.nodes[0].generate(1) + self.sync_blocks() # Fail auction bid less that 1% higher try: - self.nodes[0].placeauctionbid(vaultId1, 0, account2, "555@TSLA") # just under 1% + self.nodes[1].placeauctionbid(self.vaultId1, 0, self.account2, "555@TSLA") # just under 1% except JSONRPCException as e: errorString = e.error['message'] assert("Bid override should be at least 1% higher than current one" in errorString) + # Fail bid from not owned account + try: + self.nodes[0].placeauctionbid(self.vaultId1, 0, self.account2, "600@TSLA") + except JSONRPCException as e: + errorString = e.error['message'] + assert("Incorrect authorization for" in errorString) - self.nodes[0].placeauctionbid(vaultId1, 0, account2, "555.5@TSLA") # above 1% - self.nodes[0].generate(1) + self.nodes[1].placeauctionbid(self.vaultId1, 0, self.account2, "555.5@TSLA") # above 1% + self.nodes[1].generate(1) + self.sync_blocks() # check balances are right after greater bid - account2Bal = self.nodes[0].getaccount(account2) - accountBal = self.nodes[0].getaccount(account) - assert_equal(accountBal, ['1000.00000000@DFI', '1000.00000000@BTC', '1000.00000000@TSLA']) + account2Bal = self.nodes[0].getaccount(self.account2) + accountBal = self.nodes[0].getaccount(self.account) + assert_equal(accountBal, ['1000.00000000@DFI', '1000.00000000@BTC', '550.00000000@TSLA']) assert_equal(account2Bal, ['444.50000000@TSLA']) - # let auction end and check account balances + + def let_auction_end_and_check_balances(self): self.nodes[0].generate(8) - account2Bal = self.nodes[0].getaccount(account2) - accountBal = self.nodes[0].getaccount(account) - vault1 = self.nodes[0].getvault(vaultId1) + account2Bal = self.nodes[0].getaccount(self.account2) + accountBal = self.nodes[0].getaccount(self.account) + vault1 = self.nodes[0].getvault(self.vaultId1) assert_equal(vault1['state'], "inLiquidation") - assert_equal(vault1['liquidationHeight'], 469) + assert_equal(vault1['liquidationHeight'], 577) assert_equal(vault1['liquidationPenalty'], Decimal('5.00000000')) - assert_equal(vault1['batchCount'], 2) - assert_equal(accountBal, ['1000.00000000@DFI', '1000.00000000@BTC', '1000.00000000@TSLA']) + assert_equal(vault1['batchCount'], 1) + assert_equal(accountBal, ['1400.00000000@DFI', '1400.00000000@BTC', '550.00000000@TSLA']) # auction winner account has now first batch collaterals assert_equal(account2Bal, ['400.00000000@DFI', '400.00000000@BTC', '444.50000000@TSLA']) auction = self.nodes[0].listauctionhistory()[0] - assert_equal(auction['winner'], account2) - assert_equal(auction['blockHeight'], 432) - assert_equal(auction['vaultId'], vaultId1) - assert_equal(auction['batchIndex'], 0) - assert_equal(auction['auctionBid'], "555.50000000@TSLA") + assert_equal(auction['winner'], self.account) + assert_equal(auction['blockHeight'], 540) + assert_equal(auction['vaultId'], self.vaultId1) + assert_equal(auction['batchIndex'], 1) + assert_equal(auction['auctionBid'], "450.00000000@TSLA") assert_equal(auction['auctionWon'].sort(), ['400.00000000@DFI', '400.00000000@BTC'].sort()) - # check that still auction due to 1 batch without bid + def test_auction_without_bid_is_reopened(self): auctionlist = self.nodes[0].listauctions() - assert_equal(len(auctionlist[0]['batches']), 2) + assert_equal(len(auctionlist[0]['batches']), 1) - self.nodes[0].placeauctionbid(vaultId1, 0, account, "515@TSLA") # above 5% and leave vault with some loan to exit liquidation state + def bid_on_remaining_auction_and_exit_vault_inliquidation_state(self): + self.nodes[0].placeauctionbid(self.vaultId1, 0, self.account, "220@TSLA") # above 5% and leave vault with some loan to exit liquidation state self.nodes[0].generate(40) # let auction end - auction = self.nodes[0].listauctionhistory(account)[0] - assert_equal(auction['winner'], account) - assert_equal(auction['blockHeight'], 469) - assert_equal(auction['vaultId'], vaultId1) + auction = self.nodes[0].listauctionhistory(self.account)[0] + assert_equal(auction['winner'], self.account) + assert_equal(auction['blockHeight'], 577) + assert_equal(auction['vaultId'], self.vaultId1) assert_equal(auction['batchIndex'], 0) - assert_equal(auction['auctionBid'], "515.00000000@TSLA") - assert_equal(auction['auctionWon'].sort(), ['399.99999600@DFI', '399.99999600@DFI'].sort()) - - self.nodes[0].placeauctionbid(vaultId1, 0, account, "259@TSLA") - self.nodes[0].generate(40) # let auction end + assert_equal(auction['auctionBid'], "220.00000000@TSLA") + assert_equal(auction['auctionWon'].sort(), ['200.00000000@DFI', '200.00000000@DFI'].sort()) - auctions = self.nodes[0].listauctionhistory() + def check_auctionhistory_and_vault_balances(self): + auctions = self.nodes[0].listauctionhistory("all") assert_equal(len(auctions), 3) - assert_equal(auctions[0]['winner'], account) - assert_equal(auctions[1]['winner'], account) - assert_equal(auctions[2]['winner'], account2) - assert_equal(auctions[0]['blockHeight'], 506) - assert_equal(auctions[1]['blockHeight'], 469) - assert_equal(auctions[2]['blockHeight'], 432) - assert_equal(auctions[0]['auctionBid'], "259.00000000@TSLA") - assert_equal(auctions[1]['auctionBid'], "515.00000000@TSLA") - assert_equal(auctions[2]['auctionBid'], "555.50000000@TSLA") - - auctionsV = self.nodes[0].listauctionhistory(vaultId1) + assert_equal(auctions[0]['winner'], self.account) + assert_equal(auctions[1]['winner'], self.account2) + assert_equal(auctions[2]['winner'], self.account) + assert_equal(auctions[0]['blockHeight'], 577) + assert_equal(auctions[1]['blockHeight'], 540) + assert_equal(auctions[2]['blockHeight'], 540) + assert_equal(auctions[0]['auctionBid'], "220.00000000@TSLA") + assert_equal(auctions[1]['auctionBid'], "555.50000000@TSLA") + assert_equal(auctions[2]['auctionBid'], "450.00000000@TSLA") + + auctionsV = self.nodes[0].listauctionhistory(self.vaultId1) assert_equal(len(auctionsV), 3) assert_equal(auctions, auctionsV) - vault1 = self.nodes[0].getvault(vaultId1) - accountBal = self.nodes[0].getaccount(account) + auctionsAccount2 = self.nodes[0].listauctionhistory(self.account2) + assert_equal(len(auctionsAccount2), 1) - assert_equal(vault1['state'], "active") - assert_equal(accountBal, ['1600.00000000@DFI', '1600.00000000@BTC', '226.00000000@TSLA']) + vault1 = self.nodes[0].getvault(self.vaultId1) + accountBal = self.nodes[0].getaccount(self.account) + account2Bal = self.nodes[0].getaccount(self.account2) + assert_equal(vault1['state'], "active") + assert_equal(accountBal, ['1600.00000000@DFI', '1600.00000000@BTC', '330.00000000@TSLA']) + assert_equal(account2Bal, ['400.00000000@DFI', '400.00000000@BTC', '444.50000000@TSLA']) - self.nodes[0].deposittovault(vaultId1, account, '600@DFI') + def deposit_and_takeloan_in_now_active_vault(self): + self.nodes[0].deposittovault(self.vaultId1, self.account, '600@DFI') self.nodes[0].generate(1) - vault1 = self.nodes[0].getvault(vaultId1) - assert_equal(vault1['collateralAmounts'], ['600.00000000@DFI']) - # Trigger liquidation updating price in oracle + # update prices oracle1_prices = [{"currency": "USD", "tokenAmount": "2@TSLA"}] oracle2_prices = [{"currency": "USD", "tokenAmount": "3@TSLA"}] timestamp = calendar.timegm(time.gmtime()) - self.nodes[0].setoracledata(oracle_id1, timestamp, oracle1_prices) - self.nodes[0].setoracledata(oracle_id2, timestamp, oracle2_prices) + self.nodes[0].setoracledata(self.oracle_id1, timestamp, oracle1_prices) + self.nodes[0].setoracledata(self.oracle_id2, timestamp, oracle2_prices) self.nodes[0].generate(36) - # take loan self.nodes[0].takeloan({ - 'vaultId': vaultId1, + 'vaultId': self.vaultId1, 'amounts': "1000@TSLA" }) self.nodes[0].generate(1) + vault1 = self.nodes[0].getvault(self.vaultId1) + assert_equal(vault1['collateralAmounts'], ['600.00000000@DFI']) + assert_equal(vault1['loanAmounts'], ['1000.00038051@TSLA']) + def trigger_liquidation_again(self): + vault = self.nodes[0].getvault(self.vaultId1) + assert_equal(vault['state'], 'active') # Reset price in oracle oracle1_prices = [{"currency": "USD", "tokenAmount": "200@TSLA"}] oracle2_prices = [{"currency": "USD", "tokenAmount": "300@TSLA"}] timestamp = calendar.timegm(time.gmtime()) - self.nodes[0].setoracledata(oracle_id1, timestamp, oracle1_prices) - self.nodes[0].setoracledata(oracle_id2, timestamp, oracle2_prices) + self.nodes[0].setoracledata(self.oracle_id1, timestamp, oracle1_prices) + self.nodes[0].setoracledata(self.oracle_id2, timestamp, oracle2_prices) self.nodes[0].generate(36) + vault = self.nodes[0].getvault(self.vaultId1) + assert_equal(vault['state'], 'inLiquidation') + + def test_autoselect_account_for_bid(self): # Not enought tokens on account try: - self.nodes[0].placeauctionbid(vaultId1, 0, "*", "1600@TSLA") + self.nodes[0].placeauctionbid(self.vaultId1, 0, "*", "1600@TSLA") except JSONRPCException as e: errorString = e.error['message'] assert("Not enough tokens on account, call sendtokenstoaddress to increase it." in errorString) + self.nodes[0].minttokens("1600@TSLA") + self.nodes[0].generate(1) newAddress = self.nodes[0].getnewaddress("") self.nodes[0].sendtokenstoaddress({}, {newAddress: "1600@TSLA"}) # newAddress has now the highest amount of TSLA token self.nodes[0].generate(1) - self.nodes[0].placeauctionbid(vaultId1, 0, "*", "1600@TSLA") # Autoselect address with highest amount of TSLA token + self.nodes[0].placeauctionbid(self.vaultId1, 0, "*", "1600@TSLA") # Autoselect address with highest amount of TSLA token self.nodes[0].generate(40) # let auction end auction = self.nodes[0].listauctionhistory(newAddress)[0] assert_equal(auction['winner'], newAddress) - assert_equal(auction['blockHeight'], 600) - assert_equal(auction['vaultId'], vaultId1) + assert_equal(auction['blockHeight'], 666) + assert_equal(auction['vaultId'], self.vaultId1) assert_equal(auction['batchIndex'], 0) assert_equal(auction['auctionBid'], "1600.00000000@TSLA") + def test_lisauctionhistory_pagination_filtering(self): + self.sync_blocks() + list = self.nodes[0].listauctionhistory("all", {"limit": 1}) + assert_equal(len(list), 1) + list = self.nodes[0].listauctionhistory("all", {"maxBlockHeight": 541}) + assert_equal(len(list), 2) + list = self.nodes[0].listauctionhistory("all", {"index": 0}) + assert_equal(len(list), 3) + list = self.nodes[0].listauctionhistory("all", {"index": 1}) + assert_equal(len(list), 1) + list = self.nodes[0].listauctionhistory("all") + assert_equal(len(list), 4) + list = self.nodes[0].listauctionhistory("mine") + assert_equal(len(list), 3) + list = self.nodes[0].listauctionhistory() + assert_equal(len(list), 3) + + self.sync_blocks() + + # node 1 + list1 = self.nodes[1].listauctionhistory() + assert_equal(len(list1), 1) + list1 = self.nodes[1].listauctionhistory("all") + assert_equal(len(list1), 4) + list1 = self.nodes[1].listauctionhistory("all", {"index": 1}) + assert_equal(len(list1), 1) + list1 = self.nodes[1].listauctionhistory("all", {"index": 0}) + assert_equal(len(list1), 3) + list1 = self.nodes[1].listauctionhistory("all", {"index": 0, "limit": 2}) + assert_equal(len(list1), 2) + + def run_test(self): + self.setup() + self.trigger_liquidation() + self.place_too_low_bid() + self.place_auction_bids_batches_0_1() + self.bid_with_new_account_in_different_node() + self.let_auction_end_and_check_balances() + self.test_auction_without_bid_is_reopened() + self.bid_on_remaining_auction_and_exit_vault_inliquidation_state() + self.check_auctionhistory_and_vault_balances() + self.deposit_and_takeloan_in_now_active_vault() + self.trigger_liquidation_again() + self.test_autoselect_account_for_bid() + self.test_lisauctionhistory_pagination_filtering() + if __name__ == '__main__': LoanTest().main() diff --git a/test/functional/feature_loan_basics.py b/test/functional/feature_loan_basics.py index 89534152336..7b0c94915c5 100755 --- a/test/functional/feature_loan_basics.py +++ b/test/functional/feature_loan_basics.py @@ -570,6 +570,38 @@ def run_test(self): self.nodes[0].closevault(vaultId4, address) self.nodes[0].generate(1) + # Test for takeloan to another address + self.sync_blocks() + owner_node1 = self.nodes[1].getnewaddress() + self.nodes[1].generate(1) + owner_node0 = self.nodes[0].getnewaddress() + self.nodes[0].generate(1) + self.sync_blocks() + + # Try takeloan with no auth + try: + self.nodes[1].takeloan({ + 'vaultId': vaultId3, + 'amounts': "20@" + symboldUSD}) + except JSONRPCException as e: + errorString = e.error['message'] + assert("Incorrect authorization for" in errorString) + + # takeloan but transfer dTokens to not owned address + account_owner_node1 = self.nodes[1].getaccount(owner_node1) + assert_equal(account_owner_node1, []) + + self.nodes[0].takeloan({ + 'vaultId': vaultId3, + 'to': owner_node1, + 'amounts': "20@" + symboldUSD}) + self.nodes[0].generate(1) + self.sync_blocks() + + account_owner_node1 = self.nodes[1].getaccount(owner_node1) + assert_equal(account_owner_node1, ['20.00000000@DUSD']) + account_owner_node0 = self.nodes[1].getaccount(owner_node0) + assert_equal(account_owner_node0, []) if __name__ == '__main__': LoanTakeLoanTest().main() diff --git a/test/functional/feature_loan_scheme.py b/test/functional/feature_loan_scheme.py index 5641e1717ac..292315aaa88 100755 --- a/test/functional/feature_loan_scheme.py +++ b/test/functional/feature_loan_scheme.py @@ -20,9 +20,15 @@ def set_test_params(self): ['-txnotokens=0', '-amkheight=1', '-bayfrontheight=1', '-eunosheight=1', '-txindex=1', '-fortcanningheight=110'] ] - def run_test(self): + def goto_block(self, block_number, node_number=0): + current_block = self.nodes[node_number].getblockcount() + if block_number > current_block: + self.nodes[node_number].generate(block_number - current_block) + + def setup(self): self.nodes[0].generate(101) + def create_loanscheme_before_Fort_Canning(self): # Try and create a loan scheme before Fort Canning try: self.nodes[0].createloanscheme(1000, 0.5, 'LOANMAX') @@ -30,19 +36,30 @@ def run_test(self): errorString = e.error['message'] assert("called before FortCanning height" in errorString) + # Go to Fort Canning height self.nodes[0].generate(9) - # Create loan scheme - self.nodes[0].createloanscheme(999, 0.6, 'LOANMAX') + def create_update_loanscheme_success(self, ratio, interest, name, after_block, default=False, update=False): + # Create update loan scheme + args = [ratio, interest, name] + if after_block: + args.append(after_block) + + if update: + self.nodes[0].updateloanscheme(*args) + else: + self.nodes[0].createloanscheme(*args) self.nodes[0].generate(1) - # Check loan scheme created - results = self.nodes[0].listloanschemes() - assert_equal(results[0]['id'], 'LOANMAX') - assert_equal(results[0]['mincolratio'], 999) - assert_equal(results[0]['interestrate'], Decimal('0.60000000')) - assert_equal(results[0]['default'], True) + if after_block is None: + # Check loan scheme created + result = self.nodes[0].getloanscheme(name) + assert_equal(result['id'], name) + assert_equal(result['mincolratio'], ratio) + assert_equal(Decimal(result['interestrate']), Decimal(str(interest))) + assert_equal(result['default'], default) + def test_create_loanscheme_wrong_params(self): # Try and create a loan scheme with duplicate name try: self.nodes[0].createloanscheme(100, 1, 'LOANMAX') @@ -85,10 +102,7 @@ def run_test(self): errorString = e.error['message'] assert("id cannot be empty or more than 8 chars long" in errorString) - # Create one more loan scheme - self.nodes[0].createloanscheme(150, 5, 'LOAN0001') - self.nodes[0].generate(1) - + def test_getloanscheme_errors(self): # Try to get a loan scheme with too long an ID try: self.nodes[0].getloanscheme('scheme123') @@ -103,6 +117,7 @@ def run_test(self): errorString = e.error['message'] assert("Cannot find existing loan scheme with id " in errorString) + def test_get_list_loanscheme_success(self): # Get loan scheme loanscheme = self.nodes[0].getloanscheme('LOAN0001') assert_equal(loanscheme['id'], 'LOAN0001') @@ -116,6 +131,7 @@ def run_test(self): assert_equal(results[0]['interestrate'], Decimal('5.00000000')) assert_equal(results[0]['default'], False) + def update_loanscheme_errors(self): # Try and update a loan scheme with a non-existant name try: self.nodes[0].updateloanscheme(1000, 0.5, 'XXXXXXXX') @@ -130,17 +146,7 @@ def run_test(self): errorString = e.error['message'] assert("Loan scheme LOAN0001 with same interestrate and mincolratio already exists" in errorString) - # Update loan scheme - self.nodes[0].updateloanscheme(1001, 0.7, 'LOANMAX') - self.nodes[0].generate(1) - - # Check loan scheme updated - results = self.nodes[0].listloanschemes() - assert_equal(results[1]['id'], 'LOANMAX') - assert_equal(results[1]['mincolratio'], 1001) - assert_equal(results[1]['interestrate'], Decimal('0.70000000')) - assert_equal(results[1]['default'], True) - + def test_error_update_wrong_height(self): # Try and update a loan scheme below the current height try: self.nodes[0].updateloanscheme(1000, 0.5, 'LOANMAX', 112) @@ -148,32 +154,24 @@ def run_test(self): errorString = e.error['message'] assert("Update height below current block height, set future height" in errorString) - # Update loan scheme after a delay - self.nodes[0].updateloanscheme(1000, 0.5, 'LOANMAX', 115) - self.nodes[0].generate(1) - - # Check loan scheme not updated - results = self.nodes[0].listloanschemes() - assert_equal(results[1]['id'], 'LOANMAX') - assert_equal(results[1]['mincolratio'], 1001) - assert_equal(results[1]['interestrate'], Decimal('0.70000000')) - assert_equal(results[1]['default'], True) - + def test_update_to_same_programmed_values(self): # Try and change another loan scheme to pending loan scheme try: self.nodes[0].updateloanscheme(1000, 0.5, 'LOAN0001') except JSONRPCException as e: errorString = e.error['message'] - assert("Loan scheme LOANMAX with same interestrate and mincolratio pending on block 115" in errorString) + assert("Loan scheme LOANMAX with same interestrate and mincolratio pending on block 142" in errorString) + def goto_update_height_test_update_success(self): # Move to update block and check loan scheme has updated - self.nodes[0].generate(1) + self.goto_block(142) results = self.nodes[0].listloanschemes() assert_equal(results[1]['id'], 'LOANMAX') assert_equal(results[1]['mincolratio'], 1000) assert_equal(results[1]['interestrate'], Decimal('0.50000000')) assert_equal(results[1]['default'], True) + def rollback_and_revert_update(self): # Test rollback reverts delayed update block self.nodes[0].invalidateblock(self.nodes[0].getblockhash(self.nodes[0].getblockcount())) results = self.nodes[0].listloanschemes() @@ -182,21 +180,7 @@ def run_test(self): assert_equal(results[1]['interestrate'], Decimal('0.70000000')) assert_equal(results[1]['default'], True) - # Move forward again to update block and check loan scheme has updated - self.nodes[0].generate(1) - results = self.nodes[0].listloanschemes() - assert_equal(results[1]['id'], 'LOANMAX') - assert_equal(results[1]['mincolratio'], 1000) - assert_equal(results[1]['interestrate'], Decimal('0.50000000')) - assert_equal(results[1]['default'], True) - - # Create more loan schemes - self.nodes[0].createloanscheme(175, 3, 'LOAN0002') - self.nodes[0].createloanscheme(200, 2, 'LOAN0003') - self.nodes[0].createloanscheme(350, 1.5, 'LOAN0004') - self.nodes[0].createloanscheme(500, 1, 'LOAN0005') - self.nodes[0].generate(1) - + def test_listloanschemes_order(self): # Check all loan schemes created and shown in order results = self.nodes[0].listloanschemes() assert_equal(results[0]['id'], 'LOAN0001') @@ -224,6 +208,7 @@ def run_test(self): assert_equal(results[5]['interestrate'], Decimal('0.50000000')) assert_equal(results[5]['default'], True) + def set_default_loanscheme_errors(self): # Test changing the default loan scheme without ID try: self.nodes[0].setdefaultloanscheme('') @@ -252,23 +237,17 @@ def run_test(self): errorString = e.error['message'] assert("Loan scheme with id LOANMAX is already set as default" in errorString) - # Test changing the loan scheme - self.nodes[0].setdefaultloanscheme('LOAN0001') + def set_default_loanscheme_success(self, default_loanscheme_name): + self.nodes[0].setdefaultloanscheme(default_loanscheme_name) self.nodes[0].generate(1) results = self.nodes[0].listloanschemes() - assert_equal(results[0]['id'], 'LOAN0001') - assert_equal(results[0]['default'], True) - assert_equal(results[1]['id'], 'LOAN0002') - assert_equal(results[1]['default'], False) - assert_equal(results[2]['id'], 'LOAN0003') - assert_equal(results[2]['default'], False) - assert_equal(results[3]['id'], 'LOAN0004') - assert_equal(results[3]['default'], False) - assert_equal(results[4]['id'], 'LOAN0005') - assert_equal(results[4]['default'], False) - assert_equal(results[5]['id'], 'LOANMAX') - assert_equal(results[5]['default'], False) + for loanscheme in results: + if loanscheme['id'] == default_loanscheme_name: + assert_equal(loanscheme['default'], True) + else: + assert_equal(loanscheme['default'], False) + def destroy_loanscheme_errors(self): # Test destroying a loan scheme without ID try: self.nodes[0].destroyloanscheme('') @@ -297,65 +276,102 @@ def run_test(self): errorString = e.error['message'] assert("Cannot destroy default loan scheme, set new default first" in errorString) - # Destroy a loan scheme - self.nodes[0].destroyloanscheme('LOANMAX') + def destroy_loanscheme_successs(self, loanscheme_name): + resultsBefore = self.nodes[0].listloanschemes() + + self.nodes[0].destroyloanscheme(loanscheme_name) self.nodes[0].generate(1) - results = self.nodes[0].listloanschemes() - assert_equal(len(results), 5) - assert_equal(results[0]['id'], 'LOAN0001') - assert_equal(results[1]['id'], 'LOAN0002') - assert_equal(results[2]['id'], 'LOAN0003') - assert_equal(results[3]['id'], 'LOAN0004') - assert_equal(results[4]['id'], 'LOAN0005') + resultsAfter = self.nodes[0].listloanschemes() + assert_equal(len(resultsAfter), len(resultsBefore)-1) + for loanscheme in resultsAfter: + assert(loanscheme['id'] != loanscheme_name) + + def test_delayed_destroy_loanscheme(self, loanscheme_name, destruction_height, rollback=False): + resultsBefore = self.nodes[0].listloanschemes() # Create delayed loan scheme destruction - destruction_height = self.nodes[0].getblockcount() + 3 - self.nodes[0].destroyloanscheme('LOAN0002', destruction_height) - self.nodes[0].generate(1) + self.nodes[0].destroyloanscheme(loanscheme_name, destruction_height) + self.nodes[0].generate(2) # Loan scheme should still be present - results = self.nodes[0].listloanschemes() - assert_equal(len(results), 5) - assert_equal(results[1]['id'], 'LOAN0002') + resultsAfter = self.nodes[0].listloanschemes() + assert_equal(len(resultsAfter), len(resultsBefore)) + assert_equal(resultsAfter[1]['id'], loanscheme_name) + + if rollback: + rollback_height = self.nodes[0].getblockcount() + self.goto_block(destruction_height) + + resultsAfterDelay = self.nodes[0].listloanschemes() + assert_equal(len(resultsAfterDelay), len(resultsAfter)-1) + for loanscheme in resultsAfterDelay: + assert(loanscheme['id'] != loanscheme_name) + # rollback + self.nodes[0].invalidateblock(self.nodes[0].getblockhash(rollback_height)) + + def set_default_loanscheme_to_loanscheme_to_be_destroyed(self, loanscheme_name): # Try and set the loan scheme to be destroyed as default try: - self.nodes[0].setdefaultloanscheme('LOAN0002') + self.nodes[0].setdefaultloanscheme(loanscheme_name) except JSONRPCException as e: errorString = e.error['message'] - assert("Cannot set LOAN0002 as default, set to destroyed on block 121" in errorString) - - # Set update on same block and later on - self.nodes[0].updateloanscheme(160, 4.5, 'LOAN0002', destruction_height) - self.nodes[0].updateloanscheme(170, 4, 'LOAN0002', destruction_height + 10) - - # Move to destruction block and check - self.nodes[0].generate(2) - results = self.nodes[0].listloanschemes() - assert_equal(len(results), 4) - assert_equal(results[1]['id'], 'LOAN0003') - - # Rollback to make sure loan restored - self.nodes[0].invalidateblock(self.nodes[0].getblockhash(self.nodes[0].getblockcount())) - results = self.nodes[0].listloanschemes() - assert_equal(len(results), 5) - assert_equal(results[1]['id'], 'LOAN0002') + assert("Cannot set LOAN0002 as default, set to destroyed on block 187" in errorString) + def update_loanscheme_to_same_interest_and_ratio_than_pending_update(self): # Cannot update loan scheme due to pending update try: self.nodes[0].updateloanscheme(170, 4, 'LOAN0003') except JSONRPCException as e: errorString = e.error['message'] - assert("Loan scheme LOAN0002 with same interestrate and mincolratio pending on block 131" in errorString) + assert("Loan scheme LOAN0002 with same interestrate and mincolratio pending on block 197" in errorString) - # Go forward again to destroy loan - self.nodes[0].generate(2) + def loansecheme_check_not_exists(self, loanscheme_name): results = self.nodes[0].listloanschemes() - assert_equal(len(results), 4) - assert_equal(results[1]['id'], 'LOAN0003') + for loanscheme in results: + assert(loanscheme['id'] != loanscheme_name) + def update_existent_loan_to_destroyed_loan_parameters(self): # Can now update loan scheme as pending updates deleted self.nodes[0].updateloanscheme(170, 4, 'LOAN0003') + def run_test(self): + self.setup() + + self.create_loanscheme_before_Fort_Canning() + self.create_update_loanscheme_success(ratio=999, interest=0.6, name='LOANMAX', after_block=None, default=True) + self.test_create_loanscheme_wrong_params() + self.create_update_loanscheme_success(ratio=150, interest=5, name='LOAN0001', after_block=None) + self.test_getloanscheme_errors() + self.test_get_list_loanscheme_success() + self.update_loanscheme_errors() + self.create_update_loanscheme_success(ratio=1001, interest=0.7, name='LOANMAX', after_block=None, default=True, update=True) + self.test_error_update_wrong_height() + self.create_update_loanscheme_success(ratio=1000, interest=0.5, name='LOANMAX', after_block=142, default=True, update=True) + self.test_update_to_same_programmed_values() + self.goto_update_height_test_update_success() + self.rollback_and_revert_update() + self.goto_update_height_test_update_success() + self.create_update_loanscheme_success(ratio=175, interest=3, name='LOAN0002', after_block=None) + self.create_update_loanscheme_success(ratio=200, interest=2, name='LOAN0003', after_block=None) + self.create_update_loanscheme_success(ratio=350, interest=1.5, name='LOAN0004', after_block=None) + self.create_update_loanscheme_success(ratio=500, interest=1, name='LOAN0005', after_block=None) + self.test_listloanschemes_order() + self.set_default_loanscheme_errors() + self.set_default_loanscheme_success('LOAN0001') + self.destroy_loanscheme_errors() + self.destroy_loanscheme_successs('LOANMAX') + self.test_delayed_destroy_loanscheme('LOAN0002', 187) + self.set_default_loanscheme_to_loanscheme_to_be_destroyed('LOAN0002') + # Set update on same block and later on + self.create_update_loanscheme_success(ratio=160, interest=4.5, name='LOAN0002', after_block=187, update=True) + self.create_update_loanscheme_success(ratio=170, interest=4, name='LOAN0002', after_block=197, update=True) + self.test_delayed_destroy_loanscheme('LOAN0002', 187, rollback=True) + self.update_loanscheme_to_same_interest_and_ratio_than_pending_update() + # Go forward and test loanscheme is destroyed + self.goto_block(187) + self.loansecheme_check_not_exists('LOAN0002') + self.update_existent_loan_to_destroyed_loan_parameters() + if __name__ == '__main__': CreateLoanSchemeTest().main() diff --git a/test/functional/feature_loan_vault.py b/test/functional/feature_loan_vault.py index 6994ede2c1f..3a38dbfca6d 100755 --- a/test/functional/feature_loan_vault.py +++ b/test/functional/feature_loan_vault.py @@ -289,22 +289,39 @@ def listvaults_and_filtering(self): list_vault = self.nodes[0].listvaults() assert_equal(len(list_vault), 4) - # check listVaults filter by owner_address + # Filetering + # by owner_address list_vault = self.nodes[0].listvaults({ "ownerAddress": self.owner_addresses[1] }) assert_equal(len(list_vault), 3) for vault in list_vault: assert_equal(vault["ownerAddress"], self.owner_addresses[1]) - # check listVaults filter by loanSchemeId + # by loanSchemeId list_vault = self.nodes[0].listvaults({ "loanSchemeId": "LOAN0003" }) assert_equal(len(list_vault), 2) for vault in list_vault: assert_equal(vault["loanSchemeId"], "LOAN0003") - # check listVaults pagination + # Pagination + # limit list_vault = self.nodes[0].listvaults({}, {"limit": 1}) assert_equal(len(list_vault), 1) + # including_include_start + list_vault_include_start = self.nodes[0].listvaults({}, {"limit": 1, "including_start": False}) + assert_equal(len(list_vault_include_start), 1) + assert(list_vault[0]['vaultId'] != list_vault_include_start[0]['vaultId']) + startId = list_vault[0]['vaultId'] + + # start + list_vault_start = self.nodes[0].listvaults({}, {"start": startId}) + assert_equal(len(list_vault_start), 3) + + # start & include_start + list_vault_start = self.nodes[0].listvaults({}, {"start": startId, "including_start": True}) + assert_equal(len(list_vault_start), 4) + + def test_feeburn(self): assert_equal(self.nodes[0].getburninfo()['feeburn'], Decimal('7')) @@ -540,6 +557,17 @@ def vault_enter_liquidation_updating_oracle(self): assert_raises_rpc_error(-26, 'Vault is under liquidation', self.nodes[0].closevault, self.vaults[0], self.owner_addresses[0]) + def listvaults_state_filtering(self): + # check listvaults + list_vault = self.nodes[0].listvaults() + assert_equal(len(list_vault), 4) + + list_vault = self.nodes[0].listvaults({"state": "active"}) + assert_equal(len(list_vault), 3) + + list_vault = self.nodes[0].listvaults({"state": "inLiquidation"}) + assert_equal(len(list_vault), 1) + def updatevault_to_scheme_with_lower_collateralization_ratio(self): self.nodes[0].deposittovault(self.vaults[1], self.accountDFI, '2.5@DFI') self.nodes[0].generate(1) @@ -574,14 +602,12 @@ def closevault_with_active_loans(self): error_str = e.error['message'] assert("Vault <"+self.vaults[1]+"> has loans" in error_str) self.nodes[0].generate(1) - self.sync_blocks() def test_closevault(self): list_vault = self.nodes[1].listvaults() assert_equal(len(list_vault), 4) self.nodes[0].closevault(self.vaults[3], self.owner_addresses[1]) self.nodes[0].generate(1) - self.sync_blocks() list_vault = self.nodes[1].listvaults() assert_equal(len(list_vault), 3) try: @@ -738,6 +764,77 @@ def overflowed_collateral_value(self): self.nodes[0].closevault(vault_id, address) self.nodes[0].generate(1) + def close_vault_check_burninfo_and_owner_close_fee_payback(self): + self.sync_blocks() + # Save burninfo and balances before create vault + balance1Before = self.nodes[1].getbalance() + burninfoBefore = self.nodes[1].getburninfo() + + # Create vault + owner1 = self.nodes[1].getnewaddress() + vaultId = self.nodes[1].createvault(owner1) + self.nodes[1].generate(1) + + # Save burninfo and balances after create vault + burninfoAfterCreate = self.nodes[1].getburninfo() + balance1AfterCreate = self.nodes[1].getbalance() + + # Close vault + self.nodes[1].closevault(vaultId, owner1) + self.nodes[1].generate(1) + + # Save burninfo and balances after close vault + account1AfterClose = self.nodes[1].getaccount(owner1)[0] + burninfoAfterClose = self.nodes[1].getburninfo() + + # checks + assert_equal(balance1Before-balance1AfterCreate, Decimal('1.00004640')) # ~1 UTXO charged on createvault + assert_equal(burninfoBefore['feeburn']-burninfoAfterCreate['feeburn'], Decimal('-0.50000000')) # 0.5 DFI burned on createvault + assert_equal(account1AfterClose, '0.50000000@DFI') # 0.5 DFI returned to owner + assert_equal(burninfoAfterClose['feeburn'], burninfoAfterCreate['feeburn']) # No more DFI is burned on close vault + + def close_vault_after_update_owner_check_close_fee_payback(self): + self.sync_blocks() + owner1 = self.nodes[1].getnewaddress() + owner2 = self.nodes[0].getnewaddress() + + # Save burninfo and balances before create vault + balance1Before = self.nodes[1].getbalance() + + # Create vault + vaultId = self.nodes[1].createvault(owner1) + self.nodes[1].generate(1) + + # Save burninfo and balances after create vault + balance1AfterCreate = self.nodes[1].getbalance() + + # Update vault to owner2 + self.nodes[1].updatevault(vaultId, {"ownerAddress": owner2}) + self.nodes[1].generate(1) + self.sync_blocks() + vault = self.nodes[0].getvault(vaultId) + assert_equal(owner2, vault['ownerAddress']) + + # Close vault without auth + try: + self.nodes[1].closevault(vaultId, owner1) + except JSONRPCException as e: + errorString = e.error['message'] + assert('Incorrect authorization for' in errorString) + + self.nodes[0].closevault(vaultId, owner1) + self.nodes[0].generate(1) + + self.sync_blocks() + # Save burninfo and balances after close vault + account1AfterClose = self.nodes[1].getaccount(owner1) + account2AfterClose = self.nodes[1].getaccount(owner2) + + # checks + assert_equal(balance1Before-balance1AfterCreate, Decimal('1.00003540')) # ~1 UTXO charged on createvault + assert_equal(account1AfterClose, ['0.50000000@DFI']) # 0.5 DFI returned to owner1 + assert_equal([], account2AfterClose) # close vault called with owner1 + def loan_and_collateral_token_to_govvar(self): # Move to hard fork self.move_to_gw_fork() @@ -975,6 +1072,7 @@ def run_test(self): self.withdraw_breaking_50pctDFI_rule() self.move_interest_from_old_scheme() self.vault_enter_liquidation_updating_oracle() + self.listvaults_state_filtering() self.updatevault_to_scheme_with_lower_collateralization_ratio() self.closevault_with_active_loans() self.test_closevault() @@ -983,6 +1081,8 @@ def run_test(self): self.test_50pctDFI_fresh_vault_takeloan_withdraw() self.test_50pctDFI_rule_after_BTC_price_increase() self.overflowed_collateral_value() + self.close_vault_check_burninfo_and_owner_close_fee_payback() + self.close_vault_after_update_owner_check_close_fee_payback() self.loan_and_collateral_token_to_govvar() if __name__ == '__main__':