diff --git a/lib/ain-evm/src/evm.rs b/lib/ain-evm/src/evm.rs index 076c894166..0110bb779d 100644 --- a/lib/ain-evm/src/evm.rs +++ b/lib/ain-evm/src/evm.rs @@ -151,6 +151,7 @@ impl EVMServices { block.header.state_root }); + debug!("[construct_block] queue_id: {:?}", queue_id); debug!("[construct_block] beneficiary: {:?}", beneficiary); let (vicinity, parent_hash, current_block_number) = match parent_data { None => ( @@ -211,6 +212,10 @@ impl EVMServices { executor.update_storage(address, storage)?; } + debug!( + "[construct_block] Processing {:?} transactions in queue", + queue.transactions.len() + ); for queue_item in queue.transactions.clone() { match queue_item.tx { QueueTx::SignedTx(signed_tx) => { @@ -461,7 +466,7 @@ impl EVMServices { .core .get_latest_contract_storage(address, ain_contracts::u256_to_h256(U256::one()))?; - debug!("Count: {:#x}", count + U256::one()); + debug!("[counter_contract] count: {:#x}", count + U256::one()); Ok(DeployContractInfo { address, diff --git a/test/functional/contracts/Loop.sol b/test/functional/contracts/Loop.sol new file mode 100644 index 0000000000..d587fa2633 --- /dev/null +++ b/test/functional/contracts/Loop.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: GPL-3.0 + +pragma solidity >=0.7.0 <0.9.0; + +contract Loop { + function loop(uint256 num) public { + uint256 number = 0; + while (number < num) { + number++; + } + } +} diff --git a/test/functional/feature_evm.py b/test/functional/feature_evm.py index 642bde57f3..3584a3cb6e 100755 --- a/test/functional/feature_evm.py +++ b/test/functional/feature_evm.py @@ -4,6 +4,7 @@ # Distributed under the MIT software license, see the accompanying # file LICENSE or http://www.opensource.org/licenses/mit-license.php. """Test EVM behaviour""" +from test_framework.evm_contract import EVMContract from test_framework.evm_key_pair import EvmKeyPair from test_framework.test_framework import DefiTestFramework from test_framework.util import ( @@ -87,6 +88,9 @@ def run_test(self): # Multiple mempool fee replacement self.multiple_eth_rbf() + # Multiple mempool fee replacement + self.mempool_block_limit() + # Test that node should not crash without chainId param self.test_tx_without_chainid() @@ -134,11 +138,11 @@ def erc55_wallet_support(self): self.address = self.nodes[0].get_genesis_keys().ownerAuthAddress self.eth_address = "0x9b8a4af42140d8a4c153a822f02571a1dd037e89" self.eth_address_bech32 = "bcrt1qta8meuczw0mhqupzjl5wplz47xajz0dn0wxxr8" - eth_address_privkey = ( + self.eth_address_privkey = ( "af990cc3ba17e776f7f57fcc59942a82846d75833fa17d2ba59ce6858d886e23" ) self.to_address = "0x6c34cbb9219d8caa428835d2073e8ec88ba0a110" - to_address_privkey = ( + self.to_address_privkey = ( "17b8cb134958b3d8422b6c43b0732fcdb8c713b524df2d45de12f0c7e214ba35" ) @@ -242,8 +246,8 @@ def erc55_wallet_support(self): assert_equal(addr_info["witness_version"], 16) # Import addresses - self.nodes[0].importprivkey(eth_address_privkey) # eth_address - self.nodes[0].importprivkey(to_address_privkey) # self.to_address + self.nodes[0].importprivkey(self.eth_address_privkey) # eth_address + self.nodes[0].importprivkey(self.to_address_privkey) # self.to_address # Generate chain self.nodes[0].generate(101) @@ -1204,6 +1208,139 @@ def multiple_eth_rbf(self): assert_equal(block_txs[1], tx0) assert_equal(block_txs[2], tx1) + def mempool_block_limit(self): + abi, bytecode = EVMContract.from_file("Loop.sol", "Loop").compile() + compiled = self.nodes[0].w3.eth.contract(abi=abi, bytecode=bytecode) + tx = compiled.constructor().build_transaction( + { + "chainId": self.nodes[0].w3.eth.chain_id, + "nonce": self.nodes[0].w3.eth.get_transaction_count(self.eth_address), + "maxFeePerGas": 10_000_000_000, + "maxPriorityFeePerGas": 1_500_000_000, + "gas": 1_000_000, + } + ) + signed = self.nodes[0].w3.eth.account.sign_transaction( + tx, self.eth_address_privkey + ) + hash = self.nodes[0].w3.eth.send_raw_transaction(signed.rawTransaction) + self.nodes[0].generate(1) + receipt = self.nodes[0].w3.eth.wait_for_transaction_receipt(hash) + contract = self.nodes[0].w3.eth.contract( + address=receipt["contractAddress"], abi=abi + ) + + hashes = [] + start_nonce = self.nodes[0].w3.eth.get_transaction_count(self.eth_address) + for i in range(40): + # tx call actual used gas - 1_761_626. + # tx should always pass evm tx validation, but may return early in construct block. + tx = contract.functions.loop(10_000).build_transaction( + { + "chainId": self.nodes[0].w3.eth.chain_id, + "nonce": start_nonce + i, + "gasPrice": 25_000_000_000, + "gas": 30_000_000, + } + ) + signed = self.nodes[0].w3.eth.account.sign_transaction( + tx, self.eth_address_privkey + ) + hash = self.nodes[0].w3.eth.send_raw_transaction(signed.rawTransaction) + hashes.append(signed.hash.hex().lower()[2:]) + + hash = self.nodes[0].eth_sendTransaction( + { + "nonce": hex(start_nonce + 40), + "from": self.eth_address, + "to": self.to_address, + "value": "0xDE0B6B3A7640000", # 1 DFI + "gas": "0x5209", + "gasPrice": "0x5D21DBA00", # 25_000_000_000 + } + ) + hashes.append(hash.lower()[2:]) + hash = self.nodes[0].eth_sendTransaction( + { + "nonce": hex(start_nonce + 41), + "from": self.eth_address, + "to": self.to_address, + "value": "0xDE0B6B3A7640000", # 1 DFI + "gas": "0x5209", + "gasPrice": "0x5D21DBA00", # 25_000_000_000 + } + ) + hashes.append(hash.lower()[2:]) + + first_block_total_txs = 17 + second_block_total_txs = 17 + third_block_total_txs = 8 + + self.nodes[0].generate(1) + block_info = self.nodes[0].getblock(self.nodes[0].getbestblockhash(), 4) + # check that the first 17 evm contract call txs is minted in the current block + assert_equal(len(block_info["tx"]) - 1, first_block_total_txs) + for idx, tx_info in enumerate(block_info["tx"][1:]): + if idx == 0: + continue + assert_equal(tx_info["vm"]["vmtype"], "evm") + assert_equal(tx_info["vm"]["txtype"], "EvmTx") + assert_equal(tx_info["vm"]["msg"]["sender"], self.eth_address) + assert_equal(tx_info["vm"]["msg"]["nonce"], start_nonce + idx) + assert_equal(tx_info["vm"]["msg"]["hash"], hashes[idx]) + assert_equal(tx_info["vm"]["msg"]["to"], receipt["contractAddress"].lower()) + + self.nodes[0].generate(1) + block_info = self.nodes[0].getblock(self.nodes[0].getbestblockhash(), 4) + # check that the next 17 of the evm contract call txs is minted in the next block + assert_equal(len(block_info["tx"]) - 1, second_block_total_txs) + for idx, tx_info in enumerate(block_info["tx"][1:]): + assert_equal(tx_info["vm"]["vmtype"], "evm") + assert_equal(tx_info["vm"]["txtype"], "EvmTx") + assert_equal(tx_info["vm"]["msg"]["sender"], self.eth_address) + assert_equal( + tx_info["vm"]["msg"]["nonce"], start_nonce + first_block_total_txs + idx + ) + assert_equal( + tx_info["vm"]["msg"]["hash"], hashes[first_block_total_txs + idx] + ) + assert_equal(tx_info["vm"]["msg"]["to"], receipt["contractAddress"].lower()) + + # check that the remaining evm txs is minted into this block + self.nodes[0].generate(1) + block_info = self.nodes[0].getblock(self.nodes[0].getbestblockhash(), 4) + assert_equal(len(block_info["tx"]) - 1, third_block_total_txs) + # check ordering of evm txs - first 6 evm txs are evm contract all txs + tx_infos = block_info["tx"][1:] + for idx in range(1, (40 - first_block_total_txs - second_block_total_txs)): + assert_equal(tx_infos[idx]["vm"]["vmtype"], "evm") + assert_equal(tx_infos[idx]["vm"]["txtype"], "EvmTx") + assert_equal(tx_infos[idx]["vm"]["msg"]["sender"], self.eth_address) + assert_equal( + tx_infos[idx]["vm"]["msg"]["nonce"], + start_nonce + first_block_total_txs + second_block_total_txs + idx, + ) + assert_equal( + tx_infos[idx]["vm"]["msg"]["hash"], + hashes[first_block_total_txs + second_block_total_txs + idx], + ) + assert_equal( + tx_infos[idx]["vm"]["msg"]["to"], receipt["contractAddress"].lower() + ) + for idx in range(6, third_block_total_txs): + assert_equal(tx_infos[idx]["vm"]["vmtype"], "evm") + assert_equal(tx_infos[idx]["vm"]["txtype"], "EvmTx") + assert_equal(tx_infos[idx]["vm"]["msg"]["sender"], self.eth_address) + assert_equal( + tx_infos[idx]["vm"]["msg"]["nonce"], + start_nonce + first_block_total_txs + second_block_total_txs + idx, + ) + assert_equal( + tx_infos[idx]["vm"]["msg"]["hash"], + hashes[first_block_total_txs + second_block_total_txs + idx], + ) + assert_equal(tx_infos[idx]["vm"]["msg"]["to"], self.to_address) + def toggle_evm_enablement(self): # Deactivate EVM self.nodes[0].setgov({"ATTRIBUTES": {"v0/params/feature/evm": "false"}})