#![cfg(test)]
use alloc::vec::Vec;
use assert_matches::assert_matches;
use super::{Config, Result, test_utils::UNUSED_ENTRY_COUNT};
use crate::{
EMPTY_WORD, Map, Set, Word,
merkle::{
EmptySubtreeRoots,
smt::{
Backend, ForestInMemoryBackend, ForestOperation, LargeSmtForest, LargeSmtForestError,
RootInfo, Smt, SmtForestUpdateBatch, SmtUpdateBatch, TreeId, VersionId,
large_forest::{
LineageData,
history::{ChangedKeys, History, NodeChanges},
root::{LineageId, TreeEntry, TreeWithRoot},
test_utils::{FALLIBLE_READ_FAILURE_MESSAGE, FallibleEntriesBackend},
},
},
},
rand::test_utils::{ContinuousRng, rand_value},
};
type Forest = LargeSmtForest<ForestInMemoryBackend>;
#[test]
fn new() -> Result<()> {
let backend = ForestInMemoryBackend::new();
let forest = Forest::new(backend)?;
let config = forest.get_config();
assert_eq!(config.max_history_versions(), 10);
Ok(())
}
#[test]
fn with_config() -> Result<()> {
let backend = ForestInMemoryBackend::new();
let forest = Forest::with_config(backend, Config::default().with_max_history_versions(30))?;
let config = forest.get_config();
assert_eq!(config.max_history_versions(), 30);
Ok(())
}
#[test]
fn roots() -> Result<()> {
let backend = ForestInMemoryBackend::new();
let mut forest = Forest::new(backend)?;
let mut rng = ContinuousRng::new([0x96; 32]);
let version_1: VersionId = rng.value();
let lineage_1: LineageId = rng.value();
let lineage_2: LineageId = rng.value();
let lineage_3: LineageId = rng.value();
let root_1 = forest.add_lineage(lineage_1, version_1, SmtUpdateBatch::default())?;
assert_eq!(
root_1,
TreeWithRoot::new(lineage_1, version_1, *EmptySubtreeRoots::entry(64, 0))
);
let root_2 = forest.add_lineage(lineage_2, version_1, SmtUpdateBatch::default())?;
assert_eq!(
root_2,
TreeWithRoot::new(lineage_2, version_1, *EmptySubtreeRoots::entry(64, 0))
);
let root_3 = forest.add_lineage(lineage_3, version_1, SmtUpdateBatch::default())?;
assert_eq!(
root_3,
TreeWithRoot::new(lineage_3, version_1, *EmptySubtreeRoots::entry(64, 0))
);
let k1: Word = rng.value();
let v1: Word = rng.value();
let k2: Word = rng.value();
let v2: Word = rng.value();
let mut operations = SmtUpdateBatch::default();
operations.add_insert(k1, v1);
operations.add_insert(k2, v2);
let version_2: VersionId = version_1 + 1;
let root_4 = forest.update_tree(lineage_1, version_2, operations)?;
let roots = forest.roots().collect::<Vec<_>>();
assert_eq!(roots.len(), 4);
assert!(roots.contains(&root_1.into()));
assert!(roots.contains(&root_2.into()));
assert!(roots.contains(&root_3.into()));
assert!(roots.contains(&root_4.into()));
Ok(())
}
#[test]
fn latest_version() -> Result<()> {
let backend = ForestInMemoryBackend::new();
let mut forest = Forest::new(backend)?;
let mut rng = ContinuousRng::new([0x69; 32]);
let version_1: VersionId = rng.value();
let version_2: VersionId = version_1 + 1;
let version_3: VersionId = version_2 + 1;
let lineage_1: LineageId = rng.value();
let lineage_2: LineageId = rng.value();
let lineage_3: LineageId = rng.value();
let k1: Word = rng.value();
let v1: Word = rng.value();
let k2: Word = rng.value();
let v2: Word = rng.value();
let mut operations = SmtUpdateBatch::default();
operations.add_insert(k1, v1);
operations.add_insert(k2, v2);
forest.add_lineage(lineage_1, version_1, SmtUpdateBatch::default())?;
forest.add_lineage(lineage_2, version_1, SmtUpdateBatch::default())?;
forest.add_lineage(lineage_3, version_1, operations)?;
let k3: Word = rng.value();
let v3: Word = rng.value();
let mut operations = SmtUpdateBatch::default();
operations.add_insert(k3, v3);
forest.update_tree(lineage_1, version_2, operations)?;
let k4: Word = rng.value();
let v4: Word = rng.value();
let mut operations = SmtUpdateBatch::default();
operations.add_insert(k4, v4);
forest.update_tree(lineage_1, version_3, operations)?;
let k5: Word = rng.value();
let v5: Word = rng.value();
let mut operations = SmtUpdateBatch::default();
operations.add_insert(k5, v5);
forest.update_tree(lineage_3, version_3, operations)?;
assert_eq!(forest.latest_version(lineage_1).unwrap(), version_3);
assert_eq!(forest.latest_version(lineage_2).unwrap(), version_1);
assert_eq!(forest.latest_version(lineage_3).unwrap(), version_3);
let ne_lineage: LineageId = rng.value();
assert!(forest.latest_version(ne_lineage).is_none());
Ok(())
}
#[test]
fn lineage_roots() -> Result<()> {
let backend = ForestInMemoryBackend::new();
let mut forest = Forest::new(backend)?;
let mut rng = ContinuousRng::new([0x42; 32]);
let lineage: LineageId = rng.value();
let version_1: VersionId = rng.value();
let version_2 = version_1 + 1;
let version_3 = version_2 + 1;
let root_1 = forest.add_lineage(lineage, version_1, SmtUpdateBatch::default())?;
let k1: Word = rng.value();
let v1: Word = rng.value();
let mut operations = SmtUpdateBatch::default();
operations.add_insert(k1, v1);
let root_2 = forest.update_tree(lineage, version_2, operations)?;
let k2: Word = rng.value();
let v2: Word = rng.value();
let mut operations = SmtUpdateBatch::default();
operations.add_insert(k2, v2);
let root_3 = forest.update_tree(lineage, version_3, operations)?;
let lineage_roots = forest
.lineage_roots(lineage)
.expect("Existing lineage should have roots")
.collect::<Vec<_>>();
assert_eq!(lineage_roots.len(), 3);
assert_eq!(lineage_roots[0], root_3.root());
assert_eq!(lineage_roots[1], root_2.root());
assert_eq!(lineage_roots[2], root_1.root());
let ne_lineage: LineageId = rng.value();
assert!(forest.lineage_roots(ne_lineage).is_none());
Ok(())
}
#[test]
fn latest_root() -> Result<()> {
let backend = ForestInMemoryBackend::new();
let mut forest = Forest::new(backend)?;
let mut rng = ContinuousRng::new([0x97; 32]);
let lineage: LineageId = rng.value();
let version_1: VersionId = rng.value();
let version_2 = version_1 + 1;
let root_1 = forest.add_lineage(lineage, version_1, SmtUpdateBatch::default())?;
assert_eq!(forest.latest_root(lineage), Some(root_1.root()));
let k1: Word = rng.value();
let v1: Word = rng.value();
let mut operations = SmtUpdateBatch::default();
operations.add_insert(k1, v1);
let root_2 = forest.update_tree(lineage, version_2, operations)?;
assert_eq!(forest.latest_root(lineage), Some(root_2.root()));
let ne_lineage: LineageId = rng.value();
assert!(forest.latest_root(ne_lineage).is_none());
Ok(())
}
#[test]
fn tree_count() -> Result<()> {
let backend = ForestInMemoryBackend::new();
let mut forest = Forest::new(backend)?;
let mut rng = ContinuousRng::new([0x67; 32]);
assert_eq!(forest.tree_count(), forest.get_backend().trees()?.count());
let lineage_1: LineageId = rng.value();
let version_1: VersionId = rng.value();
let version_2 = version_1 + 1;
let version_3 = version_2 + 1;
forest.add_lineage(lineage_1, version_1, SmtUpdateBatch::default())?;
let k1: Word = rng.value();
let v1: Word = rng.value();
let mut operations = SmtUpdateBatch::default();
operations.add_insert(k1, v1);
forest.update_tree(lineage_1, version_2, operations)?;
let k2: Word = rng.value();
let v2: Word = rng.value();
let mut operations = SmtUpdateBatch::default();
operations.add_insert(k2, v2);
forest.update_tree(lineage_1, version_3, operations)?;
let lineage_2: LineageId = rng.value();
forest.add_lineage(lineage_2, version_1, SmtUpdateBatch::default())?;
assert_eq!(forest.tree_count(), 4);
Ok(())
}
#[test]
fn lineage_count() -> Result<()> {
let backend = ForestInMemoryBackend::new();
let mut forest = Forest::new(backend)?;
let mut rng = ContinuousRng::new([0x64; 32]);
assert_eq!(forest.lineage_count(), forest.get_backend().lineages()?.count());
let version: VersionId = rng.value();
let lineage_1: LineageId = rng.value();
forest.add_lineage(lineage_1, version, SmtUpdateBatch::default())?;
let lineage_2: LineageId = rng.value();
forest.add_lineage(lineage_2, version, SmtUpdateBatch::default())?;
let lineage_3: LineageId = rng.value();
forest.add_lineage(lineage_3, version, SmtUpdateBatch::default())?;
assert_eq!(forest.lineage_count(), 3);
let operations =
SmtUpdateBatch::new([ForestOperation::insert(rng.value(), rng.value())].into_iter());
forest.update_tree(lineage_1, version + 1, operations)?;
assert_eq!(forest.lineage_count(), 3);
Ok(())
}
#[test]
fn root_info() -> Result<()> {
let backend = ForestInMemoryBackend::new();
let mut forest = Forest::new(backend)?;
let mut rng = ContinuousRng::new([0x32; 32]);
let lineage_1: LineageId = rng.value();
let version_1: VersionId = rng.value();
let operations =
SmtUpdateBatch::new([ForestOperation::insert(rng.value(), rng.value())].into_iter());
let historical_root = forest.add_lineage(lineage_1, version_1, operations)?;
let version_2 = version_1 + 1;
let operations =
SmtUpdateBatch::new([ForestOperation::insert(rng.value(), rng.value())].into_iter());
let current_root = forest.update_tree(lineage_1, version_2, operations)?;
assert_eq!(
forest.root_info(TreeId::new(lineage_1, version_1)),
RootInfo::HistoricalVersion(historical_root.root())
);
assert_eq!(
forest.root_info(TreeId::new(lineage_1, version_2)),
RootInfo::LatestVersion(current_root.root())
);
let version_3 = version_2 + 1;
assert_eq!(forest.root_info(TreeId::new(lineage_1, version_3)), RootInfo::Missing);
let lineage_2: LineageId = rng.value();
assert_eq!(forest.root_info(TreeId::new(lineage_2, version_1)), RootInfo::Missing);
Ok(())
}
#[test]
fn open() -> Result<()> {
let backend = ForestInMemoryBackend::new();
let mut forest = Forest::new(backend)?;
let mut rng = ContinuousRng::new([0x08; 32]);
let missing_lineage: LineageId = rng.value();
let missing_version: VersionId = rng.value();
let missing_key: Word = rng.value();
let result = forest.open(TreeId::new(missing_lineage, missing_version), missing_key);
assert!(result.is_err());
assert_matches!(result.unwrap_err(), LargeSmtForestError::UnknownLineage(l) if l == missing_lineage);
let lineage_1: LineageId = rng.value();
let version_1: VersionId = rng.value();
let key_1: Word = rng.value();
let value_1_v1: Word = rng.value();
let key_2: Word = rng.value();
let value_2_v1: Word = rng.value();
forest.add_lineage(
lineage_1,
version_1,
SmtUpdateBatch::new(
[
ForestOperation::insert(key_1, value_1_v1),
ForestOperation::insert(key_2, value_2_v1),
]
.into_iter(),
),
)?;
let missing_tree = TreeId::new(lineage_1, missing_version);
let result = forest.open(missing_tree, missing_key);
assert!(result.is_err());
assert_matches!(result.unwrap_err(), LargeSmtForestError::UnknownTree(t) if t == missing_tree);
let too_new_version = version_1 + 1;
let too_new_tree = TreeId::new(lineage_1, too_new_version);
let result = forest.open(too_new_tree, missing_key);
assert!(result.is_err());
assert_matches!(result.unwrap_err(), LargeSmtForestError::UnknownTree(t) if t == too_new_tree);
let mut tree_v1 = Smt::new();
tree_v1.insert(key_1, value_1_v1)?;
tree_v1.insert(key_2, value_2_v1)?;
let random_key: Word = rng.value();
let forest_opening = forest.open(TreeId::new(lineage_1, version_1), random_key)?;
let tree_v1_opening = tree_v1.open(&random_key);
assert_eq!(forest_opening, tree_v1_opening);
let version_2: VersionId = rng.value();
let value_1_v2: Word = rng.value();
let key_3: Word = rng.value();
let value_3_v1: Word = rng.value();
forest.update_tree(
lineage_1,
version_2,
SmtUpdateBatch::new(
[
ForestOperation::insert(key_1, value_1_v2),
ForestOperation::insert(key_3, value_3_v1),
ForestOperation::remove(key_2),
]
.into_iter(),
),
)?;
let mut tree_v2 = tree_v1.clone();
tree_v2.insert(key_1, value_1_v2)?;
tree_v2.insert(key_3, value_3_v1)?;
tree_v2.insert(key_2, EMPTY_WORD)?;
let random_key: Word = rng.value();
let forest_opening = forest.open(TreeId::new(lineage_1, version_2), random_key)?;
let tree_v2_opening = tree_v2.open(&random_key);
assert_eq!(forest_opening, tree_v2_opening);
let forest_opening = forest.open(TreeId::new(lineage_1, version_1), random_key)?;
let tree_v1_opening = tree_v1.open(&random_key);
assert_eq!(forest_opening, tree_v1_opening);
Ok(())
}
#[test]
fn get() -> Result<()> {
let backend = ForestInMemoryBackend::new();
let mut forest = Forest::new(backend)?;
let mut rng = ContinuousRng::new([0x12; 32]);
let missing_lineage: LineageId = rng.value();
let missing_version: VersionId = rng.value();
let missing_key: Word = rng.value();
let result = forest.get(TreeId::new(missing_lineage, missing_version), missing_key);
assert!(result.is_err());
assert_matches!(result.unwrap_err(), LargeSmtForestError::UnknownLineage(l) if l == missing_lineage);
let lineage_1: LineageId = rng.value();
let version_1: VersionId = rng.value();
let key_1: Word = rng.value();
let value_1_v1: Word = rng.value();
let key_2: Word = rng.value();
let value_2_v1: Word = rng.value();
forest.add_lineage(
lineage_1,
version_1,
SmtUpdateBatch::new(
[
ForestOperation::insert(key_1, value_1_v1),
ForestOperation::insert(key_2, value_2_v1),
]
.into_iter(),
),
)?;
let missing_tree = TreeId::new(lineage_1, missing_version);
let result = forest.get(missing_tree, missing_key);
assert!(result.is_err());
assert_matches!(result.unwrap_err(), LargeSmtForestError::UnknownTree(t) if t == missing_tree);
let too_new_version = version_1 + 1;
let too_new_tree = TreeId::new(lineage_1, too_new_version);
let result = forest.get(too_new_tree, missing_key);
assert!(result.is_err());
assert_matches!(result.unwrap_err(), LargeSmtForestError::UnknownTree(t) if t == too_new_tree);
let tree_v1 = TreeId::new(lineage_1, version_1);
let non_inserted_key: Word = rng.value();
assert!(forest.get(tree_v1, non_inserted_key)?.is_none());
assert_eq!(forest.get(tree_v1, key_1)?, Some(value_1_v1));
assert_eq!(forest.get(tree_v1, key_2)?, Some(value_2_v1));
let version_2: VersionId = version_1 + 1;
let value_1_v2: Word = rng.value();
let key_3: Word = rng.value();
let value_3_v1: Word = rng.value();
forest.update_tree(
lineage_1,
version_2,
SmtUpdateBatch::new(
[
ForestOperation::insert(key_1, value_1_v2),
ForestOperation::insert(key_3, value_3_v1),
]
.into_iter(),
),
)?;
let tree_v2 = TreeId::new(lineage_1, version_2);
assert_eq!(forest.get(tree_v2, key_1)?, Some(value_1_v2));
assert_eq!(forest.get(tree_v2, key_2)?, Some(value_2_v1));
assert_eq!(forest.get(tree_v2, key_3)?, Some(value_3_v1));
assert_eq!(forest.get(tree_v1, key_1)?, Some(value_1_v1));
assert_eq!(forest.get(tree_v1, key_2)?, Some(value_2_v1));
assert!(forest.get(tree_v1, key_3)?.is_none());
Ok(())
}
#[test]
fn entry_count() -> Result<()> {
let backend = ForestInMemoryBackend::new();
let mut forest = Forest::new(backend)?;
let mut rng = ContinuousRng::new([0x22; 32]);
let lineage_1: LineageId = rng.value();
let version_1: VersionId = rng.value();
let key_1: Word = rng.value();
let value_1_v1: Word = rng.value();
let key_2: Word = rng.value();
let value_2_v1: Word = rng.value();
let mut key_3: Word = rng.value();
key_3[3] = key_1[3];
let value_3_v1: Word = rng.value();
let mut operations = SmtUpdateBatch::empty();
operations.add_insert(key_1, value_1_v1);
operations.add_insert(key_2, value_2_v1);
operations.add_insert(key_3, value_3_v1);
forest.add_lineage(lineage_1, version_1, operations)?;
let version_2: VersionId = version_1 + 1;
let value_1_v2: Word = rng.value();
let mut key_4: Word = rng.value();
key_4[3] = key_2[3];
let value_4_v1: Word = rng.value();
let mut operations = SmtUpdateBatch::empty();
operations.add_remove(key_3);
operations.add_insert(key_1, value_1_v2);
operations.add_insert(key_4, value_4_v1);
forest.update_tree(lineage_1, version_2, operations)?;
let ne_lineage: LineageId = rng.value();
match forest.entry_count(TreeId::new(ne_lineage, version_1)) {
Err(e) => assert_matches!(e, LargeSmtForestError::UnknownLineage(l) if l == ne_lineage),
Ok(_) => panic!("Result was not an error"),
};
let tree = TreeId::new(lineage_1, version_1 - 1);
match forest.entry_count(tree) {
Err(e) => assert_matches!(e, LargeSmtForestError::UnknownTree(t) if t == tree),
Ok(_) => panic!("Result was not an error"),
};
let too_new_version = version_2 + 1;
let too_new_tree = TreeId::new(lineage_1, too_new_version);
let result = forest.entry_count(too_new_tree);
assert!(result.is_err());
assert_matches!(result.unwrap_err(), LargeSmtForestError::UnknownTree(t) if t == too_new_tree);
assert_eq!(forest.entry_count(TreeId::new(lineage_1, version_1))?, 3);
assert_eq!(forest.entry_count(TreeId::new(lineage_1, version_2))?, 3);
Ok(())
}
#[test]
fn entry_count_historical_across_versions() -> Result<()> {
let backend = ForestInMemoryBackend::new();
let mut forest = Forest::new(backend)?;
let mut rng = ContinuousRng::new([0x23; 32]);
let lineage: LineageId = rng.value();
let version_1: VersionId = rng.value();
let key_1: Word = rng.value();
let value_1: Word = rng.value();
let key_2: Word = rng.value();
let value_2: Word = rng.value();
let mut ops = SmtUpdateBatch::empty();
ops.add_insert(key_1, value_1);
ops.add_insert(key_2, value_2);
forest.add_lineage(lineage, version_1, ops)?;
let version_2 = version_1 + 1;
let key_3: Word = rng.value();
let value_3: Word = rng.value();
let mut ops = SmtUpdateBatch::empty();
ops.add_insert(key_3, value_3);
forest.update_tree(lineage, version_2, ops)?;
let version_3 = version_2 + 1;
let mut ops = SmtUpdateBatch::empty();
ops.add_remove(key_1);
forest.update_tree(lineage, version_3, ops)?;
assert_eq!(forest.entry_count(TreeId::new(lineage, version_1))?, 2);
assert_eq!(forest.entry_count(TreeId::new(lineage, version_2))?, 3);
assert_eq!(forest.entry_count(TreeId::new(lineage, version_3))?, 2);
Ok(())
}
#[test]
fn entry_count_historical_across_versions_via_update_forest() -> Result<()> {
let backend = ForestInMemoryBackend::new();
let mut forest = Forest::new(backend)?;
let mut rng = ContinuousRng::new([0x24; 32]);
let lineage_a: LineageId = rng.value();
let lineage_b: LineageId = rng.value();
let version_1: VersionId = rng.value();
let a_key_1: Word = rng.value();
let a_value_1: Word = rng.value();
let a_key_2: Word = rng.value();
let a_value_2: Word = rng.value();
let b_key_1: Word = rng.value();
let b_value_1: Word = rng.value();
let mut ops_a = SmtUpdateBatch::empty();
ops_a.add_insert(a_key_1, a_value_1);
ops_a.add_insert(a_key_2, a_value_2);
forest.add_lineage(lineage_a, version_1, ops_a)?;
let mut ops_b = SmtUpdateBatch::empty();
ops_b.add_insert(b_key_1, b_value_1);
forest.add_lineage(lineage_b, version_1, ops_b)?;
let version_2 = version_1 + 1;
let a_key_3: Word = rng.value();
let a_value_3: Word = rng.value();
let b_key_2: Word = rng.value();
let b_value_2: Word = rng.value();
let b_key_3: Word = rng.value();
let b_value_3: Word = rng.value();
let mut batch = SmtForestUpdateBatch::empty();
batch.operations(lineage_a).add_insert(a_key_3, a_value_3);
batch.operations(lineage_b).add_insert(b_key_2, b_value_2);
batch.operations(lineage_b).add_insert(b_key_3, b_value_3);
forest.update_forest(version_2, batch)?;
let version_3 = version_2 + 1;
let b_key_4: Word = rng.value();
let b_value_4: Word = rng.value();
let mut batch = SmtForestUpdateBatch::empty();
batch.operations(lineage_a).add_remove(a_key_1);
batch.operations(lineage_b).add_insert(b_key_4, b_value_4);
forest.update_forest(version_3, batch)?;
assert_eq!(forest.entry_count(TreeId::new(lineage_a, version_1))?, 2);
assert_eq!(forest.entry_count(TreeId::new(lineage_a, version_2))?, 3);
assert_eq!(forest.entry_count(TreeId::new(lineage_a, version_3))?, 2);
assert_eq!(forest.entry_count(TreeId::new(lineage_b, version_1))?, 1);
assert_eq!(forest.entry_count(TreeId::new(lineage_b, version_2))?, 3);
assert_eq!(forest.entry_count(TreeId::new(lineage_b, version_3))?, 4);
Ok(())
}
#[test]
fn entries() -> Result<()> {
let backend = ForestInMemoryBackend::new();
let mut forest = Forest::new(backend)?;
let mut rng = ContinuousRng::new([0x47; 32]);
let lineage_1: LineageId = rng.value();
let version_1: VersionId = rng.value();
let key_1: Word = rng.value();
let value_1_v1: Word = rng.value();
let key_2: Word = rng.value();
let value_2_v1: Word = rng.value();
let mut key_3: Word = rng.value();
key_3[3] = key_1[3];
let value_3_v1: Word = rng.value();
let mut operations = SmtUpdateBatch::empty();
operations.add_insert(key_1, value_1_v1);
operations.add_insert(key_2, value_2_v1);
operations.add_insert(key_3, value_3_v1);
forest.add_lineage(lineage_1, version_1, operations)?;
let version_2: VersionId = version_1 + 1;
let value_1_v2: Word = rng.value();
let mut key_4: Word = rng.value();
key_4[3] = key_2[3];
let value_4_v1: Word = rng.value();
let mut operations = SmtUpdateBatch::empty();
operations.add_remove(key_3);
operations.add_insert(key_1, value_1_v2);
operations.add_insert(key_4, value_4_v1);
forest.update_tree(lineage_1, version_2, operations)?;
let ne_lineage: LineageId = rng.value();
match forest.entries(TreeId::new(ne_lineage, version_1)) {
Err(e) => assert_matches!(e, LargeSmtForestError::UnknownLineage(l) if l == ne_lineage),
Ok(_) => panic!("Result was not an error"),
};
let tree = TreeId::new(lineage_1, version_1 - 1);
match forest.entries(tree) {
Err(e) => assert_matches!(e, LargeSmtForestError::UnknownTree(t) if t == tree),
Ok(_) => panic!("Result was not an error"),
};
let too_new_version = version_2 + 1;
let too_new_tree = TreeId::new(lineage_1, too_new_version);
match forest.entries(too_new_tree) {
Err(e) => assert_matches!(e, LargeSmtForestError::UnknownTree(t) if t == too_new_tree),
Ok(_) => panic!("Result was not an error"),
}
let current_tree = TreeId::new(lineage_1, version_2);
let current_entries = forest.entries(current_tree)?.collect::<Result<Vec<_>>>()?;
assert_eq!(current_entries.len(), 3);
assert!(current_entries.contains(&TreeEntry { key: key_1, value: value_1_v2 }));
assert!(current_entries.contains(&TreeEntry { key: key_2, value: value_2_v1 }));
assert!(current_entries.contains(&TreeEntry { key: key_4, value: value_4_v1 }));
assert!(!current_entries.contains(&TreeEntry { key: key_3, value: value_3_v1 }));
let historical_tree = TreeId::new(lineage_1, version_1);
let historical_entries = forest.entries(historical_tree)?.collect::<Result<Vec<_>>>()?;
assert_eq!(historical_entries.len(), 3);
assert!(historical_entries.contains(&TreeEntry { key: key_1, value: value_1_v1 }));
assert!(historical_entries.contains(&TreeEntry { key: key_2, value: value_2_v1 }));
assert!(historical_entries.contains(&TreeEntry { key: key_3, value: value_3_v1 }));
assert!(!historical_entries.contains(&TreeEntry { key: key_4, value: value_4_v1 }));
Ok(())
}
#[test]
fn forest_overlays_correctly() -> Result<()> {
let backend = ForestInMemoryBackend::new();
let mut forest = LargeSmtForest::new(backend)?;
let key_1 = Word::parse("0x42").unwrap();
let value_1 = Word::parse("0x80").unwrap();
let key_2 = Word::parse("0xAB").unwrap();
let value_2 = Word::parse("0xCD").unwrap();
let mut operations = SmtUpdateBatch::empty();
operations.add_insert(key_1, value_1);
operations.add_insert(key_2, value_2);
let lineage = LineageId::new([0x42; 32]);
let version_1 = 1;
forest.add_lineage(lineage, version_1, operations)?;
let key_3 = Word::parse("0x67").unwrap();
let value_3 = Word::parse("0x96").unwrap();
let mut operations = SmtUpdateBatch::empty();
operations.add_insert(key_3, value_3);
operations.add_remove(key_1);
let version_2 = version_1 + 1;
forest.update_tree(lineage, version_2, operations)?;
let old_tree = TreeId::new(lineage, version_1);
let current_tree = TreeId::new(lineage, version_2);
assert!(forest.open(old_tree, key_1).is_ok());
assert!(forest.open(current_tree, key_3).is_ok());
assert_eq!(forest.get(old_tree, key_1)?, Some(value_1));
assert_eq!(forest.get(current_tree, key_3)?, Some(value_3));
assert!(forest.get(current_tree, key_1)?.is_none());
let entries_old = forest.entries(old_tree)?.collect::<Result<Vec<_>>>()?;
let entries_current = forest.entries(current_tree)?.collect::<Result<Vec<_>>>()?;
assert!(entries_old.contains(&TreeEntry { key: key_1, value: value_1 }));
assert!(entries_old.contains(&TreeEntry { key: key_2, value: value_2 }));
assert!(!entries_old.contains(&TreeEntry { key: key_3, value: value_3 }));
assert!(!entries_current.contains(&TreeEntry { key: key_1, value: value_1 }));
assert!(entries_current.contains(&TreeEntry { key: key_2, value: value_2 }));
assert!(entries_current.contains(&TreeEntry { key: key_3, value: value_3 }));
Ok(())
}
#[test]
fn entries_never_returns_empty_entry() -> Result<()> {
let backend = ForestInMemoryBackend::new();
let mut forest = Forest::new(backend)?;
let mut rng = ContinuousRng::new([0x44; 32]);
let lineage_1: LineageId = rng.value();
let version_1: VersionId = rng.value();
forest.add_lineage(lineage_1, version_1, SmtUpdateBatch::empty())?;
let version_2 = version_1 + 1;
let key_1: Word = rng.value();
let value_1: Word = rng.value();
let key_2: Word = rng.value();
let value_2: Word = rng.value();
let operations = SmtUpdateBatch::new(
[ForestOperation::insert(key_1, value_1), ForestOperation::insert(key_2, value_2)]
.into_iter(),
);
forest.update_tree(lineage_1, version_2, operations)?;
let historical_tree = TreeId::new(lineage_1, version_1);
assert_eq!(forest.entries(historical_tree)?.count(), 0);
let lineage_2: LineageId = rng.value();
let key_1 = Word::from([1u32, 0, 0, 42]);
let value_1: Word = rng.value();
forest.add_lineage(
lineage_2,
version_1,
SmtUpdateBatch::new([ForestOperation::insert(key_1, value_1)].into_iter()),
)?;
let key_2 = Word::from([2u32, 0, 0, 43]);
let value_2: Word = rng.value();
forest.update_tree(
lineage_2,
version_2,
SmtUpdateBatch::new([ForestOperation::insert(key_2, value_2)].into_iter()),
)?;
let historical_tree = TreeId::new(lineage_2, version_1);
let entries = forest.entries(historical_tree)?.collect::<Result<Vec<_>>>()?;
assert_eq!(entries.len(), 1);
assert!(entries.iter().all(|e| e.value != EMPTY_WORD));
let lineage_3: LineageId = rng.value();
let key_1 = Word::from([1u32, 0, 0, 42]);
let value_1: Word = rng.value();
forest.add_lineage(
lineage_3,
version_1,
SmtUpdateBatch::new([ForestOperation::insert(key_1, value_1)].into_iter()),
)?;
let key_2 = Word::from([2u32, 0, 0, 42]);
let value_2: Word = rng.value();
forest.update_tree(
lineage_3,
version_2,
SmtUpdateBatch::new([ForestOperation::insert(key_2, value_2)].into_iter()),
)?;
let historical_tree = TreeId::new(lineage_3, version_1);
let entries = forest.entries(historical_tree)?.collect::<Result<Vec<_>>>()?;
assert_eq!(entries.len(), 1);
assert!(entries.iter().all(|e| e.value != EMPTY_WORD));
Ok(())
}
#[test]
fn entries_history_empty_values_do_not_reorder() -> Result<()> {
let backend = ForestInMemoryBackend::new();
let mut forest = Forest::new(backend)?;
let mut rng = ContinuousRng::new([0x55; 32]);
let lineage: LineageId = rng.value();
let version_1: VersionId = rng.value();
let key_a = Word::from([2u32, 0, 0, 42]);
let value_a: Word = rng.value();
let key_c = Word::from([3u32, 0, 0, 42]);
let value_c_v1: Word = rng.value();
forest.add_lineage(
lineage,
version_1,
SmtUpdateBatch::new(
[
ForestOperation::insert(key_a, value_a),
ForestOperation::insert(key_c, value_c_v1),
]
.into_iter(),
),
)?;
let version_2 = version_1 + 1;
let key_b = Word::from([1u32, 0, 0, 42]);
let value_b: Word = rng.value();
let value_c_v2: Word = rng.value();
forest.update_tree(
lineage,
version_2,
SmtUpdateBatch::new(
[
ForestOperation::insert(key_b, value_b),
ForestOperation::insert(key_c, value_c_v2),
]
.into_iter(),
),
)?;
let historical_tree = TreeId::new(lineage, version_1);
let entries = forest.entries(historical_tree)?.collect::<Result<Vec<_>>>()?;
assert_eq!(entries.len(), 2);
assert_eq!(entries[0], TreeEntry { key: key_a, value: value_a });
assert_eq!(entries[1], TreeEntry { key: key_c, value: value_c_v1 });
assert!(entries.iter().all(|e| e.value != EMPTY_WORD));
Ok(())
}
#[test]
fn add_lineage() -> Result<()> {
let backend = ForestInMemoryBackend::new();
let mut forest = Forest::new(backend)?;
let mut rng = ContinuousRng::new([0x42; 32]);
let lineage: LineageId = rng.value();
let version: VersionId = rng.value();
let result = forest.add_lineage(lineage, version, SmtUpdateBatch::default());
assert!(result.is_ok());
let tree = Smt::new();
let result = result?;
assert_eq!(result.root(), tree.root());
assert_eq!(result.lineage(), lineage);
assert_eq!(result.version(), version);
assert!(!forest.get_non_empty_histories().contains(&lineage));
let result = forest.add_lineage(lineage, version, SmtUpdateBatch::default());
assert!(result.is_err());
assert_matches!(result.unwrap_err(), LargeSmtForestError::DuplicateLineage(l) if l == lineage);
Ok(())
}
#[test]
fn update_tree() -> Result<()> {
let backend = ForestInMemoryBackend::new();
let mut forest = Forest::new(backend)?;
let mut rng = ContinuousRng::new([0x69; 32]);
let lineage_1: LineageId = rng.value();
let version_1: VersionId = rng.value();
let key_1: Word = rng.value();
let value_1: Word = rng.value();
let mut operations = SmtUpdateBatch::default();
operations.add_insert(key_1, value_1);
let result = forest.add_lineage(lineage_1, version_1, operations)?;
let mut tree = Smt::new();
tree.insert(key_1, value_1)?;
assert_eq!(result.root(), tree.root());
assert!(!forest.get_non_empty_histories().contains(&lineage_1));
let unknown_lineage: LineageId = rng.value();
let result = forest.update_tree(unknown_lineage, version_1, SmtUpdateBatch::default());
assert!(result.is_err());
assert_matches!(
result.unwrap_err(),
LargeSmtForestError::UnknownLineage(l) if l == unknown_lineage
);
let older_version = version_1 - 1;
let result = forest.update_tree(lineage_1, older_version, SmtUpdateBatch::default());
assert!(result.is_err());
assert_matches!(
result.unwrap_err(),
LargeSmtForestError::BadVersion { provided, latest }
if provided == older_version && latest == version_1
);
let key_2: Word = rng.value();
let value_2: Word = rng.value();
let key_3: Word = rng.value();
let value_3: Word = rng.value();
let mut operations = SmtUpdateBatch::default();
operations.add_insert(key_2, value_2);
operations.add_insert(key_3, value_3);
operations.add_remove(key_1);
let version_2: VersionId = rng.value();
let result = forest.update_tree(lineage_1, version_2, operations)?;
let mutations =
tree.compute_mutations(vec![(key_1, EMPTY_WORD), (key_2, value_2), (key_3, value_3)])?;
tree.apply_mutations(mutations)?;
assert_eq!(result.root(), tree.root());
let history = forest.get_history(lineage_1);
assert_eq!(history.num_versions(), 1);
let view = history.get_view_at(version_1)?;
assert_eq!(view.value(&key_1), Some(value_1));
assert_eq!(view.value(&key_2), Some(EMPTY_WORD));
assert_eq!(view.value(&key_3), Some(EMPTY_WORD));
assert!(forest.get_non_empty_histories().contains(&lineage_1));
assert_eq!(forest.lineage_roots(lineage_1).unwrap().count(), 2);
let empty_ops = SmtUpdateBatch::default();
let version_3 = version_2 + 1;
forest.update_tree(lineage_1, version_3, empty_ops)?;
assert_eq!(forest.lineage_roots(lineage_1).unwrap().count(), 2);
let history = forest.get_history(lineage_1);
assert_eq!(history.num_versions(), 1);
Ok(())
}
#[test]
fn add_lineages() -> Result<()> {
let backend = ForestInMemoryBackend::new();
let mut forest = Forest::new(backend)?;
let mut rng = ContinuousRng::new([0xa1; 32]);
let version: VersionId = rng.value();
let empty_batch = SmtForestUpdateBatch::empty();
let results = forest.add_lineages(version, empty_batch)?;
assert!(results.is_empty());
let lineage_1: LineageId = rng.value();
let lineage_2: LineageId = rng.value();
let lineage_3: LineageId = rng.value();
let l1_key: Word = rng.value();
let l1_value: Word = rng.value();
let l2_key: Word = rng.value();
let l2_value: Word = rng.value();
let mut batch = SmtForestUpdateBatch::empty();
batch.operations(lineage_1).add_insert(l1_key, l1_value);
batch.operations(lineage_2).add_insert(l2_key, l2_value);
batch.operations(lineage_3);
let results = forest.add_lineages(version, batch)?;
assert_eq!(results.len(), 3);
let mut tree_1 = Smt::new();
tree_1.insert(l1_key, l1_value)?;
let mut tree_2 = Smt::new();
tree_2.insert(l2_key, l2_value)?;
let tree_3 = Smt::new();
assert!(
results.iter().any(|r| r.lineage() == lineage_1
&& r.root() == tree_1.root()
&& r.version() == version)
);
assert!(
results.iter().any(|r| r.lineage() == lineage_2
&& r.root() == tree_2.root()
&& r.version() == version)
);
assert!(
results.iter().any(|r| r.lineage() == lineage_3
&& r.root() == tree_3.root()
&& r.version() == version)
);
assert_eq!(
forest.root_info(TreeId::new(lineage_1, version)),
RootInfo::LatestVersion(tree_1.root())
);
assert_eq!(
forest.root_info(TreeId::new(lineage_2, version)),
RootInfo::LatestVersion(tree_2.root())
);
assert_eq!(
forest.root_info(TreeId::new(lineage_3, version)),
RootInfo::LatestVersion(tree_3.root())
);
assert!(!forest.get_non_empty_histories().contains(&lineage_1));
assert!(!forest.get_non_empty_histories().contains(&lineage_2));
assert!(!forest.get_non_empty_histories().contains(&lineage_3));
let lineage_4: LineageId = rng.value();
let mut dup_batch = SmtForestUpdateBatch::empty();
dup_batch.operations(lineage_1); dup_batch.operations(lineage_4);
let result = forest.add_lineages(version, dup_batch);
assert!(result.is_err());
assert_matches!(
result.unwrap_err(),
LargeSmtForestError::DuplicateLineage(l) if l == lineage_1
);
assert_eq!(forest.root_info(TreeId::new(lineage_4, version)), RootInfo::Missing);
Ok(())
}
#[test]
fn update_forest() -> Result<()> {
let backend = ForestInMemoryBackend::new();
let mut forest = Forest::new(backend)?;
let mut rng = ContinuousRng::new([0x69; 32]);
let version_1: VersionId = rng.value();
let lineage_1: LineageId = rng.value();
let lineage_2: LineageId = rng.value();
let lineage_3: LineageId = rng.value();
let lineage_4: LineageId = rng.value();
let l1_r1 = forest.add_lineage(lineage_1, version_1, SmtUpdateBatch::default())?;
let l2_r1 = forest.add_lineage(lineage_2, version_1, SmtUpdateBatch::default())?;
let l3_r1 = forest.add_lineage(lineage_3, version_1, SmtUpdateBatch::default())?;
let l4_r1 = forest.add_lineage(lineage_4, version_1, SmtUpdateBatch::default())?;
let l1_key_1: Word = rng.value();
let l1_value_1: Word = rng.value();
let l2_key_1: Word = rng.value();
let l2_value_1: Word = rng.value();
let l3_key_1: Word = rng.value();
let l3_value_1: Word = rng.value();
let l4_key_1: Word = rng.value();
let l4_value_1: Word = rng.value();
let ne_lineage: LineageId = rng.value();
let version_bad = version_1 - 1;
let version_2 = version_1 + 1;
let mut operations_ne_lineage = SmtForestUpdateBatch::empty();
operations_ne_lineage.operations(lineage_1).add_insert(l1_key_1, l1_value_1);
operations_ne_lineage.operations(lineage_2).add_insert(l2_key_1, l2_value_1);
operations_ne_lineage.operations(lineage_3).add_insert(l3_key_1, l3_value_1);
operations_ne_lineage.operations(lineage_4).add_insert(l4_key_1, l4_value_1);
let operations_basic = operations_ne_lineage.clone();
operations_ne_lineage.operations(ne_lineage);
let result = forest.update_forest(version_2, operations_ne_lineage);
assert!(result.is_err());
assert_matches!(result.unwrap_err(), LargeSmtForestError::UnknownLineage(l) if l == ne_lineage);
assert_eq!(
forest.root_info(TreeId::new(lineage_1, version_1)),
RootInfo::LatestVersion(l1_r1.root())
);
assert_eq!(
forest.root_info(TreeId::new(lineage_2, version_1)),
RootInfo::LatestVersion(l2_r1.root())
);
assert_eq!(
forest.root_info(TreeId::new(lineage_3, version_1)),
RootInfo::LatestVersion(l3_r1.root())
);
assert_eq!(
forest.root_info(TreeId::new(lineage_4, version_1)),
RootInfo::LatestVersion(l4_r1.root())
);
let result = forest.update_forest(version_bad, operations_basic.clone());
assert!(result.is_err());
assert_matches!(
result.unwrap_err(),
LargeSmtForestError::BadVersion { provided, latest }
if provided == version_bad && latest == version_1
);
assert_eq!(
forest.root_info(TreeId::new(lineage_1, version_1)),
RootInfo::LatestVersion(l1_r1.root())
);
assert_eq!(
forest.root_info(TreeId::new(lineage_2, version_1)),
RootInfo::LatestVersion(l2_r1.root())
);
assert_eq!(
forest.root_info(TreeId::new(lineage_3, version_1)),
RootInfo::LatestVersion(l3_r1.root())
);
assert_eq!(
forest.root_info(TreeId::new(lineage_4, version_1)),
RootInfo::LatestVersion(l4_r1.root())
);
let roots = forest.update_forest(version_2, operations_basic)?;
assert_eq!(roots.len(), 4);
let mut tree_1 = Smt::new();
tree_1.insert(l1_key_1, l1_value_1)?;
let mut tree_2 = Smt::new();
tree_2.insert(l2_key_1, l2_value_1)?;
let mut tree_3 = Smt::new();
tree_3.insert(l3_key_1, l3_value_1)?;
let mut tree_4 = Smt::new();
tree_4.insert(l4_key_1, l4_value_1)?;
assert!(roots.iter().any(|e| e.root() == tree_1.root()
&& e.version() == version_2
&& e.lineage() == lineage_1));
assert!(roots.iter().any(|e| e.root() == tree_2.root()
&& e.version() == version_2
&& e.lineage() == lineage_2));
assert!(roots.iter().any(|e| e.root() == tree_3.root()
&& e.version() == version_2
&& e.lineage() == lineage_3));
assert!(roots.iter().any(|e| e.root() == tree_4.root()
&& e.version() == version_2
&& e.lineage() == lineage_4));
assert!(forest.get_non_empty_histories().contains(&lineage_1));
assert!(forest.get_non_empty_histories().contains(&lineage_2));
assert!(forest.get_non_empty_histories().contains(&lineage_3));
assert!(forest.get_non_empty_histories().contains(&lineage_4));
let version_3 = version_2 + 1;
let key_5: Word = rng.value();
let value_5: Word = rng.value();
let mut operations_with_nop = SmtForestUpdateBatch::empty();
operations_with_nop.operations(lineage_1).add_insert(l1_key_1, l1_value_1);
operations_with_nop.operations(lineage_2);
operations_with_nop.operations(lineage_3).add_insert(key_5, value_5);
assert_eq!(forest.lineage_roots(lineage_1).unwrap().count(), 2);
assert_eq!(forest.lineage_roots(lineage_2).unwrap().count(), 2);
assert_eq!(forest.lineage_roots(lineage_3).unwrap().count(), 2);
assert_eq!(forest.lineage_roots(lineage_4).unwrap().count(), 2);
let roots = forest.update_forest(version_3, operations_with_nop)?;
assert_eq!(roots.len(), 3);
assert_eq!(forest.lineage_roots(lineage_1).unwrap().count(), 2);
assert_eq!(forest.lineage_roots(lineage_2).unwrap().count(), 2);
assert_eq!(forest.lineage_roots(lineage_3).unwrap().count(), 3);
assert_eq!(forest.lineage_roots(lineage_4).unwrap().count(), 2);
Ok(())
}
#[test]
fn truncate_removes_emptied_lineages_from_non_empty_histories() {
let lineage: LineageId = rand_value();
let root: Word = rand_value();
let mut history = History::empty(4);
let nodes = NodeChanges::default();
let changed_keys = ChangedKeys::default();
history
.add_version(rand_value(), 5, nodes, changed_keys, UNUSED_ENTRY_COUNT)
.unwrap();
assert_eq!(history.num_versions(), 1);
let lineage_data = LineageData {
history,
latest_version: 10,
latest_root: root,
};
let mut lineage_map = Map::default();
lineage_map.insert(lineage, lineage_data);
let mut non_empty = Set::default();
non_empty.insert(lineage);
let mut forest = LargeSmtForest {
config: Config::default(),
backend: ForestInMemoryBackend::new(),
lineage_data: lineage_map,
non_empty_histories: non_empty,
};
assert!(forest.non_empty_histories.contains(&lineage));
forest.truncate(10);
assert!(
!forest.non_empty_histories.contains(&lineage),
"emptied lineage must be removed from non_empty_histories after truncation"
);
}
#[test]
fn truncate_retains_non_empty_lineages_in_non_empty_histories() {
let lineage: LineageId = rand_value();
let root: Word = rand_value();
let mut history = History::empty(4);
let nodes = NodeChanges::default();
let changed_keys = ChangedKeys::default();
history
.add_version(rand_value(), 5, nodes.clone(), changed_keys.clone(), UNUSED_ENTRY_COUNT)
.unwrap();
history
.add_version(rand_value(), 8, nodes, changed_keys, UNUSED_ENTRY_COUNT)
.unwrap();
assert_eq!(history.num_versions(), 2);
let lineage_data = LineageData {
history,
latest_version: 15,
latest_root: root,
};
let mut lineage_map = Map::new();
lineage_map.insert(lineage, lineage_data);
let mut non_empty = Set::default();
non_empty.insert(lineage);
let mut forest = LargeSmtForest {
config: Config::default(),
backend: ForestInMemoryBackend::new(),
lineage_data: lineage_map,
non_empty_histories: non_empty,
};
forest.truncate(7);
assert!(
forest.non_empty_histories.contains(&lineage),
"lineage with remaining history must stay in non_empty_histories"
);
}
#[test]
fn entries_with_fallible_backend() -> Result<()> {
let backend = FallibleEntriesBackend::new();
let mut forest = LargeSmtForest::new(backend)?;
let mut rng = ContinuousRng::new([0xfa; 32]);
let lineage: LineageId = rng.value();
let version: VersionId = rng.value();
let key_1: Word = rng.value();
let value_1: Word = rng.value();
let key_2: Word = rng.value();
let value_2: Word = rng.value();
let key_3: Word = rng.value();
let value_3: Word = rng.value();
let key_4: Word = rng.value();
let value_4: Word = rng.value();
let key_5: Word = rng.value();
let value_5: Word = rng.value();
let mut operations = SmtUpdateBatch::empty();
operations.add_insert(key_1, value_1);
operations.add_insert(key_2, value_2);
operations.add_insert(key_3, value_3);
operations.add_insert(key_4, value_4);
operations.add_insert(key_5, value_5);
forest.add_lineage(lineage, version, operations)?;
let tree_id = TreeId::new(lineage, version);
let mut iter = forest.entries(tree_id)?;
let first = iter.next();
assert!(matches!(first, Some(Ok(_))), "expected first item to be Some(Ok(...))");
let second = iter.next();
assert!(matches!(second, Some(Ok(_))), "expected second item to be Some(Ok(...))");
let third = iter.next();
assert_matches!(
&third,
Some(Err(LargeSmtForestError::Unspecified(message)))
if message == FALLIBLE_READ_FAILURE_MESSAGE
);
assert!(iter.next().is_none(), "expected None after error");
assert!(iter.next().is_none(), "expected iterator to remain exhausted");
Ok(())
}
#[test]
fn entry_count_historical_bypasses_fallible_entries_iterator() -> Result<()> {
let backend = FallibleEntriesBackend::new();
let mut forest = LargeSmtForest::new(backend)?;
let mut rng = ContinuousRng::new([0xfb; 32]);
let lineage: LineageId = rng.value();
let version_1: VersionId = rng.value();
let key_1: Word = rng.value();
let value_1: Word = rng.value();
let key_2: Word = rng.value();
let value_2: Word = rng.value();
let key_3: Word = rng.value();
let value_3: Word = rng.value();
let key_4: Word = rng.value();
let value_4: Word = rng.value();
let key_5: Word = rng.value();
let value_5: Word = rng.value();
let mut operations = SmtUpdateBatch::empty();
operations.add_insert(key_1, value_1);
operations.add_insert(key_2, value_2);
operations.add_insert(key_3, value_3);
operations.add_insert(key_4, value_4);
operations.add_insert(key_5, value_5);
forest.add_lineage(lineage, version_1, operations)?;
let version_2: VersionId = version_1 + 1;
let key_6: Word = rng.value();
let value_6: Word = rng.value();
let mut operations = SmtUpdateBatch::empty();
operations.add_insert(key_6, value_6);
forest.update_tree(lineage, version_2, operations)?;
let result = forest.entry_count(TreeId::new(lineage, version_1));
assert_eq!(result?, 5);
Ok(())
}