use super::{
AccountDeltaError, ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable,
Word,
};
use crate::utils::{collections::*, string::*};
const MAX_MUTABLE_STORAGE_SLOT_IDX: u8 = 254;
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct AccountStorageDelta {
pub cleared_items: Vec<u8>,
pub updated_items: Vec<(u8, Word)>,
}
impl AccountStorageDelta {
pub fn validate(&self) -> Result<(), AccountDeltaError> {
let num_cleared_items = self.cleared_items.len();
let num_updated_items = self.updated_items.len();
if num_cleared_items > u8::MAX as usize {
return Err(AccountDeltaError::TooManyClearedStorageItems {
actual: num_cleared_items,
max: u8::MAX as usize,
});
} else if num_updated_items > u8::MAX as usize {
return Err(AccountDeltaError::TooManyRemovedAssets {
actual: num_updated_items,
max: u8::MAX as usize,
});
}
for (pos, &idx) in self.cleared_items.iter().enumerate() {
if idx > MAX_MUTABLE_STORAGE_SLOT_IDX {
return Err(AccountDeltaError::ImmutableStorageSlot(idx as usize));
}
if self.cleared_items[..pos].contains(&idx) {
return Err(AccountDeltaError::DuplicateStorageItemUpdate(idx as usize));
}
}
for (pos, (idx, _)) in self.updated_items.iter().enumerate() {
if *idx > MAX_MUTABLE_STORAGE_SLOT_IDX {
return Err(AccountDeltaError::ImmutableStorageSlot(*idx as usize));
}
if self.cleared_items.contains(idx) {
return Err(AccountDeltaError::DuplicateStorageItemUpdate(*idx as usize));
}
if self.updated_items[..pos].iter().any(|x| x.0 == *idx) {
return Err(AccountDeltaError::DuplicateStorageItemUpdate(*idx as usize));
}
}
Ok(())
}
pub fn is_empty(&self) -> bool {
self.cleared_items.is_empty() && self.updated_items.is_empty()
}
}
impl Serializable for AccountStorageDelta {
fn write_into<W: ByteWriter>(&self, target: &mut W) {
assert!(self.cleared_items.len() <= u8::MAX as usize, "too many cleared storage items");
target.write_u8(self.cleared_items.len() as u8);
for idx in self.cleared_items.iter() {
idx.write_into(target);
}
assert!(self.updated_items.len() <= u8::MAX as usize, "too many updated storage items");
target.write_u8(self.updated_items.len() as u8);
for (idx, value) in self.updated_items.iter() {
idx.write_into(target);
value.write_into(target);
}
}
}
impl Deserializable for AccountStorageDelta {
fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
let num_cleared_items = source.read_u8()? as usize;
let mut cleared_items = Vec::with_capacity(num_cleared_items);
for _ in 0..num_cleared_items {
let idx = source.read_u8()?;
if idx > MAX_MUTABLE_STORAGE_SLOT_IDX {
return Err(DeserializationError::InvalidValue(
"immutable storage item cleared".to_string(),
));
}
if cleared_items.contains(&idx) {
return Err(DeserializationError::InvalidValue(
"storage item cleared more than once".to_string(),
));
}
cleared_items.push(idx);
}
let num_updated_items = source.read_u8()? as usize;
let mut updated_items: Vec<(u8, Word)> = Vec::with_capacity(num_updated_items);
for _ in 0..num_updated_items {
let idx = source.read_u8()?;
let value = Word::read_from(source)?;
if idx > MAX_MUTABLE_STORAGE_SLOT_IDX {
return Err(DeserializationError::InvalidValue(
"immutable storage item updated".to_string(),
));
}
if updated_items.iter().any(|x| x.0 == idx) {
return Err(DeserializationError::InvalidValue(
"storage item updated more than once".to_string(),
));
}
if cleared_items.contains(&idx) {
return Err(DeserializationError::InvalidValue(
"storage item both cleared and updated".to_string(),
));
}
updated_items.push((idx, value));
}
Ok(Self { cleared_items, updated_items })
}
}
#[cfg(test)]
mod tests {
use super::{AccountStorageDelta, Deserializable, Serializable};
use crate::{ONE, ZERO};
#[test]
fn account_storage_delta_validation() {
let delta = AccountStorageDelta {
cleared_items: vec![1, 2, 3],
updated_items: vec![(4, [ONE, ONE, ONE, ONE]), (5, [ONE, ONE, ONE, ZERO])],
};
assert!(delta.validate().is_ok());
let bytes = delta.to_bytes();
assert_eq!(AccountStorageDelta::read_from_bytes(&bytes), Ok(delta));
let delta = AccountStorageDelta {
cleared_items: vec![1, 2, 255],
updated_items: vec![],
};
assert!(delta.validate().is_err());
let bytes = delta.to_bytes();
assert!(AccountStorageDelta::read_from_bytes(&bytes).is_err());
let delta = AccountStorageDelta {
cleared_items: vec![1, 2, 1],
updated_items: vec![],
};
assert!(delta.validate().is_err());
let bytes = delta.to_bytes();
assert!(AccountStorageDelta::read_from_bytes(&bytes).is_err());
let delta = AccountStorageDelta {
cleared_items: vec![],
updated_items: vec![(4, [ONE, ONE, ONE, ONE]), (255, [ONE, ONE, ONE, ZERO])],
};
assert!(delta.validate().is_err());
let bytes = delta.to_bytes();
assert!(AccountStorageDelta::read_from_bytes(&bytes).is_err());
let delta = AccountStorageDelta {
cleared_items: vec![],
updated_items: vec![
(4, [ONE, ONE, ONE, ONE]),
(5, [ONE, ONE, ONE, ZERO]),
(4, [ONE, ONE, ZERO, ZERO]),
],
};
assert!(delta.validate().is_err());
let bytes = delta.to_bytes();
assert!(AccountStorageDelta::read_from_bytes(&bytes).is_err());
let delta = AccountStorageDelta {
cleared_items: vec![1, 2, 3],
updated_items: vec![(2, [ONE, ONE, ONE, ONE]), (5, [ONE, ONE, ONE, ZERO])],
};
assert!(delta.validate().is_err());
let bytes = delta.to_bytes();
assert!(AccountStorageDelta::read_from_bytes(&bytes).is_err());
}
}