Skip to content

kona: preserve span-batch transaction type prefixes#21808

Draft
joshklop wants to merge 1 commit into
developfrom
codex/pr-21795-prefix-preservation
Draft

kona: preserve span-batch transaction type prefixes#21808
joshklop wants to merge 1 commit into
developfrom
codex/pr-21795-prefix-preservation

Conversation

@joshklop

Copy link
Copy Markdown
Contributor

Summary

  • preserve physical EIP-2718 prefix presence in Kona span-batch transaction-data parsing
  • keep a leading 0x00 so the downstream typed decoder rejects it instead of reinterpreting the payload as a prefixless legacy transaction
  • add a shared op-node/Kona conformance vector for [00 c3 80 80 80]

Root cause

Kona inferred prefix presence from the logical OpTxType. Because legacy is represented as 0, a physically present 0x00 was consumed and then dropped. That allowed the following valid legacy payload to be accepted as a prefixless legacy transaction.

Follow-up

Update Alloy's TransactionEnvelope derive macro to model legacy explicitly (for example, #[envelope(legacy)]). This can preserve Legacy = 0 for metadata and RPC while making typed_decode(0, ...) reject the reserved EIP-2718 value.

Validation

  • cargo nextest run -p kona-protocol --features std test_read_tx_data
  • cargo check -p kona-protocol --no-default-features
  • go test ./op-node/rollup/derive -run 'TestSpanBatchReadTxData(LeadingZero|Invalid|$)' -count=1

@joshklop

Copy link
Copy Markdown
Contributor Author

The #[envelope(ty = 0)] annotation is useful, but it is currently overloaded.

0 is useful as the logical/RPC transaction-kind identifier for legacy transactions:

  • it generates OpTxType::Legacy = 0;
  • it supports the conventional RPC representation "type": "0x0";
  • it tells the derive macro which variant needs untagged legacy Serde handling;
  • it supports APIs such as tx.ty() -> u8 and compact-storage discriminants.

It does not cause legacy encoding to emit a 0x00 prefix. TxLegacy separately overrides its EIP-2718 encoding to output only the legacy RLP.

The problem is that the macro also uses the same ty = 0 value for physical EIP-2718 typed decoding. It generates a typed_decode(0, payload) arm that maps 0x00 to Legacy. Those are different concepts:

  • logical/RPC kind: Legacy = 0;
  • physical EIP-2718 prefix: legacy has no prefix (None).

Under EIP-2718, a leading byte in 0x00..=0x7f denotes a typed transaction. A prefixless legacy transaction instead begins with its RLP list header. Type 0x00 was proposed for wrapped legacy transactions in EIP-2972, but that proposal was withdrawn.

There is also strong historical evidence that this was an accidental semantic change. Before commit 2280a67db09 converted OpTxEnvelope to the derive macro, its handwritten decoder explicitly rejected typed legacy:

OpTxType::Legacy => {
    Err(alloy_rlp::Error::Custom("type-0 eip2718 transactions are not supported").into())
}

After the derive conversion, #[envelope(ty = 0)] was necessary because the macro can only express a numeric typed variant or a flattened variant, and the generated decoder began accepting typed_decode(0, ...).

The root Alloy fix would be an explicit legacy form, for example:

#[envelope(legacy)]
Legacy(Signed<TxLegacy>)

That could retain OpTxType::Legacy = 0 for metadata/RPC while generating the correct codec behavior:

  • encode without a prefix;
  • decode only through fallback_decode;
  • reject typed_decode(0, ...);
  • do not report 0 as a supported physical type prefix.

So ty = 0 is not entirely a throwaway value, but using one value for both transaction-kind metadata and wire-prefix semantics is the underlying abstraction problem.

@sebastianst

Copy link
Copy Markdown
Member

This makes sense. Proposing additional improvements at #21850

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