Summary
evmone test-state transition logic can accept an invalid Cancun blob transaction when maxFeePerBlobGas is extremely large.
The affordability check in test/state/state.cpp overflows in the blob-fee component and underestimates max_total_fee, causing divergence vs go-ethereum (evm statetest / evm t8n).
This affects test tooling (evmone-statetest, evmone-t8n) and state-transition correctness in edge cases.
Spec expectation
EIP-4844 execution-layer validation requires:
max_total_fee += get_total_blob_gas(tx) * tx.max_fee_per_blob_gas
and then checking sender balance against that value (no wraparound).
Reproducer (state test JSON)
{
"blob_fee_overflow_balance_check": {
"_info": {
"comment": "Blob max fee overflow affordability check differential test"
},
"env": {
"currentCoinbase": "0x8888f1f195afa192cfee860698584c030f4c9db1",
"currentNumber": "0x01",
"currentTimestamp": "0x54c99069",
"currentGasLimit": "0x2fefd8",
"currentDifficulty": "0x0",
"currentRandom": "0x0000000000000000000000000000000000000000000000000000000000000001",
"parentBeaconBlockRoot": "0x0000000000000000000000000000000000000000000000000000000000000000",
"currentBaseFee": "0x1",
"currentExcessBlobGas": "0x0",
"withdrawals": []
},
"pre": {
"0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b": {
"code": "0x",
"nonce": "0x00",
"balance": "0x02540be400",
"storage": {}
},
"0x095e7baea6a6c7c4c2dfeb977efac326af552d87": {
"code": "0x",
"nonce": "0x00",
"balance": "0x00",
"storage": {}
}
},
"transaction": {
"data": ["0x"],
"gasLimit": ["0x5208"],
"value": ["0x0"],
"to": "0x095e7baea6a6c7c4c2dfeb977efac326af552d87",
"nonce": "0x0",
"chainId": "0x1",
"type": "0x3",
"maxFeePerGas": "0x1",
"maxPriorityFeePerGas": "0x0",
"maxFeePerBlobGas": "0x800000000000000000000000000000000000000000000000000000000000",
"blobVersionedHashes": [
"0x01a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8"
],
"sender": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b",
"secretKey": "0x45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8"
},
"post": {
"Cancun": [
{
"indexes": { "data": 0, "gas": 0, "value": 0 },
"hash": "0x94be762b5d5ce9c88c5aed904aebaec40df6f5c4d9aee8ef4ee0218e6d3ea0b3",
"logs": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
"expectException": "insufficient funds"
}
]
}
}
}
Commands
build/debug/bin/evmone-statetest blob_fee_overflow_state_test.json
evm statetest blob_fee_overflow_state_test.json
Observed
evmone-statetest fails with:
unexpected valid transaction
evm statetest (go-ethereum v1.17.2) passes:
- state root
0x94be762b5d5ce9c88c5aed904aebaec40df6f5c4d9aee8ef4ee0218e6d3ea0b3
Equivalent t8n run shows:
evmone-t8n: accepts tx (rejected: [], nonzero gas used)
geth evm t8n: rejects tx with
insufficient funds for gas * price + value ... required balance exceeds 256 bits
Suspected root cause
In test/state/state.cpp:
if (tx.type == Transaction::Type::blob)
{
const auto total_blob_gas = tx.blob_gas_used();
// FIXME: Can overflow uint256.
max_total_fee += total_blob_gas * tx.max_blob_gas_price;
}
total_blob_gas * max_blob_gas_price wraps (here 2^17 * 2^239 = 2^256) and effectively contributes 0, so affordability check is bypassed.
Practicality
This particular trigger is not practically reachable on current mainnet economics (requires enormous fee cap), but it is still a deterministic cross-client divergence and violates EIP-4844 arithmetic semantics for edge inputs.
Possible fix direction
Compute blob max fee contribution in wide precision (same style as other umul(...) usage) and reject if final required balance exceeds representable uint256/balance domain, matching geth behavior.
Summary
evmonetest-state transition logic can accept an invalid Cancun blob transaction whenmaxFeePerBlobGasis extremely large.The affordability check in
test/state/state.cppoverflows in the blob-fee component and underestimatesmax_total_fee, causing divergence vs go-ethereum (evm statetest/evm t8n).This affects test tooling (
evmone-statetest,evmone-t8n) and state-transition correctness in edge cases.Spec expectation
EIP-4844 execution-layer validation requires:
max_total_fee += get_total_blob_gas(tx) * tx.max_fee_per_blob_gasand then checking sender balance against that value (no wraparound).
Reproducer (state test JSON)
{ "blob_fee_overflow_balance_check": { "_info": { "comment": "Blob max fee overflow affordability check differential test" }, "env": { "currentCoinbase": "0x8888f1f195afa192cfee860698584c030f4c9db1", "currentNumber": "0x01", "currentTimestamp": "0x54c99069", "currentGasLimit": "0x2fefd8", "currentDifficulty": "0x0", "currentRandom": "0x0000000000000000000000000000000000000000000000000000000000000001", "parentBeaconBlockRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", "currentBaseFee": "0x1", "currentExcessBlobGas": "0x0", "withdrawals": [] }, "pre": { "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b": { "code": "0x", "nonce": "0x00", "balance": "0x02540be400", "storage": {} }, "0x095e7baea6a6c7c4c2dfeb977efac326af552d87": { "code": "0x", "nonce": "0x00", "balance": "0x00", "storage": {} } }, "transaction": { "data": ["0x"], "gasLimit": ["0x5208"], "value": ["0x0"], "to": "0x095e7baea6a6c7c4c2dfeb977efac326af552d87", "nonce": "0x0", "chainId": "0x1", "type": "0x3", "maxFeePerGas": "0x1", "maxPriorityFeePerGas": "0x0", "maxFeePerBlobGas": "0x800000000000000000000000000000000000000000000000000000000000", "blobVersionedHashes": [ "0x01a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8" ], "sender": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b", "secretKey": "0x45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8" }, "post": { "Cancun": [ { "indexes": { "data": 0, "gas": 0, "value": 0 }, "hash": "0x94be762b5d5ce9c88c5aed904aebaec40df6f5c4d9aee8ef4ee0218e6d3ea0b3", "logs": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", "expectException": "insufficient funds" } ] } } }Commands
Observed
evmone-statetestfails with:unexpected valid transactionevm statetest(go-ethereum v1.17.2) passes:0x94be762b5d5ce9c88c5aed904aebaec40df6f5c4d9aee8ef4ee0218e6d3ea0b3Equivalent
t8nrun shows:evmone-t8n: accepts tx (rejected: [], nonzero gas used)geth evm t8n: rejects tx withinsufficient funds for gas * price + value ... required balance exceeds 256 bitsSuspected root cause
In
test/state/state.cpp:total_blob_gas * max_blob_gas_pricewraps (here2^17 * 2^239 = 2^256) and effectively contributes0, so affordability check is bypassed.Practicality
This particular trigger is not practically reachable on current mainnet economics (requires enormous fee cap), but it is still a deterministic cross-client divergence and violates EIP-4844 arithmetic semantics for edge inputs.
Possible fix direction
Compute blob max fee contribution in wide precision (same style as other
umul(...)usage) and reject if final required balance exceeds representableuint256/balance domain, matching geth behavior.