diff --git a/pallets/transaction-storage/src/extension.rs b/pallets/transaction-storage/src/extension.rs index 5fba2f9c3..06cb150e5 100644 --- a/pallets/transaction-storage/src/extension.rs +++ b/pallets/transaction-storage/src/extension.rs @@ -45,55 +45,51 @@ struct TraverseResult { /// Maximum recursion depth for inspecting wrapper calls. pub const MAX_WRAPPER_DEPTH: u32 = 8; -/// Returns `true` if `call` is a storage-mutating TransactionStorage call (store, -/// store_with_cid_config, renew) — either directly or nested inside wrappers. -/// -/// Intended for use in XCM `SafeCallFilter` implementations. The runtime's -/// [`CallInspector`] provides the wrapper-recursion logic, so this function -/// works for any runtime without duplicating the blocked-call list. -pub fn is_storage_mutating_call>>( - call: &RuntimeCallOf, - depth: u32, -) -> bool -where - RuntimeCallOf: IsSubType>, -{ - if depth >= MAX_WRAPPER_DEPTH { - return true; - } - if let Some(inner_call) = call.is_sub_type() { - return matches!( - inner_call, - Call::store { .. } | Call::store_with_cid_config { .. } | Call::renew { .. } - ); - } - if let Some((inner_calls, _)) = I::inspect_wrapper(call) { - return inner_calls - .into_iter() - .any(|inner| is_storage_mutating_call::(inner, depth + 1)); - } - false -} - /// Tells [`ValidateStorageCalls`] how to find storage calls inside wrapper /// extrinsics (e.g. `Utility::batch`, `Sudo::sudo_as`). /// /// The runtime implements this for its `RuntimeCall` type, allowing the pallet extension /// to recursively validate and consume storage authorization in wrapped calls, and to /// transform the origin to [`Origin::Authorized`] for origin-preserving wrappers. -pub trait CallInspector: Clone + PartialEq + Eq + Default { +pub trait CallInspector: Clone + PartialEq + Eq + Default +where + RuntimeCallOf: IsSubType>, +{ /// If `call` is a wrapper, return: /// - The inner calls to inspect for storage authorization /// - `true` if the wrapper passes origin through to inner calls (e.g. batch), `false` if it /// changes the origin (e.g. sudo_as) /// /// Returns `None` for non-wrapper calls. - fn inspect_wrapper(call: &Call) -> Option<(Vec<&Call>, bool)>; + fn inspect_wrapper(call: &RuntimeCallOf) -> Option<(Vec<&RuntimeCallOf>, bool)>; + + /// Returns `true` if `call` is a storage-mutating TransactionStorage call (store, + /// store_with_cid_config, renew) — either directly or nested inside wrappers. + fn is_storage_mutating_call(call: &RuntimeCallOf, depth: u32) -> bool { + if depth >= MAX_WRAPPER_DEPTH { + return true; + } + if let Some(inner_call) = call.is_sub_type() { + return matches!( + inner_call, + Call::store { .. } | Call::store_with_cid_config { .. } | Call::renew { .. } + ); + } + if let Some((inner_calls, _)) = Self::inspect_wrapper(call) { + return inner_calls + .into_iter() + .any(|inner| Self::is_storage_mutating_call(inner, depth + 1)); + } + false + } } /// No-op implementation — no wrapper inspection. Direct storage calls still work. -impl CallInspector for () { - fn inspect_wrapper(_: &Call) -> Option<(Vec<&Call>, bool)> { +impl CallInspector for () +where + RuntimeCallOf: IsSubType>, +{ + fn inspect_wrapper(_: &RuntimeCallOf) -> Option<(Vec<&RuntimeCallOf>, bool)> { None } } @@ -145,7 +141,7 @@ impl fmt::Debug for ValidateStorageCalls { } } -impl>> ValidateStorageCalls +impl> ValidateStorageCalls where RuntimeCallOf: IsSubType>, { @@ -184,7 +180,7 @@ where } } -impl> + Send + Sync + 'static> +impl + Send + Sync + 'static> TransactionExtension> for ValidateStorageCalls where RuntimeCallOf: IsSubType>, diff --git a/pallets/transaction-storage/src/lib.rs b/pallets/transaction-storage/src/lib.rs index 2b18485a9..e37cc385c 100644 --- a/pallets/transaction-storage/src/lib.rs +++ b/pallets/transaction-storage/src/lib.rs @@ -139,7 +139,7 @@ pub enum AuthorizedCaller { /// Convenience alias for [`AuthorizedCaller`] bound to a runtime's `AccountId`. pub type AuthorizedCallerFor = AuthorizedCaller<::AccountId>; -pub use extension::{is_storage_mutating_call, CallInspector, MAX_WRAPPER_DEPTH}; +pub use extension::{CallInspector, MAX_WRAPPER_DEPTH}; /// An authorization to store data. #[derive(Encode, Decode, scale_info::TypeInfo, MaxEncodedLen)] diff --git a/runtimes/bulletin-polkadot/src/lib.rs b/runtimes/bulletin-polkadot/src/lib.rs index 4d798746f..f9d9e075f 100644 --- a/runtimes/bulletin-polkadot/src/lib.rs +++ b/runtimes/bulletin-polkadot/src/lib.rs @@ -377,7 +377,7 @@ impl SortedMembers for TestAccounts { #[derive(Clone, PartialEq, Eq, Default)] pub struct StorageCallInspector; -impl pallet_transaction_storage::CallInspector for StorageCallInspector { +impl pallet_transaction_storage::CallInspector for StorageCallInspector { fn inspect_wrapper(call: &RuntimeCall) -> Option<(alloc::vec::Vec<&RuntimeCall>, bool)> { match call { RuntimeCall::Utility(c) => inspect_utility_wrapper(c), @@ -393,7 +393,7 @@ impl pallet_transaction_storage::CallInspector for StorageCallInspe /// nesting. Used with `EverythingBut` as the XCM `SafeCallFilter`. impl frame_support::traits::Contains for StorageCallInspector { fn contains(call: &RuntimeCall) -> bool { - pallet_transaction_storage::is_storage_mutating_call::(call, 0) + Self::is_storage_mutating_call(call, 0) } } @@ -555,7 +555,7 @@ fn validate_purge_keys(who: &AccountId) -> TransactionValidity { } } -use pallet_transaction_storage::MAX_WRAPPER_DEPTH; +use pallet_transaction_storage::{CallInspector, MAX_WRAPPER_DEPTH}; use pallets_common::{ inspect_proxy_wrapper, inspect_sudo_wrapper, inspect_utility_wrapper, proxy_inner_calls, utility_inner_calls, diff --git a/runtimes/bulletin-westend/src/storage.rs b/runtimes/bulletin-westend/src/storage.rs index 57ddc0c4c..19cadf7a6 100644 --- a/runtimes/bulletin-westend/src/storage.rs +++ b/runtimes/bulletin-westend/src/storage.rs @@ -23,6 +23,7 @@ use frame_support::{ traits::{Contains, EitherOfDiverse, Equals, SortedMembers}, }; use frame_system::EnsureSignedBy; +use pallet_transaction_storage::CallInspector; use pallet_xcm::EnsureXcm; use pallets_common::{inspect_sudo_wrapper, inspect_utility_wrapper, NoCurrency}; use sp_keyring::Sr25519Keyring; @@ -58,7 +59,7 @@ parameter_types! { #[derive(Clone, PartialEq, Eq, Default)] pub struct StorageCallInspector; -impl pallet_transaction_storage::CallInspector for StorageCallInspector { +impl pallet_transaction_storage::CallInspector for StorageCallInspector { fn inspect_wrapper(call: &RuntimeCall) -> Option<(Vec<&RuntimeCall>, bool)> { match call { RuntimeCall::Utility(c) => inspect_utility_wrapper(c), @@ -73,7 +74,7 @@ impl pallet_transaction_storage::CallInspector for StorageCallInspe /// Used with `EverythingBut` as the XCM `SafeCallFilter`. impl Contains for StorageCallInspector { fn contains(call: &RuntimeCall) -> bool { - pallet_transaction_storage::is_storage_mutating_call::(call, 0) + Self::is_storage_mutating_call(call, 0) } }