Surfaced by the EIP-8297 test work in #3246. Two cases where computing a state root raises a raw Python exception rather than failing through the protocol's own error path.
1. Balance at or above 2^128
ethereum.binary_trie.embedding.encode_basic_data carries:
assert balance < U256(2) ** U256(128) # U128 doesn't exist
BASIC_DATA gives balance a 16-byte field, so the bound is real — but it is enforced by a bare assert during root computation, and AssertionError is not an EthereumException. There is no protocol-level cap anywhere: move_ether, create_ether and set_account_balance all operate on unbounded U256. So a block that mints a balance past the cap crashes the transition tool instead of being rejected as invalid.
The EIP states no cap either — it only gives the field's offset and width.
This is unfillable at the EEST layer (t8n dies), so it is pinned only as a unit test.
2. Unknown code hash
ethereum.state_pbt.State.get_code is a bare dict subscript and raises KeyError for a hash absent from the store. state_mpt.get_code is identical in that respect — but MPT root computation never calls it, while the PBT embedding must, because code chunk leaves commit the code itself rather than just its hash. So an account whose code is missing from the store crashes root computation on PBT and not on MPT.
The decision needed
- Should the balance bound be a protocol-level validity condition (rejecting the block) rather than an assertion? If balances past 2^128 are genuinely unreachable given the issuance schedule, saying so explicitly and keeping the assert as a defensive check would also be a fine answer — but that reasoning should be written down.
- Should
get_code raise a typed error on the PBT path, given it is now reachable during root computation?
Neither is urgent, but both turn a would-be invalid block into a tooling crash, which is unpleasant to debug and would look like a client bug rather than a bad block.
Related: #3246.
Surfaced by the EIP-8297 test work in #3246. Two cases where computing a state root raises a raw Python exception rather than failing through the protocol's own error path.
1. Balance at or above 2^128
ethereum.binary_trie.embedding.encode_basic_datacarries:BASIC_DATA gives
balancea 16-byte field, so the bound is real — but it is enforced by a bareassertduring root computation, andAssertionErroris not anEthereumException. There is no protocol-level cap anywhere:move_ether,create_etherandset_account_balanceall operate on unboundedU256. So a block that mints a balance past the cap crashes the transition tool instead of being rejected as invalid.The EIP states no cap either — it only gives the field's offset and width.
This is unfillable at the EEST layer (t8n dies), so it is pinned only as a unit test.
2. Unknown code hash
ethereum.state_pbt.State.get_codeis a bare dict subscript and raisesKeyErrorfor a hash absent from the store.state_mpt.get_codeis identical in that respect — but MPT root computation never calls it, while the PBT embedding must, because code chunk leaves commit the code itself rather than just its hash. So an account whose code is missing from the store crashes root computation on PBT and not on MPT.The decision needed
get_coderaise a typed error on the PBT path, given it is now reachable during root computation?Neither is urgent, but both turn a would-be invalid block into a tooling crash, which is unpleasant to debug and would look like a client bug rather than a bad block.
Related: #3246.