Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ancestry proof #1

Merged
merged 9 commits into from
Mar 27, 2024
Merged

Conversation

Lederstrumpf
Copy link

Description

Implements ancestry proofs for MMRs:

For a given MMR with size n and root r, an ancestry proof proves that some other root r' was the root of the MMR when it had size n' < n. If this is the case, we say root r' ancestors root r.

These ancestry proofs are used in paritytech/polkadot-sdk#1903 to prove a correct ancestor of the current mmr root to the runtime.

Closes paritytech/polkadot-sdk#1441

Implementation notes

While root r' is not a node in the mmr(n), all of its associated peaks p': peaks(n') are, see below illustration:
Ancestry proof - page 3 (2)

Hence, to prove r' ancestors r, we can:

  1. prove membership of all p' ∈ peaks(n') in mmr(n)
  2. verify that calculate_root(peaks(n')) = r'

To perform 1. we adjust the proof structure to also permit membership proofs for nodes, not just strictly leaves.
To support this the proof items also include the position of the nodes:

pub struct NodeMerkleProof<T, M> {
    mmr_size: u64,
    proof: Vec<(u64, T)>,
    merge: PhantomData<M>,
}

and the proof verification is adjusted to support this.
While this was previously performed inline for mmr::MerkleProof (https://github.com/Lederstrumpf/merkle-mountain-range/tree/linearize-proof) and therefore also affected standard leaf proofs, this PR employs dedicated node merkle proofs implemented in ancestry_proof::NodeMerkleProof: https://github.com/lederstrumpf/merkle-mountain-range/tree/14edd7ceecccfec3118114f988d7dc9ef925ac20/src/ancestry_proof.rs.
While this leads to greater code duplication than adjusting mmr::MerkleProof to support proving node membership, they are (currently) less performant than the unadjusted leaf proofs and also unaudited. Hence, having a dedicated implementation avoids having a reaudit on areas of the code that use standard membership proofs.

@acatangiu acatangiu merged commit 537f0e3 into paritytech:master Mar 27, 2024
1 check passed
Comment on lines +196 to +206
// handle special if next queue item is descendant of sibling
else if let Some(&(front_pos, ..)) = queue.front() {
if height > 0 && is_descendant_pos(sib_pos, front_pos) {
queue.push_back((pos, item, height));
continue;
} else {
return Err(Error::CorruptedProof);
}
} else {
return Err(Error::CorruptedProof);
};
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this needed ? Do you have an example input where this branch would be reached ?

Comment on lines +335 to +342
fn take_while_vec<T, P: Fn(&T) -> bool>(v: &mut Vec<T>, p: P) -> Vec<T> {
for i in 0..v.len() {
if !p(&v[i]) {
return v.drain(..i).collect();
}
}
v.drain(..).collect()
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: I would just use crate::mmr::take_while_vec() instead of duplicating it.

Comment on lines +306 to +315
pub fn bagging_peaks_hashes<T, M: Merge<Item = T>>(mut peaks_hashes: Vec<T>) -> Result<T> {
// bagging peaks
// bagging from right to left via hash(right, left).
while peaks_hashes.len() > 1 {
let right_peak = peaks_hashes.pop().expect("pop");
let left_peak = peaks_hashes.pop().expect("pop");
peaks_hashes.push(M::merge_peaks(&right_peak, &left_peak)?);
}
peaks_hashes.pop().ok_or(Error::CorruptedProof)
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: I would just use crate::mmr::bagging_peaks_hashes() instead of duplicating it.

@serban300 serban300 mentioned this pull request May 10, 2024
Copy link
Collaborator

@serban300 serban300 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Took a more in-depth look. Overall the approach seems sane. Just opened a PR with some small improvements here: #2

@Lederstrumpf could you take a look please ?

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.

Ancestry / prefix proofs for MMRs
3 participants