From 92bba1d2fe806071bf8142e2d10f0865a0b51785 Mon Sep 17 00:00:00 2001 From: vedhavyas Date: Fri, 9 Feb 2024 17:51:09 +0530 Subject: [PATCH 01/13] domain instantiation accept endowed accounts for the domain at genesis --- Cargo.lock | 4 + crates/pallet-domains/Cargo.toml | 4 +- crates/pallet-domains/src/benchmarking.rs | 2 + crates/pallet-domains/src/domain_registry.rs | 108 ++++++++++++++++-- crates/pallet-domains/src/lib.rs | 22 ++-- crates/pallet-domains/src/runtime_registry.rs | 79 +++++++++++-- crates/pallet-domains/src/staking.rs | 3 + crates/pallet-domains/src/staking_epoch.rs | 1 + crates/pallet-domains/src/tests.rs | 2 + crates/sp-domains/src/lib.rs | 47 +++++++- crates/sp-domains/src/storage.rs | 6 + .../src/chain_spec.rs | 30 ++--- crates/subspace-node/src/chain_spec.rs | 1 + .../src/domain/evm_chain_spec.rs | 45 +++----- crates/subspace-runtime/src/lib.rs | 23 +--- domains/primitives/runtime/Cargo.toml | 4 + domains/primitives/runtime/src/lib.rs | 22 +++- domains/runtime/evm/src/lib.rs | 26 +---- domains/test/runtime/evm/src/lib.rs | 26 +---- test/subspace-test-client/Cargo.toml | 1 + test/subspace-test-client/src/chain_spec.rs | 13 ++- .../src/domain_chain_spec.rs | 11 +- test/subspace-test-runtime/src/lib.rs | 23 +--- 23 files changed, 337 insertions(+), 166 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 368d370314..bf2522faa7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2647,10 +2647,12 @@ dependencies = [ name = "domain-runtime-primitives" version = "0.1.0" dependencies = [ + "fp-account", "frame-support", "frame-system", "parity-scale-codec", "scale-info", + "serde", "sp-api", "sp-core", "sp-runtime", @@ -7161,6 +7163,7 @@ dependencies = [ "frame-benchmarking", "frame-support", "frame-system", + "hex-literal", "log", "pallet-balances", "pallet-block-fees", @@ -12017,6 +12020,7 @@ dependencies = [ name = "subspace-test-client" version = "0.1.0" dependencies = [ + "domain-runtime-primitives", "evm-domain-test-runtime", "fp-evm", "futures", diff --git a/crates/pallet-domains/Cargo.toml b/crates/pallet-domains/Cargo.toml index 4c9a00c409..0115dc6312 100644 --- a/crates/pallet-domains/Cargo.toml +++ b/crates/pallet-domains/Cargo.toml @@ -18,6 +18,7 @@ frame-benchmarking = { version = "4.0.0-dev", default-features = false, git = "h frame-support = { version = "4.0.0-dev", default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "d6b500960579d73c43fc4ef550b703acfa61c4c8" } frame-system = { version = "4.0.0-dev", default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "d6b500960579d73c43fc4ef550b703acfa61c4c8" } log = { version = "0.4.20", default-features = false } +pallet-balances = { version = "4.0.0-dev", default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "d6b500960579d73c43fc4ef550b703acfa61c4c8" } scale-info = { version = "2.7.0", default-features = false, features = ["derive"] } sp-consensus-slots = { version = "0.10.0-dev", default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "d6b500960579d73c43fc4ef550b703acfa61c4c8" } sp-consensus-subspace = { version = "0.1.0", default-features = false, path = "../sp-consensus-subspace" } @@ -33,7 +34,7 @@ subspace-runtime-primitives = { version = "0.1.0", default-features = false, pat [dev-dependencies] domain-pallet-executive = { version = "0.1.0", default-features = false, path = "../../domains/pallets/executive" } -pallet-balances = { version = "4.0.0-dev", git = "https://github.com/subspace/polkadot-sdk", rev = "d6b500960579d73c43fc4ef550b703acfa61c4c8" } +hex-literal = "0.4.1" pallet-timestamp = { version = "4.0.0-dev", git = "https://github.com/subspace/polkadot-sdk", rev = "d6b500960579d73c43fc4ef550b703acfa61c4c8" } pallet-block-fees = { version = "0.1.0", default-features = false, path = "../../domains/pallets/block-fees" } sp-externalities = { version = "0.19.0", git = "https://github.com/subspace/polkadot-sdk", rev = "d6b500960579d73c43fc4ef550b703acfa61c4c8" } @@ -49,6 +50,7 @@ std = [ "frame-support/std", "frame-system/std", "log/std", + "pallet-balances/std", "scale-info/std", "sp-consensus-slots/std", "sp-consensus-subspace/std", diff --git a/crates/pallet-domains/src/benchmarking.rs b/crates/pallet-domains/src/benchmarking.rs index 8346dbe844..251370dcbd 100644 --- a/crates/pallet-domains/src/benchmarking.rs +++ b/crates/pallet-domains/src/benchmarking.rs @@ -225,6 +225,7 @@ mod benchmarks { bundle_slot_probability: (1, 1), target_bundles_per_block: 10, operator_allow_list: OperatorAllowList::Anyone, + initial_balances: Default::default(), }; #[extrinsic_call] @@ -434,6 +435,7 @@ mod benchmarks { bundle_slot_probability: (1, 1), target_bundles_per_block: 10, operator_allow_list: OperatorAllowList::Anyone, + initial_balances: Default::default(), }; assert_ok!(Domains::::instantiate_domain( diff --git a/crates/pallet-domains/src/domain_registry.rs b/crates/pallet-domains/src/domain_registry.rs index 1469002936..9af5d08058 100644 --- a/crates/pallet-domains/src/domain_registry.rs +++ b/crates/pallet-domains/src/domain_registry.rs @@ -5,11 +5,12 @@ use crate::pallet::{DomainStakingSummary, NextEVMChainId}; use crate::runtime_registry::DomainRuntimeInfo; use crate::staking::StakingSummary; use crate::{ - Config, DomainHashingFor, DomainRegistry, ExecutionReceiptOf, HoldIdentifier, NextDomainId, - RuntimeRegistry, + BalanceOf, Config, DomainHashingFor, DomainRegistry, ExecutionReceiptOf, HoldIdentifier, + NextDomainId, RuntimeRegistry, }; use alloc::string::String; use codec::{Decode, Encode}; +use domain_runtime_primitives::MultiAccountId; use frame_support::traits::fungible::{Inspect, MutateHold}; use frame_support::traits::tokens::{Fortitude, Preservation}; use frame_support::weights::Weight; @@ -25,6 +26,7 @@ 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,10 +44,11 @@ pub enum Error { FailedToGenerateGenesisStateRoot, DomainNotFound, NotDomainOwner, + FailedToGenerateRawGenesis(crate::runtime_registry::Error), } #[derive(TypeInfo, Debug, Encode, Decode, Clone, PartialEq, Eq)] -pub struct DomainConfig { +pub struct DomainConfig { /// A user defined name for this domain, should be a human-readable UTF-8 encoded string. pub domain_name: String, /// A pointer to the `RuntimeRegistry` entry for this domain. @@ -61,10 +64,12 @@ pub struct DomainConfig { pub target_bundles_per_block: u32, /// Allowed operators to operate for this domain. pub operator_allow_list: OperatorAllowList, + // Initial balances for Domain. + pub initial_balances: Vec<(MultiAccountId, Balance)>, } #[derive(TypeInfo, Debug, Encode, Decode, Clone, PartialEq, Eq)] -pub struct DomainObject { +pub struct DomainObject { /// The address of the domain creator, used to validate updating the domain config. pub owner_account_id: AccountId, /// The consensus chain block number when the domain first instantiated. @@ -72,14 +77,14 @@ pub struct DomainObject { /// The hash of the genesis execution receipt for this domain. pub genesis_receipt_hash: ReceiptHash, /// The domain config. - pub domain_config: DomainConfig, + pub domain_config: DomainConfig, /// Domain runtime specific information. pub domain_runtime_info: DomainRuntimeInfo, } pub(crate) fn can_instantiate_domain( owner_account_id: &T::AccountId, - domain_config: &DomainConfig, + domain_config: &DomainConfig>, ) -> Result<(), Error> { ensure!( domain_config.domain_name.len() as u32 <= T::MaxDomainNameLength::get(), @@ -120,7 +125,7 @@ pub(crate) fn can_instantiate_domain( } pub(crate) fn do_instantiate_domain( - domain_config: DomainConfig, + domain_config: DomainConfig>, owner_account_id: T::AccountId, created_at: BlockNumberFor, ) -> Result { @@ -141,9 +146,16 @@ pub(crate) fn do_instantiate_domain( } }; + // TODO: burn the total initial balance for domain from the domain owner account let genesis_receipt = { let state_version = runtime_obj.version.state_version(); - let raw_genesis = runtime_obj.into_complete_raw_genesis(domain_id, domain_runtime_info); + let raw_genesis = runtime_obj + .into_complete_raw_genesis::( + domain_id, + domain_runtime_info, + domain_config.initial_balances.clone(), + ) + .map_err(Error::FailedToGenerateRawGenesis)?; let state_root = raw_genesis.state_root::>(state_version); let genesis_block_hash = derive_domain_block_hash::( Zero::zero(), @@ -222,12 +234,16 @@ mod tests { use crate::pallet::{DomainRegistry, NextDomainId, RuntimeRegistry}; use crate::runtime_registry::RuntimeObject; use crate::tests::{new_test_ext, Test}; - use frame_support::assert_ok; + use domain_runtime_primitives::{AccountId20, AccountId20Converter}; use frame_support::traits::Currency; + use frame_support::{assert_err, assert_ok}; + use hex_literal::hex; use sp_domains::storage::RawGenesis; + use sp_runtime::traits::Convert; use sp_std::collections::btree_set::BTreeSet; use sp_std::vec; use sp_version::RuntimeVersion; + use subspace_runtime_primitives::SSC; type Balances = pallet_balances::Pallet; @@ -244,6 +260,7 @@ mod tests { bundle_slot_probability: (0, 0), target_bundles_per_block: 0, operator_allow_list: OperatorAllowList::Anyone, + initial_balances: Default::default(), }; let mut ext = new_test_ext(); @@ -381,4 +398,77 @@ mod tests { ); }); } + + #[test] + fn test_domain_instantiation_evm_accounts() { + let creator = 1u128; + let created_at = 0u64; + // Construct an invalid domain config initially + let mut domain_config = DomainConfig { + domain_name: "evm-domain".to_owned(), + runtime_id: 0, + max_block_size: 10, + max_block_weight: Weight::from_parts(1, 0), + bundle_slot_probability: (1, 1), + target_bundles_per_block: 1, + operator_allow_list: OperatorAllowList::Anyone, + initial_balances: vec![(MultiAccountId::Raw(vec![0, 1, 2, 3, 4, 5]), 1_000_000 * SSC)], + }; + + let mut ext = new_test_ext(); + ext.execute_with(|| { + assert_eq!(NextDomainId::::get(), 0.into()); + // Register runtime id + RuntimeRegistry::::insert( + domain_config.runtime_id, + RuntimeObject { + runtime_name: "evm".to_owned(), + runtime_type: Default::default(), + runtime_upgrades: 0, + hash: Default::default(), + raw_genesis: RawGenesis::dummy(vec![1, 2, 3, 4]), + version: RuntimeVersion { + spec_name: "test".into(), + spec_version: 1, + impl_version: 1, + transaction_version: 1, + ..Default::default() + }, + created_at: Default::default(), + updated_at: Default::default(), + }, + ); + + // Set enough fund to creator + Balances::make_free_balance_be( + &creator, + ::DomainInstantiationDeposit::get() + + ::ExistentialDeposit::get(), + ); + + // should fail due to invalid account ID type + assert_err!( + do_instantiate_domain::(domain_config.clone(), creator, created_at), + Error::FailedToGenerateRawGenesis( + crate::runtime_registry::Error::InvalidAccountIdType + ) + ); + + domain_config.initial_balances = vec![( + AccountId20Converter::convert(AccountId20::from(hex!( + "f24FF3a9CF04c71Dbc94D0b566f7A27B94566cac" + ))), + 1_000_000 * SSC, + )]; + + // should be successful + let domain_id = + 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); + assert_eq!(domain_obj.created_at, created_at); + assert_eq!(domain_obj.domain_config, domain_config); + }); + } } diff --git a/crates/pallet-domains/src/lib.rs b/crates/pallet-domains/src/lib.rs index 25b8159dc3..fd6f0cd7dc 100644 --- a/crates/pallet-domains/src/lib.rs +++ b/crates/pallet-domains/src/lib.rs @@ -175,7 +175,7 @@ mod pallet { AtLeast32BitUnsigned, BlockNumberProvider, CheckEqual, CheckedAdd, Header as HeaderT, MaybeDisplay, One, SimpleBitOps, Zero, }; - use sp_runtime::{SaturatedConversion, Saturating}; + use sp_runtime::Saturating; use sp_std::boxed::Box; use sp_std::collections::btree_map::BTreeMap; use sp_std::collections::btree_set::BTreeSet; @@ -467,7 +467,7 @@ mod pallet { _, Identity, DomainId, - DomainObject, ReceiptHashFor, T::AccountId>, + DomainObject, ReceiptHashFor, T::AccountId, BalanceOf>, OptionQuery, >; @@ -1155,7 +1155,7 @@ mod pallet { #[pallet::weight(T::WeightInfo::instantiate_domain())] pub fn instantiate_domain( origin: OriginFor, - domain_config: DomainConfig, + domain_config: DomainConfig>, ) -> DispatchResult { ensure_root(origin)?; @@ -1296,7 +1296,7 @@ mod pallet { #[pallet::genesis_config] pub struct GenesisConfig { - pub genesis_domain: Option>, + pub genesis_domain: Option>>, } impl Default for GenesisConfig { @@ -1330,6 +1330,7 @@ mod pallet { bundle_slot_probability: genesis_domain.bundle_slot_probability, target_bundles_per_block: genesis_domain.target_bundles_per_block, operator_allow_list: genesis_domain.operator_allow_list, + initial_balances: genesis_domain.initial_balances, }; let domain_owner = genesis_domain.owner_account_id; let domain_id = @@ -1339,9 +1340,7 @@ mod pallet { // 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(), + minimum_nominator_stake: genesis_domain.minimum_nominator_stake, nomination_tax: genesis_domain.nomination_tax, }; let operator_stake = T::MinOperatorStake::get(); @@ -1516,8 +1515,13 @@ impl Pallet { let domain_obj = DomainRegistry::::get(domain_id)?; 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, domain_obj.domain_runtime_info); + let raw_genesis = runtime_object + .into_complete_raw_genesis::( + domain_id, + domain_obj.domain_runtime_info, + domain_obj.domain_config.initial_balances, + ) + .ok()?; Some(( DomainInstanceData { runtime_type, diff --git a/crates/pallet-domains/src/runtime_registry.rs b/crates/pallet-domains/src/runtime_registry.rs index 240efbb0f4..0be55940fb 100644 --- a/crates/pallet-domains/src/runtime_registry.rs +++ b/crates/pallet-domains/src/runtime_registry.rs @@ -1,18 +1,21 @@ //! Runtime registry for domains use crate::pallet::{NextRuntimeId, RuntimeRegistry, ScheduledRuntimeUpgrades}; -use crate::{Config, Event}; +use crate::{BalanceOf, Config, Event}; use alloc::string::String; use codec::{Decode, Encode}; -use domain_runtime_primitives::EVMChainId; +use domain_runtime_primitives::{AccountId20, EVMChainId, MultiAccountId, TryConvertBack}; use frame_support::PalletError; use frame_system::pallet_prelude::*; +use frame_system::AccountInfo; use scale_info::TypeInfo; use sp_core::Hasher; -use sp_domains::storage::RawGenesis; +use sp_domains::storage::{RawGenesis, StorageData, StorageKey}; use sp_domains::{DomainId, DomainsDigestItem, RuntimeId, RuntimeType}; -use sp_runtime::traits::{CheckedAdd, Get}; +use sp_runtime::traits::{CheckedAdd, Get, Zero}; use sp_runtime::DigestItem; +use sp_std::collections::btree_map::BTreeMap; +use sp_std::vec; use sp_std::vec::Vec; use sp_version::RuntimeVersion; @@ -28,6 +31,8 @@ pub enum Error { MaxScheduledBlockNumber, FailedToDecodeRawGenesis, RuntimeCodeNotFoundInRawGenesis, + InitialBalanceOverflow, + InvalidAccountIdType, } #[derive(TypeInfo, Debug, Encode, Decode, Clone, PartialEq, Eq)] @@ -56,21 +61,79 @@ impl Default for DomainRuntimeInfo { } } +fn derive_initial_balances_storages( + balances: BTreeMap>, +) -> Result, Error> { + let mut initial_storages = vec![]; + let total_balance = balances + .iter() + .try_fold(BalanceOf::::zero(), |total, (_, balance)| { + total.checked_add(balance) + }) + .ok_or(Error::InitialBalanceOverflow)?; + + let total_issuance_key = sp_domains::domain_total_issuance_storage_key(); + initial_storages.push((total_issuance_key, StorageData(total_balance.encode()))); + + for (account_id, balance) in balances { + let account_storage_key = sp_domains::domain_account_storage_key(account_id); + let account_info = AccountInfo { + nonce: domain_runtime_primitives::Nonce::zero(), + consumers: 0, + // providers are set to 1 for new accounts + providers: 1, + sufficients: 0, + data: pallet_balances::AccountData { + free: balance, + ..Default::default() + }, + }; + initial_storages.push((account_storage_key, StorageData(account_info.encode()))) + } + + Ok(initial_storages) +} + impl RuntimeObject { // Return a complete raw genesis with runtime code and domain id set properly - pub fn into_complete_raw_genesis( + pub fn into_complete_raw_genesis( self, domain_id: DomainId, domain_runtime_info: DomainRuntimeInfo, - ) -> RawGenesis { + initial_balances: Vec<(MultiAccountId, BalanceOf)>, + ) -> Result { let RuntimeObject { mut raw_genesis, .. } = self; raw_genesis.set_domain_id(domain_id); match domain_runtime_info { - DomainRuntimeInfo::EVM { chain_id } => raw_genesis.set_evm_chain_id(chain_id), + DomainRuntimeInfo::EVM { chain_id } => { + raw_genesis.set_evm_chain_id(chain_id); + let initial_balances = initial_balances + .into_iter() + .try_fold( + BTreeMap::>::new(), + |mut acc, (account_id, balance)| { + let account_id = + domain_runtime_primitives::AccountId20Converter::try_convert_back( + account_id, + )?; + let balance = if let Some(previous_balance) = acc.get(&account_id) { + previous_balance.checked_add(&balance)? + } else { + balance + }; + acc.insert(account_id, balance); + Some(acc) + }, + ) + .ok_or(Error::InvalidAccountIdType)?; + raw_genesis + .set_top_storages(derive_initial_balances_storages::(initial_balances)?); + } } - raw_genesis + + Ok(raw_genesis) } } diff --git a/crates/pallet-domains/src/staking.rs b/crates/pallet-domains/src/staking.rs index 11f7aba14f..c3930d4ff9 100644 --- a/crates/pallet-domains/src/staking.rs +++ b/crates/pallet-domains/src/staking.rs @@ -1291,6 +1291,7 @@ pub(crate) mod tests { bundle_slot_probability: (0, 0), target_bundles_per_block: 0, operator_allow_list: OperatorAllowList::Anyone, + initial_balances: Default::default(), }; let domain_obj = DomainObject { @@ -1685,6 +1686,7 @@ pub(crate) mod tests { bundle_slot_probability: (0, 0), target_bundles_per_block: 0, operator_allow_list: OperatorAllowList::Anyone, + initial_balances: Default::default(), }; let domain_obj = DomainObject { @@ -1798,6 +1800,7 @@ pub(crate) mod tests { bundle_slot_probability: (0, 0), target_bundles_per_block: 0, operator_allow_list: OperatorAllowList::Anyone, + initial_balances: Default::default(), }; let domain_obj = DomainObject { diff --git a/crates/pallet-domains/src/staking_epoch.rs b/crates/pallet-domains/src/staking_epoch.rs index c6cda6a8a9..c07e7a1d1a 100644 --- a/crates/pallet-domains/src/staking_epoch.rs +++ b/crates/pallet-domains/src/staking_epoch.rs @@ -537,6 +537,7 @@ mod tests { bundle_slot_probability: (0, 0), target_bundles_per_block: 0, operator_allow_list: OperatorAllowList::Anyone, + initial_balances: Default::default(), }; let domain_obj = DomainObject { diff --git a/crates/pallet-domains/src/tests.rs b/crates/pallet-domains/src/tests.rs index a723bf6c2d..f404ad90c7 100644 --- a/crates/pallet-domains/src/tests.rs +++ b/crates/pallet-domains/src/tests.rs @@ -547,6 +547,7 @@ pub(crate) fn register_genesis_domain(creator: u128, operator_ids: Vec OperatorAllowList { } #[derive(TypeInfo, Debug, Encode, Decode, Clone, PartialEq, Eq, Serialize, Deserialize)] -pub struct GenesisDomain { +pub struct GenesisDomain { // Domain runtime items pub runtime_name: String, pub runtime_type: RuntimeType, @@ -722,6 +724,9 @@ pub struct GenesisDomain { pub signing_key: OperatorPublicKey, pub minimum_nominator_stake: Balance, pub nomination_tax: Percent, + + // initial balances + pub initial_balances: Vec<(MultiAccountId, Balance)>, } /// Types of runtime pallet domains currently supports @@ -809,7 +814,7 @@ impl DomainsDigestItem for DigestItem { /// TODO: once the chain is launched in mainnet, we should use the Host function for all domain instances. pub(crate) fn evm_chain_id_storage_key() -> StorageKey { StorageKey( - frame_support::storage::storage_prefix( + storage_prefix( // This is the name used for the `pallet_evm_chain_id` in the `construct_runtime` macro // i.e. `EVMChainId: pallet_evm_chain_id = 82,` "EVMChainId".as_bytes(), @@ -820,6 +825,42 @@ pub(crate) fn evm_chain_id_storage_key() -> StorageKey { ) } +/// Total issuance storage for Domains. +/// +/// This function should ideally use Host function to fetch the storage key +/// from the domain runtime. But since the Host function is not available at Genesis, we have to +/// assume the storage keys. +/// TODO: once the chain is launched in mainnet, we should use the Host function for all domain instances. +pub fn domain_total_issuance_storage_key() -> StorageKey { + StorageKey( + storage_prefix( + // This is the name used for the `pallet_balances` in the `construct_runtime` macro + "Balances".as_bytes(), + // This is the storage item name used inside the `pallet_balances` + "TotalIssuance".as_bytes(), + ) + .to_vec(), + ) +} + +/// Account info on frame_system on Domains +/// +/// This function should ideally use Host function to fetch the storage key +/// from the domain runtime. But since the Host function is not available at Genesis, we have to +/// assume the storage keys. +/// TODO: once the chain is launched in mainnet, we should use the Host function for all domain instances. +pub fn domain_account_storage_key(who: AccountId) -> StorageKey { + let storage_prefix = storage_prefix("System".as_bytes(), "Account".as_bytes()); + let key_hashed = who.using_encoded(Blake2_128Concat::hash); + + let mut final_key = Vec::with_capacity(storage_prefix.len() + key_hashed.len()); + + final_key.extend_from_slice(&storage_prefix); + final_key.extend_from_slice(key_hashed.as_ref()); + + StorageKey(final_key) +} + /// 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` diff --git a/crates/sp-domains/src/storage.rs b/crates/sp-domains/src/storage.rs index 1465c210d6..3d6f02b0ee 100644 --- a/crates/sp-domains/src/storage.rs +++ b/crates/sp-domains/src/storage.rs @@ -57,6 +57,12 @@ impl RawGenesis { .insert(evm_chain_id_storage_key(), StorageData(chain_id.encode())); } + pub fn set_top_storages(&mut self, storages: Vec<(StorageKey, StorageData)>) { + for (k, v) in storages { + let _ = self.top.insert(k, v); + } + } + fn set_runtime_code(&mut self, code: Vec) { let _ = self.top.insert( StorageKey(well_known_keys::CODE.to_vec()), diff --git a/crates/subspace-malicious-operator/src/chain_spec.rs b/crates/subspace-malicious-operator/src/chain_spec.rs index 45ff978c72..58822de0e4 100644 --- a/crates/subspace-malicious-operator/src/chain_spec.rs +++ b/crates/subspace-malicious-operator/src/chain_spec.rs @@ -1,3 +1,4 @@ +use domain_runtime_primitives::{AccountId20Converter, MultiAccountId}; use evm_domain_runtime::{AccountId as AccountId20, EVMChainIdConfig, EVMConfig, Precompiles}; use hex_literal::hex; use parity_scale_codec::Encode; @@ -7,7 +8,7 @@ use sp_core::crypto::AccountId32; use sp_core::{sr25519, Pair, Public}; use sp_domains::storage::RawGenesis; use sp_domains::{OperatorAllowList, OperatorPublicKey, RuntimeType}; -use sp_runtime::traits::IdentifyAccount; +use sp_runtime::traits::{Convert, IdentifyAccount}; use sp_runtime::{BuildStorage, MultiSigner, Percent}; use std::marker::PhantomData; use std::num::NonZeroU32; @@ -17,8 +18,8 @@ use subspace_runtime::{ }; use subspace_runtime_primitives::{AccountId, Balance, BlockNumber, SSC}; -pub fn domain_dev_config() -> GenericChainSpec { - let endowed_accounts = [ +fn endowed_accounts() -> Vec<(MultiAccountId, Balance)> { + [ // Alith key AccountId20::from(hex!("f24FF3a9CF04c71Dbc94D0b566f7A27B94566cac")), // Baltathar key @@ -27,8 +28,15 @@ pub fn domain_dev_config() -> GenericChainSpec GenericChainSpec { + // Alith is sudo account + let sudo_account = AccountId20::from(hex!("f24FF3a9CF04c71Dbc94D0b566f7A27B94566cac")); // TODO: Migrate once https://github.com/paritytech/polkadot-sdk/issues/2963 is un-broken #[allow(deprecated)] @@ -50,14 +58,7 @@ pub fn domain_dev_config() -> GenericChainSpec, operator_signing_key: OperatorPublicKey, raw_genesis_storage: Vec, + initial_balances: Vec<(MultiAccountId, Balance)>, } pub fn dev_config() -> Result, String> { @@ -198,6 +200,7 @@ pub fn dev_config() -> Result("Alice"), raw_genesis_storage: raw_genesis_storage.clone(), + initial_balances: endowed_accounts(), }, ) }, @@ -277,6 +280,7 @@ fn subspace_genesis_config( signing_key: genesis_domain_params.operator_signing_key, nomination_tax: Percent::from_percent(5), minimum_nominator_stake: 100 * SSC, + initial_balances: genesis_domain_params.initial_balances, }), }, } diff --git a/crates/subspace-node/src/chain_spec.rs b/crates/subspace-node/src/chain_spec.rs index d33d8186ff..676c3af5e9 100644 --- a/crates/subspace-node/src/chain_spec.rs +++ b/crates/subspace-node/src/chain_spec.rs @@ -471,6 +471,7 @@ fn subspace_genesis_config( signing_key: genesis_domain_params.operator_signing_key, nomination_tax: Percent::from_percent(5), minimum_nominator_stake: 100 * SSC, + initial_balances: evm_chain_spec::get_testnet_endowed_accounts_by_spec_id(spec_id), }), }, } diff --git a/crates/subspace-node/src/domain/evm_chain_spec.rs b/crates/subspace-node/src/domain/evm_chain_spec.rs index cf79caeb21..62b7723776 100644 --- a/crates/subspace-node/src/domain/evm_chain_spec.rs +++ b/crates/subspace-node/src/domain/evm_chain_spec.rs @@ -17,6 +17,7 @@ //! EVM domain configurations. use crate::chain_spec_utils::chain_spec_properties; +use domain_runtime_primitives::{AccountId20Converter, MultiAccountId}; use evm_domain_runtime::{ AccountId, BalancesConfig, EVMChainIdConfig, EVMConfig, Precompiles, RuntimeGenesisConfig, SudoConfig, SystemConfig, WASM_BINARY, @@ -24,8 +25,9 @@ use evm_domain_runtime::{ use hex_literal::hex; use sc_chain_spec::GenericChainSpec; use sc_service::ChainType; +use sp_runtime::traits::Convert; use std::str::FromStr; -use subspace_runtime_primitives::SSC; +use subspace_runtime_primitives::{Balance, SSC}; /// Development keys that will be injected automatically on polkadotjs apps fn get_dev_accounts() -> Vec { @@ -142,7 +144,6 @@ pub fn get_testnet_genesis_by_spec_id(spec_id: SpecId) -> RuntimeGenesisConfig { SpecId::Dev => { let accounts = get_dev_accounts(); testnet_genesis( - accounts.clone(), // Alith is Sudo Some(accounts[0]), ) @@ -150,32 +151,27 @@ pub fn get_testnet_genesis_by_spec_id(spec_id: SpecId) -> RuntimeGenesisConfig { SpecId::Gemini => { let sudo_account = AccountId::from_str("f31e60022e290708c17d6997c34de6a30d09438f") .expect("Invalid Sudo account"); - testnet_genesis( - vec![ - // Sudo account - sudo_account, - ], - Some(sudo_account), - ) + testnet_genesis(Some(sudo_account)) } SpecId::DevNet => { let sudo_account = AccountId::from_str("b66a91845249464309fad766fd0ece8144547736") .expect("Invalid Sudo account"); - testnet_genesis( - vec![ - // Sudo account - sudo_account, - ], - Some(sudo_account), - ) + testnet_genesis(Some(sudo_account)) } } } -fn testnet_genesis( - endowed_accounts: Vec, - maybe_sudo_account: Option, -) -> RuntimeGenesisConfig { +pub fn get_testnet_endowed_accounts_by_spec_id(spec_id: SpecId) -> Vec<(MultiAccountId, Balance)> { + match spec_id { + SpecId::Dev => get_dev_accounts() + .into_iter() + .map(|acc| (AccountId20Converter::convert(acc), 1_000_000 * SSC)) + .collect(), + SpecId::DevNet | SpecId::Gemini => vec![], + } +} + +fn testnet_genesis(maybe_sudo_account: Option) -> RuntimeGenesisConfig { // 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. @@ -187,14 +183,7 @@ fn testnet_genesis( sudo: SudoConfig { key: maybe_sudo_account, }, - balances: BalancesConfig { - // TODO: remove `endowed_accounts` once XDM is ready - balances: endowed_accounts - .iter() - .cloned() - .map(|k| (k, 1_000_000 * SSC)) - .collect(), - }, + balances: BalancesConfig::default(), // this is set to default and chain_id will be set into genesis during the domain // instantiation on Consensus runtime. evm_chain_id: EVMChainIdConfig::default(), diff --git a/crates/subspace-runtime/src/lib.rs b/crates/subspace-runtime/src/lib.rs index 553ace5c15..ed1d50d35d 100644 --- a/crates/subspace-runtime/src/lib.rs +++ b/crates/subspace-runtime/src/lib.rs @@ -36,7 +36,7 @@ use core::mem; use core::num::NonZeroU64; use domain_runtime_primitives::opaque::Header as DomainHeader; use domain_runtime_primitives::{ - BlockNumber as DomainNumber, Hash as DomainHash, MultiAccountId, TryConvertBack, + AccountIdConverter, BlockNumber as DomainNumber, Hash as DomainHash, }; use frame_support::inherent::ProvideInherent; use frame_support::traits::{ @@ -70,8 +70,7 @@ use sp_messenger::messages::{ ExtractedStateRootsFromProof, MessageId, }; use sp_runtime::traits::{ - AccountIdConversion, AccountIdLookup, BlakeTwo256, Block as BlockT, Convert, Keccak256, - NumberFor, + AccountIdConversion, AccountIdLookup, BlakeTwo256, Block as BlockT, Keccak256, NumberFor, }; use sp_runtime::transaction_validity::{TransactionSource, TransactionValidity}; use sp_runtime::{create_runtime_str, generic, AccountId32, ApplyExtrinsicResult, Perbill}; @@ -524,23 +523,6 @@ parameter_types! { pub const TransporterEndpointId: EndpointId = 1; } -pub struct AccountIdConverter; - -impl Convert for AccountIdConverter { - fn convert(account_id: AccountId) -> MultiAccountId { - MultiAccountId::AccountId32(account_id.into()) - } -} - -impl TryConvertBack for AccountIdConverter { - fn try_convert_back(multi_account_id: MultiAccountId) -> Option { - match multi_account_id { - MultiAccountId::AccountId32(acc) => Some(AccountId::from(acc)), - _ => None, - } - } -} - impl pallet_transporter::Config for Runtime { type RuntimeEvent = RuntimeEvent; type SelfChainId = SelfChainId; @@ -594,6 +576,7 @@ const_assert!(MinOperatorStake::get() >= MinNominatorStake::get()); const_assert!(StakeWithdrawalLockingPeriod::get() >= BlockTreePruningDepth::get()); pub struct BlockSlot; + impl pallet_domains::BlockSlot for BlockSlot { fn current_slot() -> sp_consensus_slots::Slot { Subspace::current_slot() diff --git a/domains/primitives/runtime/Cargo.toml b/domains/primitives/runtime/Cargo.toml index b5aca12904..ec815e8816 100644 --- a/domains/primitives/runtime/Cargo.toml +++ b/domains/primitives/runtime/Cargo.toml @@ -12,10 +12,12 @@ description = "Common primitives of subspace domain runtime" targets = ["x86_64-unknown-linux-gnu"] [dependencies] +fp-account = { version = "1.0.0-dev", default-features = false, git = "https://github.com/subspace/frontier", rev = "7627e61d80275a4cf24d06f27491f6c31eadb7b7" } frame-support = { version = "4.0.0-dev", default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "d6b500960579d73c43fc4ef550b703acfa61c4c8" } frame-system = { version = "4.0.0-dev", default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "d6b500960579d73c43fc4ef550b703acfa61c4c8" } parity-scale-codec = { version = "3.6.9", default-features = false, features = ["derive"] } scale-info = { version = "2.7.0", default-features = false, features = ["derive"] } +serde = { version = "1.0.195", default-features = false, features = ["alloc", "derive"] } sp-api = { version = "4.0.0-dev", default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "d6b500960579d73c43fc4ef550b703acfa61c4c8" } sp-core = { version = "21.0.0", default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "d6b500960579d73c43fc4ef550b703acfa61c4c8" } sp-runtime = { version = "24.0.0", default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "d6b500960579d73c43fc4ef550b703acfa61c4c8" } @@ -27,10 +29,12 @@ sp-weights = { version = "20.0.0", default-features = false, git = "https://gith [features] default = ["std"] std = [ + "fp-account/std", "frame-support/std", "frame-system/std", "parity-scale-codec/std", "scale-info/std", + "serde/std", "sp-api/std", "sp-core/std", "sp-runtime/std", diff --git a/domains/primitives/runtime/src/lib.rs b/domains/primitives/runtime/src/lib.rs index 19b9d90cba..67fc7359c3 100644 --- a/domains/primitives/runtime/src/lib.rs +++ b/domains/primitives/runtime/src/lib.rs @@ -20,11 +20,13 @@ extern crate alloc; use alloc::string::String; +pub use fp_account::AccountId20; use frame_support::dispatch::{DispatchClass, PerDispatchClass}; use frame_support::weights::constants::{BlockExecutionWeight, ExtrinsicBaseWeight}; use frame_system::limits::{BlockLength, BlockWeights}; use parity_scale_codec::{Decode, Encode}; use scale_info::TypeInfo; +use serde::{Deserialize, Serialize}; use sp_runtime::generic::{Era, UncheckedExtrinsic}; use sp_runtime::traits::{Block as BlockT, Convert, IdentifyAccount, NumberFor, Verify}; use sp_runtime::transaction_validity::TransactionValidityError; @@ -131,7 +133,7 @@ where } /// MultiAccountId used by all the domains to describe their account type. -#[derive(Debug, Encode, Decode, Clone, Eq, PartialEq, TypeInfo)] +#[derive(Debug, Encode, Decode, Clone, Eq, PartialEq, TypeInfo, Serialize, Deserialize)] pub enum MultiAccountId { /// 32 byte account Id. AccountId32([u8; 32]), @@ -165,6 +167,24 @@ impl TryConvertBack for AccountIdConverter { } } +/// An AccountId20 to MultiAccount converter. +pub struct AccountId20Converter; + +impl Convert for AccountId20Converter { + fn convert(account_id: AccountId20) -> MultiAccountId { + MultiAccountId::AccountId20(account_id.into()) + } +} + +impl TryConvertBack for AccountId20Converter { + fn try_convert_back(multi_account_id: MultiAccountId) -> Option { + match multi_account_id { + MultiAccountId::AccountId20(acc) => Some(AccountId20::from(acc)), + _ => None, + } + } +} + #[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone)] pub struct CheckExtrinsicsValidityError { pub extrinsic_index: u32, diff --git a/domains/runtime/evm/src/lib.rs b/domains/runtime/evm/src/lib.rs index 8ba5754f2a..4bab244399 100644 --- a/domains/runtime/evm/src/lib.rs +++ b/domains/runtime/evm/src/lib.rs @@ -17,8 +17,7 @@ pub use domain_runtime_primitives::{ EXISTENTIAL_DEPOSIT, MAXIMUM_BLOCK_WEIGHT, }; use domain_runtime_primitives::{ - CheckExtrinsicsValidityError, DecodeExtrinsicError, MultiAccountId, TryConvertBack, - SLOT_DURATION, + CheckExtrinsicsValidityError, DecodeExtrinsicError, SLOT_DURATION, }; use fp_account::EthereumSignature; use fp_self_contained::{CheckedSignature, SelfContainedCall}; @@ -50,8 +49,8 @@ use sp_messenger::messages::{ }; use sp_runtime::generic::Era; use sp_runtime::traits::{ - BlakeTwo256, Block as BlockT, Checkable, Convert, DispatchInfoOf, Dispatchable, - IdentifyAccount, IdentityLookup, One, PostDispatchInfoOf, SignedExtension, UniqueSaturatedInto, + BlakeTwo256, Block as BlockT, Checkable, DispatchInfoOf, Dispatchable, IdentifyAccount, + IdentityLookup, One, PostDispatchInfoOf, SignedExtension, UniqueSaturatedInto, ValidateUnsigned, Verify, Zero, }; use sp_runtime::transaction_validity::{ @@ -426,30 +425,13 @@ parameter_types! { pub const TransporterEndpointId: EndpointId = 1; } -pub struct AccountId20Converter; - -impl Convert for AccountId20Converter { - fn convert(account_id: AccountId) -> MultiAccountId { - MultiAccountId::AccountId20(account_id.into()) - } -} - -impl TryConvertBack for AccountId20Converter { - fn try_convert_back(multi_account_id: MultiAccountId) -> Option { - match multi_account_id { - MultiAccountId::AccountId20(acc) => Some(AccountId::from(acc)), - _ => None, - } - } -} - impl pallet_transporter::Config for Runtime { type RuntimeEvent = RuntimeEvent; type SelfChainId = SelfChainId; type SelfEndpointId = TransporterEndpointId; type Currency = Balances; type Sender = Messenger; - type AccountIdConverter = AccountId20Converter; + type AccountIdConverter = domain_runtime_primitives::AccountId20Converter; type WeightInfo = pallet_transporter::weights::SubstrateWeight; } diff --git a/domains/test/runtime/evm/src/lib.rs b/domains/test/runtime/evm/src/lib.rs index e4630f2ec9..1850be841e 100644 --- a/domains/test/runtime/evm/src/lib.rs +++ b/domains/test/runtime/evm/src/lib.rs @@ -13,8 +13,7 @@ extern crate alloc; use codec::{Decode, Encode}; pub use domain_runtime_primitives::opaque::Header; use domain_runtime_primitives::{ - block_weights, maximum_block_length, MultiAccountId, TryConvertBack, EXISTENTIAL_DEPOSIT, - MAXIMUM_BLOCK_WEIGHT, SLOT_DURATION, + block_weights, maximum_block_length, EXISTENTIAL_DEPOSIT, MAXIMUM_BLOCK_WEIGHT, SLOT_DURATION, }; pub use domain_runtime_primitives::{ opaque, Balance, BlockNumber, CheckExtrinsicsValidityError, DecodeExtrinsicError, Hash, Nonce, @@ -49,8 +48,8 @@ use sp_messenger::messages::{ }; use sp_runtime::generic::Era; use sp_runtime::traits::{ - BlakeTwo256, Block as BlockT, Checkable, Convert, DispatchInfoOf, Dispatchable, - IdentifyAccount, IdentityLookup, One, PostDispatchInfoOf, SignedExtension, UniqueSaturatedInto, + BlakeTwo256, Block as BlockT, Checkable, DispatchInfoOf, Dispatchable, IdentifyAccount, + IdentityLookup, One, PostDispatchInfoOf, SignedExtension, UniqueSaturatedInto, ValidateUnsigned, Verify, Zero, }; use sp_runtime::transaction_validity::{ @@ -424,30 +423,13 @@ parameter_types! { pub const TransporterEndpointId: EndpointId = 1; } -pub struct AccountId20Converter; - -impl Convert for AccountId20Converter { - fn convert(account_id: AccountId) -> MultiAccountId { - MultiAccountId::AccountId20(account_id.into()) - } -} - -impl TryConvertBack for AccountId20Converter { - fn try_convert_back(multi_account_id: MultiAccountId) -> Option { - match multi_account_id { - MultiAccountId::AccountId20(acc) => Some(AccountId::from(acc)), - _ => None, - } - } -} - impl pallet_transporter::Config for Runtime { type RuntimeEvent = RuntimeEvent; type SelfChainId = SelfChainId; type SelfEndpointId = TransporterEndpointId; type Currency = Balances; type Sender = Messenger; - type AccountIdConverter = AccountId20Converter; + type AccountIdConverter = domain_runtime_primitives::AccountId20Converter; type WeightInfo = pallet_transporter::weights::SubstrateWeight; } diff --git a/test/subspace-test-client/Cargo.toml b/test/subspace-test-client/Cargo.toml index 25c7f38e8c..fd78ecbca7 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] codec = { package = "parity-scale-codec", version = "3.6.5", features = ["derive"] } +domain-runtime-primitives = { version = "0.1.0", path = "../../domains/primitives/runtime" } 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 = "7627e61d80275a4cf24d06f27491f6c31eadb7b7" } futures = "0.3.29" diff --git a/test/subspace-test-client/src/chain_spec.rs b/test/subspace-test-client/src/chain_spec.rs index 3f200e0d40..638f6f9991 100644 --- a/test/subspace-test-client/src/chain_spec.rs +++ b/test/subspace-test-client/src/chain_spec.rs @@ -2,11 +2,12 @@ use crate::domain_chain_spec::testnet_evm_genesis; use codec::Encode; +use domain_runtime_primitives::AccountId20Converter; use sc_chain_spec::{ChainType, GenericChainSpec}; use sp_core::{sr25519, Pair, Public}; use sp_domains::storage::RawGenesis; use sp_domains::{GenesisDomain, OperatorAllowList, OperatorPublicKey, RuntimeType}; -use sp_runtime::traits::{IdentifyAccount, Verify}; +use sp_runtime::traits::{Convert, IdentifyAccount, Verify}; use sp_runtime::{BuildStorage, Percent}; use std::marker::PhantomData; use std::num::NonZeroU32; @@ -138,6 +139,16 @@ fn create_genesis_config( signing_key: get_from_seed::("Alice"), minimum_nominator_stake: 100 * SSC, nomination_tax: Percent::from_percent(5), + initial_balances: crate::domain_chain_spec::endowed_accounts() + .iter() + .cloned() + .map(|k| { + ( + AccountId20Converter::convert(k), + 2_000_000 * subspace_runtime_primitives::SSC, + ) + }) + .collect(), }), }, } diff --git a/test/subspace-test-client/src/domain_chain_spec.rs b/test/subspace-test-client/src/domain_chain_spec.rs index f95b3f60a8..1fd60b07ea 100644 --- a/test/subspace-test-client/src/domain_chain_spec.rs +++ b/test/subspace-test-client/src/domain_chain_spec.rs @@ -6,7 +6,6 @@ use evm_domain_test_runtime::{ use sp_core::{ecdsa, Pair, Public}; use sp_domains::DomainId; use sp_runtime::traits::{IdentifyAccount, Verify}; -use subspace_runtime_primitives::SSC; type AccountPublic = ::Signer; @@ -23,7 +22,7 @@ where .into_account() } -fn endowed_accounts() -> Vec { +pub(crate) fn endowed_accounts() -> Vec { vec![ get_account_id_from_seed::("Alice"), get_account_id_from_seed::("Bob"), @@ -52,13 +51,7 @@ pub fn testnet_evm_genesis() -> RuntimeGenesisConfig { RuntimeGenesisConfig { system: evm_domain_test_runtime::SystemConfig::default(), - balances: evm_domain_test_runtime::BalancesConfig { - balances: endowed_accounts() - .iter() - .cloned() - .map(|k| (k, 2_000_000 * SSC)) - .collect(), - }, + balances: evm_domain_test_runtime::BalancesConfig::default(), sudo: evm_domain_test_runtime::SudoConfig { key: Some(alice) }, evm_chain_id: evm_domain_test_runtime::EVMChainIdConfig { chain_id: 100, diff --git a/test/subspace-test-runtime/src/lib.rs b/test/subspace-test-runtime/src/lib.rs index bbc1181515..4a262a9b56 100644 --- a/test/subspace-test-runtime/src/lib.rs +++ b/test/subspace-test-runtime/src/lib.rs @@ -28,7 +28,7 @@ use core::mem; use core::num::NonZeroU64; use domain_runtime_primitives::opaque::Header as DomainHeader; use domain_runtime_primitives::{ - BlockNumber as DomainNumber, Hash as DomainHash, MultiAccountId, TryConvertBack, + AccountIdConverter, BlockNumber as DomainNumber, Hash as DomainHash, }; use frame_support::inherent::ProvideInherent; use frame_support::traits::{ @@ -64,8 +64,8 @@ use sp_messenger::messages::{ ExtractedStateRootsFromProof, MessageId, }; use sp_runtime::traits::{ - AccountIdConversion, AccountIdLookup, BlakeTwo256, Block as BlockT, ConstBool, Convert, - DispatchInfoOf, Keccak256, NumberFor, PostDispatchInfoOf, Zero, + AccountIdConversion, AccountIdLookup, BlakeTwo256, Block as BlockT, ConstBool, DispatchInfoOf, + Keccak256, NumberFor, PostDispatchInfoOf, Zero, }; use sp_runtime::transaction_validity::{ InvalidTransaction, TransactionSource, TransactionValidity, TransactionValidityError, @@ -569,23 +569,6 @@ parameter_types! { pub const TransporterEndpointId: EndpointId = 1; } -pub struct AccountIdConverter; - -impl Convert for AccountIdConverter { - fn convert(account_id: AccountId) -> MultiAccountId { - MultiAccountId::AccountId32(account_id.into()) - } -} - -impl TryConvertBack for AccountIdConverter { - fn try_convert_back(multi_account_id: MultiAccountId) -> Option { - match multi_account_id { - MultiAccountId::AccountId32(acc) => Some(AccountId::from(acc)), - _ => None, - } - } -} - impl pallet_transporter::Config for Runtime { type RuntimeEvent = RuntimeEvent; type SelfChainId = SelfChainId; From 927349ee927fea5d3148386937fcab9d9cd9bede Mon Sep 17 00:00:00 2001 From: vedhavyas Date: Mon, 12 Feb 2024 15:42:09 +0530 Subject: [PATCH 02/13] define DomainsTransferTracking trait and an implement it for transporter Pallet --- Cargo.lock | 1 + crates/sp-domains/src/lib.rs | 14 ++++++ domains/pallets/messenger/Cargo.toml | 40 ++++++++--------- domains/pallets/transporter/Cargo.toml | 40 ++++++++--------- domains/pallets/transporter/src/lib.rs | 59 +++++++++++++++++++++++++- 5 files changed, 114 insertions(+), 40 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bf2522faa7..fb3c78adf2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7581,6 +7581,7 @@ dependencies = [ "parity-scale-codec", "scale-info", "sp-core", + "sp-domains", "sp-io", "sp-messenger", "sp-runtime", diff --git a/crates/sp-domains/src/lib.rs b/crates/sp-domains/src/lib.rs index 49e47967e4..ada5f5769e 100644 --- a/crates/sp-domains/src/lib.rs +++ b/crates/sp-domains/src/lib.rs @@ -1059,6 +1059,20 @@ impl ExtrinsicDigest { } } +/// Trait that tracks the balances on Domains. +pub trait DomainsTransfersTracker { + type Error; + + /// Marks transfer into domain. + fn transfer_in(domain_id: DomainId, amount: Balance) -> Result<(), Self::Error>; + + /// Marks a transfer from domain. + fn transfer_out(domain_id: DomainId, amount: Balance) -> Result<(), Self::Error>; + + /// Returns the total balance on domain. + fn balance_on_domain(domain_id: DomainId) -> Result; +} + pub type ExecutionReceiptFor = ExecutionReceipt< NumberFor, ::Hash, diff --git a/domains/pallets/messenger/Cargo.toml b/domains/pallets/messenger/Cargo.toml index e12493a5b4..6235351bf5 100644 --- a/domains/pallets/messenger/Cargo.toml +++ b/domains/pallets/messenger/Cargo.toml @@ -8,9 +8,9 @@ homepage = "https://subspace.network" repository = "https://github.com/subspace/subspace" description = "Subspace node pallet for cross domain and cross chain messaging" include = [ - "/src", - "/Cargo.toml", - "/README.md", + "/src", + "/Cargo.toml", + "/README.md", ] [dependencies] @@ -36,24 +36,24 @@ sp-state-machine = { version = "0.28.0", git = "https://github.com/subspace/polk [features] default = ["std"] std = [ - "codec/std", - "frame-benchmarking?/std", - "frame-support/std", - "frame-system/std", - "log/std", - "scale-info/std", - "sp-core/std", - "sp-domains/std", - "sp-messenger/std", - "sp-runtime/std", - "sp-std/std", - "sp-trie/std", + "codec/std", + "frame-benchmarking?/std", + "frame-support/std", + "frame-system/std", + "log/std", + "scale-info/std", + "sp-core/std", + "sp-domains/std", + "sp-messenger/std", + "sp-runtime/std", + "sp-std/std", + "sp-trie/std", ] try-runtime = ["frame-support/try-runtime"] runtime-benchmarks = [ - "frame-benchmarking", - "frame-benchmarking/runtime-benchmarks", - "frame-support/runtime-benchmarks", - "frame-system/runtime-benchmarks", - "sp-messenger/runtime-benchmarks", + "frame-benchmarking", + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "sp-messenger/runtime-benchmarks", ] diff --git a/domains/pallets/transporter/Cargo.toml b/domains/pallets/transporter/Cargo.toml index 88636b6bae..034ce1a800 100644 --- a/domains/pallets/transporter/Cargo.toml +++ b/domains/pallets/transporter/Cargo.toml @@ -8,19 +8,20 @@ homepage = "https://subspace.network" repository = "https://github.com/subspace/subspace" description = "Subspace node pallet to move funds between domains." include = [ - "/src", - "/Cargo.toml", - "/README.md", + "/src", + "/Cargo.toml", + "/README.md", ] [dependencies] codec = { package = "parity-scale-codec", version = "3.6.5", default-features = false, features = ["derive"] } -domain-runtime-primitives = { path = "../../primitives/runtime" , default-features = false } +domain-runtime-primitives = { path = "../../primitives/runtime", default-features = false } frame-benchmarking = { version = "4.0.0-dev", default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "d6b500960579d73c43fc4ef550b703acfa61c4c8", optional = true } frame-support = { version = "4.0.0-dev", default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "d6b500960579d73c43fc4ef550b703acfa61c4c8" } frame-system = { version = "4.0.0-dev", default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "d6b500960579d73c43fc4ef550b703acfa61c4c8" } scale-info = { version = "2.7.0", default-features = false, features = ["derive"] } sp-core = { version = "21.0.0", default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "d6b500960579d73c43fc4ef550b703acfa61c4c8" } +sp-domains = { version = "0.1.0", default-features = false, path = "../../../crates/sp-domains" } sp-messenger = { version = "0.1.0", default-features = false, path = "../../primitives/messenger" } sp-runtime = { version = "24.0.0", default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "d6b500960579d73c43fc4ef550b703acfa61c4c8" } sp-std = { version = "8.0.0", default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "d6b500960579d73c43fc4ef550b703acfa61c4c8" } @@ -32,22 +33,23 @@ sp-io = { version = "23.0.0", git = "https://github.com/subspace/polkadot-sdk", [features] default = ["std"] std = [ - "codec/std", - "domain-runtime-primitives/std", - "frame-benchmarking?/std", - "frame-support/std", - "frame-system/std", - "scale-info/std", - "sp-core/std", - "sp-messenger/std", - "sp-runtime/std", - "sp-std/std", + "codec/std", + "domain-runtime-primitives/std", + "frame-benchmarking?/std", + "frame-support/std", + "frame-system/std", + "scale-info/std", + "sp-core/std", + "sp-domains/std", + "sp-messenger/std", + "sp-runtime/std", + "sp-std/std", ] try-runtime = ["frame-support/try-runtime"] runtime-benchmarks = [ - "frame-benchmarking", - "frame-benchmarking/runtime-benchmarks", - "frame-support/runtime-benchmarks", - "frame-system/runtime-benchmarks", - "sp-messenger/runtime-benchmarks", + "frame-benchmarking", + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "sp-messenger/runtime-benchmarks", ] diff --git a/domains/pallets/transporter/src/lib.rs b/domains/pallets/transporter/src/lib.rs index da4b2836e6..db1df15e5b 100644 --- a/domains/pallets/transporter/src/lib.rs +++ b/domains/pallets/transporter/src/lib.rs @@ -21,10 +21,13 @@ use codec::{Decode, Encode}; use domain_runtime_primitives::{MultiAccountId, TryConvertBack}; +use frame_support::ensure; use frame_support::traits::Currency; pub use pallet::*; use scale_info::TypeInfo; +use sp_domains::DomainId; use sp_messenger::messages::ChainId; +use sp_runtime::traits::{CheckedAdd, CheckedSub, Get}; #[cfg(test)] mod mock; @@ -73,6 +76,7 @@ mod pallet { use frame_support::traits::{Currency, ExistenceRequirement, WithdrawReasons}; use frame_support::weights::Weight; use frame_system::pallet_prelude::*; + use sp_domains::DomainId; use sp_messenger::endpoint::{ Endpoint, EndpointHandler as EndpointHandlerT, EndpointId, EndpointRequest, EndpointResponse, Sender, @@ -89,7 +93,7 @@ mod pallet { /// Gets the chain_id of the current execution environment. type SelfChainId: Get; - /// Gets the endpoint_id of the this pallet in a given execution environment. + /// Gets the endpoint_id of this pallet in a given execution environment. type SelfEndpointId: Get; /// Currency used by this pallet. @@ -123,6 +127,12 @@ mod pallet { OptionQuery, >; + /// Domain balances. + #[pallet::storage] + #[pallet::getter(fn domain_balances)] + pub(super) type DomainBalances = + StorageMap<_, Identity, DomainId, BalanceOf, ValueQuery>; + /// Events emitted by pallet-transporter. #[pallet::event] #[pallet::generate_deposit(pub (super) fn deposit_event)] @@ -177,6 +187,12 @@ mod pallet { UnexpectedMessage, /// Emits when the account id type is invalid. InvalidAccountId, + /// Emits when from_chain do not have enough funds to finalize the transfer. + LowBalanceOnDomain, + /// Emits when the transfer tracking was called from non-consensus chain + NonConsensusChain, + /// Emits when balance overflow + BalanceOverflow, } #[pallet::call] @@ -335,3 +351,44 @@ mod pallet { } } } + +impl sp_domains::DomainsTransfersTracker> for Pallet { + type Error = Error; + + fn balance_on_domain(domain_id: DomainId) -> Result, Self::Error> { + ensure!( + T::SelfChainId::get().is_consensus_chain(), + Error::NonConsensusChain + ); + + Ok(DomainBalances::::get(domain_id)) + } + + fn transfer_in(domain_id: DomainId, amount: BalanceOf) -> Result<(), Self::Error> { + ensure!( + T::SelfChainId::get().is_consensus_chain(), + Error::NonConsensusChain + ); + + DomainBalances::::try_mutate(domain_id, |current_balance| { + *current_balance = current_balance + .checked_add(&amount) + .ok_or(Error::BalanceOverflow)?; + Ok(()) + }) + } + + fn transfer_out(domain_id: DomainId, amount: BalanceOf) -> Result<(), Self::Error> { + ensure!( + T::SelfChainId::get().is_consensus_chain(), + Error::NonConsensusChain + ); + + DomainBalances::::try_mutate(domain_id, |current_balance| { + *current_balance = current_balance + .checked_sub(&amount) + .ok_or(Error::LowBalanceOnDomain)?; + Ok(()) + }) + } +} From 98cfe580af72c851c35c058f34f1f2ec5d30d768 Mon Sep 17 00:00:00 2001 From: vedhavyas Date: Mon, 12 Feb 2024 16:21:10 +0530 Subject: [PATCH 03/13] track transfers in/out for each domain at the time of instantiation and ER confirmation --- crates/pallet-domains/src/block_tree.rs | 32 +++++++++-- crates/pallet-domains/src/domain_registry.rs | 53 +++++++++++++++++-- crates/pallet-domains/src/lib.rs | 10 +++- crates/pallet-domains/src/runtime_registry.rs | 24 ++++----- crates/pallet-domains/src/tests.rs | 25 +++++++++ crates/sp-domains/src/lib.rs | 10 +++- crates/subspace-runtime/src/lib.rs | 1 + domains/pallets/transporter/src/lib.rs | 46 +++++++++++++++- domains/primitives/runtime/src/lib.rs | 16 +++++- test/subspace-test-runtime/src/lib.rs | 2 + 10 files changed, 188 insertions(+), 31 deletions(-) diff --git a/crates/pallet-domains/src/block_tree.rs b/crates/pallet-domains/src/block_tree.rs index c87f335e8c..862d3dc874 100644 --- a/crates/pallet-domains/src/block_tree.rs +++ b/crates/pallet-domains/src/block_tree.rs @@ -10,8 +10,10 @@ use frame_support::{ensure, PalletError}; use scale_info::TypeInfo; use sp_core::Get; use sp_domains::merkle_tree::MerkleTree; -use sp_domains::{ConfirmedDomainBlock, DomainId, ExecutionReceipt, OperatorId}; -use sp_runtime::traits::{BlockNumberProvider, CheckedSub, One, Saturating, Zero}; +use sp_domains::{ + ConfirmedDomainBlock, DomainId, DomainsTransfersTracker, ExecutionReceipt, OperatorId, +}; +use sp_runtime::traits::{BlockNumberProvider, CheckedAdd, CheckedSub, One, Saturating, Zero}; use sp_std::cmp::Ordering; use sp_std::collections::btree_map::BTreeMap; use sp_std::vec::Vec; @@ -34,6 +36,8 @@ pub enum Error { InvalidExecutionTrace, UnavailableConsensusBlockHash, InvalidStateRoot, + BalanceOverflow, + DomainTransfersTracking, } #[derive(TypeInfo, Debug, Encode, Decode, Clone, PartialEq, Eq)] @@ -344,6 +348,26 @@ pub(crate) fn process_execution_receipt( execution_receipt.consensus_block_number, ); + // tracking the transfer + // 1. Block fees are burned on domain, so it is considered transferred out + // 2. XDM transfers from the Domain + // 3. XDM transfers into the domain + let transfer_out_balance = execution_receipt + .block_fees + .total_fees() + .and_then(|total| total.checked_add(&execution_receipt.transfers.transfers_out)) + .ok_or(Error::BalanceOverflow)?; + + // track the transfers out and then track transfers in + T::DomainsTransfersTracker::transfer_out(domain_id, transfer_out_balance) + .map_err(|_| Error::DomainTransfersTracking)?; + + T::DomainsTransfersTracker::transfer_in( + domain_id, + execution_receipt.transfers.transfers_in, + ) + .map_err(|_| Error::DomainTransfersTracking)?; + LatestConfirmedDomainBlock::::insert( domain_id, ConfirmedDomainBlock { @@ -687,7 +711,7 @@ mod tests { H256::random(), stale_receipt, ); - assert!(crate::Pallet::::submit_bundle(RawOrigin::None.into(), bundle,).is_err()); + assert!(crate::Pallet::::submit_bundle(RawOrigin::None.into(), bundle).is_err()); assert_eq!( BlockTreeNodes::::get(stale_receipt_hash) @@ -735,7 +759,7 @@ mod tests { H256::random(), previous_head_receipt, ); - assert!(crate::Pallet::::submit_bundle(RawOrigin::None.into(), bundle,).is_err()); + assert!(crate::Pallet::::submit_bundle(RawOrigin::None.into(), bundle).is_err()); }); } diff --git a/crates/pallet-domains/src/domain_registry.rs b/crates/pallet-domains/src/domain_registry.rs index 9af5d08058..f269efea9b 100644 --- a/crates/pallet-domains/src/domain_registry.rs +++ b/crates/pallet-domains/src/domain_registry.rs @@ -11,16 +11,16 @@ use crate::{ use alloc::string::String; use codec::{Decode, Encode}; use domain_runtime_primitives::MultiAccountId; -use frame_support::traits::fungible::{Inspect, MutateHold}; -use frame_support::traits::tokens::{Fortitude, Preservation}; +use frame_support::traits::fungible::{Inspect, Mutate, MutateHold}; +use frame_support::traits::tokens::{Fortitude, Precision, Preservation}; use frame_support::weights::Weight; use frame_support::{ensure, PalletError}; use frame_system::pallet_prelude::*; use scale_info::TypeInfo; use sp_core::Get; use sp_domains::{ - derive_domain_block_hash, DomainId, DomainsDigestItem, OperatorAllowList, RuntimeId, - RuntimeType, + derive_domain_block_hash, DomainId, DomainsDigestItem, DomainsTransfersTracker, + OperatorAllowList, RuntimeId, RuntimeType, }; use sp_runtime::traits::{CheckedAdd, Zero}; use sp_runtime::DigestItem; @@ -44,6 +44,8 @@ pub enum Error { FailedToGenerateGenesisStateRoot, DomainNotFound, NotDomainOwner, + InitialBalanceOverflow, + TransfersTracker, FailedToGenerateRawGenesis(crate::runtime_registry::Error), } @@ -68,6 +70,20 @@ pub struct DomainConfig { pub initial_balances: Vec<(MultiAccountId, Balance)>, } +impl DomainConfig +where + AccountId: Ord, + Balance: Zero + CheckedAdd, +{ + pub(crate) fn total_issuance(&self) -> Option { + self.initial_balances + .iter() + .try_fold(Balance::zero(), |total, (_, balance)| { + total.checked_add(balance) + }) + } +} + #[derive(TypeInfo, Debug, Encode, Decode, Clone, PartialEq, Eq)] pub struct DomainObject { /// The address of the domain creator, used to validate updating the domain config. @@ -146,13 +162,29 @@ pub(crate) fn do_instantiate_domain( } }; - // TODO: burn the total initial balance for domain from the domain owner account + // burn total issuance on domain from owners account and track the domain balance + let total_issuance = domain_config + .total_issuance() + .ok_or(Error::InitialBalanceOverflow)?; + + T::Currency::burn_from( + &owner_account_id, + total_issuance, + Precision::Exact, + Fortitude::Polite, + ) + .map_err(|_| Error::InsufficientFund)?; + + T::DomainsTransfersTracker::transfer_in(domain_id, total_issuance) + .map_err(|_| Error::TransfersTracker)?; + let genesis_receipt = { let state_version = runtime_obj.version.state_version(); let raw_genesis = runtime_obj .into_complete_raw_genesis::( domain_id, domain_runtime_info, + total_issuance, domain_config.initial_balances.clone(), ) .map_err(Error::FailedToGenerateRawGenesis)?; @@ -443,6 +475,8 @@ mod tests { Balances::make_free_balance_be( &creator, ::DomainInstantiationDeposit::get() + // for domain total issuance + + 1_000_000 * SSC + ::ExistentialDeposit::get(), ); @@ -461,6 +495,15 @@ mod tests { 1_000_000 * SSC, )]; + // Set enough fund to creator + Balances::make_free_balance_be( + &creator, + ::DomainInstantiationDeposit::get() + // for domain total issuance + + 1_000_000 * SSC + + ::ExistentialDeposit::get(), + ); + // should be successful let domain_id = do_instantiate_domain::(domain_config.clone(), creator, created_at).unwrap(); diff --git a/crates/pallet-domains/src/lib.rs b/crates/pallet-domains/src/lib.rs index fd6f0cd7dc..13c0bfcd70 100644 --- a/crates/pallet-domains/src/lib.rs +++ b/crates/pallet-domains/src/lib.rs @@ -166,8 +166,8 @@ mod pallet { use sp_core::H256; use sp_domains::bundle_producer_election::ProofOfElectionError; use sp_domains::{ - BundleDigest, ConfirmedDomainBlock, DomainId, EpochIndex, GenesisDomain, OperatorAllowList, - OperatorId, OperatorPublicKey, RuntimeId, RuntimeType, + BundleDigest, ConfirmedDomainBlock, DomainId, DomainsTransfersTracker, EpochIndex, + GenesisDomain, OperatorAllowList, OperatorId, OperatorPublicKey, RuntimeId, RuntimeType, }; use sp_domains_fraud_proof::fraud_proof::FraudProof; use sp_domains_fraud_proof::InvalidTransactionCode; @@ -321,6 +321,9 @@ mod pallet { /// The block slot type BlockSlot: BlockSlot; + + /// Transfers tracker. + type DomainsTransfersTracker: DomainsTransfersTracker>; } #[pallet::pallet] @@ -343,6 +346,7 @@ mod pallet { /// Starting EVM chain ID for evm runtimes. pub struct StartingEVMChainId; + impl Get for StartingEVMChainId { fn get() -> EVMChainId { // after looking at `https://chainlist.org/?testnets=false` @@ -1515,10 +1519,12 @@ impl Pallet { let domain_obj = DomainRegistry::::get(domain_id)?; let runtime_object = RuntimeRegistry::::get(domain_obj.domain_config.runtime_id)?; let runtime_type = runtime_object.runtime_type.clone(); + let total_issuance = domain_obj.domain_config.total_issuance()?; let raw_genesis = runtime_object .into_complete_raw_genesis::( domain_id, domain_obj.domain_runtime_info, + total_issuance, domain_obj.domain_config.initial_balances, ) .ok()?; diff --git a/crates/pallet-domains/src/runtime_registry.rs b/crates/pallet-domains/src/runtime_registry.rs index 0be55940fb..49dbce5c9a 100644 --- a/crates/pallet-domains/src/runtime_registry.rs +++ b/crates/pallet-domains/src/runtime_registry.rs @@ -31,7 +31,6 @@ pub enum Error { MaxScheduledBlockNumber, FailedToDecodeRawGenesis, RuntimeCodeNotFoundInRawGenesis, - InitialBalanceOverflow, InvalidAccountIdType, } @@ -62,19 +61,11 @@ impl Default for DomainRuntimeInfo { } fn derive_initial_balances_storages( + total_issuance: BalanceOf, balances: BTreeMap>, -) -> Result, Error> { - let mut initial_storages = vec![]; - let total_balance = balances - .iter() - .try_fold(BalanceOf::::zero(), |total, (_, balance)| { - total.checked_add(balance) - }) - .ok_or(Error::InitialBalanceOverflow)?; - +) -> Vec<(StorageKey, StorageData)> { let total_issuance_key = sp_domains::domain_total_issuance_storage_key(); - initial_storages.push((total_issuance_key, StorageData(total_balance.encode()))); - + let mut initial_storages = vec![(total_issuance_key, StorageData(total_issuance.encode()))]; for (account_id, balance) in balances { let account_storage_key = sp_domains::domain_account_storage_key(account_id); let account_info = AccountInfo { @@ -91,7 +82,7 @@ fn derive_initial_balances_storages( initial_storages.push((account_storage_key, StorageData(account_info.encode()))) } - Ok(initial_storages) + initial_storages } impl RuntimeObject { @@ -100,6 +91,7 @@ impl RuntimeObject { self, domain_id: DomainId, domain_runtime_info: DomainRuntimeInfo, + total_issuance: BalanceOf, initial_balances: Vec<(MultiAccountId, BalanceOf)>, ) -> Result { let RuntimeObject { @@ -128,8 +120,10 @@ impl RuntimeObject { }, ) .ok_or(Error::InvalidAccountIdType)?; - raw_genesis - .set_top_storages(derive_initial_balances_storages::(initial_balances)?); + raw_genesis.set_top_storages(derive_initial_balances_storages::( + total_issuance, + initial_balances, + )); } } diff --git a/crates/pallet-domains/src/tests.rs b/crates/pallet-domains/src/tests.rs index f404ad90c7..0a8233b6b4 100644 --- a/crates/pallet-domains/src/tests.rs +++ b/crates/pallet-domains/src/tests.rs @@ -75,6 +75,7 @@ frame_support::construct_runtime!( type BlockNumber = u64; type Hash = H256; type AccountId = u128; + impl frame_system::Config for Test { type BaseCallFilter = frame_support::traits::Everything; type BlockWeights = (); @@ -116,6 +117,7 @@ parameter_types! { } pub struct ConfirmationDepthK; + impl Get for ConfirmationDepthK { fn get() -> BlockNumber { 10 @@ -193,6 +195,7 @@ impl frame_support::traits::Randomness for MockRandomness { } const SLOT_DURATION: u64 = 1000; + impl pallet_timestamp::Config for Test { /// A timestamp: milliseconds since the unix epoch. type Moment = Moment; @@ -202,6 +205,7 @@ impl pallet_timestamp::Config for Test { } pub struct DummyStorageFee; + impl StorageFee for DummyStorageFee { fn transaction_byte_fee() -> Balance { SSC @@ -210,6 +214,7 @@ impl StorageFee for DummyStorageFee { } pub struct DummyBlockSlot; + impl BlockSlot for DummyBlockSlot { fn current_slot() -> sp_consensus_slots::Slot { 0u64.into() @@ -220,6 +225,24 @@ impl BlockSlot for DummyBlockSlot { } } +pub struct MockDomainsTransfersTracker; + +impl sp_domains::DomainsTransfersTracker for MockDomainsTransfersTracker { + type Error = (); + + fn balance_on_domain(_domain_id: DomainId) -> Result { + Ok(Default::default()) + } + + fn transfer_in(_domain_id: DomainId, _amount: Balance) -> Result<(), Self::Error> { + Ok(()) + } + + fn transfer_out(_domain_id: DomainId, _amount: Balance) -> Result<(), Self::Error> { + Ok(()) + } +} + impl pallet_domains::Config for Test { type RuntimeEvent = RuntimeEvent; type DomainHash = sp_core::H256; @@ -250,9 +273,11 @@ impl pallet_domains::Config for Test { type PalletId = DomainsPalletId; type StorageFee = DummyStorageFee; type BlockSlot = DummyBlockSlot; + type DomainsTransfersTracker = MockDomainsTransfersTracker; } pub struct ExtrinsicStorageFees; + impl domain_pallet_executive::ExtrinsicStorageFees for ExtrinsicStorageFees { fn extract_signer(_xt: MockUncheckedExtrinsic) -> (Option, DispatchInfo) { (None, DispatchInfo::default()) diff --git a/crates/sp-domains/src/lib.rs b/crates/sp-domains/src/lib.rs index ada5f5769e..6e3df02a21 100644 --- a/crates/sp-domains/src/lib.rs +++ b/crates/sp-domains/src/lib.rs @@ -455,6 +455,14 @@ pub struct BundleDigest { pub size: u32, } +#[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone, Default)] +pub struct Transfers { + /// Total transfers that came into the domain. + pub transfers_in: Balance, + /// Total transfers that went out of the domain. + pub transfers_out: Balance, +} + /// Receipt of a domain block execution. #[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone)] pub struct ExecutionReceipt { @@ -487,7 +495,7 @@ pub struct ExecutionReceipt { /// storage fees are given to the consensus block author. pub block_fees: BlockFees, /// List of transfers from this Domain to other chains - pub transfers: BTreeMap, + pub transfers: Transfers, } impl diff --git a/crates/subspace-runtime/src/lib.rs b/crates/subspace-runtime/src/lib.rs index ed1d50d35d..98dacbc838 100644 --- a/crates/subspace-runtime/src/lib.rs +++ b/crates/subspace-runtime/src/lib.rs @@ -617,6 +617,7 @@ impl pallet_domains::Config for Runtime { type PalletId = DomainsPalletId; type StorageFee = TransactionFees; type BlockSlot = BlockSlot; + type DomainsTransfersTracker = Transporter; } parameter_types! { diff --git a/domains/pallets/transporter/src/lib.rs b/domains/pallets/transporter/src/lib.rs index db1df15e5b..3c9f2343a8 100644 --- a/domains/pallets/transporter/src/lib.rs +++ b/domains/pallets/transporter/src/lib.rs @@ -76,13 +76,13 @@ mod pallet { use frame_support::traits::{Currency, ExistenceRequirement, WithdrawReasons}; use frame_support::weights::Weight; use frame_system::pallet_prelude::*; - use sp_domains::DomainId; + use sp_domains::{DomainId, Transfers}; use sp_messenger::endpoint::{ Endpoint, EndpointHandler as EndpointHandlerT, EndpointId, EndpointRequest, EndpointResponse, Sender, }; use sp_messenger::messages::ChainId; - use sp_runtime::traits::Convert; + use sp_runtime::traits::{CheckedAdd, Convert}; use sp_std::vec; #[pallet::config] @@ -133,6 +133,13 @@ mod pallet { pub(super) type DomainBalances = StorageMap<_, Identity, DomainId, BalanceOf, ValueQuery>; + /// A temporary storage that tracks total transfers from this chain. + /// Clears on on_initialize for every block. + #[pallet::storage] + #[pallet::getter(fn chain_transfers)] + pub(super) type ChainTransfers = + StorageValue<_, Transfers>, ValueQuery>; + /// Events emitted by pallet-transporter. #[pallet::event] #[pallet::generate_deposit(pub (super) fn deposit_event)] @@ -245,10 +252,27 @@ mod pallet { chain_id: dst_chain_id, message_id, }); + + ChainTransfers::::try_mutate(|transfers| { + transfers.transfers_out = transfers + .transfers_out + .checked_add(&amount) + .ok_or(Error::::BalanceOverflow)?; + Ok::<(), Error>(()) + })?; + Ok(()) } } + #[pallet::hooks] + impl Hooks> for Pallet { + fn on_initialize(_n: BlockNumberFor) -> Weight { + ChainTransfers::::kill(); + T::DbWeight::get().writes(1) + } + } + /// Endpoint handler implementation for pallet transporter. #[derive(Debug)] pub struct EndpointHandler(pub PhantomData); @@ -283,6 +307,15 @@ mod pallet { .ok_or(Error::::InvalidAccountId)?; let _imbalance = T::Currency::deposit_creating(&account_id, req.amount); + + ChainTransfers::::try_mutate(|transfers| { + transfers.transfers_in = transfers + .transfers_in + .checked_add(&req.amount) + .ok_or(Error::::BalanceOverflow)?; + Ok::<(), Error>(()) + })?; + frame_system::Pallet::::deposit_event(Into::<::RuntimeEvent>::into( Event::::IncomingTransferSuccessful { chain_id: src_chain_id, @@ -331,6 +364,15 @@ mod pallet { T::AccountIdConverter::try_convert_back(transfer.sender.account_id) .ok_or(Error::::InvalidAccountId)?; let _imbalance = T::Currency::deposit_creating(&account_id, transfer.amount); + + ChainTransfers::::try_mutate(|transfers| { + transfers.transfers_in = transfers + .transfers_in + .checked_add(&transfer.amount) + .ok_or(Error::::BalanceOverflow)?; + Ok::<(), Error>(()) + })?; + frame_system::Pallet::::deposit_event( Into::<::RuntimeEvent>::into( Event::::OutgoingTransferFailed { diff --git a/domains/primitives/runtime/src/lib.rs b/domains/primitives/runtime/src/lib.rs index 67fc7359c3..11f84fbd92 100644 --- a/domains/primitives/runtime/src/lib.rs +++ b/domains/primitives/runtime/src/lib.rs @@ -28,7 +28,9 @@ use parity_scale_codec::{Decode, Encode}; use scale_info::TypeInfo; use serde::{Deserialize, Serialize}; use sp_runtime::generic::{Era, UncheckedExtrinsic}; -use sp_runtime::traits::{Block as BlockT, Convert, IdentifyAccount, NumberFor, Verify}; +use sp_runtime::traits::{ + Block as BlockT, CheckedAdd, Convert, IdentifyAccount, NumberFor, Verify, +}; use sp_runtime::transaction_validity::TransactionValidityError; use sp_runtime::{Digest, MultiAddress, MultiSignature, Perbill}; use sp_std::vec::Vec; @@ -209,13 +211,22 @@ pub struct BlockFees { pub domain_execution_fee: Balance, } -impl BlockFees { +impl BlockFees +where + Balance: CheckedAdd, +{ pub fn new(domain_execution_fee: Balance, consensus_storage_fee: Balance) -> Self { BlockFees { consensus_storage_fee, domain_execution_fee, } } + + /// Returns the total fees that was collected and burned on the Domain. + pub fn total_fees(&self) -> Option { + self.consensus_storage_fee + .checked_add(&self.domain_execution_fee) + } } /// Opaque types. These are used by the CLI to instantiate machinery that don't need to know @@ -308,6 +319,7 @@ sp_api::decl_runtime_apis! { #[cfg(test)] mod test { use super::block_weights; + #[test] fn test_block_weights() { // validate and build block weights diff --git a/test/subspace-test-runtime/src/lib.rs b/test/subspace-test-runtime/src/lib.rs index 4a262a9b56..aa8bfbd04b 100644 --- a/test/subspace-test-runtime/src/lib.rs +++ b/test/subspace-test-runtime/src/lib.rs @@ -612,6 +612,7 @@ parameter_types! { const_assert!(MinOperatorStake::get() >= MinNominatorStake::get()); pub struct BlockSlot; + impl pallet_domains::BlockSlot for BlockSlot { fn current_slot() -> sp_consensus_slots::Slot { Subspace::current_slot() @@ -652,6 +653,7 @@ impl pallet_domains::Config for Runtime { type PalletId = DomainsPalletId; type StorageFee = TransactionFees; type BlockSlot = BlockSlot; + type DomainsTransfersTracker = Transporter; } parameter_types! { From c1292cc14071cad76e649ab4178c84c0e7598b52 Mon Sep 17 00:00:00 2001 From: vedhavyas Date: Mon, 12 Feb 2024 18:17:41 +0530 Subject: [PATCH 04/13] generate and verify fraud proof for invalid transfers within Execution receipt --- crates/pallet-domains/src/lib.rs | 20 ++- crates/pallet-domains/src/tests.rs | 3 + .../sp-domains-fraud-proof/src/fraud_proof.rs | 19 +++ .../src/host_functions.rs | 23 ++++ crates/sp-domains-fraud-proof/src/lib.rs | 21 ++++ .../src/verification.rs | 61 +++++++++- crates/sp-domains/src/lib.rs | 10 +- .../src/stateless_runtime.rs | 4 + .../src/domain_block_processor.rs | 38 ++++-- .../client/domain-operator/src/fraud_proof.rs | 28 ++++- domains/client/domain-operator/src/tests.rs | 115 +++++++++++++++++- domains/pallets/transporter/src/lib.rs | 13 +- domains/primitives/runtime/src/lib.rs | 26 +++- domains/runtime/evm/src/lib.rs | 13 +- domains/test/runtime/evm/src/lib.rs | 14 ++- test/subspace-test-client/src/chain_spec.rs | 9 +- 16 files changed, 373 insertions(+), 44 deletions(-) diff --git a/crates/pallet-domains/src/lib.rs b/crates/pallet-domains/src/lib.rs index 13c0bfcd70..470e63b488 100644 --- a/crates/pallet-domains/src/lib.rs +++ b/crates/pallet-domains/src/lib.rs @@ -60,7 +60,7 @@ use sp_domains_fraud_proof::verification::{ verify_bundle_equivocation_fraud_proof, verify_invalid_block_fees_fraud_proof, verify_invalid_bundles_fraud_proof, verify_invalid_domain_block_hash_fraud_proof, verify_invalid_domain_extrinsics_root_fraud_proof, verify_invalid_state_transition_fraud_proof, - verify_valid_bundle_fraud_proof, + verify_invalid_transfers_fraud_proof, verify_valid_bundle_fraud_proof, }; use sp_runtime::traits::{Hash, Header, One, Zero}; use sp_runtime::{RuntimeAppPublic, SaturatedConversion, Saturating}; @@ -622,6 +622,8 @@ mod pallet { DescendantsOfFraudulentERNotPruned, /// Invalid fraud proof since block fees are not mismatched. InvalidBlockFeesFraudProof, + /// Invalid fraud proof since transfers are not mismatched. + InvalidTransfersFraudProof, /// Invalid domain block hash fraud proof. InvalidDomainBlockHashFraudProof, /// Invalid domain extrinsic fraud proof @@ -1712,6 +1714,22 @@ impl Pallet { FraudProofError::InvalidBlockFeesFraudProof })?; } + FraudProof::InvalidTransfers(req) => { + verify_invalid_transfers_fraud_proof::< + T::Block, + DomainBlockNumberFor, + T::DomainHash, + BalanceOf, + DomainHashingFor, + >(bad_receipt, req) + .map_err(|err| { + log::error!( + target: "runtime::domains", + "Domain transfers proof verification failed: {err:?}" + ); + FraudProofError::InvalidTransfersFraudProof + })?; + } FraudProof::InvalidDomainBlockHash(InvalidDomainBlockHashProof { digest_storage_proof, .. diff --git a/crates/pallet-domains/src/tests.rs b/crates/pallet-domains/src/tests.rs index 0a8233b6b4..1ce914cc8b 100644 --- a/crates/pallet-domains/src/tests.rs +++ b/crates/pallet-domains/src/tests.rs @@ -394,6 +394,9 @@ impl FraudProofHostFunctions for MockDomainFraudProofExtension { self.maybe_illegal_extrinsic_index, ) } + FraudProofVerificationInfoRequest::StorageKey { .. } => { + FraudProofVerificationInfoResponse::StorageKey(None) + } }; Some(response) diff --git a/crates/sp-domains-fraud-proof/src/fraud_proof.rs b/crates/sp-domains-fraud-proof/src/fraud_proof.rs index f8ca5f0a67..b3a9f82e17 100644 --- a/crates/sp-domains-fraud-proof/src/fraud_proof.rs +++ b/crates/sp-domains-fraud-proof/src/fraud_proof.rs @@ -296,6 +296,11 @@ pub enum VerificationError { RuntimeCode(String), #[cfg_attr(feature = "thiserror", error("Failed to get domain runtime code"))] FailedToGetDomainRuntimeCode, + #[cfg_attr( + feature = "thiserror", + error("Failed to get domain transfers storage key") + )] + FailedToGetDomainTransfersStorageKey, #[cfg(feature = "std")] #[cfg_attr( feature = "thiserror", @@ -447,6 +452,7 @@ pub enum FraudProof { ValidBundle(ValidBundleProof>), InvalidDomainBlockHash(InvalidDomainBlockHashProof>), InvalidBundles(InvalidBundlesFraudProof>), + InvalidTransfers(InvalidTransfersProof>), // Dummy fraud proof only used in test and benchmark // // NOTE: the `Dummy` must be the last variant, because the `#[cfg(..)]` will apply to @@ -474,6 +480,7 @@ impl FraudProof Self::InvalidBundles(proof) => proof.domain_id, Self::ValidBundle(proof) => proof.domain_id, Self::InvalidDomainBlockHash(proof) => proof.domain_id, + Self::InvalidTransfers(proof) => proof.domain_id, } } @@ -492,6 +499,7 @@ impl FraudProof Self::ValidBundle(proof) => Some(proof.bad_receipt_hash), Self::InvalidBundles(proof) => Some(proof.bad_receipt_hash), Self::InvalidDomainBlockHash(proof) => Some(proof.bad_receipt_hash), + Self::InvalidTransfers(proof) => Some(proof.bad_receipt_hash), } } @@ -623,6 +631,17 @@ pub struct InvalidBlockFeesProof { pub storage_proof: StorageProof, } +/// Represents an invalid transfers proof. +#[derive(Clone, Debug, Decode, Encode, Eq, PartialEq, TypeInfo)] +pub struct InvalidTransfersProof { + /// The id of the domain this fraud proof targeted + pub domain_id: DomainId, + /// Hash of the bad receipt this fraud proof targeted + pub bad_receipt_hash: ReceiptHash, + /// Storage witness needed for verifying this proof. + pub storage_proof: StorageProof, +} + /// Represents an invalid domain block hash fraud proof. #[derive(Clone, Debug, Decode, Encode, Eq, PartialEq, TypeInfo)] pub struct InvalidDomainBlockHashProof { diff --git a/crates/sp-domains-fraud-proof/src/host_functions.rs b/crates/sp-domains-fraud-proof/src/host_functions.rs index f010c09b5b..7b95bfe564 100644 --- a/crates/sp-domains-fraud-proof/src/host_functions.rs +++ b/crates/sp-domains-fraud-proof/src/host_functions.rs @@ -1,5 +1,6 @@ use crate::{ FraudProofVerificationInfoRequest, FraudProofVerificationInfoResponse, SetCodeExtrinsic, + StorageKeyRequest, }; use codec::{Decode, Encode}; use domain_block_preprocessor::inherents::extract_domain_runtime_upgrade_code; @@ -297,6 +298,23 @@ where )) } + fn storage_key( + &self, + consensus_block_hash: H256, + domain_id: DomainId, + req: StorageKeyRequest, + ) -> Option> { + let runtime_code = self.get_domain_runtime_code(consensus_block_hash, domain_id)?; + let domain_stateless_runtime = + StatelessRuntime::::new(self.executor.clone(), runtime_code.into()); + Some( + match req { + StorageKeyRequest::Transfers => domain_stateless_runtime.transfers_storage_key(), + } + .expect("Domain Runtime Api should not fail. There is no recovery from this; qed."), + ) + } + fn get_domain_election_params( &self, consensus_block_hash: H256, @@ -475,6 +493,11 @@ where transactions_check_result, ) }), + FraudProofVerificationInfoRequest::StorageKey { domain_id, req } => { + Some(FraudProofVerificationInfoResponse::StorageKey( + self.storage_key(consensus_block_hash, domain_id, req), + )) + } } } diff --git a/crates/sp-domains-fraud-proof/src/lib.rs b/crates/sp-domains-fraud-proof/src/lib.rs index f589d1258e..411cd7a72a 100644 --- a/crates/sp-domains-fraud-proof/src/lib.rs +++ b/crates/sp-domains-fraud-proof/src/lib.rs @@ -80,6 +80,13 @@ impl From for TransactionValidity { } } +/// Type that specifies the request of storage keys +#[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone)] +pub enum StorageKeyRequest { + /// Domain's transfers storage key + Transfers, +} + /// Request type to fetch required verification information for fraud proof through Host function. #[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone)] pub enum FraudProofVerificationInfoRequest { @@ -137,6 +144,11 @@ pub enum FraudProofVerificationInfoRequest { /// Storage proof for the keys used in validating the extrinsic storage_proof: StorageProof, }, + /// Request to fetch a specific storage key + StorageKey { + domain_id: DomainId, + req: StorageKeyRequest, + }, } impl PassBy for FraudProofVerificationInfoRequest { @@ -183,6 +195,8 @@ pub enum FraudProofVerificationInfoResponse { OperatorStake(Balance), /// Result of check extrinsics in single context CheckExtrinsicsInSingleContext(Option), + /// Result of the storage key request + StorageKey(Option>), } impl FraudProofVerificationInfoResponse { @@ -282,6 +296,13 @@ impl FraudProofVerificationInfoResponse { _ => None, } } + + pub fn into_storage_key(self) -> Option> { + match self { + FraudProofVerificationInfoResponse::StorageKey(result) => result, + _ => None, + } + } } sp_api::decl_runtime_apis! { diff --git a/crates/sp-domains-fraud-proof/src/verification.rs b/crates/sp-domains-fraud-proof/src/verification.rs index 5a7a8a43d2..78cb38ceb8 100644 --- a/crates/sp-domains-fraud-proof/src/verification.rs +++ b/crates/sp-domains-fraud-proof/src/verification.rs @@ -1,14 +1,14 @@ use crate::fraud_proof::{ InvalidBundlesFraudProof, InvalidExtrinsicsRootProof, InvalidStateTransitionProof, - ValidBundleProof, VerificationError, + InvalidTransfersProof, ValidBundleProof, VerificationError, }; use crate::fraud_proof_runtime_interface::get_fraud_proof_verification_info; use crate::{ fraud_proof_runtime_interface, FraudProofVerificationInfoRequest, - FraudProofVerificationInfoResponse, SetCodeExtrinsic, + FraudProofVerificationInfoResponse, SetCodeExtrinsic, StorageKeyRequest, }; use codec::{Decode, Encode}; -use domain_runtime_primitives::BlockFees; +use domain_runtime_primitives::{BlockFees, Transfers}; use hash_db::Hasher; use sp_core::storage::StorageKey; use sp_core::H256; @@ -350,6 +350,61 @@ where Ok(()) } +/// Verifies invalid transfers fraud proof. +pub fn verify_invalid_transfers_fraud_proof< + CBlock, + DomainNumber, + DomainHash, + Balance, + DomainHashing, +>( + bad_receipt: ExecutionReceipt< + NumberFor, + CBlock::Hash, + DomainNumber, + DomainHash, + Balance, + >, + proof: &InvalidTransfersProof, +) -> Result<(), VerificationError> +where + CBlock: BlockT, + CBlock::Hash: Into, + Balance: PartialEq + Decode, + DomainHashing: Hasher, +{ + let InvalidTransfersProof { + domain_id, + storage_proof, + .. + } = proof; + + let storage_key = get_fraud_proof_verification_info( + bad_receipt.consensus_block_hash.into(), + FraudProofVerificationInfoRequest::StorageKey { + domain_id: *domain_id, + req: StorageKeyRequest::Transfers, + }, + ) + .and_then(FraudProofVerificationInfoResponse::into_storage_key) + .ok_or(VerificationError::FailedToGetDomainTransfersStorageKey)?; + let storage_proof = storage_proof.clone(); + + let transfers = StorageProofVerifier::::get_decoded_value::>( + &bad_receipt.final_state_root, + storage_proof, + StorageKey(storage_key), + ) + .map_err(|_| VerificationError::InvalidStorageProof)?; + + // if the rewards matches, then this is an invalid fraud proof since rewards must be different. + if bad_receipt.transfers == transfers { + return Err(VerificationError::InvalidProof); + } + + Ok(()) +} + /// This function checks if this fraud proof is expected against the inboxed bundle entry it is targeting. /// If the entry is expected then it will be returned /// In any other cases VerificationError will be returned diff --git a/crates/sp-domains/src/lib.rs b/crates/sp-domains/src/lib.rs index 6e3df02a21..5a305935fa 100644 --- a/crates/sp-domains/src/lib.rs +++ b/crates/sp-domains/src/lib.rs @@ -34,7 +34,7 @@ use bundle_producer_election::{BundleProducerElectionParams, ProofOfElectionErro use core::num::ParseIntError; use core::ops::{Add, Sub}; use core::str::FromStr; -use domain_runtime_primitives::{BlockFees, MultiAccountId}; +use domain_runtime_primitives::{BlockFees, MultiAccountId, Transfers}; use frame_support::storage::storage_prefix; use frame_support::{Blake2_128Concat, StorageHasher}; use hexlit::hex; @@ -455,14 +455,6 @@ pub struct BundleDigest { pub size: u32, } -#[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone, Default)] -pub struct Transfers { - /// Total transfers that came into the domain. - pub transfers_in: Balance, - /// Total transfers that went out of the domain. - pub transfers_out: Balance, -} - /// Receipt of a domain block execution. #[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone)] pub struct ExecutionReceipt { diff --git a/domains/client/block-preprocessor/src/stateless_runtime.rs b/domains/client/block-preprocessor/src/stateless_runtime.rs index d885d53db6..c66409d5e5 100644 --- a/domains/client/block-preprocessor/src/stateless_runtime.rs +++ b/domains/client/block-preprocessor/src/stateless_runtime.rs @@ -248,4 +248,8 @@ where block_hash, ) } + + pub fn transfers_storage_key(&self) -> Result, ApiError> { + >::transfers_storage_key(self, Default::default()) + } } diff --git a/domains/client/domain-operator/src/domain_block_processor.rs b/domains/client/domain-operator/src/domain_block_processor.rs index ea222854e6..83b7f144be 100644 --- a/domains/client/domain-operator/src/domain_block_processor.rs +++ b/domains/client/domain-operator/src/domain_block_processor.rs @@ -333,7 +333,9 @@ where // } // } - let mut roots = self.client.runtime_api().intermediate_roots(header_hash)?; + let runtime_api = self.client.runtime_api(); + + let mut roots = runtime_api.intermediate_roots(header_hash)?; let encoded_state_root = state_root .encode() @@ -381,7 +383,8 @@ where // Get the accumulated transaction fee of all transactions included in the block // and used as the operator reward - let block_fees = self.client.runtime_api().block_fees(header_hash)?; + let block_fees = runtime_api.block_fees(header_hash)?; + let transfers = runtime_api.transfers(header_hash)?; let execution_receipt = ExecutionReceipt { domain_block_number: header_number, @@ -396,8 +399,7 @@ where execution_trace: trace, execution_trace_root: sp_core::H256(trace_root), block_fees, - // TODO: Fetch transfers from the runtime - transfers: Default::default(), + transfers, }; Ok(DomainBlockResult { @@ -640,7 +642,10 @@ where // the extrinsic can be considered as invalid due to multiple `invalid_type` (i.e. an extrinsic // can be `OutOfRangeTx` and `InvalidXDM` at the same time) thus use the checking order and // consider the first check as the mismatch. - Ordering::Equal => match local_invalid_type.checking_order().cmp(&external_invalid_type.checking_order()) { + Ordering::Equal => match local_invalid_type + .checking_order() + .cmp(&external_invalid_type.checking_order()) + { Ordering::Less => BundleMismatchType::TrueInvalid(local_invalid_type), Ordering::Greater => BundleMismatchType::FalseInvalid(external_invalid_type), Ordering::Equal => unreachable!( @@ -926,6 +931,17 @@ where }); } + if bad_receipt.transfers != local_receipt.transfers { + return self + .fraud_proof_generator + .generate_invalid_transfers_proof(self.domain_id, &local_receipt, bad_receipt_hash) + .map_err(|err| { + sp_blockchain::Error::Application(Box::from(format!( + "Failed to generate invalid transfers fraud proof: {err}" + ))) + }); + } + if bad_receipt.domain_block_hash != local_receipt.domain_block_hash { return self .fraud_proof_generator @@ -995,11 +1011,11 @@ mod tests { find_inboxed_bundles_mismatch::( &create_test_execution_receipt(vec![InboxedBundle::invalid( InvalidBundleType::UndecodableTx(0), - Default::default() + Default::default(), )]), &create_test_execution_receipt(vec![InboxedBundle::invalid( InvalidBundleType::UndecodableTx(0), - Default::default() + Default::default(), )]), ) .unwrap(), @@ -1141,11 +1157,11 @@ mod tests { find_inboxed_bundles_mismatch::( &create_test_execution_receipt(vec![InboxedBundle::valid( H256::random(), - Default::default() + Default::default(), ),]), &create_test_execution_receipt(vec![InboxedBundle::invalid( InvalidBundleType::IllegalTx(3), - Default::default() + Default::default(), ),]), ) .unwrap(), @@ -1160,11 +1176,11 @@ mod tests { find_inboxed_bundles_mismatch::( &create_test_execution_receipt(vec![InboxedBundle::invalid( InvalidBundleType::IllegalTx(3), - Default::default() + Default::default(), ),]), &create_test_execution_receipt(vec![InboxedBundle::valid( H256::random(), - Default::default() + Default::default(), ),]), ) .unwrap(), diff --git a/domains/client/domain-operator/src/fraud_proof.rs b/domains/client/domain-operator/src/fraud_proof.rs index d4294ce9b9..35ae92b83e 100644 --- a/domains/client/domain-operator/src/fraud_proof.rs +++ b/domains/client/domain-operator/src/fraud_proof.rs @@ -16,7 +16,8 @@ use sp_domains_fraud_proof::execution_prover::ExecutionProver; use sp_domains_fraud_proof::fraud_proof::{ ApplyExtrinsicMismatch, ExecutionPhase, FinalizeBlockMismatch, FraudProof, InvalidBlockFeesProof, InvalidBundlesFraudProof, InvalidDomainBlockHashProof, - InvalidExtrinsicsRootProof, InvalidStateTransitionProof, ValidBundleDigest, + InvalidExtrinsicsRootProof, InvalidStateTransitionProof, InvalidTransfersProof, + ValidBundleDigest, }; use sp_runtime::generic::BlockId; use sp_runtime::traits::{Block as BlockT, Header as HeaderT, NumberFor}; @@ -57,7 +58,7 @@ pub enum FraudProofError { decoding_error: codec::Error, }, #[error( - "Invalid extrinsic index for creating illegal tx fraud proof, \ + "Invalid extrinsic index for creating illegal tx fraud proof, \ expected extrinsic index: {index},\ is_true_invalid: {is_true_invalid} and validity response is: {extrinsics_validity_response:?}" )] @@ -149,6 +150,25 @@ where })) } + pub(crate) fn generate_invalid_transfers_proof( + &self, + domain_id: DomainId, + local_receipt: &ExecutionReceiptFor, + bad_receipt_hash: Block::Hash, + ) -> Result, FraudProofError> { + let block_hash = local_receipt.domain_block_hash; + let runtime_api = self.client.runtime_api(); + let key = runtime_api.transfers_storage_key(block_hash)?; + let proof = self + .client + .read_proof(block_hash, &mut [key.as_slice()].into_iter())?; + Ok(FraudProof::InvalidTransfers(InvalidTransfersProof { + domain_id, + bad_receipt_hash, + storage_proof: proof, + })) + } + pub(crate) fn generate_invalid_domain_block_hash_proof( &self, domain_id: DomainId, @@ -203,7 +223,7 @@ where .to_string() .into(), ) - .into()) + .into()); } }; @@ -294,7 +314,7 @@ where StorageProofProvider::< LayoutV1>, >::generate_enumerated_proof_of_inclusion( - encoded_extrinsics.as_slice(), extrinsic_index + encoded_extrinsics.as_slice(), extrinsic_index, ) .ok_or(FraudProofError::FailToGenerateProofOfInclusion)? }; diff --git a/domains/client/domain-operator/src/tests.rs b/domains/client/domain-operator/src/tests.rs index 8699bcc0f0..a6de4ce219 100644 --- a/domains/client/domain-operator/src/tests.rs +++ b/domains/client/domain-operator/src/tests.rs @@ -5,7 +5,7 @@ use crate::fraud_proof::{FraudProofGenerator, TraceDiffType}; use crate::tests::TxPoolError::InvalidTransaction as TxPoolInvalidTransaction; use crate::OperatorSlotInfo; use codec::{Decode, Encode}; -use domain_runtime_primitives::{DomainCoreApi, Hash}; +use domain_runtime_primitives::{DomainCoreApi, Hash, Transfers}; use domain_test_primitives::{OnchainStateApi, TimestampApi}; use domain_test_service::evm_domain_test_runtime::{Header, UncheckedExtrinsic}; use domain_test_service::EcdsaKeyring::{Alice, Bob, Charlie, Eve}; @@ -31,6 +31,7 @@ use sp_domains::{ use sp_domains_fraud_proof::fraud_proof::{ ApplyExtrinsicMismatch, ExecutionPhase, FinalizeBlockMismatch, FraudProof, InvalidBlockFeesProof, InvalidDomainBlockHashProof, InvalidExtrinsicsRootProof, + InvalidTransfersProof, }; use sp_domains_fraud_proof::InvalidTransactionCode; use sp_runtime::generic::{BlockId, DigestItem}; @@ -41,7 +42,7 @@ use sp_state_machine::backend::AsTrieBackend; use std::sync::Arc; use subspace_core_primitives::PotOutput; use subspace_runtime_primitives::opaque::Block as CBlock; -use subspace_runtime_primitives::Balance; +use subspace_runtime_primitives::{Balance, SSC}; use subspace_test_service::{ produce_block_with, produce_blocks, produce_blocks_until, MockConsensusNode, }; @@ -1956,6 +1957,114 @@ async fn test_invalid_block_fees_proof_creation() { assert!(!ferdie.does_receipt_exist(bad_receipt_hash).unwrap()); } +#[tokio::test(flavor = "multi_thread")] +async fn test_invalid_transfers_fraud_proof() { + let directory = TempDir::new().expect("Must be able to create temporary directory"); + + let mut builder = sc_cli::LoggerBuilder::new(""); + builder.with_colors(false); + let _ = builder.init(); + + let tokio_handle = tokio::runtime::Handle::current(); + + // Start Ferdie + let mut ferdie = MockConsensusNode::run( + tokio_handle.clone(), + Ferdie, + BasePath::new(directory.path().join("ferdie")), + ); + + // Run Alice (a evm domain authority node) + let mut alice = domain_test_service::DomainNodeBuilder::new( + tokio_handle.clone(), + Alice, + BasePath::new(directory.path().join("alice")), + ) + .build_evm_node(Role::Authority, GENESIS_DOMAIN_ID, &mut ferdie) + .await; + + let bundle_to_tx = |opaque_bundle| { + subspace_test_runtime::UncheckedExtrinsic::new_unsigned( + pallet_domains::Call::submit_bundle { opaque_bundle }.into(), + ) + .into() + }; + + produce_blocks!(ferdie, alice, 5).await.unwrap(); + + alice + .construct_and_send_extrinsic(pallet_balances::Call::transfer_allow_death { + dest: Bob.to_account_id(), + value: 1, + }) + .await + .expect("Failed to send extrinsic"); + + // Produce a bundle that contains the previously sent extrinsic and record that bundle for later use + let (slot, bundle) = ferdie.produce_slot_and_wait_for_bundle_submission().await; + let target_bundle = bundle.unwrap(); + assert_eq!(target_bundle.extrinsics.len(), 1); + produce_block_with!(ferdie.produce_block_with_slot(slot), alice) + .await + .unwrap(); + + // Get a bundle from the txn pool and modify the receipt of the target bundle to an invalid one + let (slot, bundle) = ferdie.produce_slot_and_wait_for_bundle_submission().await; + let original_submit_bundle_tx = bundle_to_tx(bundle.clone().unwrap()); + let (bad_receipt_hash, bad_submit_bundle_tx) = { + let mut opaque_bundle = bundle.unwrap(); + let receipt = &mut opaque_bundle.sealed_header.header.receipt; + receipt.transfers = Transfers { + transfers_in: 10 * SSC, + transfers_out: 20 * SSC, + }; + opaque_bundle.sealed_header.signature = Sr25519Keyring::Alice + .pair() + .sign(opaque_bundle.sealed_header.pre_hash().as_ref()) + .into(); + ( + opaque_bundle.receipt().hash::(), + bundle_to_tx(opaque_bundle), + ) + }; + + // Replace `original_submit_bundle_tx` with `bad_submit_bundle_tx` in the tx pool + ferdie + .prune_tx_from_pool(&original_submit_bundle_tx) + .await + .unwrap(); + assert!(ferdie.get_bundle_from_tx_pool(slot.into()).is_none()); + + ferdie + .submit_transaction(bad_submit_bundle_tx) + .await + .unwrap(); + + // Wait for the fraud proof that target the bad ER + let wait_for_fraud_proof_fut = ferdie.wait_for_fraud_proof(move |fp| { + matches!( + fp, + FraudProof::InvalidTransfers(InvalidTransfersProof { .. }) + ) + }); + + // Produce a consensus block that contains the `bad_submit_bundle_tx` and the bad receipt should + // be added to the consensus chain block tree + produce_block_with!(ferdie.produce_block_with_slot(slot), alice) + .await + .unwrap(); + assert!(ferdie.does_receipt_exist(bad_receipt_hash).unwrap()); + + // When the domain node operator process the primary block that contains the `bad_submit_bundle_tx`, + // it will generate and submit a fraud proof + let _ = wait_for_fraud_proof_fut.await; + + // Produce a consensus block that contains the fraud proof, the fraud proof wil be verified + // and executed, thus pruned the bad receipt from the block tree + ferdie.produce_blocks(1).await.unwrap(); + assert!(!ferdie.does_receipt_exist(bad_receipt_hash).unwrap()); +} + #[tokio::test(flavor = "multi_thread")] async fn test_invalid_domain_block_hash_proof_creation() { let directory = TempDir::new().expect("Must be able to create temporary directory"); @@ -2452,7 +2561,7 @@ async fn test_domain_block_builder_include_ext_with_failed_predispatch() { .iter() .map(Encode::encode) .collect(), - sp_core::storage::StateVersion::V1 + sp_core::storage::StateVersion::V1, ) ); } diff --git a/domains/pallets/transporter/src/lib.rs b/domains/pallets/transporter/src/lib.rs index 3c9f2343a8..8bcba75d08 100644 --- a/domains/pallets/transporter/src/lib.rs +++ b/domains/pallets/transporter/src/lib.rs @@ -72,11 +72,12 @@ mod pallet { use crate::weights::WeightInfo; use crate::{BalanceOf, Location, MessageIdOf, MultiAccountId, Transfer, TryConvertBack}; use codec::{Decode, Encode}; + use domain_runtime_primitives::Transfers; use frame_support::pallet_prelude::*; use frame_support::traits::{Currency, ExistenceRequirement, WithdrawReasons}; use frame_support::weights::Weight; use frame_system::pallet_prelude::*; - use sp_domains::{DomainId, Transfers}; + use sp_domains::DomainId; use sp_messenger::endpoint::{ Endpoint, EndpointHandler as EndpointHandlerT, EndpointId, EndpointRequest, EndpointResponse, Sender, @@ -84,6 +85,7 @@ mod pallet { use sp_messenger::messages::ChainId; use sp_runtime::traits::{CheckedAdd, Convert}; use sp_std::vec; + use sp_std::vec::Vec; #[pallet::config] pub trait Config: frame_system::Config { @@ -268,11 +270,18 @@ mod pallet { #[pallet::hooks] impl Hooks> for Pallet { fn on_initialize(_n: BlockNumberFor) -> Weight { - ChainTransfers::::kill(); + ChainTransfers::::set(Default::default()); T::DbWeight::get().writes(1) } } + impl Pallet { + pub fn transfers_storage_key() -> Vec { + use frame_support::storage::generator::StorageValue; + ChainTransfers::::storage_value_final_key().to_vec() + } + } + /// Endpoint handler implementation for pallet transporter. #[derive(Debug)] pub struct EndpointHandler(pub PhantomData); diff --git a/domains/primitives/runtime/src/lib.rs b/domains/primitives/runtime/src/lib.rs index 11f84fbd92..2219aae437 100644 --- a/domains/primitives/runtime/src/lib.rs +++ b/domains/primitives/runtime/src/lib.rs @@ -211,6 +211,15 @@ pub struct BlockFees { pub domain_execution_fee: Balance, } +/// Type that holds the transfers(in/out) for a given chain. +#[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone, Default)] +pub struct Transfers { + /// Total transfers that came into the domain. + pub transfers_in: Balance, + /// Total transfers that went out of the domain. + pub transfers_out: Balance, +} + impl BlockFees where Balance: CheckedAdd, @@ -296,23 +305,28 @@ sp_api::decl_runtime_apis! { opaque_extrinsic: sp_runtime::OpaqueExtrinsic, ) -> Result<::Extrinsic, DecodeExtrinsicError>; - /// Returns extrinsic Era if present + /// Returns extrinsic Era if present. fn extrinsic_era( extrinsic: &::Extrinsic ) -> Option; - /// Return the extrinsic weight + /// Returns the extrinsic weight. fn extrinsic_weight(ext: &Block::Extrinsic) -> Weight; - /// The accumulated transaction fee of all transactions included in the block + /// The accumulated transaction fee of all transactions included in the block. fn block_fees() -> BlockFees; - /// Return the block digest + /// Returns the block digest. fn block_digest() -> Digest; - /// Return the consumed weight of the block - #[api_version(2)] + /// Returns the consumed weight of the block. fn block_weight() -> Weight; + + /// Returns the transfers for this domain in the block. + fn transfers() -> Transfers; + + /// Returns the storage key for the Transfers on Domain. + fn transfers_storage_key() -> Vec; } } diff --git a/domains/runtime/evm/src/lib.rs b/domains/runtime/evm/src/lib.rs index 4bab244399..06801ff780 100644 --- a/domains/runtime/evm/src/lib.rs +++ b/domains/runtime/evm/src/lib.rs @@ -17,7 +17,7 @@ pub use domain_runtime_primitives::{ EXISTENTIAL_DEPOSIT, MAXIMUM_BLOCK_WEIGHT, }; use domain_runtime_primitives::{ - CheckExtrinsicsValidityError, DecodeExtrinsicError, SLOT_DURATION, + CheckExtrinsicsValidityError, DecodeExtrinsicError, Transfers, SLOT_DURATION, }; use fp_account::EthereumSignature; use fp_self_contained::{CheckedSignature, SelfContainedCall}; @@ -327,6 +327,7 @@ impl pallet_block_fees::Config for Runtime { type NegativeImbalance = >::NegativeImbalance; pub struct FinalDomainTransactionByteFee; + impl Get for FinalDomainTransactionByteFee { fn get() -> Balance { BlockFees::final_domain_transaction_byte_fee() @@ -343,6 +344,7 @@ impl pallet_transaction_payment::Config for Runtime { } pub struct ExtrinsicStorageFees; + impl domain_pallet_executive::ExtrinsicStorageFees for ExtrinsicStorageFees { fn extract_signer(xt: UncheckedExtrinsic) -> (Option, DispatchInfo) { let dispatch_info = xt.get_dispatch_info(); @@ -854,7 +856,6 @@ impl_runtime_apis! { } } - #[api_version(2)] impl domain_runtime_primitives::DomainCoreApi for Runtime { fn extract_signer( extrinsics: Vec<::Extrinsic>, @@ -975,6 +976,14 @@ impl_runtime_apis! { pallet_block_fees::Call::set_next_consensus_chain_byte_fee{ transaction_byte_fee }.into() ) } + + fn transfers() -> Transfers { + Transporter::chain_transfers() + } + + fn transfers_storage_key() -> Vec { + Transporter::transfers_storage_key() + } } impl sp_messenger::MessengerApi for Runtime { diff --git a/domains/test/runtime/evm/src/lib.rs b/domains/test/runtime/evm/src/lib.rs index 1850be841e..43e34e672e 100644 --- a/domains/test/runtime/evm/src/lib.rs +++ b/domains/test/runtime/evm/src/lib.rs @@ -13,7 +13,8 @@ extern crate alloc; use codec::{Decode, Encode}; pub use domain_runtime_primitives::opaque::Header; use domain_runtime_primitives::{ - block_weights, maximum_block_length, EXISTENTIAL_DEPOSIT, MAXIMUM_BLOCK_WEIGHT, SLOT_DURATION, + block_weights, maximum_block_length, Transfers, EXISTENTIAL_DEPOSIT, MAXIMUM_BLOCK_WEIGHT, + SLOT_DURATION, }; pub use domain_runtime_primitives::{ opaque, Balance, BlockNumber, CheckExtrinsicsValidityError, DecodeExtrinsicError, Hash, Nonce, @@ -324,6 +325,7 @@ impl pallet_block_fees::Config for Runtime { } pub struct FinalDomainTransactionByteFee; + impl Get for FinalDomainTransactionByteFee { fn get() -> Balance { BlockFees::final_domain_transaction_byte_fee() @@ -340,6 +342,7 @@ impl pallet_transaction_payment::Config for Runtime { } pub struct ExtrinsicStorageFees; + impl domain_pallet_executive::ExtrinsicStorageFees for ExtrinsicStorageFees { fn extract_signer(xt: UncheckedExtrinsic) -> (Option, DispatchInfo) { let dispatch_info = xt.get_dispatch_info(); @@ -838,7 +841,6 @@ impl_runtime_apis! { } } - #[api_version(2)] impl domain_runtime_primitives::DomainCoreApi for Runtime { fn extract_signer( extrinsics: Vec<::Extrinsic>, @@ -959,6 +961,14 @@ impl_runtime_apis! { pallet_block_fees::Call::set_next_consensus_chain_byte_fee{ transaction_byte_fee }.into() ) } + + fn transfers() -> Transfers { + Transporter::chain_transfers() + } + + fn transfers_storage_key() -> Vec { + Transporter::transfers_storage_key() + } } impl sp_messenger::MessengerApi for Runtime { diff --git a/test/subspace-test-client/src/chain_spec.rs b/test/subspace-test-client/src/chain_spec.rs index 638f6f9991..ecb0454af0 100644 --- a/test/subspace-test-client/src/chain_spec.rs +++ b/test/subspace-test-client/src/chain_spec.rs @@ -49,8 +49,15 @@ pub fn subspace_local_testnet_config() -> TestChainSpec { // Sudo account get_account_id_from_seed("Alice"), // Pre-funded accounts + // Alice also get more funds that are used during the domain instantiation vec![ - (get_account_id_from_seed("Alice"), 1_000 * SSC), + ( + get_account_id_from_seed("Alice"), + (5_000 + + crate::domain_chain_spec::endowed_accounts().len() as Balance + * 2_000_000) + * SSC, + ), (get_account_id_from_seed("Bob"), 1_000 * SSC), (get_account_id_from_seed("Charlie"), 1_000 * SSC), (get_account_id_from_seed("Dave"), 1_000 * SSC), From 90234349b36c3e043d7386fe5fa1088de821b025 Mon Sep 17 00:00:00 2001 From: vedhavyas Date: Mon, 12 Feb 2024 20:14:53 +0530 Subject: [PATCH 05/13] remove evm sudo accounts for gemini and devnet networks --- crates/subspace-node/src/domain/evm_chain_spec.rs | 13 ++----------- domains/client/domain-operator/src/tests.rs | 2 +- 2 files changed, 3 insertions(+), 12 deletions(-) diff --git a/crates/subspace-node/src/domain/evm_chain_spec.rs b/crates/subspace-node/src/domain/evm_chain_spec.rs index 62b7723776..c45ae4547e 100644 --- a/crates/subspace-node/src/domain/evm_chain_spec.rs +++ b/crates/subspace-node/src/domain/evm_chain_spec.rs @@ -26,7 +26,6 @@ use hex_literal::hex; use sc_chain_spec::GenericChainSpec; use sc_service::ChainType; use sp_runtime::traits::Convert; -use std::str::FromStr; use subspace_runtime_primitives::{Balance, SSC}; /// Development keys that will be injected automatically on polkadotjs apps @@ -148,16 +147,8 @@ pub fn get_testnet_genesis_by_spec_id(spec_id: SpecId) -> RuntimeGenesisConfig { Some(accounts[0]), ) } - SpecId::Gemini => { - let sudo_account = AccountId::from_str("f31e60022e290708c17d6997c34de6a30d09438f") - .expect("Invalid Sudo account"); - testnet_genesis(Some(sudo_account)) - } - SpecId::DevNet => { - let sudo_account = AccountId::from_str("b66a91845249464309fad766fd0ece8144547736") - .expect("Invalid Sudo account"); - testnet_genesis(Some(sudo_account)) - } + SpecId::Gemini => testnet_genesis(None), + SpecId::DevNet => testnet_genesis(None), } } diff --git a/domains/client/domain-operator/src/tests.rs b/domains/client/domain-operator/src/tests.rs index a6de4ce219..7c3c633035 100644 --- a/domains/client/domain-operator/src/tests.rs +++ b/domains/client/domain-operator/src/tests.rs @@ -2033,7 +2033,7 @@ async fn test_invalid_transfers_fraud_proof() { .prune_tx_from_pool(&original_submit_bundle_tx) .await .unwrap(); - assert!(ferdie.get_bundle_from_tx_pool(slot.into()).is_none()); + assert!(ferdie.get_bundle_from_tx_pool(slot).is_none()); ferdie .submit_transaction(bad_submit_bundle_tx) From d978eced499139032ee7e4b30f9e35977c938dd9 Mon Sep 17 00:00:00 2001 From: Nazar Mokrynskyi Date: Wed, 14 Feb 2024 17:25:40 +0200 Subject: [PATCH 06/13] Refactor farmer metrics --- .../src/bin/subspace-farmer/commands/farm.rs | 64 +++-- .../subspace-farmer/commands/farm/metrics.rs | 228 +++++++----------- .../subspace-farmer/src/single_disk_farm.rs | 9 +- 3 files changed, 139 insertions(+), 162 deletions(-) diff --git a/crates/subspace-farmer/src/bin/subspace-farmer/commands/farm.rs b/crates/subspace-farmer/src/bin/subspace-farmer/commands/farm.rs index 4896478234..1b39aa6d37 100644 --- a/crates/subspace-farmer/src/bin/subspace-farmer/commands/farm.rs +++ b/crates/subspace-farmer/src/bin/subspace-farmer/commands/farm.rs @@ -2,13 +2,13 @@ mod dsn; mod metrics; use crate::commands::farm::dsn::configure_dsn; -use crate::commands::farm::metrics::FarmerMetrics; +use crate::commands::farm::metrics::{FarmerMetrics, SectorState}; use crate::utils::shutdown_signal; use anyhow::anyhow; use bytesize::ByteSize; use clap::{Parser, ValueHint}; use futures::channel::oneshot; -use futures::stream::FuturesUnordered; +use futures::stream::{FuturesOrdered, FuturesUnordered}; use futures::{FutureExt, StreamExt}; use parking_lot::Mutex; use prometheus_client::registry::Registry; @@ -665,25 +665,23 @@ where info!("Finished collecting already plotted pieces successfully"); - for single_disk_farm in single_disk_farms.iter() { - farmer_metrics.update_farm_size( - single_disk_farm.id(), - single_disk_farm.total_sectors_count(), - ); - farmer_metrics.inc_farm_plotted( - single_disk_farm.id(), - single_disk_farm - .plotted_sectors_count() - .await - .try_into() - .unwrap(), - ); - } + let total_and_plotted_sectors = single_disk_farms + .iter() + .map(|single_disk_farm| async { + let total_sector_count = single_disk_farm.total_sectors_count(); + let plotted_sectors_count = single_disk_farm.plotted_sectors_count().await; + + (total_sector_count, plotted_sectors_count) + }) + .collect::>() + .collect::>() + .await; let mut single_disk_farms_stream = single_disk_farms .into_iter() .enumerate() - .map(|(disk_farm_index, single_disk_farm)| { + .zip(total_and_plotted_sectors) + .map(|((disk_farm_index, single_disk_farm), sector_counts)| { let disk_farm_index = disk_farm_index.try_into().expect( "More than 256 plots are not supported, this is checked above already; qed", ); @@ -709,6 +707,17 @@ where } }; + let (total_sector_count, plotted_sectors_count) = sector_counts; + farmer_metrics.update_sectors_total( + single_disk_farm.id(), + total_sector_count - plotted_sectors_count, + SectorState::NotPlotted, + ); + farmer_metrics.update_sectors_total( + single_disk_farm.id(), + plotted_sectors_count, + SectorState::Plotted, + ); single_disk_farm .on_sector_update(Arc::new({ let single_disk_farm_id = *single_disk_farm.id(); @@ -748,19 +757,24 @@ where on_plotted_sector_callback(plotted_sector, old_plotted_sector); farmer_metrics.observe_sector_plotting_time(&single_disk_farm_id, time); farmer_metrics.sector_plotted.inc(); - if old_plotted_sector.is_some() { - farmer_metrics.inc_farm_replotted(&single_disk_farm_id); - } else { - farmer_metrics.inc_farm_plotted(&single_disk_farm_id, 1); - } + farmer_metrics + .update_sector_state(&single_disk_farm_id, SectorState::Plotted); } SectorUpdate::Expiration(SectorExpirationDetails::AboutToExpire) => { - farmer_metrics.inc_farm_about_to_expire(&single_disk_farm_id, 1); + farmer_metrics.update_sector_state( + &single_disk_farm_id, + SectorState::AboutToExpire, + ); } SectorUpdate::Expiration(SectorExpirationDetails::Expired) => { - farmer_metrics.inc_farm_expired(&single_disk_farm_id, 1); + farmer_metrics + .update_sector_state(&single_disk_farm_id, SectorState::Expired); + } + SectorUpdate::Expiration(SectorExpirationDetails::Determined { + .. + }) => { + // Not interested in here } - _ => {} } })) .detach(); diff --git a/crates/subspace-farmer/src/bin/subspace-farmer/commands/farm/metrics.rs b/crates/subspace-farmer/src/bin/subspace-farmer/commands/farm/metrics.rs index 88766f9d4a..58e4d592da 100644 --- a/crates/subspace-farmer/src/bin/subspace-farmer/commands/farm/metrics.rs +++ b/crates/subspace-farmer/src/bin/subspace-farmer/commands/farm/metrics.rs @@ -3,12 +3,32 @@ use prometheus_client::metrics::family::Family; use prometheus_client::metrics::gauge::Gauge; use prometheus_client::metrics::histogram::{exponential_buckets, Histogram}; use prometheus_client::registry::{Registry, Unit}; +use std::fmt; use std::sync::atomic::{AtomicI64, AtomicU64}; use std::time::Duration; use subspace_core_primitives::SectorIndex; use subspace_farmer::single_disk_farm::farming::ProvingResult; use subspace_farmer::single_disk_farm::{FarmingError, SingleDiskFarmId}; +#[derive(Debug, Copy, Clone)] +pub(super) enum SectorState { + NotPlotted, + Plotted, + AboutToExpire, + Expired, +} + +impl fmt::Display for SectorState { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(match self { + Self::NotPlotted => "NotPlotted", + Self::Plotted => "Plotted", + Self::AboutToExpire => "AboutToExpire", + Self::Expired => "Expired", + }) + } +} + #[derive(Debug, Clone)] pub(super) struct FarmerMetrics { auditing_time: Family, Histogram>, @@ -18,10 +38,7 @@ pub(super) struct FarmerMetrics { sector_encoding_time: Family, Histogram>, sector_writing_time: Family, Histogram>, sector_plotting_time: Family, Histogram>, - farm_size: Family, Gauge>, - farm_plotted: Family, Gauge>, - farm_expired: Family, Gauge>, - farm_about_to_expire: Family, Gauge>, + sectors_total: Family, Gauge>, pub(super) sector_downloading: Counter, pub(super) sector_downloaded: Counter, pub(super) sector_encoding: Counter, @@ -110,40 +127,13 @@ impl FarmerMetrics { sector_plotting_time.clone(), ); - let farm_size = Family::<_, _>::new_with_constructor(Gauge::<_, _>::default); - - sub_registry.register_with_unit( - "farm_size", - "Farm size", - Unit::Other("sectors".to_string()), - farm_size.clone(), - ); - - let farm_plotted = Family::<_, _>::new_with_constructor(Gauge::<_, _>::default); - - sub_registry.register_with_unit( - "farm_plotted", - "Number of plotted farm sectors", - Unit::Other("sectors".to_string()), - farm_plotted.clone(), - ); - - let farm_expired = Family::<_, _>::new_with_constructor(Gauge::<_, _>::default); - - sub_registry.register_with_unit( - "farm_expired", - "Number of expired farm sectors", - Unit::Other("sectors".to_string()), - farm_expired.clone(), - ); - - let farm_about_to_expire = Family::<_, _>::new_with_constructor(Gauge::<_, _>::default); + let sectors_total = Family::<_, _>::new_with_constructor(Gauge::<_, _>::default); sub_registry.register_with_unit( - "farm_about_to_expire", - "Number of farm sectors about to expire", + "sectors_total", + "Total number of sectors with corresponding state", Unit::Other("sectors".to_string()), - farm_about_to_expire.clone(), + sectors_total.clone(), ); let sector_downloading = Counter::<_, _>::default(); @@ -226,10 +216,7 @@ impl FarmerMetrics { sector_encoding_time, sector_writing_time, sector_plotting_time, - farm_size, - farm_plotted, - farm_expired, - farm_about_to_expire, + sectors_total, sector_downloading, sector_downloaded, sector_encoding, @@ -281,6 +268,73 @@ impl FarmerMetrics { .inc(); } + pub(super) fn update_sectors_total( + &self, + single_disk_farm_id: &SingleDiskFarmId, + sectors: SectorIndex, + state: SectorState, + ) { + self.sectors_total + .get_or_create(&vec![ + ("farm_id".to_string(), single_disk_farm_id.to_string()), + ("state".to_string(), state.to_string()), + ]) + .set(i64::from(sectors)); + } + + pub(super) fn update_sector_state( + &self, + single_disk_farm_id: &SingleDiskFarmId, + state: SectorState, + ) { + self.sectors_total + .get_or_create(&vec![ + ("farm_id".to_string(), single_disk_farm_id.to_string()), + ("state".to_string(), state.to_string()), + ]) + .inc(); + match state { + SectorState::NotPlotted => { + // Never called, doesn't make sense + } + SectorState::Plotted => { + let not_plotted_sectors = self.sectors_total.get_or_create(&vec![ + ("farm_id".to_string(), single_disk_farm_id.to_string()), + ("state".to_string(), SectorState::NotPlotted.to_string()), + ]); + if not_plotted_sectors.get() > 0 { + // Initial plotting + not_plotted_sectors.dec(); + } else { + let expired_sectors = self.sectors_total.get_or_create(&vec![ + ("farm_id".to_string(), single_disk_farm_id.to_string()), + ("state".to_string(), SectorState::Expired.to_string()), + ]); + if expired_sectors.get() > 0 { + // Replaced expired sector + expired_sectors.dec(); + } else { + // Replaced about to expire sector + self.sectors_total + .get_or_create(&vec![ + ("farm_id".to_string(), single_disk_farm_id.to_string()), + ("state".to_string(), SectorState::AboutToExpire.to_string()), + ]) + .dec(); + } + } + } + SectorState::AboutToExpire | SectorState::Expired => { + self.sectors_total + .get_or_create(&vec![ + ("farm_id".to_string(), single_disk_farm_id.to_string()), + ("state".to_string(), SectorState::Plotted.to_string()), + ]) + .dec(); + } + } + } + pub(super) fn observe_sector_downloading_time( &self, single_disk_farm_id: &SingleDiskFarmId, @@ -332,100 +386,4 @@ impl FarmerMetrics { )]) .observe(time.as_secs_f64()); } - - pub(super) fn update_farm_size( - &self, - single_disk_farm_id: &SingleDiskFarmId, - sectors: SectorIndex, - ) { - self.farm_size - .get_or_create(&vec![( - "farm_id".to_string(), - single_disk_farm_id.to_string(), - )]) - .set(i64::from(sectors)); - } - - pub(super) fn inc_farm_plotted( - &self, - single_disk_farm_id: &SingleDiskFarmId, - sectors: SectorIndex, - ) { - self.farm_plotted - .get_or_create(&vec![( - "farm_id".to_string(), - single_disk_farm_id.to_string(), - )]) - .inc_by(i64::from(sectors)); - } - - pub(super) fn inc_farm_expired( - &self, - single_disk_farm_id: &SingleDiskFarmId, - sectors: SectorIndex, - ) { - self.farm_expired - .get_or_create(&vec![( - "farm_id".to_string(), - single_disk_farm_id.to_string(), - )]) - .inc_by(i64::from(sectors)); - self.farm_plotted - .get_or_create(&vec![( - "farm_id".to_string(), - single_disk_farm_id.to_string(), - )]) - .dec_by(i64::from(sectors)); - } - - pub(super) fn inc_farm_about_to_expire( - &self, - single_disk_farm_id: &SingleDiskFarmId, - sectors: SectorIndex, - ) { - self.farm_about_to_expire - .get_or_create(&vec![( - "farm_id".to_string(), - single_disk_farm_id.to_string(), - )]) - .inc_by(i64::from(sectors)); - self.farm_plotted - .get_or_create(&vec![( - "farm_id".to_string(), - single_disk_farm_id.to_string(), - )]) - .dec_by(i64::from(sectors)); - } - - pub(super) fn inc_farm_replotted(&self, single_disk_farm_id: &SingleDiskFarmId) { - self.farm_plotted - .get_or_create(&vec![( - "farm_id".to_string(), - single_disk_farm_id.to_string(), - )]) - .inc(); - if self - .farm_expired - .get_or_create(&vec![( - "farm_id".to_string(), - single_disk_farm_id.to_string(), - )]) - .get() - > 0 - { - self.farm_expired - .get_or_create(&vec![( - "farm_id".to_string(), - single_disk_farm_id.to_string(), - )]) - .dec(); - } else { - self.farm_about_to_expire - .get_or_create(&vec![( - "farm_id".to_string(), - single_disk_farm_id.to_string(), - )]) - .dec(); - } - } } diff --git a/crates/subspace-farmer/src/single_disk_farm.rs b/crates/subspace-farmer/src/single_disk_farm.rs index 06d746b605..350e4d3b45 100644 --- a/crates/subspace-farmer/src/single_disk_farm.rs +++ b/crates/subspace-farmer/src/single_disk_farm.rs @@ -1275,8 +1275,13 @@ impl SingleDiskFarm { } /// Number of sectors successfully plotted so far - pub async fn plotted_sectors_count(&self) -> usize { - self.sectors_metadata.read().await.len() + pub async fn plotted_sectors_count(&self) -> SectorIndex { + self.sectors_metadata + .read() + .await + .len() + .try_into() + .expect("Number of sectors never exceeds `SectorIndex` type; qed") } /// Read information about sectors plotted so far From 93957e17683123b30f9bfdaa73f60bc175beefad Mon Sep 17 00:00:00 2001 From: vedhavyas Date: Thu, 15 Feb 2024 10:45:30 +0530 Subject: [PATCH 07/13] move DomainCoreApi under sp_domains to avoid cyclic deps --- Cargo.lock | 1 + crates/pallet-domains/src/tests.rs | 4 +- crates/sp-domains-fraud-proof/src/tests.rs | 13 +- .../src/verification.rs | 4 +- crates/sp-domains/src/core_api.rs | 82 ++++++++++++ crates/sp-domains/src/lib.rs | 39 +++++- .../src/malicious_bundle_producer.rs | 2 +- .../src/malicious_bundle_tamper.rs | 5 +- domains/client/block-preprocessor/src/lib.rs | 2 +- .../src/stateless_runtime.rs | 5 +- .../domain-operator/src/bundle_processor.rs | 2 +- .../src/domain_block_processor.rs | 2 +- .../src/domain_bundle_producer.rs | 2 +- .../src/domain_bundle_proposer.rs | 2 +- .../domain-operator/src/domain_worker.rs | 2 +- .../client/domain-operator/src/fraud_proof.rs | 3 +- .../client/domain-operator/src/operator.rs | 2 +- domains/client/domain-operator/src/tests.rs | 4 +- domains/pallets/block-fees/Cargo.toml | 22 ++-- domains/pallets/block-fees/src/lib.rs | 10 +- domains/pallets/transporter/src/lib.rs | 3 +- domains/primitives/runtime/src/lib.rs | 118 +----------------- domains/runtime/evm/src/lib.rs | 8 +- domains/service/src/domain.rs | 3 +- domains/test/runtime/evm/src/lib.rs | 9 +- domains/test/service/src/domain.rs | 3 +- 26 files changed, 184 insertions(+), 168 deletions(-) create mode 100644 crates/sp-domains/src/core_api.rs diff --git a/Cargo.lock b/Cargo.lock index fb3c78adf2..01c0aac10d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7136,6 +7136,7 @@ dependencies = [ "parity-scale-codec", "scale-info", "sp-block-fees", + "sp-domains", "sp-runtime", "sp-std", ] diff --git a/crates/pallet-domains/src/tests.rs b/crates/pallet-domains/src/tests.rs index 1ce914cc8b..f4a2a9b217 100644 --- a/crates/pallet-domains/src/tests.rs +++ b/crates/pallet-domains/src/tests.rs @@ -884,7 +884,7 @@ fn test_invalid_block_fees_fraud_proof() { domain_id, bad_receipt_hash, // set different reward in the storage and generate proof for that value - domain_runtime_primitives::BlockFees::new( + sp_domains::BlockFees::new( domain_block .execution_receipt .block_fees @@ -909,7 +909,7 @@ type FraudProofFor = fn generate_invalid_block_fees_fraud_proof( domain_id: DomainId, bad_receipt_hash: ReceiptHashFor, - block_fees: domain_runtime_primitives::BlockFees>, + block_fees: sp_domains::BlockFees>, ) -> (FraudProofFor, T::Hash) { let storage_key = sp_domains_fraud_proof::fraud_proof::operator_block_fees_final_key(); let mut root = T::Hash::default(); diff --git a/crates/sp-domains-fraud-proof/src/tests.rs b/crates/sp-domains-fraud-proof/src/tests.rs index 32550adc34..4a067b0e4d 100644 --- a/crates/sp-domains-fraud-proof/src/tests.rs +++ b/crates/sp-domains-fraud-proof/src/tests.rs @@ -2,7 +2,7 @@ use crate::test_ethereum_tx::{ EIP1559UnsignedTransaction, EIP2930UnsignedTransaction, LegacyUnsignedTransaction, }; use codec::Encode; -use domain_runtime_primitives::{Balance, CheckExtrinsicsValidityError, DomainCoreApi}; +use domain_runtime_primitives::{Balance, CheckExtrinsicsValidityError}; use domain_test_service::evm_domain_test_runtime::{ Runtime as TestRuntime, RuntimeCall, UncheckedExtrinsic as RuntimeUncheckedExtrinsic, }; @@ -23,6 +23,7 @@ use sp_api::{ApiExt, ProvideRuntimeApi, TransactionOutcome}; use sp_core::crypto::AccountId32; use sp_core::ecdsa::Pair; use sp_core::{keccak_256, Pair as _, H160, H256, U256}; +use sp_domains::core_api::DomainCoreApi; use sp_runtime::traits::{Extrinsic, Zero}; use sp_runtime::transaction_validity::{InvalidTransaction, TransactionValidityError}; use sp_runtime::OpaqueExtrinsic; @@ -379,7 +380,7 @@ async fn storage_change_of_the_same_runtime_instance_should_perserved_cross_runt best_hash, vec![ transfer_to_charlie_with_big_tip_1.clone().into(), - transfer_to_charlie_with_big_tip_2.clone().into() + transfer_to_charlie_with_big_tip_2.clone().into(), ], best_number, best_hash, @@ -389,7 +390,7 @@ async fn storage_change_of_the_same_runtime_instance_should_perserved_cross_runt extrinsic_index: 1, transaction_validity_error: TransactionValidityError::Invalid( InvalidTransaction::Payment - ) + ), }) ); @@ -411,7 +412,7 @@ async fn storage_change_of_the_same_runtime_instance_should_perserved_cross_runt extrinsic_index: 0, transaction_validity_error: TransactionValidityError::Invalid( InvalidTransaction::Future - ) + ), }) ); @@ -460,7 +461,7 @@ async fn storage_change_of_the_same_runtime_instance_should_perserved_cross_runt best_hash, vec![transfer_with_big_tip_1.clone().into()], best_number, - best_hash + best_hash, ) .unwrap() .is_ok()); @@ -491,7 +492,7 @@ async fn storage_change_of_the_same_runtime_instance_should_perserved_cross_runt best_hash, vec![transfer_with_big_tip_3.clone().into()], best_number, - best_hash + best_hash, ) .unwrap(), if commit_mode { diff --git a/crates/sp-domains-fraud-proof/src/verification.rs b/crates/sp-domains-fraud-proof/src/verification.rs index 78cb38ceb8..9a61f144b4 100644 --- a/crates/sp-domains-fraud-proof/src/verification.rs +++ b/crates/sp-domains-fraud-proof/src/verification.rs @@ -8,7 +8,6 @@ use crate::{ FraudProofVerificationInfoResponse, SetCodeExtrinsic, StorageKeyRequest, }; use codec::{Decode, Encode}; -use domain_runtime_primitives::{BlockFees, Transfers}; use hash_db::Hasher; use sp_core::storage::StorageKey; use sp_core::H256; @@ -17,8 +16,9 @@ use sp_domains::extrinsics::{deduplicate_and_shuffle_extrinsics, extrinsics_shuf use sp_domains::proof_provider_and_verifier::StorageProofVerifier; use sp_domains::valued_trie::valued_ordered_trie_root; use sp_domains::{ - BundleValidity, ExecutionReceipt, ExtrinsicDigest, HeaderHashFor, HeaderHashingFor, + BlockFees, BundleValidity, ExecutionReceipt, ExtrinsicDigest, HeaderHashFor, HeaderHashingFor, HeaderNumberFor, InboxedBundle, InvalidBundleType, OperatorPublicKey, SealedBundleHeader, + Transfers, }; use sp_runtime::generic::Digest; use sp_runtime::traits::{Block as BlockT, Hash, Header as HeaderT, NumberFor}; diff --git a/crates/sp-domains/src/core_api.rs b/crates/sp-domains/src/core_api.rs new file mode 100644 index 0000000000..f363eb6b52 --- /dev/null +++ b/crates/sp-domains/src/core_api.rs @@ -0,0 +1,82 @@ +use crate::{BlockFees, Transfers}; +use domain_runtime_primitives::{ + opaque, Balance, CheckExtrinsicsValidityError, DecodeExtrinsicError, +}; +use sp_runtime::generic::Era; +use sp_runtime::traits::{Block as BlockT, NumberFor}; +use sp_runtime::Digest; +use sp_std::vec::Vec; +use sp_weights::Weight; +use subspace_core_primitives::U256; +use subspace_runtime_primitives::Moment; + +sp_api::decl_runtime_apis! { + /// Base API that every domain runtime must implement. + pub trait DomainCoreApi { + /// Extracts the optional signer per extrinsic. + fn extract_signer( + extrinsics: Vec<::Extrinsic>, + ) -> Vec<(Option, ::Extrinsic)>; + + fn is_within_tx_range( + extrinsic: &::Extrinsic, + bundle_vrf_hash: &U256, + tx_range: &U256, + ) -> bool; + + /// Returns the intermediate storage roots in an encoded form. + fn intermediate_roots() -> Vec<[u8; 32]>; + + /// Returns the storage root after initializing the block. + fn initialize_block_with_post_state_root(header: &::Header) -> Vec; + + /// Returns the storage root after applying the extrinsic. + fn apply_extrinsic_with_post_state_root(extrinsic: ::Extrinsic) -> Vec; + + /// Returns an encoded extrinsic aiming to upgrade the runtime using given code. + fn construct_set_code_extrinsic(code: Vec) -> Vec; + + /// Returns an encoded extrinsic to set timestamp. + fn construct_timestamp_extrinsic(moment: Moment) -> Block::Extrinsic; + + /// Returns an encoded extrinsic to set domain transaction byte fee. + fn construct_consensus_chain_byte_fee_extrinsic(consensus_chain_byte_fee: Balance) -> Block::Extrinsic; + + /// Returns true if the extrinsic is an inherent extrinsic. + fn is_inherent_extrinsic(extrinsic: &::Extrinsic) -> bool; + + /// Checks the validity of array of extrinsics + pre_dispatch + /// returning failure on first extrinsic that fails runtime call. + /// IMPORTANT: Change `CHECK_EXTRINSICS_AND_DO_PRE_DISPATCH_METHOD_NAME` constant when this method name is changed + fn check_extrinsics_and_do_pre_dispatch(uxts: Vec<::Extrinsic>, block_number: NumberFor, + block_hash: ::Hash) -> Result<(), CheckExtrinsicsValidityError>; + + /// Decodes the domain specific extrinsic from the opaque extrinsic. + fn decode_extrinsic( + opaque_extrinsic: sp_runtime::OpaqueExtrinsic, + ) -> Result<::Extrinsic, DecodeExtrinsicError>; + + /// Returns extrinsic Era if present. + fn extrinsic_era( + extrinsic: &::Extrinsic + ) -> Option; + + /// Returns the extrinsic weight. + fn extrinsic_weight(ext: &Block::Extrinsic) -> Weight; + + /// The accumulated transaction fee of all transactions included in the block. + fn block_fees() -> BlockFees; + + /// Returns the block digest. + fn block_digest() -> Digest; + + /// Returns the consumed weight of the block. + fn block_weight() -> Weight; + + /// Returns the transfers for this domain in the block. + fn transfers() -> Transfers; + + /// Returns the storage key for the Transfers on Domain. + fn transfers_storage_key() -> Vec; + } +} diff --git a/crates/sp-domains/src/lib.rs b/crates/sp-domains/src/lib.rs index 5a305935fa..c7289d4b5e 100644 --- a/crates/sp-domains/src/lib.rs +++ b/crates/sp-domains/src/lib.rs @@ -18,6 +18,7 @@ #![cfg_attr(not(feature = "std"), no_std)] pub mod bundle_producer_election; +pub mod core_api; pub mod extrinsics; pub mod merkle_tree; pub mod proof_provider_and_verifier; @@ -34,7 +35,7 @@ use bundle_producer_election::{BundleProducerElectionParams, ProofOfElectionErro use core::num::ParseIntError; use core::ops::{Add, Sub}; use core::str::FromStr; -use domain_runtime_primitives::{BlockFees, MultiAccountId, Transfers}; +use domain_runtime_primitives::MultiAccountId; use frame_support::storage::storage_prefix; use frame_support::{Blake2_128Concat, StorageHasher}; use hexlit::hex; @@ -248,6 +249,42 @@ impl From for ChainId { } } +#[derive(Clone, Debug, Decode, Default, Encode, Eq, PartialEq, TypeInfo)] +pub struct BlockFees { + /// The consensus chain storage fee + pub consensus_storage_fee: Balance, + /// The domain execution fee including the storage and compute fee on domain chain, + /// tip, and the XDM reward. + pub domain_execution_fee: Balance, +} + +impl BlockFees +where + Balance: CheckedAdd, +{ + pub fn new(domain_execution_fee: Balance, consensus_storage_fee: Balance) -> Self { + BlockFees { + consensus_storage_fee, + domain_execution_fee, + } + } + + /// Returns the total fees that was collected and burned on the Domain. + pub fn total_fees(&self) -> Option { + self.consensus_storage_fee + .checked_add(&self.domain_execution_fee) + } +} + +/// Type that holds the transfers(in/out) for a given chain. +#[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone, Default)] +pub struct Transfers { + /// Total transfers that came into the domain. + pub transfers_in: Balance, + /// Total transfers that went out of the domain. + pub transfers_out: Balance, +} + #[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone)] pub struct BundleHeader { /// Proof of bundle producer election. diff --git a/crates/subspace-malicious-operator/src/malicious_bundle_producer.rs b/crates/subspace-malicious-operator/src/malicious_bundle_producer.rs index bc197707a2..8722012be5 100644 --- a/crates/subspace-malicious-operator/src/malicious_bundle_producer.rs +++ b/crates/subspace-malicious-operator/src/malicious_bundle_producer.rs @@ -3,7 +3,6 @@ use domain_client_operator::domain_bundle_producer::DomainBundleProducer; use domain_client_operator::domain_bundle_proposer::DomainBundleProposer; use domain_client_operator::{OpaqueBundleFor, OperatorSlotInfo}; use domain_runtime_primitives::opaque::Block as DomainBlock; -use domain_runtime_primitives::DomainCoreApi; use frame_system_rpc_runtime_api::AccountNonceApi; use futures::{Stream, StreamExt, TryFutureExt}; use pallet_domains::OperatorConfig; @@ -20,6 +19,7 @@ use sp_consensus_slots::Slot; use sp_consensus_subspace::FarmerPublicKey; use sp_core::crypto::UncheckedFrom; use sp_core::Get; +use sp_domains::core_api::DomainCoreApi; use sp_domains::{BundleProducerElectionApi, DomainId, DomainsApi, OperatorId, OperatorPublicKey}; use sp_keyring::Sr25519Keyring; use sp_keystore::KeystorePtr; diff --git a/crates/subspace-malicious-operator/src/malicious_bundle_tamper.rs b/crates/subspace-malicious-operator/src/malicious_bundle_tamper.rs index 2d2690b65b..81cb025460 100644 --- a/crates/subspace-malicious-operator/src/malicious_bundle_tamper.rs +++ b/crates/subspace-malicious-operator/src/malicious_bundle_tamper.rs @@ -1,12 +1,13 @@ use domain_client_operator::{ExecutionReceiptFor, OpaqueBundleFor}; -use domain_runtime_primitives::{BlockFees, DomainCoreApi}; use parity_scale_codec::{Decode, Encode}; use sc_client_api::HeaderBackend; use sp_api::ProvideRuntimeApi; use sp_domain_digests::AsPredigest; +use sp_domains::core_api::DomainCoreApi; use sp_domains::merkle_tree::MerkleTree; use sp_domains::{ - BundleValidity, HeaderHashingFor, InvalidBundleType, OperatorPublicKey, OperatorSignature, + BlockFees, BundleValidity, HeaderHashingFor, InvalidBundleType, OperatorPublicKey, + OperatorSignature, }; use sp_keystore::KeystorePtr; use sp_runtime::traits::{Block as BlockT, Hash as HashT, Header as HeaderT, NumberFor, One, Zero}; diff --git a/domains/client/block-preprocessor/src/lib.rs b/domains/client/block-preprocessor/src/lib.rs index 7dcea37fb5..6421906111 100644 --- a/domains/client/block-preprocessor/src/lib.rs +++ b/domains/client/block-preprocessor/src/lib.rs @@ -18,11 +18,11 @@ use crate::inherents::is_runtime_upgraded; use crate::xdm_verifier::is_valid_xdm; use codec::Encode; use domain_runtime_primitives::opaque::AccountId; -use domain_runtime_primitives::DomainCoreApi; use sc_client_api::BlockBackend; use sp_api::ProvideRuntimeApi; use sp_blockchain::HeaderBackend; use sp_core::H256; +use sp_domains::core_api::DomainCoreApi; use sp_domains::extrinsics::deduplicate_and_shuffle_extrinsics; use sp_domains::{ DomainId, DomainsApi, ExecutionReceipt, ExtrinsicDigest, HeaderHashingFor, InboxedBundle, diff --git a/domains/client/block-preprocessor/src/stateless_runtime.rs b/domains/client/block-preprocessor/src/stateless_runtime.rs index c66409d5e5..17da78aa79 100644 --- a/domains/client/block-preprocessor/src/stateless_runtime.rs +++ b/domains/client/block-preprocessor/src/stateless_runtime.rs @@ -1,12 +1,11 @@ use codec::{Codec, Encode}; use domain_runtime_primitives::opaque::AccountId; -use domain_runtime_primitives::{ - Balance, CheckExtrinsicsValidityError, DecodeExtrinsicError, DomainCoreApi, -}; +use domain_runtime_primitives::{Balance, CheckExtrinsicsValidityError, DecodeExtrinsicError}; use sc_executor::RuntimeVersionOf; use sp_api::{ApiError, Core}; use sp_core::traits::{CallContext, CodeExecutor, FetchRuntimeCode, RuntimeCode}; use sp_core::Hasher; +use sp_domains::core_api::DomainCoreApi; use sp_messenger::messages::ExtractedStateRootsFromProof; use sp_messenger::MessengerApi; use sp_runtime::traits::{Block as BlockT, NumberFor}; diff --git a/domains/client/domain-operator/src/bundle_processor.rs b/domains/client/domain-operator/src/bundle_processor.rs index f9cb4d6c65..3c36eb5d82 100644 --- a/domains/client/domain-operator/src/bundle_processor.rs +++ b/domains/client/domain-operator/src/bundle_processor.rs @@ -3,7 +3,6 @@ use crate::domain_block_processor::{ }; use crate::ExecutionReceiptFor; use domain_block_preprocessor::DomainBlockPreprocessor; -use domain_runtime_primitives::DomainCoreApi; use sc_client_api::{AuxStore, BlockBackend, Finalizer, ProofProvider}; use sc_consensus::{BlockImportParams, ForkChoiceStrategy, StateAction}; use sp_api::ProvideRuntimeApi; @@ -12,6 +11,7 @@ use sp_consensus::BlockOrigin; use sp_core::traits::CodeExecutor; use sp_core::H256; use sp_domain_digests::AsPredigest; +use sp_domains::core_api::DomainCoreApi; use sp_domains::{DomainId, DomainsApi, ReceiptValidity}; use sp_domains_fraud_proof::FraudProofApi; use sp_messenger::MessengerApi; diff --git a/domains/client/domain-operator/src/domain_block_processor.rs b/domains/client/domain-operator/src/domain_block_processor.rs index 83b7f144be..c03a273128 100644 --- a/domains/client/domain-operator/src/domain_block_processor.rs +++ b/domains/client/domain-operator/src/domain_block_processor.rs @@ -6,7 +6,6 @@ use codec::{Decode, Encode}; use domain_block_builder::{BlockBuilder, BuiltBlock, RecordProof}; use domain_block_preprocessor::inherents::get_inherent_data; use domain_block_preprocessor::PreprocessResult; -use domain_runtime_primitives::DomainCoreApi; use sc_client_api::{AuxStore, BlockBackend, Finalizer, ProofProvider}; use sc_consensus::{ BlockImportParams, ForkChoiceStrategy, ImportResult, SharedBlockImport, StateAction, @@ -18,6 +17,7 @@ use sp_blockchain::{HashAndNumber, HeaderBackend, HeaderMetadata}; use sp_consensus::{BlockOrigin, SyncOracle}; use sp_core::traits::CodeExecutor; use sp_core::H256; +use sp_domains::core_api::DomainCoreApi; use sp_domains::merkle_tree::MerkleTree; use sp_domains::{BundleValidity, DomainId, DomainsApi, ExecutionReceipt, HeaderHashingFor}; use sp_domains_fraud_proof::fraud_proof::{FraudProof, ValidBundleProof}; diff --git a/domains/client/domain-operator/src/domain_bundle_producer.rs b/domains/client/domain-operator/src/domain_bundle_producer.rs index 81334b6f32..5014b64ec9 100644 --- a/domains/client/domain-operator/src/domain_bundle_producer.rs +++ b/domains/client/domain-operator/src/domain_bundle_producer.rs @@ -3,11 +3,11 @@ use crate::domain_bundle_proposer::DomainBundleProposer; use crate::utils::OperatorSlotInfo; use crate::BundleSender; use codec::Decode; -use domain_runtime_primitives::DomainCoreApi; use sc_client_api::{AuxStore, BlockBackend}; use sp_api::ProvideRuntimeApi; use sp_block_builder::BlockBuilder; use sp_blockchain::HeaderBackend; +use sp_domains::core_api::DomainCoreApi; use sp_domains::{ Bundle, BundleProducerElectionApi, DomainId, DomainsApi, OperatorId, OperatorPublicKey, OperatorSignature, SealedBundleHeader, diff --git a/domains/client/domain-operator/src/domain_bundle_proposer.rs b/domains/client/domain-operator/src/domain_bundle_proposer.rs index ffd1f35925..927e23b0f4 100644 --- a/domains/client/domain-operator/src/domain_bundle_proposer.rs +++ b/domains/client/domain-operator/src/domain_bundle_proposer.rs @@ -1,12 +1,12 @@ use crate::ExecutionReceiptFor; use codec::Encode; -use domain_runtime_primitives::DomainCoreApi; use futures::{select, FutureExt}; use sc_client_api::{AuxStore, BlockBackend}; use sc_transaction_pool_api::InPoolTransaction; use sp_api::{ApiExt, ProvideRuntimeApi}; use sp_block_builder::BlockBuilder; use sp_blockchain::HeaderBackend; +use sp_domains::core_api::DomainCoreApi; use sp_domains::{ BundleHeader, DomainId, DomainsApi, ExecutionReceipt, HeaderHashingFor, ProofOfElection, }; diff --git a/domains/client/domain-operator/src/domain_worker.rs b/domains/client/domain-operator/src/domain_worker.rs index d18942c988..154483a4e1 100644 --- a/domains/client/domain-operator/src/domain_worker.rs +++ b/domains/client/domain-operator/src/domain_worker.rs @@ -18,7 +18,6 @@ use crate::bundle_processor::BundleProcessor; use crate::domain_bundle_producer::DomainBundleProducer; use crate::utils::{BlockInfo, OperatorSlotInfo}; use crate::{NewSlotNotification, OperatorStreams}; -use domain_runtime_primitives::DomainCoreApi; use futures::channel::mpsc; use futures::{SinkExt, Stream, StreamExt}; use sc_client_api::{ @@ -30,6 +29,7 @@ use sp_block_builder::BlockBuilder; use sp_blockchain::{HeaderBackend, HeaderMetadata}; use sp_core::traits::{CodeExecutor, SpawnEssentialNamed}; use sp_core::H256; +use sp_domains::core_api::DomainCoreApi; use sp_domains::{BundleProducerElectionApi, DomainsApi, OpaqueBundle, OperatorId}; use sp_domains_fraud_proof::FraudProofApi; use sp_messenger::MessengerApi; diff --git a/domains/client/domain-operator/src/fraud_proof.rs b/domains/client/domain-operator/src/fraud_proof.rs index 35ae92b83e..ed07f9c5b4 100644 --- a/domains/client/domain-operator/src/fraud_proof.rs +++ b/domains/client/domain-operator/src/fraud_proof.rs @@ -3,13 +3,14 @@ use crate::ExecutionReceiptFor; use codec::{Decode, Encode}; use domain_block_builder::{BlockBuilder, RecordProof}; use domain_runtime_primitives::opaque::AccountId; -use domain_runtime_primitives::{CheckExtrinsicsValidityError, DomainCoreApi}; +use domain_runtime_primitives::CheckExtrinsicsValidityError; use sc_client_api::{AuxStore, BlockBackend, ProofProvider}; use sp_api::{ApiExt, ProvideRuntimeApi}; use sp_blockchain::HeaderBackend; use sp_core::traits::CodeExecutor; use sp_core::H256; use sp_domain_digests::AsPredigest; +use sp_domains::core_api::DomainCoreApi; use sp_domains::proof_provider_and_verifier::StorageProofProvider; use sp_domains::{DomainId, DomainsApi, ExtrinsicDigest, HeaderHashingFor, InvalidBundleType}; use sp_domains_fraud_proof::execution_prover::ExecutionProver; diff --git a/domains/client/domain-operator/src/operator.rs b/domains/client/domain-operator/src/operator.rs index da8c9521e3..9e8abfb5ff 100644 --- a/domains/client/domain-operator/src/operator.rs +++ b/domains/client/domain-operator/src/operator.rs @@ -4,7 +4,6 @@ use crate::domain_bundle_producer::DomainBundleProducer; use crate::domain_bundle_proposer::DomainBundleProposer; use crate::fraud_proof::FraudProofGenerator; use crate::{DomainImportNotifications, NewSlotNotification, OperatorParams}; -use domain_runtime_primitives::DomainCoreApi; use futures::channel::mpsc; use futures::{FutureExt, Stream}; use sc_client_api::{ @@ -15,6 +14,7 @@ use sp_api::ProvideRuntimeApi; use sp_blockchain::{HeaderBackend, HeaderMetadata}; use sp_core::traits::{CodeExecutor, SpawnEssentialNamed}; use sp_core::H256; +use sp_domains::core_api::DomainCoreApi; use sp_domains::{BundleProducerElectionApi, DomainsApi}; use sp_domains_fraud_proof::FraudProofApi; use sp_keystore::KeystorePtr; diff --git a/domains/client/domain-operator/src/tests.rs b/domains/client/domain-operator/src/tests.rs index 7c3c633035..d37b1e8858 100644 --- a/domains/client/domain-operator/src/tests.rs +++ b/domains/client/domain-operator/src/tests.rs @@ -5,7 +5,7 @@ use crate::fraud_proof::{FraudProofGenerator, TraceDiffType}; use crate::tests::TxPoolError::InvalidTransaction as TxPoolInvalidTransaction; use crate::OperatorSlotInfo; use codec::{Decode, Encode}; -use domain_runtime_primitives::{DomainCoreApi, Hash, Transfers}; +use domain_runtime_primitives::Hash; use domain_test_primitives::{OnchainStateApi, TimestampApi}; use domain_test_service::evm_domain_test_runtime::{Header, UncheckedExtrinsic}; use domain_test_service::EcdsaKeyring::{Alice, Bob, Charlie, Eve}; @@ -24,9 +24,11 @@ use sp_core::storage::StateVersion; use sp_core::traits::FetchRuntimeCode; use sp_core::{Pair, H256}; use sp_domain_digests::AsPredigest; +use sp_domains::core_api::DomainCoreApi; use sp_domains::merkle_tree::MerkleTree; use sp_domains::{ Bundle, BundleValidity, DomainsApi, HeaderHashingFor, InboxedBundle, InvalidBundleType, + Transfers, }; use sp_domains_fraud_proof::fraud_proof::{ ApplyExtrinsicMismatch, ExecutionPhase, FinalizeBlockMismatch, FraudProof, diff --git a/domains/pallets/block-fees/Cargo.toml b/domains/pallets/block-fees/Cargo.toml index 90372266db..4009c325e0 100644 --- a/domains/pallets/block-fees/Cargo.toml +++ b/domains/pallets/block-fees/Cargo.toml @@ -8,8 +8,8 @@ homepage = "https://subspace.network" repository = "https://github.com/subspace/subspace" description = "Subspace node pallet for charging and re-distributing domain transaction fees" include = [ - "/src", - "/Cargo.toml", + "/src", + "/Cargo.toml", ] [dependencies] @@ -20,18 +20,20 @@ frame-system = { version = "4.0.0-dev", default-features = false, git = "https:/ pallet-transaction-payment = { version = "4.0.0-dev", default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "d6b500960579d73c43fc4ef550b703acfa61c4c8" } scale-info = { version = "2.7.0", default-features = false, features = ["derive"] } sp-block-fees = { version = "0.1.0", path = "../../primitives/block-fees", default-features = false } +sp-domains = { version = "0.1.0", path = "../../../crates/sp-domains", default-features = false } sp-runtime = { version = "24.0.0", default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "d6b500960579d73c43fc4ef550b703acfa61c4c8" } sp-std = { version = "8.0.0", default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "d6b500960579d73c43fc4ef550b703acfa61c4c8" } [features] default = ["std"] std = [ - "codec/std", - "domain-runtime-primitives/std", - "frame-support/std", - "frame-system/std", - "scale-info/std", - "sp-block-fees/std", - "sp-runtime/std", - "sp-std/std", + "codec/std", + "domain-runtime-primitives/std", + "frame-support/std", + "frame-system/std", + "scale-info/std", + "sp-block-fees/std", + "sp-domains/std", + "sp-runtime/std", + "sp-std/std", ] diff --git a/domains/pallets/block-fees/src/lib.rs b/domains/pallets/block-fees/src/lib.rs index b26a2c0ba4..99370eb1d2 100644 --- a/domains/pallets/block-fees/src/lib.rs +++ b/domains/pallets/block-fees/src/lib.rs @@ -24,11 +24,11 @@ pub use pallet::*; #[frame_support::pallet] mod pallet { use codec::{Codec, MaxEncodedLen}; - use domain_runtime_primitives::BlockFees; use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; use scale_info::TypeInfo; use sp_block_fees::{InherentError, InherentType, INHERENT_IDENTIFIER}; + use sp_domains::BlockFees; use sp_runtime::traits::{AtLeast32BitUnsigned, MaybeSerializeDeserialize, Saturating}; use sp_runtime::{FixedPointOperand, SaturatedConversion}; use sp_std::fmt::Debug; @@ -98,10 +98,10 @@ mod pallet { impl Pallet { #[pallet::call_index(0)] #[pallet::weight(( - // TODO: proper weight - Weight::from_all(10_000), - DispatchClass::Mandatory - ))] + // TODO: proper weight + Weight::from_all(10_000), + DispatchClass::Mandatory + ))] pub fn set_next_consensus_chain_byte_fee( origin: OriginFor, #[pallet::compact] transaction_byte_fee: T::Balance, diff --git a/domains/pallets/transporter/src/lib.rs b/domains/pallets/transporter/src/lib.rs index 8bcba75d08..9794019e30 100644 --- a/domains/pallets/transporter/src/lib.rs +++ b/domains/pallets/transporter/src/lib.rs @@ -72,12 +72,11 @@ mod pallet { use crate::weights::WeightInfo; use crate::{BalanceOf, Location, MessageIdOf, MultiAccountId, Transfer, TryConvertBack}; use codec::{Decode, Encode}; - use domain_runtime_primitives::Transfers; use frame_support::pallet_prelude::*; use frame_support::traits::{Currency, ExistenceRequirement, WithdrawReasons}; use frame_support::weights::Weight; use frame_system::pallet_prelude::*; - use sp_domains::DomainId; + use sp_domains::{DomainId, Transfers}; use sp_messenger::endpoint::{ Endpoint, EndpointHandler as EndpointHandlerT, EndpointId, EndpointRequest, EndpointResponse, Sender, diff --git a/domains/primitives/runtime/src/lib.rs b/domains/primitives/runtime/src/lib.rs index 2219aae437..9b3d6db4fc 100644 --- a/domains/primitives/runtime/src/lib.rs +++ b/domains/primitives/runtime/src/lib.rs @@ -27,16 +27,13 @@ use frame_system::limits::{BlockLength, BlockWeights}; use parity_scale_codec::{Decode, Encode}; use scale_info::TypeInfo; use serde::{Deserialize, Serialize}; -use sp_runtime::generic::{Era, UncheckedExtrinsic}; -use sp_runtime::traits::{ - Block as BlockT, CheckedAdd, Convert, IdentifyAccount, NumberFor, Verify, -}; +use sp_runtime::generic::UncheckedExtrinsic; +use sp_runtime::traits::{Convert, IdentifyAccount, Verify}; use sp_runtime::transaction_validity::TransactionValidityError; -use sp_runtime::{Digest, MultiAddress, MultiSignature, Perbill}; +use sp_runtime::{MultiAddress, MultiSignature, Perbill}; use sp_std::vec::Vec; use sp_weights::Weight; -use subspace_core_primitives::U256; -use subspace_runtime_primitives::{Moment, SHANNON}; +use subspace_runtime_primitives::SHANNON; /// Alias to 512-bit hash when used in the context of a transaction signature on the chain. pub type Signature = MultiSignature; @@ -202,42 +199,6 @@ pub struct DecodeExtrinsicError(pub String); pub const CHECK_EXTRINSICS_AND_DO_PRE_DISPATCH_METHOD_NAME: &str = "DomainCoreApi_check_extrinsics_and_do_pre_dispatch"; -#[derive(Clone, Debug, Decode, Default, Encode, Eq, PartialEq, TypeInfo)] -pub struct BlockFees { - /// The consensus chain storage fee - pub consensus_storage_fee: Balance, - /// The domain execution fee including the storage and compute fee on domain chain, - /// tip, and the XDM reward. - pub domain_execution_fee: Balance, -} - -/// Type that holds the transfers(in/out) for a given chain. -#[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone, Default)] -pub struct Transfers { - /// Total transfers that came into the domain. - pub transfers_in: Balance, - /// Total transfers that went out of the domain. - pub transfers_out: Balance, -} - -impl BlockFees -where - Balance: CheckedAdd, -{ - pub fn new(domain_execution_fee: Balance, consensus_storage_fee: Balance) -> Self { - BlockFees { - consensus_storage_fee, - domain_execution_fee, - } - } - - /// Returns the total fees that was collected and burned on the Domain. - pub fn total_fees(&self) -> Option { - self.consensus_storage_fee - .checked_add(&self.domain_execution_fee) - } -} - /// Opaque types. These are used by the CLI to instantiate machinery that don't need to know /// the specifics of the runtime. They can then be made to be agnostic over specific formats /// of data like extrinsics, allowing for them to continue syncing the network through upgrades @@ -259,77 +220,6 @@ pub mod opaque { pub type AccountId = Vec; } -sp_api::decl_runtime_apis! { - /// Base API that every domain runtime must implement. - pub trait DomainCoreApi { - /// Extracts the optional signer per extrinsic. - fn extract_signer( - extrinsics: Vec<::Extrinsic>, - ) -> Vec<(Option, ::Extrinsic)>; - - fn is_within_tx_range( - extrinsic: &::Extrinsic, - bundle_vrf_hash: &U256, - tx_range: &U256, - ) -> bool; - - /// Returns the intermediate storage roots in an encoded form. - fn intermediate_roots() -> Vec<[u8; 32]>; - - /// Returns the storage root after initializing the block. - fn initialize_block_with_post_state_root(header: &::Header) -> Vec; - - /// Returns the storage root after applying the extrinsic. - fn apply_extrinsic_with_post_state_root(extrinsic: ::Extrinsic) -> Vec; - - /// Returns an encoded extrinsic aiming to upgrade the runtime using given code. - fn construct_set_code_extrinsic(code: Vec) -> Vec; - - /// Returns an encoded extrinsic to set timestamp. - fn construct_timestamp_extrinsic(moment: Moment) -> Block::Extrinsic; - - /// Returns an encoded extrinsic to set domain transaction byte fee. - fn construct_consensus_chain_byte_fee_extrinsic(consensus_chain_byte_fee: Balance) -> Block::Extrinsic; - - /// Returns true if the extrinsic is an inherent extrinsic. - fn is_inherent_extrinsic(extrinsic: &::Extrinsic) -> bool; - - /// Checks the validity of array of extrinsics + pre_dispatch - /// returning failure on first extrinsic that fails runtime call. - /// IMPORTANT: Change `CHECK_EXTRINSICS_AND_DO_PRE_DISPATCH_METHOD_NAME` constant when this method name is changed - fn check_extrinsics_and_do_pre_dispatch(uxts: Vec<::Extrinsic>, block_number: NumberFor, - block_hash: ::Hash) -> Result<(), CheckExtrinsicsValidityError>; - - /// Decodes the domain specific extrinsic from the opaque extrinsic. - fn decode_extrinsic( - opaque_extrinsic: sp_runtime::OpaqueExtrinsic, - ) -> Result<::Extrinsic, DecodeExtrinsicError>; - - /// Returns extrinsic Era if present. - fn extrinsic_era( - extrinsic: &::Extrinsic - ) -> Option; - - /// Returns the extrinsic weight. - fn extrinsic_weight(ext: &Block::Extrinsic) -> Weight; - - /// The accumulated transaction fee of all transactions included in the block. - fn block_fees() -> BlockFees; - - /// Returns the block digest. - fn block_digest() -> Digest; - - /// Returns the consumed weight of the block. - fn block_weight() -> Weight; - - /// Returns the transfers for this domain in the block. - fn transfers() -> Transfers; - - /// Returns the storage key for the Transfers on Domain. - fn transfers_storage_key() -> Vec; - } -} - #[cfg(test)] mod test { use super::block_weights; diff --git a/domains/runtime/evm/src/lib.rs b/domains/runtime/evm/src/lib.rs index 06801ff780..f22796d1a6 100644 --- a/domains/runtime/evm/src/lib.rs +++ b/domains/runtime/evm/src/lib.rs @@ -17,7 +17,7 @@ pub use domain_runtime_primitives::{ EXISTENTIAL_DEPOSIT, MAXIMUM_BLOCK_WEIGHT, }; use domain_runtime_primitives::{ - CheckExtrinsicsValidityError, DecodeExtrinsicError, Transfers, SLOT_DURATION, + CheckExtrinsicsValidityError, DecodeExtrinsicError, SLOT_DURATION, }; use fp_account::EthereumSignature; use fp_self_contained::{CheckedSignature, SelfContainedCall}; @@ -41,7 +41,7 @@ use pallet_transporter::EndpointHandler; use sp_api::impl_runtime_apis; use sp_core::crypto::KeyTypeId; use sp_core::{Get, OpaqueMetadata, H160, H256, U256}; -use sp_domains::DomainId; +use sp_domains::{DomainId, Transfers}; use sp_messenger::endpoint::{Endpoint, EndpointHandler as EndpointHandlerT, EndpointId}; use sp_messenger::messages::{ BlockInfo, BlockMessagesWithStorageKey, ChainId, CrossDomainMessage, @@ -856,7 +856,7 @@ impl_runtime_apis! { } } - impl domain_runtime_primitives::DomainCoreApi for Runtime { + impl sp_domains::core_api::DomainCoreApi for Runtime { fn extract_signer( extrinsics: Vec<::Extrinsic>, ) -> Vec<(Option, ::Extrinsic)> { @@ -959,7 +959,7 @@ impl_runtime_apis! { ext.get_dispatch_info().weight } - fn block_fees() -> domain_runtime_primitives::BlockFees { + fn block_fees() -> sp_domains::BlockFees { BlockFees::collected_block_fees() } diff --git a/domains/service/src/domain.rs b/domains/service/src/domain.rs index 53bacd5390..7c622e4ec4 100644 --- a/domains/service/src/domain.rs +++ b/domains/service/src/domain.rs @@ -6,7 +6,7 @@ use domain_client_block_preprocessor::inherents::CreateInherentDataProvider; use domain_client_message_relayer::GossipMessageSink; use domain_client_operator::{Operator, OperatorParams, OperatorStreams}; use domain_runtime_primitives::opaque::{Block, Header}; -use domain_runtime_primitives::{Balance, DomainCoreApi, Hash}; +use domain_runtime_primitives::{Balance, Hash}; use futures::channel::mpsc; use futures::Stream; use pallet_transaction_payment_rpc::TransactionPaymentRuntimeApi; @@ -29,6 +29,7 @@ use sp_consensus::SyncOracle; use sp_consensus_slots::Slot; use sp_core::traits::SpawnEssentialNamed; use sp_core::{Decode, Encode}; +use sp_domains::core_api::DomainCoreApi; use sp_domains::{BundleProducerElectionApi, DomainId, DomainsApi, OperatorId}; use sp_domains_fraud_proof::FraudProofApi; use sp_messenger::messages::ChainId; diff --git a/domains/test/runtime/evm/src/lib.rs b/domains/test/runtime/evm/src/lib.rs index 43e34e672e..d867212901 100644 --- a/domains/test/runtime/evm/src/lib.rs +++ b/domains/test/runtime/evm/src/lib.rs @@ -13,8 +13,7 @@ extern crate alloc; use codec::{Decode, Encode}; pub use domain_runtime_primitives::opaque::Header; use domain_runtime_primitives::{ - block_weights, maximum_block_length, Transfers, EXISTENTIAL_DEPOSIT, MAXIMUM_BLOCK_WEIGHT, - SLOT_DURATION, + block_weights, maximum_block_length, EXISTENTIAL_DEPOSIT, MAXIMUM_BLOCK_WEIGHT, SLOT_DURATION, }; pub use domain_runtime_primitives::{ opaque, Balance, BlockNumber, CheckExtrinsicsValidityError, DecodeExtrinsicError, Hash, Nonce, @@ -41,7 +40,7 @@ use pallet_transporter::EndpointHandler; use sp_api::impl_runtime_apis; use sp_core::crypto::KeyTypeId; use sp_core::{Get, OpaqueMetadata, H160, H256, U256}; -use sp_domains::DomainId; +use sp_domains::{DomainId, Transfers}; use sp_messenger::endpoint::{Endpoint, EndpointHandler as EndpointHandlerT, EndpointId}; use sp_messenger::messages::{ BlockInfo, BlockMessagesWithStorageKey, ChainId, ChannelId, CrossDomainMessage, @@ -841,7 +840,7 @@ impl_runtime_apis! { } } - impl domain_runtime_primitives::DomainCoreApi for Runtime { + impl sp_domains::core_api::DomainCoreApi for Runtime { fn extract_signer( extrinsics: Vec<::Extrinsic>, ) -> Vec<(Option, ::Extrinsic)> { @@ -944,7 +943,7 @@ impl_runtime_apis! { ext.get_dispatch_info().weight } - fn block_fees() -> domain_runtime_primitives::BlockFees { + fn block_fees() -> sp_domains::BlockFees { BlockFees::collected_block_fees() } diff --git a/domains/test/service/src/domain.rs b/domains/test/service/src/domain.rs index 4d37fc5fef..bb20baccb7 100644 --- a/domains/test/service/src/domain.rs +++ b/domains/test/service/src/domain.rs @@ -8,7 +8,7 @@ use crate::{ use cross_domain_message_gossip::ChainTxPoolMsg; use domain_client_operator::{fetch_domain_bootstrap_info, BootstrapResult, OperatorStreams}; use domain_runtime_primitives::opaque::Block; -use domain_runtime_primitives::{Balance, DomainCoreApi}; +use domain_runtime_primitives::Balance; use domain_service::providers::DefaultProvider; use domain_service::{FullClient, RuntimeExecutor}; use domain_test_primitives::OnchainStateApi; @@ -29,6 +29,7 @@ use serde::de::DeserializeOwned; use sp_api::{ApiExt, ConstructRuntimeApi, Metadata, ProvideRuntimeApi}; use sp_block_builder::BlockBuilder; use sp_core::{Decode, Encode, H256}; +use sp_domains::core_api::DomainCoreApi; use sp_domains::DomainId; use sp_messenger::messages::ChainId; use sp_messenger::{MessengerApi, RelayerApi}; From 799b68c6d1d11ba69f782e50af26ca352a9842d5 Mon Sep 17 00:00:00 2001 From: vedhavyas Date: Thu, 15 Feb 2024 13:46:27 +0530 Subject: [PATCH 08/13] update transfers to initiate and then confirm once XDM is confirmed. --- crates/pallet-domains/src/block_tree.rs | 66 ++++-- crates/pallet-domains/src/domain_registry.rs | 2 +- crates/pallet-domains/src/tests.rs | 33 ++- crates/sp-domains/src/lib.rs | 49 +++- domains/client/domain-operator/src/tests.rs | 10 +- domains/pallets/transporter/src/lib.rs | 233 +++++++++++++++---- 6 files changed, 316 insertions(+), 77 deletions(-) diff --git a/crates/pallet-domains/src/block_tree.rs b/crates/pallet-domains/src/block_tree.rs index 862d3dc874..1d918de68c 100644 --- a/crates/pallet-domains/src/block_tree.rs +++ b/crates/pallet-domains/src/block_tree.rs @@ -11,9 +11,10 @@ use scale_info::TypeInfo; use sp_core::Get; use sp_domains::merkle_tree::MerkleTree; use sp_domains::{ - ConfirmedDomainBlock, DomainId, DomainsTransfersTracker, ExecutionReceipt, OperatorId, + ChainId, ConfirmedDomainBlock, DomainId, DomainsTransfersTracker, ExecutionReceipt, OperatorId, + Transfers, }; -use sp_runtime::traits::{BlockNumberProvider, CheckedAdd, CheckedSub, One, Saturating, Zero}; +use sp_runtime::traits::{BlockNumberProvider, CheckedSub, One, Saturating, Zero}; use sp_std::cmp::Ordering; use sp_std::collections::btree_map::BTreeMap; use sp_std::vec::Vec; @@ -348,26 +349,14 @@ pub(crate) fn process_execution_receipt( execution_receipt.consensus_block_number, ); - // tracking the transfer - // 1. Block fees are burned on domain, so it is considered transferred out - // 2. XDM transfers from the Domain - // 3. XDM transfers into the domain - let transfer_out_balance = execution_receipt + let block_fees = execution_receipt .block_fees .total_fees() - .and_then(|total| total.checked_add(&execution_receipt.transfers.transfers_out)) .ok_or(Error::BalanceOverflow)?; - // track the transfers out and then track transfers in - T::DomainsTransfersTracker::transfer_out(domain_id, transfer_out_balance) + update_domain_transfers::(domain_id, &execution_receipt.transfers, block_fees) .map_err(|_| Error::DomainTransfersTracking)?; - T::DomainsTransfersTracker::transfer_in( - domain_id, - execution_receipt.transfers.transfers_in, - ) - .map_err(|_| Error::DomainTransfersTracking)?; - LatestConfirmedDomainBlock::::insert( domain_id, ConfirmedDomainBlock { @@ -404,6 +393,51 @@ pub(crate) fn process_execution_receipt( Ok(None) } +type TransferTrackerError = + <::DomainsTransfersTracker as DomainsTransfersTracker>>::Error; + +/// Updates domain transfers for following scenarios +/// 1. Block fees are burned on domain +/// 2. Confirming incoming XDM transfers to the Domain +/// 3. Noting outgoing transfers from the domain +/// 4. Cancelling outgoing transfers from the domain. +fn update_domain_transfers( + domain_id: DomainId, + transfers: &Transfers>, + block_fees: BalanceOf, +) -> Result<(), TransferTrackerError> { + let Transfers { + transfers_in, + transfers_out, + transfers_reverted, + } = transfers; + + // confirm incoming transfers + let er_chain_id = ChainId::Domain(domain_id); + transfers_in + .iter() + .try_for_each(|(from_chain_id, amount)| { + T::DomainsTransfersTracker::confirm_transfer(*from_chain_id, er_chain_id, *amount) + })?; + + // note outgoing transfers + transfers_out.iter().try_for_each(|(to_chain_id, amount)| { + T::DomainsTransfersTracker::note_transfer(er_chain_id, *to_chain_id, *amount) + })?; + + // cancel existing transfers + transfers_reverted + .iter() + .try_for_each(|(to_chain_id, amount)| { + T::DomainsTransfersTracker::cancel_transfer(er_chain_id, *to_chain_id, *amount) + })?; + + // deduct execution fees from domain + T::DomainsTransfersTracker::reduce_domain_balance(domain_id, block_fees)?; + + Ok(()) +} + fn add_new_receipt_to_block_tree( domain_id: DomainId, submitter: OperatorId, diff --git a/crates/pallet-domains/src/domain_registry.rs b/crates/pallet-domains/src/domain_registry.rs index f269efea9b..4b7b621808 100644 --- a/crates/pallet-domains/src/domain_registry.rs +++ b/crates/pallet-domains/src/domain_registry.rs @@ -175,7 +175,7 @@ pub(crate) fn do_instantiate_domain( ) .map_err(|_| Error::InsufficientFund)?; - T::DomainsTransfersTracker::transfer_in(domain_id, total_issuance) + T::DomainsTransfersTracker::initialize_domain_balance(domain_id, total_issuance) .map_err(|_| Error::TransfersTracker)?; let genesis_receipt = { diff --git a/crates/pallet-domains/src/tests.rs b/crates/pallet-domains/src/tests.rs index f4a2a9b217..32d3aef790 100644 --- a/crates/pallet-domains/src/tests.rs +++ b/crates/pallet-domains/src/tests.rs @@ -26,7 +26,7 @@ use sp_domains::merkle_tree::MerkleTree; use sp_domains::proof_provider_and_verifier::StorageProofProvider; use sp_domains::storage::RawGenesis; use sp_domains::{ - BundleHeader, DomainId, DomainsHoldIdentifier, ExecutionReceipt, ExtrinsicDigest, + BundleHeader, ChainId, DomainId, DomainsHoldIdentifier, ExecutionReceipt, ExtrinsicDigest, InboxedBundle, InvalidBundleType, OpaqueBundle, OperatorAllowList, OperatorId, OperatorPair, ProofOfElection, RuntimeType, SealedBundleHeader, StakingHoldIdentifier, }; @@ -230,15 +230,38 @@ pub struct MockDomainsTransfersTracker; impl sp_domains::DomainsTransfersTracker for MockDomainsTransfersTracker { type Error = (); - fn balance_on_domain(_domain_id: DomainId) -> Result { - Ok(Default::default()) + fn initialize_domain_balance( + _domain_id: DomainId, + _amount: Balance, + ) -> Result<(), Self::Error> { + Ok(()) + } + + fn note_transfer( + _from_chain_id: ChainId, + _to_chain_id: ChainId, + _amount: Balance, + ) -> Result<(), Self::Error> { + Ok(()) + } + + fn confirm_transfer( + _from_chain_id: ChainId, + _to_chain_id: ChainId, + _amount: Balance, + ) -> Result<(), Self::Error> { + Ok(()) } - fn transfer_in(_domain_id: DomainId, _amount: Balance) -> Result<(), Self::Error> { + fn cancel_transfer( + _from_chain_id: ChainId, + _to_chain_id: ChainId, + _amount: Balance, + ) -> Result<(), Self::Error> { Ok(()) } - fn transfer_out(_domain_id: DomainId, _amount: Balance) -> Result<(), Self::Error> { + fn reduce_domain_balance(_domain_id: DomainId, _amount: Balance) -> Result<(), Self::Error> { Ok(()) } } diff --git a/crates/sp-domains/src/lib.rs b/crates/sp-domains/src/lib.rs index c7289d4b5e..619e25b62a 100644 --- a/crates/sp-domains/src/lib.rs +++ b/crates/sp-domains/src/lib.rs @@ -233,6 +233,14 @@ impl ChainId { ChainId::Domain(_) => false, } } + + #[inline] + pub fn maybe_domain_chain(&self) -> Option { + match self { + ChainId::Consensus => None, + ChainId::Domain(domain_id) => Some(*domain_id), + } + } } impl From for ChainId { @@ -280,9 +288,11 @@ where #[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone, Default)] pub struct Transfers { /// Total transfers that came into the domain. - pub transfers_in: Balance, + pub transfers_in: BTreeMap, /// Total transfers that went out of the domain. - pub transfers_out: Balance, + pub transfers_out: BTreeMap, + /// Total transfers from this domain that were reverted. + pub transfers_reverted: BTreeMap, } #[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone)] @@ -1100,14 +1110,33 @@ impl ExtrinsicDigest { pub trait DomainsTransfersTracker { type Error; - /// Marks transfer into domain. - fn transfer_in(domain_id: DomainId, amount: Balance) -> Result<(), Self::Error>; - - /// Marks a transfer from domain. - fn transfer_out(domain_id: DomainId, amount: Balance) -> Result<(), Self::Error>; - - /// Returns the total balance on domain. - fn balance_on_domain(domain_id: DomainId) -> Result; + /// Initializes the domain balance + fn initialize_domain_balance(domain_id: DomainId, amount: Balance) -> Result<(), Self::Error>; + + /// Notes a transfer between chains. + /// Balance on from_chain_id is reduced if it is a domain chain + fn note_transfer( + from_chain_id: ChainId, + to_chain_id: ChainId, + amount: Balance, + ) -> Result<(), Self::Error>; + + /// Confirms a transfer between chains. + fn confirm_transfer( + from_chain_id: ChainId, + to_chain_id: ChainId, + amount: Balance, + ) -> Result<(), Self::Error>; + + /// Cancels an initiated transfer between chains. + fn cancel_transfer( + from_chain_id: ChainId, + to_chain_id: ChainId, + amount: Balance, + ) -> Result<(), Self::Error>; + + /// Reduces a given amount from the domain balance + fn reduce_domain_balance(domain_id: DomainId, amount: Balance) -> Result<(), Self::Error>; } pub type ExecutionReceiptFor = ExecutionReceipt< diff --git a/domains/client/domain-operator/src/tests.rs b/domains/client/domain-operator/src/tests.rs index d37b1e8858..11ce551e81 100644 --- a/domains/client/domain-operator/src/tests.rs +++ b/domains/client/domain-operator/src/tests.rs @@ -27,8 +27,8 @@ use sp_domain_digests::AsPredigest; use sp_domains::core_api::DomainCoreApi; use sp_domains::merkle_tree::MerkleTree; use sp_domains::{ - Bundle, BundleValidity, DomainsApi, HeaderHashingFor, InboxedBundle, InvalidBundleType, - Transfers, + Bundle, BundleValidity, ChainId, DomainsApi, HeaderHashingFor, InboxedBundle, + InvalidBundleType, Transfers, }; use sp_domains_fraud_proof::fraud_proof::{ ApplyExtrinsicMismatch, ExecutionPhase, FinalizeBlockMismatch, FraudProof, @@ -41,6 +41,7 @@ use sp_runtime::traits::{BlakeTwo256, Block as BlockT, Hash as HashT, Header as use sp_runtime::transaction_validity::InvalidTransaction; use sp_runtime::OpaqueExtrinsic; use sp_state_machine::backend::AsTrieBackend; +use std::collections::BTreeMap; use std::sync::Arc; use subspace_core_primitives::PotOutput; use subspace_runtime_primitives::opaque::Block as CBlock; @@ -2017,8 +2018,9 @@ async fn test_invalid_transfers_fraud_proof() { let mut opaque_bundle = bundle.unwrap(); let receipt = &mut opaque_bundle.sealed_header.header.receipt; receipt.transfers = Transfers { - transfers_in: 10 * SSC, - transfers_out: 20 * SSC, + transfers_in: BTreeMap::from([(ChainId::Consensus, 10 * SSC)]), + transfers_out: BTreeMap::from([(ChainId::Consensus, 10 * SSC)]), + transfers_reverted: Default::default(), }; opaque_bundle.sealed_header.signature = Sr25519Keyring::Alice .pair() diff --git a/domains/pallets/transporter/src/lib.rs b/domains/pallets/transporter/src/lib.rs index 9794019e30..305937a51d 100644 --- a/domains/pallets/transporter/src/lib.rs +++ b/domains/pallets/transporter/src/lib.rs @@ -21,11 +21,12 @@ use codec::{Decode, Encode}; use domain_runtime_primitives::{MultiAccountId, TryConvertBack}; +use frame_support::dispatch::DispatchResult; use frame_support::ensure; use frame_support::traits::Currency; pub use pallet::*; use scale_info::TypeInfo; -use sp_domains::DomainId; +use sp_domains::{DomainId, Transfers}; use sp_messenger::messages::ChainId; use sp_runtime::traits::{CheckedAdd, CheckedSub, Get}; @@ -76,13 +77,13 @@ mod pallet { use frame_support::traits::{Currency, ExistenceRequirement, WithdrawReasons}; use frame_support::weights::Weight; use frame_system::pallet_prelude::*; - use sp_domains::{DomainId, Transfers}; + use sp_domains::{DomainId, DomainsTransfersTracker, Transfers}; use sp_messenger::endpoint::{ Endpoint, EndpointHandler as EndpointHandlerT, EndpointId, EndpointRequest, EndpointResponse, Sender, }; use sp_messenger::messages::ChainId; - use sp_runtime::traits::{CheckedAdd, Convert}; + use sp_runtime::traits::Convert; use sp_std::vec; use sp_std::vec::Vec; @@ -141,6 +142,12 @@ mod pallet { pub(super) type ChainTransfers = StorageValue<_, Transfers>, ValueQuery>; + /// Storage to track unconfirmed transfers between different chains. + #[pallet::storage] + #[pallet::getter(fn unconfirmed_transfers)] + pub(super) type UnconfirmedTransfers = + StorageDoubleMap<_, Identity, ChainId, Identity, ChainId, BalanceOf, ValueQuery>; + /// Events emitted by pallet-transporter. #[pallet::event] #[pallet::generate_deposit(pub (super) fn deposit_event)] @@ -201,6 +208,10 @@ mod pallet { NonConsensusChain, /// Emits when balance overflow BalanceOverflow, + /// Emits when balance underflow + BalanceUnderflow, + /// Emits when domain balance is already initialized + DomainBalanceAlreadyInitialized, } #[pallet::call] @@ -254,13 +265,15 @@ mod pallet { message_id, }); - ChainTransfers::::try_mutate(|transfers| { - transfers.transfers_out = transfers - .transfers_out - .checked_add(&amount) - .ok_or(Error::::BalanceOverflow)?; - Ok::<(), Error>(()) - })?; + // if this is consensus chain, then note the transfer + // else add transfer to storage to send through ER to consensus chain + if T::SelfChainId::get().is_consensus_chain() { + Self::note_transfer(T::SelfChainId::get(), dst_chain_id, amount)? + } else { + ChainTransfers::::try_mutate(|transfers| { + Self::update_transfer_out(transfers, dst_chain_id, amount) + })?; + } Ok(()) } @@ -316,13 +329,15 @@ mod pallet { let _imbalance = T::Currency::deposit_creating(&account_id, req.amount); - ChainTransfers::::try_mutate(|transfers| { - transfers.transfers_in = transfers - .transfers_in - .checked_add(&req.amount) - .ok_or(Error::::BalanceOverflow)?; - Ok::<(), Error>(()) - })?; + // if this is consensus chain, then confirm the transfer + // else add transfer to storage to send through ER to consensus chain + if T::SelfChainId::get().is_consensus_chain() { + Pallet::::confirm_transfer(src_chain_id, T::SelfChainId::get(), req.amount)? + } else { + ChainTransfers::::try_mutate(|transfers| { + Pallet::::update_transfer_in(transfers, src_chain_id, req.amount) + })?; + } frame_system::Pallet::::deposit_event(Into::<::RuntimeEvent>::into( Event::::IncomingTransferSuccessful { @@ -373,13 +388,23 @@ mod pallet { .ok_or(Error::::InvalidAccountId)?; let _imbalance = T::Currency::deposit_creating(&account_id, transfer.amount); - ChainTransfers::::try_mutate(|transfers| { - transfers.transfers_in = transfers - .transfers_in - .checked_add(&transfer.amount) - .ok_or(Error::::BalanceOverflow)?; - Ok::<(), Error>(()) - })?; + // if this is consensus chain, then revert the transfer + // else update the Transfers storage with reverted transfer + if T::SelfChainId::get().is_consensus_chain() { + Pallet::::cancel_transfer( + T::SelfChainId::get(), + dst_chain_id, + transfer.amount, + )?; + } else { + ChainTransfers::::try_mutate(|transfers| { + Pallet::::update_transfer_revert( + transfers, + dst_chain_id, + transfer.amount, + ) + })?; + } frame_system::Pallet::::deposit_event( Into::<::RuntimeEvent>::into( @@ -405,35 +430,97 @@ mod pallet { impl sp_domains::DomainsTransfersTracker> for Pallet { type Error = Error; - fn balance_on_domain(domain_id: DomainId) -> Result, Self::Error> { + fn initialize_domain_balance( + domain_id: DomainId, + amount: BalanceOf, + ) -> Result<(), Self::Error> { + Self::ensure_consensus_chain()?; + ensure!( - T::SelfChainId::get().is_consensus_chain(), - Error::NonConsensusChain + !DomainBalances::::contains_key(domain_id), + Error::DomainBalanceAlreadyInitialized ); - Ok(DomainBalances::::get(domain_id)) + DomainBalances::::set(domain_id, amount); + Ok(()) } - fn transfer_in(domain_id: DomainId, amount: BalanceOf) -> Result<(), Self::Error> { - ensure!( - T::SelfChainId::get().is_consensus_chain(), - Error::NonConsensusChain - ); + fn note_transfer( + from_chain_id: ChainId, + to_chain_id: ChainId, + amount: BalanceOf, + ) -> Result<(), Self::Error> { + Self::ensure_consensus_chain()?; + + if let Some(domain_id) = from_chain_id.maybe_domain_chain() { + DomainBalances::::try_mutate(domain_id, |current_balance| { + *current_balance = current_balance + .checked_sub(&amount) + .ok_or(Error::LowBalanceOnDomain)?; + Ok(()) + })?; + } - DomainBalances::::try_mutate(domain_id, |current_balance| { - *current_balance = current_balance + UnconfirmedTransfers::::try_mutate(from_chain_id, to_chain_id, |total_amount| { + *total_amount = total_amount .checked_add(&amount) .ok_or(Error::BalanceOverflow)?; Ok(()) - }) + })?; + + Ok(()) } - fn transfer_out(domain_id: DomainId, amount: BalanceOf) -> Result<(), Self::Error> { - ensure!( - T::SelfChainId::get().is_consensus_chain(), - Error::NonConsensusChain - ); + fn confirm_transfer( + from_chain_id: ChainId, + to_chain_id: ChainId, + amount: BalanceOf, + ) -> Result<(), Self::Error> { + Self::ensure_consensus_chain()?; + UnconfirmedTransfers::::try_mutate(from_chain_id, to_chain_id, |total_amount| { + *total_amount = total_amount + .checked_sub(&amount) + .ok_or(Error::BalanceUnderflow)?; + Ok(()) + })?; + + if let Some(domain_id) = to_chain_id.maybe_domain_chain() { + DomainBalances::::try_mutate(domain_id, |current_balance| { + *current_balance = current_balance + .checked_add(&amount) + .ok_or(Error::BalanceOverflow)?; + Ok(()) + })?; + } + Ok(()) + } + + fn cancel_transfer( + from_chain_id: ChainId, + to_chain_id: ChainId, + amount: BalanceOf, + ) -> Result<(), Self::Error> { + Self::ensure_consensus_chain()?; + UnconfirmedTransfers::::try_mutate(from_chain_id, to_chain_id, |total_amount| { + *total_amount = total_amount + .checked_sub(&amount) + .ok_or(Error::BalanceUnderflow)?; + Ok(()) + })?; + + if let Some(domain_id) = from_chain_id.maybe_domain_chain() { + DomainBalances::::try_mutate(domain_id, |current_balance| { + *current_balance = current_balance + .checked_add(&amount) + .ok_or(Error::BalanceOverflow)?; + Ok(()) + })?; + } + Ok(()) + } + + fn reduce_domain_balance(domain_id: DomainId, amount: BalanceOf) -> Result<(), Self::Error> { DomainBalances::::try_mutate(domain_id, |current_balance| { *current_balance = current_balance .checked_sub(&amount) @@ -442,3 +529,67 @@ impl sp_domains::DomainsTransfersTracker> for Pallet }) } } + +impl Pallet { + fn ensure_consensus_chain() -> Result<(), Error> { + ensure!( + T::SelfChainId::get().is_consensus_chain(), + Error::NonConsensusChain + ); + + Ok(()) + } + + fn update_transfer_out( + transfers: &mut Transfers>, + to_chain_id: ChainId, + amount: BalanceOf, + ) -> DispatchResult { + let total_transfer = + if let Some(current_transfer_amount) = transfers.transfers_out.get(&to_chain_id) { + current_transfer_amount + .checked_add(&amount) + .ok_or(Error::::BalanceOverflow)? + } else { + amount + }; + transfers.transfers_out.insert(to_chain_id, total_transfer); + Ok(()) + } + + fn update_transfer_in( + transfers: &mut Transfers>, + from_chain_id: ChainId, + amount: BalanceOf, + ) -> DispatchResult { + let total_transfer = + if let Some(current_transfer_amount) = transfers.transfers_in.get(&from_chain_id) { + current_transfer_amount + .checked_add(&amount) + .ok_or(Error::::BalanceOverflow)? + } else { + amount + }; + transfers.transfers_in.insert(from_chain_id, total_transfer); + Ok(()) + } + + fn update_transfer_revert( + transfers: &mut Transfers>, + to_chain_id: ChainId, + amount: BalanceOf, + ) -> DispatchResult { + let total_transfer = + if let Some(current_transfer_amount) = transfers.transfers_reverted.get(&to_chain_id) { + current_transfer_amount + .checked_add(&amount) + .ok_or(Error::::BalanceOverflow)? + } else { + amount + }; + transfers + .transfers_reverted + .insert(to_chain_id, total_transfer); + Ok(()) + } +} From 53539ebd5d08811237e056eb0b4f1e30252d1fa0 Mon Sep 17 00:00:00 2001 From: vedhavyas Date: Thu, 15 Feb 2024 13:59:19 +0530 Subject: [PATCH 09/13] handle burned/dusted balance on domains and note the same on domains --- crates/pallet-domains/src/tests.rs | 1 + crates/sp-domains/src/lib.rs | 10 +++++++++- .../src/malicious_bundle_tamper.rs | 3 ++- domains/pallets/block-fees/src/lib.rs | 8 ++++++++ domains/runtime/evm/src/lib.rs | 13 ++++++++++++- 5 files changed, 32 insertions(+), 3 deletions(-) diff --git a/crates/pallet-domains/src/tests.rs b/crates/pallet-domains/src/tests.rs index 32d3aef790..af1457030a 100644 --- a/crates/pallet-domains/src/tests.rs +++ b/crates/pallet-domains/src/tests.rs @@ -918,6 +918,7 @@ fn test_invalid_block_fees_fraud_proof() { .block_fees .consensus_storage_fee + 1, + domain_block.execution_receipt.block_fees.burned_balance + 1, ), ); domain_block.execution_receipt.final_state_root = root; diff --git a/crates/sp-domains/src/lib.rs b/crates/sp-domains/src/lib.rs index 619e25b62a..7dbfcf0e31 100644 --- a/crates/sp-domains/src/lib.rs +++ b/crates/sp-domains/src/lib.rs @@ -264,16 +264,23 @@ pub struct BlockFees { /// The domain execution fee including the storage and compute fee on domain chain, /// tip, and the XDM reward. pub domain_execution_fee: Balance, + /// Burned balances on domain chain + pub burned_balance: Balance, } impl BlockFees where Balance: CheckedAdd, { - pub fn new(domain_execution_fee: Balance, consensus_storage_fee: Balance) -> Self { + pub fn new( + domain_execution_fee: Balance, + consensus_storage_fee: Balance, + burned_balance: Balance, + ) -> Self { BlockFees { consensus_storage_fee, domain_execution_fee, + burned_balance, } } @@ -281,6 +288,7 @@ where pub fn total_fees(&self) -> Option { self.consensus_storage_fee .checked_add(&self.domain_execution_fee) + .and_then(|balance| balance.checked_add(&self.burned_balance)) } } diff --git a/crates/subspace-malicious-operator/src/malicious_bundle_tamper.rs b/crates/subspace-malicious-operator/src/malicious_bundle_tamper.rs index 81cb025460..0ba3f11ffb 100644 --- a/crates/subspace-malicious-operator/src/malicious_bundle_tamper.rs +++ b/crates/subspace-malicious-operator/src/malicious_bundle_tamper.rs @@ -125,7 +125,8 @@ where match bad_receipt_type { BadReceiptType::BlockFees => { - receipt.block_fees = BlockFees::new(random_seed.into(), random_seed.into()); + receipt.block_fees = + BlockFees::new(random_seed.into(), random_seed.into(), random_seed.into()); } // TODO: modify the length of `execution_trace` once the honest operator can handle BadReceiptType::ExecutionTrace => { diff --git a/domains/pallets/block-fees/src/lib.rs b/domains/pallets/block-fees/src/lib.rs index 99370eb1d2..624b03452a 100644 --- a/domains/pallets/block-fees/src/lib.rs +++ b/domains/pallets/block-fees/src/lib.rs @@ -177,6 +177,14 @@ mod pallet { }); } + /// Note burned balance on domains + pub fn note_burned_balance(burned_balance: T::Balance) { + CollectedBlockFees::::mutate(|block_fees| { + block_fees.burned_balance = + block_fees.burned_balance.saturating_add(burned_balance); + }); + } + /// Return the final domain transaction byte fee, which consist of: /// - The `ConsensusChainByteFee` for the consensus chain storage cost since the domain /// transaction need to be bundled and submitted to the consensus chain first. diff --git a/domains/runtime/evm/src/lib.rs b/domains/runtime/evm/src/lib.rs index f22796d1a6..11c2593f04 100644 --- a/domains/runtime/evm/src/lib.rs +++ b/domains/runtime/evm/src/lib.rs @@ -23,8 +23,10 @@ use fp_account::EthereumSignature; use fp_self_contained::{CheckedSignature, SelfContainedCall}; use frame_support::dispatch::{DispatchClass, DispatchInfo, GetDispatchInfo}; use frame_support::inherent::ProvideInherent; +use frame_support::traits::fungible::Credit; use frame_support::traits::{ ConstU16, ConstU32, ConstU64, Currency, Everything, FindAuthor, Imbalance, OnFinalize, + OnUnbalanced, }; use frame_support::weights::constants::{ParityDbWeight, WEIGHT_REF_TIME_PER_SECOND}; use frame_support::weights::{ConstantMultiplier, IdentityFee, Weight}; @@ -295,6 +297,15 @@ parameter_types! { pub const MaxReserves: u32 = 50; } +/// `DustRemovalHandler` used to collect all the SSC dust left when the account is reaped. +pub struct DustRemovalHandler; + +impl OnUnbalanced> for DustRemovalHandler { + fn on_nonzero_unbalanced(dusted_amount: Credit) { + BlockFees::note_burned_balance(dusted_amount.peek()); + } +} + impl pallet_balances::Config for Runtime { type RuntimeFreezeReason = RuntimeFreezeReason; type MaxLocks = MaxLocks; @@ -302,7 +313,7 @@ impl pallet_balances::Config for Runtime { type Balance = Balance; /// The ubiquitous event type. type RuntimeEvent = RuntimeEvent; - type DustRemoval = (); + type DustRemoval = DustRemovalHandler; type ExistentialDeposit = ExistentialDeposit; type AccountStore = System; type WeightInfo = pallet_balances::weights::SubstrateWeight; From 7ff8ff4c4a14e47a5e145164d97f507314c2a4d0 Mon Sep 17 00:00:00 2001 From: vedhavyas Date: Thu, 15 Feb 2024 14:22:49 +0530 Subject: [PATCH 10/13] handle max initial accounts and min initial account balance for domain --- crates/pallet-domains/src/lib.rs | 6 +++ crates/pallet-domains/src/runtime_registry.rs | 54 ++++++++++++------- crates/pallet-domains/src/tests.rs | 4 ++ crates/subspace-node/src/chain_spec.rs | 13 ++++- crates/subspace-runtime/src/lib.rs | 4 ++ test/subspace-test-runtime/src/lib.rs | 4 ++ 6 files changed, 64 insertions(+), 21 deletions(-) diff --git a/crates/pallet-domains/src/lib.rs b/crates/pallet-domains/src/lib.rs index 470e63b488..ce702f2274 100644 --- a/crates/pallet-domains/src/lib.rs +++ b/crates/pallet-domains/src/lib.rs @@ -324,6 +324,12 @@ mod pallet { /// Transfers tracker. type DomainsTransfersTracker: DomainsTransfersTracker>; + + /// Upper limit for total initial accounts domains + type MaxInitialDomainAccounts: Get; + + /// Minimum balance for each initial domain account + type MinInitialDomainAccountBalance: Get>; } #[pallet::pallet] diff --git a/crates/pallet-domains/src/runtime_registry.rs b/crates/pallet-domains/src/runtime_registry.rs index 49dbce5c9a..17ebb5d61b 100644 --- a/crates/pallet-domains/src/runtime_registry.rs +++ b/crates/pallet-domains/src/runtime_registry.rs @@ -5,7 +5,7 @@ use crate::{BalanceOf, Config, Event}; use alloc::string::String; use codec::{Decode, Encode}; use domain_runtime_primitives::{AccountId20, EVMChainId, MultiAccountId, TryConvertBack}; -use frame_support::PalletError; +use frame_support::{ensure, PalletError}; use frame_system::pallet_prelude::*; use frame_system::AccountInfo; use scale_info::TypeInfo; @@ -32,6 +32,9 @@ pub enum Error { FailedToDecodeRawGenesis, RuntimeCodeNotFoundInRawGenesis, InvalidAccountIdType, + MaxInitialDomainAccounts, + MinInitialAccountBalance, + BalanceOverflow, } #[derive(TypeInfo, Debug, Encode, Decode, Clone, PartialEq, Eq)] @@ -101,25 +104,36 @@ impl RuntimeObject { match domain_runtime_info { DomainRuntimeInfo::EVM { chain_id } => { raw_genesis.set_evm_chain_id(chain_id); - let initial_balances = initial_balances - .into_iter() - .try_fold( - BTreeMap::>::new(), - |mut acc, (account_id, balance)| { - let account_id = - domain_runtime_primitives::AccountId20Converter::try_convert_back( - account_id, - )?; - let balance = if let Some(previous_balance) = acc.get(&account_id) { - previous_balance.checked_add(&balance)? - } else { - balance - }; - acc.insert(account_id, balance); - Some(acc) - }, - ) - .ok_or(Error::InvalidAccountIdType)?; + let initial_balances = initial_balances.into_iter().try_fold( + BTreeMap::>::new(), + |mut acc, (account_id, balance)| { + let account_id = + domain_runtime_primitives::AccountId20Converter::try_convert_back( + account_id, + ) + .ok_or(Error::InvalidAccountIdType)?; + + let balance = if let Some(previous_balance) = acc.get(&account_id) { + previous_balance + .checked_add(&balance) + .ok_or(Error::BalanceOverflow)? + } else { + balance + }; + + ensure!( + balance >= T::MinInitialDomainAccountBalance::get(), + Error::MinInitialAccountBalance + ); + + acc.insert(account_id, balance); + Ok(acc) + }, + )?; + ensure!( + initial_balances.len() as u32 <= T::MaxInitialDomainAccounts::get(), + Error::MaxInitialDomainAccounts + ); raw_genesis.set_top_storages(derive_initial_balances_storages::( total_issuance, initial_balances, diff --git a/crates/pallet-domains/src/tests.rs b/crates/pallet-domains/src/tests.rs index af1457030a..2900668585 100644 --- a/crates/pallet-domains/src/tests.rs +++ b/crates/pallet-domains/src/tests.rs @@ -184,6 +184,8 @@ parameter_types! { pub const MaxNominators: u32 = 5; pub const DomainsPalletId: PalletId = PalletId(*b"domains_"); pub const DomainChainByteFee: Balance = 1; + pub const MaxInitialDomainAccounts: u32 = 10; + pub const MinInitialDomainAccountBalance: Balance = SSC; } pub struct MockRandomness; @@ -297,6 +299,8 @@ impl pallet_domains::Config for Test { type StorageFee = DummyStorageFee; type BlockSlot = DummyBlockSlot; type DomainsTransfersTracker = MockDomainsTransfersTracker; + type MaxInitialDomainAccounts = MaxInitialDomainAccounts; + type MinInitialDomainAccountBalance = MinInitialDomainAccountBalance; } pub struct ExtrinsicStorageFees; diff --git a/crates/subspace-node/src/chain_spec.rs b/crates/subspace-node/src/chain_spec.rs index 676c3af5e9..274ebc4d0c 100644 --- a/crates/subspace-node/src/chain_spec.rs +++ b/crates/subspace-node/src/chain_spec.rs @@ -20,6 +20,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 domain_runtime_primitives::MultiAccountId; use hex_literal::hex; use parity_scale_codec::Encode; use sc_chain_spec::GenericChainSpec; @@ -107,6 +108,7 @@ struct GenesisDomainParams { domain_name: String, operator_allow_list: OperatorAllowList, operator_signing_key: OperatorPublicKey, + initial_balances: Vec<(MultiAccountId, Balance)>, } pub fn gemini_3h_compiled() -> Result, String> { @@ -189,6 +191,9 @@ pub fn gemini_3h_compiled() -> Result, St operator_signing_key: OperatorPublicKey::unchecked_from(hex!( "aa3b05b4d649666723e099cf3bafc2f2c04160ebe0e16ddc82f72d6ed97c4b6b" )), + initial_balances: evm_chain_spec::get_testnet_endowed_accounts_by_spec_id( + SpecId::Gemini, + ), }, ) }, @@ -298,6 +303,9 @@ pub fn devnet_config_compiled() -> Result operator_signing_key: OperatorPublicKey::unchecked_from(hex!( "aa3b05b4d649666723e099cf3bafc2f2c04160ebe0e16ddc82f72d6ed97c4b6b" )), + initial_balances: evm_chain_spec::get_testnet_endowed_accounts_by_spec_id( + SpecId::DevNet, + ), }, ) }, @@ -365,6 +373,9 @@ pub fn dev_config() -> Result, String> { domain_name: "evm-domain".to_owned(), operator_allow_list: OperatorAllowList::Anyone, operator_signing_key: get_public_key_from_seed::("Alice"), + initial_balances: evm_chain_spec::get_testnet_endowed_accounts_by_spec_id( + SpecId::Dev, + ), }, ) }, @@ -471,7 +482,7 @@ fn subspace_genesis_config( signing_key: genesis_domain_params.operator_signing_key, nomination_tax: Percent::from_percent(5), minimum_nominator_stake: 100 * SSC, - initial_balances: evm_chain_spec::get_testnet_endowed_accounts_by_spec_id(spec_id), + initial_balances: genesis_domain_params.initial_balances, }), }, } diff --git a/crates/subspace-runtime/src/lib.rs b/crates/subspace-runtime/src/lib.rs index 98dacbc838..63560d540c 100644 --- a/crates/subspace-runtime/src/lib.rs +++ b/crates/subspace-runtime/src/lib.rs @@ -567,6 +567,8 @@ parameter_types! { pub const MaxNominators: u32 = 256; pub SudoId: AccountId = Sudo::key().expect("Sudo account must exist"); pub const DomainsPalletId: PalletId = PalletId(*b"domains_"); + pub const MaxInitialDomainAccounts: u32 = 10; + pub const MinInitialDomainAccountBalance: Balance = SSC; } // Minimum operator stake must be >= minimum nominator stake since operator is also a nominator. @@ -618,6 +620,8 @@ impl pallet_domains::Config for Runtime { type StorageFee = TransactionFees; type BlockSlot = BlockSlot; type DomainsTransfersTracker = Transporter; + type MaxInitialDomainAccounts = MaxInitialDomainAccounts; + type MinInitialDomainAccountBalance = MinInitialDomainAccountBalance; } parameter_types! { diff --git a/test/subspace-test-runtime/src/lib.rs b/test/subspace-test-runtime/src/lib.rs index aa8bfbd04b..f11a24456a 100644 --- a/test/subspace-test-runtime/src/lib.rs +++ b/test/subspace-test-runtime/src/lib.rs @@ -606,6 +606,8 @@ parameter_types! { pub const MaxNominators: u32 = 100; pub SudoId: AccountId = Sudo::key().expect("Sudo account must exist"); pub const DomainsPalletId: PalletId = PalletId(*b"domains_"); + pub const MaxInitialDomainAccounts: u32 = 20; + pub const MinInitialDomainAccountBalance: Balance = SSC; } // Minimum operator stake must be >= minimum nominator stake since operator is also a nominator. @@ -654,6 +656,8 @@ impl pallet_domains::Config for Runtime { type StorageFee = TransactionFees; type BlockSlot = BlockSlot; type DomainsTransfersTracker = Transporter; + type MaxInitialDomainAccounts = MaxInitialDomainAccounts; + type MinInitialDomainAccountBalance = MinInitialDomainAccountBalance; } parameter_types! { From 9718be9783d5746d73cdada974eefea619eef84a Mon Sep 17 00:00:00 2001 From: Parth Desai Date: Thu, 15 Feb 2024 13:31:48 +0530 Subject: [PATCH 11/13] make submit_fraud_proof dispatch class Operational --- crates/pallet-domains/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/pallet-domains/src/lib.rs b/crates/pallet-domains/src/lib.rs index 25b8159dc3..4c94ac58e1 100644 --- a/crates/pallet-domains/src/lib.rs +++ b/crates/pallet-domains/src/lib.rs @@ -969,7 +969,7 @@ mod pallet { #[pallet::call_index(1)] // TODO: proper weight - #[pallet::weight((Weight::from_all(10_000), Pays::No))] + #[pallet::weight((Weight::from_all(10_000), DispatchClass::Operational, Pays::No))] pub fn submit_fraud_proof( origin: OriginFor, fraud_proof: Box, T::Hash, T::DomainHeader>>, From b5042c6f0466ef41670d585a91a45d3e924bda9e Mon Sep 17 00:00:00 2001 From: vedhavyas Date: Thu, 15 Feb 2024 19:41:26 +0530 Subject: [PATCH 12/13] ensure transfers are rejected by dst_domains before src_domain claims them back --- crates/pallet-domains/src/block_tree.rs | 24 +++- crates/pallet-domains/src/tests.rs | 10 +- crates/sp-domains/src/lib.rs | 24 +++- domains/client/domain-operator/src/tests.rs | 3 +- domains/pallets/transporter/src/lib.rs | 140 +++++++++++++++----- 5 files changed, 158 insertions(+), 43 deletions(-) diff --git a/crates/pallet-domains/src/block_tree.rs b/crates/pallet-domains/src/block_tree.rs index 1d918de68c..807c4c46e4 100644 --- a/crates/pallet-domains/src/block_tree.rs +++ b/crates/pallet-domains/src/block_tree.rs @@ -39,6 +39,7 @@ pub enum Error { InvalidStateRoot, BalanceOverflow, DomainTransfersTracking, + InvalidDomainTransfers, } #[derive(TypeInfo, Debug, Encode, Decode, Clone, PartialEq, Eq)] @@ -354,6 +355,13 @@ pub(crate) fn process_execution_receipt( .total_fees() .ok_or(Error::BalanceOverflow)?; + ensure!( + execution_receipt + .transfers + .is_valid(ChainId::Domain(domain_id)), + Error::BalanceOverflow + ); + update_domain_transfers::(domain_id, &execution_receipt.transfers, block_fees) .map_err(|_| Error::DomainTransfersTracking)?; @@ -409,7 +417,8 @@ fn update_domain_transfers( let Transfers { transfers_in, transfers_out, - transfers_reverted, + transfers_rejected, + rejected_transfers_claimed, } = transfers; // confirm incoming transfers @@ -425,11 +434,18 @@ fn update_domain_transfers( T::DomainsTransfersTracker::note_transfer(er_chain_id, *to_chain_id, *amount) })?; - // cancel existing transfers - transfers_reverted + // note rejected transfers + transfers_rejected + .iter() + .try_for_each(|(from_chain_id, amount)| { + T::DomainsTransfersTracker::reject_transfer(*from_chain_id, er_chain_id, *amount) + })?; + + // claim rejected transfers + rejected_transfers_claimed .iter() .try_for_each(|(to_chain_id, amount)| { - T::DomainsTransfersTracker::cancel_transfer(er_chain_id, *to_chain_id, *amount) + T::DomainsTransfersTracker::claim_rejected_transfer(er_chain_id, *to_chain_id, *amount) })?; // deduct execution fees from domain diff --git a/crates/pallet-domains/src/tests.rs b/crates/pallet-domains/src/tests.rs index 2900668585..e1fc6ccee3 100644 --- a/crates/pallet-domains/src/tests.rs +++ b/crates/pallet-domains/src/tests.rs @@ -255,7 +255,15 @@ impl sp_domains::DomainsTransfersTracker for MockDomainsTransfersTracke Ok(()) } - fn cancel_transfer( + fn claim_rejected_transfer( + _from_chain_id: ChainId, + _to_chain_id: ChainId, + _amount: Balance, + ) -> Result<(), Self::Error> { + Ok(()) + } + + fn reject_transfer( _from_chain_id: ChainId, _to_chain_id: ChainId, _amount: Balance, diff --git a/crates/sp-domains/src/lib.rs b/crates/sp-domains/src/lib.rs index 7dbfcf0e31..af1da34d1a 100644 --- a/crates/sp-domains/src/lib.rs +++ b/crates/sp-domains/src/lib.rs @@ -300,7 +300,18 @@ pub struct Transfers { /// Total transfers that went out of the domain. pub transfers_out: BTreeMap, /// Total transfers from this domain that were reverted. - pub transfers_reverted: BTreeMap, + pub rejected_transfers_claimed: BTreeMap, + /// Total transfers to this domain that were rejected. + pub transfers_rejected: BTreeMap, +} + +impl Transfers { + pub fn is_valid(&self, chain_id: ChainId) -> bool { + !self.transfers_rejected.contains_key(&chain_id) + && !self.transfers_in.contains_key(&chain_id) + && !self.transfers_out.contains_key(&chain_id) + && !self.rejected_transfers_claimed.contains_key(&chain_id) + } } #[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone)] @@ -1136,8 +1147,15 @@ pub trait DomainsTransfersTracker { amount: Balance, ) -> Result<(), Self::Error>; - /// Cancels an initiated transfer between chains. - fn cancel_transfer( + /// Claims a rejected transfer between chains. + fn claim_rejected_transfer( + from_chain_id: ChainId, + to_chain_id: ChainId, + amount: Balance, + ) -> Result<(), Self::Error>; + + /// Rejects a initiated transfer between chains. + fn reject_transfer( from_chain_id: ChainId, to_chain_id: ChainId, amount: Balance, diff --git a/domains/client/domain-operator/src/tests.rs b/domains/client/domain-operator/src/tests.rs index 11ce551e81..84a7efaba7 100644 --- a/domains/client/domain-operator/src/tests.rs +++ b/domains/client/domain-operator/src/tests.rs @@ -2020,7 +2020,8 @@ async fn test_invalid_transfers_fraud_proof() { receipt.transfers = Transfers { transfers_in: BTreeMap::from([(ChainId::Consensus, 10 * SSC)]), transfers_out: BTreeMap::from([(ChainId::Consensus, 10 * SSC)]), - transfers_reverted: Default::default(), + rejected_transfers_claimed: Default::default(), + transfers_rejected: Default::default(), }; opaque_bundle.sealed_header.signature = Sr25519Keyring::Alice .pair() diff --git a/domains/pallets/transporter/src/lib.rs b/domains/pallets/transporter/src/lib.rs index 305937a51d..6563254dae 100644 --- a/domains/pallets/transporter/src/lib.rs +++ b/domains/pallets/transporter/src/lib.rs @@ -26,7 +26,8 @@ use frame_support::ensure; use frame_support::traits::Currency; pub use pallet::*; use scale_info::TypeInfo; -use sp_domains::{DomainId, Transfers}; +use sp_domains::{DomainId, DomainsTransfersTracker, Transfers}; +use sp_messenger::endpoint::EndpointResponse; use sp_messenger::messages::ChainId; use sp_runtime::traits::{CheckedAdd, CheckedSub, Get}; @@ -148,6 +149,12 @@ mod pallet { pub(super) type UnconfirmedTransfers = StorageDoubleMap<_, Identity, ChainId, Identity, ChainId, BalanceOf, ValueQuery>; + /// Storage to track cancelled transfers between different chains. + #[pallet::storage] + #[pallet::getter(fn cancelled_transfers)] + pub(super) type CancelledTransfers = + StorageDoubleMap<_, Identity, ChainId, Identity, ChainId, BalanceOf, ValueQuery>; + /// Events emitted by pallet-transporter. #[pallet::event] #[pallet::generate_deposit(pub (super) fn deposit_event)] @@ -323,29 +330,21 @@ mod pallet { Err(_) => return Err(Error::::InvalidPayload.into()), }; - // mint the funds to dst_account - let account_id = T::AccountIdConverter::try_convert_back(req.receiver.account_id) - .ok_or(Error::::InvalidAccountId)?; - - let _imbalance = T::Currency::deposit_creating(&account_id, req.amount); - - // if this is consensus chain, then confirm the transfer - // else add transfer to storage to send through ER to consensus chain - if T::SelfChainId::get().is_consensus_chain() { - Pallet::::confirm_transfer(src_chain_id, T::SelfChainId::get(), req.amount)? - } else { - ChainTransfers::::try_mutate(|transfers| { - Pallet::::update_transfer_in(transfers, src_chain_id, req.amount) - })?; + let amount = req.amount; + let response = Pallet::::finalize_transfer(src_chain_id, message_id, req); + if response.is_err() { + // if this is consensus chain, then reject the transfer + // else update the Transfers storage with rejected transfer + if T::SelfChainId::get().is_consensus_chain() { + Pallet::::reject_transfer(src_chain_id, T::SelfChainId::get(), amount)?; + } else { + ChainTransfers::::try_mutate(|transfers| { + Pallet::::update_transfer_rejected(transfers, src_chain_id, amount) + })?; + } } - frame_system::Pallet::::deposit_event(Into::<::RuntimeEvent>::into( - Event::::IncomingTransferSuccessful { - chain_id: src_chain_id, - message_id, - }, - )); - Ok(vec![]) + response } fn message_weight(&self) -> Weight { @@ -391,7 +390,7 @@ mod pallet { // if this is consensus chain, then revert the transfer // else update the Transfers storage with reverted transfer if T::SelfChainId::get().is_consensus_chain() { - Pallet::::cancel_transfer( + Pallet::::claim_rejected_transfer( T::SelfChainId::get(), dst_chain_id, transfer.amount, @@ -496,13 +495,13 @@ impl sp_domains::DomainsTransfersTracker> for Pallet Ok(()) } - fn cancel_transfer( + fn claim_rejected_transfer( from_chain_id: ChainId, to_chain_id: ChainId, amount: BalanceOf, ) -> Result<(), Self::Error> { Self::ensure_consensus_chain()?; - UnconfirmedTransfers::::try_mutate(from_chain_id, to_chain_id, |total_amount| { + CancelledTransfers::::try_mutate(from_chain_id, to_chain_id, |total_amount| { *total_amount = total_amount .checked_sub(&amount) .ok_or(Error::BalanceUnderflow)?; @@ -520,6 +519,28 @@ impl sp_domains::DomainsTransfersTracker> for Pallet Ok(()) } + fn reject_transfer( + from_chain_id: ChainId, + to_chain_id: ChainId, + amount: BalanceOf, + ) -> Result<(), Self::Error> { + Self::ensure_consensus_chain()?; + UnconfirmedTransfers::::try_mutate(from_chain_id, to_chain_id, |total_amount| { + *total_amount = total_amount + .checked_sub(&amount) + .ok_or(Error::BalanceUnderflow)?; + Ok(()) + })?; + + CancelledTransfers::::try_mutate(from_chain_id, to_chain_id, |total_amount| { + *total_amount = total_amount + .checked_add(&amount) + .ok_or(Error::BalanceOverflow)?; + Ok(()) + })?; + Ok(()) + } + fn reduce_domain_balance(domain_id: DomainId, amount: BalanceOf) -> Result<(), Self::Error> { DomainBalances::::try_mutate(domain_id, |current_balance| { *current_balance = current_balance @@ -540,6 +561,36 @@ impl Pallet { Ok(()) } + fn finalize_transfer( + src_chain_id: ChainId, + message_id: MessageIdOf, + req: Transfer>, + ) -> EndpointResponse { + // mint the funds to dst_account + let account_id = T::AccountIdConverter::try_convert_back(req.receiver.account_id) + .ok_or(Error::::InvalidAccountId)?; + + let _imbalance = T::Currency::deposit_creating(&account_id, req.amount); + + // if this is consensus chain, then confirm the transfer + // else add transfer to storage to send through ER to consensus chain + if T::SelfChainId::get().is_consensus_chain() { + Pallet::::confirm_transfer(src_chain_id, T::SelfChainId::get(), req.amount)? + } else { + ChainTransfers::::try_mutate(|transfers| { + Pallet::::update_transfer_in(transfers, src_chain_id, req.amount) + })?; + } + + frame_system::Pallet::::deposit_event(Into::<::RuntimeEvent>::into( + Event::::IncomingTransferSuccessful { + chain_id: src_chain_id, + message_id, + }, + )); + Ok(vec![]) + } + fn update_transfer_out( transfers: &mut Transfers>, to_chain_id: ChainId, @@ -579,17 +630,38 @@ impl Pallet { to_chain_id: ChainId, amount: BalanceOf, ) -> DispatchResult { - let total_transfer = - if let Some(current_transfer_amount) = transfers.transfers_reverted.get(&to_chain_id) { - current_transfer_amount - .checked_add(&amount) - .ok_or(Error::::BalanceOverflow)? - } else { - amount - }; + let total_transfer = if let Some(current_transfer_amount) = + transfers.rejected_transfers_claimed.get(&to_chain_id) + { + current_transfer_amount + .checked_add(&amount) + .ok_or(Error::::BalanceOverflow)? + } else { + amount + }; transfers - .transfers_reverted + .rejected_transfers_claimed .insert(to_chain_id, total_transfer); Ok(()) } + + fn update_transfer_rejected( + transfers: &mut Transfers>, + from_chain_id: ChainId, + amount: BalanceOf, + ) -> DispatchResult { + let total_transfer = if let Some(current_transfer_amount) = + transfers.transfers_rejected.get(&from_chain_id) + { + current_transfer_amount + .checked_add(&amount) + .ok_or(Error::::BalanceOverflow)? + } else { + amount + }; + transfers + .transfers_rejected + .insert(from_chain_id, total_transfer); + Ok(()) + } } From 3ee42daf3eac93d145aaf25dd0e9c4f5b6598078 Mon Sep 17 00:00:00 2001 From: vedhavyas Date: Fri, 16 Feb 2024 13:15:20 +0530 Subject: [PATCH 13/13] check intital balances and reject if duplicates exisits --- crates/pallet-domains/src/block_tree.rs | 2 +- crates/pallet-domains/src/domain_registry.rs | 117 +++++++++++++++++- crates/pallet-domains/src/runtime_registry.rs | 33 +---- crates/pallet-domains/src/tests.rs | 2 +- domains/pallets/transporter/src/lib.rs | 1 + domains/primitives/runtime/src/lib.rs | 4 +- 6 files changed, 128 insertions(+), 31 deletions(-) diff --git a/crates/pallet-domains/src/block_tree.rs b/crates/pallet-domains/src/block_tree.rs index 807c4c46e4..cf3ce89977 100644 --- a/crates/pallet-domains/src/block_tree.rs +++ b/crates/pallet-domains/src/block_tree.rs @@ -359,7 +359,7 @@ pub(crate) fn process_execution_receipt( execution_receipt .transfers .is_valid(ChainId::Domain(domain_id)), - Error::BalanceOverflow + Error::InvalidDomainTransfers ); update_domain_transfers::(domain_id, &execution_receipt.transfers, block_fees) diff --git a/crates/pallet-domains/src/domain_registry.rs b/crates/pallet-domains/src/domain_registry.rs index 4b7b621808..ec57073b28 100644 --- a/crates/pallet-domains/src/domain_registry.rs +++ b/crates/pallet-domains/src/domain_registry.rs @@ -46,6 +46,9 @@ pub enum Error { NotDomainOwner, InitialBalanceOverflow, TransfersTracker, + MinInitialAccountBalance, + MaxInitialDomainAccounts, + DuplicateInitialAccounts, FailedToGenerateRawGenesis(crate::runtime_registry::Error), } @@ -73,7 +76,7 @@ pub struct DomainConfig { impl DomainConfig where AccountId: Ord, - Balance: Zero + CheckedAdd, + Balance: Zero + CheckedAdd + PartialOrd, { pub(crate) fn total_issuance(&self) -> Option { self.initial_balances @@ -82,6 +85,37 @@ where total.checked_add(balance) }) } + + pub(crate) fn check_initial_balances(&self) -> Result<(), Error> + where + Balance: From>, + { + let accounts: BTreeSet = self + .initial_balances + .iter() + .map(|(acc, _)| acc) + .cloned() + .collect(); + + ensure!( + accounts.len() == self.initial_balances.len(), + Error::DuplicateInitialAccounts + ); + + ensure!( + self.initial_balances.len() as u32 <= T::MaxInitialDomainAccounts::get(), + Error::MaxInitialDomainAccounts + ); + + for (_, balance) in &self.initial_balances { + ensure!( + *balance >= T::MinInitialDomainAccountBalance::get().into(), + Error::MinInitialAccountBalance + ); + } + + Ok(()) + } } #[derive(TypeInfo, Debug, Encode, Decode, Clone, PartialEq, Eq)] @@ -137,6 +171,8 @@ pub(crate) fn can_instantiate_domain( Error::InsufficientFund ); + domain_config.check_initial_balances::()?; + Ok(()) } @@ -488,6 +524,85 @@ mod tests { ) ); + // duplicate accounts + domain_config.initial_balances = vec![ + ( + AccountId20Converter::convert(AccountId20::from(hex!( + "f24FF3a9CF04c71Dbc94D0b566f7A27B94566cac" + ))), + 1_000_000 * SSC, + ), + ( + AccountId20Converter::convert(AccountId20::from(hex!( + "f24FF3a9CF04c71Dbc94D0b566f7A27B94566cac" + ))), + 1_000_000 * SSC, + ), + ]; + + assert_err!( + do_instantiate_domain::(domain_config.clone(), creator, created_at), + Error::DuplicateInitialAccounts + ); + + // max accounts + domain_config.initial_balances = vec![ + ( + AccountId20Converter::convert(AccountId20::from(hex!( + "f24FF3a9CF04c71Dbc94D0b566f7A27B94566cac" + ))), + 1_000_000 * SSC, + ), + ( + AccountId20Converter::convert(AccountId20::from(hex!( + "f24FF3a9CF04c71Dbc94D0b566f7A27B94566cbc" + ))), + 1_000_000 * SSC, + ), + ( + AccountId20Converter::convert(AccountId20::from(hex!( + "f24FF3a9CF04c71Dbc94D0b566f7A27B94566ccc" + ))), + 1_000_000 * SSC, + ), + ( + AccountId20Converter::convert(AccountId20::from(hex!( + "f24FF3a9CF04c71Dbc94D0b566f7A27B94566cdc" + ))), + 1_000_000 * SSC, + ), + ( + AccountId20Converter::convert(AccountId20::from(hex!( + "f24FF3a9CF04c71Dbc94D0b566f7A27B94566cec" + ))), + 1_000_000 * SSC, + ), + ( + AccountId20Converter::convert(AccountId20::from(hex!( + "f24FF3a9CF04c71Dbc94D0b566f7A27B94566cfc" + ))), + 1_000_000 * SSC, + ), + ]; + + assert_err!( + do_instantiate_domain::(domain_config.clone(), creator, created_at), + Error::MaxInitialDomainAccounts + ); + + // min balance accounts + domain_config.initial_balances = vec![( + AccountId20Converter::convert(AccountId20::from(hex!( + "f24FF3a9CF04c71Dbc94D0b566f7A27B94566cac" + ))), + 1, + )]; + + assert_err!( + do_instantiate_domain::(domain_config.clone(), creator, created_at), + Error::MinInitialAccountBalance + ); + domain_config.initial_balances = vec![( AccountId20Converter::convert(AccountId20::from(hex!( "f24FF3a9CF04c71Dbc94D0b566f7A27B94566cac" diff --git a/crates/pallet-domains/src/runtime_registry.rs b/crates/pallet-domains/src/runtime_registry.rs index 17ebb5d61b..8ed96b1ad5 100644 --- a/crates/pallet-domains/src/runtime_registry.rs +++ b/crates/pallet-domains/src/runtime_registry.rs @@ -5,7 +5,7 @@ use crate::{BalanceOf, Config, Event}; use alloc::string::String; use codec::{Decode, Encode}; use domain_runtime_primitives::{AccountId20, EVMChainId, MultiAccountId, TryConvertBack}; -use frame_support::{ensure, PalletError}; +use frame_support::PalletError; use frame_system::pallet_prelude::*; use frame_system::AccountInfo; use scale_info::TypeInfo; @@ -14,7 +14,6 @@ use sp_domains::storage::{RawGenesis, StorageData, StorageKey}; use sp_domains::{DomainId, DomainsDigestItem, RuntimeId, RuntimeType}; use sp_runtime::traits::{CheckedAdd, Get, Zero}; use sp_runtime::DigestItem; -use sp_std::collections::btree_map::BTreeMap; use sp_std::vec; use sp_std::vec::Vec; use sp_version::RuntimeVersion; @@ -32,9 +31,6 @@ pub enum Error { FailedToDecodeRawGenesis, RuntimeCodeNotFoundInRawGenesis, InvalidAccountIdType, - MaxInitialDomainAccounts, - MinInitialAccountBalance, - BalanceOverflow, } #[derive(TypeInfo, Debug, Encode, Decode, Clone, PartialEq, Eq)] @@ -65,7 +61,7 @@ impl Default for DomainRuntimeInfo { fn derive_initial_balances_storages( total_issuance: BalanceOf, - balances: BTreeMap>, + balances: Vec<(AccountId, BalanceOf)>, ) -> Vec<(StorageKey, StorageData)> { let total_issuance_key = sp_domains::domain_total_issuance_storage_key(); let mut initial_storages = vec![(total_issuance_key, StorageData(total_issuance.encode()))]; @@ -105,35 +101,18 @@ impl RuntimeObject { DomainRuntimeInfo::EVM { chain_id } => { raw_genesis.set_evm_chain_id(chain_id); let initial_balances = initial_balances.into_iter().try_fold( - BTreeMap::>::new(), - |mut acc, (account_id, balance)| { + Vec::<(AccountId20, BalanceOf)>::new(), + |mut balances, (account_id, balance)| { let account_id = domain_runtime_primitives::AccountId20Converter::try_convert_back( account_id, ) .ok_or(Error::InvalidAccountIdType)?; - let balance = if let Some(previous_balance) = acc.get(&account_id) { - previous_balance - .checked_add(&balance) - .ok_or(Error::BalanceOverflow)? - } else { - balance - }; - - ensure!( - balance >= T::MinInitialDomainAccountBalance::get(), - Error::MinInitialAccountBalance - ); - - acc.insert(account_id, balance); - Ok(acc) + balances.push((account_id, balance)); + Ok(balances) }, )?; - ensure!( - initial_balances.len() as u32 <= T::MaxInitialDomainAccounts::get(), - Error::MaxInitialDomainAccounts - ); raw_genesis.set_top_storages(derive_initial_balances_storages::( total_issuance, initial_balances, diff --git a/crates/pallet-domains/src/tests.rs b/crates/pallet-domains/src/tests.rs index e1fc6ccee3..fa1f0f0f1a 100644 --- a/crates/pallet-domains/src/tests.rs +++ b/crates/pallet-domains/src/tests.rs @@ -184,7 +184,7 @@ parameter_types! { pub const MaxNominators: u32 = 5; pub const DomainsPalletId: PalletId = PalletId(*b"domains_"); pub const DomainChainByteFee: Balance = 1; - pub const MaxInitialDomainAccounts: u32 = 10; + pub const MaxInitialDomainAccounts: u32 = 5; pub const MinInitialDomainAccountBalance: Balance = SSC; } diff --git a/domains/pallets/transporter/src/lib.rs b/domains/pallets/transporter/src/lib.rs index 6563254dae..dc94ac2bd3 100644 --- a/domains/pallets/transporter/src/lib.rs +++ b/domains/pallets/transporter/src/lib.rs @@ -30,6 +30,7 @@ use sp_domains::{DomainId, DomainsTransfersTracker, Transfers}; use sp_messenger::endpoint::EndpointResponse; use sp_messenger::messages::ChainId; use sp_runtime::traits::{CheckedAdd, CheckedSub, Get}; +use sp_std::vec; #[cfg(test)] mod mock; diff --git a/domains/primitives/runtime/src/lib.rs b/domains/primitives/runtime/src/lib.rs index 9b3d6db4fc..da5b016568 100644 --- a/domains/primitives/runtime/src/lib.rs +++ b/domains/primitives/runtime/src/lib.rs @@ -132,7 +132,9 @@ where } /// MultiAccountId used by all the domains to describe their account type. -#[derive(Debug, Encode, Decode, Clone, Eq, PartialEq, TypeInfo, Serialize, Deserialize)] +#[derive( + Debug, Encode, Decode, Clone, Eq, PartialEq, TypeInfo, Serialize, Deserialize, Ord, PartialOrd, +)] pub enum MultiAccountId { /// 32 byte account Id. AccountId32([u8; 32]),