kona: match op-node span-batch uvarint decoding - #22126
Merged
Merged
Conversation
kona decoded all six span-batch `uvarint` fields with `unsigned_varint::decode::u64`, whose accept-set differs from op-node's `binary.ReadUvarint` in both directions: - Non-minimal encodings — `[0x81, 0x00]` (`1` with a redundant trailing zero) is a valid protobuf Base128 varint. op-node accepts it, `unsigned-varint` rejects it as `NotMinimal`. - Ten-byte varints — a tenth byte terminating above `0x01` sets bits past 63. op-node reports overflow, `unsigned-varint` returns `Ok` with the high bits silently discarded. Either way a byzantine batcher can publish a span batch that one client decodes and the other rejects, splitting derivation on identical L1 data. All six fields (`rel_timestamp`, `l1_origin_num`, `block_count`, `block_tx_count`, `tx_nonce`, `tx_gas`) now route through `read_uvarint`, a port of `binary.ReadUvarint`. Encoding still uses `unsigned_varint::encode::u64`, whose minimal output the new decoder accepts in full. Shared vectors pin the pair together: a field-level table on each side and a whole span batch whose `block_count` is re-encoded non-minimally. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
joshklop
reviewed
Jul 30, 2026
joshklop
approved these changes
Jul 30, 2026
The three conformance-test blocks each restated why kona must accept exactly op-node's encodings. Keep that explanation in the `varint` module doc, which already covers the accept-set, and leave a one-line pointer at each test site. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Aligns kona's span-batch
uvarintdecoding with op-node. A byzantine batcher could otherwise publish a span batch that one client decodes and the other rejects, splitting derivation on identical L1 data.kona decoded all six span-batch
uvarintfields withunsigned_varint::decode::u64, whose accept-set differs from op-node'sbinary.ReadUvarintin both directions:[0x81, 0x00](1with a redundant trailing zero) is a valid protobuf Base128 varint. op-node accepts it;unsigned-varintrejects it asNotMinimal, so op-node builds L2 blocks the fault proof program cannot reproduce.0x01sets bits past 63. op-node reports overflow;unsigned-varintreturnsOkwith the high bits silently discarded, so the fault proof program builds blocks op-node rejects.The root cause is a wrong varint family:
unsigned-varintimplements the minimal-only multiformats encoding, not the protobuf Base128 varint that the span batch format specifies — hence a mismatch in both directions. All six fields (rel_timestamp,l1_origin_num,block_count,block_tx_count,tx_nonce,tx_gas) now route throughread_uvarint, a port ofbinary.ReadUvarint. Encoding is unchanged; every valueunsigned_varint::encode::u64emits lies inside the new accept-set.Applies from Delta onward, not fork-gated. Deploying needs no fork: in both directions the change moves kona onto op-node's accept-set, and op-node's behaviour defines the canonical chain, so it cannot alter any history op-node has already derived. Consensus-affecting (derivation): shipping to a live chain requires a new kona-client release + prestate.
Verification
A differential harness recorded Go's verdict for 649,092 byte strings — all 1- and 2-byte inputs, exhaustive over an 11-symbol alphabet to depth 5, every tenth byte after nine-byte continuation prefixes, 11-byte inputs, and 400k random strings.
read_uvarintagrees on accept/reject, value, and consumed length for every one.The committed field-level vectors are shared byte for byte between the two suites — the table in
TestSpanBatchUvarintConformancemirrors the kona tests exactly. On top of that, each client decodes a whole span batch whoseblock_counthas been re-encoded non-minimally, on its own batch (TestSpanBatchNonMinimalBlockCount/test_decode_raw_span_batch_non_minimal_block_count).Closes ethereum-optimism/protocol-team#223.
Also verified via a local OMP/GPT-5.6-sol review.
🤖 Co-created with Claude Opus 5 (1M context)