radix_common/state/
state_updates.rs

1use crate::internal_prelude::*;
2
3/// A tree-like description of all updates that happened to a stored state, to be included as a part
4/// of a transaction receipt.
5/// This structure is indexed (i.e. uses [`IndexMap`]s where [`Vec`]s could be used) for convenience
6/// and performance, since both the source (i.e. Track) and the sink (i.e. Database and API) operate
7/// on indexed structures too.
8/// This structure maintains partial information on the order of operations (please see individual
9/// fields for details), since the end users care about it. Please note that this means multiple
10/// instances of [`StateUpdates`] can represent the same transform of state store (i.e. differing
11/// only by order of some operations), and hence it is not 100% "canonical form".
12#[derive(Debug, Clone, PartialEq, Eq, Sbor, Default)]
13pub struct StateUpdates {
14    /// Indexed Node-level updates, captured in the order of first update operation to a Node.
15    pub by_node: IndexMap<NodeId, NodeStateUpdates>,
16}
17
18impl StateUpdates {
19    pub fn empty() -> Self {
20        Self {
21            by_node: Default::default(),
22        }
23    }
24
25    /// Starts a Node-level update.
26    pub fn of_node(&mut self, node_id: impl Into<NodeId>) -> &mut NodeStateUpdates {
27        self.by_node
28            .entry(node_id.into())
29            .or_insert_with(|| NodeStateUpdates::Delta {
30                by_partition: index_map_new(),
31            })
32    }
33
34    pub fn set_node_updates(
35        mut self,
36        node_id: impl Into<NodeId>,
37        node_updates: NodeStateUpdates,
38    ) -> Self {
39        self.by_node.insert(node_id.into(), node_updates);
40        self
41    }
42
43    pub fn set_substate<'a>(
44        mut self,
45        node_id: impl Into<NodeId>,
46        partition_num: PartitionNumber,
47        substate_key: impl ResolvableSubstateKey<'a>,
48        new_value: impl ScryptoEncode,
49    ) -> Self {
50        let new_value = scrypto_encode(&new_value).expect("New substate value should be encodable");
51        self.of_node(node_id.into())
52            .of_partition(partition_num)
53            .mut_update_substates([(
54                substate_key.into_substate_key(),
55                DatabaseUpdate::Set(new_value),
56            )]);
57        self
58    }
59
60    pub fn mut_add_node_updates(
61        &mut self,
62        node_id: impl Into<NodeId>,
63        node_updates: impl Into<NodeStateUpdates>,
64    ) {
65        let Some(node_updates) = node_updates.into().rebuild_without_empty_entries() else {
66            return;
67        };
68        self.of_node(node_id).mut_add_updates(node_updates);
69    }
70
71    pub fn rebuild_without_empty_entries(self) -> Self {
72        Self {
73            by_node: self
74                .by_node
75                .into_iter()
76                .filter_map(|(node_id, by_partition)| {
77                    let by_partition = by_partition.rebuild_without_empty_entries()?;
78                    Some((node_id, by_partition))
79                })
80                .collect(),
81        }
82    }
83
84    /// Roughly equivalent to the LegacyStateUpdates (when they existed), ignoring partition resets.
85    /// Should only be used for tests, not e.g. for committing to a database.
86    pub fn into_flattened_substate_updates(
87        self,
88    ) -> IndexMap<(NodeId, PartitionNumber, SubstateKey), DatabaseUpdate> {
89        let mut substate_updates = index_map_new();
90        for (node_id, node_state_updates) in self.by_node {
91            match node_state_updates {
92                NodeStateUpdates::Delta { by_partition } => {
93                    for (partition_num, partition_state_updates) in by_partition {
94                        match partition_state_updates {
95                            PartitionStateUpdates::Delta { by_substate } => {
96                                for (key, value) in by_substate {
97                                    substate_updates.insert((node_id, partition_num, key), value);
98                                }
99                            }
100                            PartitionStateUpdates::Batch(batch) => match batch {
101                                BatchPartitionStateUpdate::Reset {
102                                    new_substate_values,
103                                } => {
104                                    for (key, value) in new_substate_values {
105                                        substate_updates.insert(
106                                            (node_id, partition_num, key),
107                                            DatabaseUpdate::Set(value),
108                                        );
109                                    }
110                                }
111                            },
112                        }
113                    }
114                }
115            }
116        }
117        substate_updates
118    }
119}
120
121/// A description of all updates that happened to a state of a single Node.
122/// Note: currently, we do not support any Node-wide changes (e.g. deleting entire Node); however,
123/// we use an enum for potential future development.
124#[derive(Debug, Clone, PartialEq, Eq, Sbor)]
125pub enum NodeStateUpdates {
126    /// A "delta" update to a Node, touching only selected Partitions.
127    /// Contains indexed Partition-level updates, captured in the order of first update operation to
128    /// a Partition.
129    Delta {
130        by_partition: IndexMap<PartitionNumber, PartitionStateUpdates>,
131    },
132}
133
134impl Default for NodeStateUpdates {
135    fn default() -> Self {
136        NodeStateUpdates::Delta {
137            by_partition: index_map_new(),
138        }
139    }
140}
141
142impl NodeStateUpdates {
143    pub fn empty() -> Self {
144        Self::Delta {
145            by_partition: Default::default(),
146        }
147    }
148
149    pub fn set_substate<'a>(
150        mut self,
151        partition_num: PartitionNumber,
152        key: impl ResolvableSubstateKey<'a>,
153        value: impl ScryptoEncode,
154    ) -> Self {
155        self.mut_set_substate(partition_num, key, value);
156        self
157    }
158
159    pub fn mut_set_substate<'a>(
160        &mut self,
161        partition_num: PartitionNumber,
162        key: impl ResolvableSubstateKey<'a>,
163        value: impl ScryptoEncode,
164    ) {
165        let Self::Delta {
166            ref mut by_partition,
167        } = self;
168        by_partition
169            .entry(partition_num)
170            .or_default()
171            .mut_set_substate(key.into_substate_key(), value);
172    }
173
174    pub fn mut_add_updates(&mut self, other: impl Into<Self>) {
175        let Self::Delta {
176            by_partition: other_by_partition,
177        } = other.into();
178        for (partition_number, partition_state_updates) in other_by_partition {
179            // Avoid creating empty updates
180            if partition_state_updates.is_no_op() {
181                continue;
182            }
183            self.of_partition(partition_number)
184                .mut_add_updates(partition_state_updates);
185        }
186    }
187
188    /// Starts a Partition-level update.
189    pub fn of_partition(&mut self, partition_num: PartitionNumber) -> &mut PartitionStateUpdates {
190        match self {
191            NodeStateUpdates::Delta { by_partition } => {
192                by_partition.entry(partition_num).or_default()
193            }
194        }
195    }
196
197    pub fn of_partition_ref(
198        &self,
199        partition_num: PartitionNumber,
200    ) -> Option<&PartitionStateUpdates> {
201        match self {
202            NodeStateUpdates::Delta { by_partition } => by_partition.get(&partition_num),
203        }
204    }
205
206    pub fn rebuild_without_empty_entries(self) -> Option<Self> {
207        match self {
208            NodeStateUpdates::Delta { by_partition } => {
209                let replaced = by_partition
210                    .into_iter()
211                    .filter_map(|(partition_num, partition_state_updates)| {
212                        let new_substate =
213                            partition_state_updates.rebuild_without_empty_entries()?;
214                        Some((partition_num, new_substate))
215                    })
216                    .collect::<IndexMap<_, _>>();
217                if !replaced.is_empty() {
218                    Some(NodeStateUpdates::Delta {
219                        by_partition: replaced,
220                    })
221                } else {
222                    None
223                }
224            }
225        }
226    }
227}
228
229/// A description of all updates that happened to a state of a single Partition.
230#[derive(Debug, Clone, PartialEq, Eq, Sbor)]
231pub enum PartitionStateUpdates {
232    /// A "delta" update to a Partition, touching only selected Substates.
233    /// Contains indexed Substate-level updates, captured in the order of first update operation to
234    /// a Substate.
235    Delta {
236        by_substate: IndexMap<SubstateKey, DatabaseUpdate>,
237    },
238    /// A batch update.
239    Batch(BatchPartitionStateUpdate),
240}
241
242impl Default for PartitionStateUpdates {
243    fn default() -> Self {
244        PartitionStateUpdates::Delta {
245            by_substate: index_map_new(),
246        }
247    }
248}
249
250impl PartitionStateUpdates {
251    pub fn is_no_op(&self) -> bool {
252        match self {
253            PartitionStateUpdates::Delta { by_substate } => by_substate.is_empty(),
254            PartitionStateUpdates::Batch(BatchPartitionStateUpdate::Reset { .. }) => false,
255        }
256    }
257
258    pub fn set_substate<'a>(
259        mut self,
260        key: impl ResolvableSubstateKey<'a>,
261        value: impl ScryptoEncode,
262    ) -> Self {
263        self.mut_set_substate(key, value);
264        self
265    }
266
267    pub fn mut_add_updates(&mut self, other: impl Into<Self>) {
268        match other.into() {
269            PartitionStateUpdates::Delta { by_substate } => {
270                self.mut_update_substates(by_substate);
271            }
272            // If we reset, we replace the current state with the reset state
273            other @ PartitionStateUpdates::Batch(BatchPartitionStateUpdate::Reset { .. }) => {
274                *self = other;
275            }
276        }
277    }
278
279    pub fn mut_set_substate<'a>(
280        &mut self,
281        key: impl ResolvableSubstateKey<'a>,
282        value: impl ScryptoEncode,
283    ) {
284        let value = scrypto_encode(&value).expect("New substate value should be encodable");
285        match self {
286            PartitionStateUpdates::Delta { by_substate } => {
287                by_substate.insert(key.into_substate_key(), DatabaseUpdate::Set(value));
288            }
289            PartitionStateUpdates::Batch(BatchPartitionStateUpdate::Reset {
290                new_substate_values,
291            }) => {
292                new_substate_values.insert(key.into_substate_key(), value);
293            }
294        }
295    }
296
297    /// Resets the partition to an empty state.
298    pub fn delete(&mut self) {
299        *self = PartitionStateUpdates::Batch(BatchPartitionStateUpdate::Reset {
300            new_substate_values: index_map_new(),
301        });
302    }
303
304    pub fn contains_set_update_for(&self, key: &SubstateKey) -> bool {
305        match self {
306            PartitionStateUpdates::Delta { by_substate } => {
307                matches!(by_substate.get(key), Some(DatabaseUpdate::Set(_)))
308            }
309            PartitionStateUpdates::Batch(BatchPartitionStateUpdate::Reset {
310                new_substate_values,
311            }) => new_substate_values.contains_key(key),
312        }
313    }
314
315    pub fn mut_update_substate<'a>(
316        &mut self,
317        key: impl ResolvableSubstateKey<'a>,
318        database_update: DatabaseUpdate,
319    ) {
320        let substate_key = key.into_substate_key();
321        match self {
322            PartitionStateUpdates::Delta { by_substate } => {
323                by_substate.insert(substate_key, database_update);
324            }
325            PartitionStateUpdates::Batch(batch_updates) => {
326                batch_updates.mut_update_substate(substate_key, database_update);
327            }
328        }
329    }
330
331    pub fn update_substate<'a>(
332        mut self,
333        key: impl ResolvableSubstateKey<'a>,
334        database_update: DatabaseUpdate,
335    ) -> Self {
336        self.mut_update_substate(key, database_update);
337        self
338    }
339
340    /// Applies the given updates on top of the current updates to the partition.
341    pub fn mut_update_substates(
342        &mut self,
343        updates: impl IntoIterator<Item = (SubstateKey, DatabaseUpdate)>,
344    ) {
345        match self {
346            PartitionStateUpdates::Delta { by_substate } => {
347                by_substate.extend(updates);
348            }
349            PartitionStateUpdates::Batch(batch_updates) => {
350                batch_updates.mut_update_substates(updates);
351            }
352        }
353    }
354
355    pub fn update_substates(
356        mut self,
357        updates: impl IntoIterator<Item = (SubstateKey, DatabaseUpdate)>,
358    ) -> Self {
359        self.mut_update_substates(updates);
360        self
361    }
362
363    pub fn rebuild_without_empty_entries(self) -> Option<Self> {
364        match self {
365            PartitionStateUpdates::Delta { ref by_substate } => {
366                if !by_substate.is_empty() {
367                    Some(self)
368                } else {
369                    None
370                }
371            }
372            PartitionStateUpdates::Batch(_) => {
373                // We musn't filter out batch updates like resets, even if they set nothing new
374                Some(self)
375            }
376        }
377    }
378
379    pub fn iter_map_entries(
380        &self,
381    ) -> Box<dyn Iterator<Item = (&MapKey, DatabaseUpdateRef<'_>)> + '_> {
382        match self {
383            PartitionStateUpdates::Delta { by_substate } => {
384                Box::new(by_substate.iter().filter_map(|(key, value)| match key {
385                    SubstateKey::Map(map_key) => {
386                        let value = match value {
387                            DatabaseUpdate::Set(value) => DatabaseUpdateRef::Set(value),
388                            DatabaseUpdate::Delete => DatabaseUpdateRef::Delete,
389                        };
390                        Some((map_key, value))
391                    }
392                    SubstateKey::Field(_) | SubstateKey::Sorted(_) => None,
393                }))
394            }
395            PartitionStateUpdates::Batch(BatchPartitionStateUpdate::Reset {
396                new_substate_values,
397            }) => Box::new(
398                new_substate_values
399                    .iter()
400                    .filter_map(|(key, value)| match key {
401                        SubstateKey::Map(map_key) => Some((map_key, DatabaseUpdateRef::Set(value))),
402                        SubstateKey::Field(_) | SubstateKey::Sorted(_) => None,
403                    }),
404            ),
405        }
406    }
407}
408
409/// A description of a batch update affecting an entire Partition.
410#[derive(Debug, Clone, PartialEq, Eq, Sbor)]
411pub enum BatchPartitionStateUpdate {
412    /// A reset, dropping all Substates of a partition and replacing them with a new set.
413    /// Contains indexed new Substate values, captured in the order of creation of a Substate.
414    Reset {
415        new_substate_values: IndexMap<SubstateKey, DbSubstateValue>,
416    },
417}
418
419impl BatchPartitionStateUpdate {
420    pub fn mut_update_substates(
421        &mut self,
422        updates: impl IntoIterator<Item = (SubstateKey, DatabaseUpdate)>,
423    ) {
424        for (substate_key, database_update) in updates {
425            self.mut_update_substate(substate_key, database_update);
426        }
427    }
428
429    pub fn mut_update_substate(
430        &mut self,
431        substate_key: SubstateKey,
432        database_update: DatabaseUpdate,
433    ) {
434        let BatchPartitionStateUpdate::Reset {
435            new_substate_values,
436        } = self;
437        match database_update {
438            DatabaseUpdate::Set(new_value) => {
439                new_substate_values.insert(substate_key, new_value);
440            }
441            DatabaseUpdate::Delete => {
442                new_substate_values.swap_remove(&substate_key);
443            }
444        }
445    }
446}
447
448/// An update of a single substate's value.
449#[derive(Debug, Clone, Hash, PartialEq, Eq, Sbor, PartialOrd, Ord)]
450pub enum DatabaseUpdate {
451    Set(DbSubstateValue),
452    Delete,
453}
454
455impl DatabaseUpdate {
456    pub fn as_ref(&self) -> DatabaseUpdateRef<'_> {
457        match self {
458            DatabaseUpdate::Set(update) => DatabaseUpdateRef::Set(update),
459            DatabaseUpdate::Delete => DatabaseUpdateRef::Delete,
460        }
461    }
462}
463
464/// A 1:1 counterpart of [`DatabaseUpdate`], but operating on references.
465pub enum DatabaseUpdateRef<'v> {
466    Set(&'v [u8]),
467    Delete,
468}
469
470/// A raw substate value stored by the database.
471pub type DbSubstateValue = Vec<u8>;