radix_engine_interface/blueprints/package/
substates.rs

1use crate::blueprints::package::BlueprintType;
2use crate::internal_prelude::*;
3use radix_blueprint_schema_init::*;
4use strum::FromRepr;
5
6#[derive(FromRepr)]
7#[repr(u64)]
8pub enum NativeCodeId {
9    /// * Introduced: Babylon
10    /// * Coverage  : All package blueprints
11    /// * Changes   : N/A
12    PackageCode1 = 0u64,
13
14    /// * Introduced: Bottlenose
15    /// * Coverage  : All package blueprints
16    /// * Changes   : Add limit to reserved role key
17    PackageCode2 = 22u64,
18
19    /// * Introduced: Babylon
20    /// * Coverage  : All resource blueprints
21    /// * Changes   : N/A
22    ResourceCode1 = 1u64,
23
24    /// * Introduced: Cuttlefish
25    /// * Coverage  : `WORKTOP_ASSERT_RESOURCES_INCLUDE_IDENT` and `WORKTOP_ASSERT_RESOURCES_ONLY_IDENT`
26    /// * Changes   : Add assert resources methods to the worktop blueprint
27    ResourceCode2 = 26u64,
28
29    /// * Introduced: Babylon
30    /// * Coverage  : All identity blueprints
31    /// * Changes   : N/A
32    IdentityCode1 = 2u64,
33
34    /// * Introduced: Bottlenose
35    /// * Coverage  : `IDENTITY_CREATE_ADVANCED_IDENT`, `IDENTITY_CREATE_IDENT` and `IDENTITY_ON_VIRTUALIZE_EXPORT_NAME`
36    /// * Changes   : Do not create royalty module
37    IdentityCode2 = 25u64,
38
39    /// * Introduced: Babylon
40    /// * Coverage  : All consensus manager blueprints
41    /// * Changes   : N/A
42    ConsensusManagerCode1 = 3u64,
43
44    /// * Introduced: Anemone
45    /// * Coverage  : `CONSENSUS_MANAGER_GET_CURRENT_TIME_IDENT` and `CONSENSUS_MANAGER_COMPARE_CURRENT_TIME_IDENT`
46    /// * Changes   : Add second precision time
47    ConsensusManagerCode2 = 16u64,
48
49    /// * Introduced: Babylon
50    /// * Coverage  : All account blueprints
51    /// * Changes   : N/A
52    AccountCode1 = 5u64,
53
54    /// * Introduced: Bottlenose
55    /// * Coverage  : `ACCOUNT_TRY_DEPOSIT_OR_REFUND_IDENT` and `ACCOUNT_TRY_DEPOSIT_BATCH_OR_REFUND_IDENT`
56    /// * Changes   : Update `try_deposit` logic
57    AccountCode2 = 20u64,
58
59    /// * Introduced: Cuttlefish
60    /// * Coverage  : Account getters
61    /// * Changes   : Adds getters to the account blueprint
62    AccountCode3 = 24u64,
63
64    /// * Introduced: Babylon
65    /// * Coverage  : All access controller blueprints
66    /// * Changes   : N/A
67    AccessControllerCode1 = 6u64,
68
69    /// * Introduced: Babylon
70    /// * Coverage  : All access controller blueprints
71    /// * Changes   : Introduce fee vault
72    AccessControllerCode2 = 23u64,
73
74    /// * Introduced: Babylon
75    /// * Coverage  : All pool blueprints
76    /// * Changes   : N/A
77    PoolCode1 = 13u64,
78
79    /// * Introduced: Anemone
80    /// * Coverage  : All pool blueprints
81    /// * Changes   : Update pools to use precise decimal
82    PoolCode2 = 17u64,
83
84    /// * Introduced: Babylon
85    /// * Coverage  : All transaction tracker blueprints
86    /// * Changes   : N/A
87    TransactionTrackerCode1 = 14u64,
88
89    /// * Introduced: Babylon
90    /// * Coverage  : All test blueprints
91    /// * Changes   : N/A
92    TestUtilsCode1 = 15u64,
93
94    /// * Introduced: Bottlenose
95    /// * Coverage  : All locker blueprints
96    /// * Changes   : N/A
97    LockerCode1 = 19u64,
98
99    /// * Introduced: Babylon
100    /// * Coverage  : All transaction processor blueprints
101    /// * Changes   : N/A
102    TransactionProcessorCode1 = 7u64,
103
104    /// * Introduced: Bottlenose
105    /// * Coverage  : All transaction processor blueprints
106    /// * Changes   : Add blob limits
107    TransactionProcessorCode2 = 21u64,
108
109    /// * Introduced: Babylon
110    /// * Coverage  : All metadata object module blueprints
111    /// * Changes   : N/A
112    MetadataCode1 = 10u64,
113
114    /// * Introduced: Babylon
115    /// * Coverage  : All royalty object module blueprints
116    /// * Changes   : N/A
117    RoyaltyCode1 = 11u64,
118
119    /// * Introduced: Babylon
120    /// * Coverage  : All role assignment object module blueprints
121    /// * Changes   : N/A
122    RoleAssignmentCode1 = 12u64,
123
124    /// * Introduced: Bottlenose
125    /// * Coverage  : `ROLE_ASSIGNMENT_GET_OWNER_ROLE_IDENT`
126    /// * Changes   : Add `get_owner_role``
127    RoleAssignmentCode2 = 18u64,
128}
129
130pub const PACKAGE_FIELDS_PARTITION_OFFSET: PartitionOffset = PartitionOffset(0u8);
131pub const PACKAGE_BLUEPRINTS_PARTITION_OFFSET: PartitionOffset = PartitionOffset(1u8);
132pub const PACKAGE_BLUEPRINT_DEPENDENCIES_PARTITION_OFFSET: PartitionOffset = PartitionOffset(2u8);
133// There is no partition offset for the package schema collection as it is directly mapped to SCHEMAS_PARTITION
134pub const PACKAGE_ROYALTY_PARTITION_OFFSET: PartitionOffset = PartitionOffset(3u8);
135pub const PACKAGE_AUTH_TEMPLATE_PARTITION_OFFSET: PartitionOffset = PartitionOffset(4u8);
136pub const PACKAGE_VM_TYPE_PARTITION_OFFSET: PartitionOffset = PartitionOffset(5u8);
137pub const PACKAGE_ORIGINAL_CODE_PARTITION_OFFSET: PartitionOffset = PartitionOffset(6u8);
138pub const PACKAGE_INSTRUMENTED_CODE_PARTITION_OFFSET: PartitionOffset = PartitionOffset(7u8);
139
140define_wrapped_hash!(
141    /// Represents a particular instance of code under a package
142    CodeHash
143);
144
145#[derive(Copy, Debug, Clone, PartialEq, Eq, Sbor)]
146pub enum VmType {
147    Native,
148    ScryptoV1,
149}
150
151#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Sbor)]
152pub enum BlueprintPayloadDef {
153    Static(ScopedTypeId), // Fully Resolved type is defined in package
154    Generic(u8),          // Fully Resolved type is mapped directly to a generic defined by instance
155                          // TODO: How to represent a structure containing a generic?
156}
157
158impl BlueprintPayloadDef {
159    pub fn from_type_ref(type_ref: TypeRef<LocalTypeId>, schema_hash: SchemaHash) -> Self {
160        match type_ref {
161            TypeRef::Static(type_id) => {
162                BlueprintPayloadDef::Static(ScopedTypeId(schema_hash, type_id))
163            }
164            TypeRef::Generic(index) => BlueprintPayloadDef::Generic(index),
165        }
166    }
167}
168
169#[derive(Debug, Clone, PartialEq, Eq, Sbor)]
170pub struct FunctionSchema {
171    pub receiver: Option<ReceiverInfo>,
172    pub input: BlueprintPayloadDef,
173    pub output: BlueprintPayloadDef,
174}
175
176#[derive(Debug, Clone, Copy, PartialEq, Eq, ScryptoSbor, Ord, PartialOrd, Hash)]
177pub struct BlueprintVersion {
178    pub major: u32,
179    pub minor: u32,
180    pub patch: u32,
181}
182
183impl Default for BlueprintVersion {
184    fn default() -> Self {
185        Self {
186            major: 1,
187            minor: 0,
188            patch: 0,
189        }
190    }
191}
192
193#[derive(Debug, Clone, PartialEq, Eq, ScryptoSbor, Ord, PartialOrd, Hash)]
194pub struct CanonicalBlueprintId {
195    pub address: PackageAddress,
196    pub blueprint: String,
197    pub version: BlueprintVersion,
198}
199
200#[derive(Debug, Clone, PartialEq, Eq, ScryptoSbor, Ord, PartialOrd, Hash)]
201pub struct BlueprintVersionKey {
202    pub blueprint: String,
203    pub version: BlueprintVersion,
204}
205
206impl BlueprintVersionKey {
207    pub fn new_default<S: ToString>(blueprint: S) -> Self {
208        Self {
209            blueprint: blueprint.to_string(),
210            version: BlueprintVersion::default(),
211        }
212    }
213}
214
215#[derive(Debug, Clone, PartialEq, Eq, ScryptoSbor)]
216#[sbor(transparent)]
217pub struct BlueprintDependencies {
218    pub dependencies: IndexSet<GlobalAddress>,
219}
220
221#[derive(Debug, Clone, PartialEq, Eq, ScryptoSbor)]
222pub struct PackageExport {
223    pub code_hash: CodeHash,
224    pub export_name: String,
225}
226
227#[derive(Debug, Clone, PartialEq, Eq, ScryptoSbor)]
228pub struct BlueprintDefinition {
229    // Frontend interface, this must be backward compatible with minor version updates
230    pub interface: BlueprintInterface,
231
232    // Backend implementation pointers
233
234    // There is an implicit invariant that must be maintained in that the key set in `function_exports`
235    // matches that of the `functions` under `interface`. This is currently maintained since the
236    // `publish` interface uses `BlueprintDefinitionInit` rather than `BlueprintDefinition`.
237    pub function_exports: IndexMap<String, PackageExport>,
238    pub hook_exports: IndexMap<BlueprintHook, PackageExport>,
239}
240
241#[derive(Debug, Clone, PartialEq, Eq, ScryptoSbor)]
242pub enum KeyOrValue {
243    Key,
244    Value,
245}
246
247#[derive(Debug, Clone, PartialEq, Eq, ScryptoSbor)]
248pub enum InputOrOutput {
249    Input,
250    Output,
251}
252
253#[derive(Debug, Clone, PartialEq, Eq, ScryptoSbor)]
254pub enum BlueprintPayloadIdentifier {
255    Function(String, InputOrOutput),
256    Event(String),
257    Field(u8),
258    KeyValueEntry(u8, KeyOrValue),
259    IndexEntry(u8, KeyOrValue),
260    SortedIndexEntry(u8, KeyOrValue),
261}
262
263#[derive(Debug, Clone, PartialEq, Eq, ScryptoSbor)]
264pub enum BlueprintPartitionType {
265    KeyValueCollection,
266    IndexCollection,
267    SortedIndexCollection,
268}
269
270#[derive(Debug, Clone, PartialEq, Eq, ScryptoSbor, ManifestSbor)]
271pub struct BlueprintInterface {
272    pub blueprint_type: BlueprintType,
273    pub is_transient: bool,
274    pub generics: Vec<GenericBound>,
275    pub feature_set: IndexSet<String>,
276    pub state: IndexedStateSchema,
277    pub functions: IndexMap<String, FunctionSchema>,
278    pub events: IndexMap<String, BlueprintPayloadDef>,
279    pub types: IndexMap<String, ScopedTypeId>,
280}
281
282impl BlueprintInterface {
283    pub fn get_field_payload_def(&self, field_index: u8) -> Option<BlueprintPayloadDef> {
284        self.state.get_field_payload_def(field_index)
285    }
286
287    pub fn get_kv_key_payload_def(&self, collection_index: u8) -> Option<BlueprintPayloadDef> {
288        self.state.get_kv_key_payload_def(collection_index)
289    }
290
291    pub fn find_function(&self, ident: &str) -> Option<FunctionSchema> {
292        if let Some(x) = self.functions.get(ident) {
293            if x.receiver.is_none() {
294                return Some(x.clone());
295            }
296        }
297        None
298    }
299
300    pub fn find_method(&self, ident: &str) -> Option<FunctionSchema> {
301        if let Some(x) = self.functions.get(ident) {
302            if x.receiver.is_some() {
303                return Some(x.clone());
304            }
305        }
306        None
307    }
308
309    pub fn get_function_input_payload_def(&self, ident: &str) -> Option<BlueprintPayloadDef> {
310        let schema = self.functions.get(ident)?;
311        Some(schema.input.clone())
312    }
313
314    pub fn get_function_output_payload_def(&self, ident: &str) -> Option<BlueprintPayloadDef> {
315        let schema = self.functions.get(ident)?;
316        Some(schema.output.clone())
317    }
318
319    pub fn get_event_payload_def(&self, event_name: &str) -> Option<BlueprintPayloadDef> {
320        self.events.get(event_name).cloned()
321    }
322
323    pub fn get_payload_def(
324        &self,
325        payload_identifier: &BlueprintPayloadIdentifier,
326    ) -> Option<(BlueprintPayloadDef, bool, bool)> {
327        match payload_identifier {
328            BlueprintPayloadIdentifier::Function(function_ident, InputOrOutput::Input) => {
329                let payload_def = self.get_function_input_payload_def(function_ident.as_str())?;
330                Some((payload_def, true, true))
331            }
332            BlueprintPayloadIdentifier::Function(function_ident, InputOrOutput::Output) => {
333                let payload_def = self.get_function_output_payload_def(function_ident.as_str())?;
334                Some((payload_def, true, true))
335            }
336            BlueprintPayloadIdentifier::Field(field_index) => {
337                let payload_def = self.get_field_payload_def(*field_index)?;
338                Some((payload_def, true, self.is_transient))
339            }
340            BlueprintPayloadIdentifier::KeyValueEntry(collection_index, KeyOrValue::Key) => {
341                let payload_def = self.get_kv_key_payload_def(*collection_index)?;
342                Some((payload_def, false, self.is_transient))
343            }
344            BlueprintPayloadIdentifier::KeyValueEntry(collection_index, KeyOrValue::Value) => {
345                let (payload_def, allow_ownership) =
346                    self.state.get_kv_value_payload_def(*collection_index)?;
347                Some((payload_def, allow_ownership, self.is_transient))
348            }
349            BlueprintPayloadIdentifier::IndexEntry(collection_index, KeyOrValue::Key) => {
350                let type_pointer = self.state.get_index_payload_def_key(*collection_index)?;
351                Some((type_pointer, false, self.is_transient))
352            }
353            BlueprintPayloadIdentifier::IndexEntry(collection_index, KeyOrValue::Value) => {
354                let type_pointer = self.state.get_index_payload_def_value(*collection_index)?;
355                Some((type_pointer, false, self.is_transient))
356            }
357            BlueprintPayloadIdentifier::SortedIndexEntry(collection_index, KeyOrValue::Key) => {
358                let type_pointer = self
359                    .state
360                    .get_sorted_index_payload_def_key(*collection_index)?;
361                Some((type_pointer, false, self.is_transient))
362            }
363            BlueprintPayloadIdentifier::SortedIndexEntry(collection_index, KeyOrValue::Value) => {
364                let type_pointer = self
365                    .state
366                    .get_sorted_index_payload_def_value(*collection_index)?;
367                Some((type_pointer, false, self.is_transient))
368            }
369            BlueprintPayloadIdentifier::Event(event_name) => {
370                let type_pointer = self.get_event_payload_def(event_name.as_str())?;
371                Some((type_pointer, false, false))
372            }
373        }
374    }
375}
376
377#[derive(Debug, Copy, Clone, PartialEq, Eq)]
378pub enum SystemInstruction {
379    MapCollectionToPhysicalPartition {
380        collection_index: u8,
381        partition_num: PartitionNumber,
382    },
383}
384
385#[derive(Debug, Copy, Clone, PartialEq, Eq, ScryptoSbor, ManifestSbor, PartialOrd, Ord, Hash)]
386pub enum PartitionDescription {
387    Logical(PartitionOffset),
388    Physical(PartitionNumber),
389}
390
391#[derive(Debug, Clone, PartialEq, Eq, ScryptoSbor, ManifestSbor)]
392pub struct IndexedStateSchema {
393    pub fields: Option<(PartitionDescription, Vec<FieldSchema<BlueprintPayloadDef>>)>,
394    pub collections: Vec<(
395        PartitionDescription,
396        BlueprintCollectionSchema<BlueprintPayloadDef>,
397    )>,
398    pub num_logical_partitions: u8,
399}
400
401impl IndexedStateSchema {
402    pub fn from_schema(
403        schema_hash: SchemaHash,
404        schema: BlueprintStateSchemaInit,
405        system_mappings: IndexMap<usize, PartitionNumber>,
406    ) -> Self {
407        let mut partition_offset = 0u8;
408
409        let mut fields = None;
410        if !schema.fields.is_empty() {
411            let schema_fields = schema
412                .fields
413                .into_iter()
414                .map(|field_schema| FieldSchema {
415                    field: BlueprintPayloadDef::from_type_ref(field_schema.field, schema_hash),
416                    condition: field_schema.condition,
417                    transience: field_schema.transience,
418                })
419                .collect();
420            fields = Some((
421                PartitionDescription::Logical(PartitionOffset(partition_offset)),
422                schema_fields,
423            ));
424            partition_offset += 1;
425        };
426
427        let mut collections = Vec::new();
428        for (collection_index, collection_schema) in schema.collections.into_iter().enumerate() {
429            let schema = collection_schema
430                .map(|type_ref| BlueprintPayloadDef::from_type_ref(type_ref, schema_hash));
431
432            if let Some(partition_num) = system_mappings.get(&collection_index) {
433                collections.push((PartitionDescription::Physical(*partition_num), schema));
434            } else {
435                collections.push((
436                    PartitionDescription::Logical(PartitionOffset(partition_offset)),
437                    schema,
438                ));
439                partition_offset += 1;
440            }
441        }
442
443        Self {
444            fields,
445            collections,
446            num_logical_partitions: partition_offset,
447        }
448    }
449
450    pub fn num_logical_partitions(&self) -> u8 {
451        self.num_logical_partitions
452    }
453
454    pub fn num_fields(&self) -> usize {
455        match &self.fields {
456            Some((_, indices)) => indices.len(),
457            _ => 0usize,
458        }
459    }
460
461    pub fn get_partition(
462        &self,
463        collection_index: u8,
464    ) -> Option<(PartitionDescription, BlueprintPartitionType)> {
465        self.collections
466            .get(collection_index as usize)
467            .map(|(partition, schema)| {
468                let partition_type = match schema {
469                    BlueprintCollectionSchema::KeyValueStore(..) => {
470                        BlueprintPartitionType::KeyValueCollection
471                    }
472                    BlueprintCollectionSchema::Index(..) => BlueprintPartitionType::IndexCollection,
473                    BlueprintCollectionSchema::SortedIndex(..) => {
474                        BlueprintPartitionType::SortedIndexCollection
475                    }
476                };
477                (*partition, partition_type)
478            })
479    }
480
481    pub fn get_field_payload_def(&self, field_index: u8) -> Option<BlueprintPayloadDef> {
482        let (_partition, fields) = self.fields.clone()?;
483        let field_schema = fields.get(field_index.clone() as usize)?;
484        Some(field_schema.field.clone())
485    }
486
487    pub fn get_kv_key_payload_def(&self, collection_index: u8) -> Option<BlueprintPayloadDef> {
488        let (_partition, schema) = self.collections.get(collection_index.clone() as usize)?;
489        match schema {
490            BlueprintCollectionSchema::KeyValueStore(key_value_store) => {
491                Some(key_value_store.key.clone())
492            }
493            _ => None,
494        }
495    }
496
497    pub fn get_kv_value_payload_def(
498        &self,
499        collection_index: u8,
500    ) -> Option<(BlueprintPayloadDef, bool)> {
501        let (_partition, schema) = self.collections.get(collection_index.clone() as usize)?;
502        match schema {
503            BlueprintCollectionSchema::KeyValueStore(key_value_store) => Some((
504                key_value_store.value.clone(),
505                key_value_store.allow_ownership,
506            )),
507            _ => None,
508        }
509    }
510
511    pub fn get_index_payload_def_key(&self, collection_index: u8) -> Option<BlueprintPayloadDef> {
512        let (_partition, schema) = self.collections.get(collection_index.clone() as usize)?;
513        match schema {
514            BlueprintCollectionSchema::Index(index) => Some(index.key.clone()),
515            _ => None,
516        }
517    }
518
519    pub fn get_index_payload_def_value(&self, collection_index: u8) -> Option<BlueprintPayloadDef> {
520        let (_partition, schema) = self.collections.get(collection_index.clone() as usize)?;
521        match schema {
522            BlueprintCollectionSchema::Index(index) => Some(index.value.clone()),
523            _ => None,
524        }
525    }
526
527    pub fn get_sorted_index_payload_def_key(
528        &self,
529        collection_index: u8,
530    ) -> Option<BlueprintPayloadDef> {
531        let (_partition, schema) = self.collections.get(collection_index.clone() as usize)?;
532        match schema {
533            BlueprintCollectionSchema::SortedIndex(index) => Some(index.key.clone()),
534            _ => None,
535        }
536    }
537
538    pub fn get_sorted_index_payload_def_value(
539        &self,
540        collection_index: u8,
541    ) -> Option<BlueprintPayloadDef> {
542        let (_partition, schema) = self.collections.get(collection_index.clone() as usize)?;
543        match schema {
544            BlueprintCollectionSchema::SortedIndex(index) => Some(index.value.clone()),
545            _ => None,
546        }
547    }
548
549    pub fn fields_partition(&self) -> Option<PartitionDescription> {
550        match &self.fields {
551            Some((partition, ..)) => Some(partition.clone()),
552            _ => None,
553        }
554    }
555
556    pub fn field(
557        &self,
558        field_index: u8,
559    ) -> Option<(PartitionDescription, FieldSchema<BlueprintPayloadDef>)> {
560        match &self.fields {
561            Some((partition, fields)) => {
562                let field_index: usize = field_index.into();
563                fields
564                    .get(field_index)
565                    .cloned()
566                    .map(|f| (partition.clone(), f))
567            }
568            _ => None,
569        }
570    }
571}