From 00927c6476b217dba9ab5b8c6a779d61f654ce3f Mon Sep 17 00:00:00 2001 From: linning Date: Fri, 10 May 2024 17:32:57 +0800 Subject: [PATCH 1/4] Introduce domain runtime api block_fees_storage_key It will be used in the host function introduced in the next commit Signed-off-by: linning --- crates/pallet-domains/src/tests.rs | 2 +- .../sp-domains-fraud-proof/src/fraud_proof.rs | 9 --------- .../src/verification.rs | 2 +- crates/sp-domains/src/core_api.rs | 4 ++++ crates/sp-domains/src/lib.rs | 9 +++++++++ domains/client/block-preprocessor/Cargo.toml | 1 + .../src/stateless_runtime.rs | 19 +++++++++++++++++-- .../client/domain-operator/src/fraud_proof.rs | 2 +- domains/pallets/block-fees/src/lib.rs | 10 ++++++++++ domains/runtime/auto-id/src/lib.rs | 4 ++++ domains/runtime/evm/src/lib.rs | 4 ++++ domains/test/runtime/evm/src/lib.rs | 4 ++++ 12 files changed, 56 insertions(+), 14 deletions(-) diff --git a/crates/pallet-domains/src/tests.rs b/crates/pallet-domains/src/tests.rs index b275a7c8d8..6840a8435c 100644 --- a/crates/pallet-domains/src/tests.rs +++ b/crates/pallet-domains/src/tests.rs @@ -932,7 +932,7 @@ fn generate_invalid_block_fees_fraud_proof( bad_receipt_hash: ReceiptHashFor, block_fees: sp_domains::BlockFees>, ) -> (FraudProofFor, T::Hash) { - let storage_key = sp_domains_fraud_proof::fraud_proof::operator_block_fees_final_key(); + let storage_key = sp_domains::operator_block_fees_final_key(); let mut root = T::Hash::default(); let mut mdb = PrefixedMemoryDB::::default(); { diff --git a/crates/sp-domains-fraud-proof/src/fraud_proof.rs b/crates/sp-domains-fraud-proof/src/fraud_proof.rs index 893c68b7ff..19e28a0040 100644 --- a/crates/sp-domains-fraud-proof/src/fraud_proof.rs +++ b/crates/sp-domains-fraud-proof/src/fraud_proof.rs @@ -774,15 +774,6 @@ impl InvalidBlockFeesProof { } } -//TODO: remove there key generations from here and instead use the fraud proof host function to fetch them - -/// This is a representation of actual Block Fees storage in pallet-block-fees. -/// Any change in key or value there should be changed here accordingly. -pub fn operator_block_fees_final_key() -> Vec { - frame_support::storage::storage_prefix("BlockFees".as_ref(), "CollectedBlockFees".as_ref()) - .to_vec() -} - /// Fraud proof for the valid bundles in `ExecutionReceipt::inboxed_bundles` #[derive(Clone, Debug, Decode, Encode, Eq, PartialEq, TypeInfo)] pub struct ValidBundleProof { diff --git a/crates/sp-domains-fraud-proof/src/verification.rs b/crates/sp-domains-fraud-proof/src/verification.rs index 483837cd83..e9ef85ae5b 100644 --- a/crates/sp-domains-fraud-proof/src/verification.rs +++ b/crates/sp-domains-fraud-proof/src/verification.rs @@ -370,7 +370,7 @@ where Balance: PartialEq + Decode, DomainHashing: Hasher, { - let storage_key = StorageKey(crate::fraud_proof::operator_block_fees_final_key()); + let storage_key = StorageKey(sp_domains::operator_block_fees_final_key()); let storage_proof = storage_proof.clone(); let block_fees = diff --git a/crates/sp-domains/src/core_api.rs b/crates/sp-domains/src/core_api.rs index 1eddc3d613..639a9e6bd2 100644 --- a/crates/sp-domains/src/core_api.rs +++ b/crates/sp-domains/src/core_api.rs @@ -16,6 +16,7 @@ use subspace_runtime_primitives::Moment; sp_api::decl_runtime_apis! { /// Base API that every domain runtime must implement. + #[api_version(2)] pub trait DomainCoreApi { /// Extracts the optional signer per extrinsic. fn extract_signer( @@ -85,5 +86,8 @@ sp_api::decl_runtime_apis! { /// Returns the storage key for the Transfers on Domain. fn transfers_storage_key() -> Vec; + + /// Returns the storage key for the `CollectedBlockFees` on Domain. + fn block_fees_storage_key() -> Vec; } } diff --git a/crates/sp-domains/src/lib.rs b/crates/sp-domains/src/lib.rs index c29af8de90..9978e90d5e 100644 --- a/crates/sp-domains/src/lib.rs +++ b/crates/sp-domains/src/lib.rs @@ -1251,6 +1251,15 @@ pub struct DomainAllowlistUpdates { pub remove_chains: BTreeSet, } +//TODO: remove there key generations from here and instead use the fraud proof host function to fetch them + +/// This is a representation of actual Block Fees storage in pallet-block-fees. +/// Any change in key or value there should be changed here accordingly. +pub fn operator_block_fees_final_key() -> Vec { + frame_support::storage::storage_prefix("BlockFees".as_ref(), "CollectedBlockFees".as_ref()) + .to_vec() +} + sp_api::decl_runtime_apis! { /// API necessary for domains pallet. #[api_version(3)] diff --git a/domains/client/block-preprocessor/Cargo.toml b/domains/client/block-preprocessor/Cargo.toml index 0cfe91a2b2..6870c6ed9e 100644 --- a/domains/client/block-preprocessor/Cargo.toml +++ b/domains/client/block-preprocessor/Cargo.toml @@ -26,6 +26,7 @@ sp-domains = { version = "0.1.0", path = "../../../crates/sp-domains" } sp-executive = { version = "0.1.0", path = "../../primitives/executive" } sp-externalities = { default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "808269708cf5375526755797e8f9a9986016727d" } sp-inherents = { git = "https://github.com/subspace/polkadot-sdk", rev = "808269708cf5375526755797e8f9a9986016727d" } +sp-io = { default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "808269708cf5375526755797e8f9a9986016727d" } sp-messenger = { version = "0.1.0", path = "../../primitives/messenger" } sp-runtime = { git = "https://github.com/subspace/polkadot-sdk", rev = "808269708cf5375526755797e8f9a9986016727d" } sp-state-machine = { git = "https://github.com/subspace/polkadot-sdk", rev = "808269708cf5375526755797e8f9a9986016727d" } diff --git a/domains/client/block-preprocessor/src/stateless_runtime.rs b/domains/client/block-preprocessor/src/stateless_runtime.rs index 310f8853ee..e7b0148856 100644 --- a/domains/client/block-preprocessor/src/stateless_runtime.rs +++ b/domains/client/block-preprocessor/src/stateless_runtime.rs @@ -3,9 +3,9 @@ use domain_runtime_primitives::opaque::AccountId; use domain_runtime_primitives::{Balance, CheckExtrinsicsValidityError, DecodeExtrinsicError}; use sc_client_api::execution_extensions::ExtensionsFactory; use sc_executor::RuntimeVersionOf; -use sp_api::{ApiError, Core}; +use sp_api::{ApiError, Core, RuntimeApiInfo}; use sp_core::traits::{CallContext, CodeExecutor, FetchRuntimeCode, RuntimeCode}; -use sp_core::Hasher; +use sp_core::{Decode, Hasher}; use sp_domains::core_api::DomainCoreApi; use sp_domains::DomainAllowlistUpdates; use sp_messenger::messages::MessageKey; @@ -282,4 +282,19 @@ where pub fn transfers_storage_key(&self) -> Result, ApiError> { >::transfers_storage_key(self, Default::default()) } + + pub fn block_fees_storage_key(&self) -> Result, ApiError> { + let has_runtime_api = sp_io::misc::runtime_version(&self.runtime_code) + .and_then(|v| RuntimeVersion::decode(&mut &v[..]).ok()) + .and_then(|runtime_version| { + runtime_version.api_version(&>::ID) + }) + .map_or(false, |runtime_api_version| runtime_api_version >= 2); + + if has_runtime_api { + >::block_fees_storage_key(self, Default::default()) + } else { + Ok(sp_domains::operator_block_fees_final_key()) + } + } } diff --git a/domains/client/domain-operator/src/fraud_proof.rs b/domains/client/domain-operator/src/fraud_proof.rs index cbdcc06899..66a9d04ea6 100644 --- a/domains/client/domain-operator/src/fraud_proof.rs +++ b/domains/client/domain-operator/src/fraud_proof.rs @@ -140,7 +140,7 @@ where bad_receipt_hash: Block::Hash, ) -> Result, FraudProofError> { let block_hash = local_receipt.domain_block_hash; - let key = sp_domains_fraud_proof::fraud_proof::operator_block_fees_final_key(); + let key = sp_domains::operator_block_fees_final_key(); let proof = self .client .read_proof(block_hash, &mut [key.as_slice()].into_iter())?; diff --git a/domains/pallets/block-fees/src/lib.rs b/domains/pallets/block-fees/src/lib.rs index 624b03452a..80ba711ac6 100644 --- a/domains/pallets/block-fees/src/lib.rs +++ b/domains/pallets/block-fees/src/lib.rs @@ -17,12 +17,17 @@ #![cfg_attr(not(feature = "std"), no_std)] +#[cfg(not(feature = "std"))] +extern crate alloc; + pub mod fees; pub use pallet::*; #[frame_support::pallet] mod pallet { + #[cfg(not(feature = "std"))] + use alloc::vec::Vec; use codec::{Codec, MaxEncodedLen}; use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; @@ -193,5 +198,10 @@ mod pallet { pub fn final_domain_transaction_byte_fee() -> T::Balance { ConsensusChainByteFee::::get().saturating_add(T::DomainChainByteFee::get()) } + + pub fn block_fees_storage_key() -> Vec { + use frame_support::storage::generator::StorageValue; + CollectedBlockFees::::storage_value_final_key().to_vec() + } } } diff --git a/domains/runtime/auto-id/src/lib.rs b/domains/runtime/auto-id/src/lib.rs index 72abce5b03..5d2c5d545b 100644 --- a/domains/runtime/auto-id/src/lib.rs +++ b/domains/runtime/auto-id/src/lib.rs @@ -808,6 +808,10 @@ impl_runtime_apis! { fn transfers_storage_key() -> Vec { Transporter::transfers_storage_key() } + + fn block_fees_storage_key() -> Vec { + BlockFees::block_fees_storage_key() + } } impl sp_messenger::MessengerApi for Runtime { diff --git a/domains/runtime/evm/src/lib.rs b/domains/runtime/evm/src/lib.rs index 93dbe9fd56..639968f579 100644 --- a/domains/runtime/evm/src/lib.rs +++ b/domains/runtime/evm/src/lib.rs @@ -1205,6 +1205,10 @@ impl_runtime_apis! { fn transfers_storage_key() -> Vec { Transporter::transfers_storage_key() } + + fn block_fees_storage_key() -> Vec { + BlockFees::block_fees_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 31ab789d82..f4e57ccd14 100644 --- a/domains/test/runtime/evm/src/lib.rs +++ b/domains/test/runtime/evm/src/lib.rs @@ -1167,6 +1167,10 @@ impl_runtime_apis! { fn transfers_storage_key() -> Vec { Transporter::transfers_storage_key() } + + fn block_fees_storage_key() -> Vec { + BlockFees::block_fees_storage_key() + } } impl sp_messenger::MessengerApi for Runtime { From 0e03641fd3f82613c7e68c61228c7b32e6cae9eb Mon Sep 17 00:00:00 2001 From: linning Date: Fri, 10 May 2024 17:45:00 +0800 Subject: [PATCH 2/4] Introduce new host function for incoming stateless fraud proof NOTE all the host function are marked as register_only meaning they are only introduced in the client side not the runtime, we can remove register_only after most of the consensus node upgraded to a release contains these host function then use these function in the runtime Signed-off-by: linning --- Cargo.lock | 1 + crates/pallet-domains/src/tests.rs | 47 ++- .../src/host_functions.rs | 269 ++++++++++++++---- crates/sp-domains-fraud-proof/src/lib.rs | 51 +++- .../src/runtime_interface.rs | 75 ++++- crates/sp-domains/src/lib.rs | 13 +- 6 files changed, 395 insertions(+), 61 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e21525cd61..be912c999d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2674,6 +2674,7 @@ dependencies = [ "sp-executive", "sp-externalities", "sp-inherents", + "sp-io", "sp-keyring", "sp-messenger", "sp-runtime", diff --git a/crates/pallet-domains/src/tests.rs b/crates/pallet-domains/src/tests.rs index 6840a8435c..6c6ba38e20 100644 --- a/crates/pallet-domains/src/tests.rs +++ b/crates/pallet-domains/src/tests.rs @@ -34,8 +34,10 @@ use sp_domains_fraud_proof::fraud_proof::{ InvalidExtrinsicsRootProof, ValidBundleDigest, }; use sp_domains_fraud_proof::{ - DomainChainAllowlistUpdateExtrinsic, FraudProofExtension, FraudProofHostFunctions, + DomainChainAllowlistUpdateExtrinsic, DomainInherentExtrinsic, DomainInherentExtrinsicData, + DomainStorageKeyRequest, FraudProofExtension, FraudProofHostFunctions, FraudProofVerificationInfoRequest, FraudProofVerificationInfoResponse, SetCodeExtrinsic, + StatelessDomainRuntimeCall, }; use sp_runtime::traits::{ AccountIdConversion, BlakeTwo256, BlockNumberProvider, Hash as HashT, IdentityLookup, One, @@ -442,6 +444,14 @@ impl FraudProofHostFunctions for MockDomainFraudProofExtension { Some(H256::random()) } + fn derive_bundle_digest_v2( + &self, + _domain_runtime_code: Vec, + _bundle_body: Vec, + ) -> Option { + Some(H256::random()) + } + fn execution_proof_check( &self, _domain_id: (u32, H256), @@ -453,6 +463,41 @@ impl FraudProofHostFunctions for MockDomainFraudProofExtension { ) -> Option> { None } + + fn check_extrinsics_in_single_context( + &self, + _domain_runtime_code: Vec, + _domain_block_id: (u32, H256), + _domain_block_state_root: H256, + _bundle_extrinsics: Vec, + _encoded_proof: Vec, + ) -> Option> { + None + } + + fn construct_domain_inherent_extrinsic( + &self, + _domain_runtime_code: Vec, + _domain_inherent_extrinsic_data: DomainInherentExtrinsicData, + ) -> Option { + None + } + + fn domain_storage_key( + &self, + _domain_runtime_code: Vec, + _req: DomainStorageKeyRequest, + ) -> Option> { + None + } + + fn domain_runtime_call( + &self, + _domain_runtime_code: Vec, + _call: StatelessDomainRuntimeCall, + ) -> Option { + None + } } pub(crate) fn new_test_ext_with_extensions() -> sp_io::TestExternalities { diff --git a/crates/sp-domains-fraud-proof/src/host_functions.rs b/crates/sp-domains-fraud-proof/src/host_functions.rs index 45bc4a3496..541041a25d 100644 --- a/crates/sp-domains-fraud-proof/src/host_functions.rs +++ b/crates/sp-domains-fraud-proof/src/host_functions.rs @@ -2,8 +2,9 @@ extern crate alloc; use crate::{ - DomainChainAllowlistUpdateExtrinsic, FraudProofVerificationInfoRequest, - FraudProofVerificationInfoResponse, SetCodeExtrinsic, StorageKeyRequest, + DomainChainAllowlistUpdateExtrinsic, DomainInherentExtrinsic, DomainInherentExtrinsicData, + DomainStorageKeyRequest, FraudProofVerificationInfoRequest, FraudProofVerificationInfoResponse, + SetCodeExtrinsic, StatelessDomainRuntimeCall, StorageKeyRequest, }; #[cfg(not(feature = "std"))] use alloc::vec::Vec; @@ -53,6 +54,7 @@ pub trait FraudProofHostFunctions: Send + Sync { ) -> Option; /// Derive the bundle digest for the given bundle body. + // TODO: remove before the new network fn derive_bundle_digest( &self, consensus_block_hash: H256, @@ -60,6 +62,13 @@ pub trait FraudProofHostFunctions: Send + Sync { bundle_body: Vec, ) -> Option; + /// Derive the bundle digest for the given bundle body. + fn derive_bundle_digest_v2( + &self, + domain_runtime_code: Vec, + bundle_body: Vec, + ) -> Option; + /// Check the execution proof fn execution_proof_check( &self, @@ -71,6 +80,34 @@ pub trait FraudProofHostFunctions: Send + Sync { call_data: &[u8], domain_runtime_code: Vec, ) -> Option>; + + fn check_extrinsics_in_single_context( + &self, + domain_runtime_code: Vec, + domain_block_id: (BlockNumber, H256), + domain_block_state_root: H256, + bundle_extrinsics: Vec, + // TODO: implement `PassBy` for `sp_trie::StorageProof` in upstream to pass it directly here + encoded_proof: Vec, + ) -> Option>; + + fn construct_domain_inherent_extrinsic( + &self, + domain_runtime_code: Vec, + domain_inherent_extrinsic_data: DomainInherentExtrinsicData, + ) -> Option; + + fn domain_storage_key( + &self, + domain_runtime_code: Vec, + req: DomainStorageKeyRequest, + ) -> Option>; + + fn domain_runtime_call( + &self, + domain_runtime_code: Vec, + call: StatelessDomainRuntimeCall, + ) -> Option; } sp_externalities::decl_extension! { @@ -289,16 +326,14 @@ where let bundle_vrf_hash = U256::from_be_bytes(bundle.sealed_header.header.proof_of_election.vrf_hash()); - let domain_stateless_runtime = - StatelessRuntime::::new(self.executor.clone(), runtime_code.into()); - - let encoded_extrinsic = opaque_extrinsic.encode(); - let extrinsic = - ::Extrinsic::decode(&mut encoded_extrinsic.as_slice()).ok()?; - - domain_stateless_runtime - .is_within_tx_range(&extrinsic, &bundle_vrf_hash, &domain_tx_range) - .ok() + self.domain_runtime_call( + runtime_code, + StatelessDomainRuntimeCall::IsTxInRange { + opaque_extrinsic, + bundle_vrf_hash, + domain_tx_range, + }, + ) } fn is_inherent_extrinsic( @@ -308,17 +343,10 @@ where opaque_extrinsic: OpaqueExtrinsic, ) -> 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()); - - let encoded_extrinsic = opaque_extrinsic.encode(); - let extrinsic = - ::Extrinsic::decode(&mut encoded_extrinsic.as_slice()).ok()?; - - domain_stateless_runtime - .is_inherent_extrinsic(&extrinsic) - .ok() + self.domain_runtime_call( + runtime_code, + StatelessDomainRuntimeCall::IsInherentExtrinsic(opaque_extrinsic), + ) } fn is_valid_xdm( @@ -358,13 +386,10 @@ where opaque_extrinsic: OpaqueExtrinsic, ) -> 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(matches!( - domain_stateless_runtime.decode_extrinsic(opaque_extrinsic), - Ok(Ok(_)) - )) + self.domain_runtime_call( + runtime_code, + StatelessDomainRuntimeCall::IsDecodableExtrinsic(opaque_extrinsic), + ) } fn storage_key( @@ -410,7 +435,7 @@ where Some(operator_stake) } - fn check_extrinsics_in_single_context( + fn check_extrinsics_in_single_context_v1( &self, consensus_block_hash: H256, domain_id: DomainId, @@ -419,27 +444,14 @@ where bundle_extrinsics: Vec, storage_proof: StorageProof, ) -> Option> { - let (domain_block_number, domain_block_hash) = domain_block_id; - let runtime_code = self.get_domain_runtime_code(consensus_block_hash, domain_id)?; - - let raw_response = self.execution_proof_check( + self.check_extrinsics_in_single_context( + runtime_code, domain_block_id, domain_block_state_root, + bundle_extrinsics, storage_proof.encode(), - CHECK_EXTRINSICS_AND_DO_PRE_DISPATCH_METHOD_NAME, - // The call data must be encoded form of arguments to `DomainCoreApi::check_extrinsic_and_do_pre_dispatch` - &(&bundle_extrinsics, &domain_block_number, &domain_block_hash).encode(), - runtime_code, - )?; - - let bundle_extrinsics_validity_response: Result<(), CheckExtrinsicsValidityError> = - Decode::decode(&mut raw_response.as_slice()).ok()?; - if let Err(bundle_extrinsic_validity_error) = bundle_extrinsics_validity_response { - Some(Some(bundle_extrinsic_validity_error.extrinsic_index)) - } else { - Some(None) - } + ) } } @@ -554,7 +566,7 @@ where extrinsics, storage_proof, } => self - .check_extrinsics_in_single_context( + .check_extrinsics_in_single_context_v1( consensus_block_hash, domain_id, (domain_block_number, domain_block_hash), @@ -562,11 +574,7 @@ where extrinsics, storage_proof, ) - .map(|transactions_check_result| { - FraudProofVerificationInfoResponse::CheckExtrinsicsInSingleContext( - transactions_check_result, - ) - }), + .map(FraudProofVerificationInfoResponse::CheckExtrinsicsInSingleContext), FraudProofVerificationInfoRequest::StorageKey { domain_id, req } => { Some(FraudProofVerificationInfoResponse::StorageKey( self.storage_key(consensus_block_hash, domain_id, req), @@ -598,6 +606,15 @@ where consensus_block_hash: H256, domain_id: DomainId, bundle_body: Vec, + ) -> Option { + let domain_runtime_code = self.get_domain_runtime_code(consensus_block_hash, domain_id)?; + self.derive_bundle_digest_v2(domain_runtime_code, bundle_body) + } + + fn derive_bundle_digest_v2( + &self, + domain_runtime_code: Vec, + bundle_body: Vec, ) -> Option { let mut extrinsics = Vec::with_capacity(bundle_body.len()); for opaque_extrinsic in bundle_body { @@ -608,7 +625,6 @@ where extrinsics.push(ext); } - let domain_runtime_code = self.get_domain_runtime_code(consensus_block_hash, domain_id)?; let domain_stateless_runtime = StatelessRuntime::::new( self.executor.clone(), domain_runtime_code.into(), @@ -667,6 +683,149 @@ where ) .ok() } + + fn check_extrinsics_in_single_context( + &self, + domain_runtime_code: Vec, + domain_block_id: (BlockNumber, H256), + domain_block_state_root: H256, + bundle_extrinsics: Vec, + encoded_proof: Vec, + ) -> Option> { + let storage_proof: StorageProof = Decode::decode(&mut encoded_proof.as_ref()).ok()?; + let (domain_block_number, domain_block_hash) = domain_block_id; + + let raw_response = self.execution_proof_check( + domain_block_id, + domain_block_state_root, + storage_proof.encode(), + CHECK_EXTRINSICS_AND_DO_PRE_DISPATCH_METHOD_NAME, + // The call data must be encoded form of arguments to `DomainCoreApi::check_extrinsic_and_do_pre_dispatch` + &(&bundle_extrinsics, &domain_block_number, &domain_block_hash).encode(), + domain_runtime_code, + )?; + + let bundle_extrinsics_validity_response: Result<(), CheckExtrinsicsValidityError> = + Decode::decode(&mut raw_response.as_slice()).ok()?; + if let Err(bundle_extrinsic_validity_error) = bundle_extrinsics_validity_response { + Some(Some(bundle_extrinsic_validity_error.extrinsic_index)) + } else { + Some(None) + } + } + + fn construct_domain_inherent_extrinsic( + &self, + domain_runtime_code: Vec, + domain_inherent_extrinsic_data: DomainInherentExtrinsicData, + ) -> Option { + let DomainInherentExtrinsicData { + timestamp, + maybe_domain_runtime_upgrade, + consensus_transaction_byte_fee, + domain_chain_allowlist, + } = domain_inherent_extrinsic_data; + + let domain_stateless_runtime = StatelessRuntime::::new( + self.executor.clone(), + domain_runtime_code.into(), + ); + + let domain_timestamp_extrinsic = domain_stateless_runtime + .construct_timestamp_extrinsic(timestamp) + .ok() + .map(|ext| ext.encode())?; + + let consensus_chain_byte_fee_extrinsic = domain_stateless_runtime + .construct_consensus_chain_byte_fee_extrinsic(consensus_transaction_byte_fee) + .ok() + .map(|ext| ext.encode())?; + + let maybe_domain_chain_allowlist_extrinsic = if !domain_chain_allowlist.is_empty() { + Some( + domain_stateless_runtime + .construct_domain_update_chain_allowlist_extrinsic(domain_chain_allowlist) + .ok() + .map(|ext| ext.encode())?, + ) + } else { + None + }; + + let maybe_domain_set_code_extrinsic = match maybe_domain_runtime_upgrade { + None => None, + Some(upgraded_runtime_code) => Some( + domain_stateless_runtime + .construct_set_code_extrinsic(upgraded_runtime_code) + .ok() + .map(|ext| ext.encode())?, + ), + }; + + Some(DomainInherentExtrinsic { + domain_timestamp_extrinsic, + maybe_domain_chain_allowlist_extrinsic, + consensus_chain_byte_fee_extrinsic, + maybe_domain_set_code_extrinsic, + }) + } + + fn domain_storage_key( + &self, + domain_runtime_code: Vec, + req: DomainStorageKeyRequest, + ) -> Option> { + let domain_stateless_runtime = StatelessRuntime::::new( + self.executor.clone(), + domain_runtime_code.into(), + ); + let key = match req { + DomainStorageKeyRequest::BlockFees => domain_stateless_runtime.block_fees_storage_key(), + DomainStorageKeyRequest::Transfers => domain_stateless_runtime.transfers_storage_key(), + } + .ok()?; + Some(key) + } + + fn domain_runtime_call( + &self, + domain_runtime_code: Vec, + call: StatelessDomainRuntimeCall, + ) -> Option { + let domain_stateless_runtime = StatelessRuntime::::new( + self.executor.clone(), + domain_runtime_code.into(), + ); + + match call { + StatelessDomainRuntimeCall::IsTxInRange { + opaque_extrinsic, + bundle_vrf_hash, + domain_tx_range, + } => { + let encoded_extrinsic = opaque_extrinsic.encode(); + let extrinsic = + ::Extrinsic::decode(&mut encoded_extrinsic.as_slice()) + .ok()?; + domain_stateless_runtime + .is_within_tx_range(&extrinsic, &bundle_vrf_hash, &domain_tx_range) + .ok() + } + StatelessDomainRuntimeCall::IsInherentExtrinsic(opaque_extrinsic) => { + let encoded_extrinsic = opaque_extrinsic.encode(); + let extrinsic = + ::Extrinsic::decode(&mut encoded_extrinsic.as_slice()) + .ok()?; + domain_stateless_runtime + .is_inherent_extrinsic(&extrinsic) + .ok() + } + StatelessDomainRuntimeCall::IsDecodableExtrinsic(opaque_extrinsic) => Some(matches!( + domain_stateless_runtime.decode_extrinsic(opaque_extrinsic), + Ok(Ok(_)) + )), + } + } } type CreateProofCheckBackedResult = Result< diff --git a/crates/sp-domains-fraud-proof/src/lib.rs b/crates/sp-domains-fraud-proof/src/lib.rs index c35ae563cc..900af6d1d4 100644 --- a/crates/sp-domains-fraud-proof/src/lib.rs +++ b/crates/sp-domains-fraud-proof/src/lib.rs @@ -47,15 +47,15 @@ pub use runtime_interface::fraud_proof_runtime_interface; pub use runtime_interface::fraud_proof_runtime_interface::HostFunctions; use scale_info::TypeInfo; use sp_core::H256; -use sp_domains::{DomainId, OperatorId}; +use sp_domains::{DomainAllowlistUpdates, DomainId, OperatorId}; use sp_runtime::traits::{Header as HeaderT, NumberFor}; use sp_runtime::transaction_validity::{InvalidTransaction, TransactionValidity}; use sp_runtime::OpaqueExtrinsic; use sp_runtime_interface::pass_by; use sp_runtime_interface::pass_by::PassBy; use sp_trie::StorageProof; -use subspace_core_primitives::Randomness; -use subspace_runtime_primitives::Balance; +use subspace_core_primitives::{Randomness, U256}; +use subspace_runtime_primitives::{Balance, Moment}; /// Custom invalid validity code for the extrinsics in pallet-domains. #[repr(u8)] @@ -348,6 +348,51 @@ impl FraudProofVerificationInfoResponse { } } +#[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone)] +pub struct DomainInherentExtrinsicData { + pub timestamp: Moment, + pub maybe_domain_runtime_upgrade: Option>, + pub consensus_transaction_byte_fee: Balance, + pub domain_chain_allowlist: DomainAllowlistUpdates, +} + +impl PassBy for DomainInherentExtrinsicData { + type PassBy = pass_by::Codec; +} + +#[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone)] +pub struct DomainInherentExtrinsic { + domain_timestamp_extrinsic: Vec, + maybe_domain_chain_allowlist_extrinsic: Option>, + consensus_chain_byte_fee_extrinsic: Vec, + maybe_domain_set_code_extrinsic: Option>, +} + +#[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone)] +pub enum DomainStorageKeyRequest { + BlockFees, + Transfers, +} + +impl PassBy for DomainStorageKeyRequest { + type PassBy = pass_by::Codec; +} + +#[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone)] +pub enum StatelessDomainRuntimeCall { + IsTxInRange { + opaque_extrinsic: OpaqueExtrinsic, + domain_tx_range: U256, + bundle_vrf_hash: U256, + }, + IsInherentExtrinsic(OpaqueExtrinsic), + IsDecodableExtrinsic(OpaqueExtrinsic), +} + +impl PassBy for StatelessDomainRuntimeCall { + type PassBy = pass_by::Codec; +} + sp_api::decl_runtime_apis! { /// API necessary for fraud proof. pub trait FraudProofApi { diff --git a/crates/sp-domains-fraud-proof/src/runtime_interface.rs b/crates/sp-domains-fraud-proof/src/runtime_interface.rs index 858ef27321..e925d69e40 100644 --- a/crates/sp-domains-fraud-proof/src/runtime_interface.rs +++ b/crates/sp-domains-fraud-proof/src/runtime_interface.rs @@ -3,7 +3,11 @@ extern crate alloc; #[cfg(feature = "std")] use crate::FraudProofExtension; -use crate::{FraudProofVerificationInfoRequest, FraudProofVerificationInfoResponse}; +use crate::{ + DomainInherentExtrinsic, DomainInherentExtrinsicData, DomainStorageKeyRequest, + FraudProofVerificationInfoRequest, FraudProofVerificationInfoResponse, + StatelessDomainRuntimeCall, +}; #[cfg(not(feature = "std"))] use alloc::vec::Vec; use domain_runtime_primitives::BlockNumber; @@ -29,6 +33,7 @@ pub trait FraudProofRuntimeInterface { } /// Derive the bundle digest for the given bundle body. + #[version(1)] fn derive_bundle_digest( &mut self, consensus_block_hash: H256, @@ -40,6 +45,18 @@ pub trait FraudProofRuntimeInterface { .derive_bundle_digest(consensus_block_hash, domain_id, bundle_body) } + /// Derive the bundle digest for the given bundle body. + #[version(2, register_only)] + fn derive_bundle_digest( + &mut self, + domain_runtime_code: Vec, + bundle_body: Vec, + ) -> Option { + self.extension::() + .expect("No `FraudProofExtension` associated for the current context!") + .derive_bundle_digest_v2(domain_runtime_code, bundle_body) + } + /// Check the execution proof // TODO: remove before the new network #[version(1)] @@ -85,4 +102,60 @@ pub trait FraudProofRuntimeInterface { domain_runtime_code, ) } + + #[version(1, register_only)] + fn check_extrinsics_in_single_context( + &mut self, + domain_runtime_code: Vec, + domain_block_id: (BlockNumber, H256), + domain_block_state_root: H256, + bundle_extrinsics: Vec, + encoded_proof: Vec, + ) -> Option> { + self.extension::() + .expect("No `FraudProofExtension` associated for the current context!") + .check_extrinsics_in_single_context( + domain_runtime_code, + domain_block_id, + domain_block_state_root, + bundle_extrinsics, + encoded_proof, + ) + } + + #[version(1, register_only)] + fn construct_domain_inherent_extrinsic( + &mut self, + domain_runtime_code: Vec, + domain_inherent_extrinsic_data: DomainInherentExtrinsicData, + ) -> Option { + self.extension::() + .expect("No `FraudProofExtension` associated for the current context!") + .construct_domain_inherent_extrinsic( + domain_runtime_code, + domain_inherent_extrinsic_data, + ) + } + + #[version(1, register_only)] + fn domain_storage_key( + &mut self, + domain_runtime_code: Vec, + req: DomainStorageKeyRequest, + ) -> Option> { + self.extension::() + .expect("No `FraudProofExtension` associated for the current context!") + .domain_storage_key(domain_runtime_code, req) + } + + #[version(1, register_only)] + fn domain_runtime_call( + &mut self, + domain_runtime_code: Vec, + call: StatelessDomainRuntimeCall, + ) -> Option { + self.extension::() + .expect("No `FraudProofExtension` associated for the current context!") + .domain_runtime_call(domain_runtime_code, call) + } } diff --git a/crates/sp-domains/src/lib.rs b/crates/sp-domains/src/lib.rs index 9978e90d5e..0680951302 100644 --- a/crates/sp-domains/src/lib.rs +++ b/crates/sp-domains/src/lib.rs @@ -1243,7 +1243,7 @@ pub type ExecutionReceiptFor = ExecutionReceipt< >; /// Domain chains allowlist updates. -#[derive(Default, Debug, Encode, Decode, PartialEq, Clone, TypeInfo)] +#[derive(Default, Debug, Encode, Decode, PartialEq, Eq, Clone, TypeInfo)] pub struct DomainAllowlistUpdates { /// Chains that are allowed to open channel with this chain. pub allow_chains: BTreeSet, @@ -1251,6 +1251,17 @@ pub struct DomainAllowlistUpdates { pub remove_chains: BTreeSet, } +impl DomainAllowlistUpdates { + pub fn is_empty(&self) -> bool { + self.allow_chains.is_empty() && self.remove_chains.is_empty() + } + + pub fn clear(&mut self) { + self.allow_chains.clear(); + self.remove_chains.clear(); + } +} + //TODO: remove there key generations from here and instead use the fraud proof host function to fetch them /// This is a representation of actual Block Fees storage in pallet-block-fees. From d4364c8ab1b007ac31e2d181ce70515f0fb1422f Mon Sep 17 00:00:00 2001 From: linning Date: Tue, 14 May 2024 00:36:30 +0800 Subject: [PATCH 3/4] Fix how domain stateless runtime getting runtime version The previous way to get runtime version is wrong and will failed in test Signed-off-by: linning --- Cargo.lock | 1 - domains/client/block-preprocessor/Cargo.toml | 1 - .../src/stateless_runtime.rs | 25 ++++++++++++++----- 3 files changed, 19 insertions(+), 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index be912c999d..e21525cd61 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2674,7 +2674,6 @@ dependencies = [ "sp-executive", "sp-externalities", "sp-inherents", - "sp-io", "sp-keyring", "sp-messenger", "sp-runtime", diff --git a/domains/client/block-preprocessor/Cargo.toml b/domains/client/block-preprocessor/Cargo.toml index 6870c6ed9e..0cfe91a2b2 100644 --- a/domains/client/block-preprocessor/Cargo.toml +++ b/domains/client/block-preprocessor/Cargo.toml @@ -26,7 +26,6 @@ sp-domains = { version = "0.1.0", path = "../../../crates/sp-domains" } sp-executive = { version = "0.1.0", path = "../../primitives/executive" } sp-externalities = { default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "808269708cf5375526755797e8f9a9986016727d" } sp-inherents = { git = "https://github.com/subspace/polkadot-sdk", rev = "808269708cf5375526755797e8f9a9986016727d" } -sp-io = { default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "808269708cf5375526755797e8f9a9986016727d" } sp-messenger = { version = "0.1.0", path = "../../primitives/messenger" } sp-runtime = { git = "https://github.com/subspace/polkadot-sdk", rev = "808269708cf5375526755797e8f9a9986016727d" } sp-state-machine = { git = "https://github.com/subspace/polkadot-sdk", rev = "808269708cf5375526755797e8f9a9986016727d" } diff --git a/domains/client/block-preprocessor/src/stateless_runtime.rs b/domains/client/block-preprocessor/src/stateless_runtime.rs index e7b0148856..7e0efb8d98 100644 --- a/domains/client/block-preprocessor/src/stateless_runtime.rs +++ b/domains/client/block-preprocessor/src/stateless_runtime.rs @@ -5,7 +5,7 @@ use sc_client_api::execution_extensions::ExtensionsFactory; use sc_executor::RuntimeVersionOf; use sp_api::{ApiError, Core, RuntimeApiInfo}; use sp_core::traits::{CallContext, CodeExecutor, FetchRuntimeCode, RuntimeCode}; -use sp_core::{Decode, Hasher}; +use sp_core::Hasher; use sp_domains::core_api::DomainCoreApi; use sp_domains::DomainAllowlistUpdates; use sp_messenger::messages::MessageKey; @@ -284,11 +284,24 @@ where } pub fn block_fees_storage_key(&self) -> Result, ApiError> { - let has_runtime_api = sp_io::misc::runtime_version(&self.runtime_code) - .and_then(|v| RuntimeVersion::decode(&mut &v[..]).ok()) - .and_then(|runtime_version| { - runtime_version.api_version(&>::ID) - }) + let runtime_version = { + let mut ext = BasicExternalities::new(self.storage.clone()); + let ext_extensions = ext.extensions(); + ext_extensions.merge( + self.extension_factory + .extensions_for(Default::default(), Default::default()), + ); + let runtime_code = self.runtime_code(); + self.executor + .runtime_version(&mut ext, &runtime_code) + .map_err(|err| { + ApiError::Application(Box::from(format!( + "failed to read domain runtime version: {err}" + ))) + })? + }; + let has_runtime_api = runtime_version + .api_version(&>::ID) .map_or(false, |runtime_api_version| runtime_api_version >= 2); if has_runtime_api { From ee979eaa8cba3ef26fe3e8ddd1b253ece0ebab5a Mon Sep 17 00:00:00 2001 From: linning Date: Wed, 15 May 2024 15:50:26 +0800 Subject: [PATCH 4/4] Update comment, minor refactoring Signed-off-by: linning --- crates/sp-domains/src/lib.rs | 2 +- domains/pallets/block-fees/src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/sp-domains/src/lib.rs b/crates/sp-domains/src/lib.rs index 0680951302..7aec659dcb 100644 --- a/crates/sp-domains/src/lib.rs +++ b/crates/sp-domains/src/lib.rs @@ -1262,7 +1262,7 @@ impl DomainAllowlistUpdates { } } -//TODO: remove there key generations from here and instead use the fraud proof host function to fetch them +//TODO: This is used to keep compatible with gemini-3h, remove before next network /// This is a representation of actual Block Fees storage in pallet-block-fees. /// Any change in key or value there should be changed here accordingly. diff --git a/domains/pallets/block-fees/src/lib.rs b/domains/pallets/block-fees/src/lib.rs index 80ba711ac6..0143e05c23 100644 --- a/domains/pallets/block-fees/src/lib.rs +++ b/domains/pallets/block-fees/src/lib.rs @@ -30,6 +30,7 @@ mod pallet { use alloc::vec::Vec; use codec::{Codec, MaxEncodedLen}; use frame_support::pallet_prelude::*; + use frame_support::storage::generator::StorageValue as _; use frame_system::pallet_prelude::*; use scale_info::TypeInfo; use sp_block_fees::{InherentError, InherentType, INHERENT_IDENTIFIER}; @@ -200,7 +201,6 @@ mod pallet { } pub fn block_fees_storage_key() -> Vec { - use frame_support::storage::generator::StorageValue; CollectedBlockFees::::storage_value_final_key().to_vec() } }