diff --git a/runtime-sdk-macros/src/lib.rs b/runtime-sdk-macros/src/lib.rs index 4149e7cee6..3295dc1ef5 100644 --- a/runtime-sdk-macros/src/lib.rs +++ b/runtime-sdk-macros/src/lib.rs @@ -45,22 +45,21 @@ pub fn error_derive(input: TokenStream) -> TokenStream { /// Derives traits from a non-trait `impl` block (rather than from a `struct`). /// -/// Only the `MethodHandler` trait is supported. In other words, given an -/// `impl MyModule` block, the macro derives `impl MethodHandler for MyModule`. -/// See also the `#[handler]` attribute. +/// Only the `Module` trait is supported. In other words, given an `impl MyModule` block, the macro +/// derives implementations needed for implementing a module. +/// See also the `#[handler]` and `#[migration]` attributes. #[proc_macro_attribute] pub fn sdk_derive(args: TokenStream, input: TokenStream) -> TokenStream { let input = syn::parse_macro_input!(input as syn::ItemImpl); - if args.to_string() == "MethodHandler" { - // TODO: Change to Module once we have more handlers. + if args.to_string() == "Module" { module_derive::derive_module(input).into() } else { - emit_compile_error("#[sdk_derive] only supports #[sdk_derive(MethodHandler)]"); + emit_compile_error("#[sdk_derive] only supports #[sdk_derive(Module)]"); } } /// A helper attribute for `#[sdk_derive(...)]`. It doesn't do anyting on its own; -/// it only mark functions that represent a paratime method handler. +/// it only marks functions that represent a paratime method handler. /// The permitted forms are: /// - `#[handler(call = "my_module.MyCall")]`: Marks a function that handles /// the "my_module.MyCall" call and can be passed to @@ -92,6 +91,17 @@ pub fn handler(_args: TokenStream, input: TokenStream) -> TokenStream { input } +/// A helper attribute for `#[sdk_derive(...)]`. It doesn't do anything on its own; +/// it only marks functions that represent a module state migration. +/// +/// The permitted forms are: +/// - `#[migration(init)]`: Marks the initial (genesis) migration. +/// - `#[migration(from = 1)]`: Marks a migration from version 1. +#[proc_macro_attribute] +pub fn migration(_args: TokenStream, input: TokenStream) -> TokenStream { + input +} + /// Constructs an `oasis_sdk::core::common::version::Version` from the Cargo.toml version. #[proc_macro] pub fn version_from_cargo(_input: TokenStream) -> TokenStream { diff --git a/runtime-sdk-macros/src/module_derive/method_handler.rs b/runtime-sdk-macros/src/module_derive/method_handler.rs index 112e90e806..fb7343e779 100644 --- a/runtime-sdk-macros/src/module_derive/method_handler.rs +++ b/runtime-sdk-macros/src/module_derive/method_handler.rs @@ -275,6 +275,7 @@ impl super::Deriver for DeriveMethodHandler { }; quote! { + #[automatically_derived] impl #generics sdk::module::MethodHandler for #ty { #prefetch_impl #dispatch_call_impl @@ -286,6 +287,7 @@ impl super::Deriver for DeriveMethodHandler { #allowed_interactive_calls_impl } + #[automatically_derived] impl #generics #ty { #query_parameters_impl diff --git a/runtime-sdk-macros/src/module_derive/migration_handler.rs b/runtime-sdk-macros/src/module_derive/migration_handler.rs new file mode 100644 index 0000000000..c5e90bf417 --- /dev/null +++ b/runtime-sdk-macros/src/module_derive/migration_handler.rs @@ -0,0 +1,167 @@ +use std::collections::HashSet; + +use proc_macro2::TokenStream; +use quote::quote; + +use crate::emit_compile_error; + +/// Deriver for the `MigrationHandler` trait. +pub struct DeriveMigrationHandler { + /// Item defining the `MigrationHandler::Genesis` associated type. + genesis_ty: Option, + /// Migration functions. + migrate_fns: Vec, +} + +struct MigrateFn { + item: syn::ImplItem, + ident: syn::Ident, + from_version: u32, +} + +impl DeriveMigrationHandler { + pub fn new() -> Box { + Box::new(Self { + genesis_ty: None, + migrate_fns: vec![], + }) + } +} + +impl super::Deriver for DeriveMigrationHandler { + fn preprocess(&mut self, item: syn::ImplItem) -> Option { + match item { + // We are looking for a `type Genesis = ...;` item. + syn::ImplItem::Type(ref ty) if &ty.ident.to_string() == "Genesis" => { + self.genesis_ty = Some(item); + + None // Take the item. + } + syn::ImplItem::Fn(ref f) => { + // Check whether a `migration` attribute is set for the method. + if let Some(attrs) = parse_attrs(&f.attrs) { + self.migrate_fns.push(MigrateFn { + ident: f.sig.ident.clone(), + from_version: attrs.from_version, + item, + }); + + None + } else { + Some(item) // Return the item. + } + } + _ => Some(item), // Return the item. + } + } + + fn derive(&mut self, generics: &syn::Generics, ty: &Box) -> TokenStream { + let genesis_ty = if let Some(genesis_ty) = &self.genesis_ty { + genesis_ty + } else { + return quote! {}; + }; + + let mut seen_versions = HashSet::new(); + let (migrate_fns, mut migrate_arms): (Vec<_>, Vec<_>) = self.migrate_fns.iter().map(|f| { + let MigrateFn { item, ident, from_version } = f; + if seen_versions.contains(from_version) { + emit_compile_error(format!( + "Duplicate migration for version: {from_version}" + )); + } + seen_versions.insert(from_version); + + ( + item, + if from_version == &0 { + // Version zero is special as initializing from genesis always gets us latest. + quote! { if version == #from_version { Self::#ident(genesis); version = Self::VERSION; } } + } else { + // For other versions, each migration brings us from V to V+1. + quote! { if version == #from_version { Self::#ident(); version += 1; } } + } + ) + }).unzip(); + + // Ensure there is a genesis migration, at least an empty one that bumps the version. + if !seen_versions.contains(&0) { + migrate_arms.push(quote! { + if version == 0u32 { version = Self::VERSION; } + }); + } + + quote! { + #[automatically_derived] + impl #generics sdk::module::MigrationHandler for #ty { + #genesis_ty + + fn init_or_migrate( + _ctx: &mut C, + meta: &mut sdk::modules::core::types::Metadata, + genesis: Self::Genesis, + ) -> bool { + let mut version = meta.versions.get(Self::NAME).copied().unwrap_or_default(); + if version == Self::VERSION { + return false; // Already the latest version. + } + + #(#migrate_arms)* + + if version != Self::VERSION { + panic!("no migration for module state from version {version} to {}", Self::VERSION) + } + + // Update version information. + meta.versions.insert(Self::NAME.to_owned(), Self::VERSION); + return true; + } + } + + #[automatically_derived] + impl #generics #ty { + #(#migrate_fns)* + } + } + } +} + +#[derive(Debug, Clone, PartialEq)] +struct MigrationHandlerAttr { + /// Version that this handler handles. Zero indicates genesis. + from_version: u32, +} +impl syn::parse::Parse for MigrationHandlerAttr { + fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result { + let kind: syn::Ident = input.parse()?; + let from_version = match kind.to_string().as_str() { + "init" => 0, + "from" => { + let _: syn::token::Eq = input.parse()?; + let version: syn::LitInt = input.parse()?; + + version.base10_parse()? + } + _ => return Err(syn::Error::new(kind.span(), "invalid migration kind")), + }; + + if !input.is_empty() { + return Err(syn::Error::new(input.span(), "unexpected extra tokens")); + } + Ok(Self { from_version }) + } +} + +fn parse_attrs(attrs: &[syn::Attribute]) -> Option { + let migration_meta = attrs + .iter() + .find(|attr| attr.path().is_ident("migration"))?; + migration_meta + .parse_args() + .map_err(|err| { + emit_compile_error(format!( + "Unsupported format of #[migration(...)] attribute: {err}" + )) + }) + .ok() +} diff --git a/runtime-sdk-macros/src/module_derive/mod.rs b/runtime-sdk-macros/src/module_derive/mod.rs index 83721329ca..b2aa8e8b5c 100644 --- a/runtime-sdk-macros/src/module_derive/mod.rs +++ b/runtime-sdk-macros/src/module_derive/mod.rs @@ -1,4 +1,5 @@ mod method_handler; +mod migration_handler; use proc_macro2::TokenStream; use quote::quote; @@ -15,7 +16,10 @@ pub fn derive_module(impl_block: syn::ItemImpl) -> TokenStream { let mut base_impls: Vec = Vec::new(); let mut derivations: Vec = Vec::new(); - let mut derivers: Vec> = vec![method_handler::DeriveMethodHandler::new()]; + let mut derivers: Vec> = vec![ + migration_handler::DeriveMigrationHandler::new(), + method_handler::DeriveMethodHandler::new(), + ]; // Iterate through all impl items, collecting them and then deriving everything. 'items: for item in impl_block.items { @@ -43,6 +47,7 @@ pub fn derive_module(impl_block: syn::ItemImpl) -> TokenStream { None } else { Some(quote! { + #[automatically_derived] impl #module_generics #module_ty { #(#base_impls)* } @@ -109,6 +114,7 @@ mod tests { syn::parse_quote!( const _: () = { #uses + #[automatically_derived] impl sdk::module::MethodHandler for MyModule { fn dispatch_query( ctx: &mut C, @@ -124,11 +130,13 @@ mod tests { } } } + #[automatically_derived] impl MyModule { fn query_parameters(_ctx: &mut C, _args: ()) -> Result<::Parameters, ::Error> { Ok(Self::params()) } } + #[automatically_derived] impl MyModule { fn unannotated_fn_should_be_passed_thru(foo: Bar) -> Baz {} } @@ -157,6 +165,7 @@ mod tests { syn::parse_quote!( const _: () = { #uses + #[automatically_derived] impl sdk::module::MethodHandler for MyModule { fn prefetch( prefixes: &mut BTreeSet, @@ -214,6 +223,7 @@ mod tests { ] } } + #[automatically_derived] impl MyModule { fn query_parameters(_ctx: &mut C, _args: ()) -> Result<::Parameters, ::Error> { Ok(Self::params()) @@ -250,6 +260,7 @@ mod tests { syn::parse_quote!( const _: () = { #uses + #[automatically_derived] impl sdk::module::MethodHandler for MyModule { fn dispatch_query( ctx: &mut C, @@ -290,6 +301,7 @@ mod tests { ["module.ConfidentialQuery"].contains(&method) } } + #[automatically_derived] impl MyModule { fn query_parameters( _ctx: &mut C, @@ -326,6 +338,7 @@ mod tests { syn::parse_quote!( const _: () = { #uses + #[automatically_derived] impl sdk::module::MethodHandler for MyModule { fn dispatch_query( ctx: &mut C, @@ -348,6 +361,7 @@ mod tests { }] } } + #[automatically_derived] impl MyModule { fn query_parameters(_ctx: &mut C, _args: ()) -> Result<::Parameters, ::Error> { Ok(Self::params()) diff --git a/runtime-sdk/modules/contracts/src/abi/oasis/test.rs b/runtime-sdk/modules/contracts/src/abi/oasis/test.rs index 9723d00ee7..ca39fc1f60 100644 --- a/runtime-sdk/modules/contracts/src/abi/oasis/test.rs +++ b/runtime-sdk/modules/contracts/src/abi/oasis/test.rs @@ -318,15 +318,12 @@ fn run_contract_with_defaults( let mut ctx = mock.create_ctx_for_runtime::(context::Mode::ExecuteTx, true); let params = Parameters::default(); - core::Module::::init( - &mut ctx, - core::Genesis { - parameters: core::Parameters { - max_batch_gas: gas_limit, - ..Default::default() - }, + core::Module::::init(core::Genesis { + parameters: core::Parameters { + max_batch_gas: gas_limit, + ..Default::default() }, - ); + }); let mut tx = mock::transaction(); tx.auth_info.fee.gas = gas_limit; diff --git a/runtime-sdk/modules/contracts/src/lib.rs b/runtime-sdk/modules/contracts/src/lib.rs index 80d4e6ac31..094b77bb89 100644 --- a/runtime-sdk/modules/contracts/src/lib.rs +++ b/runtime-sdk/modules/contracts/src/lib.rs @@ -15,7 +15,7 @@ use oasis_runtime_sdk::{ self as sdk, context::{Context, TxContext}, core::common::crypto::hash::Hash, - handler, module, + handler, migration, module, module::Module as _, modules, modules::{accounts::API as _, core::API as _}, @@ -419,8 +419,16 @@ impl Module { } } -#[sdk_derive(MethodHandler)] +#[sdk_derive(Module)] impl Module { + type Genesis = Genesis; + + #[migration(init)] + fn init(genesis: Genesis) { + // Set genesis parameters. + Self::set_params(genesis.parameters); + } + #[handler(call = "contracts.Upload")] pub fn tx_upload( ctx: &mut C, @@ -863,41 +871,6 @@ impl module::Module for Module { type Parameters = Parameters; } -impl Module { - /// Initialize state from genesis. - pub fn init(_ctx: &mut C, genesis: Genesis) { - // Set genesis parameters. - Self::set_params(genesis.parameters); - } - - /// Migrate state from a previous version. - pub fn migrate(_ctx: &mut C, _from: u32) -> bool { - // No migrations currently supported. - false - } -} - -impl module::MigrationHandler for Module { - type Genesis = Genesis; - - fn init_or_migrate( - ctx: &mut C, - meta: &mut modules::core::types::Metadata, - genesis: Self::Genesis, - ) -> bool { - let version = meta.versions.get(Self::NAME).copied().unwrap_or_default(); - if version == 0 { - // Initialize state from genesis. - Self::init(ctx, genesis); - meta.versions.insert(Self::NAME.to_owned(), Self::VERSION); - return true; - } - - // Perform migration. - Self::migrate(ctx, version) - } -} - impl module::TransactionHandler for Module {} impl module::BlockHandler for Module {} impl module::InvariantHandler for Module {} diff --git a/runtime-sdk/modules/contracts/src/test.rs b/runtime-sdk/modules/contracts/src/test.rs index d6476f74d9..472b044ff1 100644 --- a/runtime-sdk/modules/contracts/src/test.rs +++ b/runtime-sdk/modules/contracts/src/test.rs @@ -132,44 +132,35 @@ fn test_hello_contract_call() { let mut mock = mock::Mock::default(); let mut ctx = mock.create_ctx_for_runtime::(context::Mode::ExecuteTx, true); - Core::::init( - &mut ctx, - core::Genesis { - parameters: core::Parameters { - max_batch_gas: 1_000_000_000, - ..Default::default() - }, - }, - ); - - Accounts::init( - &mut ctx, - accounts::Genesis { - balances: { - let mut balances = BTreeMap::new(); - // Alice. - balances.insert(keys::alice::address(), { - let mut denominations = BTreeMap::new(); - denominations.insert(Denomination::NATIVE, 1_000_000); - denominations - }); - balances - }, - total_supplies: { - let mut total_supplies = BTreeMap::new(); - total_supplies.insert(Denomination::NATIVE, 1_000_000); - total_supplies - }, + Core::::init(core::Genesis { + parameters: core::Parameters { + max_batch_gas: 1_000_000_000, ..Default::default() }, - ); + }); - Contracts::init( - &mut ctx, - Genesis { - parameters: Default::default(), + Accounts::init(accounts::Genesis { + balances: { + let mut balances = BTreeMap::new(); + // Alice. + balances.insert(keys::alice::address(), { + let mut denominations = BTreeMap::new(); + denominations.insert(Denomination::NATIVE, 1_000_000); + denominations + }); + balances }, - ); + total_supplies: { + let mut total_supplies = BTreeMap::new(); + total_supplies.insert(Denomination::NATIVE, 1_000_000); + total_supplies + }, + ..Default::default() + }); + + Contracts::init(Genesis { + parameters: Default::default(), + }); let instance_id = deploy_hello_contract(&mut ctx, vec![BaseUnits::new(1_000, Denomination::NATIVE)]); diff --git a/runtime-sdk/modules/evm/src/lib.rs b/runtime-sdk/modules/evm/src/lib.rs index 29e4102ff5..7430e50025 100644 --- a/runtime-sdk/modules/evm/src/lib.rs +++ b/runtime-sdk/modules/evm/src/lib.rs @@ -22,7 +22,7 @@ use thiserror::Error; use oasis_runtime_sdk::{ callformat, context::{BatchContext, Context, TransactionWithMeta, TxContext}, - handler, + handler, migration, module::{self, Module as _}, modules::{ self, @@ -658,8 +658,16 @@ impl Module { } } -#[sdk_derive(MethodHandler)] +#[sdk_derive(Module)] impl Module { + type Genesis = Genesis; + + #[migration(init)] + fn init(genesis: Genesis) { + // Set genesis parameters. + Self::set_params(genesis.parameters); + } + #[handler(call = "evm.Create")] fn tx_create(ctx: &mut C, body: types::Create) -> Result, Error> { Self::create(ctx, body.value, body.init_code) @@ -700,41 +708,6 @@ impl Module { } } -impl Module { - /// Initialize state from genesis. - fn init(_ctx: &mut C, genesis: Genesis) { - // Set genesis parameters. - Self::set_params(genesis.parameters); - } - - /// Migrate state from a previous version. - fn migrate(_ctx: &mut C, _from: u32) -> bool { - // No migrations currently supported. - false - } -} - -impl module::MigrationHandler for Module { - type Genesis = Genesis; - - fn init_or_migrate( - ctx: &mut C, - meta: &mut modules::core::types::Metadata, - genesis: Self::Genesis, - ) -> bool { - let version = meta.versions.get(Self::NAME).copied().unwrap_or_default(); - if version == 0 { - // Initialize state from genesis. - Self::init(ctx, genesis); - meta.versions.insert(Self::NAME.to_owned(), Self::VERSION); - return true; - } - - // Perform migration. - Self::migrate(ctx, version) - } -} - impl module::TransactionHandler for Module { fn decode_tx( _ctx: &mut C, diff --git a/runtime-sdk/modules/evm/src/test.rs b/runtime-sdk/modules/evm/src/test.rs index e05a4d042c..937fab7080 100644 --- a/runtime-sdk/modules/evm/src/test.rs +++ b/runtime-sdk/modules/evm/src/test.rs @@ -157,44 +157,35 @@ fn do_test_evm_calls(force_plain: bool) { }; } - Core::::init( - &mut ctx, - core::Genesis { - parameters: core::Parameters { - max_batch_gas: 10_000_000, - ..Default::default() - }, - }, - ); - - Accounts::init( - &mut ctx, - accounts::Genesis { - balances: { - let mut b = BTreeMap::new(); - // Dave. - b.insert(keys::dave::address(), { - let mut d = BTreeMap::new(); - d.insert(Denomination::NATIVE, 1_000_000); - d - }); - b - }, - total_supplies: { - let mut ts = BTreeMap::new(); - ts.insert(Denomination::NATIVE, 1_000_000); - ts - }, + Core::::init(core::Genesis { + parameters: core::Parameters { + max_batch_gas: 10_000_000, ..Default::default() }, - ); + }); - EVMModule::::init( - &mut ctx, - Genesis { - parameters: Default::default(), + Accounts::init(accounts::Genesis { + balances: { + let mut b = BTreeMap::new(); + // Dave. + b.insert(keys::dave::address(), { + let mut d = BTreeMap::new(); + d.insert(Denomination::NATIVE, 1_000_000); + d + }); + b }, - ); + total_supplies: { + let mut ts = BTreeMap::new(); + ts.insert(Denomination::NATIVE, 1_000_000); + ts + }, + ..Default::default() + }); + + EVMModule::::init(Genesis { + parameters: Default::default(), + }); let erc20 = load_erc20(); @@ -309,34 +300,25 @@ fn test_c10l_evm_balance_transfer() { let mut mock = mock::Mock::default(); let mut ctx = mock.create_ctx(); - Core::::init( - &mut ctx, - core::Genesis { - parameters: core::Parameters { - max_batch_gas: 10_000_000, - ..Default::default() - }, - }, - ); - - Accounts::init( - &mut ctx, - accounts::Genesis { - balances: BTreeMap::from([( - keys::dave::address(), - BTreeMap::from([(Denomination::NATIVE, 1_000_000)]), - )]), - total_supplies: BTreeMap::from([(Denomination::NATIVE, 1_000_000)]), + Core::::init(core::Genesis { + parameters: core::Parameters { + max_batch_gas: 10_000_000, ..Default::default() }, - ); + }); - EVMModule::::init( - &mut ctx, - Genesis { - parameters: Default::default(), - }, - ); + Accounts::init(accounts::Genesis { + balances: BTreeMap::from([( + keys::dave::address(), + BTreeMap::from([(Denomination::NATIVE, 1_000_000)]), + )]), + total_supplies: BTreeMap::from([(Denomination::NATIVE, 1_000_000)]), + ..Default::default() + }); + + EVMModule::::init(Genesis { + parameters: Default::default(), + }); let recipient = ethabi::Address::repeat_byte(42); let transfer_tx = transaction::Transaction { diff --git a/runtime-sdk/src/dispatcher.rs b/runtime-sdk/src/dispatcher.rs index 48860c5953..f064c6cf54 100644 --- a/runtime-sdk/src/dispatcher.rs +++ b/runtime-sdk/src/dispatcher.rs @@ -967,8 +967,10 @@ mod test { type Parameters = (); } - #[sdk_derive(MethodHandler)] + #[sdk_derive(Module)] impl AlphabetModule { + type Genesis = (); + #[handler(call = "alphabet.ReadOnly")] fn read_only(_ctx: &mut C, _args: ()) -> Result { CurrentStore::with(|store| { @@ -1005,9 +1007,6 @@ mod test { impl module::BlockHandler for AlphabetModule {} impl module::TransactionHandler for AlphabetModule {} - impl module::MigrationHandler for AlphabetModule { - type Genesis = (); - } impl module::InvariantHandler for AlphabetModule {} struct AlphabetRuntime; diff --git a/runtime-sdk/src/modules/accounts/mod.rs b/runtime-sdk/src/modules/accounts/mod.rs index 599cff30c9..17cb72aae4 100644 --- a/runtime-sdk/src/modules/accounts/mod.rs +++ b/runtime-sdk/src/modules/accounts/mod.rs @@ -12,7 +12,7 @@ use thiserror::Error; use crate::{ context::{Context, TxContext}, core::common::quantity::Quantity, - handler, module, + handler, migration, module, module::{Module as _, Parameters as _}, modules, modules::core::{Error as CoreError, API as _}, @@ -730,8 +730,69 @@ impl API for Module { } } -#[sdk_derive(MethodHandler)] +#[sdk_derive(Module)] impl Module { + type Genesis = Genesis; + + #[migration(init)] + pub fn init(genesis: Genesis) { + CurrentStore::with(|store| { + // Create accounts. + let mut store = storage::PrefixStore::new(store, &MODULE_NAME); + let mut accounts = + storage::TypedStore::new(storage::PrefixStore::new(&mut store, &state::ACCOUNTS)); + for (address, account) in genesis.accounts { + accounts.insert(address, account); + } + + // Create balances. + let mut balances = storage::PrefixStore::new(&mut store, &state::BALANCES); + let mut computed_total_supply: BTreeMap = BTreeMap::new(); + for (address, denominations) in genesis.balances.iter() { + let mut account = + storage::TypedStore::new(storage::PrefixStore::new(&mut balances, &address)); + for (denomination, value) in denominations { + account.insert(denomination, value); + + // Update computed total supply. + computed_total_supply + .entry(denomination.clone()) + .and_modify(|v| *v += value) + .or_insert_with(|| *value); + } + } + + // Validate and set total supply. + let mut total_supplies = storage::TypedStore::new(storage::PrefixStore::new( + &mut store, + &state::TOTAL_SUPPLY, + )); + for (denomination, total_supply) in genesis.total_supplies.iter() { + let computed = computed_total_supply + .remove(denomination) + .expect("unexpected total supply"); + assert!( + &computed == total_supply, + "unexpected total supply (expected: {total_supply} got: {computed})", + ); + + total_supplies.insert(denomination, total_supply); + } + for (denomination, total_supply) in computed_total_supply.iter() { + panic!("missing expected total supply: {total_supply} {denomination}",); + } + }); + + // Validate genesis parameters. + genesis + .parameters + .validate_basic() + .expect("invalid genesis parameters"); + + // Set genesis parameters. + Self::set_params(genesis.parameters); + } + #[handler(prefetch = "accounts.Transfer")] fn prefetch_transfer( add_prefix: &mut dyn FnMut(Prefix), @@ -812,94 +873,6 @@ impl module::Module for Module { type Parameters = Parameters; } -impl Module { - /// Initialize state from genesis. - pub fn init(_ctx: &mut C, genesis: Genesis) { - CurrentStore::with(|store| { - // Create accounts. - let mut store = storage::PrefixStore::new(store, &MODULE_NAME); - let mut accounts = - storage::TypedStore::new(storage::PrefixStore::new(&mut store, &state::ACCOUNTS)); - for (address, account) in genesis.accounts { - accounts.insert(address, account); - } - - // Create balances. - let mut balances = storage::PrefixStore::new(&mut store, &state::BALANCES); - let mut computed_total_supply: BTreeMap = BTreeMap::new(); - for (address, denominations) in genesis.balances.iter() { - let mut account = - storage::TypedStore::new(storage::PrefixStore::new(&mut balances, &address)); - for (denomination, value) in denominations { - account.insert(denomination, value); - - // Update computed total supply. - computed_total_supply - .entry(denomination.clone()) - .and_modify(|v| *v += value) - .or_insert_with(|| *value); - } - } - - // Validate and set total supply. - let mut total_supplies = storage::TypedStore::new(storage::PrefixStore::new( - &mut store, - &state::TOTAL_SUPPLY, - )); - for (denomination, total_supply) in genesis.total_supplies.iter() { - let computed = computed_total_supply - .remove(denomination) - .expect("unexpected total supply"); - assert!( - &computed == total_supply, - "unexpected total supply (expected: {total_supply} got: {computed})", - ); - - total_supplies.insert(denomination, total_supply); - } - for (denomination, total_supply) in computed_total_supply.iter() { - panic!("missing expected total supply: {total_supply} {denomination}",); - } - }); - - // Validate genesis parameters. - genesis - .parameters - .validate_basic() - .expect("invalid genesis parameters"); - - // Set genesis parameters. - Self::set_params(genesis.parameters); - } - - /// Migrate state from a previous version. - fn migrate(_ctx: &mut C, _from: u32) -> bool { - // No migrations currently supported. - false - } -} - -impl module::MigrationHandler for Module { - type Genesis = Genesis; - - fn init_or_migrate( - ctx: &mut C, - meta: &mut modules::core::types::Metadata, - genesis: Self::Genesis, - ) -> bool { - let version = meta.versions.get(Self::NAME).copied().unwrap_or_default(); - if version == 0 { - // Initialize state from genesis. - Self::init(ctx, genesis); - meta.versions.insert(Self::NAME.to_owned(), Self::VERSION); - return true; - } - - // Perform migration. - Self::migrate(ctx, version) - } -} - impl module::TransactionHandler for Module { fn authenticate_tx( ctx: &mut C, diff --git a/runtime-sdk/src/modules/accounts/test.rs b/runtime-sdk/src/modules/accounts/test.rs index 80d5d3695e..cf8187aa92 100644 --- a/runtime-sdk/src/modules/accounts/test.rs +++ b/runtime-sdk/src/modules/accounts/test.rs @@ -79,8 +79,10 @@ impl module::Module for TestModule { type Parameters = (); } -#[sdk_derive(MethodHandler)] +#[sdk_derive(Module)] impl TestModule { + type Genesis = (); + #[handler(call = "test.RefundFee")] fn refund_fee(ctx: &mut C, fail: bool) -> Result<(), CoreError> { // Use some gas. @@ -122,167 +124,140 @@ impl TestModule { impl module::BlockHandler for TestModule {} impl module::TransactionHandler for TestModule {} -impl module::MigrationHandler for TestModule { - type Genesis = (); -} impl module::InvariantHandler for TestModule {} #[test] #[should_panic] fn test_init_incorrect_total_supply_1() { - let mut mock = mock::Mock::default(); - let mut ctx = mock.create_ctx(); + let _mock = mock::Mock::default(); - Accounts::init( - &mut ctx, - Genesis { - balances: { - let mut balances = BTreeMap::new(); - // Alice. - balances.insert(keys::alice::address(), { - let mut denominations = BTreeMap::new(); - denominations.insert(Denomination::NATIVE, 1_000_000); - denominations - }); - balances - }, - ..Default::default() + Accounts::init(Genesis { + balances: { + let mut balances = BTreeMap::new(); + // Alice. + balances.insert(keys::alice::address(), { + let mut denominations = BTreeMap::new(); + denominations.insert(Denomination::NATIVE, 1_000_000); + denominations + }); + balances }, - ); + ..Default::default() + }); } #[test] #[should_panic] fn test_init_incorrect_total_supply_2() { - let mut mock = mock::Mock::default(); - let mut ctx = mock.create_ctx(); + let _mock = mock::Mock::default(); - Accounts::init( - &mut ctx, - Genesis { - balances: { - let mut balances = BTreeMap::new(); - // Alice. - balances.insert(keys::alice::address(), { - let mut denominations = BTreeMap::new(); - denominations.insert(Denomination::NATIVE, 1_000_000); - denominations - }); - // Bob. - balances.insert(keys::bob::address(), { - let mut denominations = BTreeMap::new(); - denominations.insert(Denomination::NATIVE, 1_000_000); - denominations - }); - balances - }, - total_supplies: { - let mut total_supplies = BTreeMap::new(); - total_supplies.insert(Denomination::NATIVE, 1_000_000); - total_supplies - }, - ..Default::default() + Accounts::init(Genesis { + balances: { + let mut balances = BTreeMap::new(); + // Alice. + balances.insert(keys::alice::address(), { + let mut denominations = BTreeMap::new(); + denominations.insert(Denomination::NATIVE, 1_000_000); + denominations + }); + // Bob. + balances.insert(keys::bob::address(), { + let mut denominations = BTreeMap::new(); + denominations.insert(Denomination::NATIVE, 1_000_000); + denominations + }); + balances }, - ); + total_supplies: { + let mut total_supplies = BTreeMap::new(); + total_supplies.insert(Denomination::NATIVE, 1_000_000); + total_supplies + }, + ..Default::default() + }); } #[cfg(feature = "unsafe-allow-debug")] #[test] fn test_debug_option_set() { - let mut mock = mock::Mock::default(); - let mut ctx = mock.create_ctx(); + let _mock = mock::Mock::default(); - Accounts::init( - &mut ctx, - Genesis { - parameters: Parameters { - debug_disable_nonce_check: true, - ..Default::default() - }, + Accounts::init(Genesis { + parameters: Parameters { + debug_disable_nonce_check: true, ..Default::default() }, - ); + ..Default::default() + }); } #[cfg(not(feature = "unsafe-allow-debug"))] #[test] #[should_panic] fn test_debug_option_set() { - let mut mock = mock::Mock::default(); - let mut ctx = mock.create_ctx(); + let _mock = mock::Mock::default(); - Accounts::init( - &mut ctx, - Genesis { - parameters: Parameters { - debug_disable_nonce_check: true, - ..Default::default() - }, + Accounts::init(Genesis { + parameters: Parameters { + debug_disable_nonce_check: true, ..Default::default() }, - ); + ..Default::default() + }); } #[test] fn test_init_1() { - let mut mock = mock::Mock::default(); - let mut ctx = mock.create_ctx(); + let _mock = mock::Mock::default(); - Accounts::init( - &mut ctx, - Genesis { - balances: { - let mut balances = BTreeMap::new(); - // Alice. - balances.insert(keys::alice::address(), { - let mut denominations = BTreeMap::new(); - denominations.insert(Denomination::NATIVE, 1_000_000); - denominations - }); - balances - }, - total_supplies: { - let mut total_supplies = BTreeMap::new(); - total_supplies.insert(Denomination::NATIVE, 1_000_000); - total_supplies - }, - ..Default::default() + Accounts::init(Genesis { + balances: { + let mut balances = BTreeMap::new(); + // Alice. + balances.insert(keys::alice::address(), { + let mut denominations = BTreeMap::new(); + denominations.insert(Denomination::NATIVE, 1_000_000); + denominations + }); + balances }, - ); + total_supplies: { + let mut total_supplies = BTreeMap::new(); + total_supplies.insert(Denomination::NATIVE, 1_000_000); + total_supplies + }, + ..Default::default() + }); } #[test] fn test_init_2() { - let mut mock = mock::Mock::default(); - let mut ctx = mock.create_ctx(); + let _mock = mock::Mock::default(); - Accounts::init( - &mut ctx, - Genesis { - balances: { - let mut balances = BTreeMap::new(); - // Alice. - balances.insert(keys::alice::address(), { - let mut denominations = BTreeMap::new(); - denominations.insert(Denomination::NATIVE, 1_000_000); - denominations - }); - // Bob. - balances.insert(keys::bob::address(), { - let mut denominations = BTreeMap::new(); - denominations.insert(Denomination::NATIVE, 1_000_000); - denominations - }); - balances - }, - total_supplies: { - let mut total_supplies = BTreeMap::new(); - total_supplies.insert(Denomination::NATIVE, 2_000_000); - total_supplies - }, - ..Default::default() + Accounts::init(Genesis { + balances: { + let mut balances = BTreeMap::new(); + // Alice. + balances.insert(keys::alice::address(), { + let mut denominations = BTreeMap::new(); + denominations.insert(Denomination::NATIVE, 1_000_000); + denominations + }); + // Bob. + balances.insert(keys::bob::address(), { + let mut denominations = BTreeMap::new(); + denominations.insert(Denomination::NATIVE, 1_000_000); + denominations + }); + balances }, - ); + total_supplies: { + let mut total_supplies = BTreeMap::new(); + total_supplies.insert(Denomination::NATIVE, 2_000_000); + total_supplies + }, + ..Default::default() + }); } #[test] @@ -290,32 +265,29 @@ fn test_api_tx_transfer_disabled() { let mut mock = mock::Mock::default(); let mut ctx = mock.create_ctx(); - Accounts::init( - &mut ctx, - Genesis { - balances: { - let mut balances = BTreeMap::new(); - // Alice. - balances.insert(keys::alice::address(), { - let mut denominations = BTreeMap::new(); - denominations.insert(Denomination::NATIVE, 1_000_000); - denominations - }); - balances - }, - total_supplies: { - let mut total_supplies = BTreeMap::new(); - total_supplies.insert(Denomination::NATIVE, 1_000_000); - total_supplies - }, - parameters: Parameters { - transfers_disabled: true, - debug_disable_nonce_check: false, - ..Default::default() - }, + Accounts::init(Genesis { + balances: { + let mut balances = BTreeMap::new(); + // Alice. + balances.insert(keys::alice::address(), { + let mut denominations = BTreeMap::new(); + denominations.insert(Denomination::NATIVE, 1_000_000); + denominations + }); + balances + }, + total_supplies: { + let mut total_supplies = BTreeMap::new(); + total_supplies.insert(Denomination::NATIVE, 1_000_000); + total_supplies + }, + parameters: Parameters { + transfers_disabled: true, + debug_disable_nonce_check: false, ..Default::default() }, - ); + ..Default::default() + }); let tx = transaction::Transaction { version: 1, @@ -401,37 +373,33 @@ fn test_prefetch() { }); } -pub(crate) fn init_accounts(ctx: &mut C) { - Accounts::init( - ctx, - Genesis { - balances: { - let mut balances = BTreeMap::new(); - // Alice. - balances.insert(keys::alice::address(), { - let mut denominations = BTreeMap::new(); - denominations.insert(Denomination::NATIVE, 1_000_000); - denominations - }); - balances - }, - total_supplies: { - let mut total_supplies = BTreeMap::new(); - total_supplies.insert(Denomination::NATIVE, 1_000_000); - total_supplies - }, - parameters: Parameters { - denomination_infos: { - let mut denomination_infos = BTreeMap::new(); - denomination_infos - .insert(Denomination::NATIVE, DenominationInfo { decimals: 9 }); - denomination_infos - }, - ..Default::default() +pub(crate) fn init_accounts(_ctx: &mut C) { + Accounts::init(Genesis { + balances: { + let mut balances = BTreeMap::new(); + // Alice. + balances.insert(keys::alice::address(), { + let mut denominations = BTreeMap::new(); + denominations.insert(Denomination::NATIVE, 1_000_000); + denominations + }); + balances + }, + total_supplies: { + let mut total_supplies = BTreeMap::new(); + total_supplies.insert(Denomination::NATIVE, 1_000_000); + total_supplies + }, + parameters: Parameters { + denomination_infos: { + let mut denomination_infos = BTreeMap::new(); + denomination_infos.insert(Denomination::NATIVE, DenominationInfo { decimals: 9 }); + denomination_infos }, ..Default::default() }, - ); + ..Default::default() + }); } #[test] @@ -794,7 +762,7 @@ fn test_query_addresses() { ..Default::default() }; - Accounts::init(&mut ctx, gen); + Accounts::init(gen); ctx.with_tx(mock::transaction().into(), |mut tx_ctx, _call| { let accs = Accounts::query_addresses(&mut tx_ctx, AddressesQuery { denomination: d1 }) @@ -819,8 +787,7 @@ fn test_query_addresses() { #[test] fn test_get_all_balances_and_total_supplies_basic() { - let mut mock = mock::Mock::default(); - let mut ctx = mock.create_ctx(); + let _mock = mock::Mock::default(); let alice = keys::alice::address(); let bob = keys::bob::address(); @@ -850,7 +817,7 @@ fn test_get_all_balances_and_total_supplies_basic() { ..Default::default() }; - Accounts::init(&mut ctx, gen); + Accounts::init(gen); let all_bals = Accounts::get_all_balances().expect("get_all_balances should succeed"); for (addr, bals) in &all_bals { @@ -895,8 +862,7 @@ fn test_get_all_balances_and_total_supplies_basic() { #[test] fn test_get_all_balances_and_total_supplies_more() { - let mut mock = mock::Mock::default(); - let mut ctx = mock.create_ctx(); + let _mock = mock::Mock::default(); let dn = Denomination::NATIVE; let d1: Denomination = "den1".parse().unwrap(); @@ -937,7 +903,7 @@ fn test_get_all_balances_and_total_supplies_more() { ..Default::default() }; - Accounts::init(&mut ctx, gen); + Accounts::init(gen); let all_bals = Accounts::get_all_balances().expect("get_all_balances should succeed"); for (addr, bals) in &all_bals { @@ -1044,7 +1010,7 @@ fn test_check_invariants_more() { ..Default::default() }; - Accounts::init(&mut ctx, gen); + Accounts::init(gen); assert!( Accounts::check_invariants(&mut ctx).is_ok(), "initial inv chk should succeed" diff --git a/runtime-sdk/src/modules/consensus/mod.rs b/runtime-sdk/src/modules/consensus/mod.rs index 4c0b35905a..4862be42c8 100644 --- a/runtime-sdk/src/modules/consensus/mod.rs +++ b/runtime-sdk/src/modules/consensus/mod.rs @@ -23,7 +23,7 @@ use oasis_core_runtime::{ use crate::{ context::{Context, TxContext}, - history, module, + history, migration, module, module::{Module as _, Parameters as _}, modules, sdk_derive, types::{ @@ -200,8 +200,22 @@ impl Module { } } -#[sdk_derive(MethodHandler)] -impl Module {} +#[sdk_derive(Module)] +impl Module { + type Genesis = Genesis; + + #[migration(init)] + pub fn init(genesis: Genesis) { + // Validate genesis parameters. + genesis + .parameters + .validate_basic() + .expect("invalid genesis parameters"); + + // Set genesis parameters. + Self::set_params(genesis.parameters); + } +} impl API for Module { fn transfer( @@ -394,50 +408,6 @@ impl module::Module for Module { type Parameters = Parameters; } -impl Module { - /// Initialize state from genesis. - fn init(_ctx: &mut C, genesis: Genesis) { - // TODO: enable loading consensus denomination from consensus state after: - // https://github.com/oasisprotocol/oasis-core/issues/3868 - - // Validate genesis parameters. - genesis - .parameters - .validate_basic() - .expect("invalid genesis parameters"); - - // Set genesis parameters. - Self::set_params(genesis.parameters); - } - - /// Migrate state from a previous version. - fn migrate(_ctx: &mut C, _from: u32) -> bool { - // No migrations currently supported. - false - } -} - -impl module::MigrationHandler for Module { - type Genesis = Genesis; - - fn init_or_migrate( - ctx: &mut C, - meta: &mut modules::core::types::Metadata, - genesis: Self::Genesis, - ) -> bool { - let version = meta.versions.get(Self::NAME).copied().unwrap_or_default(); - if version == 0 { - // Initialize state from genesis. - Self::init(ctx, genesis); - meta.versions.insert(Self::NAME.to_owned(), Self::VERSION); - return true; - } - - // Perform migration. - Self::migrate(ctx, version) - } -} - impl module::TransactionHandler for Module {} impl module::BlockHandler for Module {} diff --git a/runtime-sdk/src/modules/consensus/test.rs b/runtime-sdk/src/modules/consensus/test.rs index 34cdf62614..8e953b4429 100644 --- a/runtime-sdk/src/modules/consensus/test.rs +++ b/runtime-sdk/src/modules/consensus/test.rs @@ -440,35 +440,23 @@ fn test_query_parameters() { #[test] #[should_panic] fn test_init_bad_scaling_factor_1() { - let mut mock = mock::Mock::default(); - let mut ctx = mock.create_ctx(); - - Consensus::init( - &mut ctx, - Genesis { - parameters: Parameters { - consensus_denomination: Denomination::NATIVE, - // Zero scaling factor is invalid. - consensus_scaling_factor: 0, - }, + Consensus::init(Genesis { + parameters: Parameters { + consensus_denomination: Denomination::NATIVE, + // Zero scaling factor is invalid. + consensus_scaling_factor: 0, }, - ); + }); } #[test] #[should_panic] fn test_init_bad_scaling_factor_2() { - let mut mock = mock::Mock::default(); - let mut ctx = mock.create_ctx(); - - Consensus::init( - &mut ctx, - Genesis { - parameters: Parameters { - consensus_denomination: Denomination::NATIVE, - // Scaling factor that is not a power of 10 is invalid. - consensus_scaling_factor: 1230, - }, + Consensus::init(Genesis { + parameters: Parameters { + consensus_denomination: Denomination::NATIVE, + // Scaling factor that is not a power of 10 is invalid. + consensus_scaling_factor: 1230, }, - ); + }); } diff --git a/runtime-sdk/src/modules/consensus_accounts/mod.rs b/runtime-sdk/src/modules/consensus_accounts/mod.rs index 4a6f1ec3b4..11d129ea92 100644 --- a/runtime-sdk/src/modules/consensus_accounts/mod.rs +++ b/runtime-sdk/src/modules/consensus_accounts/mod.rs @@ -19,7 +19,7 @@ use oasis_runtime_sdk_macros::{handler, sdk_derive}; use crate::{ context::{Context, TxContext}, - error, module, + error, migration, module, module::Module as _, modules, modules::core::{Error as CoreError, API as _}, @@ -366,10 +366,18 @@ impl API } } -#[sdk_derive(MethodHandler)] +#[sdk_derive(Module)] impl Module { + type Genesis = Genesis; + + #[migration(init)] + pub fn init(genesis: Genesis) { + // Set genesis parameters. + Self::set_params(genesis.parameters); + } + /// Deposit in the runtime. #[handler(call = "consensus.Deposit")] fn tx_deposit(ctx: &mut C, body: types::Deposit) -> Result<(), Error> { @@ -696,32 +704,6 @@ impl modul type Parameters = Parameters; } -/// Module methods. - -impl module::MigrationHandler - for Module -{ - type Genesis = Genesis; - - fn init_or_migrate( - _ctx: &mut C, - meta: &mut modules::core::types::Metadata, - genesis: Self::Genesis, - ) -> bool { - let version = meta.versions.get(Self::NAME).copied().unwrap_or_default(); - if version == 0 { - // Initialize state from genesis. - // Set genesis parameters. - Self::set_params(genesis.parameters); - meta.versions.insert(Self::NAME.to_owned(), Self::VERSION); - return true; - } - - // Migrations are not supported. - false - } -} - impl module::TransactionHandler for Module { diff --git a/runtime-sdk/src/modules/core/mod.rs b/runtime-sdk/src/modules/core/mod.rs index 58afa086a2..d2261d67b2 100644 --- a/runtime-sdk/src/modules/core/mod.rs +++ b/runtime-sdk/src/modules/core/mod.rs @@ -15,7 +15,7 @@ use crate::{ core::consensus::beacon::EpochTime, dispatcher, error::Error as SDKError, - keymanager, + keymanager, migration, module::{ self, CallResult, InvariantHandler as _, MethodHandler as _, Module as _, ModuleInfoHandler as _, @@ -374,20 +374,6 @@ const CONTEXT_KEY_PRIORITY: &str = "core.Priority"; const CONTEXT_KEY_SENDER_META: &str = "core.SenderMeta"; const CONTEXT_KEY_EPOCH_CHANGED: &str = "core.EpochChanged"; -impl Module { - /// Initialize state from genesis. - pub fn init(_ctx: &mut C, genesis: Genesis) { - // Set genesis parameters. - Self::set_params(genesis.parameters); - } - - /// Migrate state from a previous version. - fn migrate(_ctx: &mut C, _from: u32) -> bool { - // No migrations currently supported. - false - } -} - impl API for Module { type Config = Cfg; @@ -495,8 +481,16 @@ impl API for Module { } } -#[sdk_derive(MethodHandler)] +#[sdk_derive(Module)] impl Module { + type Genesis = Genesis; + + #[migration(init)] + pub fn init(genesis: Genesis) { + // Set genesis parameters. + Self::set_params(genesis.parameters); + } + /// Run a transaction in simulation and return how much gas it uses. This looks up the method /// in the context's method registry. Transactions that fail still use gas, and this query will /// estimate that and return successfully, so do not use this query to see if a transaction will @@ -1031,27 +1025,6 @@ impl module::TransactionHandler for Module { } } -impl module::MigrationHandler for Module { - type Genesis = Genesis; - - fn init_or_migrate( - ctx: &mut C, - meta: &mut types::Metadata, - genesis: Self::Genesis, - ) -> bool { - let version = meta.versions.get(Self::NAME).copied().unwrap_or_default(); - if version == 0 { - // Initialize state from genesis. - Self::init(ctx, genesis); - meta.versions.insert(Self::NAME.to_owned(), Self::VERSION); - return true; - } - - // Perform migration. - Self::migrate(ctx, version) - } -} - impl module::BlockHandler for Module { fn begin_block(ctx: &mut C) { CurrentStore::with(|store| { diff --git a/runtime-sdk/src/modules/core/test.rs b/runtime-sdk/src/modules/core/test.rs index 66f0459eee..70b5bb12ee 100644 --- a/runtime-sdk/src/modules/core/test.rs +++ b/runtime-sdk/src/modules/core/test.rs @@ -215,8 +215,10 @@ impl module::Module for GasWasterModule { type Parameters = (); } -#[sdk_derive(MethodHandler)] +#[sdk_derive(Module)] impl GasWasterModule { + type Genesis = (); + #[handler(call = Self::METHOD_WASTE_GAS)] fn waste_gas( ctx: &mut C, @@ -299,9 +301,6 @@ impl GasWasterModule { impl module::BlockHandler for GasWasterModule {} impl module::TransactionHandler for GasWasterModule {} -impl module::MigrationHandler for GasWasterModule { - type Genesis = (); -} impl module::InvariantHandler for GasWasterModule {} struct Config; diff --git a/runtime-sdk/src/modules/rewards/mod.rs b/runtime-sdk/src/modules/rewards/mod.rs index 8b32a0e5ac..2af655b365 100644 --- a/runtime-sdk/src/modules/rewards/mod.rs +++ b/runtime-sdk/src/modules/rewards/mod.rs @@ -8,6 +8,7 @@ use thiserror::Error; use crate::{ context::Context, core::consensus::beacon, + migration, module::{self, Module as _, Parameters as _}, modules::{self, core::API as _}, runtime::Runtime, @@ -97,12 +98,12 @@ impl module::Module for Module { type Parameters = Parameters; } -#[sdk_derive(MethodHandler)] -impl Module {} - +#[sdk_derive(Module)] impl Module { - /// Initialize state from genesis. - fn init(_ctx: &mut C, genesis: Genesis) { + type Genesis = Genesis; + + #[migration(init)] + fn init(genesis: Genesis) { genesis .parameters .validate_basic() @@ -112,17 +113,7 @@ impl Module { Self::set_params(genesis.parameters); } - /// Migrate state from a previous version. - fn migrate(_ctx: &mut C, from: u32) -> bool { - match from { - 1 => Self::migrate_v1_to_v2(), - 2 => return false, // Current version. - _ => panic!("unsupported source module version: {from}"), - } - - true - } - + #[migration(from = 1)] fn migrate_v1_to_v2() { CurrentStore::with(|store| { // Version 2 removes the LAST_EPOCH storage state which was at 0x01. @@ -132,27 +123,6 @@ impl Module { } } -impl module::MigrationHandler for Module { - type Genesis = Genesis; - - fn init_or_migrate( - ctx: &mut C, - meta: &mut modules::core::types::Metadata, - genesis: Self::Genesis, - ) -> bool { - let version = meta.versions.get(Self::NAME).copied().unwrap_or_default(); - if version == 0 { - // Initialize state from genesis. - Self::init(ctx, genesis); - meta.versions.insert(Self::NAME.to_owned(), Self::VERSION); - return true; - } - - // Perform migration. - Self::migrate(ctx, version) - } -} - impl module::TransactionHandler for Module {} impl module::BlockHandler for Module {