From 4e898829c87a42db114290e979521bdd94e4bd33 Mon Sep 17 00:00:00 2001 From: Bernhard Schuster Date: Fri, 27 Feb 2026 13:19:22 +0100 Subject: [PATCH 1/2] misc --- bin/node/src/commands/mod.rs | 2 +- .../store/src/db/models/queries/accounts.rs | 1 + crates/store/src/inner_forest/mod.rs | 103 ++++++++---------- crates/store/src/state/apply_block.rs | 1 + crates/store/src/state/loader.rs | 1 - crates/validator/src/lib.rs | 2 +- crates/validator/src/signers/mod.rs | 4 +- 7 files changed, 53 insertions(+), 61 deletions(-) diff --git a/bin/node/src/commands/mod.rs b/bin/node/src/commands/mod.rs index b7ef3c3c5..3ec8572d7 100644 --- a/bin/node/src/commands/mod.rs +++ b/bin/node/src/commands/mod.rs @@ -121,7 +121,7 @@ pub struct BundledValidatorConfig { } impl BundledValidatorConfig { - /// Converts the [`ValidatorConfig`] into a URL and an optional [`SocketAddr`]. + /// Converts the [`BundledValidatorConfig`] into a URL and an optional [`SocketAddr`]. /// /// If the `validator_url` is set, it returns the URL and `None` for the [`SocketAddr`]. /// diff --git a/crates/store/src/db/models/queries/accounts.rs b/crates/store/src/db/models/queries/accounts.rs index ee0c5747f..1b6445c8e 100644 --- a/crates/store/src/db/models/queries/accounts.rs +++ b/crates/store/src/db/models/queries/accounts.rs @@ -1113,6 +1113,7 @@ pub(crate) fn upsert_accounts( .execute(conn)?; // insert pending storage map entries + // TODO consider batching for (acc_id, slot_name, key, value) in pending_storage_inserts { insert_account_storage_map_value(conn, acc_id, block_num, slot_name, key, value)?; } diff --git a/crates/store/src/inner_forest/mod.rs b/crates/store/src/inner_forest/mod.rs index 3c22684e7..62f7704b2 100644 --- a/crates/store/src/inner_forest/mod.rs +++ b/crates/store/src/inner_forest/mod.rs @@ -268,76 +268,64 @@ impl InnerForest { let account_id = delta.id(); let is_full_state = delta.is_full_state(); + #[cfg(debug_assertions)] if is_full_state { - self.insert_account_vault(block_num, account_id, delta.vault()); + let has_vault_root = self.vault_roots.keys().any(|(id, _)| *id == account_id); + let has_storage_root = self.storage_map_roots.keys().any(|(id, ..)| *id == account_id); + let has_storage_entries = self.storage_entries.keys().any(|(id, ..)| *id == account_id); + + assert!( + !has_vault_root && !has_storage_root && !has_storage_entries, + "full-state delta should not be applied to existing account" + ); + } + + if is_full_state { + self.insert_account_vault(block_num, account_id, delta.vault())?; } else if !delta.vault().is_empty() { - self.update_account_vault(block_num, account_id, delta.vault())?; + self.update_account_vault(block_num, account_id, delta.vault(), false)?; } if is_full_state { - self.insert_account_storage(block_num, account_id, delta.storage()); + self.insert_account_storage(block_num, account_id, delta.storage())?; } else if !delta.storage().is_empty() { - self.update_account_storage(block_num, account_id, delta.storage()); + self.update_account_storage(block_num, account_id, delta.storage(), false)?; } Ok(()) } - // ASSET VAULT DELTA PROCESSING - // -------------------------------------------------------------------------------------------- - - /// Retrieves the most recent vault SMT root for an account. If no vault root is found for the - /// account, returns an empty SMT root. - fn get_latest_vault_root(&self, account_id: AccountId) -> Word { - self.vault_roots - .range((account_id, BlockNumber::GENESIS)..=(account_id, BlockNumber::MAX)) - .next_back() - .map_or_else(Self::empty_smt_root, |(_, root)| *root) - } - - /// Inserts asset vault data into the forest for the specified account. Assumes that asset - /// vault for this account does not yet exist in the forest. fn insert_account_vault( &mut self, block_num: BlockNumber, account_id: AccountId, - delta: &AccountVaultDelta, - ) { - // get the current vault root for the account, and make sure it is empty + vault_delta: &AccountVaultDelta, + ) -> Result<(), InnerForestError> { let prev_root = self.get_latest_vault_root(account_id); assert_eq!(prev_root, Self::empty_smt_root(), "account should not be in the forest"); - // if there are no assets in the vault, add a root of an empty SMT to the vault roots map - // so that the map has entries for all accounts, and then return (i.e., no need to insert - // anything into the forest) - if delta.is_empty() { + if vault_delta.is_empty() { self.vault_roots.insert((account_id, block_num), prev_root); - return; + return Ok(()); } let mut entries: Vec<(Word, Word)> = Vec::new(); - // process fungible assets - for (faucet_id, amount_delta) in delta.fungible().iter() { + for (faucet_id, amount_delta) in vault_delta.fungible().iter() { let amount = (*amount_delta).try_into().expect("full-state amount should be non-negative"); - let asset = FungibleAsset::new(*faucet_id, amount).expect("valid faucet id"); + let asset = FungibleAsset::new(*faucet_id, amount)?; entries.push((asset.vault_key().into(), asset.into())); } - // process non-fungible assets - for (&asset, _action) in delta.non_fungible().iter() { - // TODO: assert that action is addition + for (&asset, action) in vault_delta.non_fungible().iter() { + debug_assert_eq!(action, &NonFungibleDeltaAction::Add); entries.push((asset.vault_key().into(), asset.into())); } - assert!(!entries.is_empty(), "non-empty delta should contain entries"); let num_entries = entries.len(); - let new_root = self - .forest - .batch_insert(prev_root, entries) - .expect("forest insertion should succeed"); + let new_root = self.forest.batch_insert(prev_root, entries)?; self.vault_roots.insert((account_id, block_num), new_root); @@ -363,19 +351,20 @@ impl InnerForest { &mut self, block_num: BlockNumber, account_id: AccountId, - delta: &AccountVaultDelta, - ) -> Result<(), InnerForestError> { - assert!(!delta.is_empty(), "expected the delta not to be empty"); - - // get the previous vault root; the root could be for an empty or non-empty SMT - let prev_root = self.get_latest_vault_root(account_id); + vault_delta: &AccountVaultDelta, + is_full_state: bool, + ) -> Result { + let prev_root = if is_full_state { + Self::empty_smt_root() + } else { + self.get_latest_vault_root(account_id) + }; let mut entries: Vec<(Word, Word)> = Vec::new(); // Process fungible assets - for (faucet_id, amount_delta) in delta.fungible().iter() { - let key: Word = - FungibleAsset::new(*faucet_id, 0).expect("valid faucet id").vault_key().into(); + for (faucet_id, amount_delta) in vault_delta.fungible().iter() { + let key: Word = FungibleAsset::new(*faucet_id, 0)?.vault_key().into(); let new_amount = { // amount delta is a change that must be applied to previous balance. @@ -402,13 +391,13 @@ impl InnerForest { let value = if new_amount == 0 { EMPTY_WORD } else { - FungibleAsset::new(*faucet_id, new_amount).expect("valid fungible asset").into() + FungibleAsset::new(*faucet_id, new_amount)?.into() }; entries.push((key, value)); } // Process non-fungible assets - for (asset, action) in delta.non_fungible().iter() { + for (asset, action) in vault_delta.non_fungible().iter() { let value = match action { NonFungibleDeltaAction::Add => Word::from(Asset::NonFungible(*asset)), NonFungibleDeltaAction::Remove => EMPTY_WORD, @@ -416,13 +405,16 @@ impl InnerForest { entries.push((asset.vault_key().into(), value)); } - assert!(!entries.is_empty(), "non-empty delta should contain entries"); + if entries.is_empty() { + if is_full_state { + self.vault_roots.insert((account_id, block_num), prev_root); + } + return Ok(prev_root); + } + let num_entries = entries.len(); - let new_root = self - .forest - .batch_insert(prev_root, entries) - .expect("forest insertion should succeed"); + let new_root = self.forest.batch_insert(prev_root, entries)?; self.vault_roots.insert((account_id, block_num), new_root); @@ -433,14 +425,13 @@ impl InnerForest { vault_entries = num_entries, "Updated vault in forest" ); - Ok(()) + Ok(new_root) } // STORAGE MAP DELTA PROCESSING // -------------------------------------------------------------------------------------------- - /// Retrieves the most recent storage map SMT root for an account slot. If no storage root is - /// found for the slot, returns an empty SMT root. + /// Retrieves the most recent storage map SMT root for an account slot. fn get_latest_storage_map_root( &self, account_id: AccountId, diff --git a/crates/store/src/state/apply_block.rs b/crates/store/src/state/apply_block.rs index 145432c97..7949fcbeb 100644 --- a/crates/store/src/state/apply_block.rs +++ b/crates/store/src/state/apply_block.rs @@ -277,6 +277,7 @@ impl State { .account_tree .apply_mutations(account_tree_update) .expect("Unreachable: old account tree root must be checked before this step"); + inner.blockchain.push(block_commitment); Ok(()) diff --git a/crates/store/src/state/loader.rs b/crates/store/src/state/loader.rs index c8c886148..14de0471f 100644 --- a/crates/store/src/state/loader.rs +++ b/crates/store/src/state/loader.rs @@ -376,7 +376,6 @@ pub async fn load_smt_forest( StateInitializationError::AccountToDeltaConversionFailed(e.to_string()) })?; - // Use the unified update method (will recognize it's a full-state delta) forest.update_account(block_num, &delta)?; } diff --git a/crates/validator/src/lib.rs b/crates/validator/src/lib.rs index 44f883bfc..185b9dfc6 100644 --- a/crates/validator/src/lib.rs +++ b/crates/validator/src/lib.rs @@ -5,7 +5,7 @@ mod signers; mod tx_validation; pub use server::Validator; -pub use signers::ValidatorSigner; +pub use signers::{KmsSigner, ValidatorSigner}; // CONSTANTS // ================================================================================================= diff --git a/crates/validator/src/signers/mod.rs b/crates/validator/src/signers/mod.rs index 9656e045c..7c99ec9b5 100644 --- a/crates/validator/src/signers/mod.rs +++ b/crates/validator/src/signers/mod.rs @@ -16,8 +16,8 @@ pub enum ValidatorSigner { impl ValidatorSigner { /// Constructs a signer which uses an AWS KMS key for signing. /// - /// See [`KmsSigner::new`] for details as to env var configuration and AWS IAM policies required - /// to use this functionality. + /// See [`self::KmsSigner`] for details as to env var configuration and AWS IAM policies + /// required to use this functionality. pub async fn new_kms(key_id: impl Into) -> anyhow::Result { let kms_signer = KmsSigner::new(key_id).await?; Ok(Self::Kms(kms_signer)) From af571848bcbbf28bade4412e6bafda5a539026fa Mon Sep 17 00:00:00 2001 From: Bernhard Schuster Date: Fri, 27 Feb 2026 14:30:30 +0100 Subject: [PATCH 2/2] chores --- crates/store/src/inner_forest/mod.rs | 34 +++++++++++++++++----------- crates/validator/src/signers/mod.rs | 2 +- 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/crates/store/src/inner_forest/mod.rs b/crates/store/src/inner_forest/mod.rs index 62f7704b2..dbff597c6 100644 --- a/crates/store/src/inner_forest/mod.rs +++ b/crates/store/src/inner_forest/mod.rs @@ -13,7 +13,7 @@ use miden_protocol::asset::{Asset, AssetVaultKey, AssetWitness, FungibleAsset}; use miden_protocol::block::BlockNumber; use miden_protocol::crypto::merkle::smt::{SMT_DEPTH, SmtForest}; use miden_protocol::crypto::merkle::{EmptySubtreeRoots, MerkleError}; -use miden_protocol::errors::{AssetError, StorageMapError}; +use miden_protocol::errors::{AccountError, AssetError, StorageMapError}; use miden_protocol::{EMPTY_WORD, Word}; use thiserror::Error; @@ -25,6 +25,12 @@ mod tests; #[derive(Debug, Error)] pub enum InnerForestError { + #[error(transparent)] + Account(#[from] AccountError), + #[error(transparent)] + Asset(#[from] AssetError), + #[error(transparent)] + Merkle(#[from] MerkleError), #[error( "balance underflow: account {account_id}, faucet {faucet_id}, \ previous balance {prev_balance}, delta {delta}" @@ -89,6 +95,14 @@ impl InnerForest { *EmptySubtreeRoots::entry(SMT_DEPTH, 0) } + /// Retrieves the most recent vault root for an account. + fn get_latest_vault_root(&self, account_id: AccountId) -> Word { + self.vault_roots + .range((account_id, BlockNumber::GENESIS)..=(account_id, BlockNumber::MAX)) + .next_back() + .map_or_else(Self::empty_smt_root, |(_, root)| *root) + } + /// Retrieves a vault root for the specified account at or before the specified block. pub(crate) fn get_vault_root( &self, @@ -283,13 +297,13 @@ impl InnerForest { if is_full_state { self.insert_account_vault(block_num, account_id, delta.vault())?; } else if !delta.vault().is_empty() { - self.update_account_vault(block_num, account_id, delta.vault(), false)?; + self.update_account_vault(block_num, account_id, delta.vault())?; } if is_full_state { - self.insert_account_storage(block_num, account_id, delta.storage())?; + self.insert_account_storage(block_num, account_id, delta.storage()); } else if !delta.storage().is_empty() { - self.update_account_storage(block_num, account_id, delta.storage(), false)?; + self.update_account_storage(block_num, account_id, delta.storage()); } Ok(()) @@ -336,6 +350,7 @@ impl InnerForest { vault_entries = num_entries, "Inserted vault into forest" ); + Ok(()) } /// Updates the forest with vault changes from a delta. The vault delta is assumed to be @@ -352,13 +367,8 @@ impl InnerForest { block_num: BlockNumber, account_id: AccountId, vault_delta: &AccountVaultDelta, - is_full_state: bool, ) -> Result { - let prev_root = if is_full_state { - Self::empty_smt_root() - } else { - self.get_latest_vault_root(account_id) - }; + let prev_root = self.get_latest_vault_root(account_id); let mut entries: Vec<(Word, Word)> = Vec::new(); @@ -406,9 +416,7 @@ impl InnerForest { } if entries.is_empty() { - if is_full_state { - self.vault_roots.insert((account_id, block_num), prev_root); - } + self.vault_roots.insert((account_id, block_num), prev_root); return Ok(prev_root); } diff --git a/crates/validator/src/signers/mod.rs b/crates/validator/src/signers/mod.rs index 7c99ec9b5..21bbeaa7a 100644 --- a/crates/validator/src/signers/mod.rs +++ b/crates/validator/src/signers/mod.rs @@ -16,7 +16,7 @@ pub enum ValidatorSigner { impl ValidatorSigner { /// Constructs a signer which uses an AWS KMS key for signing. /// - /// See [`self::KmsSigner`] for details as to env var configuration and AWS IAM policies + /// See [`KmsSigner`] for details as to env var configuration and AWS IAM policies /// required to use this functionality. pub async fn new_kms(key_id: impl Into) -> anyhow::Result { let kms_signer = KmsSigner::new(key_id).await?;