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
87pub 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 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 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 result
905 } else {
906 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 Some(None)
944 }
945 },
946 None => None,
947 }
948 }
949 PartitionStateUpdates::Batch(e) => match e {
950 BatchPartitionStateUpdate::Reset {
951 new_substate_values,
952 } => {
953 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
968impl<'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}