mmr: support arbitrary from/to delta updates
This commit is contained in:
parent
5f2d170435
commit
a8acc0b39d
2 changed files with 68 additions and 26 deletions
|
@ -164,32 +164,32 @@ impl Mmr {
|
||||||
///
|
///
|
||||||
/// The result is a packed sequence of the authentication elements required to update the trees
|
/// The result is a packed sequence of the authentication elements required to update the trees
|
||||||
/// that have been merged together, followed by the new peaks of the [Mmr].
|
/// that have been merged together, followed by the new peaks of the [Mmr].
|
||||||
pub fn get_delta(&self, original_forest: usize) -> Result<MmrDelta, MmrError> {
|
pub fn get_delta(&self, from_forest: usize, to_forest: usize) -> Result<MmrDelta, MmrError> {
|
||||||
if original_forest > self.forest {
|
if to_forest > self.forest || from_forest > to_forest {
|
||||||
return Err(MmrError::InvalidPeaks);
|
return Err(MmrError::InvalidPeaks);
|
||||||
}
|
}
|
||||||
|
|
||||||
if original_forest == self.forest {
|
if from_forest == to_forest {
|
||||||
return Ok(MmrDelta { forest: self.forest, data: Vec::new() });
|
return Ok(MmrDelta { forest: to_forest, data: Vec::new() });
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut result = Vec::new();
|
let mut result = Vec::new();
|
||||||
|
|
||||||
// Find the largest tree in this [Mmr] which is new to `original_forest`.
|
// Find the largest tree in this [Mmr] which is new to `from_forest`.
|
||||||
let candidate_trees = self.forest ^ original_forest;
|
let candidate_trees = to_forest ^ from_forest;
|
||||||
let mut new_high = 1 << candidate_trees.ilog2();
|
let mut new_high = 1 << candidate_trees.ilog2();
|
||||||
|
|
||||||
// Collect authentication nodes used for tree merges
|
// Collect authentication nodes used for tree merges
|
||||||
// ----------------------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------------------
|
||||||
|
|
||||||
// Find the trees from `original_forest` that have been merged into `new_high`.
|
// Find the trees from `from_forest` that have been merged into `new_high`.
|
||||||
let mut merges = original_forest & (new_high - 1);
|
let mut merges = from_forest & (new_high - 1);
|
||||||
|
|
||||||
// Find the peaks that are common to `original_forest` and this [Mmr]
|
// Find the peaks that are common to `from_forest` and this [Mmr]
|
||||||
let common_trees = original_forest ^ merges;
|
let common_trees = from_forest ^ merges;
|
||||||
|
|
||||||
if merges != 0 {
|
if merges != 0 {
|
||||||
// Skip the smallest trees unknown to `original_forest`.
|
// Skip the smallest trees unknown to `from_forest`.
|
||||||
let mut target = 1 << merges.trailing_zeros();
|
let mut target = 1 << merges.trailing_zeros();
|
||||||
|
|
||||||
// Collect siblings required to computed the merged tree's peak
|
// Collect siblings required to computed the merged tree's peak
|
||||||
|
@ -214,15 +214,15 @@ impl Mmr {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// The new high tree may not be the result of any merges, if it is smaller than all the
|
// The new high tree may not be the result of any merges, if it is smaller than all the
|
||||||
// trees of `original_forest`.
|
// trees of `from_forest`.
|
||||||
new_high = 0;
|
new_high = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Collect the new [Mmr] peaks
|
// Collect the new [Mmr] peaks
|
||||||
// ----------------------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------------------
|
||||||
|
|
||||||
let mut new_peaks = self.forest ^ common_trees ^ new_high;
|
let mut new_peaks = to_forest ^ common_trees ^ new_high;
|
||||||
let old_peaks = self.forest ^ new_peaks;
|
let old_peaks = to_forest ^ new_peaks;
|
||||||
let mut offset = nodes_in_forest(old_peaks);
|
let mut offset = nodes_in_forest(old_peaks);
|
||||||
while new_peaks != 0 {
|
while new_peaks != 0 {
|
||||||
let target = 1 << new_peaks.ilog2();
|
let target = 1 << new_peaks.ilog2();
|
||||||
|
@ -231,7 +231,7 @@ impl Mmr {
|
||||||
new_peaks ^= target;
|
new_peaks ^= target;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(MmrDelta { forest: self.forest, data: result })
|
Ok(MmrDelta { forest: to_forest, data: result })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An iterator over inner nodes in the MMR. The order of iteration is unspecified.
|
/// An iterator over inner nodes in the MMR. The order of iteration is unspecified.
|
||||||
|
|
|
@ -573,42 +573,42 @@ fn test_mmr_peaks_hash_odd() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_mmr_updates() {
|
fn test_mmr_delta() {
|
||||||
let mmr: Mmr = LEAVES.into();
|
let mmr: Mmr = LEAVES.into();
|
||||||
let acc = mmr.accumulator();
|
let acc = mmr.accumulator();
|
||||||
|
|
||||||
// original_forest can't have more elements
|
// original_forest can't have more elements
|
||||||
assert!(
|
assert!(
|
||||||
mmr.get_delta(LEAVES.len() + 1).is_err(),
|
mmr.get_delta(LEAVES.len() + 1, mmr.forest()).is_err(),
|
||||||
"Can not provide updates for a newer Mmr"
|
"Can not provide updates for a newer Mmr"
|
||||||
);
|
);
|
||||||
|
|
||||||
// if the number of elements is the same there is no change
|
// if the number of elements is the same there is no change
|
||||||
assert!(
|
assert!(
|
||||||
mmr.get_delta(LEAVES.len()).unwrap().data.is_empty(),
|
mmr.get_delta(LEAVES.len(), mmr.forest()).unwrap().data.is_empty(),
|
||||||
"There are no updates for the same Mmr version"
|
"There are no updates for the same Mmr version"
|
||||||
);
|
);
|
||||||
|
|
||||||
// missing the last element added, which is itself a tree peak
|
// missing the last element added, which is itself a tree peak
|
||||||
assert_eq!(mmr.get_delta(6).unwrap().data, vec![acc.peaks()[2]], "one peak");
|
assert_eq!(mmr.get_delta(6, mmr.forest()).unwrap().data, vec![acc.peaks()[2]], "one peak");
|
||||||
|
|
||||||
// missing the sibling to complete the tree of depth 2, and the last element
|
// missing the sibling to complete the tree of depth 2, and the last element
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
mmr.get_delta(5).unwrap().data,
|
mmr.get_delta(5, mmr.forest()).unwrap().data,
|
||||||
vec![LEAVES[5], acc.peaks()[2]],
|
vec![LEAVES[5], acc.peaks()[2]],
|
||||||
"one sibling, one peak"
|
"one sibling, one peak"
|
||||||
);
|
);
|
||||||
|
|
||||||
// missing the whole last two trees, only send the peaks
|
// missing the whole last two trees, only send the peaks
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
mmr.get_delta(4).unwrap().data,
|
mmr.get_delta(4, mmr.forest()).unwrap().data,
|
||||||
vec![acc.peaks()[1], acc.peaks()[2]],
|
vec![acc.peaks()[1], acc.peaks()[2]],
|
||||||
"two peaks"
|
"two peaks"
|
||||||
);
|
);
|
||||||
|
|
||||||
// missing the sibling to complete the first tree, and the two last trees
|
// missing the sibling to complete the first tree, and the two last trees
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
mmr.get_delta(3).unwrap().data,
|
mmr.get_delta(3, mmr.forest()).unwrap().data,
|
||||||
vec![LEAVES[3], acc.peaks()[1], acc.peaks()[2]],
|
vec![LEAVES[3], acc.peaks()[1], acc.peaks()[2]],
|
||||||
"one sibling, two peaks"
|
"one sibling, two peaks"
|
||||||
);
|
);
|
||||||
|
@ -616,18 +616,60 @@ fn test_mmr_updates() {
|
||||||
// missing half of the first tree, only send the computed element (not the leaves), and the new
|
// missing half of the first tree, only send the computed element (not the leaves), and the new
|
||||||
// peaks
|
// peaks
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
mmr.get_delta(2).unwrap().data,
|
mmr.get_delta(2, mmr.forest()).unwrap().data,
|
||||||
vec![mmr.nodes[5], acc.peaks()[1], acc.peaks()[2]],
|
vec![mmr.nodes[5], acc.peaks()[1], acc.peaks()[2]],
|
||||||
"one sibling, two peaks"
|
"one sibling, two peaks"
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
mmr.get_delta(1).unwrap().data,
|
mmr.get_delta(1, mmr.forest()).unwrap().data,
|
||||||
vec![LEAVES[1], mmr.nodes[5], acc.peaks()[1], acc.peaks()[2]],
|
vec![LEAVES[1], mmr.nodes[5], acc.peaks()[1], acc.peaks()[2]],
|
||||||
"one sibling, two peaks"
|
"one sibling, two peaks"
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(&mmr.get_delta(0).unwrap().data, acc.peaks(), "all peaks");
|
assert_eq!(&mmr.get_delta(0, mmr.forest()).unwrap().data, acc.peaks(), "all peaks");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_mmr_delta_old_forest() {
|
||||||
|
let mmr: Mmr = LEAVES.into();
|
||||||
|
|
||||||
|
// from_forest must be smaller-or-equal to to_forest
|
||||||
|
for version in 1..=mmr.forest() {
|
||||||
|
assert!(mmr.get_delta(version + 1, version).is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
// when from_forest and to_forest are equal, there are no updates
|
||||||
|
for version in 1..=mmr.forest() {
|
||||||
|
let delta = mmr.get_delta(version, version).unwrap();
|
||||||
|
assert!(delta.data.is_empty());
|
||||||
|
assert_eq!(delta.forest, version);
|
||||||
|
}
|
||||||
|
|
||||||
|
// test update which merges the odd peak to the right
|
||||||
|
for count in 0..(mmr.forest() / 2) {
|
||||||
|
// *2 because every iteration tests a pair
|
||||||
|
// +1 because the Mmr is 1-indexed
|
||||||
|
let from_forest = (count * 2) + 1;
|
||||||
|
let to_forest = (count * 2) + 2;
|
||||||
|
let delta = mmr.get_delta(from_forest, to_forest).unwrap();
|
||||||
|
|
||||||
|
// *2 because every iteration tests a pair
|
||||||
|
// +1 because sibling is the odd element
|
||||||
|
let sibling = (count * 2) + 1;
|
||||||
|
assert_eq!(delta.data, [LEAVES[sibling]]);
|
||||||
|
assert_eq!(delta.forest, to_forest);
|
||||||
|
}
|
||||||
|
|
||||||
|
let version = 4;
|
||||||
|
let delta = mmr.get_delta(1, version).unwrap();
|
||||||
|
assert_eq!(delta.data, [mmr.nodes[1], mmr.nodes[5]]);
|
||||||
|
assert_eq!(delta.forest, version);
|
||||||
|
|
||||||
|
let version = 5;
|
||||||
|
let delta = mmr.get_delta(1, version).unwrap();
|
||||||
|
assert_eq!(delta.data, [mmr.nodes[1], mmr.nodes[5], mmr.nodes[7]]);
|
||||||
|
assert_eq!(delta.forest, version);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -682,7 +724,7 @@ fn test_partial_mmr_update_single() {
|
||||||
for i in 1..100 {
|
for i in 1..100 {
|
||||||
let node = int_to_node(i);
|
let node = int_to_node(i);
|
||||||
full.add(node);
|
full.add(node);
|
||||||
let delta = full.get_delta(partial.forest()).unwrap();
|
let delta = full.get_delta(partial.forest(), full.forest()).unwrap();
|
||||||
partial.apply(delta).unwrap();
|
partial.apply(delta).unwrap();
|
||||||
|
|
||||||
assert_eq!(partial.forest(), full.forest());
|
assert_eq!(partial.forest(), full.forest());
|
||||||
|
|
Loading…
Add table
Reference in a new issue