#![cfg(test)]
pub const UNUSED_ENTRY_COUNT: usize = 0;
use alloc::{string::ToString, vec::Vec};
use core::error::Error;
use itertools::Itertools;
use miden_field::{Felt, Word};
use proptest::prelude::*;
use crate::{
EMPTY_WORD, Map, ONE, ZERO,
merkle::smt::{
Backend, ForestInMemoryBackend, ForestOperation, LargeSmtForest, LeafIndex, LineageId,
MAX_LEAF_ENTRIES, RootInfo, SMT_DEPTH, Smt, SmtForestUpdateBatch, SmtProof, SmtUpdateBatch,
TreeId, VersionId,
large_forest::{
backend::{BackendError, Result as BackendResult},
root::{TreeEntry, TreeWithRoot},
utils::MutationSet,
},
},
};
const MIN_BATCH_ENTRIES: usize = 0;
const MAX_BATCH_ENTRIES: usize = 300;
pub const FALLIBLE_READ_FAILURE_MESSAGE: &str = "simulated read failure";
pub fn to_fail(error: impl Error) -> TestCaseError {
TestCaseError::fail(error.to_string())
}
pub fn arbitrary_lineage() -> impl Strategy<Value = LineageId> {
prop::array::uniform32(any::<u8>()).prop_map(LineageId::new)
}
pub fn arbitrary_distinct_lineages() -> impl Strategy<Value = (LineageId, LineageId)> {
(arbitrary_lineage(), arbitrary_lineage())
.prop_filter("lineages must be distinct", |(a, b)| a != b)
}
pub fn arbitrary_version() -> impl Strategy<Value = VersionId> {
0..u64::MAX
}
pub fn arbitrary_felt() -> impl Strategy<Value = Felt> {
prop_oneof![any::<u64>().prop_map(Felt::new_unchecked), Just(ZERO), Just(ONE)]
}
pub fn arbitrary_word() -> impl Strategy<Value = Word> {
prop_oneof![prop::array::uniform4(arbitrary_felt()).prop_map(Word::new), Just(Word::empty()),]
}
pub fn arbitrary_non_empty_word() -> impl Strategy<Value = Word> {
arbitrary_word().prop_filter("word must be non-empty", |word| *word != EMPTY_WORD)
}
pub fn arbitrary_entries() -> impl Strategy<Value = Vec<(Word, Word)>> {
prop::collection::vec(
(arbitrary_word(), arbitrary_word()),
MIN_BATCH_ENTRIES..=MAX_BATCH_ENTRIES,
)
.prop_map(move |entries| {
let mut keys_in_leaf: Map<LeafIndex<SMT_DEPTH>, usize> = Map::default();
entries
.into_iter()
.flat_map(|(k, v)| {
let leaf_index = LeafIndex::from(k);
let count = keys_in_leaf.entry(leaf_index).or_default();
if *count >= MAX_LEAF_ENTRIES {
return None;
} else {
*count += 1;
}
Some((k, v))
})
.collect()
})
}
pub fn arbitrary_batch() -> impl Strategy<Value = SmtUpdateBatch> {
arbitrary_entries().prop_map(|e| {
SmtUpdateBatch::new(e.into_iter().map(|(k, v)| {
if v == EMPTY_WORD {
ForestOperation::remove(k)
} else {
ForestOperation::insert(k, v)
}
}))
})
}
pub fn build_tree(initial: SmtUpdateBatch) -> Result<Smt, TestCaseError> {
let mut tree = Smt::new();
apply_batch(&mut tree, initial)?;
Ok(tree)
}
pub fn apply_batch(tree: &mut Smt, batch: SmtUpdateBatch) -> Result<(), TestCaseError> {
let mutations = tree
.compute_mutations(batch.consume().into_iter().map(Into::<(Word, Word)>::into))
.map_err(to_fail)?;
tree.apply_mutations(mutations).map_err(to_fail)
}
pub fn batch_keys(batch: &SmtUpdateBatch) -> Vec<Word> {
batch.clone().consume().into_iter().map(|operation| operation.key()).collect()
}
pub fn sorted_tree_entries(tree: &Smt) -> Vec<TreeEntry> {
let mut entries = tree
.entries()
.map(|(key, value)| TreeEntry { key: *key, value: *value })
.collect_vec();
entries.sort_by_key(|entry| (entry.key, entry.value));
entries
}
pub fn sorted_forest_entries<B: Backend>(
forest: &LargeSmtForest<B>,
tree: TreeId,
) -> Result<Vec<TreeEntry>, TestCaseError> {
let mut entries = forest
.entries(tree)
.map_err(to_fail)?
.collect::<crate::merkle::smt::large_forest::Result<Vec<_>>>()
.map_err(to_fail)?;
entries.sort_by_key(|entry| (entry.key, entry.value));
Ok(entries)
}
fn word_to_option(value: Word) -> Option<Word> {
if value == EMPTY_WORD { None } else { Some(value) }
}
pub fn assert_tree_queries_match<B: Backend>(
forest: &LargeSmtForest<B>,
tree_id: TreeId,
reference: &Smt,
sample_keys: &[Word],
assert_openings: bool,
) -> Result<(), TestCaseError> {
let forest_entries = sorted_forest_entries(forest, tree_id)?;
let reference_entries = sorted_tree_entries(reference);
let reference_entry_count = reference_entries.len();
prop_assert_eq!(forest_entries, reference_entries);
prop_assert_eq!(forest.entry_count(tree_id).map_err(to_fail)?, reference_entry_count);
for key in sample_keys {
prop_assert_eq!(
forest.get(tree_id, *key).map_err(to_fail)?,
word_to_option(reference.get_value(key))
);
if assert_openings {
prop_assert_eq!(forest.open(tree_id, *key).map_err(to_fail)?, reference.open(key));
}
}
Ok(())
}
pub fn assert_lineage_metadata<B: Backend>(
forest: &LargeSmtForest<B>,
lineage: LineageId,
versions: &[(VersionId, Word)],
) -> Result<(), TestCaseError> {
let (latest_version, latest_root) =
versions.last().copied().expect("lineage must be non-empty");
prop_assert_eq!(forest.latest_version(lineage), Some(latest_version));
prop_assert_eq!(forest.latest_root(lineage), Some(latest_root));
prop_assert_eq!(
forest.lineage_roots(lineage).expect("lineage must be present").collect_vec(),
versions.iter().rev().map(|(_, root)| *root).collect_vec()
);
for (idx, (version, root)) in versions.iter().enumerate() {
let tree = TreeId::new(lineage, *version);
let expected = if idx + 1 == versions.len() {
RootInfo::LatestVersion(*root)
} else {
RootInfo::HistoricalVersion(*root)
};
prop_assert_eq!(forest.root_info(tree), expected);
}
Ok(())
}
#[derive(Debug)]
pub struct FallibleEntriesBackend {
inner: ForestInMemoryBackend,
}
impl FallibleEntriesBackend {
pub fn new() -> Self {
Self { inner: ForestInMemoryBackend::new() }
}
}
pub struct FallibleIter<I> {
inner: I,
count: usize,
}
impl<I: Iterator<Item = BackendResult<TreeEntry>>> Iterator for FallibleIter<I> {
type Item = BackendResult<TreeEntry>;
fn next(&mut self) -> Option<Self::Item> {
if self.count >= 3 {
return None;
}
self.count += 1;
if self.count <= 2 {
self.inner.next()
} else {
Some(Err(BackendError::Unspecified(FALLIBLE_READ_FAILURE_MESSAGE.into())))
}
}
}
impl Backend for FallibleEntriesBackend {
fn open(&self, lineage: LineageId, key: Word) -> BackendResult<SmtProof> {
self.inner.open(lineage, key)
}
fn get_leaf(
&self,
lineage: LineageId,
leaf_index: LeafIndex<SMT_DEPTH>,
) -> BackendResult<crate::merkle::smt::SmtLeaf> {
self.inner.get_leaf(lineage, leaf_index)
}
fn get(&self, lineage: LineageId, key: Word) -> BackendResult<Option<Word>> {
self.inner.get(lineage, key)
}
fn version(&self, lineage: LineageId) -> BackendResult<VersionId> {
self.inner.version(lineage)
}
fn lineages(&self) -> BackendResult<impl Iterator<Item = LineageId>> {
self.inner.lineages()
}
fn trees(&self) -> BackendResult<impl Iterator<Item = TreeWithRoot>> {
self.inner.trees()
}
fn entry_count(&self, lineage: LineageId) -> BackendResult<usize> {
self.inner.entry_count(lineage)
}
fn entries(
&self,
lineage: LineageId,
) -> BackendResult<impl Iterator<Item = BackendResult<TreeEntry>>> {
let inner_iter = self.inner.entries(lineage)?;
Ok(FallibleIter { inner: inner_iter, count: 0 })
}
fn add_lineage(
&mut self,
lineage: LineageId,
version: VersionId,
updates: SmtUpdateBatch,
) -> BackendResult<TreeWithRoot> {
self.inner.add_lineage(lineage, version, updates)
}
fn update_tree(
&mut self,
lineage: LineageId,
new_version: VersionId,
updates: SmtUpdateBatch,
) -> BackendResult<MutationSet> {
self.inner.update_tree(lineage, new_version, updates)
}
fn add_lineages(
&mut self,
version: VersionId,
lineages: SmtForestUpdateBatch,
) -> BackendResult<Vec<(LineageId, TreeWithRoot)>> {
self.inner.add_lineages(version, lineages)
}
fn update_forest(
&mut self,
new_version: VersionId,
updates: SmtForestUpdateBatch,
) -> BackendResult<Vec<(LineageId, MutationSet)>> {
self.inner.update_forest(new_version, updates)
}
}