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_else(|| 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_else(|| 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_else(|| 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_else(|| 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_else(|| 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_else(|| 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 pub fn key_value_store_iter(
322 &self,
323 node_id: &NodeId,
324 from_key: Option<&MapKey>,
325 ) -> Result<Box<dyn Iterator<Item = (MapKey, Vec<u8>)> + '_>, SystemReaderError> {
326 if self.state_updates.is_some() {
327 panic!("key_value_store_iter with overlay not supported.");
328 }
329
330 match self.get_type_info(node_id)? {
331 TypeInfoSubstate::KeyValueStore(..) => {}
332 _ => return Err(SystemReaderError::NotAKeyValueStore),
333 }
334
335 let iterable = self
336 .substate_db
337 .list_map_values::<KeyValueEntrySubstate<ScryptoRawValue>>(
338 node_id,
339 MAIN_BASE_PARTITION,
340 from_key,
341 )
342 .filter_map(move |(map_key, substate)| {
343 let value = substate.into_value()?;
344 let value = scrypto_encode(&value).unwrap();
345
346 Some((map_key, value))
347 });
348
349 Ok(Box::new(iterable))
350 }
351
352 pub fn collection_iter(
353 &self,
354 node_id: &NodeId,
355 module_id: ModuleId,
356 collection_index: CollectionIndex,
357 ) -> Result<Box<dyn Iterator<Item = (SubstateKey, Vec<u8>)> + '_>, SystemReaderError> {
358 self.collection_iter_advanced(node_id, module_id, collection_index, None)
359 .map(|x| x.0)
360 }
361
362 pub fn collection_iter_advanced<'s, 'x>(
363 &'s self,
364 node_id: &'x NodeId,
365 module_id: ModuleId,
366 collection_index: CollectionIndex,
367 from_substate_key: Option<&'x SubstateKey>,
368 ) -> Result<
369 (
370 Box<dyn Iterator<Item = (SubstateKey, Vec<u8>)> + 's>,
371 PartitionNumber,
372 ),
373 SystemReaderError,
374 > {
375 if self.state_updates.is_some() {
376 panic!("collection_iter_advanced with overlay not supported.");
377 }
378
379 let blueprint_id = self.get_blueprint_id(node_id, module_id)?;
380 let definition = self.get_blueprint_definition(&blueprint_id)?;
381
382 let (partition_description, schema) = definition
383 .interface
384 .state
385 .collections
386 .get(collection_index as usize)
387 .ok_or_else(|| SystemReaderError::CollectionDoesNotExist)?
388 .clone();
389
390 let partition_number = match partition_description {
391 PartitionDescription::Physical(partition_num) => partition_num,
392 PartitionDescription::Logical(offset) => {
393 module_id.base_partition_num().at_offset(offset).unwrap()
394 }
395 };
396
397 let iterable: Box<dyn Iterator<Item = (SubstateKey, Vec<u8>)> + 's> = match schema {
398 BlueprintCollectionSchema::KeyValueStore(..) => {
399 let iterable = self
400 .substate_db
401 .list_map_values::<KeyValueEntrySubstate<ScryptoRawValue>>(
402 node_id,
403 partition_number,
404 from_substate_key,
405 )
406 .filter_map(|(map_key, substate)| {
407 Some((
408 SubstateKey::Map(map_key),
409 scrypto_encode(&substate.into_value()?).unwrap(),
410 ))
411 });
412 Box::new(iterable)
413 }
414 BlueprintCollectionSchema::Index(..) => {
415 let iterable = self
416 .substate_db
417 .list_map_values::<IndexEntrySubstate<ScryptoRawValue>>(
418 node_id,
419 partition_number,
420 from_substate_key,
421 )
422 .map(|(map_key, substate)| {
423 (
424 SubstateKey::Map(map_key),
425 scrypto_encode(&substate.into_value()).unwrap(),
426 )
427 });
428 Box::new(iterable)
429 }
430 BlueprintCollectionSchema::SortedIndex(..) => {
431 let iterable = self
432 .substate_db
433 .list_sorted_values::<SortedIndexEntrySubstate<ScryptoRawValue>>(
434 node_id,
435 partition_number,
436 from_substate_key,
437 )
438 .map(|(key, substate)| {
439 (
440 SubstateKey::Sorted(key),
441 scrypto_encode(&substate.into_value()).unwrap(),
442 )
443 });
444 Box::new(iterable)
445 }
446 };
447
448 Ok((iterable, partition_number))
449 }
450
451 pub fn get_object_info<A: Into<NodeId>>(
452 &self,
453 node_id: A,
454 ) -> Result<ObjectInfo, SystemReaderError> {
455 let type_info = self
456 .fetch_substate::<TypeInfoSubstate>(
457 &node_id.into(),
458 TYPE_INFO_FIELD_PARTITION,
459 &TypeInfoField::TypeInfo.into(),
460 )
461 .ok_or_else(|| SystemReaderError::NodeIdDoesNotExist)?;
462
463 match type_info {
464 TypeInfoSubstate::Object(object_info) => Ok(object_info),
465 _ => Err(SystemReaderError::NotAnObject),
466 }
467 }
468
469 pub fn get_blueprint_id(
470 &self,
471 node_id: &NodeId,
472 module_id: ModuleId,
473 ) -> Result<BlueprintId, SystemReaderError> {
474 let type_info = self
475 .fetch_substate::<TypeInfoSubstate>(
476 node_id,
477 TYPE_INFO_FIELD_PARTITION,
478 &TypeInfoField::TypeInfo.into(),
479 )
480 .ok_or_else(|| SystemReaderError::NodeIdDoesNotExist)?;
481
482 let object_info = match type_info {
483 TypeInfoSubstate::Object(object_info) => object_info,
484 _ => {
485 return Err(SystemReaderError::NotAnObject);
486 }
487 };
488
489 let module_id: Option<AttachedModuleId> = module_id.into();
490 if let Some(module_id) = module_id {
491 match object_info.object_type {
492 ObjectType::Global { modules } => {
493 if !modules.contains_key(&module_id) {
494 return Err(SystemReaderError::ModuleDoesNotExist);
495 }
496 }
497 ObjectType::Owned => return Err(SystemReaderError::ModuleDoesNotExist),
498 }
499
500 Ok(module_id.static_blueprint())
501 } else {
502 Ok(object_info.blueprint_info.blueprint_id)
503 }
504 }
505
506 pub fn get_blueprint_definition(
507 &self,
508 blueprint_id: &BlueprintId,
509 ) -> Result<Rc<BlueprintDefinition>, SystemReaderError> {
510 let canonical_key = CanonicalBlueprintId {
511 address: blueprint_id.package_address,
512 blueprint: blueprint_id.blueprint_name.clone(),
513 version: BlueprintVersion::default(),
514 };
515 {
516 if let Some(cache) = self.blueprint_cache.borrow().get(&canonical_key) {
517 return Ok(cache.clone());
518 }
519 }
520
521 let bp_version_key = BlueprintVersionKey::new_default(blueprint_id.blueprint_name.clone());
522 let definition = Rc::new(
523 self.fetch_substate::<PackageBlueprintVersionDefinitionEntrySubstate>(
524 blueprint_id.package_address.as_node_id(),
525 MAIN_BASE_PARTITION
526 .at_offset(PACKAGE_BLUEPRINTS_PARTITION_OFFSET)
527 .unwrap(),
528 &SubstateKey::Map(scrypto_encode(&bp_version_key).unwrap()),
529 )
530 .ok_or_else(|| SystemReaderError::BlueprintDoesNotExist)?
531 .into_value()
532 .unwrap()
533 .fully_update_and_into_latest_version(),
534 );
535
536 self.blueprint_cache
537 .borrow_mut()
538 .insert(canonical_key, definition.clone());
539
540 Ok(definition)
541 }
542
543 pub fn get_kv_store_type_target(
544 &self,
545 node_id: &NodeId,
546 ) -> Result<KVStoreTypeTarget, SystemReaderError> {
547 let type_info = self
548 .fetch_substate::<TypeInfoSubstate>(
549 node_id,
550 TYPE_INFO_FIELD_PARTITION,
551 &TypeInfoField::TypeInfo.into(),
552 )
553 .ok_or_else(|| SystemReaderError::NodeIdDoesNotExist)?;
554
555 let kv_store_info = match type_info {
556 TypeInfoSubstate::KeyValueStore(kv_store_info) => kv_store_info,
557 _ => return Err(SystemReaderError::NotAKeyValueStore),
558 };
559
560 Ok(KVStoreTypeTarget {
561 kv_store_type: kv_store_info.generic_substitutions,
562 meta: *node_id,
563 })
564 }
565
566 pub fn get_blueprint_type_target(
567 &self,
568 node_id: &NodeId,
569 module_id: ModuleId,
570 ) -> Result<BlueprintTypeTarget, SystemReaderError> {
571 let type_info = self
572 .fetch_substate::<TypeInfoSubstate>(
573 node_id,
574 TYPE_INFO_FIELD_PARTITION,
575 &TypeInfoField::TypeInfo.into(),
576 )
577 .ok_or_else(|| SystemReaderError::NodeIdDoesNotExist)?;
578
579 let object_info = match type_info {
580 TypeInfoSubstate::Object(object_info) => object_info,
581 _ => return Err(SystemReaderError::NotAnObject),
582 };
583
584 let module_id: Option<AttachedModuleId> = module_id.into();
585 let target = if let Some(module_id) = module_id {
586 let blueprint_id = module_id.static_blueprint();
587 match object_info.object_type {
588 ObjectType::Global { modules } => {
589 if !modules.contains_key(&module_id) {
590 return Err(SystemReaderError::ModuleDoesNotExist);
591 }
592 }
593 ObjectType::Owned => return Err(SystemReaderError::ModuleDoesNotExist),
594 }
595
596 let target = BlueprintTypeTarget {
597 blueprint_info: BlueprintInfo {
598 blueprint_id,
599 blueprint_version: Default::default(),
600 outer_obj_info: OuterObjectInfo::None,
601 features: Default::default(),
602 generic_substitutions: Default::default(),
603 },
604 meta: SchemaValidationMeta::ExistingObject {
605 additional_schemas: *node_id,
606 },
607 };
608 target
609 } else {
610 BlueprintTypeTarget {
611 blueprint_info: object_info.blueprint_info,
612 meta: SchemaValidationMeta::ExistingObject {
613 additional_schemas: *node_id,
614 },
615 }
616 };
617
618 Ok(target)
619 }
620
621 pub fn get_kv_store_payload_schema(
622 &self,
623 target: &KVStoreTypeTarget,
624 key_or_value: KeyOrValue,
625 ) -> Result<ResolvedPayloadSchema, SystemReaderError> {
626 let (substitution, allow_ownership, allow_non_global_refs) = match key_or_value {
627 KeyOrValue::Key => (&target.kv_store_type.key_generic_substitution, false, false),
628 KeyOrValue::Value => (
629 &target.kv_store_type.value_generic_substitution,
630 target.kv_store_type.allow_ownership,
631 false,
632 ),
633 };
634
635 match substitution {
636 GenericSubstitution::Local(local_type_id) => {
637 let schema = self.get_schema(&target.meta, &local_type_id.0)?;
638
639 Ok(ResolvedPayloadSchema {
640 schema,
641 type_id: local_type_id.1,
642 allow_ownership,
643 allow_non_global_refs,
644 schema_origin: SchemaOrigin::KeyValueStore,
645 })
646 }
647 GenericSubstitution::Remote(blueprint_type_id) => {
648 let (schema, scoped_type_id) =
649 self.get_blueprint_type_schema(&blueprint_type_id)?;
650
651 Ok(ResolvedPayloadSchema {
652 schema,
653 type_id: scoped_type_id.1,
654 allow_ownership,
655 allow_non_global_refs,
656 schema_origin: SchemaOrigin::KeyValueStore,
657 })
658 }
659 }
660 }
661
662 pub fn get_blueprint_payload_schema_pointer(
663 &self,
664 target: &BlueprintTypeTarget,
665 payload_identifier: &BlueprintPayloadIdentifier,
666 ) -> Result<ObjectSubstateTypeReference, SystemReaderError> {
667 let blueprint_interface = &self
668 .get_blueprint_definition(&target.blueprint_info.blueprint_id)?
669 .interface;
670
671 let (payload_def, ..) = blueprint_interface
672 .get_payload_def(payload_identifier)
673 .ok_or_else(|| SystemReaderError::PayloadDoesNotExist)?;
674
675 let obj_type_reference = match payload_def {
676 BlueprintPayloadDef::Static(type_identifier) => {
677 ObjectSubstateTypeReference::Package(PackageTypeReference {
678 full_type_id: type_identifier
679 .under_node(target.blueprint_info.blueprint_id.package_address),
680 })
681 }
682 BlueprintPayloadDef::Generic(instance_index) => {
683 let generic_substitution = target
684 .blueprint_info
685 .generic_substitutions
686 .get(instance_index as usize)
687 .expect("Missing generic");
688
689 let entity_address = match target.meta {
690 SchemaValidationMeta::Blueprint | SchemaValidationMeta::NewObject { .. } => {
691 return Err(SystemReaderError::TargetNotSupported)
692 }
693 SchemaValidationMeta::ExistingObject { additional_schemas } => {
694 additional_schemas
695 }
696 };
697
698 match generic_substitution {
699 GenericSubstitution::Local(type_id) => {
700 ObjectSubstateTypeReference::ObjectInstance(ObjectInstanceTypeReference {
701 instance_type_id: instance_index,
702 resolved_full_type_id: type_id.under_node(entity_address),
703 })
704 }
705 GenericSubstitution::Remote(type_id) => {
706 let (_, scoped_type_id) = self.get_blueprint_type_schema(&type_id)?;
707 ObjectSubstateTypeReference::Package(PackageTypeReference {
708 full_type_id: scoped_type_id.under_node(type_id.package_address),
709 })
710 }
711 }
712 }
713 };
714
715 Ok(obj_type_reference)
716 }
717
718 pub fn get_blueprint_payload_schema(
722 &self,
723 target: &BlueprintTypeTarget,
724 payload_identifier: &BlueprintPayloadIdentifier,
725 ) -> Result<ResolvedPayloadSchema, SystemReaderError> {
726 let blueprint_interface = &self
727 .get_blueprint_definition(&target.blueprint_info.blueprint_id)?
728 .interface;
729
730 let (payload_def, allow_ownership, allow_non_global_refs) = blueprint_interface
731 .get_payload_def(payload_identifier)
732 .ok_or_else(|| SystemReaderError::PayloadDoesNotExist)?;
733
734 let (schema, index, schema_origin) = match payload_def {
736 BlueprintPayloadDef::Static(type_identifier) => {
737 let schema = self.get_schema(
738 target
739 .blueprint_info
740 .blueprint_id
741 .package_address
742 .as_node_id(),
743 &type_identifier.0,
744 )?;
745 (
746 schema,
747 type_identifier.1,
748 SchemaOrigin::Blueprint(target.blueprint_info.blueprint_id.clone()),
749 )
750 }
751 BlueprintPayloadDef::Generic(instance_index) => {
752 let generic_substitution = target
753 .blueprint_info
754 .generic_substitutions
755 .get(instance_index as usize)
756 .expect("Missing generic substitution");
757
758 match generic_substitution {
759 GenericSubstitution::Local(type_id) => {
760 let schema = match &target.meta {
761 SchemaValidationMeta::ExistingObject { additional_schemas } => {
762 self.get_schema(additional_schemas, &type_id.0)?
763 }
764 SchemaValidationMeta::NewObject { .. }
765 | SchemaValidationMeta::Blueprint => {
766 return Err(SystemReaderError::TargetNotSupported);
767 }
768 };
769
770 (schema, type_id.1, SchemaOrigin::Instance)
771 }
772 GenericSubstitution::Remote(type_id) => {
773 let (schema, scoped_type_id) = self.get_blueprint_type_schema(&type_id)?;
774 (
775 schema,
776 scoped_type_id.1,
777 SchemaOrigin::Blueprint(BlueprintId::new(
778 &type_id.package_address,
779 type_id.blueprint_name.clone(),
780 )),
781 )
782 }
783 }
784 }
785 };
786
787 Ok(ResolvedPayloadSchema {
788 schema,
789 type_id: index,
790 allow_ownership,
791 allow_non_global_refs,
792 schema_origin,
793 })
794 }
795
796 pub fn get_schema(
797 &self,
798 node_id: &NodeId,
799 schema_hash: &SchemaHash,
800 ) -> Result<Rc<VersionedScryptoSchema>, SystemReaderError> {
801 {
802 if let Some(cache) = self.schema_cache.borrow().get(schema_hash) {
803 return Ok(cache.clone());
804 }
805 }
806
807 let schema = Rc::new(
808 self.fetch_substate::<KeyValueEntrySubstate<VersionedScryptoSchema>>(
809 node_id,
810 SCHEMAS_PARTITION,
811 &SubstateKey::Map(scrypto_encode(schema_hash).unwrap()),
812 )
813 .ok_or_else(|| SystemReaderError::SchemaDoesNotExist)?
814 .into_value()
815 .expect("Schema should exist if substate exists"),
816 );
817
818 self.schema_cache
819 .borrow_mut()
820 .insert(schema_hash.clone(), schema.clone());
821
822 Ok(schema)
823 }
824
825 pub fn get_blueprint_type_schema(
826 &self,
827 type_id: &BlueprintTypeIdentifier,
828 ) -> Result<(Rc<VersionedScryptoSchema>, ScopedTypeId), SystemReaderError> {
829 let BlueprintTypeIdentifier {
830 package_address,
831 blueprint_name,
832 type_name,
833 } = type_id.clone();
834 let definition = self.get_blueprint_payload_def(&BlueprintId {
835 package_address,
836 blueprint_name,
837 })?;
838 let scoped_type_id = definition
839 .interface
840 .types
841 .get(&type_name)
842 .ok_or(SystemReaderError::BlueprintTypeNotFound(type_name.clone()))?;
843 Ok((
844 self.get_schema(package_address.as_node_id(), &scoped_type_id.0)?,
845 scoped_type_id.clone(),
846 ))
847 }
848
849 pub fn get_blueprint_payload_def(
850 &self,
851 blueprint_id: &BlueprintId,
852 ) -> Result<BlueprintDefinition, SystemReaderError> {
853 let bp_version_key = BlueprintVersionKey::new_default(blueprint_id.blueprint_name.clone());
854 let definition = self
855 .fetch_substate::<PackageBlueprintVersionDefinitionEntrySubstate>(
856 blueprint_id.package_address.as_node_id(),
857 MAIN_BASE_PARTITION
858 .at_offset(PACKAGE_BLUEPRINTS_PARTITION_OFFSET)
859 .unwrap(),
860 &SubstateKey::Map(scrypto_encode(&bp_version_key).unwrap()),
861 )
862 .ok_or_else(|| SystemReaderError::BlueprintDoesNotExist)?;
863
864 Ok(definition
865 .into_value()
866 .unwrap()
867 .fully_update_and_into_latest_version())
868 }
869
870 pub fn validate_payload<'b>(
871 &'b self,
872 payload: &[u8],
873 payload_schema: &'b ResolvedPayloadSchema,
874 depth_limit: usize,
875 ) -> Result<(), LocatedValidationError<ScryptoCustomExtension>> {
876 let validation_context: Box<dyn ValidationContext<Error = String>> =
877 Box::new(ValidationPayloadCheckerContext {
878 reader: self,
879 schema_origin: payload_schema.schema_origin.clone(),
880 allow_ownership: payload_schema.allow_ownership,
881 allow_non_global_ref: payload_schema.allow_non_global_refs,
882 });
883
884 validate_payload_against_schema::<ScryptoCustomExtension, _>(
885 payload,
886 payload_schema.schema.v1(),
887 payload_schema.type_id,
888 &validation_context,
889 depth_limit,
890 )
891 }
892
893 pub fn fetch_substate<D: ScryptoDecode>(
894 &self,
895 node_id: &NodeId,
896 partition_num: PartitionNumber,
897 key: &SubstateKey,
898 ) -> Option<D> {
899 if let Some(result) =
900 self.fetch_substate_from_state_updates::<D>(node_id, partition_num, key)
901 {
902 result
904 } else {
905 self.fetch_substate_from_database::<D>(node_id, partition_num, key)
907 }
908 }
909
910 pub fn fetch_substate_from_database<D: ScryptoDecode>(
911 &self,
912 node_id: &NodeId,
913 partition_num: PartitionNumber,
914 key: &SubstateKey,
915 ) -> Option<D> {
916 self.substate_db
917 .get_substate::<D>(node_id, partition_num, key)
918 }
919
920 pub fn fetch_substate_from_state_updates<D: ScryptoDecode>(
921 &self,
922 node_id: &NodeId,
923 partition_num: PartitionNumber,
924 substate_key: &SubstateKey,
925 ) -> Option<Option<D>> {
926 if let Some(updates) = self.state_updates {
927 updates
928 .by_node
929 .get(node_id)
930 .and_then(|node_updates| match node_updates {
931 NodeStateUpdates::Delta { by_partition } => by_partition.get(&partition_num),
932 })
933 .and_then(|partition_updates| match partition_updates {
934 PartitionStateUpdates::Delta { by_substate } => {
935 match by_substate.get(substate_key) {
936 Some(e) => match e {
937 DatabaseUpdate::Set(value) => {
938 Some(Some(scrypto_decode(value).unwrap()))
939 }
940 DatabaseUpdate::Delete => {
941 Some(None)
943 }
944 },
945 None => None,
946 }
947 }
948 PartitionStateUpdates::Batch(e) => match e {
949 BatchPartitionStateUpdate::Reset {
950 new_substate_values,
951 } => {
952 Some(
954 new_substate_values
955 .get(substate_key)
956 .map(|value| scrypto_decode(value).unwrap()),
957 )
958 }
959 },
960 })
961 } else {
962 None
963 }
964 }
965}
966
967impl<'a, S: SubstateDatabase> SystemDatabaseReader<'a, S> {
969 pub fn get_partition_descriptors(
970 &self,
971 node_id: &NodeId,
972 partition_num: &PartitionNumber,
973 ) -> Result<Vec<SystemPartitionDescriptor>, SystemReaderError> {
974 let mut descriptors = Vec::new();
975
976 if partition_num.eq(&BOOT_LOADER_PARTITION) {
977 descriptors.push(SystemPartitionDescriptor::BootLoader);
978 }
979
980 if partition_num.eq(&PROTOCOL_UPDATE_STATUS_PARTITION) {
981 descriptors.push(SystemPartitionDescriptor::ProtocolUpdateStatus);
982 }
983
984 if partition_num.eq(&TYPE_INFO_FIELD_PARTITION) {
985 descriptors.push(SystemPartitionDescriptor::TypeInfo);
986 }
987
988 if partition_num.eq(&SCHEMAS_PARTITION) {
989 descriptors.push(SystemPartitionDescriptor::Schema);
990 }
991
992 let type_info = self.get_type_info(node_id)?;
993
994 match type_info {
995 TypeInfoSubstate::Object(object_info) => {
996 let (module_id, partition_offset) = if partition_num.ge(&MAIN_BASE_PARTITION) {
997 let partition_offset = PartitionOffset(partition_num.0 - MAIN_BASE_PARTITION.0);
998 (ModuleId::Main, Some(partition_offset))
999 } else {
1000 match object_info.object_type {
1001 ObjectType::Global { modules } => {
1002 if partition_num.ge(&ROLE_ASSIGNMENT_BASE_PARTITION) {
1003 if modules.contains_key(&AttachedModuleId::RoleAssignment) {
1004 let partition_offset = PartitionOffset(
1005 partition_num.0 - ROLE_ASSIGNMENT_BASE_PARTITION.0,
1006 );
1007 (ModuleId::RoleAssignment, Some(partition_offset))
1008 } else {
1009 (ModuleId::Main, None)
1010 }
1011 } else if partition_num.ge(&ROYALTY_BASE_PARTITION) {
1012 if modules.contains_key(&AttachedModuleId::Royalty) {
1013 let partition_offset =
1014 PartitionOffset(partition_num.0 - ROYALTY_BASE_PARTITION.0);
1015 (ModuleId::Royalty, Some(partition_offset))
1016 } else {
1017 (ModuleId::Main, None)
1018 }
1019 } else if partition_num.ge(&METADATA_BASE_PARTITION) {
1020 if modules.contains_key(&AttachedModuleId::Metadata) {
1021 let partition_offset = PartitionOffset(
1022 partition_num.0 - METADATA_BASE_PARTITION.0,
1023 );
1024 (ModuleId::Metadata, Some(partition_offset))
1025 } else {
1026 (ModuleId::Main, None)
1027 }
1028 } else {
1029 (ModuleId::Main, None)
1030 }
1031 }
1032 ObjectType::Owned => (ModuleId::Main, None),
1033 }
1034 };
1035
1036 let blueprint_id = match module_id {
1037 ModuleId::Main => object_info.blueprint_info.blueprint_id,
1038 _ => module_id.static_blueprint().unwrap(),
1039 };
1040
1041 let definition = self.get_blueprint_definition(&blueprint_id).unwrap();
1042
1043 let state_schema = &definition.interface.state;
1044 match (&state_schema.fields, &partition_offset) {
1045 (
1046 Some((PartitionDescription::Logical(offset), _fields)),
1047 Some(partition_offset),
1048 ) => {
1049 if offset.eq(partition_offset) {
1050 descriptors.push(SystemPartitionDescriptor::Object(
1051 module_id,
1052 ObjectPartitionDescriptor::Fields,
1053 ));
1054 }
1055 }
1056 _ => {}
1057 }
1058
1059 for (index, (partition_description, schema)) in
1060 state_schema.collections.iter().enumerate()
1061 {
1062 let partition_descriptor = match schema {
1063 BlueprintCollectionSchema::KeyValueStore(..) => {
1064 ObjectPartitionDescriptor::KeyValueCollection(index as u8)
1065 }
1066 BlueprintCollectionSchema::Index(..) => {
1067 ObjectPartitionDescriptor::IndexCollection(index as u8)
1068 }
1069 BlueprintCollectionSchema::SortedIndex(..) => {
1070 ObjectPartitionDescriptor::SortedIndexCollection(index as u8)
1071 }
1072 };
1073
1074 match (partition_description, &partition_offset) {
1075 (PartitionDescription::Logical(offset), Some(partition_offset))
1076 if offset.eq(partition_offset) =>
1077 {
1078 descriptors.push(SystemPartitionDescriptor::Object(
1079 module_id,
1080 partition_descriptor,
1081 ))
1082 }
1083 (PartitionDescription::Physical(physical_partition), None)
1084 if physical_partition.eq(&partition_num) =>
1085 {
1086 descriptors.push(SystemPartitionDescriptor::Object(
1087 module_id,
1088 partition_descriptor,
1089 ))
1090 }
1091 _ => {}
1092 }
1093 }
1094 }
1095 TypeInfoSubstate::KeyValueStore(..) => {
1096 if partition_num.eq(&MAIN_BASE_PARTITION) {
1097 descriptors.push(SystemPartitionDescriptor::KeyValueStore);
1098 }
1099 }
1100 _ => {}
1101 }
1102
1103 Ok(descriptors)
1104 }
1105
1106 pub fn field_iter(
1107 &self,
1108 node_id: &NodeId,
1109 partition_number: PartitionNumber,
1110 ) -> Box<dyn Iterator<Item = (FieldKey, Vec<u8>)> + '_> {
1111 if self.state_updates.is_some() {
1112 panic!("fields_iter with overlay not supported.");
1113 }
1114 self.substate_db
1115 .list_field_raw_values(node_id, partition_number, None::<SubstateKey>)
1116 }
1117
1118 pub fn map_iter(
1119 &self,
1120 node_id: &NodeId,
1121 partition_number: PartitionNumber,
1122 ) -> Box<dyn Iterator<Item = (MapKey, Vec<u8>)> + '_> {
1123 if self.state_updates.is_some() {
1124 panic!("map_iter with overlay not supported.");
1125 }
1126 self.substate_db
1127 .list_map_raw_values(node_id, partition_number, None::<SubstateKey>)
1128 }
1129
1130 pub fn sorted_iter(
1131 &self,
1132 node_id: &NodeId,
1133 partition_number: PartitionNumber,
1134 ) -> Box<dyn Iterator<Item = (SortedKey, Vec<u8>)> + '_> {
1135 if self.state_updates.is_some() {
1136 panic!("sorted_iter with overlay not supported.");
1137 }
1138 self.substate_db
1139 .list_sorted_raw_values(node_id, partition_number, None::<SubstateKey>)
1140 }
1141}
1142
1143struct ValidationPayloadCheckerContext<'a, S: SubstateDatabase + ?Sized> {
1144 reader: &'a SystemDatabaseReader<'a, S>,
1145 schema_origin: SchemaOrigin,
1146 allow_non_global_ref: bool,
1147 allow_ownership: bool,
1148}
1149
1150impl<'a, S: SubstateDatabase + ?Sized> ValidationContext
1151 for ValidationPayloadCheckerContext<'a, S>
1152{
1153 type Error = String;
1154
1155 fn get_node_type_info(&self, node_id: &NodeId) -> Result<TypeInfoForValidation, String> {
1156 let type_info = self
1157 .reader
1158 .get_type_info(node_id)
1159 .map_err(|_| "Type Info missing".to_string())?;
1160 let type_info_for_validation = match type_info {
1161 TypeInfoSubstate::Object(object_info) => TypeInfoForValidation::Object {
1162 package: object_info.blueprint_info.blueprint_id.package_address,
1163 blueprint: object_info.blueprint_info.blueprint_id.blueprint_name,
1164 },
1165 TypeInfoSubstate::KeyValueStore(..) => TypeInfoForValidation::KeyValueStore,
1166 TypeInfoSubstate::GlobalAddressReservation(..) => {
1167 TypeInfoForValidation::GlobalAddressReservation
1168 }
1169 TypeInfoSubstate::GlobalAddressPhantom(..) => {
1170 return Err("Found invalid stored address phantom".to_string())
1171 }
1172 };
1173
1174 Ok(type_info_for_validation)
1175 }
1176
1177 fn schema_origin(&self) -> &SchemaOrigin {
1178 &self.schema_origin
1179 }
1180
1181 fn allow_ownership(&self) -> bool {
1182 self.allow_ownership
1183 }
1184
1185 fn allow_non_global_ref(&self) -> bool {
1186 self.allow_non_global_ref
1187 }
1188}
1189
1190impl<'a, S: SubstateDatabase + ListableSubstateDatabase> SystemDatabaseReader<'a, S> {
1191 pub fn partitions_iter(&self) -> Box<dyn Iterator<Item = (NodeId, PartitionNumber)> + '_> {
1192 if self.state_updates.is_some() {
1193 panic!("partitions_iter with overlay not supported.");
1194 }
1195
1196 self.substate_db.read_partition_keys()
1197 }
1198}
1199
1200pub struct SystemDatabaseWriter<'a, S: SubstateDatabase + CommittableSubstateDatabase> {
1201 substate_db: &'a mut S,
1202}
1203
1204impl<'a, S: SubstateDatabase + CommittableSubstateDatabase> SystemDatabaseWriter<'a, S> {
1205 pub fn new(substate_db: &'a mut S) -> Self {
1206 Self { substate_db }
1207 }
1208
1209 pub fn write_typed_object_field<V: ScryptoEncode>(
1210 &mut self,
1211 node_id: &NodeId,
1212 module_id: ModuleId,
1213 field_index: u8,
1214 value: V,
1215 ) -> Result<(), SystemReaderError> {
1216 let reader = SystemDatabaseReader::new(self.substate_db);
1217 let blueprint_id = reader.get_blueprint_id(node_id, module_id)?;
1218 let definition = reader.get_blueprint_definition(&blueprint_id)?;
1219 let partition_description = &definition
1220 .interface
1221 .state
1222 .fields
1223 .as_ref()
1224 .ok_or_else(|| SystemReaderError::FieldDoesNotExist)?
1225 .0;
1226 let partition_number = match partition_description {
1227 PartitionDescription::Logical(offset) => {
1228 let base_partition = match module_id {
1229 ModuleId::Main => MAIN_BASE_PARTITION,
1230 ModuleId::Metadata => METADATA_BASE_PARTITION,
1231 ModuleId::Royalty => ROYALTY_BASE_PARTITION,
1232 ModuleId::RoleAssignment => ROLE_ASSIGNMENT_BASE_PARTITION,
1233 };
1234 base_partition.at_offset(*offset).unwrap()
1235 }
1236 PartitionDescription::Physical(partition_number) => *partition_number,
1237 };
1238
1239 self.substate_db.update_substate(
1240 node_id,
1241 partition_number,
1242 SubstateKey::Field(field_index),
1243 FieldSubstate::new_field(value, LockStatus::Unlocked),
1244 );
1245
1246 Ok(())
1247 }
1248}