Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions miden-crypto/src/merkle/smt/forest/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ impl SmtForest {
/// Inserts the specified key-value pair into an SMT with the specified root. This will also
/// add a new root to the forest. Returns the new root.
///
/// Returns an error if an SMT with the specified root is not in the forest, these is not
/// Returns an error if an SMT with the specified root is not in the forest, there is not
/// enough data in the forest to perform the insert, or if the insert would create a leaf
/// with too many entries.
pub fn insert(&mut self, root: Word, key: Word, value: Word) -> Result<Word, MerkleError> {
Expand All @@ -136,7 +136,7 @@ impl SmtForest {
/// Inserts the specified key-value pairs into an SMT with the specified root. This will also
/// add a single new root to the forest for the entire batch of inserts. Returns the new root.
///
/// Returns an error if an SMT with the specified root is not in the forest, these is not
/// Returns an error if an SMT with the specified root is not in the forest, there is not
/// enough data in the forest to perform the insert, or if the insert would create a leaf
/// with too many entries.
pub fn batch_insert(
Expand Down
123 changes: 123 additions & 0 deletions miden-crypto/src/merkle/smt/large_forest/backend.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
//! This file contains the [`Backend`] trait for the [`LargeSmtForest`] implementation and the
//! supporting types it needs.
use alloc::{boxed::Box, vec::Vec};
use core::fmt::Debug;

use thiserror::Error;

use crate::{
Word,
merkle::{
MerkleError,
smt::{
SmtProof,
full::SMT_DEPTH,
large_forest::{
history::VersionId,
operation::{SmtForestUpdateBatch, SmtUpdateBatch},
},
},
},
};
// TYPE ALIASES
// ================================================================================================

/// The mutation set used by the forest backends.
///
/// At the moment this is used for _reverse_ mutations that "undo" the changes made to the tree(s),
/// but may be harmonised with [`SmtUpdateBatch`] in the future. For more information on its use for
/// reverse mutations, see [`crate::merkle::smt::SparseMerkleTree::apply_mutations_with_reversion`].
pub type MutationSet = crate::merkle::smt::MutationSet<SMT_DEPTH, Word, Word>;

// BACKEND
// ================================================================================================

/// The backing storage for the SMT forest, providing the necessary high-level methods for
/// performing operations on the full trees that make up the forest, while allowing the forest
/// itself to be storage agnostic.
///
/// # Backend Data Storage
///
/// Having a generic [`Backend`] provides no guarantees to the user about how it stores data and
/// what patterns are used for data access under the hood. It is, however, guaranteed to store
/// _only_ the data necessary to describe the latest state of each tree in the forest.
pub trait Backend
where
Self: Debug,
{
// QUERIES
// ============================================================================================

/// Returns an opening for the specified `key` in the SMT with the specified `root`.
fn open(&self, root: Word, key: Word) -> Result<SmtProof>;

/// Returns the value associated with the provided `key` in the SMT with the provided `root`, or
/// [`None`] if no such value exists.
fn get(&self, root: Word, key: Word) -> Result<Option<Word>>;

/// Returns the version of the tree with the provided `root`.
fn version(&self, root: Word) -> Result<VersionId>;

/// Returns an iterator over all the tree roots and versions that the backend knows about.
///
/// The iteration order is unspecified.
fn versions(&self) -> Result<impl Iterator<Item = (Word, VersionId)>>;

// SINGLE-TREE MODIFIERS
// ============================================================================================

/// Performs the provided `updates` on the tree with the provided `root`, returning the new
/// root.
///
/// Implementations must guarantee the following behavior, with non-conforming implementations
/// considered to be a bug:
///
/// - At most one new root must be added to the forest for the entire batch.
/// - If applying the provided `updates` results in no changes to the tree, no new tree must be
/// allocated.
fn update_tree(
&mut self,
root: Word,
new_version: VersionId,
updates: SmtUpdateBatch,
) -> Result<MutationSet>;

// MULTI-TREE MODIFIERS
// ============================================================================================

/// Performs the provided `updates` on the forest, setting all new tree states to have the
/// provided `new_version` and returning a vector of the mutation sets that reverse the changes
/// to each changed tree.
///
/// Implementations must guarantee the following behaviour, with non-conforming implementations
/// considered to be a bug:
///
/// - At most one new root must be added to the forest for each target root in the provided
/// `updates`.
/// - If applying the provided `updates` results in no changes to a given lineage of trees in
/// the forest, then no new tree must be allocated in that lineage.
fn update_forest(
&mut self,
new_version: VersionId,
updates: SmtForestUpdateBatch,
) -> Result<Vec<MutationSet>>;
}

// BACKEND ERROR
// ================================================================================================

/// The error type for use within Backends.
#[derive(Debug, Error)]
pub enum BackendError {
/// Raised when there is an error with the merkle tree semantics within the backend.
#[error(transparent)]
Merkle(#[from] MerkleError),

/// Raised for arbitrary other errors within the backend.
#[error(transparent)]
Other(#[from] Box<dyn core::error::Error + Sync + Send>),
}

/// The result type for use with backends.
pub type Result<T> = core::result::Result<T, BackendError>;
42 changes: 36 additions & 6 deletions miden-crypto/src/merkle/smt/large_forest/error.rs
Original file line number Diff line number Diff line change
@@ -1,21 +1,51 @@
//! This module contains the error types and helpers for working with errors from the large SMT
//! forest.

use alloc::boxed::Box;

use thiserror::Error;

use crate::merkle::{MerkleError, smt::large_forest::history::error::HistoryError};
use crate::{
Word,
merkle::{
MerkleError,
smt::large_forest::{backend::BackendError, history::error::HistoryError},
},
};

// LARGE SMT FOREST ERROR
// ================================================================================================

/// The errors returned by operations on the large SMT forest.
///
/// This type primarily serves to wrap more specific error types from various subsystems into a
/// generic interface type.
/// The type of errors returned by operations on the large SMT forest.
#[derive(Debug, Error)]
pub enum LargeSmtForestError {
/// Errors in the history subsystem of the forest.
#[error(transparent)]
HistoryError(#[from] HistoryError),

/// Raised when an attempt is made to modify a frozen tree.
#[error("Attempted to modify non-current tree with root {0}")]
InvalidModification(Word),

/// Errors with the merkle tree operations of the forest.
#[error(transparent)]
MerkleError(#[from] MerkleError),

/// Raised for arbitrary other errors.
#[error(transparent)]
Other(#[from] Box<dyn core::error::Error + Sync + Send>),
}

/// We want to forward backend errors specifically when we can, so we manually implement the
/// conversion.
impl From<BackendError> for LargeSmtForestError {
fn from(value: BackendError) -> Self {
match value {
BackendError::Merkle(e) => LargeSmtForestError::from(e),
BackendError::Other(e) => LargeSmtForestError::from(e),
}
}
}

pub mod history {}
/// The result type for use within the large SMT forest portion of the library.
pub type Result<T> = core::result::Result<T, LargeSmtForestError>;
22 changes: 16 additions & 6 deletions miden-crypto/src/merkle/smt/large_forest/history/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ use core::fmt::Debug;
use error::{HistoryError, Result};

use crate::{
Map, Set, Word,
Map, Word,
merkle::{
NodeIndex,
smt::{LeafIndex, SMT_DEPTH},
Expand Down Expand Up @@ -136,13 +136,23 @@ impl History {

/// Returns all the roots that the history knows about.
///
/// The iteration order of the roots is guaranteed to move backward in time, with earlier items
/// being roots from versions closer to the present.
///
/// # Complexity
///
/// Calling this method requires a traversal of all the versions and is hence linear in the
/// number of history versions.
#[must_use]
pub fn roots(&self) -> Set<Word> {
self.deltas.iter().map(|d| d.root).collect()
/// Calling this method provides an iterator whose consumption requires a traversal of all the
/// versions. The method's complexity is thus `O(n)` in the number of versions.
pub fn roots(&self) -> impl Iterator<Item = Word> {
self.deltas.iter().rev().map(|d| d.root)
}

/// Gets the version corresponding to the provided `root`, or returns [`None`] if the provided
/// `root` is not found within this history.
pub fn version(&self, root: Word) -> Option<VersionId> {
self.deltas
.iter()
.find_map(|d| if d.root == root { Some(d.version_id) } else { None })
}

/// Returns `true` if `root` is in the history and `false` otherwise.
Expand Down
4 changes: 3 additions & 1 deletion miden-crypto/src/merkle/smt/large_forest/history/tests.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#![cfg(feature = "std")]
//! The functional tests for the history component.

use alloc::vec::Vec;

use p3_field::PrimeCharacteristicRing;

use super::{CompactLeaf, History, LeafChanges, NodeChanges, error::Result};
Expand Down Expand Up @@ -32,7 +34,7 @@ fn roots() -> Result<()> {
history.add_version(root_2, 1, nodes.clone(), leaves.clone())?;

// We should be able to get all the roots.
let roots = history.roots();
let roots = history.roots().collect::<Vec<_>>();
assert_eq!(roots.len(), 2);
assert!(roots.contains(&root_1));
assert!(roots.contains(&root_2));
Expand Down
Loading