Skip to content

Top-level tx at nonce 2^64-2 is skipped as light failure (EIP-2681 mismatch) #1495

@chfast

Description

@chfast

evmone: top-level tx with sender nonce 2^64-2 is executed as a light failure

Summary

When sender nonce is 2^64-2 and tx nonce matches, evmone increments sender nonce to 2^64-1 and then incorrectly aborts top-level execution as a light failure (EVMC_FAILURE with full execution gas left).
geth executes the transaction normally.

This causes consensus output divergence (stateRoot, receiptsRoot, gasUsed) on the same input JSON.

Why this is wrong

Per EIP-2681, transactions are invalid only when tx nonce >= 2^64-1.
A transaction with nonce 2^64-2 is valid and should execute; sender nonce becomes 2^64-1 after inclusion.

Relevant EIP-2681 text:

  1. “Consider any transaction invalid, where the nonce exceeds or equals to 2^64-1.”

Minimal repro (t8n JSON)

alloc.json:

{
  "0x1cb29f6b656a8ae0f6c1a3352b2daa7dc5b71393": {
    "nonce": "0xfffffffffffffffe",
    "balance": "0xffffffffffffffffffffffffffffffff",
    "code": "0x",
    "storage": {}
  }
}

env.json:

{
  "currentCoinbase": "0x8888f1f195afa192cfee860698584c030f4c9db1",
  "currentNumber": "0x1",
  "currentTimestamp": "0x54c99069",
  "currentGasLimit": "0x5f5e100",
  "currentRandom": "0x0000000000000000000000000000000000000000000000000000000000000001",
  "parentBeaconBlockRoot": "0x0000000000000000000000000000000000000000000000000000000000000000",
  "currentBaseFee": "0x1",
  "currentExcessBlobGas": "0x0",
  "withdrawals": []
}

txs.json:

[
  {
    "chainId": "0x1",
    "gas": "0x186a0",
    "gasPrice": "0x1",
    "hash": "0x08278b7d422136b9796e09a103c60332e510e6d5768348c5f0deb393267080bd",
    "input": "0x",
    "nonce": "0xfffffffffffffffe",
    "r": "0x8b9272c0bd5cdbd21ae659a306b137b1d6d01c4ebdc59b743fe457cbc03784ac",
    "s": "0x35b6594ffe5276f5f6fbb1af28edd48fcb569fd02ffbef660eff4c20d4a78e17",
    "sender": "0x1cb29f6b656A8ae0f6C1A3352B2DaA7DC5b71393",
    "to": "0x0000000000000000000000000000000000000004",
    "v": "0x26",
    "value": "0x0"
  }
]

Command:

# geth
evm t8n \
  --state.fork Cancun --state.chainid 1 \
  --input.alloc alloc.json --input.env env.json --input.txs txs.json \
  --output.basedir out-geth --output.result result.json --output.alloc alloc.json --output.body body.rlp

# evmone
evmone-t8n \
  --state.fork Cancun --state.chainid 1 \
  --input.alloc alloc.json --input.env env.json --input.txs txs.json \
  --output.basedir out-evmone --output.result result.json --output.alloc alloc.json --output.body body.rlp

Observed (Cancun):

  • geth: gasUsed = 0x5217, stateRoot = 0x89ee41a1...33ea2d0
  • evmone: gasUsed = 0x5208, stateRoot = 0x34820ad7...1587de89

(receiptsRoot also differs.)

Also visible in blockchaintest

Artifact:

  • case27-nonce-maxminus1-cancun-call/blocktest_nonce_maxminus1_call.json

Running evmone-blockchaintest on this file fails with mismatched stateRoot, receiptsRoot, and gasUsed.

Root cause

evmone validates nonce correctly at tx validation time (sender nonce == 2^64-1 is rejected, 2^64-2 is accepted), then:

  1. increments sender nonce in transition, making it 2^64-1;
  2. calls Host::prepare_message() for top-level execution;
  3. prepare_message() re-checks sender_acc.nonce == Account::NonceMax and returns failure even for depth-0 tx.

This second check is appropriate for internal CREATE/CREATE2, but not for already-validated top-level transaction execution.

Relevant locations:

  • test/state/state.cpp (transition(): sender nonce bumped before execution)
  • test/state/host.cpp (prepare_message(): if (sender_acc.nonce == Account::NonceMax) return {};)
  • test/state/host.cpp (call(): nullopt => light failure with full gas returned)

Extra confirmation

The same behavior affects top-level create transactions at nonce 2^64-2 (case26-nonce-maxminus1-create): geth creates contract, evmone skips execution.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions