Skip to content

Bind challenge/response MMR proofs to the exact (leaf_index, leaf_count)#301

Draft
karolk91 wants to merge 4 commits into
devfrom
kk-challenge-leaf-index-binding
Draft

Bind challenge/response MMR proofs to the exact (leaf_index, leaf_count)#301
karolk91 wants to merge 4 commits into
devfrom
kk-challenge-leaf-index-binding

Conversation

@karolk91

Copy link
Copy Markdown
Collaborator

verify_mmr_proof(proof, root) never uses the challenged leaf index: it only checks that the leaf hashes to some peak and that the peaks bag to the root. A provider challenged on leaf i can defend with a valid proof for any leaf j it still holds, so a challenge does not prove possession of the challenged chunk. Related gaps:

  • Challenge stores no leaf_count, so the response check has nothing to bind against.
  • challenge_checkpoint accepts leaf_index >= leaf_count; under bound verification such a challenge is undefendable and resolves by timeout slash (griefing at the cost of one deposit).
  • confirm_replica_sync records (start_seq, leaf_count) = (0, 0) for historical-root syncs, which bound verification would turn into undefendable replica challenges.

Changes

  • primitives: verify_mmr_proof(proof, leaf_index, leaf_count, root) derives the expected peak index, path length, and per-level direction from (leaf_index, leaf_count) (mmr_leaf_position) and requires the climbed hash at the exact peak position. bag_peaks extracted; reused by the provider prover.
  • pallet: Challenge.leaf_count added, sourced per path: snapshot (challenge_checkpoint), signed commitment (challenge_offchain — a forged value fails signature verification), recorded sync range (challenge_replica). create_challenge rejects leaf_index >= leaf_count (LeafIndexOutOfRange). ReplicaSyncRecord is now {root, range: Option<(start_seq, leaf_count)>, block}; the range is recorded only for current-snapshot syncs, and challenge_replica on None fails with ReplicaSyncRangeUnknown. respond_to_challenge verifies bound; invalid proof still slashes immediately.
  • provider-node: commit() returns leaf_count under the same lock as mmr_root; /commit, /commitment, /delete, /checkpoint-signature sign the real leaf_count.
  • client: CommitResponse.leaf_count is required (no serde(default)).

Tests

Proof substitution rejection, out-of-range creation, signed-leaf_count binding/forgery, replica range recording/refusal — primitives test_verify_mmr_proof_*, pallet tests/{challenge,replica}.rs, provider-node challenge_binds_leaf_index. The respond_to_challenge_proof benchmark now builds the bound-verify worst case (64 peaks, 63-level climb).

Review notes

  • SCALE layout changes without migration: Challenge (new field), ProviderRole::Replica.last_sync (ReplicaSyncRecord shape).
  • New Error variants inserted mid-enum; later variant indices shift.
  • Not regenerated: crates/storage-subxt bindings, packages/papi metadata, weights.
  • Pre-existing: the fixed-offset last_sync decoder in provider-node/src/subxt_client.rs assumes the pre-dev layout (block offset already misaligned on dev).

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.

1 participant