diff --git a/Cargo.lock b/Cargo.lock index ff78befead..c48e8363ee 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11485,6 +11485,7 @@ name = "subspace-test-client" version = "0.1.0" dependencies = [ "evm-domain-test-runtime", + "fp-evm 3.0.0-dev (git+https://github.com/subspace/frontier?rev=c13d670b25b5506c1c5243f352941dc46c82ffe4)", "futures", "sc-chain-spec", "sc-client-api", @@ -11492,6 +11493,7 @@ dependencies = [ "sc-executor", "sc-service", "schnorrkel", + "serde_json", "sp-api", "sp-consensus-subspace", "sp-core", diff --git a/crates/pallet-domains/src/domain_registry.rs b/crates/pallet-domains/src/domain_registry.rs index 85c7c8b681..9b33db67b4 100644 --- a/crates/pallet-domains/src/domain_registry.rs +++ b/crates/pallet-domains/src/domain_registry.rs @@ -15,8 +15,7 @@ use scale_info::TypeInfo; use sp_core::Get; use sp_domains::domain::generate_genesis_state_root; use sp_domains::{ - DomainId, DomainInstanceData, DomainsDigestItem, GenesisDomain, ReceiptHash, RuntimeId, - RuntimeType, + DomainId, DomainInstanceData, DomainsDigestItem, ReceiptHash, RuntimeId, RuntimeType, }; use sp_runtime::traits::{CheckedAdd, Zero}; use sp_runtime::DigestItem; @@ -56,22 +55,6 @@ pub struct DomainConfig { pub target_bundles_per_block: u32, } -impl DomainConfig { - pub(crate) fn from_genesis( - genesis_domain: &GenesisDomain, - runtime_id: RuntimeId, - ) -> Self { - DomainConfig { - domain_name: genesis_domain.domain_name.clone(), - 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, - } - } -} - #[derive(TypeInfo, Debug, Encode, Decode, Clone, PartialEq, Eq)] pub struct DomainObject { /// The address of the domain creator, used to validate updating the domain config. @@ -82,6 +65,12 @@ 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( @@ -130,6 +119,7 @@ pub(crate) fn do_instantiate_domain( domain_config: DomainConfig, owner_account_id: T::AccountId, created_at: T::BlockNumber, + raw_genesis_config: Option>, ) -> Result { can_instantiate_domain::(&owner_account_id, &domain_config)?; @@ -138,7 +128,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)? + initialize_genesis_receipt::( + domain_id, + runtime_obj.runtime_type, + runtime_obj.code, + raw_genesis_config.clone(), + )? }; let genesis_receipt_hash = genesis_receipt.hash(); @@ -147,6 +142,7 @@ pub(crate) fn do_instantiate_domain( created_at, genesis_receipt_hash, domain_config, + raw_genesis_config, }; DomainRegistry::::insert(domain_id, domain_obj); @@ -183,6 +179,7 @@ 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(T::BlockNumber::zero()); let genesis_state_root = generate_genesis_state_root( @@ -190,6 +187,7 @@ fn initialize_genesis_receipt( DomainInstanceData { runtime_type, runtime_code, + raw_genesis_config, }, ) .ok_or(Error::FailedToGenerateGenesisStateRoot)?; @@ -236,7 +234,7 @@ mod tests { // Failed to instantiate domain due to `domain_name` too long assert_eq!( - do_instantiate_domain::(domain_config.clone(), creator, created_at), + do_instantiate_domain::(domain_config.clone(), creator, created_at, None), Err(Error::DomainNameTooLong) ); // Recorrect `domain_name` @@ -244,7 +242,7 @@ mod tests { // Failed to instantiate domain due to using unregistered runtime id assert_eq!( - do_instantiate_domain::(domain_config.clone(), creator, created_at), + do_instantiate_domain::(domain_config.clone(), creator, created_at, None), Err(Error::RuntimeNotFound) ); // Register runtime id @@ -270,7 +268,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), + do_instantiate_domain::(domain_config.clone(), creator, created_at, None), Err(Error::ExceedMaxDomainBlockSize) ); // Recorrect `max_block_size` @@ -278,7 +276,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), + do_instantiate_domain::(domain_config.clone(), creator, created_at, None), Err(Error::ExceedMaxDomainBlockWeight) ); // Recorrect `max_block_weight` @@ -286,12 +284,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), + do_instantiate_domain::(domain_config.clone(), creator, created_at, None), Err(Error::InvalidBundlesPerBlock) ); domain_config.target_bundles_per_block = u32::MAX; assert_eq!( - do_instantiate_domain::(domain_config.clone(), creator, created_at), + do_instantiate_domain::(domain_config.clone(), creator, created_at, None), Err(Error::InvalidBundlesPerBlock) ); // Recorrect `target_bundles_per_block` @@ -299,22 +297,22 @@ mod tests { // Failed to instantiate domain due to invalid `bundle_slot_probability` assert_eq!( - do_instantiate_domain::(domain_config.clone(), creator, created_at), + do_instantiate_domain::(domain_config.clone(), creator, created_at, None), Err(Error::InvalidSlotProbability) ); domain_config.bundle_slot_probability = (1, 0); assert_eq!( - do_instantiate_domain::(domain_config.clone(), creator, created_at), + do_instantiate_domain::(domain_config.clone(), creator, created_at, None), Err(Error::InvalidSlotProbability) ); domain_config.bundle_slot_probability = (0, 1); assert_eq!( - do_instantiate_domain::(domain_config.clone(), creator, created_at), + do_instantiate_domain::(domain_config.clone(), creator, created_at, None), Err(Error::InvalidSlotProbability) ); domain_config.bundle_slot_probability = (2, 1); assert_eq!( - do_instantiate_domain::(domain_config.clone(), creator, created_at), + do_instantiate_domain::(domain_config.clone(), creator, created_at, None), Err(Error::InvalidSlotProbability) ); // Recorrect `bundle_slot_probability` @@ -322,7 +320,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), + do_instantiate_domain::(domain_config.clone(), creator, created_at, None), Err(Error::InsufficientFund) ); // Set enough fund to creator @@ -334,7 +332,8 @@ mod tests { // `instantiate_domain` must success now let domain_id = - do_instantiate_domain::(domain_config.clone(), creator, created_at).unwrap(); + do_instantiate_domain::(domain_config.clone(), creator, created_at, None) + .unwrap(); let domain_obj = DomainRegistry::::get(domain_id).unwrap(); assert_eq!(domain_obj.owner_account_id, creator); @@ -346,7 +345,7 @@ mod tests { // cannot use the locked funds to create a new domain instance assert!( - do_instantiate_domain::(domain_config, creator, created_at) + do_instantiate_domain::(domain_config, creator, created_at, None) == Err(Error::InsufficientFund) ) }); diff --git a/crates/pallet-domains/src/lib.rs b/crates/pallet-domains/src/lib.rs index c4ac421ff1..e4969f8024 100644 --- a/crates/pallet-domains/src/lib.rs +++ b/crates/pallet-domains/src/lib.rs @@ -837,7 +837,7 @@ mod pallet { 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) + let domain_id = do_instantiate_domain::(domain_config, who, created_at, None) .map_err(Error::::from)?; Self::deposit_event(Event::DomainInstantiated { domain_id }); @@ -933,23 +933,34 @@ mod pallet { impl Hooks for Pallet { fn on_initialize(block_number: T::BlockNumber) -> Weight { if block_number.is_one() { - if let Some(ref genesis_domain) = PendingGenesisDomain::::take() { + if let Some(genesis_domain) = PendingGenesisDomain::::take() { // Register the genesis domain runtime let runtime_id = register_runtime_at_genesis::( - genesis_domain.runtime_name.clone(), - genesis_domain.runtime_type.clone(), - genesis_domain.runtime_version.clone(), - genesis_domain.code.clone(), + 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::from_genesis::(genesis_domain, runtime_id); - let domain_owner = genesis_domain.owner_account_id.clone(); - let domain_id = - do_instantiate_domain::(domain_config, domain_owner.clone(), One::one()) - .expect("Genesis domain instantiation must always succeed"); + 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 { @@ -1080,6 +1091,7 @@ impl Pallet { DomainInstanceData { runtime_type, runtime_code, + raw_genesis_config: domain_obj.raw_genesis_config, }, domain_obj.created_at, )) diff --git a/crates/pallet-domains/src/tests.rs b/crates/pallet-domains/src/tests.rs index 07cc55a661..6a1478708c 100644 --- a/crates/pallet-domains/src/tests.rs +++ b/crates/pallet-domains/src/tests.rs @@ -484,6 +484,7 @@ 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/src/lib.rs b/crates/sp-domains/src/lib.rs index 56c580da3c..8dab74f293 100644 --- a/crates/sp-domains/src/lib.rs +++ b/crates/sp-domains/src/lib.rs @@ -433,6 +433,7 @@ pub struct GenesisDomain { 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, @@ -530,6 +531,11 @@ impl DomainsDigestItem for DigestItem { 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 { diff --git a/crates/subspace-node/src/chain_spec.rs b/crates/subspace-node/src/chain_spec.rs index e10450c578..8042ed94d6 100644 --- a/crates/subspace-node/src/chain_spec.rs +++ b/crates/subspace-node/src/chain_spec.rs @@ -19,6 +19,7 @@ use crate::chain_spec_utils::{ chain_spec_properties, get_account_id_from_seed, get_public_key_from_seed, }; +use crate::domain::evm_chain_spec::{self, SpecId}; use sc_service::{ChainType, NoExtension}; use sc_subspace_chain_specs::ConsensusChainSpec; use sc_telemetry::TelemetryEndpoints; @@ -137,6 +138,7 @@ pub fn gemini_3e_compiled() -> Result, String> }) .collect::>(); subspace_genesis_config( + SpecId::Gemini, WASM_BINARY.expect("Wasm binary must be built for Gemini"), sudo_account, balances, @@ -230,6 +232,7 @@ pub fn devnet_config_compiled() -> Result, Str }) .collect::>(); subspace_genesis_config( + SpecId::DevNet, WASM_BINARY.expect("Wasm binary must be built for Gemini"), sudo_account, balances, @@ -272,6 +275,7 @@ pub fn dev_config() -> Result, String> { ChainType::Development, || { subspace_genesis_config( + SpecId::Dev, wasm_binary, // Sudo account get_account_id_from_seed("Alice"), @@ -318,6 +322,7 @@ pub fn local_config() -> Result, String> { ChainType::Local, || { subspace_genesis_config( + SpecId::Local, wasm_binary, // Sudo account get_account_id_from_seed("Alice"), @@ -363,6 +368,7 @@ pub fn local_config() -> Result, String> { /// Configure initial storage state for FRAME modules. fn subspace_genesis_config( + spec_id: SpecId, wasm_binary: &[u8], sudo_account: AccountId, balances: Vec<(AccountId, Balance)>, @@ -379,6 +385,14 @@ fn subspace_genesis_config( confirmation_depth_k, } = genesis_params; + let raw_domain_genesis_config = { + let mut domain_genesis_config = 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(); + serde_json::to_vec(&domain_genesis_config) + .expect("Genesis config serialization never fails; qed") + }; + GenesisConfig { system: SystemConfig { // Add Wasm runtime to storage. @@ -417,6 +431,7 @@ fn subspace_genesis_config( max_block_weight: MaxDomainBlockWeight::get(), bundle_slot_probability: (1, 1), target_bundles_per_block: 10, + raw_genesis_config: raw_domain_genesis_config, // TODO: Configurable genesis operator signing key. signing_key: get_public_key_from_seed::("Alice"), diff --git a/crates/subspace-node/src/domain.rs b/crates/subspace-node/src/domain.rs index 433d6ac7da..90dbc05be2 100644 --- a/crates/subspace-node/src/domain.rs +++ b/crates/subspace-node/src/domain.rs @@ -90,10 +90,19 @@ where 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 = evm_domain_runtime::RuntimeGenesisConfig::default(); + 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( diff --git a/crates/subspace-node/src/domain/evm_chain_spec.rs b/crates/subspace-node/src/domain/evm_chain_spec.rs index c0d2e3ec9d..e0247711c3 100644 --- a/crates/subspace-node/src/domain/evm_chain_spec.rs +++ b/crates/subspace-node/src/domain/evm_chain_spec.rs @@ -147,82 +147,87 @@ pub fn devnet_config GenesisConfig + 'static + Send + Sync>( } pub fn load_chain_spec(spec_id: &str) -> Result, String> { - let accounts = get_dev_accounts(); let chain_spec = match spec_id { - "dev" => { - let constructor = move || { - testnet_genesis( - accounts.clone(), - // Alith is Sudo - Some(accounts[0]), - vec![( - accounts[0], - AccountId32ToAccountId20Converter::convert( - get_from_seed::("Alice").into(), - ), - )], - 1000, - ) - }; - development_config(constructor) + "dev" => development_config(move || get_testnet_genesis_by_spec_id(SpecId::Dev)), + "gemini-3e" => gemini_3e_config(move || get_testnet_genesis_by_spec_id(SpecId::Gemini)), + "devnet" => devnet_config(move || get_testnet_genesis_by_spec_id(SpecId::DevNet)), + "" | "local" => local_testnet_config(move || get_testnet_genesis_by_spec_id(SpecId::Local)), + path => ChainSpec::from_json_file(std::path::PathBuf::from(path))?, + }; + Ok(Box::new(chain_spec)) +} + +pub enum SpecId { + Dev, + Gemini, + DevNet, + Local, +} + +pub fn get_testnet_genesis_by_spec_id(spec_id: SpecId) -> GenesisConfig { + match spec_id { + SpecId::Dev => { + let accounts = get_dev_accounts(); + testnet_genesis( + accounts.clone(), + // Alith is Sudo + Some(accounts[0]), + vec![( + accounts[0], + AccountId32ToAccountId20Converter::convert( + get_from_seed::("Alice").into(), + ), + )], + 1000, + ) } - "gemini-3e" => { - let constructor = move || { - let sudo_account = AccountId::from_str("f31e60022e290708c17d6997c34de6a30d09438f") - .expect("Invalid Sudo account"); - testnet_genesis( - vec![ - // Genesis operator - AccountId::from_str("2ac6c70c106138c8cd80da6b6a0e886b7eeee249") - .expect("Wrong executor account address"), - // Sudo account - sudo_account, - ], - Some(sudo_account), - Default::default(), - 1002, - ) - }; - gemini_3e_config(constructor) + SpecId::Gemini => { + let sudo_account = AccountId::from_str("f31e60022e290708c17d6997c34de6a30d09438f") + .expect("Invalid Sudo account"); + testnet_genesis( + vec![ + // Genesis operator + AccountId::from_str("2ac6c70c106138c8cd80da6b6a0e886b7eeee249") + .expect("Wrong executor account address"), + // Sudo account + sudo_account, + ], + Some(sudo_account), + Default::default(), + 1002, + ) } - "devnet" => { - let constructor = move || { - let sudo_account = AccountId::from_str("b66a91845249464309fad766fd0ece8144547736") - .expect("Invalid Sudo account"); - testnet_genesis( - vec![ - // Genesis operator - AccountId::from_str("cfdf9f58d9e532c3807ce62a5489cb19cfa6942d") - .expect("Wrong executor account address"), - // Sudo account - sudo_account, - ], - Some(sudo_account), - vec![( - sudo_account, - AccountId::from_str("5b267fd1ba3ace6e3c3234f9576c49c877b5beb9") - .expect("Wrong relayer account address"), - )], - 1003, - ) - }; - devnet_config(constructor) + SpecId::DevNet => { + let sudo_account = AccountId::from_str("b66a91845249464309fad766fd0ece8144547736") + .expect("Invalid Sudo account"); + testnet_genesis( + vec![ + // Genesis operator + AccountId::from_str("cfdf9f58d9e532c3807ce62a5489cb19cfa6942d") + .expect("Wrong executor account address"), + // Sudo account + sudo_account, + ], + Some(sudo_account), + vec![( + sudo_account, + AccountId::from_str("5b267fd1ba3ace6e3c3234f9576c49c877b5beb9") + .expect("Wrong relayer account address"), + )], + 1003, + ) } - "" | "local" => { - let constructor = move || { - testnet_genesis( - accounts.clone(), - // Alith is sudo - Some(accounts[0]), - vec![(accounts[0], accounts[0]), (accounts[1], accounts[1])], - 1001, - ) - }; - local_testnet_config(constructor) + SpecId::Local => { + let accounts = get_dev_accounts(); + testnet_genesis( + accounts.clone(), + // Alith is sudo + Some(accounts[0]), + vec![(accounts[0], accounts[0]), (accounts[1], accounts[1])], + 1001, + ) } - path => ChainSpec::from_json_file(std::path::PathBuf::from(path))?, - }; - Ok(Box::new(chain_spec)) + } } // HACK: `ChainSpec::from_genesis` is only allow to create hardcoded spec and `GenesisConfig` @@ -258,13 +263,6 @@ fn load_chain_spec_with( Ok(Box::new(chain_spec)) } -fn domain_instance_genesis_config(domain_id: DomainId, runtime_code: Vec) -> GenesisConfig { - let mut cfg = GenesisConfig::default(); - cfg.system.code = runtime_code; - cfg.self_domain_id.domain_id = Some(domain_id); - cfg -} - pub fn create_domain_spec( domain_id: DomainId, chain_id: &str, @@ -273,11 +271,21 @@ pub fn create_domain_spec( let DomainInstanceData { runtime_type, runtime_code, + raw_genesis_config, } = domain_instance_data; match runtime_type { RuntimeType::Evm => { - let genesis_config = domain_instance_genesis_config(domain_id, runtime_code); + 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 => GenesisConfig::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) } diff --git a/domains/client/block-preprocessor/src/lib.rs b/domains/client/block-preprocessor/src/lib.rs index f87f75016c..9ac17ad604 100644 --- a/domains/client/block-preprocessor/src/lib.rs +++ b/domains/client/block-preprocessor/src/lib.rs @@ -391,6 +391,7 @@ where return Ok(false); } + // TODO: the `check_transaction_validity` is unimplemented let is_legal_tx = self .client .runtime_api() diff --git a/domains/client/domain-operator/src/tests.rs b/domains/client/domain-operator/src/tests.rs index 9e13d8f6bc..e3bf2d9aab 100644 --- a/domains/client/domain-operator/src/tests.rs +++ b/domains/client/domain-operator/src/tests.rs @@ -182,12 +182,7 @@ async fn test_domain_block_production() { assert_eq!(alice.client.info().best_number, domain_block_number + 10); } -// TODO: Disabled because the pallet-balance genesis config of the evm domain is empty and -// there is no initial balance in the testing account thus the `transfer` extrinsic will fail -// due to unable to pay transaction fee, unlock once we can set customized genesis config in -// test environment. #[substrate_test_utils::test(flavor = "multi_thread")] -#[ignore] async fn test_domain_block_deriving_from_multiple_bundles() { let directory = TempDir::new().expect("Must be able to create temporary directory"); @@ -447,12 +442,7 @@ async fn collected_receipts_should_be_on_the_same_branch_with_current_best_block ); } -// TODO: Disabled because the pallet-balance genesis config of the evm domain is empty and -// there is no initial balance in the testing account thus the `transfer` extrinsic will fail -// due to unable to pay transaction fee, unlock once we can set customized genesis config in -// test environment. #[substrate_test_utils::test(flavor = "multi_thread")] -#[ignore] async fn test_domain_tx_propagate() { let directory = TempDir::new().expect("Must be able to create temporary directory"); diff --git a/domains/runtime/evm/src/lib.rs b/domains/runtime/evm/src/lib.rs index a4c180ad66..9ecafd9ee0 100644 --- a/domains/runtime/evm/src/lib.rs +++ b/domains/runtime/evm/src/lib.rs @@ -795,7 +795,8 @@ impl_runtime_apis! { _uxt: ::Extrinsic, _block_hash: ::Hash, ) -> Result<(), domain_runtime_primitives::CheckTxValidityError> { - unimplemented!("TODO: check transaction fee to core-evm") + // TODO: check transaction fee to core-evm + Ok(()) } fn storage_keys_for_verifying_transaction_validity( diff --git a/domains/test/runtime/evm/src/lib.rs b/domains/test/runtime/evm/src/lib.rs index 671b579c15..684ce1a12d 100644 --- a/domains/test/runtime/evm/src/lib.rs +++ b/domains/test/runtime/evm/src/lib.rs @@ -795,7 +795,8 @@ impl_runtime_apis! { _uxt: ::Extrinsic, _block_hash: ::Hash, ) -> Result<(), domain_runtime_primitives::CheckTxValidityError> { - unimplemented!("TODO: check transaction fee to core-evm") + // TODO: check transaction fee to core-evm + Ok(()) } fn storage_keys_for_verifying_transaction_validity( diff --git a/domains/test/service/src/chain_spec.rs b/domains/test/service/src/chain_spec.rs index 48ebca472e..daeb7f602c 100644 --- a/domains/test/service/src/chain_spec.rs +++ b/domains/test/service/src/chain_spec.rs @@ -1,27 +1,9 @@ //! Chain specification for the domain test runtime. -use crate::EcdsaKeyring::{Alice, Bob, Charlie, Dave, Eve, Ferdie}; -use evm_domain_test_runtime::{AccountId as AccountId20, GenesisConfig, Precompiles, Signature}; + +use evm_domain_test_runtime::GenesisConfig; use once_cell::sync::OnceCell; use sc_service::{ChainSpec, ChainType, GenericChainSpec}; -use sp_core::{ecdsa, Pair, Public}; -use sp_domains::DomainId; -use sp_runtime::traits::{IdentifyAccount, Verify}; -use subspace_runtime_primitives::SSC; - -type AccountPublic = ::Signer; - -/// Helper function to generate an account ID from seed. -pub fn get_account_id_from_seed(seed: &str) -> AccountId20 -where - AccountPublic: From<::Public>, -{ - AccountPublic::from( - TPublic::Pair::from_string(&format!("//{seed}"), None) - .expect("static values are valid; qed") - .public(), - ) - .into_account() -} +use sp_domains::{DomainId, DomainInstanceData, RuntimeType}; macro_rules! chain_spec_from_genesis { ( $constructor:expr ) => {{ @@ -40,87 +22,6 @@ macro_rules! chain_spec_from_genesis { }}; } -/// Get the chain spec for the given domain. -/// -/// Note: for convenience, the returned chain spec give some specific accounts the ability to -/// win the bundle election for a specific domain with (nearly) 100% probability in each slot: -/// [Evm domain => Alice] -pub fn get_chain_spec() -> Box { - Box::new(chain_spec_from_genesis!(testnet_evm_genesis)) -} - -fn endowed_accounts() -> Vec { - vec![ - Alice.to_account_id(), - Bob.to_account_id(), - Charlie.to_account_id(), - Dave.to_account_id(), - Eve.to_account_id(), - Ferdie.to_account_id(), - get_account_id_from_seed::("Alice//stash"), - get_account_id_from_seed::("Bob//stash"), - get_account_id_from_seed::("Charlie//stash"), - get_account_id_from_seed::("Dave//stash"), - get_account_id_from_seed::("Eve//stash"), - get_account_id_from_seed::("Ferdie//stash"), - ] -} - -fn testnet_evm_genesis() -> GenesisConfig { - // This is the simplest bytecode to revert without returning any data. - // We will pre-deploy it under all of our precompiles to ensure they can be called from - // within contracts. - // (PUSH1 0x00 PUSH1 0x00 REVERT) - let revert_bytecode = vec![0x60, 0x00, 0x60, 0x00, 0xFD]; - - evm_domain_test_runtime::GenesisConfig { - system: evm_domain_test_runtime::SystemConfig { - code: evm_domain_test_runtime::WASM_BINARY - .expect("WASM binary was not build, please build it!") - .to_vec(), - }, - transaction_payment: Default::default(), - balances: evm_domain_test_runtime::BalancesConfig { - balances: endowed_accounts() - .iter() - .cloned() - .map(|k| (k, 2_000_000 * SSC)) - .collect(), - }, - messenger: evm_domain_test_runtime::MessengerConfig { - relayers: vec![(Alice.to_account_id(), Alice.to_account_id())], - }, - sudo: evm_domain_test_runtime::SudoConfig { - key: Some(Alice.to_account_id()), - }, - evm_chain_id: evm_domain_test_runtime::EVMChainIdConfig { chain_id: 100 }, - evm: evm_domain_test_runtime::EVMConfig { - // We need _some_ code inserted at the precompile address so that - // the evm will actually call the address. - accounts: Precompiles::used_addresses() - .into_iter() - .map(|addr| { - ( - addr, - fp_evm::GenesisAccount { - nonce: Default::default(), - balance: Default::default(), - storage: Default::default(), - code: revert_bytecode.clone(), - }, - ) - }) - .collect(), - }, - 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)), - }, - } -} - /// HACK: `ChainSpec::from_genesis` is only allow to create hardcoded spec and `GenesisConfig` /// dosen't derive `Clone`, using global variable and serialization/deserialization to workaround /// these limits @@ -130,7 +31,7 @@ fn testnet_evm_genesis() -> GenesisConfig { static GENESIS_CONFIG: OnceCell> = OnceCell::new(); /// Load chain spec that contains the given `GenesisConfig` -pub fn load_chain_spec_with(genesis_config: GenesisConfig) -> Box { +fn load_chain_spec_with(genesis_config: GenesisConfig) -> Box { let _ = GENESIS_CONFIG.set( serde_json::to_vec(&genesis_config).expect("Genesis config serialization never fails; qed"), ); @@ -143,10 +44,28 @@ pub fn load_chain_spec_with(genesis_config: GenesisConfig) -> Box Box::new(chain_spec_from_genesis!(constructor)) } -/// Create a `GenesisConfig` -pub fn domain_instance_genesis_config(domain_id: DomainId, runtime_code: Vec) -> GenesisConfig { - let mut cfg = GenesisConfig::default(); - cfg.system.code = runtime_code; - cfg.self_domain_id.domain_id = Some(domain_id); - cfg +/// 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; + + 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 => GenesisConfig::default(), + }; + genesis_config.system.code = runtime_code; + genesis_config.self_domain_id.domain_id = Some(domain_id); + + load_chain_spec_with(genesis_config) + } + } } diff --git a/domains/test/service/src/domain.rs b/domains/test/service/src/domain.rs index 012777f0e1..862b35dbf5 100644 --- a/domains/test/service/src/domain.rs +++ b/domains/test/service/src/domain.rs @@ -1,7 +1,7 @@ //! Utilities used for testing with the domain. #![warn(missing_docs)] -use crate::chain_spec::{domain_instance_genesis_config, load_chain_spec_with}; +use crate::chain_spec::create_domain_spec; use crate::{construct_extrinsic_generic, node_config, EcdsaKeyring, UncheckedExtrinsicFor}; use domain_client_operator::{BootstrapResult, Bootstrapper, OperatorStreams}; use domain_runtime_primitives::opaque::Block; @@ -183,11 +183,7 @@ where .await .expect("Failed to get domain instance data") }; - let chain_spec = { - let genesis_config = - domain_instance_genesis_config(domain_id, domain_instance_data.runtime_code); - load_chain_spec_with(genesis_config) - }; + let chain_spec = create_domain_spec(domain_id, domain_instance_data); let service_config = node_config( domain_id, tokio_handle.clone(), diff --git a/test/subspace-test-client/Cargo.toml b/test/subspace-test-client/Cargo.toml index b35aa45c44..fdc26ab7e0 100644 --- a/test/subspace-test-client/Cargo.toml +++ b/test/subspace-test-client/Cargo.toml @@ -16,6 +16,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] 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 = "c13d670b25b5506c1c5243f352941dc46c82ffe4" } futures = "0.3.28" schnorrkel = "0.9.1" sc-chain-spec = { git = "https://github.com/subspace/substrate", rev = "55c157cff49b638a59d81a9f971f0f9a66829c71" } @@ -23,6 +24,7 @@ sc-client-api = { git = "https://github.com/subspace/substrate", rev = "55c157cf sc-consensus-subspace = { version = "0.1.0", path = "../../crates/sc-consensus-subspace" } sc-executor = { git = "https://github.com/subspace/substrate", rev = "55c157cff49b638a59d81a9f971f0f9a66829c71" } sc-service = { git = "https://github.com/subspace/substrate", rev = "55c157cff49b638a59d81a9f971f0f9a66829c71", default-features = false } +serde_json = "1.0.95" sp-api = { git = "https://github.com/subspace/substrate", rev = "55c157cff49b638a59d81a9f971f0f9a66829c71" } sp-consensus-subspace = { version = "0.1.0", path = "../../crates/sp-consensus-subspace" } sp-core = { git = "https://github.com/subspace/substrate", rev = "55c157cff49b638a59d81a9f971f0f9a66829c71" } diff --git a/test/subspace-test-client/src/chain_spec.rs b/test/subspace-test-client/src/chain_spec.rs index 154aa1505c..0bed5402dd 100644 --- a/test/subspace-test-client/src/chain_spec.rs +++ b/test/subspace-test-client/src/chain_spec.rs @@ -1,5 +1,6 @@ //! Chain specification for the test runtime. +use crate::domain_chain_spec::testnet_evm_genesis; use sc_chain_spec::ChainType; use sp_core::{sr25519, Pair, Public}; use sp_domains::{GenesisDomain, OperatorPublicKey, RuntimeType}; @@ -76,6 +77,13 @@ fn create_genesis_config( // who, start, period, period_count, per_period vesting: Vec<(AccountId, BlockNumber, BlockNumber, u32, Balance)>, ) -> GenesisConfig { + 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") + }; GenesisConfig { system: SystemConfig { // Add Wasm runtime to storage. @@ -109,6 +117,7 @@ fn create_genesis_config( 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 new file mode 100644 index 0000000000..b973f733db --- /dev/null +++ b/test/subspace-test-client/src/domain_chain_spec.rs @@ -0,0 +1,95 @@ +//! Chain specification for the evm domain. + +use evm_domain_test_runtime::{AccountId as AccountId20, GenesisConfig, Precompiles, Signature}; +use sp_core::{ecdsa, Pair, Public}; +use sp_domains::DomainId; +use sp_runtime::traits::{IdentifyAccount, Verify}; +use subspace_runtime_primitives::SSC; + +type AccountPublic = ::Signer; + +/// Helper function to generate an account ID from seed. +pub fn get_account_id_from_seed(seed: &str) -> AccountId20 +where + AccountPublic: From<::Public>, +{ + AccountPublic::from( + TPublic::Pair::from_string(&format!("//{seed}"), None) + .expect("static values are valid; qed") + .public(), + ) + .into_account() +} + +fn endowed_accounts() -> Vec { + vec![ + get_account_id_from_seed::("Alice"), + get_account_id_from_seed::("Bob"), + get_account_id_from_seed::("Charlie"), + get_account_id_from_seed::("Dave"), + get_account_id_from_seed::("Eve"), + get_account_id_from_seed::("Ferdie"), + get_account_id_from_seed::("Alice//stash"), + get_account_id_from_seed::("Bob//stash"), + get_account_id_from_seed::("Charlie//stash"), + get_account_id_from_seed::("Dave//stash"), + get_account_id_from_seed::("Eve//stash"), + get_account_id_from_seed::("Ferdie//stash"), + ] +} + +/// Get the genesis config of the evm domain +pub fn testnet_evm_genesis() -> GenesisConfig { + // This is the simplest bytecode to revert without returning any data. + // We will pre-deploy it under all of our precompiles to ensure they can be called from + // within contracts. + // (PUSH1 0x00 PUSH1 0x00 REVERT) + let revert_bytecode = vec![0x60, 0x00, 0x60, 0x00, 0xFD]; + + let alice = get_account_id_from_seed::("Alice"); + + evm_domain_test_runtime::GenesisConfig { + system: evm_domain_test_runtime::SystemConfig { + code: evm_domain_test_runtime::WASM_BINARY + .expect("WASM binary was not build, please build it!") + .to_vec(), + }, + transaction_payment: Default::default(), + balances: evm_domain_test_runtime::BalancesConfig { + balances: endowed_accounts() + .iter() + .cloned() + .map(|k| (k, 2_000_000 * SSC)) + .collect(), + }, + messenger: evm_domain_test_runtime::MessengerConfig { + relayers: vec![(alice, alice)], + }, + sudo: evm_domain_test_runtime::SudoConfig { key: Some(alice) }, + evm_chain_id: evm_domain_test_runtime::EVMChainIdConfig { chain_id: 100 }, + evm: evm_domain_test_runtime::EVMConfig { + // We need _some_ code inserted at the precompile address so that + // the evm will actually call the address. + accounts: Precompiles::used_addresses() + .into_iter() + .map(|addr| { + ( + addr, + fp_evm::GenesisAccount { + nonce: Default::default(), + balance: Default::default(), + storage: Default::default(), + code: revert_bytecode.clone(), + }, + ) + }) + .collect(), + }, + 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)), + }, + } +} diff --git a/test/subspace-test-client/src/lib.rs b/test/subspace-test-client/src/lib.rs index 0b76e10a61..3d30cf2313 100644 --- a/test/subspace-test-client/src/lib.rs +++ b/test/subspace-test-client/src/lib.rs @@ -19,6 +19,7 @@ #![warn(missing_docs, unused_crate_dependencies)] pub mod chain_spec; +pub mod domain_chain_spec; use futures::executor::block_on; use futures::StreamExt;