diff --git a/test/functional/feature_evm_rollback.py b/test/functional/feature_evm_rollback.py index 160ed60d84..84c7f01758 100755 --- a/test/functional/feature_evm_rollback.py +++ b/test/functional/feature_evm_rollback.py @@ -74,46 +74,63 @@ def setup(self): } } ) - self.nodes[0].generate(2) - - self.creationAddress = "0xe61a3a6eb316d773c773f4ce757a542f673023c6" - self.nodes[0].importprivkey( - "957ac3be2a08afe1fafb55bd3e1d479c4ae6d7bf1c9b2a0dcc5caad6929e6617" + self.nodes[0].generate(1) + self.nodes[0].transferdomain( + [ + { + "src": {"address": self.address, "amount": "100@DFI", "domain": 2}, + "dst": { + "address": self.ethAddress, + "amount": "100@DFI", + "domain": 3, + }, + } + ] ) + self.nodes[0].generate(1) + + self.startBlockNum = self.nodes[0].eth_blockNumber() + self.startBlock = self.nodes[0].eth_getBlockByNumber(self.startBlockNum) + self.start_height = self.nodes[0].getblockcount() def test_rollback_block(self): - self.nodes[0].generate(1) - initialBlockHash = self.nodes[0].getbestblockhash() - blockNumberPreInvalidation = self.nodes[0].eth_blockNumber() - blockPreInvalidation = self.nodes[0].eth_getBlockByNumber( - blockNumberPreInvalidation - ) - assert_equal(blockNumberPreInvalidation, "0x3") - assert_equal(blockPreInvalidation["number"], blockNumberPreInvalidation) + self.rollback_to(self.start_height) + assert_equal(self.nodes[0].eth_blockNumber(), self.startBlockNum) + assert_equal(self.nodes[0].eth_getBlockByNumber("latest"), self.startBlock) - self.nodes[0].invalidateblock(initialBlockHash) + self.nodes[0].generate(1) + nextblockHash = self.nodes[0].getbestblockhash() + nextBlockNum = self.nodes[0].eth_blockNumber() + nextBlock = self.nodes[0].eth_getBlockByNumber(nextBlockNum) + assert_equal(nextBlock["number"], nextBlockNum) + self.nodes[0].invalidateblock(nextblockHash) assert_raises_rpc_error( -32001, "Custom error: header not found", self.nodes[0].eth_getBlockByNumber, - blockNumberPreInvalidation, + nextBlockNum, ) - blockByHash = self.nodes[0].eth_getBlockByHash(blockPreInvalidation["hash"]) + blockByHash = self.nodes[0].eth_getBlockByHash(nextBlock["hash"]) assert_equal(blockByHash, None) - block = self.nodes[0].eth_getBlockByNumber("latest") - assert_equal(block["number"], "0x2") - self.nodes[0].reconsiderblock(initialBlockHash) - blockNumber = self.nodes[0].eth_blockNumber() - block = self.nodes[0].eth_getBlockByNumber(blockNumber) - assert_equal(blockNumber, blockNumberPreInvalidation) - assert_equal(block, blockPreInvalidation) + currBlockNum = self.nodes[0].eth_blockNumber() + currBlock = self.nodes[0].eth_getBlockByNumber(currBlockNum) + assert_equal(currBlockNum, self.startBlockNum) + assert_equal(currBlock, self.startBlock) + + self.nodes[0].reconsiderblock(nextblockHash) + reconsiderBlockNum = self.nodes[0].eth_blockNumber() + reconsiderBlock = self.nodes[0].eth_getBlockByNumber(reconsiderBlockNum) + assert_equal(reconsiderBlockNum, nextBlockNum) + assert_equal(reconsiderBlock, nextBlock) def test_rollback_transactions(self): - initialBlockHash = self.nodes[0].getbestblockhash() + self.rollback_to(self.start_height) + assert_equal(self.nodes[0].eth_blockNumber(), self.startBlockNum) + assert_equal(self.nodes[0].eth_getBlockByNumber("latest"), self.startBlock) - hash = self.nodes[0].eth_sendTransaction( + txHash = self.nodes[0].eth_sendTransaction( { "from": self.ethAddress, "to": self.toAddress, @@ -123,105 +140,214 @@ def test_rollback_transactions(self): } ) self.nodes[0].generate(1) - blockHash = self.nodes[0].getblockhash(self.nodes[0].getblockcount()) + currBlockNum = self.nodes[0].getblockcount() + currblockHash = self.nodes[0].getblockhash(currBlockNum) # Check accounting of EVM fees - tx = { + txInfo = { "from": self.ethAddress, "to": self.toAddress, "value": "0xa", "gas": "0x7a120", # 500_000 "gasPrice": "0x2540BE400", # 10_000_000_000, } - fees = self.nodes[0].debug_feeEstimate(tx) - self.burnt_fee = hex_to_decimal(fees["burnt_fee"]) - self.priority_fee = hex_to_decimal(fees["priority_fee"]) + fees = self.nodes[0].debug_feeEstimate(txInfo) + self.burntFee = hex_to_decimal(fees["burnt_fee"]) + self.priorityFee = hex_to_decimal(fees["priority_fee"]) attributes = self.nodes[0].getgov("ATTRIBUTES")["ATTRIBUTES"] - assert_equal(attributes["v0/live/economy/evm/block/fee_burnt"], self.burnt_fee) + assert_equal(attributes["v0/live/economy/evm/block/fee_burnt"], self.burntFee) assert_equal( - attributes["v0/live/economy/evm/block/fee_burnt_min"], self.burnt_fee + attributes["v0/live/economy/evm/block/fee_burnt_min"], self.burntFee ) assert_equal( - attributes["v0/live/economy/evm/block/fee_burnt_min_hash"], blockHash + attributes["v0/live/economy/evm/block/fee_burnt_min_hash"], currblockHash ) assert_equal( - attributes["v0/live/economy/evm/block/fee_burnt_max"], self.burnt_fee + attributes["v0/live/economy/evm/block/fee_burnt_max"], self.burntFee ) assert_equal( - attributes["v0/live/economy/evm/block/fee_burnt_max_hash"], blockHash + attributes["v0/live/economy/evm/block/fee_burnt_max_hash"], currblockHash ) assert_equal( - attributes["v0/live/economy/evm/block/fee_priority"], self.priority_fee + attributes["v0/live/economy/evm/block/fee_priority"], self.priorityFee ) assert_equal( - attributes["v0/live/economy/evm/block/fee_priority_max"], self.priority_fee + attributes["v0/live/economy/evm/block/fee_priority_max"], self.priorityFee ) - blockNumberPreInvalidation = self.nodes[0].eth_blockNumber() - blockPreInvalidation = self.nodes[0].eth_getBlockByNumber( - blockNumberPreInvalidation - ) - assert_equal(blockNumberPreInvalidation, "0x4") - assert_equal(blockPreInvalidation["number"], blockNumberPreInvalidation) + evmBlockNum = self.nodes[0].eth_blockNumber() + evmBlock = self.nodes[0].eth_getBlockByNumber(evmBlockNum) + assert_equal(evmBlock["number"], evmBlockNum) - txPreInvalidation = self.nodes[0].eth_getTransactionByHash(hash) - receiptPreInvalidation = self.nodes[0].eth_getTransactionReceipt(hash) - assert_equal(blockPreInvalidation["transactions"][0], txPreInvalidation["hash"]) + tx = self.nodes[0].eth_getTransactionByHash(txHash) + txReceipt = self.nodes[0].eth_getTransactionReceipt(txHash) + assert_equal(evmBlock["transactions"][0], tx["hash"]) assert_equal( - blockPreInvalidation["transactions"][0], - receiptPreInvalidation["transactionHash"], + evmBlock["transactions"][0], + txReceipt["transactionHash"], ) - self.nodes[0].invalidateblock(initialBlockHash) + # Check that chain tip is back to the starting block + self.nodes[0].invalidateblock(currblockHash) + currEvmBlockNum = self.nodes[0].eth_blockNumber() + currEvmBlock = self.nodes[0].eth_getBlockByNumber(currEvmBlockNum) + assert_equal(currEvmBlockNum, self.startBlockNum) + assert_equal(currEvmBlock, self.startBlock) - tx = self.nodes[0].eth_getTransactionByHash(hash) - receipt = self.nodes[0].eth_getTransactionReceipt(hash) + # Check that txs are no longer valid + tx = self.nodes[0].eth_getTransactionByHash(txHash) + receipt = self.nodes[0].eth_getTransactionReceipt(txHash) assert_equal(tx, None) assert_equal(receipt, None) - self.nodes[0].reconsiderblock(initialBlockHash) - tx = self.nodes[0].eth_getTransactionByHash(hash) - receipt = self.nodes[0].eth_getTransactionReceipt(hash) - assert_equal(blockPreInvalidation["transactions"][0], tx["hash"]) + self.nodes[0].reconsiderblock(currblockHash) + reconsiderBlockNum = self.nodes[0].eth_blockNumber() + reconsiderBlock = self.nodes[0].eth_getBlockByNumber(reconsiderBlockNum) + tx = self.nodes[0].eth_getTransactionByHash(txHash) + receipt = self.nodes[0].eth_getTransactionReceipt(txHash) + assert_equal(reconsiderBlockNum, evmBlockNum) + assert_equal(reconsiderBlock, evmBlock) + assert_equal(reconsiderBlock["transactions"][0], tx["hash"]) assert_equal( - blockPreInvalidation["transactions"][0], receipt["transactionHash"] + reconsiderBlock["transactions"][0], + receipt["transactionHash"], ) - def run_test(self): - self.setup() + def test_state_rollback(self): + self.rollback_to(self.start_height) + assert_equal(self.nodes[0].eth_blockNumber(), self.startBlockNum) + assert_equal(self.nodes[0].eth_getBlockByNumber("latest"), self.startBlock) + startBlockHash = self.nodes[0].getbestblockhash() + startdbdump = self.nodes[0].debug_dumpdb("all") + # assert_equal((startdbdump != None), True) - self.nodes[0].transferdomain( - [ - { - "src": {"address": self.address, "amount": "100@DFI", "domain": 2}, - "dst": { - "address": self.creationAddress, - "amount": "100@DFI", - "domain": 3, - }, - } - ] - ) + evmAddresses = [] + numEvmAddresses = 10 + for i in range(numEvmAddresses): + evmAddresses.append(self.nodes[0].getnewaddress("", "erc55")) + + # Transferdomain txs + tdHashes = [] + for i in range(numEvmAddresses): + hash = self.nodes[0].transferdomain( + [ + { + "src": { + "address": self.address, + "amount": "10@DFI", + "domain": 2, + }, + "dst": { + "address": evmAddresses[i], + "amount": "10@DFI", + "domain": 3, + }, + } + ] + ) + tdHashes.append(hash) self.nodes[0].generate(1) - self.nodes[0].transferdomain( - [ - { - "src": {"address": self.address, "amount": "100@DFI", "domain": 2}, - "dst": { - "address": self.ethAddress, - "amount": "100@DFI", - "domain": 3, - }, - } - ] - ) + # first block (transferdomain txs) + firstBlockNum = self.nodes[0].eth_blockNumber() + firstBlock = self.nodes[0].eth_getBlockByNumber(firstBlockNum) + firstBlockHash = self.nodes[0].getbestblockhash() + block_info = self.nodes[0].getblock(self.nodes[0].getbestblockhash(), 4) + tdtx_id = 0 + for tx_info in block_info["tx"][1:]: + if tx_info["vm"]["txtype"] == "TransferDomain": + # Check that all transferdomain txs are minted in the first block + assert_equal(tx_info["txid"], tdHashes[tdtx_id]) + tdtx_id += 1 + assert_equal(tdtx_id, numEvmAddresses) + firstdbdump = self.nodes[0].debug_dumpdb("all") + # assert_equal((firstdbdump != None), True) + + # second block (transfer txs) + hashes = [] + counts = [0] * numEvmAddresses + for i in range(numEvmAddresses): + for j in range(numEvmAddresses): + if i == j: + continue + + hash = self.nodes[0].eth_sendTransaction( + { + "nonce": hex(counts[i]), + "from": evmAddresses[i], + "to": evmAddresses[j], + "value": "0xDE0B6B3A7640000", # 1 DFI + "gas": "0x5209", + "gasPrice": "0x5D21DBA00", # 25_000_000_000 + } + ) + counts[i] += 1 + hashes.append(hash) + self.nodes[0].generate(1) + # Second block (evm txs) + secondBlockNum = self.nodes[0].eth_blockNumber() + secondBlock = self.nodes[0].eth_getBlockByNumber(secondBlockNum) + secondBlockHash = self.nodes[0].getbestblockhash() + block_info = self.nodes[0].getblock(self.nodes[0].getbestblockhash(), 4) + for idx, tx_info in enumerate(block_info["tx"][1:]): + assert_equal(tx_info["vm"]["vmtype"], "evm") + assert_equal(tx_info["vm"]["txtype"], "Evm") + assert_equal(tx_info["vm"]["msg"]["hash"], hashes[idx][2:]) + assert_equal(len(block_info["tx"][1:]), numEvmAddresses * (numEvmAddresses - 1)) + seconddbdump = self.nodes[0].debug_dumpdb("all") + # assert_equal((seconddbdump != None), True) + + # Invalidate second block + self.nodes[0].invalidateblock(secondBlockHash) + blockByHash = self.nodes[0].eth_getBlockByHash(secondBlock["hash"]) + assert_equal(blockByHash, None) + currBlockNum = self.nodes[0].eth_blockNumber() + currBlock = self.nodes[0].eth_getBlockByNumber(currBlockNum) + currBlockHash = self.nodes[0].getbestblockhash() + assert_equal(currBlockNum, firstBlockNum) + assert_equal(currBlock, firstBlock) + assert_equal(currBlockHash, firstBlockHash) + currdbdump = self.nodes[0].debug_dumpdb("all") + # assert_equal((currdbdump != None), True) + assert_equal(currdbdump, firstdbdump) + + # Invalidate first block + self.nodes[0].invalidateblock(firstBlockHash) + blockByHash = self.nodes[0].eth_getBlockByHash(firstBlock["hash"]) + assert_equal(blockByHash, None) + currBlockNum = self.nodes[0].eth_blockNumber() + currBlock = self.nodes[0].eth_getBlockByNumber(currBlockNum) + currBlockHash = self.nodes[0].getbestblockhash() + assert_equal(currBlockNum, self.startBlockNum) + assert_equal(currBlock, self.startBlock) + assert_equal(currBlockHash, startBlockHash) + currdbdump = self.nodes[0].debug_dumpdb("all") + # assert_equal((currdbdump != None), True) + assert_equal(currdbdump, startdbdump) + + # Reconsider blocks + self.nodes[0].reconsiderblock(firstBlockHash) + reconsiderBlockNum = self.nodes[0].eth_blockNumber() + reconsiderBlock = self.nodes[0].eth_getBlockByNumber(reconsiderBlockNum) + reconsiderBlockHash = self.nodes[0].getbestblockhash() + assert_equal(reconsiderBlockNum, secondBlockNum) + assert_equal(reconsiderBlock, secondBlock) + assert_equal(reconsiderBlockHash, secondBlockHash) + currdbdump = self.nodes[0].debug_dumpdb("all") + # assert_equal((currdbdump != None), True) + assert_equal(currdbdump, seconddbdump) + + def run_test(self): + self.setup() + self.test_rollback_block() self.test_rollback_transactions() + self.test_state_rollback() + if __name__ == "__main__": EVMRolllbackTest().main()