radix_engine/transaction/
system_structure.rs

1use crate::internal_prelude::*;
2use crate::system::system_db_reader::*;
3use crate::system::system_type_checker::BlueprintTypeTarget;
4use crate::system::type_info::TypeInfoSubstate;
5use radix_engine_interface::blueprints::package::*;
6use radix_substate_store_interface::interface::SubstateDatabase;
7
8#[derive(Debug, Clone, ScryptoSbor, PartialEq, Eq)]
9pub enum SubstateSystemStructure {
10    SystemField(SystemFieldStructure),
11    SystemSchema,
12    // KeyValueStore substates
13    KeyValueStoreEntry(KeyValueStoreEntryStructure),
14    // Object substates
15    ObjectField(FieldStructure),
16    ObjectKeyValuePartitionEntry(KeyValuePartitionEntryStructure),
17    ObjectIndexPartitionEntry(IndexPartitionEntryStructure),
18    ObjectSortedIndexPartitionEntry(SortedIndexPartitionEntryStructure),
19}
20
21#[derive(Debug, Clone, ScryptoSbor, PartialEq, Eq)]
22pub struct SystemFieldStructure {
23    pub field_kind: SystemFieldKind,
24}
25
26#[derive(Debug, Copy, Clone, ScryptoSbor, PartialEq, Eq)]
27pub enum SystemFieldKind {
28    TypeInfo,
29    VmBoot,
30    SystemBoot,
31    KernelBoot,
32    TransactionValidationConfiguration,
33    ProtocolUpdateStatusSummary,
34}
35
36#[derive(Debug, Clone, ScryptoSbor, PartialEq, Eq)]
37pub struct KeyValueStoreEntryStructure {
38    pub key_full_type_id: FullyScopedTypeId<NodeId>,
39    pub value_full_type_id: FullyScopedTypeId<NodeId>,
40}
41
42#[derive(Debug, Clone, ScryptoSbor, PartialEq, Eq)]
43pub struct FieldStructure {
44    pub value_schema: ObjectSubstateTypeReference,
45}
46
47#[derive(Debug, Clone, ScryptoSbor, PartialEq, Eq)]
48pub struct KeyValuePartitionEntryStructure {
49    pub key_schema: ObjectSubstateTypeReference,
50    pub value_schema: ObjectSubstateTypeReference,
51}
52
53#[derive(Debug, Clone, ScryptoSbor, PartialEq, Eq)]
54pub struct IndexPartitionEntryStructure {
55    pub key_schema: ObjectSubstateTypeReference,
56    pub value_schema: ObjectSubstateTypeReference,
57}
58
59#[derive(Debug, Clone, ScryptoSbor, PartialEq, Eq)]
60pub struct SortedIndexPartitionEntryStructure {
61    pub key_schema: ObjectSubstateTypeReference,
62    pub value_schema: ObjectSubstateTypeReference,
63}
64
65#[derive(Debug, Clone, ScryptoSbor, PartialEq, Eq)]
66pub enum ObjectSubstateTypeReference {
67    Package(PackageTypeReference),
68    ObjectInstance(ObjectInstanceTypeReference),
69}
70
71#[derive(Debug, Clone, ScryptoSbor, PartialEq, Eq)]
72pub struct PackageTypeReference {
73    pub full_type_id: FullyScopedTypeId<PackageAddress>,
74}
75
76#[derive(Debug, Clone, ScryptoSbor, PartialEq, Eq)]
77pub struct ObjectInstanceTypeReference {
78    pub instance_type_id: u8,
79    pub resolved_full_type_id: FullyScopedTypeId<NodeId>,
80}
81
82#[derive(Debug, Clone, ScryptoSbor, PartialEq, Eq)]
83pub struct EventSystemStructure {
84    pub package_type_reference: PackageTypeReference,
85}
86
87pub type SubstateSystemStructures =
88    IndexMap<NodeId, IndexMap<PartitionNumber, IndexMap<SubstateKey, SubstateSystemStructure>>>;
89
90#[derive(Default, Debug, Clone, ScryptoSbor, PartialEq, Eq)]
91pub struct SystemStructure {
92    pub substate_system_structures: SubstateSystemStructures,
93    pub event_system_structures: IndexMap<EventTypeIdentifier, EventSystemStructure>,
94}
95
96impl SystemStructure {
97    pub fn resolve<S: SubstateDatabase>(
98        substate_db: &S,
99        state_updates: &StateUpdates,
100        application_events: &Vec<(EventTypeIdentifier, Vec<u8>)>,
101    ) -> Self {
102        let mut substate_schema_mapper = SubstateSchemaMapper::new(
103            SystemDatabaseReader::new_with_overlay(substate_db, state_updates),
104        );
105        substate_schema_mapper.add_substate_structures(state_updates);
106        let substate_system_structures = substate_schema_mapper.done();
107
108        let event_system_structures =
109            EventSchemaMapper::new(substate_db, state_updates, application_events).run();
110
111        SystemStructure {
112            substate_system_structures,
113            event_system_structures,
114        }
115    }
116}
117
118/// A builder of [`SubstateSystemStructures`].
119/// Note that the implementation below assumes that substate owned objects can not be
120/// detached. If this changes, we will have to account for objects that are removed
121/// from a substate.
122pub struct SubstateSchemaMapper<'a, S: SubstateDatabase> {
123    /// The source of type information.
124    system_reader: SystemDatabaseReader<'a, S>,
125    /// The result of the build.
126    substate_structures: SubstateSystemStructures,
127}
128
129impl<'a, S: SubstateDatabase> SubstateSchemaMapper<'a, S> {
130    /// Creates an empty builder.
131    pub fn new(system_reader: SystemDatabaseReader<'a, S>) -> Self {
132        Self {
133            system_reader,
134            substate_structures: index_map_new(),
135        }
136    }
137
138    /// Resolves a [`SubstateSystemStructure`] of the given substate and adds it to the build.
139    pub fn add_substate_structure(
140        &mut self,
141        node_id: &NodeId,
142        partition_num: &PartitionNumber,
143        key: &SubstateKey,
144    ) {
145        let partition_descriptors = self
146            .system_reader
147            .get_partition_descriptors(node_id, partition_num)
148            .unwrap();
149        let substate_structure =
150            self.resolve_substate_structure(node_id, partition_descriptors, key);
151        self.substate_structures
152            .entry(node_id.clone())
153            .or_insert_with(|| index_map_new())
154            .entry(partition_num.clone())
155            .or_insert_with(|| index_map_new())
156            .insert(key.clone(), substate_structure);
157    }
158
159    /// A batch `add_substate_structure()` counterpart, tailored for processing all substates
160    /// *written* to the track (i.e. skipping reads).
161    pub fn add_substate_structures(&mut self, state_updates: &StateUpdates) {
162        for (node_id, node_updates) in &state_updates.by_node {
163            let NodeStateUpdates::Delta { by_partition } = &node_updates;
164
165            for (partition_num, partition_update) in by_partition {
166                match partition_update {
167                    PartitionStateUpdates::Delta { by_substate } => {
168                        for substate_key in by_substate.keys() {
169                            self.add_substate_structure(node_id, partition_num, substate_key);
170                        }
171                    }
172                    PartitionStateUpdates::Batch(_) => {
173                        // Do not add substate structures for partition deletions.
174                    }
175                }
176            }
177        }
178    }
179
180    /// A batch `add_substate_structure()` counterpart, tailored for processing all substates that
181    /// were *individually* updated in the given [`StateUpdates`] (i.e. ignoring substates affected
182    /// as part of a batch, e.g. during a partition deletion).
183    pub fn add_for_all_individually_updated(&mut self, updates: &StateUpdates) {
184        for (node_id, node_state_updates) in &updates.by_node {
185            match node_state_updates {
186                NodeStateUpdates::Delta { by_partition } => {
187                    for (partition_num, partition_state_updates) in by_partition {
188                        let substate_keys = match partition_state_updates {
189                            PartitionStateUpdates::Delta { by_substate } => {
190                                by_substate.keys().collect::<Vec<_>>()
191                            }
192                            PartitionStateUpdates::Batch(BatchPartitionStateUpdate::Reset {
193                                new_substate_values,
194                            }) => new_substate_values.keys().collect::<Vec<_>>(),
195                        };
196                        for substate_key in substate_keys {
197                            self.add_substate_structure(node_id, partition_num, substate_key);
198                        }
199                    }
200                }
201            }
202        }
203    }
204
205    /// Finalizes the build.
206    pub fn done(self) -> SubstateSystemStructures {
207        self.substate_structures
208    }
209
210    fn resolve_substate_structure(
211        &self,
212        node_id: &NodeId,
213        partition_descriptors: Vec<SystemPartitionDescriptor>,
214        key: &SubstateKey,
215    ) -> SubstateSystemStructure {
216        match &partition_descriptors[0] {
217            SystemPartitionDescriptor::BootLoader => {
218                SubstateSystemStructure::SystemField(SystemFieldStructure {
219                    field_kind: {
220                        let field = BootLoaderField::try_from(key)
221                            .unwrap_or_else(|()| panic!("Unknown boot loader field: {key:?}"));
222                        match field {
223                            BootLoaderField::KernelBoot => SystemFieldKind::KernelBoot,
224                            BootLoaderField::SystemBoot => SystemFieldKind::SystemBoot,
225                            BootLoaderField::VmBoot => SystemFieldKind::VmBoot,
226                            BootLoaderField::TransactionValidationConfiguration => {
227                                SystemFieldKind::TransactionValidationConfiguration
228                            }
229                        }
230                    },
231                })
232            }
233            SystemPartitionDescriptor::ProtocolUpdateStatus => {
234                SubstateSystemStructure::SystemField(SystemFieldStructure {
235                    field_kind: {
236                        let field = ProtocolUpdateStatusField::try_from(key).unwrap_or_else(|()| {
237                            panic!("Unknown protocol update status field: {key:?}")
238                        });
239                        match field {
240                            ProtocolUpdateStatusField::Summary => {
241                                SystemFieldKind::ProtocolUpdateStatusSummary
242                            }
243                        }
244                    },
245                })
246            }
247            SystemPartitionDescriptor::TypeInfo => {
248                SubstateSystemStructure::SystemField(SystemFieldStructure {
249                    field_kind: {
250                        let field = TypeInfoField::try_from(key)
251                            .unwrap_or_else(|()| panic!("Unknown type info field: {key:?}"));
252                        match field {
253                            TypeInfoField::TypeInfo => SystemFieldKind::TypeInfo,
254                        }
255                    },
256                })
257            }
258            SystemPartitionDescriptor::Schema => SubstateSystemStructure::SystemSchema,
259            SystemPartitionDescriptor::KeyValueStore => {
260                let info = self
261                    .system_reader
262                    .get_kv_store_type_target(node_id)
263                    .unwrap_or_else(|_| panic!("Could not get type info for node {node_id:?}"));
264
265                let key_full_type_id = match info.kv_store_type.key_generic_substitution {
266                    GenericSubstitution::Local(type_id) => type_id.under_node(*node_id),
267                    GenericSubstitution::Remote(type_id) => self
268                        .system_reader
269                        .get_blueprint_type_schema(&type_id)
270                        .map(|x| x.1.under_node(type_id.package_address.into_node_id()))
271                        .unwrap_or_else(|_| panic!("Could not get type info {type_id:?}")),
272                };
273                let value_full_type_id = match info.kv_store_type.value_generic_substitution {
274                    GenericSubstitution::Local(type_id) => type_id.under_node(*node_id),
275                    GenericSubstitution::Remote(type_id) => self
276                        .system_reader
277                        .get_blueprint_type_schema(&type_id)
278                        .map(|x| x.1.under_node(type_id.package_address.into_node_id()))
279                        .unwrap_or_else(|_| panic!("Could not get type info {type_id:?}")),
280                };
281                SubstateSystemStructure::KeyValueStoreEntry(KeyValueStoreEntryStructure {
282                    key_full_type_id,
283                    value_full_type_id,
284                })
285            }
286            SystemPartitionDescriptor::Object(module_id, object_partition_descriptor) => {
287                let bp_type_target = self
288                    .system_reader
289                    .get_blueprint_type_target(node_id, *module_id)
290                    .unwrap_or_else(|_| panic!("Could not get type info for node {node_id:?}"));
291
292                self.resolve_object_substate_structure(
293                    &bp_type_target,
294                    object_partition_descriptor,
295                    key,
296                )
297            }
298        }
299    }
300
301    fn resolve_object_substate_structure(
302        &self,
303        bp_type_target: &BlueprintTypeTarget,
304        object_partition_descriptor: &ObjectPartitionDescriptor,
305        key: &SubstateKey,
306    ) -> SubstateSystemStructure {
307        match object_partition_descriptor {
308            ObjectPartitionDescriptor::Fields => {
309                let field_index = match key {
310                    SubstateKey::Field(field_index) => field_index,
311                    _ => panic!("Invalid field key"),
312                };
313
314                let payload_identifier = BlueprintPayloadIdentifier::Field(*field_index);
315                let type_reference = self
316                    .system_reader
317                    .get_blueprint_payload_schema_pointer(&bp_type_target, &payload_identifier)
318                    .expect("Could not resolve to type reference");
319                return SubstateSystemStructure::ObjectField(FieldStructure {
320                    value_schema: type_reference,
321                });
322            }
323
324            ObjectPartitionDescriptor::KeyValueCollection(collection_index) => {
325                let key_identifier =
326                    BlueprintPayloadIdentifier::KeyValueEntry(*collection_index, KeyOrValue::Key);
327                let value_identifier =
328                    BlueprintPayloadIdentifier::KeyValueEntry(*collection_index, KeyOrValue::Value);
329                let key_type_reference = self
330                    .system_reader
331                    .get_blueprint_payload_schema_pointer(&bp_type_target, &key_identifier)
332                    .expect("Could not resolve to type reference");
333                let value_type_reference = self
334                    .system_reader
335                    .get_blueprint_payload_schema_pointer(&bp_type_target, &value_identifier)
336                    .expect("Could not resolve to type reference");
337                SubstateSystemStructure::ObjectKeyValuePartitionEntry(
338                    KeyValuePartitionEntryStructure {
339                        key_schema: key_type_reference,
340                        value_schema: value_type_reference,
341                    },
342                )
343            }
344
345            ObjectPartitionDescriptor::IndexCollection(collection_index) => {
346                let key_identifier =
347                    BlueprintPayloadIdentifier::IndexEntry(*collection_index, KeyOrValue::Key);
348                let value_identifier =
349                    BlueprintPayloadIdentifier::IndexEntry(*collection_index, KeyOrValue::Value);
350                let key_type_reference = self
351                    .system_reader
352                    .get_blueprint_payload_schema_pointer(&bp_type_target, &key_identifier)
353                    .expect("Could not resolve to type reference");
354                let value_type_reference = self
355                    .system_reader
356                    .get_blueprint_payload_schema_pointer(&bp_type_target, &value_identifier)
357                    .expect("Could not resolve to type reference");
358                SubstateSystemStructure::ObjectIndexPartitionEntry(IndexPartitionEntryStructure {
359                    key_schema: key_type_reference,
360                    value_schema: value_type_reference,
361                })
362            }
363
364            ObjectPartitionDescriptor::SortedIndexCollection(collection_index) => {
365                let key_identifier = BlueprintPayloadIdentifier::SortedIndexEntry(
366                    *collection_index,
367                    KeyOrValue::Key,
368                );
369                let value_identifier = BlueprintPayloadIdentifier::SortedIndexEntry(
370                    *collection_index,
371                    KeyOrValue::Value,
372                );
373                let key_type_reference = self
374                    .system_reader
375                    .get_blueprint_payload_schema_pointer(&bp_type_target, &key_identifier)
376                    .expect("Could not resolve to type reference");
377                let value_type_reference = self
378                    .system_reader
379                    .get_blueprint_payload_schema_pointer(&bp_type_target, &value_identifier)
380                    .expect("Could not resolve to type reference");
381                SubstateSystemStructure::ObjectSortedIndexPartitionEntry(
382                    SortedIndexPartitionEntryStructure {
383                        key_schema: key_type_reference,
384                        value_schema: value_type_reference,
385                    },
386                )
387            }
388        }
389    }
390}
391
392/// Note that the implementation below assumes that substate owned objects can not be
393/// detached. If this changes, we will have to account for objects that are removed
394/// from a substate.
395pub struct EventSchemaMapper<'a, S: SubstateDatabase> {
396    system_reader: SystemDatabaseReader<'a, S>,
397    application_events: &'a Vec<(EventTypeIdentifier, Vec<u8>)>,
398}
399
400impl<'a, S: SubstateDatabase> EventSchemaMapper<'a, S> {
401    pub fn new(
402        substate_db: &'a S,
403        state_updates: &'a StateUpdates,
404        application_events: &'a Vec<(EventTypeIdentifier, Vec<u8>)>,
405    ) -> Self {
406        Self {
407            system_reader: SystemDatabaseReader::new_with_overlay(substate_db, state_updates),
408            application_events,
409        }
410    }
411
412    pub fn run(&self) -> IndexMap<EventTypeIdentifier, EventSystemStructure> {
413        let mut event_system_structures = index_map_new();
414        for (event_type_identifier, _) in self.application_events {
415            if event_system_structures.contains_key(event_type_identifier) {
416                continue;
417            }
418            let blueprint_id = match &event_type_identifier.0 {
419                Emitter::Function(blueprint_id) => blueprint_id.clone(),
420                Emitter::Method(node_id, module_id) => {
421                    if let ModuleId::Main = module_id {
422                        let main_type_info = self.system_reader.get_type_info(node_id).unwrap();
423                        match main_type_info {
424                            TypeInfoSubstate::Object(info) => info.blueprint_info.blueprint_id,
425                            _ => panic!("Unexpected Type Info {:?}", main_type_info),
426                        }
427                    } else {
428                        module_id.static_blueprint().unwrap()
429                    }
430                }
431            };
432
433            let blueprint_definition = self
434                .system_reader
435                .get_blueprint_definition(&blueprint_id)
436                .unwrap();
437
438            let type_pointer = blueprint_definition
439                .interface
440                .get_event_payload_def(event_type_identifier.1.as_str())
441                .unwrap();
442
443            let BlueprintPayloadDef::Static(type_identifier) = type_pointer else {
444                panic!("Event identifier type pointer cannot be an instance type pointer");
445            };
446
447            let event_system_structure = EventSystemStructure {
448                package_type_reference: PackageTypeReference {
449                    full_type_id: type_identifier.under_node(blueprint_id.package_address),
450                },
451            };
452
453            event_system_structures.insert(event_type_identifier.clone(), event_system_structure);
454        }
455
456        event_system_structures
457    }
458}