Repository: 0xPolygonMiden/miden-node
Pull request #783: Use `AccountTree` type wrapper
Author: login: PhilippGackstatter
Created: 2025-04-04T00:57:09Z | Merged: 2025-04-18T08:59:30Z

Companion PR to https://github.com/0xPolygonMiden/miden-base/pull/1254.


Diff:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 897b21a2b9..74025a3d73 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,6 +8,7 @@
   - Database is now created during bootstrap process instead of on first startup.
   - Data directory is no longer created but is instead expected to exist.
   - The genesis block can no longer be configured which also removes the `store dump-genesis` command.
+- [BREAKING] Use `AccountTree` and update account witness proto definitions (#783).
 
 ## v0.8.0 (2025-03-26)
 
diff --git a/Cargo.lock b/Cargo.lock
index c14eaaeaaa..3856816649 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -105,9 +105,9 @@ dependencies = [
 
 [[package]]
 name = "anyhow"
-version = "1.0.97"
+version = "1.0.98"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f"
+checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487"
 
 [[package]]
 name = "arrayref"
@@ -475,9 +475,9 @@ dependencies = [
 
 [[package]]
 name = "cc"
-version = "1.2.18"
+version = "1.2.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "525046617d8376e3db1deffb079e91cef90a89fc3ca5c185bbf8c9ecdd15cd5c"
+checksum = "8e3a13707ac958681c13b39b458c073d0d9bc8a22cb1b2f4c8e55eb72c13f362"
 dependencies = [
  "jobserver",
  "libc",
@@ -536,9 +536,9 @@ dependencies = [
 
 [[package]]
 name = "clap"
-version = "4.5.35"
+version = "4.5.36"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d8aa86934b44c19c50f87cc2790e19f54f7a67aedb64101c2e1a2e5ecfb73944"
+checksum = "2df961d8c8a0d08aa9945718ccf584145eee3f3aa06cddbeac12933781102e04"
 dependencies = [
  "clap_builder",
  "clap_derive",
@@ -546,9 +546,9 @@ dependencies = [
 
 [[package]]
 name = "clap_builder"
-version = "4.5.35"
+version = "4.5.36"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2414dbb2dd0695280da6ea9261e327479e9d37b0630f6b53ba2a11c60c679fd9"
+checksum = "132dbda40fb6753878316a489d5a1242a8ef2f0d9e47ba01c951ea8aa7d013a5"
 dependencies = [
  "anstream",
  "anstyle",
@@ -1123,9 +1123,9 @@ checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2"
 
 [[package]]
 name = "h2"
-version = "0.4.8"
+version = "0.4.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5017294ff4bb30944501348f6f8e42e6ad28f42c8bbef7a74029aff064a4e3c2"
+checksum = "75249d144030531f8dee69fe9cea04d3edf809a017ae445e2abdff6629e86633"
 dependencies = [
  "atomic-waker",
  "bytes",
@@ -1631,9 +1631,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
 
 [[package]]
 name = "libc"
-version = "0.2.171"
+version = "0.2.172"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6"
+checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
 
 [[package]]
 name = "libloading"
@@ -1813,7 +1813,7 @@ dependencies = [
 [[package]]
 name = "miden-block-prover"
 version = "0.9.0"
-source = "git+https://github.com/0xPolygonMiden/miden-base.git?branch=next#b65c2f57a6c6ba9f16036968371f07e180358cc6"
+source = "git+https://github.com/0xPolygonMiden/miden-base.git?branch=next#0b0a2d8d955772583767ba656dd85a36694fa1f1"
 dependencies = [
  "miden-crypto",
  "miden-lib",
@@ -1852,7 +1852,7 @@ dependencies = [
  "glob",
  "num",
  "num-complex",
- "rand 0.9.0",
+ "rand 0.9.1",
  "rand_core 0.9.3",
  "sha3",
  "thiserror 2.0.12",
@@ -1877,7 +1877,7 @@ dependencies = [
  "miden-objects",
  "miden-tx",
  "mime",
- "rand 0.9.0",
+ "rand 0.9.1",
  "rand_chacha 0.9.0",
  "serde",
  "serde_json",
@@ -1906,7 +1906,7 @@ dependencies = [
 [[package]]
 name = "miden-lib"
 version = "0.9.0"
-source = "git+https://github.com/0xPolygonMiden/miden-base.git?branch=next#b65c2f57a6c6ba9f16036968371f07e180358cc6"
+source = "git+https://github.com/0xPolygonMiden/miden-base.git?branch=next#0b0a2d8d955772583767ba656dd85a36694fa1f1"
 dependencies = [
  "miden-assembly",
  "miden-objects",
@@ -1971,7 +1971,7 @@ dependencies = [
  "miden-node-store",
  "miden-node-utils",
  "miden-objects",
- "rand 0.9.0",
+ "rand 0.9.1",
  "rand_chacha 0.9.0",
  "tokio",
  "url",
@@ -1998,7 +1998,7 @@ dependencies = [
  "miden-tx",
  "miden-tx-batch-prover",
  "pretty_assertions",
- "rand 0.9.0",
+ "rand 0.9.1",
  "rand_chacha 0.9.0",
  "thiserror 2.0.12",
  "tokio",
@@ -2094,7 +2094,7 @@ dependencies = [
  "miden-node-store",
  "miden-node-utils",
  "miden-objects",
- "rand 0.9.0",
+ "rand 0.9.1",
  "rayon",
  "tokio",
  "tonic",
@@ -2121,7 +2121,7 @@ dependencies = [
  "opentelemetry",
  "opentelemetry-otlp",
  "opentelemetry_sdk",
- "rand 0.9.0",
+ "rand 0.9.1",
  "serde",
  "tonic",
  "tracing",
@@ -2136,7 +2136,7 @@ dependencies = [
 [[package]]
 name = "miden-objects"
 version = "0.9.0"
-source = "git+https://github.com/0xPolygonMiden/miden-base.git?branch=next#b65c2f57a6c6ba9f16036968371f07e180358cc6"
+source = "git+https://github.com/0xPolygonMiden/miden-base.git?branch=next#0b0a2d8d955772583767ba656dd85a36694fa1f1"
 dependencies = [
  "bech32",
  "getrandom 0.3.2",
@@ -2145,7 +2145,7 @@ dependencies = [
  "miden-crypto",
  "miden-processor",
  "miden-verifier",
- "rand 0.9.0",
+ "rand 0.9.1",
  "rand_xoshiro",
  "semver 1.0.26",
  "serde",
@@ -2183,7 +2183,7 @@ dependencies = [
 [[package]]
 name = "miden-proving-service-client"
 version = "0.9.0"
-source = "git+https://github.com/0xPolygonMiden/miden-base.git?branch=next#b65c2f57a6c6ba9f16036968371f07e180358cc6"
+source = "git+https://github.com/0xPolygonMiden/miden-base.git?branch=next#0b0a2d8d955772583767ba656dd85a36694fa1f1"
 dependencies = [
  "async-trait",
  "getrandom 0.3.2",
@@ -2213,7 +2213,7 @@ dependencies = [
 [[package]]
 name = "miden-tx"
 version = "0.9.0"
-source = "git+https://github.com/0xPolygonMiden/miden-base.git?branch=next#b65c2f57a6c6ba9f16036968371f07e180358cc6"
+source = "git+https://github.com/0xPolygonMiden/miden-base.git?branch=next#0b0a2d8d955772583767ba656dd85a36694fa1f1"
 dependencies = [
  "async-trait",
  "miden-lib",
@@ -2221,7 +2221,7 @@ dependencies = [
  "miden-processor",
  "miden-prover",
  "miden-verifier",
- "rand 0.9.0",
+ "rand 0.9.1",
  "rand_chacha 0.9.0",
  "thiserror 2.0.12",
  "winter-maybe-async",
@@ -2230,7 +2230,7 @@ dependencies = [
 [[package]]
 name = "miden-tx-batch-prover"
 version = "0.9.0"
-source = "git+https://github.com/0xPolygonMiden/miden-base.git?branch=next#b65c2f57a6c6ba9f16036968371f07e180358cc6"
+source = "git+https://github.com/0xPolygonMiden/miden-base.git?branch=next#0b0a2d8d955772583767ba656dd85a36694fa1f1"
 dependencies = [
  "miden-core",
  "miden-crypto",
@@ -2599,7 +2599,7 @@ dependencies = [
  "glob",
  "opentelemetry",
  "percent-encoding",
- "rand 0.9.0",
+ "rand 0.9.1",
  "serde_json",
  "thiserror 2.0.12",
  "tokio",
@@ -2796,9 +2796,9 @@ dependencies = [
 
 [[package]]
 name = "proc-macro2"
-version = "1.0.94"
+version = "1.0.95"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84"
+checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
 dependencies = [
  "unicode-ident",
 ]
@@ -2962,13 +2962,12 @@ dependencies = [
 
 [[package]]
 name = "rand"
-version = "0.9.0"
+version = "0.9.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94"
+checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97"
 dependencies = [
  "rand_chacha 0.9.0",
  "rand_core 0.9.3",
- "zerocopy 0.8.24",
 ]
 
 [[package]]
diff --git a/crates/block-producer/src/block_builder/mod.rs b/crates/block-producer/src/block_builder/mod.rs
index 89e61161a8..b86445fe99 100644
--- a/crates/block-producer/src/block_builder/mod.rs
+++ b/crates/block-producer/src/block_builder/mod.rs
@@ -309,21 +309,14 @@ impl TelemetryInjectorExt for ProposedBlock {
             u32::try_from(self.created_nullifiers().len())
                 .expect("should have less than u32::MAX created nullifiers"),
         );
-        let num_block_created_notes = self
-            .output_note_batches()
-            .iter()
-            .fold(0, |acc, output_notes| acc + output_notes.len());
+        let num_block_created_notes = self.batches().num_created_notes();
         span.set_attribute(
             "block.output_notes.count",
             u32::try_from(num_block_created_notes)
                 .expect("should have less than u32::MAX output notes"),
         );
 
-        let num_batch_created_notes = self
-            .batches()
-            .as_slice()
-            .iter()
-            .fold(0, |acc, batch| acc + batch.output_notes().len());
+        let num_batch_created_notes = self.batches().num_created_notes();
         span.set_attribute(
             "block.batches.output_notes.count",
             u32::try_from(num_batch_created_notes)
diff --git a/crates/block-producer/src/test_utils/block.rs b/crates/block-producer/src/test_utils/block.rs
index 9383fd9c7f..dc679064fa 100644
--- a/crates/block-producer/src/test_utils/block.rs
+++ b/crates/block-producer/src/test_utils/block.rs
@@ -1,11 +1,11 @@
 use miden_objects::{
-    ACCOUNT_TREE_DEPTH, Digest,
+    Digest,
     batch::ProvenBatch,
     block::{
-        BlockAccountUpdate, BlockHeader, BlockNoteIndex, BlockNoteTree, OutputNoteBatch,
-        ProvenBlock,
+        AccountTree, BlockAccountUpdate, BlockHeader, BlockNoteIndex, BlockNoteTree,
+        OutputNoteBatch, ProvenBlock,
     },
-    crypto::merkle::{Mmr, SimpleSmt},
+    crypto::merkle::Mmr,
     note::Nullifier,
     transaction::{OrderedTransactionHeaders, OutputNote},
 };
@@ -34,7 +34,9 @@ pub async fn build_expected_block_header(
     let new_account_root = {
         let mut store_accounts = store.accounts.read().await.clone();
         for (&account_id, update) in updated_accounts {
-            store_accounts.insert(account_id.into(), update.final_state_commitment().into());
+            store_accounts
+                .insert(account_id, update.final_state_commitment())
+                .expect("account IDs should be unique");
         }
 
         store_accounts.root()
@@ -71,7 +73,7 @@ pub async fn build_expected_block_header(
 
 #[derive(Debug)]
 pub struct MockBlockBuilder {
-    store_accounts: SimpleSmt<ACCOUNT_TREE_DEPTH>,
+    store_accounts: AccountTree,
     store_chain_mmr: Mmr,
     last_block_header: BlockHeader,
 
@@ -105,7 +107,8 @@ impl MockBlockBuilder {
     pub fn account_updates(mut self, updated_accounts: Vec<BlockAccountUpdate>) -> Self {
         for update in &updated_accounts {
             self.store_accounts
-                .insert(update.account_id().into(), update.final_state_commitment().into());
+                .insert(update.account_id(), update.final_state_commitment())
+                .expect("account IDs should be unique");
         }
 
         self.updated_accounts = Some(updated_accounts);
diff --git a/crates/block-producer/src/test_utils/store.rs b/crates/block-producer/src/test_utils/store.rs
index 5c1abe583a..382002943c 100644
--- a/crates/block-producer/src/test_utils/store.rs
+++ b/crates/block-producer/src/test_utils/store.rs
@@ -5,11 +5,11 @@ use std::{
 };
 
 use miden_objects::{
-    ACCOUNT_TREE_DEPTH, Digest, EMPTY_WORD, ZERO,
+    Digest, EMPTY_WORD, Word, ZERO,
     account::AccountId,
     batch::ProvenBatch,
-    block::{BlockHeader, BlockNumber, OutputNoteBatch, ProvenBlock},
-    crypto::merkle::{Mmr, SimpleSmt, Smt},
+    block::{AccountTree, BlockHeader, BlockNumber, OutputNoteBatch, ProvenBlock},
+    crypto::merkle::{Mmr, Smt},
     note::{NoteId, NoteInclusionProof},
     transaction::ProvenTransaction,
 };
@@ -26,7 +26,7 @@ use crate::{
 /// Builds a [`MockStoreSuccess`]
 #[derive(Debug)]
 pub struct MockStoreSuccessBuilder {
-    accounts: Option<SimpleSmt<ACCOUNT_TREE_DEPTH>>,
+    accounts: Option<AccountTree>,
     notes: Option<Vec<OutputNoteBatch>>,
     produced_nullifiers: Option<BTreeSet<Digest>>,
     chain_mmr: Option<Mmr>,
@@ -36,16 +36,13 @@ pub struct MockStoreSuccessBuilder {
 impl MockStoreSuccessBuilder {
     pub fn from_batches<'a>(batches_iter: impl Iterator<Item = &'a ProvenBatch> + Clone) -> Self {
         let accounts_smt = {
-            let accounts = batches_iter
-                .clone()
-                .flat_map(|batch| {
-                    batch
-                        .account_updates()
-                        .iter()
-                        .map(|(account_id, update)| (account_id, update.initial_state_commitment()))
-                })
-                .map(|(account_id, commitment)| (account_id.prefix().into(), commitment.into()));
-            SimpleSmt::<ACCOUNT_TREE_DEPTH>::with_leaves(accounts).unwrap()
+            let accounts = batches_iter.clone().flat_map(|batch| {
+                batch
+                    .account_updates()
+                    .iter()
+                    .map(|(account_id, update)| (*account_id, update.initial_state_commitment()))
+            });
+            AccountTree::with_entries(accounts).unwrap()
         };
 
         Self {
@@ -58,12 +55,7 @@ impl MockStoreSuccessBuilder {
     }
 
     pub fn from_accounts(accounts: impl Iterator<Item = (AccountId, Digest)>) -> Self {
-        let accounts_smt = {
-            let accounts = accounts
-                .map(|(account_id, commitment)| (account_id.prefix().into(), commitment.into()));
-
-            SimpleSmt::<ACCOUNT_TREE_DEPTH>::with_leaves(accounts).unwrap()
-        };
+        let accounts_smt = AccountTree::with_entries(accounts).unwrap();
 
         Self {
             accounts: Some(accounts_smt),
@@ -107,7 +99,7 @@ impl MockStoreSuccessBuilder {
 
     pub fn build(self) -> MockStoreSuccess {
         let block_num = self.block_num.unwrap_or(1.into());
-        let accounts_smt = self.accounts.unwrap_or(SimpleSmt::new().unwrap());
+        let accounts_smt = self.accounts.unwrap_or_default();
         let notes = self.notes.unwrap_or_default();
         let block_note_tree = note_created_smt_from_note_batches(notes.iter());
         let note_root = block_note_tree.root();
@@ -168,7 +160,7 @@ impl MockStoreSuccessBuilder {
 
 pub struct MockStoreSuccess {
     /// Map account id -> account commitment
-    pub accounts: Arc<RwLock<SimpleSmt<ACCOUNT_TREE_DEPTH>>>,
+    pub accounts: Arc<RwLock<AccountTree>>,
 
     /// Stores the nullifiers of the notes that were consumed
     pub produced_nullifiers: Arc<RwLock<Smt>>,
@@ -202,7 +194,8 @@ impl MockStoreSuccess {
         // update accounts
         for update in block.updated_accounts() {
             locked_accounts
-                .insert(update.account_id().into(), update.final_state_commitment().into());
+                .insert(update.account_id(), update.final_state_commitment())
+                .expect("TODO: what should we do with this error?");
         }
         let header = block.header();
         debug_assert_eq!(locked_accounts.root(), header.account_root());
@@ -254,12 +247,12 @@ impl MockStoreSuccess {
         let locked_produced_nullifiers = self.produced_nullifiers.read().await;
 
         let account_commitment = {
-            let account_commitment = locked_accounts.get_leaf(&proven_tx.account_id().into());
+            let account_commitment = locked_accounts.get(proven_tx.account_id());
 
-            if account_commitment == EMPTY_WORD {
+            if Word::from(account_commitment) == EMPTY_WORD {
                 None
             } else {
-                Some(account_commitment.into())
+                Some(account_commitment)
             }
         };
 
diff --git a/crates/proto/src/domain/account.rs b/crates/proto/src/domain/account.rs
index 751b5d2fb1..2279b117cc 100644
--- a/crates/proto/src/domain/account.rs
+++ b/crates/proto/src/domain/account.rs
@@ -4,9 +4,9 @@ use miden_node_utils::formatting::format_opt;
 use miden_objects::{
     Digest,
     account::{Account, AccountHeader, AccountId},
-    block::BlockNumber,
-    crypto::{hash::rpo::RpoDigest, merkle::MerklePath},
-    utils::{Deserializable, Serializable},
+    block::{AccountWitness, BlockNumber},
+    crypto::hash::rpo::RpoDigest,
+    utils::{Deserializable, DeserializationError, Serializable},
 };
 
 use super::try_convert;
@@ -155,16 +155,16 @@ impl TryInto<StorageMapKeysProof> for proto::requests::get_account_proofs_reques
 #[derive(Clone, Debug)]
 pub struct AccountWitnessRecord {
     pub account_id: AccountId,
-    pub initial_state_commitment: Digest,
-    pub proof: MerklePath,
+    pub witness: AccountWitness,
 }
 
 impl From<AccountWitnessRecord> for proto::responses::AccountWitness {
     fn from(from: AccountWitnessRecord) -> Self {
         Self {
             account_id: Some(from.account_id.into()),
-            initial_state_commitment: Some(from.initial_state_commitment.into()),
-            proof: Some(Into::into(&from.proof)),
+            witness_id: Some(from.witness.id().into()),
+            commitment: Some(from.witness.state_commitment().into()),
+            path: Some(from.witness.into_proof().into_parts().0.into()),
         }
     }
 }
@@ -175,22 +175,33 @@ impl TryFrom<proto::responses::AccountWitness> for AccountWitnessRecord {
     fn try_from(
         account_witness_record: proto::responses::AccountWitness,
     ) -> Result<Self, Self::Error> {
+        let witness_id = account_witness_record
+            .witness_id
+            .ok_or(proto::responses::AccountWitness::missing_field(stringify!(witness_id)))?
+            .try_into()?;
+        let commitment = account_witness_record
+            .commitment
+            .ok_or(proto::responses::AccountWitness::missing_field(stringify!(commitment)))?
+            .try_into()?;
+        let path = account_witness_record
+            .path
+            .as_ref()
+            .ok_or(proto::responses::AccountWitness::missing_field(stringify!(path)))?
+            .try_into()?;
+
+        let witness = AccountWitness::new(witness_id, commitment, path).map_err(|err| {
+            ConversionError::deserialization_error(
+                "AccountWitness",
+                DeserializationError::InvalidValue(err.to_string()),
+            )
+        })?;
+
         Ok(Self {
             account_id: account_witness_record
                 .account_id
                 .ok_or(proto::responses::AccountWitness::missing_field(stringify!(account_id)))?
                 .try_into()?,
-            initial_state_commitment: account_witness_record
-                .initial_state_commitment
-                .ok_or(proto::responses::AccountWitness::missing_field(stringify!(
-                    account_commitment
-                )))?
-                .try_into()?,
-            proof: account_witness_record
-                .proof
-                .as_ref()
-                .ok_or(proto::responses::AccountWitness::missing_field(stringify!(proof)))?
-                .try_into()?,
+            witness,
         })
     }
 }
diff --git a/crates/proto/src/domain/block.rs b/crates/proto/src/domain/block.rs
index 8912434604..5f05efca25 100644
--- a/crates/proto/src/domain/block.rs
+++ b/crates/proto/src/domain/block.rs
@@ -1,7 +1,7 @@
 use std::collections::BTreeMap;
 
 use miden_objects::{
-    block::{AccountWitness, BlockHeader, BlockInputs, NullifierWitness},
+    block::{BlockHeader, BlockInputs, NullifierWitness},
     note::{NoteId, NoteInclusionProof},
     transaction::ChainMmr,
     utils::{Deserializable, Serializable},
@@ -111,15 +111,7 @@ impl From<BlockInputs> for GetBlockInputsResponse {
             latest_block_header: Some(prev_block_header.into()),
             account_witnesses: account_witnesses
                 .into_iter()
-                .map(|(id, witness)| {
-                    let (initial_state_commitment, proof) = witness.into_parts();
-                    AccountWitnessRecord {
-                        account_id: id,
-                        initial_state_commitment,
-                        proof,
-                    }
-                    .into()
-                })
+                .map(|(id, witness)| AccountWitnessRecord { account_id: id, witness }.into())
                 .collect(),
             nullifier_witnesses: nullifier_witnesses
                 .into_iter()
@@ -151,13 +143,7 @@ impl TryFrom<GetBlockInputsResponse> for BlockInputs {
             .into_iter()
             .map(|entry| {
                 let witness_record: AccountWitnessRecord = entry.try_into()?;
-                Ok((
-                    witness_record.account_id,
-                    AccountWitness::new(
-                        witness_record.initial_state_commitment,
-                        witness_record.proof,
-                    ),
-                ))
+                Ok((witness_record.account_id, witness_record.witness))
             })
             .collect::<Result<BTreeMap<_, _>, ConversionError>>()?;
 
diff --git a/crates/proto/src/generated/responses.rs b/crates/proto/src/generated/responses.rs
index b52b157070..bfd3baf09f 100644
--- a/crates/proto/src/generated/responses.rs
+++ b/crates/proto/src/generated/responses.rs
@@ -81,19 +81,23 @@ pub struct SyncNoteResponse {
     #[prost(message, repeated, tag = "4")]
     pub notes: ::prost::alloc::vec::Vec<super::note::NoteSyncRecord>,
 }
-/// An account returned as a response to the `GetBlockInputs`.
+/// An account witness returned as a response to the `GetBlockInputs`.
 #[derive(Clone, PartialEq, ::prost::Message)]
 pub struct AccountWitness {
-    /// The account ID.
+    /// Account ID for which this proof is requested.
     #[prost(message, optional, tag = "1")]
     pub account_id: ::core::option::Option<super::account::AccountId>,
-    /// The latest account state commitment used as the initial state of the requested block.
-    /// This will be the zero digest if the account doesn't exist.
+    /// The account ID within the proof, which may be different from the above account ID.
+    /// This can happen when the requested account ID's prefix matches the prefix of an existing
+    /// account ID in the tree. Then the witness will prove inclusion of this witness ID in the tree.
     #[prost(message, optional, tag = "2")]
-    pub initial_state_commitment: ::core::option::Option<super::digest::Digest>,
-    /// Merkle path to verify the account's inclusion in the account tree.
+    pub witness_id: ::core::option::Option<super::account::AccountId>,
+    /// The state commitment whose inclusion the witness proves.
     #[prost(message, optional, tag = "3")]
-    pub proof: ::core::option::Option<super::merkle::MerklePath>,
+    pub commitment: ::core::option::Option<super::digest::Digest>,
+    /// The merkle path of the state commitment in the account tree.
+    #[prost(message, optional, tag = "4")]
+    pub path: ::core::option::Option<super::merkle::MerklePath>,
 }
 /// A nullifier returned as a response to the `GetBlockInputs`.
 #[derive(Clone, PartialEq, ::prost::Message)]
@@ -220,27 +224,21 @@ pub struct GetAccountStateDeltaResponse {
 /// Represents the result of getting account proofs.
 #[derive(Clone, PartialEq, ::prost::Message)]
 pub struct GetAccountProofsResponse {
-    /// Block number at which the state of the account was returned.
+    /// Block number at which the state of the accounts is returned.
     #[prost(fixed32, tag = "1")]
     pub block_num: u32,
     /// List of account state infos for the requested account keys.
     #[prost(message, repeated, tag = "2")]
     pub account_proofs: ::prost::alloc::vec::Vec<AccountProofsResponse>,
 }
-/// A single account proof returned as a response to the `GetAccountProofs`.
+/// A single account proof returned as a response to `GetAccountProofs`.
 #[derive(Clone, PartialEq, ::prost::Message)]
 pub struct AccountProofsResponse {
-    /// Account ID.
+    /// The account witness for the current state commitment of one account ID.
     #[prost(message, optional, tag = "1")]
-    pub account_id: ::core::option::Option<super::account::AccountId>,
-    /// Account commitment.
-    #[prost(message, optional, tag = "2")]
-    pub account_commitment: ::core::option::Option<super::digest::Digest>,
-    /// Authentication path from the `account_root` of the block header to the account.
-    #[prost(message, optional, tag = "3")]
-    pub account_proof: ::core::option::Option<super::merkle::MerklePath>,
+    pub witness: ::core::option::Option<AccountWitness>,
     /// State header for public accounts. Filled only if `include_headers` flag is set to `true`.
-    #[prost(message, optional, tag = "4")]
+    #[prost(message, optional, tag = "2")]
     pub state_header: ::core::option::Option<AccountStateHeader>,
 }
 /// State header for public accounts.
diff --git a/crates/store/src/errors.rs b/crates/store/src/errors.rs
index ed9fb5bd2e..e8dbaa3e87 100644
--- a/crates/store/src/errors.rs
+++ b/crates/store/src/errors.rs
@@ -2,7 +2,7 @@ use std::io;
 
 use deadpool::managed::PoolError;
 use miden_objects::{
-    AccountDeltaError, AccountError, NoteError,
+    AccountDeltaError, AccountError, AccountTreeError, NoteError,
     account::AccountId,
     block::BlockNumber,
     crypto::{
@@ -108,7 +108,7 @@ pub enum StateInitializationError {
     #[error("failed to create nullifier tree")]
     FailedToCreateNullifierTree(#[from] NullifierTreeError),
     #[error("failed to create accounts tree")]
-    FailedToCreateAccountsTree(#[from] MerkleError),
+    FailedToCreateAccountsTree(#[source] AccountTreeError),
 }
 
 #[derive(Debug, Error)]
@@ -134,8 +134,8 @@ pub enum GenesisError {
     // TODO: Check if needed.
     #[error("block error")]
     Block,
-    #[error("merkle error")]
-    Merkle(#[from] MerkleError),
+    #[error("failed to build genesis account tree")]
+    AccountTree(#[source] AccountTreeError),
     #[error("failed to deserialize genesis file")]
     GenesisFileDeserialization(#[from] DeserializationError),
 }
diff --git a/crates/store/src/genesis.rs b/crates/store/src/genesis.rs
index 7db1d0221b..bbe719efa7 100644
--- a/crates/store/src/genesis.rs
+++ b/crates/store/src/genesis.rs
@@ -1,9 +1,11 @@
 use miden_lib::transaction::TransactionKernel;
 use miden_objects::{
-    ACCOUNT_TREE_DEPTH, Digest,
+    Digest,
     account::{Account, delta::AccountUpdateDetails},
-    block::{BlockAccountUpdate, BlockHeader, BlockNoteTree, BlockNumber, ProvenBlock},
-    crypto::merkle::{MmrPeaks, SimpleSmt, Smt},
+    block::{
+        AccountTree, BlockAccountUpdate, BlockHeader, BlockNoteTree, BlockNumber, ProvenBlock,
+    },
+    crypto::merkle::{MmrPeaks, Smt},
     note::Nullifier,
     transaction::OrderedTransactionHeaders,
     utils::serde::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable},
@@ -57,10 +59,12 @@ impl GenesisState {
             })
             .collect();
 
-        let account_smt: SimpleSmt<ACCOUNT_TREE_DEPTH> =
-            SimpleSmt::with_leaves(accounts.iter().map(|update| {
-                (update.account_id().prefix().into(), update.final_state_commitment().into())
-            }))?;
+        let account_smt = AccountTree::with_entries(
+            accounts
+                .iter()
+                .map(|update| (update.account_id(), update.final_state_commitment())),
+        )
+        .map_err(GenesisError::AccountTree)?;
 
         let empty_nullifiers: Vec<Nullifier> = Vec::new();
         let empty_nullifier_tree = Smt::new();
diff --git a/crates/store/src/state.rs b/crates/store/src/state.rs
index 6953ed5a10..56ac506a6b 100644
--- a/crates/store/src/state.rs
+++ b/crates/store/src/state.rs
@@ -10,6 +10,7 @@ use std::{
 };
 
 use miden_node_proto::{
+    AccountWitnessRecord,
     domain::{
         account::{AccountInfo, AccountProofRequest, StorageMapKeysProof},
         batch::BatchInputs,
@@ -18,15 +19,15 @@ use miden_node_proto::{
 };
 use miden_node_utils::formatting::format_array;
 use miden_objects::{
-    ACCOUNT_TREE_DEPTH, AccountError,
+    AccountError,
     account::{AccountDelta, AccountHeader, AccountId, StorageSlot},
-    block::{AccountWitness, BlockHeader, BlockInputs, BlockNumber, NullifierWitness, ProvenBlock},
+    block::{
+        AccountTree, AccountWitness, BlockHeader, BlockInputs, BlockNumber, NullifierWitness,
+        ProvenBlock,
+    },
     crypto::{
         hash::rpo::RpoDigest,
-        merkle::{
-            LeafIndex, Mmr, MmrDelta, MmrError, MmrPeaks, MmrProof, PartialMmr, SimpleSmt,
-            SmtProof, ValuePath,
-        },
+        merkle::{Mmr, MmrDelta, MmrError, MmrPeaks, MmrProof, PartialMmr, SmtProof},
     },
     note::{NoteId, Nullifier},
     transaction::{ChainMmr, OutputNote},
@@ -152,7 +153,7 @@ impl Blockchain {
 struct InnerState {
     nullifier_tree: NullifierTree,
     blockchain: Blockchain,
-    account_tree: SimpleSmt<ACCOUNT_TREE_DEPTH>,
+    account_tree: AccountTree,
 }
 
 impl InnerState {
@@ -234,9 +235,8 @@ impl State {
 
         let header = block.header();
 
-        let tx_commitment = BlockHeader::compute_tx_commitment(
-            block.transactions().as_slice().iter().map(|tx| (tx.id(), tx.account_id())),
-        );
+        let tx_commitment = block.transactions().commitment();
+
         if header.tx_commitment() != tx_commitment {
             return Err(InvalidBlockError::InvalidBlockTxCommitment {
                 expected: tx_commitment,
@@ -315,15 +315,13 @@ impl State {
 
             // compute update for account tree
             let account_tree_update = inner.account_tree.compute_mutations(
-                block.updated_accounts().iter().map(|update| {
-                    (
-                        LeafIndex::new_max_depth(update.account_id().prefix().into()),
-                        update.final_state_commitment().into(),
-                    )
-                }),
+                block
+                    .updated_accounts()
+                    .iter()
+                    .map(|update| (update.account_id(), update.final_state_commitment())),
             );
 
-            if account_tree_update.root() != header.account_root() {
+            if account_tree_update.as_mutation_set().root() != header.account_root() {
                 return Err(InvalidBlockError::NewBlockInvalidAccountRoot.into());
             }
 
@@ -791,13 +789,7 @@ impl State {
         let account_witnesses = account_ids
             .iter()
             .copied()
-            .map(|account_id| {
-                let ValuePath {
-                    value: latest_state_commitment,
-                    path: proof,
-                } = inner.account_tree.open(&account_id.into());
-                (account_id, AccountWitness::new(latest_state_commitment, proof))
-            })
+            .map(|account_id| (account_id, inner.account_tree.open(account_id)))
             .collect::<BTreeMap<AccountId, AccountWitness>>();
 
         // Fetch witnesses for all nullifiers. We don't check whether the nullifiers are spent or
@@ -826,10 +818,7 @@ impl State {
 
         let inner = self.inner.read().await;
 
-        let account_commitment = inner
-            .account_tree
-            .open(&LeafIndex::new_max_depth(account_id.prefix().into()))
-            .value;
+        let account_commitment = inner.account_tree.get(account_id);
 
         let nullifiers = nullifiers
             .iter()
@@ -935,14 +924,13 @@ impl State {
         let responses = account_ids
             .into_iter()
             .map(|account_id| {
-                let acc_leaf_idx = LeafIndex::new_max_depth(account_id.prefix().into());
-                let opening = inner_state.account_tree.open(&acc_leaf_idx);
+                let witness = inner_state.account_tree.open(account_id);
                 let state_header = state_headers.get(&account_id).cloned();
 
+                let witness_record = AccountWitnessRecord { account_id, witness };
+
                 AccountProofsResponse {
-                    account_id: Some(account_id.into()),
-                    account_commitment: Some(opening.value.into()),
-                    account_proof: Some(opening.path.into()),
+                    witness: Some(witness_record.into()),
                     state_header,
                 }
             })
@@ -1027,16 +1015,9 @@ async fn load_mmr(db: &mut Db) -> Result<Mmr, StateInitializationError> {
 }
 
 #[instrument(target = COMPONENT, skip_all)]
-async fn load_accounts(
-    db: &mut Db,
-) -> Result<SimpleSmt<ACCOUNT_TREE_DEPTH>, StateInitializationError> {
-    let account_data: Vec<_> = db
-        .select_all_account_commitments()
-        .await?
-        .into_iter()
-        .map(|(id, account_commitment)| (id.prefix().into(), account_commitment.into()))
-        .collect();
+async fn load_accounts(db: &mut Db) -> Result<AccountTree, StateInitializationError> {
+    let account_data = db.select_all_account_commitments().await?.into_iter().collect::<Vec<_>>();
 
-    SimpleSmt::with_leaves(account_data)
+    AccountTree::with_entries(account_data)
         .map_err(StateInitializationError::FailedToCreateAccountsTree)
 }
diff --git a/proto/proto/responses.proto b/proto/proto/responses.proto
index 0783f8636b..8c68ce0e54 100644
--- a/proto/proto/responses.proto
+++ b/proto/proto/responses.proto
@@ -86,17 +86,21 @@ message SyncNoteResponse {
     repeated note.NoteSyncRecord notes = 4;
 }
 
-// An account returned as a response to the `GetBlockInputs`.
+// An account witness returned as a response to the `GetBlockInputs`.
 message AccountWitness {
-    // The account ID.
+    // Account ID for which this proof is requested.
     account.AccountId account_id = 1;
 
-    // The latest account state commitment used as the initial state of the requested block.
-    // This will be the zero digest if the account doesn't exist.
-    digest.Digest initial_state_commitment = 2;
+    // The account ID within the proof, which may be different from the above account ID.
+    // This can happen when the requested account ID's prefix matches the prefix of an existing
+    // account ID in the tree. Then the witness will prove inclusion of this witness ID in the tree.
+    account.AccountId witness_id = 2;
+
+    // The state commitment whose inclusion the witness proves.
+    digest.Digest commitment = 3;
 
-    // Merkle path to verify the account's inclusion in the account tree.
-    merkle.MerklePath proof = 3;
+    // The merkle path of the state commitment in the account tree.
+    merkle.MerklePath path = 4;
 }
 
 // A nullifier returned as a response to the `GetBlockInputs`.
@@ -210,22 +214,20 @@ message GetAccountStateDeltaResponse {
 
 // Represents the result of getting account proofs.
 message GetAccountProofsResponse {
-    // Block number at which the state of the account was returned.
+    // Block number at which the state of the accounts is returned.
     fixed32 block_num = 1;
+
     // List of account state infos for the requested account keys.
     repeated AccountProofsResponse account_proofs = 2;
 }
 
-// A single account proof returned as a response to the `GetAccountProofs`.
+// A single account proof returned as a response to `GetAccountProofs`.
 message AccountProofsResponse {
-    // Account ID.
-    account.AccountId account_id = 1;
-    // Account commitment.
-    digest.Digest account_commitment = 2;
-    // Authentication path from the `account_root` of the block header to the account.
-    merkle.MerklePath account_proof = 3;
+    // The account witness for the current state commitment of one account ID.
+    AccountWitness witness = 1;
+
     // State header for public accounts. Filled only if `include_headers` flag is set to `true`.
-    optional AccountStateHeader state_header = 4;
+    optional AccountStateHeader state_header = 2;
 }
 
 // State header for public accounts.


Reviews:
user: login: Mirko-von-Leipzig
body: Some very minor suggestions, and missing a changelog entry, but otherwise this API looks much cleaner!
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-04-04T05:35:53Z
user: login: PhilippGackstatter
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-04T15:03:44Z
user: login: PhilippGackstatter
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-04T15:08:15Z
user: login: igamigo
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-04T19:48:51Z
user: login: PhilippGackstatter
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-04T20:15:57Z
user: login: Mirko-von-Leipzig
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-05T05:16:31Z
user: login: PhilippGackstatter
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-15T16:40:00Z
user: login: bobbinth
body: Looks good! Thank you!

Since https://github.com/0xPolygonMiden/miden-base/pull/1254 we can now update the `Cargo.toml` references and merges this PR. @Mirko-von-Leipzig could you help with this? (@PhilippGackstatter is out today).

After this, I believe @igamigo will create a PR in `miden-client` to make sure integration tests are working against the latest `next` here. 
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-18T08:30:33Z
user: login: igamigo
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-18T12:42:47Z
user: login: Mirko-von-Leipzig
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-18T12:50:03Z
user: login: bobbinth
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-18T19:05:10Z

Review comments:
path: proto/proto/responses.proto
user: login: Mirko-von-Leipzig
body: This should be a breaking change entry as it changes the RPC's gRPC definitions
created_at: 2025-04-04T05:29:38Z
author_association: COLLABORATOR
diff_hunk: @@ -153,10 +148,8 @@ impl TryFrom<GetBlockInputsResponse> for BlockInputs {
                 let witness_record: AccountWitnessRecord = entry.try_into()?;
                 Ok((
                     witness_record.account_id,
-                    AccountWitness::new(
-                        witness_record.initial_state_commitment,
-                        witness_record.proof,
-                    ),
+                    AccountWitness::new(witness_record.proof)
+                        .expect("account witness record should contain a valid account witness"),
path: crates/proto/src/domain/block.rs
user: login: Mirko-von-Leipzig
body: We should probably not panic in deserialization code.

While this code is part of the internal API, I'm worried this might get re-used publicly sometime.
created_at: 2025-04-04T05:32:10Z
author_association: COLLABORATOR
diff_hunk: @@ -34,7 +34,9 @@ pub async fn build_expected_block_header(
     let new_account_root = {
         let mut store_accounts = store.accounts.read().await.clone();
         for (&account_id, update) in updated_accounts {
-            store_accounts.insert(account_id.into(), update.final_state_commitment().into());
+            store_accounts
+                .insert(account_id, update.final_state_commitment())
+                .expect("error: duplicate account ID prefix");
path: crates/block-producer/src/test_utils/block.rs
user: login: Mirko-von-Leipzig
body: ```suggestion
                .expect("account IDs should be unique");
```
created_at: 2025-04-04T05:34:11Z
author_association: COLLABORATOR
diff_hunk: @@ -105,7 +107,8 @@ impl MockBlockBuilder {
     pub fn account_updates(mut self, updated_accounts: Vec<BlockAccountUpdate>) -> Self {
         for update in &updated_accounts {
             self.store_accounts
-                .insert(update.account_id().into(), update.final_state_commitment().into());
+                .insert(update.account_id(), update.final_state_commitment())
+                .expect("error: duplicate account ID prefix");
path: crates/block-producer/src/test_utils/block.rs
user: login: Mirko-von-Leipzig
body: ```suggestion
                .expect("account IDs should be unique");
```
created_at: 2025-04-04T05:34:43Z
author_association: COLLABORATOR
diff_hunk: @@ -202,7 +194,8 @@ impl MockStoreSuccess {
         // update accounts
         for update in block.updated_accounts() {
             locked_accounts
-                .insert(update.account_id().into(), update.final_state_commitment().into());
+                .insert(update.account_id(), update.final_state_commitment())
+                .expect("TODO: what should we do with this error?");
path: crates/block-producer/src/test_utils/store.rs
user: login: PhilippGackstatter
body: @Mirko-von-Leipzig What should we do with this one? This is test code, so panicking might be fine? Otherwise I'll have to add a new variant to `StoreError` that is only used in tests.
created_at: 2025-04-04T15:03:44Z
author_association: CONTRIBUTOR
path: proto/proto/responses.proto
user: login: PhilippGackstatter
body: I see, added an entry!
created_at: 2025-04-04T15:08:15Z
author_association: CONTRIBUTOR
diff_hunk: @@ -233,14 +229,11 @@ pub struct AccountProofsResponse {
     /// Account ID.
     #[prost(message, optional, tag = "1")]
     pub account_id: ::core::option::Option<super::account::AccountId>,
-    /// Account commitment.
+    /// Proof of inclusion for the account's current state commitment in the account tree.
     #[prost(message, optional, tag = "2")]
-    pub account_commitment: ::core::option::Option<super::digest::Digest>,
-    /// Authentication path from the `account_root` of the block header to the account.
-    #[prost(message, optional, tag = "3")]
-    pub account_proof: ::core::option::Option<super::merkle::MerklePath>,
+    pub proof: ::core::option::Option<super::smt::SmtOpening>,
path: crates/proto/src/generated/responses.rs
user: login: igamigo
body: Are these opening's leaves supposed to always be of the `Single` variant (ie everything else would be an error)? If so, is there any benefit other than stronger typing to returning this as an opening instead of just the commitment? 
created_at: 2025-04-04T19:48:51Z
author_association: COLLABORATOR
diff_hunk: @@ -233,14 +229,11 @@ pub struct AccountProofsResponse {
     /// Account ID.
     #[prost(message, optional, tag = "1")]
     pub account_id: ::core::option::Option<super::account::AccountId>,
-    /// Account commitment.
+    /// Proof of inclusion for the account's current state commitment in the account tree.
     #[prost(message, optional, tag = "2")]
-    pub account_commitment: ::core::option::Option<super::digest::Digest>,
-    /// Authentication path from the `account_root` of the block header to the account.
-    #[prost(message, optional, tag = "3")]
-    pub account_proof: ::core::option::Option<super::merkle::MerklePath>,
+    pub proof: ::core::option::Option<super::smt::SmtOpening>,
path: crates/proto/src/generated/responses.rs
user: login: PhilippGackstatter
body: That's a great point. Yes, I think we can actually change the `AccountWitness` in base to `commitment: Digest` and `path: MerklePath` instead of `SmtProof`.

Thanks for the logs, I'll have a look.
created_at: 2025-04-04T20:15:57Z
author_association: CONTRIBUTOR
diff_hunk: @@ -202,7 +194,8 @@ impl MockStoreSuccess {
         // update accounts
         for update in block.updated_accounts() {
             locked_accounts
-                .insert(update.account_id().into(), update.final_state_commitment().into());
+                .insert(update.account_id(), update.final_state_commitment())
+                .expect("TODO: what should we do with this error?");
path: crates/block-producer/src/test_utils/store.rs
user: login: Mirko-von-Leipzig
body: `panic` is fine :+1: 
created_at: 2025-04-05T05:16:31Z
author_association: COLLABORATOR
diff_hunk: @@ -233,14 +229,11 @@ pub struct AccountProofsResponse {
     /// Account ID.
     #[prost(message, optional, tag = "1")]
     pub account_id: ::core::option::Option<super::account::AccountId>,
-    /// Account commitment.
+    /// Proof of inclusion for the account's current state commitment in the account tree.
     #[prost(message, optional, tag = "2")]
-    pub account_commitment: ::core::option::Option<super::digest::Digest>,
-    /// Authentication path from the `account_root` of the block header to the account.
-    #[prost(message, optional, tag = "3")]
-    pub account_proof: ::core::option::Option<super::merkle::MerklePath>,
+    pub proof: ::core::option::Option<super::smt::SmtOpening>,
path: crates/proto/src/generated/responses.rs
user: login: PhilippGackstatter
body: Changed AccountWitness to include the commitment and path directly, plus the account ID, which is necessary to be able to reconstruct an `SmtLeaf` from the other data.
created_at: 2025-04-15T16:40:00Z
author_association: CONTRIBUTOR
diff_hunk: @@ -309,21 +309,14 @@ impl TelemetryInjectorExt for ProposedBlock {
             u32::try_from(self.created_nullifiers().len())
                 .expect("should have less than u32::MAX created nullifiers"),
         );
-        let num_block_created_notes = self
-            .output_note_batches()
-            .iter()
-            .fold(0, |acc, output_notes| acc + output_notes.len());
+        let num_block_created_notes = self.batches().num_created_notes();
         span.set_attribute(
             "block.output_notes.count",
             u32::try_from(num_block_created_notes)
                 .expect("should have less than u32::MAX output notes"),
         );
 
-        let num_batch_created_notes = self
-            .batches()
-            .as_slice()
-            .iter()
-            .fold(0, |acc, batch| acc + batch.output_notes().len());
+        let num_batch_created_notes = self.batches().num_created_notes();
path: crates/block-producer/src/block_builder/mod.rs
user: login: igamigo
body: Is this right? Seems like there is no difference between `num_block_created_notes` and `num_batch_created_notes` and so `num_erased_notes` will always be 0
created_at: 2025-04-18T12:42:47Z
author_association: COLLABORATOR
diff_hunk: @@ -309,21 +309,14 @@ impl TelemetryInjectorExt for ProposedBlock {
             u32::try_from(self.created_nullifiers().len())
                 .expect("should have less than u32::MAX created nullifiers"),
         );
-        let num_block_created_notes = self
-            .output_note_batches()
-            .iter()
-            .fold(0, |acc, output_notes| acc + output_notes.len());
+        let num_block_created_notes = self.batches().num_created_notes();
         span.set_attribute(
             "block.output_notes.count",
             u32::try_from(num_block_created_notes)
                 .expect("should have less than u32::MAX output notes"),
         );
 
-        let num_batch_created_notes = self
-            .batches()
-            .as_slice()
-            .iter()
-            .fold(0, |acc, batch| acc + batch.output_notes().len());
+        let num_batch_created_notes = self.batches().num_created_notes();
path: crates/block-producer/src/block_builder/mod.rs
user: login: Mirko-von-Leipzig
body: Ah yeah nice catch. It looks like the `block` variant is incorrect now.

`ProposedBlock::output_note_batches` should be used there it seems, though the naming is a bit confusing :)

I'll create an issue.
created_at: 2025-04-18T12:50:03Z
author_association: COLLABORATOR
diff_hunk: @@ -309,21 +309,14 @@ impl TelemetryInjectorExt for ProposedBlock {
             u32::try_from(self.created_nullifiers().len())
                 .expect("should have less than u32::MAX created nullifiers"),
         );
-        let num_block_created_notes = self
-            .output_note_batches()
-            .iter()
-            .fold(0, |acc, output_notes| acc + output_notes.len());
+        let num_block_created_notes = self.batches().num_created_notes();
         span.set_attribute(
             "block.output_notes.count",
             u32::try_from(num_block_created_notes)
                 .expect("should have less than u32::MAX output notes"),
         );
 
-        let num_batch_created_notes = self
-            .batches()
-            .as_slice()
-            .iter()
-            .fold(0, |acc, batch| acc + batch.output_notes().len());
+        let num_batch_created_notes = self.batches().num_created_notes();
path: crates/block-producer/src/block_builder/mod.rs
user: login: bobbinth
body: Nice catch!
created_at: 2025-04-18T19:05:10Z
author_association: CONTRIBUTOR

PR comments:
user: login: igamigo
created_at: 2025-04-04T19:20:55Z
body: After [this comment](https://github.com/0xPolygonMiden/miden-base/pull/1254#issuecomment-2779229339), I wanted to run some tests pointing the client to this branch (and the corresponding branch from `miden-base`) and found that the node logs some errors before any external interaction:

```
2025-04-04T19:17:32.120194Z  INFO store.rpc/ApplyBlock:store.server.apply_block:apply_block:update_in_memory_structs: miden-store: crates/store/src/state.rs:285: new rpc.service: "store.rpc", rpc.method: "ApplyBlock"
2025-04-04T19:17:32.121424Z  INFO store.rpc/ApplyBlock:store.server.apply_block:apply_block:update_in_memory_structs: miden-store: crates/store/src/state.rs:285: close, time.busy: 1.20ms, time.idle: 24.8µs rpc.service: "store.rpc", rpc.method: "ApplyBlock"
2025-04-04T19:17:32.121457Z ERROR store.rpc/ApplyBlock:store.server.apply_block:apply_block: miden-store: crates/store/src/state.rs:231: error: invalid block error rpc.service: "store.rpc", rpc.method: "ApplyBlock"
2025-04-04T19:17:32.121488Z  INFO store.rpc/ApplyBlock:store.server.apply_block:apply_block: miden-store: crates/store/src/state.rs:231: close, time.busy: 1.46ms, time.idle: 956µs rpc.service: "store.rpc", rpc.method: "ApplyBlock"
2025-04-04T19:17:32.121517Z ERROR store.rpc/ApplyBlock:store.server.apply_block: miden-store: crates/store/src/server/api.rs:297: error: status: InvalidArgument, message: "invalid block error", details: [], metadata: MetadataMap { headers: {} } rpc.service: "store.rpc", rpc.method: "ApplyBlock"
2025-04-04T19:17:32.121553Z  INFO store.rpc/ApplyBlock:store.server.apply_block: miden-store: crates/store/src/server/api.rs:297: close, time.busy: 2.38ms, time.idle: 955µs rpc.service: "store.rpc", rpc.method: "ApplyBlock"
2025-04-04T19:17:32.121612Z ERROR store.rpc/ApplyBlock: tower_http::trace::on_failure: /Users/igamigo/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tower-http-0.6.2/src/trace/on_failure.rs:93: response failed, classification: Code: 3, latency: 3 ms rpc.service: "store.rpc", rpc.method: "ApplyBlock"
2025-04-04T19:17:32.121685Z  INFO store.rpc/ApplyBlock: miden_node_utils::tracing::grpc: crates/utils/src/tracing/grpc.rs:36: close, time.busy: 2.64ms, time.idle: 1.03ms rpc.service: "store.rpc", rpc.method: "ApplyBlock"
2025-04-04T19:17:32.122029Z ERROR block_builder.build_block:block_builder.commit_block:store.client.apply_block: miden-block-producer: crates/block-producer/src/store/mod.rs:232: error: gRPC client error
2025-04-04T19:17:32.122088Z  INFO block_builder.build_block:block_builder.commit_block:store.client.apply_block: miden-block-producer: crates/block-producer/src/store/mod.rs:232: close, time.busy: 286µs, time.idle: 4.74ms
2025-04-04T19:17:32.122123Z ERROR block_builder.build_block:block_builder.commit_block: miden-block-producer: crates/block-producer/src/block_builder/mod.rs:214: error: failed to apply block to store
2025-04-04T19:17:32.122152Z  INFO block_builder.build_block:block_builder.commit_block: miden-block-producer: crates/block-producer/src/block_builder/mod.rs:214: close, time.busy: 350µs, time.idle: 4.77ms
```

Haven't looked too much into it yet, but running the node on `next` does not seem to reproduce this
author_association: COLLABORATOR
user: login: PhilippGackstatter
created_at: 2025-04-15T16:39:02Z
body: > After [this comment](https://github.com/0xPolygonMiden/miden-base/pull/1254#issuecomment-2779229339), I wanted to run some tests pointing the client to this branch

The issue was that I forgot to update `SimpleSmt` usage in genesis building. I changed it, and now the node seems to startup and run correctly.

I'll try to run the client integration tests tomorrow to see if any problems occur.


author_association: CONTRIBUTOR
user: login: PhilippGackstatter
created_at: 2025-04-17T18:21:46Z
body: I ran the client integration tests, pointing the dependencies at the corresponding branch in miden-base, but I think due to the changes in the gRPC definitions a few tests are failing. So I think this PR also needs a companion PR in miden-client, cc @igamigo.
author_association: CONTRIBUTOR
user: login: igamigo
created_at: 2025-04-17T18:35:52Z
body: Yeah, changes shouldn't be too bad, I'll work on a PR soon.
author_association: COLLABORATOR
user: login: igamigo
created_at: 2025-04-18T15:44:03Z
body: FYI integration tests work on the client: https://github.com/0xPolygonMiden/miden-client/pull/859
author_association: COLLABORATOR