diff --git a/Cargo.lock b/Cargo.lock index 5f1f02f3c8..c1a2043b69 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7217,7 +7217,9 @@ dependencies = [ "frame-system", "parity-scale-codec", "scale-info", + "sp-core", "sp-domains", + "sp-io", "sp-runtime", ] @@ -10854,7 +10856,6 @@ dependencies = [ "sp-inherents", "sp-keystore", "sp-runtime", - "sp-runtime-interface", "sp-state-machine", "sp-std", "sp-trie", @@ -11940,13 +11941,13 @@ dependencies = [ "evm-domain-test-runtime", "fp-evm", "futures", + "parity-scale-codec", "sc-chain-spec", "sc-client-api", "sc-consensus-subspace", "sc-executor", "sc-service", "schnorrkel", - "serde_json", "sp-api", "sp-consensus-subspace", "sp-core", @@ -12029,7 +12030,6 @@ dependencies = [ "parking_lot 0.12.1", "rand 0.8.5", "sc-block-builder", - "sc-client-api", "sc-consensus", "sc-consensus-fraud-proof", "sc-executor", @@ -12048,14 +12048,12 @@ dependencies = [ "sp-consensus-subspace", "sp-core", "sp-domains", - "sp-externalities", "sp-inherents", "sp-keyring", "sp-runtime", "sp-timestamp", "subspace-core-primitives", "subspace-fraud-proof", - "subspace-node", "subspace-runtime-primitives", "subspace-service", "subspace-test-client", diff --git a/crates/pallet-domains/src/benchmarking.rs b/crates/pallet-domains/src/benchmarking.rs index 77712c4a18..1f41f62451 100644 --- a/crates/pallet-domains/src/benchmarking.rs +++ b/crates/pallet-domains/src/benchmarking.rs @@ -165,13 +165,13 @@ mod benchmarks { #[extrinsic_call] _( RawOrigin::Root, - b"evm-domain".to_vec(), + "evm-domain".to_owned(), RuntimeType::Evm, runtime_blob, ); let runtime_obj = RuntimeRegistry::::get(runtime_id).expect("runtime object must exist"); - assert_eq!(runtime_obj.runtime_name, b"evm-domain".to_vec()); + assert_eq!(runtime_obj.runtime_name, "evm-domain".to_owned()); assert_eq!(runtime_obj.runtime_type, RuntimeType::Evm); assert_eq!(runtime_obj.hash, runtime_hash); assert_eq!(NextRuntimeId::::get(), runtime_id + 1); @@ -187,7 +187,7 @@ mod benchmarks { // version to 0 to bypass the `can_upgrade_code` check when calling `upgrade_domain_runtime` assert_ok!(Domains::::register_domain_runtime( RawOrigin::Root.into(), - b"evm-domain".to_vec(), + "evm-domain".to_owned(), RuntimeType::Evm, runtime_blob.clone() )); @@ -221,7 +221,7 @@ mod benchmarks { let runtime_id = register_runtime::(); let domain_id = NextDomainId::::get(); let domain_config = DomainConfig { - domain_name: b"evm-domain".to_vec(), + domain_name: "evm-domain".to_owned(), runtime_id, max_block_size: 1024, max_block_weight: Weight::from_parts(1, 0), @@ -453,7 +453,7 @@ mod benchmarks { assert_ok!(Domains::::register_domain_runtime( RawOrigin::Root.into(), - b"evm-domain".to_vec(), + "evm-domain".to_owned(), RuntimeType::Evm, runtime_blob, )); @@ -475,7 +475,7 @@ mod benchmarks { let runtime_id = register_runtime::(); let domain_id = NextDomainId::::get(); let domain_config = DomainConfig { - domain_name: b"evm-domain".to_vec(), + domain_name: "evm-domain".to_owned(), runtime_id, max_block_size: 1024, max_block_weight: Weight::from_parts(1, 0), diff --git a/crates/pallet-domains/src/domain_registry.rs b/crates/pallet-domains/src/domain_registry.rs index cb3481a368..d511f0697a 100644 --- a/crates/pallet-domains/src/domain_registry.rs +++ b/crates/pallet-domains/src/domain_registry.rs @@ -6,6 +6,7 @@ use crate::staking::StakingSummary; use crate::{ Config, DomainRegistry, ExecutionReceiptOf, HoldIdentifier, NextDomainId, RuntimeRegistry, }; +use alloc::string::String; use codec::{Decode, Encode}; use frame_support::traits::fungible::{Inspect, MutateHold}; use frame_support::traits::tokens::{Fortitude, Preservation}; @@ -14,15 +15,11 @@ use frame_support::{ensure, PalletError}; use frame_system::pallet_prelude::*; use scale_info::TypeInfo; use sp_core::Get; -use sp_domains::domain::generate_genesis_state_root; -use sp_domains::{ - DomainId, DomainInstanceData, DomainsDigestItem, ReceiptHash, RuntimeId, RuntimeType, -}; +use sp_domains::{DomainId, DomainsDigestItem, ReceiptHash, RuntimeId}; use sp_runtime::traits::{CheckedAdd, Zero}; use sp_runtime::DigestItem; use sp_std::collections::btree_map::BTreeMap; use sp_std::collections::btree_set::BTreeSet; -use sp_std::vec::Vec; /// Domain registry specific errors #[derive(TypeInfo, Encode, Decode, PalletError, Debug, PartialEq)] @@ -42,7 +39,7 @@ pub enum Error { #[derive(TypeInfo, Debug, Encode, Decode, Clone, PartialEq, Eq)] pub struct DomainConfig { /// A user defined name for this domain, should be a human-readable UTF-8 encoded string. - pub domain_name: Vec, + pub domain_name: String, /// A pointer to the `RuntimeRegistry` entry for this domain. pub runtime_id: RuntimeId, /// The max block size for this domain, may not exceed the system-wide `MaxDomainBlockSize` limit. @@ -66,12 +63,6 @@ pub struct DomainObject { pub genesis_receipt_hash: ReceiptHash, /// The domain config. pub domain_config: DomainConfig, - /// The genesis config of the domain, encoded in json format. - // - /// NOTE: the WASM code in the `system-pallet` genesis config should be empty to avoid - /// redundancy with the `RuntimeRegistry`. Currently, this value only set to `Some` for - /// the genesis domain instance. - pub raw_genesis_config: Option>, } pub(crate) fn can_instantiate_domain( @@ -120,7 +111,6 @@ pub(crate) fn do_instantiate_domain( domain_config: DomainConfig, owner_account_id: T::AccountId, created_at: BlockNumberFor, - raw_genesis_config: Option>, ) -> Result { can_instantiate_domain::(&owner_account_id, &domain_config)?; @@ -129,12 +119,12 @@ pub(crate) fn do_instantiate_domain( let genesis_receipt = { let runtime_obj = RuntimeRegistry::::get(domain_config.runtime_id) .expect("Runtime object must exist as checked in `can_instantiate_domain`; qed"); - initialize_genesis_receipt::( - domain_id, - runtime_obj.runtime_type, - runtime_obj.code, - raw_genesis_config.clone(), - )? + + let state_version = runtime_obj.version.state_version(); + let raw_genesis = runtime_obj.into_complete_raw_genesis(domain_id); + let state_root = raw_genesis.state_root::(state_version); + + ExecutionReceiptOf::::genesis(state_root) }; let genesis_receipt_hash = genesis_receipt.hash(); @@ -143,7 +133,6 @@ pub(crate) fn do_instantiate_domain( created_at, genesis_receipt_hash, domain_config, - raw_genesis_config, }; DomainRegistry::::insert(domain_id, domain_obj); @@ -176,45 +165,16 @@ pub(crate) fn do_instantiate_domain( Ok(domain_id) } -fn initialize_genesis_receipt( - domain_id: DomainId, - runtime_type: RuntimeType, - runtime_code: Vec, - raw_genesis_config: Option>, -) -> Result, Error> { - let consensus_genesis_hash = frame_system::Pallet::::block_hash(BlockNumberFor::::zero()); - // The `GenesisReceiptExtension` is unavailable during runtime benchmarking, remove once - // https://github.com/paritytech/substrate/issues/14733 is resolved. - let genesis_state_root = if cfg!(feature = "runtime-benchmarks") { - Default::default() - } else { - generate_genesis_state_root( - domain_id, - DomainInstanceData { - runtime_type, - runtime_code, - raw_genesis_config, - }, - ) - .ok_or(Error::FailedToGenerateGenesisStateRoot)? - }; - Ok(ExecutionReceiptOf::::genesis( - consensus_genesis_hash, - genesis_state_root.into(), - )) -} - #[cfg(test)] mod tests { use super::*; use crate::pallet::{DomainRegistry, NextDomainId, RuntimeRegistry}; use crate::runtime_registry::RuntimeObject; - use crate::tests::{new_test_ext, GenesisStateRootGenerater, Test}; + use crate::tests::{new_test_ext, Test}; use frame_support::traits::Currency; - use sp_domains::GenesisReceiptExtension; + use sp_domains::storage::RawGenesis; use sp_std::vec; use sp_version::RuntimeVersion; - use std::sync::Arc; type Balances = pallet_balances::Pallet; @@ -224,7 +184,7 @@ mod tests { let created_at = 0u64; // Construct an invalid domain config initially let mut domain_config = DomainConfig { - domain_name: vec![0; 1024], + domain_name: String::from_utf8(vec![0; 1024]).unwrap(), runtime_id: 0, max_block_size: u32::MAX, max_block_weight: Weight::MAX, @@ -233,34 +193,31 @@ mod tests { }; let mut ext = new_test_ext(); - ext.register_extension(GenesisReceiptExtension::new(Arc::new( - GenesisStateRootGenerater, - ))); ext.execute_with(|| { assert_eq!(NextDomainId::::get(), 0.into()); // Failed to instantiate domain due to `domain_name` too long assert_eq!( - do_instantiate_domain::(domain_config.clone(), creator, created_at, None), + do_instantiate_domain::(domain_config.clone(), creator, created_at), Err(Error::DomainNameTooLong) ); // Recorrect `domain_name` - domain_config.domain_name = b"evm-domain".to_vec(); + domain_config.domain_name = "evm-domain".to_owned(); // Failed to instantiate domain due to using unregistered runtime id assert_eq!( - do_instantiate_domain::(domain_config.clone(), creator, created_at, None), + do_instantiate_domain::(domain_config.clone(), creator, created_at), Err(Error::RuntimeNotFound) ); // Register runtime id RuntimeRegistry::::insert( domain_config.runtime_id, RuntimeObject { - runtime_name: b"evm".to_vec(), + runtime_name: "evm".to_owned(), runtime_type: Default::default(), runtime_upgrades: 0, hash: Default::default(), - code: vec![1, 2, 3, 4], + raw_genesis: RawGenesis::dummy(vec![1, 2, 3, 4]), version: RuntimeVersion { spec_name: "test".into(), spec_version: 1, @@ -275,7 +232,7 @@ mod tests { // Failed to instantiate domain due to exceed max domain block size limit assert_eq!( - do_instantiate_domain::(domain_config.clone(), creator, created_at, None), + do_instantiate_domain::(domain_config.clone(), creator, created_at), Err(Error::ExceedMaxDomainBlockSize) ); // Recorrect `max_block_size` @@ -283,7 +240,7 @@ mod tests { // Failed to instantiate domain due to exceed max domain block weight limit assert_eq!( - do_instantiate_domain::(domain_config.clone(), creator, created_at, None), + do_instantiate_domain::(domain_config.clone(), creator, created_at), Err(Error::ExceedMaxDomainBlockWeight) ); // Recorrect `max_block_weight` @@ -291,12 +248,12 @@ mod tests { // Failed to instantiate domain due to invalid `target_bundles_per_block` assert_eq!( - do_instantiate_domain::(domain_config.clone(), creator, created_at, None), + do_instantiate_domain::(domain_config.clone(), creator, created_at), Err(Error::InvalidBundlesPerBlock) ); domain_config.target_bundles_per_block = u32::MAX; assert_eq!( - do_instantiate_domain::(domain_config.clone(), creator, created_at, None), + do_instantiate_domain::(domain_config.clone(), creator, created_at), Err(Error::InvalidBundlesPerBlock) ); // Recorrect `target_bundles_per_block` @@ -304,22 +261,22 @@ mod tests { // Failed to instantiate domain due to invalid `bundle_slot_probability` assert_eq!( - do_instantiate_domain::(domain_config.clone(), creator, created_at, None), + do_instantiate_domain::(domain_config.clone(), creator, created_at), Err(Error::InvalidSlotProbability) ); domain_config.bundle_slot_probability = (1, 0); assert_eq!( - do_instantiate_domain::(domain_config.clone(), creator, created_at, None), + do_instantiate_domain::(domain_config.clone(), creator, created_at), Err(Error::InvalidSlotProbability) ); domain_config.bundle_slot_probability = (0, 1); assert_eq!( - do_instantiate_domain::(domain_config.clone(), creator, created_at, None), + do_instantiate_domain::(domain_config.clone(), creator, created_at), Err(Error::InvalidSlotProbability) ); domain_config.bundle_slot_probability = (2, 1); assert_eq!( - do_instantiate_domain::(domain_config.clone(), creator, created_at, None), + do_instantiate_domain::(domain_config.clone(), creator, created_at), Err(Error::InvalidSlotProbability) ); // Recorrect `bundle_slot_probability` @@ -327,7 +284,7 @@ mod tests { // Failed to instantiate domain due to creator don't have enough fund assert_eq!( - do_instantiate_domain::(domain_config.clone(), creator, created_at, None), + do_instantiate_domain::(domain_config.clone(), creator, created_at), Err(Error::InsufficientFund) ); // Set enough fund to creator @@ -339,8 +296,7 @@ mod tests { // `instantiate_domain` must success now let domain_id = - do_instantiate_domain::(domain_config.clone(), creator, created_at, None) - .unwrap(); + do_instantiate_domain::(domain_config.clone(), creator, created_at).unwrap(); let domain_obj = DomainRegistry::::get(domain_id).unwrap(); assert_eq!(domain_obj.owner_account_id, creator); @@ -352,7 +308,7 @@ mod tests { // cannot use the locked funds to create a new domain instance assert!( - do_instantiate_domain::(domain_config, creator, created_at, None) + do_instantiate_domain::(domain_config, creator, created_at) == Err(Error::InsufficientFund) ) }); diff --git a/crates/pallet-domains/src/lib.rs b/crates/pallet-domains/src/lib.rs index 4f754091fc..e0215f80f3 100644 --- a/crates/pallet-domains/src/lib.rs +++ b/crates/pallet-domains/src/lib.rs @@ -31,6 +31,8 @@ mod staking; mod staking_epoch; pub mod weights; +extern crate alloc; + use crate::block_tree::verify_execution_receipt; use crate::staking::{do_nominate_operator, Operator, OperatorStatus}; use codec::{Decode, Encode}; @@ -130,6 +132,7 @@ mod pallet { use crate::{ BalanceOf, ElectionVerificationParams, HoldIdentifier, NominatorId, OpaqueBundleOf, }; + use alloc::string::String; use codec::FullCodec; use frame_support::pallet_prelude::*; use frame_support::traits::fungible::{InspectHold, Mutate, MutateHold}; @@ -146,8 +149,8 @@ mod pallet { RuntimeType, }; use sp_runtime::traits::{ - AtLeast32BitUnsigned, BlockNumberProvider, Bounded, CheckEqual, CheckedAdd, MaybeDisplay, - One, SimpleBitOps, Zero, + AtLeast32BitUnsigned, BlockNumberProvider, Bounded, CheckEqual, CheckedAdd, Hash, + MaybeDisplay, One, SimpleBitOps, Zero, }; use sp_runtime::SaturatedConversion; use sp_std::boxed::Box; @@ -196,6 +199,9 @@ mod pallet { + Into + From; + /// The domain hashing algorithm. + type DomainHashing: Hash + TypeInfo; + /// Same with `pallet_subspace::Config::ConfirmationDepthK`. #[pallet::constant] type ConfirmationDepthK: Get>; @@ -280,9 +286,6 @@ mod pallet { #[pallet::constant] type MaxPendingStakingOperation: Get; - #[pallet::constant] - type SudoId: Get; - /// Randomness source. type Randomness: RandomnessT>; @@ -316,7 +319,7 @@ mod pallet { BlockNumberFor, Identity, RuntimeId, - ScheduledRuntimeUpgrade, + ScheduledRuntimeUpgrade, OptionQuery, >; @@ -543,16 +546,11 @@ mod pallet { pub(super) type HeadDomainNumber = StorageMap<_, Identity, DomainId, T::DomainNumber, ValueQuery>; - /// The genesis domian that scheduled to register at block #1, should be removed once - /// https://github.com/paritytech/substrate/issues/14541 is resolved. - #[pallet::storage] - type PendingGenesisDomain = - StorageValue<_, GenesisDomain, OptionQuery>; - /// A temporary storage to hold any previous epoch details for a given domain /// if the epoch transitioned in this block so that all the submitted bundles /// within this block are verified. - /// The storage is cleared on block finalization. + /// TODO: The storage is cleared on block finalization that means this storage is already cleared when + /// verifying the `submit_bundle` extrinsic and not used at all #[pallet::storage] pub(super) type LastEpochStakingDistribution = StorageMap<_, Identity, DomainId, ElectionVerificationParams>, OptionQuery>; @@ -950,16 +948,23 @@ mod pallet { #[pallet::weight(T::WeightInfo::register_domain_runtime())] pub fn register_domain_runtime( origin: OriginFor, - runtime_name: Vec, + runtime_name: String, runtime_type: RuntimeType, - code: Vec, + // TODO: we can use `RawGenesis` as argument directly to avoid decoding but the in tool like + // `polkadot.js` it will required the user to provide each field of the struct type and not + // support upload file which will brings bad UX. + raw_genesis_storage: Vec, ) -> DispatchResult { ensure_root(origin)?; let block_number = frame_system::Pallet::::current_block_number(); - let runtime_id = - do_register_runtime::(runtime_name, runtime_type.clone(), code, block_number) - .map_err(Error::::from)?; + let runtime_id = do_register_runtime::( + runtime_name, + runtime_type.clone(), + raw_genesis_storage, + block_number, + ) + .map_err(Error::::from)?; Self::deposit_event(Event::DomainRuntimeCreated { runtime_id, @@ -974,13 +979,14 @@ mod pallet { pub fn upgrade_domain_runtime( origin: OriginFor, runtime_id: RuntimeId, - code: Vec, + raw_genesis_storage: Vec, ) -> DispatchResult { ensure_root(origin)?; let block_number = frame_system::Pallet::::current_block_number(); - let scheduled_at = do_schedule_runtime_upgrade::(runtime_id, code, block_number) - .map_err(Error::::from)?; + let scheduled_at = + do_schedule_runtime_upgrade::(runtime_id, raw_genesis_storage, block_number) + .map_err(Error::::from)?; Self::deposit_event(Event::DomainRuntimeUpgradeScheduled { runtime_id, @@ -1044,19 +1050,12 @@ mod pallet { pub fn instantiate_domain( origin: OriginFor, domain_config: DomainConfig, - raw_genesis: Vec, ) -> DispatchResult { - let (who, raw_genesis) = if raw_genesis.is_empty() { - (ensure_signed(origin)?, None) - } else { - // TODO: remove once XDM is finished - ensure_root(origin)?; - (T::SudoId::get(), Some(raw_genesis)) - }; + let who = ensure_signed(origin)?; let created_at = frame_system::Pallet::::current_block_number(); - let domain_id = do_instantiate_domain::(domain_config, who, created_at, raw_genesis) + let domain_id = do_instantiate_domain::(domain_config, who, created_at) .map_err(Error::::from)?; Self::deposit_event(Event::DomainInstantiated { domain_id }); @@ -1174,11 +1173,45 @@ mod pallet { #[pallet::genesis_build] impl BuildGenesisConfig for GenesisConfig { fn build(&self) { - // Delay the genesis domain register to block #1 due to the required `GenesisReceiptExtension` is not - // registered during genesis storage build, remove once https://github.com/paritytech/substrate/issues/14541 - // is resolved. - if let Some(genesis_domain) = &self.genesis_domain { - PendingGenesisDomain::::set(Some(genesis_domain.clone())); + if let Some(genesis_domain) = self.genesis_domain.as_ref().cloned() { + // Register the genesis domain runtime + let runtime_id = register_runtime_at_genesis::( + genesis_domain.runtime_name, + genesis_domain.runtime_type, + genesis_domain.runtime_version, + genesis_domain.raw_genesis_storage, + Zero::zero(), + ) + .expect("Genesis runtime registration must always succeed"); + + // Instantiate the genesis domain + let domain_config = DomainConfig { + domain_name: genesis_domain.domain_name, + runtime_id, + max_block_size: genesis_domain.max_block_size, + max_block_weight: genesis_domain.max_block_weight, + bundle_slot_probability: genesis_domain.bundle_slot_probability, + target_bundles_per_block: genesis_domain.target_bundles_per_block, + }; + let domain_owner = genesis_domain.owner_account_id; + let domain_id = + do_instantiate_domain::(domain_config, domain_owner.clone(), Zero::zero()) + .expect("Genesis domain instantiation must always succeed"); + + // Register domain_owner as the genesis operator. + let operator_config = OperatorConfig { + signing_key: genesis_domain.signing_key.clone(), + minimum_nominator_stake: genesis_domain + .minimum_nominator_stake + .saturated_into(), + nomination_tax: genesis_domain.nomination_tax, + }; + let operator_stake = T::MinOperatorStake::get(); + do_register_operator::(domain_owner, domain_id, operator_stake, operator_config) + .expect("Genesis operator registration must succeed"); + + do_finalize_domain_current_epoch::(domain_id, Zero::zero()) + .expect("Genesis epoch must succeed"); } } } @@ -1187,58 +1220,7 @@ mod pallet { // TODO: proper benchmark impl Hooks> for Pallet { fn on_initialize(block_number: BlockNumberFor) -> Weight { - if block_number.is_one() { - if let Some(genesis_domain) = PendingGenesisDomain::::take() { - // Register the genesis domain runtime - let runtime_id = register_runtime_at_genesis::( - genesis_domain.runtime_name, - genesis_domain.runtime_type, - genesis_domain.runtime_version, - genesis_domain.code, - One::one(), - ) - .expect("Genesis runtime registration must always succeed"); - - // Instantiate the genesis domain - let domain_config = DomainConfig { - domain_name: genesis_domain.domain_name, - runtime_id, - max_block_size: genesis_domain.max_block_size, - max_block_weight: genesis_domain.max_block_weight, - bundle_slot_probability: genesis_domain.bundle_slot_probability, - target_bundles_per_block: genesis_domain.target_bundles_per_block, - }; - let domain_owner = genesis_domain.owner_account_id; - let domain_id = do_instantiate_domain::( - domain_config, - domain_owner.clone(), - One::one(), - Some(genesis_domain.raw_genesis_config), - ) - .expect("Genesis domain instantiation must always succeed"); - - // Register domain_owner as the genesis operator. - let operator_config = OperatorConfig { - signing_key: genesis_domain.signing_key.clone(), - minimum_nominator_stake: genesis_domain - .minimum_nominator_stake - .saturated_into(), - nomination_tax: genesis_domain.nomination_tax, - }; - let operator_stake = T::MinOperatorStake::get(); - do_register_operator::( - domain_owner, - domain_id, - operator_stake, - operator_config, - ) - .expect("Genesis operator registration must succeed"); - - do_finalize_domain_current_epoch::(domain_id, One::one()) - .expect("Genesis epoch must succeed"); - } - } - + // Do scheduled domain runtime upgrade do_upgrade_runtimes::(block_number); // Store the hash of the parent consensus block for domain that have bundles submitted @@ -1379,7 +1361,7 @@ impl Pallet { pub fn domain_runtime_code(domain_id: DomainId) -> Option> { RuntimeRegistry::::get(Self::runtime_id(domain_id)?) - .map(|runtime_object| runtime_object.code) + .and_then(|mut runtime_object| runtime_object.raw_genesis.take_runtime_code()) } pub fn domain_best_number(domain_id: DomainId) -> Option { @@ -1403,14 +1385,13 @@ impl Pallet { domain_id: DomainId, ) -> Option<(DomainInstanceData, BlockNumberFor)> { let domain_obj = DomainRegistry::::get(domain_id)?; - let (runtime_type, runtime_code) = - RuntimeRegistry::::get(domain_obj.domain_config.runtime_id) - .map(|runtime_object| (runtime_object.runtime_type, runtime_object.code))?; + let runtime_object = RuntimeRegistry::::get(domain_obj.domain_config.runtime_id)?; + let runtime_type = runtime_object.runtime_type.clone(); + let raw_genesis = runtime_object.into_complete_raw_genesis(domain_id); Some(( DomainInstanceData { runtime_type, - runtime_code, - raw_genesis_config: domain_obj.raw_genesis_config, + raw_genesis, }, domain_obj.created_at, )) @@ -1625,26 +1606,19 @@ impl Pallet { domain_id: DomainId, operator_id: &OperatorId, ) -> Result<(BalanceOf, BalanceOf), BundleError> { - match LastEpochStakingDistribution::::get(domain_id) { - None => { - let domain_stake_summary = DomainStakingSummary::::get(domain_id) - .ok_or(BundleError::InvalidDomainId)?; - - let operator_stake = domain_stake_summary - .current_operators - .get(operator_id) - .ok_or(BundleError::BadOperator)?; - - Ok((*operator_stake, domain_stake_summary.current_total_stake)) - } - Some(pending_election_params) => { - let operator_stake = pending_election_params - .operators - .get(operator_id) - .ok_or(BundleError::BadOperator)?; - Ok((*operator_stake, pending_election_params.total_domain_stake)) + if let Some(pending_election_params) = LastEpochStakingDistribution::::get(domain_id) { + if let Some(operator_stake) = pending_election_params.operators.get(operator_id) { + return Ok((*operator_stake, pending_election_params.total_domain_stake)); } } + let domain_stake_summary = + DomainStakingSummary::::get(domain_id).ok_or(BundleError::InvalidDomainId)?; + let operator_stake = domain_stake_summary + .current_operators + .get(operator_id) + .ok_or(BundleError::BadOperator) + .unwrap(); + Ok((*operator_stake, domain_stake_summary.current_total_stake)) } /// Called when a bundle is added to update the bundle state for tx range diff --git a/crates/pallet-domains/src/runtime_registry.rs b/crates/pallet-domains/src/runtime_registry.rs index 8e5dbb2bc7..61a8a1b6a9 100644 --- a/crates/pallet-domains/src/runtime_registry.rs +++ b/crates/pallet-domains/src/runtime_registry.rs @@ -2,12 +2,14 @@ use crate::pallet::{NextRuntimeId, RuntimeRegistry, ScheduledRuntimeUpgrades}; use crate::{Config, Event}; +use alloc::string::String; use codec::{Decode, Encode}; use frame_support::PalletError; use frame_system::pallet_prelude::*; use scale_info::TypeInfo; use sp_core::Hasher; -use sp_domains::{DomainsDigestItem, RuntimeId, RuntimeType}; +use sp_domains::storage::RawGenesis; +use sp_domains::{DomainId, DomainsDigestItem, RuntimeId, RuntimeType}; use sp_runtime::traits::{CheckedAdd, Get}; use sp_runtime::DigestItem; use sp_std::vec::Vec; @@ -23,24 +25,40 @@ pub enum Error { MissingRuntimeObject, RuntimeUpgradeAlreadyScheduled, MaxScheduledBlockNumber, + FailedToDecodeRawGenesis, + RuntimeCodeNotFoundInRawGenesis, } #[derive(TypeInfo, Debug, Encode, Decode, Clone, PartialEq, Eq)] pub struct RuntimeObject { - pub runtime_name: Vec, + pub runtime_name: String, pub runtime_type: RuntimeType, pub runtime_upgrades: u32, pub hash: Hash, - pub code: Vec, + // The raw gensis storage that contains the runtime code. + // NOTE: don't use this field directly but `into_complete_raw_genesis` instead + pub raw_genesis: RawGenesis, pub version: RuntimeVersion, pub created_at: Number, pub updated_at: Number, } +impl RuntimeObject { + // Return a complete raw genesis with runtime code and domain id set properly + pub fn into_complete_raw_genesis(self, domain_id: DomainId) -> RawGenesis { + let RuntimeObject { + mut raw_genesis, .. + } = self; + raw_genesis.set_domain_id(domain_id); + raw_genesis + } +} + #[derive(TypeInfo, Debug, Encode, Decode, Clone, PartialEq, Eq)] -pub struct ScheduledRuntimeUpgrade { - pub code: Vec, +pub struct ScheduledRuntimeUpgrade { + pub raw_genesis: RawGenesis, pub version: RuntimeVersion, + pub hash: Hash, } /// Extracts the runtime version of the provided code. @@ -71,13 +89,20 @@ pub(crate) fn can_upgrade_code( /// Registers a new domain runtime.. pub(crate) fn do_register_runtime( - runtime_name: Vec, + runtime_name: String, runtime_type: RuntimeType, - code: Vec, + raw_genesis_storage: Vec, at: BlockNumberFor, ) -> Result { - let runtime_version = runtime_version(&code)?; - let runtime_hash = T::Hashing::hash(&code); + let raw_genesis: RawGenesis = Decode::decode(&mut raw_genesis_storage.as_slice()) + .map_err(|_| Error::FailedToDecodeRawGenesis)?; + + let code = raw_genesis + .get_runtime_code() + .ok_or(Error::RuntimeCodeNotFoundInRawGenesis)?; + + let version = runtime_version(code)?; + let runtime_hash = T::Hashing::hash(code); let runtime_id = NextRuntimeId::::get(); RuntimeRegistry::::insert( @@ -86,8 +111,8 @@ pub(crate) fn do_register_runtime( runtime_name, runtime_type, hash: runtime_hash, - code, - version: runtime_version, + raw_genesis, + version, created_at: at, updated_at: at, runtime_upgrades: 0u32, @@ -103,13 +128,20 @@ pub(crate) fn do_register_runtime( // TODO: Remove once `do_register_runtime` works at genesis. /// Registers a new domain runtime at genesis. pub(crate) fn register_runtime_at_genesis( - runtime_name: Vec, + runtime_name: String, runtime_type: RuntimeType, runtime_version: RuntimeVersion, - code: Vec, + raw_genesis_storage: Vec, at: BlockNumberFor, ) -> Result { - let runtime_hash = T::Hashing::hash(&code); + let raw_genesis: RawGenesis = Decode::decode(&mut raw_genesis_storage.as_slice()) + .map_err(|_| Error::FailedToDecodeRawGenesis)?; + + let code = raw_genesis + .get_runtime_code() + .ok_or(Error::RuntimeCodeNotFoundInRawGenesis)?; + + let runtime_hash = T::Hashing::hash(code); let runtime_id = NextRuntimeId::::get(); RuntimeRegistry::::insert( @@ -118,7 +150,7 @@ pub(crate) fn register_runtime_at_genesis( runtime_name, runtime_type, hash: runtime_hash, - code, + raw_genesis, version: runtime_version, created_at: at, updated_at: at, @@ -135,19 +167,31 @@ pub(crate) fn register_runtime_at_genesis( /// Schedules a runtime upgrade after `DomainRuntimeUpgradeDelay` from current block number. pub(crate) fn do_schedule_runtime_upgrade( runtime_id: RuntimeId, - code: Vec, + raw_genesis_storage: Vec, current_block_number: BlockNumberFor, ) -> Result, Error> { let runtime_obj = RuntimeRegistry::::get(runtime_id).ok_or(Error::MissingRuntimeObject)?; - let new_runtime_version = can_upgrade_code(&runtime_obj.version, &code)?; - let scheduled_at = current_block_number - .checked_add(&T::DomainRuntimeUpgradeDelay::get()) - .ok_or(Error::MaxScheduledBlockNumber)?; + + let new_raw_genesis: RawGenesis = Decode::decode(&mut raw_genesis_storage.as_slice()) + .map_err(|_| Error::FailedToDecodeRawGenesis)?; + + let new_code = new_raw_genesis + .get_runtime_code() + .ok_or(Error::RuntimeCodeNotFoundInRawGenesis)?; + + let new_runtime_version = can_upgrade_code(&runtime_obj.version, new_code)?; + let new_runtime_hash = T::Hashing::hash(new_code); let scheduled_upgrade = ScheduledRuntimeUpgrade { - code, + raw_genesis: new_raw_genesis, version: new_runtime_version, + hash: new_runtime_hash, }; + let scheduled_at = current_block_number + .checked_add(&T::DomainRuntimeUpgradeDelay::get()) + .ok_or(Error::MaxScheduledBlockNumber)?; + ScheduledRuntimeUpgrades::::insert(scheduled_at, runtime_id, scheduled_upgrade); + Ok(scheduled_at) } @@ -158,10 +202,9 @@ pub(crate) fn do_upgrade_runtimes(at: BlockNumberFor) { .as_mut() .expect("Runtime object exists since an upgrade is scheduled after verification"); - let runtime_hash = T::Hashing::hash(&scheduled_update.code); - runtime_obj.code = scheduled_update.code; + runtime_obj.raw_genesis = scheduled_update.raw_genesis; runtime_obj.version = scheduled_update.version; - runtime_obj.hash = runtime_hash; + runtime_obj.hash = scheduled_update.hash; runtime_obj.runtime_upgrades = runtime_obj.runtime_upgrades.saturating_add(1); runtime_obj.updated_at = at; }); @@ -188,6 +231,7 @@ mod tests { use frame_support::assert_ok; use frame_support::dispatch::RawOrigin; use frame_support::traits::OnInitialize; + use sp_domains::storage::RawGenesis; use sp_domains::{DomainsDigestItem, RuntimeId, RuntimeType}; use sp_runtime::traits::BlockNumberProvider; use sp_runtime::{Digest, DispatchError}; @@ -212,11 +256,12 @@ mod tests { read_runtime_version, )); ext.execute_with(|| { + let raw_genesis_storage = RawGenesis::dummy(vec![1, 2, 3, 4]).encode(); let res = crate::Pallet::::register_domain_runtime( RawOrigin::Root.into(), - b"evm".to_vec(), + "evm".to_owned(), RuntimeType::Evm, - vec![1, 2, 3, 4], + raw_genesis_storage, ); assert_ok!(res); @@ -233,11 +278,11 @@ mod tests { RuntimeRegistry::::insert( 0, RuntimeObject { - runtime_name: b"evm".to_vec(), + runtime_name: "evm".to_owned(), runtime_type: Default::default(), runtime_upgrades: 0, hash: Default::default(), - code: vec![1, 2, 3, 4], + raw_genesis: RawGenesis::dummy(vec![1, 2, 3, 4]), version: RuntimeVersion { spec_name: "test".into(), spec_version: 1, @@ -289,7 +334,7 @@ mod tests { let res = crate::Pallet::::upgrade_domain_runtime( RawOrigin::Root.into(), 0, - vec![6, 7, 8, 9], + RawGenesis::dummy(vec![6, 7, 8, 9]).encode(), ); assert_eq!(res, expected.map_err(DispatchError::from)) @@ -310,7 +355,7 @@ mod tests { } ); assert_eq!(runtime_obj.runtime_upgrades, 0); - assert_eq!(runtime_obj.code, vec![1, 2, 3, 4]); + assert_eq!(runtime_obj.raw_genesis, RawGenesis::dummy(vec![1, 2, 3, 4]),); let block_number = frame_system::Pallet::::current_block_number(); let scheduled_block_number = block_number @@ -376,11 +421,11 @@ mod tests { RuntimeRegistry::::insert( 0, RuntimeObject { - runtime_name: b"evm".to_vec(), + runtime_name: "evm".to_owned(), runtime_type: Default::default(), runtime_upgrades: 0, hash: Default::default(), - code: vec![1, 2, 3, 4], + raw_genesis: RawGenesis::dummy(vec![1, 2, 3, 4]), version: version.clone(), created_at: Default::default(), updated_at: Default::default(), @@ -400,7 +445,7 @@ mod tests { let res = crate::Pallet::::upgrade_domain_runtime( RawOrigin::Root.into(), 0, - vec![6, 7, 8, 9], + RawGenesis::dummy(vec![6, 7, 8, 9]).encode(), ); assert_ok!(res); diff --git a/crates/pallet-domains/src/tests.rs b/crates/pallet-domains/src/tests.rs index 58d8cd8f13..11ed4e3f31 100644 --- a/crates/pallet-domains/src/tests.rs +++ b/crates/pallet-domains/src/tests.rs @@ -21,10 +21,11 @@ use sp_domains::fraud_proof::{ ValidBundleDigest, }; use sp_domains::merkle_tree::MerkleTree; +use sp_domains::storage::RawGenesis; use sp_domains::{ - BundleHeader, DomainId, DomainInstanceData, DomainsHoldIdentifier, ExecutionReceipt, - GenerateGenesisStateRoot, GenesisReceiptExtension, OpaqueBundle, OperatorId, OperatorPair, - ProofOfElection, ReceiptHash, RuntimeType, SealedBundleHeader, StakingHoldIdentifier, + BundleHeader, DomainId, DomainsHoldIdentifier, ExecutionReceipt, OpaqueBundle, OperatorId, + OperatorPair, ProofOfElection, ReceiptHash, RuntimeType, SealedBundleHeader, + StakingHoldIdentifier, }; use sp_runtime::traits::{AccountIdConversion, BlakeTwo256, Hash as HashT, IdentityLookup, Zero}; use sp_runtime::{BuildStorage, OpaqueExtrinsic}; @@ -34,7 +35,6 @@ use sp_trie::trie_types::TrieDBMutBuilderV1; use sp_trie::{PrefixedMemoryDB, StorageProof, TrieMut}; use sp_version::RuntimeVersion; use std::sync::atomic::{AtomicU64, Ordering}; -use std::sync::Arc; use subspace_core_primitives::{Randomness, U256 as P256}; use subspace_runtime_primitives::{Moment, SSC}; @@ -228,6 +228,7 @@ impl pallet_domains::Config for Test { type RuntimeEvent = RuntimeEvent; type DomainNumber = BlockNumber; type DomainHash = sp_core::H256; + type DomainHashing = BlakeTwo256; type ConfirmationDepthK = ConfirmationDepthK; type DomainRuntimeUpgradeDelay = DomainRuntimeUpgradeDelay; type Currency = Balances; @@ -247,7 +248,6 @@ impl pallet_domains::Config for Test { type StakeEpochDuration = StakeEpochDuration; type TreasuryAccount = TreasuryAccount; type MaxPendingStakingOperation = MaxPendingStakingOperation; - type SudoId = (); type Randomness = MockRandomness; type StorageKeys = StorageKeys; type DeriveExtrinsics = DeriveExtrinsics; @@ -277,10 +277,6 @@ pub(crate) fn new_test_ext_with_extensions() -> sp_io::TestExternalities { ext.register_extension(sp_core::traits::ReadRuntimeVersionExt::new( ReadRuntimeVersion(version.encode()), )); - ext.register_extension(GenesisReceiptExtension::new(Arc::new( - GenesisStateRootGenerater, - ))); - ext } @@ -364,18 +360,6 @@ pub(crate) fn create_dummy_bundle_with_receipts( } } -pub(crate) struct GenesisStateRootGenerater; - -impl GenerateGenesisStateRoot for GenesisStateRootGenerater { - fn generate_genesis_state_root( - &self, - _domain_id: DomainId, - _domain_instance_data: DomainInstanceData, - ) -> Option { - Some(Default::default()) - } -} - pub(crate) struct ReadRuntimeVersion(pub Vec); impl sp_core::traits::ReadRuntimeVersion for ReadRuntimeVersion { @@ -396,11 +380,12 @@ pub(crate) fn run_to_block(block_number: BlockNumberFor, parent_ha } pub(crate) fn register_genesis_domain(creator: u64, operator_ids: Vec) -> DomainId { + let raw_genesis_storage = RawGenesis::dummy(vec![1, 2, 3, 4]).encode(); assert_ok!(crate::Pallet::::register_domain_runtime( RawOrigin::Root.into(), - b"evm".to_vec(), + "evm".to_owned(), RuntimeType::Evm, - vec![1, 2, 3, 4], + raw_genesis_storage, )); let domain_id = NextDomainId::::get(); @@ -412,14 +397,13 @@ pub(crate) fn register_genesis_domain(creator: u64, operator_ids: Vec::instantiate_domain( RawOrigin::Signed(creator).into(), DomainConfig { - domain_name: b"evm-domain".to_vec(), + domain_name: "evm-domain".to_owned(), runtime_id: 0, max_block_size: 1u32, max_block_weight: Weight::from_parts(1, 0), bundle_slot_probability: (1, 1), target_bundles_per_block: 1, }, - vec![], ) .unwrap(); @@ -671,7 +655,7 @@ fn test_bundle_fromat_verification() { let max_extrincis_count = 10; let max_block_size = opaque_extrinsic(0, 0).encoded_size() as u32 * max_extrincis_count; let domain_config = DomainConfig { - domain_name: b"test-domain".to_vec(), + domain_name: "test-domain".to_owned(), runtime_id: 0u32, max_block_size, max_block_weight: Weight::MAX, @@ -683,7 +667,6 @@ fn test_bundle_fromat_verification() { created_at: Default::default(), genesis_receipt_hash: Default::default(), domain_config, - raw_genesis_config: None, }; DomainRegistry::::insert(domain_id, domain_obj); diff --git a/crates/sp-domains/Cargo.toml b/crates/sp-domains/Cargo.toml index 4c30be0676..b7418bd7d9 100644 --- a/crates/sp-domains/Cargo.toml +++ b/crates/sp-domains/Cargo.toml @@ -33,7 +33,6 @@ sp-externalities = { version = "0.19.0", default-features = false, git = "https: sp-inherents = { version = "4.0.0-dev", default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "c90d6edfd8c63168eff0dce6f2ace4d66dd139f7" } sp-keystore = { version = "0.27.0", git = "https://github.com/subspace/polkadot-sdk", rev = "c90d6edfd8c63168eff0dce6f2ace4d66dd139f7", optional = true } sp-runtime = { version = "24.0.0", default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "c90d6edfd8c63168eff0dce6f2ace4d66dd139f7" } -sp-runtime-interface = { version = "17.0.0", default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "c90d6edfd8c63168eff0dce6f2ace4d66dd139f7" } sp-state-machine = { version = "0.28.0", default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "c90d6edfd8c63168eff0dce6f2ace4d66dd139f7" } sp-std = { version = "8.0.0", default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "c90d6edfd8c63168eff0dce6f2ace4d66dd139f7" } sp-trie = { version = "22.0.0", default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "c90d6edfd8c63168eff0dce6f2ace4d66dd139f7" } @@ -70,7 +69,6 @@ std = [ "sp-inherents/std", "sp-keystore", "sp-runtime/std", - "sp-runtime-interface/std", "sp-state-machine/std", "sp-std/std", "sp-trie/std", diff --git a/crates/sp-domains/src/lib.rs b/crates/sp-domains/src/lib.rs index dd80021ce2..b8315d1de4 100644 --- a/crates/sp-domains/src/lib.rs +++ b/crates/sp-domains/src/lib.rs @@ -21,12 +21,17 @@ pub mod bundle_producer_election; pub mod fraud_proof; pub mod inherents; pub mod merkle_tree; +pub mod storage; #[cfg(test)] mod tests; pub mod transaction; pub mod valued_trie_root; pub mod verification; +extern crate alloc; + +use crate::storage::{RawGenesis, StorageKey}; +use alloc::string::String; use bundle_producer_election::{BundleProducerElectionParams, VrfProofError}; use hexlit::hex; use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; @@ -43,8 +48,6 @@ use sp_runtime::traits::{ BlakeTwo256, Block as BlockT, CheckedAdd, Hash as HashT, NumberFor, Zero, }; use sp_runtime::{DigestItem, OpaqueExtrinsic, Percent}; -use sp_runtime_interface::pass_by::PassBy; -use sp_runtime_interface::{pass_by, runtime_interface}; use sp_std::vec::Vec; use sp_weights::Weight; use subspace_core_primitives::crypto::blake2b_256_hash; @@ -113,10 +116,6 @@ pub type ExtrinsicsRoot = H256; )] pub struct DomainId(u32); -impl PassBy for DomainId { - type PassBy = pass_by::Codec; -} - impl From for DomainId { #[inline] fn from(x: u32) -> Self { @@ -399,13 +398,13 @@ impl< BlakeTwo256::hash_of(self) } - pub fn genesis(consensus_genesis_hash: Hash, genesis_state_root: DomainHash) -> Self { + pub fn genesis(genesis_state_root: DomainHash) -> Self { ExecutionReceipt { domain_block_number: Zero::zero(), domain_block_hash: Default::default(), domain_block_extrinsic_root: Default::default(), parent_domain_block_receipt_hash: Default::default(), - consensus_block_hash: consensus_genesis_hash, + consensus_block_hash: Default::default(), consensus_block_number: Zero::zero(), valid_bundles: Vec::new(), invalid_bundles: Vec::new(), @@ -513,19 +512,18 @@ impl ProofOfElection { #[derive(TypeInfo, Debug, Encode, Decode, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct GenesisDomain { // Domain runtime items - pub runtime_name: Vec, + pub runtime_name: String, pub runtime_type: RuntimeType, pub runtime_version: RuntimeVersion, - pub code: Vec, + pub raw_genesis_storage: Vec, // Domain config items pub owner_account_id: AccountId, - pub domain_name: Vec, + pub domain_name: String, pub max_block_size: u32, pub max_block_weight: Weight, pub bundle_slot_probability: (u64, u64), pub target_bundles_per_block: u32, - pub raw_genesis_config: Vec, // Genesis operator pub signing_key: OperatorPublicKey, @@ -542,10 +540,6 @@ pub enum RuntimeType { Evm, } -impl PassBy for RuntimeType { - type PassBy = pass_by::Codec; -} - /// Type representing the runtime ID. pub type RuntimeId = u32; @@ -617,61 +611,28 @@ impl DomainsDigestItem for DigestItem { } } -/// `DomainInstanceData` is used to construct `RuntimeGenesisConfig` which will be further used -/// to construct the genesis block +/// The storage key of the `SelfDomainId` storage item in the `pallet-domain-id` +/// +/// Any change to the storage item name or the `pallet-domain-id` name used in the `construct_runtime` +/// macro must be reflected here. +pub fn self_domain_id_storage_key() -> StorageKey { + StorageKey( + frame_support::storage::storage_prefix( + // This is the name used for the `pallet-domain-id` in the `construct_runtime` macro + // i.e. `SelfDomainId: pallet_domain_id = 90` + "SelfDomainId".as_bytes(), + // This is the storage item name used inside the `pallet-domain-id` + "SelfDomainId".as_bytes(), + ) + .to_vec(), + ) +} + +/// `DomainInstanceData` is used to construct the genesis storage of domain instance chain #[derive(PartialEq, Eq, Clone, Encode, Decode, TypeInfo)] pub struct DomainInstanceData { pub runtime_type: RuntimeType, - pub runtime_code: Vec, - // The genesis config of the domain, encoded in json format. - // - // NOTE: the WASM code in the `system-pallet` genesis config should be empty to avoid - // redundancy with the `runtime_code` field. - pub raw_genesis_config: Option>, -} - -impl PassBy for DomainInstanceData { - type PassBy = pass_by::Codec; -} - -#[cfg(feature = "std")] -pub trait GenerateGenesisStateRoot: Send + Sync { - /// Returns the state root of genesis block built from the runtime genesis config on success. - fn generate_genesis_state_root( - &self, - domain_id: DomainId, - domain_instance_data: DomainInstanceData, - ) -> Option; -} - -#[cfg(feature = "std")] -sp_externalities::decl_extension! { - /// A domain genesis receipt extension. - pub struct GenesisReceiptExtension(std::sync::Arc); -} - -#[cfg(feature = "std")] -impl GenesisReceiptExtension { - /// Create a new instance of [`GenesisReceiptExtension`]. - pub fn new(inner: std::sync::Arc) -> Self { - Self(inner) - } -} - -/// Domain-related runtime interface -#[runtime_interface] -pub trait Domain { - fn generate_genesis_state_root( - &mut self, - domain_id: DomainId, - domain_instance_data: DomainInstanceData, - ) -> Option { - use sp_externalities::ExternalitiesExt; - - self.extension::() - .expect("No `GenesisReceiptExtension` associated for the current context!") - .generate_genesis_state_root(domain_id, domain_instance_data) - } + pub raw_genesis: RawGenesis, } #[derive(Debug, Decode, Encode, TypeInfo, Clone)] diff --git a/crates/sp-domains/src/storage.rs b/crates/sp-domains/src/storage.rs new file mode 100644 index 0000000000..07c084f041 --- /dev/null +++ b/crates/sp-domains/src/storage.rs @@ -0,0 +1,169 @@ +use crate::{self_domain_id_storage_key, DomainId}; +use hash_db::Hasher; +use parity_scale_codec::{Codec, Decode, Encode}; +use scale_info::TypeInfo; +use sp_core::storage::{well_known_keys, ChildInfo}; +#[cfg(feature = "std")] +use sp_core::storage::{Storage, StorageChild}; +use sp_runtime::StateVersion; +use sp_state_machine::{Backend, TrieBackend, TrieBackendBuilder}; +use sp_std::collections::btree_map::BTreeMap; +use sp_std::vec::Vec; +use sp_trie::{empty_trie_root, LayoutV0, MemoryDB}; + +/// Create a new empty instance of in-memory backend. +/// +/// NOTE: this function is port from `sp_state_machine::in_memory_backend::new_in_mem` which is +/// only export for `std` but we need to use it in `no_std` +fn new_in_mem() -> TrieBackend, H> +where + H: Hasher, + H::Out: Codec + Ord, +{ + let db = MemoryDB::default(); + // V1 is same as V0 for an empty trie. + TrieBackendBuilder::new(db, empty_trie_root::>()).build() +} + +// NOTE: this is port from `sp_core::storage::StorageKey` with `TypeInfo` supported +#[derive(Default, Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Encode, Decode, TypeInfo)] +pub struct StorageKey(pub Vec); + +// NOTE: this is port from `sp_core::storage::StorageData` with `TypeInfo` supported +#[derive(Default, Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Encode, Decode, TypeInfo)] +pub struct StorageData(pub Vec); + +type GenesisStorage = BTreeMap; + +/// Raw storage content for genesis block +#[derive(Default, Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Encode, Decode, TypeInfo)] +pub struct RawGenesis { + top: GenesisStorage, + children_default: BTreeMap, +} + +impl RawGenesis { + pub fn set_domain_id(&mut self, domain_id: DomainId) { + let _ = self.top.insert( + self_domain_id_storage_key(), + StorageData(domain_id.encode()), + ); + } + + fn set_runtime_code(&mut self, code: Vec) { + let _ = self.top.insert( + StorageKey(well_known_keys::CODE.to_vec()), + StorageData(code), + ); + } + + pub fn get_runtime_code(&self) -> Option<&[u8]> { + self.top + .get(&StorageKey(well_known_keys::CODE.to_vec())) + .map(|sd| sd.0.as_ref()) + } + + pub fn take_runtime_code(&mut self) -> Option> { + self.top + .remove(&StorageKey(well_known_keys::CODE.to_vec())) + .map(|sd| sd.0) + } + + pub fn state_root(&self, state_version: StateVersion) -> H::Out + where + H: Hasher, + H::Out: Codec + Ord, + { + let backend = new_in_mem::(); + + // NOTE: the `(k, v)` of `children_default` are iterated separately because the + // `full_storage_root` required `&ChildInfo` as input but if we simply map `k` + // to `&ChildInfo` it will fail due to temporary value can't live long enough. + let child_infos: Vec<_> = self + .children_default + .keys() + .map(|k| ChildInfo::new_default(k.0.as_slice())) + .collect(); + let child_delta = child_infos.iter().zip( + self.children_default + .values() + .map(|v| v.iter().map(|(k, v)| (&k.0[..], Some(&v.0[..])))), + ); + + let (root, _) = backend.full_storage_root( + self.top.iter().map(|(k, v)| (&k.0[..], Some(&v.0[..]))), + child_delta, + state_version, + ); + + root + } + + pub fn dummy(code: Vec) -> Self { + let mut raw_genesis = Self::default(); + raw_genesis.set_runtime_code(code); + raw_genesis + } +} + +#[cfg(feature = "std")] +impl RawGenesis { + /// Construct `RawGenesis` from a given storage + // + /// NOTE: This function is part from `sc-chain-spec::GenesisSource::resolve` + pub fn from_storage(storage: Storage) -> Self { + let top = storage + .top + .into_iter() + .map(|(k, v)| (StorageKey(k), StorageData(v))) + .collect(); + + let children_default = storage + .children_default + .into_iter() + .map(|(k, child)| { + ( + StorageKey(k), + child + .data + .into_iter() + .map(|(k, v)| (StorageKey(k), StorageData(v))) + .collect(), + ) + }) + .collect(); + + RawGenesis { + top, + children_default, + } + } + + /// Convert `RawGenesis` to storage, the opposite of `from_storage` + // + /// NOTE: This function is part from `::assimilate_storage` + pub fn into_storage(self) -> Storage { + let RawGenesis { + top: map, + children_default: children_map, + } = self; + let mut storage = Storage::default(); + + storage.top.extend(map.into_iter().map(|(k, v)| (k.0, v.0))); + + children_map.into_iter().for_each(|(k, v)| { + let child_info = ChildInfo::new_default(k.0.as_slice()); + storage + .children_default + .entry(k.0) + .or_insert_with(|| StorageChild { + data: Default::default(), + child_info, + }) + .data + .extend(v.into_iter().map(|(k, v)| (k.0, v.0))); + }); + + storage + } +} diff --git a/crates/subspace-node/src/bin/subspace-node.rs b/crates/subspace-node/src/bin/subspace-node.rs index 6fb9436b4c..4607d2f244 100644 --- a/crates/subspace-node/src/bin/subspace-node.rs +++ b/crates/subspace-node/src/bin/subspace-node.rs @@ -31,14 +31,11 @@ use sc_transaction_pool_api::OffchainTransactionPoolFactory; use sc_utils::mpsc::tracing_unbounded; use sp_core::crypto::Ss58AddressFormat; use sp_core::traits::SpawnEssentialNamed; -use sp_domains::GenerateGenesisStateRoot; use sp_io::SubstrateHostFunctions; use sp_messenger::messages::ChainId; use sp_wasm_interface::ExtendedHostFunctions; -use std::sync::Arc; use subspace_node::domain::{ - DomainCli, DomainGenesisBlockBuilder, DomainInstanceStarter, DomainSubcommand, - EVMDomainExecutorDispatch, + DomainCli, DomainInstanceStarter, DomainSubcommand, EVMDomainExecutorDispatch, }; use subspace_node::{Cli, ExecutorDispatch, Subcommand}; use subspace_proof_of_space::chia::ChiaTable; @@ -144,7 +141,6 @@ fn main() -> Result<(), Error> { .. } = subspace_service::new_partial::( &config, - None, &pot_external_entropy(&config, &cli)?, )?; Ok(( @@ -163,7 +159,6 @@ fn main() -> Result<(), Error> { .. } = subspace_service::new_partial::( &config, - None, &pot_external_entropy(&config, &cli)?, )?; Ok(( @@ -183,7 +178,6 @@ fn main() -> Result<(), Error> { .. } = subspace_service::new_partial::( &config, - None, &pot_external_entropy(&config, &cli)?, )?; Ok(( @@ -204,7 +198,6 @@ fn main() -> Result<(), Error> { .. } = subspace_service::new_partial::( &config, - None, &pot_external_entropy(&config, &cli)?, )?; Ok(( @@ -270,7 +263,6 @@ fn main() -> Result<(), Error> { .. } = subspace_service::new_partial::( &config, - None, &pot_external_entropy(&config, &cli)?, )?; Ok(( @@ -312,7 +304,6 @@ fn main() -> Result<(), Error> { ExecutorDispatch, >( &config, - None, &pot_external_entropy(&config, &cli)?, )?; @@ -323,7 +314,6 @@ fn main() -> Result<(), Error> { client, backend, .. } = subspace_service::new_partial::( &config, - None, &pot_external_entropy(&config, &cli)?, )?; let db = backend.expose_db(); @@ -400,7 +390,7 @@ fn main() -> Result<(), Error> { } })?; } - DomainSubcommand::BuildGenesisConfig(cmd) => cmd.run()?, + DomainSubcommand::BuildGenesisStorage(cmd) => cmd.run()?, DomainSubcommand::ExportExecutionReceipt(cmd) => { let runner = cli.create_runner(cmd)?; runner.sync_run(|consensus_chain_config| { @@ -519,14 +509,9 @@ fn main() -> Result<(), Error> { timekeeper_cpu_cores: cli.timekeeper_cpu_cores, }; - let construct_domain_genesis_block_builder = - |backend, executor| -> Arc { - Arc::new(DomainGenesisBlockBuilder::new(backend, executor)) - }; let partial_components = subspace_service::new_partial::( &consensus_chain_config.base, - Some(&construct_domain_genesis_block_builder), &pot_external_entropy, ) .map_err(|error| { diff --git a/crates/subspace-node/src/chain_spec.rs b/crates/subspace-node/src/chain_spec.rs index 0f11a51a42..c0cddc8412 100644 --- a/crates/subspace-node/src/chain_spec.rs +++ b/crates/subspace-node/src/chain_spec.rs @@ -18,13 +18,15 @@ use crate::chain_spec_utils::{chain_spec_properties, get_account_id_from_seed}; use crate::domain::evm_chain_spec::{self, SpecId}; +use parity_scale_codec::Encode; use sc_service::{ChainType, NoExtension}; use sc_subspace_chain_specs::ConsensusChainSpec; use sc_telemetry::TelemetryEndpoints; use sp_consensus_subspace::FarmerPublicKey; use sp_core::crypto::{Ss58Codec, UncheckedFrom}; +use sp_domains::storage::RawGenesis; use sp_domains::RuntimeType; -use sp_runtime::Percent; +use sp_runtime::{BuildStorage, Percent}; use std::marker::PhantomData; use std::num::NonZeroU32; use subspace_core_primitives::PotKey; @@ -420,12 +422,16 @@ fn subspace_genesis_config( confirmation_depth_k, } = genesis_params; - let (mut domain_genesis_config, genesis_domain_params) = + let (domain_genesis_config, genesis_domain_params) = evm_chain_spec::get_testnet_genesis_by_spec_id(spec_id); - // Clear the WASM code of the genesis config since it is duplicated with `GenesisDomain::code` - domain_genesis_config.system.code = Default::default(); - let raw_domain_genesis_config = serde_json::to_vec(&domain_genesis_config) - .expect("Genesis config serialization never fails; qed"); + + let raw_genesis_storage = { + let storage = domain_genesis_config + .build_storage() + .expect("Failed to build genesis storage from genesis runtime config"); + let raw_genesis = RawGenesis::from_storage(storage); + raw_genesis.encode() + }; RuntimeGenesisConfig { system: SystemConfig { @@ -454,21 +460,18 @@ fn subspace_genesis_config( }, domains: DomainsConfig { genesis_domain: Some(sp_domains::GenesisDomain { - runtime_name: b"evm".to_vec(), + runtime_name: "evm".to_owned(), runtime_type: RuntimeType::Evm, runtime_version: evm_domain_runtime::VERSION, - code: evm_domain_runtime::WASM_BINARY - .unwrap_or_else(|| panic!("EVM domain runtime not available")) - .to_owned(), + raw_genesis_storage, // Domain config, mainly for placeholder the concrete value TBD owner_account_id: sudo_account, - domain_name: b"evm-domain".to_vec(), + domain_name: "evm-domain".to_owned(), max_block_size: MaxDomainBlockSize::get(), max_block_weight: MaxDomainBlockWeight::get(), bundle_slot_probability: (1, 1), target_bundles_per_block: 10, - raw_genesis_config: raw_domain_genesis_config, signing_key: genesis_domain_params.operator_signing_key, nomination_tax: Percent::from_percent(5), minimum_nominator_stake: 100 * SSC, diff --git a/crates/subspace-node/src/domain.rs b/crates/subspace-node/src/domain.rs index 2da9633bb0..cd16a48b3b 100644 --- a/crates/subspace-node/src/domain.rs +++ b/crates/subspace-node/src/domain.rs @@ -21,14 +21,7 @@ pub(crate) mod evm_chain_spec; pub use self::cli::{DomainCli, Subcommand as DomainSubcommand}; pub use self::domain_instance_starter::DomainInstanceStarter; use evm_domain_runtime::AccountId as AccountId20; -use sc_client_api::Backend; -use sc_executor::{NativeExecutionDispatch, RuntimeVersionOf}; -use sc_service::{BuildGenesisBlock, GenesisBlockBuilder}; -use sp_core::H256; -use sp_domains::{DomainId, DomainInstanceData, RuntimeType}; -use sp_runtime::traits::{Block as BlockT, Header as HeaderT}; -use std::marker::PhantomData; -use std::sync::Arc; +use sc_executor::NativeExecutionDispatch; /// EVM domain executor instance. pub struct EVMDomainExecutorDispatch; @@ -47,83 +40,3 @@ impl NativeExecutionDispatch for EVMDomainExecutorDispatch { evm_domain_runtime::native_version() } } - -/// [`DomainGenesisBlockBuilder`] is used on the consensus node for building the -/// domain genesis block from a specific serialized domain runtime genesis config. -pub struct DomainGenesisBlockBuilder { - backend: Arc, - executor: E, - _phantom: PhantomData, -} - -impl DomainGenesisBlockBuilder -where - Block: BlockT, - B: Backend, - E: RuntimeVersionOf + Clone, -{ - /// Constructs a new instance of [`DomainGenesisBlockBuilder`]. - pub fn new(backend: Arc, executor: E) -> Self { - Self { - backend, - executor, - _phantom: Default::default(), - } - } - - /// Constructs the genesis domain block from a serialized runtime genesis config. - pub fn generate_genesis_block( - &self, - domain_id: DomainId, - domain_instance_data: DomainInstanceData, - ) -> sp_blockchain::Result { - let DomainInstanceData { - runtime_type, - runtime_code, - raw_genesis_config, - } = domain_instance_data; - let domain_genesis_block_builder = match runtime_type { - RuntimeType::Evm => { - let mut runtime_cfg = match raw_genesis_config { - Some(raw_genesis_config) => serde_json::from_slice(&raw_genesis_config) - .map_err(|_| { - sp_blockchain::Error::Application(Box::from( - "Failed to deserialize genesis config of the evm domain", - )) - })?, - None => evm_domain_runtime::RuntimeGenesisConfig::default(), - }; - runtime_cfg.system.code = runtime_code; - runtime_cfg.self_domain_id.domain_id = Some(domain_id); - GenesisBlockBuilder::new( - &runtime_cfg, - false, - self.backend.clone(), - self.executor.clone(), - )? - } - }; - domain_genesis_block_builder - .build_genesis_block() - .map(|(genesis_block, _)| genesis_block) - } -} - -impl sp_domains::GenerateGenesisStateRoot for DomainGenesisBlockBuilder -where - Block: BlockT, - Block::Hash: Into, - B: Backend, - E: RuntimeVersionOf + Clone + Send + Sync, -{ - fn generate_genesis_state_root( - &self, - domain_id: DomainId, - domain_instance_data: DomainInstanceData, - ) -> Option { - self.generate_genesis_block(domain_id, domain_instance_data) - .map(|genesis_block| *genesis_block.header().state_root()) - .ok() - .map(Into::into) - } -} diff --git a/crates/subspace-node/src/domain/cli.rs b/crates/subspace-node/src/domain/cli.rs index 4ead86d840..1f7b279229 100644 --- a/crates/subspace-node/src/domain/cli.rs +++ b/crates/subspace-node/src/domain/cli.rs @@ -17,6 +17,7 @@ use crate::domain::evm_chain_spec::{self, SpecId}; use clap::Parser; use domain_runtime_primitives::opaque::Block as DomainBlock; +use parity_scale_codec::Encode; use sc_cli::{ BlockNumberOrHash, ChainSpec, CliConfiguration, DefaultConfigurationValues, ImportParams, KeystoreParams, NetworkParams, Result, Role, RunCmd as SubstrateRunCmd, SharedParams, @@ -27,10 +28,11 @@ use sc_service::config::PrometheusConfig; use sc_service::{BasePath, Configuration}; use sp_blockchain::HeaderBackend; use sp_domain_digests::AsPredigest; +use sp_domains::storage::RawGenesis; use sp_domains::DomainId; use sp_runtime::generic::BlockId; use sp_runtime::traits::Header; -use sp_runtime::DigestItem; +use sp_runtime::{BuildStorage, DigestItem}; use std::io::Write; use std::net::SocketAddr; use std::num::ParseIntError; @@ -51,8 +53,8 @@ pub enum Subcommand { #[clap(subcommand)] Benchmark(frame_benchmarking_cli::BenchmarkCmd), - /// Build the genesis config of the evm domain chain in json format - BuildGenesisConfig(BuildGenesisConfigCmd), + /// Build the genesis storage of the evm domain chain in json format + BuildGenesisStorage(BuildGenesisStorageCmd), /// The `export-execution-receipt` command used to get the ER from the auxiliary storage of the operator client ExportExecutionReceipt(ExportExecutionReceiptCmd), @@ -276,24 +278,20 @@ impl CliConfiguration for DomainCli { } // TODO: make the command generic over different runtime type instead of just the evm domain runtime -/// The `build-genesis-config` command used to build the genesis config of the evm domain chain. +/// The `build-genesis-storage` command used to build the genesis storage of the evm domain chain. #[derive(Debug, Clone, Parser)] -pub struct BuildGenesisConfigCmd { - /// Whether output the WASM runtime code - #[arg(long, default_value_t = false)] - pub with_wasm_code: bool, - - /// The base struct of the build-genesis-config command. +pub struct BuildGenesisStorageCmd { + /// The base struct of the build-genesis-storage command. #[clap(flatten)] pub shared_params: SharedParams, } -impl BuildGenesisConfigCmd { - /// Run the build-genesis-config command +impl BuildGenesisStorageCmd { + /// Run the build-genesis-storage command pub fn run(&self) -> sc_cli::Result<()> { let is_dev = self.shared_params.is_dev(); let chain_id = self.shared_params.chain_id(is_dev); - let mut domain_genesis_config = match chain_id.as_str() { + let domain_genesis_config = match chain_id.as_str() { "gemini-3f" => evm_chain_spec::get_testnet_genesis_by_spec_id(SpecId::Gemini).0, "devnet" => evm_chain_spec::get_testnet_genesis_by_spec_id(SpecId::DevNet).0, "dev" => evm_chain_spec::get_testnet_genesis_by_spec_id(SpecId::Dev).0, @@ -306,15 +304,16 @@ impl BuildGenesisConfigCmd { } }; - if !self.with_wasm_code { - // Clear the WASM code of the genesis config - domain_genesis_config.system.code = Default::default(); - } - let raw_domain_genesis_config = serde_json::to_vec(&domain_genesis_config) - .expect("Genesis config serialization never fails; qed"); + let raw_genesis_storage = { + let storage = domain_genesis_config + .build_storage() + .expect("Failed to build genesis storage from genesis runtime config"); + let raw_genesis = RawGenesis::from_storage(storage); + raw_genesis.encode() + }; if std::io::stdout() - .write_all(raw_domain_genesis_config.as_ref()) + .write_all(raw_genesis_storage.as_ref()) .is_err() { let _ = std::io::stderr().write_all(b"Error writing to stdout\n"); diff --git a/crates/subspace-node/src/domain/domain_instance_starter.rs b/crates/subspace-node/src/domain/domain_instance_starter.rs index 957f9c0a8a..c8924b9925 100644 --- a/crates/subspace-node/src/domain/domain_instance_starter.rs +++ b/crates/subspace-node/src/domain/domain_instance_starter.rs @@ -15,7 +15,7 @@ use sc_consensus_subspace::{BlockImportingNotification, NewSlotNotification}; use sc_service::{BasePath, Configuration}; use sc_transaction_pool_api::OffchainTransactionPoolFactory; use sc_utils::mpsc::{TracingUnboundedReceiver, TracingUnboundedSender}; -use sp_domains::RuntimeType; +use sp_domains::{DomainInstanceData, RuntimeType}; use std::sync::Arc; use subspace_runtime::RuntimeApi as CRuntimeApi; use subspace_runtime_primitives::opaque::Block as CBlock; @@ -48,6 +48,11 @@ impl DomainInstanceStarter { imported_block_notification_stream, } = bootstrap_result; + let DomainInstanceData { + runtime_type, + raw_genesis, + } = domain_instance_data; + let DomainInstanceStarter { domain_cli, tokio_handle, @@ -61,16 +66,11 @@ impl DomainInstanceStarter { gossip_message_sink, } = self; - let runtime_type = domain_instance_data.runtime_type.clone(); let domain_id = domain_cli.domain_id; let domain_config = { let chain_id = domain_cli.chain_id(domain_cli.is_dev()?)?; - let domain_spec = evm_chain_spec::create_domain_spec( - domain_id, - chain_id.as_str(), - domain_instance_data, - )?; + let domain_spec = evm_chain_spec::create_domain_spec(chain_id.as_str(), raw_genesis)?; create_configuration::<_, DomainCli, DomainCli>(&domain_cli, domain_spec, tokio_handle)? }; diff --git a/crates/subspace-node/src/domain/evm_chain_spec.rs b/crates/subspace-node/src/domain/evm_chain_spec.rs index 93cf910631..70c51ef44c 100644 --- a/crates/subspace-node/src/domain/evm_chain_spec.rs +++ b/crates/subspace-node/src/domain/evm_chain_spec.rs @@ -19,15 +19,15 @@ use crate::chain_spec_utils::{chain_spec_properties, get_public_key_from_seed}; use evm_domain_runtime::{ AccountId, BalancesConfig, EVMChainIdConfig, EVMConfig, Precompiles, RuntimeGenesisConfig, - SelfDomainIdConfig, SudoConfig, SystemConfig, WASM_BINARY, + SudoConfig, SystemConfig, WASM_BINARY, }; use hex_literal::hex; -use sc_service::ChainType; +use sc_service::{ChainSpec as ChainSpecT, ChainType}; use sc_subspace_chain_specs::ExecutionChainSpec; use sp_core::crypto::UncheckedFrom; -use sp_domains::{DomainId, DomainInstanceData, OperatorPublicKey, RuntimeType}; +use sp_domains::storage::RawGenesis; +use sp_domains::OperatorPublicKey; use std::str::FromStr; -use std::sync::OnceLock; use subspace_runtime_primitives::SSC; pub type ChainSpec = ExecutionChainSpec; @@ -235,29 +235,13 @@ pub fn get_testnet_genesis_by_spec_id( } } -// HACK: `ChainSpec::from_genesis` is only allow to create hardcoded spec and `RuntimeGenesisConfig` -// dosen't derive `Clone`, using global variable and serialization/deserialization to workaround -// these limits. -static GENESIS_CONFIG: OnceLock> = OnceLock::new(); - -// Load chain spec that contains the given `RuntimeGenesisConfig` -fn load_chain_spec_with( - spec_id: &str, - genesis_config: RuntimeGenesisConfig, +pub fn create_domain_spec( + chain_id: &str, + raw_genesis: RawGenesis, ) -> Result, String> { - GENESIS_CONFIG - .set( - serde_json::to_vec(&genesis_config) - .expect("Genesis config serialization never fails; qed"), - ) - .expect("This function should only call once upon node initialization"); - let constructor = || { - let raw_genesis_config = GENESIS_CONFIG.get().expect("Value just set; qed"); - serde_json::from_slice(raw_genesis_config) - .expect("Genesis config deserialization never fails; qed") - }; - - let chain_spec = match spec_id { + // The value of the `RuntimeGenesisConfig` doesn't matter since it will be overwritten later + let constructor = RuntimeGenesisConfig::default; + let mut chain_spec = match chain_id { "dev" => development_config(constructor), "gemini-3f" => gemini_3f_config(constructor), "devnet" => devnet_config(constructor), @@ -265,36 +249,9 @@ fn load_chain_spec_with( path => ChainSpec::from_json_file(std::path::PathBuf::from(path))?, }; - Ok(Box::new(chain_spec)) -} + chain_spec.set_storage(raw_genesis.into_storage()); -pub fn create_domain_spec( - domain_id: DomainId, - chain_id: &str, - domain_instance_data: DomainInstanceData, -) -> Result, String> { - let DomainInstanceData { - runtime_type, - runtime_code, - raw_genesis_config, - } = domain_instance_data; - - match runtime_type { - RuntimeType::Evm => { - let mut genesis_config = match raw_genesis_config { - Some(raw_genesis_config) => { - serde_json::from_slice(&raw_genesis_config).map_err(|_| { - "Failed to deserialize genesis config of the evm domain".to_string() - })? - } - None => RuntimeGenesisConfig::default(), - }; - genesis_config.system.code = runtime_code; - genesis_config.self_domain_id.domain_id = Some(domain_id); - let spec = load_chain_spec_with(chain_id, genesis_config)?; - Ok(spec) - } - } + Ok(Box::new(chain_spec)) } fn testnet_genesis( @@ -318,8 +275,8 @@ fn testnet_genesis( sudo: SudoConfig { key: maybe_sudo_account, }, - transaction_payment: Default::default(), balances: BalancesConfig { + // TODO: remove `endowed_accounts` once XDM is ready balances: endowed_accounts .iter() .cloned() @@ -349,12 +306,6 @@ fn testnet_genesis( .collect(), ..Default::default() }, - ethereum: Default::default(), - base_fee: Default::default(), - self_domain_id: SelfDomainIdConfig { - // Id of the genesis domain - domain_id: Some(DomainId::new(0)), - ..Default::default() - }, + ..Default::default() } } diff --git a/crates/subspace-node/src/lib.rs b/crates/subspace-node/src/lib.rs index 44bb31c4d7..c565666c3a 100644 --- a/crates/subspace-node/src/lib.rs +++ b/crates/subspace-node/src/lib.rs @@ -42,14 +42,10 @@ impl NativeExecutionDispatch for ExecutorDispatch { type ExtendHostFunctions = ( frame_benchmarking::benchmarking::HostFunctions, sp_consensus_subspace::consensus::HostFunctions, - sp_domains::domain::HostFunctions, ); /// Otherwise we only use the default Substrate host functions. #[cfg(not(feature = "runtime-benchmarks"))] - type ExtendHostFunctions = ( - sp_consensus_subspace::consensus::HostFunctions, - sp_domains::domain::HostFunctions, - ); + type ExtendHostFunctions = sp_consensus_subspace::consensus::HostFunctions; fn dispatch(method: &str, data: &[u8]) -> Option> { subspace_runtime::api::dispatch(method, data) diff --git a/crates/subspace-runtime/src/lib.rs b/crates/subspace-runtime/src/lib.rs index 639507c6d6..5bc085a3f2 100644 --- a/crates/subspace-runtime/src/lib.rs +++ b/crates/subspace-runtime/src/lib.rs @@ -617,7 +617,6 @@ parameter_types! { pub const StakeEpochDuration: DomainNumber = 100; pub TreasuryAccount: AccountId = PalletId(*b"treasury").into_account_truncating(); pub const MaxPendingStakingOperation: u32 = 100; - pub SudoId: AccountId = Sudo::key().expect("Sudo account must exist"); } pub struct StorageKeys; @@ -645,6 +644,7 @@ impl pallet_domains::Config for Runtime { type RuntimeEvent = RuntimeEvent; type DomainNumber = DomainNumber; type DomainHash = DomainHash; + type DomainHashing = BlakeTwo256; type ConfirmationDepthK = ConfirmationDepthK; type DomainRuntimeUpgradeDelay = DomainRuntimeUpgradeDelay; type Currency = Balances; @@ -664,7 +664,6 @@ impl pallet_domains::Config for Runtime { type StakeEpochDuration = StakeEpochDuration; type TreasuryAccount = TreasuryAccount; type MaxPendingStakingOperation = MaxPendingStakingOperation; - type SudoId = SudoId; type Randomness = Subspace; type StorageKeys = StorageKeys; type DeriveExtrinsics = DeriveExtrinsics; diff --git a/crates/subspace-service/src/lib.rs b/crates/subspace-service/src/lib.rs index acb804eddb..081f62fdd2 100644 --- a/crates/subspace-service/src/lib.rs +++ b/crates/subspace-service/src/lib.rs @@ -79,7 +79,7 @@ use sp_consensus_subspace::{ }; use sp_core::traits::SpawnEssentialNamed; use sp_domains::transaction::PreValidationObjectApi; -use sp_domains::{DomainsApi, GenerateGenesisStateRoot, GenesisReceiptExtension}; +use sp_domains::DomainsApi; use sp_externalities::Extensions; use sp_objects::ObjectsApi; use sp_offchain::OffchainWorkerApi; @@ -230,7 +230,6 @@ struct SubspaceExtensionsFactory { kzg: Kzg, client: Arc, pot_verifier: PotVerifier, - domain_genesis_receipt_ext: Option>, _pos_table: PhantomData, } @@ -373,9 +372,6 @@ where } }) })); - if let Some(ext) = self.domain_genesis_receipt_ext.clone() { - exts.register(GenesisReceiptExtension::new(ext)); - } exts } } @@ -424,12 +420,6 @@ type PartialComponents = sc_service::PartialCompon #[allow(clippy::type_complexity)] pub fn new_partial( config: &Configuration, - construct_domain_genesis_block_builder: Option< - &dyn Fn( - Arc, - NativeElseWasmExecutor, - ) -> Arc, - >, pot_external_entropy: &[u8], ) -> Result, ServiceError> where @@ -472,9 +462,6 @@ where let kzg = Kzg::new(embedded_kzg_settings()); - let domain_genesis_receipt_ext = - construct_domain_genesis_block_builder.map(|f| f(backend.clone(), executor.clone())); - let client = Arc::new(client); let pot_verifier = PotVerifier::new( @@ -486,7 +473,6 @@ where kzg: kzg.clone(), client: Arc::clone(&client), pot_verifier: pot_verifier.clone(), - domain_genesis_receipt_ext, _pos_table: PhantomData, }, ); diff --git a/domains/client/domain-operator/src/domain_block_processor.rs b/domains/client/domain-operator/src/domain_block_processor.rs index 1ea2d1c44a..75cc234e48 100644 --- a/domains/client/domain-operator/src/domain_block_processor.rs +++ b/domains/client/domain-operator/src/domain_block_processor.rs @@ -354,10 +354,7 @@ where "Domain block header for #{genesis_hash:?} not found", )) })?; - ExecutionReceipt::genesis( - self.consensus_client.info().genesis_hash, - *genesis_header.state_root(), - ) + ExecutionReceipt::genesis(*genesis_header.state_root()) } else { crate::load_execution_receipt_by_domain_hash::( &*self.client, diff --git a/domains/client/domain-operator/src/domain_bundle_proposer.rs b/domains/client/domain-operator/src/domain_bundle_proposer.rs index dae12241de..6b069a1efb 100644 --- a/domains/client/domain-operator/src/domain_bundle_proposer.rs +++ b/domains/client/domain-operator/src/domain_bundle_proposer.rs @@ -217,10 +217,7 @@ where "Domain block header for #{genesis_hash:?} not found", )) })?; - return Ok(ExecutionReceipt::genesis( - self.consensus_client.info().genesis_hash, - *genesis_header.state_root(), - )); + return Ok(ExecutionReceipt::genesis(*genesis_header.state_root())); } // Get the domain block hash corresponding to `receipt_number` in the domain canonical chain diff --git a/domains/client/domain-operator/src/tests.rs b/domains/client/domain-operator/src/tests.rs index 6799d7fd31..064ce59bcf 100644 --- a/domains/client/domain-operator/src/tests.rs +++ b/domains/client/domain-operator/src/tests.rs @@ -59,9 +59,6 @@ async fn test_domain_instance_bootstrapper() { BasePath::new(directory.path().join("ferdie")), ); - // Produce 1 consensus block to initialize genesis domain - ferdie.produce_block_with_slot(1.into()).await.unwrap(); - let expected_genesis_state_root = ferdie .client .runtime_api() @@ -108,8 +105,6 @@ async fn test_domain_block_production() { Ferdie, BasePath::new(directory.path().join("ferdie")), ); - // Produce 1 consensus block to initialize genesis domain - ferdie.produce_block_with_slot(1.into()).await.unwrap(); // Run Alice (a evm domain authority node) let mut alice = domain_test_service::DomainNodeBuilder::new( @@ -137,7 +132,7 @@ async fn test_domain_block_production() { .unwrap(); } // Domain block only produced when there is bundle contains in the primary block - assert_eq!(ferdie.client.info().best_number, 51); + assert_eq!(ferdie.client.info().best_number, 50); assert_eq!(alice.client.info().best_number, 25); let consensus_block_hash = ferdie.client.info().best_hash; @@ -222,8 +217,6 @@ async fn test_processing_empty_consensus_block() { Ferdie, BasePath::new(directory.path().join("ferdie")), ); - // Produce 1 consensus block to initialize genesis domain - ferdie.produce_block_with_slot(1.into()).await.unwrap(); // Run Alice (a evm domain authority node) let mut alice = domain_test_service::DomainNodeBuilder::new( @@ -282,7 +275,7 @@ async fn test_processing_empty_consensus_block() { } } // The domain chain is not progressed as all the consensus blocks are empty - assert_eq!(ferdie.client.info().best_number, 11); + assert_eq!(ferdie.client.info().best_number, 10); assert_eq!(alice.client.info().best_number, 0); // To process non-empty consensus blocks and produce domain blocks @@ -306,8 +299,6 @@ async fn test_domain_block_deriving_from_multiple_bundles() { Ferdie, BasePath::new(directory.path().join("ferdie")), ); - // Produce 1 consensus block to initialize genesis domain - ferdie.produce_block_with_slot(1.into()).await.unwrap(); // Run Alice (a evm domain authority node) let mut alice = domain_test_service::DomainNodeBuilder::new( @@ -382,11 +373,6 @@ async fn collected_receipts_should_be_on_the_same_branch_with_current_best_block Ferdie, BasePath::new(directory.path().join("ferdie")), ); - // Produce 1 consensus block to initialize genesis domain - consensus_node - .produce_block_with_slot(1.into()) - .await - .unwrap(); // Run Alice (a evm domain authority node) let mut alice = domain_test_service::DomainNodeBuilder::new( @@ -403,7 +389,7 @@ async fn collected_receipts_should_be_on_the_same_branch_with_current_best_block let best_consensus_hash = consensus_node.client.info().best_hash; let best_consensus_number = consensus_node.client.info().best_number; - assert_eq!(best_consensus_number, 4); + assert_eq!(best_consensus_number, 3); assert_eq!(alice.client.info().best_number, 3); let domain_block_2_hash = *alice @@ -421,32 +407,25 @@ async fn collected_receipts_should_be_on_the_same_branch_with_current_best_block let parent_hash = *best_header.parent_hash(); - // Domain chain forks: + // Consensus chain forks: // 3 // / // 2 -- 3a // \ // 3b -- 4 - - // Consensus chain forks (it has 1 more block compare to the domain chain): - // 4 - // / - // 3 -- 4a - // \ - // 4b -- 5 let slot = consensus_node.produce_slot(); - let fork_block_hash_4a = consensus_node + let fork_block_hash_3a = consensus_node .produce_block_with_slot_at(slot, parent_hash, Some(vec![])) .await - .expect("Produced first consensus fork block 4a at height #4"); - // A fork block 4a at #4 produced. - assert_eq!(number_of(&consensus_node, fork_block_hash_4a), 4); - assert_ne!(fork_block_hash_4a, best_consensus_hash); + .expect("Produced first consensus fork block 3a at height #3"); + // A fork block 3a at #3 produced. + assert_eq!(number_of(&consensus_node, fork_block_hash_3a), 3); + assert_ne!(fork_block_hash_3a, best_consensus_hash); // Best hash unchanged due to the longest chain fork choice. assert_eq!(consensus_node.client.info().best_hash, best_consensus_hash); - // Hash of block number #4 unchanged. + // Hash of block number #3 unchanged. assert_eq!( - consensus_node.client.hash(4).unwrap().unwrap(), + consensus_node.client.hash(3).unwrap().unwrap(), best_consensus_hash ); @@ -460,7 +439,7 @@ async fn collected_receipts_should_be_on_the_same_branch_with_current_best_block ) }; - // Produce a bundle after the fork block #4a has been produced. + // Produce a bundle after the fork block #3a has been produced. let signed_bundle = consensus_node .notify_new_slot_and_wait_for_bundle(slot) .await; @@ -479,18 +458,18 @@ async fn collected_receipts_should_be_on_the_same_branch_with_current_best_block ); let slot = consensus_node.produce_slot(); - let fork_block_hash_4b = consensus_node + let fork_block_hash_3b = consensus_node .produce_block_with_slot_at(slot, parent_hash, Some(vec![])) .await .expect("Produced second consensus fork block 3b at height #3"); - // Another fork block 3b at #4 produced, - assert_eq!(number_of(&consensus_node, fork_block_hash_4b), 4); - assert_ne!(fork_block_hash_4b, best_consensus_hash); + // Another fork block 3b at #3 produced, + assert_eq!(number_of(&consensus_node, fork_block_hash_3b), 3); + assert_ne!(fork_block_hash_3b, best_consensus_hash); // Best hash unchanged due to the longest chain fork choice. assert_eq!(consensus_node.client.info().best_hash, best_consensus_hash); - // Hash of block number #4 unchanged. + // Hash of block number #3 unchanged. assert_eq!( - consensus_node.client.hash(4).unwrap().unwrap(), + consensus_node.client.hash(3).unwrap().unwrap(), best_consensus_hash ); @@ -504,27 +483,27 @@ async fn collected_receipts_should_be_on_the_same_branch_with_current_best_block expected_receipts_consensus_info ); - // Produce a new tip at #5. + // Produce a new tip at #4. let slot = consensus_node.produce_slot(); produce_block_with!( - consensus_node.produce_block_with_slot_at(slot, fork_block_hash_4b, Some(vec![])), + consensus_node.produce_block_with_slot_at(slot, fork_block_hash_3b, Some(vec![])), alice ) .await - .expect("Produce a new block on top of the second fork block at height #4"); + .expect("Produce a new block on top of the second fork block at height #3"); let new_best_hash = consensus_node.client.info().best_hash; let new_best_number = consensus_node.client.info().best_number; - assert_eq!(new_best_number, 5); + assert_eq!(new_best_number, 4); - // The domain best block should be reverted to #2 because the primary block #4b and #5 do + // The domain best block should be reverted to #2 because the primary block #3b and #4 do // not contains any bundles assert_eq!(alice.client.info().best_number, 2); assert_eq!(alice.client.info().best_hash, domain_block_2_hash); - // Hash of block number #4 is updated to the second fork block 4b. + // Hash of block number #3 is updated to the second fork block 3b. assert_eq!( - consensus_node.client.hash(4).unwrap().unwrap(), - fork_block_hash_4b + consensus_node.client.hash(3).unwrap().unwrap(), + fork_block_hash_3b ); let new_best_header = consensus_node @@ -533,20 +512,20 @@ async fn collected_receipts_should_be_on_the_same_branch_with_current_best_block .unwrap() .unwrap(); - assert_eq!(*new_best_header.parent_hash(), fork_block_hash_4b); + assert_eq!(*new_best_header.parent_hash(), fork_block_hash_3b); - // Produce a bundle after the new block #5 has been produced. + // Produce a bundle after the new block #4 has been produced. let (_slot, signed_bundle) = consensus_node .produce_slot_and_wait_for_bundle_submission() .await; - // In the new best fork, the receipt header number is 2 thus it produce the receipt - // of next block namely block 3 - let hash_3 = consensus_node.client.hash(3).unwrap().unwrap(); - let header_3 = consensus_node.client.header(hash_3).unwrap().unwrap(); + // In the new best fork, the receipt header number is 1 thus it produce the receipt + // of next block namely block 2 + let hash_2 = consensus_node.client.hash(2).unwrap().unwrap(); + let header_2 = consensus_node.client.header(hash_2).unwrap().unwrap(); assert_eq!( receipts_consensus_info(signed_bundle.unwrap()), - consensus_block_info(header_3) + consensus_block_info(header_2) ); } @@ -566,8 +545,6 @@ async fn test_domain_tx_propagate() { Ferdie, BasePath::new(directory.path().join("ferdie")), ); - // Produce 1 consensus block to initialize genesis domain - ferdie.produce_block_with_slot(1.into()).await.unwrap(); // Run Alice (a evm domain authority node) let mut alice = domain_test_service::DomainNodeBuilder::new( @@ -694,8 +671,6 @@ async fn test_executor_inherent_timestamp_is_set() { Ferdie, BasePath::new(directory.path().join("ferdie")), ); - // Produce 1 consensus block to initialize genesis domain - ferdie.produce_block_with_slot(1.into()).await.unwrap(); // Run Alice (a evm domain authority node) let mut alice = domain_test_service::DomainNodeBuilder::new( @@ -776,8 +751,6 @@ async fn test_invalid_state_transition_proof_creation_and_verification( Ferdie, BasePath::new(directory.path().join("ferdie")), ); - // Produce 1 consensus block to initialize genesis domain - ferdie.produce_block_with_slot(1.into()).await.unwrap(); // Run Alice (a evm domain authority node) let mut alice = domain_test_service::DomainNodeBuilder::new( @@ -1135,8 +1108,6 @@ async fn fraud_proof_verification_in_tx_pool_should_work() { Ferdie, BasePath::new(directory.path().join("ferdie")), ); - // Produce 1 consensus block to initialize genesis domain - ferdie.produce_block_with_slot(1.into()).await.unwrap(); // Run Alice (a evm domain authority node) let mut alice = domain_test_service::DomainNodeBuilder::new( @@ -1309,8 +1280,6 @@ async fn set_new_code_should_work() { Ferdie, BasePath::new(directory.path().join("ferdie")), ); - // Produce 1 consensus block to initialize genesis domain - ferdie.produce_block_with_slot(1.into()).await.unwrap(); // Run Alice (a evm domain authority node) let mut alice = domain_test_service::DomainNodeBuilder::new( @@ -1382,8 +1351,6 @@ async fn pallet_domains_unsigned_extrinsics_should_work() { Ferdie, BasePath::new(directory.path().join("ferdie")), ); - // Produce 1 consensus block to initialize genesis domain - ferdie.produce_block_with_slot(1.into()).await.unwrap(); // Run Alice (a evm domain authority node) let mut alice = domain_test_service::DomainNodeBuilder::new( @@ -1482,8 +1449,6 @@ async fn duplicated_and_stale_bundle_should_be_rejected() { Ferdie, BasePath::new(directory.path().join("ferdie")), ); - // Produce 1 consensus block to initialize genesis domain - ferdie.produce_block_with_slot(1.into()).await.unwrap(); // Run Alice (a evm domain authority node) let mut alice = domain_test_service::DomainNodeBuilder::new( @@ -1555,8 +1520,6 @@ async fn existing_bundle_can_be_resubmitted_to_new_fork() { Ferdie, BasePath::new(directory.path().join("ferdie")), ); - // Produce 1 consensus block to initialize genesis domain - ferdie.produce_block_with_slot(1.into()).await.unwrap(); // Run Alice (a evm domain authority node) let mut alice = domain_test_service::DomainNodeBuilder::new( @@ -2004,9 +1967,6 @@ async fn test_restart_domain_operator() { BasePath::new(directory.path().join("ferdie")), ); - // Produce 1 consensus block to initialize genesis domain - ferdie.produce_block_with_slot(1.into()).await.unwrap(); - // Run Alice (a evm domain authority node) let mut alice = domain_test_service::DomainNodeBuilder::new( tokio_handle.clone(), @@ -2052,7 +2012,7 @@ async fn test_restart_domain_operator() { produce_blocks!(ferdie, alice, 5).await.unwrap(); // Chain should progress base on previous run - assert_eq!(ferdie.client.info().best_number, 11); + assert_eq!(ferdie.client.info().best_number, 10); assert_eq!(alice.client.info().best_number, 10); } @@ -2073,9 +2033,6 @@ async fn test_domain_transaction_fee_and_operator_reward() { BasePath::new(directory.path().join("ferdie")), ); - // Produce 1 consensus block to initialize genesis domain - ferdie.produce_block_with_slot(1.into()).await.unwrap(); - // Run Alice (a evm domain authority node) let mut alice = domain_test_service::DomainNodeBuilder::new( tokio_handle.clone(), @@ -2142,9 +2099,6 @@ async fn test_multiple_consensus_blocks_derive_similar_domain_block() { BasePath::new(directory.path().join("ferdie")), ); - // Produce 1 consensus block to initialize genesis domain - ferdie.produce_block_with_slot(1.into()).await.unwrap(); - // Run Alice (a evm domain authority node) let mut alice = domain_test_service::DomainNodeBuilder::new( tokio_handle.clone(), diff --git a/domains/pallets/domain-id/Cargo.toml b/domains/pallets/domain-id/Cargo.toml index eb4994361b..b261322501 100644 --- a/domains/pallets/domain-id/Cargo.toml +++ b/domains/pallets/domain-id/Cargo.toml @@ -20,6 +20,10 @@ scale-info = { version = "2.7.0", default-features = false, features = ["derive" sp-domains = { version = "0.1.0", default-features = false, path = "../../../crates/sp-domains" } sp-runtime = { version = "24.0.0", default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "c90d6edfd8c63168eff0dce6f2ace4d66dd139f7" } +[dev-dependencies] +sp-core = { version = "21.0.0", default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "c90d6edfd8c63168eff0dce6f2ace4d66dd139f7" } +sp-io = { version = "23.0.0", default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "c90d6edfd8c63168eff0dce6f2ace4d66dd139f7" } + [features] default = ["std"] std = [ diff --git a/domains/pallets/domain-id/src/lib.rs b/domains/pallets/domain-id/src/lib.rs index 3cd31a730d..eb2f7dfd55 100644 --- a/domains/pallets/domain-id/src/lib.rs +++ b/domains/pallets/domain-id/src/lib.rs @@ -17,6 +17,9 @@ #![cfg_attr(not(feature = "std"), no_std)] +#[cfg(test)] +mod tests; + pub use pallet::*; #[frame_support::pallet] @@ -28,7 +31,7 @@ mod pallet { pub trait Config: frame_system::Config {} #[pallet::storage] - pub(super) type SelfDomainId = StorageValue<_, DomainId, ValueQuery>; + pub(super) type SelfDomainId = StorageValue<_, DomainId, OptionQuery>; /// Pallet domain-id to store self domain id. #[pallet::pallet] @@ -47,16 +50,16 @@ mod pallet { #[pallet::genesis_build] impl BuildGenesisConfig for GenesisConfig { fn build(&self) { - SelfDomainId::::set( - self.domain_id - .expect("Genesis config of pallet-domain-id must be set"), - ); + // NOTE: the domain id of a domain instance is allocated during instantiation and can not + // be known ahead of time, thus the value in the `RuntimeGenesisConfig` of chain spec can + // be arbitrary and is ignored, it will be reset to the correct id during domain instantiation. + SelfDomainId::::set(self.domain_id); } } impl Pallet { pub fn self_domain_id() -> DomainId { - SelfDomainId::::get() + SelfDomainId::::get().expect("Domain ID must be set during domain instantiation") } } } diff --git a/domains/pallets/domain-id/src/tests.rs b/domains/pallets/domain-id/src/tests.rs new file mode 100644 index 0000000000..1b3066aaa6 --- /dev/null +++ b/domains/pallets/domain-id/src/tests.rs @@ -0,0 +1,67 @@ +use crate::{self as pallet_domain_id}; +use frame_support::parameter_types; +use frame_support::traits::{ConstU16, ConstU32, ConstU64}; +use sp_core::storage::StateVersion; +use sp_core::H256; +use sp_runtime::traits::{BlakeTwo256, IdentityLookup}; +use sp_runtime::BuildStorage; + +type Block = frame_system::mocking::MockBlock; + +frame_support::construct_runtime!( + pub struct Test { + System: frame_system = 0, + SelfDomainId: pallet_domain_id = 1, + } +); + +impl pallet_domain_id::Config for Test {} + +parameter_types! { + pub const ExtrinsicsRootStateVersion: StateVersion = StateVersion::V0; +} + +impl frame_system::Config for Test { + type BaseCallFilter = frame_support::traits::Everything; + type BlockWeights = (); + type BlockLength = (); + type DbWeight = (); + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + type Nonce = u64; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = u64; + type Lookup = IdentityLookup; + type Block = Block; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = ConstU64<250>; + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = (); + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type SS58Prefix = ConstU16<42>; + type OnSetCode = (); + type MaxConsumers = ConstU32<16>; + type ExtrinsicsRootStateVersion = ExtrinsicsRootStateVersion; +} + +pub(crate) fn new_test_ext() -> sp_io::TestExternalities { + let t = frame_system::GenesisConfig::::default() + .build_storage() + .unwrap(); + + t.into() +} + +#[test] +fn test_domain_id_storage_key() { + new_test_ext().execute_with(|| { + assert_eq!( + pallet_domain_id::SelfDomainId::::hashed_key().to_vec(), + sp_domains::self_domain_id_storage_key().0 + ); + }); +} diff --git a/domains/test/runtime/evm/src/lib.rs b/domains/test/runtime/evm/src/lib.rs index 199cb47a8a..63c1893dc1 100644 --- a/domains/test/runtime/evm/src/lib.rs +++ b/domains/test/runtime/evm/src/lib.rs @@ -183,7 +183,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("subspace-evm-domain"), impl_name: create_runtime_str!("subspace-evm-domain"), authoring_version: 0, - spec_version: 0, + spec_version: 1, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 0, diff --git a/domains/test/service/src/chain_spec.rs b/domains/test/service/src/chain_spec.rs index 5bf3dfefcb..aa89b0de5b 100644 --- a/domains/test/service/src/chain_spec.rs +++ b/domains/test/service/src/chain_spec.rs @@ -2,70 +2,25 @@ use evm_domain_test_runtime::RuntimeGenesisConfig; use sc_service::{ChainSpec, ChainType, GenericChainSpec}; -use sp_domains::{DomainId, DomainInstanceData, RuntimeType}; -use std::sync::OnceLock; - -macro_rules! chain_spec_from_genesis { - ( $constructor:expr ) => {{ - GenericChainSpec::from_genesis( - "Local Testnet", - "local_testnet", - ChainType::Local, - $constructor, - vec![], - None, - None, - None, - None, - None, - ) - }}; -} - -/// HACK: `ChainSpec::from_genesis` is only allow to create hardcoded spec and `RuntimeGenesisConfig` -/// dosen't derive `Clone`, using global variable and serialization/deserialization to workaround -/// these limits -// TODO: find a better solution, tests will run parallelly thus `load_chain_spec_with` multiple -// time, when we support more domain in the future the genesis domain of different domain will -// mixup in the current workaround. -static GENESIS_CONFIG: OnceLock> = OnceLock::new(); - -/// Load chain spec that contains the given `RuntimeGenesisConfig` -fn load_chain_spec_with(genesis_config: RuntimeGenesisConfig) -> Box { - let _ = GENESIS_CONFIG.set( - serde_json::to_vec(&genesis_config).expect("Genesis config serialization never fails; qed"), - ); - let constructor = || { - let raw_genesis_config = GENESIS_CONFIG.get().expect("Value just set; qed"); - serde_json::from_slice::(raw_genesis_config) - .expect("Genesis config deserialization never fails; qed") - }; - - Box::new(chain_spec_from_genesis!(constructor)) -} +use sp_domains::storage::RawGenesis; /// Create chain spec -pub fn create_domain_spec( - domain_id: DomainId, - domain_instance_data: DomainInstanceData, -) -> Box { - let DomainInstanceData { - runtime_type, - runtime_code, - raw_genesis_config, - } = domain_instance_data; +pub fn create_domain_spec(raw_genesis: RawGenesis) -> Box { + let mut chain_spec = GenericChainSpec::from_genesis( + "Local Testnet", + "local_testnet", + ChainType::Local, + // The value of the `RuntimeGenesisConfig` doesn't matter since it will be overwritten later + RuntimeGenesisConfig::default, + vec![], + None, + None, + None, + None, + None, + ); - match runtime_type { - RuntimeType::Evm => { - let mut genesis_config = match raw_genesis_config { - Some(raw_genesis_config) => serde_json::from_slice(&raw_genesis_config) - .expect("Raw genesis config should be well-formatted"), - None => RuntimeGenesisConfig::default(), - }; - genesis_config.system.code = runtime_code; - genesis_config.self_domain_id.domain_id = Some(domain_id); + chain_spec.set_storage(raw_genesis.into_storage()); - load_chain_spec_with(genesis_config) - } - } + Box::new(chain_spec) } diff --git a/domains/test/service/src/domain.rs b/domains/test/service/src/domain.rs index 136483454c..a4fb71f659 100644 --- a/domains/test/service/src/domain.rs +++ b/domains/test/service/src/domain.rs @@ -182,7 +182,7 @@ where .await .expect("Failed to get domain instance data") }; - let chain_spec = create_domain_spec(domain_id, domain_instance_data); + let chain_spec = create_domain_spec(domain_instance_data.raw_genesis); let domain_config = node_config( domain_id, tokio_handle.clone(), diff --git a/domains/test/service/src/lib.rs b/domains/test/service/src/lib.rs index 77545a76c8..249713bfff 100644 --- a/domains/test/service/src/lib.rs +++ b/domains/test/service/src/lib.rs @@ -215,7 +215,7 @@ where >::from_raw( function.clone(), extra.clone(), - ((), 0, 0, genesis_block, current_block_hash, (), (), ()), + ((), 1, 0, genesis_block, current_block_hash, (), (), ()), ); let signature = raw_payload.using_encoded(|e| caller.sign(e)); UncheckedExtrinsicFor::::new_signed( diff --git a/test/subspace-test-client/Cargo.toml b/test/subspace-test-client/Cargo.toml index cd39769e3d..6504dd0077 100644 --- a/test/subspace-test-client/Cargo.toml +++ b/test/subspace-test-client/Cargo.toml @@ -15,6 +15,7 @@ include = [ targets = ["x86_64-unknown-linux-gnu"] [dependencies] +codec = { package = "parity-scale-codec", version = "3.6.5", features = [ "derive" ] } evm-domain-test-runtime = { version = "0.1.0", path = "../../domains/test/runtime/evm" } fp-evm = { version = "3.0.0-dev", git = "https://github.com/subspace/frontier", rev = "04f06279e7ec0b095b82be35f41d09d36f3290f5" } futures = "0.3.28" @@ -24,7 +25,6 @@ sc-client-api = { git = "https://github.com/subspace/polkadot-sdk", rev = "c90d6 sc-consensus-subspace = { version = "0.1.0", path = "../../crates/sc-consensus-subspace" } sc-executor = { git = "https://github.com/subspace/polkadot-sdk", rev = "c90d6edfd8c63168eff0dce6f2ace4d66dd139f7" } sc-service = { git = "https://github.com/subspace/polkadot-sdk", rev = "c90d6edfd8c63168eff0dce6f2ace4d66dd139f7", default-features = false } -serde_json = "1.0.106" sp-api = { git = "https://github.com/subspace/polkadot-sdk", rev = "c90d6edfd8c63168eff0dce6f2ace4d66dd139f7" } sp-consensus-subspace = { version = "0.1.0", path = "../../crates/sp-consensus-subspace" } sp-core = { git = "https://github.com/subspace/polkadot-sdk", rev = "c90d6edfd8c63168eff0dce6f2ace4d66dd139f7" } diff --git a/test/subspace-test-client/src/chain_spec.rs b/test/subspace-test-client/src/chain_spec.rs index e72b1e4c4e..0f3cf40410 100644 --- a/test/subspace-test-client/src/chain_spec.rs +++ b/test/subspace-test-client/src/chain_spec.rs @@ -1,11 +1,13 @@ //! Chain specification for the test runtime. use crate::domain_chain_spec::testnet_evm_genesis; +use codec::Encode; use sc_chain_spec::ChainType; use sp_core::{sr25519, Pair, Public}; +use sp_domains::storage::RawGenesis; use sp_domains::{GenesisDomain, OperatorPublicKey, RuntimeType}; use sp_runtime::traits::{IdentifyAccount, Verify}; -use sp_runtime::Percent; +use sp_runtime::{BuildStorage, Percent}; use std::marker::PhantomData; use std::num::NonZeroU32; use subspace_runtime_primitives::{AccountId, Balance, BlockNumber, Signature}; @@ -79,12 +81,13 @@ fn create_genesis_config( // who, start, period, period_count, per_period vesting: Vec<(AccountId, BlockNumber, BlockNumber, u32, Balance)>, ) -> RuntimeGenesisConfig { - let raw_domain_genesis_config = { - let mut domain_genesis_config = testnet_evm_genesis(); - // Clear the WASM code of the genesis config since it is duplicated with `GenesisDomain::code` - domain_genesis_config.system.code = Default::default(); - serde_json::to_vec(&domain_genesis_config) - .expect("Genesis config serialization never fails; qed") + let raw_genesis_storage = { + let domain_genesis_config = testnet_evm_genesis(); + let storage = domain_genesis_config + .build_storage() + .expect("Failed to build genesis storage from genesis runtime config"); + let raw_genesis = RawGenesis::from_storage(storage); + raw_genesis.encode() }; RuntimeGenesisConfig { system: SystemConfig { @@ -108,21 +111,18 @@ fn create_genesis_config( vesting: VestingConfig { vesting }, domains: DomainsConfig { genesis_domain: Some(GenesisDomain { - runtime_name: b"evm".to_vec(), + runtime_name: "evm".to_owned(), runtime_type: RuntimeType::Evm, runtime_version: evm_domain_test_runtime::VERSION, - code: evm_domain_test_runtime::WASM_BINARY - .unwrap_or_else(|| panic!("EVM domain runtime not available")) - .to_owned(), + raw_genesis_storage, // Domain config, mainly for placeholder the concrete value TBD owner_account_id: sudo_account, - domain_name: b"evm-domain".to_vec(), + domain_name: "evm-domain".to_owned(), max_block_size: MaxDomainBlockSize::get(), max_block_weight: MaxDomainBlockWeight::get(), bundle_slot_probability: (1, 1), target_bundles_per_block: 10, - raw_genesis_config: raw_domain_genesis_config, signing_key: get_from_seed::("Alice"), minimum_nominator_stake: 100 * SSC, diff --git a/test/subspace-test-client/src/domain_chain_spec.rs b/test/subspace-test-client/src/domain_chain_spec.rs index 6432e99e5d..ba36733c5e 100644 --- a/test/subspace-test-client/src/domain_chain_spec.rs +++ b/test/subspace-test-client/src/domain_chain_spec.rs @@ -57,7 +57,6 @@ pub fn testnet_evm_genesis() -> RuntimeGenesisConfig { .to_vec(), ..Default::default() }, - transaction_payment: Default::default(), balances: evm_domain_test_runtime::BalancesConfig { balances: endowed_accounts() .iter() @@ -89,12 +88,12 @@ pub fn testnet_evm_genesis() -> RuntimeGenesisConfig { .collect(), ..Default::default() }, - ethereum: Default::default(), - base_fee: Default::default(), self_domain_id: evm_domain_test_runtime::SelfDomainIdConfig { - // Id of the genesis domain - domain_id: Some(DomainId::new(0)), + // Set the domain id of the genesis domain to an arbitrary value + // it should be overwritten with the correct value + domain_id: Some(DomainId::new(123)), ..Default::default() }, + ..Default::default() } } diff --git a/test/subspace-test-client/src/lib.rs b/test/subspace-test-client/src/lib.rs index c9647a0e4b..15208a7e48 100644 --- a/test/subspace-test-client/src/lib.rs +++ b/test/subspace-test-client/src/lib.rs @@ -54,10 +54,7 @@ const MAX_PIECES_IN_SECTOR: u16 = 32; pub struct TestExecutorDispatch; impl sc_executor::NativeExecutionDispatch for TestExecutorDispatch { - type ExtendHostFunctions = ( - sp_consensus_subspace::consensus::HostFunctions, - sp_domains::domain::HostFunctions, - ); + type ExtendHostFunctions = sp_consensus_subspace::consensus::HostFunctions; fn dispatch(method: &str, data: &[u8]) -> Option> { subspace_test_runtime::api::dispatch(method, data) diff --git a/test/subspace-test-runtime/src/lib.rs b/test/subspace-test-runtime/src/lib.rs index 83b86ea36d..4d729e1bc0 100644 --- a/test/subspace-test-runtime/src/lib.rs +++ b/test/subspace-test-runtime/src/lib.rs @@ -641,7 +641,6 @@ parameter_types! { pub const StakeEpochDuration: DomainNumber = 5; pub TreasuryAccount: AccountId = PalletId(*b"treasury").into_account_truncating(); pub const MaxPendingStakingOperation: u32 = 100; - pub SudoId: AccountId = Sudo::key().expect("Sudo account must exist"); } pub struct StorageKeys; @@ -669,6 +668,7 @@ impl pallet_domains::Config for Runtime { type RuntimeEvent = RuntimeEvent; type DomainNumber = DomainNumber; type DomainHash = DomainHash; + type DomainHashing = BlakeTwo256; type ConfirmationDepthK = ConfirmationDepthK; type DomainRuntimeUpgradeDelay = DomainRuntimeUpgradeDelay; type Currency = Balances; @@ -688,7 +688,6 @@ impl pallet_domains::Config for Runtime { type StakeEpochDuration = StakeEpochDuration; type TreasuryAccount = TreasuryAccount; type MaxPendingStakingOperation = MaxPendingStakingOperation; - type SudoId = SudoId; type Randomness = Subspace; type StorageKeys = StorageKeys; type DeriveExtrinsics = DeriveExtrinsics; diff --git a/test/subspace-test-service/Cargo.toml b/test/subspace-test-service/Cargo.toml index 5a9d7ea39e..fe437045c7 100644 --- a/test/subspace-test-service/Cargo.toml +++ b/test/subspace-test-service/Cargo.toml @@ -26,7 +26,6 @@ rand = "0.8.5" pallet-domains = { version = "0.1.0", path = "../../crates/pallet-domains" } parking_lot = "0.12.1" sc-block-builder = { version = "0.10.0-dev", git = "https://github.com/subspace/polkadot-sdk", rev = "c90d6edfd8c63168eff0dce6f2ace4d66dd139f7" } -sc-client-api = { git = "https://github.com/subspace/polkadot-sdk", rev = "c90d6edfd8c63168eff0dce6f2ace4d66dd139f7" } sc-consensus = { version = "0.10.0-dev", git = "https://github.com/subspace/polkadot-sdk", rev = "c90d6edfd8c63168eff0dce6f2ace4d66dd139f7" } sc-executor = { git = "https://github.com/subspace/polkadot-sdk", rev = "c90d6edfd8c63168eff0dce6f2ace4d66dd139f7" } sc-consensus-fraud-proof = { version = "0.1.0", path = "../../crates/sc-consensus-fraud-proof" } @@ -45,14 +44,12 @@ sp-consensus = { git = "https://github.com/subspace/polkadot-sdk", rev = "c90d6e sp-consensus-subspace = { version = "0.1.0", path = "../../crates/sp-consensus-subspace" } sp-consensus-slots = { version = "0.10.0-dev", default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "c90d6edfd8c63168eff0dce6f2ace4d66dd139f7" } sp-domains = { version = "0.1.0", path = "../../crates/sp-domains" } -sp-externalities = { version = "0.19.0", git = "https://github.com/subspace/polkadot-sdk", rev = "c90d6edfd8c63168eff0dce6f2ace4d66dd139f7" } sp-keyring = { git = "https://github.com/subspace/polkadot-sdk", rev = "c90d6edfd8c63168eff0dce6f2ace4d66dd139f7" } sp-timestamp = { version = "4.0.0-dev", git = "https://github.com/subspace/polkadot-sdk", rev = "c90d6edfd8c63168eff0dce6f2ace4d66dd139f7" } sp-inherents = { version = "4.0.0-dev", git = "https://github.com/subspace/polkadot-sdk", rev = "c90d6edfd8c63168eff0dce6f2ace4d66dd139f7" } sp-runtime = { git = "https://github.com/subspace/polkadot-sdk", rev = "c90d6edfd8c63168eff0dce6f2ace4d66dd139f7" } subspace-core-primitives = { version = "0.1.0", default-features = false, path = "../../crates/subspace-core-primitives" } subspace-fraud-proof = { path = "../../crates/subspace-fraud-proof" } -subspace-node = { path = "../../crates/subspace-node" } subspace-runtime-primitives = { path = "../../crates/subspace-runtime-primitives" } subspace-service = { path = "../../crates/subspace-service" } subspace-test-client = { path = "../subspace-test-client" } diff --git a/test/subspace-test-service/src/lib.rs b/test/subspace-test-service/src/lib.rs index ed321bd25f..c1f04ea962 100644 --- a/test/subspace-test-service/src/lib.rs +++ b/test/subspace-test-service/src/lib.rs @@ -26,8 +26,6 @@ use futures::{select, FutureExt, StreamExt}; use jsonrpsee::RpcModule; use parking_lot::Mutex; use sc_block_builder::BlockBuilderProvider; -use sc_client_api::execution_extensions::ExtensionsFactory; -use sc_client_api::ExecutorProvider; use sc_consensus::block_import::{ BlockCheckParams, BlockImportParams, ForkChoiceStrategy, ImportResult, }; @@ -58,8 +56,7 @@ use sp_consensus_subspace::digests::{CompatibleDigestItem, PreDigest, PreDigestP use sp_consensus_subspace::FarmerPublicKey; use sp_core::traits::SpawnEssentialNamed; use sp_core::H256; -use sp_domains::{GenerateGenesisStateRoot, GenesisReceiptExtension, OpaqueBundle}; -use sp_externalities::Extensions; +use sp_domains::OpaqueBundle; use sp_inherents::{InherentData, InherentDataProvider}; use sp_keyring::Sr25519Keyring; use sp_runtime::generic::{BlockId, Digest}; @@ -74,7 +71,6 @@ use subspace_core_primitives::{Randomness, Solution}; use subspace_fraud_proof::invalid_state_transition_proof::InvalidStateTransitionProofVerifier; use subspace_fraud_proof::invalid_transaction_proof::InvalidTransactionProofVerifier; use subspace_fraud_proof::verifier_api::VerifierClient; -use subspace_node::domain::DomainGenesisBlockBuilder; use subspace_runtime_primitives::opaque::Block; use subspace_runtime_primitives::{AccountId, Balance, Hash}; use subspace_service::tx_pre_validator::ConsensusChainTxPreValidator; @@ -175,23 +171,6 @@ type StorageChanges = sp_api::StorageChanges; type TxPreValidator = ConsensusChainTxPreValidator; -struct MockExtensionsFactory(Arc); - -impl ExtensionsFactory for MockExtensionsFactory -where - Block: BlockT, -{ - fn extensions_for( - &self, - _block_hash: Block::Hash, - _block_number: NumberFor, - ) -> Extensions { - let mut exts = Extensions::new(); - exts.register(GenesisReceiptExtension::new(self.0.clone())); - exts - } -} - /// A mock Subspace consensus node instance used for testing. pub struct MockConsensusNode { /// `TaskManager`'s instance. @@ -261,12 +240,6 @@ impl MockConsensusNode { sc_service::new_full_parts::(&config, None, executor.clone()) .expect("Fail to new full parts"); - client - .execution_extensions() - .set_extensions_factory(MockExtensionsFactory(Arc::new( - DomainGenesisBlockBuilder::new(backend.clone(), executor.clone()), - ))); - let client = Arc::new(client); let select_chain = sc_consensus::LongestChain::new(backend.clone());