From fed49a161730bd83e7697f725b043bee49c5b319 Mon Sep 17 00:00:00 2001 From: Juan Munoz Date: Fri, 14 Nov 2025 10:50:10 -0300 Subject: [PATCH 1/6] refactor: use `SmtForest` in `SqliteStore` --- crates/sqlite-store/src/account.rs | 59 +++++++------- crates/sqlite-store/src/lib.rs | 36 ++++----- crates/sqlite-store/src/merkle_store.rs | 102 ++++++++++-------------- crates/sqlite-store/src/sync.rs | 10 +-- crates/sqlite-store/src/transaction.rs | 10 +-- 5 files changed, 98 insertions(+), 119 deletions(-) diff --git a/crates/sqlite-store/src/account.rs b/crates/sqlite-store/src/account.rs index dd06a1eb4..2ea01adfc 100644 --- a/crates/sqlite-store/src/account.rs +++ b/crates/sqlite-store/src/account.rs @@ -20,13 +20,14 @@ use miden_client::account::{ StorageSlotType, }; use miden_client::asset::{Asset, AssetVault, AssetWitness, FungibleAsset, NonFungibleDeltaAction}; -use miden_client::crypto::{MerkleStore, SmtLeaf, SmtProof}; +use miden_client::crypto::{SmtLeaf, SmtProof}; use miden_client::store::{AccountRecord, AccountStatus, StoreError}; use miden_client::sync::NoteTagRecord; use miden_client::utils::{Deserializable, Serializable}; use miden_client::{AccountError, Felt, Word}; use miden_objects::account::StorageMapWitness; use miden_objects::asset::AssetVaultKey; +use miden_objects::crypto::merkle::SmtForest; use rusqlite::types::Value; use rusqlite::{Connection, Params, Transaction, named_params, params}; @@ -186,7 +187,7 @@ impl SqliteStore { pub(crate) fn insert_account( conn: &mut Connection, - merkle_store: &Arc>, + smt_forest: &Arc>, account: &Account, initial_address: &Address, ) -> Result<(), StoreError> { @@ -207,16 +208,16 @@ impl SqliteStore { tx.commit().into_store_error()?; - let mut merkle_store = merkle_store.write().expect("merkle_store write lock not poisoned"); - insert_storage_map_nodes(&mut merkle_store, account.storage()); - insert_asset_nodes(&mut merkle_store, account.vault()); + let mut smt_forest = smt_forest.write().expect("smt_forest write lock not poisoned"); + insert_storage_map_nodes(&mut smt_forest, account.storage()); + insert_asset_nodes(&mut smt_forest, account.vault()); Ok(()) } pub(crate) fn update_account( conn: &mut Connection, - merkle_store: &Arc>, + smt_forest: &Arc>, new_account_state: &Account, ) -> Result<(), StoreError> { const QUERY: &str = "SELECT id FROM accounts WHERE id = ?"; @@ -242,9 +243,9 @@ impl SqliteStore { return Err(StoreError::AccountDataNotFound(new_account_state.id())); } - let mut merkle_store = merkle_store.write().expect("merkle_store write lock not poisoned"); + let mut smt_forest = smt_forest.write().expect("smt_forest write lock not poisoned"); let tx = conn.transaction().into_store_error()?; - Self::update_account_state(&tx, &mut merkle_store, new_account_state)?; + Self::update_account_state(&tx, &mut smt_forest, new_account_state)?; tx.commit().into_store_error() } @@ -330,10 +331,10 @@ impl SqliteStore { } /// Fetches a specific asset from the account's vault without the need of loading the entire - /// vault. The Merkle proof is also retrieved from the [`MerkleStore`]. + /// vault. The Merkle proof is also retrieved from the [`SmtForest`]. pub(crate) fn get_account_asset( conn: &mut Connection, - merkle_store: &Arc>, + smt_forest: &Arc>, account_id: AccountId, faucet_id_prefix: AccountIdPrefix, ) -> Result, StoreError> { @@ -351,9 +352,9 @@ impl SqliteStore { return Ok(None); }; - let merkle_store = merkle_store.read().expect("merkle_store read lock not poisoned"); + let smt_forest = smt_forest.read().expect("smt_forest read lock not poisoned"); - let proof = get_asset_proof(&merkle_store, header.vault_root(), &asset)?; + let proof = get_asset_proof(&smt_forest, header.vault_root(), &asset)?; let witness = AssetWitness::new(proof)?; Ok(Some((asset, witness))) @@ -363,7 +364,7 @@ impl SqliteStore { /// The Merkle proof is also retrieved from the [`MerkleStore`]. pub(crate) fn get_account_map_item( conn: &mut Connection, - merkle_store: &Arc>, + smt_forest: &Arc>, account_id: AccountId, index: u8, key: Word, @@ -384,10 +385,10 @@ impl SqliteStore { }; let item = map.get(&key); - let merkle_store = merkle_store.read().expect("merkle_store read lock not poisoned"); + let smt_forest = smt_forest.read().expect("smt_forest read lock not poisoned"); // TODO: change the api of get_storage_map_item_proof - let path = get_storage_map_item_proof(&merkle_store, map.root(), key)?.1.try_into()?; + let path = get_storage_map_item_proof(&smt_forest, map.root(), key)?.1.try_into()?; let leaf = SmtLeaf::new_single(StorageMap::hash_key(key), item); let proof = SmtProof::new(path, leaf)?; @@ -444,7 +445,7 @@ impl SqliteStore { /// `updated_fungible_assets` and `updated_storage_maps` parameters. pub(super) fn apply_account_delta( tx: &Transaction<'_>, - merkle_store: &mut MerkleStore, + smt_forest: &mut SmtForest, init_account_state: &AccountHeader, final_account_state: &AccountHeader, mut updated_fungible_assets: BTreeMap, @@ -524,7 +525,7 @@ impl SqliteStore { .into_store_error()?; update_asset_nodes( - merkle_store, + smt_forest, init_account_state.vault_root(), updated_assets.values().copied(), )?; @@ -546,7 +547,7 @@ impl SqliteStore { let mut map = updated_storage_maps.remove(index).unwrap_or_default(); update_storage_map_nodes( - merkle_store, + smt_forest, map.root(), map_delta.entries().iter().map(|(key, value)| ((*key).into(), *value)), )?; @@ -700,16 +701,16 @@ impl SqliteStore { /// We can later identify the proper account state by looking at the nonce. pub(super) fn update_account_state( tx: &Transaction<'_>, - merkle_store: &mut MerkleStore, + smt_forest: &mut SmtForest, new_account_state: &Account, ) -> Result<(), StoreError> { - insert_storage_map_nodes(merkle_store, new_account_state.storage()); + insert_storage_map_nodes(smt_forest, new_account_state.storage()); Self::insert_storage_slots( tx, new_account_state.storage().commitment(), new_account_state.storage().slots().iter().enumerate(), )?; - insert_asset_nodes(merkle_store, new_account_state.vault()); + insert_asset_nodes(smt_forest, new_account_state.vault()); Self::insert_assets( tx, new_account_state.vault().root(), @@ -1271,16 +1272,16 @@ mod tests { let account_id = account.id(); let final_state: AccountHeader = (&account_after_delta).into(); - let merkle_store = store.merkle_store.clone(); + let smt_forest = store.smt_forest.clone(); store .interact_with_connection(move |conn| { let tx = conn.transaction().into_store_error()?; - let mut merkle_store = - merkle_store.write().expect("merkle_store write lock not poisoned"); + let mut smt_forest = + smt_forest.write().expect("smt_forest write lock not poisoned"); SqliteStore::apply_account_delta( &tx, - &mut merkle_store, + &mut smt_forest, &account.into(), &final_state, BTreeMap::default(), @@ -1350,7 +1351,7 @@ mod tests { let account_id = account.id(); let final_state: AccountHeader = (&account_after_delta).into(); - let merkle_store = store.merkle_store.clone(); + let smt_forest = store.smt_forest.clone(); store .interact_with_connection(move |conn| { let fungible_assets = SqliteStore::get_account_fungible_assets_for_delta( @@ -1364,12 +1365,12 @@ mod tests { &delta, )?; let tx = conn.transaction().into_store_error()?; - let mut merkle_store = - merkle_store.write().expect("merkle_store write lock not poisoned"); + let mut smt_forest = + smt_forest.write().expect("smt_forest write lock not poisoned"); SqliteStore::apply_account_delta( &tx, - &mut merkle_store, + &mut smt_forest, &account.into(), &final_state, fungible_assets, diff --git a/crates/sqlite-store/src/lib.rs b/crates/sqlite-store/src/lib.rs index d678dbc52..52f1bab80 100644 --- a/crates/sqlite-store/src/lib.rs +++ b/crates/sqlite-store/src/lib.rs @@ -31,7 +31,7 @@ use miden_client::account::{ }; use miden_client::asset::{Asset, AssetVault, AssetWitness}; use miden_client::block::BlockHeader; -use miden_client::crypto::{InOrderIndex, MerkleStore, MmrPeaks}; +use miden_client::crypto::{InOrderIndex, MmrPeaks}; use miden_client::note::{BlockNumber, NoteScript, NoteTag, Nullifier}; use miden_client::store::{ AccountRecord, @@ -48,6 +48,7 @@ use miden_client::store::{ use miden_client::sync::{NoteTagRecord, StateSyncUpdate}; use miden_client::transaction::{TransactionRecord, TransactionStoreUpdate}; use miden_objects::account::StorageMapWitness; +use miden_objects::crypto::merkle::SmtForest; use rusqlite::Connection; use rusqlite::types::Value; use sql_error::SqlResultExt; @@ -75,7 +76,7 @@ pub use builder::ClientBuilderSqliteExt; /// Current table definitions can be found at `store.sql` migration file. pub struct SqliteStore { pub(crate) pool: Pool, - merkle_store: Arc>, + smt_forest: Arc>, } impl SqliteStore { @@ -98,7 +99,7 @@ impl SqliteStore { let store = SqliteStore { pool, - merkle_store: Arc::new(RwLock::new(MerkleStore::new())), + smt_forest: Arc::new(RwLock::new(SmtForest::new())), }; // Initialize merkle store @@ -106,10 +107,9 @@ impl SqliteStore { let vault = store.get_account_vault(id).await?; let storage = store.get_account_storage(id).await?; - let mut merkle_store = - store.merkle_store.write().expect("merkle_store write lock not poisoned"); - insert_asset_nodes(&mut merkle_store, &vault); - insert_storage_map_nodes(&mut merkle_store, &storage); + let mut smt_forest = store.smt_forest.write().expect("smt write lock not poisoned"); + insert_asset_nodes(&mut smt_forest, &vault); + insert_storage_map_nodes(&mut smt_forest, &storage); } Ok(store) @@ -169,9 +169,9 @@ impl Store for SqliteStore { } async fn apply_state_sync(&self, state_sync_update: StateSyncUpdate) -> Result<(), StoreError> { - let merkle_store = self.merkle_store.clone(); + let smt_forest = self.smt_forest.clone(); self.interact_with_connection(move |conn| { - SqliteStore::apply_state_sync(conn, &merkle_store, state_sync_update) + SqliteStore::apply_state_sync(conn, &smt_forest, state_sync_update) }) .await } @@ -187,9 +187,9 @@ impl Store for SqliteStore { } async fn apply_transaction(&self, tx_update: TransactionStoreUpdate) -> Result<(), StoreError> { - let merkle_store = self.merkle_store.clone(); + let smt_forest = self.smt_forest.clone(); self.interact_with_connection(move |conn| { - SqliteStore::apply_transaction(conn, &merkle_store, &tx_update) + SqliteStore::apply_transaction(conn, &smt_forest, &tx_update) }) .await } @@ -304,17 +304,17 @@ impl Store for SqliteStore { initial_address: Address, ) -> Result<(), StoreError> { let cloned_account = account.clone(); - let merkle_store = self.merkle_store.clone(); + let smt_forest = self.smt_forest.clone(); self.interact_with_connection(move |conn| { - SqliteStore::insert_account(conn, &merkle_store, &cloned_account, &initial_address) + SqliteStore::insert_account(conn, &smt_forest, &cloned_account, &initial_address) }) .await } async fn update_account(&self, account: &Account) -> Result<(), StoreError> { let cloned_account = account.clone(); - let merkle_store = self.merkle_store.clone(); + let merkle_store = self.smt_forest.clone(); self.interact_with_connection(move |conn| { SqliteStore::update_account(conn, &merkle_store, &cloned_account) @@ -411,9 +411,9 @@ impl Store for SqliteStore { account_id: AccountId, faucet_id_prefix: AccountIdPrefix, ) -> Result, StoreError> { - let merkle_store = self.merkle_store.clone(); + let smt_forest = self.smt_forest.clone(); self.interact_with_connection(move |conn| { - SqliteStore::get_account_asset(conn, &merkle_store, account_id, faucet_id_prefix) + SqliteStore::get_account_asset(conn, &smt_forest, account_id, faucet_id_prefix) }) .await } @@ -434,10 +434,10 @@ impl Store for SqliteStore { index: u8, key: Word, ) -> Result<(Word, StorageMapWitness), StoreError> { - let merkle_store = self.merkle_store.clone(); + let smt_forest = self.smt_forest.clone(); self.interact_with_connection(move |conn| { - SqliteStore::get_account_map_item(conn, &merkle_store, account_id, index, key) + SqliteStore::get_account_map_item(conn, &smt_forest, account_id, index, key) }) .await } diff --git a/crates/sqlite-store/src/merkle_store.rs b/crates/sqlite-store/src/merkle_store.rs index d6d4a6f72..765772b45 100644 --- a/crates/sqlite-store/src/merkle_store.rs +++ b/crates/sqlite-store/src/merkle_store.rs @@ -1,21 +1,18 @@ use miden_client::Word; use miden_client::account::{AccountStorage, StorageMap, StorageSlot}; use miden_client::asset::{Asset, AssetVault}; -use miden_client::crypto::{MerklePath, MerkleStore, NodeIndex, SMT_DEPTH, SmtLeaf, SmtProof}; +use miden_client::crypto::{MerklePath, SMT_DEPTH, SmtLeaf, SmtProof}; use miden_client::store::StoreError; use miden_objects::asset::AssetVaultKey; -use miden_objects::crypto::merkle::Smt; +use miden_objects::crypto::merkle::{EmptySubtreeRoots, Smt, SmtForest}; /// Retrieves the Merkle proof for a specific asset in the merkle store. pub fn get_asset_proof( - merkle_store: &MerkleStore, + smt_forest: &SmtForest, vault_root: Word, asset: &Asset, ) -> Result { - let path = merkle_store - .get_path(vault_root, get_node_index(asset.vault_key())?)? - .path - .try_into()?; + let path = smt_forest.open(vault_root, asset.vault_key().into())?.path().clone(); let vault_key: Word = asset.vault_key().into(); let leaf = SmtLeaf::new_single(vault_key, (*asset).into()); @@ -24,63 +21,64 @@ pub fn get_asset_proof( /// Updates the merkle store with the new asset values. pub fn update_asset_nodes( - merkle_store: &mut MerkleStore, - mut root: Word, + smt_forest: &mut SmtForest, + root: Word, assets: impl Iterator, ) -> Result { - for asset in assets { - root = merkle_store - .set_node( - root, - get_node_index(asset.vault_key())?, - get_node_value(asset.vault_key(), asset.into()), - )? - .root; - } + let entries: Vec<(Word, Word)> = assets + .map(|asset| { + let key: Word = asset.vault_key().into(); + let value: Word = asset.into(); + (key, value) + }) + .collect(); - Ok(root) + let empty_root = *EmptySubtreeRoots::entry(SMT_DEPTH, 0); + let new_root = smt_forest.batch_insert(empty_root, entries).map_err(StoreError::from)?; + debug_assert_eq!(new_root, root); + Ok(new_root) } /// Inserts the asset vault SMT nodes to the merkle store. -pub fn insert_asset_nodes(merkle_store: &mut MerkleStore, vault: &AssetVault) { +pub fn insert_asset_nodes(smt_forest: &mut SmtForest, vault: &AssetVault) { // We need to build the SMT from the vault iterable entries as // we don't have direct access to the vault's SMT nodes. // Safe unwrap as we are sure that the vault's SMT nodes are valid. let smt = Smt::with_entries(vault.assets().map(|asset| (asset.vault_key().into(), asset.into()))) .unwrap(); - merkle_store.extend(smt.inner_nodes()); + + let empty_root = *EmptySubtreeRoots::entry(SMT_DEPTH, 0); + let entries: Vec<(Word, Word)> = smt.entries().map(|(k, v)| (*k, *v)).collect(); + let new_root = smt_forest.batch_insert(empty_root, entries).unwrap(); // TODO: handle unwrap + debug_assert_eq!(new_root, smt.root()); } /// Retrieves the Merkle proof for a specific storage map item in the merkle store. pub fn get_storage_map_item_proof( - merkle_store: &MerkleStore, + smt_forest: &SmtForest, map_root: Word, key: Word, ) -> Result<(Word, MerklePath), StoreError> { let hashed_key = AssetVaultKey::new_unchecked(StorageMap::hash_key(key)); - let vp = merkle_store.get_path(map_root, get_node_index(hashed_key)?)?; - Ok((vp.value, vp.path)) + let proof = smt_forest.open(map_root, hashed_key.into()).map_err(StoreError::from)?; + Ok((proof.compute_root(), proof.path().clone().into())) } -/// Updates the merkle store with the new storage map entries. pub fn update_storage_map_nodes( - merkle_store: &mut MerkleStore, - mut root: Word, - entries: impl Iterator, + smt_forest: &mut SmtForest, + root: Word, + entries: impl Iterator + Clone, ) -> Result { - for (key, value) in entries { - let hashed_key = AssetVaultKey::new_unchecked(StorageMap::hash_key(key)); - root = merkle_store - .set_node(root, get_node_index(hashed_key)?, get_node_value(hashed_key, value))? - .root; - } - - Ok(root) + let empty_root = *EmptySubtreeRoots::entry(SMT_DEPTH, 0); + let new_root = smt_forest.batch_insert(empty_root, entries).map_err(StoreError::from)?; + // Resulting root should match the map's root + debug_assert_eq!(new_root, root); + Ok(new_root) } /// Inserts all storage map SMT nodes to the merkle store. -pub fn insert_storage_map_nodes(merkle_store: &mut MerkleStore, storage: &AccountStorage) { +pub fn insert_storage_map_nodes(smt_forest: &mut SmtForest, storage: &AccountStorage) { let maps = storage.slots().iter().filter_map(|slot| { if let StorageSlot::Map(map) = slot { Some(map) @@ -90,30 +88,10 @@ pub fn insert_storage_map_nodes(merkle_store: &mut MerkleStore, storage: &Accoun }); for map in maps { - merkle_store.extend(map.inner_nodes()); + let empty_root = *EmptySubtreeRoots::entry(SMT_DEPTH, 0); + let entries: Vec<(Word, Word)> = map.entries().map(|(k, v)| (*k, *v)).collect(); + let new_root = smt_forest.batch_insert(empty_root, entries).unwrap(); // TODO: handle unwrap + // Resulting root should match the map's root + debug_assert_eq!(new_root, map.root()); } } - -// HELPERS -// ================================================================================================ - -/// Builds the merkle node index for the given key. -/// -/// This logic is based on the way [`miden_objects::crypto::merkle::Smt`] is structured internally. -/// It has a set depth and uses the third felt as the position. The reason we want to copy the smt's -/// internal structure is so that merkle paths and roots match. For more information, see the -/// [`miden_objects::crypto::merkle::Smt`] documentation and implementation. -fn get_node_index(key: AssetVaultKey) -> Result { - let vault_key_word: Word = key.into(); - Ok(NodeIndex::new(SMT_DEPTH, vault_key_word[3].as_int())?) -} - -/// Builds the merkle node value for the given key and value. -/// -/// This logic is based on the way [`miden_objects::crypto::merkle::Smt`] generates the values for -/// its internal merkle tree. It generates an [`SmtLeaf`] from the key and value, and then hashes it -/// to produce the node value. -fn get_node_value(key: AssetVaultKey, value: Word) -> Word { - let vault_key_word: Word = key.into(); - SmtLeaf::Single((vault_key_word, value)).hash() -} diff --git a/crates/sqlite-store/src/sync.rs b/crates/sqlite-store/src/sync.rs index 497a1ee23..95393c319 100644 --- a/crates/sqlite-store/src/sync.rs +++ b/crates/sqlite-store/src/sync.rs @@ -5,11 +5,11 @@ use std::sync::{Arc, RwLock}; use std::vec::Vec; use miden_client::Word; -use miden_client::crypto::MerkleStore; use miden_client::note::{BlockNumber, NoteTag}; use miden_client::store::StoreError; use miden_client::sync::{NoteTagRecord, NoteTagSource, StateSyncUpdate}; use miden_client::utils::{Deserializable, Serializable}; +use miden_objects::crypto::merkle::SmtForest; use rusqlite::{Connection, Transaction, params}; use super::SqliteStore; @@ -99,7 +99,7 @@ impl SqliteStore { pub(super) fn apply_state_sync( conn: &mut Connection, - merkle_store: &Arc>, + smt_forest: &Arc>, state_sync_update: StateSyncUpdate, ) -> Result<(), StoreError> { let StateSyncUpdate { @@ -169,11 +169,11 @@ impl SqliteStore { Self::undo_account_state(&tx, &account_hashes_to_delete)?; // Update public accounts on the db that have been updated onchain - let mut merkle_store = merkle_store.write().expect("merkle_store write lock not poisoned"); + let mut smt_forest = smt_forest.write().expect("smt_forest write lock not poisoned"); for account in account_updates.updated_public_accounts() { - Self::update_account_state(&tx, &mut merkle_store, account)?; + Self::update_account_state(&tx, &mut smt_forest, account)?; } - drop(merkle_store); + drop(smt_forest); for (account_id, digest) in account_updates.mismatched_private_accounts() { Self::lock_account_on_unexpected_commitment(&tx, account_id, digest)?; diff --git a/crates/sqlite-store/src/transaction.rs b/crates/sqlite-store/src/transaction.rs index 4c7806e89..628a54009 100644 --- a/crates/sqlite-store/src/transaction.rs +++ b/crates/sqlite-store/src/transaction.rs @@ -6,7 +6,6 @@ use std::sync::{Arc, RwLock}; use std::vec::Vec; use miden_client::Word; -use miden_client::crypto::MerkleStore; use miden_client::note::ToInputNoteCommitments; use miden_client::store::{StoreError, TransactionFilter}; use miden_client::transaction::{ @@ -17,6 +16,7 @@ use miden_client::transaction::{ TransactionStoreUpdate, }; use miden_client::utils::{Deserializable as _, Serializable as _}; +use miden_objects::crypto::merkle::SmtForest; use rusqlite::types::Value; use rusqlite::{Connection, Transaction, params}; @@ -106,7 +106,7 @@ impl SqliteStore { /// Inserts a transaction and updates the current state based on the `tx_result` changes. pub fn apply_transaction( conn: &mut Connection, - merkle_store: &Arc>, + smt_forest: &Arc>, tx_update: &TransactionStoreUpdate, ) -> Result<(), StoreError> { let executed_transaction = tx_update.executed_transaction(); @@ -157,17 +157,17 @@ impl SqliteStore { upsert_transaction_record(&tx, &transaction_record)?; // Account Data - let mut merkle_store = merkle_store.write().expect("merkle_store write lock not poisoned"); + let mut smt_forest = smt_forest.write().expect("smt_forest write lock not poisoned"); Self::apply_account_delta( &tx, - &mut merkle_store, + &mut smt_forest, &executed_transaction.initial_account().into(), executed_transaction.final_account(), updated_fungible_assets, updated_storage_maps, executed_transaction.account_delta(), )?; - drop(merkle_store); + drop(smt_forest); // Note Updates apply_note_updates_tx(&tx, tx_update.note_updates())?; From edb086e369a547efc61b3ca91edacebd41b8f5cd Mon Sep 17 00:00:00 2001 From: Juan Munoz Date: Fri, 14 Nov 2025 15:32:51 -0300 Subject: [PATCH 2/6] fix: more failing tests --- crates/sqlite-store/src/merkle_store.rs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/crates/sqlite-store/src/merkle_store.rs b/crates/sqlite-store/src/merkle_store.rs index 765772b45..4451fa8c0 100644 --- a/crates/sqlite-store/src/merkle_store.rs +++ b/crates/sqlite-store/src/merkle_store.rs @@ -33,9 +33,7 @@ pub fn update_asset_nodes( }) .collect(); - let empty_root = *EmptySubtreeRoots::entry(SMT_DEPTH, 0); - let new_root = smt_forest.batch_insert(empty_root, entries).map_err(StoreError::from)?; - debug_assert_eq!(new_root, root); + let new_root = smt_forest.batch_insert(root, entries).map_err(StoreError::from)?; Ok(new_root) } @@ -72,8 +70,6 @@ pub fn update_storage_map_nodes( ) -> Result { let empty_root = *EmptySubtreeRoots::entry(SMT_DEPTH, 0); let new_root = smt_forest.batch_insert(empty_root, entries).map_err(StoreError::from)?; - // Resulting root should match the map's root - debug_assert_eq!(new_root, root); Ok(new_root) } @@ -90,8 +86,6 @@ pub fn insert_storage_map_nodes(smt_forest: &mut SmtForest, storage: &AccountSto for map in maps { let empty_root = *EmptySubtreeRoots::entry(SMT_DEPTH, 0); let entries: Vec<(Word, Word)> = map.entries().map(|(k, v)| (*k, *v)).collect(); - let new_root = smt_forest.batch_insert(empty_root, entries).unwrap(); // TODO: handle unwrap - // Resulting root should match the map's root - debug_assert_eq!(new_root, map.root()); + smt_forest.batch_insert(empty_root, entries).unwrap(); // TODO: handle unwrap } } From 220f1ab12cf0e1af0ab5ef9d3878e2155c970759 Mon Sep 17 00:00:00 2001 From: Juan Munoz Date: Fri, 14 Nov 2025 17:18:37 -0300 Subject: [PATCH 3/6] chore: rename module --- crates/sqlite-store/src/account.rs | 9 +-------- crates/sqlite-store/src/lib.rs | 4 ++-- .../sqlite-store/src/{merkle_store.rs => smt_forest.rs} | 3 +-- 3 files changed, 4 insertions(+), 12 deletions(-) rename crates/sqlite-store/src/{merkle_store.rs => smt_forest.rs} (95%) diff --git a/crates/sqlite-store/src/account.rs b/crates/sqlite-store/src/account.rs index 2ea01adfc..16091d9e6 100644 --- a/crates/sqlite-store/src/account.rs +++ b/crates/sqlite-store/src/account.rs @@ -32,7 +32,7 @@ use rusqlite::types::Value; use rusqlite::{Connection, Params, Transaction, named_params, params}; use super::{SqliteStore, column_value_as_u64, u64_to_value}; -use crate::merkle_store::{ +use crate::smt_forest::{ get_asset_proof, get_storage_map_item_proof, insert_asset_nodes, @@ -692,13 +692,6 @@ impl SqliteStore { // HELPERS // -------------------------------------------------------------------------------------------- - /// Update previously-existing account after a transaction execution. Apart from updating the - /// `SQLite` database, this function also updates the [`MerkleStore`] by adding the vault and - /// storage SMT's nodes. - /// - /// Because the Client retrieves the account by account ID before applying the delta, we don't - /// need to check that it exists here. This inserts a new row into the accounts table. - /// We can later identify the proper account state by looking at the nonce. pub(super) fn update_account_state( tx: &Transaction<'_>, smt_forest: &mut SmtForest, diff --git a/crates/sqlite-store/src/lib.rs b/crates/sqlite-store/src/lib.rs index 52f1bab80..419adc8b7 100644 --- a/crates/sqlite-store/src/lib.rs +++ b/crates/sqlite-store/src/lib.rs @@ -53,13 +53,13 @@ use rusqlite::Connection; use rusqlite::types::Value; use sql_error::SqlResultExt; -use crate::merkle_store::{insert_asset_nodes, insert_storage_map_nodes}; +use crate::smt_forest::{insert_asset_nodes, insert_storage_map_nodes}; mod account; mod builder; mod chain_data; mod db_management; -mod merkle_store; +mod smt_forest; mod note; mod sql_error; mod sync; diff --git a/crates/sqlite-store/src/merkle_store.rs b/crates/sqlite-store/src/smt_forest.rs similarity index 95% rename from crates/sqlite-store/src/merkle_store.rs rename to crates/sqlite-store/src/smt_forest.rs index 4451fa8c0..ad420d351 100644 --- a/crates/sqlite-store/src/merkle_store.rs +++ b/crates/sqlite-store/src/smt_forest.rs @@ -68,8 +68,7 @@ pub fn update_storage_map_nodes( root: Word, entries: impl Iterator + Clone, ) -> Result { - let empty_root = *EmptySubtreeRoots::entry(SMT_DEPTH, 0); - let new_root = smt_forest.batch_insert(empty_root, entries).map_err(StoreError::from)?; + let new_root = smt_forest.batch_insert(root, entries).map_err(StoreError::from)?; Ok(new_root) } From cab65be74f36d971a403aa514aa8fa6555f7a461 Mon Sep 17 00:00:00 2001 From: Juan Munoz Date: Wed, 26 Nov 2025 16:01:16 -0300 Subject: [PATCH 4/6] fix: failing test --- crates/sqlite-store/src/smt_forest.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/crates/sqlite-store/src/smt_forest.rs b/crates/sqlite-store/src/smt_forest.rs index ad420d351..d9dd44147 100644 --- a/crates/sqlite-store/src/smt_forest.rs +++ b/crates/sqlite-store/src/smt_forest.rs @@ -33,7 +33,9 @@ pub fn update_asset_nodes( }) .collect(); - let new_root = smt_forest.batch_insert(root, entries).map_err(StoreError::from)?; + smt_forest.pop_smts([root]); + let empty_root = *EmptySubtreeRoots::entry(SMT_DEPTH, 0); + let new_root = smt_forest.batch_insert(empty_root, entries).map_err(StoreError::from)?; Ok(new_root) } @@ -68,7 +70,11 @@ pub fn update_storage_map_nodes( root: Word, entries: impl Iterator + Clone, ) -> Result { - let new_root = smt_forest.batch_insert(root, entries).map_err(StoreError::from)?; + println!("In update storage map nodes"); + smt_forest.pop_smts([root]); + let empty_root = *EmptySubtreeRoots::entry(SMT_DEPTH, 0); + let new_root = smt_forest.batch_insert(empty_root, entries).map_err(StoreError::from)?; + println!("Funciono!"); Ok(new_root) } From ee21605f3cf5e9b00ba2c642a9733904c0d606a2 Mon Sep 17 00:00:00 2001 From: Juan Munoz Date: Wed, 26 Nov 2025 16:07:32 -0300 Subject: [PATCH 5/6] chore: fix failing CI --- CHANGELOG.md | 1 + crates/sqlite-store/src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5915c7abd..7d75385ae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ * Fixed a bug where insertions in the `Addresses` table in the IndexedDB Store resulted in the `id` and `address` fields being inverted with each other ([#1532](https://github.com/0xMiden/miden-client/pull/1532)). * Added doc_cfg as top level cfg_attr to turn on feature annotations in docs.rs and added make targets to serve the docs ([#1543](https://github.com/0xMiden/miden-client/pull/1543)). +* Updated `SqliteStore`: replaced `MerkleStore` with `SmtForest` ([#1526](https://github.com/0xMiden/miden-client/pull/1526)). ## Miden Client CLI - 0.12.4 (2025-11-17) diff --git a/crates/sqlite-store/src/lib.rs b/crates/sqlite-store/src/lib.rs index 419adc8b7..22d18a7dc 100644 --- a/crates/sqlite-store/src/lib.rs +++ b/crates/sqlite-store/src/lib.rs @@ -59,8 +59,8 @@ mod account; mod builder; mod chain_data; mod db_management; -mod smt_forest; mod note; +mod smt_forest; mod sql_error; mod sync; mod transaction; From 30240968eee5dae342ae4db191bd45cb719d74cb Mon Sep 17 00:00:00 2001 From: Juan Munoz Date: Wed, 26 Nov 2025 17:11:01 -0300 Subject: [PATCH 6/6] chore: remove prints --- crates/sqlite-store/src/smt_forest.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/crates/sqlite-store/src/smt_forest.rs b/crates/sqlite-store/src/smt_forest.rs index d9dd44147..a11f12f63 100644 --- a/crates/sqlite-store/src/smt_forest.rs +++ b/crates/sqlite-store/src/smt_forest.rs @@ -70,11 +70,9 @@ pub fn update_storage_map_nodes( root: Word, entries: impl Iterator + Clone, ) -> Result { - println!("In update storage map nodes"); smt_forest.pop_smts([root]); let empty_root = *EmptySubtreeRoots::entry(SMT_DEPTH, 0); let new_root = smt_forest.batch_insert(empty_root, entries).map_err(StoreError::from)?; - println!("Funciono!"); Ok(new_root) }