Skip to content

Evm nonce error fix - #5442

Open
mpapierski wants to merge 18 commits into
casper-network:feat-evmfrom
mpapierski:evm-nonce-error-fix
Open

mpapierski wants to merge 18 commits into
casper-network:feat-evmfrom
mpapierski:evm-nonce-error-fix

Conversation

@mpapierski

@mpapierski mpapierski commented Sep 17, 2026

Copy link
Copy Markdown
Collaborator

On an EVM-enabled network, two transactions with the same nonce could be accepted against the same pre-state and included in one block.

The first transaction advanced the sender’s nonce. When the second transaction was executed, revm rejected it because its nonce was now stale:

EVM execution failed: transaction validation error: nonce 104 too low, expected 105

We treated that validation error as a transaction-conversion failure. It then became a fatal control announcement and stopped the reactor.

A transaction that becomes invalid during block execution should fail on its own. It should not stop the node.

What this PR changes

EVM preconditions are now checked against the current block state before payment processing and execution. A rejected transaction produces a failed ExecutionResult::Evm with zero cost, no state changes, and a useful error message.

This also closes paths where a transaction received while validating a proposed block could bypass checks normally performed by the transaction acceptor. EVM chainspec compliance is now checked when creating the transaction footprint, before the transaction enters a block or the transaction buffer.

The same handling is applied to the currently known revm transaction-validation errors, including:

  • A stale or otherwise invalid nonce
  • Insufficient intrinsic gas
  • Gas below the Prague calldata floor
  • Oversized contract initcode
  • Nonce overflow
  • A sender account containing deployed code

We also reject unsupported EVM values containing fractional motes and transactions with a positive effective priority fee before they can reach a fatal execution path.

Regression coverage

The regression tests cover:

  • Two transactions using the same nonce in one block
  • Each currently supported revm validation failure
  • Fractional-mote values
  • Positive effective priority fees
  • Fractional values and priority fees received through peer-fetched proposals
  • The result variant and error message returned for an included but rejected EVM transaction
  • A contract transferring one wei and then executing SELFDESTRUCT

For rejected transactions, the tests confirm that:

  • The reactor continues running
  • The result is a failed ExecutionResult::Evm
  • The error message describes the validation failure
  • Cost and consumed gas are zero
  • No effects or logs are produced
  • The sender nonce is unchanged
  • Balances and total supply are unchanged

SELFDESTRUCT dust accounting

A separate regression exposed an accounting problem when a contract transferred a sub-mote amount and was then destroyed. This amount is aggregated in the transaction outcome and will be burned.

Exercise two transactions accepted against the same pre-state nonce
and included in one block. The second transaction must become a
zero-cost precondition failure without stopping the reactor or changing
state.
Compare each EVM nonce with the evolving block state before creating
payment effects. Record a zero-cost transaction failure when an earlier
transaction in the same block has already advanced that nonce.
Cover intrinsic gas, Prague calldata floors, oversized initcode, nonce
overflow, and senders with deployed code. Each case must produce a
zero-cost transaction failure without stopping the reactor or changing
state.
Run revm transaction validation against the evolving block state before
creating payment effects. Convert every current validation rejection into
a per-transaction error so malformed execution requests cannot stop the
reactor.
Require block-included EVM transactions to retain the EVM execution
result shape when a precondition rejects them. Sidecar relies on that
variant to project receipts for every EVM transaction in a block.
Build an EVM receipt for every rejected block-included EVM request and
carry the precondition error alongside it. This keeps receipt projection
consistent while retaining a useful error message for clients.
Send a validly signed transaction whose value cannot be represented by
a Casper purse. The transaction must fail without stopping the reactor,
charging fees, advancing the nonce, or changing total supply.
Run EVM request validation before required-balance calculation. A value
that contains fractional motes can then become a transaction-scoped
precondition failure instead of a fatal payment conversion error.
Exercise a signed transaction that offers an effective priority fee on
a network that does not support transaction prioritization. It must be
rejected without reactor failure or state changes.
Repeat transaction-acceptor chainspec checks in the execution
precondition path. Proposed transactions can bypass the acceptor, so
policy violations must still become per-transaction failures.
Transfer one wei from a newly created contract and self-destruct it in
the same transaction. Require execution to succeed, report one mote of
dust, preserve supply for later consumption, and avoid reactor failure.
Combine balances removed by self-destruct with remaining wei before
converting the execution outcome to motes. Avoid pruning state keys that
were created and destroyed within the same transaction.
Show that signature validation alone accepts a peer-fetched transaction
whose value cannot be represented in motes. Block validation must still
reject that transaction before accepting the proposal.
Show that peer fetch validation accepts a correctly signed transaction
with an unsupported effective priority fee. Require proposal validation
to apply the missing chainspec policy check.
Apply EVM chainspec compliance while creating transaction footprints.
This covers both the transaction buffer and proposed-block validation, so
peer-fetched policy violations never reach block execution.
Tell future changes to reject malformed network-reachable input or
record per-item failures. Reserve fatal reactor announcements for internal
invariants and unrecoverable local state.
Provide a testnet-derived chainspec with EVM and addressable
entities enabled. Add a mainnet-derived node config with speculative
execution and temporary higher request limits for sidecar.

Update the devnet documentation to consume the checked-in EVM
resources directly.
Comment thread executor/evm/src/error.rs
Transaction(String),
/// revm rejected the transaction during pre-execution validation.
#[error("EVM transaction validation failed: {0}")]
InvalidTransaction(#[source] EvmTransactionError),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: could you attach this at the end?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants