diff --git a/domains/client/domain-executor/src/core_bundle_processor.rs b/domains/client/domain-executor/src/core_bundle_processor.rs index 18b06be1d44..4767f65a6b3 100644 --- a/domains/client/domain-executor/src/core_bundle_processor.rs +++ b/domains/client/domain-executor/src/core_bundle_processor.rs @@ -1,9 +1,7 @@ -use crate::domain_block_processor::{ - preprocess_primary_block, DomainBlockProcessor, PendingPrimaryBlocks, -}; +use crate::domain_block_preprocessor::CoreDomainBlockPreprocessor; +use crate::domain_block_processor::{DomainBlockProcessor, PendingPrimaryBlocks}; use crate::parent_chain::{CoreDomainParentChain, ParentChainInterface}; -use crate::utils::{translate_number_type, DomainBundles}; -use crate::xdm_verifier::verify_xdm_with_system_domain_client; +use crate::utils::translate_number_type; use crate::TransactionFor; use domain_runtime_primitives::{AccountId, DomainCoreApi}; use sc_client_api::{AuxStore, BlockBackend, StateBackendFor}; @@ -17,7 +15,6 @@ use sp_messenger::MessengerApi; use sp_runtime::traits::{Block as BlockT, HashFor}; use std::marker::PhantomData; use std::sync::Arc; -use subspace_core_primitives::Randomness; use system_runtime_primitives::SystemDomainApi; pub(crate) struct CoreBundleProcessor @@ -33,6 +30,8 @@ where client: Arc, backend: Arc, keystore: SyncCryptoStorePtr, + core_domain_block_preprocessor: + CoreDomainBlockPreprocessor, domain_block_processor: DomainBlockProcessor, _phantom_data: PhantomData<(SBlock, PBlock)>, } @@ -53,6 +52,7 @@ where client: self.client.clone(), backend: self.backend.clone(), keystore: self.keystore.clone(), + core_domain_block_preprocessor: self.core_domain_block_preprocessor.clone(), domain_block_processor: self.domain_block_processor.clone(), _phantom_data: self._phantom_data, } @@ -103,6 +103,12 @@ where system_domain_client.clone(), domain_id, ); + let core_domain_block_preprocessor = CoreDomainBlockPreprocessor::new( + domain_id, + client.clone(), + primary_chain_client.clone(), + system_domain_client.clone(), + ); Self { domain_id, primary_chain_client, @@ -111,6 +117,7 @@ where client, backend, keystore, + core_domain_block_preprocessor, domain_block_processor, _phantom_data: PhantomData::default(), } @@ -165,12 +172,9 @@ where on top of #{parent_number},{parent_hash}" ); - let (bundles, shuffling_seed, maybe_new_runtime) = - preprocess_primary_block(self.domain_id, &*self.primary_chain_client, primary_hash)?; - - let extrinsics = self - .bundles_to_extrinsics(parent_hash, bundles, shuffling_seed) - .map(|extrinsics| self.filter_invalid_xdm_extrinsics(extrinsics))?; + let (extrinsics, maybe_new_runtime) = self + .core_domain_block_preprocessor + .preprocess_primary_block(primary_hash, parent_hash)?; let domain_block_result = self .domain_block_processor @@ -225,46 +229,4 @@ where Ok(built_block_info) } - - fn bundles_to_extrinsics( - &self, - parent_hash: Block::Hash, - bundles: DomainBundles, - shuffling_seed: Randomness, - ) -> Result, sp_blockchain::Error> { - let bundles = match bundles { - DomainBundles::System(..) => { - return Err(sp_blockchain::Error::Application(Box::from( - "Core bundle processor can not process system bundles.", - ))); - } - DomainBundles::Core(bundles) => bundles, - }; - let extrinsics = self - .domain_block_processor - .compile_own_domain_bundles(bundles); - self.domain_block_processor - .deduplicate_and_shuffle_extrinsics(parent_hash, extrinsics, shuffling_seed) - } - - fn filter_invalid_xdm_extrinsics(&self, exts: Vec) -> Vec { - exts.into_iter() - .filter(|ext| { - match verify_xdm_with_system_domain_client::<_, Block, SBlock, PBlock>( - &self.system_domain_client, - &(ext.clone().into()), - ) { - Ok(valid) => valid, - Err(err) => { - tracing::error!( - target = "core_domain_xdm_filter", - "failed to verify extrinsic: {}", - err - ); - false - } - } - }) - .collect() - } } diff --git a/domains/client/domain-executor/src/domain_block_preprocessor.rs b/domains/client/domain-executor/src/domain_block_preprocessor.rs new file mode 100644 index 00000000000..571da9bcfd4 --- /dev/null +++ b/domains/client/domain-executor/src/domain_block_preprocessor.rs @@ -0,0 +1,467 @@ +//! This module provides the feature of extracting the potential new domain runtime and final +//! list of extrinsics for the domain block from the original primary block. + +use crate::state_root_extractor::StateRootExtractorWithSystemDomainClient; +use crate::xdm_verifier::{ + verify_xdm_with_primary_chain_client, verify_xdm_with_system_domain_client, +}; +use codec::{Decode, Encode}; +use domain_runtime_primitives::{AccountId, DomainCoreApi}; +use rand::seq::SliceRandom; +use rand::SeedableRng; +use rand_chacha::ChaCha8Rng; +use sc_client_api::BlockBackend; +use sp_api::ProvideRuntimeApi; +use sp_blockchain::HeaderBackend; +use sp_domains::{DomainId, ExecutorApi, OpaqueBundles}; +use sp_messenger::MessengerApi; +use sp_runtime::generic::DigestItem; +use sp_runtime::traits::{Block as BlockT, Header as HeaderT, NumberFor}; +use std::borrow::Cow; +use std::collections::{BTreeMap, VecDeque}; +use std::fmt::Debug; +use std::marker::PhantomData; +use std::sync::Arc; +use subspace_core_primitives::Randomness; +use subspace_wasm_tools::read_core_domain_runtime_blob; +use system_runtime_primitives::SystemDomainApi; + +type MaybeNewRuntime = Option>; + +type DomainBlockElements = ( + Vec<::Extrinsic>, + Randomness, + MaybeNewRuntime, +); + +/// Extracts the raw materials for building a new domain block from the primary block. +fn prepare_domain_block_elements( + domain_id: DomainId, + primary_chain_client: &PClient, + block_hash: PBlock::Hash, +) -> sp_blockchain::Result> +where + Block: BlockT, + PBlock: BlockT, + PClient: HeaderBackend + BlockBackend + ProvideRuntimeApi + Send + Sync, + PClient::Api: ExecutorApi, +{ + let extrinsics = primary_chain_client + .block_body(block_hash)? + .ok_or_else(|| { + sp_blockchain::Error::Backend(format!("BlockBody of {block_hash:?} unavailable")) + })?; + + let header = primary_chain_client.header(block_hash)?.ok_or_else(|| { + sp_blockchain::Error::Backend(format!("BlockHeader of {block_hash:?} unavailable")) + })?; + + let maybe_new_runtime = if header + .digest() + .logs + .iter() + .any(|item| *item == DigestItem::RuntimeEnvironmentUpdated) + { + let system_domain_runtime = primary_chain_client + .runtime_api() + .system_domain_wasm_bundle(block_hash)?; + + let new_runtime = match domain_id { + DomainId::SYSTEM => system_domain_runtime, + DomainId::CORE_PAYMENTS => { + read_core_domain_runtime_blob(system_domain_runtime.as_ref(), domain_id) + .map_err(|err| sp_blockchain::Error::Application(Box::new(err)))? + .into() + } + _ => { + return Err(sp_blockchain::Error::Application(Box::from(format!( + "No new runtime code for {domain_id:?}" + )))); + } + }; + + Some(new_runtime) + } else { + None + }; + + let shuffling_seed = primary_chain_client + .runtime_api() + .extrinsics_shuffling_seed(block_hash, header)?; + + Ok((extrinsics, shuffling_seed, maybe_new_runtime)) +} + +fn compile_own_domain_bundles( + bundles: OpaqueBundles, +) -> Vec +where + Block: BlockT, + PBlock: BlockT, +{ + bundles + .into_iter() + .flat_map(|bundle| { + bundle.extrinsics.into_iter().filter_map(|opaque_extrinsic| { + match <::Extrinsic>::decode( + &mut opaque_extrinsic.encode().as_slice(), + ) { + Ok(uxt) => Some(uxt), + Err(e) => { + tracing::error!( + error = ?e, + "Failed to decode the opaque extrisic in bundle, this should not happen" + ); + None + }, + } + }) + }) + .collect::>() +} + +fn deduplicate_and_shuffle_extrinsics( + client: &Arc, + parent_hash: Block::Hash, + mut extrinsics: Vec, + shuffling_seed: Randomness, +) -> Result, sp_blockchain::Error> +where + Block: BlockT, + Client: ProvideRuntimeApi, + Client::Api: DomainCoreApi, +{ + let mut seen = Vec::new(); + extrinsics.retain(|uxt| match seen.contains(uxt) { + true => { + tracing::trace!(extrinsic = ?uxt, "Duplicated extrinsic"); + false + } + false => { + seen.push(uxt.clone()); + true + } + }); + drop(seen); + + tracing::trace!(?extrinsics, "Origin deduplicated extrinsics"); + + let extrinsics: Vec<_> = match client.runtime_api().extract_signer(parent_hash, extrinsics) { + Ok(res) => res, + Err(e) => { + tracing::error!(error = ?e, "Error at calling runtime api: extract_signer"); + return Err(e.into()); + } + }; + + let extrinsics = shuffle_extrinsics::<::Extrinsic>(extrinsics, shuffling_seed); + + Ok(extrinsics) +} + +/// Shuffles the extrinsics in a deterministic way. +/// +/// The extrinsics are grouped by the signer. The extrinsics without a signer, i.e., unsigned +/// extrinsics, are considered as a special group. The items in different groups are cross shuffled, +/// while the order of items inside the same group is still maintained. +fn shuffle_extrinsics( + extrinsics: Vec<(Option, Extrinsic)>, + shuffling_seed: Randomness, +) -> Vec { + let mut rng = ChaCha8Rng::from_seed(shuffling_seed); + + let mut positions = extrinsics + .iter() + .map(|(maybe_signer, _)| maybe_signer) + .cloned() + .collect::>(); + + // Shuffles the positions using Fisher–Yates algorithm. + positions.shuffle(&mut rng); + + let mut grouped_extrinsics: BTreeMap, VecDeque<_>> = extrinsics + .into_iter() + .fold(BTreeMap::new(), |mut groups, (maybe_signer, tx)| { + groups + .entry(maybe_signer) + .or_insert_with(VecDeque::new) + .push_back(tx); + groups + }); + + // The relative ordering for the items in the same group does not change. + let shuffled_extrinsics = positions + .into_iter() + .map(|maybe_signer| { + grouped_extrinsics + .get_mut(&maybe_signer) + .expect("Extrinsics are grouped correctly; qed") + .pop_front() + .expect("Extrinsic definitely exists as it's correctly grouped above; qed") + }) + .collect::>(); + + tracing::trace!(?shuffled_extrinsics, "Shuffled extrinsics"); + + shuffled_extrinsics +} + +pub struct SystemDomainBlockPreprocessor { + client: Arc, + primary_chain_client: Arc, + state_root_extractor: StateRootExtractorWithSystemDomainClient, + _phantom_data: PhantomData<(Block, PBlock)>, +} + +impl Clone + for SystemDomainBlockPreprocessor +{ + fn clone(&self) -> Self { + Self { + client: self.client.clone(), + primary_chain_client: self.primary_chain_client.clone(), + state_root_extractor: self.state_root_extractor.clone(), + _phantom_data: self._phantom_data, + } + } +} + +impl SystemDomainBlockPreprocessor +where + Block: BlockT, + PBlock: BlockT, + PBlock::Hash: From, + NumberFor: From>, + Client: HeaderBackend + ProvideRuntimeApi, + Client::Api: DomainCoreApi + + SystemDomainApi, PBlock::Hash> + + MessengerApi>, + PClient: HeaderBackend + + BlockBackend + + ProvideRuntimeApi + + Send + + Sync + + 'static, + PClient::Api: ExecutorApi, +{ + pub fn new(client: Arc, primary_chain_client: Arc) -> Self { + let state_root_extractor = StateRootExtractorWithSystemDomainClient::new(client.clone()); + Self { + client, + primary_chain_client, + state_root_extractor, + _phantom_data: Default::default(), + } + } + + pub fn preprocess_primary_block( + &self, + primary_hash: PBlock::Hash, + domain_hash: Block::Hash, + ) -> sp_blockchain::Result<(Vec, MaybeNewRuntime)> { + let (primary_extrinsics, shuffling_seed, maybe_new_runtime) = + prepare_domain_block_elements::( + DomainId::SYSTEM, + &*self.primary_chain_client, + primary_hash, + )?; + + let (system_bundles, core_bundles) = self + .primary_chain_client + .runtime_api() + .extract_system_bundles(primary_hash, primary_extrinsics)?; + + let origin_system_extrinsics = compile_own_domain_bundles::(system_bundles); + + let extrinsics = self + .client + .runtime_api() + .construct_submit_core_bundle_extrinsics(domain_hash, core_bundles)? + .into_iter() + .filter_map( + |uxt| match <::Extrinsic>::decode(&mut uxt.as_slice()) { + Ok(uxt) => Some(uxt), + Err(e) => { + tracing::error!( + error = ?e, + "Failed to decode the opaque extrisic in bundle, this should not happen" + ); + None + } + }, + ) + .chain(origin_system_extrinsics) + .collect::>(); + + let extrinsics = deduplicate_and_shuffle_extrinsics( + &self.client, + domain_hash, + extrinsics, + shuffling_seed, + ) + .map(|extrinsincs| self.filter_invalid_xdm_extrinsics(extrinsincs))?; + + Ok((extrinsics, maybe_new_runtime)) + } + + fn filter_invalid_xdm_extrinsics(&self, exts: Vec) -> Vec { + exts.into_iter() + .filter(|ext| { + match verify_xdm_with_primary_chain_client::( + &self.primary_chain_client, + &self.state_root_extractor, + ext, + ) { + Ok(valid) => valid, + Err(err) => { + tracing::error!( + target = "system_domain_xdm_filter", + "failed to verify extrinsic: {}", + err + ); + false + } + } + }) + .collect() + } +} + +pub struct CoreDomainBlockPreprocessor { + domain_id: DomainId, + client: Arc, + system_domain_client: Arc, + primary_chain_client: Arc, + _phantom_data: PhantomData<(Block, PBlock, SBlock)>, +} + +impl Clone + for CoreDomainBlockPreprocessor +{ + fn clone(&self) -> Self { + Self { + domain_id: self.domain_id, + client: self.client.clone(), + system_domain_client: self.system_domain_client.clone(), + primary_chain_client: self.primary_chain_client.clone(), + _phantom_data: self._phantom_data, + } + } +} + +impl + CoreDomainBlockPreprocessor +where + Block: BlockT, + PBlock: BlockT, + SBlock: BlockT, + Client: HeaderBackend + ProvideRuntimeApi, + Client::Api: DomainCoreApi, + PClient: HeaderBackend + BlockBackend + ProvideRuntimeApi + Send + Sync, + PClient::Api: ExecutorApi, + SClient: HeaderBackend + ProvideRuntimeApi + 'static, + SClient::Api: SystemDomainApi, PBlock::Hash> + + MessengerApi>, + Block::Extrinsic: Into, +{ + pub fn new( + domain_id: DomainId, + client: Arc, + primary_chain_client: Arc, + system_domain_client: Arc, + ) -> Self { + Self { + domain_id, + client, + system_domain_client, + primary_chain_client, + _phantom_data: Default::default(), + } + } + + pub fn preprocess_primary_block( + &self, + primary_hash: PBlock::Hash, + domain_hash: Block::Hash, + ) -> sp_blockchain::Result<(Vec, MaybeNewRuntime)> { + let (primary_extrinsics, shuffling_seed, maybe_new_runtime) = + prepare_domain_block_elements::( + self.domain_id, + &*self.primary_chain_client, + primary_hash, + )?; + + let core_bundles = self + .primary_chain_client + .runtime_api() + .extract_core_bundles(primary_hash, primary_extrinsics, self.domain_id)?; + + let extrinsics = compile_own_domain_bundles::(core_bundles); + + let extrinsics = deduplicate_and_shuffle_extrinsics( + &self.client, + domain_hash, + extrinsics, + shuffling_seed, + ) + .map(|extrinsics| self.filter_invalid_xdm_extrinsics(extrinsics))?; + + Ok((extrinsics, maybe_new_runtime)) + } + + fn filter_invalid_xdm_extrinsics(&self, exts: Vec) -> Vec { + exts.into_iter() + .filter(|ext| { + match verify_xdm_with_system_domain_client::<_, Block, SBlock, PBlock>( + &self.system_domain_client, + &(ext.clone().into()), + ) { + Ok(valid) => valid, + Err(err) => { + tracing::error!( + target = "core_domain_xdm_filter", + "failed to verify extrinsic: {}", + err + ); + false + } + } + }) + .collect() + } +} + +#[cfg(test)] +mod tests { + use super::shuffle_extrinsics; + use sp_keyring::sr25519::Keyring; + use sp_runtime::traits::{BlakeTwo256, Hash as HashT}; + + #[test] + fn shuffle_extrinsics_should_work() { + let alice = Keyring::Alice.to_account_id(); + let bob = Keyring::Bob.to_account_id(); + let charlie = Keyring::Charlie.to_account_id(); + + let extrinsics = vec![ + (Some(alice.clone()), 10), + (None, 100), + (Some(bob.clone()), 1), + (Some(bob), 2), + (Some(charlie.clone()), 30), + (Some(alice.clone()), 11), + (Some(charlie), 31), + (None, 101), + (None, 102), + (Some(alice), 12), + ]; + + let dummy_seed = BlakeTwo256::hash_of(&[1u8; 64]).into(); + let shuffled_extrinsics = shuffle_extrinsics(extrinsics, dummy_seed); + + assert_eq!( + shuffled_extrinsics, + vec![100, 30, 10, 1, 11, 101, 31, 12, 102, 2] + ); + } +} diff --git a/domains/client/domain-executor/src/domain_block_processor.rs b/domains/client/domain-executor/src/domain_block_processor.rs index 061e09c525f..092b93102bd 100644 --- a/domains/client/domain-executor/src/domain_block_processor.rs +++ b/domains/client/domain-executor/src/domain_block_processor.rs @@ -1,5 +1,5 @@ use crate::fraud_proof::{find_trace_mismatch, FraudProofGenerator}; -use crate::utils::{shuffle_extrinsics, to_number_primitive, translate_number_type, DomainBundles}; +use crate::utils::{to_number_primitive, translate_number_type}; use crate::{ExecutionReceiptFor, TransactionFor}; use codec::{Decode, Encode}; use domain_block_builder::{BlockBuilder, BuiltBlock, RecordProof}; @@ -14,92 +14,11 @@ use sp_consensus::{BlockOrigin, SyncOracle}; use sp_core::traits::CodeExecutor; use sp_domains::fraud_proof::FraudProof; use sp_domains::merkle_tree::MerkleTree; -use sp_domains::{DomainId, ExecutionReceipt, ExecutorApi, OpaqueBundles}; -use sp_runtime::generic::DigestItem; +use sp_domains::{DomainId, ExecutionReceipt, ExecutorApi}; use sp_runtime::traits::{Block as BlockT, HashFor, Header as HeaderT, One, Zero}; use sp_runtime::Digest; use std::borrow::Cow; use std::sync::Arc; -use subspace_core_primitives::Randomness; -use subspace_wasm_tools::read_core_domain_runtime_blob; - -type DomainBlockElements = ( - DomainBundles, - Randomness, - Option>, -); - -/// Extracts the necessary materials for building a new domain block from the primary block. -pub(crate) fn preprocess_primary_block( - domain_id: DomainId, - primary_chain_client: &PClient, - block_hash: PBlock::Hash, -) -> sp_blockchain::Result> -where - Block: BlockT, - PBlock: BlockT, - PClient: HeaderBackend + BlockBackend + ProvideRuntimeApi + Send + Sync, - PClient::Api: ExecutorApi, -{ - let extrinsics = primary_chain_client - .block_body(block_hash)? - .ok_or_else(|| { - sp_blockchain::Error::Backend(format!("BlockBody of {block_hash:?} unavailable")) - })?; - - let header = primary_chain_client.header(block_hash)?.ok_or_else(|| { - sp_blockchain::Error::Backend(format!("BlockHeader of {block_hash:?} unavailable")) - })?; - - let maybe_new_runtime = if header - .digest() - .logs - .iter() - .any(|item| *item == DigestItem::RuntimeEnvironmentUpdated) - { - let system_domain_runtime = primary_chain_client - .runtime_api() - .system_domain_wasm_bundle(block_hash)?; - - let new_runtime = match domain_id { - DomainId::SYSTEM => system_domain_runtime, - DomainId::CORE_PAYMENTS => { - read_core_domain_runtime_blob(system_domain_runtime.as_ref(), domain_id) - .map_err(|err| sp_blockchain::Error::Application(Box::new(err)))? - .into() - } - _ => { - return Err(sp_blockchain::Error::Application(Box::from(format!( - "No new runtime code for {domain_id:?}" - )))); - } - }; - - Some(new_runtime) - } else { - None - }; - - let shuffling_seed = primary_chain_client - .runtime_api() - .extrinsics_shuffling_seed(block_hash, header)?; - - let domain_bundles = if domain_id.is_system() { - let (system_bundles, core_bundles) = primary_chain_client - .runtime_api() - .extract_system_bundles(block_hash, extrinsics)?; - DomainBundles::System(system_bundles, core_bundles) - } else if domain_id.is_core() { - let core_bundles = primary_chain_client - .runtime_api() - .extract_core_bundles(block_hash, extrinsics, domain_id)?; - DomainBundles::Core(core_bundles) - } else { - unreachable!("Open domains are unsupported") - }; - - Ok((domain_bundles, shuffling_seed, maybe_new_runtime)) -} pub(crate) struct DomainBlockResult where @@ -294,70 +213,6 @@ where } } - pub(crate) fn compile_own_domain_bundles( - &self, - bundles: OpaqueBundles, - ) -> Vec { - bundles - .into_iter() - .flat_map(|bundle| { - bundle.extrinsics.into_iter().filter_map(|opaque_extrinsic| { - match <::Extrinsic>::decode( - &mut opaque_extrinsic.encode().as_slice(), - ) { - Ok(uxt) => Some(uxt), - Err(e) => { - tracing::error!( - error = ?e, - "Failed to decode the opaque extrisic in bundle, this should not happen" - ); - None - }, - } - }) - }) - .collect::>() - } - - pub(crate) fn deduplicate_and_shuffle_extrinsics( - &self, - parent_hash: Block::Hash, - mut extrinsics: Vec, - shuffling_seed: Randomness, - ) -> Result, sp_blockchain::Error> { - let mut seen = Vec::new(); - extrinsics.retain(|uxt| match seen.contains(uxt) { - true => { - tracing::trace!(extrinsic = ?uxt, "Duplicated extrinsic"); - false - } - false => { - seen.push(uxt.clone()); - true - } - }); - drop(seen); - - tracing::trace!(?extrinsics, "Origin deduplicated extrinsics"); - - let extrinsics: Vec<_> = match self - .client - .runtime_api() - .extract_signer(parent_hash, extrinsics) - { - Ok(res) => res, - Err(e) => { - tracing::error!(error = ?e, "Error at calling runtime api: extract_signer"); - return Err(e.into()); - } - }; - - let extrinsics = - shuffle_extrinsics::<::Extrinsic>(extrinsics, shuffling_seed); - - Ok(extrinsics) - } - pub(crate) async fn process_domain_block( &self, (primary_hash, primary_number): (PBlock::Hash, NumberFor), diff --git a/domains/client/domain-executor/src/lib.rs b/domains/client/domain-executor/src/lib.rs index 7ce703fc435..e0caa7fbfbe 100644 --- a/domains/client/domain-executor/src/lib.rs +++ b/domains/client/domain-executor/src/lib.rs @@ -86,6 +86,7 @@ mod core_bundle_processor; mod core_domain_worker; mod core_executor; mod core_gossip_message_validator; +mod domain_block_preprocessor; mod domain_block_processor; mod domain_bundle_producer; mod domain_bundle_proposer; diff --git a/domains/client/domain-executor/src/system_bundle_processor.rs b/domains/client/domain-executor/src/system_bundle_processor.rs index 695c4bfaa90..87430bea8a6 100644 --- a/domains/client/domain-executor/src/system_bundle_processor.rs +++ b/domains/client/domain-executor/src/system_bundle_processor.rs @@ -1,11 +1,8 @@ -use crate::domain_block_processor::{ - preprocess_primary_block, DomainBlockProcessor, PendingPrimaryBlocks, -}; +use crate::domain_block_preprocessor::SystemDomainBlockPreprocessor; +use crate::domain_block_processor::{DomainBlockProcessor, PendingPrimaryBlocks}; use crate::state_root_extractor::StateRootExtractorWithSystemDomainClient; -use crate::utils::{translate_number_type, DomainBundles}; -use crate::xdm_verifier::verify_xdm_with_primary_chain_client; +use crate::utils::translate_number_type; use crate::TransactionFor; -use codec::Decode; use domain_runtime_primitives::{AccountId, DomainCoreApi}; use sc_client_api::{AuxStore, BlockBackend, StateBackendFor}; use sc_consensus::BlockImport; @@ -13,13 +10,12 @@ use sp_api::{NumberFor, ProvideRuntimeApi}; use sp_blockchain::{HeaderBackend, HeaderMetadata}; use sp_core::traits::CodeExecutor; use sp_domain_digests::AsPredigest; -use sp_domains::{DomainId, ExecutorApi}; +use sp_domains::ExecutorApi; use sp_keystore::SyncCryptoStorePtr; use sp_messenger::MessengerApi; use sp_runtime::traits::{Block as BlockT, HashFor, One, Zero}; use sp_runtime::{Digest, DigestItem}; use std::sync::Arc; -use subspace_core_primitives::Randomness; use system_runtime_primitives::SystemDomainApi; pub(crate) struct SystemBundleProcessor @@ -30,6 +26,7 @@ where client: Arc, backend: Arc, keystore: SyncCryptoStorePtr, + system_domain_block_preprocessor: SystemDomainBlockPreprocessor, domain_block_processor: DomainBlockProcessor, state_root_extractor: StateRootExtractorWithSystemDomainClient, } @@ -45,6 +42,7 @@ where client: self.client.clone(), backend: self.backend.clone(), keystore: self.keystore.clone(), + system_domain_block_preprocessor: self.system_domain_block_preprocessor.clone(), domain_block_processor: self.domain_block_processor.clone(), state_root_extractor: self.state_root_extractor.clone(), } @@ -87,11 +85,14 @@ where keystore: SyncCryptoStorePtr, domain_block_processor: DomainBlockProcessor, ) -> Self { + let system_domain_block_preprocessor = + SystemDomainBlockPreprocessor::new(client.clone(), primary_chain_client.clone()); Self { primary_chain_client, client: client.clone(), backend, keystore, + system_domain_block_preprocessor, domain_block_processor, state_root_extractor: StateRootExtractorWithSystemDomainClient::new(client), } @@ -143,12 +144,9 @@ where let (primary_hash, primary_number) = primary_info; let (parent_hash, parent_number) = parent_info; - let (bundles, shuffling_seed, maybe_new_runtime) = - preprocess_primary_block(DomainId::SYSTEM, &*self.primary_chain_client, primary_hash)?; - - let extrinsics = self - .bundles_to_extrinsics(parent_hash, bundles, shuffling_seed) - .map(|extrinsincs| self.filter_invalid_xdm_extrinsics(extrinsincs))?; + let (extrinsics, maybe_new_runtime) = self + .system_domain_block_preprocessor + .preprocess_primary_block(primary_hash, parent_hash)?; let logs = if primary_number == One::one() { // Manually inject the genesis block info. @@ -222,68 +220,4 @@ where Ok(built_block_info) } - - fn bundles_to_extrinsics( - &self, - parent_hash: Block::Hash, - bundles: DomainBundles, - shuffling_seed: Randomness, - ) -> Result, sp_blockchain::Error> { - let (system_bundles, core_bundles) = match bundles { - DomainBundles::System(system_bundles, core_bundles) => (system_bundles, core_bundles), - DomainBundles::Core(_) => { - return Err(sp_blockchain::Error::Application(Box::from( - "System bundle processor can not process core bundles.", - ))); - } - }; - - let origin_system_extrinsics = self - .domain_block_processor - .compile_own_domain_bundles(system_bundles); - let extrinsics = self - .client - .runtime_api() - .construct_submit_core_bundle_extrinsics(parent_hash, core_bundles)? - .into_iter() - .filter_map( - |uxt| match <::Extrinsic>::decode(&mut uxt.as_slice()) { - Ok(uxt) => Some(uxt), - Err(e) => { - tracing::error!( - error = ?e, - "Failed to decode the opaque extrisic in bundle, this should not happen" - ); - None - } - }, - ) - .chain(origin_system_extrinsics) - .collect::>(); - - self.domain_block_processor - .deduplicate_and_shuffle_extrinsics(parent_hash, extrinsics, shuffling_seed) - } - - fn filter_invalid_xdm_extrinsics(&self, exts: Vec) -> Vec { - exts.into_iter() - .filter(|ext| { - match verify_xdm_with_primary_chain_client::( - &self.primary_chain_client, - &self.state_root_extractor, - ext, - ) { - Ok(valid) => valid, - Err(err) => { - tracing::error!( - target = "system_domain_xdm_filter", - "failed to verify extrinsic: {}", - err - ); - false - } - } - }) - .collect() - } } diff --git a/domains/client/domain-executor/src/utils.rs b/domains/client/domain-executor/src/utils.rs index ce6d6a74cf4..d250f5f79ef 100644 --- a/domains/client/domain-executor/src/utils.rs +++ b/domains/client/domain-executor/src/utils.rs @@ -1,27 +1,8 @@ use codec::{Decode, Encode}; -use domain_runtime_primitives::AccountId; -use rand::seq::SliceRandom; -use rand::SeedableRng; -use rand_chacha::ChaCha8Rng; use sp_consensus_slots::Slot; -use sp_domains::{OpaqueBundles, SignedOpaqueBundles}; use sp_runtime::traits::{Block as BlockT, NumberFor}; -use std::collections::{BTreeMap, VecDeque}; use std::convert::TryInto; -use std::fmt::Debug; -use subspace_core_primitives::{Blake2b256Hash, BlockNumber, Randomness}; - -pub(super) enum DomainBundles -where - Block: BlockT, - PBlock: BlockT, -{ - System( - OpaqueBundles, - SignedOpaqueBundles, - ), - Core(OpaqueBundles), -} +use subspace_core_primitives::{Blake2b256Hash, BlockNumber}; /// Data required to produce bundles on executor node. #[derive(PartialEq, Clone, Debug)] @@ -80,85 +61,3 @@ where { B2::Hash::decode(&mut block_hash.encode().as_slice()).unwrap() } - -/// Shuffles the extrinsics in a deterministic way. -/// -/// The extrinsics are grouped by the signer. The extrinsics without a signer, i.e., unsigned -/// extrinsics, are considered as a special group. The items in different groups are cross shuffled, -/// while the order of items inside the same group is still maintained. -pub(crate) fn shuffle_extrinsics( - extrinsics: Vec<(Option, Extrinsic)>, - shuffling_seed: Randomness, -) -> Vec { - let mut rng = ChaCha8Rng::from_seed(shuffling_seed); - - let mut positions = extrinsics - .iter() - .map(|(maybe_signer, _)| maybe_signer) - .cloned() - .collect::>(); - - // Shuffles the positions using Fisher–Yates algorithm. - positions.shuffle(&mut rng); - - let mut grouped_extrinsics: BTreeMap, VecDeque<_>> = extrinsics - .into_iter() - .fold(BTreeMap::new(), |mut groups, (maybe_signer, tx)| { - groups - .entry(maybe_signer) - .or_insert_with(VecDeque::new) - .push_back(tx); - groups - }); - - // The relative ordering for the items in the same group does not change. - let shuffled_extrinsics = positions - .into_iter() - .map(|maybe_signer| { - grouped_extrinsics - .get_mut(&maybe_signer) - .expect("Extrinsics are grouped correctly; qed") - .pop_front() - .expect("Extrinsic definitely exists as it's correctly grouped above; qed") - }) - .collect::>(); - - tracing::trace!(?shuffled_extrinsics, "Shuffled extrinsics"); - - shuffled_extrinsics -} - -#[cfg(test)] -mod tests { - use super::shuffle_extrinsics; - use sp_keyring::sr25519::Keyring; - use sp_runtime::traits::{BlakeTwo256, Hash as HashT}; - - #[test] - fn shuffle_extrinsics_should_work() { - let alice = Keyring::Alice.to_account_id(); - let bob = Keyring::Bob.to_account_id(); - let charlie = Keyring::Charlie.to_account_id(); - - let extrinsics = vec![ - (Some(alice.clone()), 10), - (None, 100), - (Some(bob.clone()), 1), - (Some(bob), 2), - (Some(charlie.clone()), 30), - (Some(alice.clone()), 11), - (Some(charlie), 31), - (None, 101), - (None, 102), - (Some(alice), 12), - ]; - - let dummy_seed = BlakeTwo256::hash_of(&[1u8; 64]).into(); - let shuffled_extrinsics = shuffle_extrinsics(extrinsics, dummy_seed); - - assert_eq!( - shuffled_extrinsics, - vec![100, 30, 10, 1, 11, 101, 31, 12, 102, 2] - ); - } -}