From 932202f6da36c6f1e3524527bd5ac3ed24bafa38 Mon Sep 17 00:00:00 2001 From: dastansam Date: Fri, 27 Sep 2024 21:02:52 +0600 Subject: [PATCH] Add `CheckHistorySeeder` signed extension --- crates/pallet-history-seeding/src/lib.rs | 15 +-- crates/pallet-history-seeding/src/tests.rs | 23 +--- .../src/malicious_bundle_producer.rs | 6 +- crates/subspace-runtime/src/lib.rs | 3 +- .../subspace-runtime/src/signed_extensions.rs | 125 ++++++++++++++++++ 5 files changed, 134 insertions(+), 38 deletions(-) diff --git a/crates/pallet-history-seeding/src/lib.rs b/crates/pallet-history-seeding/src/lib.rs index c265500193..5760d460a6 100644 --- a/crates/pallet-history-seeding/src/lib.rs +++ b/crates/pallet-history-seeding/src/lib.rs @@ -25,12 +25,6 @@ pub mod pallet { #[pallet::pallet] pub struct Pallet(_); - #[pallet::error] - pub enum Error { - /// The sender is not authorized to seed history - NotAuthorized, - } - #[pallet::storage] #[pallet::getter(fn history_seeder)] pub(super) type HistorySeeder = StorageValue<_, T::AccountId, OptionQuery>; @@ -40,14 +34,7 @@ pub mod pallet { /// Seed history with a remark #[pallet::call_index(0)] #[pallet::weight((T::WeightInfo::seed_history(remark.len() as u32), Pays::No))] - pub fn seed_history(origin: OriginFor, remark: Vec) -> DispatchResult { - let who = ensure_signed(origin.clone())?; - - ensure!( - Some(who.clone()) == Self::history_seeder(), - Error::::NotAuthorized - ); - + pub fn seed_history(_origin: OriginFor, remark: Vec) -> DispatchResult { let _ = remark; Ok(()) diff --git a/crates/pallet-history-seeding/src/tests.rs b/crates/pallet-history-seeding/src/tests.rs index e32a082b56..32f82a2111 100644 --- a/crates/pallet-history-seeding/src/tests.rs +++ b/crates/pallet-history-seeding/src/tests.rs @@ -1,4 +1,4 @@ -use crate::{self as pallet_history_seeding, Error}; +use crate::{self as pallet_history_seeding}; use frame_support::traits::BuildGenesisConfig; use frame_support::{assert_noop, assert_ok, construct_runtime, derive_impl}; use frame_system as system; @@ -68,26 +68,5 @@ fn seed_history_works() { RuntimeOrigin::signed(1), remark.clone() )); - - // Ensure unauthorized account cannot seed history - assert_noop!( - HistorySeeding::seed_history(RuntimeOrigin::signed(2), remark), - Error::::NotAuthorized - ); - }); -} - -#[test] -fn seed_history_fails_when_no_seeder_set() { - new_test_ext().execute_with(|| { - let remark = vec![1, 2, 3]; - assert_noop!( - HistorySeeding::seed_history(RuntimeOrigin::signed(1), remark.clone()), - Error::::NotAuthorized - ); - assert_noop!( - HistorySeeding::seed_history(RuntimeOrigin::root(), remark), - sp_runtime::DispatchError::BadOrigin - ); }); } diff --git a/crates/subspace-malicious-operator/src/malicious_bundle_producer.rs b/crates/subspace-malicious-operator/src/malicious_bundle_producer.rs index d87c81232a..fa1fa02219 100644 --- a/crates/subspace-malicious-operator/src/malicious_bundle_producer.rs +++ b/crates/subspace-malicious-operator/src/malicious_bundle_producer.rs @@ -31,7 +31,9 @@ use sp_transaction_pool::runtime_api::TaggedTransactionQueue; use std::error::Error; use std::sync::Arc; use subspace_core_primitives::PotOutput; -use subspace_runtime::{DisablePallets, Runtime, RuntimeCall, SignedExtra, UncheckedExtrinsic}; +use subspace_runtime::{ + CheckHistorySeeder, DisablePallets, Runtime, RuntimeCall, SignedExtra, UncheckedExtrinsic, +}; use subspace_runtime_primitives::opaque::Block as CBlock; use subspace_runtime_primitives::{AccountId, Balance, Nonce}; @@ -426,6 +428,7 @@ pub fn construct_signed_extrinsic( frame_system::CheckWeight::::new(), pallet_transaction_payment::ChargeTransactionPayment::::from(0u128), DisablePallets, + CheckHistorySeeder::::new(), ); let raw_payload = generic::SignedPayload::::from_raw( call.clone(), @@ -440,6 +443,7 @@ pub fn construct_signed_extrinsic( (), (), (), + (), ), ); diff --git a/crates/subspace-runtime/src/lib.rs b/crates/subspace-runtime/src/lib.rs index 892f156252..6db541e7f8 100644 --- a/crates/subspace-runtime/src/lib.rs +++ b/crates/subspace-runtime/src/lib.rs @@ -35,7 +35,7 @@ include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); use crate::fees::{OnChargeTransaction, TransactionByteFee}; use crate::object_mapping::extract_block_object_mapping; -pub use crate::signed_extensions::DisablePallets; +pub use crate::signed_extensions::{CheckHistorySeeder, DisablePallets}; use codec::{Decode, Encode, MaxEncodedLen}; use core::mem; use core::num::NonZeroU64; @@ -968,6 +968,7 @@ pub type SignedExtra = ( frame_system::CheckWeight, pallet_transaction_payment::ChargeTransactionPayment, DisablePallets, + CheckHistorySeeder, ); /// Unchecked extrinsic type as expected by this runtime. pub type UncheckedExtrinsic = diff --git a/crates/subspace-runtime/src/signed_extensions.rs b/crates/subspace-runtime/src/signed_extensions.rs index 1e4ae2a619..782b54e609 100644 --- a/crates/subspace-runtime/src/signed_extensions.rs +++ b/crates/subspace-runtime/src/signed_extensions.rs @@ -1,5 +1,6 @@ use crate::{Runtime, RuntimeCall, RuntimeConfigs}; use codec::{Decode, Encode}; +use core::marker::PhantomData; use scale_info::TypeInfo; use sp_runtime::traits::{DispatchInfoOf, SignedExtension}; use sp_runtime::transaction_validity::{ @@ -68,3 +69,127 @@ impl SignedExtension for DisablePallets { } } } + +/// A custom signed extension to check if the caller is an authorized history seeder for +/// the `history_seeding` pallet. +#[derive(Encode, Decode, Clone, Eq, PartialEq, TypeInfo)] +#[scale_info(skip_type_params(T))] +pub struct CheckHistorySeeder(PhantomData); + +impl core::fmt::Debug for CheckHistorySeeder { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("CheckHistorySeeder").finish() + } +} + +impl CheckHistorySeeder { + pub fn new() -> Self { + Self(core::marker::PhantomData) + } +} + +impl Default for CheckHistorySeeder { + fn default() -> Self { + Self::new() + } +} + +impl SignedExtension for CheckHistorySeeder { + const IDENTIFIER: &'static str = "CheckHistorySeeder"; + + type AccountId = T::AccountId; + type Call = ::RuntimeCall; + type AdditionalSigned = (); + type Pre = (); + + fn additional_signed(&self) -> Result { + Ok(()) + } + + fn validate( + &self, + who: &Self::AccountId, + call: &Self::Call, + _info: &DispatchInfoOf, + _len: usize, + ) -> TransactionValidity { + match call { + crate::RuntimeCall::HistorySeeding(pallet_history_seeding::Call::seed_history { + .. + }) => { + if Some(who.clone()) != pallet_history_seeding::Pallet::::history_seeder() { + return Err(TransactionValidityError::Invalid( + InvalidTransaction::BadSigner, + )); + } + + Ok(ValidTransaction::default()) + } + _ => Ok(ValidTransaction::default()), + } + } + + fn pre_dispatch( + self, + who: &Self::AccountId, + _call: &Self::Call, + _info: &DispatchInfoOf, + _len: usize, + ) -> Result { + self.validate(who, _call, _info, _len)?; + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use crate::{CheckHistorySeeder, Runtime, RuntimeCall, RuntimeOrigin}; + use codec::Encode; + use frame_support::dispatch::DispatchInfo; + use frame_support::pallet_prelude::{InvalidTransaction, TransactionValidityError}; + use frame_support::{assert_err, assert_ok}; + use sp_runtime::traits::SignedExtension; + use sp_runtime::{AccountId32, BuildStorage}; + + pub fn new_test_ext() -> sp_io::TestExternalities { + let t = frame_system::GenesisConfig::::default() + .build_storage() + .unwrap(); + t.into() + } + + #[test] + fn test_check_history_seeder_works() { + new_test_ext().execute_with(|| { + let call = RuntimeCall::HistorySeeding(pallet_history_seeding::Call::seed_history { + remark: vec![0u8; 256], + }); + + let who = AccountId32::new([0u8; 32]); + + assert_err!( + CheckHistorySeeder::::new().pre_dispatch( + &who, + &call, + &DispatchInfo::default(), + call.encoded_size() + ), + TransactionValidityError::Invalid(InvalidTransaction::BadSigner), + ); + + // set seeder + pallet_history_seeding::Pallet::::set_history_seeder( + RuntimeOrigin::root(), + who.clone(), + ) + .unwrap(); + + assert_ok!(CheckHistorySeeder::::new().pre_dispatch( + &who, + &call, + &DispatchInfo::default(), + call.encoded_size() + )); + }); + } +}