Conversation
A peer can answer a GetBlocksProof/GetTransactionsProof request with a legacy (v0) message that omits the V1 extra fields (uncle hashes and block extensions). The client fabricated None extensions for every proved header without checking whether the header's authenticated extra_hash actually commits to an empty uncle list and no extension. Stored headers then served None from ExtensionProvider, so scripts using the CKB2023 load_extension syscall verified against missing extension data and local verification diverged from consensus. For a block without uncles and without an extension, extra_hash is Byte32::zero() (the empty uncles hash is zero, and extra_hash equals the uncles hash when no extension is committed). Reject legacy messages whose headers have any other extra_hash, since such blocks can only be proved with the V1 fields. Refs: sec-reports PENDING-HIGH-0007
The mock chain's block assembler attaches an extension to every mined block, so proof-message tests now send V1 messages carrying the real uncle hashes and extensions, like an honest server would. Add regression tests for rejecting legacy (v0) proofs of extension-bearing blocks, rejecting V1 proofs with incorrect extensions, and unit tests for the extra-hash verification helpers.
There was a problem hiding this comment.
Pull request overview
This PR hardens the light-client proof handlers against a legacy (v0) response that omits V1 “extra fields” (uncles hash + extension), preventing the client from accepting headers whose authenticated extra_hash commits to data that the legacy message cannot carry. This closes a consensus-divergence vector for CKB2023 load_extension by ensuring legacy proofs are only accepted for headers that commit to “no uncles, no extension”.
Changes:
- Add
verify_legacy_extra_hash()and invoke it when handling legacy (v0)SendBlocksProof/SendTransactionsProofresponses. - Update protocol-level tests to send V1 messages with real
blocks_uncles_hashandblocks_extensionfrom the snapshot when appropriate. - Add unit tests covering accept/reject behavior for legacy extra-hash verification and extension mismatch verification.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| light-client-lib/src/protocols/light_client/components/send_blocks_proof.rs | Adds verify_legacy_extra_hash() and enforces it on the legacy branch before continuing proof validation. |
| light-client-lib/src/protocols/light_client/components/send_transactions_proof.rs | Enforces verify_legacy_extra_hash() for legacy tx-proof responses to prevent extension omission. |
| light-client-lib/src/protocols/light_client/components/mod.rs | Re-exports verify_legacy_extra_hash for reuse by components. |
| light-client-lib/src/protocols/light_client/components/tests/send_blocks_proof.rs | Adds focused unit tests for legacy extra-hash acceptance/rejection and V1 extension mismatch behavior. |
| light-client-lib/src/protocols/light_client/components/tests/mod.rs | Registers the new component test module. |
| light-client-lib/src/tests/protocols/light_client/send_blocks_proof.rs | Extends integration-style blocks-proof tests to cover legacy rejection for extension-bearing blocks and V1 incorrect-extension rejection; updates harness to send realistic V1 fields. |
| light-client-lib/src/tests/protocols/light_client/send_transactions_proof.rs | Updates tx-proof tests to send V1 fields in normal cases and adds an integration test ensuring legacy tx-proofs are rejected for extension-bearing blocks. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
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.
Problem
A peer can answer a
GetBlocksProof/GetTransactionsProofrequest with a legacy (v0) message that omits the V1 extra fields (blocks_uncles_hash,blocks_extension). The client fabricatedNoneextensions for every proved header without checking whether the header's authenticatedextra_hashactually commits to an empty uncle list and no extension. Stored headers then servedNonefromExtensionProvider, so scripts using the CKB2023load_extensionsyscall were evaluated against missing extension data and local verification diverged from consensus.Refs: sec-reports
PENDING-HIGH-0007("Peer can omit CKB2023 block extensions").Fix
For a block without uncles and without an extension,
extra_hashisByte32::zero()(the empty uncles hash is zero, andextra_hashequals the uncles hash when no extension is committed). Any other value commits to data the legacy message cannot carry.send_blocks_proof.rs: newverify_legacy_extra_hash()helper, called in the legacy branch before the MMR proof.send_transactions_proof.rs: same check in its legacy branch.InvalidProof(439), which is ban-eligible through the existing misbehavior rate limiter.Compatibility
Tests
verify_legacy_extra_hashandverify_extra_hash(accept/reject cases).ckb-light-client-libsuite: 122/122 pass.ckb-light-clientbin compiles; clippy clean for the changed code.