From fd0312b0a1cb388ac31f92a962cc9fc29337d0b0 Mon Sep 17 00:00:00 2001 From: dcorral <55594560+hidiego@users.noreply.github.com> Date: Fri, 9 Sep 2022 11:50:36 +0200 Subject: [PATCH] Add merge tests and enhance tests framework with helper functions (#1442) * Add rollback_to function to test framework --- test/functional/feature_poolswap.py | 20 +- test/functional/feature_restore_utxo.py | 11 +- test/functional/feature_smart_contracts.py | 25 +- test/functional/feature_token_merge.py | 468 ++++++++++++++++++ .../feature_token_merge_usd_value.py | 325 ++++++++++++ .../feature_token_split_mechanism.py | 25 +- .../feature_token_split_usd_value.py | 11 +- test/functional/rpc_getstoredinterest.py | 12 - .../test_framework/test_framework.py | 19 + test/functional/test_framework/util.py | 3 + test/functional/test_runner.py | 2 + 11 files changed, 846 insertions(+), 75 deletions(-) create mode 100755 test/functional/feature_token_merge.py create mode 100755 test/functional/feature_token_merge_usd_value.py diff --git a/test/functional/feature_poolswap.py b/test/functional/feature_poolswap.py index d212dea45c1..9554f7065b2 100755 --- a/test/functional/feature_poolswap.py +++ b/test/functional/feature_poolswap.py @@ -13,7 +13,6 @@ from test_framework.authproxy import JSONRPCException from test_framework.util import ( assert_equal, - connect_nodes_bi, disconnect_nodes, assert_raises_rpc_error, ) @@ -23,24 +22,17 @@ class PoolPairTest (DefiTestFramework): def set_test_params(self): - self.num_nodes = 4 + self.num_nodes = 3 # node0: main (Foundation) - # node3: revert create (all) - # node2: Non Foundation self.setup_clean_chain = True self.extra_args = [ ['-txnotokens=0', '-amkheight=50', '-bayfrontheight=50', '-bayfrontgardensheight=0', '-dakotaheight=160', '-fortcanningheight=163', '-fortcanninghillheight=170', '-fortcanningroadheight=177', '-acindex=1', '-dexstats'], ['-txnotokens=0', '-amkheight=50', '-bayfrontheight=50', '-bayfrontgardensheight=0', '-dakotaheight=160', '-fortcanningheight=163', '-fortcanninghillheight=170', '-fortcanningroadheight=177', '-acindex=1', '-dexstats'], - ['-txnotokens=0', '-amkheight=50', '-bayfrontheight=50', '-bayfrontgardensheight=0', '-dakotaheight=160', '-fortcanningheight=163', '-fortcanninghillheight=170', '-fortcanningroadheight=177', '-dexstats'], ['-txnotokens=0', '-amkheight=50', '-bayfrontheight=50', '-bayfrontgardensheight=0', '-dakotaheight=160', '-fortcanningheight=163', '-fortcanninghillheight=170', '-fortcanningroadheight=177', '-dexstats']] def setup(self): assert_equal(len(self.nodes[0].listtokens()), 1) # only one token == DFI - self.setup_tokens() - # Stop node #3 for future revert - self.stop_node(3) - self.symbolGOLD = "GOLD#" + self.get_id_token("GOLD") self.symbolSILVER = "SILVER#" + self.get_id_token("SILVER") self.idGold = list(self.nodes[0].gettoken(self.symbolGOLD).keys())[0] @@ -537,14 +529,10 @@ def update_comission_and_fee_to_1pct_pool2(self): assert_equal(attributes['v0/live/economy/dex/%s/fee_burn_a'%(self.idBL)], Decimal(str(round(dexoutfee, 8)))) def revert_to_initial_state(self): - self.start_node(3) - self.nodes[3].generate(30) - - connect_nodes_bi(self.nodes, 0, 3) - connect_nodes_bi(self.nodes, 1, 3) - connect_nodes_bi(self.nodes, 2, 3) - self.sync_blocks() + self.rollback_to(block=0, nodes=[0, 1, 2]) assert_equal(len(self.nodes[0].listpoolpairs()), 0) + assert_equal(len(self.nodes[1].listpoolpairs()), 0) + assert_equal(len(self.nodes[2].listpoolpairs()), 0) def run_test(self): diff --git a/test/functional/feature_restore_utxo.py b/test/functional/feature_restore_utxo.py index 32a026646cb..a5f862ca2e9 100755 --- a/test/functional/feature_restore_utxo.py +++ b/test/functional/feature_restore_utxo.py @@ -16,13 +16,6 @@ def set_test_params(self): self.extra_args = [['-txnotokens=0', '-amkheight=1', '-bayfrontheight=1'], ['-txnotokens=0', '-amkheight=1', '-bayfrontheight=1']] - def rollback(self, count): - block = self.nodes[1].getblockhash(count) - self.nodes[1].invalidateblock(block) - self.nodes[1].clearmempool() - assert_equal(len(self.nodes[1].getrawmempool()), 0) - assert_equal(self.nodes[1].getblockcount(), count - 1) - def run_test(self): self.nodes[0].generate(101) self.sync_blocks() @@ -69,7 +62,7 @@ def run_test(self): # Set up for rollback tests disconnect_nodes(self.nodes[0], 1) - block = self.nodes[1].getblockcount() + 1 + block = self.nodes[1].getblockcount() node1_utxos = len(self.nodes[1].listunspent()) # Test rollbacks @@ -79,7 +72,7 @@ def run_test(self): self.nodes[1].generate(1) self.nodes[1].accounttoaccount(node1_source, {node1_source: "1@BTC"}) self.nodes[1].generate(1) - self.rollback(block) + self.rollback_to(block, nodes=[1]) assert_equal(len(self.nodes[1].listunspent()), node1_utxos) if __name__ == '__main__': diff --git a/test/functional/feature_smart_contracts.py b/test/functional/feature_smart_contracts.py index 2eebf857b3a..edeb1cc4d29 100755 --- a/test/functional/feature_smart_contracts.py +++ b/test/functional/feature_smart_contracts.py @@ -17,11 +17,6 @@ def set_test_params(self): self.setup_clean_chain = True self.extra_args = [['-txnotokens=0', '-amkheight=1', '-bayfrontheight=1', '-eunosheight=1', '-fortcanningheight=1', '-fortcanninghillheight=1010', '-subsidytest=1', '-txindex=1', '-jellyfish_regtest=1']] - def rollback(self, count): - block = self.nodes[0].getblockhash(count) - self.nodes[0].invalidateblock(block) - self.nodes[0].clearmempool() - def run_test(self): self.nodes[0].generate(1000) @@ -147,7 +142,7 @@ def run_test(self): assert_equal(balance + staker_reward + community_reward - Decimal('18336.225') + fee, self.nodes[0].getbalance()) # Test swap for more than in community fund by 1 Sat - block = self.nodes[0].getblockcount() + 1 + block = self.nodes[0].getblockcount() self.nodes[0].setgov({"ATTRIBUTES":{'v0/params/dfip2201/premium':'0.00000000'}}) self.nodes[0].generate(1) assert_raises_rpc_error(-32600, 'amount 18336.22500000 is less than 18336.22500001', self.nodes[0].executesmartcontract, dfip, '18336.22500001@2', address) @@ -159,20 +154,20 @@ def run_test(self): assert('0' not in self.nodes[0].listsmartcontracts()) # Set "real world" prices - self.rollback(block) + self.rollback_to(block) prices = [{'currency': 'USD', 'tokenAmount': '2@DFI'}, {'currency': 'USD', 'tokenAmount': '40000@BTC'}] self.nodes[0].setoracledata(oracle, int(time.time()), prices) self.nodes[0].generate(10) # Test default 2.5% premium - block = self.nodes[0].getblockcount() + 1 + block = self.nodes[0].getblockcount() self.nodes[0].executesmartcontract(dfip, '0.09999999@2', address) self.nodes[0].generate(1) assert_equal(2049.999795, float(self.nodes[0].getaccount(address)[0].split('@')[0])) # Test 5% premium - self.rollback(block) + self.rollback_to(block) self.nodes[0].setgov({"ATTRIBUTES":{'v0/params/dfip2201/premium':'0.05'}}) self.nodes[0].generate(1) self.nodes[0].executesmartcontract(dfip, '0.09999999@2', address) @@ -180,7 +175,7 @@ def run_test(self): assert_equal(2099.99979, float(self.nodes[0].getaccount(address)[0].split('@')[0])) # Test 0.1% premium - self.rollback(block) + self.rollback_to(block) self.nodes[0].setgov({"ATTRIBUTES":{'v0/params/dfip2201/premium':'0.001'}}) self.nodes[0].generate(1) self.nodes[0].executesmartcontract(dfip, '0.09999999@2', address) @@ -188,7 +183,7 @@ def run_test(self): assert_equal(2001.9997998, float(self.nodes[0].getaccount(address)[0].split('@')[0])) # Test 0.000001% premium - self.rollback(block) + self.rollback_to(block) self.nodes[0].setgov({"ATTRIBUTES":{'v0/params/dfip2201/premium':'0.00000001'}}) self.nodes[0].generate(1) self.nodes[0].executesmartcontract(dfip, '0.1@2', address) @@ -196,7 +191,7 @@ def run_test(self): assert_equal(2000.00002, float(self.nodes[0].getaccount(address)[0].split('@')[0])) # Test 0% premium - self.rollback(block) + self.rollback_to(block) self.nodes[0].setgov({"ATTRIBUTES":{'v0/params/dfip2201/premium':'0.00000000'}}) self.nodes[0].generate(1) self.nodes[0].executesmartcontract(dfip, '0.1@2', address) @@ -204,13 +199,13 @@ def run_test(self): assert_equal(2000, float(self.nodes[0].getaccount(address)[0].split('@')[0])) # Swap min amount - self.rollback(block) + self.rollback_to(block) self.nodes[0].executesmartcontract(dfip, '0.00001@2', address) self.nodes[0].generate(1) assert_equal(0.205, float(self.nodes[0].getaccount(address)[0].split('@')[0])) # Test smallest min amount - self.rollback(block) + self.rollback_to(block) self.nodes[0].setgov({"ATTRIBUTES":{'v0/params/dfip2201/minswap':'0.00000001'}}) self.nodes[0].generate(1) self.nodes[0].executesmartcontract(dfip, '0.00000001@2', address) @@ -218,7 +213,7 @@ def run_test(self): assert_equal(0.000205, float(self.nodes[0].getaccount(address)[0].split('@')[0])) # Test no smallest min amount - self.rollback(block) + self.rollback_to(block) self.nodes[0].setgov({"ATTRIBUTES":{'v0/params/dfip2201/minswap':'0.00000001'}}) self.nodes[0].generate(1) self.nodes[0].executesmartcontract(dfip, '0.00000001@2', address) diff --git a/test/functional/feature_token_merge.py b/test/functional/feature_token_merge.py new file mode 100755 index 00000000000..75ca0d4da85 --- /dev/null +++ b/test/functional/feature_token_merge.py @@ -0,0 +1,468 @@ +#!/usr/bin/env python3 +# Copyright (c) 2014-2019 The Bitcoin Core developers +# Copyright (c) DeFi Blockchain Developers +# Distributed under the MIT software license, see the accompanying +# file LICENSE or http://www.opensource.org/licenses/mit-license.php. + +"""Test token merge""" + +from decimal import Decimal +import time + +from test_framework.test_framework import DefiTestFramework + +def truncate(str, decimal): + return str if not str.find('.') + 1 else str[:str.find('.') + decimal + 1] + +class TokenMergeTest(DefiTestFramework): + def set_test_params(self): + self.num_nodes = 1 + self.setup_clean_chain = True + self.extra_args = [ + ['-txnotokens=0', '-amkheight=1', '-bayfrontheight=1', '-eunosheight=1', '-fortcanningheight=1', '-fortcanningmuseumheight=1', '-fortcanninghillheight=1', '-fortcanningroadheight=1', '-fortcanningcrunchheight=1', '-greatworldheight=1', '-jellyfish_regtest=1', '-subsidytest=1']] + + def setup_oracles(self): + # Symbols + self.symbolDUSD = 'DUSD' + self.symbolDFI = 'DFI' + self.symbolT1 = 'T1' + self.symbolT2 = 'T2' + self.symbolT3 = 'T3' + + # Price feeds + price_feed = [ + {"currency": "USD", "token": self.symbolDFI}, + {"currency": "USD", "token": self.symbolDUSD}, + {"currency": "USD", "token": self.symbolT2}, + {"currency": "USD", "token": self.symbolT1}, + {"currency": "USD", "token": self.symbolT3}, + ] + + # Appoint oracle + oracle_address = self.nodes[0].getnewaddress("", "legacy") + oracle = self.nodes[0].appointoracle(oracle_address, price_feed, 10) + self.nodes[0].generate(1) + + # Set Oracle prices + oracle_prices = [ + {"currency": "USD", "tokenAmount": f"1@{self.symbolDUSD}"}, + {"currency": "USD", "tokenAmount": f"3@{self.symbolDFI}"}, + {"currency": "USD", "tokenAmount": f"10000@{self.symbolT1}"}, + {"currency": "USD", "tokenAmount": f"100@{self.symbolT2}"}, + {"currency": "USD", "tokenAmount": f"0.00000001@{self.symbolT3}"}, + ] + self.nodes[0].setoracledata(oracle, int(time.time()), oracle_prices) + self.nodes[0].generate(10) + + def setup_tokens(self): + # Set loan tokens + self.nodes[0].setloantoken({ + 'symbol': self.symbolT2, + 'name': self.symbolT2, + 'fixedIntervalPriceId': f"{self.symbolT2}/USD", + "isDAT": True, + 'interest': 0 + }) + self.nodes[0].generate(1) + + self.nodes[0].setloantoken({ + 'symbol': self.symbolDUSD, + 'name': self.symbolDUSD, + 'fixedIntervalPriceId': f"{self.symbolDUSD}/USD", + 'mintable': True, + 'interest': 0 + }) + self.nodes[0].generate(1) + + self.nodes[0].setloantoken({ + 'symbol': self.symbolT1, + 'name': self.symbolT1, + 'fixedIntervalPriceId': f"{self.symbolT1}/USD", + 'mintable': True, + 'interest': 0 + }) + self.nodes[0].generate(1) + + self.nodes[0].setloantoken({ + 'symbol': self.symbolT3, + 'name': self.symbolT3, + 'fixedIntervalPriceId': f"{self.symbolT3}/USD", + 'mintable': True, + 'interest': 0 + }) + self.nodes[0].generate(1) + + # Set collateral tokens + self.nodes[0].setcollateraltoken({ + 'token': self.symbolDFI, + 'factor': 1, + 'fixedIntervalPriceId': f"{self.symbolDFI}/USD" + }) + self.nodes[0].generate(1) + + self.nodes[0].setcollateraltoken({ + 'token': self.symbolDUSD, + 'factor': 1, + 'fixedIntervalPriceId': f"{self.symbolDUSD}/USD" + }) + self.nodes[0].generate(1) + + self.nodes[0].setcollateraltoken({ + 'token': self.symbolT2, + 'factor': 1, + 'fixedIntervalPriceId': f"{self.symbolT2}/USD" + }) + self.nodes[0].generate(1) + + self.nodes[0].setcollateraltoken({ + 'token': self.symbolT1, + 'factor': 1, + 'fixedIntervalPriceId': f"{self.symbolT1}/USD" + }) + self.nodes[0].generate(1) + + # Store token IDs + self.idDUSD = list(self.nodes[0].gettoken(self.symbolDUSD).keys())[0] + self.idT1 = list(self.nodes[0].gettoken(self.symbolT1).keys())[0] + self.idT2 = list(self.nodes[0].gettoken(self.symbolT2).keys())[0] + self.idT3 = list(self.nodes[0].gettoken(self.symbolT3).keys())[0] + + def setup_accounts(self): + self.account1 = self.nodes[0].get_genesis_keys().ownerAuthAddress + self.account2 = self.nodes[0].getnewaddress() + self.account3 = self.nodes[0].getnewaddress() + + self.nodes[0].utxostoaccount({self.account1: "100000@DFI"}) + self.nodes[0].generate(1) + + self.nodes[0].minttokens("110300001@DUSD") + self.nodes[0].minttokens("110000@T1") + self.nodes[0].minttokens("205000@T2") + self.nodes[0].minttokens("100000000@T3") + self.nodes[0].generate(1) + self.sync_blocks() + + self.nodes[0].accounttoaccount(self.account1, {self.account2: ["55039700.499@DUSD", "49900@DFI", "54890@T1", "102295@T2", "49900000@T3"]}) + self.nodes[0].accounttoaccount(self.account1, {self.account3: ["110300.0010@DUSD", "100@DFI", "110@T1", "205@T2", "100000@T3"]}) + self.nodes[0].generate(1) + self.sync_blocks() + + def setup_pools(self): + self.nodes[0].createpoolpair({ + "tokenA": self.symbolDFI, + "tokenB": self.symbolDUSD, + "commission": 0.01, + "status": True, + "ownerAddress": self.account1, + }) + self.nodes[0].generate(1) + self.symbolDFI_DUSD = "DFI-DUSD" + self.idDFI_DUSD = list(self.nodes[0].gettoken(self.symbolDFI_DUSD).keys())[0] + + self.nodes[0].createpoolpair({ + "tokenA": self.symbolT1, + "tokenB": self.symbolDUSD, + "commission": 0.01, + "status": True, + "ownerAddress": self.account1, + }) + self.nodes[0].generate(1) + self.symbolT1_DUSD = "T1-DUSD" + self.idT1_DUSD = list(self.nodes[0].gettoken(self.symbolT1_DUSD).keys())[0] + + self.nodes[0].createpoolpair({ + "tokenA": self.symbolT2, + "tokenB": self.symbolDUSD, + "commission": 0.05, + "status": True, + "ownerAddress": self.account1, + }) + self.nodes[0].generate(1) + self.symbolT2_DUSD = "T2-DUSD" + self.idT2_DUSD = list(self.nodes[0].gettoken(self.symbolT2_DUSD).keys())[0] + + self.nodes[0].createpoolpair({ + "tokenA": self.symbolT3, + "tokenB": self.symbolDUSD, + "commission": 0.01, + "status": True, + "ownerAddress": self.account1, + }) + self.nodes[0].generate(1) + self.symbolT3_DUSD = "T3-DUSD" + self.idT3_DUSD = list(self.nodes[0].gettoken(self.symbolT3_DUSD).keys())[0] + + self.nodes[0].generate(1) + + self.nodes[0].createpoolpair({ + "tokenA": self.symbolT1, + "tokenB": self.symbolT2, + "commission": 0.001, + "status": True, + "ownerAddress": self.account1, + }) + self.nodes[0].generate(1) + self.sync_blocks() + + self.symbolT1_T2 = "T1-T2" + self.idT1_T2 = list(self.nodes[0].gettoken(self.symbolT1_T2).keys())[0] + + + # Add liquidity + for _ in range(10): + self.nodes[0].addpoolliquidity({self.account1: ["5000@DFI", "15000@DUSD"]}, self.account1) + self.nodes[0].generate(1) + self.nodes[0].addpoolliquidity({self.account2: ["4990@DFI", "14970@DUSD"]}, self.account2) + self.nodes[0].generate(1) + self.nodes[0].addpoolliquidity({self.account3: ["10@DFI", "30@DUSD"]}, self.account3) + self.nodes[0].generate(1) + + for _ in range(10): + self.nodes[0].addpoolliquidity({self.account1: ["500@T1", "5000000@DUSD"]}, self.account1) + self.nodes[0].generate(1) + self.nodes[0].addpoolliquidity({self.account2: ["499@T1", "4990000@DUSD"]}, self.account2) + self.nodes[0].generate(1) + self.nodes[0].addpoolliquidity({self.account3: ["1@T1", "10000@DUSD"]}, self.account3) + self.nodes[0].generate(1) + + for _ in range(10): + self.nodes[0].addpoolliquidity({self.account1: ["10000@T2", "500000@DUSD"]}, self.account1) + self.nodes[0].generate(1) + self.nodes[0].addpoolliquidity({self.account2: ["9980@T2", "499000@DUSD"]}, self.account2) + self.nodes[0].generate(1) + self.nodes[0].addpoolliquidity({self.account3: ["20@T2", "1000@DUSD"]}, self.account3) + self.nodes[0].generate(1) + + for _ in range(10): + self.nodes[0].addpoolliquidity({self.account1: ["5000000@T3", "0.05@DUSD"]}, self.account1) + self.nodes[0].generate(1) + self.nodes[0].addpoolliquidity({self.account2: ["4990000@T3", "0.0499@DUSD"]}, self.account2) + self.nodes[0].generate(1) + self.nodes[0].addpoolliquidity({self.account3: ["10000@T3", "0.0001@DUSD"]}, self.account3) + self.nodes[0].generate(1) + + for _ in range(10): + self.nodes[0].addpoolliquidity({self.account1: ["5000@T1", "250@T2"]}, self.account1) + self.nodes[0].generate(1) + self.nodes[0].addpoolliquidity({self.account2: ["4990@T1", "249.5@T2"]}, self.account2) + self.nodes[0].generate(1) + self.nodes[0].addpoolliquidity({self.account3: ["10@T1", "0.5@T2"]}, self.account3) + self.nodes[0].generate(1) + + def setup(self): + self.nodes[0].generate(101) + self.setup_oracles() + self.setup_tokens() + self.setup_accounts() + self.setup_pools() + + # Make the split and return split height for revert if needed + def merge(self, tokenId, keepLocked=False): + tokenSymbol = self.getTokenSymbolFromId(tokenId) + self.nodes[0].setgov({"ATTRIBUTES":{f'v0/locks/token/{tokenId}':'true'}}) + self.nodes[0].generate(1) + + # Token split + splitHeight = self.nodes[0].getblockcount() + 2 + self.nodes[0].setgov({"ATTRIBUTES":{f'v0/oracles/splits/{str(splitHeight)}':f'{tokenId}/-2'}}) + self.nodes[0].generate(2) + + tokenId = list(self.nodes[0].gettoken(tokenSymbol).keys())[0] + if not keepLocked: + self.nodes[0].setgov({"ATTRIBUTES":{f'v0/locks/token/{tokenId}':'false'}}) + self.nodes[0].generate(1) + + return splitHeight + + def getTokenSymbolFromId(self, tokenId): + token = self.nodes[0].gettoken(tokenId) + tokenSymbol = token[str(tokenId)]["symbol"].split('/')[0] + return tokenSymbol + + # Returns a list of pool token ids in which token is present + def getTokenPools(self, tokenId): + tokenSymbol = self.getTokenSymbolFromId(tokenId) + tokenPools = {} + currentPools = self.nodes[0].listpoolpairs() + for pool in currentPools: + if tokenSymbol in currentPools[pool]["symbol"] and currentPools[pool]["status"]: + tokenPools[pool] = currentPools[pool] + assert(len(tokenPools) > 0) + return tokenPools + + def check_attributes_on_merge(self, tokenId, revert=False): + tokenSymbol = self.getTokenSymbolFromId(tokenId) + revert_block = self.nodes[0].getblockcount() + pools = self.getTokenPools(tokenId) + poolsSymbol = [] + for pool in pools: + poolsSymbol.append(self.getTokenSymbolFromId(pool)) + + # set LP and Tokens gov vars + for poolId in pools: + self.nodes[0].setgov({"ATTRIBUTES":{f'v0/poolpairs/{poolId}/token_a_fee_pct': '0.01', + f'v0/poolpairs/{poolId}/token_b_fee_pct': '0.03'}}) + + self.nodes[0].setgov({"ATTRIBUTES":{f'v0/token/{tokenId}/dex_in_fee_pct': '0.02', + f'v0/token/{tokenId}/dex_out_fee_pct': '0.005'}}) + self.nodes[0].generate(1) + + result = self.nodes[0].listgovs()[8][0]['ATTRIBUTES'] + assert(f'v0/token/{tokenId}/dex_in_fee_pct' in result) + assert(f'v0/token/{tokenId}/dex_out_fee_pct' in result) + for poolId in pools: + assert(f'v0/poolpairs/{poolId}/token_a_fee_pct' in result) + assert(f'v0/poolpairs/{poolId}/token_b_fee_pct' in result) + + splitHeight = self.merge(tokenId) - 2 + self.nodes[0].generate(1) + + new_token_id = list(self.nodes[0].gettoken(tokenSymbol).keys())[0] + new_pools = [] + for poolSymbol in poolsSymbol: + new_pools.append(list(self.nodes[0].gettoken(poolSymbol).keys())[0]) + + result = self.nodes[0].listgovs()[8][0]['ATTRIBUTES'] + + for poolId in pools: + assert(f'v0/poolpairs/{poolId}/token_a_fee_pct' not in result) + assert(f'v0/poolpairs/{poolId}/token_b_fee_pct' not in result) + assert(f'v0/token/{tokenId}/dex_in_fee_pct' not in result) + assert(f'v0/token/{tokenId}/dex_out_fee_pct' not in result) + + for new_pool_id in new_pools: + assert(f'v0/poolpairs/{new_pool_id}/token_a_fee_pct' in result) + assert(f'v0/poolpairs/{new_pool_id}/token_b_fee_pct' in result) + assert(f'v0/token/{new_token_id}/dex_in_fee_pct' in result) + assert(f'v0/token/{new_token_id}/dex_out_fee_pct' in result) + + if not revert: + return new_token_id + else: + self.rollback_to(splitHeight) + result = self.nodes[0].listgovs()[8][0]['ATTRIBUTES'] + for poolId in pools: + assert(f'v0/poolpairs/{poolId}/token_a_fee_pct' in result) + assert(f'v0/poolpairs/{poolId}/token_b_fee_pct' in result) + assert(f'v0/token/{tokenId}/dex_in_fee_pct' in result) + assert(f'v0/token/{tokenId}/dex_out_fee_pct' in result) + for new_pool_id in new_pools: + assert(f'v0/poolpairs/{new_pool_id}/token_a_fee_pct' not in result) + assert(f'v0/poolpairs/{new_pool_id}/token_b_fee_pct' not in result) + assert(f'v0/token/{new_token_id}/dex_in_fee_pct' not in result) + assert(f'v0/token/{new_token_id}/dex_out_fee_pct' not in result) + + self.rollback_to(revert_block) + result = self.nodes[0].listgovs()[8][0]['ATTRIBUTES'] + for new_pool_id in new_pools: + assert(f'v0/poolpairs/{new_pool_id}/token_a_fee_pct' not in result) + assert(f'v0/poolpairs/{new_pool_id}/token_b_fee_pct' not in result) + assert(f'v0/token/{new_token_id}/dex_in_fee_pct' not in result) + assert(f'v0/token/{new_token_id}/dex_out_fee_pct' not in result) + return 0 + + def getAmountFromAccount(self, account, symbol): + amounts = self.nodes[0].getaccount(account) + amountStr = '0' + for amount in amounts: + amountSplit = amount.split('@') + if symbol == amountSplit[1]: + amountStr = amountSplit[0] + return amountStr + + def check_amounts_on_merge(self, poolId, tokenId, revert=False): + self.nodes[0].generate(10) + tokenSymbol = self.getTokenSymbolFromId(tokenId) + poolSymbol = self.getTokenSymbolFromId(poolId) + tokenBSymbol = poolSymbol.split('-')[0] + if tokenBSymbol == tokenSymbol: + tokenBSymbol = poolSymbol.split('-')[1] + revertHeight = self.nodes[0].getblockcount() + + amountLPBeforeAcc1 = self.getAmountFromAccount(self.account1, poolSymbol) + amountLPBeforeAcc2 = self.getAmountFromAccount(self.account2, poolSymbol) + amountLPBeforeAcc3 = self.getAmountFromAccount(self.account3, poolSymbol) + if amountLPBeforeAcc1 != '0': + self.nodes[0].removepoolliquidity(self.account1, amountLPBeforeAcc1+"@"+poolSymbol, []) + self.nodes[0].generate(1) + if amountLPBeforeAcc2 != '0': + self.nodes[0].removepoolliquidity(self.account2, amountLPBeforeAcc2+"@"+poolSymbol, []) + self.nodes[0].generate(1) + if amountLPBeforeAcc3 != '0': + self.nodes[0].removepoolliquidity(self.account3, amountLPBeforeAcc3+"@"+poolSymbol, []) + self.nodes[0].generate(1) + + amountTokenBeforeAcc1 = self.getAmountFromAccount(self.account1, tokenSymbol) + amountTokenB_BeforeAcc1 = self.getAmountFromAccount(self.account1, tokenBSymbol) + amountTokenBeforeAcc2 = self.getAmountFromAccount(self.account2, tokenSymbol) + amountTokenB_BeforeAcc2 = self.getAmountFromAccount(self.account2, tokenBSymbol) + amountTokenBeforeAcc3 = self.getAmountFromAccount(self.account3, tokenSymbol) + amountTokenB_BeforeAcc3 = self.getAmountFromAccount(self.account3, tokenBSymbol) + + self.rollback_to(revertHeight) + + self.merge(tokenId) + new_token_id = list(self.nodes[0].gettoken(tokenSymbol).keys())[0] + + amountLPAfterAcc1 = self.getAmountFromAccount(self.account1, poolSymbol) + amountLPAfterAcc2 = self.getAmountFromAccount(self.account2, poolSymbol) + amountLPAfterAcc3 = self.getAmountFromAccount(self.account3, poolSymbol) + self.nodes[0].removepoolliquidity(self.account1, amountLPAfterAcc1+"@"+poolSymbol, []) + self.nodes[0].generate(1) + self.nodes[0].removepoolliquidity(self.account2, amountLPAfterAcc2+"@"+poolSymbol, []) + self.nodes[0].generate(1) + self.nodes[0].removepoolliquidity(self.account3, amountLPAfterAcc3+"@"+poolSymbol, []) + self.nodes[0].generate(1) + amountTokenAfterAcc1 = self.getAmountFromAccount(self.account1, tokenSymbol) + amountTokenB_AfterAcc1 = self.getAmountFromAccount(self.account1, tokenBSymbol) + amountTokenAfterAcc2 = self.getAmountFromAccount(self.account2, tokenSymbol) + amountTokenB_AfterAcc2 = self.getAmountFromAccount(self.account2, tokenBSymbol) + amountTokenAfterAcc3 = self.getAmountFromAccount(self.account3, tokenSymbol) + amountTokenB_AfterAcc3 = self.getAmountFromAccount(self.account3, tokenBSymbol) + # Check difference is not grater than 0,001% rounding difference + assert((Decimal(amountTokenB_BeforeAcc1) - Decimal(amountTokenB_AfterAcc1)).copy_abs() <= (Decimal(0.00001)*Decimal(amountTokenB_BeforeAcc1))) + assert((Decimal(amountTokenB_BeforeAcc2) - Decimal(amountTokenB_AfterAcc2)).copy_abs() <= (Decimal(0.00001)*Decimal(amountTokenB_BeforeAcc2))) + assert((Decimal(amountTokenB_BeforeAcc3) - Decimal(amountTokenB_AfterAcc3)).copy_abs() <= (Decimal(0.00001)*Decimal(amountTokenB_BeforeAcc3))) + assert(((Decimal(amountTokenBeforeAcc1)/2) - Decimal(amountTokenAfterAcc1)).copy_abs() <= Decimal(0.00001)*Decimal(amountTokenBeforeAcc1)) + assert(((Decimal(amountTokenBeforeAcc2)/2) - Decimal(amountTokenAfterAcc2)).copy_abs() <= Decimal(0.00001)*Decimal(amountTokenBeforeAcc2)) + assert(((Decimal(amountTokenBeforeAcc3)/2) - Decimal(amountTokenAfterAcc3)).copy_abs() <= Decimal(0.00001)*Decimal(amountTokenBeforeAcc3)) + + if revert: + self.rollback_to(revertHeight) + return new_token_id + + def run_test(self): + self.setup() + self.check_attributes_on_merge(self.idT1, revert=True) + self.check_attributes_on_merge(self.idT2, revert=True) + self.check_attributes_on_merge(self.idT1, revert=True) + self.check_attributes_on_merge(self.idT3, revert=True) + self.idT3 = self.check_attributes_on_merge(self.idT3, revert=False) + self.idT1 = self.check_attributes_on_merge(self.idT1, revert=False) + self.idT2 = self.check_attributes_on_merge(self.idT2, revert=False) + self.idT3 = self.check_attributes_on_merge(self.idT3, revert=False) + + # Second round split + self.check_attributes_on_merge(self.idT1, revert=True) + self.check_attributes_on_merge(self.idT2, revert=True) + self.check_attributes_on_merge(self.idT1, revert=True) + self.check_attributes_on_merge(self.idT3, revert=True) + self.idT3 = self.check_attributes_on_merge(self.idT3, revert=False) + self.idT1 = self.check_attributes_on_merge(self.idT1, revert=False) + self.idT2 = self.check_attributes_on_merge(self.idT2, revert=False) + self.idT3 = self.check_attributes_on_merge(self.idT3, revert=False) + + # Check amounts pre an dpost merge + self.check_amounts_on_merge(self.idT1_DUSD, self.idT1, revert=True) + self.check_amounts_on_merge(self.idT2_DUSD, self.idT2, revert=True) + self.check_amounts_on_merge(self.idT3_DUSD, self.idT3, revert=True) + self.check_amounts_on_merge(self.idT1_T2, self.idT2, revert=True) + self.check_amounts_on_merge(self.idT1_T2, self.idT1, revert=True) + self.idT1 = self.check_amounts_on_merge(self.idT1_DUSD, self.idT1, revert=False) + self.idT2 = self.check_amounts_on_merge(self.idT2_DUSD, self.idT2, revert=False) + self.idT3 = self.check_amounts_on_merge(self.idT3_DUSD, self.idT3, revert=False) + self.idT2 = self.check_amounts_on_merge(self.idT1_T2, self.idT2, revert=False) + +if __name__ == '__main__': + TokenMergeTest().main() + diff --git a/test/functional/feature_token_merge_usd_value.py b/test/functional/feature_token_merge_usd_value.py new file mode 100755 index 00000000000..ab36e12b812 --- /dev/null +++ b/test/functional/feature_token_merge_usd_value.py @@ -0,0 +1,325 @@ +#!/usr/bin/env python3 +# Copyright (c) 2014-2019 The Bitcoin Core developers +# Copyright (c) DeFi Blockchain Developers +# Distributed under the MIT software license, see the accompanying +# file LICENSE or http://www.opensource.org/licenses/mit-license.php. + +"""Test token merge""" + +from test_framework.test_framework import DefiTestFramework +from test_framework.util import assert_equal, assert_greater_than_or_equal, almost_equal + +from decimal import Decimal +import time +import random + +def truncate(str, decimal): + return str if not str.find('.') + 1 else str[:str.find('.') + decimal + 1] + +class TokenMergeUSDValueTest(DefiTestFramework): + def set_test_params(self): + self.num_nodes = 1 + self.setup_clean_chain = True + self.extra_args = [ + ['-txnotokens=0', '-amkheight=1', '-bayfrontheight=1', '-eunosheight=1', '-fortcanningheight=1', '-fortcanningmuseumheight=1', '-fortcanninghillheight=1', '-fortcanningroadheight=1', '-fortcanningcrunchheight=1', '-greatworldheight=1', '-jellyfish_regtest=1', '-subsidytest=1']] + + def setup_oracles(self): + # Symbols + self.symbolDUSD = 'DUSD' + self.symbolT1 = 'T1' + + # Price feeds + price_feed = [ + {"currency": "USD", "token": self.symbolDUSD}, + {"currency": "USD", "token": self.symbolT1}, + ] + + # Appoint oracle + oracle_address = self.nodes[0].getnewaddress("", "legacy") + self.oracle = self.nodes[0].appointoracle(oracle_address, price_feed, 10) + self.nodes[0].generate(1) + + # Set Oracle prices + oracle_prices = [ + {"currency": "USD", "tokenAmount": f"1@{self.symbolDUSD}"}, + {"currency": "USD", "tokenAmount": f"1@{self.symbolT1}"}, + ] + self.nodes[0].setoracledata(self.oracle, int(time.time()), oracle_prices) + self.nodes[0].generate(10) + + def setup_tokens(self): + # Set loan tokens + + self.nodes[0].setloantoken({ + 'symbol': self.symbolDUSD, + 'name': self.symbolDUSD, + 'fixedIntervalPriceId': f"{self.symbolDUSD}/USD", + 'mintable': True, + 'interest': 0 + }) + self.nodes[0].generate(1) + + self.nodes[0].setloantoken({ + 'symbol': self.symbolT1, + 'name': self.symbolT1, + 'fixedIntervalPriceId': f"{self.symbolT1}/USD", + 'mintable': True, + 'interest': 0 + }) + self.nodes[0].generate(1) + + self.nodes[0].setcollateraltoken({ + 'token': self.symbolDUSD, + 'factor': 1, + 'fixedIntervalPriceId': f"{self.symbolDUSD}/USD" + }) + self.nodes[0].generate(1) + + self.nodes[0].setcollateraltoken({ + 'token': self.symbolT1, + 'factor': 1, + 'fixedIntervalPriceId': f"{self.symbolT1}/USD" + }) + self.nodes[0].generate(1) + + # Store token IDs + self.idDUSD = list(self.nodes[0].gettoken(self.symbolDUSD).keys())[0] + self.idT1 = list(self.nodes[0].gettoken(self.symbolT1).keys())[0] + + def generate_and_fill_accounts(self, nAccounts=20): + self.accounts = [] + for _ in range(nAccounts): + self.accounts.append(self.nodes[0].getnewaddress()) + totalDUSD = 10000000 + totalT1 = 10000000 + self.accounts.sort() + self.nodes[0].utxostoaccount({self.account1: "10000000@0"}) + self.nodes[0].minttokens(str(totalDUSD)+"@DUSD") + self.nodes[0].minttokens(str(totalT1)+"@T1") + self.nodes[0].generate(1) + + perAccountDUSD = totalDUSD/nAccounts + perAccountT1 = totalT1/nAccounts + for account in self.accounts: + self.nodes[0].accounttoaccount(self.account1, {account: [str(perAccountDUSD)+"@DUSD", str(perAccountT1)+"@T1"]}) + self.nodes[0].generate(1) + + def setup_accounts(self): + self.account1 = self.nodes[0].get_genesis_keys().ownerAuthAddress + self.generate_and_fill_accounts() + + def add_total_account_to_liquidity_pool(self): + size = 1000000 + for account in self.accounts: + totalAmount = Decimal(self.get_amount_from_account(account, self.symbolDUSD)) + while size >= 10: + while Decimal(totalAmount) >= size: + tmpAmount = Decimal(random.randint(int(size/10), int(size-1))) + self.nodes[0].addpoolliquidity({account: [str(tmpAmount)+"@T1", str(tmpAmount)+"@DUSD"]}, account) + self.nodes[0].generate(1) + totalAmount -= tmpAmount + size /= 10 + finalAmount = Decimal(self.get_amount_from_account(account, self.symbolDUSD)) + self.nodes[0].addpoolliquidity({account: [str(finalAmount)+"@T1", str(finalAmount)+"@DUSD"]}, account) + self.nodes[0].generate(1) + totalAmount -= finalAmount + + def setup_pools(self): + self.nodes[0].createpoolpair({ + "tokenA": self.symbolT1, + "tokenB": self.symbolDUSD, + "commission": 0, + "status": True, + "ownerAddress": self.account1, + }) + self.nodes[0].generate(1) + self.symbolT1_DUSD = "T1-DUSD" + self.idT1_DUSD = list(self.nodes[0].gettoken(self.symbolT1_DUSD).keys())[0] + + self.add_total_account_to_liquidity_pool() + + def setup(self): + self.nodes[0].generate(101) + self.setup_oracles() + self.setup_tokens() + self.setup_accounts() + self.setup_pools() + + # /20 split + def oracle_split(self): + oracle_prices = [ + {"currency": "USD", "tokenAmount": f"1@{self.symbolDUSD}"}, + {"currency": "USD", "tokenAmount": f"0.2@{self.symbolT1}"}, + ] + self.nodes[0].setoracledata(self.oracle, int(time.time()), oracle_prices) + self.nodes[0].generate(10) + + def merge(self, tokenId, keepLocked=False, oracleSplit=False, multiplier=2): + self.nodes[0].setgov({"ATTRIBUTES":{f'v0/locks/token/{tokenId}':'true'}}) + self.nodes[0].generate(1) + + if oracleSplit: + self.oracle_split() + + # Token split + splitHeight = self.nodes[0].getblockcount() + 2 + self.nodes[0].setgov({"ATTRIBUTES":{f'v0/oracles/splits/{str(splitHeight)}':f'{tokenId}/-{multiplier}'}}) + self.nodes[0].generate(2) + + self.idT1old = tokenId + self.idT1 = list(self.nodes[0].gettoken(self.symbolT1).keys())[0] + + if not keepLocked: + self.nodes[0].setgov({"ATTRIBUTES":{f'v0/locks/token/{self.idT1}':'false'}}) + self.nodes[0].generate(1) + + def remove_from_pool(self, account): + amountLP = self.get_amount_from_account(account, "T1-DUSD") + self.nodes[0].removepoolliquidity(account, amountLP+"@T1-DUSD", []) + self.nodes[0].generate(1) + + def accounts_usd_values(self): + values =[] + revertHeight = self.nodes[0].getblockcount() + activePriceT1 = self.nodes[0].getfixedintervalprice(f"{self.symbolT1}/USD")["activePrice"] + activePriceDUSD = self.nodes[0].getfixedintervalprice(f"{self.symbolDUSD}/USD")["activePrice"] + for account in self.accounts: + amounts = {} + self.remove_from_pool(account) + amounts["account"] = account + amounts["DUSD"] = Decimal(self.get_amount_from_account(account, "DUSD")) * Decimal(activePriceDUSD) + amounts["T1"] = Decimal(self.get_amount_from_account(account, "T1")) *Decimal(activePriceT1) + values.append(amounts) + self.rollback_to(revertHeight) + return values + + def compare_value_list(self, pre, post): + for index, amount in enumerate(pre): + if index != 0: + almost_equal(amount["DUSD"], post[index]["DUSD"]) + almost_equal(amount["T1"], post[index]["T1"]) + + def compare_vaults_list(self, pre, post): + for index, vault in enumerate(pre): + if index != 0: + almost_equal(vault["collateralValue"], post[index]["collateralValue"]) + almost_equal(vault["loanValue"], post[index]["loanValue"]) + almost_equal(vault["collateralRatio"], post[index]["collateralRatio"]) + + def get_token_symbol_from_id(self, tokenId): + token = self.nodes[0].gettoken(tokenId) + tokenSymbol = token[str(tokenId)]["symbol"].split('/')[0] + return tokenSymbol + + # Returns a list of pool token ids in which token is present + def get_token_pools(self, tokenId): + tokenSymbol = self.get_token_symbol_from_id(tokenId) + tokenPools = {} + currentPools = self.nodes[0].listpoolpairs() + for pool in currentPools: + if tokenSymbol in currentPools[pool]["symbol"] and currentPools[pool]["status"]: + tokenPools[pool] = currentPools[pool] + assert(len(tokenPools) > 0) + return tokenPools + + def get_amount_from_account(self, account, symbol): + amounts = self.nodes[0].getaccount(account) + amountStr = '0' + for amount in amounts: + amountSplit = amount.split('@') + if symbol == amountSplit[1]: + amountStr = amountSplit[0] + return amountStr + + def compare_usd_account_value_on_merge(self, revert=False): + revertHeight = self.nodes[0].getblockcount() + value_accounts_pre_split = self.accounts_usd_values() + self.merge(self.idT1, oracleSplit=True, multiplier=20) + value_accounts_post_split = self.accounts_usd_values() + # TODO fail + self.compare_value_list(value_accounts_pre_split, value_accounts_post_split) + if revert: + self.rollback_to(revertHeight) + self.idT1=self.idT1old + + def setup_vaults(self, collateralSplit=False): + self.nodes[0].createloanscheme(200, 0.01, 'LOAN_0') + + self.vaults = [] + vaultCount = 0 + for account in self.accounts: + self.remove_from_pool(account) + vaultId= self.nodes[0].createvault(account) + self.nodes[0].generate(1) + vaultCount += 1 + self.vaults.append(vaultId) + amountT1 = self.get_amount_from_account(account, "T1") + amountT1 = Decimal(amountT1)/Decimal(4) + if collateralSplit: + amountT1 = Decimal(amountT1)/Decimal(2) + amountDUSD = self.get_amount_from_account(account, "DUSD") + amountDUSD = Decimal(amountDUSD)/Decimal(2) + if collateralSplit: + self.nodes[0].deposittovault(vaultId, account, str(amountT1)+"@T1") + self.nodes[0].deposittovault(vaultId, account, str(amountDUSD)+"@DUSD") + self.nodes[0].generate(1) + amountT1Loan = Decimal(amountT1)/Decimal(2) + self.nodes[0].takeloan({ + 'vaultId': vaultId, + 'amounts': str(amountT1Loan)+"@T1"}) + self.nodes[0].generate(1) + + def get_vaults_usd_values(self): + vault_values = [] + for vault in self.vaults: + vaultInfo = self.nodes[0].getvault(vault) + vault_values.append(vaultInfo) + return vault_values + + def compare_usd_vaults_values_on_split(self, revert=False): + revertHeight = self.nodes[0].getblockcount() + self.setup_vaults(collateralSplit=False) + vault_values_pre_split = self.get_vaults_usd_values() + self.merge(self.idT1, oracleSplit=True, multiplier=20) + self.nodes[0].generate(40) + vault_values_post_split = self.get_vaults_usd_values() + self.compare_vaults_list(vault_values_pre_split,vault_values_post_split) + + if revert: + self.rollback_to(revertHeight) + self.idT1=self.idT1old + + def test_values_non_zero_with_token_locked(self): + self.setup_vaults() + self.merge(self.idT1, keepLocked=True) + vaults_values = self.get_vaults_usd_values() + for vault in vaults_values: + assert_equal(vault["state"], "frozen") + assert_equal(vault["collateralValue"], -1) + assert_equal(vault["loanValue"], -1) + assert_equal(vault["interestValue"], -1) + assert_equal(vault["informativeRatio"], -1) + assert_equal(vault["collateralRatio"], -1) + + def test_values_after_token_unlock(self): + # Unlock token + self.nodes[0].setgov({"ATTRIBUTES":{f'v0/locks/token/{self.idT1}':'false'}}) + self.nodes[0].generate(1) + vaults_values = self.get_vaults_usd_values() + for vault in vaults_values: + assert_equal(vault["state"], "active") + assert_greater_than_or_equal(vault["collateralValue"], 0) + assert_greater_than_or_equal(vault["loanValue"], 0) + assert_greater_than_or_equal(vault["interestValue"], 0) + assert_greater_than_or_equal(vault["informativeRatio"], 0) + assert_greater_than_or_equal(vault["collateralRatio"], 0) + + def run_test(self): + self.setup() + self.compare_usd_account_value_on_merge(revert=True) + self.compare_usd_vaults_values_on_split(revert=True) + self.test_values_non_zero_with_token_locked() + self.test_values_after_token_unlock() + +if __name__ == '__main__': + TokenMergeUSDValueTest().main() diff --git a/test/functional/feature_token_split_mechanism.py b/test/functional/feature_token_split_mechanism.py index c7829d6de32..fc15f2c2596 100755 --- a/test/functional/feature_token_split_mechanism.py +++ b/test/functional/feature_token_split_mechanism.py @@ -286,15 +286,6 @@ def getTokenSymbolFromId(self, tokenId): tokenSymbol = token[str(tokenId)]["symbol"].split('/')[0] return tokenSymbol - def revert(self, block, revertTokeIds=False): - blockhash = self.nodes[0].getblockhash(block) - self.nodes[0].invalidateblock(blockhash) - self.nodes[0].clearmempool() - if revertTokeIds: - self.idT1 = list(self.nodes[0].gettoken(self.symbolT1).keys())[0] - self.idT2 = list(self.nodes[0].gettoken(self.symbolT2).keys())[0] - self.idT3 = list(self.nodes[0].gettoken(self.symbolT3).keys())[0] - # Returns a list of pool token ids in which token is present def getTokenPools(self, tokenId): tokenSymbol = self.getTokenSymbolFromId(tokenId) @@ -331,7 +322,7 @@ def check_attributes_on_split(self, tokenId, revert=False): assert(f'v0/poolpairs/{poolId}/token_a_fee_pct' in result) assert(f'v0/poolpairs/{poolId}/token_b_fee_pct' in result) - splitHeight = self.split(tokenId) + splitHeight = self.split(tokenId) - 2 self.nodes[0].generate(1) new_token_id = list(self.nodes[0].gettoken(tokenSymbol).keys())[0] @@ -356,7 +347,7 @@ def check_attributes_on_split(self, tokenId, revert=False): if not revert: return new_token_id else: - self.revert(splitHeight) + self.rollback_to(splitHeight) result = self.nodes[0].listgovs()[8][0]['ATTRIBUTES'] for poolId in pools: assert(f'v0/poolpairs/{poolId}/token_a_fee_pct' in result) @@ -369,7 +360,7 @@ def check_attributes_on_split(self, tokenId, revert=False): assert(f'v0/token/{new_token_id}/dex_in_fee_pct' not in result) assert(f'v0/token/{new_token_id}/dex_out_fee_pct' not in result) - self.revert(revert_block) + self.rollback_to(revert_block) result = self.nodes[0].listgovs()[8][0]['ATTRIBUTES'] for new_pool_id in new_pools: assert(f'v0/poolpairs/{new_pool_id}/token_a_fee_pct' not in result) @@ -416,7 +407,7 @@ def check_amounts_on_split(self, poolId, tokenId, revert=False): amountTokenBeforeAcc3 = self.getAmountFromAccount(self.account3, tokenSymbol) amountTokenB_BeforeAcc3 = self.getAmountFromAccount(self.account3, tokenBSymbol) - self.revert(revertHeight) + self.rollback_to(revertHeight) self.split(tokenId) new_token_id = list(self.nodes[0].gettoken(tokenSymbol).keys())[0] @@ -445,7 +436,7 @@ def check_amounts_on_split(self, poolId, tokenId, revert=False): assert(((Decimal(amountTokenBeforeAcc3)*2) - Decimal(amountTokenAfterAcc3)).copy_abs() <= Decimal(0.00001)*Decimal(amountTokenBeforeAcc3)) if revert: - self.revert(revertHeight) + self.rollback_to(revertHeight) return new_token_id def run_test(self): @@ -470,7 +461,11 @@ def run_test(self): self.idT2 = self.check_attributes_on_split(self.idT2, revert=False) self.idT3 = self.check_attributes_on_split(self.idT3, revert=False) - self.revert(initialStateBlock, revertTokeIds=True) + self.rollback_to(initialStateBlock) + # Update token ids + self.idT1 = list(self.nodes[0].gettoken(self.symbolT1).keys())[0] + self.idT2 = list(self.nodes[0].gettoken(self.symbolT2).keys())[0] + self.idT3 = list(self.nodes[0].gettoken(self.symbolT3).keys())[0] self.gotoFCC() self.check_amounts_on_split(self.idT1_DUSD, self.idT1, revert=True) diff --git a/test/functional/feature_token_split_usd_value.py b/test/functional/feature_token_split_usd_value.py index 2c546ad5b3d..fcc6fcb5cbb 100755 --- a/test/functional/feature_token_split_usd_value.py +++ b/test/functional/feature_token_split_usd_value.py @@ -188,11 +188,6 @@ def remove_from_pool(self, account): self.nodes[0].removepoolliquidity(account, amountLP+"@T1-DUSD", []) self.nodes[0].generate(1) - def revert(self, block): - blockhash = self.nodes[0].getblockhash(block) - self.nodes[0].invalidateblock(blockhash) - self.nodes[0].clearmempool() - def accounts_usd_values(self): values =[] revertHeight = self.nodes[0].getblockcount() @@ -205,7 +200,7 @@ def accounts_usd_values(self): amounts["DUSD"] = Decimal(self.get_amount_from_account(account, "DUSD")) * Decimal(activePriceDUSD) amounts["T1"] = Decimal(self.get_amount_from_account(account, "T1")) *Decimal(activePriceT1) values.append(amounts) - self.revert(revertHeight) + self.rollback_to(revertHeight) return values def compare_value_list(self, pre, post): @@ -254,7 +249,7 @@ def compare_usd_account_value_on_split(self, revert=False): # TODO fail self.compare_value_list(value_accounts_pre_split, value_accounts_post_split) if revert: - self.revert(revertHeight) + self.rollback_to(revertHeight) self.idT1=self.idT1old def setup_vaults(self, collateralSplit=False): @@ -301,7 +296,7 @@ def compare_usd_vaults_values_on_split(self, revert=False): self.compare_vaults_list(vault_values_pre_split,vault_values_post_split) if revert: - self.revert(revertHeight) + self.rollback_to(revertHeight) self.idT1=self.idT1old def test_values_non_zero_with_token_locked(self): diff --git a/test/functional/rpc_getstoredinterest.py b/test/functional/rpc_getstoredinterest.py index a42fe61aff8..19b815899bb 100755 --- a/test/functional/rpc_getstoredinterest.py +++ b/test/functional/rpc_getstoredinterest.py @@ -34,18 +34,6 @@ def set_test_params(self): '-txindex=1', '-simulatemainnet=1'] ] - # Utils - def rollback_to(self, block): - self.log.info("rollback to: %d", block) - node = self.nodes[0] - current_height = node.getblockcount() - if current_height == block: - return - blockhash = node.getblockhash(block + 1) - node.invalidateblock(blockhash) - node.clearmempool() - assert_equal(block, node.getblockcount()) - def new_vault(self, loan_scheme, deposit=10): vaultId = self.nodes[0].createvault(self.account0, loan_scheme) self.nodes[0].generate(1) diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py index ed19bc35dcd..1ba5ee5a4ea 100755 --- a/test/functional/test_framework/test_framework.py +++ b/test/functional/test_framework/test_framework.py @@ -404,6 +404,25 @@ def import_deterministic_coinbase_privkeys(self): n.importprivkey(privkey=n.get_genesis_keys().ownerPrivKey, label='coinbase', rescan=True) n.importprivkey(privkey=n.get_genesis_keys().operatorPrivKey, label='coinbase', rescan=True) + # rollback one node (Default = node 0) + def _rollback_to(self, block, node=0): + node = self.nodes[node] + current_height = node.getblockcount() + if current_height == block: + return + blockhash = node.getblockhash(block + 1) + node.invalidateblock(blockhash) + node.clearmempool() + + # rollback to block + # nodes param is a list of node numbers to roll back ([0, 1, 2, 3...] (Default -> None -> node 0) + def rollback_to(self, block, nodes=None): + if nodes is None: + self._rollback_to(block) + else: + for node in nodes: + self._rollback_to(block, node=node) + def run_test(self): """Tests must override this method to define test logic""" raise NotImplementedError diff --git a/test/functional/test_framework/util.py b/test/functional/test_framework/util.py index 1e17d93b7e9..6de07ba6d25 100644 --- a/test/functional/test_framework/util.py +++ b/test/functional/test_framework/util.py @@ -46,6 +46,9 @@ def assert_greater_than_or_equal(thing1, thing2): if thing1 < thing2: raise AssertionError("%s < %s" % (str(thing1), str(thing2))) +def almost_equal(x, y, threshold=0.0001): + return abs(x-y) < threshold + def assert_raises(exc, fun, *args, **kwds): assert_raises_message(exc, None, fun, *args, **kwds) diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py index 199a8ee1664..a3a5015b287 100755 --- a/test/functional/test_runner.py +++ b/test/functional/test_runner.py @@ -127,6 +127,8 @@ 'feature_token_split.py', 'feature_token_split_mechanism.py', 'feature_token_split_usd_value.py', + 'feature_token_merge_usd_value.py', + 'feature_token_merge.py', 'feature_communitybalance_reorg.py', 'feature_auth_return_change.py', 'feature_setgov.py',