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