diff --git a/crates/pallet-domains/src/tests.rs b/crates/pallet-domains/src/tests.rs index 748d3b62265..1b80d340f64 100644 --- a/crates/pallet-domains/src/tests.rs +++ b/crates/pallet-domains/src/tests.rs @@ -470,7 +470,7 @@ fn submit_fraud_proof_should_work() { domain_id, bad_receipt_hash: Hash::random(), parent_number: 99, - parent_hash: block_hashes[98], + primary_parent_hash: block_hashes[98], pre_state_root: H256::random(), post_state_root: H256::random(), proof: StorageProof::empty(), diff --git a/crates/pallet-receipts/src/lib.rs b/crates/pallet-receipts/src/lib.rs index f8b2b5bfb81..b0a2d6f3fc7 100644 --- a/crates/pallet-receipts/src/lib.rs +++ b/crates/pallet-receipts/src/lib.rs @@ -359,11 +359,12 @@ impl Pallet { FraudProofError::ExecutionReceiptInFuture ); - let parent_hash = T::Hash::decode(&mut fraud_proof.parent_hash.encode().as_slice()) - .map_err(|_| FraudProofError::WrongHashType)?; + let primary_parent_hash = + T::Hash::decode(&mut fraud_proof.primary_parent_hash.encode().as_slice()) + .map_err(|_| FraudProofError::WrongHashType)?; let parent_number: T::BlockNumber = fraud_proof.parent_number.into(); ensure!( - Self::primary_hash(fraud_proof.domain_id, parent_number) == Some(parent_hash), + Self::primary_hash(fraud_proof.domain_id, parent_number) == Some(primary_parent_hash), FraudProofError::UnknownBlock ); diff --git a/crates/sp-domains/src/fraud_proof.rs b/crates/sp-domains/src/fraud_proof.rs index ccd567d57a1..3ab629432b0 100644 --- a/crates/sp-domains/src/fraud_proof.rs +++ b/crates/sp-domains/src/fraud_proof.rs @@ -9,11 +9,12 @@ use sp_trie::StorageProof; use subspace_core_primitives::BlockNumber; use subspace_runtime_primitives::AccountId; -/// A phase of a block's execution. +/// A phase of a block's execution, carrying necessary information needed for verifying the +/// invalid state transition proof. #[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone)] pub enum ExecutionPhase { /// Executes the `initialize_block` hook. - InitializeBlock, + InitializeBlock { domain_parent_hash: H256 }, /// Executes some extrinsic. ApplyExtrinsic(u32), /// Executes the `finalize_block` hook. @@ -26,7 +27,7 @@ impl ExecutionPhase { match self { // TODO: Replace `DomainCoreApi_initialize_block_with_post_state_root` with `Core_initalize_block` // Should be a same issue with https://github.com/paritytech/substrate/pull/10922#issuecomment-1068997467 - Self::InitializeBlock => "DomainCoreApi_initialize_block_with_post_state_root", + Self::InitializeBlock { .. } => "DomainCoreApi_initialize_block_with_post_state_root", Self::ApplyExtrinsic(_) => "BlockBuilder_apply_extrinsic", Self::FinalizeBlock { .. } => "BlockBuilder_finalize_block", } @@ -39,7 +40,7 @@ impl ExecutionPhase { /// result of execution reported in [`FraudProof`] is expected or not. pub fn verifying_method(&self) -> &'static str { match self { - Self::InitializeBlock => "DomainCoreApi_initialize_block_with_post_state_root", + Self::InitializeBlock { .. } => "DomainCoreApi_initialize_block_with_post_state_root", Self::ApplyExtrinsic(_) => "DomainCoreApi_apply_extrinsic_with_post_state_root", Self::FinalizeBlock { .. } => "BlockBuilder_finalize_block", } @@ -51,7 +52,7 @@ impl ExecutionPhase { execution_result: Vec, ) -> Result { match self { - Self::InitializeBlock | Self::ApplyExtrinsic(_) => { + Self::InitializeBlock { .. } | Self::ApplyExtrinsic(_) => { let encoded_storage_root = Vec::::decode(&mut execution_result.as_slice()) .map_err(VerificationError::InitializeBlockOrApplyExtrinsicDecode)?; Header::Hash::decode(&mut encoded_storage_root.as_slice()) @@ -158,10 +159,11 @@ pub struct InvalidStateTransitionProof { pub bad_receipt_hash: H256, /// Parent number. pub parent_number: BlockNumber, - /// Parent hash of the block at which the invalid execution occurred. + /// Hash of the primary block corresponding to `parent_number`. /// - /// Runtime code for this block's execution is retrieved on top of the parent block. - pub parent_hash: H256, + /// Runtime code for the execution of the domain block that is being challenged + /// is retrieved on top of the primary parent block from the primary chain. + pub primary_parent_hash: H256, /// State root before the fraudulent transaction. pub pre_state_root: H256, /// State root after the fraudulent transaction. diff --git a/crates/subspace-fraud-proof/src/invalid_state_transition_proof.rs b/crates/subspace-fraud-proof/src/invalid_state_transition_proof.rs index d01ee2459f6..e628badda64 100644 --- a/crates/subspace-fraud-proof/src/invalid_state_transition_proof.rs +++ b/crates/subspace-fraud-proof/src/invalid_state_transition_proof.rs @@ -259,22 +259,23 @@ where ) -> Result<(), VerificationError> { let InvalidStateTransitionProof { domain_id, - bad_receipt_hash, parent_number, - parent_hash, + bad_receipt_hash, pre_state_root, execution_phase, .. } = invalid_state_transition_proof; let pre_state_root_onchain = match execution_phase { - ExecutionPhase::InitializeBlock => self.client.runtime_api().state_root( - self.client.info().best_hash, - *domain_id, - NumberFor::::from(*parent_number), - Block::Hash::decode(&mut parent_hash.encode().as_slice()) - .expect("Block Hash must be H256; qed"), - )?, + ExecutionPhase::InitializeBlock { domain_parent_hash } => { + self.client.runtime_api().state_root( + self.client.info().best_hash, + *domain_id, + NumberFor::::from(*parent_number), + Block::Hash::decode(&mut domain_parent_hash.encode().as_slice()) + .expect("Block Hash must be H256; qed"), + )? + } ExecutionPhase::ApplyExtrinsic(trace_index_of_pre_state_root) | ExecutionPhase::FinalizeBlock { total_extrinsics: trace_index_of_pre_state_root, @@ -319,7 +320,7 @@ where )?; let post_state_root_onchain = match execution_phase { - ExecutionPhase::InitializeBlock => trace + ExecutionPhase::InitializeBlock { .. } => trace .get(0) .ok_or(VerificationError::PostStateRootNotFound)?, ExecutionPhase::ApplyExtrinsic(trace_index_of_post_state_root) @@ -396,7 +397,7 @@ where let InvalidStateTransitionProof { domain_id, - parent_hash, + primary_parent_hash, pre_state_root, post_state_root, proof, @@ -404,7 +405,7 @@ where .. } = invalid_state_transition_proof; - let at = PBlock::Hash::decode(&mut parent_hash.encode().as_slice()) + let at = PBlock::Hash::decode(&mut primary_parent_hash.encode().as_slice()) .expect("Block Hash must be H256; qed"); let system_wasm_bundle = self .client diff --git a/crates/subspace-fraud-proof/src/tests.rs b/crates/subspace-fraud-proof/src/tests.rs index 1e2610f9284..3b454c90f5e 100644 --- a/crates/subspace-fraud-proof/src/tests.rs +++ b/crates/subspace-fraud-proof/src/tests.rs @@ -159,7 +159,9 @@ async fn execution_proof_creation_and_verification_should_work() { parent_header.hash(), Default::default(), ); - let execution_phase = ExecutionPhase::InitializeBlock; + let execution_phase = ExecutionPhase::InitializeBlock { + domain_parent_hash: parent_header.hash(), + }; let initialize_block_call_data = new_header.encode(); let prover = ExecutionProver::new( @@ -208,7 +210,7 @@ async fn execution_proof_creation_and_verification_should_work() { domain_id: TEST_DOMAIN_ID, bad_receipt_hash: Hash::random(), parent_number: parent_number_alice, - parent_hash: parent_hash_alice, + primary_parent_hash: parent_hash_alice, pre_state_root: *parent_header.state_root(), post_state_root: intermediate_roots[0].into(), proof: storage_proof, @@ -266,7 +268,7 @@ async fn execution_proof_creation_and_verification_should_work() { domain_id: TEST_DOMAIN_ID, bad_receipt_hash: Hash::random(), parent_number: parent_number_alice, - parent_hash: parent_hash_alice, + primary_parent_hash: parent_hash_alice, pre_state_root: intermediate_roots[target_extrinsic_index].into(), post_state_root: intermediate_roots[target_extrinsic_index + 1].into(), proof: storage_proof, @@ -320,7 +322,7 @@ async fn execution_proof_creation_and_verification_should_work() { domain_id: TEST_DOMAIN_ID, bad_receipt_hash: Hash::random(), parent_number: parent_number_alice, - parent_hash: parent_hash_alice, + primary_parent_hash: parent_hash_alice, pre_state_root: intermediate_roots.last().unwrap().into(), post_state_root: post_execution_root, proof: storage_proof, @@ -498,7 +500,7 @@ async fn invalid_execution_proof_should_not_work() { domain_id: TEST_DOMAIN_ID, bad_receipt_hash: Hash::random(), parent_number: parent_number_alice, - parent_hash: parent_hash_alice, + primary_parent_hash: parent_hash_alice, pre_state_root: post_delta_root0, post_state_root: post_delta_root1, proof: proof1, @@ -512,7 +514,7 @@ async fn invalid_execution_proof_should_not_work() { domain_id: TEST_DOMAIN_ID, bad_receipt_hash: Hash::random(), parent_number: parent_number_alice, - parent_hash: parent_hash_alice, + primary_parent_hash: parent_hash_alice, pre_state_root: post_delta_root0, post_state_root: post_delta_root1, proof: proof0.clone(), @@ -526,7 +528,7 @@ async fn invalid_execution_proof_should_not_work() { domain_id: TEST_DOMAIN_ID, bad_receipt_hash: Hash::random(), parent_number: parent_number_alice, - parent_hash: parent_hash_alice, + primary_parent_hash: parent_hash_alice, pre_state_root: post_delta_root0, post_state_root: post_delta_root1, proof: proof0, diff --git a/domains/client/domain-executor/src/core_executor.rs b/domains/client/domain-executor/src/core_executor.rs index c973906bac2..b515865c335 100644 --- a/domains/client/domain-executor/src/core_executor.rs +++ b/domains/client/domain-executor/src/core_executor.rs @@ -31,7 +31,7 @@ pub struct Executor, transaction_pool: Arc, backend: Arc, - fraud_proof_generator: FraudProofGenerator, + fraud_proof_generator: FraudProofGenerator, _phantom_data: PhantomData<(SBlock, SClient)>, } @@ -133,6 +133,7 @@ where let fraud_proof_generator = FraudProofGenerator::new( params.client.clone(), + params.primary_chain_client.clone(), params.spawner.clone(), params.backend.clone(), params.code_executor, @@ -183,7 +184,9 @@ where }) } - pub fn fraud_proof_generator(&self) -> FraudProofGenerator { + pub fn fraud_proof_generator( + &self, + ) -> FraudProofGenerator { self.fraud_proof_generator.clone() } } diff --git a/domains/client/domain-executor/src/core_gossip_message_validator.rs b/domains/client/domain-executor/src/core_gossip_message_validator.rs index 7dfd9f160bb..a4a77be051e 100644 --- a/domains/client/domain-executor/src/core_gossip_message_validator.rs +++ b/domains/client/domain-executor/src/core_gossip_message_validator.rs @@ -22,6 +22,7 @@ pub struct CoreGossipMessageValidator< PBlock, Client, SClient, + PClient, TransactionPool, Backend, E, @@ -34,17 +35,19 @@ pub struct CoreGossipMessageValidator< parent_chain: ParentChain, client: Arc, transaction_pool: Arc, - gossip_message_validator: GossipMessageValidator, - _phantom_data: PhantomData<(SBlock, SClient)>, + gossip_message_validator: GossipMessageValidator, + _phantom_data: PhantomData<(SBlock, SClient, PClient)>, } -impl Clone +impl + Clone for CoreGossipMessageValidator< Block, SBlock, PBlock, Client, SClient, + PClient, TransactionPool, Backend, E, @@ -67,13 +70,14 @@ where } } -impl +impl CoreGossipMessageValidator< Block, SBlock, PBlock, Client, SClient, + PClient, TransactionPool, Backend, E, @@ -93,6 +97,7 @@ where + sp_api::ApiExt>, SClient: HeaderBackend + ProvideRuntimeApi + 'static, SClient::Api: SystemDomainApi, PBlock::Hash>, + PClient: HeaderBackend + 'static, Backend: sc_client_api::Backend + 'static, TransactionFor: sp_trie::HashDBT, sp_trie::DBValue>, TransactionPool: sc_transaction_pool_api::TransactionPool + 'static, @@ -104,7 +109,7 @@ where client: Arc, spawner: Box, transaction_pool: Arc, - fraud_proof_generator: FraudProofGenerator, + fraud_proof_generator: FraudProofGenerator, ) -> Self { let gossip_message_validator = GossipMessageValidator::new(client.clone(), spawner, fraud_proof_generator); @@ -118,7 +123,7 @@ where } } -impl +impl GossipMessageHandler for CoreGossipMessageValidator< Block, @@ -126,6 +131,7 @@ impl>, SClient: HeaderBackend + ProvideRuntimeApi + 'static, SClient::Api: SystemDomainApi, PBlock::Hash>, + PClient: HeaderBackend + 'static, Backend: sc_client_api::Backend + 'static, TransactionFor: sp_trie::HashDBT, sp_trie::DBValue>, TransactionPool: sc_transaction_pool_api::TransactionPool + 'static, diff --git a/domains/client/domain-executor/src/domain_block_processor.rs b/domains/client/domain-executor/src/domain_block_processor.rs index 092b93102bd..e7f5ef6c66f 100644 --- a/domains/client/domain-executor/src/domain_block_processor.rs +++ b/domains/client/domain-executor/src/domain_block_processor.rs @@ -40,7 +40,7 @@ where primary_chain_client: Arc, primary_network_sync_oracle: Arc, backend: Arc, - fraud_proof_generator: FraudProofGenerator, + fraud_proof_generator: FraudProofGenerator, } impl Clone @@ -105,7 +105,7 @@ where primary_chain_client: Arc, primary_network_sync_oracle: Arc, backend: Arc, - fraud_proof_generator: FraudProofGenerator, + fraud_proof_generator: FraudProofGenerator, ) -> Self { Self { domain_id, diff --git a/domains/client/domain-executor/src/fraud_proof.rs b/domains/client/domain-executor/src/fraud_proof.rs index b7437ad89e1..f562bdd9b4d 100644 --- a/domains/client/domain-executor/src/fraud_proof.rs +++ b/domains/client/domain-executor/src/fraud_proof.rs @@ -29,20 +29,22 @@ pub enum FraudProofError { RuntimeApi(#[from] sp_api::ApiError), } -pub struct FraudProofGenerator { +pub struct FraudProofGenerator { client: Arc, + primary_chain_client: Arc, spawner: Box, backend: Arc, code_executor: Arc, _phantom: PhantomData<(Block, PBlock)>, } -impl Clone - for FraudProofGenerator +impl Clone + for FraudProofGenerator { fn clone(&self) -> Self { Self { client: self.client.clone(), + primary_chain_client: self.primary_chain_client.clone(), spawner: self.spawner.clone(), backend: self.backend.clone(), code_executor: self.code_executor.clone(), @@ -51,7 +53,8 @@ impl Clone } } -impl FraudProofGenerator +impl + FraudProofGenerator where Block: BlockT, PBlock: BlockT, @@ -59,18 +62,21 @@ where HeaderBackend + BlockBackend + AuxStore + ProvideRuntimeApi + 'static, Client::Api: sp_block_builder::BlockBuilder + sp_api::ApiExt>, + PClient: HeaderBackend + 'static, Backend: sc_client_api::Backend + Send + Sync + 'static, TransactionFor: sp_trie::HashDBT, sp_trie::DBValue>, E: CodeExecutor, { pub fn new( client: Arc, + primary_chain_client: Arc, spawner: Box, backend: Arc, code_executor: Arc, ) -> Self { Self { client, + primary_chain_client, spawner, backend, code_executor, @@ -115,6 +121,22 @@ where }, )?; + let primary_parent_hash = H256::decode( + &mut self + .primary_chain_client + .header(local_receipt.primary_hash)? + .ok_or_else(|| { + sp_blockchain::Error::Backend(format!( + "Header not found for primary {:?}", + local_receipt.primary_hash + )) + })? + .parent_hash() + .encode() + .as_slice(), + ) + .map_err(|_| FraudProofError::InvalidStateRootType)?; + // TODO: abstract the execution proof impl to be reusable in the test. let invalid_state_transition_proof = if local_trace_index == 0 { // `initialize_block` execution proof. @@ -128,7 +150,9 @@ where parent_header.hash(), Default::default(), ); - let execution_phase = ExecutionPhase::InitializeBlock; + let execution_phase = ExecutionPhase::InitializeBlock { + domain_parent_hash: as_h256(&parent_header.hash())?, + }; let initialize_block_call_data = new_header.encode(); let proof = prover.prove_execution::>( @@ -142,7 +166,7 @@ where domain_id, bad_receipt_hash, parent_number, - parent_hash: as_h256(&parent_header.hash())?, + primary_parent_hash, pre_state_root, post_state_root, proof, @@ -183,7 +207,7 @@ where domain_id, bad_receipt_hash, parent_number, - parent_hash: as_h256(&parent_header.hash())?, + primary_parent_hash, pre_state_root, post_state_root, proof, @@ -206,7 +230,7 @@ where domain_id, bad_receipt_hash, parent_number, - parent_hash: as_h256(&parent_header.hash())?, + primary_parent_hash, pre_state_root, post_state_root, proof, diff --git a/domains/client/domain-executor/src/gossip_message_validator.rs b/domains/client/domain-executor/src/gossip_message_validator.rs index 8414847ffed..46c25f7086f 100644 --- a/domains/client/domain-executor/src/gossip_message_validator.rs +++ b/domains/client/domain-executor/src/gossip_message_validator.rs @@ -45,14 +45,14 @@ impl From for GossipMessageError { } /// Base domain gossip message validator. -pub struct GossipMessageValidator { +pub struct GossipMessageValidator { client: Arc, spawner: Box, - fraud_proof_generator: FraudProofGenerator, + fraud_proof_generator: FraudProofGenerator, } -impl Clone - for GossipMessageValidator +impl Clone + for GossipMessageValidator { fn clone(&self) -> Self { Self { @@ -63,7 +63,8 @@ impl Clone } } -impl GossipMessageValidator +impl + GossipMessageValidator where Block: BlockT, PBlock: BlockT, @@ -75,6 +76,7 @@ where + 'static, Client::Api: sp_block_builder::BlockBuilder + sp_api::ApiExt>, + PClient: HeaderBackend + 'static, Backend: sc_client_api::Backend + 'static, TransactionFor: sp_trie::HashDBT, sp_trie::DBValue>, E: CodeExecutor, @@ -82,7 +84,7 @@ where pub(crate) fn new( client: Arc, spawner: Box, - fraud_proof_generator: FraudProofGenerator, + fraud_proof_generator: FraudProofGenerator, ) -> Self { Self { client, diff --git a/domains/client/domain-executor/src/system_executor.rs b/domains/client/domain-executor/src/system_executor.rs index a079c568cf0..8c270a8bc96 100644 --- a/domains/client/domain-executor/src/system_executor.rs +++ b/domains/client/domain-executor/src/system_executor.rs @@ -33,7 +33,7 @@ where spawner: Box, transaction_pool: Arc, backend: Arc, - fraud_proof_generator: FraudProofGenerator, + fraud_proof_generator: FraudProofGenerator, bundle_processor: SystemBundleProcessor, } @@ -133,6 +133,7 @@ where let fraud_proof_generator = FraudProofGenerator::new( params.client.clone(), + params.primary_chain_client.clone(), params.spawner.clone(), params.backend.clone(), params.code_executor, @@ -181,7 +182,9 @@ where }) } - pub fn fraud_proof_generator(&self) -> FraudProofGenerator { + pub fn fraud_proof_generator( + &self, + ) -> FraudProofGenerator { self.fraud_proof_generator.clone() } diff --git a/domains/client/domain-executor/src/system_gossip_message_validator.rs b/domains/client/domain-executor/src/system_gossip_message_validator.rs index fa5def9c861..6d33abbc3b4 100644 --- a/domains/client/domain-executor/src/system_gossip_message_validator.rs +++ b/domains/client/domain-executor/src/system_gossip_message_validator.rs @@ -19,6 +19,7 @@ pub struct SystemGossipMessageValidator< Block, PBlock, Client, + PClient, TransactionPool, Backend, E, @@ -27,14 +28,15 @@ pub struct SystemGossipMessageValidator< parent_chain: ParentChain, client: Arc, transaction_pool: Arc, - gossip_message_validator: GossipMessageValidator, + gossip_message_validator: GossipMessageValidator, } -impl Clone +impl Clone for SystemGossipMessageValidator< Block, PBlock, Client, + PClient, TransactionPool, Backend, E, @@ -53,8 +55,17 @@ where } } -impl - SystemGossipMessageValidator +impl + SystemGossipMessageValidator< + Block, + PBlock, + Client, + PClient, + TransactionPool, + Backend, + E, + ParentChain, + > where Block: BlockT, PBlock: BlockT, @@ -67,6 +78,7 @@ where Client::Api: SystemDomainApi, PBlock::Hash> + sp_block_builder::BlockBuilder + sp_api::ApiExt>, + PClient: HeaderBackend + 'static, Backend: sc_client_api::Backend + 'static, TransactionFor: sp_trie::HashDBT, sp_trie::DBValue>, TransactionPool: sc_transaction_pool_api::TransactionPool + 'static, @@ -78,7 +90,7 @@ where client: Arc, spawner: Box, transaction_pool: Arc, - fraud_proof_generator: FraudProofGenerator, + fraud_proof_generator: FraudProofGenerator, ) -> Self { let gossip_message_validator = GossipMessageValidator::new(client.clone(), spawner, fraud_proof_generator); @@ -91,12 +103,13 @@ where } } -impl +impl GossipMessageHandler for SystemGossipMessageValidator< Block, PBlock, Client, + PClient, TransactionPool, Backend, E, @@ -115,6 +128,7 @@ where + sp_block_builder::BlockBuilder + sp_api::ApiExt>, Backend: sc_client_api::Backend + 'static, + PClient: HeaderBackend + 'static, TransactionFor: sp_trie::HashDBT, sp_trie::DBValue>, TransactionPool: sc_transaction_pool_api::TransactionPool + 'static, E: CodeExecutor, diff --git a/domains/client/domain-executor/src/tests.rs b/domains/client/domain-executor/src/tests.rs index e3422a74e41..06168a80a39 100644 --- a/domains/client/domain-executor/src/tests.rs +++ b/domains/client/domain-executor/src/tests.rs @@ -154,7 +154,9 @@ async fn fraud_proof_verification_in_tx_pool_should_work() { parent_header.hash(), digest, ); - let execution_phase = ExecutionPhase::InitializeBlock; + let execution_phase = ExecutionPhase::InitializeBlock { + domain_parent_hash: parent_header.hash(), + }; let initialize_block_call_data = new_header.encode(); let storage_proof = prover @@ -178,7 +180,7 @@ async fn fraud_proof_verification_in_tx_pool_should_work() { domain_id: DomainId::SYSTEM, bad_receipt_hash: Hash::random(), parent_number: parent_number_ferdie, - parent_hash: parent_hash_ferdie, + primary_parent_hash: parent_hash_ferdie, pre_state_root: *parent_header.state_root(), post_state_root: intermediate_roots[0].into(), proof: storage_proof, diff --git a/domains/service/src/core_domain.rs b/domains/service/src/core_domain.rs index 5b52c3fd4e4..67ad4c486d3 100644 --- a/domains/service/src/core_domain.rs +++ b/domains/service/src/core_domain.rs @@ -407,7 +407,7 @@ where .await?; let gossip_message_validator = - CoreGossipMessageValidator::<_, SBlock, PBlock, _, SClient, _, _, _, _>::new( + CoreGossipMessageValidator::<_, SBlock, PBlock, _, SClient, _, _, _, _, _>::new( CoreDomainParentChain::<_, SBlock, PBlock>::new( system_domain_client.clone(), domain_id,