Skip to content

Commit f674c51

Browse files
XinyuCROrandy-cro
andauthored
fix: support 4byteTracer for tracer (#742)
* fix: support 4byteTracer for tracer (#736) * fix: tests * fix: changelog * fix: lint * fix: test * fix: use new addr # Conflicts: # CHANGELOG.md * fix: changelog --------- Co-authored-by: randy-cro <[email protected]>
1 parent dce2fb3 commit f674c51

File tree

5 files changed

+152
-12
lines changed

5 files changed

+152
-12
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
3939

4040
* (evm) [#725](https://github.com/crypto-org-chain/ethermint/pull/725) feat(RPC): add authorizationList from eth_getTransactionByHash response for EIP-7702 transactions
4141
* (evm) [#740](https://github.com/crypto-org-chain/ethermint/pull/740) fix: missing tx context during vm initialisation
42+
* (evm) [#742](https://github.com/crypto-org-chain/ethermint/pull/742) fix: prevent nil pointer dereference in tracer hooks
4243

4344
## [v0.22.0] - 2025-08-12
4445

tests/integration_tests/hardhat/contracts/TestBlockTxProperties.sol

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,20 @@
22
pragma solidity ^0.8.0;
33

44
contract TestBlockTxProperties {
5+
event TxDetailsEvent(
6+
address indexed origin,
7+
address indexed sender,
8+
uint value,
9+
bytes data,
10+
uint gas,
11+
uint gasprice,
12+
bytes4 sig
13+
);
14+
15+
function emitTxDetails() public payable {
16+
emit TxDetailsEvent(tx.origin, msg.sender, msg.value, msg.data, gasleft(), tx.gasprice, msg.sig);
17+
}
18+
519
function getBlockHash(uint256 blockNumber) public view returns (bytes32) {
620
return blockhash(blockNumber);
721
}

tests/integration_tests/test_block.py

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1+
from eth_utils.crypto import keccak
12
from web3 import Web3
23

3-
from .utils import CONTRACTS, deploy_contract, w3_wait_for_new_blocks
4+
from .utils import ADDRS, CONTRACTS, deploy_contract, w3_wait_for_new_blocks
45

56

67
def test_call(ethermint):
@@ -11,3 +12,25 @@ def test_call(ethermint):
1112
res = Web3.to_hex(contract.caller.getBlockHash(height))
1213
blk = w3.eth.get_block(height)
1314
assert res == Web3.to_hex(blk.hash), res
15+
16+
17+
def test_block_tx_properties(ethermint):
18+
w3 = ethermint.w3
19+
contract, _ = deploy_contract(w3, CONTRACTS["TestBlockTxProperties"])
20+
acc = ADDRS["community"]
21+
gas_price = w3.eth.gas_price
22+
tx_hash = contract.functions.emitTxDetails().transact(
23+
{"from": acc, "gas": 200000, "gasPrice": gas_price}
24+
)
25+
tx_hash = Web3.to_hex(tx_hash)
26+
tx_receipt = w3.eth.wait_for_transaction_receipt(tx_hash)
27+
tx_details_event = contract.events.TxDetailsEvent().process_receipt(tx_receipt)
28+
assert tx_details_event is not None
29+
data = tx_details_event[0]["args"]
30+
print("event_data: ", data)
31+
assert data["origin"].lower() == acc.lower()
32+
assert data["sender"].lower() == acc.lower()
33+
assert data["value"] == 0
34+
expected_sig = keccak(b"emitTxDetails()")[:4]
35+
assert data["sig"] == data["data"] == expected_sig
36+
assert data["gasprice"] == gas_price

tests/integration_tests/test_tracers.py

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
EXPECTED_STRUCT_TRACER,
1515
)
1616
from .utils import (
17+
ACCOUNTS,
1718
ADDRS,
1819
CONTRACTS,
1920
create_contract_transaction,
@@ -754,3 +755,100 @@ def process(w3):
754755
assert isinstance(res[0], exceptions.ContractLogicError)
755756
assert isinstance(res[-1], exceptions.ContractLogicError)
756757
assert str(res[0]) == str(res[-1]) == "('execution reverted', '0x')"
758+
759+
760+
def test_4byte_tracer_intrinsic_gas_too_low(ethermint, geth):
761+
method = "debug_traceCall"
762+
tracer = {"tracer": "4byteTracer"}
763+
acc = derive_new_account(6)
764+
765+
tx = {
766+
"from": ACCOUNTS["community"].address,
767+
"to": acc.address,
768+
"gas": "0x4e29",
769+
}
770+
771+
def process(w3):
772+
tx_res = w3.provider.make_request(method, [tx, "latest", tracer])
773+
return json.dumps(tx_res["error"], sort_keys=True)
774+
775+
providers = [ethermint.w3, geth.w3]
776+
with ThreadPoolExecutor(len(providers)) as exec:
777+
tasks = [exec.submit(process, w3) for w3 in providers]
778+
res = [future.result() for future in as_completed(tasks)]
779+
assert len(res) == len(providers)
780+
res = [json.loads(r) for r in res]
781+
assert res[0]["code"] == res[-1]["code"] == -32000
782+
assert "intrinsic gas too low" in res[0]["message"]
783+
assert "intrinsic gas too low" in res[-1]["message"]
784+
785+
786+
def test_4byte_tracer_success(ethermint, geth):
787+
method = "debug_traceCall"
788+
tracer = {"tracer": "4byteTracer"}
789+
acc = derive_new_account(6)
790+
791+
tx = {
792+
"from": ACCOUNTS["community"].address,
793+
"to": acc.address,
794+
"gas": hex(21000),
795+
}
796+
797+
def process(w3):
798+
tx_res = w3.provider.make_request(method, [tx, "latest", tracer])
799+
return json.dumps(tx_res["result"], sort_keys=True)
800+
801+
providers = [ethermint.w3, geth.w3]
802+
with ThreadPoolExecutor(len(providers)) as exec:
803+
tasks = [exec.submit(process, w3) for w3 in providers]
804+
res = [future.result() for future in as_completed(tasks)]
805+
assert len(res) == len(providers)
806+
assert res[0] == res[-1]
807+
808+
809+
def test_prestate_tracer_block_miner_address(ethermint, geth):
810+
"""
811+
prestateTracer on a tx will include the block miner address
812+
"""
813+
acc = ACCOUNTS["community"]
814+
receiver = derive_new_account(12)
815+
816+
def process(w3):
817+
assert (
818+
w3.eth.get_balance(receiver.address) == 0
819+
), "receiver balance need to be 0"
820+
tx = {
821+
"from": acc.address,
822+
"to": receiver.address,
823+
"value": 1,
824+
}
825+
receipt = send_transaction(w3, tx, key=acc.key)
826+
tx_hash = Web3.to_hex(receipt["transactionHash"])
827+
tracer = {"tracer": "prestateTracer"}
828+
tx_res = w3.provider.make_request("debug_traceTransaction", [tx_hash, tracer])
829+
latest_block = w3.eth.get_block(receipt.blockNumber)
830+
block_miner = latest_block.miner
831+
return [json.dumps(tx_res["result"], sort_keys=True), block_miner]
832+
833+
providers = [ethermint.w3, geth.w3]
834+
with ThreadPoolExecutor(len(providers)) as exec:
835+
tasks = [exec.submit(process, w3) for w3 in providers]
836+
res = [future.result() for future in as_completed(tasks)]
837+
miner_lhs = res[0][1].lower()
838+
miner_rhs = res[1][1].lower()
839+
assert len(res) == len(providers)
840+
841+
from_addr = acc.address.lower()
842+
to_addr = receiver.address.lower()
843+
844+
lhs = json.loads(res[0][0])
845+
rhs = json.loads(res[1][0])
846+
847+
assert len(lhs) == len(rhs) == 3, (lhs, rhs)
848+
849+
assert lhs[from_addr] is not None
850+
assert lhs[to_addr] is not None
851+
assert rhs[from_addr] is not None
852+
assert rhs[to_addr] is not None
853+
assert lhs[miner_lhs] is not None
854+
assert rhs[miner_rhs] is not None

x/evm/keeper/state_transition.go

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -362,20 +362,24 @@ func (k *Keeper) ApplyMessageWithConfig(
362362
tracer.OnGasChange(0, msg.GasLimit, tracing.GasChangeTxInitialBalance)
363363
}
364364

365-
tracer.OnTxStart(
366-
evm.GetVMContext(),
367-
ethtypes.NewTx(&ethtypes.LegacyTx{
368-
To: msg.To,
369-
Data: msg.Data,
370-
Value: msg.Value,
371-
Gas: msg.GasLimit,
372-
}),
373-
msg.From,
374-
)
365+
if tracer.OnTxStart != nil {
366+
tracer.OnTxStart(
367+
evm.GetVMContext(),
368+
ethtypes.NewTx(&ethtypes.LegacyTx{
369+
To: msg.To,
370+
Data: msg.Data,
371+
Value: msg.Value,
372+
Gas: msg.GasLimit,
373+
}),
374+
msg.From,
375+
)
376+
}
375377

376378
defer func() {
377379
debugFn()
378-
tracer.OnTxEnd(&ethtypes.Receipt{GasUsed: gasUsed}, err)
380+
if tracer.OnTxEnd != nil {
381+
tracer.OnTxEnd(&ethtypes.Receipt{GasUsed: gasUsed}, err)
382+
}
379383
}()
380384

381385
if cfg.DebugTrace {

0 commit comments

Comments
 (0)