Skip to content

Add EIP-8130 firehose tracing preparation notes - #24

Draft
sduchesneau wants to merge 1 commit into
release/1.xfrom
docs/prepare-eip-8130
Draft

Add EIP-8130 firehose tracing preparation notes#24
sduchesneau wants to merge 1 commit into
release/1.xfrom
docs/prepare-eip-8130

Conversation

@sduchesneau

Copy link
Copy Markdown

Adds docs/prepare_EIP-8130.md: analysis of what EIP-8130 (tx type 0x79, Cobalt hardfork, mainnet target Sept 2026) changes for the Firehose block model and tracer, what breaks today, and the preparation plan.

Doc only, no code changes.

Comment thread docs/prepare_EIP-8130.md
## Impact on the Firehose block model (`sf/ethereum/type/v2/type.proto`)

1. **New `Type` enum value.** Nothing maps 0x79. Suggest
`TRX_TYPE_BASE_EIP8130 = 121`, following the convention of matching the

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Question would be if likely this transaction is going to be the same ultimately on Mainnet. Knowning that in advance we could make an inform decision by using a type shared across all Ethereum, like we have for DynamicFee and AccessList tx, etc.

This is unsure to me if the format stay the same, maybe we make the bet that mostly all elements will be similar on each network.

Comment thread docs/prepare_EIP-8130.md
Comment on lines +66 to +73
2. **Transaction fields that don't fit the current message.**
`TransactionTrace.nonce` is `uint64`, but 8130 has a 256-bit `nonce_key`
plus `nonce_sequence`. There is no `to` (a phase/call list instead), no
`value`, no v/r/s (opaque auth blobs), and new concepts: `payer`, validity
window, `metadata`, `account_changes` (create / config change / delegation
entries — structurally similar to how `set_code_authorizations` was added
for EIP-7702). This needs a new set of fields or a sub-message, populated
only for this type, like the blob and set-code precedents.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Question is how much of current fields are retained?

If the majority of fields are re-used and that some are no empty/zero in the new type, I vote that we introduce new fields like we did over time.

Feel those should not be named base_ because most probably they will keep same semantic.

Comment thread docs/prepare_EIP-8130.md
entries — structurally similar to how `set_code_authorizations` was added
for EIP-7702). This needs a new set of fields or a sub-message, populated
only for this type, like the blob and set-code precedents.
3. **Receipt.** `phaseStatuses` is not part of the consensus receipt, but the

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Document states that phaseStatuses is for inflight receipt exchanged between components, Firehose indexer only ever technically run executed transaction in a block, so probably means we should ignore.

If it has execution impact, happy to revisit.

Comment thread docs/prepare_EIP-8130.md
transaction produces *multiple top-level frames* (one per call, grouped
into phases), and a transaction can be `SUCCEEDED` while individual phases
reverted (cross-phase durability). `state_reverted` per call can express
the reverted phases, but consumers' "root call #0" processing rules break;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Yeah a multi root call would be the ideal solution.

In EIP 8320, two questions:

  • If a transaction receipt is a failure, does this means all its inner call should be reverted?
    • What about fees paid?
  • If all calls are failure, can a transaction be in success?

Generally speaking, with a EIP 8320 feature, what is the relation between the transaction success and its root calls.

Comment thread docs/prepare_EIP-8130.md
Comment on lines +85 to +92
5. **Logs.** The `ActorAuthorized` / `ActorRevoked` / `AccountCreated` /
`DelegationApplied` logs are pushed straight into the revm journal
(`emit_event` → `internals.log(...)`) outside any call frame, and appear in
the receipt **ahead of** the calls' logs. No LOG opcode executes, so
inspector log hooks never fire. This is the same mechanism as the B20
precompile logs already handled by the tracer — good precedent — but it is
precisely the shape that caused the fh-5 panic
("N call logs but N+1 receipt logs").

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Yeah the inspector is not tracing low enough to be aware of this which is a bummer. We can always simulate of the logic is not too intense.

We should investigate about how we can better collaborate on reth to never "miss" those.

Comment thread docs/prepare_EIP-8130.md
Comment on lines +93 to +98
6. **State changes outside frames.** Payer precharge/settlement means
`REASON_GAS_BUY` / `REASON_GAS_REFUND` balance changes land on the
**payer**, not `from`. 2D nonces are `StorageChange`s on the NonceManager
precompile, not `NonceChange`s. Auto-delegation writes `0xef0100‖target`
code, and account creation sets code directly at a CREATE2 address —
`CodeChange`s from the pre-call pipeline, not from a CREATE frame.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

We follow semantics of the chain about changes happening to whom for which reason. Make sense anyway since REASON_GAS_BUY/REASON_GAS_REFUND are a bit special since they are never "reverted".

My understanding here is that we keep 1 max of each (maybe 0 if no refund), still valid with new trx type?

Comment thread docs/prepare_EIP-8130.md
Comment on lines +126 to +128
4. **Add a `firehose_8130` system test** alongside `firehose_b20` in
`base-firehose-tests`, with JSON-projection goldens (not full-protobuf,
per the `nop_transfer` lesson).

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This should be done in position 3 for those reasons:

  • Base probably already have EIP 8320 system tests that we can copy and put Firehose tracer on top of such block
  • Feedback loop is much faster in this setup, a devnet is harder for the agent to work on, preferable to start with code only tests first.

Comment thread docs/prepare_EIP-8130.md
4. **Add a `firehose_8130` system test** alongside `firehose_b20` in
`base-firehose-tests`, with JSON-projection goldens (not full-protobuf,
per the `nop_transfer` lesson).
5. **Track two upstream signals**: `cobalt_timestamp` appearing in

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Not sure of which tracking we talk about.

Something though, maybe evm-firehose-tracer-rs might require some changes to handle this new unknown fork.

@sduchesneau

Copy link
Copy Markdown
Author

Note

This reply was written by Claude — treat it as Claude's opinion, reviewed but not independently verified by @sduchesneau.

Addressing each review comment:

1. Base-specific vs shared enum value (TRX_TYPE_BASE_EIP8130)
The wire format is defined by the L1 EIP, not by Base: the spec's L1 profile is normative and the L2 profile only varies the gas schedule and the canonical authenticator set — the transaction encoding is shared. Optimism co-authored it and OP Stack rollout is announced for later in 2026, so several chains will carry this format before (or whether or not) L1 activates it. So agreed: name it TRX_TYPE_EIP8130 = 121, no BASE_ prefix, value matching the wire byte like TRX_TYPE_OPTIMISM_DEPOSIT = 126. Accepted risk: the EIP is still Draft, so L1 could in principle pick a different type byte later; on Base it's frozen at 0x79 for Cobalt, and if L1 ever diverges we add a second enum value at that point.

2. How many current fields are retained
Majority reused with identical semantics: hash, from (= resolved sender), gas_limit, max_fee_per_gas, max_priority_fee_per_gas, gas_price (effective), gas_used, index, ordinals, status, receipt, calls. Empty for this type: to, value, input, v/r/s, access_list, blob fields. nonce (uint64) could carry nonce_sequence, but nonce_key is a U256 and needs its own BigInt field. Net new: nonce_key, valid_after, valid_before, payer, metadata, sender_auth, payer_auth, account_changes (new message, same pattern as set_code_authorizations was for EIP-7702), plus phase attribution on calls. So agreed on both points: extend with new fields, no base_ prefix.

3. phaseStatuses
Agree we can drop it, but for a different reason than executed-vs-inflight: it is persisted and served over RPC for executed transactions. The real argument is that it's derived data the receipts-trie doesn't commit to, and it's fully reconstructible from the model provided calls carry a phase index and the transaction carries its phase structure: a phase is 0x00 iff its calls are state_reverted, or it produced no call frames because an earlier phase reverted. With those two things in the model, phaseStatuses adds nothing.

4. Relation between transaction status and root calls
Answers from the executor (crates/common/evm/src/eip8130.rs docs and comments):

  • Receipt status is success only if all phases committed. Any phase revert (or policy-gate block) → Revert, and every later phase is skipped.
  • But state changes from phases that committed before the reverting phase survive, including their logs, and the pre-call account_changes (creations, config changes, delegations) also persist — plus nonce and fee as today.
  • So "if the receipt is a failure, are all inner calls reverted?" — No. This is the biggest semantic break: the current documented consumer rule ("for a FAILED/REVERTED tx, only process gas/nonce/fee changes on the root call") is wrong for 0x79. A reverted 8130 transaction can carry durable state changes and logs. Consumers must rely on per-call state_reverted for this type regardless of receipt status, and the proto docs need to say so loudly.
  • Fees: all gas consumed (including by the reverting phase) is charged, to the payer (sender when self-paying).
  • "If all calls fail, can the tx succeed?" — No: phase 0 reverting reverts the transaction. But the opposite oddity exists: an account_changes-only transaction with empty calls succeeds with zero call frames — no root call at all, another shape consumers have never seen.

5. Injected logs invisible to the inspector
Agree on collaborating upstream. Concrete angle: everything funnels through one chokepoint — StorageCtx::emit_eventinternals.log() straight into the journal. We already patched alloy-evm (streamingfast/evm) to route EIP-4788/2935 system calls through the Inspector; a notification at that single journal-log site (upstream or in our fork) would cover B20 + 8130 + anything future in one place, instead of per-feature receipt-vs-call reconciliation. The existing B20 reconciliation stays as the working fallback.

6. GAS_BUY / GAS_REFUND cardinality
Still holds with the new type: one precharge (GAS_BUY) at effective_gas_limit × max_fee_per_gas and at most one settlement refund (GAS_REFUND) — on the payer's address when sponsored, sender when self-paying. Same cardinality, different address; consumers assuming from pays gas are the ones affected. One thing to verify while implementing: payer-auth gas is metered outside gas_limit (bounded by MAX_AUTHENTICATION_GAS) — whether it's folded into the precharge or produces a separate charge is an apply.rs detail worth confirming, though the model carries the address either way.

7. Devnet ordering
Agreed — will reorder: capture-based code-only tests first (base's own 8130 execution tests with BaseFirehoseCapture::install on top, same pattern as run_prestate; fast loop, no Docker), Docker devnet firehose_8130 system test after.

8. "Track two upstream signals"
Clarifying what the tracking means: watch upstream base/base for (a) cobalt_timestamp being set for sepolia in crates/common/chains/src/config.rs — the moment the deadline becomes real — and (b) the wire-format PRs (valid_after/valid_before ms window base#4330, SignedAccountChanges base#4327) landing in a tagged release, since they change the RLP layout our decoding and goldens depend on. And yes, evm-firehose-tracer-rs needs changes regardless of any fork signal — that's what plan items 1–3 cover; the doc should say that explicitly.

Happy to fold 3, 4, 7 and 8 back into the doc.

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