radix_substate_store_interface/interface.rs
1use crate::db_key_mapper::*;
2use radix_common::prelude::*;
3
4pub type DbNodeKey = Vec<u8>;
5
6pub type DbPartitionNum = u8;
7
8/// A database-level key of an entire partition.
9/// Seen from the higher-level API: it represents a pair (RE Node ID, Module ID).
10/// Seen from the lower-level implementation: it is used as a key in the upper-layer tree of our
11/// two-layered JMT.
12#[derive(Debug, Clone, Hash, PartialEq, Eq, Ord, PartialOrd, Sbor)]
13pub struct DbPartitionKey {
14 pub node_key: DbNodeKey,
15 pub partition_num: DbPartitionNum,
16}
17
18/// A database-level key of a substate within a known partition.
19/// Seen from the higher-level API: it represents a local Substate Key.
20/// Seen from the lower-level implementation: it is used as a key in the Substate-Tier JMT.
21#[derive(Debug, Clone, Hash, PartialEq, Eq, Ord, PartialOrd, Sbor)]
22pub struct DbSortKey(pub Vec<u8>);
23
24/// A fully-specified key of a substate (i.e. specifying its partition and sort key).
25pub type DbSubstateKey = (DbPartitionKey, DbSortKey);
26
27/// A key-value entry of a substate within a known partition.
28pub type PartitionEntry = (DbSortKey, DbSubstateValue);
29
30pub trait CreateDatabaseUpdates {
31 type DatabaseUpdates;
32
33 /// Uses the default [`DatabaseKeyMapper`], [`SpreadPrefixKeyMapper`], to express self using database-level key encoding.
34 fn create_database_updates(&self) -> Self::DatabaseUpdates {
35 self.create_database_updates_with_mapper::<SpreadPrefixKeyMapper>()
36 }
37
38 /// Uses the given [`DatabaseKeyMapper`] to express self using database-level key encoding.
39 fn create_database_updates_with_mapper<M: DatabaseKeyMapper>(&self) -> Self::DatabaseUpdates;
40}
41
42/// A canonical description of all database updates to be applied.
43/// Note: this struct can be migrated to an enum if we ever have a need for database-wide batch
44/// changes (see [`PartitionDatabaseUpdates`] enum).
45#[derive(Debug, Clone, PartialEq, Eq, Sbor, Default)]
46pub struct DatabaseUpdates {
47 /// Node-level updates.
48 pub node_updates: IndexMap<DbNodeKey, NodeDatabaseUpdates>,
49}
50
51impl DatabaseUpdates {
52 pub fn node_ids(&self) -> impl Iterator<Item = NodeId> + '_ {
53 self.node_updates
54 .keys()
55 .map(|key| SpreadPrefixKeyMapper::from_db_node_key(key))
56 }
57}
58
59impl CreateDatabaseUpdates for StateUpdates {
60 type DatabaseUpdates = DatabaseUpdates;
61
62 fn create_database_updates_with_mapper<M: DatabaseKeyMapper>(&self) -> DatabaseUpdates {
63 DatabaseUpdates {
64 node_updates: self
65 .by_node
66 .iter()
67 .map(|(node_id, node_state_updates)| {
68 (
69 M::to_db_node_key(node_id),
70 node_state_updates.create_database_updates_with_mapper::<M>(),
71 )
72 })
73 .collect(),
74 }
75 }
76}
77
78/// A canonical description of specific Node's updates to be applied.
79/// Note: this struct can be migrated to an enum if we ever have a need for Node-wide batch changes
80/// (see [`PartitionDatabaseUpdates`] enum).
81#[derive(Debug, Clone, PartialEq, Eq, Sbor, Default)]
82pub struct NodeDatabaseUpdates {
83 /// Partition-level updates.
84 pub partition_updates: IndexMap<DbPartitionNum, PartitionDatabaseUpdates>,
85}
86
87impl CreateDatabaseUpdates for NodeStateUpdates {
88 type DatabaseUpdates = NodeDatabaseUpdates;
89
90 fn create_database_updates_with_mapper<M: DatabaseKeyMapper>(&self) -> NodeDatabaseUpdates {
91 match self {
92 NodeStateUpdates::Delta { by_partition } => NodeDatabaseUpdates {
93 partition_updates: by_partition
94 .iter()
95 .map(|(partition_num, partition_state_updates)| {
96 (
97 M::to_db_partition_num(*partition_num),
98 partition_state_updates.create_database_updates_with_mapper::<M>(),
99 )
100 })
101 .collect(),
102 },
103 }
104 }
105}
106
107/// A canonical description of specific Partition's updates to be applied.
108#[derive(Debug, Clone, PartialEq, Eq, Sbor)]
109pub enum PartitionDatabaseUpdates {
110 /// A delta change, touching just selected substates.
111 Delta {
112 substate_updates: IndexMap<DbSortKey, DatabaseUpdate>,
113 },
114
115 /// A reset, dropping all Substates of a partition and replacing them with a new set.
116 Reset {
117 new_substate_values: IndexMap<DbSortKey, DbSubstateValue>,
118 },
119}
120
121impl PartitionDatabaseUpdates {
122 /// Returns an effective change applied to the given Substate by this Partition update.
123 /// May return [`None`] only if the Substate was unaffected.
124 ///
125 /// This method is useful for index-updating logic which does not care about the nature of the
126 /// Partition update (i.e. delta vs reset).
127 pub fn get_substate_change(&self, sort_key: &DbSortKey) -> Option<DatabaseUpdateRef> {
128 match self {
129 Self::Delta { substate_updates } => {
130 substate_updates.get(sort_key).map(|update| match update {
131 DatabaseUpdate::Set(value) => DatabaseUpdateRef::Set(value),
132 DatabaseUpdate::Delete => DatabaseUpdateRef::Delete,
133 })
134 }
135 Self::Reset {
136 new_substate_values,
137 } => new_substate_values
138 .get(sort_key)
139 .map(|value| DatabaseUpdateRef::Set(value))
140 .or_else(|| Some(DatabaseUpdateRef::Delete)),
141 }
142 }
143}
144
145impl CreateDatabaseUpdates for PartitionStateUpdates {
146 type DatabaseUpdates = PartitionDatabaseUpdates;
147
148 fn create_database_updates_with_mapper<M: DatabaseKeyMapper>(
149 &self,
150 ) -> PartitionDatabaseUpdates {
151 match self {
152 PartitionStateUpdates::Delta { by_substate } => PartitionDatabaseUpdates::Delta {
153 substate_updates: by_substate
154 .iter()
155 .map(|(key, update)| (M::to_db_sort_key(key), update.clone()))
156 .collect(),
157 },
158 PartitionStateUpdates::Batch(batch) => batch.create_database_updates_with_mapper::<M>(),
159 }
160 }
161}
162
163impl CreateDatabaseUpdates for BatchPartitionStateUpdate {
164 type DatabaseUpdates = PartitionDatabaseUpdates;
165
166 fn create_database_updates_with_mapper<M: DatabaseKeyMapper>(
167 &self,
168 ) -> PartitionDatabaseUpdates {
169 match self {
170 BatchPartitionStateUpdate::Reset {
171 new_substate_values,
172 } => PartitionDatabaseUpdates::Reset {
173 new_substate_values: new_substate_values
174 .iter()
175 .map(|(key, value)| (M::to_db_sort_key(key), value.clone()))
176 .collect(),
177 },
178 }
179 }
180}
181
182impl Default for PartitionDatabaseUpdates {
183 fn default() -> Self {
184 Self::Delta {
185 substate_updates: index_map_new(),
186 }
187 }
188}
189
190impl DatabaseUpdates {
191 /// Constructs an instance from the given legacy representation (a map of maps), which is only
192 /// capable of specifying "deltas" (i.e. individual substate changes; no partition deletes).
193 ///
194 /// Note: This method is only meant for tests/demos - with regular Engine usage, the
195 /// [`DatabaseUpdates`] can be obtained directly from the receipt.
196 pub fn from_delta_maps(
197 maps: IndexMap<DbPartitionKey, IndexMap<DbSortKey, DatabaseUpdate>>,
198 ) -> DatabaseUpdates {
199 let mut database_updates = DatabaseUpdates::default();
200 for (
201 DbPartitionKey {
202 node_key,
203 partition_num,
204 },
205 substate_updates,
206 ) in maps
207 {
208 database_updates
209 .node_updates
210 .entry(node_key)
211 .or_default()
212 .partition_updates
213 .insert(
214 partition_num,
215 PartitionDatabaseUpdates::Delta { substate_updates },
216 );
217 }
218 database_updates
219 }
220}
221
222/// A read interface between Track and a database vendor.
223pub trait SubstateDatabase {
224 /// Reads a substate value by its db partition and db sort key, or [`Option::None`] if missing.
225 ///
226 /// ## Alternatives
227 ///
228 /// It's likely easier to use the [`get_substate`][SubstateDatabaseExtensions::get_substate] or
229 /// [`get_raw_substate`][SubstateDatabaseExtensions::get_raw_substate] methods instead, which
230 /// allow providing logical keys.
231 /// These methods should also exist on the database type as long as the
232 /// [`SubstateDatabaseExtensions`] trait is in scope.
233 fn get_raw_substate_by_db_key(
234 &self,
235 partition_key: &DbPartitionKey,
236 sort_key: &DbSortKey,
237 ) -> Option<DbSubstateValue>;
238
239 /// Iterates over all entries of the given partition (starting either from the beginning, or
240 /// from the given [`DbSortKey`]), in a lexicographical order (ascending) of the [`DbSortKey`]s.
241 /// Note: If the exact given starting key does not exist, the iteration starts with its
242 /// immediate successor.
243 ///
244 /// ## Alternatives
245 ///
246 /// There are lots of methods starting `list_` which allow iterating using more intuitive abstractions.
247 /// These methods are present as long as the [`SubstateDatabaseExtensions`] trait is in scope.
248 fn list_raw_values_from_db_key(
249 &self,
250 partition_key: &DbPartitionKey,
251 from_sort_key: Option<&DbSortKey>,
252 ) -> Box<dyn Iterator<Item = PartitionEntry> + '_>;
253}
254
255impl<T: SubstateDatabase + ?Sized> SubstateDatabaseExtensions for T {}
256
257/// These are a separate trait so that [`SubstateDatabase`] stays object-safe,
258/// and can be used as `dyn SubstateDatabase`.
259///
260/// Generic parameters aren't permitted on object-safe traits.
261pub trait SubstateDatabaseExtensions: SubstateDatabase {
262 /// Gets the raw bytes of the substate's value, if it exists.
263 ///
264 /// # Example
265 /// ```ignore
266 /// let is_bootstrapped = db.get_raw_substate(
267 /// PACKAGE_PACKAGE,
268 /// TYPE_INFO_FIELD_PARTITION,
269 /// TypeInfoField::TypeInfo,
270 /// ).is_some();
271 /// ```
272 fn get_raw_substate<'a>(
273 &self,
274 node_id: impl AsRef<NodeId>,
275 partition_number: PartitionNumber,
276 substate_key: impl ResolvableSubstateKey<'a>,
277 ) -> Option<Vec<u8>> {
278 self.get_raw_substate_by_db_key(
279 &db_partition_key(node_id, partition_number),
280 &db_sort_key(substate_key),
281 )
282 }
283
284 /// Gets the substate's value, if it exists, and returns it decoded as `Some(V)`.
285 /// If it doesn't exist, `None` is returned.
286 ///
287 /// # Panics
288 /// This method panics if:
289 /// * There is an error decoding the value into the `V`.
290 ///
291 /// # Example use:
292 /// ```ignore
293 /// let type_info_substate = db.get_substate::<TypeInfoSubstate>(
294 /// PACKAGE_PACKAGE,
295 /// TYPE_INFO_FIELD_PARTITION,
296 /// TypeInfoField::TypeInfo,
297 /// )?;
298 /// ```
299 fn get_substate<'a, V: ScryptoDecode>(
300 &self,
301 node_id: impl AsRef<NodeId>,
302 partition_number: PartitionNumber,
303 substate_key: impl ResolvableSubstateKey<'a>,
304 ) -> Option<V> {
305 let raw = self.get_raw_substate(node_id, partition_number, substate_key)?;
306 Some(decode_value(&raw))
307 }
308
309 /// Gets the value of a subsate which is expected to exist, returns it decoded as `V`.
310 ///
311 /// # Panics
312 /// This method panics if:
313 /// * The substate doesn't exist in the database.
314 /// * There is an error decoding the value into the `V`.
315 ///
316 /// # Example use:
317 /// ```ignore
318 /// let existing_type_info_substate: TypeInfoSubstate = db.get_existing_substate(
319 /// PACKAGE_PACKAGE,
320 /// TYPE_INFO_FIELD_PARTITION,
321 /// TypeInfoField::TypeInfo,
322 /// )?;
323 /// ```
324 fn get_existing_substate<'a, V: ScryptoDecode>(
325 &self,
326 node_id: impl AsRef<NodeId>,
327 partition_number: PartitionNumber,
328 substate_key: impl ResolvableSubstateKey<'a>,
329 ) -> V {
330 let substate_value = self.get_substate(node_id, partition_number, substate_key);
331 substate_value.unwrap_or_else(|| {
332 panic!(
333 "Expected substate of type {} to already exist.",
334 core::any::type_name::<V>(),
335 )
336 })
337 }
338
339 // ------------------------------------------------------------------------------------
340 // LIST RAW
341 // ------------------------------------------------------------------------------------
342
343 /// Returns an iterator of the substates of a partition from an inclusive start cursor.
344 ///
345 /// The iterator returns raw keys and values.
346 ///
347 /// Pass `None::<SubstateKey>` as the cursor to iterate from the start of the partition.
348 #[inline]
349 fn list_raw_values<'a>(
350 &self,
351 node_id: impl AsRef<NodeId>,
352 partition_number: PartitionNumber,
353 from_substate_key_inclusive: impl ResolvableOptionalSubstateKey<'a>,
354 ) -> Box<dyn Iterator<Item = (DbSortKey, Vec<u8>)> + '_> {
355 self.list_raw_values_from_db_key(
356 &db_partition_key(node_id, partition_number),
357 optional_db_sort_key(from_substate_key_inclusive).as_ref(),
358 )
359 }
360
361 // ------------------------------------------------------------------------------------
362 // LIST KINDED PARTITIONS (OF A KNOWN BUT GENERIC KIND)
363 // ------------------------------------------------------------------------------------
364 // NOTE: There is not `list_kinded_entries` because mapping of the key requires knowing
365 // the specific kind of the substate key.
366 // ------------------------------------------------------------------------------------
367
368 /// Returns an iterator of the substates of a partition from an inclusive start cursor.
369 ///
370 /// The iterator returns `K` and the raw value for each substate.
371 /// The caller must specify `K` as [`FieldKey`], [`MapKey`] or [`SortedKey`].
372 ///
373 /// Pass `None::<SubstateKey>` as the cursor to iterate from the start of the partition.
374 fn list_kinded_raw_values<'a, K: SubstateKeyContent>(
375 &self,
376 node_id: impl AsRef<NodeId>,
377 partition_number: PartitionNumber,
378 from_substate_key_inclusive: impl ResolvableOptionalSubstateKey<'a>,
379 ) -> Box<dyn Iterator<Item = (K, Vec<u8>)> + '_> {
380 let iterable = self
381 .list_raw_values_from_db_key(
382 &db_partition_key(node_id, partition_number),
383 optional_db_sort_key(from_substate_key_inclusive).as_ref(),
384 )
385 .map(|(db_sort_key, raw_value)| {
386 (
387 SpreadPrefixKeyMapper::from_db_sort_key_to_inner::<K>(&db_sort_key),
388 raw_value,
389 )
390 });
391 Box::new(iterable)
392 }
393
394 /// Returns an iterator of the substates of a partition from an inclusive start cursor.
395 ///
396 /// The iterator returns `K` and `V` for each substate.
397 /// The caller must specify `K` as [`FieldKey`], [`MapKey`] or [`SortedKey`].
398 /// The value type `V` can be specified or inferred.
399 ///
400 /// Pass `None::<SubstateKey>` as the cursor to iterate from the start of the partition.
401 ///
402 /// # Panics
403 /// This method panics if:
404 /// * There is an error decoding the value bytes into `V`.
405 fn list_kinded_values<'a, K: SubstateKeyContent, V: ScryptoDecode>(
406 &self,
407 node_id: impl AsRef<NodeId>,
408 partition_number: PartitionNumber,
409 from_substate_key_inclusive: impl ResolvableOptionalSubstateKey<'a>,
410 ) -> Box<dyn Iterator<Item = (K, V)> + '_> {
411 let iterator = self
412 .list_raw_values(node_id, partition_number, from_substate_key_inclusive)
413 .map(|(db_sort_key, raw_value)| {
414 (
415 SpreadPrefixKeyMapper::from_db_sort_key_to_inner::<K>(&db_sort_key),
416 decode_value::<V>(&raw_value),
417 )
418 });
419 Box::new(iterator)
420 }
421
422 // ------------------------------------------------------------------------------------
423 // LIST FIELD PARTITIONS
424 // ------------------------------------------------------------------------------------
425
426 /// Returns an iterator of the substates of a field partition from an inclusive start cursor.
427 ///
428 /// The iterator returns the `FieldKey = u8` and the raw value for each substate.
429 ///
430 /// Pass `None::<SubstateKey>` as the cursor to iterate from the start of the partition.
431 fn list_field_raw_values<'a>(
432 &self,
433 node_id: impl AsRef<NodeId>,
434 partition_number: PartitionNumber,
435 from_substate_key_inclusive: impl ResolvableOptionalSubstateKey<'a>,
436 ) -> Box<dyn Iterator<Item = (FieldKey, Vec<u8>)> + '_> {
437 self.list_kinded_raw_values::<FieldKey>(
438 node_id,
439 partition_number,
440 from_substate_key_inclusive,
441 )
442 }
443
444 /// Returns an iterator of the substates of a field partition from an inclusive start cursor.
445 ///
446 /// The iterator returns the `FieldKey = u8` and the decoded value `V` for each substate.
447 /// The value type `V` can be specified or inferred.
448 ///
449 /// Pass `None::<SubstateKey>` as the cursor to iterate from the start of the partition.
450 ///
451 /// # Panics
452 /// This method panics if:
453 /// * There is an error decoding the value bytes into `V`.
454 fn list_field_values<'a, V: ScryptoDecode>(
455 &self,
456 node_id: impl AsRef<NodeId>,
457 partition_number: PartitionNumber,
458 from_substate_key_inclusive: impl ResolvableOptionalSubstateKey<'a>,
459 ) -> Box<dyn Iterator<Item = (FieldKey, V)> + '_> {
460 self.list_kinded_values::<FieldKey, V>(
461 node_id,
462 partition_number,
463 from_substate_key_inclusive,
464 )
465 }
466
467 /// Returns an iterator of the substates of a field partition from an inclusive start cursor.
468 ///
469 /// The iterator returns the decoded key type `K` and the decoded value `V` for each substate.
470 /// The key type `K` and value types `V` can be specified or inferred.
471 ///
472 /// Pass `None::<SubstateKey>` as the cursor to iterate from the start of the partition.
473 ///
474 /// # Panics
475 /// This method panics if:
476 /// * There is an error converting the field key byte into `K`.
477 /// * There is an error decoding the value bytes into `V`.
478 fn list_field_entries<'a, K: TryFrom<FieldKey>, V: ScryptoDecode>(
479 &self,
480 node_id: impl AsRef<NodeId>,
481 partition_number: PartitionNumber,
482 from_substate_key_inclusive: impl ResolvableOptionalSubstateKey<'a>,
483 ) -> Box<dyn Iterator<Item = (K, V)> + '_> {
484 let iterator = self
485 .list_raw_values(node_id, partition_number, from_substate_key_inclusive)
486 .map(|(db_sort_key, raw_value)| {
487 (
488 K::try_from(SpreadPrefixKeyMapper::from_db_sort_key_to_inner::<FieldKey>(&db_sort_key))
489 .unwrap_or_else(|_| panic!("The field key type should be able to be decoded from the substate's key")),
490 decode_value::<V>(&raw_value),
491 )
492 });
493 Box::new(iterator)
494 }
495
496 // ------------------------------------------------------------------------------------
497 // LIST MAP PARTITIONS
498 // ------------------------------------------------------------------------------------
499
500 /// Returns an iterator of the substates of a map partition from an inclusive start cursor.
501 ///
502 /// The iterator returns the `MapKey = Vec<u8>` and the raw value for each substate.
503 ///
504 /// Pass `None::<SubstateKey>` as the cursor to iterate from the start of the partition.
505 fn list_map_raw_values<'a>(
506 &self,
507 node_id: impl AsRef<NodeId>,
508 partition_number: PartitionNumber,
509 from_substate_key_inclusive: impl ResolvableOptionalSubstateKey<'a>,
510 ) -> Box<dyn Iterator<Item = (MapKey, Vec<u8>)> + '_> {
511 self.list_kinded_raw_values::<MapKey>(
512 node_id,
513 partition_number,
514 from_substate_key_inclusive,
515 )
516 }
517
518 /// Returns an iterator of the substates of a map partition from an inclusive start cursor.
519 ///
520 /// The iterator returns the `MapKey = Vec<u8>` and the decoded value `V` for each substate.
521 /// The value type `V` can be specified or inferred.
522 ///
523 /// Pass `None::<SubstateKey>` as the cursor to iterate from the start of the partition.
524 ///
525 /// # Panics
526 /// This method panics if:
527 /// * There is an error decoding the value bytes into `V`.
528 fn list_map_values<'a, V: ScryptoDecode>(
529 &self,
530 node_id: impl AsRef<NodeId>,
531 partition_number: PartitionNumber,
532 from_substate_key_inclusive: impl ResolvableOptionalSubstateKey<'a>,
533 ) -> Box<dyn Iterator<Item = (MapKey, V)> + '_> {
534 self.list_kinded_values::<MapKey, V>(node_id, partition_number, from_substate_key_inclusive)
535 }
536
537 /// Returns an iterator of the substates of a map partition from an inclusive start cursor.
538 ///
539 /// The iterator returns the decoded key type `K` and the decoded value `V` for each substate.
540 /// The key type `K` and value types `V` can be specified or inferred.
541 ///
542 /// Pass `None::<SubstateKey>` as the cursor to iterate from the start of the partition.
543 ///
544 /// # Panics
545 /// This method panics if:
546 /// * There is an error decoding the field bytes into `K`.
547 /// * There is an error decoding the value bytes into `V`.
548 fn list_map_entries<'a, K: ScryptoDecode, V: ScryptoDecode>(
549 &self,
550 node_id: impl AsRef<NodeId>,
551 partition_number: PartitionNumber,
552 from_substate_key_inclusive: impl ResolvableOptionalSubstateKey<'a>,
553 ) -> Box<dyn Iterator<Item = (K, V)> + '_> {
554 let iterator = self
555 .list_map_raw_values(node_id, partition_number, from_substate_key_inclusive)
556 .map(|(raw_key, raw_value)| (decode_key::<K>(&raw_key), decode_value::<V>(&raw_value)));
557 Box::new(iterator)
558 }
559
560 // ------------------------------------------------------------------------------------
561 // LIST SORTED PARTITIONS
562 // ------------------------------------------------------------------------------------
563
564 /// Returns an iterator of the substates of a sorted partition from an inclusive start cursor.
565 ///
566 /// The iterator returns the `SortedKey = ([u8; 2], Vec<u8>)` and the raw value for each substate.
567 ///
568 /// Pass `None::<SubstateKey>` as the cursor to iterate from the start of the partition.
569 fn list_sorted_raw_values<'a>(
570 &self,
571 node_id: impl AsRef<NodeId>,
572 partition_number: PartitionNumber,
573 from_substate_key_inclusive: impl ResolvableOptionalSubstateKey<'a>,
574 ) -> Box<dyn Iterator<Item = (SortedKey, Vec<u8>)> + '_> {
575 self.list_kinded_raw_values::<SortedKey>(
576 node_id,
577 partition_number,
578 from_substate_key_inclusive,
579 )
580 }
581
582 /// Returns an iterator of the substates of a sorted partition from an inclusive start cursor.
583 ///
584 /// The iterator returns the `SortedKey = ([u8; 2], Vec<u8>)` and the decoded value `V`
585 /// for each substate. The value type `V` can be specified or inferred.
586 ///
587 /// Pass `None::<SubstateKey>` as the cursor to iterate from the start of the partition.
588 ///
589 /// # Panics
590 /// This method panics if:
591 /// * There is an error decoding the value bytes into `V`.
592 fn list_sorted_values<'a, V: ScryptoDecode>(
593 &self,
594 node_id: impl AsRef<NodeId>,
595 partition_number: PartitionNumber,
596 from_substate_key_inclusive: impl ResolvableOptionalSubstateKey<'a>,
597 ) -> Box<dyn Iterator<Item = (SortedKey, V)> + '_> {
598 self.list_kinded_values::<SortedKey, V>(
599 node_id,
600 partition_number,
601 from_substate_key_inclusive,
602 )
603 }
604}
605
606fn db_partition_key(
607 node_id: impl AsRef<NodeId>,
608 partition_number: PartitionNumber,
609) -> DbPartitionKey {
610 SpreadPrefixKeyMapper::to_db_partition_key(node_id.as_ref(), partition_number)
611}
612
613fn db_sort_key<'a>(substate_key: impl ResolvableSubstateKey<'a>) -> DbSortKey {
614 SpreadPrefixKeyMapper::to_db_sort_key_from_ref(substate_key.into_substate_key_or_ref().as_ref())
615}
616
617fn optional_db_sort_key<'a>(
618 optional_substate_key: impl ResolvableOptionalSubstateKey<'a>,
619) -> Option<DbSortKey> {
620 optional_substate_key
621 .into_optional_substate_key_or_ref()
622 .map(|key_or_ref| SpreadPrefixKeyMapper::to_db_sort_key_from_ref(key_or_ref.as_ref()))
623}
624
625fn decode_key<K: ScryptoDecode>(raw: &[u8]) -> K {
626 scrypto_decode::<K>(&raw).unwrap_or_else(|err| {
627 panic!(
628 "Expected key to be decodable as {}. Error: {:?}.",
629 core::any::type_name::<K>(),
630 err,
631 )
632 })
633}
634
635fn decode_value<V: ScryptoDecode>(raw: &[u8]) -> V {
636 scrypto_decode::<V>(&raw).unwrap_or_else(|err| {
637 panic!(
638 "Expected value to be decodable as {}. Error: {:?}.",
639 core::any::type_name::<V>(),
640 err,
641 )
642 })
643}
644
645/// A write interface between Track and a database vendor.
646pub trait CommittableSubstateDatabase {
647 /// Commits state changes to the database.
648 fn commit(&mut self, database_updates: &DatabaseUpdates);
649}
650
651impl<T: CommittableSubstateDatabase + ?Sized> CommittableSubstateDatabaseExtensions for T {}
652
653pub trait CommittableSubstateDatabaseExtensions: CommittableSubstateDatabase {
654 fn update_substate_raw<'a>(
655 &mut self,
656 node_id: impl AsRef<NodeId>,
657 partition_number: PartitionNumber,
658 substate_key: impl ResolvableSubstateKey<'a>,
659 value: Vec<u8>,
660 ) {
661 self.commit(&DatabaseUpdates::from_delta_maps(indexmap!(
662 SpreadPrefixKeyMapper::to_db_partition_key(
663 node_id.as_ref(),
664 partition_number,
665 ) => indexmap!(
666 SpreadPrefixKeyMapper::to_db_sort_key_from_ref(
667 substate_key.into_substate_key_or_ref().as_ref(),
668 ) => DatabaseUpdate::Set(
669 value
670 )
671 )
672 )))
673 }
674
675 fn delete_substate<'a>(
676 &mut self,
677 node_id: impl AsRef<NodeId>,
678 partition_number: PartitionNumber,
679 substate_key: impl ResolvableSubstateKey<'a>,
680 ) {
681 self.commit(&DatabaseUpdates::from_delta_maps(indexmap!(
682 SpreadPrefixKeyMapper::to_db_partition_key(
683 node_id.as_ref(),
684 partition_number,
685 ) => indexmap!(
686 SpreadPrefixKeyMapper::to_db_sort_key_from_ref(
687 substate_key.into_substate_key_or_ref().as_ref(),
688 ) => DatabaseUpdate::Delete,
689 )
690 )))
691 }
692
693 fn update_substate<'a, E: ScryptoEncode>(
694 &mut self,
695 node_id: impl AsRef<NodeId>,
696 partition_number: PartitionNumber,
697 substate_key: impl ResolvableSubstateKey<'a>,
698 value: E,
699 ) {
700 let encoded_value = scrypto_encode(&value).unwrap_or_else(|err| {
701 panic!(
702 "Expected value to be encodable as {}. Error: {:?}.",
703 core::any::type_name::<E>(),
704 err,
705 )
706 });
707 self.update_substate_raw(node_id, partition_number, substate_key, encoded_value)
708 }
709}
710
711/// A partition listing interface between Track and a database vendor.
712pub trait ListableSubstateDatabase {
713 /// Iterates over all partition keys, in an arbitrary order.
714 ///
715 /// ## Alternatives
716 /// You likely want to use the [`read_partition_keys`][ListableSubstateDatabaseExtensions::read_partition_keys]
717 /// method instead, which returns an unmapped key. This is available if
718 /// the trait [`ListableSubstateDatabaseExtensions`] is in scope.
719 fn list_partition_keys(&self) -> Box<dyn Iterator<Item = DbPartitionKey> + '_>;
720}
721
722impl<T: ListableSubstateDatabase + ?Sized> ListableSubstateDatabaseExtensions for T {}
723
724/// These are a separate trait so that [`ListableSubstateDatabase`] stays object-safe,
725/// and can be used as `dyn ListableSubstateDatabase`.
726///
727/// Generic parameters aren't permitted on object-safe traits.
728pub trait ListableSubstateDatabaseExtensions: ListableSubstateDatabase {
729 fn read_partition_keys(&self) -> Box<dyn Iterator<Item = (NodeId, PartitionNumber)> + '_> {
730 let iterator = self
731 .list_partition_keys()
732 .map(|key| SpreadPrefixKeyMapper::from_db_partition_key(&key));
733 Box::new(iterator)
734 }
735}