Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 2 additions & 47 deletions crates/store/src/account_state_forest/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
use std::collections::BTreeSet;
use std::num::NonZeroUsize;

use miden_crypto::hash::rpo::Rpo256;
Expand All @@ -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,
Expand Down Expand Up @@ -293,52 +291,9 @@ impl<B: Backend> AccountStateForest<B> {
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<StorageMapWitness, WitnessError> {
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<AssetVaultKey>,
) -> Result<Vec<AssetWitness>, 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<Vec<_>, 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(
Expand Down
121 changes: 0 additions & 121 deletions crates/store/src/account_state_forest/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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};
Expand All @@ -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]
Expand All @@ -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();
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
// ================================================================================================

Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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);
}
64 changes: 1 addition & 63 deletions crates/store/src/db/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,14 @@ 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,
PublicAccountIdsPage,
PublicAccountStateRootsPage,
};
use crate::db::models::queries::{BlockHeaderCommitment, StorageMapValuesPage};
use crate::db::models::{Page, queries};
use crate::errors::{DatabaseError, NoteSyncError};
use crate::genesis::GenesisBlock;

Expand Down Expand Up @@ -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<Vec<BlockHeader>> {
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<Vec<BlockHeaderCommitment>> {
Expand Down Expand Up @@ -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<Option<AccountInfo>> {
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(
Expand All @@ -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<BlockNumber>,
) -> Result<(Vec<AccountId>, 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.
Expand Down Expand Up @@ -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<NoteRecord>, 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,
Expand Down
2 changes: 2 additions & 0 deletions crates/store/src/db/models/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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<u64>,
Expand Down
Loading
Loading