use alloc::collections::BTreeMap;
use super::slot_patch::MergeOutcome;
use crate::Word;
use crate::account::{StorageMap, StorageMapKey, StoragePatchOperation, StorageSlotName};
use crate::errors::AccountPatchError;
use crate::utils::serde::{
ByteReader,
ByteWriter,
Deserializable,
DeserializationError,
Serializable,
};
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum StorageMapPatch {
Create { entries: StorageMapPatchEntries },
Update { entries: StorageMapPatchEntries },
Remove,
}
impl StorageMapPatch {
const CREATE: u8 = 0;
const UPDATE: u8 = 1;
const REMOVE: u8 = 2;
pub fn entries(&self) -> Option<&StorageMapPatchEntries> {
match self {
StorageMapPatch::Create { entries } | StorageMapPatch::Update { entries } => {
Some(entries)
},
StorageMapPatch::Remove => None,
}
}
pub fn into_entries(self) -> Option<StorageMapPatchEntries> {
match self {
StorageMapPatch::Create { entries } | StorageMapPatch::Update { entries } => {
Some(entries)
},
StorageMapPatch::Remove => None,
}
}
pub fn patch_op(&self) -> StoragePatchOperation {
match self {
StorageMapPatch::Create { .. } => StoragePatchOperation::Create,
StorageMapPatch::Update { .. } => StoragePatchOperation::Update,
StorageMapPatch::Remove => StoragePatchOperation::Remove,
}
}
pub(super) fn merge(
&mut self,
slot_name: &StorageSlotName,
other: Self,
) -> Result<MergeOutcome, AccountPatchError> {
match (self, other) {
(StorageMapPatch::Create { .. }, StorageMapPatch::Create { .. }) => {
return Err(AccountPatchError::StoragePatchMergeDoubleCreate(slot_name.clone()));
},
(
StorageMapPatch::Create { entries: current },
StorageMapPatch::Update { entries: incoming },
) => current.merge(incoming),
(StorageMapPatch::Create { .. }, StorageMapPatch::Remove) => {
return Ok(MergeOutcome::Remove);
},
(StorageMapPatch::Update { .. }, StorageMapPatch::Create { .. }) => {
return Err(AccountPatchError::StoragePatchMergeCreateAfterUpdate(
slot_name.clone(),
));
},
(
StorageMapPatch::Update { entries: current },
StorageMapPatch::Update { entries: incoming },
) => current.merge(incoming),
(current @ StorageMapPatch::Update { .. }, StorageMapPatch::Remove) => {
*current = StorageMapPatch::Remove
},
(current @ StorageMapPatch::Remove, incoming @ StorageMapPatch::Create { .. }) => {
*current = incoming;
},
(StorageMapPatch::Remove, StorageMapPatch::Update { .. }) => {
return Err(AccountPatchError::StoragePatchMergeUpdateAfterRemove(
slot_name.clone(),
));
},
(StorageMapPatch::Remove, StorageMapPatch::Remove) => {
return Err(AccountPatchError::StoragePatchMergeDoubleRemove(slot_name.clone()));
},
}
Ok(MergeOutcome::Keep)
}
}
impl Serializable for StorageMapPatch {
fn write_into<W: ByteWriter>(&self, target: &mut W) {
match self {
StorageMapPatch::Create { entries } => {
target.write_u8(Self::CREATE);
target.write(entries);
},
StorageMapPatch::Update { entries } => {
target.write_u8(Self::UPDATE);
target.write(entries);
},
StorageMapPatch::Remove => {
target.write_u8(Self::REMOVE);
},
}
}
fn get_size_hint(&self) -> usize {
let tag_size = 0u8.get_size_hint();
match self {
StorageMapPatch::Create { entries } | StorageMapPatch::Update { entries } => {
tag_size + entries.get_size_hint()
},
StorageMapPatch::Remove => tag_size,
}
}
}
impl Deserializable for StorageMapPatch {
fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
match source.read_u8()? {
Self::CREATE => Ok(Self::Create { entries: source.read()? }),
Self::UPDATE => Ok(Self::Update { entries: source.read()? }),
Self::REMOVE => Ok(Self::Remove),
other => Err(DeserializationError::InvalidValue(format!(
"unknown storage map patch variant {other}"
))),
}
}
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct StorageMapPatchEntries(BTreeMap<StorageMapKey, Word>);
impl StorageMapPatchEntries {
pub fn new() -> Self {
Self(BTreeMap::new())
}
pub fn from_raw(entries: BTreeMap<StorageMapKey, Word>) -> Self {
Self(entries)
}
pub fn num_entries(&self) -> usize {
self.0.len()
}
pub fn as_map(&self) -> &BTreeMap<StorageMapKey, Word> {
&self.0
}
pub fn insert(&mut self, key: StorageMapKey, value: Word) {
self.0.insert(key, value);
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
fn cleared_entries(&self) -> impl Iterator<Item = &StorageMapKey> + Clone {
self.0.iter().filter(|(_, value)| value.is_empty()).map(|(key, _)| key)
}
fn updated_entries(&self) -> impl Iterator<Item = (&StorageMapKey, &Word)> + Clone {
self.0.iter().filter(|(_, value)| !value.is_empty())
}
fn merge(&mut self, other: Self) {
self.0.extend(other.0);
}
pub fn as_map_mut(&mut self) -> &mut BTreeMap<StorageMapKey, Word> {
&mut self.0
}
pub fn into_map(self) -> BTreeMap<StorageMapKey, Word> {
self.0
}
}
impl FromIterator<(StorageMapKey, Word)> for StorageMapPatchEntries {
fn from_iter<T: IntoIterator<Item = (StorageMapKey, Word)>>(iter: T) -> Self {
Self::from_raw(BTreeMap::from_iter(iter))
}
}
impl From<StorageMap> for StorageMapPatchEntries {
fn from(map: StorageMap) -> Self {
StorageMapPatchEntries::from_raw(map.into_entries().into_iter().collect())
}
}
impl Serializable for StorageMapPatchEntries {
fn write_into<W: ByteWriter>(&self, target: &mut W) {
target.write_usize(self.cleared_entries().count());
target.write_many(self.cleared_entries());
target.write_usize(self.updated_entries().count());
target.write_many(self.updated_entries());
}
fn get_size_hint(&self) -> usize {
let num_cleared = self.cleared_entries().count();
let num_updated = self.updated_entries().count();
num_cleared.get_size_hint()
+ num_cleared * StorageMapKey::SERIALIZED_SIZE
+ num_updated.get_size_hint()
+ num_updated * (StorageMapKey::SERIALIZED_SIZE + Word::SERIALIZED_SIZE)
}
}
impl Deserializable for StorageMapPatchEntries {
fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
let mut entries = BTreeMap::new();
let num_cleared = source.read_usize()?;
for key in source.read_many_iter::<StorageMapKey>(num_cleared)? {
let key = key?;
if entries.insert(key, Word::empty()).is_some() {
return Err(DeserializationError::InvalidValue(format!(
"duplicate key {key} in storage map patch entries"
)));
}
}
let num_updated = source.read_usize()?;
for entry in source.read_many_iter::<(StorageMapKey, Word)>(num_updated)? {
let (key, value) = entry?;
if entries.insert(key, value).is_some() {
return Err(DeserializationError::InvalidValue(format!(
"duplicate key {key} in storage map patch entries"
)));
}
}
Ok(Self::from_raw(entries))
}
}