-
Notifications
You must be signed in to change notification settings - Fork 3
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
Ancestry proof #1
Conversation
More opaque wrt. code duplication, but less cognitive overhead for review.
// 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); | ||
}; |
There was a problem hiding this comment.
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 ?
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() | ||
} |
There was a problem hiding this comment.
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.
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) | ||
} |
There was a problem hiding this comment.
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.
There was a problem hiding this 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 ?
Description
Implements ancestry proofs for MMRs:
For a given MMR with size
n
and rootr
, an ancestry proof proves that some other rootr'
was the root of the MMR when it had sizen' < n
. If this is the case, we say rootr'
ancestors rootr
.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 themmr(n)
, all of its associated peaksp': peaks(n')
are, see below illustration:Hence, to prove
r'
ancestorsr
, we can:p' ∈ peaks(n')
in mmr(n)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:
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 inancestry_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.