use alloc::string::ToString;
use alloc::vec::Vec;
use crate::Word;
use crate::account::{AccountId, AccountIdPrefix};
use crate::crypto::merkle::MerkleError;
use crate::crypto::merkle::smt::{MutationSet, SMT_DEPTH, Smt, SmtLeaf};
use crate::errors::AccountTreeError;
use crate::utils::serde::{
ByteReader,
ByteWriter,
Deserializable,
DeserializationError,
Serializable,
};
mod partial;
pub use partial::PartialAccountTree;
mod witness;
pub use witness::AccountWitness;
mod backend;
pub use backend::AccountTreeBackend;
mod account_id_key;
pub use account_id_key::AccountIdKey;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AccountTree<S = Smt> {
smt: S,
}
impl<S> Default for AccountTree<S>
where
S: Default,
{
fn default() -> Self {
Self { smt: Default::default() }
}
}
impl<S> AccountTree<S>
where
S: AccountTreeBackend<Error = MerkleError>,
{
pub const DEPTH: u8 = SMT_DEPTH;
pub(super) const KEY_SUFFIX_IDX: usize = 2;
pub(super) const KEY_PREFIX_IDX: usize = 3;
pub fn new(smt: S) -> Result<Self, AccountTreeError> {
for (_leaf_idx, leaf) in smt.leaves() {
match leaf {
SmtLeaf::Empty(_) => {
continue;
},
SmtLeaf::Single((key, _)) => {
AccountIdKey::try_from_word(key).map_err(|err| {
AccountTreeError::InvalidAccountIdKey { key, source: err }
})?;
},
SmtLeaf::Multiple(entries) => {
if let Some((key, _)) = entries.first() {
let key = *key;
let account_id = AccountIdKey::try_from_word(key).map_err(|err| {
AccountTreeError::InvalidAccountIdKey { key, source: err }
})?;
return Err(AccountTreeError::DuplicateIdPrefix {
duplicate_prefix: account_id.prefix(),
});
}
},
}
}
Ok(Self::new_unchecked(smt))
}
pub fn new_unchecked(smt: S) -> Self {
AccountTree { smt }
}
pub fn open(&self, account_id: AccountId) -> AccountWitness {
let key = AccountIdKey::from(account_id).as_word();
let proof = self.smt.open(&key);
AccountWitness::from_smt_proof(account_id, proof)
}
pub fn get(&self, account_id: AccountId) -> Word {
let key = AccountIdKey::from(account_id).as_word();
self.smt.get_value(&key)
}
pub fn root(&self) -> Word {
self.smt.root()
}
pub fn contains_account_id_prefix(&self, account_id_prefix: AccountIdPrefix) -> bool {
let key = Self::id_prefix_to_smt_key(account_id_prefix);
let is_empty = matches!(self.smt.get_leaf(&key), SmtLeaf::Empty(_));
!is_empty
}
pub fn num_accounts(&self) -> usize {
self.smt.num_leaves()
}
pub fn account_commitments(&self) -> impl Iterator<Item = (AccountId, Word)> {
self.smt.leaves().map(|(_leaf_idx, leaf)| {
let SmtLeaf::Single((key, commitment)) = leaf else {
unreachable!("empty and multiple variant should never be encountered")
};
(
AccountId::try_from_elements(key[Self::KEY_SUFFIX_IDX], key[Self::KEY_PREFIX_IDX])
.expect("account tree should only contain valid IDs"),
commitment,
)
})
}
pub fn compute_mutations(
&self,
account_commitments: impl IntoIterator<Item = (AccountId, Word)>,
) -> Result<AccountMutationSet, AccountTreeError> {
let mutation_set = self
.smt
.compute_mutations(Vec::from_iter(
account_commitments
.into_iter()
.map(|(id, commitment)| (AccountIdKey::from(id).as_word(), commitment)),
))
.map_err(AccountTreeError::ComputeMutations)?;
for id_key in mutation_set.new_pairs().keys() {
match self.smt.get_leaf(id_key) {
SmtLeaf::Empty(_) => (),
SmtLeaf::Single((existing_key, _)) => {
if existing_key != *id_key {
return Err(AccountTreeError::DuplicateIdPrefix {
duplicate_prefix: AccountIdKey::try_from_word(*id_key)
.expect("account tree should only contain valid IDs")
.prefix(),
});
}
},
SmtLeaf::Multiple(_) => {
unreachable!(
"account tree should never contain duplicate ID prefixes and therefore never a multiple leaf"
)
},
}
}
Ok(AccountMutationSet::new(mutation_set))
}
pub fn insert(
&mut self,
account_id: AccountId,
state_commitment: Word,
) -> Result<Word, AccountTreeError> {
let key = AccountIdKey::from(account_id).as_word();
let prev_value = self.smt.insert(key, state_commitment)
.expect("account tree should always have a single value per key, and hence cannot exceed the maximum leaf number");
if self.smt.get_leaf(&key).num_entries() >= 2 {
return Err(AccountTreeError::DuplicateIdPrefix {
duplicate_prefix: account_id.prefix(),
});
}
Ok(prev_value)
}
pub fn apply_mutations(
&mut self,
mutations: AccountMutationSet,
) -> Result<(), AccountTreeError> {
self.smt
.apply_mutations(mutations.into_mutation_set())
.map_err(AccountTreeError::ApplyMutations)
}
pub fn apply_mutations_with_reversion(
&mut self,
mutations: AccountMutationSet,
) -> Result<AccountMutationSet, AccountTreeError> {
let reversion = self
.smt
.apply_mutations_with_reversion(mutations.into_mutation_set())
.map_err(AccountTreeError::ApplyMutations)?;
Ok(AccountMutationSet::new(reversion))
}
fn id_prefix_to_smt_key(account_id: AccountIdPrefix) -> Word {
let mut key = Word::empty();
key[Self::KEY_PREFIX_IDX] = account_id.as_felt();
key
}
}
impl Serializable for AccountTree {
fn write_into<W: ByteWriter>(&self, target: &mut W) {
self.account_commitments().collect::<Vec<_>>().write_into(target);
}
}
impl Deserializable for AccountTree {
fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
let entries = Vec::<(AccountId, Word)>::read_from(source)?;
let mut seen_prefixes = alloc::collections::BTreeSet::new();
for (id, _) in &entries {
if !seen_prefixes.insert(id.prefix()) {
return Err(DeserializationError::InvalidValue(format!(
"Duplicate account ID prefix: {}",
id.prefix()
)));
}
}
let smt = Smt::with_entries(
entries.into_iter().map(|(k, v)| (AccountIdKey::from(k).as_word(), v)),
)
.map_err(|err| DeserializationError::InvalidValue(err.to_string()))?;
Ok(Self::new_unchecked(smt))
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AccountMutationSet {
mutation_set: MutationSet<SMT_DEPTH, Word, Word>,
}
impl AccountMutationSet {
fn new(mutation_set: MutationSet<SMT_DEPTH, Word, Word>) -> Self {
Self { mutation_set }
}
pub fn as_mutation_set(&self) -> &MutationSet<SMT_DEPTH, Word, Word> {
&self.mutation_set
}
pub fn into_mutation_set(self) -> MutationSet<SMT_DEPTH, Word, Word> {
self.mutation_set
}
}
#[cfg(test)]
pub(super) mod tests {
use std::vec::Vec;
use assert_matches::assert_matches;
use super::*;
use crate::account::{AccountStorageMode, AccountType};
use crate::testing::account_id::{AccountIdBuilder, account_id};
pub(crate) fn setup_duplicate_prefix_ids() -> [(AccountId, Word); 2] {
let id0 = AccountId::try_from(account_id(
AccountType::FungibleFaucet,
AccountStorageMode::Public,
0xaabb_ccdd,
))
.unwrap();
let id1 = AccountId::try_from(account_id(
AccountType::FungibleFaucet,
AccountStorageMode::Public,
0xaabb_ccff,
))
.unwrap();
assert_eq!(id0.prefix(), id1.prefix(), "test requires that these ids have the same prefix");
let commitment0 = Word::from([0, 0, 0, 42u32]);
let commitment1 = Word::from([0, 0, 0, 24u32]);
assert_eq!(id0.prefix(), id1.prefix(), "test requires that these ids have the same prefix");
[(id0, commitment0), (id1, commitment1)]
}
#[test]
fn insert_fails_on_duplicate_prefix() {
let mut tree = AccountTree::<Smt>::default();
let [(id0, commitment0), (id1, commitment1)] = setup_duplicate_prefix_ids();
tree.insert(id0, commitment0).unwrap();
assert_eq!(tree.get(id0), commitment0);
let err = tree.insert(id1, commitment1).unwrap_err();
assert_matches!(err, AccountTreeError::DuplicateIdPrefix {
duplicate_prefix
} if duplicate_prefix == id0.prefix());
}
#[test]
fn insert_succeeds_on_multiple_updates() {
let mut tree = AccountTree::<Smt>::default();
let [(id0, commitment0), (_, commitment1)] = setup_duplicate_prefix_ids();
tree.insert(id0, commitment0).unwrap();
tree.insert(id0, commitment1).unwrap();
assert_eq!(tree.get(id0), commitment1);
}
#[test]
fn apply_mutations() {
let id0 = AccountIdBuilder::new().build_with_seed([5; 32]);
let id1 = AccountIdBuilder::new().build_with_seed([6; 32]);
let id2 = AccountIdBuilder::new().build_with_seed([7; 32]);
let digest0 = Word::from([0, 0, 0, 1u32]);
let digest1 = Word::from([0, 0, 0, 2u32]);
let digest2 = Word::from([0, 0, 0, 3u32]);
let digest3 = Word::from([0, 0, 0, 4u32]);
let mut tree = AccountTree::with_entries([(id0, digest0), (id1, digest1)]).unwrap();
let mutations = tree
.compute_mutations([(id0, digest1), (id1, digest2), (id2, digest3)])
.unwrap();
tree.apply_mutations(mutations).unwrap();
assert_eq!(tree.num_accounts(), 3);
assert_eq!(tree.get(id0), digest1);
assert_eq!(tree.get(id1), digest2);
assert_eq!(tree.get(id2), digest3);
}
#[test]
fn duplicates_in_compute_mutations() {
let [pair0, pair1] = setup_duplicate_prefix_ids();
let id2 = AccountIdBuilder::new().build_with_seed([5; 32]);
let commitment2 = Word::from([0, 0, 0, 99u32]);
let tree = AccountTree::with_entries([pair0, (id2, commitment2)]).unwrap();
let err = tree.compute_mutations([pair1]).unwrap_err();
assert_matches!(err, AccountTreeError::DuplicateIdPrefix {
duplicate_prefix
} if duplicate_prefix == pair1.0.prefix());
}
#[test]
fn account_commitments() {
let id0 = AccountIdBuilder::new().build_with_seed([5; 32]);
let id1 = AccountIdBuilder::new().build_with_seed([6; 32]);
let id2 = AccountIdBuilder::new().build_with_seed([7; 32]);
let digest0 = Word::from([0, 0, 0, 1u32]);
let digest1 = Word::from([0, 0, 0, 2u32]);
let digest2 = Word::from([0, 0, 0, 3u32]);
let empty_digest = Word::empty();
let mut tree =
AccountTree::with_entries([(id0, digest0), (id1, digest1), (id2, digest2)]).unwrap();
tree.insert(id2, empty_digest).unwrap();
assert_eq!(tree.num_accounts(), 2);
let accounts: Vec<_> = tree.account_commitments().collect();
assert_eq!(accounts.len(), 2);
assert!(accounts.contains(&(id0, digest0)));
assert!(accounts.contains(&(id1, digest1)));
}
#[test]
fn account_witness() {
let id0 = AccountIdBuilder::new().build_with_seed([5; 32]);
let id1 = AccountIdBuilder::new().build_with_seed([6; 32]);
let digest0 = Word::from([0, 0, 0, 1u32]);
let digest1 = Word::from([0, 0, 0, 2u32]);
let tree = AccountTree::with_entries([(id0, digest0), (id1, digest1)]).unwrap();
assert_eq!(tree.num_accounts(), 2);
for id in [id0, id1] {
let proof = tree.smt.open(&AccountIdKey::from(id).as_word());
let (control_path, control_leaf) = proof.into_parts();
let witness = tree.open(id);
assert_eq!(witness.leaf(), control_leaf);
assert_eq!(witness.path(), &control_path);
}
}
#[test]
fn contains_account_prefix() {
let [pair0, pair1] = setup_duplicate_prefix_ids();
let tree = AccountTree::with_entries([pair0]).unwrap();
assert_eq!(tree.num_accounts(), 1);
assert!(tree.contains_account_id_prefix(pair0.0.prefix()));
assert!(tree.contains_account_id_prefix(pair1.0.prefix()));
let id1 = AccountIdBuilder::new().build_with_seed([7; 32]);
assert!(!tree.contains_account_id_prefix(id1.prefix()));
}
#[cfg(feature = "std")]
#[test]
fn large_smt_backend_basic_operations() {
use miden_crypto::merkle::smt::{LargeSmt, MemoryStorage};
let id0 = AccountIdBuilder::new().build_with_seed([5; 32]);
let id1 = AccountIdBuilder::new().build_with_seed([6; 32]);
let id2 = AccountIdBuilder::new().build_with_seed([7; 32]);
let digest0 = Word::from([0, 0, 0, 1u32]);
let digest1 = Word::from([0, 0, 0, 2u32]);
let digest2 = Word::from([0, 0, 0, 3u32]);
let tree = LargeSmt::<MemoryStorage>::with_entries(
MemoryStorage::default(),
[
(AccountIdKey::from(id0).as_word(), digest0),
(AccountIdKey::from(id1).as_word(), digest1),
],
)
.map(AccountTree::new_unchecked)
.unwrap();
assert_eq!(tree.num_accounts(), 2);
assert_eq!(tree.get(id0), digest0);
assert_eq!(tree.get(id1), digest1);
let witness0 = tree.open(id0);
assert_eq!(witness0.id(), id0);
let mut tree_mut = LargeSmt::<MemoryStorage>::with_entries(
MemoryStorage::default(),
[
(AccountIdKey::from(id0).as_word(), digest0),
(AccountIdKey::from(id1).as_word(), digest1),
],
)
.map(AccountTree::new_unchecked)
.unwrap();
tree_mut.insert(id2, digest2).unwrap();
assert_eq!(tree_mut.num_accounts(), 3);
assert_eq!(tree_mut.get(id2), digest2);
assert_eq!(tree.num_accounts(), 2);
}
#[cfg(feature = "std")]
#[test]
fn large_smt_backend_duplicate_prefix_check() {
use miden_crypto::merkle::smt::{LargeSmt, MemoryStorage};
let [(id0, commitment0), (id1, commitment1)] = setup_duplicate_prefix_ids();
let mut tree = AccountTree::new_unchecked(LargeSmt::new(MemoryStorage::default()).unwrap());
tree.insert(id0, commitment0).unwrap();
assert_eq!(tree.get(id0), commitment0);
let err = tree.insert(id1, commitment1).unwrap_err();
assert_matches!(
err,
AccountTreeError::DuplicateIdPrefix { duplicate_prefix }
if duplicate_prefix == id0.prefix()
);
}
#[cfg(feature = "std")]
#[test]
fn large_smt_backend_apply_mutations() {
use miden_crypto::merkle::smt::{LargeSmt, MemoryStorage};
let id0 = AccountIdBuilder::new().build_with_seed([5; 32]);
let id1 = AccountIdBuilder::new().build_with_seed([6; 32]);
let id2 = AccountIdBuilder::new().build_with_seed([7; 32]);
let digest0 = Word::from([0, 0, 0, 1u32]);
let digest1 = Word::from([0, 0, 0, 2u32]);
let digest2 = Word::from([0, 0, 0, 3u32]);
let digest3 = Word::from([0, 0, 0, 4u32]);
let mut tree = LargeSmt::with_entries(
MemoryStorage::default(),
[
(AccountIdKey::from(id0).as_word(), digest0),
(AccountIdKey::from(id1).as_word(), digest1),
],
)
.map(AccountTree::new_unchecked)
.unwrap();
let mutations = tree
.compute_mutations([(id0, digest1), (id1, digest2), (id2, digest3)])
.unwrap();
tree.apply_mutations(mutations).unwrap();
assert_eq!(tree.num_accounts(), 3);
assert_eq!(tree.get(id0), digest1);
assert_eq!(tree.get(id1), digest2);
assert_eq!(tree.get(id2), digest3);
}
#[cfg(feature = "std")]
#[test]
fn large_smt_backend_same_root_as_regular_smt() {
use miden_crypto::merkle::smt::{LargeSmt, MemoryStorage};
let id0 = AccountIdBuilder::new().build_with_seed([5; 32]);
let id1 = AccountIdBuilder::new().build_with_seed([6; 32]);
let digest0 = Word::from([0, 0, 0, 1u32]);
let digest1 = Word::from([0, 0, 0, 2u32]);
let large_tree = LargeSmt::with_entries(
MemoryStorage::default(),
[
(AccountIdKey::from(id0).as_word(), digest0),
(AccountIdKey::from(id1).as_word(), digest1),
],
)
.map(AccountTree::new_unchecked)
.unwrap();
let regular_tree = AccountTree::with_entries([(id0, digest0), (id1, digest1)]).unwrap();
assert_eq!(large_tree.root(), regular_tree.root());
let large_commitments: std::collections::BTreeMap<_, _> =
large_tree.account_commitments().collect();
let regular_commitments: std::collections::BTreeMap<_, _> =
regular_tree.account_commitments().collect();
assert_eq!(large_commitments, regular_commitments);
}
}