use alloc::collections::{BTreeMap, BTreeSet};
use alloc::sync::Arc;
use alloc::vec::Vec;
use miden_protocol::Word;
use miden_protocol::account::{AccountId, PartialAccount, StorageMapKey, StorageMapWitness};
use miden_protocol::asset::{AssetId, AssetWitness};
use miden_protocol::block::{BlockHeader, BlockNumber};
use miden_protocol::note::NoteScript;
use miden_protocol::transaction::{AccountInputs, PartialBlockchain};
use miden_tx::TransactionMastStore;
use crate::utils::RwLock;
type AccountCache = BTreeMap<AccountId, PartialAccount>;
type BlockchainCache = BTreeMap<BTreeSet<BlockNumber>, (BlockHeader, PartialBlockchain)>;
type VaultWitnessCache = BTreeMap<(Word, AssetId), AssetWitness>;
pub(super) struct DataStoreCache {
pub(super) mast_store: Arc<TransactionMastStore>,
foreign_account_inputs: RwLock<BTreeMap<AccountId, AccountInputs>>,
note_scripts: RwLock<BTreeMap<Word, NoteScript>>,
storage_map_witnesses: RwLock<BTreeMap<(Word, StorageMapKey), StorageMapWitness>>,
partial_accounts: RwLock<AccountCache>,
blockchains: RwLock<BlockchainCache>,
vault_asset_witnesses: RwLock<VaultWitnessCache>,
cache_execution_inputs: bool,
ref_block: RwLock<Option<BlockNumber>>,
}
impl DataStoreCache {
pub(super) fn new() -> Self {
Self {
mast_store: Arc::new(TransactionMastStore::new()),
foreign_account_inputs: RwLock::new(BTreeMap::new()),
note_scripts: RwLock::new(BTreeMap::new()),
storage_map_witnesses: RwLock::new(BTreeMap::new()),
partial_accounts: RwLock::new(BTreeMap::new()),
blockchains: RwLock::new(BTreeMap::new()),
vault_asset_witnesses: RwLock::new(BTreeMap::new()),
cache_execution_inputs: false,
ref_block: RwLock::new(None),
}
}
pub(super) fn enable_execution_input_cache(&mut self) {
self.cache_execution_inputs = true;
}
pub(super) fn replace_foreign_account_inputs(
&self,
foreign_accounts: impl IntoIterator<Item = AccountInputs>,
) {
let mut cache = self.foreign_account_inputs.write();
cache.clear();
for account_inputs in foreign_accounts {
cache.insert(account_inputs.id(), account_inputs);
}
}
pub(super) fn insert_foreign_account_inputs(&self, account_inputs: AccountInputs) {
self.foreign_account_inputs.write().insert(account_inputs.id(), account_inputs);
}
pub(super) fn get_foreign_account_inputs(
&self,
account_id: AccountId,
) -> Option<AccountInputs> {
self.foreign_account_inputs.read().get(&account_id).cloned()
}
pub(super) fn with_foreign_account_inputs<R>(
&self,
account_id: AccountId,
f: impl FnOnce(&AccountInputs) -> R,
) -> Option<R> {
self.foreign_account_inputs.read().get(&account_id).map(f)
}
pub(super) fn insert_note_scripts(&self, note_scripts: impl IntoIterator<Item = NoteScript>) {
let mut cache = self.note_scripts.write();
for script in note_scripts {
cache.insert(script.root().into(), script);
}
}
pub(super) fn get_note_script(&self, script_root: Word) -> Option<NoteScript> {
self.note_scripts.read().get(&script_root).cloned()
}
pub(super) fn insert_storage_map_witness(
&self,
map_root: Word,
map_key: StorageMapKey,
witness: StorageMapWitness,
) {
self.storage_map_witnesses.write().insert((map_root, map_key), witness);
}
pub(super) fn get_storage_map_witness(
&self,
map_root: Word,
map_key: StorageMapKey,
) -> Option<StorageMapWitness> {
self.storage_map_witnesses.read().get(&(map_root, map_key)).cloned()
}
pub(super) fn get_partial_account(&self, account_id: AccountId) -> Option<PartialAccount> {
if !self.cache_execution_inputs {
return None;
}
self.partial_accounts.read().get(&account_id).cloned()
}
pub(super) fn insert_partial_account(&self, account: &PartialAccount) {
if !self.cache_execution_inputs {
return;
}
self.partial_accounts.write().insert(account.id(), account.clone());
}
pub(super) fn get_blockchain(
&self,
ref_blocks: &BTreeSet<BlockNumber>,
) -> Option<(BlockHeader, PartialBlockchain)> {
if !self.cache_execution_inputs {
return None;
}
self.blockchains.read().get(ref_blocks).cloned()
}
pub(super) fn insert_blockchain(
&self,
ref_blocks: BTreeSet<BlockNumber>,
header: &BlockHeader,
blockchain: &PartialBlockchain,
) {
if !self.cache_execution_inputs {
return;
}
self.blockchains
.write()
.insert(ref_blocks, (header.clone(), blockchain.clone()));
}
pub(super) fn get_vault_asset_witnesses(
&self,
vault_root: Word,
asset_ids: &BTreeSet<AssetId>,
) -> Option<Vec<AssetWitness>> {
if !self.cache_execution_inputs {
return None;
}
let cache = self.vault_asset_witnesses.read();
asset_ids
.iter()
.map(|asset_id| cache.get(&(vault_root, *asset_id)).cloned())
.collect()
}
pub(super) fn insert_vault_asset_witnesses(
&self,
vault_root: Word,
asset_ids: &BTreeSet<AssetId>,
witnesses: &[AssetWitness],
) {
if !self.cache_execution_inputs {
return;
}
let mut cache = self.vault_asset_witnesses.write();
for (asset_id, witness) in asset_ids.iter().zip(witnesses) {
cache.insert((vault_root, *asset_id), witness.clone());
}
}
pub(super) fn ref_block(&self) -> Option<BlockNumber> {
*self.ref_block.read()
}
pub(super) fn set_ref_block(&self, block_num: BlockNumber) {
*self.ref_block.write() = Some(block_num);
}
}