feat: add snapshot Merkle inclusion proof verifier (#463)#491
Open
morelucks wants to merge 1 commit into
Open
Conversation
Closes RevoraOrg#463 Add verify_snapshot_inclusion(issuer, namespace, token, snapshot_ref, leaf, proof) -> bool, a pure off-chain Merkle inclusion proof helper that lets holders prove their leaf was included in a finalized snapshot without storing the full holder set on-chain. ## Design ### Leaf encoding (identical to finalize_snapshot) leaf_hash = SHA-256( index_xdr || holder_xdr || shares_bps_xdr ) where each field is XDR-serialised via .to_xdr(&env), matching the exact byte layout used in finalize_snapshot. This guarantees that any leaf produced by the existing finalization pass is directly provable. ### Merkle tree construction (sorted-pair) Internal nodes are computed as: parent = SHA-256( min(left, right) || max(left, right) ) Sorting the pair lexicographically before hashing makes the tree position-independent — no left/right flag is needed in the proof array. Off-chain tooling builds proofs with the same sorted-pair rule. ### Proof verification 1. Hash the supplied raw leaf bytes with SHA-256. 2. For each sibling in the proof array, sort the pair and hash. 3. The final value must equal the committed content_hash. ### Security guards - Returns false (not an error) when the snapshot does not exist or is not yet finalized. An unfinalized snapshot has an untrusted content_hash and must not be accepted as a proof root. - Every proof element is BytesN<32> — fixed-width, no length extension. - SHA-256 collision resistance makes forging a valid proof infeasible. Relevant files: src/lib.rs (verify_snapshot_inclusion)
|
@morelucks Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits. You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀 |
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.
Summary
Closes #463
Adds
verify_snapshot_inclusion— a pure, stateless Merkle inclusion proof helper that lets off-chain holders prove their leaf was included in a finalized snapshot without storing the full holder set on-chain.What changed
src/lib.rs— single new public functionverify_snapshot_inclusionDesign
Leaf encoding
Identical to
finalize_snapshot(same hash function, same serialisation):Each field is XDR-serialised with
.to_xdr(&env). This guarantees that any leaf produced by the existing finalization pass is directly provable — no separate encoding step.Sorted-pair Merkle tree
Internal nodes:
Sorting the pair lexicographically before hashing makes the tree position-independent: the proof array carries only sibling hashes, no left/right flags. Off-chain tooling must build proofs with the same sorted-pair rule.
Verification algorithm
currentproof: sort pair, concatenate, SHA-256 → newcurrentcurrent == entry.content_hash→true, otherwisefalseSecurity properties
falsefor unfinalized snapshots (untrustedcontent_hash)false— no error leaked (prevents enumeration)Vec<BytesN<32>>— no length-extension attack surfacefinalize_snapshotenv.crypto().sha256()throughoutLeaf encoding reference (for off-chain implementors)
Pass
raw(the pre-hash bytes) as theleafargument toverify_snapshot_inclusion.