Skip to main content

miden_protocol/account/patch/storage/
slot_patch.rs

1use crate::account::{
2    StorageMapPatch,
3    StorageMapPatchEntries,
4    StoragePatchOperation,
5    StorageSlotContent,
6    StorageSlotName,
7    StorageSlotType,
8    StorageValuePatch,
9};
10use crate::errors::AccountPatchError;
11use crate::utils::serde::{
12    ByteReader,
13    ByteWriter,
14    Deserializable,
15    DeserializationError,
16    Serializable,
17};
18
19// STORAGE SLOT PATCH
20// ================================================================================================
21
22/// The patch of a single storage slot.
23///
24/// - [`StorageSlotPatch::Value`] carries the [`StorageValuePatch`] for a value slot.
25/// - [`StorageSlotPatch::Map`] carries the [`StorageMapPatch`] for a map slot.
26#[derive(Debug, Clone, PartialEq, Eq)]
27pub enum StorageSlotPatch {
28    Value(StorageValuePatch),
29    Map(StorageMapPatch),
30}
31
32impl StorageSlotPatch {
33    // CONSTANTS
34    // ----------------------------------------------------------------------------------------
35
36    /// The type byte for value slot patches.
37    const VALUE: u8 = 0;
38
39    /// The type byte for map slot patches.
40    const MAP: u8 = 1;
41
42    // ACCESSORS
43    // ----------------------------------------------------------------------------------------
44
45    /// Returns the [`StorageSlotType`] of this slot patch.
46    pub fn slot_type(&self) -> StorageSlotType {
47        match self {
48            StorageSlotPatch::Value(_) => StorageSlotType::Value,
49            StorageSlotPatch::Map(_) => StorageSlotType::Map,
50        }
51    }
52
53    /// Returns the [`StoragePatchOperation`] that this slot patch represents.
54    pub fn patch_op(&self) -> StoragePatchOperation {
55        match self {
56            StorageSlotPatch::Value(value_patch) => value_patch.patch_op(),
57            StorageSlotPatch::Map(map_patch) => map_patch.patch_op(),
58        }
59    }
60
61    /// Returns `true` if the slot patch is of type [`StorageSlotPatch::Value`], `false` otherwise.
62    pub fn is_value(&self) -> bool {
63        matches!(self, Self::Value(_))
64    }
65
66    /// Returns `true` if the slot patch is of type [`StorageSlotPatch::Map`], `false` otherwise.
67    pub fn is_map(&self) -> bool {
68        matches!(self, Self::Map(_))
69    }
70
71    // HELPERS
72    // ----------------------------------------------------------------------------------------
73
74    /// Merges `other` into `self`, with `other` taking precedence.
75    ///
76    /// Returns whether the merged slot patch should be kept or removed because it cancels out (see
77    /// [`MergeOutcome`]).
78    ///
79    /// # Errors
80    ///
81    /// Returns an error if merging failed due to a slot type mismatch.
82    pub(super) fn merge(
83        &mut self,
84        slot_name: &StorageSlotName,
85        other: Self,
86    ) -> Result<MergeOutcome, AccountPatchError> {
87        match (self, other) {
88            (StorageSlotPatch::Value(current), StorageSlotPatch::Value(new)) => {
89                current.merge(slot_name, new)
90            },
91            (StorageSlotPatch::Map(current), StorageSlotPatch::Map(new)) => {
92                current.merge(slot_name, new)
93            },
94            (..) => Err(AccountPatchError::StorageSlotUsedAsDifferentTypes(slot_name.clone())),
95        }
96    }
97}
98
99impl From<StorageSlotContent> for StorageSlotPatch {
100    /// Converts a slot's content into a [`StorageSlotPatch`] that creates the slot. Used when
101    /// building a full state patch from an existing account.
102    fn from(content: StorageSlotContent) -> Self {
103        match content {
104            StorageSlotContent::Value(value) => {
105                StorageSlotPatch::Value(StorageValuePatch::Create { value })
106            },
107            StorageSlotContent::Map(storage_map) => {
108                StorageSlotPatch::Map(StorageMapPatch::Create {
109                    entries: StorageMapPatchEntries::from(storage_map),
110                })
111            },
112        }
113    }
114}
115
116impl Serializable for StorageSlotPatch {
117    fn write_into<W: ByteWriter>(&self, target: &mut W) {
118        match self {
119            StorageSlotPatch::Value(value_patch) => {
120                target.write_u8(Self::VALUE);
121                target.write(value_patch);
122            },
123            StorageSlotPatch::Map(map_patch) => {
124                target.write_u8(Self::MAP);
125                target.write(map_patch);
126            },
127        }
128    }
129
130    fn get_size_hint(&self) -> usize {
131        let tag_size = 0u8.get_size_hint();
132        match self {
133            StorageSlotPatch::Value(value_patch) => tag_size + value_patch.get_size_hint(),
134            StorageSlotPatch::Map(map_patch) => tag_size + map_patch.get_size_hint(),
135        }
136    }
137}
138
139impl Deserializable for StorageSlotPatch {
140    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
141        match source.read_u8()? {
142            Self::VALUE => Ok(Self::Value(source.read()?)),
143            Self::MAP => Ok(Self::Map(source.read()?)),
144            other => Err(DeserializationError::InvalidValue(format!(
145                "unknown storage slot patch variant {other}"
146            ))),
147        }
148    }
149}
150
151// MERGE OUTCOME
152// ================================================================================================
153
154/// The outcome of merging one slot patch into another.
155///
156/// Merging can cause a slot patch to cancel out, in which case the parent
157/// [`AccountStoragePatch`](crate::account::AccountStoragePatch) drops the slot patch rather than
158/// commit to a no-op. This happens when a `Create` is followed by a `Remove`: the slot is taken
159/// from absent to present and back to absent, so the base state is left unchanged.
160pub(super) enum MergeOutcome {
161    /// The merged slot patch should be kept.
162    Keep,
163    /// The merged slot patch cancels out and should be removed.
164    Remove,
165}