Skip to content

Generate merkle proofs and multiproofs - #54

Draft
dharjeezy wants to merge 11 commits into
sigp:mainfrom
polytope-labs:proofs-followup
Draft

dharjeezy wants to merge 11 commits into
sigp:mainfrom
polytope-labs:proofs-followup

Conversation

@dharjeezy

Copy link
Copy Markdown

Draft, and stacked on #53. This is the proof generation half that was split out of #53 at your request. Since a cross repo PR can only target main, GitHub shows 11 commits here and includes #53's no_std change in the diff. Only the 10 proof commits are new. Once #53 merges, rebasing leaves just those.

Opened for visibility rather than to jump the queue. If you would rather own the proof API yourselves, say so and I will close this. I noticed ssz_types#50 after opening #53 and would rather not duplicate your design work.

What this does

Adds merkle proof and multiproof generation over the containers this crate already merkleizes, addressed by generalized index.

Why

Verifying Ethereum consensus proofs inside a Substrate runtime needs proofs against progressive container roots. progressive_merkle_hasher.rs already computes those roots here, so this builds on it rather than alongside it.

What is in it

Piece What
single proofs over both balanced and progressive containers, gindex addressed
multiproofs get_helper_indices and calculate_multi_merkle_root, following the consensus-specs ssz/merkle-proofs.md algorithms
ContainerFields / TreeHashFields kept disjoint, so a progressive container cannot reach the balanced multiproof builder
index validation rejects unverifiable index sets, rejects a zero index in any position, and bounds the gindex arithmetic

A test asserts the tree the proof module rebuilds matches what ProgressiveMerkleHasher streams, so the two cannot drift apart.

On the design

There is no proof API here today, so the shape is a proposal, not a convention. If you want it elsewhere, with a different surface, or split further, say so and it will be reworked. The commits are kept separate to make that easy.

Verification

Against the proposed dependency branches via path patches.

Check Result
cargo test 84 + 33 + 6 passed
cargo check --no-default-features --features sha2 --target wasm32-unknown-unknown clean
cargo fmt --check no diffs

The trybuild test cannot pass under a path patch, since it spawns its own cargo build that does not inherit the patch and resolves ethereum_hashing from crates.io. It should pass once ethereum_hashing#23 releases.

dharjeezy and others added 11 commits September 11, 2026 10:21
…erifying multiproof entry point and tighten the canonical form check

`ContainerFields::field_roots` now returns a `BalancedFieldRoots` newtype and
`generate_multiproof` accepts only that, so handing a progressive container's
`FieldRoots` to the balanced builder no longer compiles; a trybuild case pins
the error. The previous commit's docs claimed this but the free function still
took any slice.

`multiproof::verify_merkle_multiproof` compares the recomputed root against the
expected one, mirroring `is_valid_merkle_branch`. `calculate_multi_merkle_root`
is documented as the prover side whose `Ok` is not a verdict, since a tampered
leaf is never structurally invalid.

`check_active_fields` also requires a zero root at every inactive position
(`InactiveFieldNotZero`), so the raw API refuses states no container produces.

`progressive_container_depth` is read off the gindex so the two can never
disagree about which indices fit, and `progressive_container_proof` builds each
level's subtree once instead of rebuilding the field's own level.

`the_top_level_round_trips` now asserts unconditionally on every target rather
than being vacuous on 64 bit hosts; the derived `prove_fields` on a balanced
container gets an integration test; the stale spec path points at
`ethereum/ssz-specs`; and the unused `ContainerFields` import in the
integration tests is used again.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…t BalancedFieldRoots iterate

Review of the previous commit found two unresolved doc links in the
`multiproof` module doc (the names live inside the module) and a redundant
link target on `verify_merkle_multiproof`; rustdoc is now clean for proof.rs.

The `ContainerFields` doc said the balanced-over-progressive mistake "is a type
error", which is only true of the derived impls: a hand written impl or an
explicit `BalancedFieldRoots::new` is a deliberate act. The doc says so now,
`new` documents that it cannot validate its input, and
`progressive_container_root` documents the 256 field limit the single chunk
`active_fields` imposes.

`&BalancedFieldRoots` implements `IntoIterator`, which `Deref` alone did not
give it. Read only, so it opens no construction path.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
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