From b2feb8f3989b01d098a50e4eb45f490cbc9be988 Mon Sep 17 00:00:00 2001 From: Xinyu <91446598+XinyuCRO@users.noreply.github.com> Date: Wed, 17 Sep 2025 10:14:53 +0800 Subject: [PATCH 1/2] fix: support 4byteTracer for tracer (#736) * fix: tests * fix: changelog * fix: lint * fix: test * fix: use new addr # Conflicts: # CHANGELOG.md --- CHANGELOG.md | 1 + .../contracts/TestBlockTxProperties.sol | 14 +++ tests/integration_tests/test_block.py | 25 ++++- tests/integration_tests/test_tracers.py | 98 +++++++++++++++++++ x/evm/keeper/state_transition.go | 26 ++--- 5 files changed, 152 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index af85d6a5f2..3ea1fe9e54 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,6 +39,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ * (evm) [#725](https://github.com/crypto-org-chain/ethermint/pull/725) feat(RPC): add authorizationList from eth_getTransactionByHash response for EIP-7702 transactions * (evm) [#740](https://github.com/crypto-org-chain/ethermint/pull/740) fix: missing tx context during vm initialisation +* (evm) [#736](https://github.com/crypto-org-chain/ethermint/pull/736) fix: prevent nil pointer dereference in tracer hooks ## [v0.22.0] - 2025-08-12 diff --git a/tests/integration_tests/hardhat/contracts/TestBlockTxProperties.sol b/tests/integration_tests/hardhat/contracts/TestBlockTxProperties.sol index 07d5b0d20d..5ea6f3397b 100644 --- a/tests/integration_tests/hardhat/contracts/TestBlockTxProperties.sol +++ b/tests/integration_tests/hardhat/contracts/TestBlockTxProperties.sol @@ -2,6 +2,20 @@ pragma solidity ^0.8.0; contract TestBlockTxProperties { + event TxDetailsEvent( + address indexed origin, + address indexed sender, + uint value, + bytes data, + uint gas, + uint gasprice, + bytes4 sig + ); + + function emitTxDetails() public payable { + emit TxDetailsEvent(tx.origin, msg.sender, msg.value, msg.data, gasleft(), tx.gasprice, msg.sig); + } + function getBlockHash(uint256 blockNumber) public view returns (bytes32) { return blockhash(blockNumber); } diff --git a/tests/integration_tests/test_block.py b/tests/integration_tests/test_block.py index 99367d4231..68c97ac3cb 100644 --- a/tests/integration_tests/test_block.py +++ b/tests/integration_tests/test_block.py @@ -1,6 +1,7 @@ +from eth_utils.crypto import keccak from web3 import Web3 -from .utils import CONTRACTS, deploy_contract, w3_wait_for_new_blocks +from .utils import ADDRS, CONTRACTS, deploy_contract, w3_wait_for_new_blocks def test_call(ethermint): @@ -11,3 +12,25 @@ def test_call(ethermint): res = Web3.to_hex(contract.caller.getBlockHash(height)) blk = w3.eth.get_block(height) assert res == Web3.to_hex(blk.hash), res + + +def test_block_tx_properties(ethermint): + w3 = ethermint.w3 + contract, _ = deploy_contract(w3, CONTRACTS["TestBlockTxProperties"]) + acc = ADDRS["community"] + gas_price = w3.eth.gas_price + tx_hash = contract.functions.emitTxDetails().transact( + {"from": acc, "gas": 200000, "gasPrice": gas_price} + ) + tx_hash = Web3.to_hex(tx_hash) + tx_receipt = w3.eth.wait_for_transaction_receipt(tx_hash) + tx_details_event = contract.events.TxDetailsEvent().process_receipt(tx_receipt) + assert tx_details_event is not None + data = tx_details_event[0]["args"] + print("event_data: ", data) + assert data["origin"].lower() == acc.lower() + assert data["sender"].lower() == acc.lower() + assert data["value"] == 0 + expected_sig = keccak(b"emitTxDetails()")[:4] + assert data["sig"] == data["data"] == expected_sig + assert data["gasprice"] == gas_price diff --git a/tests/integration_tests/test_tracers.py b/tests/integration_tests/test_tracers.py index a3cb592047..005c4082e6 100644 --- a/tests/integration_tests/test_tracers.py +++ b/tests/integration_tests/test_tracers.py @@ -14,6 +14,7 @@ EXPECTED_STRUCT_TRACER, ) from .utils import ( + ACCOUNTS, ADDRS, CONTRACTS, create_contract_transaction, @@ -754,3 +755,100 @@ def process(w3): assert isinstance(res[0], exceptions.ContractLogicError) assert isinstance(res[-1], exceptions.ContractLogicError) assert str(res[0]) == str(res[-1]) == "('execution reverted', '0x')" + + +def test_4byte_tracer_intrinsic_gas_too_low(ethermint, geth): + method = "debug_traceCall" + tracer = {"tracer": "4byteTracer"} + acc = derive_new_account(6) + + tx = { + "from": ACCOUNTS["community"].address, + "to": acc.address, + "gas": "0x4e29", + } + + def process(w3): + tx_res = w3.provider.make_request(method, [tx, "latest", tracer]) + return json.dumps(tx_res["error"], sort_keys=True) + + providers = [ethermint.w3, geth.w3] + with ThreadPoolExecutor(len(providers)) as exec: + tasks = [exec.submit(process, w3) for w3 in providers] + res = [future.result() for future in as_completed(tasks)] + assert len(res) == len(providers) + res = [json.loads(r) for r in res] + assert res[0]["code"] == res[-1]["code"] == -32000 + assert "intrinsic gas too low" in res[0]["message"] + assert "intrinsic gas too low" in res[-1]["message"] + + +def test_4byte_tracer_success(ethermint, geth): + method = "debug_traceCall" + tracer = {"tracer": "4byteTracer"} + acc = derive_new_account(6) + + tx = { + "from": ACCOUNTS["community"].address, + "to": acc.address, + "gas": hex(21000), + } + + def process(w3): + tx_res = w3.provider.make_request(method, [tx, "latest", tracer]) + return json.dumps(tx_res["result"], sort_keys=True) + + providers = [ethermint.w3, geth.w3] + with ThreadPoolExecutor(len(providers)) as exec: + tasks = [exec.submit(process, w3) for w3 in providers] + res = [future.result() for future in as_completed(tasks)] + assert len(res) == len(providers) + assert res[0] == res[-1] + + +def test_prestate_tracer_block_miner_address(ethermint, geth): + """ + prestateTracer on a tx will include the block miner address + """ + acc = ACCOUNTS["community"] + receiver = derive_new_account(12) + + def process(w3): + assert ( + w3.eth.get_balance(receiver.address) == 0 + ), "receiver balance need to be 0" + tx = { + "from": acc.address, + "to": receiver.address, + "value": 1, + } + receipt = send_transaction(w3, tx, key=acc.key) + tx_hash = Web3.to_hex(receipt["transactionHash"]) + tracer = {"tracer": "prestateTracer"} + tx_res = w3.provider.make_request("debug_traceTransaction", [tx_hash, tracer]) + latest_block = w3.eth.get_block(receipt.blockNumber) + block_miner = latest_block.miner + return [json.dumps(tx_res["result"], sort_keys=True), block_miner] + + providers = [ethermint.w3, geth.w3] + with ThreadPoolExecutor(len(providers)) as exec: + tasks = [exec.submit(process, w3) for w3 in providers] + res = [future.result() for future in as_completed(tasks)] + miner_lhs = res[0][1].lower() + miner_rhs = res[1][1].lower() + assert len(res) == len(providers) + + from_addr = acc.address.lower() + to_addr = receiver.address.lower() + + lhs = json.loads(res[0][0]) + rhs = json.loads(res[1][0]) + + assert len(lhs) == len(rhs) == 3, (lhs, rhs) + + assert lhs[from_addr] is not None + assert lhs[to_addr] is not None + assert rhs[from_addr] is not None + assert rhs[to_addr] is not None + assert lhs[miner_lhs] is not None + assert rhs[miner_rhs] is not None diff --git a/x/evm/keeper/state_transition.go b/x/evm/keeper/state_transition.go index 0ccabaedcd..938efebabb 100644 --- a/x/evm/keeper/state_transition.go +++ b/x/evm/keeper/state_transition.go @@ -362,20 +362,24 @@ func (k *Keeper) ApplyMessageWithConfig( tracer.OnGasChange(0, msg.GasLimit, tracing.GasChangeTxInitialBalance) } - tracer.OnTxStart( - evm.GetVMContext(), - ethtypes.NewTx(ðtypes.LegacyTx{ - To: msg.To, - Data: msg.Data, - Value: msg.Value, - Gas: msg.GasLimit, - }), - msg.From, - ) + if tracer.OnTxStart != nil { + tracer.OnTxStart( + evm.GetVMContext(), + ethtypes.NewTx(ðtypes.LegacyTx{ + To: msg.To, + Data: msg.Data, + Value: msg.Value, + Gas: msg.GasLimit, + }), + msg.From, + ) + } defer func() { debugFn() - tracer.OnTxEnd(ðtypes.Receipt{GasUsed: gasUsed}, err) + if tracer.OnTxEnd != nil { + tracer.OnTxEnd(ðtypes.Receipt{GasUsed: gasUsed}, err) + } }() if cfg.DebugTrace { From e322b20ba9f5cf58d858ee7bf832915653f5016c Mon Sep 17 00:00:00 2001 From: Xinyu Date: Wed, 17 Sep 2025 10:19:13 +0800 Subject: [PATCH 2/2] fix: changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3ea1fe9e54..d68bf5767c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,7 +39,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ * (evm) [#725](https://github.com/crypto-org-chain/ethermint/pull/725) feat(RPC): add authorizationList from eth_getTransactionByHash response for EIP-7702 transactions * (evm) [#740](https://github.com/crypto-org-chain/ethermint/pull/740) fix: missing tx context during vm initialisation -* (evm) [#736](https://github.com/crypto-org-chain/ethermint/pull/736) fix: prevent nil pointer dereference in tracer hooks +* (evm) [#742](https://github.com/crypto-org-chain/ethermint/pull/742) fix: prevent nil pointer dereference in tracer hooks ## [v0.22.0] - 2025-08-12