mirror of
https://github.com/mimblewimble/grin.git
synced 2025-01-21 11:31:08 +03:00
028b14d9d9
* only store leaves in rm_log * cleanup * commit * add failing test to cover case where we compact an already compacted data file * fix the logic for pruning the data file * rm_log only contains leaves prunelist only contains non-leaf subtree roots * cleanup * commit * bitmap impl running in parallel * rustfmt * this is better - rewind unremoves outpu pos spent by rewound inputs * commit * commit * pass bitmap around when rewinding * store and retrieve input bitmap per block from the db * Replace the RemoveLog with the UTXO set. * utxo set starting to pass tests * stuff works... * split store types out added some tests to compare the performance of the rm_log and the proposed utxo_set * pull prune_list out into standalone file * cleanup, get rid of unused height param * cleanup and add doc comments * add support for migrating rm_log to utxo_set * take snapshot of utxo file during fast sync implement migration of rm_log -> utxo_set * rename rewound_copy to snapshot * fixup pmmr tests to reflect cutoff_pos * cleanup unused import * check_compact needs to rewind the utxo_set as appropriate * fix pool tests * fixup core tests * cache block_input_bitmaps via LruCache in store * cache block header on initial write to db * rename utxo_set -> leaf_set and remove references to "spent" in grin_store * better document the rewind behavior
1026 lines
28 KiB
Rust
1026 lines
28 KiB
Rust
// Copyright 2018 The Grin Developers
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
//! PMMR tests
|
|
#[macro_use]
|
|
extern crate grin_core as core;
|
|
extern crate croaring;
|
|
|
|
use croaring::Bitmap;
|
|
|
|
use core::core::hash::Hash;
|
|
use core::core::pmmr::{self, Backend, MerkleProof, PMMR};
|
|
use core::core::prune_list::PruneList;
|
|
use core::core::BlockHeader;
|
|
use core::ser::{self, Error, PMMRIndexHashable, PMMRable, Readable, Reader, Writeable, Writer};
|
|
|
|
/// Simple MMR backend implementation based on a Vector. Pruning does not
|
|
/// compact the Vec itself.
|
|
#[derive(Clone, Debug)]
|
|
pub struct VecBackend<T>
|
|
where
|
|
T: PMMRable,
|
|
{
|
|
/// Backend elements
|
|
pub elems: Vec<Option<(Hash, Option<T>)>>,
|
|
/// Positions of removed elements
|
|
pub remove_list: Vec<u64>,
|
|
}
|
|
|
|
impl<T> Backend<T> for VecBackend<T>
|
|
where
|
|
T: PMMRable,
|
|
{
|
|
fn append(&mut self, _position: u64, data: Vec<(Hash, Option<T>)>) -> Result<(), String> {
|
|
self.elems.append(&mut map_vec!(data, |d| Some(d.clone())));
|
|
Ok(())
|
|
}
|
|
|
|
fn get_hash(&self, position: u64) -> Option<Hash> {
|
|
if self.remove_list.contains(&position) {
|
|
None
|
|
} else {
|
|
if let Some(ref elem) = self.elems[(position - 1) as usize] {
|
|
Some(elem.0)
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
}
|
|
|
|
fn get_data(&self, position: u64) -> Option<T> {
|
|
if self.remove_list.contains(&position) {
|
|
None
|
|
} else {
|
|
if let Some(ref elem) = self.elems[(position - 1) as usize] {
|
|
elem.1.clone()
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
}
|
|
|
|
fn get_from_file(&self, position: u64) -> Option<Hash> {
|
|
if let Some(ref x) = self.elems[(position - 1) as usize] {
|
|
Some(x.0)
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
|
|
fn get_data_from_file(&self, position: u64) -> Option<T> {
|
|
if let Some(ref x) = self.elems[(position - 1) as usize] {
|
|
x.1.clone()
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
|
|
fn remove(&mut self, position: u64) -> Result<(), String> {
|
|
self.remove_list.push(position);
|
|
Ok(())
|
|
}
|
|
|
|
fn rewind(
|
|
&mut self,
|
|
position: u64,
|
|
rewind_add_pos: &Bitmap,
|
|
rewind_rm_pos: &Bitmap,
|
|
) -> Result<(), String> {
|
|
panic!("not yet implemented for vec backend...");
|
|
}
|
|
|
|
fn get_data_file_path(&self) -> String {
|
|
"".to_string()
|
|
}
|
|
|
|
fn snapshot(&self, header: &BlockHeader) -> Result<(), String> {
|
|
Ok(())
|
|
}
|
|
|
|
fn dump_stats(&self) {}
|
|
}
|
|
|
|
impl<T> VecBackend<T>
|
|
where
|
|
T: PMMRable,
|
|
{
|
|
/// Instantiates a new VecBackend<T>
|
|
pub fn new() -> VecBackend<T> {
|
|
VecBackend {
|
|
elems: vec![],
|
|
remove_list: vec![],
|
|
}
|
|
}
|
|
|
|
/// Current number of elements in the underlying Vec.
|
|
pub fn used_size(&self) -> usize {
|
|
let mut usz = self.elems.len();
|
|
for (idx, _) in self.elems.iter().enumerate() {
|
|
let idx = idx as u64;
|
|
if self.remove_list.contains(&idx) {
|
|
usz -= 1;
|
|
}
|
|
}
|
|
usz
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn some_all_ones() {
|
|
for n in vec![1, 7, 255] {
|
|
assert!(pmmr::all_ones(n), "{} should be all ones", n);
|
|
}
|
|
for n in vec![0, 6, 9, 128] {
|
|
assert!(!pmmr::all_ones(n), "{} should not be all ones", n);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn some_most_signif() {
|
|
assert_eq!(pmmr::most_significant_pos(0), 0);
|
|
assert_eq!(pmmr::most_significant_pos(1), 1);
|
|
assert_eq!(pmmr::most_significant_pos(6), 3);
|
|
assert_eq!(pmmr::most_significant_pos(7), 3);
|
|
assert_eq!(pmmr::most_significant_pos(8), 4);
|
|
assert_eq!(pmmr::most_significant_pos(128), 8);
|
|
}
|
|
|
|
#[test]
|
|
#[allow(unused_variables)]
|
|
fn first_100_mmr_heights() {
|
|
let first_100_str = "0 0 1 0 0 1 2 0 0 1 0 0 1 2 3 0 0 1 0 0 1 2 0 0 1 0 0 1 2 3 4 \
|
|
0 0 1 0 0 1 2 0 0 1 0 0 1 2 3 0 0 1 0 0 1 2 0 0 1 0 0 1 2 3 4 5 \
|
|
0 0 1 0 0 1 2 0 0 1 0 0 1 2 3 0 0 1 0 0 1 2 0 0 1 0 0 1 2 3 4 0 0 1 0 0";
|
|
let first_100 = first_100_str.split(' ').map(|n| n.parse::<u64>().unwrap());
|
|
let mut count = 1;
|
|
for n in first_100 {
|
|
assert_eq!(
|
|
n,
|
|
pmmr::bintree_postorder_height(count),
|
|
"expected {}, got {}",
|
|
n,
|
|
pmmr::bintree_postorder_height(count)
|
|
);
|
|
count += 1;
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_n_leaves() {
|
|
// make sure we handle an empty MMR correctly
|
|
assert_eq!(pmmr::n_leaves(0), 0);
|
|
|
|
// and various sizes on non-empty MMRs
|
|
assert_eq!(pmmr::n_leaves(1), 1);
|
|
assert_eq!(pmmr::n_leaves(2), 2);
|
|
assert_eq!(pmmr::n_leaves(3), 2);
|
|
assert_eq!(pmmr::n_leaves(4), 3);
|
|
assert_eq!(pmmr::n_leaves(5), 4);
|
|
assert_eq!(pmmr::n_leaves(6), 4);
|
|
assert_eq!(pmmr::n_leaves(7), 4);
|
|
assert_eq!(pmmr::n_leaves(8), 5);
|
|
assert_eq!(pmmr::n_leaves(9), 6);
|
|
assert_eq!(pmmr::n_leaves(10), 6);
|
|
}
|
|
|
|
/// Find parent and sibling positions for various node positions.
|
|
#[test]
|
|
fn various_families() {
|
|
// 0 0 1 0 0 1 2 0 0 1 0 0 1 2 3
|
|
assert_eq!(pmmr::family(1), (3, 2));
|
|
assert_eq!(pmmr::family(2), (3, 1));
|
|
assert_eq!(pmmr::family(3), (7, 6));
|
|
assert_eq!(pmmr::family(4), (6, 5));
|
|
assert_eq!(pmmr::family(5), (6, 4));
|
|
assert_eq!(pmmr::family(6), (7, 3));
|
|
assert_eq!(pmmr::family(7), (15, 14));
|
|
assert_eq!(pmmr::family(1_000), (1_001, 997));
|
|
}
|
|
|
|
#[test]
|
|
fn test_is_left_sibling() {
|
|
assert_eq!(pmmr::is_left_sibling(1), true);
|
|
assert_eq!(pmmr::is_left_sibling(2), false);
|
|
assert_eq!(pmmr::is_left_sibling(3), true);
|
|
}
|
|
|
|
#[test]
|
|
fn various_branches() {
|
|
// the two leaf nodes in a 3 node tree (height 1)
|
|
assert_eq!(pmmr::family_branch(1, 3), [(3, 2)]);
|
|
assert_eq!(pmmr::family_branch(2, 3), [(3, 1)]);
|
|
|
|
// the root node in a 3 node tree
|
|
assert_eq!(pmmr::family_branch(3, 3), []);
|
|
|
|
// leaf node in a larger tree of 7 nodes (height 2)
|
|
assert_eq!(pmmr::family_branch(1, 7), [(3, 2), (7, 6)]);
|
|
|
|
// note these only go as far up as the local peak, not necessarily the single
|
|
// root
|
|
assert_eq!(pmmr::family_branch(1, 4), [(3, 2)]);
|
|
// pos 4 in a tree of size 4 is a local peak
|
|
assert_eq!(pmmr::family_branch(4, 4), []);
|
|
// pos 4 in a tree of size 5 is also still a local peak
|
|
assert_eq!(pmmr::family_branch(4, 5), []);
|
|
// pos 4 in a tree of size 6 has a parent and a sibling
|
|
assert_eq!(pmmr::family_branch(4, 6), [(6, 5)]);
|
|
// a tree of size 7 is all under a single root
|
|
assert_eq!(pmmr::family_branch(4, 7), [(6, 5), (7, 3)]);
|
|
|
|
// ok now for a more realistic one, a tree with over a million nodes in it
|
|
// find the "family path" back up the tree from a leaf node at 0
|
|
// Note: the first two entries in the branch are consistent with a small 7 node
|
|
// tree Note: each sibling is on the left branch, this is an example of the
|
|
// largest possible list of peaks before we start combining them into larger
|
|
// peaks.
|
|
assert_eq!(
|
|
pmmr::family_branch(1, 1_049_000),
|
|
[
|
|
(3, 2),
|
|
(7, 6),
|
|
(15, 14),
|
|
(31, 30),
|
|
(63, 62),
|
|
(127, 126),
|
|
(255, 254),
|
|
(511, 510),
|
|
(1023, 1022),
|
|
(2047, 2046),
|
|
(4095, 4094),
|
|
(8191, 8190),
|
|
(16383, 16382),
|
|
(32767, 32766),
|
|
(65535, 65534),
|
|
(131071, 131070),
|
|
(262143, 262142),
|
|
(524287, 524286),
|
|
(1048575, 1048574),
|
|
]
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn some_peaks() {
|
|
// 0 0 1 0 0 1 2 0 0 1 0 0 1 2 3
|
|
|
|
let empty: Vec<u64> = vec![];
|
|
|
|
// make sure we handle an empty MMR correctly
|
|
assert_eq!(pmmr::peaks(0), empty);
|
|
|
|
// and various non-empty MMRs
|
|
assert_eq!(pmmr::peaks(1), [1]);
|
|
assert_eq!(pmmr::peaks(2), empty);
|
|
assert_eq!(pmmr::peaks(3), [3]);
|
|
assert_eq!(pmmr::peaks(4), [3, 4]);
|
|
assert_eq!(pmmr::peaks(5), empty);
|
|
assert_eq!(pmmr::peaks(6), empty);
|
|
assert_eq!(pmmr::peaks(7), [7]);
|
|
assert_eq!(pmmr::peaks(8), [7, 8]);
|
|
assert_eq!(pmmr::peaks(9), empty);
|
|
assert_eq!(pmmr::peaks(10), [7, 10]);
|
|
assert_eq!(pmmr::peaks(11), [7, 10, 11]);
|
|
assert_eq!(pmmr::peaks(22), [15, 22]);
|
|
assert_eq!(pmmr::peaks(32), [31, 32]);
|
|
assert_eq!(pmmr::peaks(35), [31, 34, 35]);
|
|
assert_eq!(pmmr::peaks(42), [31, 38, 41, 42]);
|
|
|
|
// large realistic example with almost 1.5 million nodes
|
|
// note the distance between peaks decreases toward the right (trees get
|
|
// smaller)
|
|
assert_eq!(
|
|
pmmr::peaks(1048555),
|
|
[
|
|
524287, 786430, 917501, 983036, 1015803, 1032186, 1040377, 1044472, 1046519, 1047542,
|
|
1048053, 1048308, 1048435, 1048498, 1048529, 1048544, 1048551, 1048554, 1048555,
|
|
],
|
|
);
|
|
}
|
|
|
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
|
struct TestElem([u32; 4]);
|
|
|
|
impl PMMRable for TestElem {
|
|
fn len() -> usize {
|
|
16
|
|
}
|
|
}
|
|
|
|
impl Writeable for TestElem {
|
|
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), Error> {
|
|
writer.write_u32(self.0[0])?;
|
|
writer.write_u32(self.0[1])?;
|
|
writer.write_u32(self.0[2])?;
|
|
writer.write_u32(self.0[3])
|
|
}
|
|
}
|
|
|
|
impl Readable for TestElem {
|
|
fn read(reader: &mut Reader) -> Result<TestElem, Error> {
|
|
Ok(TestElem([
|
|
reader.read_u32()?,
|
|
reader.read_u32()?,
|
|
reader.read_u32()?,
|
|
reader.read_u32()?,
|
|
]))
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn empty_merkle_proof() {
|
|
let proof = MerkleProof::empty();
|
|
assert_eq!(proof.verify(), false);
|
|
}
|
|
|
|
#[test]
|
|
fn pmmr_merkle_proof() {
|
|
// 0 0 1 0 0 1 2 0 0 1 0 0 1 2 3
|
|
|
|
let mut ba = VecBackend::new();
|
|
let mut pmmr = PMMR::new(&mut ba);
|
|
|
|
pmmr.push(TestElem([0, 0, 0, 1])).unwrap();
|
|
assert_eq!(pmmr.last_pos, 1);
|
|
let proof = pmmr.merkle_proof(1).unwrap();
|
|
let root = pmmr.root();
|
|
assert_eq!(proof.peaks, [root]);
|
|
assert!(proof.path.is_empty());
|
|
assert!(proof.verify());
|
|
|
|
// push two more elements into the PMMR
|
|
pmmr.push(TestElem([0, 0, 0, 2])).unwrap();
|
|
pmmr.push(TestElem([0, 0, 0, 3])).unwrap();
|
|
assert_eq!(pmmr.last_pos, 4);
|
|
|
|
let proof1 = pmmr.merkle_proof(1).unwrap();
|
|
assert_eq!(proof1.peaks.len(), 2);
|
|
assert_eq!(proof1.path.len(), 1);
|
|
assert!(proof1.verify());
|
|
|
|
let proof2 = pmmr.merkle_proof(2).unwrap();
|
|
assert_eq!(proof2.peaks.len(), 2);
|
|
assert_eq!(proof2.path.len(), 1);
|
|
assert!(proof2.verify());
|
|
|
|
// check that we cannot generate a merkle proof for pos 3 (not a leaf node)
|
|
assert_eq!(
|
|
pmmr.merkle_proof(3).err(),
|
|
Some(format!("not a leaf at pos 3"))
|
|
);
|
|
|
|
let proof4 = pmmr.merkle_proof(4).unwrap();
|
|
assert_eq!(proof4.peaks.len(), 2);
|
|
assert!(proof4.path.is_empty());
|
|
assert!(proof4.verify());
|
|
|
|
// now add a few more elements to the PMMR to build a larger merkle proof
|
|
for x in 4..1000 {
|
|
pmmr.push(TestElem([0, 0, 0, x])).unwrap();
|
|
}
|
|
let proof = pmmr.merkle_proof(1).unwrap();
|
|
assert_eq!(proof.peaks.len(), 8);
|
|
assert_eq!(proof.path.len(), 9);
|
|
assert!(proof.verify());
|
|
}
|
|
|
|
#[test]
|
|
fn pmmr_merkle_proof_prune_and_rewind() {
|
|
let mut ba = VecBackend::new();
|
|
let mut pmmr = PMMR::new(&mut ba);
|
|
pmmr.push(TestElem([0, 0, 0, 1])).unwrap();
|
|
pmmr.push(TestElem([0, 0, 0, 2])).unwrap();
|
|
let proof = pmmr.merkle_proof(2).unwrap();
|
|
|
|
// now prune an element and check we can still generate
|
|
// the correct Merkle proof for the other element (after sibling pruned)
|
|
pmmr.prune(1).unwrap();
|
|
let proof_2 = pmmr.merkle_proof(2).unwrap();
|
|
assert_eq!(proof, proof_2);
|
|
}
|
|
|
|
#[test]
|
|
fn merkle_proof_ser_deser() {
|
|
let mut ba = VecBackend::new();
|
|
let mut pmmr = PMMR::new(&mut ba);
|
|
for x in 0..15 {
|
|
pmmr.push(TestElem([0, 0, 0, x])).unwrap();
|
|
}
|
|
let proof = pmmr.merkle_proof(9).unwrap();
|
|
assert!(proof.verify());
|
|
|
|
let mut vec = Vec::new();
|
|
ser::serialize(&mut vec, &proof).expect("serialization failed");
|
|
let proof_2: MerkleProof = ser::deserialize(&mut &vec[..]).unwrap();
|
|
|
|
assert_eq!(proof, proof_2);
|
|
}
|
|
|
|
#[test]
|
|
#[allow(unused_variables)]
|
|
fn pmmr_push_root() {
|
|
let elems = [
|
|
TestElem([0, 0, 0, 1]),
|
|
TestElem([0, 0, 0, 2]),
|
|
TestElem([0, 0, 0, 3]),
|
|
TestElem([0, 0, 0, 4]),
|
|
TestElem([0, 0, 0, 5]),
|
|
TestElem([0, 0, 0, 6]),
|
|
TestElem([0, 0, 0, 7]),
|
|
TestElem([0, 0, 0, 8]),
|
|
TestElem([1, 0, 0, 0]),
|
|
];
|
|
|
|
let mut ba = VecBackend::new();
|
|
let mut pmmr = PMMR::new(&mut ba);
|
|
|
|
// one element
|
|
pmmr.push(elems[0]).unwrap();
|
|
pmmr.dump(false);
|
|
let pos_0 = elems[0].hash_with_index(0);
|
|
assert_eq!(pmmr.peaks(), vec![pos_0]);
|
|
assert_eq!(pmmr.root(), pos_0);
|
|
assert_eq!(pmmr.unpruned_size(), 1);
|
|
|
|
// two elements
|
|
pmmr.push(elems[1]).unwrap();
|
|
pmmr.dump(false);
|
|
let pos_1 = elems[1].hash_with_index(1);
|
|
let pos_2 = (pos_0, pos_1).hash_with_index(2);
|
|
assert_eq!(pmmr.peaks(), vec![pos_2]);
|
|
assert_eq!(pmmr.root(), pos_2);
|
|
assert_eq!(pmmr.unpruned_size(), 3);
|
|
|
|
// three elements
|
|
pmmr.push(elems[2]).unwrap();
|
|
pmmr.dump(false);
|
|
let pos_3 = elems[2].hash_with_index(3);
|
|
assert_eq!(pmmr.peaks(), vec![pos_2, pos_3]);
|
|
assert_eq!(pmmr.root(), (pos_2, pos_3).hash_with_index(4));
|
|
assert_eq!(pmmr.unpruned_size(), 4);
|
|
|
|
// four elements
|
|
pmmr.push(elems[3]).unwrap();
|
|
pmmr.dump(false);
|
|
let pos_4 = elems[3].hash_with_index(4);
|
|
let pos_5 = (pos_3, pos_4).hash_with_index(5);
|
|
let pos_6 = (pos_2, pos_5).hash_with_index(6);
|
|
assert_eq!(pmmr.peaks(), vec![pos_6]);
|
|
assert_eq!(pmmr.root(), pos_6);
|
|
assert_eq!(pmmr.unpruned_size(), 7);
|
|
|
|
// five elements
|
|
pmmr.push(elems[4]).unwrap();
|
|
pmmr.dump(false);
|
|
let pos_7 = elems[4].hash_with_index(7);
|
|
assert_eq!(pmmr.peaks(), vec![pos_6, pos_7]);
|
|
assert_eq!(pmmr.root(), (pos_6, pos_7).hash_with_index(8));
|
|
assert_eq!(pmmr.unpruned_size(), 8);
|
|
|
|
// six elements
|
|
pmmr.push(elems[5]).unwrap();
|
|
let pos_8 = elems[5].hash_with_index(8);
|
|
let pos_9 = (pos_7, pos_8).hash_with_index(9);
|
|
assert_eq!(pmmr.peaks(), vec![pos_6, pos_9]);
|
|
assert_eq!(pmmr.root(), (pos_6, pos_9).hash_with_index(10));
|
|
assert_eq!(pmmr.unpruned_size(), 10);
|
|
|
|
// seven elements
|
|
pmmr.push(elems[6]).unwrap();
|
|
let pos_10 = elems[6].hash_with_index(10);
|
|
assert_eq!(pmmr.peaks(), vec![pos_6, pos_9, pos_10]);
|
|
assert_eq!(
|
|
pmmr.root(),
|
|
(pos_6, (pos_9, pos_10).hash_with_index(11)).hash_with_index(11)
|
|
);
|
|
assert_eq!(pmmr.unpruned_size(), 11);
|
|
|
|
// 001001200100123
|
|
// eight elements
|
|
pmmr.push(elems[7]).unwrap();
|
|
let pos_11 = elems[7].hash_with_index(11);
|
|
let pos_12 = (pos_10, pos_11).hash_with_index(12);
|
|
let pos_13 = (pos_9, pos_12).hash_with_index(13);
|
|
let pos_14 = (pos_6, pos_13).hash_with_index(14);
|
|
assert_eq!(pmmr.peaks(), vec![pos_14]);
|
|
assert_eq!(pmmr.root(), pos_14);
|
|
assert_eq!(pmmr.unpruned_size(), 15);
|
|
|
|
// nine elements
|
|
pmmr.push(elems[8]).unwrap();
|
|
let pos_15 = elems[8].hash_with_index(15);
|
|
assert_eq!(pmmr.peaks(), vec![pos_14, pos_15]);
|
|
assert_eq!(pmmr.root(), (pos_14, pos_15).hash_with_index(16));
|
|
assert_eq!(pmmr.unpruned_size(), 16);
|
|
}
|
|
|
|
#[test]
|
|
fn pmmr_get_last_n_insertions() {
|
|
let elems = [
|
|
TestElem([0, 0, 0, 1]),
|
|
TestElem([0, 0, 0, 2]),
|
|
TestElem([0, 0, 0, 3]),
|
|
TestElem([0, 0, 0, 4]),
|
|
TestElem([0, 0, 0, 5]),
|
|
TestElem([0, 0, 0, 6]),
|
|
TestElem([0, 0, 0, 7]),
|
|
TestElem([0, 0, 0, 8]),
|
|
TestElem([1, 0, 0, 0]),
|
|
];
|
|
|
|
let mut ba = VecBackend::new();
|
|
let mut pmmr = PMMR::new(&mut ba);
|
|
|
|
// test when empty
|
|
let res = pmmr.get_last_n_insertions(19);
|
|
assert!(res.len() == 0);
|
|
|
|
pmmr.push(elems[0]).unwrap();
|
|
let res = pmmr.get_last_n_insertions(19);
|
|
assert!(res.len() == 1);
|
|
|
|
pmmr.push(elems[1]).unwrap();
|
|
|
|
let res = pmmr.get_last_n_insertions(12);
|
|
assert!(res.len() == 2);
|
|
|
|
pmmr.push(elems[2]).unwrap();
|
|
|
|
let res = pmmr.get_last_n_insertions(2);
|
|
assert!(res.len() == 2);
|
|
|
|
pmmr.push(elems[3]).unwrap();
|
|
|
|
let res = pmmr.get_last_n_insertions(19);
|
|
assert!(res.len() == 4);
|
|
|
|
pmmr.push(elems[5]).unwrap();
|
|
pmmr.push(elems[6]).unwrap();
|
|
pmmr.push(elems[7]).unwrap();
|
|
pmmr.push(elems[8]).unwrap();
|
|
|
|
let res = pmmr.get_last_n_insertions(7);
|
|
assert!(res.len() == 7);
|
|
}
|
|
|
|
#[test]
|
|
#[allow(unused_variables)]
|
|
fn pmmr_prune() {
|
|
let elems = [
|
|
TestElem([0, 0, 0, 1]),
|
|
TestElem([0, 0, 0, 2]),
|
|
TestElem([0, 0, 0, 3]),
|
|
TestElem([0, 0, 0, 4]),
|
|
TestElem([0, 0, 0, 5]),
|
|
TestElem([0, 0, 0, 6]),
|
|
TestElem([0, 0, 0, 7]),
|
|
TestElem([0, 0, 0, 8]),
|
|
TestElem([1, 0, 0, 0]),
|
|
];
|
|
|
|
let orig_root: Hash;
|
|
let sz: u64;
|
|
let mut ba = VecBackend::new();
|
|
{
|
|
let mut pmmr = PMMR::new(&mut ba);
|
|
for elem in &elems[..] {
|
|
pmmr.push(*elem).unwrap();
|
|
}
|
|
orig_root = pmmr.root();
|
|
sz = pmmr.unpruned_size();
|
|
}
|
|
|
|
// First check the initial numbers of elements.
|
|
assert_eq!(ba.elems.len(), 16);
|
|
assert_eq!(ba.remove_list.len(), 0);
|
|
|
|
// pruning a leaf with no parent should do nothing
|
|
{
|
|
let mut pmmr: PMMR<TestElem, _> = PMMR::at(&mut ba, sz);
|
|
pmmr.prune(16).unwrap();
|
|
assert_eq!(orig_root, pmmr.root());
|
|
}
|
|
assert_eq!(ba.elems.len(), 16);
|
|
assert_eq!(ba.remove_list.len(), 1);
|
|
|
|
// pruning leaves with no shared parent just removes 1 element
|
|
{
|
|
let mut pmmr: PMMR<TestElem, _> = PMMR::at(&mut ba, sz);
|
|
pmmr.prune(2).unwrap();
|
|
assert_eq!(orig_root, pmmr.root());
|
|
}
|
|
assert_eq!(ba.elems.len(), 16);
|
|
assert_eq!(ba.remove_list.len(), 2);
|
|
|
|
{
|
|
let mut pmmr: PMMR<TestElem, _> = PMMR::at(&mut ba, sz);
|
|
pmmr.prune(4).unwrap();
|
|
assert_eq!(orig_root, pmmr.root());
|
|
}
|
|
assert_eq!(ba.elems.len(), 16);
|
|
assert_eq!(ba.remove_list.len(), 3);
|
|
|
|
// pruning a non-leaf node has no effect
|
|
{
|
|
let mut pmmr: PMMR<TestElem, _> = PMMR::at(&mut ba, sz);
|
|
pmmr.prune(3).unwrap_err();
|
|
assert_eq!(orig_root, pmmr.root());
|
|
}
|
|
assert_eq!(ba.elems.len(), 16);
|
|
assert_eq!(ba.remove_list.len(), 3);
|
|
|
|
// TODO - no longer true (leaves only now) - pruning sibling removes subtree
|
|
{
|
|
let mut pmmr: PMMR<TestElem, _> = PMMR::at(&mut ba, sz);
|
|
pmmr.prune(5).unwrap();
|
|
assert_eq!(orig_root, pmmr.root());
|
|
}
|
|
assert_eq!(ba.elems.len(), 16);
|
|
assert_eq!(ba.remove_list.len(), 4);
|
|
|
|
// TODO - no longeer true (leaves only now) - pruning all leaves under level >1
|
|
// removes all subtree
|
|
{
|
|
let mut pmmr: PMMR<TestElem, _> = PMMR::at(&mut ba, sz);
|
|
pmmr.prune(1).unwrap();
|
|
assert_eq!(orig_root, pmmr.root());
|
|
}
|
|
assert_eq!(ba.elems.len(), 16);
|
|
assert_eq!(ba.remove_list.len(), 5);
|
|
|
|
// pruning everything should only leave us with a single peak
|
|
{
|
|
let mut pmmr: PMMR<TestElem, _> = PMMR::at(&mut ba, sz);
|
|
for n in 1..16 {
|
|
let _ = pmmr.prune(n);
|
|
}
|
|
assert_eq!(orig_root, pmmr.root());
|
|
}
|
|
assert_eq!(ba.elems.len(), 16);
|
|
assert_eq!(ba.remove_list.len(), 9);
|
|
}
|
|
|
|
#[test]
|
|
fn pmmr_next_pruned_idx() {
|
|
let mut pl = PruneList::new();
|
|
|
|
assert_eq!(pl.pruned_nodes.len(), 0);
|
|
assert_eq!(pl.next_pruned_idx(1), Some(0));
|
|
assert_eq!(pl.next_pruned_idx(2), Some(0));
|
|
assert_eq!(pl.next_pruned_idx(3), Some(0));
|
|
|
|
pl.add(2);
|
|
assert_eq!(pl.pruned_nodes.len(), 1);
|
|
assert_eq!(pl.pruned_nodes, [2]);
|
|
assert_eq!(pl.next_pruned_idx(1), Some(0));
|
|
assert_eq!(pl.next_pruned_idx(2), None);
|
|
assert_eq!(pl.next_pruned_idx(3), Some(1));
|
|
assert_eq!(pl.next_pruned_idx(4), Some(1));
|
|
|
|
pl.add(1);
|
|
assert_eq!(pl.pruned_nodes.len(), 1);
|
|
assert_eq!(pl.pruned_nodes, [3]);
|
|
assert_eq!(pl.next_pruned_idx(1), None);
|
|
assert_eq!(pl.next_pruned_idx(2), None);
|
|
assert_eq!(pl.next_pruned_idx(3), None);
|
|
assert_eq!(pl.next_pruned_idx(4), Some(1));
|
|
assert_eq!(pl.next_pruned_idx(5), Some(1));
|
|
|
|
pl.add(3);
|
|
assert_eq!(pl.pruned_nodes.len(), 1);
|
|
assert_eq!(pl.pruned_nodes, [3]);
|
|
assert_eq!(pl.next_pruned_idx(1), None);
|
|
assert_eq!(pl.next_pruned_idx(2), None);
|
|
assert_eq!(pl.next_pruned_idx(3), None);
|
|
assert_eq!(pl.next_pruned_idx(4), Some(1));
|
|
assert_eq!(pl.next_pruned_idx(5), Some(1));
|
|
}
|
|
|
|
#[test]
|
|
fn pmmr_prune_leaf_shift() {
|
|
let mut pl = PruneList::new();
|
|
|
|
// start with an empty prune list (nothing shifted)
|
|
assert_eq!(pl.pruned_nodes.len(), 0);
|
|
assert_eq!(pl.get_leaf_shift(1), Some(0));
|
|
assert_eq!(pl.get_leaf_shift(2), Some(0));
|
|
assert_eq!(pl.get_leaf_shift(4), Some(0));
|
|
|
|
// now add a single leaf pos to the prune list
|
|
// note this does not shift anything (we only start shifting after pruning a
|
|
// parent)
|
|
pl.add(1);
|
|
assert_eq!(pl.pruned_nodes.len(), 1);
|
|
assert_eq!(pl.pruned_nodes, [1]);
|
|
assert_eq!(pl.get_leaf_shift(1), Some(0));
|
|
assert_eq!(pl.get_leaf_shift(2), Some(0));
|
|
assert_eq!(pl.get_leaf_shift(3), Some(0));
|
|
assert_eq!(pl.get_leaf_shift(4), Some(0));
|
|
|
|
// now add the sibling leaf pos (pos 1 and pos 2) which will prune the parent
|
|
// at pos 3 this in turn will "leaf shift" the leaf at pos 3 by 2
|
|
pl.add(2);
|
|
assert_eq!(pl.pruned_nodes.len(), 1);
|
|
assert_eq!(pl.pruned_nodes, [3]);
|
|
assert_eq!(pl.get_leaf_shift(1), None);
|
|
assert_eq!(pl.get_leaf_shift(2), None);
|
|
assert_eq!(pl.get_leaf_shift(3), Some(2));
|
|
assert_eq!(pl.get_leaf_shift(4), Some(2));
|
|
assert_eq!(pl.get_leaf_shift(5), Some(2));
|
|
|
|
// now prune an additional leaf at pos 4
|
|
// leaf offset of subsequent pos will be 2
|
|
// 00100120
|
|
pl.add(4);
|
|
assert_eq!(pl.pruned_nodes, [3, 4]);
|
|
assert_eq!(pl.get_leaf_shift(1), None);
|
|
assert_eq!(pl.get_leaf_shift(2), None);
|
|
assert_eq!(pl.get_leaf_shift(3), Some(2));
|
|
assert_eq!(pl.get_leaf_shift(4), Some(2));
|
|
assert_eq!(pl.get_leaf_shift(5), Some(2));
|
|
assert_eq!(pl.get_leaf_shift(6), Some(2));
|
|
assert_eq!(pl.get_leaf_shift(7), Some(2));
|
|
assert_eq!(pl.get_leaf_shift(8), Some(2));
|
|
|
|
// now prune the sibling at pos 5
|
|
// the two smaller subtrees (pos 3 and pos 6) are rolled up to larger subtree
|
|
// (pos 7) the leaf offset is now 4 to cover entire subtree containing first
|
|
// 4 leaves 00100120
|
|
pl.add(5);
|
|
assert_eq!(pl.pruned_nodes, [7]);
|
|
assert_eq!(pl.get_leaf_shift(1), None);
|
|
assert_eq!(pl.get_leaf_shift(2), None);
|
|
assert_eq!(pl.get_leaf_shift(3), None);
|
|
assert_eq!(pl.get_leaf_shift(4), None);
|
|
assert_eq!(pl.get_leaf_shift(5), None);
|
|
assert_eq!(pl.get_leaf_shift(6), None);
|
|
assert_eq!(pl.get_leaf_shift(7), Some(4));
|
|
assert_eq!(pl.get_leaf_shift(8), Some(4));
|
|
assert_eq!(pl.get_leaf_shift(9), Some(4));
|
|
|
|
// now check we can prune some of these in an arbitrary order
|
|
// final result is one leaf (pos 2) and one small subtree (pos 6) pruned
|
|
// with leaf offset of 2 to account for the pruned subtree
|
|
let mut pl = PruneList::new();
|
|
pl.add(2);
|
|
pl.add(5);
|
|
pl.add(4);
|
|
assert_eq!(pl.pruned_nodes, [2, 6]);
|
|
assert_eq!(pl.get_leaf_shift(1), Some(0));
|
|
assert_eq!(pl.get_leaf_shift(2), Some(0));
|
|
assert_eq!(pl.get_leaf_shift(3), Some(0));
|
|
assert_eq!(pl.get_leaf_shift(4), None);
|
|
assert_eq!(pl.get_leaf_shift(5), None);
|
|
assert_eq!(pl.get_leaf_shift(6), Some(2));
|
|
assert_eq!(pl.get_leaf_shift(7), Some(2));
|
|
assert_eq!(pl.get_leaf_shift(8), Some(2));
|
|
assert_eq!(pl.get_leaf_shift(9), Some(2));
|
|
|
|
pl.add(1);
|
|
assert_eq!(pl.pruned_nodes, [7]);
|
|
assert_eq!(pl.get_leaf_shift(1), None);
|
|
assert_eq!(pl.get_leaf_shift(2), None);
|
|
assert_eq!(pl.get_leaf_shift(3), None);
|
|
assert_eq!(pl.get_leaf_shift(4), None);
|
|
assert_eq!(pl.get_leaf_shift(5), None);
|
|
assert_eq!(pl.get_leaf_shift(6), None);
|
|
assert_eq!(pl.get_leaf_shift(7), Some(4));
|
|
assert_eq!(pl.get_leaf_shift(8), Some(4));
|
|
assert_eq!(pl.get_leaf_shift(9), Some(4));
|
|
}
|
|
|
|
#[test]
|
|
fn pmmr_prune_shift() {
|
|
let mut pl = PruneList::new();
|
|
assert!(pl.pruned_nodes.is_empty());
|
|
assert_eq!(pl.get_shift(1), Some(0));
|
|
assert_eq!(pl.get_shift(2), Some(0));
|
|
assert_eq!(pl.get_shift(3), Some(0));
|
|
|
|
// prune a single leaf node
|
|
// pruning only a leaf node does not shift any subsequent pos
|
|
// we will only start shifting when a parent can be pruned
|
|
pl.add(1);
|
|
assert_eq!(pl.pruned_nodes, [1]);
|
|
assert_eq!(pl.get_shift(1), Some(0));
|
|
assert_eq!(pl.get_shift(2), Some(0));
|
|
assert_eq!(pl.get_shift(3), Some(0));
|
|
|
|
pl.add(2);
|
|
assert_eq!(pl.pruned_nodes, [3]);
|
|
assert_eq!(pl.get_shift(1), None);
|
|
assert_eq!(pl.get_shift(2), None);
|
|
// pos 3 is in the prune list, so removed but not compacted, but still shifted
|
|
assert_eq!(pl.get_shift(3), Some(2));
|
|
assert_eq!(pl.get_shift(4), Some(2));
|
|
assert_eq!(pl.get_shift(5), Some(2));
|
|
assert_eq!(pl.get_shift(6), Some(2));
|
|
|
|
// pos 3 is not a leaf and is already in prune list
|
|
// prune it and check we are still consistent
|
|
pl.add(3);
|
|
assert_eq!(pl.pruned_nodes, [3]);
|
|
assert_eq!(pl.get_shift(1), None);
|
|
assert_eq!(pl.get_shift(2), None);
|
|
// pos 3 is in the prune list, so removed but not compacted, but still shifted
|
|
assert_eq!(pl.get_shift(3), Some(2));
|
|
assert_eq!(pl.get_shift(4), Some(2));
|
|
assert_eq!(pl.get_shift(5), Some(2));
|
|
assert_eq!(pl.get_shift(6), Some(2));
|
|
|
|
pl.add(4);
|
|
assert_eq!(pl.pruned_nodes, [3, 4]);
|
|
assert_eq!(pl.get_shift(1), None);
|
|
assert_eq!(pl.get_shift(2), None);
|
|
// pos 3 is in the prune list, so removed but not compacted, but still shifted
|
|
assert_eq!(pl.get_shift(3), Some(2));
|
|
// pos 4 is also in the prune list and also shifted by same amount
|
|
assert_eq!(pl.get_shift(4), Some(2));
|
|
// subsequent nodes also shifted consistently
|
|
assert_eq!(pl.get_shift(5), Some(2));
|
|
assert_eq!(pl.get_shift(6), Some(2));
|
|
|
|
pl.add(5);
|
|
assert_eq!(pl.pruned_nodes, [7]);
|
|
assert_eq!(pl.get_shift(1), None);
|
|
assert_eq!(pl.get_shift(2), None);
|
|
assert_eq!(pl.get_shift(3), None);
|
|
assert_eq!(pl.get_shift(4), None);
|
|
assert_eq!(pl.get_shift(5), None);
|
|
assert_eq!(pl.get_shift(6), None);
|
|
// everything prior to pos 7 is compacted away
|
|
// pos 7 is shifted by 6 to account for this
|
|
assert_eq!(pl.get_shift(7), Some(6));
|
|
assert_eq!(pl.get_shift(8), Some(6));
|
|
assert_eq!(pl.get_shift(9), Some(6));
|
|
|
|
// prune a bunch more
|
|
for x in 6..1000 {
|
|
pl.add(x);
|
|
}
|
|
// and check we shift by a large number (hopefully the correct number...)
|
|
assert_eq!(pl.get_shift(1010), Some(996));
|
|
|
|
let mut pl = PruneList::new();
|
|
pl.add(2);
|
|
pl.add(5);
|
|
pl.add(4);
|
|
assert_eq!(pl.pruned_nodes, [2, 6]);
|
|
assert_eq!(pl.get_shift(1), Some(0));
|
|
assert_eq!(pl.get_shift(2), Some(0));
|
|
assert_eq!(pl.get_shift(3), Some(0));
|
|
assert_eq!(pl.get_shift(4), None);
|
|
assert_eq!(pl.get_shift(5), None);
|
|
assert_eq!(pl.get_shift(6), Some(2));
|
|
assert_eq!(pl.get_shift(7), Some(2));
|
|
assert_eq!(pl.get_shift(8), Some(2));
|
|
assert_eq!(pl.get_shift(9), Some(2));
|
|
|
|
// TODO - put some of these tests back in place for completeness
|
|
|
|
//
|
|
// let mut pl = PruneList::new();
|
|
// pl.add(4);
|
|
// assert_eq!(pl.pruned_nodes.len(), 1);
|
|
// assert_eq!(pl.pruned_nodes, [4]);
|
|
// assert_eq!(pl.get_shift(1), Some(0));
|
|
// assert_eq!(pl.get_shift(2), Some(0));
|
|
// assert_eq!(pl.get_shift(3), Some(0));
|
|
// assert_eq!(pl.get_shift(4), None);
|
|
// assert_eq!(pl.get_shift(5), Some(1));
|
|
// assert_eq!(pl.get_shift(6), Some(1));
|
|
//
|
|
//
|
|
// pl.add(5);
|
|
// assert_eq!(pl.pruned_nodes.len(), 1);
|
|
// assert_eq!(pl.pruned_nodes[0], 6);
|
|
// assert_eq!(pl.get_shift(8), Some(3));
|
|
// assert_eq!(pl.get_shift(2), Some(0));
|
|
// assert_eq!(pl.get_shift(5), None);
|
|
//
|
|
// pl.add(2);
|
|
// assert_eq!(pl.pruned_nodes.len(), 2);
|
|
// assert_eq!(pl.pruned_nodes[0], 2);
|
|
// assert_eq!(pl.get_shift(8), Some(4));
|
|
// assert_eq!(pl.get_shift(1), Some(0));
|
|
//
|
|
// pl.add(8);
|
|
// pl.add(11);
|
|
// assert_eq!(pl.pruned_nodes.len(), 4);
|
|
//
|
|
// pl.add(1);
|
|
// assert_eq!(pl.pruned_nodes.len(), 3);
|
|
// assert_eq!(pl.pruned_nodes[0], 7);
|
|
// assert_eq!(pl.get_shift(12), Some(9));
|
|
//
|
|
// pl.add(12);
|
|
// assert_eq!(pl.pruned_nodes.len(), 3);
|
|
// assert_eq!(pl.get_shift(12), None);
|
|
// assert_eq!(pl.get_shift(9), Some(8));
|
|
// assert_eq!(pl.get_shift(17), Some(11));
|
|
}
|
|
|
|
#[test]
|
|
fn check_all_ones() {
|
|
for i in 0..1000000 {
|
|
assert_eq!(old_all_ones(i), pmmr::all_ones(i));
|
|
}
|
|
}
|
|
|
|
// Check if the binary representation of a number is all ones.
|
|
fn old_all_ones(num: u64) -> bool {
|
|
if num == 0 {
|
|
return false;
|
|
}
|
|
let mut bit = 1;
|
|
while num >= bit {
|
|
if num & bit == 0 {
|
|
return false;
|
|
}
|
|
bit = bit << 1;
|
|
}
|
|
true
|
|
}
|
|
|
|
#[test]
|
|
fn check_most_significant_pos() {
|
|
for i in 0u64..1000000 {
|
|
assert_eq!(old_most_significant_pos(i), pmmr::most_significant_pos(i));
|
|
}
|
|
}
|
|
|
|
// Get the position of the most significant bit in a number.
|
|
fn old_most_significant_pos(num: u64) -> u64 {
|
|
let mut pos = 0;
|
|
let mut bit = 1;
|
|
while num >= bit {
|
|
bit = bit << 1;
|
|
pos += 1;
|
|
}
|
|
pos
|
|
}
|
|
|
|
#[test]
|
|
fn check_insertion_to_pmmr_index() {
|
|
assert_eq!(pmmr::insertion_to_pmmr_index(1), 1);
|
|
assert_eq!(pmmr::insertion_to_pmmr_index(2), 2);
|
|
assert_eq!(pmmr::insertion_to_pmmr_index(3), 4);
|
|
assert_eq!(pmmr::insertion_to_pmmr_index(4), 5);
|
|
assert_eq!(pmmr::insertion_to_pmmr_index(5), 8);
|
|
assert_eq!(pmmr::insertion_to_pmmr_index(6), 9);
|
|
assert_eq!(pmmr::insertion_to_pmmr_index(7), 11);
|
|
assert_eq!(pmmr::insertion_to_pmmr_index(8), 12);
|
|
}
|
|
|
|
#[test]
|
|
fn check_elements_from_insertion_index() {
|
|
let mut ba = VecBackend::new();
|
|
let mut pmmr = PMMR::new(&mut ba);
|
|
for x in 1..1000 {
|
|
pmmr.push(TestElem([0, 0, 0, x])).unwrap();
|
|
}
|
|
// Normal case
|
|
let res = pmmr.elements_from_insertion_index(1, 100);
|
|
assert_eq!(res.0, 100);
|
|
assert_eq!(res.1.len(), 100);
|
|
assert_eq!(res.1[0].0[3], 1);
|
|
assert_eq!(res.1[99].0[3], 100);
|
|
|
|
// middle of pack
|
|
let res = pmmr.elements_from_insertion_index(351, 70);
|
|
assert_eq!(res.0, 420);
|
|
assert_eq!(res.1.len(), 70);
|
|
assert_eq!(res.1[0].0[3], 351);
|
|
assert_eq!(res.1[69].0[3], 420);
|
|
|
|
// past the end
|
|
let res = pmmr.elements_from_insertion_index(650, 1000);
|
|
assert_eq!(res.0, 999);
|
|
assert_eq!(res.1.len(), 350);
|
|
assert_eq!(res.1[0].0[3], 650);
|
|
assert_eq!(res.1[349].0[3], 999);
|
|
|
|
// pruning a few nodes should get consistent results
|
|
pmmr.prune(pmmr::insertion_to_pmmr_index(650)).unwrap();
|
|
pmmr.prune(pmmr::insertion_to_pmmr_index(651)).unwrap();
|
|
pmmr.prune(pmmr::insertion_to_pmmr_index(800)).unwrap();
|
|
pmmr.prune(pmmr::insertion_to_pmmr_index(900)).unwrap();
|
|
pmmr.prune(pmmr::insertion_to_pmmr_index(998)).unwrap();
|
|
let res = pmmr.elements_from_insertion_index(650, 1000);
|
|
assert_eq!(res.0, 999);
|
|
assert_eq!(res.1.len(), 345);
|
|
assert_eq!(res.1[0].0[3], 652);
|
|
assert_eq!(res.1[344].0[3], 999);
|
|
}
|