use super::{ BTreeMap, BTreeSet, EmptySubtreeRoots, InnerNodeInfo, MerkleError, MerklePath, NodeIndex, Rpo256, RpoDigest, StarkField, Vec, Word, }; use core::{cmp, ops::Deref}; mod nodes; use nodes::NodeStore; mod values; use values::ValueStore; #[cfg(test)] mod tests; // TIERED SPARSE MERKLE TREE // ================================================================================================ /// Tiered (compacted) Sparse Merkle tree mapping 256-bit keys to 256-bit values. Both keys and /// values are represented by 4 field elements. /// /// Leaves in the tree can exist only on specific depths called "tiers". These depths are: 16, 32, /// 48, and 64. Initially, when a tree is empty, it is equivalent to an empty Sparse Merkle tree /// of depth 64 (i.e., leaves at depth 64 are set to [ZERO; 4]). As non-empty values are inserted /// into the tree they are added to the first available tier. /// /// For example, when the first key-value pair is inserted, it will be stored in a node at depth /// 16 such that the 16 most significant bits of the key determine the position of the node at /// depth 16. If another value with a key sharing the same 16-bit prefix is inserted, both values /// move into the next tier (depth 32). This process is repeated until values end up at the bottom /// tier (depth 64). If multiple values have keys with a common 64-bit prefix, such key-value pairs /// are stored in a sorted list at the bottom tier. /// /// To differentiate between internal and leaf nodes, node values are computed as follows: /// - Internal nodes: hash(left_child, right_child). /// - Leaf node at depths 16, 32, or 64: hash(key, value, domain=depth). /// - Leaf node at depth 64: hash([key_0, value_0, ..., key_n, value_n, domain=64]). #[derive(Debug, Clone, PartialEq, Eq)] pub struct TieredSmt { root: RpoDigest, nodes: NodeStore, values: ValueStore, } impl TieredSmt { // CONSTANTS // -------------------------------------------------------------------------------------------- /// The number of levels between tiers. const TIER_SIZE: u8 = 16; /// Depths at which leaves can exist in a tiered SMT. const TIER_DEPTHS: [u8; 4] = [16, 32, 48, 64]; /// Maximum node depth. This is also the bottom tier of the tree. const MAX_DEPTH: u8 = 64; /// Value of an empty leaf. pub const EMPTY_VALUE: Word = super::empty_roots::EMPTY_WORD; // CONSTRUCTORS // -------------------------------------------------------------------------------------------- /// Returns a new [TieredSmt] instantiated with the specified key-value pairs. /// /// # Errors /// Returns an error if the provided entries contain multiple values for the same key. pub fn with_leaves(entries: R) -> Result where R: IntoIterator, I: Iterator + ExactSizeIterator, { // create an empty tree let mut tree = Self::default(); // append leaves to the tree returning an error if a duplicate entry for the same key // is found let mut empty_entries = BTreeSet::new(); for (key, value) in entries { let old_value = tree.insert(key, value); if old_value != Self::EMPTY_VALUE || empty_entries.contains(&key) { return Err(MerkleError::DuplicateValuesForKey(key)); } // if we've processed an empty entry, add the key to the set of empty entry keys, and // if this key was already in the set, return an error if value == Self::EMPTY_VALUE && !empty_entries.insert(key) { return Err(MerkleError::DuplicateValuesForKey(key)); } } Ok(tree) } // PUBLIC ACCESSORS // -------------------------------------------------------------------------------------------- /// Returns the root of this Merkle tree. pub const fn root(&self) -> RpoDigest { self.root } /// Returns a node at the specified index. /// /// # Errors /// Returns an error if: /// - The specified index depth is 0 or greater than 64. /// - The node with the specified index does not exists in the Merkle tree. This is possible /// when a leaf node with the same index prefix exists at a tier higher than the requested /// node. pub fn get_node(&self, index: NodeIndex) -> Result { self.nodes.get_node(index) } /// Returns a Merkle path from the node at the specified index to the root. /// /// The node itself is not included in the path. /// /// # Errors /// Returns an error if: /// - The specified index depth is 0 or greater than 64. /// - The node with the specified index does not exists in the Merkle tree. This is possible /// when a leaf node with the same index prefix exists at a tier higher than the node to /// which the path is requested. pub fn get_path(&self, index: NodeIndex) -> Result { self.nodes.get_path(index) } /// Returns the value associated with the specified key. /// /// If nothing was inserted into this tree for the specified key, [ZERO; 4] is returned. pub fn get_value(&self, key: RpoDigest) -> Word { match self.values.get(&key) { Some(value) => *value, None => Self::EMPTY_VALUE, } } // STATE MUTATORS // -------------------------------------------------------------------------------------------- /// Inserts the provided value into the tree under the specified key and returns the value /// previously stored under this key. /// /// If the value for the specified key was not previously set, [ZERO; 4] is returned. pub fn insert(&mut self, key: RpoDigest, value: Word) -> Word { // if an empty value is being inserted, remove the leaf node to make it look as if the // value was never inserted if value == Self::EMPTY_VALUE { return self.remove_leaf_node(key); } // insert the value into the value store, and if the key was already in the store, update // it with the new value if let Some(old_value) = self.values.insert(key, value) { if old_value != value { // if the new value is different from the old value, determine the location of // the leaf node for this key, build the node, and update the root let (index, leaf_exists) = self.nodes.get_leaf_index(&key); debug_assert!(leaf_exists); let node = self.build_leaf_node(index, key, value); self.root = self.nodes.update_leaf_node(index, node); } return old_value; }; // determine the location for the leaf node; this index could have 3 different meanings: // - it points to a root of an empty subtree or an empty node at depth 64; in this case, // we can replace the node with the value node immediately. // - it points to an existing leaf at the bottom tier (i.e., depth = 64); in this case, // we need to process update the bottom leaf. // - it points to an existing leaf node for a different key with the same prefix (same // key case was handled above); in this case, we need to move the leaf to a lower tier let (index, leaf_exists) = self.nodes.get_leaf_index(&key); self.root = if leaf_exists && index.depth() == Self::MAX_DEPTH { // returned index points to a leaf at the bottom tier let node = self.build_leaf_node(index, key, value); self.nodes.update_leaf_node(index, node) } else if leaf_exists { // returned index pointes to a leaf for a different key with the same prefix // get the key-value pair for the key with the same prefix; since the key-value // pair has already been inserted into the value store, we need to filter it out // when looking for the other key-value pair let (other_key, other_value) = self .values .get_first_filtered(index_to_prefix(&index), &key) .expect("other key-value pair not found"); // determine how far down the tree should we move the leaves let common_prefix_len = get_common_prefix_tier_depth(&key, other_key); let depth = cmp::min(common_prefix_len + Self::TIER_SIZE, Self::MAX_DEPTH); // compute node locations for new and existing key-value paris let new_index = LeafNodeIndex::from_key(&key, depth); let other_index = LeafNodeIndex::from_key(other_key, depth); // compute node values for the new and existing key-value pairs let new_node = self.build_leaf_node(new_index, key, value); let other_node = self.build_leaf_node(other_index, *other_key, *other_value); // replace the leaf located at index with a subtree containing nodes for new and // existing key-value paris self.nodes.replace_leaf_with_subtree( index, [(new_index, new_node), (other_index, other_node)], ) } else { // returned index points to an empty subtree or an empty leaf at the bottom tier let node = self.build_leaf_node(index, key, value); self.nodes.insert_leaf_node(index, node) }; Self::EMPTY_VALUE } // ITERATORS // -------------------------------------------------------------------------------------------- /// Returns an iterator over all key-value pairs in this [TieredSmt]. pub fn iter(&self) -> impl Iterator { self.values.iter() } /// Returns an iterator over all inner nodes of this [TieredSmt] (i.e., nodes not at depths 16 /// 32, 48, or 64). /// /// The iterator order is unspecified. pub fn inner_nodes(&self) -> impl Iterator + '_ { self.nodes.inner_nodes() } /// Returns an iterator over upper leaves (i.e., depth = 16, 32, or 48) for this [TieredSmt] /// where each yielded item is a (node, key, value) tuple. /// /// The iterator order is unspecified. pub fn upper_leaves(&self) -> impl Iterator + '_ { self.nodes.upper_leaves().map(|(index, node)| { let key_prefix = index_to_prefix(index); let (key, value) = self.values.get_first(key_prefix).expect("upper leaf not found"); debug_assert_eq!(*index, LeafNodeIndex::from_key(key, index.depth()).into()); (*node, *key, *value) }) } /// Returns an iterator over bottom leaves (i.e., depth = 64) of this [TieredSmt]. /// /// Each yielded item consists of the hash of the leaf and its contents, where contents is /// a vector containing key-value pairs of entries storied in this leaf. /// /// The iterator order is unspecified. pub fn bottom_leaves(&self) -> impl Iterator)> + '_ { self.nodes.bottom_leaves().map(|(&prefix, node)| { let values = self.values.get_all(prefix).expect("bottom leaf not found"); (*node, values) }) } // HELPER METHODS // -------------------------------------------------------------------------------------------- /// Removes the node holding the key-value pair for the specified key from this tree, and /// returns the value associated with the specified key. /// /// If no value was associated with the specified key, [ZERO; 4] is returned. fn remove_leaf_node(&mut self, key: RpoDigest) -> Word { // remove the key-value pair from the value store; if no value was associated with the // specified key, return. let old_value = match self.values.remove(&key) { Some(old_value) => old_value, None => return Self::EMPTY_VALUE, }; // determine the location of the leaf holding the key-value pair to be removed let (index, leaf_exists) = self.nodes.get_leaf_index(&key); debug_assert!(leaf_exists); // if the leaf is at the bottom tier and after removing the key-value pair from it, the // leaf is still not empty, just recompute its hash and update the leaf node. if index.depth() == Self::MAX_DEPTH { if let Some(values) = self.values.get_all(index.value()) { let node = hash_bottom_leaf(&values); self.root = self.nodes.update_leaf_node(index, node); return old_value; }; } // if the removed key-value pair has a lone sibling at the current tier with a root at // higher tier, we need to move the sibling to a higher tier if let Some((sib_key, sib_val, new_sib_index)) = self.values.get_lone_sibling(index) { // determine the current index of the sibling node let sib_index = LeafNodeIndex::from_key(sib_key, index.depth()); debug_assert!(sib_index.depth() > new_sib_index.depth()); // compute node value for the new location of the sibling leaf and replace the subtree // with this leaf node let node = self.build_leaf_node(new_sib_index, *sib_key, *sib_val); let new_sib_depth = new_sib_index.depth(); self.root = self.nodes.replace_subtree_with_leaf(index, sib_index, new_sib_depth, node); } else { // if the removed key-value pair did not have a sibling at the current tier with a // root at higher tiers, just clear the leaf node self.root = self.nodes.clear_leaf_node(index); } old_value } /// Builds and returns a leaf node value for the node located as the specified index. /// /// This method assumes that the key-value pair for the node has already been inserted into /// the value store, however, for depths 16, 32, and 48, the node is computed directly from /// the passed-in values (for depth 64, the value store is queried to get all the key-value /// pairs located at the specified index). fn build_leaf_node(&self, index: LeafNodeIndex, key: RpoDigest, value: Word) -> RpoDigest { let depth = index.depth(); // insert the key into index-key map and compute the new value of the node if index.depth() == Self::MAX_DEPTH { // for the bottom tier, we add the key-value pair to the existing leaf, or create a // new leaf with this key-value pair let values = self.values.get_all(index.value()).unwrap(); hash_bottom_leaf(&values) } else { debug_assert_eq!(self.values.get_first(index_to_prefix(&index)), Some(&(key, value))); hash_upper_leaf(key, value, depth) } } } impl Default for TieredSmt { fn default() -> Self { let root = EmptySubtreeRoots::empty_hashes(Self::MAX_DEPTH)[0]; Self { root, nodes: NodeStore::new(root), values: ValueStore::default(), } } } // LEAF NODE INDEX // ================================================================================================ /// A wrapper around [NodeIndex] to provide type-safe references to nodes at depths 16, 32, 48, and /// 64. #[derive(Debug, Default, Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Hash)] pub struct LeafNodeIndex(NodeIndex); impl LeafNodeIndex { /// Returns a new [LeafNodeIndex] instantiated from the provided [NodeIndex]. /// /// In debug mode, panics if index depth is not 16, 32, 48, or 64. pub fn new(index: NodeIndex) -> Self { // check if the depth is 16, 32, 48, or 64; this works because for a valid depth, // depth - 16, can be 0, 16, 32, or 48 - i.e., the value is either 0 or any of the 4th // or 5th bits are set. We can test for this by computing a bitwise AND with a value // which has all but the 4th and 5th bits set (which is !48). debug_assert_eq!(((index.depth() - 16) & !48), 0, "invalid tier depth {}", index.depth()); Self(index) } /// Returns a new [LeafNodeIndex] instantiated from the specified key inserted at the specified /// depth. /// /// The value for the key is computed by taking n most significant bits from the most significant /// element of the key, where n is the specified depth. pub fn from_key(key: &RpoDigest, depth: u8) -> Self { let mse = get_key_prefix(key); Self::new(NodeIndex::new_unchecked(depth, mse >> (TieredSmt::MAX_DEPTH - depth))) } /// Returns a new [LeafNodeIndex] instantiated for testing purposes. #[cfg(test)] pub fn make(depth: u8, value: u64) -> Self { Self::new(NodeIndex::make(depth, value)) } /// Traverses towards the root until the specified depth is reached. /// /// The new depth must be a valid tier depth - i.e., 16, 32, 48, or 64. pub fn move_up_to(&mut self, depth: u8) { debug_assert_eq!(((depth - 16) & !48), 0, "invalid tier depth: {depth}"); self.0.move_up_to(depth); } } impl Deref for LeafNodeIndex { type Target = NodeIndex; fn deref(&self) -> &Self::Target { &self.0 } } impl From for LeafNodeIndex { fn from(value: NodeIndex) -> Self { Self::new(value) } } impl From for NodeIndex { fn from(value: LeafNodeIndex) -> Self { value.0 } } // HELPER FUNCTIONS // ================================================================================================ /// Returns the value representing the 64 most significant bits of the specified key. fn get_key_prefix(key: &RpoDigest) -> u64 { Word::from(key)[3].as_int() } /// Returns the index value shifted to be in the most significant bit positions of the returned /// u64 value. fn index_to_prefix(index: &NodeIndex) -> u64 { index.value() << (TieredSmt::MAX_DEPTH - index.depth()) } /// Returns tiered common prefix length between the most significant elements of the provided keys. /// /// Specifically: /// - returns 64 if the most significant elements are equal. /// - returns 48 if the common prefix is between 48 and 63 bits. /// - returns 32 if the common prefix is between 32 and 47 bits. /// - returns 16 if the common prefix is between 16 and 31 bits. /// - returns 0 if the common prefix is fewer than 16 bits. fn get_common_prefix_tier_depth(key1: &RpoDigest, key2: &RpoDigest) -> u8 { let e1 = get_key_prefix(key1); let e2 = get_key_prefix(key2); let ex = (e1 ^ e2).leading_zeros() as u8; (ex / 16) * 16 } /// Computes node value for leaves at tiers 16, 32, or 48. /// /// Node value is computed as: hash(key || value, domain = depth). pub fn hash_upper_leaf(key: RpoDigest, value: Word, depth: u8) -> RpoDigest { const NUM_UPPER_TIERS: usize = TieredSmt::TIER_DEPTHS.len() - 1; debug_assert!(TieredSmt::TIER_DEPTHS[..NUM_UPPER_TIERS].contains(&depth)); Rpo256::merge_in_domain(&[key, value.into()], depth.into()) } /// Computes node value for leaves at the bottom tier (depth 64). /// /// Node value is computed as: hash([key_0, value_0, ..., key_n, value_n], domain=64). /// /// TODO: when hashing in domain is implemented for `hash_elements()`, combine this function with /// `hash_upper_leaf()` function. pub fn hash_bottom_leaf(values: &[(RpoDigest, Word)]) -> RpoDigest { let mut elements = Vec::with_capacity(values.len() * 8); for (key, val) in values.iter() { elements.extend_from_slice(key.as_elements()); elements.extend_from_slice(val.as_slice()); } // TODO: hash in domain Rpo256::hash_elements(&elements) }