Skip to content

Improve nonce-rejection error reporting (include tx nonce + next/pending nonce) #975

Description

@vishalchangrani

Summary

The transaction mempool's nonce-rejection errors are currently opaque sentinels with no nonce context, and the in-flight rejection isn't logged at all. We should enrich them to include the transaction's nonce and the next acceptable / pending nonce, matching the de-facto standard set by geth, Erigon, and Reth. This makes "my transaction was rejected" reports self-diagnosable (the sender can see exactly why and how to fix it) — which is the same motivation behind the nonce-aware pool itself.

This came out of @zhangchiqing's review comment on #974 (discussion). Rather than scope it into that PR, we're tracking the broader improvement here.

Current behavior

In services/requester/tx_mempool.go, Add rejects with bare sentinels and no nonce details:

  • ErrInFlightNonce — nonce at/below the highest nonce we've already submitted (not yet confirmed on-chain). Not logged.
  • ErrNonceTooLow — nonce below the on-chain frontier.
  • ErrNonceTooHigh — nonce more than TxMaxNonceGap beyond the frontier.

None of these tell the client (or our logs) what the offending nonce was or what nonce we would accept.

Proposed improvement

Include, in the returned error (and a log line), at least:

  • the transaction's nonce, and
  • the next acceptable / pending nonce (for in-flight: highestSent + 1; for too-low: the indexed frontier; for too-high: the accepted upper bound).

Use a client-friendly label (e.g. "pending nonce" / "next acceptable nonce") rather than internal implementation terms. Keep the existing sentinels so errors.Is continues to work (wrap with %w).

Precedent (researched)

Every major execution client puts both nonces in the client-facing error string:

  • gethnonce too low: next nonce 6, tx nonce 5 (core/txpool/validation.go); state layer also emits … address … tx: <n> state: <m> (core/state_transition.go). The %w-wrapped string reaches the JSON-RPC client (error code -32003).
  • Erigon — same format: nonce too low: address 0x… tx: 150 state: 161.
  • Rethtransaction nonce is not consistent: next nonce 6, tx nonce 5 (InvalidTransactionError::NonceNotConsistent).
  • Nethermind — the lone outlier: bare "nonce too low" (and no nonce in its log either).

Notably, no client logs the nonce while hiding it from the client — the precedent is "both" (geth/Erigon/Reth) or "neither" (Nethermind).

Not a state-leak concern

The account nonce is public on-chain state (eth_getTransactionCount(addr, "pending"|"latest")), so echoing it leaks nothing. The only gateway-specific value is the in-flight "highest sent but not yet confirmed" nonce; conceptually this is the same "next nonce we'd accept" that geth/Reth already return, and exposing it is what makes the in-flight rejection debuggable (a client polling eth_getTransactionCount would not otherwise see it).

Scope

  • Enrich ErrInFlightNonce, ErrNonceTooLow, ErrNonceTooHigh with nonce context (error + log).
  • Keep sentinels wrap-compatible (errors.Is).
  • Add/adjust tests for the new error content.

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Status
    No status

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions