radix_engine/system/
system_db_reader.rs

1use crate::internal_prelude::*;
2use radix_engine_interface::api::{AttachedModuleId, CollectionIndex, ModuleId};
3use radix_engine_interface::blueprints::package::*;
4use radix_engine_interface::types::*;
5use radix_substate_store_interface::interface::*;
6use sbor::{validate_payload_against_schema, LocalTypeId, LocatedValidationError};
7
8use crate::blueprints::package::PackageBlueprintVersionDefinitionEntrySubstate;
9use crate::system::payload_validation::{SchemaOrigin, TypeInfoForValidation, ValidationContext};
10use crate::system::system_substates::FieldSubstate;
11use crate::system::system_substates::KeyValueEntrySubstate;
12use crate::system::system_substates::LockStatus;
13use crate::system::system_type_checker::{
14    BlueprintTypeTarget, KVStoreTypeTarget, SchemaValidationMeta,
15};
16use crate::system::type_info::TypeInfoSubstate;
17use crate::transaction::{
18    ObjectInstanceTypeReference, ObjectSubstateTypeReference, PackageTypeReference,
19};
20use radix_blueprint_schema_init::BlueprintCollectionSchema;
21
22#[derive(Clone, Debug)]
23pub enum SystemPartitionDescription {
24    TypeInfo,
25    Schema,
26    Module(ModuleId, PartitionOffset),
27}
28
29#[derive(Clone, Debug)]
30pub enum ObjectPartitionDescriptor {
31    Fields,
32    KeyValueCollection(u8),
33    IndexCollection(u8),
34    SortedIndexCollection(u8),
35}
36
37#[derive(Clone, Debug)]
38pub enum SystemPartitionDescriptor {
39    BootLoader,
40    ProtocolUpdateStatus,
41    TypeInfo,
42    Schema,
43    KeyValueStore,
44    Object(ModuleId, ObjectPartitionDescriptor),
45}
46
47pub struct ResolvedPayloadSchema {
48    pub schema: Rc<VersionedScryptoSchema>,
49    pub type_id: LocalTypeId,
50    pub allow_ownership: bool,
51    pub allow_non_global_refs: bool,
52    pub schema_origin: SchemaOrigin,
53}
54
55#[derive(Debug, Clone, PartialEq, Eq)]
56pub enum ObjectCollectionKey<'a, K: ScryptoEncode> {
57    KeyValue(u8, &'a K),
58    Index(u8, &'a K),
59    SortedIndex(u8, u16, &'a K),
60}
61
62impl<'a, K: ScryptoEncode> ObjectCollectionKey<'a, K> {
63    fn collection_index(&self) -> u8 {
64        match self {
65            ObjectCollectionKey::KeyValue(index, ..)
66            | ObjectCollectionKey::Index(index, ..)
67            | ObjectCollectionKey::SortedIndex(index, ..) => *index,
68        }
69    }
70}
71
72#[derive(Debug, Clone, PartialEq, Eq)]
73pub enum SystemReaderError {
74    FieldDoesNotExist,
75    CollectionDoesNotExist,
76    NodeIdDoesNotExist,
77    PayloadDoesNotExist,
78    BlueprintDoesNotExist,
79    ModuleDoesNotExist,
80    NotAKeyValueStore,
81    NotAnObject,
82    SchemaDoesNotExist,
83    TargetNotSupported,
84    BlueprintTypeNotFound(String),
85}
86
87/// A System Layer (Layer 2) abstraction over an underlying substate database
88pub struct SystemDatabaseReader<'a, S: SubstateDatabase + ?Sized> {
89    substate_db: &'a S,
90    state_updates: Option<&'a StateUpdates>,
91
92    blueprint_cache: RefCell<NonIterMap<CanonicalBlueprintId, Rc<BlueprintDefinition>>>,
93    schema_cache: RefCell<NonIterMap<SchemaHash, Rc<VersionedScryptoSchema>>>,
94}
95
96impl<'a, S: SubstateDatabase + ?Sized> SystemDatabaseReader<'a, S> {
97    pub fn new_with_overlay(substate_db: &'a S, state_updates: &'a StateUpdates) -> Self {
98        Self {
99            substate_db,
100            state_updates: Some(state_updates),
101            blueprint_cache: RefCell::new(NonIterMap::new()),
102            schema_cache: RefCell::new(NonIterMap::new()),
103        }
104    }
105
106    pub fn new(substate_db: &'a S) -> Self {
107        Self {
108            substate_db,
109            state_updates: None,
110            blueprint_cache: RefCell::new(NonIterMap::new()),
111            schema_cache: RefCell::new(NonIterMap::new()),
112        }
113    }
114
115    pub fn get_type_info(&self, node_id: &NodeId) -> Result<TypeInfoSubstate, SystemReaderError> {
116        self.fetch_substate::<TypeInfoSubstate>(
117            node_id,
118            TYPE_INFO_FIELD_PARTITION,
119            &TypeInfoField::TypeInfo.into(),
120        )
121        .ok_or(SystemReaderError::NodeIdDoesNotExist)
122    }
123
124    pub fn get_package_definition(
125        &self,
126        package_address: PackageAddress,
127    ) -> BTreeMap<BlueprintVersionKey, BlueprintDefinition> {
128        let entries = self
129            .substate_db
130            .list_map_values::<PackageBlueprintVersionDefinitionEntrySubstate>(
131                package_address.as_node_id(),
132                MAIN_BASE_PARTITION
133                    .at_offset(PACKAGE_BLUEPRINTS_PARTITION_OFFSET)
134                    .unwrap(),
135                None::<SubstateKey>,
136            );
137
138        let mut blueprints = BTreeMap::new();
139        for (key, blueprint_definition) in entries {
140            let bp_version_key: BlueprintVersionKey = scrypto_decode(&key).unwrap();
141
142            blueprints.insert(
143                bp_version_key,
144                blueprint_definition
145                    .into_value()
146                    .unwrap()
147                    .fully_update_and_into_latest_version(),
148            );
149        }
150
151        blueprints
152    }
153
154    pub fn read_object_field(
155        &self,
156        node_id: &NodeId,
157        module_id: ModuleId,
158        field_index: u8,
159    ) -> Result<IndexedScryptoValue, SystemReaderError> {
160        self.read_object_field_advanced(node_id, module_id, field_index)
161            .map(|x| x.0)
162    }
163
164    pub fn read_object_field_advanced(
165        &self,
166        node_id: &NodeId,
167        module_id: ModuleId,
168        field_index: u8,
169    ) -> Result<(IndexedScryptoValue, PartitionNumber), SystemReaderError> {
170        let blueprint_id = self.get_blueprint_id(node_id, module_id)?;
171        let definition = self.get_blueprint_definition(&blueprint_id)?;
172        let partition_description = &definition
173            .interface
174            .state
175            .fields
176            .as_ref()
177            .ok_or(SystemReaderError::FieldDoesNotExist)?
178            .0;
179        let partition_number = match partition_description {
180            PartitionDescription::Logical(offset) => {
181                let base_partition = match module_id {
182                    ModuleId::Main => MAIN_BASE_PARTITION,
183                    ModuleId::Metadata => METADATA_BASE_PARTITION,
184                    ModuleId::Royalty => ROYALTY_BASE_PARTITION,
185                    ModuleId::RoleAssignment => ROLE_ASSIGNMENT_BASE_PARTITION,
186                };
187                base_partition.at_offset(*offset).unwrap()
188            }
189            PartitionDescription::Physical(partition_number) => *partition_number,
190        };
191
192        let substate: FieldSubstate<ScryptoValue> = self
193            .substate_db
194            .get_substate(node_id, partition_number, SubstateKey::Field(field_index))
195            .ok_or(SystemReaderError::FieldDoesNotExist)?;
196
197        Ok((
198            IndexedScryptoValue::from_scrypto_value(substate.into_payload()),
199            partition_number,
200        ))
201    }
202
203    pub fn read_typed_kv_entry<K: ScryptoEncode, V: ScryptoDecode>(
204        &self,
205        node_id: &NodeId,
206        key: &K,
207    ) -> Option<V> {
208        self.substate_db
209            .get_substate::<KeyValueEntrySubstate<V>>(
210                node_id,
211                MAIN_BASE_PARTITION,
212                SubstateKey::Map(scrypto_encode(key).unwrap()),
213            )
214            .and_then(|v| v.into_value())
215    }
216
217    pub fn read_typed_object_field<V: ScryptoDecode>(
218        &self,
219        node_id: &NodeId,
220        module_id: ModuleId,
221        field_index: u8,
222    ) -> Result<V, SystemReaderError> {
223        let blueprint_id = self.get_blueprint_id(node_id, module_id)?;
224        let definition = self.get_blueprint_definition(&blueprint_id)?;
225        let partition_description = &definition
226            .interface
227            .state
228            .fields
229            .as_ref()
230            .ok_or(SystemReaderError::FieldDoesNotExist)?
231            .0;
232        let partition_number = match partition_description {
233            PartitionDescription::Logical(offset) => {
234                let base_partition = match module_id {
235                    ModuleId::Main => MAIN_BASE_PARTITION,
236                    ModuleId::Metadata => METADATA_BASE_PARTITION,
237                    ModuleId::Royalty => ROYALTY_BASE_PARTITION,
238                    ModuleId::RoleAssignment => ROLE_ASSIGNMENT_BASE_PARTITION,
239                };
240                base_partition.at_offset(*offset).unwrap()
241            }
242            PartitionDescription::Physical(partition_number) => *partition_number,
243        };
244
245        let substate: FieldSubstate<V> = self
246            .substate_db
247            .get_substate(node_id, partition_number, SubstateKey::Field(field_index))
248            .ok_or(SystemReaderError::FieldDoesNotExist)?;
249
250        Ok(substate.into_payload())
251    }
252
253    pub fn get_partition_of_collection(
254        &self,
255        node_id: &NodeId,
256        module_id: ModuleId,
257        collection_index: CollectionIndex,
258    ) -> Result<PartitionNumber, SystemReaderError> {
259        let blueprint_id = self.get_blueprint_id(node_id, module_id)?;
260        let definition = self.get_blueprint_definition(&blueprint_id)?;
261
262        let (partition_description, ..) = definition
263            .interface
264            .state
265            .collections
266            .get(collection_index as usize)
267            .ok_or(SystemReaderError::CollectionDoesNotExist)?;
268
269        let partition_number = match partition_description {
270            PartitionDescription::Logical(offset) => {
271                module_id.base_partition_num().at_offset(*offset).unwrap()
272            }
273            PartitionDescription::Physical(partition_number) => *partition_number,
274        };
275
276        Ok(partition_number)
277    }
278
279    pub fn read_object_collection_entry<K: ScryptoEncode, V: ScryptoDecode>(
280        &self,
281        node_id: &NodeId,
282        module_id: ModuleId,
283        collection_key: ObjectCollectionKey<K>,
284    ) -> Result<Option<V>, SystemReaderError> {
285        let partition_number = self.get_partition_of_collection(
286            node_id,
287            module_id,
288            collection_key.collection_index(),
289        )?;
290
291        let entry = match collection_key {
292            ObjectCollectionKey::KeyValue(_, key) => self
293                .substate_db
294                .get_substate::<KeyValueEntrySubstate<V>>(
295                    node_id,
296                    partition_number,
297                    SubstateKey::Map(scrypto_encode(key).unwrap()),
298                )
299                .and_then(|value| value.into_value()),
300            ObjectCollectionKey::Index(_, key) => self
301                .substate_db
302                .get_substate::<IndexEntrySubstate<V>>(
303                    node_id,
304                    partition_number,
305                    SubstateKey::Map(scrypto_encode(key).unwrap()),
306                )
307                .map(|value| value.into_value()),
308            ObjectCollectionKey::SortedIndex(_, sort, key) => self
309                .substate_db
310                .get_substate::<SortedIndexEntrySubstate<V>>(
311                    node_id,
312                    partition_number,
313                    SubstateKey::Sorted((sort.to_be_bytes(), scrypto_encode(key).unwrap())),
314                )
315                .map(|value| value.into_value()),
316        };
317
318        Ok(entry)
319    }
320
321    #[allow(clippy::type_complexity)]
322    pub fn key_value_store_iter(
323        &self,
324        node_id: &NodeId,
325        from_key: Option<&MapKey>,
326    ) -> Result<Box<dyn Iterator<Item = (MapKey, Vec<u8>)> + '_>, SystemReaderError> {
327        if self.state_updates.is_some() {
328            panic!("key_value_store_iter with overlay not supported.");
329        }
330
331        match self.get_type_info(node_id)? {
332            TypeInfoSubstate::KeyValueStore(..) => {}
333            _ => return Err(SystemReaderError::NotAKeyValueStore),
334        }
335
336        let iterable = self
337            .substate_db
338            .list_map_values::<KeyValueEntrySubstate<ScryptoRawValue>>(
339                node_id,
340                MAIN_BASE_PARTITION,
341                from_key,
342            )
343            .filter_map(move |(map_key, substate)| {
344                let value = substate.into_value()?;
345                let value = scrypto_encode(&value).unwrap();
346
347                Some((map_key, value))
348            });
349
350        Ok(Box::new(iterable))
351    }
352
353    #[allow(clippy::type_complexity)]
354    pub fn collection_iter(
355        &self,
356        node_id: &NodeId,
357        module_id: ModuleId,
358        collection_index: CollectionIndex,
359    ) -> Result<Box<dyn Iterator<Item = (SubstateKey, Vec<u8>)> + '_>, SystemReaderError> {
360        self.collection_iter_advanced(node_id, module_id, collection_index, None)
361            .map(|x| x.0)
362    }
363
364    #[allow(clippy::type_complexity)]
365    pub fn collection_iter_advanced<'s, 'x>(
366        &'s self,
367        node_id: &'x NodeId,
368        module_id: ModuleId,
369        collection_index: CollectionIndex,
370        from_substate_key: Option<&'x SubstateKey>,
371    ) -> Result<
372        (
373            Box<dyn Iterator<Item = (SubstateKey, Vec<u8>)> + 's>,
374            PartitionNumber,
375        ),
376        SystemReaderError,
377    > {
378        if self.state_updates.is_some() {
379            panic!("collection_iter_advanced with overlay not supported.");
380        }
381
382        let blueprint_id = self.get_blueprint_id(node_id, module_id)?;
383        let definition = self.get_blueprint_definition(&blueprint_id)?;
384
385        let (partition_description, schema) = definition
386            .interface
387            .state
388            .collections
389            .get(collection_index as usize)
390            .ok_or(SystemReaderError::CollectionDoesNotExist)?
391            .clone();
392
393        let partition_number = match partition_description {
394            PartitionDescription::Physical(partition_num) => partition_num,
395            PartitionDescription::Logical(offset) => {
396                module_id.base_partition_num().at_offset(offset).unwrap()
397            }
398        };
399
400        let iterable: Box<dyn Iterator<Item = (SubstateKey, Vec<u8>)> + 's> = match schema {
401            BlueprintCollectionSchema::KeyValueStore(..) => {
402                let iterable = self
403                    .substate_db
404                    .list_map_values::<KeyValueEntrySubstate<ScryptoRawValue>>(
405                        node_id,
406                        partition_number,
407                        from_substate_key,
408                    )
409                    .filter_map(|(map_key, substate)| {
410                        Some((
411                            SubstateKey::Map(map_key),
412                            scrypto_encode(&substate.into_value()?).unwrap(),
413                        ))
414                    });
415                Box::new(iterable)
416            }
417            BlueprintCollectionSchema::Index(..) => {
418                let iterable = self
419                    .substate_db
420                    .list_map_values::<IndexEntrySubstate<ScryptoRawValue>>(
421                        node_id,
422                        partition_number,
423                        from_substate_key,
424                    )
425                    .map(|(map_key, substate)| {
426                        (
427                            SubstateKey::Map(map_key),
428                            scrypto_encode(&substate.into_value()).unwrap(),
429                        )
430                    });
431                Box::new(iterable)
432            }
433            BlueprintCollectionSchema::SortedIndex(..) => {
434                let iterable = self
435                    .substate_db
436                    .list_sorted_values::<SortedIndexEntrySubstate<ScryptoRawValue>>(
437                        node_id,
438                        partition_number,
439                        from_substate_key,
440                    )
441                    .map(|(key, substate)| {
442                        (
443                            SubstateKey::Sorted(key),
444                            scrypto_encode(&substate.into_value()).unwrap(),
445                        )
446                    });
447                Box::new(iterable)
448            }
449        };
450
451        Ok((iterable, partition_number))
452    }
453
454    pub fn get_object_info<A: Into<NodeId>>(
455        &self,
456        node_id: A,
457    ) -> Result<ObjectInfo, SystemReaderError> {
458        let type_info = self
459            .fetch_substate::<TypeInfoSubstate>(
460                &node_id.into(),
461                TYPE_INFO_FIELD_PARTITION,
462                &TypeInfoField::TypeInfo.into(),
463            )
464            .ok_or(SystemReaderError::NodeIdDoesNotExist)?;
465
466        match type_info {
467            TypeInfoSubstate::Object(object_info) => Ok(object_info),
468            _ => Err(SystemReaderError::NotAnObject),
469        }
470    }
471
472    pub fn get_blueprint_id(
473        &self,
474        node_id: &NodeId,
475        module_id: ModuleId,
476    ) -> Result<BlueprintId, SystemReaderError> {
477        let type_info = self
478            .fetch_substate::<TypeInfoSubstate>(
479                node_id,
480                TYPE_INFO_FIELD_PARTITION,
481                &TypeInfoField::TypeInfo.into(),
482            )
483            .ok_or(SystemReaderError::NodeIdDoesNotExist)?;
484
485        let object_info = match type_info {
486            TypeInfoSubstate::Object(object_info) => object_info,
487            _ => {
488                return Err(SystemReaderError::NotAnObject);
489            }
490        };
491
492        let module_id: Option<AttachedModuleId> = module_id.into();
493        if let Some(module_id) = module_id {
494            match object_info.object_type {
495                ObjectType::Global { modules } => {
496                    if !modules.contains_key(&module_id) {
497                        return Err(SystemReaderError::ModuleDoesNotExist);
498                    }
499                }
500                ObjectType::Owned => return Err(SystemReaderError::ModuleDoesNotExist),
501            }
502
503            Ok(module_id.static_blueprint())
504        } else {
505            Ok(object_info.blueprint_info.blueprint_id)
506        }
507    }
508
509    pub fn get_blueprint_definition(
510        &self,
511        blueprint_id: &BlueprintId,
512    ) -> Result<Rc<BlueprintDefinition>, SystemReaderError> {
513        let canonical_key = CanonicalBlueprintId {
514            address: blueprint_id.package_address,
515            blueprint: blueprint_id.blueprint_name.clone(),
516            version: BlueprintVersion::default(),
517        };
518        {
519            if let Some(cache) = self.blueprint_cache.borrow().get(&canonical_key) {
520                return Ok(cache.clone());
521            }
522        }
523
524        let bp_version_key = BlueprintVersionKey::new_default(blueprint_id.blueprint_name.clone());
525        let definition = Rc::new(
526            self.fetch_substate::<PackageBlueprintVersionDefinitionEntrySubstate>(
527                blueprint_id.package_address.as_node_id(),
528                MAIN_BASE_PARTITION
529                    .at_offset(PACKAGE_BLUEPRINTS_PARTITION_OFFSET)
530                    .unwrap(),
531                &SubstateKey::Map(scrypto_encode(&bp_version_key).unwrap()),
532            )
533            .ok_or(SystemReaderError::BlueprintDoesNotExist)?
534            .into_value()
535            .unwrap()
536            .fully_update_and_into_latest_version(),
537        );
538
539        self.blueprint_cache
540            .borrow_mut()
541            .insert(canonical_key, definition.clone());
542
543        Ok(definition)
544    }
545
546    pub fn get_kv_store_type_target(
547        &self,
548        node_id: &NodeId,
549    ) -> Result<KVStoreTypeTarget, SystemReaderError> {
550        let type_info = self
551            .fetch_substate::<TypeInfoSubstate>(
552                node_id,
553                TYPE_INFO_FIELD_PARTITION,
554                &TypeInfoField::TypeInfo.into(),
555            )
556            .ok_or(SystemReaderError::NodeIdDoesNotExist)?;
557
558        let kv_store_info = match type_info {
559            TypeInfoSubstate::KeyValueStore(kv_store_info) => kv_store_info,
560            _ => return Err(SystemReaderError::NotAKeyValueStore),
561        };
562
563        Ok(KVStoreTypeTarget {
564            kv_store_type: kv_store_info.generic_substitutions,
565            meta: *node_id,
566        })
567    }
568
569    pub fn get_blueprint_type_target(
570        &self,
571        node_id: &NodeId,
572        module_id: ModuleId,
573    ) -> Result<BlueprintTypeTarget, SystemReaderError> {
574        let type_info = self
575            .fetch_substate::<TypeInfoSubstate>(
576                node_id,
577                TYPE_INFO_FIELD_PARTITION,
578                &TypeInfoField::TypeInfo.into(),
579            )
580            .ok_or(SystemReaderError::NodeIdDoesNotExist)?;
581
582        let object_info = match type_info {
583            TypeInfoSubstate::Object(object_info) => object_info,
584            _ => return Err(SystemReaderError::NotAnObject),
585        };
586
587        let module_id: Option<AttachedModuleId> = module_id.into();
588        let target = if let Some(module_id) = module_id {
589            let blueprint_id = module_id.static_blueprint();
590            match object_info.object_type {
591                ObjectType::Global { modules } => {
592                    if !modules.contains_key(&module_id) {
593                        return Err(SystemReaderError::ModuleDoesNotExist);
594                    }
595                }
596                ObjectType::Owned => return Err(SystemReaderError::ModuleDoesNotExist),
597            }
598
599            BlueprintTypeTarget {
600                blueprint_info: BlueprintInfo {
601                    blueprint_id,
602                    blueprint_version: Default::default(),
603                    outer_obj_info: OuterObjectInfo::None,
604                    features: Default::default(),
605                    generic_substitutions: Default::default(),
606                },
607                meta: SchemaValidationMeta::ExistingObject {
608                    additional_schemas: *node_id,
609                },
610            }
611        } else {
612            BlueprintTypeTarget {
613                blueprint_info: object_info.blueprint_info,
614                meta: SchemaValidationMeta::ExistingObject {
615                    additional_schemas: *node_id,
616                },
617            }
618        };
619
620        Ok(target)
621    }
622
623    pub fn get_kv_store_payload_schema(
624        &self,
625        target: &KVStoreTypeTarget,
626        key_or_value: KeyOrValue,
627    ) -> Result<ResolvedPayloadSchema, SystemReaderError> {
628        let (substitution, allow_ownership, allow_non_global_refs) = match key_or_value {
629            KeyOrValue::Key => (&target.kv_store_type.key_generic_substitution, false, false),
630            KeyOrValue::Value => (
631                &target.kv_store_type.value_generic_substitution,
632                target.kv_store_type.allow_ownership,
633                false,
634            ),
635        };
636
637        match substitution {
638            GenericSubstitution::Local(local_type_id) => {
639                let schema = self.get_schema(&target.meta, &local_type_id.0)?;
640
641                Ok(ResolvedPayloadSchema {
642                    schema,
643                    type_id: local_type_id.1,
644                    allow_ownership,
645                    allow_non_global_refs,
646                    schema_origin: SchemaOrigin::KeyValueStore,
647                })
648            }
649            GenericSubstitution::Remote(blueprint_type_id) => {
650                let (schema, scoped_type_id) = self.get_blueprint_type_schema(blueprint_type_id)?;
651
652                Ok(ResolvedPayloadSchema {
653                    schema,
654                    type_id: scoped_type_id.1,
655                    allow_ownership,
656                    allow_non_global_refs,
657                    schema_origin: SchemaOrigin::KeyValueStore,
658                })
659            }
660        }
661    }
662
663    pub fn get_blueprint_payload_schema_pointer(
664        &self,
665        target: &BlueprintTypeTarget,
666        payload_identifier: &BlueprintPayloadIdentifier,
667    ) -> Result<ObjectSubstateTypeReference, SystemReaderError> {
668        let blueprint_interface = &self
669            .get_blueprint_definition(&target.blueprint_info.blueprint_id)?
670            .interface;
671
672        let (payload_def, ..) = blueprint_interface
673            .get_payload_def(payload_identifier)
674            .ok_or(SystemReaderError::PayloadDoesNotExist)?;
675
676        let obj_type_reference = match payload_def {
677            BlueprintPayloadDef::Static(type_identifier) => {
678                ObjectSubstateTypeReference::Package(PackageTypeReference {
679                    full_type_id: type_identifier
680                        .under_node(target.blueprint_info.blueprint_id.package_address),
681                })
682            }
683            BlueprintPayloadDef::Generic(instance_index) => {
684                let generic_substitution = target
685                    .blueprint_info
686                    .generic_substitutions
687                    .get(instance_index as usize)
688                    .expect("Missing generic");
689
690                let entity_address = match target.meta {
691                    SchemaValidationMeta::Blueprint | SchemaValidationMeta::NewObject { .. } => {
692                        return Err(SystemReaderError::TargetNotSupported)
693                    }
694                    SchemaValidationMeta::ExistingObject { additional_schemas } => {
695                        additional_schemas
696                    }
697                };
698
699                match generic_substitution {
700                    GenericSubstitution::Local(type_id) => {
701                        ObjectSubstateTypeReference::ObjectInstance(ObjectInstanceTypeReference {
702                            instance_type_id: instance_index,
703                            resolved_full_type_id: type_id.under_node(entity_address),
704                        })
705                    }
706                    GenericSubstitution::Remote(type_id) => {
707                        let (_, scoped_type_id) = self.get_blueprint_type_schema(type_id)?;
708                        ObjectSubstateTypeReference::Package(PackageTypeReference {
709                            full_type_id: scoped_type_id.under_node(type_id.package_address),
710                        })
711                    }
712                }
713            }
714        };
715
716        Ok(obj_type_reference)
717    }
718
719    // TODO: The logic here is currently copied from system_type_checker.rs get_payload_schema().
720    // It would be nice to use the same underlying code but currently too many refactors are required
721    // to make that happen.
722    pub fn get_blueprint_payload_schema(
723        &self,
724        target: &BlueprintTypeTarget,
725        payload_identifier: &BlueprintPayloadIdentifier,
726    ) -> Result<ResolvedPayloadSchema, SystemReaderError> {
727        let blueprint_interface = &self
728            .get_blueprint_definition(&target.blueprint_info.blueprint_id)?
729            .interface;
730
731        let (payload_def, allow_ownership, allow_non_global_refs) = blueprint_interface
732            .get_payload_def(payload_identifier)
733            .ok_or(SystemReaderError::PayloadDoesNotExist)?;
734
735        // Given the payload definition, retrieve the info to be able to do schema validation on a payload
736        let (schema, index, schema_origin) = match payload_def {
737            BlueprintPayloadDef::Static(type_identifier) => {
738                let schema = self.get_schema(
739                    target
740                        .blueprint_info
741                        .blueprint_id
742                        .package_address
743                        .as_node_id(),
744                    &type_identifier.0,
745                )?;
746                (
747                    schema,
748                    type_identifier.1,
749                    SchemaOrigin::Blueprint(target.blueprint_info.blueprint_id.clone()),
750                )
751            }
752            BlueprintPayloadDef::Generic(instance_index) => {
753                let generic_substitution = target
754                    .blueprint_info
755                    .generic_substitutions
756                    .get(instance_index as usize)
757                    .expect("Missing generic substitution");
758
759                match generic_substitution {
760                    GenericSubstitution::Local(type_id) => {
761                        let schema = match &target.meta {
762                            SchemaValidationMeta::ExistingObject { additional_schemas } => {
763                                self.get_schema(additional_schemas, &type_id.0)?
764                            }
765                            SchemaValidationMeta::NewObject { .. }
766                            | SchemaValidationMeta::Blueprint => {
767                                return Err(SystemReaderError::TargetNotSupported);
768                            }
769                        };
770
771                        (schema, type_id.1, SchemaOrigin::Instance)
772                    }
773                    GenericSubstitution::Remote(type_id) => {
774                        let (schema, scoped_type_id) = self.get_blueprint_type_schema(type_id)?;
775                        (
776                            schema,
777                            scoped_type_id.1,
778                            SchemaOrigin::Blueprint(BlueprintId::new(
779                                &type_id.package_address,
780                                type_id.blueprint_name.clone(),
781                            )),
782                        )
783                    }
784                }
785            }
786        };
787
788        Ok(ResolvedPayloadSchema {
789            schema,
790            type_id: index,
791            allow_ownership,
792            allow_non_global_refs,
793            schema_origin,
794        })
795    }
796
797    pub fn get_schema(
798        &self,
799        node_id: &NodeId,
800        schema_hash: &SchemaHash,
801    ) -> Result<Rc<VersionedScryptoSchema>, SystemReaderError> {
802        {
803            if let Some(cache) = self.schema_cache.borrow().get(schema_hash) {
804                return Ok(cache.clone());
805            }
806        }
807
808        let schema = Rc::new(
809            self.fetch_substate::<KeyValueEntrySubstate<VersionedScryptoSchema>>(
810                node_id,
811                SCHEMAS_PARTITION,
812                &SubstateKey::Map(scrypto_encode(schema_hash).unwrap()),
813            )
814            .ok_or(SystemReaderError::SchemaDoesNotExist)?
815            .into_value()
816            .expect("Schema should exist if substate exists"),
817        );
818
819        self.schema_cache
820            .borrow_mut()
821            .insert(*schema_hash, schema.clone());
822
823        Ok(schema)
824    }
825
826    pub fn get_blueprint_type_schema(
827        &self,
828        type_id: &BlueprintTypeIdentifier,
829    ) -> Result<(Rc<VersionedScryptoSchema>, ScopedTypeId), SystemReaderError> {
830        let BlueprintTypeIdentifier {
831            package_address,
832            blueprint_name,
833            type_name,
834        } = type_id.clone();
835        let definition = self.get_blueprint_payload_def(&BlueprintId {
836            package_address,
837            blueprint_name,
838        })?;
839        let scoped_type_id = definition
840            .interface
841            .types
842            .get(&type_name)
843            .ok_or(SystemReaderError::BlueprintTypeNotFound(type_name.clone()))?;
844        Ok((
845            self.get_schema(package_address.as_node_id(), &scoped_type_id.0)?,
846            *scoped_type_id,
847        ))
848    }
849
850    pub fn get_blueprint_payload_def(
851        &self,
852        blueprint_id: &BlueprintId,
853    ) -> Result<BlueprintDefinition, SystemReaderError> {
854        let bp_version_key = BlueprintVersionKey::new_default(blueprint_id.blueprint_name.clone());
855        let definition = self
856            .fetch_substate::<PackageBlueprintVersionDefinitionEntrySubstate>(
857                blueprint_id.package_address.as_node_id(),
858                MAIN_BASE_PARTITION
859                    .at_offset(PACKAGE_BLUEPRINTS_PARTITION_OFFSET)
860                    .unwrap(),
861                &SubstateKey::Map(scrypto_encode(&bp_version_key).unwrap()),
862            )
863            .ok_or(SystemReaderError::BlueprintDoesNotExist)?;
864
865        Ok(definition
866            .into_value()
867            .unwrap()
868            .fully_update_and_into_latest_version())
869    }
870
871    pub fn validate_payload<'b>(
872        &'b self,
873        payload: &[u8],
874        payload_schema: &'b ResolvedPayloadSchema,
875        depth_limit: usize,
876    ) -> Result<(), LocatedValidationError<'b, ScryptoCustomExtension>> {
877        let validation_context: Box<dyn ValidationContext<Error = String>> =
878            Box::new(ValidationPayloadCheckerContext {
879                reader: self,
880                schema_origin: payload_schema.schema_origin.clone(),
881                allow_ownership: payload_schema.allow_ownership,
882                allow_non_global_ref: payload_schema.allow_non_global_refs,
883            });
884
885        validate_payload_against_schema::<ScryptoCustomExtension, _>(
886            payload,
887            payload_schema.schema.v1(),
888            payload_schema.type_id,
889            &validation_context,
890            depth_limit,
891        )
892    }
893
894    pub fn fetch_substate<D: ScryptoDecode>(
895        &self,
896        node_id: &NodeId,
897        partition_num: PartitionNumber,
898        key: &SubstateKey,
899    ) -> Option<D> {
900        if let Some(result) =
901            self.fetch_substate_from_state_updates::<D>(node_id, partition_num, key)
902        {
903            // If result can be determined from the state updates.
904            result
905        } else {
906            // Otherwise, read from the substate database.
907            self.fetch_substate_from_database::<D>(node_id, partition_num, key)
908        }
909    }
910
911    pub fn fetch_substate_from_database<D: ScryptoDecode>(
912        &self,
913        node_id: &NodeId,
914        partition_num: PartitionNumber,
915        key: &SubstateKey,
916    ) -> Option<D> {
917        self.substate_db
918            .get_substate::<D>(node_id, partition_num, key)
919    }
920
921    pub fn fetch_substate_from_state_updates<D: ScryptoDecode>(
922        &self,
923        node_id: &NodeId,
924        partition_num: PartitionNumber,
925        substate_key: &SubstateKey,
926    ) -> Option<Option<D>> {
927        if let Some(updates) = self.state_updates {
928            updates
929                .by_node
930                .get(node_id)
931                .and_then(|node_updates| match node_updates {
932                    NodeStateUpdates::Delta { by_partition } => by_partition.get(&partition_num),
933                })
934                .and_then(|partition_updates| match partition_updates {
935                    PartitionStateUpdates::Delta { by_substate } => {
936                        match by_substate.get(substate_key) {
937                            Some(e) => match e {
938                                DatabaseUpdate::Set(value) => {
939                                    Some(Some(scrypto_decode(value).unwrap()))
940                                }
941                                DatabaseUpdate::Delete => {
942                                    // Return `Some(None)` if the substate is deleted.
943                                    Some(None)
944                                }
945                            },
946                            None => None,
947                        }
948                    }
949                    PartitionStateUpdates::Batch(e) => match e {
950                        BatchPartitionStateUpdate::Reset {
951                            new_substate_values,
952                        } => {
953                            // Return `Some(None)` if the substate key isn't in the new value set.
954                            Some(
955                                new_substate_values
956                                    .get(substate_key)
957                                    .map(|value| scrypto_decode(value).unwrap()),
958                            )
959                        }
960                    },
961                })
962        } else {
963            None
964        }
965    }
966}
967
968// Reverse Mapping Functionality
969impl<'a, S: SubstateDatabase> SystemDatabaseReader<'a, S> {
970    pub fn get_partition_descriptors(
971        &self,
972        node_id: &NodeId,
973        partition_num: &PartitionNumber,
974    ) -> Result<Vec<SystemPartitionDescriptor>, SystemReaderError> {
975        let mut descriptors = Vec::new();
976
977        if partition_num.eq(&BOOT_LOADER_PARTITION) {
978            descriptors.push(SystemPartitionDescriptor::BootLoader);
979        }
980
981        if partition_num.eq(&PROTOCOL_UPDATE_STATUS_PARTITION) {
982            descriptors.push(SystemPartitionDescriptor::ProtocolUpdateStatus);
983        }
984
985        if partition_num.eq(&TYPE_INFO_FIELD_PARTITION) {
986            descriptors.push(SystemPartitionDescriptor::TypeInfo);
987        }
988
989        if partition_num.eq(&SCHEMAS_PARTITION) {
990            descriptors.push(SystemPartitionDescriptor::Schema);
991        }
992
993        let type_info = self.get_type_info(node_id)?;
994
995        match type_info {
996            TypeInfoSubstate::Object(object_info) => {
997                let (module_id, partition_offset) = if partition_num.ge(&MAIN_BASE_PARTITION) {
998                    let partition_offset = PartitionOffset(partition_num.0 - MAIN_BASE_PARTITION.0);
999                    (ModuleId::Main, Some(partition_offset))
1000                } else {
1001                    match object_info.object_type {
1002                        ObjectType::Global { modules } => {
1003                            if partition_num.ge(&ROLE_ASSIGNMENT_BASE_PARTITION) {
1004                                if modules.contains_key(&AttachedModuleId::RoleAssignment) {
1005                                    let partition_offset = PartitionOffset(
1006                                        partition_num.0 - ROLE_ASSIGNMENT_BASE_PARTITION.0,
1007                                    );
1008                                    (ModuleId::RoleAssignment, Some(partition_offset))
1009                                } else {
1010                                    (ModuleId::Main, None)
1011                                }
1012                            } else if partition_num.ge(&ROYALTY_BASE_PARTITION) {
1013                                if modules.contains_key(&AttachedModuleId::Royalty) {
1014                                    let partition_offset =
1015                                        PartitionOffset(partition_num.0 - ROYALTY_BASE_PARTITION.0);
1016                                    (ModuleId::Royalty, Some(partition_offset))
1017                                } else {
1018                                    (ModuleId::Main, None)
1019                                }
1020                            } else if partition_num.ge(&METADATA_BASE_PARTITION) {
1021                                if modules.contains_key(&AttachedModuleId::Metadata) {
1022                                    let partition_offset = PartitionOffset(
1023                                        partition_num.0 - METADATA_BASE_PARTITION.0,
1024                                    );
1025                                    (ModuleId::Metadata, Some(partition_offset))
1026                                } else {
1027                                    (ModuleId::Main, None)
1028                                }
1029                            } else {
1030                                (ModuleId::Main, None)
1031                            }
1032                        }
1033                        ObjectType::Owned => (ModuleId::Main, None),
1034                    }
1035                };
1036
1037                let blueprint_id = match module_id {
1038                    ModuleId::Main => object_info.blueprint_info.blueprint_id,
1039                    _ => module_id.static_blueprint().unwrap(),
1040                };
1041
1042                let definition = self.get_blueprint_definition(&blueprint_id).unwrap();
1043
1044                let state_schema = &definition.interface.state;
1045                if let (
1046                    Some((PartitionDescription::Logical(offset), _fields)),
1047                    Some(partition_offset),
1048                ) = (&state_schema.fields, &partition_offset)
1049                {
1050                    if offset.eq(partition_offset) {
1051                        descriptors.push(SystemPartitionDescriptor::Object(
1052                            module_id,
1053                            ObjectPartitionDescriptor::Fields,
1054                        ));
1055                    }
1056                }
1057
1058                for (index, (partition_description, schema)) in
1059                    state_schema.collections.iter().enumerate()
1060                {
1061                    let partition_descriptor = match schema {
1062                        BlueprintCollectionSchema::KeyValueStore(..) => {
1063                            ObjectPartitionDescriptor::KeyValueCollection(index as u8)
1064                        }
1065                        BlueprintCollectionSchema::Index(..) => {
1066                            ObjectPartitionDescriptor::IndexCollection(index as u8)
1067                        }
1068                        BlueprintCollectionSchema::SortedIndex(..) => {
1069                            ObjectPartitionDescriptor::SortedIndexCollection(index as u8)
1070                        }
1071                    };
1072
1073                    match (partition_description, &partition_offset) {
1074                        (PartitionDescription::Logical(offset), Some(partition_offset))
1075                            if offset.eq(partition_offset) =>
1076                        {
1077                            descriptors.push(SystemPartitionDescriptor::Object(
1078                                module_id,
1079                                partition_descriptor,
1080                            ))
1081                        }
1082                        (PartitionDescription::Physical(physical_partition), None)
1083                            if physical_partition.eq(partition_num) =>
1084                        {
1085                            descriptors.push(SystemPartitionDescriptor::Object(
1086                                module_id,
1087                                partition_descriptor,
1088                            ))
1089                        }
1090                        _ => {}
1091                    }
1092                }
1093            }
1094            TypeInfoSubstate::KeyValueStore(..) => {
1095                if partition_num.eq(&MAIN_BASE_PARTITION) {
1096                    descriptors.push(SystemPartitionDescriptor::KeyValueStore);
1097                }
1098            }
1099            _ => {}
1100        }
1101
1102        Ok(descriptors)
1103    }
1104
1105    pub fn field_iter(
1106        &self,
1107        node_id: &NodeId,
1108        partition_number: PartitionNumber,
1109    ) -> Box<dyn Iterator<Item = (FieldKey, Vec<u8>)> + '_> {
1110        if self.state_updates.is_some() {
1111            panic!("fields_iter with overlay not supported.");
1112        }
1113        self.substate_db
1114            .list_field_raw_values(node_id, partition_number, None::<SubstateKey>)
1115    }
1116
1117    pub fn map_iter(
1118        &self,
1119        node_id: &NodeId,
1120        partition_number: PartitionNumber,
1121    ) -> Box<dyn Iterator<Item = (MapKey, Vec<u8>)> + '_> {
1122        if self.state_updates.is_some() {
1123            panic!("map_iter with overlay not supported.");
1124        }
1125        self.substate_db
1126            .list_map_raw_values(node_id, partition_number, None::<SubstateKey>)
1127    }
1128
1129    pub fn sorted_iter(
1130        &self,
1131        node_id: &NodeId,
1132        partition_number: PartitionNumber,
1133    ) -> Box<dyn Iterator<Item = (SortedKey, Vec<u8>)> + '_> {
1134        if self.state_updates.is_some() {
1135            panic!("sorted_iter with overlay not supported.");
1136        }
1137        self.substate_db
1138            .list_sorted_raw_values(node_id, partition_number, None::<SubstateKey>)
1139    }
1140}
1141
1142struct ValidationPayloadCheckerContext<'a, S: SubstateDatabase + ?Sized> {
1143    reader: &'a SystemDatabaseReader<'a, S>,
1144    schema_origin: SchemaOrigin,
1145    allow_non_global_ref: bool,
1146    allow_ownership: bool,
1147}
1148
1149impl<'a, S: SubstateDatabase + ?Sized> ValidationContext
1150    for ValidationPayloadCheckerContext<'a, S>
1151{
1152    type Error = String;
1153
1154    fn get_node_type_info(&self, node_id: &NodeId) -> Result<TypeInfoForValidation, String> {
1155        let type_info = self
1156            .reader
1157            .get_type_info(node_id)
1158            .map_err(|_| "Type Info missing".to_string())?;
1159        let type_info_for_validation = match type_info {
1160            TypeInfoSubstate::Object(object_info) => TypeInfoForValidation::Object {
1161                package: object_info.blueprint_info.blueprint_id.package_address,
1162                blueprint: object_info.blueprint_info.blueprint_id.blueprint_name,
1163            },
1164            TypeInfoSubstate::KeyValueStore(..) => TypeInfoForValidation::KeyValueStore,
1165            TypeInfoSubstate::GlobalAddressReservation(..) => {
1166                TypeInfoForValidation::GlobalAddressReservation
1167            }
1168            TypeInfoSubstate::GlobalAddressPhantom(..) => {
1169                return Err("Found invalid stored address phantom".to_string())
1170            }
1171        };
1172
1173        Ok(type_info_for_validation)
1174    }
1175
1176    fn schema_origin(&self) -> &SchemaOrigin {
1177        &self.schema_origin
1178    }
1179
1180    fn allow_ownership(&self) -> bool {
1181        self.allow_ownership
1182    }
1183
1184    fn allow_non_global_ref(&self) -> bool {
1185        self.allow_non_global_ref
1186    }
1187}
1188
1189impl<'a, S: SubstateDatabase + ListableSubstateDatabase> SystemDatabaseReader<'a, S> {
1190    pub fn partitions_iter(&self) -> Box<dyn Iterator<Item = (NodeId, PartitionNumber)> + '_> {
1191        if self.state_updates.is_some() {
1192            panic!("partitions_iter with overlay not supported.");
1193        }
1194
1195        self.substate_db.read_partition_keys()
1196    }
1197}
1198
1199pub struct SystemDatabaseWriter<'a, S: SubstateDatabase + CommittableSubstateDatabase> {
1200    substate_db: &'a mut S,
1201}
1202
1203impl<'a, S: SubstateDatabase + CommittableSubstateDatabase> SystemDatabaseWriter<'a, S> {
1204    pub fn new(substate_db: &'a mut S) -> Self {
1205        Self { substate_db }
1206    }
1207
1208    pub fn write_typed_object_field<V: ScryptoEncode>(
1209        &mut self,
1210        node_id: &NodeId,
1211        module_id: ModuleId,
1212        field_index: u8,
1213        value: V,
1214    ) -> Result<(), SystemReaderError> {
1215        let reader = SystemDatabaseReader::new(self.substate_db);
1216        let blueprint_id = reader.get_blueprint_id(node_id, module_id)?;
1217        let definition = reader.get_blueprint_definition(&blueprint_id)?;
1218        let partition_description = &definition
1219            .interface
1220            .state
1221            .fields
1222            .as_ref()
1223            .ok_or(SystemReaderError::FieldDoesNotExist)?
1224            .0;
1225        let partition_number = match partition_description {
1226            PartitionDescription::Logical(offset) => {
1227                let base_partition = match module_id {
1228                    ModuleId::Main => MAIN_BASE_PARTITION,
1229                    ModuleId::Metadata => METADATA_BASE_PARTITION,
1230                    ModuleId::Royalty => ROYALTY_BASE_PARTITION,
1231                    ModuleId::RoleAssignment => ROLE_ASSIGNMENT_BASE_PARTITION,
1232                };
1233                base_partition.at_offset(*offset).unwrap()
1234            }
1235            PartitionDescription::Physical(partition_number) => *partition_number,
1236        };
1237
1238        self.substate_db.update_substate(
1239            node_id,
1240            partition_number,
1241            SubstateKey::Field(field_index),
1242            FieldSubstate::new_field(value, LockStatus::Unlocked),
1243        );
1244
1245        Ok(())
1246    }
1247}