diff --git a/Cargo.lock b/Cargo.lock index f3f8ec7c5e..15c0cc3c85 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -14028,6 +14028,7 @@ dependencies = [ "log", "mimalloc", "pallet-domains", + "pallet-subspace", "pallet-sudo", "pallet-transaction-payment", "parity-scale-codec", @@ -14527,6 +14528,7 @@ dependencies = [ "jsonrpsee 0.24.8", "mmr-gadget", "pallet-domains", + "pallet-subspace", "pallet-transaction-payment", "parity-scale-codec", "parking_lot 0.12.3", diff --git a/crates/pallet-subspace/src/extensions.rs b/crates/pallet-subspace/src/extensions.rs new file mode 100644 index 0000000000..705c23231d --- /dev/null +++ b/crates/pallet-subspace/src/extensions.rs @@ -0,0 +1,181 @@ +//! Extensions for unsigned general extrinsics + +#[cfg(feature = "runtime-benchmarks")] +pub mod benchmarking; +pub mod weights; + +use crate::extensions::weights::WeightInfo; +use crate::pallet::Call as SubspaceCall; +use crate::{Config, Pallet as Subspace}; +use codec::{Decode, Encode}; +use frame_support::pallet_prelude::{PhantomData, TypeInfo, Weight}; +use frame_support::RuntimeDebugNoBound; +use frame_system::pallet_prelude::{BlockNumberFor, RuntimeCallFor}; +use scale_info::prelude::fmt; +use sp_consensus_subspace::SignedVote; +use sp_runtime::traits::{ + AsSystemOriginSigner, DispatchInfoOf, DispatchOriginOf, Dispatchable, Implication, + PostDispatchInfoOf, TransactionExtension, ValidateResult, +}; +use sp_runtime::transaction_validity::{ + InvalidTransaction, TransactionSource, TransactionValidityError, ValidTransaction, +}; +use sp_runtime::DispatchResult; + +/// Trait to convert Runtime call to possible Subspace call. +pub trait MaybeSubspaceCall +where + Runtime: Config, +{ + fn maybe_subspace_call(&self) -> Option<&SubspaceCall>; +} + +/// Weight info used by this extension +#[derive(RuntimeDebugNoBound)] +pub enum ExtensionWeightData { + /// Represents the validated call's used weight + Validated(Weight), + /// Skipped validation + Skipped, +} + +/// Extensions for pallet-subspace unsigned extrinsics. +#[derive(Encode, Decode, Clone, Eq, PartialEq, TypeInfo)] +pub struct SubspaceExtension(PhantomData); + +impl SubspaceExtension { + pub fn new() -> Self { + Self(PhantomData) + } +} + +impl Default for SubspaceExtension { + fn default() -> Self { + Self::new() + } +} + +impl fmt::Debug for SubspaceExtension { + #[cfg(feature = "std")] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "SubspaceExtension",) + } + + #[cfg(not(feature = "std"))] + fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result { + Ok(()) + } +} + +impl SubspaceExtension +where + Runtime: Config + scale_info::TypeInfo + fmt::Debug + Send + Sync, +{ + fn do_check_vote( + signed_vote: &SignedVote, Runtime::Hash, Runtime::AccountId>, + source: TransactionSource, + ) -> Result<(ValidTransaction, Weight), TransactionValidityError> { + let pre_dispatch = source == TransactionSource::InBlock; + if pre_dispatch { + Subspace::::pre_dispatch_vote(signed_vote) + .map(|weight| (ValidTransaction::default(), weight)) + } else { + Subspace::::validate_vote(signed_vote) + } + } + + fn max_weight() -> Weight { + ::ExtensionWeightInfo::vote() + .max(::ExtensionWeightInfo::vote_with_equivocation()) + } +} + +impl TransactionExtension> for SubspaceExtension +where + Runtime: Config + scale_info::TypeInfo + fmt::Debug + Send + Sync, + as Dispatchable>::RuntimeOrigin: + AsSystemOriginSigner<::AccountId> + Clone, + RuntimeCallFor: MaybeSubspaceCall, +{ + const IDENTIFIER: &'static str = "SubspaceExtension"; + type Implicit = (); + type Val = ExtensionWeightData; + type Pre = ExtensionWeightData; + + fn weight(&self, call: &RuntimeCallFor) -> Weight { + match call.maybe_subspace_call() { + Some(SubspaceCall::vote { .. }) => Self::max_weight(), + _ => Weight::zero(), + } + } + + fn validate( + &self, + origin: DispatchOriginOf>, + call: &RuntimeCallFor, + _info: &DispatchInfoOf>, + _len: usize, + _self_implicit: Self::Implicit, + _inherited_implication: &impl Implication, + source: TransactionSource, + ) -> ValidateResult> { + // we only care about unsigned calls + if origin.as_system_origin_signer().is_some() { + return Ok(( + ValidTransaction::default(), + ExtensionWeightData::Skipped, + origin, + )); + }; + + let subspace_call = match call.maybe_subspace_call() { + Some(subspace_call) => subspace_call, + None => { + return Ok(( + ValidTransaction::default(), + ExtensionWeightData::Skipped, + origin, + )) + } + }; + + let (validity, weight_used) = match subspace_call { + SubspaceCall::vote { signed_vote } => Self::do_check_vote(signed_vote, source)?, + _ => return Err(InvalidTransaction::Call.into()), + }; + + Ok(( + validity, + ExtensionWeightData::Validated(weight_used), + origin, + )) + } + + fn prepare( + self, + val: Self::Val, + _origin: &DispatchOriginOf>, + _call: &RuntimeCallFor, + _info: &DispatchInfoOf>, + _len: usize, + ) -> Result { + Ok(val) + } + + fn post_dispatch_details( + pre: Self::Pre, + _info: &DispatchInfoOf>, + _post_info: &PostDispatchInfoOf>, + _len: usize, + _result: &DispatchResult, + ) -> Result { + match pre { + // return the unused weight for a validated call. + ExtensionWeightData::Validated(used_weight) => { + Ok(Self::max_weight().saturating_sub(used_weight)) + } + // return no weight since this call is not validated and took no weight. + ExtensionWeightData::Skipped => Ok(Weight::zero()), + } + } +} diff --git a/crates/pallet-subspace/src/extensions/benchmarking.rs b/crates/pallet-subspace/src/extensions/benchmarking.rs new file mode 100644 index 0000000000..e9a8f37ff7 --- /dev/null +++ b/crates/pallet-subspace/src/extensions/benchmarking.rs @@ -0,0 +1,147 @@ +//! Benchmarks for Subspace extension + +use crate::extensions::SubspaceExtension; +use crate::pallet::{ + BlockSlots, CurrentBlockAuthorInfo, CurrentBlockVoters, ParentVoteVerificationData, + SegmentCommitment as SubspaceSegmentCommitment, SolutionRanges, +}; +use crate::{Config, Pallet as Subspace, VoteVerificationData}; +use codec::{Decode, Encode}; +use frame_benchmarking::v2::*; +use frame_support::dispatch::{DispatchInfo, PostDispatchInfo}; +use frame_system::pallet_prelude::RuntimeCallFor; +use scale_info::prelude::fmt; +use sp_consensus_slots::Slot; +use sp_consensus_subspace::SignedVote; +use sp_runtime::traits::{AsSystemOriginSigner, Dispatchable, NumberFor}; +use sp_runtime::transaction_validity::TransactionSource; +use sp_std::collections::btree_map::BTreeMap; +use subspace_core_primitives::pieces::PieceOffset; +use subspace_core_primitives::sectors::SectorIndex; +use subspace_core_primitives::segments::{SegmentCommitment, SegmentIndex}; +use subspace_core_primitives::solutions::RewardSignature; +use subspace_core_primitives::{PublicKey, ScalarBytes}; + +/// Hard-coded data used to benchmark check_vote +#[derive(Encode, Decode)] +struct VoteData { + pub segment_index: SegmentIndex, + pub segment_commitment: SegmentCommitment, + pub signed_vote: SignedVote, T::Hash, T::AccountId>, + pub public_key: PublicKey, + pub parent_block_data: (NumberFor, T::Hash), + pub current_block_number: NumberFor, + pub vote_verification_data: VoteVerificationData, + pub current_slot: Slot, + pub reward_address: T::AccountId, +} + +pub struct Pallet(Subspace); + +#[allow(clippy::multiple_bound_locations)] +#[benchmarks(where + T: Send + Sync + scale_info::TypeInfo + fmt::Debug, + RuntimeCallFor: Dispatchable, + as Dispatchable>::RuntimeOrigin: AsSystemOriginSigner<::AccountId> + Clone) +] +mod benchmarks { + use super::*; + + #[benchmark] + fn vote() { + let VoteData { + segment_index, + segment_commitment, + signed_vote, + public_key: _, + parent_block_data, + current_block_number, + vote_verification_data, + current_slot, + reward_address: _, + } = VoteData::::decode(&mut include_bytes!("./fixtures/vote.data").as_slice()).unwrap(); + + SubspaceSegmentCommitment::::insert(segment_index, segment_commitment); + + // Reset so that any solution works for votes + SolutionRanges::::mutate(|solution_ranges| { + solution_ranges.current = u64::MIN; + solution_ranges.voting_current = u64::MAX; + }); + + frame_system::Pallet::::set_block_number(current_block_number); + frame_system::pallet::BlockHash::::insert(parent_block_data.0, parent_block_data.1); + ParentVoteVerificationData::::put(vote_verification_data); + BlockSlots::::mutate(|block_slots| { + block_slots + .try_insert(current_block_number, current_slot) + .expect("one entry just removed before inserting; qed"); + }); + CurrentBlockVoters::::put(BTreeMap::< + (PublicKey, SectorIndex, PieceOffset, ScalarBytes, Slot), + (Option, RewardSignature), + >::default()); + + #[block] + { + SubspaceExtension::::do_check_vote(&signed_vote, TransactionSource::InBlock) + .unwrap(); + } + } + + #[benchmark] + fn vote_with_equivocation() { + let VoteData { + segment_index, + segment_commitment, + signed_vote, + public_key, + parent_block_data, + current_block_number, + vote_verification_data, + current_slot, + reward_address, + } = VoteData::::decode(&mut include_bytes!("./fixtures/vote.data").as_slice()).unwrap(); + + SubspaceSegmentCommitment::::insert(segment_index, segment_commitment); + + // Reset so that any solution works for votes + SolutionRanges::::mutate(|solution_ranges| { + solution_ranges.current = u64::MIN; + solution_ranges.voting_current = u64::MAX; + }); + + frame_system::Pallet::::set_block_number(current_block_number); + frame_system::pallet::BlockHash::::insert(parent_block_data.0, parent_block_data.1); + ParentVoteVerificationData::::put(vote_verification_data); + BlockSlots::::mutate(|block_slots| { + block_slots + .try_insert(current_block_number, current_slot) + .expect("one entry just removed before inserting; qed"); + }); + CurrentBlockVoters::::put(BTreeMap::< + (PublicKey, SectorIndex, PieceOffset, ScalarBytes, Slot), + (Option, RewardSignature), + >::default()); + + CurrentBlockAuthorInfo::::put(( + public_key, + signed_vote.vote.solution().sector_index, + signed_vote.vote.solution().piece_offset, + signed_vote.vote.solution().chunk, + *signed_vote.vote.slot(), + Some(reward_address), + )); + #[block] + { + SubspaceExtension::::do_check_vote(&signed_vote, TransactionSource::InBlock) + .unwrap(); + } + } + + impl_benchmark_test_suite!( + Pallet, + crate::mock::new_test_ext(crate::mock::allow_all_pot_extension()), + crate::mock::Test + ); +} diff --git a/crates/pallet-subspace/src/extensions/fixtures/vote.data b/crates/pallet-subspace/src/extensions/fixtures/vote.data new file mode 100644 index 0000000000..2941eb2bdc Binary files /dev/null and b/crates/pallet-subspace/src/extensions/fixtures/vote.data differ diff --git a/crates/pallet-subspace/src/extensions/weights.rs b/crates/pallet-subspace/src/extensions/weights.rs new file mode 100644 index 0000000000..0c91bae21a --- /dev/null +++ b/crates/pallet-subspace/src/extensions/weights.rs @@ -0,0 +1,99 @@ + +//! Autogenerated weights for `pallet_subspace_extension` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 46.0.0 +//! DATE: 2025-02-27, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `mac-mini.local`, CPU: `` +//! WASM-EXECUTION: `Compiled`, CHAIN: `None`, DB CACHE: 1024 + +// Executed Command: +// ./target/release/subspace-node +// benchmark +// pallet +// --runtime=./target/release/wbuild/subspace-runtime/subspace_runtime.compact.compressed.wasm +// --genesis-builder=runtime +// --steps=50 +// --repeat=20 +// --pallet=pallet_subspace_extension +// --extrinsic=* +// --wasm-execution=compiled +// --heap-pages=4096 +// --output=./crates/pallet-subspace/src/extensions/weights.rs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::Weight}; +use core::marker::PhantomData; + +/// Weight functions needed for pallet subspace extension. +pub trait WeightInfo { + fn vote() -> Weight; + fn vote_with_equivocation() -> Weight; +} + +/// Weight functions for `pallet_subspace_extension`. +pub struct SubstrateWeight(PhantomData); +impl WeightInfo for SubstrateWeight { + /// Storage: `System::BlockHash` (r:1 w:0) + /// Proof: `System::BlockHash` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) + /// Storage: `Subspace::SolutionRanges` (r:1 w:0) + /// Proof: `Subspace::SolutionRanges` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Subspace::BlockSlots` (r:1 w:0) + /// Proof: `Subspace::BlockSlots` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Subspace::ParentVoteVerificationData` (r:1 w:0) + /// Proof: `Subspace::ParentVoteVerificationData` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Subspace::SegmentCommitment` (r:2 w:0) + /// Proof: `Subspace::SegmentCommitment` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Subspace::CounterForSegmentCommitment` (r:1 w:0) + /// Proof: `Subspace::CounterForSegmentCommitment` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `Subspace::ParentBlockAuthorInfo` (r:1 w:0) + /// Proof: `Subspace::ParentBlockAuthorInfo` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Subspace::CurrentBlockAuthorInfo` (r:1 w:0) + /// Proof: `Subspace::CurrentBlockAuthorInfo` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Subspace::ParentBlockVoters` (r:1 w:0) + /// Proof: `Subspace::ParentBlockVoters` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Subspace::CurrentBlockVoters` (r:1 w:1) + /// Proof: `Subspace::CurrentBlockVoters` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn vote() -> Weight { + // Proof Size summary in bytes: + // Measured: `397` + // Estimated: `6337` + // Minimum execution time: 1_921_000_000 picoseconds. + Weight::from_parts(1_926_000_000, 0) + .saturating_add(Weight::from_parts(0, 6337)) + .saturating_add(T::DbWeight::get().reads(11)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: `System::BlockHash` (r:1 w:0) + /// Proof: `System::BlockHash` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) + /// Storage: `Subspace::SolutionRanges` (r:1 w:0) + /// Proof: `Subspace::SolutionRanges` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Subspace::BlockSlots` (r:1 w:0) + /// Proof: `Subspace::BlockSlots` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Subspace::ParentVoteVerificationData` (r:1 w:0) + /// Proof: `Subspace::ParentVoteVerificationData` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Subspace::SegmentCommitment` (r:2 w:0) + /// Proof: `Subspace::SegmentCommitment` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `Subspace::CounterForSegmentCommitment` (r:1 w:0) + /// Proof: `Subspace::CounterForSegmentCommitment` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `Subspace::ParentBlockAuthorInfo` (r:1 w:0) + /// Proof: `Subspace::ParentBlockAuthorInfo` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Subspace::CurrentBlockAuthorInfo` (r:1 w:1) + /// Proof: `Subspace::CurrentBlockAuthorInfo` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Subspace::CurrentBlockVoters` (r:1 w:1) + /// Proof: `Subspace::CurrentBlockVoters` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn vote_with_equivocation() -> Weight { + // Proof Size summary in bytes: + // Measured: `532` + // Estimated: `6472` + // Minimum execution time: 1_927_000_000 picoseconds. + Weight::from_parts(1_935_000_000, 0) + .saturating_add(Weight::from_parts(0, 6472)) + .saturating_add(T::DbWeight::get().reads(10)) + .saturating_add(T::DbWeight::get().writes(2)) + } +} diff --git a/crates/pallet-subspace/src/lib.rs b/crates/pallet-subspace/src/lib.rs index c69096b736..137c06e78f 100644 --- a/crates/pallet-subspace/src/lib.rs +++ b/crates/pallet-subspace/src/lib.rs @@ -14,15 +14,17 @@ mod tests; #[cfg(feature = "runtime-benchmarks")] mod benchmarking; +pub mod extensions; pub mod weights; +use crate::extensions::weights::WeightInfo as ExtensionWeightInfo; #[cfg(not(feature = "std"))] use alloc::string::String; use codec::{Decode, Encode, MaxEncodedLen}; use core::num::NonZeroU64; use frame_support::dispatch::DispatchResult; use frame_support::traits::Get; -use frame_system::offchain::{CreateInherent, SubmitTransaction}; +use frame_system::offchain::SubmitTransaction; use frame_system::pallet_prelude::*; use log::{debug, error, warn}; pub use pallet::*; @@ -40,6 +42,7 @@ use sp_runtime::transaction_validity::{ InvalidTransaction, TransactionPriority, TransactionSource, TransactionValidity, TransactionValidityError, ValidTransaction, }; +use sp_runtime::Weight; use sp_std::collections::btree_map::BTreeMap; use sp_std::prelude::*; use subspace_core_primitives::pieces::PieceOffset; @@ -51,6 +54,7 @@ use subspace_core_primitives::solutions::{RewardSignature, SolutionRange}; use subspace_core_primitives::{ BlockHash, PublicKey, ScalarBytes, SlotNumber, REWARD_SIGNING_CONTEXT, }; +use subspace_runtime_primitives::CreateUnsigned; use subspace_verification::{ check_reward_signature, derive_next_solution_range, derive_pot_entropy, PieceCheckParams, VerifySolutionParams, @@ -85,7 +89,7 @@ struct VoteVerificationData { #[frame_support::pallet] pub mod pallet { - use super::{EraChangeTrigger, VoteVerificationData}; + use super::{EraChangeTrigger, ExtensionWeightInfo, VoteVerificationData}; use crate::weights::WeightInfo; use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; @@ -223,6 +227,9 @@ pub mod pallet { /// Maximum number of block number to block slot mappings to keep (oldest pruned first). #[pallet::constant] type BlockSlotCount: Get; + + /// Extension weight information for the pallet's extensions. + type ExtensionWeightInfo: ExtensionWeightInfo; } #[derive(Debug, Default, Encode, Decode, TypeInfo)] @@ -682,7 +689,6 @@ pub mod pallet { Call::store_segment_headers { segment_headers } => { Self::validate_segment_header(source, segment_headers) } - Call::vote { signed_vote } => Self::validate_vote(signed_vote), _ => InvalidTransaction::Call.into(), } } @@ -692,7 +698,6 @@ pub mod pallet { Call::store_segment_headers { segment_headers } => { Self::pre_dispatch_segment_header(segment_headers) } - Call::vote { signed_vote } => Self::pre_dispatch_vote(signed_vote), _ => Err(InvalidTransaction::Call.into()), } } @@ -1187,16 +1192,16 @@ impl Pallet { impl Pallet where - T: Config + CreateInherent>, + T: Config + CreateUnsigned>, { - /// Submit farmer vote vote that is essentially a header with bigger solution range than + /// Submit farmer vote that is essentially a header with bigger solution range than /// acceptable for block authoring. pub fn submit_vote(signed_vote: SignedVote, T::Hash, T::AccountId>) { let call = Call::vote { signed_vote: Box::new(signed_vote), }; - let ext = T::create_inherent(call.into()); + let ext = T::create_unsigned(call.into()); match SubmitTransaction::>::submit_transaction(ext) { Ok(()) => { debug!(target: "runtime::subspace", "Submitted Subspace vote"); @@ -1251,7 +1256,7 @@ impl Pallet { fn validate_vote( signed_vote: &SignedVote, T::Hash, T::AccountId>, - ) -> TransactionValidity { + ) -> Result<(ValidTransaction, Weight), TransactionValidityError> { check_vote::(signed_vote, false)?; ValidTransaction::with_tag_prefix("SubspaceVote") @@ -1261,16 +1266,17 @@ impl Pallet { .longevity(2) .and_provides(signed_vote.signature) .build() + .map(|validity| (validity, T::ExtensionWeightInfo::vote())) } fn pre_dispatch_vote( signed_vote: &SignedVote, T::Hash, T::AccountId>, - ) -> Result<(), TransactionValidityError> { + ) -> Result { match check_vote::(signed_vote, true) { - Ok(()) => Ok(()), + Ok(()) => Ok(T::ExtensionWeightInfo::vote()), Err(CheckVoteError::Equivocated { .. }) => { // Return Ok such that changes from this pre-dispatch are persisted - Ok(()) + Ok(T::ExtensionWeightInfo::vote_with_equivocation()) } Err(error) => Err(error.into()), } diff --git a/crates/pallet-subspace/src/mock.rs b/crates/pallet-subspace/src/mock.rs index b5a5bc4947..292517f312 100644 --- a/crates/pallet-subspace/src/mock.rs +++ b/crates/pallet-subspace/src/mock.rs @@ -146,6 +146,7 @@ impl Config for Test { type BlockSlotCount = BlockSlotCount; type WeightInfo = (); + type ExtensionWeightInfo = crate::extensions::weights::SubstrateWeight; } pub fn go_to_block( diff --git a/crates/sp-consensus-subspace/Cargo.toml b/crates/sp-consensus-subspace/Cargo.toml index 41908ac07f..9b0a13d04f 100644 --- a/crates/sp-consensus-subspace/Cargo.toml +++ b/crates/sp-consensus-subspace/Cargo.toml @@ -61,3 +61,5 @@ std = [ "subspace-verification/std", "thiserror/std", ] + +runtime-benchmarks = [] diff --git a/crates/sp-consensus-subspace/src/lib.rs b/crates/sp-consensus-subspace/src/lib.rs index 6d4c6e5c71..927cc1d1b3 100644 --- a/crates/sp-consensus-subspace/src/lib.rs +++ b/crates/sp-consensus-subspace/src/lib.rs @@ -34,8 +34,6 @@ use subspace_core_primitives::{BlockHash, BlockNumber, PublicKey, SlotNumber}; #[cfg(feature = "std")] use subspace_kzg::Kzg; #[cfg(feature = "std")] -use subspace_proof_of_space::chia::ChiaTable; -#[cfg(feature = "std")] use subspace_proof_of_space::shim::ShimTable; #[cfg(feature = "std")] use subspace_proof_of_space::PosTableType; @@ -460,34 +458,53 @@ pub trait Consensus { slot: SlotNumber, params: WrappedVerifySolutionParams<'_>, ) -> Result { - use sp_externalities::ExternalitiesExt; - use subspace_proof_of_space::PosTableType; - - let pos_table_type = self - .extension::() - .expect("No `PosExtension` associated for the current context!") - .0; - - let kzg = &self - .extension::() - .expect("No `KzgExtension` associated for the current context!") - .0; - - match pos_table_type { - PosTableType::Chia => subspace_verification::verify_solution::( - &solution.0, - slot, - ¶ms.0, - kzg, - ) - .map_err(|error| error.to_string()), - PosTableType::Shim => subspace_verification::verify_solution::( + // TODO: we need to conditional compile for benchmarks here since + // benchmark externalities does not provide custom extensions. + // Remove this once the issue is resolved: https://github.com/paritytech/polkadot-sdk/issues/137 + #[cfg(feature = "runtime-benchmarks")] + { + subspace_verification::verify_solution::( &solution.0, slot, ¶ms.0, - kzg, + kzg_instance(), ) - .map_err(|error| error.to_string()), + .map_err(|error| error.to_string()) + } + + #[cfg(not(feature = "runtime-benchmarks"))] + { + use sp_externalities::ExternalitiesExt; + #[cfg(feature = "std")] + use subspace_proof_of_space::chia::ChiaTable; + use subspace_proof_of_space::PosTableType; + + let pos_table_type = self + .extension::() + .expect("No `PosExtension` associated for the current context!") + .0; + + let kzg = &self + .extension::() + .expect("No `KzgExtension` associated for the current context!") + .0; + + match pos_table_type { + PosTableType::Chia => subspace_verification::verify_solution::( + &solution.0, + slot, + ¶ms.0, + kzg, + ) + .map_err(|error| error.to_string()), + PosTableType::Shim => subspace_verification::verify_solution::( + &solution.0, + slot, + ¶ms.0, + kzg, + ) + .map_err(|error| error.to_string()), + } } } @@ -500,14 +517,26 @@ pub trait Consensus { proof_of_time: WrappedPotOutput, quick_verification: bool, ) -> bool { - use sp_externalities::ExternalitiesExt; + // TODO: we need to conditional compile for benchmarks here since + // benchmark externalities does not provide custom extensions. + // Remove this once the issue is resolved: https://github.com/paritytech/polkadot-sdk/issues/137 + #[cfg(feature = "runtime-benchmarks")] + { + let _ = (slot, parent_hash, proof_of_time, quick_verification); + true + } + + #[cfg(not(feature = "runtime-benchmarks"))] + { + use sp_externalities::ExternalitiesExt; - let verifier = &self - .extension::() - .expect("No `PotExtension` associated for the current context!") - .0; + let verifier = &self + .extension::() + .expect("No `PotExtension` associated for the current context!") + .0; - verifier(parent_hash, slot, proof_of_time.0, quick_verification) + verifier(parent_hash, slot, proof_of_time.0, quick_verification) + } } } @@ -552,7 +581,7 @@ sp_api::decl_runtime_apis! { /// Solution ranges. fn solution_ranges() -> SolutionRanges; - /// Submit farmer vote vote that is essentially a header with bigger solution range than + /// Submit farmer vote that is essentially a header with bigger solution range than /// acceptable for block authoring. Only useful in an offchain context. fn submit_vote_extrinsic( signed_vote: SignedVote< @@ -587,3 +616,11 @@ sp_api::decl_runtime_apis! { fn chain_constants() -> ChainConstants; } } + +#[cfg(all(feature = "std", feature = "runtime-benchmarks"))] +fn kzg_instance() -> &'static Kzg { + use std::sync::OnceLock; + static KZG: OnceLock = OnceLock::new(); + + KZG.get_or_init(Kzg::new) +} diff --git a/crates/subspace-malicious-operator/Cargo.toml b/crates/subspace-malicious-operator/Cargo.toml index aa68f26b28..ddace90414 100644 --- a/crates/subspace-malicious-operator/Cargo.toml +++ b/crates/subspace-malicious-operator/Cargo.toml @@ -38,6 +38,7 @@ mimalloc = "0.1.43" pallet-domains = { version = "0.1.0", default-features = false, path = "../pallet-domains" } pallet-transaction-payment = { default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "949bb3cfb64ab974e4fc49328fb3e81c96bc0fbe" } parity-scale-codec = "3.6.12" +pallet-subspace = { version = "0.1.0", default-features = false, path = "../pallet-subspace" } pallet-sudo = { default-features = false, git = "https://github.com/subspace/polkadot-sdk", rev = "949bb3cfb64ab974e4fc49328fb3e81c96bc0fbe" } sc-chain-spec = { git = "https://github.com/subspace/polkadot-sdk", rev = "949bb3cfb64ab974e4fc49328fb3e81c96bc0fbe" } sc-cli = { git = "https://github.com/subspace/polkadot-sdk", rev = "949bb3cfb64ab974e4fc49328fb3e81c96bc0fbe", default-features = false } diff --git a/crates/subspace-malicious-operator/src/malicious_bundle_producer.rs b/crates/subspace-malicious-operator/src/malicious_bundle_producer.rs index 56709287ac..38e474b6fd 100644 --- a/crates/subspace-malicious-operator/src/malicious_bundle_producer.rs +++ b/crates/subspace-malicious-operator/src/malicious_bundle_producer.rs @@ -416,7 +416,8 @@ pub fn construct_signed_extrinsic( frame_system::CheckWeight::::new(), pallet_transaction_payment::ChargeTransactionPayment::::from(0u128), DisablePallets, - subspace_runtime_primitives::extensions::DisableGeneralExtrinsics::::new(), + pallet_subspace::extensions::SubspaceExtension::::new(), + subspace_runtime_primitives::extensions::CheckAllowedGeneralExtrinsics::::new(), ); let raw_payload = generic::SignedPayload::::from_raw( call.clone(), @@ -432,6 +433,7 @@ pub fn construct_signed_extrinsic( (), (), (), + (), ), ); diff --git a/crates/subspace-runtime-primitives/src/extensions.rs b/crates/subspace-runtime-primitives/src/extensions.rs index cfd831e611..034d94e465 100644 --- a/crates/subspace-runtime-primitives/src/extensions.rs +++ b/crates/subspace-runtime-primitives/src/extensions.rs @@ -1,5 +1,6 @@ //! Extensions for Runtimes +use crate::AllowedUnsignedExtrinsics; use codec::{Decode, Encode}; use frame_support::pallet_prelude::{PhantomData, TypeInfo}; use frame_system::pallet_prelude::RuntimeCallFor; @@ -12,28 +13,26 @@ use sp_runtime::traits::{ }; use sp_runtime::transaction_validity::{InvalidTransaction, TransactionSource, ValidTransaction}; -/// Disable General Extrinsics until we migrate from Bare to General. -// TODO: Should either adapt or remove use of this extension during and after migration to -// General Extrinsics from bare extrinsics +/// Checks allowed General Extrinsics #[derive(Encode, Decode, Clone, Eq, PartialEq, TypeInfo)] -pub struct DisableGeneralExtrinsics(PhantomData); +pub struct CheckAllowedGeneralExtrinsics(PhantomData); -impl DisableGeneralExtrinsics { +impl CheckAllowedGeneralExtrinsics { pub fn new() -> Self { Self(PhantomData) } } -impl Default for DisableGeneralExtrinsics { +impl Default for CheckAllowedGeneralExtrinsics { fn default() -> Self { Self::new() } } -impl fmt::Debug for DisableGeneralExtrinsics { +impl fmt::Debug for CheckAllowedGeneralExtrinsics { #[cfg(feature = "std")] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "DisableGeneralExtrinsics",) + write!(f, "CheckAllowedGeneralExtrinsics",) } #[cfg(not(feature = "std"))] @@ -42,13 +41,15 @@ impl fmt::Debug for DisableGeneralExtrinsics { } } -impl TransactionExtension> for DisableGeneralExtrinsics +impl TransactionExtension> + for CheckAllowedGeneralExtrinsics where Runtime: Config + scale_info::TypeInfo + fmt::Debug + Send + Sync, as Dispatchable>::RuntimeOrigin: AsSystemOriginSigner<::AccountId> + Clone, + RuntimeCallFor: AllowedUnsignedExtrinsics, { - const IDENTIFIER: &'static str = "DisableGeneralExtrinsics"; + const IDENTIFIER: &'static str = "CheckAllowedGeneralExtrinsics"; type Implicit = (); type Val = (); type Pre = (); @@ -56,14 +57,14 @@ where fn validate( &self, origin: DispatchOriginOf>, - _call: &RuntimeCallFor, + call: &RuntimeCallFor, _info: &DispatchInfoOf>, _len: usize, _self_implicit: Self::Implicit, _inherited_implication: &impl Implication, _source: TransactionSource, ) -> ValidateResult> { - if origin.as_system_origin_signer().is_none() { + if origin.as_system_origin_signer().is_none() && !call.is_allowed_unsigned() { Err(InvalidTransaction::Call.into()) } else { Ok((ValidTransaction::default(), (), origin)) diff --git a/crates/subspace-runtime-primitives/src/lib.rs b/crates/subspace-runtime-primitives/src/lib.rs index 1eb674e3ac..210f14cb0c 100644 --- a/crates/subspace-runtime-primitives/src/lib.rs +++ b/crates/subspace-runtime-primitives/src/lib.rs @@ -17,6 +17,7 @@ use frame_support::traits::tokens; use frame_support::weights::constants::WEIGHT_REF_TIME_PER_SECOND; use frame_support::{Deserialize, Serialize}; use frame_system::limits::BlockLength; +use frame_system::offchain::CreateTransactionBase; use pallet_transaction_payment::{Multiplier, TargetedFeeAdjustment}; use scale_info::TypeInfo; use sp_core::parameter_types; @@ -254,6 +255,17 @@ pub enum HoldIdentifier { Preimage, } +/// Interface for creating an unsigned general extrinsic +pub trait CreateUnsigned: CreateTransactionBase { + /// Create an unsigned extrinsic. + fn create_unsigned(call: Self::RuntimeCall) -> Self::Extrinsic; +} + +/// Interface for checking allowed unsigned general extrinsics +pub trait AllowedUnsignedExtrinsics { + fn is_allowed_unsigned(&self) -> bool; +} + #[cfg(feature = "testing")] pub mod tests_utils { use frame_support::dispatch::DispatchClass; diff --git a/crates/subspace-runtime/Cargo.toml b/crates/subspace-runtime/Cargo.toml index 60a4747fa1..78f6a65bc1 100644 --- a/crates/subspace-runtime/Cargo.toml +++ b/crates/subspace-runtime/Cargo.toml @@ -154,5 +154,6 @@ runtime-benchmarks = [ "pallet-timestamp/runtime-benchmarks", "pallet-transporter/runtime-benchmarks", "pallet-utility/runtime-benchmarks", + "sp-consensus-subspace/runtime-benchmarks", "sp-runtime/runtime-benchmarks", ] diff --git a/crates/subspace-runtime/src/lib.rs b/crates/subspace-runtime/src/lib.rs index 956e5bddcc..54cb405181 100644 --- a/crates/subspace-runtime/src/lib.rs +++ b/crates/subspace-runtime/src/lib.rs @@ -80,7 +80,10 @@ use sp_runtime::traits::{ NumberFor, }; use sp_runtime::transaction_validity::{TransactionSource, TransactionValidity}; -use sp_runtime::{generic, AccountId32, ApplyExtrinsicResult, ExtrinsicInclusionMode, Perbill}; +use sp_runtime::{ + generic, AccountId32, ApplyExtrinsicResult, ExtrinsicInclusionMode, Perbill, + SaturatedConversion, +}; use sp_std::collections::btree_map::BTreeMap; use sp_std::collections::btree_set::BTreeSet; use sp_std::marker::PhantomData; @@ -299,6 +302,7 @@ impl pallet_subspace::Config for Runtime { type EraChangeTrigger = pallet_subspace::NormalEraChange; type BlockSlotCount = BlockSlotCount; type WeightInfo = pallet_subspace::weights::SubstrateWeight; + type ExtensionWeightInfo = pallet_subspace::extensions::weights::SubstrateWeight; } impl pallet_timestamp::Config for Runtime { @@ -728,6 +732,15 @@ where } } +impl subspace_runtime_primitives::CreateUnsigned for Runtime +where + RuntimeCall: From, +{ + fn create_unsigned(call: Self::RuntimeCall) -> Self::Extrinsic { + create_unsigned_general_extrinsic(call) + } +} + parameter_types! { pub const TransporterEndpointId: EndpointId = 1; } @@ -1022,8 +1035,8 @@ pub type SignedExtra = ( frame_system::CheckWeight, pallet_transaction_payment::ChargeTransactionPayment, DisablePallets, - // TODO: remove or adapt after or during migration to General extrinsic respectively - subspace_runtime_primitives::extensions::DisableGeneralExtrinsics, + pallet_subspace::extensions::SubspaceExtension, + subspace_runtime_primitives::extensions::CheckAllowedGeneralExtrinsics, ); /// Unchecked extrinsic type as expected by this runtime. pub type UncheckedExtrinsic = @@ -1044,6 +1057,26 @@ pub type Executive = frame_executive::Executive< ), >; +// List of allowed general unsigned extrinsics. +// New unsigned general extrinsics must be included here. +impl subspace_runtime_primitives::AllowedUnsignedExtrinsics for RuntimeCall { + fn is_allowed_unsigned(&self) -> bool { + matches!( + self, + RuntimeCall::Subspace(pallet_subspace::Call::vote { .. }) + ) + } +} + +impl pallet_subspace::extensions::MaybeSubspaceCall for RuntimeCall { + fn maybe_subspace_call(&self) -> Option<&pallet_subspace::Call> { + match self { + RuntimeCall::Subspace(call) => Some(call), + _ => None, + } + } +} + fn extract_segment_headers(ext: &UncheckedExtrinsic) -> Option> { match &ext.function { RuntimeCall::Subspace(pallet_subspace::Call::store_segment_headers { segment_headers }) => { @@ -1081,6 +1114,35 @@ fn is_xdm_mmr_proof_valid(ext: &::Extrinsic) -> Option { } } +fn create_unsigned_general_extrinsic(call: RuntimeCall) -> UncheckedExtrinsic { + let period = BlockHashCount::get() + .checked_next_power_of_two() + .map(|c| c / 2) + .unwrap_or(2) as u64; + + let current_block = System::block_number().saturated_into::(); + + let extra: SignedExtra = ( + frame_system::CheckNonZeroSender::::new(), + frame_system::CheckSpecVersion::::new(), + frame_system::CheckTxVersion::::new(), + frame_system::CheckGenesis::::new(), + frame_system::CheckMortality::::from(generic::Era::mortal(period, current_block)), + // for unsigned extrinsic, nonce check will be skipped + // so set a default value + frame_system::CheckNonce::::from(0), + frame_system::CheckWeight::::new(), + // for unsigned extrinsic, transaction fee check will be skipped + // so set a default value + pallet_transaction_payment::ChargeTransactionPayment::::from(0u128), + DisablePallets, + pallet_subspace::extensions::SubspaceExtension::::new(), + subspace_runtime_primitives::extensions::CheckAllowedGeneralExtrinsics::::new(), + ); + + UncheckedExtrinsic::new_transaction(call, extra) +} + struct RewardAddress([u8; 32]); impl From for RewardAddress { @@ -1145,6 +1207,7 @@ mod benches { [pallet_timestamp, Timestamp] [pallet_messenger, Messenger] [pallet_transporter, Transporter] + [pallet_subspace_extension, SubspaceExtensionBench::] ); } @@ -1653,6 +1716,7 @@ impl_runtime_apis! { use frame_support::traits::StorageInfoTrait; use frame_system_benchmarking::Pallet as SystemBench; use baseline::Pallet as BaselineBench; + use pallet_subspace::extensions::benchmarking::Pallet as SubspaceExtensionBench; let mut list = Vec::::new(); list_benchmarks!(list, extra); @@ -1670,6 +1734,7 @@ impl_runtime_apis! { use frame_system_benchmarking::Pallet as SystemBench; use baseline::Pallet as BaselineBench; + use pallet_subspace::extensions::benchmarking::Pallet as SubspaceExtensionBench; use frame_support::traits::WhitelistedStorageKeys; let whitelist: Vec = AllPalletsWithSystem::whitelisted_storage_keys(); diff --git a/domains/runtime/auto-id/src/lib.rs b/domains/runtime/auto-id/src/lib.rs index 704ecb06a3..fe73db5bd3 100644 --- a/domains/runtime/auto-id/src/lib.rs +++ b/domains/runtime/auto-id/src/lib.rs @@ -91,8 +91,7 @@ pub type SignedExtra = ( frame_system::CheckNonce, domain_check_weight::CheckWeight, pallet_transaction_payment::ChargeTransactionPayment, - // TODO: remove or adapt after or during migration to General extrinsic respectively - subspace_runtime_primitives::extensions::DisableGeneralExtrinsics, + subspace_runtime_primitives::extensions::CheckAllowedGeneralExtrinsics, ); /// Unchecked extrinsic type as expected by this runtime. @@ -523,6 +522,15 @@ construct_runtime!( } ); +// List of allowed general unsigned extrinsics. +// New unsigned general extrinsics must be included here. +impl subspace_runtime_primitives::AllowedUnsignedExtrinsics for RuntimeCall { + fn is_allowed_unsigned(&self) -> bool { + // TODO: update once we start migration for domains + false + } +} + fn is_xdm_mmr_proof_valid(ext: &::Extrinsic) -> Option { match &ext.function { RuntimeCall::Messenger(pallet_messenger::Call::relay_message { msg }) diff --git a/domains/runtime/evm/src/lib.rs b/domains/runtime/evm/src/lib.rs index 3883f30ea7..ca0ff6e8d6 100644 --- a/domains/runtime/evm/src/lib.rs +++ b/domains/runtime/evm/src/lib.rs @@ -122,8 +122,7 @@ pub type SignedExtra = ( domain_check_weight::CheckWeight, pallet_transaction_payment::ChargeTransactionPayment, CheckContractCreation, - // TODO: remove or adapt after or during migration to General extrinsic respectively - subspace_runtime_primitives::extensions::DisableGeneralExtrinsics, + subspace_runtime_primitives::extensions::CheckAllowedGeneralExtrinsics, ); /// Custom signed extra for check_and_pre_dispatch. @@ -139,7 +138,7 @@ type CustomSignedExtra = ( pallet_transaction_payment::ChargeTransactionPayment, CheckContractCreation, // TODO: remove or adapt after or during migration to General extrinsic respectively - subspace_runtime_primitives::extensions::DisableGeneralExtrinsics, + subspace_runtime_primitives::extensions::CheckAllowedGeneralExtrinsics, ); /// Unchecked extrinsic type as expected by this runtime. @@ -903,6 +902,15 @@ impl fp_rpc::ConvertTransaction for TransactionConve } } +// List of allowed general unsigned extrinsics. +// New unsigned general extrinsics must be included here. +impl subspace_runtime_primitives::AllowedUnsignedExtrinsics for RuntimeCall { + fn is_allowed_unsigned(&self) -> bool { + // TODO: update once we start migration for domains + false + } +} + fn is_xdm_mmr_proof_valid(ext: &::Extrinsic) -> Option { match &ext.0.function { RuntimeCall::Messenger(pallet_messenger::Call::relay_message { msg }) diff --git a/domains/test/runtime/auto-id/src/lib.rs b/domains/test/runtime/auto-id/src/lib.rs index 886731babe..ce22828270 100644 --- a/domains/test/runtime/auto-id/src/lib.rs +++ b/domains/test/runtime/auto-id/src/lib.rs @@ -91,8 +91,7 @@ pub type SignedExtra = ( frame_system::CheckNonce, domain_check_weight::CheckWeight, pallet_transaction_payment::ChargeTransactionPayment, - // TODO: remove or adapt after or during migration to General extrinsic respectively - subspace_runtime_primitives::extensions::DisableGeneralExtrinsics, + subspace_runtime_primitives::extensions::CheckAllowedGeneralExtrinsics, ); /// Unchecked extrinsic type as expected by this runtime. @@ -516,6 +515,15 @@ construct_runtime!( } ); +// List of allowed general unsigned extrinsics. +// New unsigned general extrinsics must be included here. +impl subspace_runtime_primitives::AllowedUnsignedExtrinsics for RuntimeCall { + fn is_allowed_unsigned(&self) -> bool { + // TODO: update once we start migration for domains + false + } +} + fn is_xdm_mmr_proof_valid(ext: &::Extrinsic) -> Option { match &ext.function { RuntimeCall::Messenger(pallet_messenger::Call::relay_message { msg }) diff --git a/domains/test/runtime/evm/src/lib.rs b/domains/test/runtime/evm/src/lib.rs index 1c66ab03ba..388df487c9 100644 --- a/domains/test/runtime/evm/src/lib.rs +++ b/domains/test/runtime/evm/src/lib.rs @@ -120,8 +120,7 @@ pub type SignedExtra = ( domain_check_weight::CheckWeight, pallet_transaction_payment::ChargeTransactionPayment, pallet_evm_tracker::create_contract::CheckContractCreation, - // TODO: remove or adapt after or during migration to General extrinsic respectively - subspace_runtime_primitives::extensions::DisableGeneralExtrinsics, + subspace_runtime_primitives::extensions::CheckAllowedGeneralExtrinsics, ); /// Custom signed extra for check_and_pre_dispatch. @@ -136,8 +135,7 @@ type CustomSignedExtra = ( domain_check_weight::CheckWeight, pallet_transaction_payment::ChargeTransactionPayment, pallet_evm_tracker::create_contract::CheckContractCreation, - // TODO: remove or adapt after or during migration to General extrinsic respectively - subspace_runtime_primitives::extensions::DisableGeneralExtrinsics, + subspace_runtime_primitives::extensions::CheckAllowedGeneralExtrinsics, ); /// Unchecked extrinsic type as expected by this runtime. @@ -181,7 +179,7 @@ pub fn construct_extrinsic_raw_payload( domain_check_weight::CheckWeight::::new(), pallet_transaction_payment::ChargeTransactionPayment::::from(tip), pallet_evm_tracker::create_contract::CheckContractCreation::::new(), - subspace_runtime_primitives::extensions::DisableGeneralExtrinsics::::new(), + subspace_runtime_primitives::extensions::CheckAllowedGeneralExtrinsics::::new(), ); ( generic::SignedPayload::, SignedExtra>::from_raw( @@ -940,6 +938,15 @@ impl fp_rpc::ConvertTransaction for TransactionConve } } +// List of allowed general unsigned extrinsics. +// New unsigned general extrinsics must be included here. +impl subspace_runtime_primitives::AllowedUnsignedExtrinsics for RuntimeCall { + fn is_allowed_unsigned(&self) -> bool { + // TODO: update once we start migration for domains + false + } +} + fn is_xdm_mmr_proof_valid(ext: &::Extrinsic) -> Option { match &ext.0.function { RuntimeCall::Messenger(pallet_messenger::Call::relay_message { msg }) diff --git a/test/subspace-test-runtime/src/lib.rs b/test/subspace-test-runtime/src/lib.rs index c8fd17449b..0ef87b185b 100644 --- a/test/subspace-test-runtime/src/lib.rs +++ b/test/subspace-test-runtime/src/lib.rs @@ -91,7 +91,10 @@ use sp_runtime::traits::{ use sp_runtime::transaction_validity::{ InvalidTransaction, TransactionSource, TransactionValidity, TransactionValidityError, }; -use sp_runtime::{generic, AccountId32, ApplyExtrinsicResult, ExtrinsicInclusionMode, Perbill}; +use sp_runtime::{ + generic, AccountId32, ApplyExtrinsicResult, ExtrinsicInclusionMode, Perbill, + SaturatedConversion, +}; use sp_std::collections::btree_map::BTreeMap; use sp_std::collections::btree_set::BTreeSet; use sp_std::marker::PhantomData; @@ -314,6 +317,7 @@ impl pallet_subspace::Config for Runtime { type EraChangeTrigger = pallet_subspace::NormalEraChange; type BlockSlotCount = BlockSlotCount; type WeightInfo = pallet_subspace::weights::SubstrateWeight; + type ExtensionWeightInfo = pallet_subspace::extensions::weights::SubstrateWeight; } impl pallet_timestamp::Config for Runtime { @@ -688,6 +692,15 @@ where } } +impl subspace_runtime_primitives::CreateUnsigned for Runtime +where + RuntimeCall: From, +{ + fn create_unsigned(call: Self::RuntimeCall) -> Self::Extrinsic { + create_unsigned_general_extrinsic(call) + } +} + parameter_types! { pub const TransporterEndpointId: EndpointId = 1; } @@ -927,8 +940,8 @@ pub type SignedExtra = ( frame_system::CheckNonce, frame_system::CheckWeight, pallet_transaction_payment::ChargeTransactionPayment, - // TODO: remove or adapt after or during migration to General extrinsic respectively - subspace_runtime_primitives::extensions::DisableGeneralExtrinsics, + pallet_subspace::extensions::SubspaceExtension, + subspace_runtime_primitives::extensions::CheckAllowedGeneralExtrinsics, ); /// Unchecked extrinsic type as expected by this runtime. pub type UncheckedExtrinsic = @@ -944,6 +957,26 @@ pub type Executive = frame_executive::Executive< /// The payload being signed in transactions. pub type SignedPayload = generic::SignedPayload; +impl pallet_subspace::extensions::MaybeSubspaceCall for RuntimeCall { + fn maybe_subspace_call(&self) -> Option<&pallet_subspace::Call> { + match self { + RuntimeCall::Subspace(call) => Some(call), + _ => None, + } + } +} + +// List of allowed general unsigned extrinsics. +// New unsigned general extrinsics must be included here. +impl subspace_runtime_primitives::AllowedUnsignedExtrinsics for RuntimeCall { + fn is_allowed_unsigned(&self) -> bool { + matches!( + self, + RuntimeCall::Subspace(pallet_subspace::Call::vote { .. }) + ) + } +} + fn extract_segment_headers(ext: &UncheckedExtrinsic) -> Option> { match &ext.function { RuntimeCall::Subspace(pallet_subspace::Call::store_segment_headers { segment_headers }) => { @@ -1122,6 +1155,34 @@ fn extract_successful_bundles( .collect() } +fn create_unsigned_general_extrinsic(call: RuntimeCall) -> UncheckedExtrinsic { + let period = BlockHashCount::get() + .checked_next_power_of_two() + .map(|c| c / 2) + .unwrap_or(2) as u64; + + let current_block = System::block_number().saturated_into::(); + + let extra: SignedExtra = ( + frame_system::CheckNonZeroSender::::new(), + frame_system::CheckSpecVersion::::new(), + frame_system::CheckTxVersion::::new(), + frame_system::CheckGenesis::::new(), + frame_system::CheckMortality::::from(generic::Era::mortal(period, current_block)), + // for unsigned extrinsic, nonce check will be skipped + // so set a default value + frame_system::CheckNonce::::from(0), + frame_system::CheckWeight::::new(), + // for unsigned extrinsic, transaction fee check will be skipped + // so set a default value + pallet_transaction_payment::ChargeTransactionPayment::::from(0u128), + pallet_subspace::extensions::SubspaceExtension::::new(), + subspace_runtime_primitives::extensions::CheckAllowedGeneralExtrinsics::::new(), + ); + + UncheckedExtrinsic::new_transaction(call, extra) +} + struct RewardAddress([u8; 32]); impl From for RewardAddress { diff --git a/test/subspace-test-service/Cargo.toml b/test/subspace-test-service/Cargo.toml index e07b99ba73..fec73a8b7a 100644 --- a/test/subspace-test-service/Cargo.toml +++ b/test/subspace-test-service/Cargo.toml @@ -27,6 +27,7 @@ pallet-transaction-payment = { git = "https://github.com/subspace/polkadot-sdk", mmr-gadget = { git = "https://github.com/subspace/polkadot-sdk", rev = "949bb3cfb64ab974e4fc49328fb3e81c96bc0fbe" } rand = "0.8.5" pallet-domains = { version = "0.1.0", path = "../../crates/pallet-domains" } +pallet-subspace = { version = "0.1.0", path = "../../crates/pallet-subspace" } parking_lot = "0.12.2" sc-block-builder = { git = "https://github.com/subspace/polkadot-sdk", rev = "949bb3cfb64ab974e4fc49328fb3e81c96bc0fbe" } sc-client-api = { git = "https://github.com/subspace/polkadot-sdk", rev = "949bb3cfb64ab974e4fc49328fb3e81c96bc0fbe" } diff --git a/test/subspace-test-service/src/lib.rs b/test/subspace-test-service/src/lib.rs index a80606b59f..015828e142 100644 --- a/test/subspace-test-service/src/lib.rs +++ b/test/subspace-test-service/src/lib.rs @@ -1370,7 +1370,8 @@ where frame_system::CheckNonce::::from(nonce), frame_system::CheckWeight::::new(), pallet_transaction_payment::ChargeTransactionPayment::::from(tip), - subspace_runtime_primitives::extensions::DisableGeneralExtrinsics::::new(), + pallet_subspace::extensions::SubspaceExtension::::new(), + subspace_runtime_primitives::extensions::CheckAllowedGeneralExtrinsics::::new(), ); ( generic::SignedPayload::< @@ -1379,7 +1380,7 @@ where >::from_raw( function, extra.clone(), - ((), 100, 1, genesis_block, current_block_hash, (), (), (), ()), + ((), 100, 1, genesis_block, current_block_hash, (), (), (), (), ()), ), extra, )