From 7497b5c1daaef008ff80b042d8c76d9c756beb74 Mon Sep 17 00:00:00 2001 From: teor Date: Mon, 20 Jan 2025 16:10:25 +1000 Subject: [PATCH] Add storage migration for new domain registry fields --- crates/pallet-domains/src/lib.rs | 3 +- .../pallet-domains/src/migration_v2_to_v3.rs | 299 ++++++++++++++++++ crates/subspace-runtime/src/lib.rs | 4 +- 3 files changed, 304 insertions(+), 2 deletions(-) create mode 100644 crates/pallet-domains/src/migration_v2_to_v3.rs diff --git a/crates/pallet-domains/src/lib.rs b/crates/pallet-domains/src/lib.rs index fbf91ba2ac..ff00b60e80 100644 --- a/crates/pallet-domains/src/lib.rs +++ b/crates/pallet-domains/src/lib.rs @@ -27,6 +27,7 @@ mod tests; pub mod block_tree; mod bundle_storage_fund; pub mod domain_registry; +pub mod migration_v2_to_v3; pub mod runtime_registry; mod staking; mod staking_epoch; @@ -161,7 +162,7 @@ pub type BlockTreeNodeFor = crate::block_tree::BlockTreeNode< >; /// The current storage version. -const STORAGE_VERSION: StorageVersion = StorageVersion::new(1); +const STORAGE_VERSION: StorageVersion = StorageVersion::new(3); /// The number of bundle of a particular domain to be included in the block is probabilistic /// and based on the consensus chain slot probability and domain bundle slot probability, usually diff --git a/crates/pallet-domains/src/migration_v2_to_v3.rs b/crates/pallet-domains/src/migration_v2_to_v3.rs new file mode 100644 index 0000000000..ee32a51404 --- /dev/null +++ b/crates/pallet-domains/src/migration_v2_to_v3.rs @@ -0,0 +1,299 @@ +//! Migration module for pallet-domains storage version v2 to v3. +//! +//! TODO: remove this module after it has been deployed to Taurus. + +use crate::{Config, Pallet}; +use frame_support::migrations::VersionedMigration; +use frame_support::traits::UncheckedOnRuntimeUpgrade; +use frame_support::weights::Weight; + +pub type VersionCheckedMigrateDomainsV2ToV3 = VersionedMigration< + 2, + 3, + VersionUncheckedMigrateV2ToV3, + Pallet, + ::DbWeight, +>; + +pub struct VersionUncheckedMigrateV2ToV3(sp_std::marker::PhantomData); +impl UncheckedOnRuntimeUpgrade for VersionUncheckedMigrateV2ToV3 { + fn on_runtime_upgrade() -> Weight { + domain_registry_structure_migration::migrate_domain_registry_structure::() + } +} + +mod domain_registry_structure_migration { + use crate::domain_registry::{DomainConfig as DomainConfigV3, DomainObject as DomainObjectV3}; + use crate::pallet::DomainRegistry as DomainRegistryV3; + use crate::runtime_registry::DomainRuntimeInfo as DomainRuntimeInfoV3; + use crate::{BalanceOf, BlockNumberFor, Config, Pallet, ReceiptHashFor}; + use codec::{Decode, Encode}; + use domain_runtime_primitives::{EVMChainId, MultiAccountId}; + use frame_support::pallet_prelude::{OptionQuery, TypeInfo, Weight}; + use frame_support::{storage_alias, Identity}; + use scale_info::prelude::string::String; + use sp_core::Get; + use sp_domains::{ + AutoIdDomainRuntimeConfig, + DomainId, + // changed in V3, but we use Into to convert to it + // DomainRuntimeConfig as DomainRuntimeConfigV3, + EvmDomainRuntimeConfig, + OperatorAllowList, + RuntimeId, + }; + use sp_runtime::Vec; + + #[derive(TypeInfo, Debug, Encode, Decode, Clone, PartialEq, Eq)] + pub struct DomainConfigV2 { + /// A user defined name for this domain, should be a human-readable UTF-8 encoded string. + pub domain_name: String, + /// A pointer to the `RuntimeRegistry` entry for this domain. + pub runtime_id: RuntimeId, + /// The max bundle size for this domain, may not exceed the system-wide `MaxDomainBlockSize` limit. + pub max_bundle_size: u32, + /// The max bundle weight for this domain, may not exceed the system-wide `MaxDomainBlockWeight` limit. + pub max_bundle_weight: Weight, + /// The probability of successful bundle in a slot (active slots coefficient). This defines the + /// expected bundle production rate, must be `> 0` and `≤ 1`. + pub bundle_slot_probability: (u64, u64), + /// Allowed operators to operate for this domain. + pub operator_allow_list: OperatorAllowList, + // Initial balances for Domain. + pub initial_balances: Vec<(MultiAccountId, Balance)>, + } + + /// Domain runtime specific information to create domain raw genesis. + #[derive(TypeInfo, Debug, Encode, Decode, Clone, PartialEq, Eq, Copy)] + #[allow(clippy::upper_case_acronyms)] + pub enum DomainRuntimeInfoV2 { + EVM { chain_id: EVMChainId }, + AutoId, + } + + #[derive(TypeInfo, Debug, Encode, Decode, Clone, PartialEq, Eq)] + pub struct DomainObjectV2 { + /// The address of the domain creator, used to validate updating the domain config. + pub owner_account_id: AccountId, + /// The consensus chain block number when the domain first instantiated. + pub created_at: Number, + /// The hash of the genesis execution receipt for this domain. + pub genesis_receipt_hash: ReceiptHash, + /// The domain config. + pub domain_config: DomainConfigV2, + /// Domain runtime specific information. + pub domain_runtime_info: DomainRuntimeInfoV2, + /// The amount of balance hold on the domain owner account + pub domain_instantiation_deposit: Balance, + } + + #[storage_alias] + pub(super) type DomainRegistry = StorageMap< + Pallet, + Identity, + DomainId, + DomainObjectV2< + BlockNumberFor, + ReceiptHashFor, + ::AccountId, + BalanceOf, + >, + OptionQuery, + >; + + pub(super) fn migrate_domain_registry_structure() -> Weight { + let mut domain_count = 0; + + DomainRegistryV3::::translate_values::< + DomainObjectV2, ReceiptHashFor, T::AccountId, BalanceOf>, + _, + >(|domain_object_v2| { + domain_count += 1; + + let (domain_runtime_config, domain_runtime_info) = + match domain_object_v2.domain_runtime_info { + DomainRuntimeInfoV2::EVM { chain_id } => { + // Added in V3 + let domain_runtime_config = EvmDomainRuntimeConfig::default(); + ( + domain_runtime_config.clone().into(), + DomainRuntimeInfoV3::Evm { + chain_id, + domain_runtime_config, + }, + ) + } + DomainRuntimeInfoV2::AutoId => { + // Added in V3 + let domain_runtime_config = AutoIdDomainRuntimeConfig::default(); + ( + domain_runtime_config.clone().into(), + DomainRuntimeInfoV3::AutoId { + domain_runtime_config, + }, + ) + } + }; + + Some(DomainObjectV3 { + owner_account_id: domain_object_v2.owner_account_id, + created_at: domain_object_v2.created_at, + genesis_receipt_hash: domain_object_v2.genesis_receipt_hash, + domain_config: DomainConfigV3 { + domain_name: domain_object_v2.domain_config.domain_name, + runtime_id: domain_object_v2.domain_config.runtime_id, + max_bundle_size: domain_object_v2.domain_config.max_bundle_size, + max_bundle_weight: domain_object_v2.domain_config.max_bundle_weight, + bundle_slot_probability: domain_object_v2.domain_config.bundle_slot_probability, + operator_allow_list: domain_object_v2.domain_config.operator_allow_list, + initial_balances: domain_object_v2.domain_config.initial_balances, + // Added in V3 + domain_runtime_config, + }, + domain_runtime_info, + domain_instantiation_deposit: domain_object_v2.domain_instantiation_deposit, + }) + }); + + // 1 read and 1 write per domain + T::DbWeight::get().reads_writes(domain_count, domain_count) + } +} + +#[cfg(test)] +mod tests { + use super::domain_registry_structure_migration::{ + migrate_domain_registry_structure, DomainConfigV2, DomainObjectV2, DomainRegistry, + DomainRuntimeInfoV2, + }; + use crate::domain_registry::{DomainConfig as DomainConfigV3, DomainObject as DomainObjectV3}; + use crate::pallet::DomainRegistry as DomainRegistryV3; + use crate::runtime_registry::DomainRuntimeInfo as DomainRuntimeInfoV3; + use crate::tests::{new_test_ext, Test}; + use sp_domains::{ + AutoIdDomainRuntimeConfig, DomainId, DomainRuntimeConfig as DomainRuntimeConfigV3, + EvmDomainRuntimeConfig, OperatorAllowList, PermissionedActionAllowedBy, + }; + + #[test] + fn test_domain_registry_structure_migration_evm() { + let mut ext = new_test_ext(); + let domain_id: DomainId = 0.into(); + let chain_id = 8u32.into(); + let domain = DomainObjectV2 { + owner_account_id: 1u32.into(), + created_at: 2u32.into(), + genesis_receipt_hash: Default::default(), + domain_config: DomainConfigV2 { + domain_name: "test-evm-migrate".to_string(), + runtime_id: 3u32, + max_bundle_size: 4, + max_bundle_weight: 5.into(), + bundle_slot_probability: (6, 7), + operator_allow_list: OperatorAllowList::Anyone, + initial_balances: vec![], + }, + domain_runtime_info: DomainRuntimeInfoV2::EVM { chain_id }, + domain_instantiation_deposit: 9u32.into(), + }; + + ext.execute_with(|| DomainRegistry::::set(domain_id, Some(domain.clone()))); + + ext.commit_all().unwrap(); + + ext.execute_with(|| { + let weights = migrate_domain_registry_structure::(); + assert_eq!( + weights, + ::DbWeight::get().reads_writes(1, 1), + ); + assert_eq!( + DomainRegistryV3::::get(domain_id), + Some(DomainObjectV3 { + owner_account_id: domain.owner_account_id, + created_at: domain.created_at, + genesis_receipt_hash: domain.genesis_receipt_hash, + domain_config: DomainConfigV3 { + domain_name: domain.domain_config.domain_name, + runtime_id: domain.domain_config.runtime_id, + max_bundle_size: domain.domain_config.max_bundle_size, + max_bundle_weight: domain.domain_config.max_bundle_weight, + bundle_slot_probability: domain.domain_config.bundle_slot_probability, + operator_allow_list: domain.domain_config.operator_allow_list, + initial_balances: domain.domain_config.initial_balances, + domain_runtime_config: DomainRuntimeConfigV3::Evm(EvmDomainRuntimeConfig { + initial_contract_creation_allow_list: + PermissionedActionAllowedBy::Anyone + }), + }, + domain_runtime_info: DomainRuntimeInfoV3::Evm { + chain_id, + domain_runtime_config: EvmDomainRuntimeConfig { + initial_contract_creation_allow_list: + PermissionedActionAllowedBy::Anyone + }, + }, + domain_instantiation_deposit: domain.domain_instantiation_deposit, + }) + ); + }); + } + + #[test] + fn test_domain_registry_structure_migration_auto_id() { + let mut ext = new_test_ext(); + let domain_id: DomainId = 10.into(); + let domain = DomainObjectV2 { + owner_account_id: 11u32.into(), + created_at: 12u32.into(), + genesis_receipt_hash: Default::default(), + domain_config: DomainConfigV2 { + domain_name: "test-auto-id-migrate".to_string(), + runtime_id: 13u32, + max_bundle_size: 14, + max_bundle_weight: 15.into(), + bundle_slot_probability: (16, 17), + operator_allow_list: OperatorAllowList::Anyone, + initial_balances: vec![], + }, + domain_runtime_info: DomainRuntimeInfoV2::AutoId, + domain_instantiation_deposit: 19u32.into(), + }; + + ext.execute_with(|| DomainRegistry::::set(domain_id, Some(domain.clone()))); + + ext.commit_all().unwrap(); + + ext.execute_with(|| { + let weights = migrate_domain_registry_structure::(); + assert_eq!( + weights, + ::DbWeight::get().reads_writes(1, 1), + ); + assert_eq!( + DomainRegistryV3::::get(domain_id), + Some(DomainObjectV3 { + owner_account_id: domain.owner_account_id, + created_at: domain.created_at, + genesis_receipt_hash: domain.genesis_receipt_hash, + domain_config: DomainConfigV3 { + domain_name: domain.domain_config.domain_name, + runtime_id: domain.domain_config.runtime_id, + max_bundle_size: domain.domain_config.max_bundle_size, + max_bundle_weight: domain.domain_config.max_bundle_weight, + bundle_slot_probability: domain.domain_config.bundle_slot_probability, + operator_allow_list: domain.domain_config.operator_allow_list, + initial_balances: domain.domain_config.initial_balances, + domain_runtime_config: DomainRuntimeConfigV3::AutoId( + AutoIdDomainRuntimeConfig {} + ), + }, + domain_runtime_info: DomainRuntimeInfoV3::AutoId { + domain_runtime_config: AutoIdDomainRuntimeConfig {} + }, + domain_instantiation_deposit: domain.domain_instantiation_deposit, + }) + ); + }); + } +} diff --git a/crates/subspace-runtime/src/lib.rs b/crates/subspace-runtime/src/lib.rs index ed930abdf7..a766fc98b4 100644 --- a/crates/subspace-runtime/src/lib.rs +++ b/crates/subspace-runtime/src/lib.rs @@ -136,7 +136,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: Cow::Borrowed("subspace"), impl_name: Cow::Borrowed("subspace"), authoring_version: 0, - spec_version: 2, + spec_version: 12, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 0, @@ -978,6 +978,8 @@ pub type Executive = frame_executive::Executive< frame_system::ChainContext, Runtime, AllPalletsWithSystem, + // TODO: remove once migration has been deployed to Taurus + pallet_domains::migration_v2_to_v3::VersionCheckedMigrateDomainsV2ToV3, >; fn extract_segment_headers(ext: &UncheckedExtrinsic) -> Option> {