diff --git a/Cargo.lock b/Cargo.lock index 0aa76561ae..063cbd7e56 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7224,6 +7224,7 @@ dependencies = [ "scale-info", "sp-core", "sp-domains", + "sp-domains-fraud-proof", "sp-externalities", "sp-io", "sp-runtime", @@ -10821,7 +10822,6 @@ dependencies = [ name = "sp-domains" version = "0.1.0" dependencies = [ - "async-trait", "blake2", "domain-runtime-primitives", "frame-support", @@ -10839,10 +10839,8 @@ dependencies = [ "sp-blockchain", "sp-consensus-slots", "sp-core", - "sp-externalities", - "sp-inherents", - "sp-keystore", "sp-runtime", + "sp-runtime-interface", "sp-state-machine", "sp-std", "sp-trie", @@ -10853,6 +10851,27 @@ dependencies = [ "trie-db", ] +[[package]] +name = "sp-domains-fraud-proof" +version = "0.1.0" +dependencies = [ + "domain-block-preprocessor", + "hash-db 0.16.0", + "parity-scale-codec", + "scale-info", + "sp-api", + "sp-blockchain", + "sp-core", + "sp-domains", + "sp-externalities", + "sp-runtime", + "sp-runtime-interface", + "sp-std", + "sp-trie", + "subspace-core-primitives", + "trie-db", +] + [[package]] name = "sp-externalities" version = "0.19.0" @@ -11725,6 +11744,7 @@ dependencies = [ "sp-core", "sp-domain-digests", "sp-domains", + "sp-domains-fraud-proof", "sp-io", "sp-messenger", "sp-runtime", @@ -11898,6 +11918,7 @@ dependencies = [ "sp-consensus-subspace", "sp-core", "sp-domains", + "sp-domains-fraud-proof", "sp-externalities", "sp-objects", "sp-offchain", @@ -11940,6 +11961,7 @@ dependencies = [ "sp-consensus-subspace", "sp-core", "sp-domains", + "sp-domains-fraud-proof", "sp-runtime", "subspace-archiving", "subspace-core-primitives", @@ -12018,6 +12040,7 @@ dependencies = [ "parking_lot 0.12.1", "rand 0.8.5", "sc-block-builder", + "sc-client-api", "sc-consensus", "sc-consensus-fraud-proof", "sc-executor", @@ -12036,6 +12059,8 @@ dependencies = [ "sp-consensus-subspace", "sp-core", "sp-domains", + "sp-domains-fraud-proof", + "sp-externalities", "sp-inherents", "sp-keyring", "sp-runtime", diff --git a/crates/pallet-domains/Cargo.toml b/crates/pallet-domains/Cargo.toml index 86876277d8..fd7deafbfc 100644 --- a/crates/pallet-domains/Cargo.toml +++ b/crates/pallet-domains/Cargo.toml @@ -20,6 +20,7 @@ log = { version = "0.4.20", default-features = false } 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 = "c90d6edfd8c63168eff0dce6f2ace4d66dd139f7" } sp-domains = { version = "0.1.0", default-features = false, path = "../sp-domains" } +sp-domains-fraud-proof = { version = "0.1.0", default-features = false, path = "../sp-domains-fraud-proof" } sp-io = { version = "23.0.0", default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "c90d6edfd8c63168eff0dce6f2ace4d66dd139f7" } sp-runtime = { version = "24.0.0", default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "c90d6edfd8c63168eff0dce6f2ace4d66dd139f7" } sp-std = { version = "8.0.0", default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "c90d6edfd8c63168eff0dce6f2ace4d66dd139f7" } @@ -45,6 +46,7 @@ std = [ "scale-info/std", "sp-core/std", "sp-domains/std", + "sp-domains-fraud-proof/std", "sp-io/std", "sp-runtime/std", "sp-std/std", diff --git a/crates/pallet-domains/src/block_tree.rs b/crates/pallet-domains/src/block_tree.rs index 415da78faa..4d2f8189af 100644 --- a/crates/pallet-domains/src/block_tree.rs +++ b/crates/pallet-domains/src/block_tree.rs @@ -2,7 +2,7 @@ use crate::pallet::StateRoots; use crate::{ - BalanceOf, BlockTree, Config, ConsensusBlockInfo, DomainBlockDescendants, DomainBlocks, + BalanceOf, BlockTree, Config, ConsensusBlockHash, DomainBlockDescendants, DomainBlocks, ExecutionInbox, ExecutionReceiptOf, HeadReceiptNumber, InboxedBundleAuthor, }; use codec::{Decode, Encode}; @@ -169,8 +169,8 @@ pub(crate) fn verify_execution_receipt( ); let excepted_consensus_block_hash = - match ConsensusBlockInfo::::get(domain_id, consensus_block_number) { - Some((hash, _)) => hash, + match ConsensusBlockHash::::get(domain_id, consensus_block_number) { + Some(hash) => hash, // The `initialize_block` of non-system pallets is skipped in the `validate_transaction`, // thus the hash of best block, which is recorded in the this pallet's `on_initialize` hook, // is unavailable at this point. @@ -310,7 +310,7 @@ pub(crate) fn process_execution_receipt( // its receipt's `extrinsics_root` anymore. let _ = ExecutionInbox::::clear_prefix((domain_id, to_prune), u32::MAX, None); - ConsensusBlockInfo::::remove( + ConsensusBlockHash::::remove( domain_id, execution_receipt.consensus_block_number, ); @@ -469,11 +469,8 @@ mod tests { if block_number != 1 { // `ConsensusBlockHash` should be set to `Some` since last consensus block contains bundle assert_eq!( - ConsensusBlockInfo::::get(domain_id, block_number - 1), - Some(( - frame_system::Pallet::::block_hash(block_number - 1), - H256::default() - )) + ConsensusBlockHash::::get(domain_id, block_number - 1), + Some(frame_system::Pallet::::block_hash(block_number - 1)) ); // ER point to last consensus block should have `NewHead` type assert_eq!( @@ -554,7 +551,7 @@ mod tests { verify_execution_receipt::(domain_id, &pruned_receipt), Error::InvalidExtrinsicsRoots ); - assert!(ConsensusBlockInfo::::get( + assert!(ConsensusBlockHash::::get( domain_id, pruned_receipt.consensus_block_number, ) @@ -760,10 +757,10 @@ mod tests { verify_execution_receipt::(domain_id, &future_receipt), Error::UnavailableConsensusBlockHash ); - ConsensusBlockInfo::::insert( + ConsensusBlockHash::::insert( domain_id, future_receipt.consensus_block_number, - (future_receipt.consensus_block_hash, H256::default()), + future_receipt.consensus_block_hash, ); // Return `UnknownParentBlockReceipt` error as its parent receipt is missing from the block tree diff --git a/crates/pallet-domains/src/lib.rs b/crates/pallet-domains/src/lib.rs index 45a4f7b0ff..02346765c0 100644 --- a/crates/pallet-domains/src/lib.rs +++ b/crates/pallet-domains/src/lib.rs @@ -46,14 +46,17 @@ use scale_info::TypeInfo; use sp_core::H256; use sp_domains::bundle_producer_election::{is_below_threshold, BundleProducerElectionParams}; use sp_domains::fraud_proof::{FraudProof, InvalidTotalRewardsProof}; -use sp_domains::verification::{ - verify_invalid_domain_extrinsics_root_fraud_proof, verify_invalid_total_rewards_fraud_proof, -}; +use sp_domains::verification::verify_invalid_total_rewards_fraud_proof; use sp_domains::{ DomainBlockLimit, DomainId, DomainInstanceData, ExecutionReceipt, OpaqueBundle, OperatorId, OperatorPublicKey, ProofOfElection, RuntimeId, DOMAIN_EXTRINSICS_SHUFFLING_SEED_SUBJECT, EMPTY_EXTRINSIC_ROOT, }; +use sp_domains_fraud_proof::fraud_proof_runtime_interface::get_fraud_proof_verification_info; +use sp_domains_fraud_proof::verification::verify_invalid_domain_extrinsics_root_fraud_proof; +use sp_domains_fraud_proof::{ + FraudProofVerificationInfoRequest, FraudProofVerificationInfoResponse, +}; use sp_runtime::traits::{BlakeTwo256, CheckedSub, Hash, One, Zero}; use sp_runtime::{RuntimeAppPublic, SaturatedConversion, Saturating}; use sp_std::boxed::Box; @@ -141,8 +144,7 @@ mod pallet { use frame_support::{Identity, PalletError}; use frame_system::pallet_prelude::*; use sp_core::H256; - use sp_domains::fraud_proof::{DeriveExtrinsics, FraudProof, StorageKeys}; - use sp_domains::inherents::{InherentError, InherentType, INHERENT_IDENTIFIER}; + use sp_domains::fraud_proof::FraudProof; use sp_domains::transaction::InvalidTransactionCode; use sp_domains::{ BundleDigest, DomainId, EpochIndex, GenesisDomain, OperatorId, ReceiptHash, RuntimeId, @@ -160,7 +162,6 @@ mod pallet { use sp_std::vec; use sp_std::vec::Vec; use subspace_core_primitives::U256; - use subspace_runtime_primitives::Moment; #[pallet::config] pub trait Config: frame_system::Config { @@ -288,12 +289,6 @@ mod pallet { /// Randomness source. type Randomness: RandomnessT>; - - /// Trait impl to fetch storage keys. - type StorageKeys: StorageKeys; - - /// Derive extrinsics trait impl. - type DeriveExtrinsics: DeriveExtrinsics; } #[pallet::pallet] @@ -495,7 +490,7 @@ mod pallet { OptionQuery, >; - /// The consensus block hash and state root used to verify ER and storage proofs, + /// The consensus block hash used to verify ER, /// only store the consensus block hash for a domain /// if that consensus block contains bundle of the domain, the hash will be pruned when the ER /// that point to the consensus block is pruned. @@ -504,15 +499,8 @@ mod pallet { /// for more details, this will be fixed once https://github.com/subspace/subspace/issues/1731 is implemented. #[pallet::storage] #[pallet::getter(fn consensus_block_info)] - pub type ConsensusBlockInfo = StorageDoubleMap< - _, - Identity, - DomainId, - Identity, - BlockNumberFor, - (T::Hash, T::Hash), - OptionQuery, - >; + pub type ConsensusBlockHash = + StorageDoubleMap<_, Identity, DomainId, Identity, BlockNumberFor, T::Hash, OptionQuery>; /// A set of `BundleDigest` from all bundles that successfully submitted to the consensus block, /// these bundles will be used to construct the domain block and `ExecutionInbox` is used to: @@ -598,10 +586,14 @@ mod pallet { DescendantsOfFraudulentERNotPruned, /// Invalid fraud proof since total rewards are not mismatched. InvalidTotalRewardsFraudProof(sp_domains::verification::VerificationError), - /// Missing state root for a given consensus block - MissingConsensusStateRoot, /// Invalid domain extrinsic fraud proof InvalidExtrinsicRootFraudProof(sp_domains::verification::VerificationError), + /// Failed to get block randomness. + FailedToGetBlockRandomness, + /// Failed to get domain timestamp extrinsic. + FailedToGetDomainTimestampExtrinsic, + /// Received invalid Verification info from host function. + ReceivedInvalidVerificationInfo, } impl From for Error { @@ -1134,27 +1126,6 @@ mod pallet { Ok(()) } - - /// Submit parent state root to the blockchain. - #[pallet::call_index(11)] - #[pallet::weight((Weight::from_all(10_000), DispatchClass::Mandatory, Pays::No))] - pub fn store_parent_state_root( - origin: OriginFor, - parent_state_root: T::Hash, - ) -> DispatchResult { - ensure_none(origin)?; - let block_number = frame_system::Pallet::::block_number(); - let parent_number = block_number - One::one(); - - let domains_ids = DomainRegistry::::iter_keys().collect::>(); - for domain_id in domains_ids { - if let Some(info) = ConsensusBlockInfo::::get(domain_id, parent_number) { - let info = (info.0, parent_state_root); - ConsensusBlockInfo::::insert(domain_id, parent_number, info); - } - } - Ok(()) - } } #[pallet::genesis_config] @@ -1228,11 +1199,7 @@ mod pallet { let parent_number = block_number - One::one(); let parent_hash = frame_system::Pallet::::block_hash(parent_number); for (domain_id, _) in SuccessfulBundles::::drain() { - ConsensusBlockInfo::::insert( - domain_id, - parent_number, - (parent_hash, T::Hash::default()), - ); + ConsensusBlockHash::::insert(domain_id, parent_number, parent_hash); } Weight::zero() @@ -1254,42 +1221,6 @@ mod pallet { .build() } - #[pallet::inherent] - impl ProvideInherent for Pallet { - type Call = Call; - type Error = InherentError; - const INHERENT_IDENTIFIER: InherentIdentifier = INHERENT_IDENTIFIER; - - fn create_inherent(data: &InherentData) -> Option { - let inherent_data = data - .get_data::>(&INHERENT_IDENTIFIER) - .expect("Domains inherent data not correctly encoded") - .expect("Domains inherent data must be provided"); - - let parent_state_root = inherent_data.parent_state_root; - Some(Call::store_parent_state_root { parent_state_root }) - } - - fn check_inherent(call: &Self::Call, data: &InherentData) -> Result<(), Self::Error> { - if let Call::store_parent_state_root { parent_state_root } = call { - let inherent_data = data - .get_data::>(&INHERENT_IDENTIFIER) - .expect("Domains inherent data not correctly encoded") - .expect("Domains inherent data must be provided"); - - if parent_state_root != &inherent_data.parent_state_root { - return Err(InherentError::IncorrectParentStateRoot); - } - } - - Ok(()) - } - - fn is_inherent(call: &Self::Call) -> bool { - matches!(call, Call::store_parent_state_root { .. }) - } - } - #[pallet::validate_unsigned] impl ValidateUnsigned for Pallet { type Call = Call; @@ -1299,7 +1230,6 @@ mod pallet { .map_err(|_| InvalidTransaction::Call.into()), Call::submit_fraud_proof { fraud_proof } => Self::validate_fraud_proof(fraud_proof) .map_err(|_| InvalidTransaction::Call.into()), - Call::store_parent_state_root { .. } => Ok(()), _ => Err(InvalidTransaction::Call.into()), } } @@ -1340,13 +1270,6 @@ mod pallet { // TODO: proper tag value. unsigned_validity("SubspaceSubmitFraudProof", fraud_proof) } - Call::store_parent_state_root { .. } => { - ValidTransaction::with_tag_prefix("Domain Parent State root Inherent") - .priority(TransactionPriority::MAX) - .longevity(0) - .propagate(false) - .build() - } _ => InvalidTransaction::Call.into(), } @@ -1574,12 +1497,30 @@ impl Pallet { .map_err(FraudProofError::InvalidTotalRewardsFraudProof)?; } FraudProof::InvalidExtrinsicsRoot(proof) => { - let consensus_state_root = ConsensusBlockInfo::::get( - proof.domain_id, - bad_receipt.consensus_block_number, + let consensus_block_hash = bad_receipt.consensus_block_hash; + let block_randomness = match get_fraud_proof_verification_info( + H256::from_slice(consensus_block_hash.as_ref()), + FraudProofVerificationInfoRequest::BlockRandomness, ) - .ok_or(FraudProofError::MissingConsensusStateRoot)? - .1; + .ok_or(FraudProofError::FailedToGetBlockRandomness)? + { + FraudProofVerificationInfoResponse::BlockRandomness(randomness) => { + Ok(randomness) + } + _ => Err(FraudProofError::ReceivedInvalidVerificationInfo), + }?; + + let domain_timestamp_extrinsic = match get_fraud_proof_verification_info( + H256::from_slice(consensus_block_hash.as_ref()), + FraudProofVerificationInfoRequest::DomainTimestampExtrinsic(proof.domain_id), + ) + .ok_or(FraudProofError::FailedToGetDomainTimestampExtrinsic)? + { + FraudProofVerificationInfoResponse::DomainTimestampExtrinsic( + domain_timestamp_extrinsic, + ) => Ok(domain_timestamp_extrinsic), + _ => Err(FraudProofError::ReceivedInvalidVerificationInfo), + }?; verify_invalid_domain_extrinsics_root_fraud_proof::< T::Block, @@ -1587,9 +1528,13 @@ impl Pallet { T::DomainHash, BalanceOf, T::Hashing, - T::StorageKeys, - T::DeriveExtrinsics, - >(consensus_state_root, bad_receipt, proof) + T::DomainHashing, + >( + bad_receipt, + proof, + block_randomness, + domain_timestamp_extrinsic, + ) .map_err(FraudProofError::InvalidExtrinsicRootFraudProof)?; } _ => {} diff --git a/crates/pallet-domains/src/tests.rs b/crates/pallet-domains/src/tests.rs index a555940998..c54916af66 100644 --- a/crates/pallet-domains/src/tests.rs +++ b/crates/pallet-domains/src/tests.rs @@ -1,13 +1,12 @@ use crate::block_tree::DomainBlock; use crate::domain_registry::{DomainConfig, DomainObject}; use crate::{ - self as pallet_domains, BalanceOf, BlockTree, BundleError, Config, ConsensusBlockInfo, + self as pallet_domains, BalanceOf, BlockTree, BundleError, Config, ConsensusBlockHash, DomainBlocks, DomainRegistry, ExecutionInbox, ExecutionReceiptOf, FraudProofError, FungibleHoldId, HeadReceiptNumber, NextDomainId, Operator, OperatorStatus, Operators, }; use codec::{Decode, Encode, MaxEncodedLen}; use frame_support::dispatch::RawOrigin; -use frame_support::storage::generator::StorageValue; use frame_support::traits::{ConstU16, ConstU32, ConstU64, Currency, Hooks}; use frame_support::weights::Weight; use frame_support::{assert_err, assert_ok, parameter_types, PalletId}; @@ -27,10 +26,15 @@ use sp_domains::{ OperatorId, OperatorPair, ProofOfElection, ReceiptHash, RuntimeType, SealedBundleHeader, StakingHoldIdentifier, }; +use sp_domains_fraud_proof::{ + FraudProofExtension, FraudProofHostFunctions, FraudProofVerificationInfoRequest, + FraudProofVerificationInfoResponse, +}; use sp_runtime::traits::{AccountIdConversion, BlakeTwo256, Hash as HashT, IdentityLookup, Zero}; use sp_runtime::{BuildStorage, OpaqueExtrinsic}; use sp_state_machine::backend::AsTrieBackend; use sp_state_machine::{prove_read, Backend, TrieBackendBuilder}; +use sp_std::sync::Arc; use sp_trie::trie_types::TrieDBMutBuilderV1; use sp_trie::{PrefixedMemoryDB, StorageProof, TrieMut}; use sp_version::RuntimeVersion; @@ -193,20 +197,6 @@ impl frame_support::traits::Randomness for MockRandomness { } } -pub struct StorageKeys; -impl sp_domains::fraud_proof::StorageKeys for StorageKeys { - fn block_randomness_storage_key() -> StorageKey { - StorageKey( - frame_support::storage::storage_prefix("Subspace".as_ref(), "BlockRandomness".as_ref()) - .to_vec(), - ) - } - - fn timestamp_storage_key() -> StorageKey { - StorageKey(pallet_timestamp::pallet::Now::::storage_value_final_key().to_vec()) - } -} - const SLOT_DURATION: u64 = 1000; impl pallet_timestamp::Config for Test { /// A timestamp: milliseconds since the unix epoch. @@ -216,14 +206,6 @@ impl pallet_timestamp::Config for Test { type WeightInfo = (); } -pub struct DeriveExtrinsics; -impl sp_domains::fraud_proof::DeriveExtrinsics for DeriveExtrinsics { - fn derive_timestamp_extrinsic(now: Moment) -> Vec { - UncheckedExtrinsic::new_unsigned(pallet_timestamp::Call::::set { now }.into()) - .encode() - } -} - impl pallet_domains::Config for Test { type RuntimeEvent = RuntimeEvent; type DomainNumber = BlockNumber; @@ -249,8 +231,6 @@ impl pallet_domains::Config for Test { type TreasuryAccount = TreasuryAccount; type MaxPendingStakingOperation = MaxPendingStakingOperation; type Randomness = MockRandomness; - type StorageKeys = StorageKeys; - type DeriveExtrinsics = DeriveExtrinsics; } pub(crate) fn new_test_ext() -> sp_io::TestExternalities { @@ -261,6 +241,38 @@ pub(crate) fn new_test_ext() -> sp_io::TestExternalities { t.into() } +pub(crate) struct MockDomainFraudProofExtension { + block_randomness: Randomness, + timestamp: Moment, +} + +impl FraudProofHostFunctions for MockDomainFraudProofExtension { + fn get_fraud_proof_verification_info( + &self, + _consensus_block_hash: H256, + fraud_proof_verification_info_req: FraudProofVerificationInfoRequest, + ) -> Option { + let response = match fraud_proof_verification_info_req { + FraudProofVerificationInfoRequest::BlockRandomness => { + FraudProofVerificationInfoResponse::BlockRandomness(self.block_randomness) + } + FraudProofVerificationInfoRequest::DomainTimestampExtrinsic(_) => { + FraudProofVerificationInfoResponse::DomainTimestampExtrinsic( + UncheckedExtrinsic::new_unsigned( + pallet_timestamp::Call::::set { + now: self.timestamp, + } + .into(), + ) + .encode(), + ) + } + }; + + Some(response) + } +} + pub(crate) fn new_test_ext_with_extensions() -> sp_io::TestExternalities { let version = RuntimeVersion { spec_name: "test".into(), @@ -849,7 +861,7 @@ fn test_invalid_domain_extrinsic_root_proof() { let operator_id = 1u64; let head_domain_number = 10; let mut ext = new_test_ext_with_extensions(); - ext.execute_with(|| { + let fraud_proof = ext.execute_with(|| { let domain_id = register_genesis_domain(creator, vec![operator_id]); extend_block_tree(domain_id, operator_id, head_domain_number + 1); assert_eq!( @@ -875,64 +887,42 @@ fn test_invalid_domain_extrinsic_root_proof() { bad_receipt.domain_block_extrinsic_root = H256::random(); let bad_receipt_hash = bad_receipt.hash(); - let (fraud_proof, root) = generate_invalid_domain_extrinsic_root_fraud_proof::( - domain_id, - bad_receipt_hash, - valid_bundle_digests, - Randomness::from([1u8; 32]), - 1000, - ); + let fraud_proof = + generate_invalid_domain_extrinsic_root_fraud_proof::(domain_id, bad_receipt_hash); let (consensus_block_number, consensus_block_hash) = ( bad_receipt.consensus_block_number, bad_receipt.consensus_block_hash, ); - ConsensusBlockInfo::::insert( - domain_id, - consensus_block_number, - (consensus_block_hash, root), - ); + ConsensusBlockHash::::insert(domain_id, consensus_block_number, consensus_block_hash); DomainBlocks::::insert(bad_receipt_hash, domain_block); - assert_ok!(Domains::validate_fraud_proof(&fraud_proof),); + fraud_proof }); + + let fraud_proof_ext = FraudProofExtension::new(Arc::new(MockDomainFraudProofExtension { + block_randomness: Randomness::from([1u8; 32]), + timestamp: 1000, + })); + ext.register_extension(fraud_proof_ext); + + ext.execute_with(|| { + assert_ok!(Domains::validate_fraud_proof(&fraud_proof),); + }) } fn generate_invalid_domain_extrinsic_root_fraud_proof( domain_id: DomainId, bad_receipt_hash: ReceiptHash, - valid_bundle_digests: Vec, - randomness: Randomness, - moment: Moment, -) -> (FraudProof, T::Hash>, T::Hash) { - let randomness_storage_key = - frame_support::storage::storage_prefix("Subspace".as_ref(), "BlockRandomness".as_ref()) - .to_vec(); - let timestamp_storage_key = - pallet_timestamp::pallet::Now::::storage_value_final_key().to_vec(); - let mut root = T::Hash::default(); - let mut mdb = PrefixedMemoryDB::::default(); - { - let mut trie = TrieDBMutBuilderV1::new(&mut mdb, &mut root).build(); - trie.insert(&randomness_storage_key, &randomness.encode()) - .unwrap(); - trie.insert(×tamp_storage_key, &moment.encode()) - .unwrap(); - }; +) -> FraudProof, T::Hash> { + let valid_bundle_digests = vec![ValidBundleDigest { + bundle_index: 0, + bundle_digest: vec![(Some(vec![1, 2, 3]), ExtrinsicDigest::Data(vec![4, 5, 6]))], + }]; - let backend = TrieBackendBuilder::new(mdb, root).build(); - let (_, randomness_storage_proof) = - storage_proof_for_key::(backend.clone(), StorageKey(randomness_storage_key)); - let (root, timestamp_storage_proof) = - storage_proof_for_key::(backend, StorageKey(timestamp_storage_key)); - ( - FraudProof::InvalidExtrinsicsRoot(InvalidExtrinsicsRootProof { - domain_id, - bad_receipt_hash, - randomness_storage_proof, - valid_bundle_digests, - timestamp_storage_proof, - }), - root, - ) + FraudProof::InvalidExtrinsicsRoot(InvalidExtrinsicsRootProof { + domain_id, + bad_receipt_hash, + valid_bundle_digests, + }) } #[test] @@ -974,7 +964,7 @@ fn test_basic_fraud_proof_processing() { assert!( !ExecutionInbox::::get((domain_id, block_number, block_number)).is_empty() ); - assert!(ConsensusBlockInfo::::get(domain_id, block_number).is_some()); + assert!(ConsensusBlockHash::::get(domain_id, block_number).is_some()); } // Re-submit the valid ER diff --git a/crates/sp-domains-fraud-proof/Cargo.toml b/crates/sp-domains-fraud-proof/Cargo.toml new file mode 100644 index 0000000000..74d6da9e8b --- /dev/null +++ b/crates/sp-domains-fraud-proof/Cargo.toml @@ -0,0 +1,49 @@ +[package] +name = "sp-domains-fraud-proof" +description = "Domains fraud proof primitves for Consensus chain" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" +version = "0.1.0" +authors = ["Subspace Labs "] +edition = "2021" +include = [ + "/src", + "/Cargo.toml", +] + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.6.5", default-features = false, features = ["derive"] } +hash-db = { version = "0.16.0", default-features = false } +scale-info = { version = "2.7.0", default-features = false, features = ["derive"] } +domain-block-preprocessor = { version = "0.1.0", default-features = false, path = "../../domains/client/block-preprocessor", optional = true } +sp-api = { version = "4.0.0-dev", default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "c90d6edfd8c63168eff0dce6f2ace4d66dd139f7" } +sp-blockchain = { version = "4.0.0-dev", git = "https://github.com/subspace/polkadot-sdk", rev = "c90d6edfd8c63168eff0dce6f2ace4d66dd139f7", optional = true } +sp-core = { version = "21.0.0", default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "c90d6edfd8c63168eff0dce6f2ace4d66dd139f7" } +sp-domains = { version = "0.1.0", default-features = false, path = "../sp-domains" } +sp-externalities = { version = "0.19.0", default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "c90d6edfd8c63168eff0dce6f2ace4d66dd139f7" } +sp-runtime = { version = "24.0.0", default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "c90d6edfd8c63168eff0dce6f2ace4d66dd139f7" } +sp-runtime-interface = { version = "17.0.0", default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "c90d6edfd8c63168eff0dce6f2ace4d66dd139f7" } +sp-std = { version = "8.0.0", default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "c90d6edfd8c63168eff0dce6f2ace4d66dd139f7" } +sp-trie = { version = "22.0.0", default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "c90d6edfd8c63168eff0dce6f2ace4d66dd139f7" } +subspace-core-primitives = { version = "0.1.0", default-features = false, path = "../subspace-core-primitives" } +trie-db = { version = "0.28.0", default-features = false } + +[features] +default = ["std"] +std = [ + "codec/std", + "hash-db/std", + "scale-info/std", + "domain-block-preprocessor", + "sp-api/std", + "sp-blockchain", + "sp-core/std", + "sp-domains/std", + "sp-externalities/std", + "sp-runtime/std", + "sp-runtime-interface/std", + "sp-std/std", + "sp-trie/std", + "subspace-core-primitives/std", + "trie-db/std" +] +runtime-benchmarks = [] diff --git a/crates/sp-domains-fraud-proof/src/host_functions.rs b/crates/sp-domains-fraud-proof/src/host_functions.rs new file mode 100644 index 0000000000..098c143b3e --- /dev/null +++ b/crates/sp-domains-fraud-proof/src/host_functions.rs @@ -0,0 +1,131 @@ +use crate::{FraudProofVerificationInfoRequest, FraudProofVerificationInfoResponse}; +use codec::Encode; +use domain_block_preprocessor::runtime_api::InherentExtrinsicConstructor; +use domain_block_preprocessor::runtime_api_light::RuntimeApiLight; +use sp_api::{BlockT, ProvideRuntimeApi}; +use sp_blockchain::HeaderBackend; +use sp_core::traits::CodeExecutor; +use sp_core::H256; +use sp_domains::{DomainId, DomainsApi}; +use sp_runtime::traits::NumberFor; +use std::marker::PhantomData; +use std::sync::Arc; +use subspace_core_primitives::Randomness; + +/// Trait to query and verify Domains Fraud proof. +pub trait FraudProofHostFunctions: Send + Sync { + /// Returns the required verification info for the runtime to verify the Fraud proof. + fn get_fraud_proof_verification_info( + &self, + consensus_block_hash: H256, + fraud_proof_verification_req: FraudProofVerificationInfoRequest, + ) -> Option; +} + +sp_externalities::decl_extension! { + /// Domains fraud proof host function + pub struct FraudProofExtension(std::sync::Arc); +} + +impl FraudProofExtension { + /// Create a new instance of [`FraudProofExtension`]. + pub fn new(inner: std::sync::Arc) -> Self { + Self(inner) + } +} + +/// Trait Impl to query and verify Domains Fraud proof. +pub struct FraudProofHostFunctionsImpl { + consensus_client: Arc, + executor: Arc, + _phantom: PhantomData<(Block, DomainBlock)>, +} + +impl + FraudProofHostFunctionsImpl +{ + pub fn new(consensus_client: Arc, executor: Arc) -> Self { + FraudProofHostFunctionsImpl { + consensus_client, + executor, + _phantom: Default::default(), + } + } +} + +impl + FraudProofHostFunctionsImpl +where + Block: BlockT, + Block::Hash: From, + DomainBlock: BlockT, + Client: HeaderBackend + ProvideRuntimeApi, + Client::Api: DomainsApi, DomainBlock::Hash>, + Executor: CodeExecutor, +{ + fn get_block_randomness(&self, consensus_block_hash: H256) -> Option { + let runtime_api = self.consensus_client.runtime_api(); + let consensus_block_hash = consensus_block_hash.into(); + runtime_api + .extrinsics_shuffling_seed(consensus_block_hash) + .ok() + } + + fn derive_domain_timestamp_extrinsic( + &self, + consensus_block_hash: H256, + domain_id: DomainId, + ) -> Option> { + let runtime_api = self.consensus_client.runtime_api(); + let consensus_block_hash = consensus_block_hash.into(); + let runtime_code = runtime_api + .domain_runtime_code(consensus_block_hash, domain_id) + .ok()??; + let timestamp = runtime_api.timestamp(consensus_block_hash).ok()?; + + let domain_runtime_api_light = + RuntimeApiLight::new(self.executor.clone(), runtime_code.into()); + + InherentExtrinsicConstructor::::construct_timestamp_inherent_extrinsic( + &domain_runtime_api_light, + // We do not care about the domain hash since this is stateless call into + // domain runtime, + Default::default(), + timestamp, + ) + .ok() + .map(|ext| ext.encode()) + } +} + +impl FraudProofHostFunctions + for FraudProofHostFunctionsImpl +where + Block: BlockT, + Block::Hash: From, + DomainBlock: BlockT, + Client: HeaderBackend + ProvideRuntimeApi, + Client::Api: DomainsApi, DomainBlock::Hash>, + Executor: CodeExecutor, +{ + fn get_fraud_proof_verification_info( + &self, + consensus_block_hash: H256, + fraud_proof_verification_req: FraudProofVerificationInfoRequest, + ) -> Option { + match fraud_proof_verification_req { + FraudProofVerificationInfoRequest::BlockRandomness => self + .get_block_randomness(consensus_block_hash) + .map(|block_randomness| { + FraudProofVerificationInfoResponse::BlockRandomness(block_randomness) + }), + FraudProofVerificationInfoRequest::DomainTimestampExtrinsic(doman_id) => self + .derive_domain_timestamp_extrinsic(consensus_block_hash, doman_id) + .map(|domain_timestamp_extrinsic| { + FraudProofVerificationInfoResponse::DomainTimestampExtrinsic( + domain_timestamp_extrinsic, + ) + }), + } + } +} diff --git a/crates/sp-domains-fraud-proof/src/lib.rs b/crates/sp-domains-fraud-proof/src/lib.rs new file mode 100644 index 0000000000..1ac4a3cb90 --- /dev/null +++ b/crates/sp-domains-fraud-proof/src/lib.rs @@ -0,0 +1,60 @@ +// Copyright (C) 2022 Subspace Labs, Inc. +// SPDX-License-Identifier: GPL-3.0-or-later + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +//! Subspace fraud proof primitives for consensus chain. +#![cfg_attr(not(feature = "std"), no_std)] + +#[cfg(feature = "std")] +mod host_functions; +mod runtime_interface; +pub mod verification; + +use codec::{Decode, Encode}; +#[cfg(feature = "std")] +pub use host_functions::{ + FraudProofExtension, FraudProofHostFunctions, FraudProofHostFunctionsImpl, +}; +pub use runtime_interface::fraud_proof_runtime_interface; +#[cfg(feature = "std")] +pub use runtime_interface::fraud_proof_runtime_interface::HostFunctions; +use sp_api::scale_info::TypeInfo; +use sp_domains::DomainId; +use sp_runtime_interface::pass_by; +use sp_runtime_interface::pass_by::PassBy; +use sp_std::vec::Vec; +use subspace_core_primitives::Randomness; + +/// Request type to fetch required verification information for fraud proof through Host function. +#[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone)] +pub enum FraudProofVerificationInfoRequest { + /// Block randomness at a given consensus block hash. + BlockRandomness, + /// Domain timestamp extrinsic using the timestamp at a given consensus block hash. + DomainTimestampExtrinsic(DomainId), +} + +impl PassBy for FraudProofVerificationInfoRequest { + type PassBy = pass_by::Codec; +} + +/// Response holds required verification information for fraud proof from Host function. +#[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone)] +pub enum FraudProofVerificationInfoResponse { + /// Block randomness fetched from consensus state at a specific block hash. + BlockRandomness(Randomness), + /// Encoded domain timestamp extrinsic using the timestamp from consensus state at a specific block hash. + DomainTimestampExtrinsic(Vec), +} diff --git a/crates/sp-domains-fraud-proof/src/runtime_interface.rs b/crates/sp-domains-fraud-proof/src/runtime_interface.rs new file mode 100644 index 0000000000..cbe25aeefd --- /dev/null +++ b/crates/sp-domains-fraud-proof/src/runtime_interface.rs @@ -0,0 +1,22 @@ +#[cfg(feature = "std")] +use crate::FraudProofExtension; +use crate::{FraudProofVerificationInfoRequest, FraudProofVerificationInfoResponse}; +use sp_core::H256; +#[cfg(feature = "std")] +use sp_externalities::ExternalitiesExt; +use sp_runtime_interface::runtime_interface; + +/// Domain fraud proof related runtime interface +#[runtime_interface] +pub trait FraudProofRuntimeInterface { + /// Returns required fraud proof verification information to the runtime through host function. + fn get_fraud_proof_verification_info( + &mut self, + consensus_block_hash: H256, + fraud_proof_verification_req: FraudProofVerificationInfoRequest, + ) -> Option { + self.extension::() + .expect("No `FraudProofExtension` associated for the current context!") + .get_fraud_proof_verification_info(consensus_block_hash, fraud_proof_verification_req) + } +} diff --git a/crates/sp-domains-fraud-proof/src/verification.rs b/crates/sp-domains-fraud-proof/src/verification.rs new file mode 100644 index 0000000000..6800803b97 --- /dev/null +++ b/crates/sp-domains-fraud-proof/src/verification.rs @@ -0,0 +1,87 @@ +use hash_db::Hasher; +use sp_core::H256; +use sp_domains::fraud_proof::{ExtrinsicDigest, InvalidExtrinsicsRootProof}; +use sp_domains::valued_trie_root::valued_ordered_trie_root; +use sp_domains::verification::{ + deduplicate_and_shuffle_extrinsics, extrinsics_shuffling_seed, VerificationError, +}; +use sp_domains::ExecutionReceipt; +use sp_runtime::traits::{BlakeTwo256, Block, Hash, NumberFor}; +use sp_std::vec::Vec; +use sp_trie::LayoutV1; +use subspace_core_primitives::Randomness; +use trie_db::node::Value; + +pub fn verify_invalid_domain_extrinsics_root_fraud_proof< + CBlock, + DomainNumber, + DomainHash, + Balance, + Hashing, + DomainHashing, +>( + bad_receipt: ExecutionReceipt< + NumberFor, + CBlock::Hash, + DomainNumber, + DomainHash, + Balance, + >, + fraud_proof: &InvalidExtrinsicsRootProof, + block_randomness: Randomness, + domain_timestamp_extrinsic: Vec, +) -> Result<(), VerificationError> +where + CBlock: Block, + Hashing: Hasher, + DomainHashing: Hasher, + DomainHash: Into, +{ + let InvalidExtrinsicsRootProof { + valid_bundle_digests, + .. + } = fraud_proof; + + let mut bundle_extrinsics_digests = Vec::new(); + for (bad_receipt_valid_bundle_digest, bundle_digest) in bad_receipt + .valid_bundle_digests() + .into_iter() + .zip(valid_bundle_digests) + { + let bundle_digest_hash = BlakeTwo256::hash_of(&bundle_digest.bundle_digest); + if bundle_digest_hash != bad_receipt_valid_bundle_digest { + return Err(VerificationError::InvalidBundleDigest); + } + + bundle_extrinsics_digests.extend(bundle_digest.bundle_digest.clone()); + } + + let shuffling_seed = + H256::from_slice(extrinsics_shuffling_seed::(block_randomness).as_ref()); + + let mut ordered_extrinsics = deduplicate_and_shuffle_extrinsics( + bundle_extrinsics_digests, + Randomness::from(shuffling_seed.to_fixed_bytes()), + ); + + let timestamp_extrinsic = + ExtrinsicDigest::new::>(domain_timestamp_extrinsic); + ordered_extrinsics.insert(0, timestamp_extrinsic); + + let ordered_trie_node_values = ordered_extrinsics + .iter() + .map(|ext_digest| match ext_digest { + ExtrinsicDigest::Data(data) => Value::Inline(data), + ExtrinsicDigest::Hash(hash) => Value::Node(hash.0.as_slice()), + }) + .collect(); + + // TODO: domain runtime upgrade extrinsic + let extrinsics_root = + valued_ordered_trie_root::>(ordered_trie_node_values); + if bad_receipt.domain_block_extrinsic_root == extrinsics_root { + return Err(VerificationError::InvalidProof); + } + + Ok(()) +} diff --git a/crates/sp-domains/Cargo.toml b/crates/sp-domains/Cargo.toml index b7418bd7d9..6249defbdb 100644 --- a/crates/sp-domains/Cargo.toml +++ b/crates/sp-domains/Cargo.toml @@ -12,7 +12,6 @@ description = "Primitives of domains pallet" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -async-trait = { version = "0.1.73", optional = true } blake2 = { version = "0.10.6", default-features = false } domain-runtime-primitives = { version = "0.1.0", default-features = false, path = "../../domains/primitives/runtime" } frame-support = { version = "4.0.0-dev", default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "c90d6edfd8c63168eff0dce6f2ace4d66dd139f7" } @@ -29,10 +28,8 @@ sp-application-crypto = { version = "23.0.0", default-features = false, git = "h sp-blockchain = { version = "4.0.0-dev", git = "https://github.com/subspace/polkadot-sdk", rev = "c90d6edfd8c63168eff0dce6f2ace4d66dd139f7", optional = true } sp-consensus-slots = { version = "0.10.0-dev", default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "c90d6edfd8c63168eff0dce6f2ace4d66dd139f7" } sp-core = { version = "21.0.0", default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "c90d6edfd8c63168eff0dce6f2ace4d66dd139f7" } -sp-externalities = { version = "0.19.0", default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "c90d6edfd8c63168eff0dce6f2ace4d66dd139f7" } -sp-inherents = { version = "4.0.0-dev", default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "c90d6edfd8c63168eff0dce6f2ace4d66dd139f7" } -sp-keystore = { version = "0.27.0", git = "https://github.com/subspace/polkadot-sdk", rev = "c90d6edfd8c63168eff0dce6f2ace4d66dd139f7", optional = true } sp-runtime = { version = "24.0.0", default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "c90d6edfd8c63168eff0dce6f2ace4d66dd139f7" } +sp-runtime-interface = { version = "17.0.0", default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "c90d6edfd8c63168eff0dce6f2ace4d66dd139f7" } sp-state-machine = { version = "0.28.0", default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "c90d6edfd8c63168eff0dce6f2ace4d66dd139f7" } sp-std = { version = "8.0.0", default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "c90d6edfd8c63168eff0dce6f2ace4d66dd139f7" } sp-trie = { version = "22.0.0", default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "c90d6edfd8c63168eff0dce6f2ace4d66dd139f7" } @@ -49,7 +46,6 @@ rand = { version = "0.8.5", features = ["min_const_gen"] } [features] default = ["std"] std = [ - "async-trait", "blake2/std", "domain-runtime-primitives/std", "frame-support/std", @@ -65,10 +61,8 @@ std = [ "sp-application-crypto/std", "sp-consensus-slots/std", "sp-core/std", - "sp-externalities/std", - "sp-inherents/std", - "sp-keystore", "sp-runtime/std", + "sp-runtime-interface/std", "sp-state-machine/std", "sp-std/std", "sp-trie/std", diff --git a/crates/sp-domains/src/fraud_proof.rs b/crates/sp-domains/src/fraud_proof.rs index 7300d62b11..54507a448b 100644 --- a/crates/sp-domains/src/fraud_proof.rs +++ b/crates/sp-domains/src/fraud_proof.rs @@ -3,7 +3,6 @@ use hash_db::Hasher; use parity_scale_codec::{Decode, Encode}; use scale_info::TypeInfo; use sp_consensus_slots::Slot; -use sp_core::storage::StorageKey; use sp_core::H256; use sp_runtime::traits::{BlakeTwo256, Hash as HashT, Header as HeaderT}; use sp_std::vec::Vec; @@ -70,12 +69,6 @@ impl ExecutionPhase { } } -/// Trait to derive domain extrinsics such as timestamp on Consensus chain. -pub trait DeriveExtrinsics { - /// Derives pallet_timestamp::set extrinsic. - fn derive_timestamp_extrinsic(moment: Moment) -> Vec; -} - /// Error type of fraud proof verification on consensus node. #[derive(Debug)] #[cfg_attr(feature = "thiserror", derive(thiserror::Error))] @@ -436,11 +429,12 @@ pub enum ExtrinsicDigest { impl ExtrinsicDigest { pub fn new(ext: Vec) -> Self where - Layout::Hash: Hasher, + Layout::Hash: Hasher, + ::Out: Into, { if let Some(threshold) = Layout::MAX_INLINE_VALUE { if ext.len() >= threshold as usize { - ExtrinsicDigest::Hash(Layout::Hash::hash(&ext)) + ExtrinsicDigest::Hash(Layout::Hash::hash(&ext).into()) } else { ExtrinsicDigest::Data(ext) } @@ -471,10 +465,6 @@ pub struct InvalidExtrinsicsRootProof { pub bad_receipt_hash: ReceiptHash, /// Valid Bundle digests pub valid_bundle_digests: Vec, - /// Randomness Storage proof - pub randomness_storage_proof: StorageProof, - /// Timestamp Storage proof - pub timestamp_storage_proof: StorageProof, } impl InvalidTotalRewardsProof { @@ -487,12 +477,6 @@ impl InvalidTotalRewardsProof { } } -/// Trait to get Storage keys. -pub trait StorageKeys { - fn block_randomness_storage_key() -> StorageKey; - fn timestamp_storage_key() -> StorageKey; -} - /// This is a representation of actual Block Rewards storage in pallet-operator-rewards. /// Any change in key or value there should be changed here accordingly. pub fn operator_block_rewards_final_key() -> Vec { diff --git a/crates/sp-domains/src/inherents.rs b/crates/sp-domains/src/inherents.rs deleted file mode 100644 index aeff2e59d1..0000000000 --- a/crates/sp-domains/src/inherents.rs +++ /dev/null @@ -1,76 +0,0 @@ -//! Inherents for domain - -use parity_scale_codec::{Decode, Encode}; -#[cfg(feature = "std")] -use sp_inherents::{Error, InherentData}; -use sp_inherents::{InherentIdentifier, IsFatalError}; - -/// The domain inherent identifier. -pub const INHERENT_IDENTIFIER: InherentIdentifier = *b"domains_"; - -/// Errors that can occur while checking provided inherent data. -#[derive(Debug, Encode)] -#[cfg_attr(feature = "std", derive(Decode))] -pub enum InherentError { - /// Incorrect parent state root. - IncorrectParentStateRoot, -} - -impl IsFatalError for InherentError { - fn is_fatal_error(&self) -> bool { - true - } -} - -/// The type of the Domains inherent data. -#[derive(Debug, Encode, Decode)] -pub struct InherentType { - /// Parent state root. - pub parent_state_root: Hash, -} - -/// Provides the parent state root for Domains inherent data -#[cfg(feature = "std")] -pub struct InherentDataProvider { - data: InherentType, -} - -#[cfg(feature = "std")] -impl InherentDataProvider { - /// Create new inherent data provider from the given `data`. - pub fn new(parent_state_root: Hash) -> Self { - Self { - data: InherentType { parent_state_root }, - } - } - - /// Returns the `data` of this inherent data provider. - pub fn data(&self) -> &InherentType { - &self.data - } -} - -#[cfg(feature = "std")] -#[async_trait::async_trait] -impl sp_inherents::InherentDataProvider for InherentDataProvider -where - Hash: Send + Sync + Encode + Decode, -{ - async fn provide_inherent_data(&self, inherent_data: &mut InherentData) -> Result<(), Error> { - inherent_data.put_data(INHERENT_IDENTIFIER, &self.data) - } - - async fn try_handle_error( - &self, - identifier: &InherentIdentifier, - error: &[u8], - ) -> Option> { - if *identifier != INHERENT_IDENTIFIER { - return None; - } - - let error = InherentError::decode(&mut &*error).ok()?; - - Some(Err(Error::Application(Box::from(format!("{error:?}"))))) - } -} diff --git a/crates/sp-domains/src/lib.rs b/crates/sp-domains/src/lib.rs index 4a3c66783d..5a48592a8b 100644 --- a/crates/sp-domains/src/lib.rs +++ b/crates/sp-domains/src/lib.rs @@ -19,7 +19,6 @@ pub mod bundle_producer_election; pub mod fraud_proof; -pub mod inherents; pub mod merkle_tree; pub mod storage; #[cfg(test)] @@ -48,6 +47,8 @@ use sp_runtime::traits::{ BlakeTwo256, Block as BlockT, CheckedAdd, Hash as HashT, NumberFor, Zero, }; use sp_runtime::{DigestItem, OpaqueExtrinsic, Percent}; +use sp_runtime_interface::pass_by; +use sp_runtime_interface::pass_by::PassBy; use sp_std::vec::Vec; use sp_weights::Weight; use subspace_core_primitives::crypto::blake3_hash; @@ -164,6 +165,10 @@ impl DomainId { } } +impl PassBy for DomainId { + type PassBy = pass_by::Codec; +} + #[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone)] pub struct BundleHeader { /// Proof of bundle producer election. @@ -828,12 +833,6 @@ sp_api::decl_runtime_apis! { /// Returns the chain state root at the given block. fn domain_state_root(domain_id: DomainId, number: DomainNumber, hash: DomainHash) -> Option; - - /// Returns the storage key for block randomness. - fn block_randomness_storage_key() -> Vec; - - /// Returns the storage key for timestamp; - fn timestamp_storage_key() -> Vec; } pub trait BundleProducerElectionApi { diff --git a/crates/sp-domains/src/verification.rs b/crates/sp-domains/src/verification.rs index bcc0f1f46e..4658a06aad 100644 --- a/crates/sp-domains/src/verification.rs +++ b/crates/sp-domains/src/verification.rs @@ -1,7 +1,3 @@ -use crate::fraud_proof::{ - DeriveExtrinsics, ExtrinsicDigest, InvalidExtrinsicsRootProof, StorageKeys, -}; -use crate::valued_trie_root::valued_ordered_trie_root; use crate::{ExecutionReceipt, DOMAIN_EXTRINSICS_SHUFFLING_SEED_SUBJECT}; use domain_runtime_primitives::opaque::AccountId; use frame_support::PalletError; @@ -12,8 +8,7 @@ use rand::SeedableRng; use rand_chacha::ChaCha8Rng; use scale_info::TypeInfo; use sp_core::storage::StorageKey; -use sp_core::H256; -use sp_runtime::traits::{BlakeTwo256, Block, Hash, NumberFor}; +use sp_runtime::traits::{Block, NumberFor}; use sp_state_machine::trace; use sp_std::collections::btree_map::BTreeMap; use sp_std::collections::vec_deque::VecDeque; @@ -22,8 +17,6 @@ use sp_std::marker::PhantomData; use sp_std::vec::Vec; use sp_trie::{read_trie_value, LayoutV1, StorageProof}; use subspace_core_primitives::Randomness; -use subspace_runtime_primitives::Moment; -use trie_db::node::Value; /// Verification error. #[derive(Debug, PartialEq, Eq, Encode, Decode, PalletError, TypeInfo)] @@ -100,98 +93,7 @@ where Ok(()) } -pub fn verify_invalid_domain_extrinsics_root_fraud_proof< - CBlock, - DomainNumber, - DomainHash, - Balance, - Hashing, - SK, - DE, ->( - consensus_state_root: CBlock::Hash, - bad_receipt: ExecutionReceipt< - NumberFor, - CBlock::Hash, - DomainNumber, - DomainHash, - Balance, - >, - fraud_proof: &InvalidExtrinsicsRootProof, -) -> Result<(), VerificationError> -where - CBlock: Block, - Hashing: Hasher, - SK: StorageKeys, - DE: DeriveExtrinsics, -{ - let InvalidExtrinsicsRootProof { - valid_bundle_digests, - randomness_storage_proof, - timestamp_storage_proof, - .. - } = fraud_proof; - - let mut bundle_extrinsics_digests = Vec::new(); - for (bad_receipt_valid_bundle_digest, bundle_digest) in bad_receipt - .valid_bundle_digests() - .into_iter() - .zip(valid_bundle_digests) - { - let bundle_digest_hash = BlakeTwo256::hash_of(&bundle_digest.bundle_digest); - if bundle_digest_hash != bad_receipt_valid_bundle_digest { - return Err(VerificationError::InvalidBundleDigest); - } - - bundle_extrinsics_digests.extend(bundle_digest.bundle_digest.clone()); - } - - let block_randomness = StorageProofVerifier::::verify_and_get_value::( - &consensus_state_root, - randomness_storage_proof.clone(), - SK::block_randomness_storage_key(), - ) - .map_err(|_| VerificationError::InvalidProof)?; - - let timestamp = StorageProofVerifier::::verify_and_get_value::( - &consensus_state_root, - timestamp_storage_proof.clone(), - SK::timestamp_storage_key(), - ) - .map_err(|_| VerificationError::InvalidProof)?; - - let shuffling_seed = - H256::decode(&mut extrinsics_shuffling_seed::(block_randomness).as_ref()) - .map_err(|_| VerificationError::FailedToDecode)?; - - let mut ordered_extrinsics = deduplicate_and_shuffle_extrinsics( - bundle_extrinsics_digests, - Randomness::from(shuffling_seed.to_fixed_bytes()), - ); - - let timestamp_extrinsic = - ExtrinsicDigest::new::>(DE::derive_timestamp_extrinsic(timestamp)); - ordered_extrinsics.insert(0, timestamp_extrinsic); - - let ordered_trie_node_values = ordered_extrinsics - .iter() - .map(|ext_digest| match ext_digest { - ExtrinsicDigest::Data(data) => Value::Inline(data), - ExtrinsicDigest::Hash(hash) => Value::Node(hash.0.as_slice()), - }) - .collect(); - - // TODO: domain runtime upgrade extrinsic - let extrinsics_root = - valued_ordered_trie_root::>(ordered_trie_node_values); - if bad_receipt.domain_block_extrinsic_root == extrinsics_root { - return Err(VerificationError::InvalidProof); - } - - Ok(()) -} - -fn extrinsics_shuffling_seed(block_randomness: Randomness) -> Hashing::Out +pub fn extrinsics_shuffling_seed(block_randomness: Randomness) -> Hashing::Out where Hashing: Hasher, { diff --git a/crates/subspace-node/Cargo.toml b/crates/subspace-node/Cargo.toml index a2c9113e19..aa6313f9ea 100644 --- a/crates/subspace-node/Cargo.toml +++ b/crates/subspace-node/Cargo.toml @@ -9,11 +9,11 @@ build = "build.rs" homepage = "https://subspace.network" repository = "https://github.com/subspace/subspace" include = [ - "/res", - "/src", - "/build.rs", - "/Cargo.toml", - "/README.md" + "/res", + "/src", + "/build.rs", + "/Cargo.toml", + "/README.md" ] [package.metadata.docs.rs] @@ -63,6 +63,7 @@ sp-consensus-subspace = { version = "0.1.0", path = "../sp-consensus-subspace" } sp-core = { version = "21.0.0", git = "https://github.com/subspace/polkadot-sdk", rev = "c90d6edfd8c63168eff0dce6f2ace4d66dd139f7" } sp-domains = { version = "0.1.0", path = "../sp-domains" } sp-domain-digests = { version = "0.1.0", path = "../../domains/primitives/digests" } +sp-domains-fraud-proof = { version = "0.1.0", path = "../sp-domains-fraud-proof" } sp-io = { version = "23.0.0", git = "https://github.com/subspace/polkadot-sdk", rev = "c90d6edfd8c63168eff0dce6f2ace4d66dd139f7" } sp-messenger = { version = "0.1.0", path = "../../domains/primitives/messenger" } sp-runtime = { version = "24.0.0", git = "https://github.com/subspace/polkadot-sdk", rev = "c90d6edfd8c63168eff0dce6f2ace4d66dd139f7" } @@ -84,11 +85,11 @@ substrate-build-script-utils = { version = "3.0.0", git = "https://github.com/su [features] default = [] do-not-enforce-cost-of-storage = [ - "subspace-runtime/do-not-enforce-cost-of-storage" + "subspace-runtime/do-not-enforce-cost-of-storage" ] runtime-benchmarks = [ - "frame-benchmarking/runtime-benchmarks", - "frame-benchmarking-cli/runtime-benchmarks", - "subspace-runtime/runtime-benchmarks", - "evm-domain-runtime/runtime-benchmarks", + "frame-benchmarking/runtime-benchmarks", + "frame-benchmarking-cli/runtime-benchmarks", + "subspace-runtime/runtime-benchmarks", + "evm-domain-runtime/runtime-benchmarks", ] diff --git a/crates/subspace-node/src/lib.rs b/crates/subspace-node/src/lib.rs index e2e07e3c4e..8fcbbe1ec7 100644 --- a/crates/subspace-node/src/lib.rs +++ b/crates/subspace-node/src/lib.rs @@ -42,10 +42,14 @@ impl NativeExecutionDispatch for ExecutorDispatch { type ExtendHostFunctions = ( frame_benchmarking::benchmarking::HostFunctions, sp_consensus_subspace::consensus::HostFunctions, + sp_domains_fraud_proof::HostFunctions, ); /// Otherwise we only use the default Substrate host functions. #[cfg(not(feature = "runtime-benchmarks"))] - type ExtendHostFunctions = sp_consensus_subspace::consensus::HostFunctions; + type ExtendHostFunctions = ( + sp_consensus_subspace::consensus::HostFunctions, + sp_domains_fraud_proof::HostFunctions, + ); fn dispatch(method: &str, data: &[u8]) -> Option> { subspace_runtime::api::dispatch(method, data) diff --git a/crates/subspace-runtime/src/lib.rs b/crates/subspace-runtime/src/lib.rs index fa0ed7a31a..267993a488 100644 --- a/crates/subspace-runtime/src/lib.rs +++ b/crates/subspace-runtime/src/lib.rs @@ -41,7 +41,6 @@ use domain_runtime_primitives::{ BlockNumber as DomainNumber, Hash as DomainHash, MultiAccountId, TryConvertBack, }; use frame_support::inherent::ProvideInherent; -use frame_support::storage::generator::StorageValue; use frame_support::traits::{ConstU16, ConstU32, ConstU64, ConstU8, Currency, Everything, Get}; use frame_support::weights::constants::{RocksDbWeight, WEIGHT_REF_TIME_PER_SECOND}; use frame_support::weights::{ConstantMultiplier, IdentityFee, Weight}; @@ -59,7 +58,7 @@ use sp_consensus_subspace::{ Vote, }; use sp_core::crypto::{ByteArray, KeyTypeId}; -use sp_core::storage::{StateVersion, StorageKey}; +use sp_core::storage::StateVersion; use sp_core::{OpaqueMetadata, H256}; use sp_domains::bundle_producer_election::BundleProducerElectionParams; use sp_domains::{ @@ -619,27 +618,6 @@ parameter_types! { pub const MaxPendingStakingOperation: u32 = 100; } -pub struct StorageKeys; -impl sp_domains::fraud_proof::StorageKeys for StorageKeys { - fn block_randomness_storage_key() -> StorageKey { - StorageKey( - pallet_subspace::pallet::BlockRandomness::::storage_value_final_key().to_vec(), - ) - } - - fn timestamp_storage_key() -> StorageKey { - StorageKey(pallet_timestamp::pallet::Now::::storage_value_final_key().to_vec()) - } -} - -pub struct DeriveExtrinsics; -impl sp_domains::fraud_proof::DeriveExtrinsics for DeriveExtrinsics { - fn derive_timestamp_extrinsic(now: Moment) -> Vec { - UncheckedExtrinsic::new_unsigned(pallet_timestamp::Call::::set { now }.into()) - .encode() - } -} - impl pallet_domains::Config for Runtime { type RuntimeEvent = RuntimeEvent; type DomainNumber = DomainNumber; @@ -665,8 +643,6 @@ impl pallet_domains::Config for Runtime { type TreasuryAccount = TreasuryAccount; type MaxPendingStakingOperation = MaxPendingStakingOperation; type Randomness = Subspace; - type StorageKeys = StorageKeys; - type DeriveExtrinsics = DeriveExtrinsics; } pub struct StakingOnReward; @@ -1016,7 +992,6 @@ impl_runtime_apis! { fn is_inherent(ext: &::Extrinsic) -> bool { match &ext.function { - RuntimeCall::Domains(call) => Domains::is_inherent(call), RuntimeCall::Subspace(call) => Subspace::is_inherent(call), RuntimeCall::Timestamp(call) => Timestamp::is_inherent(call), _ => false, @@ -1113,14 +1088,6 @@ impl_runtime_apis! { fn domain_state_root(domain_id: DomainId, number: DomainNumber, hash: DomainHash) -> Option{ Domains::domain_state_root(domain_id, number, hash) } - - fn block_randomness_storage_key() -> Vec { - pallet_subspace::pallet::BlockRandomness::::storage_value_final_key().to_vec() - } - - fn timestamp_storage_key() -> Vec { - pallet_timestamp::pallet::Now::::storage_value_final_key().to_vec() - } } impl sp_domains::BundleProducerElectionApi for Runtime { diff --git a/crates/subspace-service/Cargo.toml b/crates/subspace-service/Cargo.toml index 256c775860..3f5d315c4a 100644 --- a/crates/subspace-service/Cargo.toml +++ b/crates/subspace-service/Cargo.toml @@ -8,8 +8,8 @@ license = "GPL-3.0-or-later" homepage = "https://subspace.network" repository = "https://github.com/subspace/subspace" include = [ - "/src", - "/Cargo.toml", + "/src", + "/Cargo.toml", ] [package.metadata.docs.rs] @@ -61,6 +61,7 @@ sp-consensus-slots = { version = "0.10.0-dev", git = "https://github.com/subspac sp-consensus-subspace = { version = "0.1.0", path = "../sp-consensus-subspace" } sp-core = { version = "21.0.0", git = "https://github.com/subspace/polkadot-sdk", rev = "c90d6edfd8c63168eff0dce6f2ace4d66dd139f7" } sp-domains = { version = "0.1.0", path = "../sp-domains" } +sp-domains-fraud-proof = { version = "0.1.0", path = "../sp-domains-fraud-proof" } sp-externalities = { version = "0.19.0", git = "https://github.com/subspace/polkadot-sdk", rev = "c90d6edfd8c63168eff0dce6f2ace4d66dd139f7" } sp-objects = { version = "0.1.0", path = "../sp-objects" } sp-offchain = { version = "4.0.0-dev", git = "https://github.com/subspace/polkadot-sdk", rev = "c90d6edfd8c63168eff0dce6f2ace4d66dd139f7" } diff --git a/crates/subspace-service/src/lib.rs b/crates/subspace-service/src/lib.rs index 2b2bca8dbd..71b9064da2 100644 --- a/crates/subspace-service/src/lib.rs +++ b/crates/subspace-service/src/lib.rs @@ -35,6 +35,7 @@ use crate::metrics::NodeMetrics; use crate::tx_pre_validator::ConsensusChainTxPreValidator; use core::sync::atomic::{AtomicU32, Ordering}; use cross_domain_message_gossip::cdm_gossip_peers_set_config; +use domain_runtime_primitives::opaque::Block as DomainBlock; use domain_runtime_primitives::{BlockNumber as DomainNumber, Hash as DomainHash}; pub use dsn::DsnConfig; use frame_system_rpc_runtime_api::AccountNonceApi; @@ -77,9 +78,11 @@ use sp_consensus_subspace::digests::extract_pre_digest; use sp_consensus_subspace::{ FarmerPublicKey, KzgExtension, PosExtension, PotExtension, PotNextSlotInput, SubspaceApi, }; -use sp_core::traits::SpawnEssentialNamed; +use sp_core::traits::{CodeExecutor, SpawnEssentialNamed}; +use sp_core::H256; use sp_domains::transaction::PreValidationObjectApi; use sp_domains::DomainsApi; +use sp_domains_fraud_proof::{FraudProofExtension, FraudProofHostFunctionsImpl}; use sp_externalities::Extensions; use sp_objects::ObjectsApi; use sp_offchain::OffchainWorkerApi; @@ -226,20 +229,25 @@ pub struct SubspaceConfiguration { pub timekeeper_cpu_cores: HashSet, } -struct SubspaceExtensionsFactory { +struct SubspaceExtensionsFactory { kzg: Kzg, client: Arc, pot_verifier: PotVerifier, - _pos_table: PhantomData, + executor: Arc, + _pos_table: PhantomData<(PosTable, DomainBlock)>, } -impl ExtensionsFactory - for SubspaceExtensionsFactory +impl ExtensionsFactory + for SubspaceExtensionsFactory where PosTable: Table, Block: BlockT, + Block::Hash: From, + DomainBlock: BlockT, Client: HeaderBackend + ProvideRuntimeApi + Send + Sync + 'static, - Client::Api: SubspaceApi, + Client::Api: SubspaceApi + + DomainsApi, DomainBlock::Hash>, + ExecutorDispatch: CodeExecutor, { fn extensions_for( &self, @@ -352,6 +360,14 @@ where } }) })); + + exts.register(FraudProofExtension::new(Arc::new( + FraudProofHostFunctionsImpl::<_, _, DomainBlock, ExecutorDispatch>::new( + self.client.clone(), + self.executor.clone(), + ), + ))); + exts } } @@ -448,14 +464,24 @@ where PotSeed::from_genesis(client.info().genesis_hash.as_ref(), pot_external_entropy), POT_VERIFIER_CACHE_SIZE, ); - client.execution_extensions().set_extensions_factory( - SubspaceExtensionsFactory:: { + + let invalid_state_transition_proof_verifier = InvalidStateTransitionProofVerifier::new( + client.clone(), + executor.clone(), + VerifierClient::new(client.clone()), + ); + + let executor = Arc::new(executor); + + client + .execution_extensions() + .set_extensions_factory(SubspaceExtensionsFactory:: { kzg: kzg.clone(), client: Arc::clone(&client), pot_verifier: pot_verifier.clone(), + executor: executor.clone(), _pos_table: PhantomData, - }, - ); + }); let telemetry = telemetry.map(|(worker, telemetry)| { task_manager @@ -468,13 +494,7 @@ where let invalid_transaction_proof_verifier = InvalidTransactionProofVerifier::new( client.clone(), - Arc::new(executor.clone()), - VerifierClient::new(client.clone()), - ); - - let invalid_state_transition_proof_verifier = InvalidStateTransitionProofVerifier::new( - client.clone(), - executor, + executor.clone(), VerifierClient::new(client.clone()), ); @@ -527,7 +547,6 @@ where .expect("Parent header must always exist when block is created; qed"); let parent_block_number = parent_header.number; - let parent_state_root = parent_header.state_root; let subspace_inherents = sp_consensus_subspace::inherents::InherentDataProvider::from_timestamp_and_slot_duration( @@ -536,10 +555,7 @@ where subspace_link.segment_headers_for_block(parent_block_number + 1), ); - let domain_inherents = - sp_domains::inherents::InherentDataProvider::new(parent_state_root); - - Ok((timestamp, subspace_inherents, domain_inherents)) + Ok((timestamp, subspace_inherents)) } } }, @@ -997,7 +1013,6 @@ where .expect("Parent header must always exist when block is created; qed"); let parent_block_number = parent_header.number; - let parent_state_root = parent_header.state_root; let subspace_inherents = sp_consensus_subspace::inherents::InherentDataProvider::from_timestamp_and_slot_duration( @@ -1006,10 +1021,7 @@ where subspace_link.segment_headers_for_block(parent_block_number + 1), ); - let domain_inherents = - sp_domains::inherents::InherentDataProvider::new(parent_state_root); - - Ok((subspace_inherents, timestamp, domain_inherents)) + Ok((subspace_inherents, timestamp)) } } }, diff --git a/domains/client/block-preprocessor/src/inherents.rs b/domains/client/block-preprocessor/src/inherents.rs index 4c93c1b5da..0bfe9b0815 100644 --- a/domains/client/block-preprocessor/src/inherents.rs +++ b/domains/client/block-preprocessor/src/inherents.rs @@ -42,12 +42,9 @@ where .runtime_api() .timestamp(consensus_block_hash)?; - let mut inherent_exts = vec![]; - if let Some(inherent_timestamp) = - domain_runtime_api.construct_timestamp_inherent_extrinsic(domain_parent_hash, moment)? - { - inherent_exts.push(inherent_timestamp) - } + let inherent_exts = + vec![domain_runtime_api + .construct_timestamp_inherent_extrinsic(domain_parent_hash, moment)?]; Ok(inherent_exts) } diff --git a/domains/client/block-preprocessor/src/runtime_api.rs b/domains/client/block-preprocessor/src/runtime_api.rs index 4f54e86e08..bd5f5d477c 100644 --- a/domains/client/block-preprocessor/src/runtime_api.rs +++ b/domains/client/block-preprocessor/src/runtime_api.rs @@ -26,7 +26,7 @@ pub trait InherentExtrinsicConstructor { &self, at: Block::Hash, moment: subspace_runtime_primitives::Moment, - ) -> Result, ApiError>; + ) -> Result; } /// Trait to wrap the new domain runtime as an extrinsic of diff --git a/domains/client/block-preprocessor/src/runtime_api_full.rs b/domains/client/block-preprocessor/src/runtime_api_full.rs index 3afd53c177..d1f5acab99 100644 --- a/domains/client/block-preprocessor/src/runtime_api_full.rs +++ b/domains/client/block-preprocessor/src/runtime_api_full.rs @@ -58,7 +58,7 @@ where &self, at: Block::Hash, moment: Moment, - ) -> Result, ApiError> { + ) -> Result { let api = self.client.runtime_api(); api.construct_inherent_timestamp_extrinsic(at, moment) } diff --git a/domains/client/block-preprocessor/src/runtime_api_light.rs b/domains/client/block-preprocessor/src/runtime_api_light.rs index 69ba683668..4241531339 100644 --- a/domains/client/block-preprocessor/src/runtime_api_light.rs +++ b/domains/client/block-preprocessor/src/runtime_api_light.rs @@ -216,7 +216,7 @@ where &self, at: Block::Hash, moment: Moment, - ) -> Result, ApiError> { + ) -> Result { >::construct_inherent_timestamp_extrinsic( self, at, moment, ) diff --git a/domains/client/domain-operator/src/fraud_proof.rs b/domains/client/domain-operator/src/fraud_proof.rs index d44254a870..99140456c3 100644 --- a/domains/client/domain-operator/src/fraud_proof.rs +++ b/domains/client/domain-operator/src/fraud_proof.rs @@ -218,28 +218,11 @@ where }); } - let consensus_runtime = self.consensus_client.runtime_api(); - let block_randomness_storage_key = - consensus_runtime.block_randomness_storage_key(consensus_block_hash)?; - let randomness_storage_proof = self.consensus_client.read_proof( - consensus_block_hash, - &mut [block_randomness_storage_key.as_slice()].into_iter(), - )?; - - let timestamp_storage_key = - consensus_runtime.timestamp_storage_key(consensus_block_hash)?; - let timestamp_storage_proof = self.consensus_client.read_proof( - consensus_block_hash, - &mut [timestamp_storage_key.as_slice()].into_iter(), - )?; - Ok(FraudProof::InvalidExtrinsicsRoot( InvalidExtrinsicsRootProof { domain_id, bad_receipt_hash, valid_bundle_digests, - randomness_storage_proof, - timestamp_storage_proof, }, )) } diff --git a/domains/primitives/runtime/src/lib.rs b/domains/primitives/runtime/src/lib.rs index b7c1755a82..fc6952186c 100644 --- a/domains/primitives/runtime/src/lib.rs +++ b/domains/primitives/runtime/src/lib.rs @@ -203,6 +203,6 @@ sp_api::decl_runtime_apis! { /// Api that construct inherent extrinsics. pub trait InherentExtrinsicApi { /// Api to construct inherent timestamp extrinsic from given time - fn construct_inherent_timestamp_extrinsic(moment: Moment) -> Option; + fn construct_inherent_timestamp_extrinsic(moment: Moment) -> Block::Extrinsic; } } diff --git a/domains/runtime/evm/src/lib.rs b/domains/runtime/evm/src/lib.rs index 2e6f2e9793..7feedceeef 100644 --- a/domains/runtime/evm/src/lib.rs +++ b/domains/runtime/evm/src/lib.rs @@ -884,12 +884,10 @@ impl_runtime_apis! { } impl domain_runtime_primitives::InherentExtrinsicApi for Runtime { - fn construct_inherent_timestamp_extrinsic(moment: Moment) -> Option<::Extrinsic> { - Some( - UncheckedExtrinsic::new_unsigned( - pallet_timestamp::Call::set{ now: moment }.into() - ) - ) + fn construct_inherent_timestamp_extrinsic(moment: Moment) -> ::Extrinsic { + UncheckedExtrinsic::new_unsigned( + pallet_timestamp::Call::set{ now: moment }.into() + ) } } diff --git a/domains/test/runtime/evm/src/lib.rs b/domains/test/runtime/evm/src/lib.rs index 8311c73d9a..57e11b0920 100644 --- a/domains/test/runtime/evm/src/lib.rs +++ b/domains/test/runtime/evm/src/lib.rs @@ -881,12 +881,10 @@ impl_runtime_apis! { } impl domain_runtime_primitives::InherentExtrinsicApi for Runtime { - fn construct_inherent_timestamp_extrinsic(moment: Moment) -> Option<::Extrinsic> { - Some( - UncheckedExtrinsic::new_unsigned( - pallet_timestamp::Call::set{ now: moment }.into() - ) - ) + fn construct_inherent_timestamp_extrinsic(moment: Moment) -> ::Extrinsic { + UncheckedExtrinsic::new_unsigned( + pallet_timestamp::Call::set{ now: moment }.into() + ) } } diff --git a/test/subspace-test-client/Cargo.toml b/test/subspace-test-client/Cargo.toml index 6504dd0077..7e258716a8 100644 --- a/test/subspace-test-client/Cargo.toml +++ b/test/subspace-test-client/Cargo.toml @@ -7,8 +7,8 @@ license = "GPL-3.0-or-later" homepage = "https://subspace.network" repository = "https://github.com/subspace/subspace" include = [ - "/src", - "/Cargo.toml", + "/src", + "/Cargo.toml", ] [package.metadata.docs.rs] @@ -29,6 +29,7 @@ sp-api = { git = "https://github.com/subspace/polkadot-sdk", rev = "c90d6edfd8c6 sp-consensus-subspace = { version = "0.1.0", path = "../../crates/sp-consensus-subspace" } sp-core = { git = "https://github.com/subspace/polkadot-sdk", rev = "c90d6edfd8c63168eff0dce6f2ace4d66dd139f7" } sp-domains = { version = "0.1.0", path = "../../crates/sp-domains" } +sp-domains-fraud-proof = { version = "0.1.0", path = "../../crates/sp-domains-fraud-proof" } sp-runtime = { git = "https://github.com/subspace/polkadot-sdk", rev = "c90d6edfd8c63168eff0dce6f2ace4d66dd139f7" } subspace-archiving = { path = "../../crates/subspace-archiving" } subspace-core-primitives = { path = "../../crates/subspace-core-primitives" } @@ -42,5 +43,5 @@ zeroize = "1.6.0" [features] do-not-enforce-cost-of-storage = [ - "subspace-test-runtime/do-not-enforce-cost-of-storage", + "subspace-test-runtime/do-not-enforce-cost-of-storage", ] diff --git a/test/subspace-test-client/src/lib.rs b/test/subspace-test-client/src/lib.rs index 6ea15579dd..502df44d17 100644 --- a/test/subspace-test-client/src/lib.rs +++ b/test/subspace-test-client/src/lib.rs @@ -54,7 +54,10 @@ const MAX_PIECES_IN_SECTOR: u16 = 32; pub struct TestExecutorDispatch; impl sc_executor::NativeExecutionDispatch for TestExecutorDispatch { - type ExtendHostFunctions = sp_consensus_subspace::consensus::HostFunctions; + type ExtendHostFunctions = ( + sp_consensus_subspace::consensus::HostFunctions, + sp_domains_fraud_proof::HostFunctions, + ); fn dispatch(method: &str, data: &[u8]) -> Option> { subspace_test_runtime::api::dispatch(method, data) diff --git a/test/subspace-test-runtime/src/lib.rs b/test/subspace-test-runtime/src/lib.rs index 0c1de22aea..e3c719d964 100644 --- a/test/subspace-test-runtime/src/lib.rs +++ b/test/subspace-test-runtime/src/lib.rs @@ -29,7 +29,6 @@ use domain_runtime_primitives::{ BlockNumber as DomainNumber, Hash as DomainHash, MultiAccountId, TryConvertBack, }; use frame_support::inherent::ProvideInherent; -use frame_support::storage::generator::StorageValue; use frame_support::traits::{ ConstU128, ConstU16, ConstU32, ConstU64, ConstU8, Currency, ExistenceRequirement, Get, Imbalance, WithdrawReasons, @@ -52,7 +51,7 @@ use sp_consensus_subspace::{ Vote, }; use sp_core::crypto::{ByteArray, KeyTypeId}; -use sp_core::storage::{StateVersion, StorageKey}; +use sp_core::storage::StateVersion; use sp_core::{Hasher, OpaqueMetadata, H256}; use sp_domains::bundle_producer_election::BundleProducerElectionParams; use sp_domains::fraud_proof::FraudProof; @@ -643,27 +642,6 @@ parameter_types! { pub const MaxPendingStakingOperation: u32 = 100; } -pub struct StorageKeys; -impl sp_domains::fraud_proof::StorageKeys for StorageKeys { - fn block_randomness_storage_key() -> StorageKey { - StorageKey( - pallet_subspace::pallet::BlockRandomness::::storage_value_final_key().to_vec(), - ) - } - - fn timestamp_storage_key() -> StorageKey { - StorageKey(pallet_timestamp::pallet::Now::::storage_value_final_key().to_vec()) - } -} - -pub struct DeriveExtrinsics; -impl sp_domains::fraud_proof::DeriveExtrinsics for DeriveExtrinsics { - fn derive_timestamp_extrinsic(now: Moment) -> Vec { - UncheckedExtrinsic::new_unsigned(pallet_timestamp::Call::::set { now }.into()) - .encode() - } -} - impl pallet_domains::Config for Runtime { type RuntimeEvent = RuntimeEvent; type DomainNumber = DomainNumber; @@ -689,8 +667,6 @@ impl pallet_domains::Config for Runtime { type TreasuryAccount = TreasuryAccount; type MaxPendingStakingOperation = MaxPendingStakingOperation; type Randomness = Subspace; - type StorageKeys = StorageKeys; - type DeriveExtrinsics = DeriveExtrinsics; } parameter_types! { @@ -1305,7 +1281,6 @@ impl_runtime_apis! { fn is_inherent(ext: &::Extrinsic) -> bool { match &ext.function { - RuntimeCall::Domains(call) => Domains::is_inherent(call), RuntimeCall::Subspace(call) => Subspace::is_inherent(call), RuntimeCall::Timestamp(call) => Timestamp::is_inherent(call), _ => false, @@ -1402,14 +1377,6 @@ impl_runtime_apis! { fn domain_state_root(domain_id: DomainId, number: DomainNumber, hash: DomainHash) -> Option{ Domains::domain_state_root(domain_id, number, hash) } - - fn block_randomness_storage_key() -> Vec { - pallet_subspace::pallet::BlockRandomness::::storage_value_final_key().to_vec() - } - - fn timestamp_storage_key() -> Vec { - pallet_timestamp::pallet::Now::::storage_value_final_key().to_vec() - } } impl sp_domains::BundleProducerElectionApi for Runtime { diff --git a/test/subspace-test-service/Cargo.toml b/test/subspace-test-service/Cargo.toml index fe437045c7..fb8388f36a 100644 --- a/test/subspace-test-service/Cargo.toml +++ b/test/subspace-test-service/Cargo.toml @@ -7,8 +7,8 @@ license = "GPL-3.0-or-later" homepage = "https://subspace.network" repository = "https://github.com/subspace/subspace" include = [ - "/src", - "/Cargo.toml", + "/src", + "/Cargo.toml", ] [package.metadata.docs.rs] @@ -26,6 +26,7 @@ rand = "0.8.5" pallet-domains = { version = "0.1.0", path = "../../crates/pallet-domains" } parking_lot = "0.12.1" sc-block-builder = { version = "0.10.0-dev", git = "https://github.com/subspace/polkadot-sdk", rev = "c90d6edfd8c63168eff0dce6f2ace4d66dd139f7" } +sc-client-api = { git = "https://github.com/subspace/polkadot-sdk", rev = "c90d6edfd8c63168eff0dce6f2ace4d66dd139f7" } sc-consensus = { version = "0.10.0-dev", git = "https://github.com/subspace/polkadot-sdk", rev = "c90d6edfd8c63168eff0dce6f2ace4d66dd139f7" } sc-executor = { git = "https://github.com/subspace/polkadot-sdk", rev = "c90d6edfd8c63168eff0dce6f2ace4d66dd139f7" } sc-consensus-fraud-proof = { version = "0.1.0", path = "../../crates/sc-consensus-fraud-proof" } @@ -44,6 +45,8 @@ sp-consensus = { git = "https://github.com/subspace/polkadot-sdk", rev = "c90d6e sp-consensus-subspace = { version = "0.1.0", path = "../../crates/sp-consensus-subspace" } sp-consensus-slots = { version = "0.10.0-dev", default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "c90d6edfd8c63168eff0dce6f2ace4d66dd139f7" } sp-domains = { version = "0.1.0", path = "../../crates/sp-domains" } +sp-domains-fraud-proof = { version = "0.1.0", path = "../../crates/sp-domains-fraud-proof" } +sp-externalities = { version = "0.19.0", git = "https://github.com/subspace/polkadot-sdk", rev = "c90d6edfd8c63168eff0dce6f2ace4d66dd139f7" } sp-keyring = { git = "https://github.com/subspace/polkadot-sdk", rev = "c90d6edfd8c63168eff0dce6f2ace4d66dd139f7" } sp-timestamp = { version = "4.0.0-dev", git = "https://github.com/subspace/polkadot-sdk", rev = "c90d6edfd8c63168eff0dce6f2ace4d66dd139f7" } sp-inherents = { version = "4.0.0-dev", git = "https://github.com/subspace/polkadot-sdk", rev = "c90d6edfd8c63168eff0dce6f2ace4d66dd139f7" } @@ -63,5 +66,5 @@ sp-keyring = { git = "https://github.com/subspace/polkadot-sdk", rev = "c90d6edf [features] do-not-enforce-cost-of-storage = [ - "subspace-test-runtime/do-not-enforce-cost-of-storage", + "subspace-test-runtime/do-not-enforce-cost-of-storage", ] diff --git a/test/subspace-test-service/src/lib.rs b/test/subspace-test-service/src/lib.rs index c1f04ea962..09e6c742fe 100644 --- a/test/subspace-test-service/src/lib.rs +++ b/test/subspace-test-service/src/lib.rs @@ -20,12 +20,15 @@ use codec::{Decode, Encode}; use cross_domain_message_gossip::GossipWorkerBuilder; +use domain_runtime_primitives::opaque::Block as DomainBlock; use domain_runtime_primitives::BlockNumber as DomainNumber; use futures::channel::mpsc; use futures::{select, FutureExt, StreamExt}; use jsonrpsee::RpcModule; use parking_lot::Mutex; use sc_block_builder::BlockBuilderProvider; +use sc_client_api::execution_extensions::ExtensionsFactory; +use sc_client_api::ExecutorProvider; use sc_consensus::block_import::{ BlockCheckParams, BlockImportParams, ForkChoiceStrategy, ImportResult, }; @@ -54,9 +57,11 @@ use sp_consensus::{BlockOrigin, Error as ConsensusError}; use sp_consensus_slots::Slot; use sp_consensus_subspace::digests::{CompatibleDigestItem, PreDigest, PreDigestPotInfo}; use sp_consensus_subspace::FarmerPublicKey; -use sp_core::traits::SpawnEssentialNamed; +use sp_core::traits::{CodeExecutor, SpawnEssentialNamed}; use sp_core::H256; -use sp_domains::OpaqueBundle; +use sp_domains::{DomainsApi, OpaqueBundle}; +use sp_domains_fraud_proof::{FraudProofExtension, FraudProofHostFunctionsImpl}; +use sp_externalities::Extensions; use sp_inherents::{InherentData, InherentDataProvider}; use sp_keyring::Sr25519Keyring; use sp_runtime::generic::{BlockId, Digest}; @@ -171,6 +176,47 @@ type StorageChanges = sp_api::StorageChanges; type TxPreValidator = ConsensusChainTxPreValidator; +struct MockExtensionsFactory { + consensus_client: Arc, + executor: Arc, + _phantom: PhantomData, +} + +impl MockExtensionsFactory { + fn new(consensus_client: Arc, executor: Arc) -> Self { + Self { + consensus_client, + executor, + _phantom: Default::default(), + } + } +} + +impl ExtensionsFactory + for MockExtensionsFactory +where + Block: BlockT, + Block::Hash: From, + DomainBlock: BlockT, + Client: HeaderBackend + ProvideRuntimeApi + 'static, + Client::Api: DomainsApi, DomainBlock::Hash>, + Executor: CodeExecutor, +{ + fn extensions_for( + &self, + _block_hash: Block::Hash, + _block_number: NumberFor, + ) -> Extensions { + let mut exts = Extensions::new(); + exts.register(FraudProofExtension::new(Arc::new( + FraudProofHostFunctionsImpl::<_, _, DomainBlock, Executor>::new( + self.consensus_client.clone(), + self.executor.clone(), + ), + ))); + exts + } +} /// A mock Subspace consensus node instance used for testing. pub struct MockConsensusNode { /// `TaskManager`'s instance. @@ -241,6 +287,12 @@ impl MockConsensusNode { .expect("Fail to new full parts"); let client = Arc::new(client); + client + .execution_extensions() + .set_extensions_factory(MockExtensionsFactory::<_, DomainBlock, _>::new( + client.clone(), + Arc::new(executor.clone()), + )); let select_chain = sc_consensus::LongestChain::new(backend.clone()); @@ -547,19 +599,14 @@ impl MockConsensusNode { extrinsics } - async fn mock_inherent_data( - slot: Slot, - parent_state_root: ::Hash, - ) -> Result> { + async fn mock_inherent_data(slot: Slot) -> Result> { let timestamp = sp_timestamp::InherentDataProvider::new(Timestamp::new( >::into(slot) * SLOT_DURATION, )); let subspace_inherents = sp_consensus_subspace::inherents::InherentDataProvider::new(slot, vec![]); - let domain_inherents = sp_domains::inherents::InherentDataProvider::new(parent_state_root); - - let inherent_data = (subspace_inherents, timestamp, domain_inherents) + let inherent_data = (subspace_inherents, timestamp) .create_inherent_data() .await?; @@ -588,9 +635,8 @@ impl MockConsensusNode { extrinsics: Vec<::Extrinsic>, ) -> Result<(Block, StorageChanges), Box> { let digest = self.mock_subspace_digest(slot); - let parent_state_root = self.client.header(parent_hash)?.unwrap().state_root; - let inherent_data = Self::mock_inherent_data(slot, parent_state_root).await?; + let inherent_data = Self::mock_inherent_data(slot).await?; let mut block_builder = self.client.new_block_at(parent_hash, digest, false)?;