radix_engine/track/
interface.rs

1use crate::errors::RuntimeError;
2use crate::internal_prelude::*;
3use radix_engine_interface::types::*;
4use radix_substate_store_interface::db_key_mapper::SubstateKeyContent;
5
6#[derive(Debug, Clone, PartialEq, Eq)]
7pub enum CallbackError<E, C> {
8    Error(E),
9    CallbackError(C),
10}
11
12impl<E> CallbackError<E, RuntimeError> {
13    pub fn to_runtime_error<F: FnOnce(E) -> RuntimeError>(self, f: F) -> RuntimeError {
14        match self {
15            CallbackError::Error(e) => f(e),
16            CallbackError::CallbackError(c) => c,
17        }
18    }
19}
20
21impl<E, C> CallbackError<E, C> {
22    pub fn map<N, F: FnOnce(E) -> N>(self, f: F) -> CallbackError<N, C> {
23        match self {
24            CallbackError::Error(e) => CallbackError::Error(f(e)),
25            CallbackError::CallbackError(c) => CallbackError::CallbackError(c),
26        }
27    }
28}
29
30pub type NodeSubstates = BTreeMap<PartitionNumber, BTreeMap<SubstateKey, IndexedScryptoValue>>;
31
32pub(crate) trait NodeSubstatesExt {
33    fn into_node_state_updates(self) -> NodeStateUpdates;
34}
35
36impl NodeSubstatesExt for NodeSubstates {
37    fn into_node_state_updates(self) -> NodeStateUpdates {
38        let mut updates = NodeStateUpdates::empty();
39        for (partition_num, substates) in self {
40            for (substate_key, indexed_scrypto_value) in substates {
41                updates.of_partition(partition_num).mut_update_substate(
42                    substate_key,
43                    DatabaseUpdate::Set(indexed_scrypto_value.unpack().0),
44                );
45            }
46        }
47        updates
48    }
49}
50
51pub enum TrackedSubstateInfo {
52    New,
53    Updated,
54    Unmodified,
55}
56
57/// Represents the interface between Radix Engine and Track.
58///
59/// In practice, we will likely end up with only one implementation.
60///
61/// The trait here is for formalizing the interface and intended user flow.
62pub trait CommitableSubstateStore {
63    /// Marks a substate as transient, or a substate which was never and will never be persisted
64    fn mark_as_transient(
65        &mut self,
66        node_id: NodeId,
67        partition_num: PartitionNumber,
68        substate_key: SubstateKey,
69    );
70
71    /// Inserts a node into the substate store.
72    ///
73    /// Clients must ensure the `node_id` is new and unique; otherwise, the behavior is undefined.
74    ///
75    /// # Panics
76    /// - If the partition is invalid
77    fn create_node<E, F: FnMut(IOAccess) -> Result<(), E>>(
78        &mut self,
79        node_id: NodeId,
80        node_substates: NodeSubstates,
81        on_io_access: &mut F,
82    ) -> Result<(), E>;
83
84    fn get_tracked_substate_info(
85        &mut self,
86        node_id: &NodeId,
87        partition_num: PartitionNumber,
88        substate_key: &SubstateKey,
89    ) -> TrackedSubstateInfo;
90
91    fn read_substate(
92        &mut self,
93        node_id: &NodeId,
94        partition_num: PartitionNumber,
95        substate_key: &SubstateKey,
96    ) -> Option<&IndexedScryptoValue> {
97        self.get_substate(node_id, partition_num, substate_key, &mut |_| -> Result<
98            (),
99            (),
100        > {
101            Ok(())
102        })
103        .unwrap()
104    }
105
106    fn get_substate<E, F: FnMut(IOAccess) -> Result<(), E>>(
107        &mut self,
108        node_id: &NodeId,
109        partition_num: PartitionNumber,
110        substate_key: &SubstateKey,
111        on_io_access: &mut F,
112    ) -> Result<Option<&IndexedScryptoValue>, E>;
113
114    /// Inserts a substate into the substate store.
115    ///
116    /// Clients must ensure the `node_id`/`partition_num` is a node which has been created; otherwise, the behavior
117    /// is undefined.
118    fn set_substate<E, F: FnMut(IOAccess) -> Result<(), E>>(
119        &mut self,
120        node_id: NodeId,
121        partition_num: PartitionNumber,
122        substate_key: SubstateKey,
123        substate_value: IndexedScryptoValue,
124        on_io_access: &mut F,
125    ) -> Result<(), E>;
126
127    fn force_write(
128        &mut self,
129        node_id: &NodeId,
130        partition_num: &PartitionNumber,
131        substate_key: &SubstateKey,
132    );
133
134    /// Deletes a substate from the substate store.
135    ///
136    /// Clients must ensure the `node_id`/`partition_num` is a node which has been created;
137    /// Clients must ensure this isn't called on a virtualized partition;
138    /// Otherwise, the behavior is undefined.
139    ///
140    /// Returns tuple of substate and boolean which is true for the first database access.
141    fn remove_substate<E, F: FnMut(IOAccess) -> Result<(), E>>(
142        &mut self,
143        node_id: &NodeId,
144        partition_num: PartitionNumber,
145        substate_key: &SubstateKey,
146        on_io_access: &mut F,
147    ) -> Result<Option<IndexedScryptoValue>, E>;
148
149    /// Returns Substate Keys of maximum count for a given partition.
150    ///
151    /// Clients must ensure that the SubstateKeyContent which the partition is
152    /// associated with is passed in. The returned SubstateKeys are guaranteed to be of
153    /// this type.
154    /// Otherwise, behavior is undefined.
155    ///
156    /// Returns list of substate keys and database access info
157    fn scan_keys<K: SubstateKeyContent, E, F: FnMut(IOAccess) -> Result<(), E>>(
158        &mut self,
159        node_id: &NodeId,
160        partition_num: PartitionNumber,
161        count: u32,
162        on_io_access: &mut F,
163    ) -> Result<Vec<SubstateKey>, E>;
164
165    /// Removes substates of maximum count for a given partition.
166    ///
167    /// Clients must ensure that the SubstateKeyContent which the partition is
168    /// associated with is passed in. The returned SubstateKeys are guaranteed to be of
169    /// this type.
170    /// Otherwise, behavior is undefined.
171    ///
172    /// Returns list of removed substates with their associated keys and values, as well as database access info
173    fn drain_substates<K: SubstateKeyContent, E, F: FnMut(IOAccess) -> Result<(), E>>(
174        &mut self,
175        node_id: &NodeId,
176        partition_num: PartitionNumber,
177        count: u32,
178        on_io_access: &mut F,
179    ) -> Result<Vec<(SubstateKey, IndexedScryptoValue)>, E>;
180
181    /// Returns tuple of substate vector and boolean which is true for the first database access.
182    fn scan_sorted_substates<E, F: FnMut(IOAccess) -> Result<(), E>>(
183        &mut self,
184        node_id: &NodeId,
185        partition_num: PartitionNumber,
186        count: u32,
187        on_io_access: &mut F,
188    ) -> Result<Vec<(SortedKey, IndexedScryptoValue)>, E>;
189
190    /// Note: unstable interface, for intent transaction tracker only
191    fn delete_partition(&mut self, node_id: &NodeId, partition_num: PartitionNumber);
192
193    /// Return the commit info
194    fn get_commit_info(&mut self) -> StoreCommitInfo;
195}
196
197#[derive(Debug, Clone, Copy)]
198pub struct CanonicalPartition {
199    pub node_id: NodeId,
200    pub partition_number: PartitionNumber,
201}
202
203#[derive(Debug, Clone, ScryptoSbor, PartialEq, Eq)]
204pub struct CanonicalSubstateKey {
205    pub node_id: NodeId,
206    pub partition_number: PartitionNumber,
207    pub substate_key: SubstateKey, // TODO: use reference if this turns out to be costly
208}
209
210impl CanonicalSubstateKey {
211    pub fn of(partition: CanonicalPartition, substate_key: SubstateKey) -> Self {
212        Self {
213            node_id: partition.node_id,
214            partition_number: partition.partition_number,
215            substate_key,
216        }
217    }
218}
219
220impl CanonicalSubstateKey {
221    #[allow(clippy::len_without_is_empty)]
222    pub fn len(&self) -> usize {
223        self.node_id.as_bytes().len()
224            + 1
225            + match &self.substate_key {
226                SubstateKey::Field(_) => 1,
227                SubstateKey::Map(k) => k.len(),
228                SubstateKey::Sorted(k) => 2 + k.1.len(),
229            }
230    }
231}
232
233#[derive(Debug, Clone, ScryptoSbor, PartialEq, Eq)]
234pub enum IOAccess {
235    /// Some substate was read from database.
236    ReadFromDb(CanonicalSubstateKey, usize),
237    /// Non-existent substate was read from database.
238    ReadFromDbNotFound(CanonicalSubstateKey),
239
240    /// A substate in track has been updated
241    TrackSubstateUpdated {
242        /// The canonical substate key
243        canonical_substate_key: CanonicalSubstateKey,
244        /// Previous size of the substate, or `None` if it's a new entry.
245        /// The current size before the update rather than the size in the underlying store.
246        old_size: Option<usize>,
247        /// The new substate size, or `None` if it's removed
248        new_size: Option<usize>,
249    },
250
251    /// A substate in track has been updated
252    HeapSubstateUpdated {
253        /// The canonical substate key
254        canonical_substate_key: CanonicalSubstateKey,
255        /// Previous size of the substate, or `None` if it's a new entry.
256        /// The current size before the update rather than the size in the underlying store.
257        old_size: Option<usize>,
258        /// The new substate size, or `None` if it's removed
259        new_size: Option<usize>,
260    },
261}
262
263impl IOAccess {
264    pub fn node_id(&self) -> NodeId {
265        match self {
266            IOAccess::ReadFromDb(key, _)
267            | IOAccess::ReadFromDbNotFound(key)
268            | IOAccess::TrackSubstateUpdated {
269                canonical_substate_key: key,
270                ..
271            }
272            | IOAccess::HeapSubstateUpdated {
273                canonical_substate_key: key,
274                ..
275            } => key.node_id,
276        }
277    }
278}
279
280pub type StoreCommitInfo = Vec<StoreCommit>;
281
282#[derive(Debug, Clone)]
283pub enum StoreCommit {
284    Insert {
285        canonical_substate_key: CanonicalSubstateKey,
286        size: usize,
287    },
288    Update {
289        canonical_substate_key: CanonicalSubstateKey,
290        size: usize,
291        old_size: usize,
292    },
293    Delete {
294        canonical_substate_key: CanonicalSubstateKey,
295        old_size: usize,
296    },
297}
298
299impl StoreCommit {
300    pub fn node_id(&self) -> NodeId {
301        match self {
302            StoreCommit::Insert {
303                canonical_substate_key,
304                ..
305            }
306            | StoreCommit::Update {
307                canonical_substate_key,
308                ..
309            }
310            | StoreCommit::Delete {
311                canonical_substate_key,
312                ..
313            } => canonical_substate_key.node_id,
314        }
315    }
316
317    pub fn len_increase(&self) -> usize {
318        match self {
319            StoreCommit::Insert {
320                canonical_substate_key,
321                size,
322                ..
323            } => canonical_substate_key.len() + *size,
324            StoreCommit::Update { size, old_size, .. } => (*size).saturating_sub(*old_size),
325            StoreCommit::Delete { .. } => 0, // TODO: refund?
326        }
327    }
328}