diff --git a/crates/store/src/account_state_forest/mod.rs b/crates/store/src/account_state_forest/mod.rs index e455a0cf0..592518d6b 100644 --- a/crates/store/src/account_state_forest/mod.rs +++ b/crates/store/src/account_state_forest/mod.rs @@ -1,4 +1,3 @@ -use std::collections::BTreeSet; use std::num::NonZeroUsize; use miden_crypto::hash::rpo::Rpo256; @@ -14,10 +13,9 @@ use miden_protocol::account::{ NonFungibleDeltaAction, StorageMapKey, StorageMapKeyHash, - StorageMapWitness, StorageSlotName, }; -use miden_protocol::asset::{Asset, AssetVaultKey, AssetWitness, FungibleAsset}; +use miden_protocol::asset::{Asset, FungibleAsset}; use miden_protocol::block::BlockNumber; use miden_protocol::crypto::merkle::smt::{ ForestOperation, @@ -293,52 +291,9 @@ impl AccountStateForest { self.get_tree_root(lineage, block_num) } - // WITNESSES and PROOFS + // DETAILS // -------------------------------------------------------------------------------------------- - /// Retrieves a storage map witness for the specified account and storage slot. - /// - /// Note that the `raw_key` is the raw, user-provided key that needs to be hashed in order to - /// get the actual key into the storage map. - #[instrument(target = COMPONENT, skip_all)] - pub(crate) fn get_storage_map_witness( - &self, - account_id: AccountId, - slot_name: &StorageSlotName, - block_num: BlockNumber, - raw_key: StorageMapKey, - ) -> Result { - let lineage = Self::storage_lineage_id(account_id, slot_name); - let tree = self.get_tree_id(lineage, block_num).ok_or(WitnessError::RootNotFound)?; - let key = raw_key.hash().into(); - let proof = self.forest.open(tree, key).map_err(Self::map_forest_error_to_witness)?; - - Ok(StorageMapWitness::new(proof, vec![raw_key])?) - } - - /// Retrieves a vault asset witnesses for the specified account and asset keys at the specified - /// block number. - #[instrument(target = COMPONENT, skip_all)] - pub fn get_vault_asset_witnesses( - &self, - account_id: AccountId, - block_num: BlockNumber, - asset_keys: BTreeSet, - ) -> Result, WitnessError> { - let lineage = Self::vault_lineage_id(account_id); - let tree = self.get_tree_id(lineage, block_num).ok_or(WitnessError::RootNotFound)?; - let witnessees: Result, WitnessError> = - Result::from_iter(asset_keys.into_iter().map(|key| { - let proof = self - .forest - .open(tree, key.into()) - .map_err(Self::map_forest_error_to_witness)?; - let asset = AssetWitness::new(proof)?; - Ok(asset) - })); - witnessees - } - /// Enumerates vault contents for the specified account at the requested block. #[instrument(target = COMPONENT, skip_all)] pub(crate) fn get_vault_details( diff --git a/crates/store/src/account_state_forest/tests.rs b/crates/store/src/account_state_forest/tests.rs index a1f84e237..042bb71f5 100644 --- a/crates/store/src/account_state_forest/tests.rs +++ b/crates/store/src/account_state_forest/tests.rs @@ -9,7 +9,6 @@ use miden_protocol::asset::{ NonFungibleAsset, NonFungibleAssetDetails, }; -use miden_protocol::crypto::merkle::smt::SmtProof; use miden_protocol::testing::account_id::{ ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET, ACCOUNT_ID_REGULAR_PUBLIC_ACCOUNT_IMMUTABLE_CODE, @@ -278,69 +277,6 @@ fn vault_state_is_not_available_for_block_gaps() { assert!(forest.get_vault_root(account_id, block_6).is_some()); } -#[test] -fn witness_queries_work_with_sparse_lineage_updates() { - use std::collections::BTreeMap; - - use assert_matches::assert_matches; - use miden_protocol::account::delta::{StorageMapDelta, StorageSlotDelta}; - - let mut forest = AccountStateForest::new(); - let account_id = dummy_account(); - let faucet_id = dummy_faucet(); - let slot_name = StorageSlotName::mock(6); - let raw_key = StorageMapKey::from_index(1u32); - let value = Word::from([9u32, 0, 0, 0]); - - let block_1 = BlockNumber::GENESIS.child(); - let mut vault_delta_1 = AccountVaultDelta::default(); - vault_delta_1.add_asset(dummy_fungible_asset(faucet_id, 100)).unwrap(); - let mut map_delta_1 = StorageMapDelta::default(); - map_delta_1.insert(raw_key, value); - let raw = BTreeMap::from_iter([(slot_name.clone(), StorageSlotDelta::Map(map_delta_1))]); - let storage_delta_1 = AccountStorageDelta::from_raw(raw); - let delta_1 = dummy_partial_delta(account_id, vault_delta_1, storage_delta_1); - forest.update_account(block_1, &delta_1).unwrap(); - - let block_3 = block_1.child().child(); - let mut vault_delta_3 = AccountVaultDelta::default(); - vault_delta_3.add_asset(dummy_fungible_asset(faucet_id, 50)).unwrap(); - let delta_3 = dummy_partial_delta(account_id, vault_delta_3, AccountStorageDelta::default()); - forest.update_account(block_3, &delta_3).unwrap(); - - let block_2 = block_1.child(); - let asset_key = FungibleAsset::new(faucet_id, 0).unwrap().vault_key(); - let witnesses = forest - .get_vault_asset_witnesses(account_id, block_2, [asset_key].into()) - .unwrap(); - let proof: SmtProof = witnesses[0].clone().into(); - let root_at_2 = forest.get_vault_root(account_id, block_2).unwrap(); - assert_eq!(proof.compute_root(), root_at_2); - - let storage_witness = forest - .get_storage_map_witness(account_id, &slot_name, block_2, raw_key) - .unwrap(); - let storage_root_at_2 = forest.get_storage_map_root(account_id, &slot_name, block_2).unwrap(); - let storage_proof: SmtProof = storage_witness.into(); - assert_eq!(storage_proof.compute_root(), storage_root_at_2); - - let storage_witness_at_3 = forest - .get_storage_map_witness(account_id, &slot_name, block_3, raw_key) - .unwrap(); - let storage_root_at_3 = forest.get_storage_map_root(account_id, &slot_name, block_3).unwrap(); - let storage_proof_at_3: SmtProof = storage_witness_at_3.into(); - assert_eq!(storage_proof_at_3.compute_root(), storage_root_at_3); - - let vault_root_at_3 = forest.get_vault_root(account_id, block_3).unwrap(); - assert_matches!( - forest - .forest - .open(forest.tree_id_for_vault_root(account_id, block_3), asset_key.into()), - Ok(_) - ); - assert_ne!(vault_root_at_3, AccountStateForest::empty_smt_root()); -} - #[test] fn vault_full_state_with_empty_vault_records_root() { use miden_protocol::account::{Account, AccountStorage}; @@ -363,11 +299,6 @@ fn vault_full_state_with_empty_vault_records_root() { let recorded_root = forest.get_vault_root(account_id, block_num); assert_eq!(recorded_root, Some(AccountStateForest::empty_smt_root())); - - let witnesses = forest - .get_vault_asset_witnesses(account_id, block_num, std::collections::BTreeSet::new()) - .expect("get_vault_asset_witnesses should succeed for accounts with empty vaults"); - assert!(witnesses.is_empty()); } #[test] @@ -380,7 +311,6 @@ fn vault_shared_root_retained_when_one_entry_pruned() { let asset_amount = u64::from(HISTORICAL_BLOCK_RETENTION); let amount_increment = asset_amount / u64::from(HISTORICAL_BLOCK_RETENTION); let asset = dummy_fungible_asset(faucet_id, asset_amount); - let asset_key = asset.vault_key(); let mut vault_delta_1 = AccountVaultDelta::default(); vault_delta_1.add_asset(asset).unwrap(); @@ -414,13 +344,6 @@ fn vault_shared_root_retained_when_one_entry_pruned() { let vault_root_at_52 = forest.get_vault_root(account1, block_at_52); assert_eq!(vault_root_at_52, Some(root1)); - - let witnesses = forest - .get_vault_asset_witnesses(account1, block_at_52, [asset_key].into()) - .unwrap(); - assert_eq!(witnesses.len(), 1); - let proof: SmtProof = witnesses[0].clone().into(); - assert_eq!(proof.compute_root(), root1); } // STORAGE MAP TESTS @@ -733,44 +656,6 @@ fn storage_map_all_entries_returns_cache_miss_when_raw_key_is_not_cached() { ); } -#[test] -fn storage_map_key_hashing_and_raw_entries_are_consistent() { - use std::collections::BTreeMap; - - use miden_protocol::account::delta::{StorageMapDelta, StorageSlotDelta}; - - const SLOT_INDEX: usize = 4; - const KEY_VALUE: u32 = 11; - const VALUE_VALUE: u32 = 22; - - let mut forest = AccountStateForest::new(); - let account_id = dummy_account(); - let slot_name = StorageSlotName::mock(SLOT_INDEX); - let block_num = BlockNumber::GENESIS.child(); - let raw_key = StorageMapKey::from_index(KEY_VALUE); - let value = Word::from([VALUE_VALUE, 0, 0, 0]); - - let mut map_delta = StorageMapDelta::default(); - map_delta.insert(raw_key, value); - let raw = BTreeMap::from_iter([(slot_name.clone(), StorageSlotDelta::Map(map_delta))]); - let storage_delta = AccountStorageDelta::from_raw(raw); - let delta = dummy_partial_delta(account_id, AccountVaultDelta::default(), storage_delta); - forest.update_account(block_num, &delta).unwrap(); - - let root = forest.get_storage_map_root(account_id, &slot_name, block_num).unwrap(); - - let witness = forest - .get_storage_map_witness(account_id, &slot_name, block_num, raw_key) - .unwrap(); - let proof: SmtProof = witness.into(); - let hashed_key = raw_key.hash().into(); - // Witness proofs use hashed keys because SMT leaves are keyed by the hash. - assert_eq!(proof.compute_root(), root); - assert_eq!(proof.get(&hashed_key), Some(value)); - // Raw keys never appear in SMT proofs, only their hashed counterparts. - assert_eq!(proof.get(&raw_key.into()), None); -} - // PRUNING TESTS // ================================================================================================ @@ -1083,7 +968,6 @@ fn shared_vault_root_retained_when_one_account_changes() { let block_1 = BlockNumber::GENESIS.child(); let initial_amount = 1000u64; let asset = dummy_fungible_asset(faucet_id, initial_amount); - let asset_key = asset.vault_key(); let mut vault_delta_1 = AccountVaultDelta::default(); vault_delta_1.add_asset(asset).unwrap(); @@ -1115,9 +999,4 @@ fn shared_vault_root_retained_when_one_account_changes() { assert_ne!(root2_at_block1, root2_at_block2, "account2 vault should have changed"); assert!(forest.get_vault_root(account1, block_2).is_some()); - - let witnesses = forest - .get_vault_asset_witnesses(account1, block_2, [asset_key].into()) - .expect("witness generation should succeed for prior version"); - assert_eq!(witnesses.len(), 1); } diff --git a/crates/store/src/db/mod.rs b/crates/store/src/db/mod.rs index 21f2a2a65..7fe96137f 100644 --- a/crates/store/src/db/mod.rs +++ b/crates/store/src/db/mod.rs @@ -32,6 +32,7 @@ use tracing::{info, instrument}; use crate::COMPONENT; use crate::db::migrations::{bootstrap_database, migrate_database, verify_latest_schema}; use crate::db::models::conv::SqlTypeConvert; +use crate::db::models::queries; pub use crate::db::models::queries::{ AccountCommitmentsPage, NullifiersPage, @@ -39,7 +40,6 @@ pub use crate::db::models::queries::{ PublicAccountStateRootsPage, }; use crate::db::models::queries::{BlockHeaderCommitment, StorageMapValuesPage}; -use crate::db::models::{Page, queries}; use crate::errors::{DatabaseError, NoteSyncError}; use crate::genesis::GenesisBlock; @@ -327,16 +327,6 @@ impl Db { .await } - /// Loads all the block headers from the DB. - #[instrument(level = "debug", target = COMPONENT, skip_all, ret(level = "debug"), err)] - pub async fn select_all_block_headers(&self) -> Result> { - self.transact("all block headers", |conn| { - let raw = queries::select_all_block_headers(conn)?; - Ok(raw) - }) - .await - } - /// Loads all the block headers from the DB. #[instrument(level = "debug", target = COMPONENT, skip_all, ret(level = "debug"), err)] pub async fn select_all_block_header_commitments(&self) -> Result> { @@ -393,18 +383,6 @@ impl Db { .await } - /// Loads public account details for a network account by its full account ID. - #[instrument(level = "debug", target = COMPONENT, skip_all, ret(level = "debug"), err)] - pub async fn select_network_account_by_id( - &self, - account_id: AccountId, - ) -> Result> { - self.transact("Get network account by id", move |conn| { - queries::select_network_account_by_id(conn, account_id) - }) - .await - } - /// Returns the subset of the provided account IDs that classify as network accounts. #[instrument(level = "debug", target = COMPONENT, skip_all, ret(level = "debug"), err)] pub async fn select_network_accounts_subset( @@ -417,30 +395,6 @@ impl Db { .await } - /// Returns network account IDs within the specified block range (based on account creation - /// block). - /// - /// The function may return fewer accounts than exist in the range if the result would exceed - /// `MAX_RESPONSE_PAYLOAD_BYTES / AccountId::SERIALIZED_SIZE` rows. In this case, the result is - /// truncated at a block boundary to ensure all accounts from included blocks are returned. - /// - /// # Returns - /// - /// A tuple containing: - /// - A vector of network account IDs. - /// - The last block number that was fully included in the result. When truncated, this will be - /// less than the requested range end. - #[instrument(level = "debug", target = COMPONENT, skip_all, ret(level = "debug"), err)] - pub async fn select_all_network_account_ids( - &self, - block_range: RangeInclusive, - ) -> Result<(Vec, BlockNumber)> { - self.transact("Get all network account IDs", move |conn| { - queries::select_all_network_account_ids(conn, block_range) - }) - .await - } - /// Queries the account code by its commitment hash. /// /// Returns `None` if no code exists with that commitment. @@ -668,22 +622,6 @@ impl Db { }) } - /// Loads the network notes for an account that are unconsumed by a specified block number. - /// Pagination is used to limit the number of notes returned. - pub(crate) async fn select_unconsumed_network_notes( - &self, - account_id: AccountId, - block_num: BlockNumber, - page: Page, - ) -> Result<(Vec, Page)> { - self.transact("unconsumed network notes for account", move |conn| { - models::queries::select_unconsumed_network_notes_by_account_id( - conn, account_id, block_num, page, - ) - }) - .await - } - pub async fn get_account_vault_sync( &self, account_id: AccountId, diff --git a/crates/store/src/db/models/mod.rs b/crates/store/src/db/models/mod.rs index 09d4bf92f..6eee11b51 100644 --- a/crates/store/src/db/models/mod.rs +++ b/crates/store/src/db/models/mod.rs @@ -13,6 +13,7 @@ //! The first step in debugging should always be using the fully qualified //! calling syntext when dealing with diesel. +#[cfg(test)] use std::num::NonZeroUsize; use crate::errors::DatabaseError; @@ -25,6 +26,7 @@ pub(crate) mod utils; pub(crate) use utils::*; /// The page token and size to query from the DB. +#[cfg(test)] #[derive(Debug, Copy, Clone)] pub struct Page { pub token: Option, diff --git a/crates/store/src/db/models/queries/accounts.rs b/crates/store/src/db/models/queries/accounts.rs index e0b9038c4..e10cea78c 100644 --- a/crates/store/src/db/models/queries/accounts.rs +++ b/crates/store/src/db/models/queries/accounts.rs @@ -226,50 +226,6 @@ pub(crate) fn select_full_account( Ok(Account::new(account_id, vault, storage, code, nonce, None)?) } -/// Select the latest account info for a network account by its full account ID. -/// -/// # Returns -/// -/// The latest account info, `None` if the account was not found, or an error. -/// -/// # Raw SQL -/// -/// ```sql -/// SELECT -/// accounts.account_id, -/// accounts.account_commitment, -/// accounts.block_num -/// FROM -/// accounts -/// WHERE -/// account_id = ?1 -/// AND network_account_type = 1 -/// AND is_latest = 1 -/// ``` -pub(crate) fn select_network_account_by_id( - conn: &mut SqliteConnection, - account_id: AccountId, -) -> Result, DatabaseError> { - let maybe_summary = SelectDsl::select(schema::accounts::table, AccountSummaryRaw::as_select()) - .filter(schema::accounts::account_id.eq(account_id.to_bytes())) - .filter(schema::accounts::network_account_type.eq(NetworkAccountType::Network.to_raw_sql())) - .filter(schema::accounts::is_latest.eq(true)) - .get_result::(conn) - .optional() - .map_err(DatabaseError::Diesel)?; - - match maybe_summary { - None => Ok(None), - Some(raw) => { - let summary: AccountSummary = raw.try_into()?; - let account_id = summary.account_id; - // Backfill account details from database - let details = select_full_account(conn, account_id).ok(); - Ok(Some(AccountInfo { summary, details })) - }, - } -} - /// Page of account commitments returned by [`select_account_commitments_paged`]. #[derive(Debug)] pub struct AccountCommitmentsPage { @@ -643,85 +599,6 @@ pub(crate) fn select_all_accounts( Ok(account_infos) } -/// Returns network account IDs within the specified block range (based on account creation -/// block). -/// -/// The function may return fewer accounts than exist in the range if the result would exceed -/// `MAX_RESPONSE_PAYLOAD_BYTES / AccountId::SERIALIZED_SIZE` rows. In this case, the result is -/// truncated at a block boundary to ensure all accounts from included blocks are returned. -/// -/// # Returns -/// -/// A tuple containing: -/// - A vector of network account IDs. -/// - The last block number that was fully included in the result. When truncated, this will be less -/// than the requested range end. -pub(crate) fn select_all_network_account_ids( - conn: &mut SqliteConnection, - block_range: RangeInclusive, -) -> Result<(Vec, BlockNumber), DatabaseError> { - const ROW_OVERHEAD_BYTES: usize = AccountId::SERIALIZED_SIZE; - const MAX_ROWS: usize = MAX_RESPONSE_PAYLOAD_BYTES / ROW_OVERHEAD_BYTES; - - const _: () = assert!( - MAX_ROWS > miden_protocol::MAX_ACCOUNTS_PER_BLOCK, - "Block pagination limit must exceed maximum block capacity to uphold assumed logic invariant" - ); - - if block_range.is_empty() { - return Err(DatabaseError::InvalidBlockRange { - from: *block_range.start(), - to: *block_range.end(), - }); - } - - let account_ids_raw: Vec<(Vec, i64)> = Box::new( - QueryDsl::select( - schema::accounts::table - .filter( - schema::accounts::network_account_type - .eq(NetworkAccountType::Network.to_raw_sql()), - ) - .filter(schema::accounts::is_latest.eq(true)), - (schema::accounts::account_id, schema::accounts::created_at_block), - ) - .filter( - schema::accounts::block_num - .between(block_range.start().to_raw_sql(), block_range.end().to_raw_sql()), - ) - .order(schema::accounts::created_at_block.asc()) - .limit(i64::try_from(MAX_ROWS + 1).expect("limit fits within i64")), - ) - .load::<(Vec, i64)>(conn)?; - - if account_ids_raw.len() > MAX_ROWS { - // SAFETY: We just checked that len > MAX_ROWS, so the vec is not empty. - let last_created_at_block = account_ids_raw.last().expect("vec is not empty").1; - - let account_ids = account_ids_raw - .into_iter() - .take_while(|(_, created_at_block)| *created_at_block != last_created_at_block) - .map(|(id_bytes, _)| { - AccountId::read_from_bytes(&id_bytes).map_err(DatabaseError::DeserializationError) - }) - .collect::, DatabaseError>>()?; - - let last_block_included = - BlockNumber::from_raw_sql(last_created_at_block.saturating_sub(1))?; - - Ok((account_ids, last_block_included)) - } else { - let account_ids = account_ids_raw - .into_iter() - .map(|(id_bytes, _)| { - AccountId::read_from_bytes(&id_bytes).map_err(DatabaseError::DeserializationError) - }) - .collect::, DatabaseError>>()?; - - Ok((account_ids, *block_range.end())) - } -} - #[derive(Debug, Clone, PartialEq, Eq)] pub struct StorageMapValue { pub block_num: BlockNumber, diff --git a/crates/store/src/db/models/queries/block_headers.rs b/crates/store/src/db/models/queries/block_headers.rs index f9a793860..42e4d8d55 100644 --- a/crates/store/src/db/models/queries/block_headers.rs +++ b/crates/store/src/db/models/queries/block_headers.rs @@ -127,29 +127,6 @@ pub fn select_block_headers( vec_raw_try_into(raw_block_headers) } -/// Select all block headers from the DB using the given [`SqliteConnection`]. -/// -/// # Returns -/// -/// A vector of [`BlockHeader`] or an error. -/// -/// # Raw SQL -/// -/// ```sql -/// SELECT block_num, block_header -/// FROM block_headers -/// ORDER BY block_num ASC -/// ``` -pub fn select_all_block_headers( - conn: &mut SqliteConnection, -) -> Result, DatabaseError> { - let raw_block_headers = - QueryDsl::select(schema::block_headers::table, BlockHeaderRawRow::as_select()) - .order(schema::block_headers::block_num.asc()) - .load::(conn)?; - vec_raw_try_into(raw_block_headers) -} - /// Select all block headers from the DB using the given [`SqliteConnection`]. /// /// # Returns diff --git a/crates/store/src/db/models/queries/notes.rs b/crates/store/src/db/models/queries/notes.rs index 4ed020503..dbe556cd2 100644 --- a/crates/store/src/db/models/queries/notes.rs +++ b/crates/store/src/db/models/queries/notes.rs @@ -6,8 +6,9 @@ use std::collections::{BTreeMap, BTreeSet, HashSet}; use std::ops::RangeInclusive; +#[cfg(test)] +use diesel::prelude::BoolExpressionMethods; use diesel::prelude::{ - BoolExpressionMethods, ExpressionMethods, Insertable, QueryDsl, @@ -53,6 +54,8 @@ use miden_protocol::utils::serde::{Deserializable, Serializable}; use miden_standards::note::NetworkAccountTarget; use crate::COMPONENT; +#[cfg(test)] +use crate::db::models::Page; use crate::db::models::conv::{ SqlTypeConvert, idx_to_raw_sql, @@ -61,7 +64,7 @@ use crate::db::models::conv::{ }; use crate::db::models::queries::select_block_header_by_block_num; use crate::db::models::{serialize_vec, vec_raw_try_into}; -use crate::db::{DatabaseError, NoteRecord, NoteSyncRecord, NoteSyncUpdate, Page, schema}; +use crate::db::{DatabaseError, NoteRecord, NoteSyncRecord, NoteSyncUpdate, schema}; use crate::errors::NoteSyncError; /// Estimated byte size of a [`NoteSyncUpdate`] excluding its notes. @@ -484,6 +487,7 @@ pub(crate) fn select_note_script_by_root( /// LIMIT ?4 /// ``` #[expect(clippy::cast_sign_loss, reason = "row_id is a positive integer")] +#[cfg(test)] pub(crate) fn select_unconsumed_network_notes_by_account_id( conn: &mut SqliteConnection, account_id: AccountId, diff --git a/crates/store/src/db/tests.rs b/crates/store/src/db/tests.rs index 98df691cb..beec90064 100644 --- a/crates/store/src/db/tests.rs +++ b/crates/store/src/db/tests.rs @@ -36,7 +36,6 @@ use miden_protocol::block::{ use miden_protocol::crypto::dsa::ecdsa_k256_keccak::SigningKey; use miden_protocol::crypto::merkle::SparseMerklePath; use miden_protocol::crypto::merkle::mmr::{Forest, Mmr}; -use miden_protocol::crypto::merkle::smt::SmtProof; use miden_protocol::crypto::rand::RandomCoin; use miden_protocol::note::{ Note, @@ -79,7 +78,7 @@ use rand::Rng; use tempfile::tempdir; use super::{AccountInfo, NoteRecord, NoteSyncRecord, NullifierInfo, TransactionRecord}; -use crate::account_state_forest::HISTORICAL_BLOCK_RETENTION; +use crate::account_state_forest::{AccountStorageMapResult, HISTORICAL_BLOCK_RETENTION}; use crate::db::models::queries::{StorageMapValue, insert_account_storage_map_value}; use crate::db::models::{Page, queries, utils}; use crate::errors::DatabaseError; @@ -722,9 +721,6 @@ fn db_block_header() { let res = queries::select_block_header_by_block_num(conn, None).unwrap(); assert!(res.is_none()); - let res = queries::select_all_block_headers(conn).unwrap(); - assert!(res.is_empty()); - let block_header = BlockHeader::new( 1_u8.into(), num_to_word(2), @@ -779,7 +775,11 @@ fn db_block_header() { let res = queries::select_block_header_by_block_num(conn, None).unwrap(); assert_eq!(res.unwrap(), block_header2); - let res = queries::select_all_block_headers(conn).unwrap(); + let res = queries::select_block_headers( + conn, + [block_header.block_num(), block_header2.block_num()].into_iter(), + ) + .unwrap(); assert_eq!(res, [block_header, block_header2]); } @@ -3305,27 +3305,6 @@ fn account_state_forest_shared_roots_not_deleted_prematurely() { assert_eq!(root1, root2); assert_eq!(root2, root3); - // Verify we can get witnesses for all three accounts and verify them against roots - let witness1 = forest - .get_storage_map_witness(account1, &slot_name, block01, key1) - .expect("Account1 should have accessible storage map"); - let witness2 = forest - .get_storage_map_witness(account2, &slot_name, block02, key1) - .expect("Account2 should have accessible storage map"); - let witness3 = forest - .get_storage_map_witness(account3, &slot_name, block02, key1) - .expect("Account3 should have accessible storage map"); - - // Verify witnesses against storage map roots using SmtProof::compute_root - let proof1: SmtProof = witness1.into(); - assert_eq!(proof1.compute_root(), root1, "Witness1 must verify against root1"); - - let proof2: SmtProof = witness2.into(); - assert_eq!(proof2.compute_root(), root2, "Witness2 must verify against root2"); - - let proof3: SmtProof = witness3.into(); - assert_eq!(proof3.compute_root(), root3, "Witness3 must verify against root3"); - let total_roots_removed = forest.prune(block50); assert_eq!(total_roots_removed, 0); @@ -3375,28 +3354,9 @@ fn account_state_forest_shared_roots_not_deleted_prematurely() { assert_eq!(total_roots_removed, 0); // Account2 and Account3 should still be accessible at their recent blocks - let account1_root = forest.get_storage_map_root(account1, &slot_name, block53).unwrap(); - let account2_root = forest.get_storage_map_root(account2, &slot_name, block51).unwrap(); - let account3_root = forest.get_storage_map_root(account3, &slot_name, block52).unwrap(); - - // Verify we can still get witnesses for account2 and account3 and verify against roots - let witness1_after = forest - .get_storage_map_witness(account2, &slot_name, block51, key1) - .expect("Account2 should still have accessible storage map after pruning account1"); - let witness2_after = forest - .get_storage_map_witness(account3, &slot_name, block52, key1) - .expect("Account3 should still have accessible storage map after pruning account1"); - - // Verify witnesses against storage map roots - let proof1: SmtProof = witness1_after.into(); - assert_eq!(proof1.compute_root(), account2_root,); - let proof2: SmtProof = witness2_after.into(); - assert_eq!(proof2.compute_root(), account3_root,); - let account1_witness = forest - .get_storage_map_witness(account1, &slot_name, block53, key1) - .expect("Account1 should still have accessible storage map after pruning"); - let account1_proof: SmtProof = account1_witness.into(); - assert_eq!(account1_proof.compute_root(), account1_root,); + forest.get_storage_map_root(account1, &slot_name, block53).unwrap(); + forest.get_storage_map_root(account2, &slot_name, block51).unwrap(); + forest.get_storage_map_root(account3, &slot_name, block52).unwrap(); } #[test] @@ -3467,9 +3427,6 @@ fn account_state_forest_retains_latest_after_100_blocks_and_pruning() { Some(root) if root == initial_storage_map_root ); - let witness = forest.get_storage_map_witness(account_id, &slot_map, block_100, key1); - assert!(witness.is_ok()); - // Now add an update at block 51 (within retention window) to test that old entries get pruned // when newer entries exist let block_51 = BlockNumber::from(51); @@ -3500,23 +3457,12 @@ fn account_state_forest_retains_latest_after_100_blocks_and_pruning() { let vault_root_at_51 = forest .get_vault_root(account_id, block_51) .expect("Should have vault root at block 51"); - let storage_root_at_51 = forest + forest .get_storage_map_root(account_id, &slot_map, block_51) .expect("Should have storage root at block 51"); assert_ne!(vault_root_at_51, initial_vault_root); - let witness = forest - .get_storage_map_witness(account_id, &slot_map, block_51, key1) - .expect("Should be able to get witness for key1"); - - let proof: SmtProof = witness.into(); - assert_eq!( - proof.compute_root(), - storage_root_at_51, - "Witness must verify against storage root" - ); - let vault_root_at_1 = forest.get_vault_root(account_id, block_1); assert!(vault_root_at_1.is_some()); } @@ -3561,20 +3507,6 @@ fn account_state_forest_preserves_most_recent_vault_only() { .get_vault_root(account_id, block_1) .expect("Should still have vault root at block 1"); assert_eq!(vault_root_at_1, initial_vault_root, "Vault root should be preserved"); - - // Verify we can get witnesses for the vault and verify against vault root - let witnesses = forest - .get_vault_asset_witnesses(account_id, block_1, [asset.vault_key()].into()) - .expect("Should be able to get vault witness after pruning"); - - assert_eq!(witnesses.len(), 1, "Should have one witness"); - let witness = &witnesses[0]; - let proof: SmtProof = witness.clone().into(); - assert_eq!( - proof.compute_root(), - vault_root_at_1, - "Vault witness must verify against vault root" - ); } #[test] @@ -3717,19 +3649,11 @@ fn account_state_forest_preserves_most_recent_storage_map_only() { .expect("Should still have storage root at block 1"); assert_eq!(storage_root_at_1, initial_storage_root, "Storage root should be preserved"); - // Verify we can get witnesses for the storage map and verify against storage root - let witness = forest - .get_storage_map_witness(account_id, &slot_map, block_1, key1) - .expect("Should be able to get storage witness after pruning"); - - let proof: SmtProof = witness.into(); - assert_eq!( - proof.compute_root(), - storage_root_at_1, - "Storage witness must verify against storage root" - ); - // Verify we can get all entries + let result = forest + .get_storage_map_details_for_all_entries(account_id, slot_map, block_100) + .expect("should have storage map details"); + assert_matches!(result, AccountStorageMapResult::Details(details) if details.entries == StorageMapEntries::AllEntries(vec![(key1, value1)])); } #[test] diff --git a/crates/store/src/errors.rs b/crates/store/src/errors.rs index 2de361521..9fcb7e03b 100644 --- a/crates/store/src/errors.rs +++ b/crates/store/src/errors.rs @@ -272,14 +272,6 @@ impl From for NoteSyncError { } } -#[derive(Error, Debug)] -pub enum GetCurrentBlockchainDataError { - #[error("failed to retrieve block header")] - ErrorRetrievingBlockHeader(#[source] DatabaseError), - #[error("failed to instantiate MMR peaks")] - InvalidPeaks(MmrError), -} - #[derive(Error, Debug)] pub enum GetBatchInputsError { #[error("failed to select note inclusion proofs")] diff --git a/crates/store/src/state/mod.rs b/crates/store/src/state/mod.rs index 9b0c1684d..87b2fdc18 100644 --- a/crates/store/src/state/mod.rs +++ b/crates/store/src/state/mod.rs @@ -5,38 +5,33 @@ use std::collections::{BTreeMap, BTreeSet, HashSet}; use std::num::NonZeroUsize; -use std::ops::RangeInclusive; use std::path::{Path, PathBuf}; use std::sync::Arc; -use miden_node_proto::domain::account::AccountInfo; use miden_node_proto::domain::batch::BatchInputs; use miden_node_utils::clap::StorageOptions; use miden_node_utils::formatting::format_array; use miden_protocol::Word; -use miden_protocol::account::{AccountId, StorageMapKey, StorageMapWitness, StorageSlotName}; -use miden_protocol::asset::{AssetVaultKey, AssetWitness}; +use miden_protocol::account::AccountId; use miden_protocol::block::account_tree::AccountWitness; use miden_protocol::block::nullifier_tree::{NullifierTree, NullifierWitness}; use miden_protocol::block::{BlockHeader, BlockInputs, BlockNumber, Blockchain}; -use miden_protocol::crypto::merkle::mmr::{MmrPeaks, MmrProof, PartialMmr}; +use miden_protocol::crypto::merkle::mmr::{MmrProof, PartialMmr}; use miden_protocol::crypto::merkle::smt::{LargeSmt, SmtStorage}; use miden_protocol::note::{NoteId, NoteScript, Nullifier}; use miden_protocol::transaction::PartialBlockchain; use tokio::sync::{Mutex, RwLock, watch}; use tracing::{Instrument, Span, info, instrument}; -use crate::account_state_forest::{AccountStateForest, AccountStateForestBackend, WitnessError}; +use crate::account_state_forest::{AccountStateForest, AccountStateForestBackend}; use crate::accounts::AccountTreeWithHistory; use crate::blocks::BlockStore; -use crate::db::models::Page; use crate::db::{Db, NoteRecord, NullifierInfo}; use crate::errors::{ DatabaseError, GetBatchInputsError, GetBlockHeaderError, GetBlockInputsError, - GetCurrentBlockchainDataError, StateInitializationError, }; use crate::proven_tip::ProvenTipWriter; @@ -404,35 +399,6 @@ impl State { self.db.select_notes_by_id(note_ids).await } - /// If the input block number is the current chain tip, `None` is returned. Otherwise, gets the - /// current chain tip's block header with its corresponding MMR peaks. - pub async fn get_current_blockchain_data( - &self, - block_num: Option, - ) -> Result, GetCurrentBlockchainDataError> { - if let Some(number) = block_num - && number == self.chain_tip(Finality::Committed).await - { - return Ok(None); - } - - // SAFETY: `select_block_header_by_block_num` will always return `Some(chain_tip_header)` - // when `None` is passed - let block_header: BlockHeader = self - .db - .select_block_header_by_block_num(None) - .await - .map_err(GetCurrentBlockchainDataError::ErrorRetrievingBlockHeader)? - .unwrap(); - - let blockchain = &self.inner.read().await.blockchain; - let peaks = blockchain - .peaks_at(block_header.block_num()) - .map_err(GetCurrentBlockchainDataError::InvalidPeaks)?; - - Ok(Some((block_header, peaks))) - } - /// Fetches the inputs for a transaction batch from the database. /// /// ## Inputs @@ -738,19 +704,6 @@ impl State { }) } - /// Returns details for public (on-chain) account. - pub async fn get_account_details(&self, id: AccountId) -> Result { - self.db.select_account(id).await - } - - /// Returns details for public (on-chain) network accounts by full account ID. - pub async fn get_network_account_details_by_id( - &self, - account_id: AccountId, - ) -> Result, DatabaseError> { - self.db.select_network_account_by_id(account_id).await - } - /// Filters `account_ids` down to the subset classified as network accounts. pub async fn filter_network_accounts( &self, @@ -759,21 +712,6 @@ impl State { self.db.select_network_accounts_subset(account_ids.to_vec()).await } - /// Returns network account IDs within the specified block range (based on account creation - /// block). - /// - /// The function may return fewer accounts than exist in the range if the result would exceed - /// `MAX_RESPONSE_PAYLOAD_BYTES / AccountId::SERIALIZED_SIZE` rows. In this case, the result is - /// truncated at a block boundary to ensure all accounts from included blocks are returned. - /// - /// The response includes the last block number that was fully included in the result. - pub async fn get_all_network_accounts( - &self, - block_range: RangeInclusive, - ) -> Result<(Vec, BlockNumber), DatabaseError> { - self.db.select_all_network_account_ids(block_range).await - } - /// Returns the effective chain tip for the given finality level. /// /// - [`Finality::Committed`]: returns the latest committed block number (from in-memory MMR). @@ -813,17 +751,6 @@ impl State { self.block_store.load_proof(block_num).await.map_err(Into::into) } - /// Returns the network notes for an account that are unconsumed by a specified block number, - /// along with the next pagination token. - pub async fn get_unconsumed_network_notes_for_account( - &self, - account_id: AccountId, - block_num: BlockNumber, - page: Page, - ) -> Result<(Vec, Page), DatabaseError> { - self.db.select_unconsumed_network_notes(account_id, block_num, page).await - } - /// Returns the script for a note by its root. pub async fn get_note_script_by_root( &self, @@ -831,33 +758,4 @@ impl State { ) -> Result, DatabaseError> { self.db.select_note_script_by_root(root).await } - - /// Returns vault asset witnesses for the specified account and block number. - pub fn get_vault_asset_witnesses( - &self, - account_id: AccountId, - block_num: BlockNumber, - vault_keys: BTreeSet, - ) -> Result, WitnessError> { - self.with_forest_read_blocking(|forest| { - forest.get_vault_asset_witnesses(account_id, block_num, vault_keys) - }) - } - - /// Returns a storage map witness for the specified account and storage entry at the block - /// number. - /// - /// Note that the `raw_key` is the raw, user-provided key that needs to be hashed in order to - /// get the actual key into the storage map. - pub fn get_storage_map_witness( - &self, - account_id: AccountId, - slot_name: &StorageSlotName, - block_num: BlockNumber, - raw_key: StorageMapKey, - ) -> Result { - self.with_forest_read_blocking(|forest| { - forest.get_storage_map_witness(account_id, slot_name, block_num, raw_key) - }) - } }