Summary
evmone-t8n accepts transactions based on the JSON sender field without validating that (v,r,s) recovers to the same sender.
This creates a cross-client divergence with geth evm t8n: a tx signed by account A can be executed as account B in evmone-t8n if JSON sets sender=B.
Why this matters
In t8n/state-transition semantics, sender should be derived from signature and chain rules (or at least validated against provided sender).
Current behavior lets malformed tx JSON produce wrong state roots and acceptance/rejection results.
Reproducer
alloc.json
{
"0x1111111111111111111111111111111111111111": {
"code": "0x",
"nonce": "0x0",
"balance": "0xde0b6b3a7640000",
"storage": {}
}
}
env.json
{
"currentCoinbase": "0x8888f1f195afa192cfee860698584c030f4c9db1",
"currentNumber": "0x01",
"currentTimestamp": "0x54c99069",
"currentGasLimit": "0x2fefd8",
"currentRandom": "0x0000000000000000000000000000000000000000000000000000000000000001",
"parentBeaconBlockRoot": "0x0000000000000000000000000000000000000000000000000000000000000000",
"currentBaseFee": "0x1",
"currentExcessBlobGas": "0x0",
"withdrawals": []
}
txs.json
v,r,s below are from a tx signed by 0xa94f5374... (not by 0x1111...).
[
{
"to": "0x095E7BAea6a6c7c4c2DfeB977eFac326aF552d87",
"input": "0x",
"gas": "0x5208",
"nonce": "0x0",
"value": "0x0",
"gasPrice": "0x1",
"chainId": "0x1",
"sender": "0x1111111111111111111111111111111111111111",
"v": "0x26",
"r": "0x62d32a1bac6b89021fce16cf51479a8b00f26523933afde1b62f7957ed1943a3",
"s": "0x6f3941435cb48b0c649c1b0d707fc6878407f6287c9cd89c33c0048bd2710e45"
}
]
Commands
evmone-t8n \
--state.fork Cancun --state.reward 0 --state.chainid 1 \
--input.alloc alloc.json --input.env env.json --input.txs txs.json \
--output.basedir out-evmone --output.result out.json --output.alloc outAlloc.json
evm t8n \
--state.fork Cancun --state.reward 0 --state.chainid 1 \
--input.alloc alloc.json --input.env env.json --input.txs txs.json \
--output.basedir out-geth --output.result out.json --output.alloc outAlloc.json
Observed
evmone-t8n:
rejected: []
gasUsed: 0x5208
- executes transaction and updates state.
- geth
evm t8n:
- rejects tx:
insufficient funds ... address 0xa94f5374... have 0 want 21000
gasUsed: 0x0
- no executed tx in block result.
Suspected root cause
sender is loaded from JSON and used directly for state checks/execution:
test/utils/statetest_loader.cpp
o.sender = load_if_exists<evmc::address>(j, "sender");
- parses
v/r/s but does not recover/validate sender from signature.
test/state/state.cpp
- validation and balance checks use
tx.sender directly.
So (v,r,s) authenticity is effectively not enforced for tx sender in evmone-t8n.
Suggested fix direction
Recover sender from signature (per tx type / chain id / fork rules) and either:
- Use recovered sender as authoritative, or
- Reject when provided
sender disagrees with recovered sender.
Also reject malformed signatures consistently with other clients.
Summary
evmone-t8naccepts transactions based on the JSONsenderfield without validating that(v,r,s)recovers to the same sender.This creates a cross-client divergence with geth
evm t8n: a tx signed by accountAcan be executed as accountBinevmone-t8nif JSON setssender=B.Why this matters
In t8n/state-transition semantics, sender should be derived from signature and chain rules (or at least validated against provided sender).
Current behavior lets malformed tx JSON produce wrong state roots and acceptance/rejection results.
Reproducer
alloc.json{ "0x1111111111111111111111111111111111111111": { "code": "0x", "nonce": "0x0", "balance": "0xde0b6b3a7640000", "storage": {} } }env.json{ "currentCoinbase": "0x8888f1f195afa192cfee860698584c030f4c9db1", "currentNumber": "0x01", "currentTimestamp": "0x54c99069", "currentGasLimit": "0x2fefd8", "currentRandom": "0x0000000000000000000000000000000000000000000000000000000000000001", "parentBeaconBlockRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", "currentBaseFee": "0x1", "currentExcessBlobGas": "0x0", "withdrawals": [] }txs.jsonv,r,sbelow are from a tx signed by0xa94f5374...(not by0x1111...).[ { "to": "0x095E7BAea6a6c7c4c2DfeB977eFac326aF552d87", "input": "0x", "gas": "0x5208", "nonce": "0x0", "value": "0x0", "gasPrice": "0x1", "chainId": "0x1", "sender": "0x1111111111111111111111111111111111111111", "v": "0x26", "r": "0x62d32a1bac6b89021fce16cf51479a8b00f26523933afde1b62f7957ed1943a3", "s": "0x6f3941435cb48b0c649c1b0d707fc6878407f6287c9cd89c33c0048bd2710e45" } ]Commands
Observed
evmone-t8n:rejected: []gasUsed: 0x5208evm t8n:insufficient funds ... address 0xa94f5374... have 0 want 21000gasUsed: 0x0Suspected root cause
senderis loaded from JSON and used directly for state checks/execution:test/utils/statetest_loader.cppo.sender = load_if_exists<evmc::address>(j, "sender");v/r/sbut does not recover/validate sender from signature.test/state/state.cpptx.senderdirectly.So
(v,r,s)authenticity is effectively not enforced for tx sender inevmone-t8n.Suggested fix direction
Recover sender from signature (per tx type / chain id / fork rules) and either:
senderdisagrees with recovered sender.Also reject malformed signatures consistently with other clients.