From 0d23201fc08d3204198131149dc57649d663711f Mon Sep 17 00:00:00 2001 From: David Edey Date: Tue, 8 Aug 2023 19:03:34 +0100 Subject: [PATCH 01/28] feature: Added versioned macro --- sbor/src/lib.rs | 3 + sbor/src/versioned.rs | 166 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 169 insertions(+) create mode 100644 sbor/src/versioned.rs diff --git a/sbor/src/lib.rs b/sbor/src/lib.rs index 4858e4aca96..c2b4217a870 100644 --- a/sbor/src/lib.rs +++ b/sbor/src/lib.rs @@ -42,6 +42,8 @@ pub mod traversal; pub mod value; /// SBOR value kinds - ie the types of value that are supported. pub mod value_kind; +/// Data model versioning helper macro +pub mod versioned; pub use basic::*; pub(crate) use categorize::{categorize_generic, categorize_simple}; @@ -59,6 +61,7 @@ pub use payload_validation::*; pub use schema::*; pub use value::*; pub use value_kind::*; +pub use versioned::*; // Re-export derives extern crate sbor_derive; diff --git a/sbor/src/versioned.rs b/sbor/src/versioned.rs new file mode 100644 index 00000000000..6b7a3813bc8 --- /dev/null +++ b/sbor/src/versioned.rs @@ -0,0 +1,166 @@ +pub enum UpdateResult { + Updated(T), + AtLatest(T), +} + +/// A marker trait to indicate that the type is versioned. +/// This can be used for type bounds for requiring that types are versioned. +pub trait IsVersioned {} + +/// This macro is intended for creating a data model which supports versioning. +/// This is useful for creating an SBOR data model which can be updated in future. +/// In future, enum variants can be added, and automatically mapped to. +/// +/// In the future, this may become a programmatic macro to support better error handling / +/// edge case detection, and opting into SBOR handling. +#[macro_export] +macro_rules! versioned { + ( + $(#[$attributes:meta])* + $vis:vis enum $name:ident { + previous_versions: [ + $($version_num:expr => $version_ident:ident -> { updates_to: $update_to_version_num:expr }),* + $(,)? // Optional trailing comma + ], + latest_version: { + $latest_version:expr => $latest_version_ident:ident, + }$(,)? + } + ) => { + paste::paste! { + $(#[$attributes])* + // We include the repr(u8) so that the SBOR discriminants are assigned + // to match the version numbers if SBOR is used on the versioned enum + #[repr(u8)] + $vis enum $name { + $( + []($version_ident) = $version_num, + )* + []($latest_version_ident) = $latest_version, + } + + impl $name { + pub fn update_once(self) -> UpdateResult { + match self { + $( + Self::[](value) => crate::UpdateResult::Updated(Self::[](value.into())), + )* + Self::[](value) => crate::UpdateResult::AtLatest(Self::[](value)), + } + } + + pub fn update_to_latest(mut self) -> Self { + loop { + match self.update_once() { + UpdateResult::Updated(new) => { + self = new; + } + UpdateResult::AtLatest(latest) => { + return latest; + } + } + } + } + } + + impl crate::IsVersioned for $name {} + + $( + impl From<$version_ident> for $name { + fn from(value: $version_ident) -> Self { + Self::[](value) + } + } + )* + + impl From<$latest_version_ident> for $name { + fn from(value: $latest_version_ident) -> Self { + Self::[](value) + } + } + } + }; +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::*; + + crate::versioned!( + #[derive(Debug, Clone, PartialEq, Eq, Sbor)] + enum VersionedExample { + previous_versions: [ + 1 => ExampleV1 -> { updates_to: 2 }, + 2 => ExampleV2 -> { updates_to: 4 }, + 3 => ExampleV3 -> { updates_to: 4 }, + ], + latest_version: { + 4 => ExampleV4, + }, + } + ); + + // Define the concrete versions + type ExampleV1 = u8; + type ExampleV2 = u16; + + #[derive(Debug, Clone, PartialEq, Eq, Sbor)] + struct ExampleV3(u16); + + #[derive(Debug, Clone, PartialEq, Eq, Sbor)] + struct ExampleV4 { + the_value: u16, + } + + impl ExampleV4 { + pub fn of(value: u16) -> Self { + Self { + the_value: value, + } + } + } + + // And explicit updates between them, which are needed + // for the versioned type + impl From for ExampleV4 { + fn from(value: ExampleV2) -> Self { + Self { + the_value: value, + } + } + } + + impl From for ExampleV4 { + fn from(value: ExampleV3) -> Self { + Self { + the_value: value.0, + } + } + } + + #[test] + pub fn updates_to_latest_work() { + let expected_latest = VersionedExample::V4(ExampleV4::of(5)); + let v1: ExampleV1 = 5; + assert_eq!( + VersionedExample::from(v1).update_to_latest(), + expected_latest.clone(), + ); + let v2: ExampleV2 = 5; + assert_eq!( + VersionedExample::from(v2).update_to_latest(), + expected_latest.clone(), + ); + let v3 = ExampleV3(5); + assert_eq!( + VersionedExample::from(v3).update_to_latest(), + expected_latest.clone(), + ); + let v4 = ExampleV4::of(5); + assert_eq!( + VersionedExample::from(v4).update_to_latest(), + expected_latest.clone(), + ); + } +} \ No newline at end of file From fe5d5da329ce954d1f30b4c6b19805e614e5143c Mon Sep 17 00:00:00 2001 From: David Edey Date: Tue, 8 Aug 2023 19:18:37 +0100 Subject: [PATCH 02/28] tweak: Update versioned macro --- sbor/src/versioned.rs | 55 ++++++++++++++++++++++++++++--------------- 1 file changed, 36 insertions(+), 19 deletions(-) diff --git a/sbor/src/versioned.rs b/sbor/src/versioned.rs index 6b7a3813bc8..245802bf16e 100644 --- a/sbor/src/versioned.rs +++ b/sbor/src/versioned.rs @@ -5,16 +5,21 @@ pub enum UpdateResult { /// A marker trait to indicate that the type is versioned. /// This can be used for type bounds for requiring that types are versioned. -pub trait IsVersioned {} +pub trait HasLatestVersion { + type Latest; + fn into_latest(self) -> Self::Latest; +} /// This macro is intended for creating a data model which supports versioning. /// This is useful for creating an SBOR data model which can be updated in future. /// In future, enum variants can be added, and automatically mapped to. +/// +/// NOTE: A circular version update chain will be an infinite loop at runtime. Be careful. /// /// In the future, this may become a programmatic macro to support better error handling / -/// edge case detection, and opting into SBOR handling. +/// edge case detection, and opting into more explicit SBOR handling. #[macro_export] -macro_rules! versioned { +macro_rules! define_versioned { ( $(#[$attributes:meta])* $vis:vis enum $name:ident { @@ -63,7 +68,16 @@ macro_rules! versioned { } } - impl crate::IsVersioned for $name {} + impl crate::HasLatestVersion for $name { + type Latest = $latest_version_ident; + + fn into_latest(self) -> Self::Latest { + let Self::[](latest) = self.update_to_latest() else { + panic!("Invalid resolved latest version not equal to latest type") + }; + return latest; + } + } $( impl From<$version_ident> for $name { @@ -87,7 +101,7 @@ mod tests { use super::*; use crate::*; - crate::versioned!( + crate::define_versioned!( #[derive(Debug, Clone, PartialEq, Eq, Sbor)] enum VersionedExample { previous_versions: [ @@ -141,26 +155,29 @@ mod tests { #[test] pub fn updates_to_latest_work() { - let expected_latest = VersionedExample::V4(ExampleV4::of(5)); + let expected_latest = ExampleV4::of(5); let v1: ExampleV1 = 5; - assert_eq!( - VersionedExample::from(v1).update_to_latest(), - expected_latest.clone(), - ); + validate_latest(v1, expected_latest.clone()); let v2: ExampleV2 = 5; - assert_eq!( - VersionedExample::from(v2).update_to_latest(), - expected_latest.clone(), - ); + validate_latest(v2, expected_latest.clone()); let v3 = ExampleV3(5); + validate_latest(v3, expected_latest.clone()); + let v4 = ExampleV4::of(5); + validate_latest(v4, expected_latest.clone()); + } + + fn validate_latest(actual: impl Into, expected: ::Latest) { + let versioned_actual = actual.into(); + let versioned_expected = VersionedExample::from(expected.clone()); + // Check update_to_latest (which returns a VersionedExample) assert_eq!( - VersionedExample::from(v3).update_to_latest(), - expected_latest.clone(), + versioned_actual.clone().update_to_latest(), + versioned_expected, ); - let v4 = ExampleV4::of(5); + // Check into_latest (which returns an ExampleV4) assert_eq!( - VersionedExample::from(v4).update_to_latest(), - expected_latest.clone(), + versioned_actual.into_latest(), + expected, ); } } \ No newline at end of file From f487591580c3b2b4656e6db51611720d563a178f Mon Sep 17 00:00:00 2001 From: David Edey Date: Wed, 9 Aug 2023 11:58:49 +0100 Subject: [PATCH 03/28] feature: Updates to defined_versioned Generics are now supported, as is a simplified define_single_versioned --- sbor/src/versioned.rs | 169 +++++++++++++++++++++++++++++++----------- 1 file changed, 126 insertions(+), 43 deletions(-) diff --git a/sbor/src/versioned.rs b/sbor/src/versioned.rs index 245802bf16e..050984d599a 100644 --- a/sbor/src/versioned.rs +++ b/sbor/src/versioned.rs @@ -13,7 +13,36 @@ pub trait HasLatestVersion { /// This macro is intended for creating a data model which supports versioning. /// This is useful for creating an SBOR data model which can be updated in future. /// In future, enum variants can be added, and automatically mapped to. -/// +/// +/// This macro is just a simpler wrapper around the [`define_versioned`] macro, +/// for use when there's just a single version. +#[macro_export] +macro_rules! define_single_versioned { + ( + $(#[$attributes:meta])* + $vis:vis enum $name:ident + $(< $( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+ >)? + => + $latest_version_type:ty + ) => { + $crate::define_versioned!( + $(#[$attributes])* + $vis enum $name + $(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? + { + previous_versions: [], + latest_version: { + 1 => $latest_version_type + }, + } + ); + }; +} + +/// This macro is intended for creating a data model which supports versioning. +/// This is useful for creating an SBOR data model which can be updated in future. +/// In future, enum variants can be added, and automatically mapped to. +/// /// NOTE: A circular version update chain will be an infinite loop at runtime. Be careful. /// /// In the future, this may become a programmatic macro to support better error handling / @@ -22,35 +51,68 @@ pub trait HasLatestVersion { macro_rules! define_versioned { ( $(#[$attributes:meta])* - $vis:vis enum $name:ident { + $vis:vis enum $name:ident + // Now match the optional type parameters + // See https://stackoverflow.com/questions/41603424/rust-macro-accepting-type-with-generic-parameters + $(< $( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+ >)? + { previous_versions: [ - $($version_num:expr => $version_ident:ident -> { updates_to: $update_to_version_num:expr }),* + $($version_num:expr => $version_type:ty: { updates_to: $update_to_version_num:expr }),* $(,)? // Optional trailing comma ], latest_version: { - $latest_version:expr => $latest_version_ident:ident, - }$(,)? + $latest_version:expr => $latest_version_type:ty + $(,)? // Optional trailing comma + } + $(,)? // Optional trailing comma } ) => { paste::paste! { + // Create inline sub-macros to handle the type generics nested inside + // iteration over previous_versions + // See eg https://stackoverflow.com/a/73543948 + macro_rules! [<$name _trait_impl>] { + ( + $trait:ty, + $impl_block:tt + ) => { + #[allow(dead_code)] + impl + $(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? + $trait + for $name $(< $( $lt ),+ >)? + $impl_block + }; + } + $(#[$attributes])* // We include the repr(u8) so that the SBOR discriminants are assigned // to match the version numbers if SBOR is used on the versioned enum #[repr(u8)] - $vis enum $name { + $vis enum $name $(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? + { $( - []($version_ident) = $version_num, + []($version_type) = $version_num, )* - []($latest_version_ident) = $latest_version, + []($latest_version_type) = $latest_version, } - impl $name { + #[allow(dead_code)] + impl + $(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? + $name + $(< $( $lt ),+ >)? + { + pub fn new_latest(value: $latest_version_type) -> Self { + Self::[](value) + } + pub fn update_once(self) -> UpdateResult { match self { $( - Self::[](value) => crate::UpdateResult::Updated(Self::[](value.into())), + Self::[](value) => $crate::UpdateResult::Updated(Self::[](value.into())), )* - Self::[](value) => crate::UpdateResult::AtLatest(Self::[](value)), + Self::[](value) => $crate::UpdateResult::AtLatest(Self::[](value)), } } @@ -68,30 +130,38 @@ macro_rules! define_versioned { } } - impl crate::HasLatestVersion for $name { - type Latest = $latest_version_ident; + [<$name _trait_impl>]!( + $crate::HasLatestVersion, + { + type Latest = $latest_version_type; - fn into_latest(self) -> Self::Latest { - let Self::[](latest) = self.update_to_latest() else { - panic!("Invalid resolved latest version not equal to latest type") - }; - return latest; + #[allow(irrefutable_let_patterns)] + fn into_latest(self) -> Self::Latest { + let Self::[](latest) = self.update_to_latest() else { + panic!("Invalid resolved latest version not equal to latest type") + }; + return latest; + } } - } + ); - $( - impl From<$version_ident> for $name { - fn from(value: $version_ident) -> Self { + $([<$name _trait_impl>]!( + From<$version_type>, + { + fn from(value: $version_type) -> Self { Self::[](value) } } - )* + );)* - impl From<$latest_version_ident> for $name { - fn from(value: $latest_version_ident) -> Self { - Self::[](value) + [<$name _trait_impl>]!( + From<$latest_version_type>, + { + fn from(value: $latest_version_type) -> Self { + Self::[](value) + } } - } + ); } }; } @@ -105,9 +175,9 @@ mod tests { #[derive(Debug, Clone, PartialEq, Eq, Sbor)] enum VersionedExample { previous_versions: [ - 1 => ExampleV1 -> { updates_to: 2 }, - 2 => ExampleV2 -> { updates_to: 4 }, - 3 => ExampleV3 -> { updates_to: 4 }, + 1 => ExampleV1: { updates_to: 2 }, + 2 => ExampleV2: { updates_to: 4 }, + 3 => ExampleV3: { updates_to: 4 }, ], latest_version: { 4 => ExampleV4, @@ -129,9 +199,7 @@ mod tests { impl ExampleV4 { pub fn of(value: u16) -> Self { - Self { - the_value: value, - } + Self { the_value: value } } } @@ -139,17 +207,13 @@ mod tests { // for the versioned type impl From for ExampleV4 { fn from(value: ExampleV2) -> Self { - Self { - the_value: value, - } + Self { the_value: value } } } impl From for ExampleV4 { fn from(value: ExampleV3) -> Self { - Self { - the_value: value.0, - } + Self { the_value: value.0 } } } @@ -166,7 +230,10 @@ mod tests { validate_latest(v4, expected_latest.clone()); } - fn validate_latest(actual: impl Into, expected: ::Latest) { + fn validate_latest( + actual: impl Into, + expected: ::Latest, + ) { let versioned_actual = actual.into(); let versioned_expected = VersionedExample::from(expected.clone()); // Check update_to_latest (which returns a VersionedExample) @@ -175,9 +242,25 @@ mod tests { versioned_expected, ); // Check into_latest (which returns an ExampleV4) + assert_eq!(versioned_actual.into_latest(), expected,); + } + + #[derive(Debug, Clone, PartialEq, Eq)] + struct GenericModelV1(T); + + define_single_versioned!( + /// This is some rust doc as an example annotation + #[derive(Debug, Clone, PartialEq, Eq)] + enum GenericModel => GenericModelV1 + ); + + #[test] + pub fn generated_single_versioned_works() { + let v1_model = GenericModelV1(51u64); + let versioned = GenericModel::from(v1_model.clone()); assert_eq!( - versioned_actual.into_latest(), - expected, + versioned.into_latest(), + v1_model ); } -} \ No newline at end of file +} From b13160b076fa1af2250fd11533301766447e8704 Mon Sep 17 00:00:00 2001 From: David Edey Date: Wed, 9 Aug 2023 15:02:21 +0100 Subject: [PATCH 04/28] feature: Add additional traits to `Versioned` --- sbor/src/versioned.rs | 95 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 76 insertions(+), 19 deletions(-) diff --git a/sbor/src/versioned.rs b/sbor/src/versioned.rs index 050984d599a..22719740ceb 100644 --- a/sbor/src/versioned.rs +++ b/sbor/src/versioned.rs @@ -8,6 +8,20 @@ pub enum UpdateResult { pub trait HasLatestVersion { type Latest; fn into_latest(self) -> Self::Latest; + fn as_latest_ref(&self) -> Option<&Self::Latest>; +} + +pub trait CloneIntoLatest { + type Latest; + fn clone_into_latest(&self) -> Self::Latest; +} + +impl + Clone, Latest> CloneIntoLatest for T { + type Latest = Latest; + + fn clone_into_latest(&self) -> Self::Latest { + self.clone().into_latest() + } } /// This macro is intended for creating a data model which supports versioning. @@ -23,7 +37,7 @@ macro_rules! define_single_versioned { $vis:vis enum $name:ident $(< $( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+ >)? => - $latest_version_type:ty + $latest_version_alias:ty = $latest_version_type:ty ) => { $crate::define_versioned!( $(#[$attributes])* @@ -32,7 +46,7 @@ macro_rules! define_single_versioned { { previous_versions: [], latest_version: { - 1 => $latest_version_type + 1 => $latest_version_alias = $latest_version_type }, } ); @@ -56,12 +70,14 @@ macro_rules! define_versioned { // See https://stackoverflow.com/questions/41603424/rust-macro-accepting-type-with-generic-parameters $(< $( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+ >)? { - previous_versions: [ - $($version_num:expr => $version_type:ty: { updates_to: $update_to_version_num:expr }),* - $(,)? // Optional trailing comma - ], + $( + previous_versions: [ + $($version_num:expr => $version_type:ty: { updates_to: $update_to_version_num:expr }),* + $(,)? // Optional trailing comma + ], + )? latest_version: { - $latest_version:expr => $latest_version_type:ty + $latest_version:expr => $latest_version_alias:ty = $latest_version_type:ty $(,)? // Optional trailing comma } $(,)? // Optional trailing comma @@ -85,15 +101,18 @@ macro_rules! define_versioned { }; } + #[allow(dead_code)] + $vis type $latest_version_alias = $latest_version_type; + $(#[$attributes])* // We include the repr(u8) so that the SBOR discriminants are assigned // to match the version numbers if SBOR is used on the versioned enum #[repr(u8)] $vis enum $name $(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? { - $( + $($( []($version_type) = $version_num, - )* + )*)? []($latest_version_type) = $latest_version, } @@ -109,9 +128,9 @@ macro_rules! define_versioned { pub fn update_once(self) -> UpdateResult { match self { - $( + $($( Self::[](value) => $crate::UpdateResult::Updated(Self::[](value.into())), - )* + )*)? Self::[](value) => $crate::UpdateResult::AtLatest(Self::[](value)), } } @@ -142,17 +161,25 @@ macro_rules! define_versioned { }; return latest; } + + #[allow(unreachable_patterns)] + fn as_latest_ref(&self) -> Option<&Self::Latest> { + match self { + Self::[](latest) => Some(latest), + _ => None, + } + } } ); - $([<$name _trait_impl>]!( + $($([<$name _trait_impl>]!( From<$version_type>, { fn from(value: $version_type) -> Self { Self::[](value) } } - );)* + );)*)? [<$name _trait_impl>]!( From<$latest_version_type>, @@ -162,6 +189,31 @@ macro_rules! define_versioned { } } ); + + #[allow(dead_code)] + $vis trait [<$name Version>] { + // Note - we have an explicit Versioned associated type so that + // different generic parameters can each create their own specific concrete type + type Versioned; + + fn into_versioned(self) -> Self::Versioned; + } + + macro_rules! [<$name _versionable_impl>] { + ($inner_type:ty) => { + impl$(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? [<$name Version>] for $inner_type + { + type Versioned = $name $(< $( $lt ),+ >)?; + + fn into_versioned(self) -> Self::Versioned { + self.into() + } + } + }; + } + + $($([<$name _versionable_impl>]!($version_type);)*)? + [<$name _versionable_impl>]!($latest_version_type); } }; } @@ -180,7 +232,7 @@ mod tests { 3 => ExampleV3: { updates_to: 4 }, ], latest_version: { - 4 => ExampleV4, + 4 => Example = ExampleV4, }, } ); @@ -251,16 +303,21 @@ mod tests { define_single_versioned!( /// This is some rust doc as an example annotation #[derive(Debug, Clone, PartialEq, Eq)] - enum GenericModel => GenericModelV1 + enum VersionedGenericModel => GenericModel = GenericModelV1 ); #[test] pub fn generated_single_versioned_works() { - let v1_model = GenericModelV1(51u64); - let versioned = GenericModel::from(v1_model.clone()); + let v1_model: GenericModel<_> = GenericModelV1(51u64); + let versioned = VersionedGenericModel::from(v1_model.clone()); + let versioned_2 = v1_model.clone().into_versioned(); + assert_eq!( + versioned.clone().into_latest(), + v1_model.clone() + ); assert_eq!( - versioned.into_latest(), - v1_model + versioned, + versioned_2 ); } } From ed0a7753977607496931474a90432570ccf6c9f6 Mon Sep 17 00:00:00 2001 From: David Edey Date: Thu, 10 Aug 2023 13:31:51 +0100 Subject: [PATCH 05/28] feature: `declare_native_blueprint_state!` macro --- radix-engine/src/blueprints/macros.rs | 476 ++++++++++++++++++++++++++ radix-engine/src/blueprints/mod.rs | 1 + radix-engine/src/system/system.rs | 7 + sbor/src/versioned.rs | 18 +- 4 files changed, 490 insertions(+), 12 deletions(-) create mode 100644 radix-engine/src/blueprints/macros.rs diff --git a/radix-engine/src/blueprints/macros.rs b/radix-engine/src/blueprints/macros.rs new file mode 100644 index 00000000000..2b8873d35d2 --- /dev/null +++ b/radix-engine/src/blueprints/macros.rs @@ -0,0 +1,476 @@ +use crate::types::*; +use crate::system::system::*; + +pub trait FieldContent: Sized { + type VersionedContent: From; + + fn into_locked_substate(self) -> FieldSubstate { + FieldSubstate::new_locked_field(self.into()) + } + + fn into_mutable_substate(self) -> FieldSubstate { + FieldSubstate::new_field(self.into()) + } +} + +pub trait KVEntryContent: Sized { + type VersionedContent: From; + + fn into_locked_substate(self) -> KeyValueEntrySubstate { + KeyValueEntrySubstate::entry(self.into()) + } + + fn into_mutable_substate(self) -> KeyValueEntrySubstate { + KeyValueEntrySubstate::locked_entry(self.into()) + } +} + +pub trait IndexEntryContent: Sized { + type VersionedContent: From; + + fn into_substate(self) -> Self::VersionedContent { + self.into() + } +} + +pub trait SortedIndexEntryContent: Sized { + type VersionedContent: From; + + fn into_substate(self) -> Self::VersionedContent { + self.into() + } +} + +macro_rules! generate_wrapped_substate_type_alias { + (SystemField, $module_ident:ident, $field_ident:ident) => { + paste::paste! { + pub type [<$module_ident $field_ident FieldSubstate>] = []; + } + }; + (Field, $blueprint_ident:ident, $field_ident:ident) => { + paste::paste! { + pub type [<$blueprint_ident $field_ident FieldSubstate>] = $crate::system::system::FieldSubstate<[]>; + } + }; + (KeyValue, $blueprint_ident:ident, $collection_ident:ident) => { + paste::paste! { + pub type [<$blueprint_ident $collection_ident EntrySubstate>] = $crate::system::system::KeyValueEntrySubstate<[]>; + } + }; + (Index, $blueprint_ident:ident, $collection_ident:ident) => { + // No wrapper around Index substates + paste::paste! { + pub type [<$blueprint_ident $collection_ident EntrySubstate>] = []; + } + }; + (SortedIndex, $blueprint_ident:ident, $collection_ident:ident) => { + // There is no wrapper around Index substates + paste::paste! { + pub type [<$blueprint_ident $collection_ident EntrySubstate>] = []; + } + }; + ($unknown_system_substate_type:ident, $blueprint_ident:ident, $collection_ident:ident) => { + paste::paste! { + compile_error!(concat!( + "Unrecognized system substate type: `", + stringify!($unknown_system_substate_type), + "` - expected `Field`, `SystemField`, `KeyValue`, `Index` or `SortedIndex`" + )); + } + }; +} + +macro_rules! generate_collection_substate_content_trait { + (KeyValue, $type:ident, $versioned:ident) => { + impl KVEntryContent for $type { + type VersionedContent = $versioned; + } + }; + (Index, $type:ident, $versioned:ident) => { + impl IndexEntryContent for $type { + type VersionedContent = $versioned; + } + }; + (SortedIndex, $type:ident, $versioned:ident) => { + impl SortedIndexEntryContent for $type { + type VersionedContent = $versioned; + } + }; + ($unknown_system_substate_type:ident, $type:ident, $versioned:ident) => { + paste::paste! { + compile_error!(concat!( + "Unrecognized system collection substate type: `", + stringify!($unknown_system_substate_type), + "` - expected `KeyValue`, `Index` or `SortedIndex`" + )); + } + }; +} + +macro_rules! map_entry_substate_to_kv_entry { + (KeyValue, $entry_substate:ident) => { + paste::paste! { + KVEntry { + value: $entry_substate.value.map(|v| scrypto_encode(&v).unwrap()), + locked: match $entry_substate.mutability { + SubstateMutability::Mutable => true, + SubstateMutability::Immutable => false, + }, + } + } + }; + (Index, $entry_substate:ident) => { + // This code still needs to compile, but it shouldn't be possible to execute + panic!("Not possible to map an Index entry to a KVEntry") + }; + (SortedIndex, $entry_substate:ident) => { + // This code still needs to compile, but it shouldn't be possible to execute + panic!("Not possible to map a SortedIndex entry to a KVEntry") + }; + ($unknown_system_substate_type:ident, $entry_substate:ident) => { + paste::paste! { + compile_error!(concat!( + "Unrecognized system collection substate type: `", + stringify!($unknown_system_substate_type), + "` - expected `KeyValue`, `Index` or `SortedIndex`" + )); + } + }; +} + +/// Generates types and typed-interfaces for native blueprints and their +/// interaction with the substate store. +/// +/// * For fields, assumes the existence of a type called: +/// `FieldV1` +/// * For collections, assumes the existence of types called: +/// `Key` +/// `ValueV1` +macro_rules! declare_native_blueprint_state { + ( + blueprint_ident: $blueprint_ident:ident, + fields: { + $( + $field_property_name:ident: { + ident: $field_ident:ident, + condition: $field_condition:expr + $(,)? // Optional trialing comma + } + ),* + $(,)? // Optional trialing comma + }, + collections: { + $( + $collection_property_name:ident: $collection_type:ident { + entry_ident: $collection_ident:ident, + key_type: $collection_key_type:ty, + can_own: $collection_can_own:expr + // Collection options for (eg) passing in a property name + // of the sorted index parameter for SortedIndex + $(, options: $collection_options:tt)? + $(,)? // Optional trialing comma + } + ),* + $(,)? // Optional trialing comma + } + $(,)? + ) => { + paste::paste! { + #[allow(unused_imports)] + mod [<$blueprint_ident _models>] { + use super::*; + use sbor::*; + use $crate::types::*; + use $crate::errors::RuntimeError; + use $crate::system::system::*; + use radix_engine_interface::api::*; + //-------------------------------------------------------- + // MODELS + //-------------------------------------------------------- + + // Generate models for each field + $( + // TODO: In future, expand this macro to support multi-versioned fields + sbor::define_single_versioned!( + #[derive(Debug, PartialEq, Eq, ScryptoSbor)] + pub enum [] => [<$blueprint_ident $field_ident Field>] = [<$blueprint_ident $field_ident FieldV1>] + ); + generate_wrapped_substate_type_alias!(Field, $blueprint_ident, $field_ident); + impl FieldContent for [] { + type VersionedContent = Self; + } + impl FieldContent for [<$blueprint_ident $field_ident Field>] { + type VersionedContent = []; + } + );* + + // Generate models for each collection + $( + pub type [<$blueprint_ident $collection_ident Key>] = $collection_key_type; + // TODO: In future, expand this macro to support multi-versioned collection values + sbor::define_single_versioned!( + #[derive(Debug, PartialEq, Eq, ScryptoSbor)] + pub enum [] => [<$blueprint_ident $collection_ident Value>] = [<$blueprint_ident $collection_ident ValueV1>] + ); + generate_wrapped_substate_type_alias!($collection_type, $blueprint_ident, $collection_ident); + generate_collection_substate_content_trait!($collection_type, [], Self); + generate_collection_substate_content_trait!($collection_type, [<$blueprint_ident $collection_ident Value>], []); + )* + + //-------------------------------------------------------- + // Node Layout + // (to replace node_layout.rs) + //-------------------------------------------------------- + #[repr(u8)] + #[derive(Debug, Clone, Sbor, PartialEq, Eq, Hash, PartialOrd, Ord, FromRepr)] + pub enum [<$blueprint_ident Field>] { + $($field_ident,)* + } + + impl From<[<$blueprint_ident Field>]> for SubstateKey { + fn from(value: [<$blueprint_ident Field>]) -> Self { + SubstateKey::Field(value as u8) + } + } + + impl From<[<$blueprint_ident Field>]> for u8 { + fn from(value: [<$blueprint_ident Field>]) -> Self { + value as u8 + } + } + + impl TryFrom<&SubstateKey> for [<$blueprint_ident Field>] { + type Error = (); + + fn try_from(key: &SubstateKey) -> Result { + match key { + SubstateKey::Field(x) => Self::from_repr(*x).ok_or(()), + _ => Err(()), + } + } + } + + impl TryFrom for [<$blueprint_ident Field>] { + type Error = (); + + fn try_from(offset: u8) -> Result { + Self::from_repr(offset).ok_or(()) + } + } + + #[repr(u8)] + #[derive(Debug, Clone, Sbor, PartialEq, Eq, Hash, PartialOrd, Ord, FromRepr)] + pub enum [<$blueprint_ident Partition>] { + Field, + $([<$collection_ident $collection_type>],)* + } + + impl [<$blueprint_ident Partition>] { + pub const fn offset(&self) -> PartitionOffset { + PartitionOffset(*self as u8) + } + + pub const fn main_partition(&self) -> PartitionNumber { + MAIN_BASE_PARTITION.at_offset(self.offset()).unwrap() + } + } + + impl From<[<$blueprint_ident Partition>]> for PartitionOffset { + fn from(value: [<$blueprint_ident Partition>]) -> Self { + PartitionOffset(value as u8) + } + } + + //--------------------------------- + // Typed Substate - Keys and Values + //--------------------------------- + #[derive(Debug, Clone)] + pub enum [<$blueprint_ident TypedSubstateKey>] { + Fields([<$blueprint_ident Field>]), + $([<$collection_ident $collection_type Entries>]([<$blueprint_ident $collection_ident Key>]),)* + } + + #[derive(Debug)] + pub enum [<$blueprint_ident TypedFieldSubstateValue>] { + $($field_ident([<$blueprint_ident $field_ident FieldSubstate>]),)* + } + + #[derive(Debug)] + pub enum [<$blueprint_ident TypedSubstateValue>] { + Field([<$blueprint_ident TypedFieldSubstateValue>]), + $([<$collection_ident $collection_type>]([<$blueprint_ident $collection_ident EntrySubstate>]),)* + } + + //---------------------- + // Object Initialization + //---------------------- + + /// This doesn't support: + /// * Features + /// * Instance schemas + /// * Feature-dependent fields + /// * IndexEntries (because the underlying new_object API doesn't support them) + pub struct [<$blueprint_ident StateInit>] { + $( + pub $field_property_name: [<$blueprint_ident $field_ident FieldSubstate>], + )* + $( + pub $collection_property_name: IndexMap< + [<$blueprint_ident $collection_ident Key>], + [<$blueprint_ident $collection_ident EntrySubstate>] + >, + )* + } + + impl [<$blueprint_ident StateInit>] { + pub fn into_system_substates(self) -> (Vec, BTreeMap, KVEntry>>) { + let mut field_values = vec![]; + $( + let field_content = scrypto_encode(&self.$field_property_name.value).unwrap(); + let locked = match &self.$field_property_name.mutability { + SubstateMutability::Mutable => true, + SubstateMutability::Immutable => false, + }; + field_values.push(FieldValue { + value: field_content, + locked, + }); + )* + let mut all_collection_entries = BTreeMap::new(); + let mut collection_index: u8 = 0; + $( + #[allow(unreachable_code)] + let this_collection_entries = self.$collection_property_name + .into_iter() + .map(|(key, entry_substate)| { + let key = scrypto_encode(&key).unwrap(); + let value = map_entry_substate_to_kv_entry!($collection_type, entry_substate); + (key, value) + }) + .collect(); + all_collection_entries.insert(collection_index, this_collection_entries); + collection_index += 1; + )* + (field_values, all_collection_entries) + } + + pub fn into_new_object>(self, api: &mut Y) -> Result { + let (field_values, all_collection_entries) = self.into_system_substates(); + api.new_object( + stringify!($blueprint_ident), + vec![], // Features + None, // Instance schema + field_values, + all_collection_entries, + ) + } + } + + //------------- + // State API (TODO!) + //------------- + + pub struct [<$blueprint_ident StateApi>]<'a, Y: ClientApi> { + api: &'a mut Y, + } + + impl<'a, Y: ClientApi> [<$blueprint_ident StateApi>]<'a, Y> { + pub fn with(client_api: &'a mut Y) -> Self { + Self { + api: client_api, + } + } + } + + impl<'a, Y: ClientApi<$crate::errors::RuntimeError>> From<&'a mut Y> for [<$blueprint_ident StateApi>]<'a, Y> { + fn from(value: &'a mut Y) -> Self { + Self::with(value) + } + } + } + #[allow(unused)] + pub(crate) use [<$blueprint_ident _models>]::*; + } + } +} + +// See PackageNativePackage "definition()" +// See "create_bootstrap_package_partitions" in package.rs +// See "globalize_package" in package.rs + +// This macro should: +// * Able to create: +// - VersionedSubstateType +// - LatestSubstateType +// +// * Generate node_layout.rs +// +// And future, it would be cool to support: +// +// * Able to create BlueprintStateSchemaInit +// -> ie FieldSchema (field's type, and Condition) +// -> ie BlueprintCollectionSchema (one of three options, plus: key / value / can_own) +// +// * Able to create APIs for reading/writing individual fields + +// Fields: +// + +pub(crate) use declare_native_blueprint_state; +use radix_engine_common::types::PartitionNumber; + +#[derive(Debug, PartialEq, Eq, Sbor)] +struct PackageRoyaltyFieldV1; + +#[derive(Debug, PartialEq, Eq, Sbor)] +struct PackageBlueprintDefinitionValueV1; + +#[derive(Debug, PartialEq, Eq, Sbor)] +struct PackageMyCoolIndexValueV1; + +#[derive(Debug, PartialEq, Eq, Sbor)] +struct PackageMyCoolSortedIndexValueV1; + +use radix_engine_interface::blueprints::package::*; + +declare_native_blueprint_state!{ + blueprint_ident: Package, + fields: { + royalty: { + // Generates: + // - PackageRoyaltyField + // - VersionedPackageRoyaltyField + // Must find type called: + // - PackageRoyaltyFieldV1 + ident: Royalty, + condition: Condition::Xyz ( + + ), + } + }, + // Generates static colletion offsets + collection + // Eg PackageCollections::BlueprintDefinition.collection_offset() + // EG PackageCollections::BlueprintDefinition.partition_number() + collections: { + blueprint_definitions: KeyValue { + entry_ident: BlueprintDefinition, + key_type: BlueprintVersion, + can_own: true, + }, + abc: Index { + entry_ident: MyCoolIndex, + key_type: BlueprintVersion, + can_own: true, + }, + def: SortedIndex { + entry_ident: MyCoolSortedIndex, + key_type: BlueprintVersion, + can_own: true, + }, + } +} + +fn xyz() { + PackageField::Royalty; + PackagePartitionOffset::Fields; +} \ No newline at end of file diff --git a/radix-engine/src/blueprints/mod.rs b/radix-engine/src/blueprints/mod.rs index 7829195a341..cc70088cc2a 100644 --- a/radix-engine/src/blueprints/mod.rs +++ b/radix-engine/src/blueprints/mod.rs @@ -1,3 +1,4 @@ +pub mod macros; pub mod access_controller; pub mod account; pub mod consensus_manager; diff --git a/radix-engine/src/system/system.rs b/radix-engine/src/system/system.rs index 0274be4aa15..b2bfc95b033 100644 --- a/radix-engine/src/system/system.rs +++ b/radix-engine/src/system/system.rs @@ -71,6 +71,13 @@ impl FieldSubstate { mutability: SubstateMutability::Mutable, } } + + pub fn new_locked_field(value: V) -> Self { + Self { + value: (value,), + mutability: SubstateMutability::Immutable, + } + } } pub type KeyValueEntrySubstate = DynSubstate>; diff --git a/sbor/src/versioned.rs b/sbor/src/versioned.rs index 22719740ceb..74ce00d2032 100644 --- a/sbor/src/versioned.rs +++ b/sbor/src/versioned.rs @@ -126,7 +126,7 @@ macro_rules! define_versioned { Self::[](value) } - pub fn update_once(self) -> UpdateResult { + pub fn update_once(self) -> $crate::UpdateResult { match self { $($( Self::[](value) => $crate::UpdateResult::Updated(Self::[](value.into())), @@ -138,10 +138,10 @@ macro_rules! define_versioned { pub fn update_to_latest(mut self) -> Self { loop { match self.update_once() { - UpdateResult::Updated(new) => { + $crate::UpdateResult::Updated(new) => { self = new; } - UpdateResult::AtLatest(latest) => { + $crate::UpdateResult::AtLatest(latest) => { return latest; } } @@ -204,7 +204,7 @@ macro_rules! define_versioned { impl$(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? [<$name Version>] for $inner_type { type Versioned = $name $(< $( $lt ),+ >)?; - + fn into_versioned(self) -> Self::Versioned { self.into() } @@ -311,13 +311,7 @@ mod tests { let v1_model: GenericModel<_> = GenericModelV1(51u64); let versioned = VersionedGenericModel::from(v1_model.clone()); let versioned_2 = v1_model.clone().into_versioned(); - assert_eq!( - versioned.clone().into_latest(), - v1_model.clone() - ); - assert_eq!( - versioned, - versioned_2 - ); + assert_eq!(versioned.clone().into_latest(), v1_model.clone()); + assert_eq!(versioned, versioned_2); } } From 258b5690fc661cec05b7ddb72a81f373e135edd6 Mon Sep 17 00:00:00 2001 From: David Edey Date: Thu, 10 Aug 2023 13:38:55 +0100 Subject: [PATCH 06/28] example: Add versioned schema --- .../src/data/scrypto/custom_schema.rs | 4 +- radix-engine/src/blueprints/macros.rs | 14 +++---- radix-engine/src/blueprints/mod.rs | 2 +- sbor/src/basic.rs | 1 + sbor/src/schema/schema.rs | 38 ++++++++++++++++--- 5 files changed, 45 insertions(+), 14 deletions(-) diff --git a/radix-engine-common/src/data/scrypto/custom_schema.rs b/radix-engine-common/src/data/scrypto/custom_schema.rs index 81418d373ea..64b5b74d18e 100644 --- a/radix-engine-common/src/data/scrypto/custom_schema.rs +++ b/radix-engine-common/src/data/scrypto/custom_schema.rs @@ -2,6 +2,7 @@ use crate::internal_prelude::*; pub type ScryptoTypeKind = TypeKind; pub type ScryptoSchema = Schema; +pub type VersionedScryptoSchema = VersionedSchema; pub type ScryptoTypeData = TypeData; /// A schema for the values that a codec can decode / views as valid @@ -205,7 +206,8 @@ pub trait HasSchemaHash { fn generate_schema_hash(&self) -> Hash; } -impl HasSchemaHash for Schema { +// TODO - change to VersionedScryptoSchema when we use that in substates +impl HasSchemaHash for ScryptoSchema { fn generate_schema_hash(&self) -> Hash { hash(scrypto_encode(self).unwrap()) } diff --git a/radix-engine/src/blueprints/macros.rs b/radix-engine/src/blueprints/macros.rs index 2b8873d35d2..d866fc049ab 100644 --- a/radix-engine/src/blueprints/macros.rs +++ b/radix-engine/src/blueprints/macros.rs @@ -1,5 +1,5 @@ -use crate::types::*; use crate::system::system::*; +use crate::types::*; pub trait FieldContent: Sized { type VersionedContent: From; @@ -241,7 +241,7 @@ macro_rules! declare_native_blueprint_state { impl TryFrom<&SubstateKey> for [<$blueprint_ident Field>] { type Error = (); - + fn try_from(key: &SubstateKey) -> Result { match key { SubstateKey::Field(x) => Self::from_repr(*x).ok_or(()), @@ -252,7 +252,7 @@ macro_rules! declare_native_blueprint_state { impl TryFrom for [<$blueprint_ident Field>] { type Error = (); - + fn try_from(offset: u8) -> Result { Self::from_repr(offset).ok_or(()) } @@ -373,7 +373,7 @@ macro_rules! declare_native_blueprint_state { pub struct [<$blueprint_ident StateApi>]<'a, Y: ClientApi> { api: &'a mut Y, } - + impl<'a, Y: ClientApi> [<$blueprint_ident StateApi>]<'a, Y> { pub fn with(client_api: &'a mut Y) -> Self { Self { @@ -381,7 +381,7 @@ macro_rules! declare_native_blueprint_state { } } } - + impl<'a, Y: ClientApi<$crate::errors::RuntimeError>> From<&'a mut Y> for [<$blueprint_ident StateApi>]<'a, Y> { fn from(value: &'a mut Y) -> Self { Self::with(value) @@ -433,7 +433,7 @@ struct PackageMyCoolSortedIndexValueV1; use radix_engine_interface::blueprints::package::*; -declare_native_blueprint_state!{ +declare_native_blueprint_state! { blueprint_ident: Package, fields: { royalty: { @@ -473,4 +473,4 @@ declare_native_blueprint_state!{ fn xyz() { PackageField::Royalty; PackagePartitionOffset::Fields; -} \ No newline at end of file +} diff --git a/radix-engine/src/blueprints/mod.rs b/radix-engine/src/blueprints/mod.rs index cc70088cc2a..b5c80e63043 100644 --- a/radix-engine/src/blueprints/mod.rs +++ b/radix-engine/src/blueprints/mod.rs @@ -1,8 +1,8 @@ -pub mod macros; pub mod access_controller; pub mod account; pub mod consensus_manager; pub mod identity; +pub mod macros; pub mod native_schema; pub mod package; pub mod pool; diff --git a/sbor/src/basic.rs b/sbor/src/basic.rs index adaba70de00..eb78e8329a6 100644 --- a/sbor/src/basic.rs +++ b/sbor/src/basic.rs @@ -259,6 +259,7 @@ pub type BasicRawValue<'a> = RawValue<'a, NoCustomExtension>; pub type BasicOwnedRawValue = RawValue<'static, NoCustomExtension>; pub type BasicTypeKind = TypeKind; pub type BasicSchema = Schema; +pub type BasicVersionedSchema = VersionedSchema; pub type BasicTypeData = TypeData; impl<'a> CustomDisplayContext<'a> for () { diff --git a/sbor/src/schema/schema.rs b/sbor/src/schema/schema.rs index bef5157f8f9..39ea12673f5 100644 --- a/sbor/src/schema/schema.rs +++ b/sbor/src/schema/schema.rs @@ -1,14 +1,36 @@ use crate::rust::prelude::*; use crate::*; +define_versioned!( + #[derive(Debug, Clone, PartialEq, Eq, Sbor)] + #[sbor(child_types = "S::CustomTypeKind, S::CustomTypeValidation")] + pub enum VersionedSchema { + latest_version: { + 1 => Schema = SchemaV1::, + } + } +); + +impl VersionedSchema { + pub fn empty() -> Self { + Schema::empty().into() + } +} + +impl Default for VersionedSchema { + fn default() -> Self { + Self::empty() + } +} + /// An array of custom type kinds, and associated extra information which can attach to the type kinds -#[derive(Default, Debug, Clone, PartialEq, Eq, Sbor)] +#[derive(Debug, Clone, PartialEq, Eq, Sbor)] // NB - the generic parameter E isn't embedded in the value model itself - instead: -// * Via TypeKind, E::CustomTypeKind gets embedded -// * Via TypeValidation, E::CustomTypeValidation gets embedded +// * Via TypeKind, S::CustomTypeKind gets embedded +// * Via TypeValidation, S::CustomTypeValidation gets embedded // So theses are the child types which need to be registered with the sbor macro for it to compile #[sbor(child_types = "S::CustomTypeKind, S::CustomTypeValidation")] -pub struct Schema { +pub struct SchemaV1 { pub type_kinds: Vec>, pub type_metadata: Vec, // TODO: reconsider adding type hash when it's ready! pub type_validations: Vec>, @@ -17,7 +39,7 @@ pub struct Schema { pub type SchemaTypeKind = TypeKind<::CustomTypeKind, LocalTypeIndex>; -impl Schema { +impl SchemaV1 { pub fn empty() -> Self { Self { type_kinds: vec![], @@ -121,6 +143,12 @@ impl Schema { } } +impl Default for SchemaV1 { + fn default() -> Self { + Self::empty() + } +} + #[derive(Debug, Default)] pub struct ArrayData<'m> { pub array_name: Option<&'m str>, From 70e10b598e80c4d7d2988cbc21bc0e1f41534c5d Mon Sep 17 00:00:00 2001 From: David Edey Date: Thu, 10 Aug 2023 14:34:19 +0100 Subject: [PATCH 07/28] feature: Started implementing Package state --- radix-engine/src/blueprints/macros.rs | 482 +++++++++++------- radix-engine/src/blueprints/package/mod.rs | 2 + .../src/blueprints/package/substates.rs | 42 ++ 3 files changed, 345 insertions(+), 181 deletions(-) create mode 100644 radix-engine/src/blueprints/package/substates.rs diff --git a/radix-engine/src/blueprints/macros.rs b/radix-engine/src/blueprints/macros.rs index d866fc049ab..a4948c022a3 100644 --- a/radix-engine/src/blueprints/macros.rs +++ b/radix-engine/src/blueprints/macros.rs @@ -41,102 +41,6 @@ pub trait SortedIndexEntryContent: Sized { } } -macro_rules! generate_wrapped_substate_type_alias { - (SystemField, $module_ident:ident, $field_ident:ident) => { - paste::paste! { - pub type [<$module_ident $field_ident FieldSubstate>] = []; - } - }; - (Field, $blueprint_ident:ident, $field_ident:ident) => { - paste::paste! { - pub type [<$blueprint_ident $field_ident FieldSubstate>] = $crate::system::system::FieldSubstate<[]>; - } - }; - (KeyValue, $blueprint_ident:ident, $collection_ident:ident) => { - paste::paste! { - pub type [<$blueprint_ident $collection_ident EntrySubstate>] = $crate::system::system::KeyValueEntrySubstate<[]>; - } - }; - (Index, $blueprint_ident:ident, $collection_ident:ident) => { - // No wrapper around Index substates - paste::paste! { - pub type [<$blueprint_ident $collection_ident EntrySubstate>] = []; - } - }; - (SortedIndex, $blueprint_ident:ident, $collection_ident:ident) => { - // There is no wrapper around Index substates - paste::paste! { - pub type [<$blueprint_ident $collection_ident EntrySubstate>] = []; - } - }; - ($unknown_system_substate_type:ident, $blueprint_ident:ident, $collection_ident:ident) => { - paste::paste! { - compile_error!(concat!( - "Unrecognized system substate type: `", - stringify!($unknown_system_substate_type), - "` - expected `Field`, `SystemField`, `KeyValue`, `Index` or `SortedIndex`" - )); - } - }; -} - -macro_rules! generate_collection_substate_content_trait { - (KeyValue, $type:ident, $versioned:ident) => { - impl KVEntryContent for $type { - type VersionedContent = $versioned; - } - }; - (Index, $type:ident, $versioned:ident) => { - impl IndexEntryContent for $type { - type VersionedContent = $versioned; - } - }; - (SortedIndex, $type:ident, $versioned:ident) => { - impl SortedIndexEntryContent for $type { - type VersionedContent = $versioned; - } - }; - ($unknown_system_substate_type:ident, $type:ident, $versioned:ident) => { - paste::paste! { - compile_error!(concat!( - "Unrecognized system collection substate type: `", - stringify!($unknown_system_substate_type), - "` - expected `KeyValue`, `Index` or `SortedIndex`" - )); - } - }; -} - -macro_rules! map_entry_substate_to_kv_entry { - (KeyValue, $entry_substate:ident) => { - paste::paste! { - KVEntry { - value: $entry_substate.value.map(|v| scrypto_encode(&v).unwrap()), - locked: match $entry_substate.mutability { - SubstateMutability::Mutable => true, - SubstateMutability::Immutable => false, - }, - } - } - }; - (Index, $entry_substate:ident) => { - // This code still needs to compile, but it shouldn't be possible to execute - panic!("Not possible to map an Index entry to a KVEntry") - }; - (SortedIndex, $entry_substate:ident) => { - // This code still needs to compile, but it shouldn't be possible to execute - panic!("Not possible to map a SortedIndex entry to a KVEntry") - }; - ($unknown_system_substate_type:ident, $entry_substate:ident) => { - paste::paste! { - compile_error!(concat!( - "Unrecognized system collection substate type: `", - stringify!($unknown_system_substate_type), - "` - expected `KeyValue`, `Index` or `SortedIndex`" - )); - } - }; -} /// Generates types and typed-interfaces for native blueprints and their /// interaction with the substate store. @@ -146,13 +50,38 @@ macro_rules! map_entry_substate_to_kv_entry { /// * For collections, assumes the existence of types called: /// `Key` /// `ValueV1` +/// +/// In future, resolve the `x_type` fields as a $x:tt and then +/// map in other macros into: +/// ``` +/// StaticSingleVersioned, +/// Static { +/// the_type: x, +/// }, +/// Instance { +/// ident: +/// }, +/// StaticMultiVersioned(V1, V2), +/// ``` +#[allow(unused)] macro_rules! declare_native_blueprint_state { ( blueprint_ident: $blueprint_ident:ident, + blueprint_snake_case: $blueprint_property_name:ident, + instance_schema_types: [ + // If no types => instance schema disabled + $( + $instance_type_property_name:ident: { + ident: $instance_type_ident:ident, + } + ),* + $(,)? + ], fields: { $( $field_property_name:ident: { ident: $field_ident:ident, + field_type: StaticSingleVersioned, condition: $field_condition:expr $(,)? // Optional trialing comma } @@ -163,7 +92,10 @@ macro_rules! declare_native_blueprint_state { $( $collection_property_name:ident: $collection_type:ident { entry_ident: $collection_ident:ident, - key_type: $collection_key_type:ty, + key_type: Static { // Will be Static { type: X } or Instance { index: X } + the_type: $collection_key_type:ty, + }, + value_type: StaticSingleVersioned, can_own: $collection_can_own:expr // Collection options for (eg) passing in a property name // of the sorted index parameter for SortedIndex @@ -176,8 +108,10 @@ macro_rules! declare_native_blueprint_state { $(,)? ) => { paste::paste! { - #[allow(unused_imports)] - mod [<$blueprint_ident _models>] { + pub use [<$blueprint_property_name _models>]::*; + + #[allow(unused_imports, dead_code, unused_mut, unused_assignments, unused_variables, unreachable_code)] + mod [<$blueprint_property_name _models>] { use super::*; use sbor::*; use $crate::types::*; @@ -218,11 +152,10 @@ macro_rules! declare_native_blueprint_state { )* //-------------------------------------------------------- - // Node Layout - // (to replace node_layout.rs) + // Node Layout (to replace node_layout.rs) //-------------------------------------------------------- #[repr(u8)] - #[derive(Debug, Clone, Sbor, PartialEq, Eq, Hash, PartialOrd, Ord, FromRepr)] + #[derive(Debug, Clone, Copy, Sbor, PartialEq, Eq, Hash, PartialOrd, Ord, FromRepr)] pub enum [<$blueprint_ident Field>] { $($field_ident,)* } @@ -259,7 +192,7 @@ macro_rules! declare_native_blueprint_state { } #[repr(u8)] - #[derive(Debug, Clone, Sbor, PartialEq, Eq, Hash, PartialOrd, Ord, FromRepr)] + #[derive(Debug, Clone, Copy, Sbor, PartialEq, Eq, Hash, PartialOrd, Ord, FromRepr)] pub enum [<$blueprint_ident Partition>] { Field, $([<$collection_ident $collection_type>],)* @@ -271,7 +204,11 @@ macro_rules! declare_native_blueprint_state { } pub const fn main_partition(&self) -> PartitionNumber { - MAIN_BASE_PARTITION.at_offset(self.offset()).unwrap() + match MAIN_BASE_PARTITION.at_offset(self.offset()) { + // Work around .unwrap() on Option not being const + Some(x) => x, + None => panic!("Offset larger than allowed value") + } } } @@ -301,11 +238,50 @@ macro_rules! declare_native_blueprint_state { $([<$collection_ident $collection_type>]([<$blueprint_ident $collection_ident EntrySubstate>]),)* } + //---------------------- + // Schema + //---------------------- + pub struct [<$blueprint_ident StateSchemaInit>]; + + impl [<$blueprint_ident StateSchemaInit>] { + pub fn create_schema_init( + type_aggregator: &mut TypeAggregator, + ) -> BlueprintStateSchemaInit { + let mut fields = vec![]; + $( + // TODO - Implement instance schema + fields.push(FieldSchema { + field: TypeRef::Static( + type_aggregator.add_child_type_and_descendents::<[]>() + ), + condition: $field_condition, + }); + )* + let mut collections = vec![]; + $( + // TODO - Implement instance schema + collections.push(map_collection_schema!( + $collection_type, + type_aggregator, + $collection_key_type, + [], + $collection_can_own + )); + )* + BlueprintStateSchemaInit { + fields, + collections, + } + } + } + //---------------------- // Object Initialization //---------------------- - /// This doesn't support: + /// Used for initializing blueprint state. + /// + /// Note - this doesn't support: /// * Features /// * Instance schemas /// * Feature-dependent fields @@ -339,7 +315,6 @@ macro_rules! declare_native_blueprint_state { let mut all_collection_entries = BTreeMap::new(); let mut collection_index: u8 = 0; $( - #[allow(unreachable_code)] let this_collection_entries = self.$collection_property_name .into_iter() .map(|(key, entry_substate)| { @@ -388,89 +363,234 @@ macro_rules! declare_native_blueprint_state { } } } - #[allow(unused)] - pub(crate) use [<$blueprint_ident _models>]::*; } } } -// See PackageNativePackage "definition()" -// See "create_bootstrap_package_partitions" in package.rs -// See "globalize_package" in package.rs - -// This macro should: -// * Able to create: -// - VersionedSubstateType -// - LatestSubstateType -// -// * Generate node_layout.rs -// -// And future, it would be cool to support: -// -// * Able to create BlueprintStateSchemaInit -// -> ie FieldSchema (field's type, and Condition) -// -> ie BlueprintCollectionSchema (one of three options, plus: key / value / can_own) -// -// * Able to create APIs for reading/writing individual fields - -// Fields: -// - +#[allow(unused)] pub(crate) use declare_native_blueprint_state; -use radix_engine_common::types::PartitionNumber; - -#[derive(Debug, PartialEq, Eq, Sbor)] -struct PackageRoyaltyFieldV1; - -#[derive(Debug, PartialEq, Eq, Sbor)] -struct PackageBlueprintDefinitionValueV1; -#[derive(Debug, PartialEq, Eq, Sbor)] -struct PackageMyCoolIndexValueV1; +pub(crate) use helper_macros::*; -#[derive(Debug, PartialEq, Eq, Sbor)] -struct PackageMyCoolSortedIndexValueV1; - -use radix_engine_interface::blueprints::package::*; - -declare_native_blueprint_state! { - blueprint_ident: Package, - fields: { - royalty: { - // Generates: - // - PackageRoyaltyField - // - VersionedPackageRoyaltyField - // Must find type called: - // - PackageRoyaltyFieldV1 - ident: Royalty, - condition: Condition::Xyz ( - - ), - } - }, - // Generates static colletion offsets + collection - // Eg PackageCollections::BlueprintDefinition.collection_offset() - // EG PackageCollections::BlueprintDefinition.partition_number() - collections: { - blueprint_definitions: KeyValue { - entry_ident: BlueprintDefinition, - key_type: BlueprintVersion, - can_own: true, - }, - abc: Index { - entry_ident: MyCoolIndex, - key_type: BlueprintVersion, - can_own: true, - }, - def: SortedIndex { - entry_ident: MyCoolSortedIndex, - key_type: BlueprintVersion, - can_own: true, - }, +mod helper_macros { + macro_rules! generate_wrapped_substate_type_alias { + (SystemField, $module_ident:ident, $field_ident:ident) => { + paste::paste! { + pub type [<$module_ident $field_ident FieldSubstate>] = []; + } + }; + (Field, $blueprint_ident:ident, $field_ident:ident) => { + paste::paste! { + pub type [<$blueprint_ident $field_ident FieldSubstate>] = $crate::system::system::FieldSubstate<[]>; + } + }; + (KeyValue, $blueprint_ident:ident, $collection_ident:ident) => { + paste::paste! { + pub type [<$blueprint_ident $collection_ident EntrySubstate>] = $crate::system::system::KeyValueEntrySubstate<[]>; + } + }; + (Index, $blueprint_ident:ident, $collection_ident:ident) => { + // No wrapper around Index substates + paste::paste! { + pub type [<$blueprint_ident $collection_ident EntrySubstate>] = []; + } + }; + (SortedIndex, $blueprint_ident:ident, $collection_ident:ident) => { + // There is no wrapper around Index substates + paste::paste! { + pub type [<$blueprint_ident $collection_ident EntrySubstate>] = []; + } + }; + ($unknown_system_substate_type:ident, $blueprint_ident:ident, $collection_ident:ident) => { + paste::paste! { + compile_error!(concat!( + "Unrecognized system substate type: `", + stringify!($unknown_system_substate_type), + "` - expected `Field`, `SystemField`, `KeyValue`, `Index` or `SortedIndex`" + )); + } + }; } + + #[allow(unused)] + pub(crate) use generate_wrapped_substate_type_alias; + + macro_rules! generate_collection_substate_content_trait { + (KeyValue, $type:ident, $versioned:ident) => { + impl KVEntryContent for $type { + type VersionedContent = $versioned; + } + }; + (Index, $type:ident, $versioned:ident) => { + impl IndexEntryContent for $type { + type VersionedContent = $versioned; + } + }; + (SortedIndex, $type:ident, $versioned:ident) => { + impl SortedIndexEntryContent for $type { + type VersionedContent = $versioned; + } + }; + ($unknown_system_substate_type:ident, $type:ident, $versioned:ident) => { + paste::paste! { + compile_error!(concat!( + "Unrecognized system collection substate type: `", + stringify!($unknown_system_substate_type), + "` - expected `KeyValue`, `Index` or `SortedIndex`" + )); + } + }; + } + + #[allow(unused)] + pub(crate) use generate_collection_substate_content_trait; + + macro_rules! map_collection_schema { + (KeyValue, $aggregator:ident, $collection_key_type:ty, $collection_value_type:ty, $collection_can_own:expr$(,)?) => { + paste::paste! { + BlueprintCollectionSchema::KeyValueStore( + BlueprintKeyValueSchema { + key: TypeRef::Static( + $aggregator.add_child_type_and_descendents::<$collection_key_type>(), + ), + value: TypeRef::Static( + $aggregator.add_child_type_and_descendents::<$collection_value_type>() + ), + can_own: $collection_can_own, + }, + ) + } + }; + (Index, $aggregator:ident, $collection_key_type:ty, $collection_value_type:ty, $collection_can_own:expr$(,)?) => { + BlueprintCollectionSchema::Index( + BlueprintKeyValueSchema { + key: TypeRef::Static( + $aggregator.add_child_type_and_descendents::<$collection_key_type>(), + ), + value: TypeRef::Static( + $aggregator.add_child_type_and_descendents::<$collection_value_type>() + ), + can_own: $collection_can_own, + }, + ) + }; + (SortedIndex, $aggregator:ident, $collection_key_type:ty, $collection_value_type:ty, $collection_can_own:expr$(,)?) => { + BlueprintCollectionSchema::SortedIndex( + BlueprintKeyValueSchema { + key: TypeRef::Static( + $aggregator.add_child_type_and_descendents::<$collection_key_type>(), + ), + value: TypeRef::Static( + $aggregator.add_child_type_and_descendents::<$collection_value_type>() + ), + can_own: $collection_can_own, + }, + ) + }; + ($unknown_system_substate_type:ident, $aggregator:ident, $collection_key_type:ty, $collection_value_type:ty, $collection_can_own:expr$(,)?) => { + paste::paste! { + compile_error!(concat!( + "Unrecognized system collection substate type: `", + stringify!($unknown_system_substate_type), + "` - expected `KeyValue`, `Index` or `SortedIndex`" + )); + } + }; + } + + #[allow(unused)] + pub(crate) use map_collection_schema; + + macro_rules! map_entry_substate_to_kv_entry { + (KeyValue, $entry_substate:ident) => { + paste::paste! { + KVEntry { + value: $entry_substate.value.map(|v| scrypto_encode(&v).unwrap()), + locked: match $entry_substate.mutability { + SubstateMutability::Mutable => true, + SubstateMutability::Immutable => false, + }, + } + } + }; + (Index, $entry_substate:ident) => { + // This code still needs to compile, but it shouldn't be possible to execute + panic!("Not possible to map an Index entry to a KVEntry") + }; + (SortedIndex, $entry_substate:ident) => { + // This code still needs to compile, but it shouldn't be possible to execute + panic!("Not possible to map a SortedIndex entry to a KVEntry") + }; + ($unknown_system_substate_type:ident, $entry_substate:ident) => { + paste::paste! { + compile_error!(concat!( + "Unrecognized system collection substate type: `", + stringify!($unknown_system_substate_type), + "` - expected `KeyValue`, `Index` or `SortedIndex`" + )); + } + }; + } + + #[allow(unused)] + pub(crate) use map_entry_substate_to_kv_entry; } -fn xyz() { - PackageField::Royalty; - PackagePartitionOffset::Fields; +#[cfg(test)] +mod tests { + use super::*; + + // Check that the below compiles + #[derive(Debug, PartialEq, Eq, Sbor)] + pub struct PackageRoyaltyFieldV1; + + #[derive(Debug, PartialEq, Eq, Sbor)] + pub struct PackageBlueprintDefinitionValueV1; + + #[derive(Debug, PartialEq, Eq, Sbor)] + pub struct PackageMyCoolIndexValueV1; + + #[derive(Debug, PartialEq, Eq, Sbor)] + pub struct PackageMyCoolSortedIndexValueV1; + + use radix_engine_interface::blueprints::package::*; + + declare_native_blueprint_state! { + blueprint_ident: Package, + blueprint_snake_case: package, + instance_schema_types: [], + fields: { + royalty: { + ident: Royalty, + field_type: StaticSingleVersioned, + condition: Condition::Always, + } + }, + collections: { + blueprint_definitions: KeyValue { + entry_ident: BlueprintDefinition, + key_type: Static { + the_type: BlueprintVersion, + }, + value_type: StaticSingleVersioned, + can_own: true, + }, + abc: Index { + entry_ident: MyCoolIndex, + key_type: Static { + the_type: BlueprintVersion, + }, + value_type: StaticSingleVersioned, + can_own: true, + }, + def: SortedIndex { + entry_ident: MyCoolSortedIndex, + key_type: Static { + the_type: BlueprintVersion, + }, + value_type: StaticSingleVersioned, + can_own: true, + }, + } + } } diff --git a/radix-engine/src/blueprints/package/mod.rs b/radix-engine/src/blueprints/package/mod.rs index cdc10407e57..dd0fdfe9188 100644 --- a/radix-engine/src/blueprints/package/mod.rs +++ b/radix-engine/src/blueprints/package/mod.rs @@ -1,3 +1,5 @@ mod package; +mod substates; +pub use substates::*; pub use package::*; diff --git a/radix-engine/src/blueprints/package/substates.rs b/radix-engine/src/blueprints/package/substates.rs new file mode 100644 index 00000000000..fc6a36dca61 --- /dev/null +++ b/radix-engine/src/blueprints/package/substates.rs @@ -0,0 +1,42 @@ +use crate::blueprints::macros::*; +use super::*; +use crate::types::*; + +declare_native_blueprint_state!{ + blueprint_ident: Package, + blueprint_snake_case: package, + instance_schema_types: [], + fields: { + royalty: { + ident: RoyaltyAccumulator, + field_type: StaticSingleVersioned, + condition: Condition::Always, + } + }, + collections: { + blueprint_definitions: KeyValue { + entry_ident: BlueprintDefinition, + key_type: Static { + the_type: BlueprintVersionKey, + }, + value_type: StaticSingleVersioned, + can_own: false, + }, + } +} + +//------------- +// Field models +//------------- + +#[derive(Debug, PartialEq, Eq, ScryptoSbor)] +pub struct PackageRoyaltyAccumulatorFieldV1 { + /// The vault for collecting package royalties. + pub royalty_vault: Vault, +} + +//------------- +// Collection models +//------------- + +pub type PackageBlueprintDefinitionValueV1 = BlueprintDefinition; \ No newline at end of file From 69bddd60b071c2f9baaf5b48225f6c8b2226ca3c Mon Sep 17 00:00:00 2001 From: David Edey Date: Thu, 10 Aug 2023 17:46:36 +0100 Subject: [PATCH 08/28] tweak: Add support for different type refs --- radix-engine/src/blueprints/macros.rs | 349 +++++++++++------- .../src/blueprints/package/substates.rs | 143 ++++++- 2 files changed, 346 insertions(+), 146 deletions(-) diff --git a/radix-engine/src/blueprints/macros.rs b/radix-engine/src/blueprints/macros.rs index a4948c022a3..c2aedf14de0 100644 --- a/radix-engine/src/blueprints/macros.rs +++ b/radix-engine/src/blueprints/macros.rs @@ -2,41 +2,49 @@ use crate::system::system::*; use crate::types::*; pub trait FieldContent: Sized { - type VersionedContent: From; + type ActualContent: From; - fn into_locked_substate(self) -> FieldSubstate { + fn into_locked_substate(self) -> FieldSubstate { FieldSubstate::new_locked_field(self.into()) } - fn into_mutable_substate(self) -> FieldSubstate { + fn into_mutable_substate(self) -> FieldSubstate { FieldSubstate::new_field(self.into()) } } -pub trait KVEntryContent: Sized { - type VersionedContent: From; +pub trait KeyContent: Sized { + type ActualContent: From; - fn into_locked_substate(self) -> KeyValueEntrySubstate { + fn into_key(self) -> Self::ActualContent { + self.into() + } +} + +pub trait KeyValueEntryContent: Sized { + type ActualContent: From; + + fn into_locked_substate(self) -> KeyValueEntrySubstate { KeyValueEntrySubstate::entry(self.into()) } - fn into_mutable_substate(self) -> KeyValueEntrySubstate { + fn into_mutable_substate(self) -> KeyValueEntrySubstate { KeyValueEntrySubstate::locked_entry(self.into()) } } pub trait IndexEntryContent: Sized { - type VersionedContent: From; + type ActualContent: From; - fn into_substate(self) -> Self::VersionedContent { + fn into_substate(self) -> Self::ActualContent { self.into() } } pub trait SortedIndexEntryContent: Sized { - type VersionedContent: From; + type ActualContent: From; - fn into_substate(self) -> Self::VersionedContent { + fn into_substate(self) -> Self::ActualContent { self.into() } } @@ -46,10 +54,9 @@ pub trait SortedIndexEntryContent: Sized { /// interaction with the substate store. /// /// * For fields, assumes the existence of a type called: -/// `FieldV1` +/// * `FieldV1` /// * For collections, assumes the existence of types called: -/// `Key` -/// `ValueV1` +/// * `ValueV1` /// /// In future, resolve the `x_type` fields as a $x:tt and then /// map in other macros into: @@ -81,7 +88,7 @@ macro_rules! declare_native_blueprint_state { $( $field_property_name:ident: { ident: $field_ident:ident, - field_type: StaticSingleVersioned, + field_type: $field_type:tt, condition: $field_condition:expr $(,)? // Optional trialing comma } @@ -92,10 +99,8 @@ macro_rules! declare_native_blueprint_state { $( $collection_property_name:ident: $collection_type:ident { entry_ident: $collection_ident:ident, - key_type: Static { // Will be Static { type: X } or Instance { index: X } - the_type: $collection_key_type:ty, - }, - value_type: StaticSingleVersioned, + key_type: $collection_key_type:tt, + value_type: $collection_value_type:tt, can_own: $collection_can_own:expr // Collection options for (eg) passing in a property name // of the sorted index parameter for SortedIndex @@ -124,31 +129,44 @@ macro_rules! declare_native_blueprint_state { // Generate models for each field $( - // TODO: In future, expand this macro to support multi-versioned fields - sbor::define_single_versioned!( - #[derive(Debug, PartialEq, Eq, ScryptoSbor)] - pub enum [] => [<$blueprint_ident $field_ident Field>] = [<$blueprint_ident $field_ident FieldV1>] + generate_content_type_aliases!( + content_trait: FieldContent, + ident_core: [<$blueprint_ident $field_ident Field>], + type [<$blueprint_ident $field_ident FieldContent>] = $field_type + ); + // Part 2 - Generate the common Substate type aliases for FieldContent + generate_system_substate_type_alias!( + Field, + type [<$blueprint_ident $field_ident FieldSubstate>] = WRAPPED [<$blueprint_ident $field_ident FieldContent>] ); - generate_wrapped_substate_type_alias!(Field, $blueprint_ident, $field_ident); - impl FieldContent for [] { - type VersionedContent = Self; - } - impl FieldContent for [<$blueprint_ident $field_ident Field>] { - type VersionedContent = []; - } );* // Generate models for each collection $( - pub type [<$blueprint_ident $collection_ident Key>] = $collection_key_type; - // TODO: In future, expand this macro to support multi-versioned collection values - sbor::define_single_versioned!( - #[derive(Debug, PartialEq, Eq, ScryptoSbor)] - pub enum [] => [<$blueprint_ident $collection_ident Value>] = [<$blueprint_ident $collection_ident ValueV1>] + // Key + // > Set up any Versioned types + // > Set up the _KeyContent alias + // > Set up the KeyContent traits + generate_content_type_aliases!( + content_trait: KeyContent, + ident_core: [<$blueprint_ident $collection_ident KeyInner>], + type [<$blueprint_ident $collection_ident Key>] = $collection_key_type + ); + + // Values + // > Set up any Versioned types + // > Set up the _ValueContent alias + // > Set up the _EntryContent traits + generate_content_type_aliases!( + content_trait: [<$collection_type EntryContent>], + ident_core: [<$blueprint_ident $collection_ident Value>], + type [<$blueprint_ident $collection_ident ValueContent>] = $collection_value_type + ); + // > Set up the _EntrySubstate alias + generate_system_substate_type_alias!( + $collection_type, + type [<$blueprint_ident $collection_ident EntrySubstate>] = WRAPPED [<$blueprint_ident $collection_ident ValueContent>] ); - generate_wrapped_substate_type_alias!($collection_type, $blueprint_ident, $collection_ident); - generate_collection_substate_content_trait!($collection_type, [], Self); - generate_collection_substate_content_trait!($collection_type, [<$blueprint_ident $collection_ident Value>], []); )* //-------------------------------------------------------- @@ -264,7 +282,9 @@ macro_rules! declare_native_blueprint_state { $collection_type, type_aggregator, $collection_key_type, - [], + [<$blueprint_ident $collection_ident Key>], + $collection_value_type, + [<$blueprint_ident $collection_ident ValueContent>], $collection_can_own )); )* @@ -373,133 +393,177 @@ pub(crate) use declare_native_blueprint_state; pub(crate) use helper_macros::*; mod helper_macros { - macro_rules! generate_wrapped_substate_type_alias { - (SystemField, $module_ident:ident, $field_ident:ident) => { - paste::paste! { - pub type [<$module_ident $field_ident FieldSubstate>] = []; - } - }; - (Field, $blueprint_ident:ident, $field_ident:ident) => { - paste::paste! { - pub type [<$blueprint_ident $field_ident FieldSubstate>] = $crate::system::system::FieldSubstate<[]>; - } - }; - (KeyValue, $blueprint_ident:ident, $collection_ident:ident) => { + macro_rules! generate_content_type_aliases { + ( + content_trait: $content_trait:ident, + ident_core: $ident_core:ident, + type $alias:ident = { + kind: StaticSingleVersioned + $(,)? + }$(,)? + ) => { paste::paste! { - pub type [<$blueprint_ident $collection_ident EntrySubstate>] = $crate::system::system::KeyValueEntrySubstate<[]>; - } - }; - (Index, $blueprint_ident:ident, $collection_ident:ident) => { - // No wrapper around Index substates - paste::paste! { - pub type [<$blueprint_ident $collection_ident EntrySubstate>] = []; + sbor::define_single_versioned!( + #[derive(Debug, PartialEq, Eq, ScryptoSbor)] + pub enum [] => $ident_core = [<$ident_core V1>] + ); + pub type $alias = []; + impl $content_trait for $ident_core { + type ActualContent = $alias; + } + impl $content_trait for $alias { + type ActualContent = Self; + } } }; - (SortedIndex, $blueprint_ident:ident, $collection_ident:ident) => { - // There is no wrapper around Index substates + ( + content_trait: $content_trait:ident, + ident_core: $ident_core:ident, + type $alias:ident = { + kind: Static, + the_type: $static_type:ty + $(,)? + }$(,)? + ) => { paste::paste! { - pub type [<$blueprint_ident $collection_ident EntrySubstate>] = []; + pub type $alias = $static_type; + // TODO(David) - Fix this for eg BlueprintVersion being used in multiple keys + // Maybe by making these aliases actually new-types(?) + // impl $content_trait for $alias { + // type ActualContent = Self; + // } } }; - ($unknown_system_substate_type:ident, $blueprint_ident:ident, $collection_ident:ident) => { - paste::paste! { - compile_error!(concat!( - "Unrecognized system substate type: `", - stringify!($unknown_system_substate_type), - "` - expected `Field`, `SystemField`, `KeyValue`, `Index` or `SortedIndex`" - )); + ( + content_trait: $content_trait:ident, + ident_core: $ident_core:ident, + type $alias:ident = { + kind: Instance, + ident: $instance_ident:ident + $(,)? } + ) => { + // Don't output any types for an instance schema }; + // TODO - Add support for some kind of StaticMultiVersioned type here } #[allow(unused)] - pub(crate) use generate_wrapped_substate_type_alias; - - macro_rules! generate_collection_substate_content_trait { - (KeyValue, $type:ident, $versioned:ident) => { - impl KVEntryContent for $type { - type VersionedContent = $versioned; - } + pub(crate) use generate_content_type_aliases; + + macro_rules! generate_system_substate_type_alias { + (SystemField, type $alias:ident = WRAPPED $content:ty$(,)?) => { + // There is no system wrapper around SystemField substates + pub type $alias = $content; }; - (Index, $type:ident, $versioned:ident) => { - impl IndexEntryContent for $type { - type VersionedContent = $versioned; - } + (Field, type $alias:ident = WRAPPED $content:ty$(,)?) => { + pub type $alias = FieldSubstate<$content>; }; - (SortedIndex, $type:ident, $versioned:ident) => { - impl SortedIndexEntryContent for $type { - type VersionedContent = $versioned; - } + (KeyValue, type $alias:ident = WRAPPED $content:ty$(,)?) => { + pub type $alias = KeyValueEntrySubstate<$content>; }; - ($unknown_system_substate_type:ident, $type:ident, $versioned:ident) => { - paste::paste! { - compile_error!(concat!( - "Unrecognized system collection substate type: `", - stringify!($unknown_system_substate_type), - "` - expected `KeyValue`, `Index` or `SortedIndex`" - )); - } + (Index, type $alias:ident = WRAPPED $content:ty$(,)?) => { + // There is no system wrapper around Index substates + pub type $alias = $content; + }; + (SortedIndex, type $alias:ident = WRAPPED $content:ty$(,)?) => { + // There is no system wrapper around SortedIndex substates + pub type $alias = $content; + }; + ($unknown_system_substate_type:ident, type $alias:ident = WRAPPED $content:ty$(,)?) => { + compile_error!(concat!( + "Unrecognized system substate type: `", + stringify!($unknown_system_substate_type), + "` - expected `Field`, `SystemField`, `KeyValue`, `Index` or `SortedIndex`" + )); }; } #[allow(unused)] - pub(crate) use generate_collection_substate_content_trait; + pub(crate) use generate_system_substate_type_alias; macro_rules! map_collection_schema { - (KeyValue, $aggregator:ident, $collection_key_type:ty, $collection_value_type:ty, $collection_can_own:expr$(,)?) => { - paste::paste! { - BlueprintCollectionSchema::KeyValueStore( - BlueprintKeyValueSchema { - key: TypeRef::Static( - $aggregator.add_child_type_and_descendents::<$collection_key_type>(), - ), - value: TypeRef::Static( - $aggregator.add_child_type_and_descendents::<$collection_value_type>() - ), - can_own: $collection_can_own, - }, - ) - } + (KeyValue, $aggregator:ident, $key_type:tt, $key_content_alias:ident, $value_type:tt, $value_content_alias:ident, $can_own:expr$(,)?) => { + BlueprintCollectionSchema::KeyValueStore( + BlueprintKeyValueSchema { + key: map_type_ref!($aggregator, $key_type, $key_content_alias), + value: map_type_ref!($aggregator, $value_type, $value_content_alias), + can_own: $can_own, + }, + ) }; - (Index, $aggregator:ident, $collection_key_type:ty, $collection_value_type:ty, $collection_can_own:expr$(,)?) => { + (Index, $aggregator:ident, $key_type:tt, $key_content_alias:ident, $value_type:tt, $value_content_alias:ident, $can_own:expr$(,)?) => { BlueprintCollectionSchema::Index( BlueprintKeyValueSchema { - key: TypeRef::Static( - $aggregator.add_child_type_and_descendents::<$collection_key_type>(), - ), - value: TypeRef::Static( - $aggregator.add_child_type_and_descendents::<$collection_value_type>() - ), - can_own: $collection_can_own, + key: map_type_ref!($aggregator, $key_type, $key_content_alias), + value: map_type_ref!($aggregator, $value_type, $value_content_alias), + can_own: $can_own, }, ) }; - (SortedIndex, $aggregator:ident, $collection_key_type:ty, $collection_value_type:ty, $collection_can_own:expr$(,)?) => { + (SortedIndex, $aggregator:ident, $key_type:tt, $key_content_alias:ident, $value_type:tt, $value_content_alias:ident, $can_own:expr$(,)?) => { BlueprintCollectionSchema::SortedIndex( BlueprintKeyValueSchema { - key: TypeRef::Static( - $aggregator.add_child_type_and_descendents::<$collection_key_type>(), - ), - value: TypeRef::Static( - $aggregator.add_child_type_and_descendents::<$collection_value_type>() - ), - can_own: $collection_can_own, + key: map_type_ref!($aggregator, $key_type, $key_content_alias), + value: map_type_ref!($aggregator, $value_type, $value_content_alias), + can_own: $can_own, }, ) }; - ($unknown_system_substate_type:ident, $aggregator:ident, $collection_key_type:ty, $collection_value_type:ty, $collection_can_own:expr$(,)?) => { - paste::paste! { - compile_error!(concat!( - "Unrecognized system collection substate type: `", - stringify!($unknown_system_substate_type), - "` - expected `KeyValue`, `Index` or `SortedIndex`" - )); - } + ($unknown_system_substate_type:ident, $aggregator:ident, $collection_key_type:tt, $collection_value_type:tt, $collection_can_own:expr$(,)?) => { + compile_error!(concat!( + "Unrecognized system collection substate type: `", + stringify!($unknown_system_substate_type), + "` - expected `KeyValue`, `Index` or `SortedIndex`" + )); }; } #[allow(unused)] pub(crate) use map_collection_schema; + + macro_rules! map_type_ref { + ( + $aggregator:ident, + { + kind: StaticSingleVersioned + $(,)? + }, + $content_alias:ident$(,)? + ) => { + TypeRef::Static( + $aggregator.add_child_type_and_descendents::<$content_alias>(), + ) + }; + ( + $aggregator:ident, + { + kind: Static, + the_type: $static_type:ty + $(,)? + }, + $content_alias:ident$(,)? + ) => { + TypeRef::Static( + $aggregator.add_child_type_and_descendents::<$content_alias>(), + ) + }; + ( + $aggregator:ident, + { + kind: Instance, + ident: $instance_ident:ident + $(,)? + }, + $content_alias:ident$(,)? + ) => { + compile_error!("Instance schemas not yet supported - close though!") + }; + // TODO - Add support for some kind of StaticMultiVersioned type here + } + + #[allow(unused)] + pub(crate) use map_type_ref; macro_rules! map_entry_substate_to_kv_entry { (KeyValue, $entry_substate:ident) => { @@ -562,33 +626,44 @@ mod tests { fields: { royalty: { ident: Royalty, - field_type: StaticSingleVersioned, + field_type: { + kind: StaticSingleVersioned, + }, condition: Condition::Always, } }, collections: { blueprint_definitions: KeyValue { entry_ident: BlueprintDefinition, - key_type: Static { + key_type: { + kind: Static, the_type: BlueprintVersion, }, - value_type: StaticSingleVersioned, + value_type: { + kind: StaticSingleVersioned, + }, can_own: true, }, abc: Index { entry_ident: MyCoolIndex, - key_type: Static { + key_type: { + kind: Static, the_type: BlueprintVersion, }, - value_type: StaticSingleVersioned, + value_type: { + kind: StaticSingleVersioned, + }, can_own: true, }, def: SortedIndex { entry_ident: MyCoolSortedIndex, - key_type: Static { + key_type: { + kind: Static, the_type: BlueprintVersion, }, - value_type: StaticSingleVersioned, + value_type: { + kind: StaticSingleVersioned, + }, can_own: true, }, } diff --git a/radix-engine/src/blueprints/package/substates.rs b/radix-engine/src/blueprints/package/substates.rs index fc6a36dca61..34f9dff8301 100644 --- a/radix-engine/src/blueprints/package/substates.rs +++ b/radix-engine/src/blueprints/package/substates.rs @@ -9,17 +9,99 @@ declare_native_blueprint_state!{ fields: { royalty: { ident: RoyaltyAccumulator, - field_type: StaticSingleVersioned, + field_type: { + kind: StaticSingleVersioned, + }, condition: Condition::Always, } }, collections: { - blueprint_definitions: KeyValue { - entry_ident: BlueprintDefinition, - key_type: Static { + blueprint_version_definitions: KeyValue { + entry_ident: BlueprintVersionDefinition, + key_type: { + kind: Static, + the_type: BlueprintVersionKey, + }, + value_type: { + kind: StaticSingleVersioned, + }, + can_own: false, + }, + blueprint_version_dependencies: KeyValue { + entry_ident: BlueprintVersionDependencies, + key_type: { + kind: Static, + the_type: BlueprintVersionKey, + }, + value_type: { + kind: StaticSingleVersioned, + }, + can_own: false, + }, + schemas: KeyValue { + entry_ident: Schema, + key_type: { + kind: Static, + the_type: SchemaHash, + }, + value_type: { + kind: StaticSingleVersioned, + }, + can_own: false, + }, + blueprint_version_royalty_configs: KeyValue { + entry_ident: BlueprintVersionRoyaltyConfig, + key_type: { + kind: Static, + the_type: BlueprintVersionKey, + }, + value_type: { + kind: StaticSingleVersioned, + }, + can_own: false, + }, + blueprint_version_auth_configs: KeyValue { + entry_ident: BlueprintVersionAuthConfig, + key_type: { + kind: Static, the_type: BlueprintVersionKey, }, - value_type: StaticSingleVersioned, + value_type: { + kind: StaticSingleVersioned, + }, + can_own: false, + }, + code_vm_type: KeyValue { + entry_ident: CodeVmType, + key_type: { + kind: Static, + the_type: CodeHash, + }, + value_type: { + kind: StaticSingleVersioned, + }, + can_own: false, + }, + code_original_code: KeyValue { + entry_ident: CodeOriginalCode, + key_type: { + kind: Static, + the_type: CodeHash, + }, + value_type: { + kind: StaticSingleVersioned, + }, + can_own: false, + }, + code_instrumented_code: KeyValue { + entry_ident: CodeInstrumentedCode, + key_type: { + kind: Static, + the_type: CodeHash, + }, + value_type: { + kind: StaticSingleVersioned, + }, can_own: false, }, } @@ -35,8 +117,51 @@ pub struct PackageRoyaltyAccumulatorFieldV1 { pub royalty_vault: Vault, } -//------------- -// Collection models -//------------- +//--------------------------------------- +// Collection models - Schemas +//--------------------------------------- + +define_wrapped_hash!( + /// Represents a particular schema under a package + SchemaHash +); -pub type PackageBlueprintDefinitionValueV1 = BlueprintDefinition; \ No newline at end of file +// TODO(David): Change to VersionedSchema when can define a type as not-implicitly-versioned +// TODO: Move to Schema partition when we have it +pub type PackageSchemaValueV1 = ScryptoSchema; + +//--------------------------------------- +// Collection models - By BlueprintVersion +//--------------------------------------- + +pub type PackageBlueprintVersionDefinitionValueV1 = BlueprintDefinition; +pub type PackageBlueprintVersionDependenciesValueV1 = BlueprintDependencies; +pub type PackageBlueprintVersionRoyaltyConfigValueV1 = PackageRoyaltyConfig; +pub type PackageBlueprintVersionAuthConfigValueV1 = AuthConfig; + +//--------------------------------------- +// Collection models - By Code +//--------------------------------------- + +define_wrapped_hash!( + /// Represents a particular instance of code under a package + CodeHash +); + +#[derive(Debug, PartialEq, Eq, ScryptoSbor)] +#[sbor(transparent)] +pub struct PackageCodeVmTypeValueV1 { + pub vm_type: VmType, +} + +#[derive(Debug, PartialEq, Eq, ScryptoSbor)] +#[sbor(transparent)] +pub struct PackageCodeOriginalCodeValueV1 { + pub code: Vec, +} + +#[derive(Debug, PartialEq, Eq, ScryptoSbor)] +#[sbor(transparent)] +pub struct PackageCodeInstrumentedCodeValueV1 { + pub code: Vec, +} \ No newline at end of file From 24aaef813fa627bd2f17908270ba0ca7b42e6f16 Mon Sep 17 00:00:00 2001 From: David Edey Date: Thu, 10 Aug 2023 17:54:30 +0100 Subject: [PATCH 09/28] feature: Partial implementation for Package state --- radix-engine/src/blueprints/macros.rs | 79 ++++++-------- radix-engine/src/blueprints/package/mod.rs | 2 +- .../src/blueprints/package/package.rs | 103 +----------------- .../src/blueprints/package/substates.rs | 6 +- 4 files changed, 43 insertions(+), 147 deletions(-) diff --git a/radix-engine/src/blueprints/macros.rs b/radix-engine/src/blueprints/macros.rs index c2aedf14de0..d4790fadc29 100644 --- a/radix-engine/src/blueprints/macros.rs +++ b/radix-engine/src/blueprints/macros.rs @@ -49,7 +49,6 @@ pub trait SortedIndexEntryContent: Sized { } } - /// Generates types and typed-interfaces for native blueprints and their /// interaction with the substate store. /// @@ -57,7 +56,7 @@ pub trait SortedIndexEntryContent: Sized { /// * `FieldV1` /// * For collections, assumes the existence of types called: /// * `ValueV1` -/// +/// /// In future, resolve the `x_type` fields as a $x:tt and then /// map in other macros into: /// ``` @@ -66,7 +65,7 @@ pub trait SortedIndexEntryContent: Sized { /// the_type: x, /// }, /// Instance { -/// ident: +/// ident: /// }, /// StaticMultiVersioned(V1, V2), /// ``` @@ -444,10 +443,9 @@ mod helper_macros { } ) => { // Don't output any types for an instance schema - }; - // TODO - Add support for some kind of StaticMultiVersioned type here + }; // TODO - Add support for some kind of StaticMultiVersioned type here } - + #[allow(unused)] pub(crate) use generate_content_type_aliases; @@ -478,37 +476,31 @@ mod helper_macros { )); }; } - + #[allow(unused)] pub(crate) use generate_system_substate_type_alias; - + macro_rules! map_collection_schema { (KeyValue, $aggregator:ident, $key_type:tt, $key_content_alias:ident, $value_type:tt, $value_content_alias:ident, $can_own:expr$(,)?) => { - BlueprintCollectionSchema::KeyValueStore( - BlueprintKeyValueSchema { - key: map_type_ref!($aggregator, $key_type, $key_content_alias), - value: map_type_ref!($aggregator, $value_type, $value_content_alias), - can_own: $can_own, - }, - ) + BlueprintCollectionSchema::KeyValueStore(BlueprintKeyValueSchema { + key: map_type_ref!($aggregator, $key_type, $key_content_alias), + value: map_type_ref!($aggregator, $value_type, $value_content_alias), + can_own: $can_own, + }) }; (Index, $aggregator:ident, $key_type:tt, $key_content_alias:ident, $value_type:tt, $value_content_alias:ident, $can_own:expr$(,)?) => { - BlueprintCollectionSchema::Index( - BlueprintKeyValueSchema { - key: map_type_ref!($aggregator, $key_type, $key_content_alias), - value: map_type_ref!($aggregator, $value_type, $value_content_alias), - can_own: $can_own, - }, - ) + BlueprintCollectionSchema::Index(BlueprintKeyValueSchema { + key: map_type_ref!($aggregator, $key_type, $key_content_alias), + value: map_type_ref!($aggregator, $value_type, $value_content_alias), + can_own: $can_own, + }) }; (SortedIndex, $aggregator:ident, $key_type:tt, $key_content_alias:ident, $value_type:tt, $value_content_alias:ident, $can_own:expr$(,)?) => { - BlueprintCollectionSchema::SortedIndex( - BlueprintKeyValueSchema { - key: map_type_ref!($aggregator, $key_type, $key_content_alias), - value: map_type_ref!($aggregator, $value_type, $value_content_alias), - can_own: $can_own, - }, - ) + BlueprintCollectionSchema::SortedIndex(BlueprintKeyValueSchema { + key: map_type_ref!($aggregator, $key_type, $key_content_alias), + value: map_type_ref!($aggregator, $value_type, $value_content_alias), + can_own: $can_own, + }) }; ($unknown_system_substate_type:ident, $aggregator:ident, $collection_key_type:tt, $collection_value_type:tt, $collection_can_own:expr$(,)?) => { compile_error!(concat!( @@ -518,7 +510,7 @@ mod helper_macros { )); }; } - + #[allow(unused)] pub(crate) use map_collection_schema; @@ -531,9 +523,7 @@ mod helper_macros { }, $content_alias:ident$(,)? ) => { - TypeRef::Static( - $aggregator.add_child_type_and_descendents::<$content_alias>(), - ) + TypeRef::Static($aggregator.add_child_type_and_descendents::<$content_alias>()) }; ( $aggregator:ident, @@ -544,9 +534,7 @@ mod helper_macros { }, $content_alias:ident$(,)? ) => { - TypeRef::Static( - $aggregator.add_child_type_and_descendents::<$content_alias>(), - ) + TypeRef::Static($aggregator.add_child_type_and_descendents::<$content_alias>()) }; ( $aggregator:ident, @@ -558,13 +546,12 @@ mod helper_macros { $content_alias:ident$(,)? ) => { compile_error!("Instance schemas not yet supported - close though!") - }; - // TODO - Add support for some kind of StaticMultiVersioned type here + }; // TODO - Add support for some kind of StaticMultiVersioned type here } #[allow(unused)] pub(crate) use map_type_ref; - + macro_rules! map_entry_substate_to_kv_entry { (KeyValue, $entry_substate:ident) => { paste::paste! { @@ -595,7 +582,7 @@ mod helper_macros { } }; } - + #[allow(unused)] pub(crate) use map_entry_substate_to_kv_entry; } @@ -607,18 +594,18 @@ mod tests { // Check that the below compiles #[derive(Debug, PartialEq, Eq, Sbor)] pub struct PackageRoyaltyFieldV1; - + #[derive(Debug, PartialEq, Eq, Sbor)] pub struct PackageBlueprintDefinitionValueV1; - + #[derive(Debug, PartialEq, Eq, Sbor)] pub struct PackageMyCoolIndexValueV1; - + #[derive(Debug, PartialEq, Eq, Sbor)] pub struct PackageMyCoolSortedIndexValueV1; - + use radix_engine_interface::blueprints::package::*; - + declare_native_blueprint_state! { blueprint_ident: Package, blueprint_snake_case: package, @@ -667,5 +654,5 @@ mod tests { can_own: true, }, } - } + } } diff --git a/radix-engine/src/blueprints/package/mod.rs b/radix-engine/src/blueprints/package/mod.rs index dd0fdfe9188..043ea1854da 100644 --- a/radix-engine/src/blueprints/package/mod.rs +++ b/radix-engine/src/blueprints/package/mod.rs @@ -1,5 +1,5 @@ mod package; mod substates; -pub use substates::*; pub use package::*; +pub use substates::*; diff --git a/radix-engine/src/blueprints/package/package.rs b/radix-engine/src/blueprints/package/package.rs index b5077bd8f9c..ef882b7c94b 100644 --- a/radix-engine/src/blueprints/package/package.rs +++ b/radix-engine/src/blueprints/package/package.rs @@ -14,16 +14,10 @@ use native_sdk::resource::NativeVault; use native_sdk::resource::ResourceManager; use radix_engine_interface::api::node_modules::auth::AuthAddresses; use radix_engine_interface::api::node_modules::metadata::MetadataInit; -use radix_engine_interface::api::{ - ClientApi, FieldValue, KVEntry, LockFlags, ObjectModuleId, OBJECT_HANDLE_SELF, -}; +use radix_engine_interface::api::*; pub use radix_engine_interface::blueprints::package::*; use radix_engine_interface::blueprints::resource::{require, Bucket}; -use radix_engine_interface::schema::{ - BlueprintCollectionSchema, BlueprintEventSchemaInit, BlueprintFunctionsSchemaInit, - BlueprintKeyValueSchema, BlueprintSchemaInit, BlueprintStateSchemaInit, FieldSchema, - FunctionSchemaInit, TypeRef, -}; +use radix_engine_interface::schema::*; use sbor::LocalTypeIndex; use syn::Ident; @@ -42,6 +36,8 @@ pub use radix_engine_interface::blueprints::package::{ PackageInstrumentedCodeSubstate, PackageOriginalCodeSubstate, PackageRoyaltyAccumulatorSubstate, }; +use super::*; + pub const PACKAGE_ROYALTY_FEATURE: &str = "package_royalty"; #[derive(Debug, Clone, PartialEq, Eq, ScryptoSbor)] @@ -843,91 +839,7 @@ impl PackageNativePackage { pub fn definition() -> PackageDefinition { let mut aggregator = TypeAggregator::::new(); - let mut fields = Vec::new(); - fields.push(FieldSchema::if_feature( - aggregator.add_child_type_and_descendents::(), - PACKAGE_ROYALTY_FEATURE, - )); - - let mut collections = Vec::new(); - collections.push(BlueprintCollectionSchema::KeyValueStore( - BlueprintKeyValueSchema { - key: TypeRef::Static( - aggregator.add_child_type_and_descendents::(), - ), - value: TypeRef::Static( - aggregator.add_child_type_and_descendents::(), - ), - can_own: false, - }, - )); - collections.push(BlueprintCollectionSchema::KeyValueStore( - BlueprintKeyValueSchema { - key: TypeRef::Static( - aggregator.add_child_type_and_descendents::(), - ), - value: TypeRef::Static( - aggregator.add_child_type_and_descendents::(), - ), - can_own: false, - }, - )); - collections.push(BlueprintCollectionSchema::KeyValueStore( - BlueprintKeyValueSchema { - key: TypeRef::Static(aggregator.add_child_type_and_descendents::()), - value: TypeRef::Static( - aggregator.add_child_type_and_descendents::(), - ), - can_own: false, - }, - )); - collections.push(BlueprintCollectionSchema::KeyValueStore( - BlueprintKeyValueSchema { - key: TypeRef::Static( - aggregator.add_child_type_and_descendents::(), - ), - value: TypeRef::Static( - aggregator.add_child_type_and_descendents::(), - ), - can_own: false, - }, - )); - collections.push(BlueprintCollectionSchema::KeyValueStore( - BlueprintKeyValueSchema { - key: TypeRef::Static( - aggregator.add_child_type_and_descendents::(), - ), - value: TypeRef::Static(aggregator.add_child_type_and_descendents::()), - can_own: false, - }, - )); - collections.push(BlueprintCollectionSchema::KeyValueStore( - BlueprintKeyValueSchema { - key: TypeRef::Static(aggregator.add_child_type_and_descendents::()), - value: TypeRef::Static( - aggregator.add_child_type_and_descendents::(), - ), - can_own: false, - }, - )); - collections.push(BlueprintCollectionSchema::KeyValueStore( - BlueprintKeyValueSchema { - key: TypeRef::Static(aggregator.add_child_type_and_descendents::()), - value: TypeRef::Static( - aggregator.add_child_type_and_descendents::(), - ), - can_own: false, - }, - )); - collections.push(BlueprintCollectionSchema::KeyValueStore( - BlueprintKeyValueSchema { - key: TypeRef::Static(aggregator.add_child_type_and_descendents::()), - value: TypeRef::Static( - aggregator.add_child_type_and_descendents::(), - ), - can_own: false, - }, - )); + let state = PackageStateSchemaInit::create_schema_init(&mut aggregator); let mut functions = BTreeMap::new(); functions.insert( @@ -999,10 +911,7 @@ impl PackageNativePackage { schema: BlueprintSchemaInit { generics: vec![], schema, - state: BlueprintStateSchemaInit { - fields, - collections, - }, + state, events: BlueprintEventSchemaInit::default(), functions: BlueprintFunctionsSchemaInit { functions, diff --git a/radix-engine/src/blueprints/package/substates.rs b/radix-engine/src/blueprints/package/substates.rs index 34f9dff8301..7e7dec5e79b 100644 --- a/radix-engine/src/blueprints/package/substates.rs +++ b/radix-engine/src/blueprints/package/substates.rs @@ -1,8 +1,8 @@ -use crate::blueprints::macros::*; use super::*; +use crate::blueprints::macros::*; use crate::types::*; -declare_native_blueprint_state!{ +declare_native_blueprint_state! { blueprint_ident: Package, blueprint_snake_case: package, instance_schema_types: [], @@ -164,4 +164,4 @@ pub struct PackageCodeOriginalCodeValueV1 { #[sbor(transparent)] pub struct PackageCodeInstrumentedCodeValueV1 { pub code: Vec, -} \ No newline at end of file +} From b04288bbbce494bbec4abcedb4cdbb45d23fc382 Mon Sep 17 00:00:00 2001 From: David Edey Date: Thu, 10 Aug 2023 18:08:08 +0100 Subject: [PATCH 10/28] tweak: Add some clearer TODOs --- radix-engine-common/src/data/scrypto/custom_schema.rs | 2 +- radix-engine/src/blueprints/macros.rs | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/radix-engine-common/src/data/scrypto/custom_schema.rs b/radix-engine-common/src/data/scrypto/custom_schema.rs index 64b5b74d18e..518524b944a 100644 --- a/radix-engine-common/src/data/scrypto/custom_schema.rs +++ b/radix-engine-common/src/data/scrypto/custom_schema.rs @@ -206,7 +206,7 @@ pub trait HasSchemaHash { fn generate_schema_hash(&self) -> Hash; } -// TODO - change to VersionedScryptoSchema when we use that in substates +// TODO(David) - change to hash the VersionedScryptoSchema when we use that in substates impl HasSchemaHash for ScryptoSchema { fn generate_schema_hash(&self) -> Hash { hash(scrypto_encode(self).unwrap()) diff --git a/radix-engine/src/blueprints/macros.rs b/radix-engine/src/blueprints/macros.rs index d4790fadc29..949f7a672d7 100644 --- a/radix-engine/src/blueprints/macros.rs +++ b/radix-engine/src/blueprints/macros.rs @@ -266,7 +266,7 @@ macro_rules! declare_native_blueprint_state { ) -> BlueprintStateSchemaInit { let mut fields = vec![]; $( - // TODO - Implement instance schema + // TODO(David) - Implement instance schema fields.push(FieldSchema { field: TypeRef::Static( type_aggregator.add_child_type_and_descendents::<[]>() @@ -276,7 +276,7 @@ macro_rules! declare_native_blueprint_state { )* let mut collections = vec![]; $( - // TODO - Implement instance schema + // TODO(David) - Implement instance schema collections.push(map_collection_schema!( $collection_type, type_aggregator, @@ -427,7 +427,8 @@ mod helper_macros { paste::paste! { pub type $alias = $static_type; // TODO(David) - Fix this for eg BlueprintVersion being used in multiple keys - // Maybe by making these aliases actually new-types(?) + // - Maybe by making these aliases actually new-types(?) + // - Or by removing these traits completely perhaps... // impl $content_trait for $alias { // type ActualContent = Self; // } From c0adfeb9adea20d6f959a3e71401394f2469a761 Mon Sep 17 00:00:00 2001 From: David Edey Date: Fri, 11 Aug 2023 11:46:46 +0100 Subject: [PATCH 11/28] impl: Continue package impl of new types --- radix-engine/src/blueprints/macros.rs | 157 +++++-- .../src/blueprints/package/package.rs | 406 ++++++------------ radix-engine/src/system/bootstrap.rs | 4 +- 3 files changed, 252 insertions(+), 315 deletions(-) diff --git a/radix-engine/src/blueprints/macros.rs b/radix-engine/src/blueprints/macros.rs index 949f7a672d7..a9980df5232 100644 --- a/radix-engine/src/blueprints/macros.rs +++ b/radix-engine/src/blueprints/macros.rs @@ -57,17 +57,25 @@ pub trait SortedIndexEntryContent: Sized { /// * For collections, assumes the existence of types called: /// * `ValueV1` /// -/// In future, resolve the `x_type` fields as a $x:tt and then -/// map in other macros into: +/// The types should look something like /// ``` -/// StaticSingleVersioned, -/// Static { +/// { +/// kind: StaticSingleVersioned, +/// } +/// { +/// kind: Static, /// the_type: x, /// }, -/// Instance { -/// ident: +/// { +/// kind: Instance, +/// ident: GenericTypeParameterName, /// }, -/// StaticMultiVersioned(V1, V2), +/// // In future +/// { +/// kind: StaticMultiVersioned, +/// previous_versions: [V1, V2], +/// latest: V3, +/// } /// ``` #[allow(unused)] macro_rules! declare_native_blueprint_state { @@ -119,6 +127,7 @@ macro_rules! declare_native_blueprint_state { use super::*; use sbor::*; use $crate::types::*; + use $crate::track::interface::*; use $crate::errors::RuntimeError; use $crate::system::system::*; use radix_engine_interface::api::*; @@ -220,7 +229,7 @@ macro_rules! declare_native_blueprint_state { PartitionOffset(*self as u8) } - pub const fn main_partition(&self) -> PartitionNumber { + pub const fn as_main_partition(&self) -> PartitionNumber { match MAIN_BASE_PARTITION.at_offset(self.offset()) { // Work around .unwrap() on Option not being const Some(x) => x, @@ -307,7 +316,7 @@ macro_rules! declare_native_blueprint_state { /// * IndexEntries (because the underlying new_object API doesn't support them) pub struct [<$blueprint_ident StateInit>] { $( - pub $field_property_name: [<$blueprint_ident $field_ident FieldSubstate>], + pub $field_property_name: Option<[<$blueprint_ident $field_ident FieldSubstate>]>, )* $( pub $collection_property_name: IndexMap< @@ -321,31 +330,111 @@ macro_rules! declare_native_blueprint_state { pub fn into_system_substates(self) -> (Vec, BTreeMap, KVEntry>>) { let mut field_values = vec![]; $( - let field_content = scrypto_encode(&self.$field_property_name.value).unwrap(); - let locked = match &self.$field_property_name.mutability { - SubstateMutability::Mutable => true, - SubstateMutability::Immutable => false, - }; - field_values.push(FieldValue { - value: field_content, - locked, - }); + { + let field = self.$field_property_name.expect( + concat!( + "The field `", + stringify!($field_property_name), + "` was None. Until the system and macro supports feature-based optional fields, all fields need to be present" + ) + ); + let field_content = scrypto_encode(&field.value).unwrap(); + let locked = match &field.mutability { + SubstateMutability::Mutable => true, + SubstateMutability::Immutable => false, + }; + field_values.push(FieldValue { + value: field_content, + locked, + }); + } )* let mut all_collection_entries = BTreeMap::new(); let mut collection_index: u8 = 0; $( - let this_collection_entries = self.$collection_property_name + { + let this_collection_entries = self.$collection_property_name + .into_iter() + .map(|(key, entry_substate)| { + let key = scrypto_encode(&key).unwrap(); + let value = map_entry_substate_to_kv_entry!($collection_type, entry_substate); + (key, value) + }) + .collect(); + all_collection_entries.insert(collection_index, this_collection_entries); + collection_index += 1; + } + )* + (field_values, all_collection_entries) + } + + /// This is used mostly for flashing + pub fn into_kernel_main_partitions(self) -> NodeSubstates { + // PartitionNumber => SubstateKey => IndexedScryptoValue + let mut partitions: NodeSubstates = BTreeMap::new(); + let (field_values, mut kv_entries) = self.into_system_substates(); + + // Fields + { + let mut field_index = 0u8; + let mut field_partition_substates = BTreeMap::new(); + $({ + let key = SubstateKey::from([<$blueprint_ident Field>]::$field_ident); + let expected_field_index = u8::from([<$blueprint_ident Field>]::$field_ident); + // Double-check they agree + assert_eq!(field_index, expected_field_index); + field_partition_substates.insert( + key, + IndexedScryptoValue::from_typed(&field_values[field_index as usize]), + ); + field_index += 1; + })* + partitions.insert( + [<$blueprint_ident Partition>]::Field.as_main_partition(), + field_partition_substates, + ); + } + + // Each Collection + $({ + let mut collection_index = 0u8; + let collection_kv_entries = kv_entries.remove(&collection_index).unwrap(); + let collection_partition = [<$blueprint_ident Partition>]::[<$collection_ident $collection_type>]; + let collection_partition_substates = collection_kv_entries .into_iter() - .map(|(key, entry_substate)| { - let key = scrypto_encode(&key).unwrap(); - let value = map_entry_substate_to_kv_entry!($collection_type, entry_substate); - (key, value) + .filter_map(|(key_bytes, kv_entry)| { + let substate = match kv_entry { + KVEntry { value: Some(value_bytes), locked: false } => { + KeyValueEntrySubstate::entry( + scrypto_decode::(&value_bytes).unwrap() + ) + } + KVEntry { value: Some(value_bytes), locked: true } => { + KeyValueEntrySubstate::locked_entry( + scrypto_decode::(&value_bytes).unwrap() + ) + } + KVEntry { value: None, locked: true } => { + KeyValueEntrySubstate::locked_empty_entry() + } + KVEntry { value: None, locked: false } => { + return None; + } + }; + + Some(( + SubstateKey::Map(key_bytes), + IndexedScryptoValue::from_typed(&substate) + )) }) .collect(); - all_collection_entries.insert(collection_index, this_collection_entries); - collection_index += 1; - )* - (field_values, all_collection_entries) + partitions.insert( + collection_partition.as_main_partition(), + collection_partition_substates, + ); + })* + + partitions } pub fn into_new_object>(self, api: &mut Y) -> Result { @@ -594,21 +683,21 @@ mod tests { // Check that the below compiles #[derive(Debug, PartialEq, Eq, Sbor)] - pub struct PackageRoyaltyFieldV1; + pub struct TestBlueprintRoyaltyFieldV1; #[derive(Debug, PartialEq, Eq, Sbor)] - pub struct PackageBlueprintDefinitionValueV1; + pub struct TestBlueprintMyCoolKeyValueStoreValueV1; #[derive(Debug, PartialEq, Eq, Sbor)] - pub struct PackageMyCoolIndexValueV1; + pub struct TestBlueprintMyCoolIndexValueV1; #[derive(Debug, PartialEq, Eq, Sbor)] - pub struct PackageMyCoolSortedIndexValueV1; + pub struct TestBlueprintMyCoolSortedIndexValueV1; use radix_engine_interface::blueprints::package::*; declare_native_blueprint_state! { - blueprint_ident: Package, + blueprint_ident: TestBlueprint, blueprint_snake_case: package, instance_schema_types: [], fields: { @@ -621,8 +710,8 @@ mod tests { } }, collections: { - blueprint_definitions: KeyValue { - entry_ident: BlueprintDefinition, + some_key_value_store: KeyValue { + entry_ident: MyCoolKeyValueStore, key_type: { kind: Static, the_type: BlueprintVersion, diff --git a/radix-engine/src/blueprints/package/package.rs b/radix-engine/src/blueprints/package/package.rs index ef882b7c94b..d8859421d67 100644 --- a/radix-engine/src/blueprints/package/package.rs +++ b/radix-engine/src/blueprints/package/package.rs @@ -1,3 +1,4 @@ +use crate::blueprints::macros::*; use crate::blueprints::util::SecurifiedRoleAssignment; use crate::errors::*; use crate::kernel::kernel_api::{KernelApi, KernelSubstateApi}; @@ -474,172 +475,44 @@ impl SecurifiedRoleAssignment for SecurifiedPackage { } pub fn create_bootstrap_package_partitions( - package_structure: PackageStructure, + mut package_state_init: PackageStateInit, metadata: MetadataInit, ) -> NodeSubstates { - let mut partitions: NodeSubstates = BTreeMap::new(); - - { - let blueprints_partition = package_structure - .definitions - .into_iter() - .map(|(blueprint, definition)| { - let key = BlueprintVersionKey { - blueprint, - version: BlueprintVersion::default(), - }; - let value = KeyValueEntrySubstate::locked_entry(definition); - ( - SubstateKey::Map(scrypto_encode(&key).unwrap()), - IndexedScryptoValue::from_typed(&value), - ) - }) - .collect(); - - partitions.insert( - MAIN_BASE_PARTITION - .at_offset(PACKAGE_BLUEPRINTS_PARTITION_OFFSET) - .unwrap(), - blueprints_partition, - ); - }; - - { - let minor_version_configs = package_structure - .dependencies - .into_iter() - .map(|(blueprint, minor_version_config)| { - let key = BlueprintVersionKey { - blueprint, - version: BlueprintVersion::default(), - }; - - let value = KeyValueEntrySubstate::locked_entry(minor_version_config); - ( - SubstateKey::Map(scrypto_encode(&key).unwrap()), - IndexedScryptoValue::from_typed(&value), - ) - }) - .collect(); - - partitions.insert( - MAIN_BASE_PARTITION - .at_offset(PACKAGE_BLUEPRINT_DEPENDENCIES_PARTITION_OFFSET) - .unwrap(), - minor_version_configs, - ); - }; - - { - let schemas_partition = package_structure - .schemas - .into_iter() - .map(|(hash, schema)| { - let value = KeyValueEntrySubstate::locked_entry(schema); - - ( - SubstateKey::Map(scrypto_encode(&hash).unwrap()), - IndexedScryptoValue::from_typed(&value), - ) - }) - .collect(); - - partitions.insert( - MAIN_BASE_PARTITION - .at_offset(PACKAGE_SCHEMAS_PARTITION_OFFSET) - .unwrap(), - schemas_partition, - ); - } - + //----------------- + // MAIN PARTITIONS: + //----------------- + + // TODO: + // - Improve the System ObjectAPI to take field indices (and so support optional) fields + // - Improve the declare_native_blueprint_state! macro to support features + // and so optional field substates, if/when that feature is disabled { - let vm_type_partition = package_structure - .vm_type - .into_iter() - .map(|(hash, code_substate)| { - let value = KeyValueEntrySubstate::locked_entry(code_substate); - ( - SubstateKey::Map(scrypto_encode(&hash).unwrap()), - IndexedScryptoValue::from_typed(&value), - ) - }) - .collect(); - - partitions.insert( - MAIN_BASE_PARTITION - .at_offset(PACKAGE_VM_TYPE_PARTITION_OFFSET) - .unwrap(), - vm_type_partition, + // Native packages disable the package royalty feature + // Due to (current) restrictions on the into_kernel_main_partitions() method, we need to + // temporarily add in a faked royalty substate - to be removed a few lines later + package_state_init.royalty = Some( + PackageRoyaltyAccumulatorField { + royalty_vault: Vault(Own(NodeId([0; NodeId::LENGTH]))), + } + .into_locked_substate(), ); } - { - let original_code_partition = package_structure - .original_code - .into_iter() - .map(|(hash, code_substate)| { - let value = KeyValueEntrySubstate::locked_entry(code_substate); - ( - SubstateKey::Map(scrypto_encode(&hash).unwrap()), - IndexedScryptoValue::from_typed(&value), - ) - }) - .collect(); - - partitions.insert( - MAIN_BASE_PARTITION - .at_offset(PACKAGE_ORIGINAL_CODE_PARTITION_OFFSET) - .unwrap(), - original_code_partition, - ); - } + let mut partitions = package_state_init.into_kernel_main_partitions(); { - let instrumented_code_partition = package_structure - .instrumented_code - .into_iter() - .map(|(hash, code_substate)| { - let value = KeyValueEntrySubstate::locked_entry(code_substate); - ( - SubstateKey::Map(scrypto_encode(&hash).unwrap()), - IndexedScryptoValue::from_typed(&value), - ) - }) - .collect(); - - partitions.insert( - MAIN_BASE_PARTITION - .at_offset(PACKAGE_INSTRUMENTED_CODE_PARTITION_OFFSET) - .unwrap(), - instrumented_code_partition, - ); - } - - { - let auth_partition = package_structure - .auth_configs - .into_iter() - .map(|(blueprint, auth_template)| { - let key = BlueprintVersionKey { - blueprint, - version: BlueprintVersion::default(), - }; - let value = KeyValueEntrySubstate::locked_entry(auth_template); - ( - SubstateKey::Map(scrypto_encode(&key).unwrap()), - IndexedScryptoValue::from_typed(&value), - ) - }) - .collect(); - - partitions.insert( - MAIN_BASE_PARTITION - .at_offset(PACKAGE_AUTH_TEMPLATE_PARTITION_OFFSET) - .unwrap(), - auth_partition, - ); + // Remove the Royalty field because that feature is turned off + let field_partition = partitions + .get_mut(&PackagePartition::Field.as_main_partition()) + .unwrap(); + field_partition + .remove_entry(&PackageField::Royalty.into()) + .unwrap(); } + //------------------- + // MODULE PARTITIONS: + //------------------- { let mut metadata_partition = BTreeMap::new(); for (key, value) in metadata.data { @@ -661,6 +534,9 @@ pub fn create_bootstrap_package_partitions( partitions.insert(METADATA_BASE_PARTITION, metadata_partition); } + //------------------- + // SYSTEM PARTITIONS: + //------------------- { partitions.insert( TYPE_INFO_FIELD_PARTITION, @@ -687,7 +563,7 @@ pub fn create_bootstrap_package_partitions( fn globalize_package( package_address_reservation: Option, - package_structure: PackageStructure, + mut package_state_init: PackageStateInit, metadata: Own, role_assignment: RoleAssignment, api: &mut Y, @@ -695,118 +571,19 @@ fn globalize_package( where Y: ClientApi, { - let mut kv_entries: BTreeMap, KVEntry>> = BTreeMap::new(); - - let vault = ResourceManager(XRD).new_empty_vault(api)?; - let royalty = PackageRoyaltyAccumulatorSubstate { - royalty_vault: Vault(vault), - }; - - { - let mut definition_partition = BTreeMap::new(); - for (blueprint, definition) in package_structure.definitions { - let key = BlueprintVersionKey::new_default(blueprint); - let entry = KVEntry { - value: Some(scrypto_encode(&definition).unwrap()), - locked: true, - }; - definition_partition.insert(scrypto_encode(&key).unwrap(), entry); + package_state_init.royalty = Some( + PackageRoyaltyAccumulatorField { + royalty_vault: Vault(ResourceManager(XRD).new_empty_vault(api)?), } - kv_entries.insert(0u8, definition_partition); - } - - { - let mut dependency_partition = BTreeMap::new(); - for (blueprint, dependencies) in package_structure.dependencies { - let key = BlueprintVersionKey::new_default(blueprint); - let entry = KVEntry { - value: Some(scrypto_encode(&dependencies).unwrap()), - locked: true, - }; - dependency_partition.insert(scrypto_encode(&key).unwrap(), entry); - } - kv_entries.insert(1u8, dependency_partition); - } - - { - let mut schemas_partition = BTreeMap::new(); - for (hash, schema) in package_structure.schemas { - let entry = KVEntry { - value: Some(scrypto_encode(&schema).unwrap()), - locked: true, - }; - schemas_partition.insert(scrypto_encode(&hash).unwrap(), entry); - } - kv_entries.insert(2u8, schemas_partition); - } - - { - let mut package_royalties_partition = BTreeMap::new(); - for (blueprint, package_royalty) in package_structure.package_royalties { - let key = BlueprintVersionKey::new_default(blueprint); - let entry = KVEntry { - value: Some(scrypto_encode(&package_royalty).unwrap()), - locked: true, - }; - package_royalties_partition.insert(scrypto_encode(&key).unwrap(), entry); - } - kv_entries.insert(3u8, package_royalties_partition); - } - - { - let mut auth_partition = BTreeMap::new(); - for (blueprint, auth_config) in package_structure.auth_configs { - let key = BlueprintVersionKey::new_default(blueprint); - let entry = KVEntry { - value: Some(scrypto_encode(&auth_config).unwrap()), - locked: true, - }; - auth_partition.insert(scrypto_encode(&key).unwrap(), entry); - } - kv_entries.insert(4u8, auth_partition); - } - - { - let mut vm_type_partition = BTreeMap::new(); - for (hash, code_substate) in package_structure.vm_type { - let entry = KVEntry { - value: Some(scrypto_encode(&code_substate).unwrap()), - locked: true, - }; - vm_type_partition.insert(scrypto_encode(&hash).unwrap(), entry); - } - kv_entries.insert(5u8, vm_type_partition); - } - - { - let mut original_code_partition = BTreeMap::new(); - for (hash, code_substate) in package_structure.original_code { - let entry = KVEntry { - value: Some(scrypto_encode(&code_substate).unwrap()), - locked: true, - }; - original_code_partition.insert(scrypto_encode(&hash).unwrap(), entry); - } - kv_entries.insert(6u8, original_code_partition); - } - - { - let mut instrumented_code_partition = BTreeMap::new(); - for (hash, code_substate) in package_structure.instrumented_code { - let entry = KVEntry { - value: Some(scrypto_encode(&code_substate).unwrap()), - locked: true, - }; - instrumented_code_partition.insert(scrypto_encode(&hash).unwrap(), entry); - } - kv_entries.insert(7u8, instrumented_code_partition); - } + .into_locked_substate(), + ); + let (fields, kv_entries) = package_state_init.into_system_substates(); let package_object = api.new_object( PACKAGE_BLUEPRINT, vec![PACKAGE_ROYALTY_FEATURE], None, - vec![FieldValue::immutable(&royalty)], + fields, kv_entries, )?; @@ -1007,11 +784,11 @@ impl PackageNativePackage { } } - pub fn validate_and_build_package_structure( + pub fn validate_and_build_package_state_init( definition: PackageDefinition, vm_type: VmType, original_code: Vec, - ) -> Result { + ) -> Result { // Validate schema validate_package_schema(definition.blueprints.values().map(|s| &s.schema)) .map_err(|e| RuntimeError::ApplicationError(ApplicationError::PackageError(e)))?; @@ -1149,18 +926,89 @@ impl PackageNativePackage { } }; - let package_structure = PackageStructure { - definitions, - dependencies, - schemas, - vm_type: vm_type_substates, - original_code: original_code_substates, - instrumented_code: instrumented_code_substates, - auth_configs, - package_royalties, + let package_state_init = PackageStateInit { + royalty: None, // This is added later, for globalized packages, + blueprint_version_definitions: definitions + .into_iter() + .map(|(blueprint_name, blueprint_definition)| { + ( + BlueprintVersionKey::new_default(blueprint_name), + blueprint_definition.into_locked_substate(), + ) + }) + .collect(), + blueprint_version_dependencies: dependencies + .into_iter() + .map(|(blueprint_name, blueprint_dependencies)| { + ( + BlueprintVersionKey::new_default(blueprint_name), + blueprint_dependencies.into_locked_substate(), + ) + }) + .collect(), + schemas: schemas + .into_iter() + .map(|(schema_hash, schema)| { + (SchemaHash::from(schema_hash), schema.into_locked_substate()) + }) + .collect(), + blueprint_version_royalty_configs: package_royalties + .into_iter() + .map(|(blueprint_name, royalty_config)| { + ( + BlueprintVersionKey::new_default(blueprint_name), + royalty_config.into_locked_substate(), + ) + }) + .collect(), + blueprint_version_auth_configs: auth_configs + .into_iter() + .map(|(blueprint_name, auth_config)| { + ( + BlueprintVersionKey::new_default(blueprint_name), + auth_config.into_locked_substate(), + ) + }) + .collect(), + code_vm_type: vm_type_substates + .into_iter() + .map(|(code_hash, vm_type)| { + ( + CodeHash::from(code_hash), + PackageCodeVmTypeValue { + vm_type: vm_type.vm_type, + } + .into_locked_substate(), + ) + }) + .collect(), + code_original_code: original_code_substates + .into_iter() + .map(|(code_hash, original_code)| { + ( + CodeHash::from(code_hash), + PackageCodeOriginalCodeValue { + code: original_code.code, + } + .into_locked_substate(), + ) + }) + .collect(), + code_instrumented_code: instrumented_code_substates + .into_iter() + .map(|(code_hash, instrumented_code)| { + ( + CodeHash::from(code_hash), + PackageCodeInstrumentedCodeValue { + code: instrumented_code.code, + } + .into_locked_substate(), + ) + }) + .collect(), }; - Ok(package_structure) + Ok(package_state_init) } pub(crate) fn publish_native( @@ -1174,7 +1022,7 @@ impl PackageNativePackage { Y: ClientApi, { validate_royalties(&definition, api)?; - let package_structure = Self::validate_and_build_package_structure( + let package_state_init = Self::validate_and_build_package_state_init( definition, VmType::Native, native_package_code_id.to_be_bytes().to_vec(), @@ -1184,7 +1032,7 @@ impl PackageNativePackage { globalize_package( package_address, - package_structure, + package_state_init, metadata, role_assignment, api, @@ -1202,7 +1050,7 @@ impl PackageNativePackage { { validate_royalties(&definition, api)?; let package_structure = - Self::validate_and_build_package_structure(definition, VmType::ScryptoV1, code)?; + Self::validate_and_build_package_state_init(definition, VmType::ScryptoV1, code)?; let (address_reservation, address) = api.allocate_global_address(BlueprintId { package_address: PACKAGE_PACKAGE, @@ -1243,7 +1091,7 @@ impl PackageNativePackage { { validate_royalties(&definition, api)?; let package_structure = - Self::validate_and_build_package_structure(definition, VmType::ScryptoV1, code)?; + Self::validate_and_build_package_state_init(definition, VmType::ScryptoV1, code)?; let metadata = Metadata::create_with_data(metadata_init, api)?; let role_assignment = SecurifiedPackage::create_advanced(owner_role, api)?; diff --git a/radix-engine/src/system/bootstrap.rs b/radix-engine/src/system/bootstrap.rs index b3738cb7fad..c87b1b6f6b3 100644 --- a/radix-engine/src/system/bootstrap.rs +++ b/radix-engine/src/system/bootstrap.rs @@ -470,14 +470,14 @@ pub fn create_system_bootstrap_flash( for (address, definition, native_code_id, metadata_init) in package_flashes { let partitions = { - let package_structure = PackageNativePackage::validate_and_build_package_structure( + let package_state_init = PackageNativePackage::validate_and_build_package_state_init( definition, VmType::Native, native_code_id.to_be_bytes().to_vec(), ) .expect("Invalid Package Package definition"); - create_bootstrap_package_partitions(package_structure, metadata_init) + create_bootstrap_package_partitions(package_state_init, metadata_init) }; for (partition_num, partition_substates) in partitions { From f6b11f2d231d48b218c4088ddc52ecb0baa7fd5a Mon Sep 17 00:00:00 2001 From: David Edey Date: Fri, 11 Aug 2023 12:14:30 +0100 Subject: [PATCH 12/28] tweak: Change to using new types for keys and content --- radix-engine/src/blueprints/macros.rs | 132 ++++++++++-------- .../src/blueprints/package/package.rs | 19 +-- 2 files changed, 86 insertions(+), 65 deletions(-) diff --git a/radix-engine/src/blueprints/macros.rs b/radix-engine/src/blueprints/macros.rs index a9980df5232..37698dbebe2 100644 --- a/radix-engine/src/blueprints/macros.rs +++ b/radix-engine/src/blueprints/macros.rs @@ -1,50 +1,40 @@ use crate::system::system::*; use crate::types::*; -pub trait FieldContent: Sized { - type ActualContent: From; - - fn into_locked_substate(self) -> FieldSubstate { +pub trait FieldContent>: Sized { + fn into_locked_substate(self) -> FieldSubstate { FieldSubstate::new_locked_field(self.into()) } - fn into_mutable_substate(self) -> FieldSubstate { + fn into_mutable_substate(self) -> FieldSubstate { FieldSubstate::new_field(self.into()) } } -pub trait KeyContent: Sized { - type ActualContent: From; - - fn into_key(self) -> Self::ActualContent { +pub trait KeyContent>: Sized { + fn into_key(self) -> ActualContent { self.into() } } -pub trait KeyValueEntryContent: Sized { - type ActualContent: From; - - fn into_locked_substate(self) -> KeyValueEntrySubstate { +pub trait KeyValueEntryContent>: Sized { + fn into_locked_substate(self) -> KeyValueEntrySubstate { KeyValueEntrySubstate::entry(self.into()) } - fn into_mutable_substate(self) -> KeyValueEntrySubstate { + fn into_mutable_substate(self) -> KeyValueEntrySubstate { KeyValueEntrySubstate::locked_entry(self.into()) } } -pub trait IndexEntryContent: Sized { - type ActualContent: From; - - fn into_substate(self) -> Self::ActualContent { +pub trait IndexEntryContent>: Sized { + fn into_substate(self) -> ActualContent { self.into() } } -pub trait SortedIndexEntryContent: Sized { - type ActualContent: From; - - fn into_substate(self) -> Self::ActualContent { +pub trait SortedIndexEntryContent>: Sized { + fn into_substate(self) -> ActualContent { self.into() } } @@ -97,10 +87,10 @@ macro_rules! declare_native_blueprint_state { ident: $field_ident:ident, field_type: $field_type:tt, condition: $field_condition:expr - $(,)? // Optional trialing comma + $(,)? // Optional trailing comma } ),* - $(,)? // Optional trialing comma + $(,)? // Optional trailing comma }, collections: { $( @@ -112,10 +102,10 @@ macro_rules! declare_native_blueprint_state { // Collection options for (eg) passing in a property name // of the sorted index parameter for SortedIndex $(, options: $collection_options:tt)? - $(,)? // Optional trialing comma + $(,)? // Optional trailing comma } ),* - $(,)? // Optional trialing comma + $(,)? // Optional trailing comma } $(,)? ) => { @@ -137,12 +127,19 @@ macro_rules! declare_native_blueprint_state { // Generate models for each field $( - generate_content_type_aliases!( + // Value + // > Set up Versioned types (if relevant). Assumes __FieldV1 exists and then creates + // - Versioned__Field + // - __Field (alias for __FieldV1) + // > Set up the (transparent) _FieldContent new type for the content of the field + // > Set up the FieldContent trait for anything which can be resolved into the field content + generate_content_type!( content_trait: FieldContent, ident_core: [<$blueprint_ident $field_ident Field>], - type [<$blueprint_ident $field_ident FieldContent>] = $field_type + #[derive(Debug, PartialEq, Eq, ScryptoSbor)] + struct [<$blueprint_ident $field_ident FieldContent>] = $field_type ); - // Part 2 - Generate the common Substate type aliases for FieldContent + // > Set up the _FieldSubstate alias for the system-wrapped substate generate_system_substate_type_alias!( Field, type [<$blueprint_ident $field_ident FieldSubstate>] = WRAPPED [<$blueprint_ident $field_ident FieldContent>] @@ -152,25 +149,32 @@ macro_rules! declare_native_blueprint_state { // Generate models for each collection $( // Key - // > Set up any Versioned types - // > Set up the _KeyContent alias - // > Set up the KeyContent traits - generate_content_type_aliases!( + // > Set up Versioned types (if relevant). Assumes __KeyInnerV1 exists and then creates + // - Versioned__KeyInner + // - __KeyInner (alias for __KeyInnerV1) + // > Create the (transparent) _Key new type for the key + // > Set up the KeyContent traits for anything which can be resolved into a key + generate_content_type!( content_trait: KeyContent, ident_core: [<$blueprint_ident $collection_ident KeyInner>], - type [<$blueprint_ident $collection_ident Key>] = $collection_key_type + #[derive(Debug, Clone, Hash, PartialEq, Eq, ScryptoSbor)] + #[sbor(transparent_name)] + struct [<$blueprint_ident $collection_ident Key>] = $collection_key_type ); // Values - // > Set up any Versioned types - // > Set up the _ValueContent alias + // > Set up Versioned types (if relevant). Assumes __ValueV1 exists and then creates + // - Versioned__Value + // - __Value (alias for __ValueV1) + // > Set up the (transparent) _ValueContent new type for the value content // > Set up the _EntryContent traits - generate_content_type_aliases!( + generate_content_type!( content_trait: [<$collection_type EntryContent>], ident_core: [<$blueprint_ident $collection_ident Value>], - type [<$blueprint_ident $collection_ident ValueContent>] = $collection_value_type + #[derive(Debug, PartialEq, Eq, ScryptoSbor)] + struct [<$blueprint_ident $collection_ident ValueContent>] = $collection_value_type ); - // > Set up the _EntrySubstate alias + // > Set up the _EntrySubstate alias for the system-wrapped substate generate_system_substate_type_alias!( $collection_type, type [<$blueprint_ident $collection_ident EntrySubstate>] = WRAPPED [<$blueprint_ident $collection_ident ValueContent>] @@ -481,52 +485,66 @@ pub(crate) use declare_native_blueprint_state; pub(crate) use helper_macros::*; mod helper_macros { - macro_rules! generate_content_type_aliases { + macro_rules! generate_content_type { ( content_trait: $content_trait:ident, ident_core: $ident_core:ident, - type $alias:ident = { + $(#[$attributes:meta])* + struct $content_type_name:ident = { kind: StaticSingleVersioned $(,)? }$(,)? ) => { paste::paste! { sbor::define_single_versioned!( - #[derive(Debug, PartialEq, Eq, ScryptoSbor)] + $(#[$attributes])* pub enum [] => $ident_core = [<$ident_core V1>] ); - pub type $alias = []; - impl $content_trait for $ident_core { - type ActualContent = $alias; + $(#[$attributes])* + #[sbor(transparent)] + pub struct $content_type_name(pub []); + impl From<[]> for $content_type_name { + fn from(value: []) -> Self { + Self(value) + } } - impl $content_trait for $alias { - type ActualContent = Self; + impl $content_trait<$content_type_name> for [] {} + // Also add impls from the "latest" type + impl From<$ident_core> for $content_type_name { + fn from(value: $ident_core) -> Self { + Self(value.into()) + } } + impl $content_trait<$content_type_name> for $ident_core {} } }; ( content_trait: $content_trait:ident, ident_core: $ident_core:ident, - type $alias:ident = { + $(#[$attributes:meta])* + struct $content_type_name:ident = { kind: Static, the_type: $static_type:ty $(,)? }$(,)? ) => { paste::paste! { - pub type $alias = $static_type; - // TODO(David) - Fix this for eg BlueprintVersion being used in multiple keys - // - Maybe by making these aliases actually new-types(?) - // - Or by removing these traits completely perhaps... - // impl $content_trait for $alias { - // type ActualContent = Self; - // } + $(#[$attributes])* + #[sbor(transparent)] + pub struct $content_type_name(pub $static_type); + impl From<$static_type> for $content_type_name { + fn from(value: $static_type) -> Self { + Self(value) + } + } + impl $content_trait<$content_type_name> for $static_type {} } }; ( content_trait: $content_trait:ident, ident_core: $ident_core:ident, - type $alias:ident = { + $(#[$attributes:meta])* + struct $alias:ident = { kind: Instance, ident: $instance_ident:ident $(,)? @@ -537,7 +555,7 @@ mod helper_macros { } #[allow(unused)] - pub(crate) use generate_content_type_aliases; + pub(crate) use generate_content_type; macro_rules! generate_system_substate_type_alias { (SystemField, type $alias:ident = WRAPPED $content:ty$(,)?) => { diff --git a/radix-engine/src/blueprints/package/package.rs b/radix-engine/src/blueprints/package/package.rs index d8859421d67..61f03089cfd 100644 --- a/radix-engine/src/blueprints/package/package.rs +++ b/radix-engine/src/blueprints/package/package.rs @@ -932,7 +932,7 @@ impl PackageNativePackage { .into_iter() .map(|(blueprint_name, blueprint_definition)| { ( - BlueprintVersionKey::new_default(blueprint_name), + BlueprintVersionKey::new_default(blueprint_name).into_key(), blueprint_definition.into_locked_substate(), ) }) @@ -941,7 +941,7 @@ impl PackageNativePackage { .into_iter() .map(|(blueprint_name, blueprint_dependencies)| { ( - BlueprintVersionKey::new_default(blueprint_name), + BlueprintVersionKey::new_default(blueprint_name).into_key(), blueprint_dependencies.into_locked_substate(), ) }) @@ -949,14 +949,17 @@ impl PackageNativePackage { schemas: schemas .into_iter() .map(|(schema_hash, schema)| { - (SchemaHash::from(schema_hash), schema.into_locked_substate()) + ( + SchemaHash::from(schema_hash).into_key(), + schema.into_locked_substate(), + ) }) .collect(), blueprint_version_royalty_configs: package_royalties .into_iter() .map(|(blueprint_name, royalty_config)| { ( - BlueprintVersionKey::new_default(blueprint_name), + BlueprintVersionKey::new_default(blueprint_name).into_key(), royalty_config.into_locked_substate(), ) }) @@ -965,7 +968,7 @@ impl PackageNativePackage { .into_iter() .map(|(blueprint_name, auth_config)| { ( - BlueprintVersionKey::new_default(blueprint_name), + BlueprintVersionKey::new_default(blueprint_name).into_key(), auth_config.into_locked_substate(), ) }) @@ -974,7 +977,7 @@ impl PackageNativePackage { .into_iter() .map(|(code_hash, vm_type)| { ( - CodeHash::from(code_hash), + CodeHash::from(code_hash).into_key(), PackageCodeVmTypeValue { vm_type: vm_type.vm_type, } @@ -986,7 +989,7 @@ impl PackageNativePackage { .into_iter() .map(|(code_hash, original_code)| { ( - CodeHash::from(code_hash), + CodeHash::from(code_hash).into_key(), PackageCodeOriginalCodeValue { code: original_code.code, } @@ -998,7 +1001,7 @@ impl PackageNativePackage { .into_iter() .map(|(code_hash, instrumented_code)| { ( - CodeHash::from(code_hash), + CodeHash::from(code_hash).into_key(), PackageCodeInstrumentedCodeValue { code: instrumented_code.code, } From a615c68a588f3eed4f07a2d648829246ffd40078 Mon Sep 17 00:00:00 2001 From: David Edey Date: Sat, 12 Aug 2023 01:33:42 +0100 Subject: [PATCH 13/28] wip: Continue fixing for package --- .../src/data/scrypto/custom_schema.rs | 6 +- .../src/types/type_identifier.rs | 11 +- .../src/blueprints/package/substates.rs | 59 ++------- .../src/types/node_layout.rs | 29 ----- .../src/typed_substate_layout.rs | 115 ++---------------- .../tests/schema_sanity_check.rs | 2 +- radix-engine/src/blueprints/macros.rs | 74 ++++++++++- .../src/blueprints/package/package.rs | 65 +++------- .../src/blueprints/package/substates.rs | 30 +++-- radix-engine/src/system/system.rs | 2 +- radix-engine/src/system/system_callback.rs | 2 +- .../src/transaction/system_structure.rs | 8 +- radix-engine/src/utils/package_extractor.rs | 6 +- radix-engine/src/vm/scrypto_vm.rs | 3 +- radix-engine/src/vm/vm.rs | 17 ++- radix-engine/src/vm/wasm/traits.rs | 3 +- radix-engine/src/vm/wasm/wasmer.rs | 9 +- radix-engine/src/vm/wasm/wasmi.rs | 9 +- scrypto-unit/src/test_runner.rs | 22 ++-- simulator/src/ledger/dumper.rs | 5 +- simulator/src/resim/cmd_publish.rs | 2 - simulator/src/resim/error.rs | 3 +- simulator/src/resim/mod.rs | 9 +- 23 files changed, 189 insertions(+), 302 deletions(-) diff --git a/radix-engine-common/src/data/scrypto/custom_schema.rs b/radix-engine-common/src/data/scrypto/custom_schema.rs index 518524b944a..e5db12a388b 100644 --- a/radix-engine-common/src/data/scrypto/custom_schema.rs +++ b/radix-engine-common/src/data/scrypto/custom_schema.rs @@ -203,13 +203,13 @@ impl CustomSchema for ScryptoCustomSchema { } pub trait HasSchemaHash { - fn generate_schema_hash(&self) -> Hash; + fn generate_schema_hash(&self) -> SchemaHash; } // TODO(David) - change to hash the VersionedScryptoSchema when we use that in substates impl HasSchemaHash for ScryptoSchema { - fn generate_schema_hash(&self) -> Hash { - hash(scrypto_encode(self).unwrap()) + fn generate_schema_hash(&self) -> SchemaHash { + SchemaHash::from(hash(scrypto_encode(self).unwrap())) } } diff --git a/radix-engine-common/src/types/type_identifier.rs b/radix-engine-common/src/types/type_identifier.rs index 34b58ddea0e..86aa8296530 100644 --- a/radix-engine-common/src/types/type_identifier.rs +++ b/radix-engine-common/src/types/type_identifier.rs @@ -1,6 +1,9 @@ -use crate::crypto::Hash; -use crate::Sbor; -use sbor::LocalTypeIndex; +use crate::internal_prelude::*; + +define_wrapped_hash!( + /// Represents a particular schema under a package + SchemaHash +); #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Sbor)] -pub struct TypeIdentifier(pub Hash, pub LocalTypeIndex); +pub struct TypeIdentifier(pub SchemaHash, pub LocalTypeIndex); diff --git a/radix-engine-interface/src/blueprints/package/substates.rs b/radix-engine-interface/src/blueprints/package/substates.rs index 8de3fb951a9..206985fc522 100644 --- a/radix-engine-interface/src/blueprints/package/substates.rs +++ b/radix-engine-interface/src/blueprints/package/substates.rs @@ -1,12 +1,9 @@ use crate::blueprints::package::BlueprintType; +use crate::prelude::*; use crate::schema::*; use crate::types::*; use crate::*; use radix_engine_common::crypto::Hash; -use radix_engine_interface::blueprints::resource::Vault; -use sbor::rust::fmt; -use sbor::rust::fmt::{Debug, Formatter}; -use sbor::rust::prelude::*; pub const PACKAGE_CODE_ID: u64 = 0u64; pub const RESOURCE_CODE_ID: u64 = 1u64; @@ -32,57 +29,17 @@ pub const PACKAGE_VM_TYPE_PARTITION_OFFSET: PartitionOffset = PartitionOffset(6u pub const PACKAGE_ORIGINAL_CODE_PARTITION_OFFSET: PartitionOffset = PartitionOffset(7u8); pub const PACKAGE_INSTRUMENTED_CODE_PARTITION_OFFSET: PartitionOffset = PartitionOffset(8u8); +define_wrapped_hash!( + /// Represents a particular instance of code under a package + CodeHash +); + #[derive(Copy, Debug, Clone, PartialEq, Eq, Sbor)] pub enum VmType { Native, ScryptoV1, } -#[derive(Debug, Clone, Sbor, PartialEq, Eq)] -pub struct PackageVmTypeSubstate { - pub vm_type: VmType, -} - -#[derive(Clone, Sbor, PartialEq, Eq)] -pub struct PackageOriginalCodeSubstate { - pub code: Vec, -} - -#[derive(Clone, Sbor, PartialEq, Eq)] -pub struct PackageInstrumentedCodeSubstate { - pub code: Vec, -} - -impl Debug for PackageOriginalCodeSubstate { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - f.debug_struct("PackageOriginalCodeSubstate") - .field("len", &self.code.len()) - .finish() - } -} - -impl Debug for PackageInstrumentedCodeSubstate { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - f.debug_struct("PackageInstrumentedCodeSubstate") - .field("len", &self.code.len()) - .finish() - } -} - -#[derive(Debug, PartialEq, Eq, ScryptoSbor)] -pub struct PackageRoyaltyAccumulatorSubstate { - /// The vault for collecting package royalties. - pub royalty_vault: Vault, -} - -impl Clone for PackageRoyaltyAccumulatorSubstate { - fn clone(&self) -> Self { - Self { - royalty_vault: Vault(self.royalty_vault.0.clone()), - } - } -} - #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Sbor)] pub enum TypePointer { Package(TypeIdentifier), // For static types @@ -143,7 +100,7 @@ pub struct BlueprintDependencies { #[derive(Debug, Clone, PartialEq, Eq, ScryptoSbor)] pub struct PackageExport { - pub code_hash: Hash, + pub code_hash: CodeHash, pub export_name: String, } @@ -226,7 +183,7 @@ pub struct IndexedStateSchema { } impl IndexedStateSchema { - pub fn from_schema(schema_hash: Hash, schema: BlueprintStateSchemaInit) -> Self { + pub fn from_schema(schema_hash: SchemaHash, schema: BlueprintStateSchemaInit) -> Self { let mut partition_offset = 0u8; let mut fields = None; diff --git a/radix-engine-interface/src/types/node_layout.rs b/radix-engine-interface/src/types/node_layout.rs index 728778a0017..d03c8a01b43 100644 --- a/radix-engine-interface/src/types/node_layout.rs +++ b/radix-engine-interface/src/types/node_layout.rs @@ -60,12 +60,6 @@ pub enum ComponentField { State0, } -#[repr(u8)] -#[derive(Debug, Clone, Sbor, PartialEq, Eq, Hash, PartialOrd, Ord, FromRepr)] -pub enum PackageField { - Royalty, -} - #[repr(u8)] #[derive(Debug, Clone, Sbor, PartialEq, Eq, Hash, PartialOrd, Ord, FromRepr)] pub enum FungibleResourceManagerField { @@ -73,28 +67,6 @@ pub enum FungibleResourceManagerField { TotalSupply, } -#[repr(u8)] -#[derive(Debug, Clone, Sbor, PartialEq, Eq, Hash, PartialOrd, Ord, FromRepr)] -pub enum PackagePartitionOffset { - Fields, - Blueprints, - BlueprintDependencies, - Schemas, - RoyaltyConfig, - AuthConfig, - VmType, - OriginalCode, - InstrumentedCode, -} - -impl TryFrom for PackagePartitionOffset { - type Error = (); - - fn try_from(offset: u8) -> Result { - Self::from_repr(offset).ok_or(()) - } -} - #[repr(u8)] #[derive(Debug, Clone, Sbor, PartialEq, Eq, Hash, PartialOrd, Ord, FromRepr)] pub enum NonFungibleResourceManagerPartitionOffset { @@ -321,7 +293,6 @@ substate_key!(TypeInfoField); substate_key!(RoyaltyField); substate_key!(RoleAssignmentField); substate_key!(ComponentField); -substate_key!(PackageField); substate_key!(FungibleResourceManagerField); substate_key!(FungibleVaultField); substate_key!(FungibleBucketField); diff --git a/radix-engine-queries/src/typed_substate_layout.rs b/radix-engine-queries/src/typed_substate_layout.rs index def1cc930ad..3540870a5ab 100644 --- a/radix-engine-queries/src/typed_substate_layout.rs +++ b/radix-engine-queries/src/typed_substate_layout.rs @@ -118,15 +118,7 @@ pub enum TypedMetadataModuleSubstateKey { #[derive(Debug, Clone)] pub enum TypedMainModuleSubstateKey { // Objects - PackageField(PackageField), - PackageBlueprintKey(BlueprintVersionKey), - PackageBlueprintDependenciesKey(BlueprintVersionKey), - PackageSchemaKey(Hash), - PackageRoyaltyKey(BlueprintVersionKey), - PackageAuthTemplateKey(BlueprintVersionKey), - PackageVmTypeKey(Hash), - PackageOriginalCodeKey(Hash), - PackageInstrumentedCodeKey(Hash), + Package(PackageTypedSubstateKey), FungibleResourceField(FungibleResourceManagerField), NonFungibleResourceField(NonFungibleResourceManagerField), NonFungibleResourceData(NonFungibleLocalId), @@ -262,60 +254,10 @@ fn to_typed_object_substate_key_internal( )?) } EntityType::GlobalPackage => { - let partition_offset = PackagePartitionOffset::try_from(partition_offset)?; - match partition_offset { - PackagePartitionOffset::Fields => { - TypedMainModuleSubstateKey::PackageField(PackageField::try_from(substate_key)?) - } - PackagePartitionOffset::Blueprints => { - let key = substate_key.for_map().ok_or(())?; - TypedMainModuleSubstateKey::PackageBlueprintKey( - scrypto_decode(&key).map_err(|_| ())?, - ) - } - PackagePartitionOffset::BlueprintDependencies => { - let key = substate_key.for_map().ok_or(())?; - TypedMainModuleSubstateKey::PackageBlueprintDependenciesKey( - scrypto_decode(&key).map_err(|_| ())?, - ) - } - PackagePartitionOffset::Schemas => { - let key = substate_key.for_map().ok_or(())?; - TypedMainModuleSubstateKey::PackageSchemaKey( - scrypto_decode(&key).map_err(|_| ())?, - ) - } - PackagePartitionOffset::RoyaltyConfig => { - let key = substate_key.for_map().ok_or(())?; - TypedMainModuleSubstateKey::PackageRoyaltyKey( - scrypto_decode(&key).map_err(|_| ())?, - ) - } - PackagePartitionOffset::AuthConfig => { - let key = substate_key.for_map().ok_or(())?; - TypedMainModuleSubstateKey::PackageAuthTemplateKey( - scrypto_decode(&key).map_err(|_| ())?, - ) - } - PackagePartitionOffset::VmType => { - let key = substate_key.for_map().ok_or(())?; - TypedMainModuleSubstateKey::PackageVmTypeKey( - scrypto_decode(&key).map_err(|_| ())?, - ) - } - PackagePartitionOffset::OriginalCode => { - let key = substate_key.for_map().ok_or(())?; - TypedMainModuleSubstateKey::PackageOriginalCodeKey( - scrypto_decode(&key).map_err(|_| ())?, - ) - } - PackagePartitionOffset::InstrumentedCode => { - let key = substate_key.for_map().ok_or(())?; - TypedMainModuleSubstateKey::PackageInstrumentedCodeKey( - scrypto_decode(&key).map_err(|_| ())?, - ) - } - } + TypedMainModuleSubstateKey::Package(PackageTypedSubstateKey::for_key_in_partition( + &PackagePartition::try_from(PartitionOffset(partition_offset))?, + substate_key, + )?) } EntityType::GlobalFungibleResourceManager => { TypedMainModuleSubstateKey::FungibleResourceField( @@ -482,15 +424,7 @@ pub enum TypedMetadataModuleSubstateValue { #[derive(Debug)] pub enum TypedMainModuleSubstateValue { // Objects - Package(TypedPackageFieldValue), - PackageBlueprint(KeyValueEntrySubstate), - PackageBlueprintDependencies(KeyValueEntrySubstate), - PackageSchema(KeyValueEntrySubstate), - PackageAuthTemplate(KeyValueEntrySubstate), - PackageRoyalty(KeyValueEntrySubstate), - PackageVmType(KeyValueEntrySubstate), - PackageOriginalCode(KeyValueEntrySubstate), - PackageInstrumentedCode(KeyValueEntrySubstate), + Package(PackageTypedSubstateValue), FungibleResource(TypedFungibleResourceManagerFieldValue), NonFungibleResource(TypedNonFungibleResourceManagerFieldValue), NonFungibleResourceData(KeyValueEntrySubstate), @@ -515,11 +449,6 @@ pub enum TypedMainModuleSubstateValue { GenericKeyValueStoreEntry(KeyValueEntrySubstate), } -#[derive(Debug)] -pub enum TypedPackageFieldValue { - Royalty(FieldSubstate), -} - #[derive(Debug)] pub enum TypedFungibleResourceManagerFieldValue { Divisibility(FieldSubstate), @@ -662,35 +591,9 @@ fn to_typed_object_substate_value( data: &[u8], ) -> Result { let substate_value = match substate_key { - TypedMainModuleSubstateKey::PackageField(offset) => { - TypedMainModuleSubstateValue::Package(match offset { - PackageField::Royalty => TypedPackageFieldValue::Royalty(scrypto_decode(data)?), - }) - } - TypedMainModuleSubstateKey::PackageBlueprintKey(_key) => { - TypedMainModuleSubstateValue::PackageBlueprint(scrypto_decode(data)?) - } - TypedMainModuleSubstateKey::PackageBlueprintDependenciesKey(..) => { - TypedMainModuleSubstateValue::PackageBlueprintDependencies(scrypto_decode(data)?) - } - TypedMainModuleSubstateKey::PackageSchemaKey(..) => { - TypedMainModuleSubstateValue::PackageSchema(scrypto_decode(data)?) - } - TypedMainModuleSubstateKey::PackageRoyaltyKey(_fn_key) => { - TypedMainModuleSubstateValue::PackageRoyalty(scrypto_decode(data)?) - } - TypedMainModuleSubstateKey::PackageAuthTemplateKey(_fn_key) => { - TypedMainModuleSubstateValue::PackageAuthTemplate(scrypto_decode(data)?) - } - TypedMainModuleSubstateKey::PackageVmTypeKey(..) => { - TypedMainModuleSubstateValue::PackageVmType(scrypto_decode(data)?) - } - TypedMainModuleSubstateKey::PackageOriginalCodeKey(..) => { - TypedMainModuleSubstateValue::PackageOriginalCode(scrypto_decode(data)?) - } - TypedMainModuleSubstateKey::PackageInstrumentedCodeKey(..) => { - TypedMainModuleSubstateValue::PackageInstrumentedCode(scrypto_decode(data)?) - } + TypedMainModuleSubstateKey::Package(key) => TypedMainModuleSubstateValue::Package( + PackageTypedSubstateValue::from_key_and_data(key, data)?, + ), TypedMainModuleSubstateKey::FungibleResourceField(offset) => { TypedMainModuleSubstateValue::FungibleResource(match offset { FungibleResourceManagerField::Divisibility => { diff --git a/radix-engine-tests/tests/schema_sanity_check.rs b/radix-engine-tests/tests/schema_sanity_check.rs index c113d0f65fe..f71f28c007b 100644 --- a/radix-engine-tests/tests/schema_sanity_check.rs +++ b/radix-engine-tests/tests/schema_sanity_check.rs @@ -159,7 +159,7 @@ fn check_type_pointers( } fn check_type_pointer( - schemas_by_hash: &IndexMap, + schemas_by_hash: &IndexMap, type_pointer: &TypePointer, ) -> CheckResult { match type_pointer { diff --git a/radix-engine/src/blueprints/macros.rs b/radix-engine/src/blueprints/macros.rs index 37698dbebe2..3c43da4d22c 100644 --- a/radix-engine/src/blueprints/macros.rs +++ b/radix-engine/src/blueprints/macros.rs @@ -139,6 +139,7 @@ macro_rules! declare_native_blueprint_state { #[derive(Debug, PartialEq, Eq, ScryptoSbor)] struct [<$blueprint_ident $field_ident FieldContent>] = $field_type ); + // > Set up the _FieldSubstate alias for the system-wrapped substate generate_system_substate_type_alias!( Field, @@ -162,6 +163,18 @@ macro_rules! declare_native_blueprint_state { struct [<$blueprint_ident $collection_ident Key>] = $collection_key_type ); + // TODO(David) - Properly handle SortedIndex: + // Fix Key types for SortedIndex to have a named u16 part of the key, + // use a different key trait, and use .for_sorted_key in the below. + impl TryFrom<&SubstateKey> for [<$blueprint_ident $collection_ident Key>] { + type Error = (); + + fn try_from(substate_key: &SubstateKey) -> Result { + let key = substate_key.for_map().ok_or(())?; + scrypto_decode(&key).map_err(|_| ())? + } + } + // Values // > Set up Versioned types (if relevant). Assumes __ValueV1 exists and then creates // - Versioned__Value @@ -248,6 +261,14 @@ macro_rules! declare_native_blueprint_state { } } + impl TryFrom for [<$blueprint_ident Partition>] { + type Error = (); + + fn try_from(offset: PartitionOffset) -> Result { + Self::from_repr(offset.0).ok_or(()) + } + } + //--------------------------------- // Typed Substate - Keys and Values //--------------------------------- @@ -257,6 +278,26 @@ macro_rules! declare_native_blueprint_state { $([<$collection_ident $collection_type Entries>]([<$blueprint_ident $collection_ident Key>]),)* } + impl [<$blueprint_ident TypedSubstateKey>] { + pub fn for_key_in_partition(partition: &[<$blueprint_ident Partition>], substate_key: &SubstateKey) -> Result { + let key = match partition { + [<$blueprint_ident Partition>]::Field => { + [<$blueprint_ident TypedSubstateKey>]::Fields( + [<$blueprint_ident Field>]::try_from(substate_key)? + ) + } + $( + [<$blueprint_ident Partition>]::[<$collection_ident $collection_type>] => { + [<$blueprint_ident TypedSubstateKey>]::[<$collection_ident $collection_type Entries>]( + [<$blueprint_ident $collection_ident Key>]::try_from(substate_key)?, + ) + } + )* + }; + Ok(key) + } + } + #[derive(Debug)] pub enum [<$blueprint_ident TypedFieldSubstateValue>] { $($field_ident([<$blueprint_ident $field_ident FieldSubstate>]),)* @@ -268,6 +309,30 @@ macro_rules! declare_native_blueprint_state { $([<$collection_ident $collection_type>]([<$blueprint_ident $collection_ident EntrySubstate>]),)* } + impl [<$blueprint_ident TypedSubstateValue>] { + pub fn from_key_and_data(key: &[<$blueprint_ident TypedSubstateKey>], data: &[u8]) -> Result { + let substate_value = match key { + // Fields + $( + [<$blueprint_ident TypedSubstateKey>]::Fields([<$blueprint_ident Field>]::$field_ident) => { + [<$blueprint_ident TypedSubstateValue>]::Field( + [<$blueprint_ident TypedFieldSubstateValue>]::$field_ident(scrypto_decode(data)?) + ) + } + )* + // Collections + $( + [<$blueprint_ident TypedSubstateKey>]::[<$collection_ident $collection_type Entries>](_) => { + [<$blueprint_ident TypedSubstateValue>]::[<$collection_ident $collection_type>]( + scrypto_decode(data)? + ) + } + )* + }; + Ok(substate_value) + } + } + //---------------------- // Schema //---------------------- @@ -544,14 +609,17 @@ mod helper_macros { content_trait: $content_trait:ident, ident_core: $ident_core:ident, $(#[$attributes:meta])* - struct $alias:ident = { + struct $content_type_name:ident = { kind: Instance, ident: $instance_ident:ident $(,)? } ) => { - // Don't output any types for an instance schema - }; // TODO - Add support for some kind of StaticMultiVersioned type here + $(#[$attributes])* + #[sbor(transparent)] + pub struct $content_type_name<$instance_ident = ScryptoValue>(pub $instance_ident); + }; + // TODO - Add support for some kind of StaticMultiVersioned type here } #[allow(unused)] diff --git a/radix-engine/src/blueprints/package/package.rs b/radix-engine/src/blueprints/package/package.rs index 61f03089cfd..6ad8765ffd2 100644 --- a/radix-engine/src/blueprints/package/package.rs +++ b/radix-engine/src/blueprints/package/package.rs @@ -26,16 +26,11 @@ use syn::Ident; use crate::roles_template; use crate::system::node_modules::role_assignment::RoleAssignmentNativePackage; use crate::system::node_modules::royalty::RoyaltyUtil; -use crate::system::system::{ - FieldSubstate, KeyValueEntrySubstate, SubstateMutability, SystemService, -}; +use crate::system::system::*; use crate::system::system_callback::{SystemConfig, SystemLockData}; use crate::system::system_callback_api::SystemCallbackObject; use crate::system::system_modules::auth::{AuthError, ResolvedPermission}; use crate::vm::VmPackageValidation; -pub use radix_engine_interface::blueprints::package::{ - PackageInstrumentedCodeSubstate, PackageOriginalCodeSubstate, PackageRoyaltyAccumulatorSubstate, -}; use super::*; @@ -506,7 +501,7 @@ pub fn create_bootstrap_package_partitions( .get_mut(&PackagePartition::Field.as_main_partition()) .unwrap(); field_partition - .remove_entry(&PackageField::Royalty.into()) + .remove_entry(&PackageField::RoyaltyAccumulator.into()) .unwrap(); } @@ -599,17 +594,6 @@ where Ok(PackageAddress::new_or_panic(address.into_node_id().0)) } -pub struct PackageStructure { - pub definitions: BTreeMap, - pub dependencies: BTreeMap, - pub schemas: BTreeMap, - pub vm_type: BTreeMap, - pub original_code: BTreeMap, - pub instrumented_code: BTreeMap, - pub auth_configs: BTreeMap, - pub package_royalties: BTreeMap, -} - pub struct PackageNativePackage; impl PackageNativePackage { @@ -813,17 +797,17 @@ impl PackageNativePackage { let mut original_code_substates = BTreeMap::new(); let mut instrumented_code_substates = BTreeMap::new(); - let code_hash = hash(&original_code); - vm_type_substates.insert(code_hash, PackageVmTypeSubstate { vm_type }); + let code_hash = CodeHash::from(hash(&original_code)); + vm_type_substates.insert(code_hash, PackageCodeVmTypeValue { vm_type }); original_code_substates.insert( code_hash, - PackageOriginalCodeSubstate { + PackageCodeOriginalCodeValue { code: original_code, }, ); if let Some(code) = instrumented_code { instrumented_code_substates - .insert(code_hash, PackageInstrumentedCodeSubstate { code: code }); + .insert(code_hash, PackageCodeInstrumentedCodeValue { code }); }; { @@ -975,37 +959,20 @@ impl PackageNativePackage { .collect(), code_vm_type: vm_type_substates .into_iter() - .map(|(code_hash, vm_type)| { - ( - CodeHash::from(code_hash).into_key(), - PackageCodeVmTypeValue { - vm_type: vm_type.vm_type, - } - .into_locked_substate(), - ) - }) + .map(|(code_hash, vm_type)| (code_hash.into_key(), vm_type.into_locked_substate())) .collect(), code_original_code: original_code_substates .into_iter() .map(|(code_hash, original_code)| { - ( - CodeHash::from(code_hash).into_key(), - PackageCodeOriginalCodeValue { - code: original_code.code, - } - .into_locked_substate(), - ) + (code_hash.into_key(), original_code.into_locked_substate()) }) .collect(), code_instrumented_code: instrumented_code_substates .into_iter() .map(|(code_hash, instrumented_code)| { ( - CodeHash::from(code_hash).into_key(), - PackageCodeInstrumentedCodeValue { - code: instrumented_code.code, - } - .into_locked_substate(), + code_hash.into_key(), + instrumented_code.into_locked_substate(), ) }) .collect(), @@ -1164,15 +1131,15 @@ impl PackageRoyaltyNativeBlueprint { let handle = api.kernel_open_substate( receiver, MAIN_BASE_PARTITION, - &PackageField::Royalty.into(), + &PackageField::RoyaltyAccumulator.into(), LockFlags::MUTABLE, SystemLockData::default(), )?; - let substate: FieldSubstate = + let substate: PackageRoyaltyAccumulatorFieldSubstate = api.kernel_read_substate(handle)?.as_typed().unwrap(); - let vault_id = substate.value.0.royalty_vault.0; + let vault_id = substate.value.0 .0.into_latest().royalty_vault.0; let package_address = PackageAddress::new_or_panic(receiver.0); apply_royalty_cost( api, @@ -1199,12 +1166,12 @@ impl PackageRoyaltyNativeBlueprint { let handle = api.actor_open_field( OBJECT_HANDLE_SELF, - PackageField::Royalty.into(), + PackageField::RoyaltyAccumulator.into(), LockFlags::read_only(), )?; - let mut substate: PackageRoyaltyAccumulatorSubstate = api.field_read_typed(handle)?; - let bucket = substate.royalty_vault.take_all(api)?; + let mut substate: PackageRoyaltyAccumulatorFieldContent = api.field_read_typed(handle)?; + let bucket = substate.0.into_latest().royalty_vault.take_all(api)?; Ok(bucket) } diff --git a/radix-engine/src/blueprints/package/substates.rs b/radix-engine/src/blueprints/package/substates.rs index 7e7dec5e79b..bcf9b4aacb8 100644 --- a/radix-engine/src/blueprints/package/substates.rs +++ b/radix-engine/src/blueprints/package/substates.rs @@ -121,11 +121,6 @@ pub struct PackageRoyaltyAccumulatorFieldV1 { // Collection models - Schemas //--------------------------------------- -define_wrapped_hash!( - /// Represents a particular schema under a package - SchemaHash -); - // TODO(David): Change to VersionedSchema when can define a type as not-implicitly-versioned // TODO: Move to Schema partition when we have it pub type PackageSchemaValueV1 = ScryptoSchema; @@ -143,25 +138,36 @@ pub type PackageBlueprintVersionAuthConfigValueV1 = AuthConfig; // Collection models - By Code //--------------------------------------- -define_wrapped_hash!( - /// Represents a particular instance of code under a package - CodeHash -); - #[derive(Debug, PartialEq, Eq, ScryptoSbor)] #[sbor(transparent)] pub struct PackageCodeVmTypeValueV1 { pub vm_type: VmType, } -#[derive(Debug, PartialEq, Eq, ScryptoSbor)] +#[derive(PartialEq, Eq, ScryptoSbor)] #[sbor(transparent)] pub struct PackageCodeOriginalCodeValueV1 { pub code: Vec, } -#[derive(Debug, PartialEq, Eq, ScryptoSbor)] +impl Debug for PackageCodeOriginalCodeValueV1 { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("PackageCodeOriginalCodeValueV1") + .field("len", &self.code.len()) + .finish() + } +} + +#[derive(PartialEq, Eq, ScryptoSbor)] #[sbor(transparent)] pub struct PackageCodeInstrumentedCodeValueV1 { pub code: Vec, } + +impl Debug for PackageCodeInstrumentedCodeValueV1 { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("PackageCodeInstrumentedCodeValueV1") + .field("len", &self.code.len()) + .finish() + } +} diff --git a/radix-engine/src/system/system.rs b/radix-engine/src/system/system.rs index b2bfc95b033..bbb5cd25a43 100644 --- a/radix-engine/src/system/system.rs +++ b/radix-engine/src/system/system.rs @@ -498,7 +498,7 @@ where pub fn get_schema( &mut self, package_address: PackageAddress, - schema_hash: &Hash, + schema_hash: &SchemaHash, ) -> Result { let def = self .api diff --git a/radix-engine/src/system/system_callback.rs b/radix-engine/src/system/system_callback.rs index 546586d2cc4..7a6cf4c5538 100644 --- a/radix-engine/src/system/system_callback.rs +++ b/radix-engine/src/system/system_callback.rs @@ -85,7 +85,7 @@ impl SystemLockData { pub struct SystemConfig { pub callback_obj: C, pub blueprint_cache: NonIterMap, - pub schema_cache: NonIterMap, + pub schema_cache: NonIterMap, pub auth_cache: NonIterMap, pub modules: SystemModuleMixer, } diff --git a/radix-engine/src/transaction/system_structure.rs b/radix-engine/src/transaction/system_structure.rs index c5e3c778314..129adde8c10 100644 --- a/radix-engine/src/transaction/system_structure.rs +++ b/radix-engine/src/transaction/system_structure.rs @@ -35,9 +35,9 @@ pub enum SystemFieldKind { #[derive(Debug, Clone, ScryptoSbor, PartialEq, Eq)] pub struct KeyValueStoreEntryStructure { key_value_store_address: InternalAddress, - key_schema_hash: Hash, + key_schema_hash: SchemaHash, key_local_type_index: LocalTypeIndex, - value_schema_hash: Hash, + value_schema_hash: SchemaHash, value_local_type_index: LocalTypeIndex, } @@ -73,14 +73,14 @@ pub enum ObjectSubstateTypeReference { #[derive(Debug, Clone, ScryptoSbor, PartialEq, Eq)] pub struct PackageTypeReference { package_address: PackageAddress, - schema_hash: Hash, + schema_hash: SchemaHash, local_type_index: LocalTypeIndex, } #[derive(Debug, Clone, ScryptoSbor, PartialEq, Eq)] pub struct ObjectInstanceTypeReference { entity_address: NodeId, - schema_hash: Hash, + schema_hash: SchemaHash, instance_type_index: u8, local_type_index: LocalTypeIndex, } diff --git a/radix-engine/src/utils/package_extractor.rs b/radix-engine/src/utils/package_extractor.rs index f3577016763..94e910ec26e 100644 --- a/radix-engine/src/utils/package_extractor.rs +++ b/radix-engine/src/utils/package_extractor.rs @@ -3,7 +3,9 @@ use crate::system::system_modules::costing::SystemLoanFeeReserve; use crate::types::*; use crate::vm::wasm::*; use crate::vm::wasm_runtime::NoOpWasmRuntime; -use radix_engine_interface::blueprints::package::{BlueprintDefinitionInit, PackageDefinition}; +use radix_engine_interface::blueprints::package::{ + BlueprintDefinitionInit, CodeHash, PackageDefinition, +}; use sbor::rust::iter; #[derive(Debug)] @@ -28,7 +30,7 @@ pub fn extract_definition(code: &[u8]) -> Result { @@ -24,7 +25,7 @@ impl ScryptoVm { pub fn create_instance( &self, package_address: &PackageAddress, - code_hash: Hash, + code_hash: CodeHash, instrumented_code: &[u8], ) -> ScryptoVmInstance { ScryptoVmInstance { diff --git a/radix-engine/src/vm/vm.rs b/radix-engine/src/vm/vm.rs index d8a0888742a..da707ef91ba 100644 --- a/radix-engine/src/vm/vm.rs +++ b/radix-engine/src/vm/vm.rs @@ -1,4 +1,7 @@ -use crate::blueprints::package::{PackageError, VmType}; +use crate::blueprints::package::{ + PackageCodeInstrumentedCodeEntrySubstate, PackageCodeOriginalCodeEntrySubstate, + PackageCodeVmTypeEntrySubstate, PackageError, VmType, +}; use crate::errors::{ApplicationError, RuntimeError}; use crate::kernel::kernel_api::{KernelInternalApi, KernelNodeApi, KernelSubstateApi}; use crate::system::system::KeyValueEntrySubstate; @@ -63,14 +66,14 @@ impl<'g, W: WasmEngine + 'g, E: NativeVmExtension> SystemCallbackObject for Vm<' SystemLockData::default(), )?; let vm_type = api.kernel_read_substate(handle)?; - let vm_type: KeyValueEntrySubstate = vm_type.as_typed().unwrap(); + let vm_type: PackageCodeVmTypeEntrySubstate = vm_type.as_typed().unwrap(); api.kernel_close_substate(handle)?; vm_type .value .expect(&format!("Vm type not found: {:?}", export)) }; - let output = match vm_type.vm_type { + let output = match vm_type.0.into_latest().vm_type { VmType::Native => { let original_code = { let handle = api.kernel_open_substate_with_default( @@ -87,7 +90,7 @@ impl<'g, W: WasmEngine + 'g, E: NativeVmExtension> SystemCallbackObject for Vm<' SystemLockData::default(), )?; let original_code = api.kernel_read_substate(handle)?; - let original_code: KeyValueEntrySubstate = + let original_code: PackageCodeOriginalCodeEntrySubstate = original_code.as_typed().unwrap(); api.kernel_close_substate(handle)?; original_code @@ -99,7 +102,7 @@ impl<'g, W: WasmEngine + 'g, E: NativeVmExtension> SystemCallbackObject for Vm<' .kernel_get_system() .callback_obj .native_vm - .create_instance(address, &original_code.code)?; + .create_instance(address, &original_code.0.into_latest().code)?; let output = { vm_instance.invoke(export.export_name.as_str(), input, api)? }; output @@ -120,12 +123,14 @@ impl<'g, W: WasmEngine + 'g, E: NativeVmExtension> SystemCallbackObject for Vm<' SystemLockData::default(), )?; let instrumented_code = api.kernel_read_substate(handle)?; - let instrumented_code: KeyValueEntrySubstate = + let instrumented_code: PackageCodeInstrumentedCodeEntrySubstate = instrumented_code.as_typed().unwrap(); api.kernel_close_substate(handle)?; instrumented_code .value .expect(&format!("Instrumented code not found: {:?}", export)) + .0 + .into_latest() }; let mut scrypto_vm_instance = { diff --git a/radix-engine/src/vm/wasm/traits.rs b/radix-engine/src/vm/wasm/traits.rs index 35cf8fd3d43..09701765ac3 100644 --- a/radix-engine/src/vm/wasm/traits.rs +++ b/radix-engine/src/vm/wasm/traits.rs @@ -1,6 +1,7 @@ use crate::errors::InvokeError; use crate::types::*; use crate::vm::wasm::errors::*; +use radix_engine_interface::blueprints::package::CodeHash; use sbor::rust::boxed::Box; use sbor::rust::vec::Vec; @@ -190,5 +191,5 @@ pub trait WasmEngine { /// Instantiate a Scrypto module. /// /// The code must have been validated and instrumented!!! - fn instantiate(&self, code_hash: Hash, instrumented_code: &[u8]) -> Self::WasmInstance; + fn instantiate(&self, code_hash: CodeHash, instrumented_code: &[u8]) -> Self::WasmInstance; } diff --git a/radix-engine/src/vm/wasm/wasmer.rs b/radix-engine/src/vm/wasm/wasmer.rs index 581511d2a23..8d6af139b71 100644 --- a/radix-engine/src/vm/wasm/wasmer.rs +++ b/radix-engine/src/vm/wasm/wasmer.rs @@ -3,6 +3,7 @@ use crate::types::*; use crate::vm::wasm::constants::*; use crate::vm::wasm::errors::*; use crate::vm::wasm::traits::*; +use radix_engine_interface::blueprints::package::CodeHash; use sbor::rust::sync::{Arc, Mutex}; use wasmer::{ imports, Function, HostEnvInitError, Instance, LazyInit, Module, RuntimeError, Store, @@ -76,9 +77,9 @@ pub struct WasmerEngine { store: Store, // This flag disables cache in wasm_instrumenter/wasmi/wasmer to prevent non-determinism when fuzzing #[cfg(all(not(feature = "radix_engine_fuzzing"), not(feature = "moka")))] - modules_cache: RefCell>>, + modules_cache: RefCell>>, #[cfg(all(not(feature = "radix_engine_fuzzing"), feature = "moka"))] - modules_cache: moka::sync::Cache>, + modules_cache: moka::sync::Cache>, #[cfg(feature = "radix_engine_fuzzing")] modules_cache: usize, } @@ -803,7 +804,7 @@ impl WasmerEngine { #[cfg(all(not(feature = "radix_engine_fuzzing"), feature = "moka"))] let modules_cache = moka::sync::Cache::builder() .weigher( - |_metered_code_key: &Hash, _value: &Arc| -> u32 { + |_metered_code_key: &CodeHash, _value: &Arc| -> u32 { // No sophisticated weighing mechanism, just keep a fixed size cache 1u32 }, @@ -823,7 +824,7 @@ impl WasmerEngine { impl WasmEngine for WasmerEngine { type WasmInstance = WasmerInstance; - fn instantiate(&self, code_hash: Hash, instrumented_code: &[u8]) -> WasmerInstance { + fn instantiate(&self, code_hash: CodeHash, instrumented_code: &[u8]) -> WasmerInstance { #[cfg(not(feature = "radix_engine_fuzzing"))] { #[cfg(not(feature = "moka"))] diff --git a/radix-engine/src/vm/wasm/wasmi.rs b/radix-engine/src/vm/wasm/wasmi.rs index 5b444a567e2..15f9392e6b4 100644 --- a/radix-engine/src/vm/wasm/wasmi.rs +++ b/radix-engine/src/vm/wasm/wasmi.rs @@ -1,3 +1,4 @@ +use radix_engine_interface::blueprints::package::CodeHash; use sbor::rust::mem::transmute; use sbor::rust::mem::MaybeUninit; #[cfg(not(feature = "radix_engine_fuzzing"))] @@ -1230,9 +1231,9 @@ pub struct WasmiEngineOptions { pub struct WasmiEngine { // This flag disables cache in wasm_instrumenter/wasmi/wasmer to prevent non-determinism when fuzzing #[cfg(all(not(feature = "radix_engine_fuzzing"), not(feature = "moka")))] - modules_cache: RefCell>>, + modules_cache: RefCell>>, #[cfg(all(not(feature = "radix_engine_fuzzing"), feature = "moka"))] - modules_cache: moka::sync::Cache>, + modules_cache: moka::sync::Cache>, #[cfg(feature = "radix_engine_fuzzing")] #[allow(dead_code)] modules_cache: usize, @@ -1254,7 +1255,7 @@ impl WasmiEngine { )); #[cfg(all(not(feature = "radix_engine_fuzzing"), feature = "moka"))] let modules_cache = moka::sync::Cache::builder() - .weigher(|_key: &Hash, _value: &Arc| -> u32 { + .weigher(|_key: &CodeHash, _value: &Arc| -> u32 { // No sophisticated weighing mechanism, just keep a fixed size cache 1u32 }) @@ -1271,7 +1272,7 @@ impl WasmEngine for WasmiEngine { type WasmInstance = WasmiInstance; #[allow(unused_variables)] - fn instantiate(&self, code_hash: Hash, instrumented_code: &[u8]) -> WasmiInstance { + fn instantiate(&self, code_hash: CodeHash, instrumented_code: &[u8]) -> WasmiInstance { #[cfg(not(feature = "radix_engine_fuzzing"))] { #[cfg(not(feature = "moka"))] diff --git a/scrypto-unit/src/test_runner.rs b/scrypto-unit/src/test_runner.rs index fd148c090c3..f8f19eb9ce8 100644 --- a/scrypto-unit/src/test_runner.rs +++ b/scrypto-unit/src/test_runner.rs @@ -28,8 +28,8 @@ use radix_engine_interface::blueprints::consensus_manager::{ }; use radix_engine_interface::blueprints::package::{ BlueprintDefinitionInit, PackageDefinition, PackagePublishNativeManifestInput, - PackagePublishWasmAdvancedManifestInput, PackageRoyaltyAccumulatorSubstate, TypePointer, - PACKAGE_BLUEPRINT, PACKAGE_PUBLISH_NATIVE_IDENT, PACKAGE_PUBLISH_WASM_ADVANCED_IDENT, + PackagePublishWasmAdvancedManifestInput, TypePointer, PACKAGE_BLUEPRINT, + PACKAGE_PUBLISH_NATIVE_IDENT, PACKAGE_PUBLISH_WASM_ADVANCED_IDENT, PACKAGE_SCHEMAS_PARTITION_OFFSET, }; use radix_engine_interface::constants::CONSENSUS_MANAGER; @@ -38,9 +38,7 @@ use radix_engine_interface::network::NetworkDefinition; use radix_engine_interface::time::Instant; use radix_engine_interface::{dec, freeze_roles, rule}; use radix_engine_queries::query::{ResourceAccounter, StateTreeTraverser, VaultFinder}; -use radix_engine_queries::typed_substate_layout::{ - BlueprintDefinition, BlueprintVersionKey, PACKAGE_BLUEPRINTS_PARTITION_OFFSET, -}; +use radix_engine_queries::typed_substate_layout::*; use radix_engine_store_interface::db_key_mapper::DatabaseKeyMapper; use radix_engine_store_interface::db_key_mapper::{ MappedCommittableSubstateDatabase, MappedSubstateDatabase, SpreadPrefixKeyMapper, @@ -550,17 +548,17 @@ impl TestRunner { pub fn inspect_package_royalty(&mut self, package_address: PackageAddress) -> Option { let output = self .database - .get_mapped::>( + .get_mapped::( package_address.as_node_id(), MAIN_BASE_PARTITION, - &PackageField::Royalty.into(), + &PackageField::RoyaltyAccumulator.into(), )? .value .0; self.database .get_mapped::>( - output.royalty_vault.0.as_node_id(), + output.0.into_latest().royalty_vault.0.as_node_id(), MAIN_BASE_PARTITION, &FungibleVaultField::LiquidFungible.into(), ) @@ -609,7 +607,7 @@ impl TestRunner { pub fn get_package_scrypto_schemas( &self, package_address: &PackageAddress, - ) -> IndexMap { + ) -> IndexMap { let mut schemas = index_map_new(); for entry in self .substate_db() @@ -620,12 +618,12 @@ impl TestRunner { .unwrap(), )) { - let hash: Hash = + let hash: SchemaHash = scrypto_decode(&SpreadPrefixKeyMapper::map_from_db_sort_key(&entry.0)).unwrap(); - let value: KeyValueEntrySubstate = scrypto_decode(&entry.1).unwrap(); + let value: PackageSchemaEntrySubstate = scrypto_decode(&entry.1).unwrap(); match value.value { Some(schema) => { - schemas.insert(hash, schema); + schemas.insert(hash, schema.0.into_latest()); } None => {} } diff --git a/simulator/src/ledger/dumper.rs b/simulator/src/ledger/dumper.rs index 6d7e1e0673a..1a1e18e5a65 100644 --- a/simulator/src/ledger/dumper.rs +++ b/simulator/src/ledger/dumper.rs @@ -8,6 +8,7 @@ use radix_engine::types::*; use radix_engine_interface::blueprints::package::*; use radix_engine_interface::network::NetworkDefinition; use radix_engine_queries::query::ResourceAccounter; +use radix_engine_queries::typed_substate_layout::PackageCodeOriginalCodeEntrySubstate; use radix_engine_store_interface::{ db_key_mapper::{MappedSubstateDatabase, SpreadPrefixKeyMapper}, interface::SubstateDatabase, @@ -31,7 +32,7 @@ pub fn dump_package( ) -> Result<(), EntityDumpError> { let address_bech32_encoder = AddressBech32Encoder::new(&NetworkDefinition::simulator()); let (_, substate) = substate_db - .list_mapped::, MapKey>( + .list_mapped::( package_address.as_node_id(), MAIN_BASE_PARTITION .at_offset(PACKAGE_ORIGINAL_CODE_PARTITION_OFFSET) @@ -50,7 +51,7 @@ pub fn dump_package( output, "{}: {} bytes", "Code size".green().bold(), - substate.value.unwrap().code.len() + substate.value.unwrap().0.into_latest().code.len() ); Ok(()) } diff --git a/simulator/src/resim/cmd_publish.rs b/simulator/src/resim/cmd_publish.rs index d9650af9f22..6240ee35bad 100644 --- a/simulator/src/resim/cmd_publish.rs +++ b/simulator/src/resim/cmd_publish.rs @@ -5,9 +5,7 @@ use radix_engine_interface::blueprints::package::{ BlueprintDefinition, BlueprintDependencies, FunctionSchema, IndexedStateSchema, PackageExport, TypePointer, VmType, *, }; -use radix_engine_interface::blueprints::package::{PackageDefinition, PackageOriginalCodeSubstate}; use radix_engine_interface::schema::TypeRef; -use radix_engine_queries::typed_substate_layout::PackageVmTypeSubstate; use radix_engine_store_interface::{ db_key_mapper::{DatabaseKeyMapper, SpreadPrefixKeyMapper}, interface::{CommittableSubstateDatabase, DatabaseUpdate}, diff --git a/simulator/src/resim/error.rs b/simulator/src/resim/error.rs index 0216117efbb..88613757008 100644 --- a/simulator/src/resim/error.rs +++ b/simulator/src/resim/error.rs @@ -8,6 +8,7 @@ use radix_engine::utils::ExtractSchemaError; use radix_engine::vm::wasm::PrepareError; use radix_engine_interface::blueprints::resource::ParseNonFungibleGlobalIdError; use radix_engine_interface::network::ParseNetworkError; +use radix_engine_interface::types::SchemaHash; use sbor::*; use transaction::errors::*; use transaction::model::PrepareError as TransactionPrepareError; @@ -25,7 +26,7 @@ pub enum Error { HomeDirUnknown, PackageNotFound(PackageAddress), - SchemaNotFound(PackageAddress, Hash), + SchemaNotFound(PackageAddress, SchemaHash), BlueprintNotFound(PackageAddress, String), ComponentNotFound(ComponentAddress), InstanceSchemaNot(ComponentAddress, u8), diff --git a/simulator/src/resim/mod.rs b/simulator/src/resim/mod.rs index 74a48581c47..d6eaaa1f494 100644 --- a/simulator/src/resim/mod.rs +++ b/simulator/src/resim/mod.rs @@ -76,6 +76,7 @@ use radix_engine_interface::blueprints::package::{ use radix_engine_interface::blueprints::resource::FromPublicKey; use radix_engine_interface::crypto::hash; use radix_engine_interface::network::NetworkDefinition; +use radix_engine_queries::typed_substate_layout::PackageSchemaEntrySubstate; use radix_engine_store_interface::{ db_key_mapper::{ MappedCommittableSubstateDatabase, MappedSubstateDatabase, SpreadPrefixKeyMapper, @@ -377,7 +378,7 @@ pub fn export_object_info(component_address: ComponentAddress) -> Result Result { let scrypto_vm = ScryptoVm::::default(); let native_vm = DefaultNativeVm::new(); @@ -386,7 +387,7 @@ pub fn export_schema( Bootstrapper::new(&mut substate_db, vm, false).bootstrap_test_default(); let schema = substate_db - .get_mapped::>( + .get_mapped::( package_address.as_node_id(), MAIN_BASE_PARTITION .at_offset(PACKAGE_SCHEMAS_PARTITION_OFFSET) @@ -395,7 +396,9 @@ pub fn export_schema( ) .ok_or(Error::SchemaNotFound(package_address, schema_hash))? .value - .unwrap(); + .unwrap() + .0 + .into_latest(); Ok(schema) } From 3284bc1cbf2e3cc52e450e3ba668cc8b55938daa Mon Sep 17 00:00:00 2001 From: David Edey Date: Mon, 14 Aug 2023 03:14:09 +0100 Subject: [PATCH 14/28] feature: Further updates to `declare_native_blueprint_state` --- radix-engine-tests/benches/costing.rs | 7 +- .../tests/schema_sanity_check.rs | 2 +- radix-engine/src/blueprints/macros.rs | 404 +++++++++++++++--- .../src/blueprints/package/package.rs | 208 ++++----- .../src/blueprints/package/substates.rs | 36 +- radix-engine/src/errors.rs | 1 + radix-engine/src/track/interface.rs | 32 ++ radix-engine/src/track/mod.rs | 1 + radix-engine/src/vm/vm.rs | 8 +- scrypto-schema/src/lib.rs | 14 + simulator/src/ledger/dumper.rs | 2 +- simulator/src/resim/cmd_publish.rs | 171 +++----- simulator/src/resim/error.rs | 2 +- 13 files changed, 567 insertions(+), 321 deletions(-) diff --git a/radix-engine-tests/benches/costing.rs b/radix-engine-tests/benches/costing.rs index e6c60b4475d..5f7c56b2f8f 100644 --- a/radix-engine-tests/benches/costing.rs +++ b/radix-engine-tests/benches/costing.rs @@ -11,7 +11,7 @@ use radix_engine::{ wasm_runtime::NoOpWasmRuntime, }, }; -use radix_engine_queries::typed_substate_layout::PackageDefinition; +use radix_engine_queries::typed_substate_layout::{CodeHash, PackageDefinition}; use sbor::rust::iter; use scrypto_unit::TestRunnerBuilder; use transaction::{ @@ -87,7 +87,8 @@ fn bench_spin_loop(c: &mut Criterion) { fee_reserve, &mut wasm_execution_units_consumed, )); - let mut instance = wasm_engine.instantiate(Hash([0u8; 32]), &instrumented_code); + let mut instance = + wasm_engine.instantiate(CodeHash(Hash([0u8; 32])), &instrumented_code); instance .invoke_export("Test_f", vec![Buffer(0)], &mut runtime) .unwrap(); @@ -118,7 +119,7 @@ macro_rules! bench_instantiate { c.bench_function(concat!("costing::instantiate_", $what), |b| { b.iter(|| { let wasm_engine = DefaultWasmEngine::default(); - wasm_engine.instantiate(Hash([0u8; 32]), &instrumented_code); + wasm_engine.instantiate(CodeHash(Hash([0u8; 32])), &instrumented_code); }) }); diff --git a/radix-engine-tests/tests/schema_sanity_check.rs b/radix-engine-tests/tests/schema_sanity_check.rs index f71f28c007b..0dc5d5666d4 100644 --- a/radix-engine-tests/tests/schema_sanity_check.rs +++ b/radix-engine-tests/tests/schema_sanity_check.rs @@ -146,7 +146,7 @@ fn scan_native_blueprint_schemas_and_highlight_unsafe_types() { } fn check_type_pointers( - schemas_by_hash: &IndexMap, + schemas_by_hash: &IndexMap, type_pointers: &[TypePointer], ) -> CheckResult { for ty in type_pointers { diff --git a/radix-engine/src/blueprints/macros.rs b/radix-engine/src/blueprints/macros.rs index 3c43da4d22c..3246df698f4 100644 --- a/radix-engine/src/blueprints/macros.rs +++ b/radix-engine/src/blueprints/macros.rs @@ -1,6 +1,165 @@ +use crate::errors::*; use crate::system::system::*; use crate::types::*; +// TODO(David) - Move this, features and content stuff under a `models` module? +// TODO(David) - Create an internal_prelude in `blueprints` +// and an internal_prelude and prelud in engine +pub trait FeatureSetResolver { + fn feature_names_str(&self) -> Vec<&'static str>; + + fn feature_names_str_set(&self) -> BTreeSet<&'static str> { + self.feature_names_str().into_iter().collect() + } + + fn feature_names_string(&self) -> Vec { + self.feature_names_str() + .into_iter() + .map(|s| s.to_owned()) + .collect() + } + + fn feature_names_string_set(&self) -> BTreeSet { + self.feature_names_str() + .into_iter() + .map(|s| s.to_owned()) + .collect() + } +} + +/// For feature checks against a non-inner object +pub enum FeatureChecks { + None, + RequireAllSubstates, + ForFeatures { own_features: TOwn }, +} + +impl From for FeatureChecks { + fn from(value: T) -> Self { + FeatureChecks::ForFeatures { + own_features: value, + } + } +} + +impl FeatureChecks { + pub fn assert_valid( + &self, + substate_name: &'static str, + condition: &Condition, + is_present: bool, + ) -> Result<(), RuntimeError> { + let is_valid = match self { + FeatureChecks::None => Ok(()), + FeatureChecks::RequireAllSubstates => { + if is_present { + Ok(()) + } else { + Err(format!("Required all substates to be present, but {} was not present", substate_name)) + } + }, + FeatureChecks::ForFeatures { own_features } => { + match condition { + Condition::Always => { + if is_present { + Ok(()) + } else { + Err(format!("Substate condition for {} required it to be always present, but it was not", substate_name)) + } + } + Condition::IfFeature(feature) => { + let feature_enabled = own_features.feature_names_str().contains(&feature.as_str()); + if feature_enabled && !is_present { + Err(format!("Substate condition for {} required it to be present when the feature {} was enabled, but it was absent", substate_name, feature)) + } else if !feature_enabled && is_present { + Err(format!("Substate condition for {} required it to be absent when the feature {} was disabled, but it was present", substate_name, feature)) + } else { + Ok(()) + } + }, + Condition::IfOuterFeature(_) => { + Err(format!("Substate condition for {} required an outer object feature, but the blueprint does not have an outer blueprint defined", substate_name)) + } + } + }, + }; + is_valid.map_err(|error_message| { + RuntimeError::SystemError(SystemError::InvalidNativeSubstatesForFeature(error_message)) + }) + } +} + +/// For feature checks against an inner object +pub enum InnerObjectFeatureChecks { + None, + RequireAllSubstates, + ForFeatures { + own_features: TOwn, + outer_object_features: TOuter, + }, +} + +impl InnerObjectFeatureChecks { + pub fn assert_valid( + &self, + substate_name: &'static str, + condition: &Condition, + is_present: bool, + ) -> Result<(), RuntimeError> { + let is_valid = match self { + Self::None => Ok(()), + Self::RequireAllSubstates => { + if is_present { + Ok(()) + } else { + Err(format!( + "Required all substates to be present, but {} was not present", + substate_name + )) + } + } + Self::ForFeatures { + own_features, + outer_object_features, + } => match condition { + Condition::Always => { + if is_present { + Ok(()) + } else { + Err(format!("Substate condition for {} required it to be always present, but it was not", substate_name)) + } + } + Condition::IfFeature(feature) => { + let feature_enabled = + own_features.feature_names_str().contains(&feature.as_str()); + if feature_enabled && !is_present { + Err(format!("Substate condition for {} required it to be present when the feature {} was enabled, but it was absent", substate_name, feature)) + } else if !feature_enabled && is_present { + Err(format!("Substate condition for {} required it to be absent when the feature {} was disabled, but it was present", substate_name, feature)) + } else { + Ok(()) + } + } + Condition::IfOuterFeature(feature) => { + let feature_enabled = outer_object_features + .feature_names_str() + .contains(&feature.as_str()); + if feature_enabled && !is_present { + Err(format!("Substate condition for {} required it to be present when the outer object feature {} was enabled, but it was absent", substate_name, feature)) + } else if !feature_enabled && is_present { + Err(format!("Substate condition for {} required it to be absent when the outer object feature {} was disabled, but it was present", substate_name, feature)) + } else { + Ok(()) + } + } + }, + }; + is_valid.map_err(|error_message| { + RuntimeError::SystemError(SystemError::InvalidNativeSubstatesForFeature(error_message)) + }) + } +} + pub trait FieldContent>: Sized { fn into_locked_substate(self) -> FieldSubstate { FieldSubstate::new_locked_field(self.into()) @@ -39,15 +198,14 @@ pub trait SortedIndexEntryContent>: Sized { } } -/// Generates types and typed-interfaces for native blueprints and their -/// interaction with the substate store. +/// Generates types and typed-interfaces for native blueprints, their +/// state models, features, partitions, and their interaction with +/// the substate store. /// -/// * For fields, assumes the existence of a type called: -/// * `FieldV1` -/// * For collections, assumes the existence of types called: -/// * `ValueV1` +/// See the below structure for detail on how it should look - or check +/// out [../package/substates.rs](the package substates definition). /// -/// The types should look something like +/// Each type token-tree should look something like the following. /// ``` /// { /// kind: StaticSingleVersioned, @@ -67,12 +225,50 @@ pub trait SortedIndexEntryContent>: Sized { /// latest: V3, /// } /// ``` +/// +/// By default, choose `StaticSingleVersioned`, which will create a +/// forward-compatible enum wrapper with a single version. +/// For Fields, it will assume the existence of a type called +/// `V1` and will generate the following types: +/// * `` - a type alias for the latest version (V1). +/// * `Versioned` - the enum wrapper with a single version. +/// * `FieldContent` - a transparent new type for the full content (wrapping the versioned enum) +/// * `FieldSubstate` - a type for the full system-wrapped substate +/// +/// For collection values, it will assume the existence of `V1` +/// and generate the following types: +/// * `` - a type alias for the latest version (V1). +/// * `Versioned` - the enum wrapper with a single version. +/// * `EntryContent` - a transparent new type for the full content (wrapping the versioned enum) +/// * `EntrySubstate` - a type for the full system-wrapped substate +/// +/// For collection keys, it will assume the existence of `KeyInnerV1` +/// and generate the following types: +/// * `KeyInner` - a type alias for the latest version (V1) +/// * `VersionedKeyInner` - the enum wrapper with a single version. +/// * `KeyContent` - a transparent new type for the full key content (wrapping the versioned enum) +/// * `Key` - a type for the full key (eg includes the u16 for a sorted index key) #[allow(unused)] macro_rules! declare_native_blueprint_state { ( blueprint_ident: $blueprint_ident:ident, blueprint_snake_case: $blueprint_property_name:ident, - instance_schema_types: [ + $( + outer_blueprint: { + ident: $outer_blueprint_ident:ident + $(,)? + }, + )? + features: { + $( + $feature_property_name:ident: { + ident: $feature_ident:ident, + description: $feature_description:expr, + } + ),* + $(,)? + }, + instance_schema_types: { // If no types => instance schema disabled $( $instance_type_property_name:ident: { @@ -80,7 +276,7 @@ macro_rules! declare_native_blueprint_state { } ),* $(,)? - ], + }, fields: { $( $field_property_name:ident: { @@ -118,7 +314,7 @@ macro_rules! declare_native_blueprint_state { use sbor::*; use $crate::types::*; use $crate::track::interface::*; - use $crate::errors::RuntimeError; + use $crate::errors::*; use $crate::system::system::*; use radix_engine_interface::api::*; //-------------------------------------------------------- @@ -135,7 +331,7 @@ macro_rules! declare_native_blueprint_state { // > Set up the FieldContent trait for anything which can be resolved into the field content generate_content_type!( content_trait: FieldContent, - ident_core: [<$blueprint_ident $field_ident Field>], + ident_core: [<$blueprint_ident $field_ident>], #[derive(Debug, PartialEq, Eq, ScryptoSbor)] struct [<$blueprint_ident $field_ident FieldContent>] = $field_type ); @@ -158,15 +354,19 @@ macro_rules! declare_native_blueprint_state { generate_content_type!( content_trait: KeyContent, ident_core: [<$blueprint_ident $collection_ident KeyInner>], - #[derive(Debug, Clone, Hash, PartialEq, Eq, ScryptoSbor)] + #[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, ScryptoSbor)] #[sbor(transparent_name)] - struct [<$blueprint_ident $collection_ident Key>] = $collection_key_type + struct [<$blueprint_ident $collection_ident KeyContent>] = $collection_key_type ); + // TODO(David): Tweak when I work out the right strategy for keys. Probably just want a single new-type. + // So probably just don't use generate_content_type but use generate_key_type or something instead? + pub type [<$blueprint_ident $collection_ident Key>] = [<$blueprint_ident $collection_ident KeyContent>]; + // TODO(David) - Properly handle SortedIndex: // Fix Key types for SortedIndex to have a named u16 part of the key, // use a different key trait, and use .for_sorted_key in the below. - impl TryFrom<&SubstateKey> for [<$blueprint_ident $collection_ident Key>] { + impl TryFrom<&SubstateKey> for [<$blueprint_ident $collection_ident KeyContent>] { type Error = (); fn try_from(substate_key: &SubstateKey) -> Result { @@ -183,14 +383,14 @@ macro_rules! declare_native_blueprint_state { // > Set up the _EntryContent traits generate_content_type!( content_trait: [<$collection_type EntryContent>], - ident_core: [<$blueprint_ident $collection_ident Value>], + ident_core: [<$blueprint_ident $collection_ident>], #[derive(Debug, PartialEq, Eq, ScryptoSbor)] - struct [<$blueprint_ident $collection_ident ValueContent>] = $collection_value_type + struct [<$blueprint_ident $collection_ident EntryContent>] = $collection_value_type ); // > Set up the _EntrySubstate alias for the system-wrapped substate generate_system_substate_type_alias!( $collection_type, - type [<$blueprint_ident $collection_ident EntrySubstate>] = WRAPPED [<$blueprint_ident $collection_ident ValueContent>] + type [<$blueprint_ident $collection_ident EntrySubstate>] = WRAPPED [<$blueprint_ident $collection_ident EntryContent>] ); )* @@ -269,6 +469,38 @@ macro_rules! declare_native_blueprint_state { } } + #[derive(Debug, Clone, Copy, Sbor, PartialEq, Eq, Hash)] + pub struct [<$blueprint_ident FeatureSet>] { + $(pub [<$feature_property_name>]: bool,)* + } + + impl FeatureSetResolver for [<$blueprint_ident FeatureSet>] { + fn feature_names_str(&self) -> Vec<&'static str> { + let mut names = vec![]; + $( + if self.[<$feature_property_name>] { + names.push(stringify!($feature_property_name)); + } + )* + names + } + } + + #[derive(Debug, Clone, Copy, Sbor, PartialEq, Eq, Hash)] + pub enum [<$blueprint_ident Feature>] { + $($feature_ident,)* + } + + impl BlueprintFeature for [<$blueprint_ident Feature>] { + fn feature_name(&self) -> &'static str { + match *self { + $( + Self::$feature_ident => stringify!(Self::$feature_property_name), + )* + } + } + } + //--------------------------------- // Typed Substate - Keys and Values //--------------------------------- @@ -347,7 +579,7 @@ macro_rules! declare_native_blueprint_state { // TODO(David) - Implement instance schema fields.push(FieldSchema { field: TypeRef::Static( - type_aggregator.add_child_type_and_descendents::<[]>() + type_aggregator.add_child_type_and_descendents::<[<$blueprint_ident $field_ident FieldContent>]>() ), condition: $field_condition, }); @@ -359,9 +591,9 @@ macro_rules! declare_native_blueprint_state { $collection_type, type_aggregator, $collection_key_type, - [<$blueprint_ident $collection_ident Key>], + [<$blueprint_ident $collection_ident KeyContent>], $collection_value_type, - [<$blueprint_ident $collection_ident ValueContent>], + [<$blueprint_ident $collection_ident EntryContent>], $collection_can_own )); )* @@ -383,6 +615,7 @@ macro_rules! declare_native_blueprint_state { /// * Instance schemas /// * Feature-dependent fields /// * IndexEntries (because the underlying new_object API doesn't support them) + #[derive(Debug, Default)] pub struct [<$blueprint_ident StateInit>] { $( pub $field_property_name: Option<[<$blueprint_ident $field_ident FieldSubstate>]>, @@ -395,27 +628,35 @@ macro_rules! declare_native_blueprint_state { )* } + type [<$blueprint_ident FeatureChecks>] = [<$(ignore_arg!($outer_blueprint_ident) InnerObject)? FeatureChecks>]::< + [<$blueprint_ident FeatureSet>], + $([<$outer_blueprint_ident FeatureSet>],)? + >; + impl [<$blueprint_ident StateInit>] { - pub fn into_system_substates(self) -> (Vec, BTreeMap, KVEntry>>) { - let mut field_values = vec![]; + pub fn into_system_substates(self, feature_checks: [<$blueprint_ident FeatureChecks>]) -> Result<(BTreeMap, BTreeMap, KVEntry>>), RuntimeError> { + let mut field_values = BTreeMap::new(); $( { - let field = self.$field_property_name.expect( - concat!( - "The field `", - stringify!($field_property_name), - "` was None. Until the system and macro supports feature-based optional fields, all fields need to be present" - ) - ); - let field_content = scrypto_encode(&field.value).unwrap(); - let locked = match &field.mutability { - SubstateMutability::Mutable => true, - SubstateMutability::Immutable => false, - }; - field_values.push(FieldValue { - value: field_content, - locked, - }); + feature_checks.assert_valid( + stringify!($field_ident), + &$field_condition, + self.$field_property_name.is_some(), + )?; + if let Some(field) = self.$field_property_name { + let field_content = scrypto_encode(&field.value).unwrap(); + let locked = match &field.mutability { + SubstateMutability::Mutable => true, + SubstateMutability::Immutable => false, + }; + field_values.insert( + [<$blueprint_ident Field>]::$field_ident.into(), + FieldValue { + value: field_content, + locked, + } + ); + } } )* let mut all_collection_entries = BTreeMap::new(); @@ -434,30 +675,45 @@ macro_rules! declare_native_blueprint_state { collection_index += 1; } )* - (field_values, all_collection_entries) + Ok((field_values, all_collection_entries)) + } + + // TODO: Remove this when the new object api supports non-vec field values + pub fn into_system_substates_legacy(self, feature_checks: [<$blueprint_ident FeatureChecks>]) -> Result<(Vec, BTreeMap, KVEntry>>), RuntimeError> { + let (mut field_values, collection_entries) = self.into_system_substates(feature_checks)?; + let mut field_values_vec = vec![]; + let mut field_index = 0; + $( + { + let field_value = field_values.remove(&field_index).expect( + concat!( + "The field `", + stringify!($field_property_name), + "` was None. Until the system and macro supports feature-based optional fields, all fields need to be present" + ) + ); + field_values_vec.push(field_value); + field_index += 1; + } + )* + Ok((field_values_vec, collection_entries)) } /// This is used mostly for flashing - pub fn into_kernel_main_partitions(self) -> NodeSubstates { + pub fn into_kernel_main_partitions(self, feature_checks: [<$blueprint_ident FeatureChecks>]) -> Result { // PartitionNumber => SubstateKey => IndexedScryptoValue let mut partitions: NodeSubstates = BTreeMap::new(); - let (field_values, mut kv_entries) = self.into_system_substates(); + let (mut field_values, mut kv_entries) = self.into_system_substates(feature_checks)?; // Fields { - let mut field_index = 0u8; let mut field_partition_substates = BTreeMap::new(); - $({ - let key = SubstateKey::from([<$blueprint_ident Field>]::$field_ident); - let expected_field_index = u8::from([<$blueprint_ident Field>]::$field_ident); - // Double-check they agree - assert_eq!(field_index, expected_field_index); + for (field_index, field_value) in field_values { field_partition_substates.insert( - key, - IndexedScryptoValue::from_typed(&field_values[field_index as usize]), + SubstateKey::Field(field_index), + IndexedScryptoValue::from_typed(&field_value), ); - field_index += 1; - })* + } partitions.insert( [<$blueprint_ident Partition>]::Field.as_main_partition(), field_partition_substates, @@ -465,8 +721,8 @@ macro_rules! declare_native_blueprint_state { } // Each Collection + let mut collection_index = 0u8; $({ - let mut collection_index = 0u8; let collection_kv_entries = kv_entries.remove(&collection_index).unwrap(); let collection_partition = [<$blueprint_ident Partition>]::[<$collection_ident $collection_type>]; let collection_partition_substates = collection_kv_entries @@ -501,19 +757,31 @@ macro_rules! declare_native_blueprint_state { collection_partition.as_main_partition(), collection_partition_substates, ); + collection_index += 1; })* - partitions + Ok(partitions) } - pub fn into_new_object>(self, api: &mut Y) -> Result { - let (field_values, all_collection_entries) = self.into_system_substates(); + pub fn into_new_object>( + self, + api: &mut Y, + own_features: [<$blueprint_ident FeatureSet>], + $(outer_object_features: [<$outer_blueprint_ident FeatureSet>],)? + instance_schema: Option, + ) -> Result { + let (field_values, all_collection_entries) = self.into_system_substates_legacy( + [<$blueprint_ident FeatureChecks>]::ForFeatures { + own_features, + $(ignore_arg!($outer_blueprint_ident) outer_object_features,)? + } + )?; api.new_object( stringify!($blueprint_ident), - vec![], // Features - None, // Instance schema - field_values, - all_collection_entries, + own_features.feature_names_str(), + instance_schema, + field_values, // TODO: Change to take the IndexMap and get rid of into_system_substates_legacy + all_collection_entries, // TODO: Change to take other collections, not just KVEntry ) } } @@ -549,7 +817,14 @@ pub(crate) use declare_native_blueprint_state; pub(crate) use helper_macros::*; +#[allow(unused_macros)] mod helper_macros { + macro_rules! ignore_arg { + ($($ignored:tt)*) => {}; + } + #[allow(unused)] + pub(crate) use ignore_arg; + macro_rules! generate_content_type { ( content_trait: $content_trait:ident, @@ -769,23 +1044,24 @@ mod tests { // Check that the below compiles #[derive(Debug, PartialEq, Eq, Sbor)] - pub struct TestBlueprintRoyaltyFieldV1; + pub struct TestBlueprintRoyaltyV1; #[derive(Debug, PartialEq, Eq, Sbor)] - pub struct TestBlueprintMyCoolKeyValueStoreValueV1; + pub struct TestBlueprintMyCoolKeyValueStoreV1; #[derive(Debug, PartialEq, Eq, Sbor)] - pub struct TestBlueprintMyCoolIndexValueV1; + pub struct TestBlueprintMyCoolIndexV1; #[derive(Debug, PartialEq, Eq, Sbor)] - pub struct TestBlueprintMyCoolSortedIndexValueV1; + pub struct TestBlueprintMyCoolSortedIndexV1; use radix_engine_interface::blueprints::package::*; declare_native_blueprint_state! { blueprint_ident: TestBlueprint, blueprint_snake_case: package, - instance_schema_types: [], + features: {}, + instance_schema_types: {}, fields: { royalty: { ident: Royalty, diff --git a/radix-engine/src/blueprints/package/package.rs b/radix-engine/src/blueprints/package/package.rs index 6ad8765ffd2..d479145c2f6 100644 --- a/radix-engine/src/blueprints/package/package.rs +++ b/radix-engine/src/blueprints/package/package.rs @@ -470,40 +470,21 @@ impl SecurifiedRoleAssignment for SecurifiedPackage { } pub fn create_bootstrap_package_partitions( - mut package_state_init: PackageStateInit, + package_state_init: PackageStateInit, metadata: MetadataInit, ) -> NodeSubstates { + // No features necessary + let own_features = PackageFeatureSet { + package_royalty: false, + }; + //----------------- // MAIN PARTITIONS: //----------------- - // TODO: - // - Improve the System ObjectAPI to take field indices (and so support optional) fields - // - Improve the declare_native_blueprint_state! macro to support features - // and so optional field substates, if/when that feature is disabled - { - // Native packages disable the package royalty feature - // Due to (current) restrictions on the into_kernel_main_partitions() method, we need to - // temporarily add in a faked royalty substate - to be removed a few lines later - package_state_init.royalty = Some( - PackageRoyaltyAccumulatorField { - royalty_vault: Vault(Own(NodeId([0; NodeId::LENGTH]))), - } - .into_locked_substate(), - ); - } - - let mut partitions = package_state_init.into_kernel_main_partitions(); - - { - // Remove the Royalty field because that feature is turned off - let field_partition = partitions - .get_mut(&PackagePartition::Field.as_main_partition()) - .unwrap(); - field_partition - .remove_entry(&PackageField::RoyaltyAccumulator.into()) - .unwrap(); - } + let mut partitions = package_state_init + .into_kernel_main_partitions(own_features.into()) + .expect("Expected that correct substates are present for given features"); //------------------- // MODULE PARTITIONS: @@ -546,7 +527,7 @@ pub fn create_bootstrap_package_partitions( blueprint_info: BlueprintInfo { blueprint_id: BlueprintId::new(&PACKAGE_PACKAGE, PACKAGE_BLUEPRINT), outer_obj_info: OuterObjectInfo::default(), - features: btreeset!(), + features: own_features.feature_names_string_set(), instance_schema: None, }, })), @@ -567,19 +548,17 @@ where Y: ClientApi, { package_state_init.royalty = Some( - PackageRoyaltyAccumulatorField { + PackageRoyaltyAccumulator { royalty_vault: Vault(ResourceManager(XRD).new_empty_vault(api)?), } .into_locked_substate(), ); - let (fields, kv_entries) = package_state_init.into_system_substates(); - - let package_object = api.new_object( - PACKAGE_BLUEPRINT, - vec![PACKAGE_ROYALTY_FEATURE], + let package_object = package_state_init.into_new_object( + api, + PackageFeatureSet { + package_royalty: true, + }, None, - fields, - kv_entries, )?; let address = api.globalize( @@ -788,35 +767,49 @@ impl PackageNativePackage { VmPackageValidation::validate(&definition, vm_type, &original_code)?; // Build Package structure - let mut definitions = BTreeMap::new(); - let mut dependencies = BTreeMap::new(); - let mut schemas = BTreeMap::new(); - let mut package_royalties = BTreeMap::new(); - let mut auth_configs = BTreeMap::new(); - let mut vm_type_substates = BTreeMap::new(); - let mut original_code_substates = BTreeMap::new(); - let mut instrumented_code_substates = BTreeMap::new(); + let mut blueprint_version_definitions = IndexMap::default(); + let mut blueprint_version_dependencies = IndexMap::default(); + let mut schemas = IndexMap::default(); + let mut blueprint_version_royalty_configs = IndexMap::default(); + let mut blueprint_version_auth_configs = IndexMap::default(); + let mut vm_type_substates = IndexMap::default(); + let mut original_code_substates = IndexMap::default(); + let mut instrumented_code_substates = IndexMap::default(); let code_hash = CodeHash::from(hash(&original_code)); - vm_type_substates.insert(code_hash, PackageCodeVmTypeValue { vm_type }); + vm_type_substates.insert( + code_hash.into_key(), + PackageCodeVmType { vm_type }.into_locked_substate(), + ); original_code_substates.insert( - code_hash, - PackageCodeOriginalCodeValue { + code_hash.into_key(), + PackageCodeOriginalCode { code: original_code, - }, + } + .into_locked_substate(), ); - if let Some(code) = instrumented_code { - instrumented_code_substates - .insert(code_hash, PackageCodeInstrumentedCodeValue { code }); + if let Some(instrumented_code) = instrumented_code { + instrumented_code_substates.insert( + code_hash.into_key(), + PackageCodeInstrumentedCode { instrumented_code }.into_locked_substate(), + ); }; { - for (blueprint, definition_init) in definition.blueprints { - auth_configs.insert(blueprint.clone(), definition_init.auth_config); + for (blueprint_name, definition_init) in definition.blueprints { + let blueprint_version_key = BlueprintVersionKey::new_default(blueprint_name); + + blueprint_version_auth_configs.insert( + blueprint_version_key.clone().into_key(), + definition_init.auth_config.into_locked_substate(), + ); let blueprint_schema = definition_init.schema.schema.clone(); let schema_hash = blueprint_schema.generate_schema_hash(); - schemas.insert(schema_hash, blueprint_schema); + schemas.insert( + schema_hash.into_key(), + blueprint_schema.into_locked_substate(), + ); let mut functions = BTreeMap::new(); let mut function_exports = BTreeMap::new(); @@ -899,86 +892,37 @@ impl PackageNativePackage { .collect() }, }; - definitions.insert(blueprint.clone(), definition); - - let minor_version_config = BlueprintDependencies { - dependencies: definition_init.dependencies, - }; - dependencies.insert(blueprint.clone(), minor_version_config); + blueprint_version_definitions.insert( + blueprint_version_key.clone().into_key(), + definition.into_locked_substate(), + ); + + blueprint_version_dependencies.insert( + blueprint_version_key.clone().into_key(), + BlueprintDependencies { + dependencies: definition_init.dependencies, + } + .into_locked_substate(), + ); - package_royalties.insert(blueprint.clone(), definition_init.royalty_config); + blueprint_version_royalty_configs.insert( + blueprint_version_key.into_key(), + definition_init.royalty_config.into_locked_substate(), + ); } }; - let package_state_init = PackageStateInit { - royalty: None, // This is added later, for globalized packages, - blueprint_version_definitions: definitions - .into_iter() - .map(|(blueprint_name, blueprint_definition)| { - ( - BlueprintVersionKey::new_default(blueprint_name).into_key(), - blueprint_definition.into_locked_substate(), - ) - }) - .collect(), - blueprint_version_dependencies: dependencies - .into_iter() - .map(|(blueprint_name, blueprint_dependencies)| { - ( - BlueprintVersionKey::new_default(blueprint_name).into_key(), - blueprint_dependencies.into_locked_substate(), - ) - }) - .collect(), - schemas: schemas - .into_iter() - .map(|(schema_hash, schema)| { - ( - SchemaHash::from(schema_hash).into_key(), - schema.into_locked_substate(), - ) - }) - .collect(), - blueprint_version_royalty_configs: package_royalties - .into_iter() - .map(|(blueprint_name, royalty_config)| { - ( - BlueprintVersionKey::new_default(blueprint_name).into_key(), - royalty_config.into_locked_substate(), - ) - }) - .collect(), - blueprint_version_auth_configs: auth_configs - .into_iter() - .map(|(blueprint_name, auth_config)| { - ( - BlueprintVersionKey::new_default(blueprint_name).into_key(), - auth_config.into_locked_substate(), - ) - }) - .collect(), - code_vm_type: vm_type_substates - .into_iter() - .map(|(code_hash, vm_type)| (code_hash.into_key(), vm_type.into_locked_substate())) - .collect(), - code_original_code: original_code_substates - .into_iter() - .map(|(code_hash, original_code)| { - (code_hash.into_key(), original_code.into_locked_substate()) - }) - .collect(), - code_instrumented_code: instrumented_code_substates - .into_iter() - .map(|(code_hash, instrumented_code)| { - ( - code_hash.into_key(), - instrumented_code.into_locked_substate(), - ) - }) - .collect(), - }; - - Ok(package_state_init) + Ok(PackageStateInit { + royalty: None, // This is added later, if the feature is turned on + blueprint_version_definitions, + blueprint_version_dependencies, + schemas, + blueprint_version_royalty_configs, + blueprint_version_auth_configs, + code_vm_type: vm_type_substates, + code_original_code: original_code_substates, + code_instrumented_code: instrumented_code_substates, + }) } pub(crate) fn publish_native( @@ -1170,7 +1114,7 @@ impl PackageRoyaltyNativeBlueprint { LockFlags::read_only(), )?; - let mut substate: PackageRoyaltyAccumulatorFieldContent = api.field_read_typed(handle)?; + let substate: PackageRoyaltyAccumulatorFieldContent = api.field_read_typed(handle)?; let bucket = substate.0.into_latest().royalty_vault.take_all(api)?; Ok(bucket) diff --git a/radix-engine/src/blueprints/package/substates.rs b/radix-engine/src/blueprints/package/substates.rs index bcf9b4aacb8..c92f10db24f 100644 --- a/radix-engine/src/blueprints/package/substates.rs +++ b/radix-engine/src/blueprints/package/substates.rs @@ -5,14 +5,20 @@ use crate::types::*; declare_native_blueprint_state! { blueprint_ident: Package, blueprint_snake_case: package, - instance_schema_types: [], + features: { + package_royalty: { + ident: PackageRoyalty, + description: "Enables the package royalty substate", + } + }, + instance_schema_types: {}, fields: { royalty: { ident: RoyaltyAccumulator, field_type: { kind: StaticSingleVersioned, }, - condition: Condition::Always, + condition: Condition::if_feature(PackageFeature::PackageRoyalty), } }, collections: { @@ -112,7 +118,7 @@ declare_native_blueprint_state! { //------------- #[derive(Debug, PartialEq, Eq, ScryptoSbor)] -pub struct PackageRoyaltyAccumulatorFieldV1 { +pub struct PackageRoyaltyAccumulatorV1 { /// The vault for collecting package royalties. pub royalty_vault: Vault, } @@ -123,16 +129,16 @@ pub struct PackageRoyaltyAccumulatorFieldV1 { // TODO(David): Change to VersionedSchema when can define a type as not-implicitly-versioned // TODO: Move to Schema partition when we have it -pub type PackageSchemaValueV1 = ScryptoSchema; +pub type PackageSchemaV1 = ScryptoSchema; //--------------------------------------- // Collection models - By BlueprintVersion //--------------------------------------- -pub type PackageBlueprintVersionDefinitionValueV1 = BlueprintDefinition; -pub type PackageBlueprintVersionDependenciesValueV1 = BlueprintDependencies; -pub type PackageBlueprintVersionRoyaltyConfigValueV1 = PackageRoyaltyConfig; -pub type PackageBlueprintVersionAuthConfigValueV1 = AuthConfig; +pub type PackageBlueprintVersionDefinitionV1 = BlueprintDefinition; +pub type PackageBlueprintVersionDependenciesV1 = BlueprintDependencies; +pub type PackageBlueprintVersionRoyaltyConfigV1 = PackageRoyaltyConfig; +pub type PackageBlueprintVersionAuthConfigV1 = AuthConfig; //--------------------------------------- // Collection models - By Code @@ -140,17 +146,17 @@ pub type PackageBlueprintVersionAuthConfigValueV1 = AuthConfig; #[derive(Debug, PartialEq, Eq, ScryptoSbor)] #[sbor(transparent)] -pub struct PackageCodeVmTypeValueV1 { +pub struct PackageCodeVmTypeV1 { pub vm_type: VmType, } #[derive(PartialEq, Eq, ScryptoSbor)] #[sbor(transparent)] -pub struct PackageCodeOriginalCodeValueV1 { +pub struct PackageCodeOriginalCodeV1 { pub code: Vec, } -impl Debug for PackageCodeOriginalCodeValueV1 { +impl Debug for PackageCodeOriginalCodeV1 { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("PackageCodeOriginalCodeValueV1") .field("len", &self.code.len()) @@ -160,14 +166,14 @@ impl Debug for PackageCodeOriginalCodeValueV1 { #[derive(PartialEq, Eq, ScryptoSbor)] #[sbor(transparent)] -pub struct PackageCodeInstrumentedCodeValueV1 { - pub code: Vec, +pub struct PackageCodeInstrumentedCodeV1 { + pub instrumented_code: Vec, } -impl Debug for PackageCodeInstrumentedCodeValueV1 { +impl Debug for PackageCodeInstrumentedCodeV1 { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("PackageCodeInstrumentedCodeValueV1") - .field("len", &self.code.len()) + .field("len", &self.instrumented_code.len()) .finish() } } diff --git a/radix-engine/src/errors.rs b/radix-engine/src/errors.rs index f56534c285d..1326aa3ebe3 100644 --- a/radix-engine/src/errors.rs +++ b/radix-engine/src/errors.rs @@ -254,6 +254,7 @@ pub enum SystemError { TransactionRuntimeModuleNotEnabled, PayloadValidationAgainstSchemaError(PayloadValidationAgainstSchemaError), EventError(EventError), + InvalidNativeSubstatesForFeature(String), } #[derive(Debug, Clone, PartialEq, Eq, ScryptoSbor)] diff --git a/radix-engine/src/track/interface.rs b/radix-engine/src/track/interface.rs index dd2b95e89f1..6e3f5ed0118 100644 --- a/radix-engine/src/track/interface.rs +++ b/radix-engine/src/track/interface.rs @@ -29,6 +29,38 @@ impl CallbackError { pub type NodeSubstates = BTreeMap>; +pub use database_update_conversion::*; + +mod database_update_conversion { + // This is for resim, but it makes sense to put it here alongside NodeSubstates + use super::*; + use radix_engine_store_interface::db_key_mapper::*; + use radix_engine_store_interface::interface::*; + + pub trait IntoDatabaseUpdates { + fn into_database_updates(self, node_id: &NodeId) -> DatabaseUpdates; + } + + impl IntoDatabaseUpdates for NodeSubstates { + fn into_database_updates(self, node_id: &NodeId) -> DatabaseUpdates { + self.into_iter() + .map(|(partition_number, substates)| { + let db_partition_key = M::to_db_partition_key(node_id, partition_number); + let partition_updates = substates + .into_iter() + .map(|(substate_key, value)| { + let db_sort_key = M::to_db_sort_key(&substate_key); + let update = DatabaseUpdate::Set(value.as_vec_ref().clone()); + (db_sort_key, update) + }) + .collect(); + (db_partition_key, partition_updates) + }) + .collect() + } + } +} + pub enum TrackedSubstateInfo { New, Updated, diff --git a/radix-engine/src/track/mod.rs b/radix-engine/src/track/mod.rs index 23f761ceb61..439640a1a6d 100644 --- a/radix-engine/src/track/mod.rs +++ b/radix-engine/src/track/mod.rs @@ -5,4 +5,5 @@ pub mod utils; #[cfg(test)] mod test; +pub use interface::*; pub use track::*; diff --git a/radix-engine/src/vm/vm.rs b/radix-engine/src/vm/vm.rs index da707ef91ba..bffff2533ac 100644 --- a/radix-engine/src/vm/vm.rs +++ b/radix-engine/src/vm/vm.rs @@ -137,11 +137,15 @@ impl<'g, W: WasmEngine + 'g, E: NativeVmExtension> SystemCallbackObject for Vm<' api.kernel_get_system() .callback_obj .scrypto_vm - .create_instance(address, export.code_hash, &instrumented_code.code) + .create_instance( + address, + export.code_hash, + &instrumented_code.instrumented_code, + ) }; api.consume_cost_units(ClientCostingEntry::PrepareWasmCode { - size: instrumented_code.code.len(), + size: instrumented_code.instrumented_code.len(), })?; let output = diff --git a/scrypto-schema/src/lib.rs b/scrypto-schema/src/lib.rs index 9f5f3255c57..26e6c8c4cf1 100644 --- a/scrypto-schema/src/lib.rs +++ b/scrypto-schema/src/lib.rs @@ -183,6 +183,10 @@ impl BlueprintCollectionSchema { } } +pub trait BlueprintFeature { + fn feature_name(&self) -> &'static str; +} + #[derive(Debug, Clone, PartialEq, Eq, Sbor)] pub enum Condition { Always, @@ -190,6 +194,16 @@ pub enum Condition { IfOuterFeature(String), } +impl Condition { + pub fn if_feature(feature: impl BlueprintFeature) -> Self { + Self::IfFeature(feature.feature_name().into()) + } + + pub fn if_outer_feature(feature: impl BlueprintFeature) -> Self { + Self::IfOuterFeature(feature.feature_name().into()) + } +} + #[derive(Debug, Clone, PartialEq, Eq, ScryptoSbor, ManifestSbor)] pub struct FieldSchema { pub field: V, diff --git a/simulator/src/ledger/dumper.rs b/simulator/src/ledger/dumper.rs index 1a1e18e5a65..734eb13f42e 100644 --- a/simulator/src/ledger/dumper.rs +++ b/simulator/src/ledger/dumper.rs @@ -3,7 +3,7 @@ use crate::utils::*; use colored::*; use radix_engine::blueprints::resource::*; use radix_engine::system::node_modules::type_info::TypeInfoSubstate; -use radix_engine::system::system::{FieldSubstate, KeyValueEntrySubstate}; +use radix_engine::system::system::FieldSubstate; use radix_engine::types::*; use radix_engine_interface::blueprints::package::*; use radix_engine_interface::network::NetworkDefinition; diff --git a/simulator/src/resim/cmd_publish.rs b/simulator/src/resim/cmd_publish.rs index 6240ee35bad..ea861a55a88 100644 --- a/simulator/src/resim/cmd_publish.rs +++ b/simulator/src/resim/cmd_publish.rs @@ -1,15 +1,15 @@ use clap::Parser; use colored::*; +use radix_engine::blueprints::macros::*; +use radix_engine::track::IntoDatabaseUpdates; use radix_engine::types::*; use radix_engine_interface::blueprints::package::{ BlueprintDefinition, BlueprintDependencies, FunctionSchema, IndexedStateSchema, PackageExport, TypePointer, VmType, *, }; use radix_engine_interface::schema::TypeRef; -use radix_engine_store_interface::{ - db_key_mapper::{DatabaseKeyMapper, SpreadPrefixKeyMapper}, - interface::{CommittableSubstateDatabase, DatabaseUpdate}, -}; +use radix_engine_queries::typed_substate_layout::*; +use radix_engine_store_interface::interface::CommittableSubstateDatabase; use std::ffi::OsStr; use std::fs; use std::path::PathBuf; @@ -72,91 +72,51 @@ impl Publish { let node_id: NodeId = package_address.0.into(); - let blueprints_partition_key = SpreadPrefixKeyMapper::to_db_partition_key( - &node_id, - MAIN_BASE_PARTITION - .at_offset(PACKAGE_BLUEPRINTS_PARTITION_OFFSET) - .unwrap(), - ); - let schemas_partition_key = SpreadPrefixKeyMapper::to_db_partition_key( - &node_id, - MAIN_BASE_PARTITION - .at_offset(PACKAGE_SCHEMAS_PARTITION_OFFSET) - .unwrap(), - ); - let dependencies_partition_key = SpreadPrefixKeyMapper::to_db_partition_key( - &node_id, - MAIN_BASE_PARTITION - .at_offset(PACKAGE_BLUEPRINT_DEPENDENCIES_PARTITION_OFFSET) - .unwrap(), - ); - let vm_type_partition_key = SpreadPrefixKeyMapper::to_db_partition_key( - &node_id, - MAIN_BASE_PARTITION - .at_offset(PACKAGE_VM_TYPE_PARTITION_OFFSET) - .unwrap(), - ); - let original_code_partition_key = SpreadPrefixKeyMapper::to_db_partition_key( - &node_id, - MAIN_BASE_PARTITION - .at_offset(PACKAGE_ORIGINAL_CODE_PARTITION_OFFSET) - .unwrap(), - ); - let instrumented_code_partition_key = SpreadPrefixKeyMapper::to_db_partition_key( - &node_id, - MAIN_BASE_PARTITION - .at_offset(PACKAGE_INSTRUMENTED_CODE_PARTITION_OFFSET) - .unwrap(), - ); - let mut blueprint_updates = index_map_new(); - let mut dependency_updates = index_map_new(); - let mut schema_updates = index_map_new(); - let mut vm_type_updates = index_map_new(); - let mut original_code_updates = index_map_new(); - let mut instrumented_code_updates = index_map_new(); - - let code_hash = hash(&code); + let code_hash = CodeHash::from(hash(&code)); let instrumented_code = WasmValidator::default() .validate(&code, package_definition.blueprints.values()) .map_err(Error::InvalidPackage)? .0; - let vm_type = PackageVmTypeSubstate { - vm_type: VmType::ScryptoV1, - }; - let original_code = PackageOriginalCodeSubstate { code }; - let instrumented_code = PackageOriginalCodeSubstate { - code: instrumented_code, - }; - { - let key = - SpreadPrefixKeyMapper::map_to_db_sort_key(&scrypto_encode(&code_hash).unwrap()); - let update = DatabaseUpdate::Set(scrypto_encode(&vm_type).unwrap()); - vm_type_updates.insert(key, update); - let key = - SpreadPrefixKeyMapper::map_to_db_sort_key(&scrypto_encode(&code_hash).unwrap()); - let update = DatabaseUpdate::Set(scrypto_encode(&original_code).unwrap()); - original_code_updates.insert(key, update); - - let key = - SpreadPrefixKeyMapper::map_to_db_sort_key(&scrypto_encode(&code_hash).unwrap()); - let update = DatabaseUpdate::Set(scrypto_encode(&instrumented_code).unwrap()); - instrumented_code_updates.insert(key, update); - } + let mut package_state_to_set = PackageStateInit { + royalty: None, // No change + // To be set later in this function + blueprint_version_definitions: indexmap!(), + blueprint_version_dependencies: indexmap!(), + schemas: indexmap!(), + blueprint_version_royalty_configs: indexmap!(), + blueprint_version_auth_configs: indexmap!(), + // Code set now + code_vm_type: indexmap! { + code_hash.into_key() => PackageCodeVmType { + vm_type: VmType::ScryptoV1 + }.into_locked_substate() + }, + code_original_code: indexmap! { + code_hash.into_key() => PackageCodeOriginalCode { + code + }.into_locked_substate() + }, + code_instrumented_code: indexmap! { + code_hash.into_key() => PackageCodeInstrumentedCode { + instrumented_code + }.into_locked_substate() + }, + }; - for (b, s) in package_definition.blueprints { + for (blueprint_name, blueprint_definition) in package_definition.blueprints { let mut functions = BTreeMap::new(); let mut function_exports = BTreeMap::new(); - let blueprint_schema = s.schema.clone(); + let blueprint_version_key = BlueprintVersionKey::new_default(blueprint_name); + let blueprint_schema = blueprint_definition.schema.clone(); let schema_hash = blueprint_schema.schema.generate_schema_hash(); - let key = SpreadPrefixKeyMapper::map_to_db_sort_key( - &scrypto_encode(&schema_hash).unwrap(), + package_state_to_set.schemas.insert( + schema_hash.into_key(), + blueprint_schema.schema.into_locked_substate(), ); - let update = DatabaseUpdate::Set(scrypto_encode(&blueprint_schema).unwrap()); - schema_updates.insert(key, update); - for (function, setup) in s.schema.functions.functions { + for (function, setup) in blueprint_definition.schema.functions.functions { functions.insert( function.clone(), FunctionSchema { @@ -182,7 +142,7 @@ impl Publish { function_exports.insert(function, export); } - let events = s + let events = blueprint_definition .schema .events .event_schema @@ -202,40 +162,47 @@ impl Publish { let def = BlueprintDefinition { interface: BlueprintInterface { - generics: s.schema.generics, - blueprint_type: s.blueprint_type, + generics: blueprint_definition.schema.generics, + blueprint_type: blueprint_definition.blueprint_type, is_transient: false, - feature_set: s.feature_set, + feature_set: blueprint_definition.feature_set, functions, events, - state: IndexedStateSchema::from_schema(schema_hash, s.schema.state), + state: IndexedStateSchema::from_schema( + schema_hash, + blueprint_definition.schema.state, + ), }, function_exports, hook_exports: BTreeMap::new(), }; - let key = SpreadPrefixKeyMapper::map_to_db_sort_key(&scrypto_encode(&b).unwrap()); - let update = DatabaseUpdate::Set(scrypto_encode(&def).unwrap()); - blueprint_updates.insert(key, update); - - let config = BlueprintDependencies { - dependencies: s.dependencies, - }; - let key = SpreadPrefixKeyMapper::map_to_db_sort_key(&scrypto_encode(&b).unwrap()); - let update = DatabaseUpdate::Set( - scrypto_encode(&KeyValueEntrySubstate::entry(config)).unwrap(), + package_state_to_set.blueprint_version_definitions.insert( + blueprint_version_key.clone().into_key(), + def.into_locked_substate(), + ); + package_state_to_set.blueprint_version_dependencies.insert( + blueprint_version_key.clone().into_key(), + BlueprintDependencies { + dependencies: blueprint_definition.dependencies, + } + .into_locked_substate(), ); - dependency_updates.insert(key, update); + package_state_to_set.blueprint_version_auth_configs.insert( + blueprint_version_key.clone().into_key(), + blueprint_definition.auth_config.into_locked_substate(), + ); + package_state_to_set + .blueprint_version_royalty_configs + .insert( + blueprint_version_key.clone().into_key(), + blueprint_definition.royalty_config.into_locked_substate(), + ); } - let database_updates = indexmap!( - blueprints_partition_key => blueprint_updates, - dependencies_partition_key => dependency_updates, - schemas_partition_key => schema_updates, - vm_type_partition_key => vm_type_updates, - original_code_partition_key => original_code_updates, - instrumented_code_partition_key => instrumented_code_updates, - ); - + let database_updates = package_state_to_set + .into_kernel_main_partitions(FeatureChecks::None) + .unwrap() + .into_database_updates::(&node_id); substate_db.commit(&database_updates); writeln!(out, "Package updated!").map_err(Error::IOError)?; diff --git a/simulator/src/resim/error.rs b/simulator/src/resim/error.rs index 88613757008..430e4c88db6 100644 --- a/simulator/src/resim/error.rs +++ b/simulator/src/resim/error.rs @@ -3,7 +3,7 @@ use std::path::PathBuf; use radix_engine::errors::{RejectionError, RuntimeError}; use radix_engine::transaction::AbortReason; -use radix_engine::types::{ComponentAddress, Hash, PackageAddress}; +use radix_engine::types::{ComponentAddress, PackageAddress}; use radix_engine::utils::ExtractSchemaError; use radix_engine::vm::wasm::PrepareError; use radix_engine_interface::blueprints::resource::ParseNonFungibleGlobalIdError; From 534681e734be43b49b663f6a7cca65b9af40f000 Mon Sep 17 00:00:00 2001 From: David Edey Date: Mon, 14 Aug 2023 04:15:22 +0100 Subject: [PATCH 15/28] fix: Misc fixes --- assets/faucet.wasm | Bin 241330 -> 241393 bytes assets/flash_loan.wasm | Bin 313614 -> 313761 bytes assets/genesis_helper.wasm | Bin 350536 -> 350577 bytes assets/metadata.wasm | Bin 186661 -> 186758 bytes assets/radiswap.wasm | Bin 224107 -> 224188 bytes .../src/blueprints/package/substates.rs | 11 ------ .../tests/kernel_open_substate.rs | 8 +--- radix-engine/src/blueprints/macros.rs | 37 +++++++++++------- .../src/blueprints/package/package.rs | 18 ++++----- radix-engine/src/system/system.rs | 19 +++++---- radix-engine/src/system/system_callback.rs | 5 +-- radix-engine/src/transaction/system_reader.rs | 18 ++++----- .../src/transaction/transaction_executor.rs | 16 ++++---- radix-engine/src/vm/vm.rs | 14 ++----- sbor-derive-common/src/utils.rs | 7 +++- scrypto-unit/src/test_runner.rs | 23 +++++------ simulator/src/ledger/dumper.rs | 6 +-- simulator/src/resim/mod.rs | 17 ++++---- 18 files changed, 88 insertions(+), 111 deletions(-) diff --git a/assets/faucet.wasm b/assets/faucet.wasm index 75c69c209052dd4e3bc0ea9533b2b342f35d5149..7124b18754e00d698ecdd9cc3b47f0d25ece336b 100644 GIT binary patch delta 83709 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 UagXxnPyg|Y;%DW-yTpqB2l+`4F#rGn delta 47477 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 diff --git a/assets/genesis_helper.wasm b/assets/genesis_helper.wasm index 7edbe9a44e17dca7b887b6c218a65c05673db01a..af7b39af7e201541ff4b4d556c6cc0bb925a3fdc 100644 GIT binary patch literal 350577 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 Q$_Ed<+W?W>mGI2}5BQf4WdHyG diff --git a/assets/metadata.wasm b/assets/metadata.wasm index 22bdd407dc7e9b25ef0b19059f540d267610554e..507581faaa033925073d9b08413c6ccc2a199cdf 100644 GIT binary patch delta 39194 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

qwtI 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 O^Hy?SJo9bj^#21VzK_`e delta 53495 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 Vec<&'static str>; fn feature_names_str_set(&self) -> BTreeSet<&'static str> { @@ -28,6 +28,7 @@ pub trait FeatureSetResolver { } /// For feature checks against a non-inner object +#[derive(Debug)] pub enum FeatureChecks { None, RequireAllSubstates, @@ -205,7 +206,21 @@ pub trait SortedIndexEntryContent>: Sized { /// See the below structure for detail on how it should look - or check /// out [../package/substates.rs](the package substates definition). /// -/// Each type token-tree should look something like the following. +/// For each field, the following types will be created: +/// * `FieldContent` - a transparent new type for the full content +/// * `FieldSubstate` - a type for the full system-wrapped substate +/// +/// For each collection value, the following types will be created: +/// * `EntryContent` - a transparent new type for the full content +/// * `EntrySubstate` - a type for the full system-wrapped substate +/// +/// For each collection key, the following types will be created: +/// * `KeyContent` - a transparent new type for the full key content +/// * `Key` - a type for the full key (eg includes the u16 for a sorted index key) +/// +/// The content of each of the above can take a number of forms. +/// This is configured via specifying the type as one of the following. +/// By default, choose StaticSingleVersioned for fields and collection values. /// ``` /// { /// kind: StaticSingleVersioned, @@ -226,28 +241,22 @@ pub trait SortedIndexEntryContent>: Sized { /// } /// ``` /// -/// By default, choose `StaticSingleVersioned`, which will create a -/// forward-compatible enum wrapper with a single version. +/// Choosing `StaticSingleVersioned`, which will create a +/// forward-compatible enum wrapper with a single version for the content. /// For Fields, it will assume the existence of a type called /// `V1` and will generate the following types: /// * `` - a type alias for the latest version (V1). -/// * `Versioned` - the enum wrapper with a single version. -/// * `FieldContent` - a transparent new type for the full content (wrapping the versioned enum) -/// * `FieldSubstate` - a type for the full system-wrapped substate +/// * `Versioned` - the enum wrapper with a single version. This will be the content of `FieldContent`. /// /// For collection values, it will assume the existence of `V1` /// and generate the following types: /// * `` - a type alias for the latest version (V1). -/// * `Versioned` - the enum wrapper with a single version. -/// * `EntryContent` - a transparent new type for the full content (wrapping the versioned enum) -/// * `EntrySubstate` - a type for the full system-wrapped substate +/// * `Versioned` - the enum wrapper with a single version. This will be the content of `EntryContent`. /// /// For collection keys, it will assume the existence of `KeyInnerV1` /// and generate the following types: /// * `KeyInner` - a type alias for the latest version (V1) -/// * `VersionedKeyInner` - the enum wrapper with a single version. -/// * `KeyContent` - a transparent new type for the full key content (wrapping the versioned enum) -/// * `Key` - a type for the full key (eg includes the u16 for a sorted index key) +/// * `VersionedKeyInner` - the enum wrapper with a single version. This will be the content of `KeyContent`. #[allow(unused)] macro_rules! declare_native_blueprint_state { ( @@ -495,7 +504,7 @@ macro_rules! declare_native_blueprint_state { fn feature_name(&self) -> &'static str { match *self { $( - Self::$feature_ident => stringify!(Self::$feature_property_name), + Self::$feature_ident => stringify!($feature_property_name), )* } } diff --git a/radix-engine/src/blueprints/package/package.rs b/radix-engine/src/blueprints/package/package.rs index d479145c2f6..25f2b06d660 100644 --- a/radix-engine/src/blueprints/package/package.rs +++ b/radix-engine/src/blueprints/package/package.rs @@ -1045,9 +1045,7 @@ impl PackageRoyaltyNativeBlueprint { let handle = api.kernel_open_substate_with_default( receiver, - MAIN_BASE_PARTITION - .at_offset(PACKAGE_ROYALTY_PARTITION_OFFSET) - .unwrap(), + PackagePartition::BlueprintVersionRoyaltyConfigKeyValue.as_main_partition(), &SubstateKey::Map(scrypto_encode(&bp_version_key).unwrap()), LockFlags::read_only(), Some(|| { @@ -1057,13 +1055,13 @@ impl PackageRoyaltyNativeBlueprint { SystemLockData::default(), )?; - let substate: KeyValueEntrySubstate = + let substate: PackageBlueprintVersionRoyaltyConfigEntrySubstate = api.kernel_read_substate(handle)?.as_typed().unwrap(); api.kernel_close_substate(handle)?; let royalty_charge = substate .value - .and_then(|royalty_config| match royalty_config { + .and_then(|royalty_config| match royalty_config.0.into_latest() { PackageRoyaltyConfig::Enabled(royalty_amounts) => { royalty_amounts.get(ident).cloned() } @@ -1074,7 +1072,7 @@ impl PackageRoyaltyNativeBlueprint { if royalty_charge.is_non_zero() { let handle = api.kernel_open_substate( receiver, - MAIN_BASE_PARTITION, + PackagePartition::Field.as_main_partition(), &PackageField::RoyaltyAccumulator.into(), LockFlags::MUTABLE, SystemLockData::default(), @@ -1189,9 +1187,7 @@ impl PackageAuthNativeBlueprint { let handle = api.kernel_open_substate_with_default( receiver, - MAIN_BASE_PARTITION - .at_offset(PACKAGE_AUTH_TEMPLATE_PARTITION_OFFSET) - .unwrap(), + PackagePartition::BlueprintVersionAuthConfigKeyValue.as_main_partition(), &SubstateKey::Map(scrypto_encode(&bp_version_key).unwrap()), LockFlags::read_only(), Some(|| { @@ -1201,12 +1197,12 @@ impl PackageAuthNativeBlueprint { SystemLockData::default(), )?; - let auth_template: KeyValueEntrySubstate = + let auth_template: PackageBlueprintVersionAuthConfigEntrySubstate = api.kernel_read_substate(handle)?.as_typed().unwrap(); api.kernel_close_substate(handle)?; let template = match auth_template.value { - Some(template) => template, + Some(template) => template.0.into_latest(), None => { return Err(RuntimeError::SystemError( SystemError::AuthTemplateDoesNotExist(package_bp_version_id), diff --git a/radix-engine/src/system/system.rs b/radix-engine/src/system/system.rs index bbb5cd25a43..8a681bcdb33 100644 --- a/radix-engine/src/system/system.rs +++ b/radix-engine/src/system/system.rs @@ -1,6 +1,9 @@ use super::id_allocation::IDAllocation; use super::payload_validation::*; use super::system_modules::costing::CostingEntry; +use crate::blueprints::package::{ + PackageBlueprintVersionDefinitionEntrySubstate, PackagePartition, PackageSchemaEntrySubstate, +}; use crate::errors::{ ApplicationError, CannotGlobalizeError, CreateObjectError, InvalidDropAccess, InvalidGlobalizeAccess, InvalidModuleType, PayloadValidationAgainstSchemaError, RuntimeError, @@ -512,9 +515,7 @@ where let handle = self.api.kernel_open_substate_with_default( package_address.as_node_id(), - MAIN_BASE_PARTITION - .at_offset(PACKAGE_SCHEMAS_PARTITION_OFFSET) - .unwrap(), + PackagePartition::SchemaKeyValue.as_main_partition(), &SubstateKey::Map(scrypto_encode(schema_hash).unwrap()), LockFlags::read_only(), Some(|| { @@ -524,11 +525,11 @@ where SystemLockData::default(), )?; - let substate: KeyValueEntrySubstate = + let substate: PackageSchemaEntrySubstate = self.api.kernel_read_substate(handle)?.as_typed().unwrap(); self.api.kernel_close_substate(handle)?; - let schema = substate.value.unwrap(); + let schema = substate.value.unwrap().0.into_latest(); self.api .kernel_get_system_state() @@ -572,9 +573,7 @@ where let handle = self.api.kernel_open_substate_with_default( package_address.as_node_id(), - MAIN_BASE_PARTITION - .at_offset(PACKAGE_BLUEPRINTS_PARTITION_OFFSET) - .unwrap(), + PackagePartition::BlueprintVersionDefinitionKeyValue.as_main_partition(), &SubstateKey::Map(scrypto_encode(bp_version_key).unwrap()), LockFlags::read_only(), Some(|| { @@ -584,12 +583,12 @@ where SystemLockData::default(), )?; - let substate: KeyValueEntrySubstate = + let substate: PackageBlueprintVersionDefinitionEntrySubstate = self.api.kernel_read_substate(handle)?.as_typed().unwrap(); self.api.kernel_close_substate(handle)?; let definition = match substate.value { - Some(definition) => definition, + Some(definition) => definition.0.into_latest(), None => { return Err(RuntimeError::SystemError( SystemError::BlueprintDoesNotExist(canonical_bp_id), diff --git a/radix-engine/src/system/system_callback.rs b/radix-engine/src/system/system_callback.rs index 7a6cf4c5538..89c453d609d 100644 --- a/radix-engine/src/system/system_callback.rs +++ b/radix-engine/src/system/system_callback.rs @@ -3,6 +3,7 @@ use crate::blueprints::account::ACCOUNT_CREATE_VIRTUAL_ED25519_ID; use crate::blueprints::account::ACCOUNT_CREATE_VIRTUAL_SECP256K1_ID; use crate::blueprints::identity::IDENTITY_CREATE_VIRTUAL_ED25519_ID; use crate::blueprints::identity::IDENTITY_CREATE_VIRTUAL_SECP256K1_ID; +use crate::blueprints::package::PackagePartition; use crate::errors::SystemUpstreamError; use crate::errors::{RuntimeError, SystemError}; use crate::kernel::actor::Actor; @@ -271,9 +272,7 @@ impl KernelCallbackObject for SystemConfig { let handle = system.kernel_open_substate_with_default( blueprint_id.package_address.as_node_id(), - MAIN_BASE_PARTITION - .at_offset(PACKAGE_BLUEPRINT_DEPENDENCIES_PARTITION_OFFSET) - .unwrap(), + PackagePartition::BlueprintVersionDependenciesKeyValue.as_main_partition(), &SubstateKey::Map(scrypto_encode(&key).unwrap()), LockFlags::read_only(), Some(|| { diff --git a/radix-engine/src/transaction/system_reader.rs b/radix-engine/src/transaction/system_reader.rs index 0f90bd4d347..4b5be6548fb 100644 --- a/radix-engine/src/transaction/system_reader.rs +++ b/radix-engine/src/transaction/system_reader.rs @@ -1,9 +1,7 @@ use radix_engine_common::data::scrypto::ScryptoDecode; use radix_engine_common::prelude::scrypto_encode; use radix_engine_interface::api::ObjectModuleId; -use radix_engine_interface::blueprints::package::{ - BlueprintDefinition, BlueprintVersionKey, PACKAGE_BLUEPRINTS_PARTITION_OFFSET, -}; +use radix_engine_interface::blueprints::package::{BlueprintDefinition, BlueprintVersionKey}; use radix_engine_interface::types::*; use radix_engine_interface::*; use radix_engine_store_interface::{ @@ -11,9 +9,12 @@ use radix_engine_store_interface::{ interface::SubstateDatabase, }; use sbor::rust::prelude::*; +use sbor::HasLatestVersion; +use crate::blueprints::package::{ + PackageBlueprintVersionDefinitionEntrySubstate, PackagePartition, +}; use crate::system::node_modules::type_info::TypeInfoSubstate; -use crate::system::system::KeyValueEntrySubstate; use crate::track::TrackedNode; pub enum SystemPartitionDescription { @@ -72,15 +73,14 @@ impl<'a, S: SubstateDatabase> SystemReader<'a, S> { ) -> Option { let bp_version_key = BlueprintVersionKey::new_default(blueprint_id.blueprint_name.clone()); let definition = self - .fetch_substate::>( + .fetch_substate::( blueprint_id.package_address.as_node_id(), - MAIN_BASE_PARTITION - .at_offset(PACKAGE_BLUEPRINTS_PARTITION_OFFSET) - .unwrap(), + PackagePartition::BlueprintVersionDefinitionKeyValue + .as_main_partition(), &SubstateKey::Map(scrypto_encode(&bp_version_key).unwrap()), )?; - definition.value + definition.value.map(|v| v.0.into_latest()) } pub fn fetch_substate( diff --git a/radix-engine/src/transaction/transaction_executor.rs b/radix-engine/src/transaction/transaction_executor.rs index 1a51107b028..0dd343882f1 100644 --- a/radix-engine/src/transaction/transaction_executor.rs +++ b/radix-engine/src/transaction/transaction_executor.rs @@ -1,4 +1,7 @@ use crate::blueprints::consensus_manager::{ConsensusManagerSubstate, ValidatorRewardsSubstate}; +use crate::blueprints::package::{ + PackageBlueprintVersionDefinitionEntrySubstate, PackagePartition, +}; use crate::blueprints::resource::BurnFungibleResourceEvent; use crate::blueprints::transaction_processor::TransactionProcessorError; use crate::blueprints::transaction_tracker::{TransactionStatus, TransactionTrackerSubstate}; @@ -18,9 +21,7 @@ use crate::transaction::*; use crate::types::*; use radix_engine_common::constants::*; use radix_engine_interface::api::ObjectModuleId; -use radix_engine_interface::blueprints::package::{ - BlueprintDefinition, BlueprintVersionKey, PACKAGE_BLUEPRINTS_PARTITION_OFFSET, -}; +use radix_engine_interface::blueprints::package::BlueprintVersionKey; use radix_engine_interface::blueprints::resource::LiquidFungibleResource; use radix_engine_interface::blueprints::transaction_processor::InstructionOutput; use radix_engine_store_interface::{db_key_mapper::SpreadPrefixKeyMapper, interface::*}; @@ -311,9 +312,8 @@ where let substate = track .read_substate( RESOURCE_PACKAGE.as_node_id(), - MAIN_BASE_PARTITION - .at_offset(PACKAGE_BLUEPRINTS_PARTITION_OFFSET) - .unwrap(), + PackagePartition::BlueprintVersionDefinitionKeyValue + .as_main_partition(), &SubstateKey::Map( scrypto_encode(&BlueprintVersionKey::new_default( FUNGIBLE_RESOURCE_MANAGER_BLUEPRINT, @@ -322,11 +322,13 @@ where ), ) .unwrap(); - let substate: KeyValueEntrySubstate = + let substate: PackageBlueprintVersionDefinitionEntrySubstate = substate.as_typed().unwrap(); let type_pointer = substate .value .unwrap() + .0 + .into_latest() .interface .get_event_type_pointer("BurnFungibleResourceEvent") .unwrap(); diff --git a/radix-engine/src/vm/vm.rs b/radix-engine/src/vm/vm.rs index bffff2533ac..31711c7e2bf 100644 --- a/radix-engine/src/vm/vm.rs +++ b/radix-engine/src/vm/vm.rs @@ -1,6 +1,6 @@ use crate::blueprints::package::{ PackageCodeInstrumentedCodeEntrySubstate, PackageCodeOriginalCodeEntrySubstate, - PackageCodeVmTypeEntrySubstate, PackageError, VmType, + PackageCodeVmTypeEntrySubstate, PackageError, PackagePartition, VmType, }; use crate::errors::{ApplicationError, RuntimeError}; use crate::kernel::kernel_api::{KernelInternalApi, KernelNodeApi, KernelSubstateApi}; @@ -54,9 +54,7 @@ impl<'g, W: WasmEngine + 'g, E: NativeVmExtension> SystemCallbackObject for Vm<' let vm_type = { let handle = api.kernel_open_substate_with_default( address.as_node_id(), - MAIN_BASE_PARTITION - .at_offset(PACKAGE_VM_TYPE_PARTITION_OFFSET) - .unwrap(), + PackagePartition::CodeVmTypeKeyValue.as_main_partition(), &SubstateKey::Map(scrypto_encode(&export.code_hash).unwrap()), LockFlags::read_only(), Some(|| { @@ -78,9 +76,7 @@ impl<'g, W: WasmEngine + 'g, E: NativeVmExtension> SystemCallbackObject for Vm<' let original_code = { let handle = api.kernel_open_substate_with_default( address.as_node_id(), - MAIN_BASE_PARTITION - .at_offset(PACKAGE_ORIGINAL_CODE_PARTITION_OFFSET) - .unwrap(), + PackagePartition::CodeOriginalCodeKeyValue.as_main_partition(), &SubstateKey::Map(scrypto_encode(&export.code_hash).unwrap()), LockFlags::read_only(), Some(|| { @@ -111,9 +107,7 @@ impl<'g, W: WasmEngine + 'g, E: NativeVmExtension> SystemCallbackObject for Vm<' let instrumented_code = { let handle = api.kernel_open_substate_with_default( address.as_node_id(), - MAIN_BASE_PARTITION - .at_offset(PACKAGE_INSTRUMENTED_CODE_PARTITION_OFFSET) - .unwrap(), + PackagePartition::CodeInstrumentedCodeKeyValue.as_main_partition(), &SubstateKey::Map(scrypto_encode(&export.code_hash).unwrap()), LockFlags::read_only(), Some(|| { diff --git a/sbor-derive-common/src/utils.rs b/sbor-derive-common/src/utils.rs index a9d58470466..c58781ca1e7 100644 --- a/sbor-derive-common/src/utils.rs +++ b/sbor-derive-common/src/utils.rs @@ -187,7 +187,6 @@ pub fn extract_typed_attributes( } } } - return Ok(fields); } Ok(fields) @@ -827,9 +826,13 @@ mod tests { let attr: Attribute = parse_quote! { #[sbor(skip, custom_value_kind = "NoCustomValueKind")] }; - let extracted = extract_typed_attributes(&[attr], "sbor").unwrap(); + let attr2: Attribute = parse_quote! { + #[sbor(skip3)] + }; + let extracted = extract_typed_attributes(&[attr, attr2], "sbor").unwrap(); assert_eq!(extracted.get_bool_value("skip").unwrap(), true); assert_eq!(extracted.get_bool_value("skip2").unwrap(), false); + assert_eq!(extracted.get_bool_value("skip3").unwrap(), true); assert!(matches!( extracted.get_bool_value("custom_value_kind"), Err(_) diff --git a/scrypto-unit/src/test_runner.rs b/scrypto-unit/src/test_runner.rs index f8f19eb9ce8..72b99362615 100644 --- a/scrypto-unit/src/test_runner.rs +++ b/scrypto-unit/src/test_runner.rs @@ -30,7 +30,6 @@ use radix_engine_interface::blueprints::package::{ BlueprintDefinitionInit, PackageDefinition, PackagePublishNativeManifestInput, PackagePublishWasmAdvancedManifestInput, TypePointer, PACKAGE_BLUEPRINT, PACKAGE_PUBLISH_NATIVE_IDENT, PACKAGE_PUBLISH_WASM_ADVANCED_IDENT, - PACKAGE_SCHEMAS_PARTITION_OFFSET, }; use radix_engine_interface::constants::CONSENSUS_MANAGER; use radix_engine_interface::math::Decimal; @@ -613,9 +612,7 @@ impl TestRunner { .substate_db() .list_entries(&SpreadPrefixKeyMapper::to_db_partition_key( package_address.as_node_id(), - MAIN_BASE_PARTITION - .at_offset(PACKAGE_SCHEMAS_PARTITION_OFFSET) - .unwrap(), + PackagePartition::SchemaKeyValue.as_main_partition(), )) { let hash: SchemaHash = @@ -641,18 +638,16 @@ impl TestRunner { .substate_db() .list_entries(&SpreadPrefixKeyMapper::to_db_partition_key( package_address.as_node_id(), - MAIN_BASE_PARTITION - .at_offset(PACKAGE_BLUEPRINTS_PARTITION_OFFSET) - .unwrap(), + PackagePartition::BlueprintVersionDefinitionKeyValue.as_main_partition(), )) { let key: BlueprintVersionKey = scrypto_decode(&SpreadPrefixKeyMapper::map_from_db_sort_key(&entry.0)).unwrap(); - let value: KeyValueEntrySubstate = + let value: PackageBlueprintVersionDefinitionEntrySubstate = scrypto_decode(&entry.1).unwrap(); match value.value { Some(definition) => { - definitions.insert(key, definition); + definitions.insert(key, definition.0.into_latest()); } None => {} } @@ -1936,16 +1931,16 @@ impl TestRunner { TypePointer::Package(type_identifier) => { let schema = self .substate_db() - .get_mapped::>( + .get_mapped::( package_address.as_node_id(), - MAIN_BASE_PARTITION - .at_offset(PACKAGE_SCHEMAS_PARTITION_OFFSET) - .unwrap(), + PackagePartition::SchemaKeyValue.as_main_partition(), &SubstateKey::Map(scrypto_encode(&type_identifier.0).unwrap()), ) .unwrap() .value - .unwrap(); + .unwrap() + .0 + .into_latest(); (type_identifier.1, schema) } diff --git a/simulator/src/ledger/dumper.rs b/simulator/src/ledger/dumper.rs index 734eb13f42e..69ffad1a241 100644 --- a/simulator/src/ledger/dumper.rs +++ b/simulator/src/ledger/dumper.rs @@ -7,8 +7,8 @@ use radix_engine::system::system::FieldSubstate; use radix_engine::types::*; use radix_engine_interface::blueprints::package::*; use radix_engine_interface::network::NetworkDefinition; -use radix_engine_queries::query::ResourceAccounter; use radix_engine_queries::typed_substate_layout::PackageCodeOriginalCodeEntrySubstate; +use radix_engine_queries::{query::ResourceAccounter, typed_substate_layout::PackagePartition}; use radix_engine_store_interface::{ db_key_mapper::{MappedSubstateDatabase, SpreadPrefixKeyMapper}, interface::SubstateDatabase, @@ -34,9 +34,7 @@ pub fn dump_package( let (_, substate) = substate_db .list_mapped::( package_address.as_node_id(), - MAIN_BASE_PARTITION - .at_offset(PACKAGE_ORIGINAL_CODE_PARTITION_OFFSET) - .unwrap(), + PackagePartition::CodeOriginalCodeKeyValue.as_main_partition(), ) .next() .ok_or(EntityDumpError::PackageNotFound)?; diff --git a/simulator/src/resim/mod.rs b/simulator/src/resim/mod.rs index d6eaaa1f494..5266009b4cf 100644 --- a/simulator/src/resim/mod.rs +++ b/simulator/src/resim/mod.rs @@ -71,12 +71,11 @@ use radix_engine::vm::{DefaultNativeVm, ScryptoVm, Vm}; use radix_engine_interface::api::ObjectModuleId; use radix_engine_interface::blueprints::package::{ BlueprintDefinition, BlueprintInterface, BlueprintVersionKey, TypePointer, - PACKAGE_SCHEMAS_PARTITION_OFFSET, }; use radix_engine_interface::blueprints::resource::FromPublicKey; use radix_engine_interface::crypto::hash; use radix_engine_interface::network::NetworkDefinition; -use radix_engine_queries::typed_substate_layout::PackageSchemaEntrySubstate; +use radix_engine_queries::typed_substate_layout::{PackagePartition, PackageSchemaEntrySubstate}; use radix_engine_store_interface::{ db_key_mapper::{ MappedCommittableSubstateDatabase, MappedSubstateDatabase, SpreadPrefixKeyMapper, @@ -389,9 +388,7 @@ pub fn export_schema( let schema = substate_db .get_mapped::( package_address.as_node_id(), - MAIN_BASE_PARTITION - .at_offset(PACKAGE_SCHEMAS_PARTITION_OFFSET) - .unwrap(), + PackagePartition::SchemaKeyValue.as_main_partition(), &SubstateKey::Map(scrypto_encode(&schema_hash).unwrap()), ) .ok_or(Error::SchemaNotFound(package_address, schema_hash))? @@ -478,16 +475,16 @@ pub fn get_event_schema( match schema_pointer { TypePointer::Package(TypeIdentifier(schema_hash, index)) => { let schema = substate_db - .get_mapped::>( + .get_mapped::( package_address.as_node_id(), - MAIN_BASE_PARTITION - .at_offset(PACKAGE_SCHEMAS_PARTITION_OFFSET) - .unwrap(), + PackagePartition::SchemaKeyValue.as_main_partition(), &SubstateKey::Map(scrypto_encode(&schema_hash).unwrap()), ) .unwrap() .value - .unwrap(); + .unwrap() + .0 + .into_latest(); Some((index, schema)) } From f0267186c0e1e8550c1c3ccfa05bcd0db713018a Mon Sep 17 00:00:00 2001 From: David Edey Date: Tue, 15 Aug 2023 15:14:55 +0100 Subject: [PATCH 16/28] fix: Further tweaks and fixes --- radix-engine-tests/assets/cost_flash_loan.csv | 16 +++++++-------- ...ost_mint_large_size_nfts_from_manifest.csv | 14 ++++++------- ...ost_mint_small_size_nfts_from_manifest.csv | 14 ++++++------- .../assets/cost_publish_large_package.csv | 20 +++++++++---------- radix-engine-tests/assets/cost_radiswap.csv | 14 ++++++------- radix-engine-tests/assets/cost_transfer.csv | 12 +++++------ .../cost_transfer_to_virtual_account.csv | 12 +++++------ radix-engine/src/blueprints/macros.rs | 12 +++++------ radix-engine/src/system/system.rs | 4 ++++ 9 files changed, 61 insertions(+), 57 deletions(-) diff --git a/radix-engine-tests/assets/cost_flash_loan.csv b/radix-engine-tests/assets/cost_flash_loan.csv index 05ff7a49782..882746c808e 100644 --- a/radix-engine-tests/assets/cost_flash_loan.csv +++ b/radix-engine-tests/assets/cost_flash_loan.csv @@ -1,9 +1,9 @@ -Total Cost (XRD) , 0.11974838, 100.0% -+ Execution Cost (XRD) , 0.11965838, 99.9% +Total Cost (XRD) , 0.11979272, 100.0% ++ Execution Cost (XRD) , 0.11970272, 99.9% + Tipping Cost (XRD) , 0, 0.0% + State Expansion Cost (XRD) , 0.00009, 0.1% + Royalty Cost (XRD) , 0, 0.0% -Total Cost Units Consumed , 11965838, 100.0% +Total Cost Units Consumed , 11970272, 100.0% AllocateNodeId , 5304, 0.0% BeforeInvoke , 9320, 0.1% CloseSubstate , 85680, 0.7% @@ -15,18 +15,18 @@ DropNode , EmitEvent , 4548, 0.0% GenerateRuid , 500, 0.0% LockFee , 500, 0.0% -OpenSubstate , 3925939, 32.8% +OpenSubstate , 3925976, 32.8% OpenSubstate::GlobalAccount , 13397, 0.1% OpenSubstate::GlobalFungibleResourceManager , 15014, 0.1% OpenSubstate::GlobalGenericComponent , 6052, 0.1% OpenSubstate::GlobalNonFungibleResourceManager , 31635, 0.3% -OpenSubstate::GlobalPackage , 1841986, 15.4% +OpenSubstate::GlobalPackage , 1843594, 15.4% OpenSubstate::InternalFungibleVault , 38110, 0.3% OpenSubstate::InternalGenericComponent , 146790, 1.2% -PrepareWasmCode , 1621116, 13.5% +PrepareWasmCode , 1621964, 13.5% QueryActor , 9500, 0.1% QueryAuthZone , 2000, 0.0% -ReadSubstate , 1953498, 16.3% +ReadSubstate , 1955106, 16.3% RunNativeCode::AuthZone_pop , 37132, 0.3% RunNativeCode::AuthZone_push , 50694, 0.4% RunNativeCode::Worktop_drain , 13505, 0.1% @@ -55,7 +55,7 @@ RunNativeCode::try_deposit_batch_or_abort , RunNativeCode::unlock_amount_FungibleVault , 51728, 0.4% RunNativeCode::withdraw , 30320, 0.3% RunWasmCode::BasicFlashLoan_repay_loan , 70268, 0.6% -RunWasmCode::BasicFlashLoan_take_loan , 118886, 1.0% +RunWasmCode::BasicFlashLoan_take_loan , 119219, 1.0% TxBaseCost , 50000, 0.4% TxPayloadCost , 20680, 0.2% TxSignatureVerification , 7000, 0.1% diff --git a/radix-engine-tests/assets/cost_mint_large_size_nfts_from_manifest.csv b/radix-engine-tests/assets/cost_mint_large_size_nfts_from_manifest.csv index d4ad9fcc2f8..afbe7eaf497 100644 --- a/radix-engine-tests/assets/cost_mint_large_size_nfts_from_manifest.csv +++ b/radix-engine-tests/assets/cost_mint_large_size_nfts_from_manifest.csv @@ -1,9 +1,9 @@ -Total Cost (XRD) , 11.19575797, 100.0% -+ Execution Cost (XRD) , 0.99634797, 8.9% +Total Cost (XRD) , 11.19579647, 100.0% ++ Execution Cost (XRD) , 0.99638647, 8.9% + Tipping Cost (XRD) , 0, 0.0% + State Expansion Cost (XRD) , 10.19941, 91.1% + Royalty Cost (XRD) , 0, 0.0% -Total Cost Units Consumed , 99634797, 100.0% +Total Cost Units Consumed , 99638647, 100.0% AllocateNodeId , 2184, 0.0% BeforeInvoke , 2040526, 2.0% CloseSubstate , 48195, 0.0% @@ -17,19 +17,19 @@ DropNode , EmitEvent , 18764, 0.0% LockFee , 500, 0.0% MoveModule , 20335, 0.0% -OpenSubstate , 3157411, 3.2% +OpenSubstate , 3157477, 3.2% OpenSubstate::GlobalAccount , 5855, 0.0% OpenSubstate::GlobalFungibleResourceManager , 1452, 0.0% OpenSubstate::GlobalGenericComponent , 2884, 0.0% OpenSubstate::GlobalNonFungibleResourceManager , 14195, 0.0% -OpenSubstate::GlobalPackage , 783282, 0.8% +OpenSubstate::GlobalPackage , 784656, 0.8% OpenSubstate::InternalFungibleVault , 4803, 0.0% OpenSubstate::InternalGenericComponent , 36522, 0.0% OpenSubstate::InternalKeyValueStore , 877, 0.0% OpenSubstate::InternalNonFungibleVault , 125864, 0.1% -PrepareWasmCode , 627948, 0.6% +PrepareWasmCode , 628984, 0.6% QueryActor , 2000, 0.0% -ReadSubstate , 889484, 0.9% +ReadSubstate , 890858, 0.9% RunNativeCode::Worktop_drain , 13505, 0.0% RunNativeCode::Worktop_drop , 15385, 0.0% RunNativeCode::Worktop_put , 18262, 0.0% diff --git a/radix-engine-tests/assets/cost_mint_small_size_nfts_from_manifest.csv b/radix-engine-tests/assets/cost_mint_small_size_nfts_from_manifest.csv index 376edb8a5a2..5deec10119f 100644 --- a/radix-engine-tests/assets/cost_mint_small_size_nfts_from_manifest.csv +++ b/radix-engine-tests/assets/cost_mint_small_size_nfts_from_manifest.csv @@ -1,9 +1,9 @@ -Total Cost (XRD) , 1.0121173, 100.0% -+ Execution Cost (XRD) , 0.9292473, 91.8% +Total Cost (XRD) , 1.0121558, 100.0% ++ Execution Cost (XRD) , 0.9292858, 91.8% + Tipping Cost (XRD) , 0, 0.0% + State Expansion Cost (XRD) , 0.08287, 8.2% + Royalty Cost (XRD) , 0, 0.0% -Total Cost Units Consumed , 92924730, 100.0% +Total Cost Units Consumed , 92928580, 100.0% AllocateNodeId , 2184, 0.0% BeforeInvoke , 16462, 0.0% CloseSubstate , 68040, 0.1% @@ -17,19 +17,19 @@ DropNode , EmitEvent , 32372, 0.0% LockFee , 500, 0.0% MoveModule , 36022, 0.0% -OpenSubstate , 3157411, 3.4% +OpenSubstate , 3157477, 3.4% OpenSubstate::GlobalAccount , 5855, 0.0% OpenSubstate::GlobalFungibleResourceManager , 1452, 0.0% OpenSubstate::GlobalGenericComponent , 2884, 0.0% OpenSubstate::GlobalNonFungibleResourceManager , 14195, 0.0% -OpenSubstate::GlobalPackage , 783282, 0.8% +OpenSubstate::GlobalPackage , 784656, 0.8% OpenSubstate::InternalFungibleVault , 4803, 0.0% OpenSubstate::InternalGenericComponent , 39924, 0.0% OpenSubstate::InternalKeyValueStore , 877, 0.0% OpenSubstate::InternalNonFungibleVault , 225278, 0.2% -PrepareWasmCode , 627948, 0.7% +PrepareWasmCode , 628984, 0.7% QueryActor , 2000, 0.0% -ReadSubstate , 957902, 1.0% +ReadSubstate , 959276, 1.0% RunNativeCode::Worktop_drain , 13505, 0.0% RunNativeCode::Worktop_drop , 15385, 0.0% RunNativeCode::Worktop_put , 18262, 0.0% diff --git a/radix-engine-tests/assets/cost_publish_large_package.csv b/radix-engine-tests/assets/cost_publish_large_package.csv index 05478c89bb9..033cead832d 100644 --- a/radix-engine-tests/assets/cost_publish_large_package.csv +++ b/radix-engine-tests/assets/cost_publish_large_package.csv @@ -1,30 +1,30 @@ -Total Cost (XRD) , 21.84127372, 100.0% -+ Execution Cost (XRD) , 0.88498372, 4.1% +Total Cost (XRD) , 21.84140173, 100.0% ++ Execution Cost (XRD) , 0.88505173, 4.1% + Tipping Cost (XRD) , 0, 0.0% -+ State Expansion Cost (XRD) , 20.95629, 95.9% ++ State Expansion Cost (XRD) , 20.95635, 95.9% + Royalty Cost (XRD) , 0, 0.0% -Total Cost Units Consumed , 88498372, 100.0% +Total Cost Units Consumed , 88505173, 100.0% AllocateNodeId , 1560, 0.0% BeforeInvoke , 2096382, 2.4% CloseSubstate , 12915, 0.0% Commit::GlobalGenericComponent , 100018, 0.1% -Commit::GlobalPackage , 1123861, 1.3% +Commit::GlobalPackage , 1123863, 1.3% Commit::InternalFungibleVault , 400053, 0.5% -CreateNode , 4198824, 4.7% +CreateNode , 4198836, 4.7% DropNode , 15857, 0.0% EmitEvent , 1288, 0.0% LockFee , 500, 0.0% MoveModule , 664, 0.0% -OpenSubstate , 2355811, 2.7% +OpenSubstate , 2355950, 2.7% OpenSubstate::GlobalFungibleResourceManager , 4434, 0.0% OpenSubstate::GlobalGenericComponent , 2884, 0.0% -OpenSubstate::GlobalPackage , 739772, 0.8% +OpenSubstate::GlobalPackage , 742578, 0.8% OpenSubstate::InternalFungibleVault , 6156, 0.0% OpenSubstate::InternalGenericComponent , 12556, 0.0% OpenSubstate::InternalKeyValueStore , 877, 0.0% -PrepareWasmCode , 627948, 0.7% +PrepareWasmCode , 628984, 0.7% QueryActor , 1500, 0.0% -ReadSubstate , 741434, 0.8% +ReadSubstate , 744240, 0.8% RunNativeCode::Worktop_drop , 15385, 0.0% RunNativeCode::create , 31390, 0.0% RunNativeCode::create_empty_vault_FungibleResourceManager , 29769, 0.0% diff --git a/radix-engine-tests/assets/cost_radiswap.csv b/radix-engine-tests/assets/cost_radiswap.csv index 36cadb55704..3c18573b7f3 100644 --- a/radix-engine-tests/assets/cost_radiswap.csv +++ b/radix-engine-tests/assets/cost_radiswap.csv @@ -1,9 +1,9 @@ -Total Cost (XRD) , 0.07988852, 100.0% -+ Execution Cost (XRD) , 0.07773852, 97.3% +Total Cost (XRD) , 0.07990518, 100.0% ++ Execution Cost (XRD) , 0.07775518, 97.3% + Tipping Cost (XRD) , 0, 0.0% + State Expansion Cost (XRD) , 0.00215, 2.7% + Royalty Cost (XRD) , 0, 0.0% -Total Cost Units Consumed , 7773852, 100.0% +Total Cost Units Consumed , 7775518, 100.0% AllocateNodeId , 3224, 0.0% BeforeInvoke , 4226, 0.1% CloseSubstate , 51765, 0.7% @@ -14,17 +14,17 @@ CreateNode , DropNode , 39446, 0.5% EmitEvent , 5416, 0.1% LockFee , 500, 0.0% -OpenSubstate , 3712849, 47.8% +OpenSubstate , 3712871, 47.8% OpenSubstate::GlobalAccount , 14165, 0.2% OpenSubstate::GlobalFungibleResourceManager , 17747, 0.2% OpenSubstate::GlobalGenericComponent , 2842, 0.0% -OpenSubstate::GlobalPackage , 716668, 9.2% +OpenSubstate::GlobalPackage , 717370, 9.2% OpenSubstate::GlobalTwoResourcePool , 13912, 0.2% OpenSubstate::InternalFungibleVault , 26432, 0.3% OpenSubstate::InternalGenericComponent , 75364, 1.0% -PrepareWasmCode , 573122, 7.4% +PrepareWasmCode , 573362, 7.4% QueryActor , 4000, 0.1% -ReadSubstate , 781984, 10.1% +ReadSubstate , 782686, 10.1% RunNativeCode::Worktop_drain , 13505, 0.2% RunNativeCode::Worktop_drop , 15385, 0.2% RunNativeCode::Worktop_put , 36524, 0.5% diff --git a/radix-engine-tests/assets/cost_transfer.csv b/radix-engine-tests/assets/cost_transfer.csv index c99f3b535e9..03bf48269a4 100644 --- a/radix-engine-tests/assets/cost_transfer.csv +++ b/radix-engine-tests/assets/cost_transfer.csv @@ -1,9 +1,9 @@ -Total Cost (XRD) , 0.03028743, 100.0% -+ Execution Cost (XRD) , 0.03028743, 100.0% +Total Cost (XRD) , 0.03029209, 100.0% ++ Execution Cost (XRD) , 0.03029209, 100.0% + Tipping Cost (XRD) , 0, 0.0% + State Expansion Cost (XRD) , 0, 0.0% + Royalty Cost (XRD) , 0, 0.0% -Total Cost Units Consumed , 3028743, 100.0% +Total Cost Units Consumed , 3029209, 100.0% AllocateNodeId , 1352, 0.0% BeforeInvoke , 1768, 0.1% CloseSubstate , 22155, 0.7% @@ -12,14 +12,14 @@ CreateNode , DropNode , 16925, 0.6% EmitEvent , 1940, 0.1% LockFee , 500, 0.0% -OpenSubstate , 2122724, 70.1% +OpenSubstate , 2122730, 70.1% OpenSubstate::GlobalAccount , 13397, 0.4% OpenSubstate::GlobalFungibleResourceManager , 7111, 0.2% -OpenSubstate::GlobalPackage , 81258, 2.7% +OpenSubstate::GlobalPackage , 81488, 2.7% OpenSubstate::InternalFungibleVault , 11703, 0.4% OpenSubstate::InternalGenericComponent , 29520, 1.0% QueryActor , 2500, 0.1% -ReadSubstate , 105820, 3.5% +ReadSubstate , 106050, 3.5% RunNativeCode::Worktop_drain , 13505, 0.4% RunNativeCode::Worktop_drop , 15385, 0.5% RunNativeCode::Worktop_put , 18262, 0.6% diff --git a/radix-engine-tests/assets/cost_transfer_to_virtual_account.csv b/radix-engine-tests/assets/cost_transfer_to_virtual_account.csv index 4b7f213388f..d1844b26a20 100644 --- a/radix-engine-tests/assets/cost_transfer_to_virtual_account.csv +++ b/radix-engine-tests/assets/cost_transfer_to_virtual_account.csv @@ -1,9 +1,9 @@ -Total Cost (XRD) , 0.06613517, 100.0% -+ Execution Cost (XRD) , 0.05760517, 87.1% +Total Cost (XRD) , 0.0661424, 100.0% ++ Execution Cost (XRD) , 0.0576124, 87.1% + Tipping Cost (XRD) , 0, 0.0% + State Expansion Cost (XRD) , 0.00853, 12.9% + Royalty Cost (XRD) , 0, 0.0% -Total Cost Units Consumed , 5760517, 100.0% +Total Cost Units Consumed , 5761240, 100.0% AllocateNodeId , 2600, 0.0% BeforeInvoke , 3288, 0.1% CloseSubstate , 30870, 0.5% @@ -14,17 +14,17 @@ DropNode , EmitEvent , 3228, 0.1% LockFee , 500, 0.0% MoveModule , 747, 0.0% -OpenSubstate , 3244380, 56.3% +OpenSubstate , 3244391, 56.3% OpenSubstate::GlobalAccount , 8310, 0.1% OpenSubstate::GlobalFungibleResourceManager , 13572, 0.2% OpenSubstate::GlobalNonFungibleResourceManager , 2604, 0.0% -OpenSubstate::GlobalPackage , 129096, 2.2% +OpenSubstate::GlobalPackage , 129452, 2.2% OpenSubstate::GlobalVirtualSecp256k1Account , 6108, 0.1% OpenSubstate::InternalAccount , 692, 0.0% OpenSubstate::InternalFungibleVault , 13958, 0.2% OpenSubstate::InternalGenericComponent , 37112, 0.6% QueryActor , 2500, 0.0% -ReadSubstate , 158030, 2.7% +ReadSubstate , 158386, 2.7% RunNativeCode::Worktop_drain , 13505, 0.2% RunNativeCode::Worktop_drop , 15385, 0.3% RunNativeCode::Worktop_put , 18262, 0.3% diff --git a/radix-engine/src/blueprints/macros.rs b/radix-engine/src/blueprints/macros.rs index 9b5447ce7b7..c6a195b0a4d 100644 --- a/radix-engine/src/blueprints/macros.rs +++ b/radix-engine/src/blueprints/macros.rs @@ -216,7 +216,7 @@ pub trait SortedIndexEntryContent>: Sized { /// /// For each collection key, the following types will be created: /// * `KeyContent` - a transparent new type for the full key content -/// * `Key` - a type for the full key (eg includes the u16 for a sorted index key) +/// * `SubstateKey` - a type for the full key (eg includes the u16 for a sorted index key) /// /// The content of each of the above can take a number of forms. /// This is configured via specifying the type as one of the following. @@ -370,7 +370,7 @@ macro_rules! declare_native_blueprint_state { // TODO(David): Tweak when I work out the right strategy for keys. Probably just want a single new-type. // So probably just don't use generate_content_type but use generate_key_type or something instead? - pub type [<$blueprint_ident $collection_ident Key>] = [<$blueprint_ident $collection_ident KeyContent>]; + pub type [<$blueprint_ident $collection_ident SubstateKey>] = [<$blueprint_ident $collection_ident KeyContent>]; // TODO(David) - Properly handle SortedIndex: // Fix Key types for SortedIndex to have a named u16 part of the key, @@ -516,7 +516,7 @@ macro_rules! declare_native_blueprint_state { #[derive(Debug, Clone)] pub enum [<$blueprint_ident TypedSubstateKey>] { Fields([<$blueprint_ident Field>]), - $([<$collection_ident $collection_type Entries>]([<$blueprint_ident $collection_ident Key>]),)* + $([<$collection_ident $collection_type Entries>]([<$blueprint_ident $collection_ident SubstateKey>]),)* } impl [<$blueprint_ident TypedSubstateKey>] { @@ -530,7 +530,7 @@ macro_rules! declare_native_blueprint_state { $( [<$blueprint_ident Partition>]::[<$collection_ident $collection_type>] => { [<$blueprint_ident TypedSubstateKey>]::[<$collection_ident $collection_type Entries>]( - [<$blueprint_ident $collection_ident Key>]::try_from(substate_key)?, + [<$blueprint_ident $collection_ident SubstateKey>]::try_from(substate_key)?, ) } )* @@ -631,7 +631,7 @@ macro_rules! declare_native_blueprint_state { )* $( pub $collection_property_name: IndexMap< - [<$blueprint_ident $collection_ident Key>], + [<$blueprint_ident $collection_ident SubstateKey>], [<$blueprint_ident $collection_ident EntrySubstate>] >, )* @@ -653,7 +653,7 @@ macro_rules! declare_native_blueprint_state { self.$field_property_name.is_some(), )?; if let Some(field) = self.$field_property_name { - let field_content = scrypto_encode(&field.value).unwrap(); + let field_content = scrypto_encode(&field.field_content()).unwrap(); let locked = match &field.mutability { SubstateMutability::Mutable => true, SubstateMutability::Immutable => false, diff --git a/radix-engine/src/system/system.rs b/radix-engine/src/system/system.rs index 8a681bcdb33..03b48d99acd 100644 --- a/radix-engine/src/system/system.rs +++ b/radix-engine/src/system/system.rs @@ -81,6 +81,10 @@ impl FieldSubstate { mutability: SubstateMutability::Immutable, } } + + pub fn field_content(&self) -> &V { + &self.value.0 + } } pub type KeyValueEntrySubstate = DynSubstate>; From b8141a265e97d203f072b81008c4dd539a70b14b Mon Sep 17 00:00:00 2001 From: David Edey Date: Tue, 15 Aug 2023 15:58:29 +0100 Subject: [PATCH 17/28] fix: Various fixes --- radix-engine-queries/src/typed_substate_layout.rs | 4 ++-- radix-engine/src/blueprints/macros.rs | 4 ++-- simulator/src/ledger/dumper.rs | 1 - simulator/src/resim/mod.rs | 10 +++++----- 4 files changed, 9 insertions(+), 10 deletions(-) diff --git a/radix-engine-queries/src/typed_substate_layout.rs b/radix-engine-queries/src/typed_substate_layout.rs index 3540870a5ab..cddd8dc462d 100644 --- a/radix-engine-queries/src/typed_substate_layout.rs +++ b/radix-engine-queries/src/typed_substate_layout.rs @@ -236,8 +236,8 @@ pub fn to_typed_object_module_substate_key( return to_typed_object_substate_key_internal(entity_type, partition_offset, substate_key) .map_err(|_| { format!( - "Could not convert {:?} {:?} key to TypedObjectSubstateKey", - entity_type, substate_key + "Could not convert {:?} (partition offset {}) {:?} key to TypedObjectSubstateKey", + entity_type, partition_offset, substate_key ) }); } diff --git a/radix-engine/src/blueprints/macros.rs b/radix-engine/src/blueprints/macros.rs index c6a195b0a4d..17c20848a39 100644 --- a/radix-engine/src/blueprints/macros.rs +++ b/radix-engine/src/blueprints/macros.rs @@ -375,12 +375,12 @@ macro_rules! declare_native_blueprint_state { // TODO(David) - Properly handle SortedIndex: // Fix Key types for SortedIndex to have a named u16 part of the key, // use a different key trait, and use .for_sorted_key in the below. - impl TryFrom<&SubstateKey> for [<$blueprint_ident $collection_ident KeyContent>] { + impl TryFrom<&SubstateKey> for [<$blueprint_ident $collection_ident SubstateKey>] { type Error = (); fn try_from(substate_key: &SubstateKey) -> Result { let key = substate_key.for_map().ok_or(())?; - scrypto_decode(&key).map_err(|_| ())? + scrypto_decode::(&key).map_err(|_| ()) } } diff --git a/simulator/src/ledger/dumper.rs b/simulator/src/ledger/dumper.rs index 69ffad1a241..0f22c320154 100644 --- a/simulator/src/ledger/dumper.rs +++ b/simulator/src/ledger/dumper.rs @@ -5,7 +5,6 @@ use radix_engine::blueprints::resource::*; use radix_engine::system::node_modules::type_info::TypeInfoSubstate; use radix_engine::system::system::FieldSubstate; use radix_engine::types::*; -use radix_engine_interface::blueprints::package::*; use radix_engine_interface::network::NetworkDefinition; use radix_engine_queries::typed_substate_layout::PackageCodeOriginalCodeEntrySubstate; use radix_engine_queries::{query::ResourceAccounter, typed_substate_layout::PackagePartition}; diff --git a/simulator/src/resim/mod.rs b/simulator/src/resim/mod.rs index 5266009b4cf..5482052a86c 100644 --- a/simulator/src/resim/mod.rs +++ b/simulator/src/resim/mod.rs @@ -58,7 +58,7 @@ use radix_engine::blueprints::consensus_manager::{ }; use radix_engine::system::bootstrap::Bootstrapper; use radix_engine::system::node_modules::type_info::TypeInfoSubstate; -use radix_engine::system::system::{FieldSubstate, KeyValueEntrySubstate}; +use radix_engine::system::system::FieldSubstate; use radix_engine::transaction::execute_and_commit_transaction; use radix_engine::transaction::TransactionOutcome; use radix_engine::transaction::TransactionReceipt; @@ -75,7 +75,7 @@ use radix_engine_interface::blueprints::package::{ use radix_engine_interface::blueprints::resource::FromPublicKey; use radix_engine_interface::crypto::hash; use radix_engine_interface::network::NetworkDefinition; -use radix_engine_queries::typed_substate_layout::{PackagePartition, PackageSchemaEntrySubstate}; +use radix_engine_queries::typed_substate_layout::*; use radix_engine_store_interface::{ db_key_mapper::{ MappedCommittableSubstateDatabase, MappedSubstateDatabase, SpreadPrefixKeyMapper, @@ -337,9 +337,9 @@ pub fn export_package_schema( Bootstrapper::new(&mut substate_db, vm, false).bootstrap_test_default(); let entries = substate_db - .list_mapped::, MapKey>( + .list_mapped::( package_address.as_node_id(), - MAIN_BASE_PARTITION.at_offset(PartitionOffset(1u8)).unwrap(), + PackagePartition::BlueprintVersionDefinitionKeyValue.as_main_partition(), ); let mut blueprints = BTreeMap::new(); @@ -349,7 +349,7 @@ pub fn export_package_schema( _ => panic!("Unexpected"), }; - blueprints.insert(bp_version_key, blueprint_definition.value.unwrap()); + blueprints.insert(bp_version_key, blueprint_definition.value.unwrap().0.into_latest()); } Ok(blueprints) From e081804c4c9b7fcc51a79b67f2bf24f36eea5347 Mon Sep 17 00:00:00 2001 From: David Edey Date: Tue, 15 Aug 2023 16:46:12 +0100 Subject: [PATCH 18/28] tweak: Update names of new-types to XPayload not XContent --- radix-engine/src/blueprints/macros.rs | 58 +++++++++---------- .../src/blueprints/package/package.rs | 2 +- simulator/src/resim/mod.rs | 5 +- 3 files changed, 34 insertions(+), 31 deletions(-) diff --git a/radix-engine/src/blueprints/macros.rs b/radix-engine/src/blueprints/macros.rs index 17c20848a39..25deb351f1c 100644 --- a/radix-engine/src/blueprints/macros.rs +++ b/radix-engine/src/blueprints/macros.rs @@ -161,40 +161,40 @@ impl InnerObjectFeatureChe } } -pub trait FieldContent>: Sized { - fn into_locked_substate(self) -> FieldSubstate { +pub trait FieldContent>: Sized { + fn into_locked_substate(self) -> FieldSubstate { FieldSubstate::new_locked_field(self.into()) } - fn into_mutable_substate(self) -> FieldSubstate { + fn into_mutable_substate(self) -> FieldSubstate { FieldSubstate::new_field(self.into()) } } -pub trait KeyContent>: Sized { - fn into_key(self) -> ActualContent { +pub trait KeyContent>: Sized { + fn into_key(self) -> KeyPayload { self.into() } } -pub trait KeyValueEntryContent>: Sized { - fn into_locked_substate(self) -> KeyValueEntrySubstate { +pub trait KeyValueEntryContent>: Sized { + fn into_locked_substate(self) -> KeyValueEntrySubstate { KeyValueEntrySubstate::entry(self.into()) } - fn into_mutable_substate(self) -> KeyValueEntrySubstate { + fn into_mutable_substate(self) -> KeyValueEntrySubstate { KeyValueEntrySubstate::locked_entry(self.into()) } } -pub trait IndexEntryContent>: Sized { - fn into_substate(self) -> ActualContent { +pub trait IndexEntryContent>: Sized { + fn into_substate(self) -> EntryPayload { self.into() } } -pub trait SortedIndexEntryContent>: Sized { - fn into_substate(self) -> ActualContent { +pub trait SortedIndexEntryContent>: Sized { + fn into_substate(self) -> EntryPayload { self.into() } } @@ -207,15 +207,15 @@ pub trait SortedIndexEntryContent>: Sized { /// out [../package/substates.rs](the package substates definition). /// /// For each field, the following types will be created: -/// * `FieldContent` - a transparent new type for the full content +/// * `FieldPayload` - a transparent new type for the field content /// * `FieldSubstate` - a type for the full system-wrapped substate /// /// For each collection value, the following types will be created: -/// * `EntryContent` - a transparent new type for the full content +/// * `EntryPayload` - a transparent new type for the entry content /// * `EntrySubstate` - a type for the full system-wrapped substate /// /// For each collection key, the following types will be created: -/// * `KeyContent` - a transparent new type for the full key content +/// * `KeyPayload` - a transparent new type for the key content /// * `SubstateKey` - a type for the full key (eg includes the u16 for a sorted index key) /// /// The content of each of the above can take a number of forms. @@ -246,17 +246,17 @@ pub trait SortedIndexEntryContent>: Sized { /// For Fields, it will assume the existence of a type called /// `V1` and will generate the following types: /// * `` - a type alias for the latest version (V1). -/// * `Versioned` - the enum wrapper with a single version. This will be the content of `FieldContent`. +/// * `Versioned` - the enum wrapper with a single version. This will be the content of `FieldPayload`. /// /// For collection values, it will assume the existence of `V1` /// and generate the following types: /// * `` - a type alias for the latest version (V1). -/// * `Versioned` - the enum wrapper with a single version. This will be the content of `EntryContent`. +/// * `Versioned` - the enum wrapper with a single version. This will be the content of `EntryPayload`. /// /// For collection keys, it will assume the existence of `KeyInnerV1` /// and generate the following types: /// * `KeyInner` - a type alias for the latest version (V1) -/// * `VersionedKeyInner` - the enum wrapper with a single version. This will be the content of `KeyContent`. +/// * `VersionedKeyInner` - the enum wrapper with a single version. This will be the content of `KeyPayload`. #[allow(unused)] macro_rules! declare_native_blueprint_state { ( @@ -336,19 +336,19 @@ macro_rules! declare_native_blueprint_state { // > Set up Versioned types (if relevant). Assumes __FieldV1 exists and then creates // - Versioned__Field // - __Field (alias for __FieldV1) - // > Set up the (transparent) _FieldContent new type for the content of the field - // > Set up the FieldContent trait for anything which can be resolved into the field content + // > Set up the (transparent) _FieldPayload new type for the content of the field + // > Set up the FieldContent trait for anything which can be resolved into the field payload generate_content_type!( content_trait: FieldContent, ident_core: [<$blueprint_ident $field_ident>], #[derive(Debug, PartialEq, Eq, ScryptoSbor)] - struct [<$blueprint_ident $field_ident FieldContent>] = $field_type + struct [<$blueprint_ident $field_ident FieldPayload>] = $field_type ); // > Set up the _FieldSubstate alias for the system-wrapped substate generate_system_substate_type_alias!( Field, - type [<$blueprint_ident $field_ident FieldSubstate>] = WRAPPED [<$blueprint_ident $field_ident FieldContent>] + type [<$blueprint_ident $field_ident FieldSubstate>] = WRAPPED [<$blueprint_ident $field_ident FieldPayload>] ); );* @@ -365,12 +365,12 @@ macro_rules! declare_native_blueprint_state { ident_core: [<$blueprint_ident $collection_ident KeyInner>], #[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, ScryptoSbor)] #[sbor(transparent_name)] - struct [<$blueprint_ident $collection_ident KeyContent>] = $collection_key_type + struct [<$blueprint_ident $collection_ident KeyPayload>] = $collection_key_type ); // TODO(David): Tweak when I work out the right strategy for keys. Probably just want a single new-type. // So probably just don't use generate_content_type but use generate_key_type or something instead? - pub type [<$blueprint_ident $collection_ident SubstateKey>] = [<$blueprint_ident $collection_ident KeyContent>]; + pub type [<$blueprint_ident $collection_ident SubstateKey>] = [<$blueprint_ident $collection_ident KeyPayload>]; // TODO(David) - Properly handle SortedIndex: // Fix Key types for SortedIndex to have a named u16 part of the key, @@ -394,12 +394,12 @@ macro_rules! declare_native_blueprint_state { content_trait: [<$collection_type EntryContent>], ident_core: [<$blueprint_ident $collection_ident>], #[derive(Debug, PartialEq, Eq, ScryptoSbor)] - struct [<$blueprint_ident $collection_ident EntryContent>] = $collection_value_type + struct [<$blueprint_ident $collection_ident EntryPayload>] = $collection_value_type ); // > Set up the _EntrySubstate alias for the system-wrapped substate generate_system_substate_type_alias!( $collection_type, - type [<$blueprint_ident $collection_ident EntrySubstate>] = WRAPPED [<$blueprint_ident $collection_ident EntryContent>] + type [<$blueprint_ident $collection_ident EntrySubstate>] = WRAPPED [<$blueprint_ident $collection_ident EntryPayload>] ); )* @@ -588,7 +588,7 @@ macro_rules! declare_native_blueprint_state { // TODO(David) - Implement instance schema fields.push(FieldSchema { field: TypeRef::Static( - type_aggregator.add_child_type_and_descendents::<[<$blueprint_ident $field_ident FieldContent>]>() + type_aggregator.add_child_type_and_descendents::<[<$blueprint_ident $field_ident FieldPayload>]>() ), condition: $field_condition, }); @@ -600,9 +600,9 @@ macro_rules! declare_native_blueprint_state { $collection_type, type_aggregator, $collection_key_type, - [<$blueprint_ident $collection_ident KeyContent>], + [<$blueprint_ident $collection_ident KeyPayload>], $collection_value_type, - [<$blueprint_ident $collection_ident EntryContent>], + [<$blueprint_ident $collection_ident EntryPayload>], $collection_can_own )); )* diff --git a/radix-engine/src/blueprints/package/package.rs b/radix-engine/src/blueprints/package/package.rs index 25f2b06d660..b55dde62c13 100644 --- a/radix-engine/src/blueprints/package/package.rs +++ b/radix-engine/src/blueprints/package/package.rs @@ -1112,7 +1112,7 @@ impl PackageRoyaltyNativeBlueprint { LockFlags::read_only(), )?; - let substate: PackageRoyaltyAccumulatorFieldContent = api.field_read_typed(handle)?; + let substate: PackageRoyaltyAccumulatorFieldPayload = api.field_read_typed(handle)?; let bucket = substate.0.into_latest().royalty_vault.take_all(api)?; Ok(bucket) diff --git a/simulator/src/resim/mod.rs b/simulator/src/resim/mod.rs index 5482052a86c..cccb1bb1cf5 100644 --- a/simulator/src/resim/mod.rs +++ b/simulator/src/resim/mod.rs @@ -349,7 +349,10 @@ pub fn export_package_schema( _ => panic!("Unexpected"), }; - blueprints.insert(bp_version_key, blueprint_definition.value.unwrap().0.into_latest()); + blueprints.insert( + bp_version_key, + blueprint_definition.value.unwrap().0.into_latest(), + ); } Ok(blueprints) From 5fa5b54c4d7cc50ecc94e0c1b27645a48d03c759 Mon Sep 17 00:00:00 2001 From: David Edey Date: Thu, 17 Aug 2023 02:43:29 +0100 Subject: [PATCH 19/28] tidy: Remove unused model --- radix-engine/src/blueprints/package/substates.rs | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/radix-engine/src/blueprints/package/substates.rs b/radix-engine/src/blueprints/package/substates.rs index 6c533a3fc19..786b2c289b8 100644 --- a/radix-engine/src/blueprints/package/substates.rs +++ b/radix-engine/src/blueprints/package/substates.rs @@ -52,6 +52,7 @@ declare_native_blueprint_state! { }, value_type: { kind: Static, + // TODO(David): Change to VersionedSchema in this substate in the SCHEMAS_PARTITION the_type: ScryptoSchema, }, allow_ownership: false, @@ -127,14 +128,6 @@ pub struct PackageRoyaltyAccumulatorV1 { pub royalty_vault: Vault, } -//--------------------------------------- -// Collection models - Schemas -//--------------------------------------- - -// TODO(David): Change to VersionedSchema when can define a type as not-implicitly-versioned -// TODO: Move to Schema partition when we have it -pub type PackageSchemaV1 = ScryptoSchema; - //--------------------------------------- // Collection models - By BlueprintVersion //--------------------------------------- From 9baed9a69271babc78256c16468bf5390126d9dd Mon Sep 17 00:00:00 2001 From: David Edey Date: Thu, 17 Aug 2023 11:42:11 +0100 Subject: [PATCH 20/28] tweak: Minor refactor to macros --- radix-engine/src/blueprints/macros.rs | 76 +++++++++++++++++++-------- 1 file changed, 53 insertions(+), 23 deletions(-) diff --git a/radix-engine/src/blueprints/macros.rs b/radix-engine/src/blueprints/macros.rs index b5f128e683c..dad0d836fdc 100644 --- a/radix-engine/src/blueprints/macros.rs +++ b/radix-engine/src/blueprints/macros.rs @@ -444,6 +444,13 @@ macro_rules! declare_native_blueprint_state { } } + /// A list of all logical (real) and physical (mapped) partitions for the + /// $blueprint_ident blueprint. + /// + /// Note: In future, we could add a separate LogicalPartition enum, to + /// not include physical partitions - however it's very hard to do in + /// declarative macro land, because enum variants can't be + /// macro invocations (to eg filter out the physical partition types) #[repr(u8)] #[derive(Debug, Clone, Copy, Sbor, PartialEq, Eq, Hash, PartialOrd, Ord)] pub enum [<$blueprint_ident Partition>] { @@ -460,12 +467,19 @@ macro_rules! declare_native_blueprint_state { return PartitionDescription::Logical(PartitionOffset(module_partition_offset)); } $( - generate_next_partition_description!( - let current_partition_description, - module_partition_offset, + let mapped_physical_partition = extract_collection_option!( + mapped_physical_partition, $($collection_options)? ); - + let current_partition_description = match mapped_physical_partition { + Some(physical_partition) => { + PartitionDescription::Physical(physical_partition) + }, + None => { + module_partition_offset += 1; + PartitionDescription::Logical(PartitionOffset(module_partition_offset)) + }, + }; if (*self as u8) == (Self::[<$collection_ident $collection_type>] as u8) { return current_partition_description; } @@ -565,6 +579,13 @@ macro_rules! declare_native_blueprint_state { // Typed Substate - Keys and Values //--------------------------------- #[derive(Debug, Clone)] + /// All the SubstateKeys for all logical (real) and physical (mapped) + /// partitions for the $blueprint_ident blueprint. + /// + /// Note: In future, we could remove keys for physical partitions from this, + /// as they can't be persisted as-is - however it's very hard to do in + /// declarative macro land, because enum variants can't be + /// macro invocations (to eg filter out the physical partition types) pub enum [<$blueprint_ident TypedSubstateKey>] { Fields([<$blueprint_ident Field>]), $([<$collection_ident $collection_type Entries>]([<$blueprint_ident $collection_ident SubstateKey>]),)* @@ -596,6 +617,13 @@ macro_rules! declare_native_blueprint_state { } #[derive(Debug)] + /// All the Substate values for all logical (real) and physical (mapped) + /// partitions for the $blueprint_ident blueprint. + /// + /// Note: In future, we could remove values for physical partitions from this, + /// as they can't be persisted as-is - however it's very hard to do in + /// declarative macro land, because enum variants can't be + /// macro invocations (to eg filter out the physical partition types) pub enum [<$blueprint_ident TypedSubstateValue>] { Field([<$blueprint_ident TypedFieldSubstateValue>]), $([<$collection_ident $collection_type>]([<$blueprint_ident $collection_ident EntrySubstate>]),)* @@ -671,10 +699,9 @@ macro_rules! declare_native_blueprint_state { /// Used for initializing blueprint state. /// /// Note - this doesn't support: - /// * Features - /// * Instance schemas - /// * Feature-dependent fields + /// * Instance schemas / generics (yet) /// * IndexEntries (because the underlying new_object API doesn't support them) + /// > these panic at create time #[derive(Debug, Default)] pub struct [<$blueprint_ident StateInit>] { $( @@ -1097,32 +1124,35 @@ mod helper_macros { #[allow(unused)] pub(crate) use map_entry_substate_to_kv_entry; - macro_rules! generate_next_partition_description { + macro_rules! extract_collection_option { ( - let $current_partition_description:ident, - $module_partition_offset:ident, + mapped_physical_partition, // Name of field { - mapped_physical_partition: $physical_partition:expr$(,)? - // When other options are added, need to explicitly add - // an ordering of optional other key / values - } + mapped_physical_partition: $value:tt$(,)? + $(sorted_index_key_property: $ignored:tt$(,)?)? + }$(,)? + ) => { + Some($value) + }; + ( + sorted_index_key_property, // Name of field + { + $(mapped_physical_partition: $ignored:tt$(,)?)? + sorted_index_key_property: $value:tt$(,)? + }$(,)? ) => { - let $current_partition_description = - PartitionDescription::Physical($physical_partition); + Some($value) }; ( - let $current_partition_description:ident, - $module_partition_offset:ident, - $($non_matching_stuff:tt)? + $option_field_name:ident, + $($non_matching_stuff:tt)?$(,)? ) => { - $module_partition_offset += 1; - let $current_partition_description = - PartitionDescription::Logical(PartitionOffset($module_partition_offset)); + None }; } #[allow(unused)] - pub(crate) use generate_next_partition_description; + pub(crate) use extract_collection_option; } #[cfg(test)] From e50cbf589237c5ba23fba72b7a1c6ec37a6ece72 Mon Sep 17 00:00:00 2001 From: David Edey Date: Thu, 17 Aug 2023 12:22:29 +0100 Subject: [PATCH 21/28] refactor: Move blueprint model/macros around --- radix-engine/src/blueprints/mod.rs | 6 +- radix-engine/src/blueprints/models/content.rs | 39 ++++ .../src/blueprints/models/feature_set.rs | 157 +++++++++++++ radix-engine/src/blueprints/models/mod.rs | 7 + .../native_blueprint_state_macro.rs} | 215 ++---------------- .../src/blueprints/package/package.rs | 21 +- .../src/blueprints/package/substates.rs | 3 +- radix-engine/src/lib.rs | 14 ++ 8 files changed, 245 insertions(+), 217 deletions(-) create mode 100644 radix-engine/src/blueprints/models/content.rs create mode 100644 radix-engine/src/blueprints/models/feature_set.rs create mode 100644 radix-engine/src/blueprints/models/mod.rs rename radix-engine/src/blueprints/{macros.rs => models/native_blueprint_state_macro.rs} (86%) diff --git a/radix-engine/src/blueprints/mod.rs b/radix-engine/src/blueprints/mod.rs index b5c80e63043..d5a31b99224 100644 --- a/radix-engine/src/blueprints/mod.rs +++ b/radix-engine/src/blueprints/mod.rs @@ -2,7 +2,7 @@ pub mod access_controller; pub mod account; pub mod consensus_manager; pub mod identity; -pub mod macros; +pub mod models; pub mod native_schema; pub mod package; pub mod pool; @@ -10,3 +10,7 @@ pub mod resource; pub mod transaction_processor; pub mod transaction_tracker; pub mod util; + +pub(crate) mod internal_prelude { + pub use super::models::*; +} diff --git a/radix-engine/src/blueprints/models/content.rs b/radix-engine/src/blueprints/models/content.rs new file mode 100644 index 00000000000..77b92681910 --- /dev/null +++ b/radix-engine/src/blueprints/models/content.rs @@ -0,0 +1,39 @@ +use crate::internal_prelude::*; + +pub trait FieldContent>: Sized { + fn into_locked_substate(self) -> FieldSubstate { + FieldSubstate::new_locked_field(self.into()) + } + + fn into_mutable_substate(self) -> FieldSubstate { + FieldSubstate::new_field(self.into()) + } +} + +pub trait KeyContent>: Sized { + fn into_key(self) -> KeyPayload { + self.into() + } +} + +pub trait KeyValueEntryContent>: Sized { + fn into_locked_substate(self) -> KeyValueEntrySubstate { + KeyValueEntrySubstate::entry(self.into()) + } + + fn into_mutable_substate(self) -> KeyValueEntrySubstate { + KeyValueEntrySubstate::locked_entry(self.into()) + } +} + +pub trait IndexEntryContent>: Sized { + fn into_substate(self) -> EntryPayload { + self.into() + } +} + +pub trait SortedIndexEntryContent>: Sized { + fn into_substate(self) -> EntryPayload { + self.into() + } +} diff --git a/radix-engine/src/blueprints/models/feature_set.rs b/radix-engine/src/blueprints/models/feature_set.rs new file mode 100644 index 00000000000..36739df44df --- /dev/null +++ b/radix-engine/src/blueprints/models/feature_set.rs @@ -0,0 +1,157 @@ +use crate::internal_prelude::*; + +pub trait FeatureSetResolver: Debug { + fn feature_names_str(&self) -> Vec<&'static str>; + + fn feature_names_str_set(&self) -> BTreeSet<&'static str> { + self.feature_names_str().into_iter().collect() + } + + fn feature_names_string(&self) -> Vec { + self.feature_names_str() + .into_iter() + .map(|s| s.to_owned()) + .collect() + } + + fn feature_names_string_set(&self) -> BTreeSet { + self.feature_names_str() + .into_iter() + .map(|s| s.to_owned()) + .collect() + } +} + +/// For feature checks against a non-inner object +#[derive(Debug)] +pub enum FeatureChecks { + None, + RequireAllSubstates, + ForFeatures { own_features: TOwn }, +} + +impl From for FeatureChecks { + fn from(value: T) -> Self { + FeatureChecks::ForFeatures { + own_features: value, + } + } +} + +impl FeatureChecks { + pub fn assert_valid( + &self, + substate_name: &'static str, + condition: &Condition, + is_present: bool, + ) -> Result<(), RuntimeError> { + let is_valid = match self { + FeatureChecks::None => Ok(()), + FeatureChecks::RequireAllSubstates => { + if is_present { + Ok(()) + } else { + Err(format!("Required all substates to be present, but {} was not present", substate_name)) + } + }, + FeatureChecks::ForFeatures { own_features } => { + match condition { + Condition::Always => { + if is_present { + Ok(()) + } else { + Err(format!("Substate condition for {} required it to be always present, but it was not", substate_name)) + } + } + Condition::IfFeature(feature) => { + let feature_enabled = own_features.feature_names_str().contains(&feature.as_str()); + if feature_enabled && !is_present { + Err(format!("Substate condition for {} required it to be present when the feature {} was enabled, but it was absent", substate_name, feature)) + } else if !feature_enabled && is_present { + Err(format!("Substate condition for {} required it to be absent when the feature {} was disabled, but it was present", substate_name, feature)) + } else { + Ok(()) + } + }, + Condition::IfOuterFeature(_) => { + Err(format!("Substate condition for {} required an outer object feature, but the blueprint does not have an outer blueprint defined", substate_name)) + } + } + }, + }; + is_valid.map_err(|error_message| { + RuntimeError::SystemError(SystemError::InvalidNativeSubstatesForFeature(error_message)) + }) + } +} + +/// For feature checks against an inner object +pub enum InnerObjectFeatureChecks { + None, + RequireAllSubstates, + ForFeatures { + own_features: TOwn, + outer_object_features: TOuter, + }, +} + +impl InnerObjectFeatureChecks { + pub fn assert_valid( + &self, + substate_name: &'static str, + condition: &Condition, + is_present: bool, + ) -> Result<(), RuntimeError> { + let is_valid = match self { + Self::None => Ok(()), + Self::RequireAllSubstates => { + if is_present { + Ok(()) + } else { + Err(format!( + "Required all substates to be present, but {} was not present", + substate_name + )) + } + } + Self::ForFeatures { + own_features, + outer_object_features, + } => match condition { + Condition::Always => { + if is_present { + Ok(()) + } else { + Err(format!("Substate condition for {} required it to be always present, but it was not", substate_name)) + } + } + Condition::IfFeature(feature) => { + let feature_enabled = + own_features.feature_names_str().contains(&feature.as_str()); + if feature_enabled && !is_present { + Err(format!("Substate condition for {} required it to be present when the feature {} was enabled, but it was absent", substate_name, feature)) + } else if !feature_enabled && is_present { + Err(format!("Substate condition for {} required it to be absent when the feature {} was disabled, but it was present", substate_name, feature)) + } else { + Ok(()) + } + } + Condition::IfOuterFeature(feature) => { + let feature_enabled = outer_object_features + .feature_names_str() + .contains(&feature.as_str()); + if feature_enabled && !is_present { + Err(format!("Substate condition for {} required it to be present when the outer object feature {} was enabled, but it was absent", substate_name, feature)) + } else if !feature_enabled && is_present { + Err(format!("Substate condition for {} required it to be absent when the outer object feature {} was disabled, but it was present", substate_name, feature)) + } else { + Ok(()) + } + } + }, + }; + is_valid.map_err(|error_message| { + RuntimeError::SystemError(SystemError::InvalidNativeSubstatesForFeature(error_message)) + }) + } +} diff --git a/radix-engine/src/blueprints/models/mod.rs b/radix-engine/src/blueprints/models/mod.rs new file mode 100644 index 00000000000..45f493c3c57 --- /dev/null +++ b/radix-engine/src/blueprints/models/mod.rs @@ -0,0 +1,7 @@ +mod content; +mod feature_set; +mod native_blueprint_state_macro; + +pub use content::*; +pub use feature_set::*; +pub(crate) use native_blueprint_state_macro::*; diff --git a/radix-engine/src/blueprints/macros.rs b/radix-engine/src/blueprints/models/native_blueprint_state_macro.rs similarity index 86% rename from radix-engine/src/blueprints/macros.rs rename to radix-engine/src/blueprints/models/native_blueprint_state_macro.rs index dad0d836fdc..35b17bdb9b0 100644 --- a/radix-engine/src/blueprints/macros.rs +++ b/radix-engine/src/blueprints/models/native_blueprint_state_macro.rs @@ -1,203 +1,4 @@ -use crate::errors::*; -use crate::system::system::*; -use crate::types::*; - -// TODO(David) - Move this, features and content stuff under a `models` module? -// TODO(David) - Create an internal_prelude in `blueprints` -// and an internal_prelude and prelud in engine -pub trait FeatureSetResolver: Debug { - fn feature_names_str(&self) -> Vec<&'static str>; - - fn feature_names_str_set(&self) -> BTreeSet<&'static str> { - self.feature_names_str().into_iter().collect() - } - - fn feature_names_string(&self) -> Vec { - self.feature_names_str() - .into_iter() - .map(|s| s.to_owned()) - .collect() - } - - fn feature_names_string_set(&self) -> BTreeSet { - self.feature_names_str() - .into_iter() - .map(|s| s.to_owned()) - .collect() - } -} - -/// For feature checks against a non-inner object -#[derive(Debug)] -pub enum FeatureChecks { - None, - RequireAllSubstates, - ForFeatures { own_features: TOwn }, -} - -impl From for FeatureChecks { - fn from(value: T) -> Self { - FeatureChecks::ForFeatures { - own_features: value, - } - } -} - -impl FeatureChecks { - pub fn assert_valid( - &self, - substate_name: &'static str, - condition: &Condition, - is_present: bool, - ) -> Result<(), RuntimeError> { - let is_valid = match self { - FeatureChecks::None => Ok(()), - FeatureChecks::RequireAllSubstates => { - if is_present { - Ok(()) - } else { - Err(format!("Required all substates to be present, but {} was not present", substate_name)) - } - }, - FeatureChecks::ForFeatures { own_features } => { - match condition { - Condition::Always => { - if is_present { - Ok(()) - } else { - Err(format!("Substate condition for {} required it to be always present, but it was not", substate_name)) - } - } - Condition::IfFeature(feature) => { - let feature_enabled = own_features.feature_names_str().contains(&feature.as_str()); - if feature_enabled && !is_present { - Err(format!("Substate condition for {} required it to be present when the feature {} was enabled, but it was absent", substate_name, feature)) - } else if !feature_enabled && is_present { - Err(format!("Substate condition for {} required it to be absent when the feature {} was disabled, but it was present", substate_name, feature)) - } else { - Ok(()) - } - }, - Condition::IfOuterFeature(_) => { - Err(format!("Substate condition for {} required an outer object feature, but the blueprint does not have an outer blueprint defined", substate_name)) - } - } - }, - }; - is_valid.map_err(|error_message| { - RuntimeError::SystemError(SystemError::InvalidNativeSubstatesForFeature(error_message)) - }) - } -} - -/// For feature checks against an inner object -pub enum InnerObjectFeatureChecks { - None, - RequireAllSubstates, - ForFeatures { - own_features: TOwn, - outer_object_features: TOuter, - }, -} - -impl InnerObjectFeatureChecks { - pub fn assert_valid( - &self, - substate_name: &'static str, - condition: &Condition, - is_present: bool, - ) -> Result<(), RuntimeError> { - let is_valid = match self { - Self::None => Ok(()), - Self::RequireAllSubstates => { - if is_present { - Ok(()) - } else { - Err(format!( - "Required all substates to be present, but {} was not present", - substate_name - )) - } - } - Self::ForFeatures { - own_features, - outer_object_features, - } => match condition { - Condition::Always => { - if is_present { - Ok(()) - } else { - Err(format!("Substate condition for {} required it to be always present, but it was not", substate_name)) - } - } - Condition::IfFeature(feature) => { - let feature_enabled = - own_features.feature_names_str().contains(&feature.as_str()); - if feature_enabled && !is_present { - Err(format!("Substate condition for {} required it to be present when the feature {} was enabled, but it was absent", substate_name, feature)) - } else if !feature_enabled && is_present { - Err(format!("Substate condition for {} required it to be absent when the feature {} was disabled, but it was present", substate_name, feature)) - } else { - Ok(()) - } - } - Condition::IfOuterFeature(feature) => { - let feature_enabled = outer_object_features - .feature_names_str() - .contains(&feature.as_str()); - if feature_enabled && !is_present { - Err(format!("Substate condition for {} required it to be present when the outer object feature {} was enabled, but it was absent", substate_name, feature)) - } else if !feature_enabled && is_present { - Err(format!("Substate condition for {} required it to be absent when the outer object feature {} was disabled, but it was present", substate_name, feature)) - } else { - Ok(()) - } - } - }, - }; - is_valid.map_err(|error_message| { - RuntimeError::SystemError(SystemError::InvalidNativeSubstatesForFeature(error_message)) - }) - } -} - -pub trait FieldContent>: Sized { - fn into_locked_substate(self) -> FieldSubstate { - FieldSubstate::new_locked_field(self.into()) - } - - fn into_mutable_substate(self) -> FieldSubstate { - FieldSubstate::new_field(self.into()) - } -} - -pub trait KeyContent>: Sized { - fn into_key(self) -> KeyPayload { - self.into() - } -} - -pub trait KeyValueEntryContent>: Sized { - fn into_locked_substate(self) -> KeyValueEntrySubstate { - KeyValueEntrySubstate::entry(self.into()) - } - - fn into_mutable_substate(self) -> KeyValueEntrySubstate { - KeyValueEntrySubstate::locked_entry(self.into()) - } -} - -pub trait IndexEntryContent>: Sized { - fn into_substate(self) -> EntryPayload { - self.into() - } -} - -pub trait SortedIndexEntryContent>: Sized { - fn into_substate(self) -> EntryPayload { - self.into() - } -} +use crate::internal_prelude::*; /// Generates types and typed-interfaces for native blueprints, their /// state models, features, partitions, and their interaction with @@ -548,12 +349,24 @@ macro_rules! declare_native_blueprint_state { $(pub [<$feature_property_name>]: bool,)* } + impl [<$blueprint_ident FeatureSet>] { + pub fn all_features() -> BTreeSet { + let mut features = BTreeSet::new(); + $( + features.insert( + [<$blueprint_ident Feature>]::$feature_ident.feature_name().to_string() + ); + )* + features + } + } + impl FeatureSetResolver for [<$blueprint_ident FeatureSet>] { fn feature_names_str(&self) -> Vec<&'static str> { let mut names = vec![]; $( if self.[<$feature_property_name>] { - names.push(stringify!($feature_property_name)); + names.push([<$blueprint_ident Feature>]::$feature_ident.feature_name()); } )* names diff --git a/radix-engine/src/blueprints/package/package.rs b/radix-engine/src/blueprints/package/package.rs index 2feabdf1920..6ea27062b76 100644 --- a/radix-engine/src/blueprints/package/package.rs +++ b/radix-engine/src/blueprints/package/package.rs @@ -1,6 +1,6 @@ -use crate::blueprints::macros::*; +use super::substates::*; use crate::blueprints::util::SecurifiedRoleAssignment; -use crate::errors::*; +use crate::internal_prelude::*; use crate::kernel::kernel_api::{KernelApi, KernelSubstateApi}; use crate::system::node_init::type_info_partition; use crate::system::node_modules::metadata::MetadataEntrySubstate; @@ -34,10 +34,6 @@ use crate::system::system_callback_api::SystemCallbackObject; use crate::system::system_modules::auth::{AuthError, ResolvedPermission}; use crate::vm::VmPackageValidation; -use super::*; - -pub const PACKAGE_ROYALTY_FEATURE: &str = "package_royalty"; - #[derive(Debug, Clone, PartialEq, Eq, ScryptoSbor)] pub enum PackageError { InvalidWasm(PrepareError), @@ -690,14 +686,11 @@ impl PackageNativePackage { PACKAGE_BLUEPRINT.to_string() => BlueprintDefinitionInit { blueprint_type: BlueprintType::default(), is_transient: false, - feature_set: btreeset!( - PACKAGE_ROYALTY_FEATURE.to_string(), - ), + feature_set: PackageFeatureSet::all_features(), dependencies: btreeset!( PACKAGE_OF_DIRECT_CALLER_VIRTUAL_BADGE.into(), PACKAGE_OWNER_BADGE.into(), ), - schema: BlueprintSchemaInit { generics: vec![], schema, @@ -708,7 +701,6 @@ impl PackageNativePackage { }, hooks: BlueprintHooksInit::default(), }, - royalty_config: PackageRoyaltyConfig::default(), auth_config: AuthConfig { function_auth: FunctionAuth::AccessRules( @@ -1119,7 +1111,7 @@ impl PackageRoyaltyNativeBlueprint { if !service.is_feature_enabled( receiver, ObjectModuleId::Main, - PACKAGE_ROYALTY_FEATURE, + PackageFeature::PackageRoyalty.feature_name(), )? { return Ok(()); } @@ -1181,7 +1173,10 @@ impl PackageRoyaltyNativeBlueprint { where Y: ClientApi, { - if !api.actor_is_feature_enabled(OBJECT_HANDLE_SELF, PACKAGE_ROYALTY_FEATURE)? { + if !api.actor_is_feature_enabled( + OBJECT_HANDLE_SELF, + PackageFeature::PackageRoyalty.feature_name(), + )? { return Err(RuntimeError::ApplicationError( ApplicationError::PackageError(PackageError::RoyaltiesNotEnabled), )); diff --git a/radix-engine/src/blueprints/package/substates.rs b/radix-engine/src/blueprints/package/substates.rs index 786b2c289b8..c88f27ea26d 100644 --- a/radix-engine/src/blueprints/package/substates.rs +++ b/radix-engine/src/blueprints/package/substates.rs @@ -1,6 +1,5 @@ use super::*; -use crate::blueprints::macros::*; -use crate::types::*; +use crate::internal_prelude::*; declare_native_blueprint_state! { blueprint_ident: Package, diff --git a/radix-engine/src/lib.rs b/radix-engine/src/lib.rs index 25a5d360d3c..51a4119595c 100644 --- a/radix-engine/src/lib.rs +++ b/radix-engine/src/lib.rs @@ -30,3 +30,17 @@ pub mod errors; pub mod utils; pub mod vm; + +pub mod prelude { + // Note - radix_engine::types was previously something like an internal/ + // external prelude, but let's normalize radix-engine to have a prelude + // like everything else, and add to it where needed + pub use crate::types::*; +} + +pub(crate) mod internal_prelude { + pub use super::prelude::*; + pub use crate::blueprints::internal_prelude::*; + pub use crate::errors::*; + pub use crate::system::system::*; +} From dc4b02fb59e8633db0fc0877f526b9cede38b1cf Mon Sep 17 00:00:00 2001 From: David Edey Date: Thu, 17 Aug 2023 14:07:50 +0100 Subject: [PATCH 22/28] refactor: Further minor moves / refactors --- .../src/blueprints/package/substates.rs | 41 ++++++++----------- radix-engine-interface/src/lib.rs | 4 ++ .../src/blueprints/package/package.rs | 11 ++--- sbor/src/lib.rs | 1 + sbor/src/schema/mod.rs | 7 ++++ scrypto-schema/src/lib.rs | 1 - simulator/src/resim/cmd_publish.rs | 26 +++--------- 7 files changed, 38 insertions(+), 53 deletions(-) diff --git a/radix-engine-interface/src/blueprints/package/substates.rs b/radix-engine-interface/src/blueprints/package/substates.rs index 6993e55b4b2..c1e10630f8a 100644 --- a/radix-engine-interface/src/blueprints/package/substates.rs +++ b/radix-engine-interface/src/blueprints/package/substates.rs @@ -1,9 +1,5 @@ use crate::blueprints::package::BlueprintType; -use crate::prelude::*; -use crate::schema::*; -use crate::types::*; -use crate::*; -use radix_engine_common::crypto::Hash; +use crate::internal_prelude::*; pub const PACKAGE_CODE_ID: u64 = 0u64; pub const RESOURCE_CODE_ID: u64 = 1u64; @@ -36,6 +32,17 @@ pub enum BlueprintPayloadDef { // TODO: How to represent a structure containing a generic? } +impl BlueprintPayloadDef { + pub fn from_type_ref(type_ref: TypeRef, schema_hash: SchemaHash) -> Self { + match type_ref { + TypeRef::Static(type_index) => { + BlueprintPayloadDef::Static(TypeIdentifier(schema_hash, type_index)) + } + TypeRef::Generic(index) => BlueprintPayloadDef::Generic(index), + } + } +} + #[derive(Debug, Clone, PartialEq, Eq, Sbor)] pub struct FunctionSchema { pub receiver: Option, @@ -280,19 +287,9 @@ impl IndexedStateSchema { let schema_fields = schema .fields .into_iter() - .map(|field_schema| { - let pointer = match field_schema.field { - TypeRef::Static(type_index) => { - BlueprintPayloadDef::Static(TypeIdentifier(schema_hash, type_index)) - } - TypeRef::Generic(instance_index) => { - BlueprintPayloadDef::Generic(instance_index) - } - }; - FieldSchema { - field: pointer, - condition: field_schema.condition, - } + .map(|field_schema| FieldSchema { + field: BlueprintPayloadDef::from_type_ref(field_schema.field, schema_hash), + condition: field_schema.condition, }) .collect(); fields = Some(( @@ -304,12 +301,8 @@ impl IndexedStateSchema { let mut collections = Vec::new(); for (collection_index, collection_schema) in schema.collections.into_iter().enumerate() { - let schema = collection_schema.map(|type_ref| match type_ref { - TypeRef::Static(type_index) => { - BlueprintPayloadDef::Static(TypeIdentifier(schema_hash, type_index)) - } - TypeRef::Generic(generic_index) => BlueprintPayloadDef::Generic(generic_index), - }); + let schema = collection_schema + .map(|type_ref| BlueprintPayloadDef::from_type_ref(type_ref, schema_hash)); if let Some(partition_num) = system_mappings.get(&collection_index) { collections.push((PartitionDescription::Physical(*partition_num), schema)); diff --git a/radix-engine-interface/src/lib.rs b/radix-engine-interface/src/lib.rs index d89a9d48e67..f200e646796 100644 --- a/radix-engine-interface/src/lib.rs +++ b/radix-engine-interface/src/lib.rs @@ -60,3 +60,7 @@ pub mod prelude { non_fungible_data_update_roles, recall_roles, role_entry, roles2, rule, withdraw_roles, }; } + +pub(crate) mod internal_prelude { + pub use crate::prelude::*; +} diff --git a/radix-engine/src/blueprints/package/package.rs b/radix-engine/src/blueprints/package/package.rs index 6ea27062b76..1519fbf78f3 100644 --- a/radix-engine/src/blueprints/package/package.rs +++ b/radix-engine/src/blueprints/package/package.rs @@ -898,13 +898,10 @@ impl PackageNativePackage { let mut events = BTreeMap::new(); for (key, type_ref) in definition_init.schema.events.event_schema { - let index = match type_ref { - TypeRef::Static(index) => { - BlueprintPayloadDef::Static(TypeIdentifier(schema_hash, index)) - } - TypeRef::Generic(index) => BlueprintPayloadDef::Generic(index), - }; - events.insert(key, index); + events.insert( + key, + BlueprintPayloadDef::from_type_ref(type_ref, schema_hash), + ); } let system_instructions = system_instructions diff --git a/sbor/src/lib.rs b/sbor/src/lib.rs index c2b4217a870..b600c7e043c 100644 --- a/sbor/src/lib.rs +++ b/sbor/src/lib.rs @@ -91,6 +91,7 @@ pub mod prelude { pub use crate::enum_variant::FixedEnumVariant as SborFixedEnumVariant; pub use crate::path::{SborPath, SborPathBuf}; pub use crate::representations; + pub use crate::schema::prelude::*; pub use crate::value::{CustomValue as SborCustomValue, Value as SborValue}; pub use crate::value_kind::*; pub use crate::{ diff --git a/sbor/src/schema/mod.rs b/sbor/src/schema/mod.rs index 81e8fb97503..137dfe56cb1 100644 --- a/sbor/src/schema/mod.rs +++ b/sbor/src/schema/mod.rs @@ -17,3 +17,10 @@ pub use type_aggregator::*; pub use type_data::*; pub use type_link::*; pub use well_known_types::*; + +pub mod prelude { + pub use super::schema::*; + pub use super::type_aggregator::*; + pub use super::type_data::*; + pub use super::type_link::*; +} diff --git a/scrypto-schema/src/lib.rs b/scrypto-schema/src/lib.rs index 788460be8ed..4318b79588f 100644 --- a/scrypto-schema/src/lib.rs +++ b/scrypto-schema/src/lib.rs @@ -7,7 +7,6 @@ compile_error!("Feature `std` and `alloc` can't be enabled at the same time."); use bitflags::bitflags; use radix_engine_common::prelude::*; -use sbor::*; #[derive(Debug, Clone, PartialEq, Eq, ScryptoSbor, ManifestSbor)] pub struct KeyValueStoreGenericSubstitutions { diff --git a/simulator/src/resim/cmd_publish.rs b/simulator/src/resim/cmd_publish.rs index 4c8f7b98331..d3d85ab6ae5 100644 --- a/simulator/src/resim/cmd_publish.rs +++ b/simulator/src/resim/cmd_publish.rs @@ -1,13 +1,12 @@ use clap::Parser; use colored::*; -use radix_engine::blueprints::macros::*; +use radix_engine::blueprints::models::*; use radix_engine::track::IntoDatabaseUpdates; use radix_engine::types::*; use radix_engine_interface::blueprints::package::{ BlueprintDefinition, BlueprintDependencies, BlueprintPayloadDef, FunctionSchema, IndexedStateSchema, PackageExport, VmType, *, }; -use radix_engine_interface::schema::TypeRef; use radix_engine_queries::typed_substate_layout::*; use radix_engine_store_interface::interface::CommittableSubstateDatabase; use std::ffi::OsStr; @@ -121,18 +120,8 @@ impl Publish { function.clone(), FunctionSchema { receiver: setup.receiver, - input: match setup.input { - TypeRef::Static(type_index) => BlueprintPayloadDef::Static( - TypeIdentifier(schema_hash, type_index), - ), - TypeRef::Generic(index) => BlueprintPayloadDef::Generic(index), - }, - output: match setup.output { - TypeRef::Static(type_index) => BlueprintPayloadDef::Static( - TypeIdentifier(schema_hash, type_index), - ), - TypeRef::Generic(index) => BlueprintPayloadDef::Generic(index), - }, + input: BlueprintPayloadDef::from_type_ref(setup.input, schema_hash), + output: BlueprintPayloadDef::from_type_ref(setup.output, schema_hash), }, ); let export = PackageExport { @@ -147,15 +136,10 @@ impl Publish { .events .event_schema .into_iter() - .map(|(key, index)| { + .map(|(key, type_ref)| { ( key, - match index { - TypeRef::Static(index) => { - BlueprintPayloadDef::Static(TypeIdentifier(schema_hash, index)) - } - TypeRef::Generic(index) => BlueprintPayloadDef::Generic(index), - }, + BlueprintPayloadDef::from_type_ref(type_ref, schema_hash), ) }) .collect(); From fd78605b77791cd130544ebe2f81db2a788f8b92 Mon Sep 17 00:00:00 2001 From: David Edey Date: Thu, 17 Aug 2023 15:17:12 +0100 Subject: [PATCH 23/28] tweak: Add support for generics in declare_native_blueprint_state --- .../models/native_blueprint_state_macro.rs | 161 ++++++++++++------ .../src/blueprints/package/substates.rs | 1 - sbor-derive-common/src/utils.rs | 13 +- 3 files changed, 116 insertions(+), 59 deletions(-) diff --git a/radix-engine/src/blueprints/models/native_blueprint_state_macro.rs b/radix-engine/src/blueprints/models/native_blueprint_state_macro.rs index 35b17bdb9b0..516d94760bb 100644 --- a/radix-engine/src/blueprints/models/native_blueprint_state_macro.rs +++ b/radix-engine/src/blueprints/models/native_blueprint_state_macro.rs @@ -31,8 +31,8 @@ use crate::internal_prelude::*; /// the_type: x, /// }, /// { -/// kind: Instance, -/// ident: GenericTypeParameterName, +/// kind: Generic, +/// ident: BlueprintGenericParameterIdent, /// }, /// // In future /// { @@ -69,6 +69,18 @@ macro_rules! declare_native_blueprint_state { $(,)? }, )? + $( + generics: { + $( + $generic_property_name:ident: { + ident: $generic_ident:ident, + description: $generic_description:expr + $(,)? + } + ),* + $(,)? + }, + )? features: { $( $feature_property_name:ident: { @@ -78,15 +90,6 @@ macro_rules! declare_native_blueprint_state { ),* $(,)? }, - instance_schema_types: { - // If no types => instance schema disabled - $( - $instance_type_property_name:ident: { - ident: $instance_type_ident:ident, - } - ),* - $(,)? - }, fields: { $( $field_property_name:ident: { @@ -152,7 +155,7 @@ macro_rules! declare_native_blueprint_state { Field, type [<$blueprint_ident $field_ident FieldSubstate>] = WRAPPED [<$blueprint_ident $field_ident FieldPayload>] ); - );* + )* // Generate models for each collection $( @@ -344,6 +347,35 @@ macro_rules! declare_native_blueprint_state { } } + $( + #[repr(u8)] + #[derive(Debug, Clone, Copy, Sbor, PartialEq, Eq, Hash, FromRepr)] + pub enum [<$blueprint_ident Generic>] { + $($generic_ident,)* + } + + impl [<$blueprint_ident Generic>] { + pub const fn generic_index(&self) -> u8 { + *self as u8 + } + } + )? + + #[derive(Debug, Clone, Copy, Sbor, PartialEq, Eq, Hash)] + pub enum [<$blueprint_ident Feature>] { + $($feature_ident,)* + } + + impl BlueprintFeature for [<$blueprint_ident Feature>] { + fn feature_name(&self) -> &'static str { + match *self { + $( + Self::$feature_ident => stringify!($feature_property_name), + )* + } + } + } + #[derive(Debug, Clone, Copy, Sbor, PartialEq, Eq, Hash)] pub struct [<$blueprint_ident FeatureSet>] { $(pub [<$feature_property_name>]: bool,)* @@ -373,21 +405,6 @@ macro_rules! declare_native_blueprint_state { } } - #[derive(Debug, Clone, Copy, Sbor, PartialEq, Eq, Hash)] - pub enum [<$blueprint_ident Feature>] { - $($feature_ident,)* - } - - impl BlueprintFeature for [<$blueprint_ident Feature>] { - fn feature_name(&self) -> &'static str { - match *self { - $( - Self::$feature_ident => stringify!($feature_property_name), - )* - } - } - } - //--------------------------------- // Typed Substate - Keys and Values //--------------------------------- @@ -477,19 +494,21 @@ macro_rules! declare_native_blueprint_state { ) -> BlueprintStateSchemaInit { let mut fields = vec![]; $( - // TODO(David) - Implement instance schema fields.push(FieldSchema { - field: TypeRef::Static( - type_aggregator.add_child_type_and_descendents::<[<$blueprint_ident $field_ident FieldPayload>]>() + field: map_type_ref!( + $blueprint_ident, + type_aggregator, + $field_type, + [<$blueprint_ident $field_ident FieldPayload>], ), condition: $field_condition, }); )* let mut collections = vec![]; $( - // TODO(David) - Implement instance schema collections.push(map_collection_schema!( $collection_type, + $blueprint_ident, type_aggregator, $collection_key_type, [<$blueprint_ident $collection_ident KeyPayload>], @@ -512,7 +531,6 @@ macro_rules! declare_native_blueprint_state { /// Used for initializing blueprint state. /// /// Note - this doesn't support: - /// * Instance schemas / generics (yet) /// * IndexEntries (because the underlying new_object API doesn't support them) /// > these panic at create time #[derive(Debug, Default)] @@ -785,14 +803,14 @@ mod helper_macros { ident_core: $ident_core:ident, $(#[$attributes:meta])* struct $content_type_name:ident = { - kind: Instance, - ident: $instance_ident:ident + kind: Generic, + ident: $generic_ident:ident $(,)? } ) => { $(#[$attributes])* - #[sbor(transparent)] - pub struct $content_type_name<$instance_ident = ScryptoValue>(pub $instance_ident); + #[sbor(transparent, categorize_types = "")] + pub struct $content_type_name<$generic_ident: ScryptoEncode + ScryptoDecode = ScryptoValue>(pub $generic_ident); }; // TODO - Add support for some kind of StaticMultiVersioned type here } @@ -832,28 +850,43 @@ mod helper_macros { pub(crate) use generate_system_substate_type_alias; macro_rules! map_collection_schema { - (KeyValue, $aggregator:ident, $key_type:tt, $key_content_alias:ident, $value_type:tt, $value_content_alias:ident, $allow_ownership:expr$(,)?) => { + (KeyValue, $blueprint_ident:ident, $aggregator:ident, $key_type:tt, $key_payload_alias:ident, $value_type:tt, $value_payload_alias:ident, $allow_ownership:expr$(,)?) => { BlueprintCollectionSchema::KeyValueStore(BlueprintKeyValueSchema { - key: map_type_ref!($aggregator, $key_type, $key_content_alias), - value: map_type_ref!($aggregator, $value_type, $value_content_alias), + key: map_type_ref!($blueprint_ident, $aggregator, $key_type, $key_payload_alias), + value: map_type_ref!( + $blueprint_ident, + $aggregator, + $value_type, + $value_payload_alias + ), allow_ownership: $allow_ownership, }) }; - (Index, $aggregator:ident, $key_type:tt, $key_content_alias:ident, $value_type:tt, $value_content_alias:ident, $allow_ownership:expr$(,)?) => { + (Index, $blueprint_ident:ident, $aggregator:ident, $key_type:tt, $key_payload_alias:ident, $value_type:tt, $value_payload_alias:ident, $allow_ownership:expr$(,)?) => { BlueprintCollectionSchema::Index(BlueprintKeyValueSchema { - key: map_type_ref!($aggregator, $key_type, $key_content_alias), - value: map_type_ref!($aggregator, $value_type, $value_content_alias), + key: map_type_ref!($blueprint_ident, $aggregator, $key_type, $key_payload_alias), + value: map_type_ref!( + $blueprint_ident, + $aggregator, + $value_type, + $value_payload_alias + ), allow_ownership: $allow_ownership, }) }; - (SortedIndex, $aggregator:ident, $key_type:tt, $key_content_alias:ident, $value_type:tt, $value_content_alias:ident, $allow_ownership:expr$(,)?) => { + (SortedIndex, $blueprint_ident:ident, $aggregator:ident, $key_type:tt, $key_payload_alias:ident, $value_type:tt, $value_payload_alias:ident, $allow_ownership:expr$(,)?) => { BlueprintCollectionSchema::SortedIndex(BlueprintKeyValueSchema { - key: map_type_ref!($aggregator, $key_type, $key_content_alias), - value: map_type_ref!($aggregator, $value_type, $value_content_alias), + key: map_type_ref!($blueprint_ident, $aggregator, $key_type, $key_payload_alias), + value: map_type_ref!( + $blueprint_ident, + $aggregator, + $value_type, + $value_payload_alias + ), allow_ownership: $allow_ownership, }) }; - ($unknown_system_substate_type:ident, $aggregator:ident, $collection_key_type:tt, $collection_value_type:tt, $collection_allow_ownership:expr$(,)?) => { + ($unknown_system_substate_type:ident, $blueprint_ident:ident, $aggregator:ident, $key_type:tt, $key_payload_alias:ident, $value_type:tt, $value_payload_alias:ident, $allow_ownership:expr$(,)?) => { compile_error!(concat!( "Unrecognized system collection substate type: `", stringify!($unknown_system_substate_type), @@ -867,36 +900,41 @@ mod helper_macros { macro_rules! map_type_ref { ( + $blueprint_ident:ident, $aggregator:ident, { kind: StaticSingleVersioned $(,)? }, - $content_alias:ident$(,)? + $payload_alias:ident$(,)? ) => { - TypeRef::Static($aggregator.add_child_type_and_descendents::<$content_alias>()) + TypeRef::Static($aggregator.add_child_type_and_descendents::<$payload_alias>()) }; ( + $blueprint_ident:ident, $aggregator:ident, { kind: Static, the_type: $static_type:ty $(,)? }, - $content_alias:ident$(,)? + $payload_alias:ident$(,)? ) => { - TypeRef::Static($aggregator.add_child_type_and_descendents::<$content_alias>()) + TypeRef::Static($aggregator.add_child_type_and_descendents::<$payload_alias>()) }; ( + $blueprint_ident:ident, $aggregator:ident, { - kind: Instance, - ident: $instance_ident:ident + kind: Generic, + ident: $generic_ident:ident $(,)? }, - $content_alias:ident$(,)? + $payload_alias:ident$(,)? ) => { - compile_error!("Instance schemas not yet supported - close though!") + paste::paste! { + TypeRef::Generic([<$blueprint_ident Generic>]::$generic_ident.generic_index()) + } }; // TODO - Add support for some kind of StaticMultiVersioned type here } @@ -990,8 +1028,13 @@ mod tests { declare_native_blueprint_state! { blueprint_ident: TestBlueprint, blueprint_snake_case: package, + generics: { + abc: { + ident: Abc, + description: "Some generic parameter called Abc", + } + }, features: {}, - instance_schema_types: {}, fields: { royalty: { ident: Royalty, @@ -999,6 +1042,14 @@ mod tests { kind: StaticSingleVersioned, }, condition: Condition::Always, + }, + some_generic_field: { + ident: GenericField, + field_type: { + kind: Generic, + ident: Abc, + }, + condition: Condition::Always, } }, collections: { diff --git a/radix-engine/src/blueprints/package/substates.rs b/radix-engine/src/blueprints/package/substates.rs index c88f27ea26d..edab0488f87 100644 --- a/radix-engine/src/blueprints/package/substates.rs +++ b/radix-engine/src/blueprints/package/substates.rs @@ -10,7 +10,6 @@ declare_native_blueprint_state! { description: "Enables the package royalty substate", } }, - instance_schema_types: {}, fields: { royalty: { ident: RoyaltyAccumulator, diff --git a/sbor-derive-common/src/utils.rs b/sbor-derive-common/src/utils.rs index c58781ca1e7..82d7bcce2d5 100644 --- a/sbor-derive-common/src/utils.rs +++ b/sbor-derive-common/src/utils.rs @@ -371,13 +371,18 @@ fn get_child_types(attributes: &[Attribute], existing_generics: &Generics) -> Re parse_comma_separated_types(&comma_separated_types) } -fn get_types_requiring_categorize_bound( +fn get_types_requiring_categorize_bound_for_encode_and_decode( attributes: &[Attribute], child_types: &[Type], ) -> Result> { let Some(comma_separated_types) = get_sbor_attribute_string_value(attributes, "categorize_types")? else { // A categorize bound is only needed for child types when you have a collection, eg Vec // But if no explicit "categorize_types" is set, we assume all are needed. + // > Note as of Aug 2023: + // This is perhaps the wrong call. + // In future, I'd suggest: + // - Change this to assume none, and add categorize_child_types_for_encode if needed. + // - Add separate categorize_child_types_for_categorize - and also default to not needed. // These can be removed / overriden with the "categorize_types" field return Ok(child_types.to_owned()); }; @@ -572,7 +577,8 @@ pub fn build_decode_generics<'a>( let decoder_generic: Path = parse_str(&decoder_label)?; let child_types = get_child_types(&attributes, &impl_generics)?; - let categorize_types = get_types_requiring_categorize_bound(&attributes, &child_types)?; + let categorize_types = + get_types_requiring_categorize_bound_for_encode_and_decode(&attributes, &child_types)?; let mut where_clause = where_clause.cloned(); if child_types.len() > 0 || categorize_types.len() > 0 { @@ -637,7 +643,8 @@ pub fn build_encode_generics<'a>( let encoder_generic: Path = parse_str(&encoder_label)?; let child_types = get_child_types(&attributes, &impl_generics)?; - let categorize_types = get_types_requiring_categorize_bound(&attributes, &child_types)?; + let categorize_types = + get_types_requiring_categorize_bound_for_encode_and_decode(&attributes, &child_types)?; let mut where_clause = where_clause.cloned(); if child_types.len() > 0 || categorize_types.len() > 0 { From 6c6c0772316f2f1155eecee5462307bdff4c6d6a Mon Sep 17 00:00:00 2001 From: David Edey Date: Thu, 17 Aug 2023 16:28:45 +0100 Subject: [PATCH 24/28] tweak: Output separate method for substate flashing --- .../models/native_blueprint_state_macro.rs | 143 ++++++++++-------- .../src/blueprints/package/package.rs | 8 +- simulator/src/resim/cmd_publish.rs | 10 +- 3 files changed, 90 insertions(+), 71 deletions(-) diff --git a/radix-engine/src/blueprints/models/native_blueprint_state_macro.rs b/radix-engine/src/blueprints/models/native_blueprint_state_macro.rs index 516d94760bb..6ce385465cb 100644 --- a/radix-engine/src/blueprints/models/native_blueprint_state_macro.rs +++ b/radix-engine/src/blueprints/models/native_blueprint_state_macro.rs @@ -617,70 +617,6 @@ macro_rules! declare_native_blueprint_state { Ok((field_values_vec, collection_entries)) } - /// This is used mostly for flashing - pub fn into_kernel_main_partitions(self, feature_checks: [<$blueprint_ident FeatureChecks>]) -> Result { - // PartitionNumber => SubstateKey => IndexedScryptoValue - let mut partitions: NodeSubstates = BTreeMap::new(); - let (mut field_values, mut kv_entries) = self.into_system_substates(feature_checks)?; - - // Fields - { - let mut field_partition_substates = BTreeMap::new(); - for (field_index, field_value) in field_values { - field_partition_substates.insert( - SubstateKey::Field(field_index), - IndexedScryptoValue::from_typed(&field_value), - ); - } - partitions.insert( - [<$blueprint_ident Partition>]::Field.as_main_partition(), - field_partition_substates, - ); - } - - // Each Collection - let mut collection_index = 0u8; - $({ - let collection_kv_entries = kv_entries.remove(&collection_index).unwrap(); - let collection_partition = [<$blueprint_ident Partition>]::[<$collection_ident $collection_type>]; - let collection_partition_substates = collection_kv_entries - .into_iter() - .filter_map(|(key_bytes, kv_entry)| { - let substate = match kv_entry { - KVEntry { value: Some(value_bytes), locked: false } => { - KeyValueEntrySubstate::entry( - scrypto_decode::(&value_bytes).unwrap() - ) - } - KVEntry { value: Some(value_bytes), locked: true } => { - KeyValueEntrySubstate::locked_entry( - scrypto_decode::(&value_bytes).unwrap() - ) - } - KVEntry { value: None, locked: true } => { - KeyValueEntrySubstate::locked_empty_entry() - } - KVEntry { value: None, locked: false } => { - return None; - } - }; - - Some(( - SubstateKey::Map(key_bytes), - IndexedScryptoValue::from_typed(&substate) - )) - }) - .collect(); - partitions.insert( - collection_partition.as_main_partition(), - collection_partition_substates, - ); - collection_index += 1; - })* - - Ok(partitions) - } - pub fn into_new_object>( self, api: &mut Y, @@ -704,6 +640,85 @@ macro_rules! declare_native_blueprint_state { } } + //------------- + // Flashing + //------------- + + /// This method converts the state init into the node substates, + /// at a kernel / flash level of abstraction. + /// + /// This can further be mapped to level 0 with a call to: + /// `.into_database_updates::(&node_id)` + /// + /// We decided to have this as a separate function away from + /// the impl of [<$blueprint_ident StateInit>], as it's conceptually + /// a helper method at a different abstraction level. + pub fn []( + state_init: [<$blueprint_ident StateInit>], + feature_checks: [<$blueprint_ident FeatureChecks>], + ) -> Result { + // PartitionNumber => SubstateKey => IndexedScryptoValue + let mut partitions: NodeSubstates = BTreeMap::new(); + let (mut field_values, mut kv_entries) = state_init.into_system_substates(feature_checks)?; + + // Fields + { + let mut field_partition_substates = BTreeMap::new(); + for (field_index, field_value) in field_values { + field_partition_substates.insert( + SubstateKey::Field(field_index), + IndexedScryptoValue::from_typed(&field_value), + ); + } + partitions.insert( + [<$blueprint_ident Partition>]::Field.as_main_partition(), + field_partition_substates, + ); + } + + // Each Collection + let mut collection_index = 0u8; + $({ + let collection_kv_entries = kv_entries.remove(&collection_index).unwrap(); + let collection_partition = [<$blueprint_ident Partition>]::[<$collection_ident $collection_type>]; + let collection_partition_substates = collection_kv_entries + .into_iter() + .filter_map(|(key_bytes, kv_entry)| { + let substate = match kv_entry { + KVEntry { value: Some(value_bytes), locked: false } => { + KeyValueEntrySubstate::entry( + scrypto_decode::(&value_bytes).unwrap() + ) + } + KVEntry { value: Some(value_bytes), locked: true } => { + KeyValueEntrySubstate::locked_entry( + scrypto_decode::(&value_bytes).unwrap() + ) + } + KVEntry { value: None, locked: true } => { + KeyValueEntrySubstate::locked_empty_entry() + } + KVEntry { value: None, locked: false } => { + return None; + } + }; + + Some(( + SubstateKey::Map(key_bytes), + IndexedScryptoValue::from_typed(&substate) + )) + }) + .collect(); + partitions.insert( + collection_partition.as_main_partition(), + collection_partition_substates, + ); + collection_index += 1; + })* + + Ok(partitions) + } + //------------- // State API (TODO!) //------------- diff --git a/radix-engine/src/blueprints/package/package.rs b/radix-engine/src/blueprints/package/package.rs index 1519fbf78f3..43fc2376edd 100644 --- a/radix-engine/src/blueprints/package/package.rs +++ b/radix-engine/src/blueprints/package/package.rs @@ -509,9 +509,11 @@ pub fn create_bootstrap_package_partitions( // MAIN PARTITIONS: //----------------- - let mut partitions = package_state_init - .into_kernel_main_partitions(own_features.into()) - .expect("Expected that correct substates are present for given features"); + let mut partitions = map_package_state_into_main_partition_node_substate_flash( + package_state_init, + own_features.into(), + ) + .expect("Expected that correct substates are present for given features"); //------------------- // MODULE PARTITIONS: diff --git a/simulator/src/resim/cmd_publish.rs b/simulator/src/resim/cmd_publish.rs index d3d85ab6ae5..7eaff50a57d 100644 --- a/simulator/src/resim/cmd_publish.rs +++ b/simulator/src/resim/cmd_publish.rs @@ -186,10 +186,12 @@ impl Publish { ); } - let database_updates = package_state_to_set - .into_kernel_main_partitions(FeatureChecks::None) - .unwrap() - .into_database_updates::(&node_id); + let database_updates = map_package_state_into_main_partition_node_substate_flash( + package_state_to_set, + FeatureChecks::None, + ) + .unwrap() + .into_database_updates::(&node_id); substate_db.commit(&database_updates); writeln!(out, "Package updated!").map_err(Error::IOError)?; From c407711f3d0999173f9f5109e79854d0bb534222 Mon Sep 17 00:00:00 2001 From: David Edey Date: Fri, 18 Aug 2023 21:57:18 +0100 Subject: [PATCH 25/28] tweak: Updates to the content traits --- .../src/data/scrypto/definitions.rs | 2 + radix-engine/src/blueprints/models/content.rs | 194 +++++- .../models/native_blueprint_state_macro.rs | 550 ++++++++++-------- .../src/blueprints/package/package.rs | 8 +- radix-engine/src/system/system.rs | 8 +- radix-engine/src/system/system_db_reader.rs | 4 +- radix-engine/src/vm/vm.rs | 5 +- sbor/src/versioned.rs | 8 +- scrypto-unit/src/test_runner.rs | 4 +- simulator/src/ledger/dumper.rs | 2 +- simulator/src/resim/mod.rs | 2 +- 11 files changed, 492 insertions(+), 295 deletions(-) diff --git a/radix-engine-common/src/data/scrypto/definitions.rs b/radix-engine-common/src/data/scrypto/definitions.rs index 6290d825057..7b1cc61e672 100644 --- a/radix-engine-common/src/data/scrypto/definitions.rs +++ b/radix-engine-common/src/data/scrypto/definitions.rs @@ -8,6 +8,8 @@ pub type ScryptoDecoder<'a> = VecDecoder<'a, ScryptoCustomValueKind>; pub type ScryptoTraverser<'a> = VecTraverser<'a, ScryptoCustomTraversal>; pub type ScryptoValueKind = ValueKind; pub type ScryptoValue = Value; +pub type RawScryptoValue<'a> = RawValue<'a, ScryptoCustomExtension>; +pub type RawScryptoPayload<'a> = RawPayload<'a, ScryptoCustomExtension>; // The following trait "aliases" are to be used in parameters. // diff --git a/radix-engine/src/blueprints/models/content.rs b/radix-engine/src/blueprints/models/content.rs index 77b92681910..bfd2fd8f07a 100644 --- a/radix-engine/src/blueprints/models/content.rs +++ b/radix-engine/src/blueprints/models/content.rs @@ -1,39 +1,195 @@ use crate::internal_prelude::*; -pub trait FieldContent>: Sized { - fn into_locked_substate(self) -> FieldSubstate { - FieldSubstate::new_locked_field(self.into()) +macro_rules! declare_payload_new_type { + ( + content_trait: $content_trait:ident, + payload_trait: $payload_trait:ident, + $(#[$attributes:meta])* + $vis:vis struct $payload_type_name:ident + $(< $( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? $( = $deflt:tt)? ),+ >)? + ($content_type:ty)$(;)? + ) => { + $(#[$attributes])* + #[sbor(transparent, categorize_types = "")] + $vis struct $payload_type_name + $(< $( $lt $( : $clt $(+ $dlt )* )? $( = $deflt)? ),+ >)? + (pub $content_type); + + impl $(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? + core::convert::From<$content_type> + for $payload_type_name $(< $( $lt ),+ >)? + { + fn from(value: $content_type) -> Self { + Self(value) + } + } + + impl $(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? + core::convert::AsRef<$content_type> + for $payload_type_name $(< $( $lt ),+ >)? + { + fn as_ref(&self) -> &$content_type { + &self.0 + } + } + + impl $(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? + core::convert::AsMut<$content_type> + for $payload_type_name $(< $( $lt ),+ >)? + { + fn as_mut(&mut self) -> &mut $content_type { + &mut self.0 + } + } + + impl $(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? + $payload_trait + for $payload_type_name $(< $( $lt ),+ >)? + { + type Content = $content_type; + + fn into_content(self) -> Self::Content { + self.0 + } + } + + impl $(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? + $content_trait<$payload_type_name$(< $( $lt ),+ >)?> + for $payload_type_name $(< $( $lt ),+ >)? + { + fn into_payload(self) -> $payload_type_name$(< $( $lt ),+ >)? { + self + } + } + + impl $(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? + $content_trait<$payload_type_name$(< $( $lt ),+ >)?> + for $content_type + { + fn into_payload(self) -> $payload_type_name$(< $( $lt ),+ >)? { + $payload_type_name(self) + } + } + } +} +#[allow(unused)] +pub(crate) use declare_payload_new_type; + +pub trait FieldPayload: AsRef + AsMut + From { + type Content: FieldContent; + + fn into_content(self) -> Self::Content; + + fn from_content>(content: T) -> Self + where + Self: From, + { + content.into_payload() + } +} + +pub trait FieldContent: Sized { + fn into_payload(self) -> Payload; + + fn into_locked_substate(self) -> FieldSubstate { + FieldSubstate::new_locked_field(self.into_payload()) + } + + fn into_mutable_substate(self) -> FieldSubstate { + FieldSubstate::new_field(self.into_payload()) + } +} + +pub trait KeyPayload: AsRef + AsMut + From { + type Content: KeyContent; + + fn into_content(self) -> Self::Content; + + fn from_content>(content: T) -> Self + where + Self: From, + { + content.into_key() + } +} + +pub trait KeyContent: Sized { + fn into_payload(self) -> Payload; + + fn into_key(self) -> Payload { + self.into_payload() } +} + +pub trait KeyValueEntryPayload: + AsRef + AsMut + From +{ + type Content: KeyValueEntryContent; + + fn into_content(self) -> Self::Content; - fn into_mutable_substate(self) -> FieldSubstate { - FieldSubstate::new_field(self.into()) + fn from_content>(content: T) -> Self + where + Self: From, + { + content.into_payload() } } -pub trait KeyContent>: Sized { - fn into_key(self) -> KeyPayload { - self.into() +pub trait KeyValueEntryContent: Sized { + fn into_payload(self) -> Payload; + + fn into_locked_substate(self) -> KeyValueEntrySubstate { + KeyValueEntrySubstate::entry(self.into_payload()) + } + + fn into_mutable_substate(self) -> KeyValueEntrySubstate { + KeyValueEntrySubstate::locked_entry(self.into_payload()) } } -pub trait KeyValueEntryContent>: Sized { - fn into_locked_substate(self) -> KeyValueEntrySubstate { - KeyValueEntrySubstate::entry(self.into()) +pub trait IndexEntryPayload: + AsRef + AsMut + From +{ + type Content: IndexEntryContent; + + fn into_content(self) -> Self::Content; + + fn from_content>(content: T) -> Self + where + Self: From, + { + content.into_payload() } +} + +pub trait IndexEntryContent: Sized { + fn into_payload(self) -> Payload; - fn into_mutable_substate(self) -> KeyValueEntrySubstate { - KeyValueEntrySubstate::locked_entry(self.into()) + fn into_substate(self) -> Payload { + self.into_payload() } } -pub trait IndexEntryContent>: Sized { - fn into_substate(self) -> EntryPayload { - self.into() +pub trait SortedIndexEntryPayload: + AsRef + AsMut + From +{ + type Content: SortedIndexEntryContent; + + fn into_content(self) -> Self::Content; + + fn from_content>(content: T) -> Self + where + Self: From, + { + content.into_payload() } } -pub trait SortedIndexEntryContent>: Sized { - fn into_substate(self) -> EntryPayload { - self.into() +pub trait SortedIndexEntryContent: Sized { + fn into_payload(self) -> Payload; + + fn into_substate(self) -> Payload { + self.into_payload() } } diff --git a/radix-engine/src/blueprints/models/native_blueprint_state_macro.rs b/radix-engine/src/blueprints/models/native_blueprint_state_macro.rs index 6ce385465cb..a1e50bd613d 100644 --- a/radix-engine/src/blueprints/models/native_blueprint_state_macro.rs +++ b/radix-engine/src/blueprints/models/native_blueprint_state_macro.rs @@ -145,6 +145,7 @@ macro_rules! declare_native_blueprint_state { // > Set up the FieldContent trait for anything which can be resolved into the field payload generate_content_type!( content_trait: FieldContent, + payload_trait: FieldPayload, ident_core: [<$blueprint_ident $field_ident>], #[derive(Debug, PartialEq, Eq, ScryptoSbor)] struct [<$blueprint_ident $field_ident FieldPayload>] = $field_type @@ -167,6 +168,7 @@ macro_rules! declare_native_blueprint_state { // > Set up the KeyContent traits for anything which can be resolved into a key generate_content_type!( content_trait: KeyContent, + payload_trait: KeyPayload, ident_core: [<$blueprint_ident $collection_ident KeyInner>], #[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, ScryptoSbor)] #[sbor(transparent_name)] @@ -190,13 +192,18 @@ macro_rules! declare_native_blueprint_state { } // Values - // > Set up Versioned types (if relevant). Assumes __ValueV1 exists and then creates - // - Versioned__Value - // - __Value (alias for __ValueV1) - // > Set up the (transparent) _ValueContent new type for the value content - // > Set up the _EntryContent traits + // > If relevant, set up Versioned types, which: + // - Assumes [BlueprintCollection]V1 exists + // - Creates Versioned[BlueprintCollection] enum + // - Creates [BlueprintCollection] as a "latest" type alias for [BlueprintCollection]V1 + // > Set up the [BlueprintCollection]EntryPayload transparent new type for the value content + // > Set up the [Collectiontype]EntryContent::<[BlueprintCollection]EntryPayload> trait for: + // - The [BlueprintCollection] if it exists + // - The Versioned[BlueprintCollection] if it exists + // - The static content type, if it exists generate_content_type!( content_trait: [<$collection_type EntryContent>], + payload_trait: [<$collection_type EntryPayload>], ident_core: [<$blueprint_ident $collection_ident>], #[derive(Debug, PartialEq, Eq, ScryptoSbor)] struct [<$blueprint_ident $collection_ident EntryPayload>] = $collection_value_type @@ -208,8 +215,49 @@ macro_rules! declare_native_blueprint_state { ); )* + //------------------------------------- + // System - Generate schema definitions + //------------------------------------- + pub struct [<$blueprint_ident StateSchemaInit>]; + + impl [<$blueprint_ident StateSchemaInit>] { + pub fn create_schema_init( + type_aggregator: &mut TypeAggregator, + ) -> BlueprintStateSchemaInit { + let mut fields = vec![]; + $( + fields.push(FieldSchema { + field: map_type_ref!( + $blueprint_ident, + type_aggregator, + $field_type, + [<$blueprint_ident $field_ident FieldPayload>], + ), + condition: $field_condition, + }); + )* + let mut collections = vec![]; + $( + collections.push(map_collection_schema!( + $collection_type, + $blueprint_ident, + type_aggregator, + $collection_key_type, + [<$blueprint_ident $collection_ident KeyPayload>], + $collection_value_type, + [<$blueprint_ident $collection_ident EntryPayload>], + $collection_allow_ownership + )); + )* + BlueprintStateSchemaInit { + fields, + collections, + } + } + } + //-------------------------------------------------------- - // Node Layout (to replace node_layout.rs) + // System - Fields, Collections, Features and Generics //-------------------------------------------------------- #[repr(u8)] #[derive(Debug, Clone, Copy, Sbor, PartialEq, Eq, Hash, PartialOrd, Ord, FromRepr)] @@ -248,93 +296,6 @@ macro_rules! declare_native_blueprint_state { } } - /// A list of all logical (real) and physical (mapped) partitions for the - /// $blueprint_ident blueprint. - /// - /// Note: In future, we could add a separate LogicalPartition enum, to - /// not include physical partitions - however it's very hard to do in - /// declarative macro land, because enum variants can't be - /// macro invocations (to eg filter out the physical partition types) - #[repr(u8)] - #[derive(Debug, Clone, Copy, Sbor, PartialEq, Eq, Hash, PartialOrd, Ord)] - pub enum [<$blueprint_ident Partition>] { - Field, - $([<$collection_ident $collection_type>],)* - } - - impl [<$blueprint_ident Partition>] { - pub const fn description(&self) -> PartitionDescription { - // NOTE: This should really be a fixed mapping - but it's hard to do with declarative macros - // It's a const function, so might hopefully be compiled away - let mut module_partition_offset = 0; - if (*self as u8) == (Self::Field as u8) { - return PartitionDescription::Logical(PartitionOffset(module_partition_offset)); - } - $( - let mapped_physical_partition = extract_collection_option!( - mapped_physical_partition, - $($collection_options)? - ); - let current_partition_description = match mapped_physical_partition { - Some(physical_partition) => { - PartitionDescription::Physical(physical_partition) - }, - None => { - module_partition_offset += 1; - PartitionDescription::Logical(PartitionOffset(module_partition_offset)) - }, - }; - if (*self as u8) == (Self::[<$collection_ident $collection_type>] as u8) { - return current_partition_description; - } - )* - panic!("Partition somehow did not match for calculating its description") - } - - pub const fn as_main_partition(&self) -> PartitionNumber { - match self.description() { - PartitionDescription::Physical(partition_num) => partition_num, - PartitionDescription::Logical(offset) => { - match MAIN_BASE_PARTITION.at_offset(offset) { - // This map works around unwrap/expect on Option not being const - Some(x) => x, - None => panic!("Offset larger than allowed value") - } - } - } - } - } - - impl TryFrom<[<$blueprint_ident Partition>]> for PartitionOffset { - type Error = (); - - fn try_from(value: [<$blueprint_ident Partition>]) -> Result { - match value.description() { - PartitionDescription::Logical(offset) => Ok(offset), - PartitionDescription::Physical(partition_num) => Err(()), - } - } - } - - impl TryFrom for [<$blueprint_ident Partition>] { - type Error = (); - - fn try_from(offset: PartitionOffset) -> Result { - // NOTE: This should really be a fixed mapping - but it's hard to do with declarative macros - // Hopefully this will be compiled away because Partition::description is const - let description = PartitionDescription::Logical(offset); - if description == Self::Field.description() { - return Ok(Self::Field); - } - $( - if description == Self::[<$collection_ident $collection_type>].description() { - return Ok(Self::[<$collection_ident $collection_type>]); - } - )* - Err(()) - } - } - #[repr(u8)] #[derive(Debug, Clone, Copy, Sbor, PartialEq, Eq, Hash, PartialOrd, Ord, FromRepr)] pub enum [<$blueprint_ident Collection>] { @@ -405,128 +366,31 @@ macro_rules! declare_native_blueprint_state { } } - //--------------------------------- - // Typed Substate - Keys and Values - //--------------------------------- - #[derive(Debug, Clone)] - /// All the SubstateKeys for all logical (real) and physical (mapped) - /// partitions for the $blueprint_ident blueprint. - /// - /// Note: In future, we could remove keys for physical partitions from this, - /// as they can't be persisted as-is - however it's very hard to do in - /// declarative macro land, because enum variants can't be - /// macro invocations (to eg filter out the physical partition types) - pub enum [<$blueprint_ident TypedSubstateKey>] { - Fields([<$blueprint_ident Field>]), - $([<$collection_ident $collection_type Entries>]([<$blueprint_ident $collection_ident SubstateKey>]),)* - } - - impl [<$blueprint_ident TypedSubstateKey>] { - pub fn for_key_in_partition(partition: &[<$blueprint_ident Partition>], substate_key: &SubstateKey) -> Result { - let key = match partition { - [<$blueprint_ident Partition>]::Field => { - [<$blueprint_ident TypedSubstateKey>]::Fields( - [<$blueprint_ident Field>]::try_from(substate_key)? - ) - } - $( - [<$blueprint_ident Partition>]::[<$collection_ident $collection_type>] => { - [<$blueprint_ident TypedSubstateKey>]::[<$collection_ident $collection_type Entries>]( - [<$blueprint_ident $collection_ident SubstateKey>]::try_from(substate_key)?, - ) - } - )* - }; - Ok(key) - } - } - - #[derive(Debug)] - pub enum [<$blueprint_ident TypedFieldSubstateValue>] { - $($field_ident([<$blueprint_ident $field_ident FieldSubstate>]),)* - } + //-------------------------------------- + // Application - Typed State API (TODO!) + //-------------------------------------- - #[derive(Debug)] - /// All the Substate values for all logical (real) and physical (mapped) - /// partitions for the $blueprint_ident blueprint. - /// - /// Note: In future, we could remove values for physical partitions from this, - /// as they can't be persisted as-is - however it's very hard to do in - /// declarative macro land, because enum variants can't be - /// macro invocations (to eg filter out the physical partition types) - pub enum [<$blueprint_ident TypedSubstateValue>] { - Field([<$blueprint_ident TypedFieldSubstateValue>]), - $([<$collection_ident $collection_type>]([<$blueprint_ident $collection_ident EntrySubstate>]),)* + pub struct [<$blueprint_ident StateApi>]<'a, Y: ClientApi> { + api: &'a mut Y, } - impl [<$blueprint_ident TypedSubstateValue>] { - pub fn from_key_and_data(key: &[<$blueprint_ident TypedSubstateKey>], data: &[u8]) -> Result { - let substate_value = match key { - // Fields - $( - [<$blueprint_ident TypedSubstateKey>]::Fields([<$blueprint_ident Field>]::$field_ident) => { - [<$blueprint_ident TypedSubstateValue>]::Field( - [<$blueprint_ident TypedFieldSubstateValue>]::$field_ident(scrypto_decode(data)?) - ) - } - )* - // Collections - $( - [<$blueprint_ident TypedSubstateKey>]::[<$collection_ident $collection_type Entries>](_) => { - [<$blueprint_ident TypedSubstateValue>]::[<$collection_ident $collection_type>]( - scrypto_decode(data)? - ) - } - )* - }; - Ok(substate_value) + impl<'a, Y: ClientApi> [<$blueprint_ident StateApi>]<'a, Y> { + pub fn with(client_api: &'a mut Y) -> Self { + Self { + api: client_api, + } } } - //---------------------- - // Schema - //---------------------- - pub struct [<$blueprint_ident StateSchemaInit>]; - - impl [<$blueprint_ident StateSchemaInit>] { - pub fn create_schema_init( - type_aggregator: &mut TypeAggregator, - ) -> BlueprintStateSchemaInit { - let mut fields = vec![]; - $( - fields.push(FieldSchema { - field: map_type_ref!( - $blueprint_ident, - type_aggregator, - $field_type, - [<$blueprint_ident $field_ident FieldPayload>], - ), - condition: $field_condition, - }); - )* - let mut collections = vec![]; - $( - collections.push(map_collection_schema!( - $collection_type, - $blueprint_ident, - type_aggregator, - $collection_key_type, - [<$blueprint_ident $collection_ident KeyPayload>], - $collection_value_type, - [<$blueprint_ident $collection_ident EntryPayload>], - $collection_allow_ownership - )); - )* - BlueprintStateSchemaInit { - fields, - collections, - } + impl<'a, Y: ClientApi<$crate::errors::RuntimeError>> From<&'a mut Y> for [<$blueprint_ident StateApi>]<'a, Y> { + fn from(value: &'a mut Y) -> Self { + Self::with(value) } } - //---------------------- - // Object Initialization - //---------------------- + //-------------------------------- + // System - Object Initialization + //-------------------------------- /// Used for initializing blueprint state. /// @@ -562,7 +426,7 @@ macro_rules! declare_native_blueprint_state { self.$field_property_name.is_some(), )?; if let Some(field) = self.$field_property_name { - let field_content = scrypto_encode(&field.field_content()).unwrap(); + let payload = scrypto_encode(&field.payload()).unwrap(); let locked = match &field.mutability { SubstateMutability::Mutable => true, SubstateMutability::Immutable => false, @@ -570,7 +434,7 @@ macro_rules! declare_native_blueprint_state { field_values.insert( [<$blueprint_ident Field>]::$field_ident.into(), FieldValue { - value: field_content, + value: payload, locked, } ); @@ -640,6 +504,177 @@ macro_rules! declare_native_blueprint_state { } } + + //-------------------------------------------------------- + // System/DB - Node Partitions & Layout + //-------------------------------------------------------- + + /// A list of all logical (real) and physical (mapped) partitions for the + /// $blueprint_ident blueprint. + /// + /// Note: In future, we could add a separate LogicalPartition enum, to + /// not include physical partitions - however it's very hard to do in + /// declarative macro land, because enum variants can't be + /// macro invocations (to eg filter out the physical partition types) + #[repr(u8)] + #[derive(Debug, Clone, Copy, Sbor, PartialEq, Eq, Hash, PartialOrd, Ord)] + pub enum [<$blueprint_ident Partition>] { + Field, + $([<$collection_ident $collection_type>],)* + } + + impl [<$blueprint_ident Partition>] { + pub const fn description(&self) -> PartitionDescription { + // NOTE: This should really be a fixed mapping - but it's hard to do with declarative macros + // It's a const function, so might hopefully be compiled away + let mut module_partition_offset = 0; + if (*self as u8) == (Self::Field as u8) { + return PartitionDescription::Logical(PartitionOffset(module_partition_offset)); + } + $( + let mapped_physical_partition = extract_collection_option!( + mapped_physical_partition, + $($collection_options)? + ); + let current_partition_description = match mapped_physical_partition { + Some(physical_partition) => { + PartitionDescription::Physical(physical_partition) + }, + None => { + module_partition_offset += 1; + PartitionDescription::Logical(PartitionOffset(module_partition_offset)) + }, + }; + if (*self as u8) == (Self::[<$collection_ident $collection_type>] as u8) { + return current_partition_description; + } + )* + panic!("Partition somehow did not match for calculating its description") + } + + pub const fn as_main_partition(&self) -> PartitionNumber { + match self.description() { + PartitionDescription::Physical(partition_num) => partition_num, + PartitionDescription::Logical(offset) => { + match MAIN_BASE_PARTITION.at_offset(offset) { + // This map works around unwrap/expect on Option not being const + Some(x) => x, + None => panic!("Offset larger than allowed value") + } + } + } + } + } + + impl TryFrom<[<$blueprint_ident Partition>]> for PartitionOffset { + type Error = (); + + fn try_from(value: [<$blueprint_ident Partition>]) -> Result { + match value.description() { + PartitionDescription::Logical(offset) => Ok(offset), + PartitionDescription::Physical(partition_num) => Err(()), + } + } + } + + impl TryFrom for [<$blueprint_ident Partition>] { + type Error = (); + + fn try_from(offset: PartitionOffset) -> Result { + // NOTE: This should really be a fixed mapping - but it's hard to do with declarative macros + // Hopefully this will be compiled away because Partition::description is const + let description = PartitionDescription::Logical(offset); + if description == Self::Field.description() { + return Ok(Self::Field); + } + $( + if description == Self::[<$collection_ident $collection_type>].description() { + return Ok(Self::[<$collection_ident $collection_type>]); + } + )* + Err(()) + } + } + + //--------------------------------- + // Typed - Substate Keys and Values + //--------------------------------- + + /// All the SubstateKeys for all logical (real) and physical (mapped) + /// partitions for the $blueprint_ident blueprint. + /// + /// Note: In future, we could remove keys for physical partitions from this, + /// as they can't be persisted as-is - however it's very hard to do in + /// declarative macro land, because enum variants can't be + /// macro invocations (to eg filter out the physical partition types) + #[derive(Debug, Clone)] + pub enum [<$blueprint_ident TypedSubstateKey>] { + Fields([<$blueprint_ident Field>]), + $([<$collection_ident $collection_type Entries>]([<$blueprint_ident $collection_ident SubstateKey>]),)* + } + + impl [<$blueprint_ident TypedSubstateKey>] { + pub fn for_key_in_partition(partition: &[<$blueprint_ident Partition>], substate_key: &SubstateKey) -> Result { + let key = match partition { + [<$blueprint_ident Partition>]::Field => { + [<$blueprint_ident TypedSubstateKey>]::Fields( + [<$blueprint_ident Field>]::try_from(substate_key)? + ) + } + $( + [<$blueprint_ident Partition>]::[<$collection_ident $collection_type>] => { + [<$blueprint_ident TypedSubstateKey>]::[<$collection_ident $collection_type Entries>]( + [<$blueprint_ident $collection_ident SubstateKey>]::try_from(substate_key)?, + ) + } + )* + }; + Ok(key) + } + } + + #[derive(Debug)] + pub enum [<$blueprint_ident TypedFieldSubstateValue>] { + $($field_ident([<$blueprint_ident $field_ident FieldSubstate>]),)* + } + + #[derive(Debug)] + /// All the Substate values for all logical (real) and physical (mapped) + /// partitions for the $blueprint_ident blueprint. + /// + /// Note: In future, we could remove values for physical partitions from this, + /// as they can't be persisted as-is - however it's very hard to do in + /// declarative macro land, because enum variants can't be + /// macro invocations (to eg filter out the physical partition types) + pub enum [<$blueprint_ident TypedSubstateValue>] { + Field([<$blueprint_ident TypedFieldSubstateValue>]), + $([<$collection_ident $collection_type>]([<$blueprint_ident $collection_ident EntrySubstate>]),)* + } + + impl [<$blueprint_ident TypedSubstateValue>] { + pub fn from_key_and_data(key: &[<$blueprint_ident TypedSubstateKey>], data: &[u8]) -> Result { + let substate_value = match key { + // Fields + $( + [<$blueprint_ident TypedSubstateKey>]::Fields([<$blueprint_ident Field>]::$field_ident) => { + [<$blueprint_ident TypedSubstateValue>]::Field( + [<$blueprint_ident TypedFieldSubstateValue>]::$field_ident(scrypto_decode(data)?) + ) + } + )* + // Collections + $( + [<$blueprint_ident TypedSubstateKey>]::[<$collection_ident $collection_type Entries>](_) => { + [<$blueprint_ident TypedSubstateValue>]::[<$collection_ident $collection_type>]( + scrypto_decode(data)? + ) + } + )* + }; + Ok(substate_value) + } + } + //------------- // Flashing //------------- @@ -718,28 +753,6 @@ macro_rules! declare_native_blueprint_state { Ok(partitions) } - - //------------- - // State API (TODO!) - //------------- - - pub struct [<$blueprint_ident StateApi>]<'a, Y: ClientApi> { - api: &'a mut Y, - } - - impl<'a, Y: ClientApi> [<$blueprint_ident StateApi>]<'a, Y> { - pub fn with(client_api: &'a mut Y) -> Self { - Self { - api: client_api, - } - } - } - - impl<'a, Y: ClientApi<$crate::errors::RuntimeError>> From<&'a mut Y> for [<$blueprint_ident StateApi>]<'a, Y> { - fn from(value: &'a mut Y) -> Self { - Self::with(value) - } - } } } } @@ -761,9 +774,10 @@ mod helper_macros { macro_rules! generate_content_type { ( content_trait: $content_trait:ident, + payload_trait: $payload_trait:ident, ident_core: $ident_core:ident, $(#[$attributes:meta])* - struct $content_type_name:ident = { + struct $payload_type_name:ident = { kind: StaticSingleVersioned $(,)? }$(,)? @@ -773,59 +787,81 @@ mod helper_macros { $(#[$attributes])* pub enum [] => $ident_core = [<$ident_core V1>] ); - $(#[$attributes])* - #[sbor(transparent)] - pub struct $content_type_name(pub []); - impl From<[]> for $content_type_name { - fn from(value: []) -> Self { - Self(value) + declare_payload_new_type!( + content_trait: $content_trait, + payload_trait: $payload_trait, + $(#[$attributes])* + pub struct $payload_type_name([]); + ); + + impl HasLatestVersion for $payload_type_name + { + type Latest = <[] as HasLatestVersion>::Latest; + fn into_latest(self) -> Self::Latest { + self.into_content().into_latest() + } + + fn as_latest_ref(&self) -> Option<&Self::Latest> { + self.as_ref().as_latest_ref() } } - impl $content_trait<$content_type_name> for [] {} - // Also add impls from the "latest" type - impl From<$ident_core> for $content_type_name { - fn from(value: $ident_core) -> Self { - Self(value.into()) + + // Now implement other relevant content traits, for: + // > The "latest" type: $ident_core + impl $content_trait<$payload_type_name> for $ident_core { + fn into_payload(self) -> $payload_type_name { + $payload_type_name(self.into()) } } - impl $content_trait<$content_type_name> for $ident_core {} } }; ( content_trait: $content_trait:ident, + payload_trait: $payload_trait:ident, ident_core: $ident_core:ident, $(#[$attributes:meta])* - struct $content_type_name:ident = { + struct $payload_type_name:ident = { kind: Static, the_type: $static_type:ty $(,)? }$(,)? ) => { paste::paste! { - $(#[$attributes])* - #[sbor(transparent)] - pub struct $content_type_name(pub $static_type); - impl From<$static_type> for $content_type_name { - fn from(value: $static_type) -> Self { - Self(value) - } - } - impl $content_trait<$content_type_name> for $static_type {} + declare_payload_new_type!( + content_trait: $content_trait, + payload_trait: $payload_trait, + $(#[$attributes])* + pub struct $payload_type_name($static_type); + ); } }; ( content_trait: $content_trait:ident, + payload_trait: $payload_trait:ident, ident_core: $ident_core:ident, $(#[$attributes:meta])* - struct $content_type_name:ident = { + struct $payload_type_name:ident = { kind: Generic, ident: $generic_ident:ident $(,)? } ) => { - $(#[$attributes])* - #[sbor(transparent, categorize_types = "")] - pub struct $content_type_name<$generic_ident: ScryptoEncode + ScryptoDecode = ScryptoValue>(pub $generic_ident); + paste::paste! { + declare_payload_new_type!( + content_trait: $content_trait, + payload_trait: $payload_trait, + $(#[$attributes])* + pub struct $payload_type_name<$generic_ident: [<$ident_core ContentMarker>] = ScryptoValue>($generic_ident); + ); + // We choose to create an explicit marker trait, as an alternative to a blanket impl + // over ScryptoEncode + ScryptoDecode. Any explicit types can implement this trait. + // This avoids every type getting implementations for every such generic type, + // which would require disambiguation everywhere `to_substate()` is used. + // Anyone needing a type implementing content can use the payload type itself + pub trait [<$ident_core ContentMarker>]: ScryptoEncode + ScryptoDecode {} + impl [<$ident_core ContentMarker>] for ScryptoValue {} + impl [<$ident_core ContentMarker>] for RawScryptoValue<'_> {} + } }; // TODO - Add support for some kind of StaticMultiVersioned type here } diff --git a/radix-engine/src/blueprints/package/package.rs b/radix-engine/src/blueprints/package/package.rs index 43fc2376edd..2b95ebe0300 100644 --- a/radix-engine/src/blueprints/package/package.rs +++ b/radix-engine/src/blueprints/package/package.rs @@ -1134,7 +1134,7 @@ impl PackageRoyaltyNativeBlueprint { let royalty_charge = substate .value - .and_then(|royalty_config| match royalty_config.0.into_latest() { + .and_then(|royalty_config| match royalty_config.into_latest() { PackageRoyaltyConfig::Enabled(royalty_amounts) => { royalty_amounts.get(ident).cloned() } @@ -1154,7 +1154,7 @@ impl PackageRoyaltyNativeBlueprint { let substate: PackageRoyaltyAccumulatorFieldSubstate = api.kernel_read_substate(handle)?.as_typed().unwrap(); - let vault_id = substate.value.0 .0.into_latest().royalty_vault.0; + let vault_id = substate.into_payload().into_latest().royalty_vault.0; let package_address = PackageAddress::new_or_panic(receiver.0); apply_royalty_cost( api, @@ -1188,7 +1188,7 @@ impl PackageRoyaltyNativeBlueprint { )?; let substate: PackageRoyaltyAccumulatorFieldPayload = api.field_read_typed(handle)?; - let bucket = substate.0.into_latest().royalty_vault.take_all(api)?; + let bucket = substate.into_latest().royalty_vault.take_all(api)?; Ok(bucket) } @@ -1277,7 +1277,7 @@ impl PackageAuthNativeBlueprint { api.kernel_close_substate(handle)?; let template = match auth_template.value { - Some(template) => template.0.into_latest(), + Some(template) => template.into_latest(), None => { return Err(RuntimeError::SystemError( SystemError::AuthTemplateDoesNotExist(package_bp_version_id), diff --git a/radix-engine/src/system/system.rs b/radix-engine/src/system/system.rs index ef6180070e6..dd047197338 100644 --- a/radix-engine/src/system/system.rs +++ b/radix-engine/src/system/system.rs @@ -83,9 +83,13 @@ impl FieldSubstate { } } - pub fn field_content(&self) -> &V { + pub fn payload(&self) -> &V { &self.value.0 } + + pub fn into_payload(self) -> V { + self.value.0 + } } pub type KeyValueEntrySubstate = DynSubstate>; @@ -391,7 +395,7 @@ where self.api.kernel_close_substate(handle)?; let definition = match substate.value { - Some(definition) => definition.0.into_latest(), + Some(definition) => definition.into_latest(), None => { return Err(RuntimeError::SystemError( SystemError::BlueprintDoesNotExist(canonical_bp_id), diff --git a/radix-engine/src/system/system_db_reader.rs b/radix-engine/src/system/system_db_reader.rs index 0d8f452fc15..90f663b99de 100644 --- a/radix-engine/src/system/system_db_reader.rs +++ b/radix-engine/src/system/system_db_reader.rs @@ -101,7 +101,7 @@ impl<'a, S: SubstateDatabase> SystemDatabaseReader<'a, S> { blueprints.insert( bp_version_key, - blueprint_definition.value.unwrap().0.into_latest(), + blueprint_definition.value.unwrap().into_latest(), ); } @@ -137,7 +137,7 @@ impl<'a, S: SubstateDatabase> SystemDatabaseReader<'a, S> { &SubstateKey::Map(scrypto_encode(&bp_version_key).unwrap()), )?; - definition.value.map(|v| v.0.into_latest()) + definition.value.map(|v| v.into_latest()) } pub fn fetch_substate( diff --git a/radix-engine/src/vm/vm.rs b/radix-engine/src/vm/vm.rs index 31711c7e2bf..5c1782861a6 100644 --- a/radix-engine/src/vm/vm.rs +++ b/radix-engine/src/vm/vm.rs @@ -71,7 +71,7 @@ impl<'g, W: WasmEngine + 'g, E: NativeVmExtension> SystemCallbackObject for Vm<' .expect(&format!("Vm type not found: {:?}", export)) }; - let output = match vm_type.0.into_latest().vm_type { + let output = match vm_type.into_latest().vm_type { VmType::Native => { let original_code = { let handle = api.kernel_open_substate_with_default( @@ -98,7 +98,7 @@ impl<'g, W: WasmEngine + 'g, E: NativeVmExtension> SystemCallbackObject for Vm<' .kernel_get_system() .callback_obj .native_vm - .create_instance(address, &original_code.0.into_latest().code)?; + .create_instance(address, &original_code.into_latest().code)?; let output = { vm_instance.invoke(export.export_name.as_str(), input, api)? }; output @@ -123,7 +123,6 @@ impl<'g, W: WasmEngine + 'g, E: NativeVmExtension> SystemCallbackObject for Vm<' instrumented_code .value .expect(&format!("Instrumented code not found: {:?}", export)) - .0 .into_latest() }; diff --git a/sbor/src/versioned.rs b/sbor/src/versioned.rs index 74ce00d2032..efeb426cf88 100644 --- a/sbor/src/versioned.rs +++ b/sbor/src/versioned.rs @@ -35,14 +35,14 @@ macro_rules! define_single_versioned { ( $(#[$attributes:meta])* $vis:vis enum $name:ident - $(< $( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+ >)? + $(< $( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? $( = $deflt:tt)? ),+ >)? => $latest_version_alias:ty = $latest_version_type:ty ) => { $crate::define_versioned!( $(#[$attributes])* $vis enum $name - $(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? + $(< $( $lt $( : $clt $(+ $dlt )* )? $( = $deflt)? ),+ >)? { previous_versions: [], latest_version: { @@ -68,7 +68,7 @@ macro_rules! define_versioned { $vis:vis enum $name:ident // Now match the optional type parameters // See https://stackoverflow.com/questions/41603424/rust-macro-accepting-type-with-generic-parameters - $(< $( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+ >)? + $(< $( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? $( = $deflt:tt)? ),+ >)? { $( previous_versions: [ @@ -108,7 +108,7 @@ macro_rules! define_versioned { // We include the repr(u8) so that the SBOR discriminants are assigned // to match the version numbers if SBOR is used on the versioned enum #[repr(u8)] - $vis enum $name $(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? + $vis enum $name $(< $( $lt $( : $clt $(+ $dlt )* )? $( = $deflt)? ),+ >)? { $($( []($version_type) = $version_num, diff --git a/scrypto-unit/src/test_runner.rs b/scrypto-unit/src/test_runner.rs index e0eee8f5c6c..502cda15a13 100644 --- a/scrypto-unit/src/test_runner.rs +++ b/scrypto-unit/src/test_runner.rs @@ -540,7 +540,7 @@ impl TestRunner { self.database .get_mapped::>( - output.0.into_latest().royalty_vault.0.as_node_id(), + output.into_latest().royalty_vault.0.as_node_id(), MAIN_BASE_PARTITION, &FungibleVaultField::LiquidFungible.into(), ) @@ -630,7 +630,7 @@ impl TestRunner { scrypto_decode(&entry.1).unwrap(); match value.value { Some(definition) => { - definitions.insert(key, definition.0.into_latest()); + definitions.insert(key, definition.into_latest()); } None => {} } diff --git a/simulator/src/ledger/dumper.rs b/simulator/src/ledger/dumper.rs index 6f59096128a..f4ffb0c4d24 100644 --- a/simulator/src/ledger/dumper.rs +++ b/simulator/src/ledger/dumper.rs @@ -48,7 +48,7 @@ pub fn dump_package( output, "{}: {} bytes", "Code size".green().bold(), - substate.value.unwrap().0.into_latest().code.len() + substate.value.unwrap().into_latest().code.len() ); let metadata = get_entity_metadata(package_address.as_node_id(), substate_db); diff --git a/simulator/src/resim/mod.rs b/simulator/src/resim/mod.rs index e6b203f841c..0ac7a2098e3 100644 --- a/simulator/src/resim/mod.rs +++ b/simulator/src/resim/mod.rs @@ -456,7 +456,7 @@ pub fn get_event_schema( ), ) .unwrap(); - let bp_interface = bp_definition.value.unwrap().0.into_latest().interface; + let bp_interface = bp_definition.value.unwrap().into_latest().interface; let event_def = bp_interface.events.get(event_name)?; match event_def { From 62449126fa90004ff3834ab778275e6468383ecf Mon Sep 17 00:00:00 2001 From: David Edey Date: Mon, 21 Aug 2023 01:39:24 +0100 Subject: [PATCH 26/28] feature: Improvements to key and content traits and models --- radix-engine/src/blueprints/models/content.rs | 195 ----------- radix-engine/src/blueprints/models/keys.rs | 312 ++++++++++++++++++ radix-engine/src/blueprints/models/mod.rs | 6 +- .../models/native_blueprint_state_macro.rs | 136 +++++--- .../src/blueprints/models/payloads.rs | 230 +++++++++++++ .../src/blueprints/package/substates.rs | 18 +- scrypto-unit/src/test_runner.rs | 2 +- 7 files changed, 653 insertions(+), 246 deletions(-) delete mode 100644 radix-engine/src/blueprints/models/content.rs create mode 100644 radix-engine/src/blueprints/models/keys.rs create mode 100644 radix-engine/src/blueprints/models/payloads.rs diff --git a/radix-engine/src/blueprints/models/content.rs b/radix-engine/src/blueprints/models/content.rs deleted file mode 100644 index bfd2fd8f07a..00000000000 --- a/radix-engine/src/blueprints/models/content.rs +++ /dev/null @@ -1,195 +0,0 @@ -use crate::internal_prelude::*; - -macro_rules! declare_payload_new_type { - ( - content_trait: $content_trait:ident, - payload_trait: $payload_trait:ident, - $(#[$attributes:meta])* - $vis:vis struct $payload_type_name:ident - $(< $( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? $( = $deflt:tt)? ),+ >)? - ($content_type:ty)$(;)? - ) => { - $(#[$attributes])* - #[sbor(transparent, categorize_types = "")] - $vis struct $payload_type_name - $(< $( $lt $( : $clt $(+ $dlt )* )? $( = $deflt)? ),+ >)? - (pub $content_type); - - impl $(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? - core::convert::From<$content_type> - for $payload_type_name $(< $( $lt ),+ >)? - { - fn from(value: $content_type) -> Self { - Self(value) - } - } - - impl $(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? - core::convert::AsRef<$content_type> - for $payload_type_name $(< $( $lt ),+ >)? - { - fn as_ref(&self) -> &$content_type { - &self.0 - } - } - - impl $(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? - core::convert::AsMut<$content_type> - for $payload_type_name $(< $( $lt ),+ >)? - { - fn as_mut(&mut self) -> &mut $content_type { - &mut self.0 - } - } - - impl $(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? - $payload_trait - for $payload_type_name $(< $( $lt ),+ >)? - { - type Content = $content_type; - - fn into_content(self) -> Self::Content { - self.0 - } - } - - impl $(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? - $content_trait<$payload_type_name$(< $( $lt ),+ >)?> - for $payload_type_name $(< $( $lt ),+ >)? - { - fn into_payload(self) -> $payload_type_name$(< $( $lt ),+ >)? { - self - } - } - - impl $(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? - $content_trait<$payload_type_name$(< $( $lt ),+ >)?> - for $content_type - { - fn into_payload(self) -> $payload_type_name$(< $( $lt ),+ >)? { - $payload_type_name(self) - } - } - } -} -#[allow(unused)] -pub(crate) use declare_payload_new_type; - -pub trait FieldPayload: AsRef + AsMut + From { - type Content: FieldContent; - - fn into_content(self) -> Self::Content; - - fn from_content>(content: T) -> Self - where - Self: From, - { - content.into_payload() - } -} - -pub trait FieldContent: Sized { - fn into_payload(self) -> Payload; - - fn into_locked_substate(self) -> FieldSubstate { - FieldSubstate::new_locked_field(self.into_payload()) - } - - fn into_mutable_substate(self) -> FieldSubstate { - FieldSubstate::new_field(self.into_payload()) - } -} - -pub trait KeyPayload: AsRef + AsMut + From { - type Content: KeyContent; - - fn into_content(self) -> Self::Content; - - fn from_content>(content: T) -> Self - where - Self: From, - { - content.into_key() - } -} - -pub trait KeyContent: Sized { - fn into_payload(self) -> Payload; - - fn into_key(self) -> Payload { - self.into_payload() - } -} - -pub trait KeyValueEntryPayload: - AsRef + AsMut + From -{ - type Content: KeyValueEntryContent; - - fn into_content(self) -> Self::Content; - - fn from_content>(content: T) -> Self - where - Self: From, - { - content.into_payload() - } -} - -pub trait KeyValueEntryContent: Sized { - fn into_payload(self) -> Payload; - - fn into_locked_substate(self) -> KeyValueEntrySubstate { - KeyValueEntrySubstate::entry(self.into_payload()) - } - - fn into_mutable_substate(self) -> KeyValueEntrySubstate { - KeyValueEntrySubstate::locked_entry(self.into_payload()) - } -} - -pub trait IndexEntryPayload: - AsRef + AsMut + From -{ - type Content: IndexEntryContent; - - fn into_content(self) -> Self::Content; - - fn from_content>(content: T) -> Self - where - Self: From, - { - content.into_payload() - } -} - -pub trait IndexEntryContent: Sized { - fn into_payload(self) -> Payload; - - fn into_substate(self) -> Payload { - self.into_payload() - } -} - -pub trait SortedIndexEntryPayload: - AsRef + AsMut + From -{ - type Content: SortedIndexEntryContent; - - fn into_content(self) -> Self::Content; - - fn from_content>(content: T) -> Self - where - Self: From, - { - content.into_payload() - } -} - -pub trait SortedIndexEntryContent: Sized { - fn into_payload(self) -> Payload; - - fn into_substate(self) -> Payload { - self.into_payload() - } -} diff --git a/radix-engine/src/blueprints/models/keys.rs b/radix-engine/src/blueprints/models/keys.rs new file mode 100644 index 00000000000..f48a747db7d --- /dev/null +++ b/radix-engine/src/blueprints/models/keys.rs @@ -0,0 +1,312 @@ +use crate::internal_prelude::*; + +macro_rules! declare_key_new_type { + // First - explicitly support SortedIndex + ( + content_trait: SortedIndexKeyContentSource, + payload_trait: SortedIndexKeyPayload, + full_key_content: { + full_content_type: $full_content_type:ty, + sort_prefix_property_name: $sort_prefix_property_name:ident + $(,)? + }, + $(#[$attributes:meta])* + $vis:vis struct $payload_type_name:ident + $(< $( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? $( = $deflt:tt)? ),+ >)? + ($content_type:ty)$(;)? + ) => { + $(#[$attributes])* + #[sbor(categorize_types = "")] + /// This type represents the payload of the key of a particular sorted index collection. + $vis struct $payload_type_name + $(< $( $lt $( : $clt $(+ $dlt )* )? $( = $deflt)? ),+ >)? + { + pub $sort_prefix_property_name: u16, + pub content: $content_type, + } + + impl $payload_type_name { + pub fn new($sort_prefix_property_name: u16, content: $content_type) -> Self { + Self { + $sort_prefix_property_name, + content, + } + } + + pub fn sort_prefix(&self) -> u16 { + self.$sort_prefix_property_name + } + } + + impl $(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? + core::convert::AsRef<$content_type> + for $payload_type_name $(< $( $lt ),+ >)? + { + fn as_ref(&self) -> &$content_type { + &self.content + } + } + + impl $(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? + core::convert::AsMut<$content_type> + for $payload_type_name $(< $( $lt ),+ >)? + { + fn as_mut(&mut self) -> &mut $content_type { + &mut self.content + } + } + + impl $(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? + SortedIndexKeyPayload + for $payload_type_name $(< $( $lt ),+ >)? + { + type Content = $content_type; + type FullContent = $full_content_type; + + fn into_sort_key_and_content(self) -> (u16, Self::Content) { + (self.sort_prefix(), self.content) + } + + fn from_sort_key_and_content(sort_prefix: u16, content: Self::Content) -> Self { + Self { + $sort_prefix_property_name: sort_prefix, + content, + } + } + } + + impl $(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? + TryFrom<&SubstateKey> + for $payload_type_name $(< $( $lt ),+ >)? + { + type Error = (); + + fn try_from(substate_key: &SubstateKey) -> Result { + let (sort_prefix, payload_bytes) = substate_key.for_sorted().ok_or(())?; + let content = scrypto_decode(payload_bytes).map_err(|_| ())?; + Ok(Self::from_sort_key_and_content(*sort_prefix, content)) + } + } + + // Note - we assume that both: + // * SortedIndexKeyContentSource<_Payload> + // * SortedIndexKeyContentSource<_Payload> + // are already/manually implemented for $content_type + }; + ( + content_trait: SortedIndexKeyContentSource, + payload_trait: SortedIndexKeyPayload, + $(#[$attributes:meta])* + $vis:vis struct $payload_type_name:ident + $(< $( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? $( = $deflt:tt)? ),+ >)? + ($content_type:ty)$(;)? + ) => { + compile_error!( + "Make sure to add a `full_key_content: { full_content_type: , sort_prefix_property_name: <..>}` property after the `key_type` property for SortedIndex collection definitions" + ) + }; + ( + content_trait: $content_trait:ident, + payload_trait: $payload_trait:ident, + full_key_content: $full_key_content:tt, + $(#[$attributes:meta])* + $vis:vis struct $payload_type_name:ident + $(< $( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? $( = $deflt:tt)? ),+ >)? + ($content_type:ty)$(;)? + ) => { + compile_error!( + "Only add a `full_key_content` property for SortedIndex collection definitions" + ) + }; + // Then - support the other collection types + ( + content_trait: $content_trait:ident, + payload_trait: $payload_trait:ident, + $(#[$attributes:meta])* + $vis:vis struct $payload_type_name:ident + $(< $( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? $( = $deflt:tt)? ),+ >)? + ($content_type:ty)$(;)? + ) => { + $(#[$attributes])* + #[sbor(transparent, categorize_types = "", transparent_name)] + /// This new type represents the payload of the key of a particular collection. + $vis struct $payload_type_name + $(< $( $lt $( : $clt $(+ $dlt )* )? $( = $deflt)? ),+ >)? + { + pub content: $content_type, + } + + impl $(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? + core::convert::From<$content_type> + for $payload_type_name $(< $( $lt ),+ >)? + { + fn from(value: $content_type) -> Self { + Self { + content: value, + } + } + } + + impl $(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? + core::convert::AsRef<$content_type> + for $payload_type_name $(< $( $lt ),+ >)? + { + fn as_ref(&self) -> &$content_type { + &self.content + } + } + + impl $(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? + core::convert::AsMut<$content_type> + for $payload_type_name $(< $( $lt ),+ >)? + { + fn as_mut(&mut self) -> &mut $content_type { + &mut self.content + } + } + + impl $(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? + TryFrom<&SubstateKey> + for $payload_type_name $(< $( $lt ),+ >)? + { + type Error = (); + + fn try_from(substate_key: &SubstateKey) -> Result { + let key = substate_key.for_map().ok_or(())?; + scrypto_decode::(&key).map_err(|_| ()) + } + } + + impl $(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? + $payload_trait + for $payload_type_name $(< $( $lt ),+ >)? + { + type Content = $content_type; + + fn into_content(self) -> Self::Content { + self.content + } + } + + impl $(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? + $content_trait<$payload_type_name$(< $( $lt ),+ >)?> + for $content_type + { + fn into_content(self) -> $content_type { + self + } + } + } +} +#[allow(unused)] +pub(crate) use declare_key_new_type; + +/// This trait is intended to be implemented by an explicit new type for for the given +/// `{ content: T }` key of a particular key value collection. +pub trait KeyValueKeyPayload: + Sized + AsRef + AsMut + From +{ + type Content: KeyValueKeyContentSource; + + fn into_content(self) -> Self::Content; + fn from_content(inner_content: Self::Content) -> Self { + Self::from(inner_content) + } + + fn from_content_source>(content: T) -> Self { + Self::from_content(content.into_content()) + } +} + +/// This trait is intended to be implemented by types which embody the content +/// of a key for a particular key value collection. +/// +/// Note: +/// * Multiple types might be mappable into the key payload, and so implement this trait +/// * This trait is only one way - from value into content +/// * This trait uses a generic, because the same type might be usable as a key for multiple +/// substates +pub trait KeyValueKeyContentSource: Sized { + fn into_content(self) -> Payload::Content; + + fn into_key(self) -> Payload { + Payload::from_content_source(self) + } +} + +/// This trait is intended to be implemented by an explicit new type for the given +/// `{ content: T }` key of a particular index collection. +pub trait IndexKeyPayload: + Sized + AsRef + AsMut + From +{ + type Content: IndexKeyContentSource; + + fn into_content(self) -> Self::Content; + fn from_content(content: Self::Content) -> Self { + Self::from(content) + } + + fn from_content_source>(content: T) -> Self { + Self::from_content(content.into_content()) + } +} + +/// This trait is intended to be implemented by types which embody the content +/// of a key for a particular index collection. +/// +/// Note: +/// * Multiple types might be mappable into the key payload, and so implement this trait +/// * This trait is only one way - from value into content +/// * This trait uses a generic, because the same type might be usable as a key for multiple +/// substates +pub trait IndexKeyContentSource: Sized { + fn into_content(self) -> Payload::Content; + + fn into_key(self) -> Payload { + Payload::from_content_source(self) + } +} + +/// This trait is intended to be implemented by an explicit new type for the given +/// `{ sort_index: u16, content: T }` key for a particular sorted index collection. +pub trait SortedIndexKeyPayload: Sized + AsRef + AsMut { + type Content; + type FullContent: SortedIndexKeyFullContent; + + fn from_sort_key_and_content(sort_key: u16, content: Self::Content) -> Self; + fn into_sort_key_and_content(self) -> (u16, Self::Content); + + fn into_full_content(self) -> Self::FullContent { + let (sort_key, content) = self.into_sort_key_and_content(); + Self::FullContent::from_sort_key_and_content(sort_key, content) + } + + fn from_content_source>(content: T) -> Self { + let (sort_key, content) = content.into_sort_key_and_content(); + Self::from_sort_key_and_content(sort_key, content) + } +} + +/// This trait is intended to be implemented by types which embody the content +/// of a key for a particular sorted index collection. +/// +/// Note: +/// * Multiple types might be mappable into the key payload, and so implement this trait +/// * This trait is only one way - from value into content +/// * This trait uses a generic, because the same type might be usable as a key for multiple +/// explicit substates +pub trait SortedIndexKeyContentSource: Sized { + fn into_sort_key_and_content(self) -> (u16, Payload::Content); + + fn into_key(self) -> Payload { + Payload::from_content_source(self) + } +} + +/// This trait is intended to be implemented by the canonical content +/// of a key for a particular sorted index collection. +pub trait SortedIndexKeyFullContent: + SortedIndexKeyContentSource +{ + fn from_sort_key_and_content(sort_key: u16, content: Payload::Content) -> Self; +} diff --git a/radix-engine/src/blueprints/models/mod.rs b/radix-engine/src/blueprints/models/mod.rs index 45f493c3c57..8dcc077b2fb 100644 --- a/radix-engine/src/blueprints/models/mod.rs +++ b/radix-engine/src/blueprints/models/mod.rs @@ -1,7 +1,9 @@ -mod content; mod feature_set; +mod keys; mod native_blueprint_state_macro; +mod payloads; -pub use content::*; pub use feature_set::*; +pub use keys::*; pub(crate) use native_blueprint_state_macro::*; +pub use payloads::*; diff --git a/radix-engine/src/blueprints/models/native_blueprint_state_macro.rs b/radix-engine/src/blueprints/models/native_blueprint_state_macro.rs index a1e50bd613d..d97e9f42a71 100644 --- a/radix-engine/src/blueprints/models/native_blueprint_state_macro.rs +++ b/radix-engine/src/blueprints/models/native_blueprint_state_macro.rs @@ -28,7 +28,7 @@ use crate::internal_prelude::*; /// } /// { /// kind: Static, -/// the_type: x, +/// content_type: x, /// }, /// { /// kind: Generic, @@ -106,6 +106,8 @@ macro_rules! declare_native_blueprint_state { $collection_property_name:ident: $collection_type:ident { entry_ident: $collection_ident:ident, key_type: $collection_key_type:tt, + // The full_key_content is required if it's a sorted index + $(full_key_content: $full_key_content:tt,)? value_type: $collection_value_type:tt, allow_ownership: $collection_allow_ownership:expr // Advanced collection options for (eg): @@ -144,7 +146,7 @@ macro_rules! declare_native_blueprint_state { // > Set up the (transparent) _FieldPayload new type for the content of the field // > Set up the FieldContent trait for anything which can be resolved into the field payload generate_content_type!( - content_trait: FieldContent, + content_trait: FieldContentSource, payload_trait: FieldPayload, ident_core: [<$blueprint_ident $field_ident>], #[derive(Debug, PartialEq, Eq, ScryptoSbor)] @@ -161,36 +163,14 @@ macro_rules! declare_native_blueprint_state { // Generate models for each collection $( // Key - // > Set up Versioned types (if relevant). Assumes __KeyInnerV1 exists and then creates - // - Versioned__KeyInner - // - __KeyInner (alias for __KeyInnerV1) - // > Create the (transparent) _Key new type for the key - // > Set up the KeyContent traits for anything which can be resolved into a key - generate_content_type!( - content_trait: KeyContent, - payload_trait: KeyPayload, - ident_core: [<$blueprint_ident $collection_ident KeyInner>], + generate_key_type!( + content_trait: [<$collection_type KeyContentSource>], + payload_trait: [<$collection_type KeyPayload>], + $(full_key_content: $full_key_content,)? #[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, ScryptoSbor)] - #[sbor(transparent_name)] struct [<$blueprint_ident $collection_ident KeyPayload>] = $collection_key_type ); - // TODO(David): Tweak when I work out the right strategy for keys. Probably just want a single new-type. - // So probably just don't use generate_content_type but use generate_key_type or something instead? - pub type [<$blueprint_ident $collection_ident SubstateKey>] = [<$blueprint_ident $collection_ident KeyPayload>]; - - // TODO(David) - Properly handle SortedIndex: - // Fix Key types for SortedIndex to have a named u16 part of the key, - // use a different key trait, and use .for_sorted_key in the below. - impl TryFrom<&SubstateKey> for [<$blueprint_ident $collection_ident SubstateKey>] { - type Error = (); - - fn try_from(substate_key: &SubstateKey) -> Result { - let key = substate_key.for_map().ok_or(())?; - scrypto_decode::(&key).map_err(|_| ()) - } - } - // Values // > If relevant, set up Versioned types, which: // - Assumes [BlueprintCollection]V1 exists @@ -202,7 +182,7 @@ macro_rules! declare_native_blueprint_state { // - The Versioned[BlueprintCollection] if it exists // - The static content type, if it exists generate_content_type!( - content_trait: [<$collection_type EntryContent>], + content_trait: [<$collection_type EntryContentSource>], payload_trait: [<$collection_type EntryPayload>], ident_core: [<$blueprint_ident $collection_ident>], #[derive(Debug, PartialEq, Eq, ScryptoSbor)] @@ -404,7 +384,7 @@ macro_rules! declare_native_blueprint_state { )* $( pub $collection_property_name: IndexMap< - [<$blueprint_ident $collection_ident SubstateKey>], + [<$blueprint_ident $collection_ident KeyPayload>], [<$blueprint_ident $collection_ident EntrySubstate>] >, )* @@ -610,7 +590,7 @@ macro_rules! declare_native_blueprint_state { #[derive(Debug, Clone)] pub enum [<$blueprint_ident TypedSubstateKey>] { Fields([<$blueprint_ident Field>]), - $([<$collection_ident $collection_type Entries>]([<$blueprint_ident $collection_ident SubstateKey>]),)* + $([<$collection_ident $collection_type Entries>]([<$blueprint_ident $collection_ident KeyPayload>]),)* } impl [<$blueprint_ident TypedSubstateKey>] { @@ -624,7 +604,7 @@ macro_rules! declare_native_blueprint_state { $( [<$blueprint_ident Partition>]::[<$collection_ident $collection_type>] => { [<$blueprint_ident TypedSubstateKey>]::[<$collection_ident $collection_type Entries>]( - [<$blueprint_ident $collection_ident SubstateKey>]::try_from(substate_key)?, + [<$blueprint_ident $collection_ident KeyPayload>]::try_from(substate_key)?, ) } )* @@ -809,8 +789,8 @@ mod helper_macros { // Now implement other relevant content traits, for: // > The "latest" type: $ident_core impl $content_trait<$payload_type_name> for $ident_core { - fn into_payload(self) -> $payload_type_name { - $payload_type_name(self.into()) + fn into_content(self) -> [] { + self.into() } } } @@ -822,7 +802,7 @@ mod helper_macros { $(#[$attributes:meta])* struct $payload_type_name:ident = { kind: Static, - the_type: $static_type:ty + content_type: $static_type:ty $(,)? }$(,)? ) => { @@ -869,6 +849,64 @@ mod helper_macros { #[allow(unused)] pub(crate) use generate_content_type; + macro_rules! generate_key_type { + ( + content_trait: $content_trait:ident, + payload_trait: $payload_trait:ident, + $(full_key_content: $full_key_content:tt,)? + $(#[$attributes:meta])* + struct $payload_type_name:ident = { + kind: StaticSingleVersioned + $(,)? + }$(,)? + ) => { + compile_error!( + "A StaticSingleVersioned key is not supported, because keys cannot be lazily updated, because they need to be static" + ); + }; + ( + content_trait: $content_trait:ident, + payload_trait: $payload_trait:ident, + $(full_key_content: $full_key_content:tt,)? + $(#[$attributes:meta])* + struct $payload_type_name:ident = { + kind: Static, + content_type: $static_type:ty + $(,)? + }$(,)? + ) => { + paste::paste! { + declare_key_new_type!( + content_trait: $content_trait, + payload_trait: $payload_trait, + $(full_key_content: $full_key_content,)? + $(#[$attributes])* + pub struct $payload_type_name($static_type); + ); + } + }; + ( + content_trait: $content_trait:ident, + payload_trait: $payload_trait:ident, + $(full_key_content: $full_key_content:tt,)? + $(#[$attributes:meta])* + struct $payload_type_name:ident = { + kind: Generic, + ident: $generic_ident:ident + $(,)? + } + ) => { + paste::paste! { + compile_error!( + "A Generic key is not currently supported by these macros" + ); + } + }; + } + + #[allow(unused)] + pub(crate) use generate_key_type; + macro_rules! generate_system_substate_type_alias { (SystemField, type $alias:ident = WRAPPED $content:ty$(,)?) => { // There is no system wrapper around SystemField substates @@ -966,7 +1004,7 @@ mod helper_macros { $aggregator:ident, { kind: Static, - the_type: $static_type:ty + content_type: $static_type:ty $(,)? }, $payload_alias:ident$(,)? @@ -1108,7 +1146,7 @@ mod tests { entry_ident: MyCoolKeyValueStore, key_type: { kind: Static, - the_type: BlueprintVersion, + content_type: BlueprintVersion, }, value_type: { kind: StaticSingleVersioned, @@ -1119,7 +1157,7 @@ mod tests { entry_ident: MyCoolIndex, key_type: { kind: Static, - the_type: BlueprintVersion, + content_type: BlueprintVersion, }, value_type: { kind: StaticSingleVersioned, @@ -1130,7 +1168,11 @@ mod tests { entry_ident: MyCoolSortedIndex, key_type: { kind: Static, - the_type: BlueprintVersion, + content_type: BlueprintVersion, + }, + full_key_content: { + full_content_type: ExampleSortedIndexKey, + sort_prefix_property_name: sort_prefix, }, value_type: { kind: StaticSingleVersioned, @@ -1139,4 +1181,20 @@ mod tests { }, } } + + pub struct ExampleSortedIndexKey(u16, BlueprintVersion); + + impl SortedIndexKeyFullContent for ExampleSortedIndexKey { + fn from_sort_key_and_content(sort_key: u16, content: BlueprintVersion) -> Self { + ExampleSortedIndexKey(sort_key, content) + } + } + + impl SortedIndexKeyContentSource + for ExampleSortedIndexKey + { + fn into_sort_key_and_content(self) -> (u16, BlueprintVersion) { + (self.0, self.1) + } + } } diff --git a/radix-engine/src/blueprints/models/payloads.rs b/radix-engine/src/blueprints/models/payloads.rs new file mode 100644 index 00000000000..d2b1d714e46 --- /dev/null +++ b/radix-engine/src/blueprints/models/payloads.rs @@ -0,0 +1,230 @@ +use crate::internal_prelude::*; + +macro_rules! declare_payload_new_type { + ( + content_trait: $content_trait:ident, + payload_trait: $payload_trait:ident, + $(#[$attributes:meta])* + $vis:vis struct $payload_type_name:ident + $(< $( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? $( = $deflt:tt)? ),+ >)? + ($content_type:ty)$(;)? + ) => { + $(#[$attributes])* + #[sbor(transparent, categorize_types = "")] + /// This new type represents the payload of a particular field or collection. + /// It is unique to this particular field/collection. + $vis struct $payload_type_name + $(< $( $lt $( : $clt $(+ $dlt )* )? $( = $deflt)? ),+ >)? + { + pub content: $content_type + } + + impl $(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? + core::convert::From<$content_type> + for $payload_type_name $(< $( $lt ),+ >)? + { + fn from(value: $content_type) -> Self { + Self { + content: value, + } + } + } + + impl $(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? + core::convert::AsRef<$content_type> + for $payload_type_name $(< $( $lt ),+ >)? + { + fn as_ref(&self) -> &$content_type { + &self.content + } + } + + impl $(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? + core::convert::AsMut<$content_type> + for $payload_type_name $(< $( $lt ),+ >)? + { + fn as_mut(&mut self) -> &mut $content_type { + &mut self.content + } + } + + impl $(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? + $payload_trait + for $payload_type_name $(< $( $lt ),+ >)? + { + type Content = $content_type; + + fn into_content(self) -> Self::Content { + self.content + } + } + + impl $(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? + $content_trait<$payload_type_name$(< $( $lt ),+ >)?> + for $content_type + { + fn into_content(self) -> $content_type { + self + } + } + } +} +#[allow(unused)] +pub(crate) use declare_payload_new_type; + +/// This trait is intended to be implemented by an explicit new type for for the given +/// `{ content: T }` payload of a particular field. +pub trait FieldPayload: + Sized + AsRef + AsMut + From +{ + type Content: FieldContentSource; + + fn into_content(self) -> Self::Content; + fn from_content(inner_content: Self::Content) -> Self { + Self::from(inner_content) + } + + fn from_content_source>(content: T) -> Self { + Self::from_content(content.into_content()) + } +} + +/// This trait is intended to be implemented by types which embody the content +/// of a particular field payload. +/// +/// Note: +/// * Multiple types might be mappable into the payload, and so implement this trait +/// * This trait is only one way - from value into content +/// * This trait uses a generic, because the same type might be usable as a payload for multiple +/// substates +pub trait FieldContentSource: Sized { + fn into_content(self) -> Payload::Content; + + fn into_payload(self) -> Payload { + Payload::from_content_source(self) + } + + fn into_locked_substate(self) -> FieldSubstate { + FieldSubstate::new_locked_field(self.into_payload()) + } + + fn into_mutable_substate(self) -> FieldSubstate { + FieldSubstate::new_field(self.into_payload()) + } +} + +/// This trait is intended to be implemented by an explicit new type for for the given +/// `{ content: T }` payload of a particular key value collection. +pub trait KeyValueEntryPayload: + Sized + AsRef + AsMut + From +{ + type Content: KeyValueEntryContentSource; + + fn into_content(self) -> Self::Content; + fn from_content(inner_content: Self::Content) -> Self { + Self::from(inner_content) + } + + fn from_content_source>(content: T) -> Self { + Self::from_content(content.into_content()) + } +} + +/// This trait is intended to be implemented by types which embody the content +/// of a particular key value entry payload. +/// +/// Note: +/// * Multiple types might be mappable into the payload, and so implement this trait +/// * This trait is only one way - from value into content +/// * This trait uses a generic, because the same type might be usable as a payload for multiple +/// substates +pub trait KeyValueEntryContentSource: Sized { + fn into_content(self) -> Payload::Content; + + fn into_payload(self) -> Payload { + Payload::from_content_source(self) + } + + fn into_locked_substate(self) -> KeyValueEntrySubstate { + KeyValueEntrySubstate::entry(self.into_payload()) + } + + fn into_mutable_substate(self) -> KeyValueEntrySubstate { + KeyValueEntrySubstate::locked_entry(self.into_payload()) + } +} + +/// This trait is intended to be implemented by an explicit new type for for the given +/// `{ content: T }` payload of a particular index collection. +pub trait IndexEntryPayload: + Sized + AsRef + AsMut + From +{ + type Content: IndexEntryContentSource; + + fn into_content(self) -> Self::Content; + fn from_content(inner_content: Self::Content) -> Self { + Self::from(inner_content) + } + + fn from_content_source>(content: T) -> Self { + Self::from_content(content.into_content()) + } +} + +/// This trait is intended to be implemented by types which embody the content +/// of a particular index entry payload. +/// +/// Note: +/// * Multiple types might be mappable into the payload, and so implement this trait +/// * This trait is only one way - from value into content +/// * This trait uses a generic, because the same type might be usable as a payload for multiple +/// substates +pub trait IndexEntryContentSource: Sized { + fn into_content(self) -> Payload::Content; + + fn into_payload(self) -> Payload { + Payload::from_content_source(self) + } + + fn into_substate(self) -> Payload { + self.into_payload() + } +} + +/// This trait is intended to be implemented by an explicit new type for for the given +/// `{ content: T }` payload of a particular sorted index collection. +pub trait SortedIndexEntryPayload: + Sized + AsRef + AsMut + From +{ + type Content: SortedIndexEntryContentSource; + + fn into_content(self) -> Self::Content; + + fn from_content(inner_content: Self::Content) -> Self { + Self::from(inner_content) + } + + fn from_content_source>(content: T) -> Self { + Self::from_content(content.into_content()) + } +} + +/// This trait is intended to be implemented by types which embody the content +/// of a particular sorted index entry payload. +/// +/// Note: +/// * Multiple types might be mappable into the payload, and so implement this trait +/// * This trait is only one way - from value into content +/// * This trait uses a generic, because the same type might be usable as a payload for multiple +/// substates +pub trait SortedIndexEntryContentSource: Sized { + fn into_content(self) -> Payload::Content; + + fn into_payload(self) -> Payload { + Payload::from_content_source(self) + } + + fn into_substate(self) -> Payload { + self.into_payload() + } +} diff --git a/radix-engine/src/blueprints/package/substates.rs b/radix-engine/src/blueprints/package/substates.rs index edab0488f87..7783146cd56 100644 --- a/radix-engine/src/blueprints/package/substates.rs +++ b/radix-engine/src/blueprints/package/substates.rs @@ -24,7 +24,7 @@ declare_native_blueprint_state! { entry_ident: BlueprintVersionDefinition, key_type: { kind: Static, - the_type: BlueprintVersionKey, + content_type: BlueprintVersionKey, }, value_type: { kind: StaticSingleVersioned, @@ -35,7 +35,7 @@ declare_native_blueprint_state! { entry_ident: BlueprintVersionDependencies, key_type: { kind: Static, - the_type: BlueprintVersionKey, + content_type: BlueprintVersionKey, }, value_type: { kind: StaticSingleVersioned, @@ -46,12 +46,12 @@ declare_native_blueprint_state! { entry_ident: Schema, key_type: { kind: Static, - the_type: SchemaHash, + content_type: SchemaHash, }, value_type: { kind: Static, // TODO(David): Change to VersionedSchema in this substate in the SCHEMAS_PARTITION - the_type: ScryptoSchema, + content_type: ScryptoSchema, }, allow_ownership: false, options: { @@ -62,7 +62,7 @@ declare_native_blueprint_state! { entry_ident: BlueprintVersionRoyaltyConfig, key_type: { kind: Static, - the_type: BlueprintVersionKey, + content_type: BlueprintVersionKey, }, value_type: { kind: StaticSingleVersioned, @@ -73,7 +73,7 @@ declare_native_blueprint_state! { entry_ident: BlueprintVersionAuthConfig, key_type: { kind: Static, - the_type: BlueprintVersionKey, + content_type: BlueprintVersionKey, }, value_type: { kind: StaticSingleVersioned, @@ -84,7 +84,7 @@ declare_native_blueprint_state! { entry_ident: CodeVmType, key_type: { kind: Static, - the_type: CodeHash, + content_type: CodeHash, }, value_type: { kind: StaticSingleVersioned, @@ -95,7 +95,7 @@ declare_native_blueprint_state! { entry_ident: CodeOriginalCode, key_type: { kind: Static, - the_type: CodeHash, + content_type: CodeHash, }, value_type: { kind: StaticSingleVersioned, @@ -106,7 +106,7 @@ declare_native_blueprint_state! { entry_ident: CodeInstrumentedCode, key_type: { kind: Static, - the_type: CodeHash, + content_type: CodeHash, }, value_type: { kind: StaticSingleVersioned, diff --git a/scrypto-unit/src/test_runner.rs b/scrypto-unit/src/test_runner.rs index 502cda15a13..e0da08bc88b 100644 --- a/scrypto-unit/src/test_runner.rs +++ b/scrypto-unit/src/test_runner.rs @@ -603,7 +603,7 @@ impl TestRunner { let value: PackageSchemaEntrySubstate = scrypto_decode(&entry.1).unwrap(); match value.value { Some(schema) => { - schemas.insert(hash, schema.0); + schemas.insert(hash, schema.content); } None => {} } From 97b8cad0b8a895c62adae6b17d41730382c41107 Mon Sep 17 00:00:00 2001 From: David Edey Date: Mon, 21 Aug 2023 02:15:44 +0100 Subject: [PATCH 27/28] tweak: Minor tweaks to mapped physical partition definition --- .../models/native_blueprint_state_macro.rs | 67 +++++-------------- .../src/blueprints/package/substates.rs | 4 +- 2 files changed, 18 insertions(+), 53 deletions(-) diff --git a/radix-engine/src/blueprints/models/native_blueprint_state_macro.rs b/radix-engine/src/blueprints/models/native_blueprint_state_macro.rs index 416588e59a2..021825f2620 100644 --- a/radix-engine/src/blueprints/models/native_blueprint_state_macro.rs +++ b/radix-engine/src/blueprints/models/native_blueprint_state_macro.rs @@ -106,15 +106,12 @@ macro_rules! declare_native_blueprint_state { $( $collection_property_name:ident: $collection_type:ident { entry_ident: $collection_ident:ident, + $(mapped_physical_partition: $mapped_physical_partition:expr,)? key_type: $collection_key_type:tt, // The full_key_content is required if it's a sorted index $(full_key_content: $full_key_content:tt,)? value_type: $collection_value_type:tt, allow_ownership: $collection_allow_ownership:expr - // Advanced collection options for (eg): - // - Passing in a property name of the sorted index parameter for SortedIndex - // - Specifying a Logical partition mapping - $(, options: $collection_options:tt)? $(,)? // Optional trailing comma } ),* @@ -214,8 +211,8 @@ macro_rules! declare_native_blueprint_state { $field_type, [<$blueprint_ident $field_ident FieldPayload>], ), - condition: optional_expression_to_option!($($field_condition)?).unwrap_or(Condition::Always), - transience: optional_expression_to_option!($($field_transience)?).unwrap_or(FieldTransience::NotTransient), + condition: optional_or_fallback!($({ $field_condition })?, { Condition::Always }), + transience: optional_or_fallback!($({ $field_transience})?, { FieldTransience::NotTransient }), }); )* let mut collections = vec![]; @@ -404,7 +401,7 @@ macro_rules! declare_native_blueprint_state { { feature_checks.assert_valid( stringify!($field_ident), - &optional_expression_to_option!($($field_condition)?).unwrap_or(Condition::Always), + &optional_or_fallback!($({ $field_condition })?, { Condition::Always }), self.$field_property_name.is_some(), )?; if let Some(field) = self.$field_property_name { @@ -514,19 +511,15 @@ macro_rules! declare_native_blueprint_state { return PartitionDescription::Logical(PartitionOffset(module_partition_offset)); } $( - let mapped_physical_partition = extract_collection_option!( - mapped_physical_partition, - $($collection_options)? - ); - let current_partition_description = match mapped_physical_partition { - Some(physical_partition) => { - PartitionDescription::Physical(physical_partition) - }, - None => { + let current_partition_description = optional_or_fallback!( + $({ + PartitionDescription::Physical($mapped_physical_partition) + })?, + { module_partition_offset += 1; PartitionDescription::Logical(PartitionOffset(module_partition_offset)) - }, - }; + } + ); if (*self as u8) == (Self::[<$collection_ident $collection_type>] as u8) { return current_partition_description; } @@ -1066,43 +1059,17 @@ mod helper_macros { #[allow(unused)] pub(crate) use map_entry_substate_to_kv_entry; - macro_rules! optional_expression_to_option { - ($expression:expr$(,)?) => { Some($expression) }; - () => { None }; - } - - #[allow(unused)] - pub(crate) use optional_expression_to_option; - - macro_rules! extract_collection_option { - ( - mapped_physical_partition, // Name of field - { - mapped_physical_partition: $value:tt$(,)? - $(sorted_index_key_property: $ignored:tt$(,)?)? - }$(,)? - ) => { - Some($value) + macro_rules! optional_or_fallback { + ($value:tt, $fallback:tt$(,)?) => { + $value }; - ( - sorted_index_key_property, // Name of field - { - $(mapped_physical_partition: $ignored:tt$(,)?)? - sorted_index_key_property: $value:tt$(,)? - }$(,)? - ) => { - Some($value) - }; - ( - $option_field_name:ident, - $($non_matching_stuff:tt)?$(,)? - ) => { - None + (, $fallback:tt$(,)?) => { + $fallback }; } #[allow(unused)] - pub(crate) use extract_collection_option; + pub(crate) use optional_or_fallback; } #[cfg(test)] diff --git a/radix-engine/src/blueprints/package/substates.rs b/radix-engine/src/blueprints/package/substates.rs index 7783146cd56..8cecb50621c 100644 --- a/radix-engine/src/blueprints/package/substates.rs +++ b/radix-engine/src/blueprints/package/substates.rs @@ -44,6 +44,7 @@ declare_native_blueprint_state! { }, schemas: KeyValue { entry_ident: Schema, + mapped_physical_partition: SCHEMAS_PARTITION, key_type: { kind: Static, content_type: SchemaHash, @@ -54,9 +55,6 @@ declare_native_blueprint_state! { content_type: ScryptoSchema, }, allow_ownership: false, - options: { - mapped_physical_partition: SCHEMAS_PARTITION, - }, }, blueprint_version_royalty_configs: KeyValue { entry_ident: BlueprintVersionRoyaltyConfig, From 792a283300d350e34179c9cecd73be42dfaa98d8 Mon Sep 17 00:00:00 2001 From: David Edey Date: Mon, 21 Aug 2023 02:21:46 +0100 Subject: [PATCH 28/28] fix: Minor fix --- radix-engine/src/blueprints/models/keys.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radix-engine/src/blueprints/models/keys.rs b/radix-engine/src/blueprints/models/keys.rs index f48a747db7d..54570203cd5 100644 --- a/radix-engine/src/blueprints/models/keys.rs +++ b/radix-engine/src/blueprints/models/keys.rs @@ -84,7 +84,7 @@ macro_rules! declare_key_new_type { fn try_from(substate_key: &SubstateKey) -> Result { let (sort_prefix, payload_bytes) = substate_key.for_sorted().ok_or(())?; let content = scrypto_decode(payload_bytes).map_err(|_| ())?; - Ok(Self::from_sort_key_and_content(*sort_prefix, content)) + Ok(Self::from_sort_key_and_content(u16::from_be_bytes(*sort_prefix), content)) } }

XZop`g`dnHOy! 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
9E^MKzf$ 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
m>4*wl5A86#8nCD{O~7!mwkI8 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 literal 350536 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°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­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 z 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