Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Migrate Unsigned pallet-subspace submit_vote to General extrinsic #3408

Merged
merged 7 commits into from
Feb 28, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

181 changes: 181 additions & 0 deletions crates/pallet-subspace/src/extensions.rs
Original file line number Diff line number Diff line change
@@ -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<Runtime>
where
Runtime: Config,
{
fn maybe_subspace_call(&self) -> Option<&SubspaceCall<Runtime>>;
}

/// 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<Runtime>(PhantomData<Runtime>);

impl<Runtime> SubspaceExtension<Runtime> {
pub fn new() -> Self {
Self(PhantomData)
}
}

impl<Runtime> Default for SubspaceExtension<Runtime> {
fn default() -> Self {
Self::new()
}
}

impl<T: Config> fmt::Debug for SubspaceExtension<T> {
#[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<Runtime> SubspaceExtension<Runtime>
where
Runtime: Config + scale_info::TypeInfo + fmt::Debug + Send + Sync,
{
fn do_check_vote(
signed_vote: &SignedVote<BlockNumberFor<Runtime>, Runtime::Hash, Runtime::AccountId>,
source: TransactionSource,
) -> Result<(ValidTransaction, Weight), TransactionValidityError> {
let pre_dispatch = source == TransactionSource::InBlock;
if pre_dispatch {
Subspace::<Runtime>::pre_dispatch_vote(signed_vote)
.map(|weight| (ValidTransaction::default(), weight))
} else {
Subspace::<Runtime>::validate_vote(signed_vote)
}
}

fn max_weight() -> Weight {
<Runtime as Config>::ExtensionWeightInfo::vote()
.max(<Runtime as Config>::ExtensionWeightInfo::vote_with_equivocation())
}
}

impl<Runtime> TransactionExtension<RuntimeCallFor<Runtime>> for SubspaceExtension<Runtime>
where
Runtime: Config + scale_info::TypeInfo + fmt::Debug + Send + Sync,
<RuntimeCallFor<Runtime> as Dispatchable>::RuntimeOrigin:
AsSystemOriginSigner<<Runtime as frame_system::Config>::AccountId> + Clone,
RuntimeCallFor<Runtime>: MaybeSubspaceCall<Runtime>,
{
const IDENTIFIER: &'static str = "SubspaceExtension";
type Implicit = ();
type Val = ExtensionWeightData;
type Pre = ExtensionWeightData;

fn weight(&self, call: &RuntimeCallFor<Runtime>) -> Weight {
match call.maybe_subspace_call() {
Some(SubspaceCall::vote { .. }) => Self::max_weight(),
_ => Weight::zero(),
}
}

fn validate(
&self,
origin: DispatchOriginOf<RuntimeCallFor<Runtime>>,
call: &RuntimeCallFor<Runtime>,
_info: &DispatchInfoOf<RuntimeCallFor<Runtime>>,
_len: usize,
_self_implicit: Self::Implicit,
_inherited_implication: &impl Implication,
source: TransactionSource,
) -> ValidateResult<Self::Val, RuntimeCallFor<Runtime>> {
// 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<RuntimeCallFor<Runtime>>,
_call: &RuntimeCallFor<Runtime>,
_info: &DispatchInfoOf<RuntimeCallFor<Runtime>>,
_len: usize,
) -> Result<Self::Pre, TransactionValidityError> {
Ok(val)
}

fn post_dispatch_details(
pre: Self::Pre,
_info: &DispatchInfoOf<RuntimeCallFor<Runtime>>,
_post_info: &PostDispatchInfoOf<RuntimeCallFor<Runtime>>,
_len: usize,
_result: &DispatchResult,
) -> Result<Weight, TransactionValidityError> {
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()),
}
}
}
147 changes: 147 additions & 0 deletions crates/pallet-subspace/src/extensions/benchmarking.rs
Original file line number Diff line number Diff line change
@@ -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<T: frame_system::Config> {
pub segment_index: SegmentIndex,
pub segment_commitment: SegmentCommitment,
pub signed_vote: SignedVote<NumberFor<T::Block>, T::Hash, T::AccountId>,
pub public_key: PublicKey,
pub parent_block_data: (NumberFor<T::Block>, T::Hash),
pub current_block_number: NumberFor<T::Block>,
pub vote_verification_data: VoteVerificationData,
pub current_slot: Slot,
pub reward_address: T::AccountId,
}

pub struct Pallet<T: Config>(Subspace<T>);

#[allow(clippy::multiple_bound_locations)]
#[benchmarks(where
T: Send + Sync + scale_info::TypeInfo + fmt::Debug,
RuntimeCallFor<T>: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo>,
<RuntimeCallFor<T> as Dispatchable>::RuntimeOrigin: AsSystemOriginSigner<<T as frame_system::Config>::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::<T>::decode(&mut include_bytes!("./fixtures/vote.data").as_slice()).unwrap();

SubspaceSegmentCommitment::<T>::insert(segment_index, segment_commitment);

// Reset so that any solution works for votes
SolutionRanges::<T>::mutate(|solution_ranges| {
solution_ranges.current = u64::MIN;
solution_ranges.voting_current = u64::MAX;
});

frame_system::Pallet::<T>::set_block_number(current_block_number);
frame_system::pallet::BlockHash::<T>::insert(parent_block_data.0, parent_block_data.1);
ParentVoteVerificationData::<T>::put(vote_verification_data);
BlockSlots::<T>::mutate(|block_slots| {
block_slots
.try_insert(current_block_number, current_slot)
.expect("one entry just removed before inserting; qed");
});
CurrentBlockVoters::<T>::put(BTreeMap::<
(PublicKey, SectorIndex, PieceOffset, ScalarBytes, Slot),
(Option<T::AccountId>, RewardSignature),
>::default());

#[block]
{
SubspaceExtension::<T>::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::<T>::decode(&mut include_bytes!("./fixtures/vote.data").as_slice()).unwrap();

SubspaceSegmentCommitment::<T>::insert(segment_index, segment_commitment);

// Reset so that any solution works for votes
SolutionRanges::<T>::mutate(|solution_ranges| {
solution_ranges.current = u64::MIN;
solution_ranges.voting_current = u64::MAX;
});

frame_system::Pallet::<T>::set_block_number(current_block_number);
frame_system::pallet::BlockHash::<T>::insert(parent_block_data.0, parent_block_data.1);
ParentVoteVerificationData::<T>::put(vote_verification_data);
BlockSlots::<T>::mutate(|block_slots| {
block_slots
.try_insert(current_block_number, current_slot)
.expect("one entry just removed before inserting; qed");
});
CurrentBlockVoters::<T>::put(BTreeMap::<
(PublicKey, SectorIndex, PieceOffset, ScalarBytes, Slot),
(Option<T::AccountId>, RewardSignature),
>::default());

CurrentBlockAuthorInfo::<T>::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::<T>::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
);
}
Binary file not shown.
Loading
Loading