use alloc::collections::{BTreeMap, BTreeSet};
use miden_crypto::Word;
use miden_crypto::merkle::InnerNodeInfo;
use miden_crypto::merkle::smt::SmtLeaf;
use super::{AccountStorage, AccountStorageHeader, StorageSlotContent};
use crate::account::PartialStorageMap;
use crate::errors::AccountError;
use crate::utils::serde::{
ByteReader,
ByteWriter,
Deserializable,
DeserializationError,
Serializable,
};
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct PartialStorage {
commitment: Word,
header: AccountStorageHeader,
maps: BTreeMap<Word, PartialStorageMap>,
}
impl PartialStorage {
pub fn new(
storage_header: AccountStorageHeader,
storage_maps: impl IntoIterator<Item = PartialStorageMap>,
) -> Result<Self, AccountError> {
let storage_map_roots: BTreeSet<_> = storage_header.map_slot_roots().collect();
let mut maps = BTreeMap::new();
for smt in storage_maps {
if !storage_map_roots.contains(&smt.root()) {
return Err(AccountError::StorageMapRootNotFound(smt.root()));
}
maps.insert(smt.root(), smt);
}
let commitment = storage_header.to_commitment();
Ok(Self { commitment, header: storage_header, maps })
}
pub fn new_full(account_storage: AccountStorage) -> Self {
let header: AccountStorageHeader = account_storage.to_header();
let commitment = header.to_commitment();
let mut maps = BTreeMap::new();
for slot in account_storage {
if let StorageSlotContent::Map(storage_map) = slot.into_parts().1 {
let partial_map = PartialStorageMap::new_full(storage_map);
maps.insert(partial_map.root(), partial_map);
}
}
PartialStorage { header, maps, commitment }
}
pub fn new_minimal(account_storage: &AccountStorage) -> Self {
let header: AccountStorageHeader = account_storage.to_header();
let commitment = header.to_commitment();
let mut maps = BTreeMap::new();
for slot in account_storage.slots() {
if let StorageSlotContent::Map(storage_map) = slot.content() {
let partial_map = PartialStorageMap::new_minimal(storage_map);
maps.insert(partial_map.root(), partial_map);
}
}
PartialStorage { header, maps, commitment }
}
pub fn header(&self) -> &AccountStorageHeader {
&self.header
}
pub fn commitment(&self) -> Word {
self.commitment
}
pub fn into_parts(self) -> (Word, AccountStorageHeader, BTreeMap<Word, PartialStorageMap>) {
(self.commitment, self.header, self.maps)
}
pub fn inner_nodes(&self) -> impl Iterator<Item = InnerNodeInfo> {
self.maps.iter().flat_map(|(_, map)| map.inner_nodes())
}
pub fn maps(&self) -> impl Iterator<Item = &PartialStorageMap> + '_ {
self.maps.values()
}
pub fn leaves(&self) -> impl Iterator<Item = &SmtLeaf> + '_ {
self.maps().flat_map(|map| map.leaves()).map(|(_, leaf)| leaf)
}
}
impl Serializable for PartialStorage {
fn write_into<W: ByteWriter>(&self, target: &mut W) {
target.write(&self.header);
target.write(&self.maps);
}
}
impl Deserializable for PartialStorage {
fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
let header: AccountStorageHeader = source.read()?;
let map_smts: BTreeMap<Word, PartialStorageMap> = source.read()?;
let commitment = header.to_commitment();
Ok(PartialStorage { header, maps: map_smts, commitment })
}
}
#[cfg(test)]
mod tests {
use anyhow::Context;
use miden_core::Word;
use crate::account::{
AccountStorage,
AccountStorageHeader,
PartialStorage,
PartialStorageMap,
StorageMap,
StorageMapKey,
StorageSlot,
StorageSlotName,
};
#[test]
pub fn new_partial_storage() -> anyhow::Result<()> {
let map_key_present = StorageMapKey::from_array([1, 2, 3, 4]);
let map_key_absent = StorageMapKey::from_array([9, 12, 18, 3]);
let mut map_1 = StorageMap::new();
map_1.insert(map_key_absent, Word::try_from([1u64, 2, 3, 2])?).unwrap();
map_1.insert(map_key_present, Word::try_from([5u64, 4, 3, 2])?).unwrap();
assert_eq!(map_1.get(&map_key_present), [5u64, 4, 3, 2].try_into()?);
let slot_name = StorageSlotName::new("miden::test_map")?;
let storage =
AccountStorage::new(vec![StorageSlot::with_map(slot_name.clone(), map_1.clone())])
.unwrap();
let storage_header = AccountStorageHeader::from(&storage);
let witness = map_1.open(&map_key_present);
let partial_storage =
PartialStorage::new(storage_header, [PartialStorageMap::with_witnesses([witness])?])
.context("creating partial storage")?;
let slot_header = partial_storage.header.find_slot_header_by_name(&slot_name).unwrap();
let retrieved_map = partial_storage.maps.get(&slot_header.value()).unwrap();
assert!(retrieved_map.open(&map_key_absent).is_err());
assert!(retrieved_map.open(&map_key_present).is_ok());
Ok(())
}
}