diff --git a/CHANGELOG.md b/CHANGELOG.md index f2f81f2127..9f453fbe38 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Delegate stake pools in Kusama ([polkadot-fellows/runtimes#540](https://github.com/polkadot-fellows/runtimes/pull/540)) - Snowbridge: Add support for bridging Ether ([polkadot-fellows/runtimes#548](https://github.com/polkadot-fellows/runtimes/pull/548)) +- Adds support for remote proxies on AssetHub Polkadot and AssetHub Kusama. ‼️ Builders: Please read the docs and the implications around the lifetime of a proxy on a remote chain. ‼️ ([polkadot-fellows/runtimes#535](https://github.com/polkadot-fellows/runtimes/pull/535)) + ### Changed - Kusama Treasury: remove funding to the Kappa Sigma Mu Society and disable burn ([polkadot-fellows/runtimes#507](https://github.com/polkadot-fellows/runtimes/pull/507)) diff --git a/Cargo.lock b/Cargo.lock index 6adf1aaeb0..dadec39626 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -659,6 +659,7 @@ dependencies = [ "pallet-nfts", "pallet-nfts-runtime-api", "pallet-proxy", + "pallet-remote-proxy", "pallet-session", "pallet-state-trie-migration", "pallet-timestamp", @@ -676,7 +677,6 @@ dependencies = [ "polkadot-core-primitives", "polkadot-parachain-primitives", "polkadot-runtime-common", - "polkadot-runtime-constants", "primitive-types", "scale-info", "serde_json", @@ -6104,11 +6104,15 @@ name = "kusama-runtime-constants" version = "1.0.0" dependencies = [ "frame-support", + "pallet-remote-proxy", + "parity-scale-codec", "polkadot-primitives", "polkadot-runtime-common", + "scale-info", "smallvec", "sp-core 34.0.0", "sp-runtime 39.0.5", + "sp-trie 37.0.0", "sp-weights 31.0.0", "staging-xcm-builder", ] @@ -8788,6 +8792,27 @@ dependencies = [ "sp-runtime 39.0.5", ] +[[package]] +name = "pallet-remote-proxy" +version = "1.0.0" +dependencies = [ + "cumulus-pallet-parachain-system", + "cumulus-primitives-core", + "frame-benchmarking", + "frame-support", + "frame-system", + "pallet-balances", + "pallet-proxy", + "pallet-utility", + "parity-scale-codec", + "scale-info", + "sp-core 34.0.0", + "sp-io 38.0.0", + "sp-runtime 39.0.5", + "sp-state-machine 0.43.0", + "sp-trie 37.0.0", +] + [[package]] name = "pallet-salary" version = "23.2.0" @@ -9449,6 +9474,7 @@ dependencies = [ "frame-system-benchmarking", "frame-system-rpc-runtime-api", "frame-try-runtime", + "hex-literal", "log", "pallet-asset-conversion", "pallet-asset-tx-payment", @@ -10080,11 +10106,15 @@ name = "polkadot-runtime-constants" version = "1.0.0" dependencies = [ "frame-support", + "pallet-remote-proxy", + "parity-scale-codec", "polkadot-primitives", "polkadot-runtime-common", + "scale-info", "smallvec", "sp-core 34.0.0", "sp-runtime 39.0.5", + "sp-trie 37.0.0", "sp-weights 31.0.0", "staging-xcm-builder", ] diff --git a/Cargo.toml b/Cargo.toml index 2cf8cfd39a..fae3e21d3f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -147,6 +147,7 @@ pallet-proxy = { version = "38.0.0", default-features = false } pallet-ranked-collective = { version = "38.2.0", default-features = false } pallet-recovery = { version = "38.0.0", default-features = false } pallet-referenda = { version = "38.0.0", default-features = false } +pallet-remote-proxy = { path = "pallets/remote-proxy", default-features = false } pallet-salary = { version = "23.2.0", default-features = false } pallet-scheduler = { version = "39.0.0", default-features = false } pallet-session = { version = "38.0.0", default-features = false } @@ -229,11 +230,12 @@ sp-offchain = { version = "34.0.0", default-features = false } sp-runtime = { version = "39.0.5", default-features = false } sp-session = { version = "36.0.0", default-features = false } sp-staking = { version = "36.0.0", default-features = false } +sp-state-machine = { version = "0.43.0", default-features = false } sp-std = { version = "14.0.0", default-features = false } sp-storage = { version = "21.0.0", default-features = false } sp-tracing = { version = "17.0.1", default-features = false } sp-transaction-pool = { version = "34.0.0", default-features = false } -sp-trie = { version = "37.0.0" } +sp-trie = { version = "37.0.0", default-features = false } sp-version = { version = "37.0.0", default-features = false } sp-weights = { version = "31.0.0", default-features = false } static_assertions = { version = "1.1.0" } @@ -284,6 +286,7 @@ members = [ "integration-tests/emulated/tests/people/people-kusama", "integration-tests/emulated/tests/people/people-polkadot", "integration-tests/zombienet", + "pallets/remote-proxy", "relay/common", "relay/kusama", "relay/kusama/constants", diff --git a/integration-tests/emulated/chains/parachains/testing/penpal/Cargo.toml b/integration-tests/emulated/chains/parachains/testing/penpal/Cargo.toml index a9b5ce3aa6..b31580dd63 100644 --- a/integration-tests/emulated/chains/parachains/testing/penpal/Cargo.toml +++ b/integration-tests/emulated/chains/parachains/testing/penpal/Cargo.toml @@ -23,3 +23,11 @@ xcm = { workspace = true, default-features = true } # Runtimes kusama-emulated-chain = { workspace = true } polkadot-emulated-chain = { workspace = true } + +[features] +runtime-benchmarks = [ + "cumulus-primitives-core/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "parachains-common/runtime-benchmarks", + "penpal-runtime/runtime-benchmarks", +] diff --git a/pallets/remote-proxy/Cargo.toml b/pallets/remote-proxy/Cargo.toml new file mode 100644 index 0000000000..0ef8d62252 --- /dev/null +++ b/pallets/remote-proxy/Cargo.toml @@ -0,0 +1,70 @@ +[package] +name = "pallet-remote-proxy" +version.workspace = true +authors.workspace = true +edition.workspace = true +repository.workspace = true +license.workspace = true + +[dependencies] +codec = { features = ["derive", "max-encoded-len"], workspace = true } +scale-info = { features = ["derive"], workspace = true } + +cumulus-pallet-parachain-system = { workspace = true } +cumulus-primitives-core = { workspace = true } +frame-benchmarking = { workspace = true, optional = true } +frame-support = { workspace = true } +frame-system = { workspace = true } +pallet-proxy = { workspace = true } +sp-core = { workspace = true } +sp-trie = { workspace = true } +sp-runtime = { workspace = true } + +[dev-dependencies] +pallet-balances = { workspace = true } +pallet-utility = { workspace = true } +sp-io = { workspace = true } +sp-state-machine = { workspace = true } + +[features] +default = ["std"] + +std = [ + "codec/std", + "cumulus-pallet-parachain-system/std", + "cumulus-primitives-core/std", + "frame-benchmarking/std", + "frame-support/std", + "frame-system/std", + "pallet-balances/std", + "pallet-proxy/std", + "pallet-utility/std", + "scale-info/std", + "sp-core/std", + "sp-io/std", + "sp-runtime/std", + "sp-state-machine/std", + "sp-trie/std", +] + +try-runtime = [ + "cumulus-pallet-parachain-system/try-runtime", + "frame-support/try-runtime", + "frame-system/try-runtime", + "pallet-balances/try-runtime", + "pallet-proxy/try-runtime", + "pallet-utility/try-runtime", + "sp-runtime/try-runtime", +] + +runtime-benchmarks = [ + "cumulus-pallet-parachain-system/runtime-benchmarks", + "cumulus-primitives-core/runtime-benchmarks", + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "pallet-balances/runtime-benchmarks", + "pallet-proxy/runtime-benchmarks", + "pallet-utility/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", +] diff --git a/pallets/remote-proxy/src/benchmarking.rs b/pallets/remote-proxy/src/benchmarking.rs new file mode 100644 index 0000000000..fb74844d6d --- /dev/null +++ b/pallets/remote-proxy/src/benchmarking.rs @@ -0,0 +1,136 @@ +// Copyright (C) Polkadot Fellows. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Benchmarks for Remote Proxy Pallet + +use super::*; +use crate::Pallet as RemoteProxy; +use alloc::{boxed::Box, vec}; +use frame_benchmarking::v2::{ + account, impl_test_function, instance_benchmarks, whitelisted_caller, +}; +use frame_support::traits::Currency; +use frame_system::RawOrigin; +use sp_runtime::{ + traits::{Bounded, StaticLookup}, + BoundedVec, +}; + +const SEED: u32 = 0; + +type BalanceOf = <::Currency as Currency< + ::AccountId, +>>::Balance; + +fn assert_last_event( + generic_event: ::RuntimeEvent, +) { + frame_system::Pallet::::assert_last_event(generic_event.into()); +} + +#[instance_benchmarks] +mod benchmarks { + use super::*; + use frame_benchmarking::BenchmarkError; + + #[benchmark] + fn remote_proxy() -> Result<(), BenchmarkError> { + // In this case the caller is the "target" proxy + let caller: T::AccountId = account("target", 0, SEED); + ::Currency::make_free_balance_be( + &caller, + BalanceOf::::max_value() / 2u32.into(), + ); + // ... and "real" is the traditional caller. This is not a typo. + let real: T::AccountId = whitelisted_caller(); + let real_lookup = T::Lookup::unlookup(real.clone()); + let call: ::RuntimeCall = + frame_system::Call::::remark { remark: vec![] }.into(); + let (proof, block_number, storage_root) = + T::RemoteProxy::create_remote_proxy_proof(&caller, &real); + BlockToRoot::::set(BoundedVec::truncate_from(vec![(block_number, storage_root)])); + + #[extrinsic_call] + _(RawOrigin::Signed(caller), real_lookup, None, Box::new(call), proof); + + assert_last_event::(pallet_proxy::Event::ProxyExecuted { result: Ok(()) }.into()); + + Ok(()) + } + + #[benchmark] + fn register_remote_proxy_proof() -> Result<(), BenchmarkError> { + // In this case the caller is the "target" proxy + let caller: T::AccountId = account("target", 0, SEED); + ::Currency::make_free_balance_be( + &caller, + BalanceOf::::max_value() / 2u32.into(), + ); + // ... and "real" is the traditional caller. This is not a typo. + let real: T::AccountId = whitelisted_caller(); + let (proof, block_number, storage_root) = + T::RemoteProxy::create_remote_proxy_proof(&caller, &real); + BlockToRoot::::set(BoundedVec::truncate_from(vec![(block_number, storage_root)])); + + #[extrinsic_call] + _(RawOrigin::Signed(caller), proof); + + Ok(()) + } + + #[benchmark] + fn remote_proxy_with_registered_proof() -> Result<(), BenchmarkError> { + // In this case the caller is the "target" proxy + let caller: T::AccountId = account("target", 0, SEED); + ::Currency::make_free_balance_be( + &caller, + BalanceOf::::max_value() / 2u32.into(), + ); + // ... and "real" is the traditional caller. This is not a typo. + let real: T::AccountId = whitelisted_caller(); + let real_lookup = T::Lookup::unlookup(real.clone()); + let call: ::RuntimeCall = + frame_system::Call::::remark { remark: vec![] }.into(); + let (proof, block_number, storage_root) = + T::RemoteProxy::create_remote_proxy_proof(&caller, &real); + BlockToRoot::::set(BoundedVec::truncate_from(vec![(block_number, storage_root)])); + + #[block] + { + frame_support::dispatch_context::run_in_context(|| { + frame_support::dispatch_context::with_context::< + crate::RemoteProxyContext>, + _, + >(|context| { + context.or_default().proofs.push(proof.clone()); + }); + + RemoteProxy::::remote_proxy_with_registered_proof( + RawOrigin::Signed(caller).into(), + real_lookup, + None, + Box::new(call), + ) + .unwrap() + }) + } + + assert_last_event::(pallet_proxy::Event::ProxyExecuted { result: Ok(()) }.into()); + + Ok(()) + } + + impl_benchmark_test_suite!(RemoteProxy, crate::tests::new_test_ext(), crate::tests::Test); +} diff --git a/pallets/remote-proxy/src/lib.rs b/pallets/remote-proxy/src/lib.rs new file mode 100644 index 0000000000..9b9ed8c0ab --- /dev/null +++ b/pallets/remote-proxy/src/lib.rs @@ -0,0 +1,496 @@ +// Copyright (C) Polkadot Fellows. +// This file is part of Polkadot. + +// Polkadot 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. + +// Polkadot 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 Polkadot. If not, see . + +//! Remote proxy pallet +//! +//! The pallet provides the functionality for using a proxy on a remote chain. The exact remote +//! location of the proxy depends on the [`RemoteProxyInterface`] implementation provided to this +//! pallet. The underlying implementation works by verifying proofs from the remote location that +//! prove the existence of a proxy. The remote proof is verified against a storage root from the +//! remote location. These storage roots are extracted from the relay chain. So, the security +//! of the proxy depends on the remote location. This means that the remote location should be a +//! trusted chain that for example doesn't create fake proxies. +//! +//! ## Functions +//! +//! The pallet provides the following functions: +//! +//! - [`Pallet::remote_proxy`]: Dispatch a wrapped call using the given proof over the existence of +//! a remote proxy. +//! +//! - [`Pallet::register_remote_proxy_proof`]: Register the given `proof` in the current dispatch. +//! +//! - [`Pallet::remote_proxy_with_registered_proof`]: Use a previously registered `proof` to +//! dispatch the wrapped call. +//! +//! ## Security considerations +//! +//! As explained above the security of the proxy depends on the remote location. So, if the remote +//! location is not trusted, it should not be configured as remote location. When configuring +//! [`MaxStorageRootsToKeep`](Config::MaxStorageRootsToKeep) it should be considered that the +//! lifetime of a proxy will be [`MaxStorageRootsToKeep`](Config::MaxStorageRootsToKeep) in the +//! past. This means when deleting a proxy at the remote location at X, it will take +//! [`MaxStorageRootsToKeep`](Config::MaxStorageRootsToKeep) time until the proxy can not be used +//! anymore. The reason for this is that the caller will be able to provide an old `proof` at which +//! the proxy was still available. + +#![cfg_attr(not(feature = "std"), no_std)] + +extern crate alloc; + +#[cfg(feature = "runtime-benchmarks")] +mod benchmarking; +#[cfg(test)] +mod tests; +mod weight; + +use alloc::{boxed::Box, vec::Vec}; +use codec::{Encode, MaxEncodedLen}; +use frame_support::{storage::storage_prefix, Parameter, StorageHasher, Twox64Concat}; +use scale_info::TypeInfo; +use sp_core::Hasher; +use sp_runtime::traits::Saturating; + +pub use cumulus_primitives_core::PersistedValidationData; +pub use pallet::*; +pub use pallet_proxy::ProxyDefinition; +pub use weight::WeightInfo; + +/// The remote proxy interface. +pub trait RemoteProxyInterface { + /// The remote account id. + type RemoteAccountId: Parameter + MaxEncodedLen; + /// The remote proxy type. + type RemoteProxyType: Parameter + MaxEncodedLen; + /// The remote block number. + type RemoteBlockNumber: Parameter + + Saturating + + MaxEncodedLen + + Default + + PartialOrd + + Ord + + From; + /// The hash type used by the remote chain. + type RemoteHash: Parameter + MaxEncodedLen; + /// The hasher used by the remote chain. + type RemoteHasher: Hasher; + + /// Get the latest block to storage root mapping. + fn block_to_storage_root( + validation_data: &PersistedValidationData, + ) -> Option<(Self::RemoteBlockNumber, ::Out)>; + + /// The storage key where to find the [`ProxyDefinition`] for the given proxy account in the + /// remote chain. + fn proxy_definition_storage_key(proxy: &Self::RemoteAccountId) -> Vec { + let mut key = storage_prefix(b"Proxy", b"Proxies").to_vec(); + proxy.using_encoded(|p| { + key.extend(Twox64Concat::hash(p)); + }); + key + } + + /// Convert the local account id to the remote account id. + /// + /// If the conversion is not possible, return `None`. + fn local_to_remote_account_id(local: &AccountId) -> Option; + + /// Convert the remote proxy definition to the local proxy definition. + /// + /// If the conversion is not possible, return `None`. + fn remote_to_local_proxy_defintion( + remote: ProxyDefinition< + Self::RemoteAccountId, + Self::RemoteProxyType, + Self::RemoteBlockNumber, + >, + ) -> Option>; + + /// Create a remote proxy proof to be used in benchmarking. + /// + /// Returns the `proof`, `block_number` and `storage_root`. The later are required to validate + /// the `proof`. + #[cfg(feature = "runtime-benchmarks")] + fn create_remote_proxy_proof( + caller: &AccountId, + proxy: &AccountId, + ) -> (RemoteProxyProof, Self::RemoteBlockNumber, Self::RemoteHash); +} + +#[frame_support::pallet] +pub mod pallet { + use super::*; + use cumulus_pallet_parachain_system::OnSystemEvent; + use cumulus_primitives_core::PersistedValidationData; + use frame_support::{dispatch_context, pallet_prelude::*, traits::IsSubType}; + use frame_system::pallet_prelude::*; + use sp_runtime::traits::{Dispatchable, StaticLookup, Zero}; + + type AccountIdLookupOf = <::Lookup as StaticLookup>::Source; + + pub(crate) type RemoteBlockNumberOf = + <>::RemoteProxy as RemoteProxyInterface< + ::AccountId, + ::ProxyType, + BlockNumberFor, + >>::RemoteBlockNumber; + type RemoteAccountIdOf = <>::RemoteProxy as RemoteProxyInterface< + ::AccountId, + ::ProxyType, + BlockNumberFor, + >>::RemoteAccountId; + type RemoteHasherOf = <>::RemoteProxy as RemoteProxyInterface< + ::AccountId, + ::ProxyType, + BlockNumberFor, + >>::RemoteHasher; + type RemoteHashOf = <>::RemoteProxy as RemoteProxyInterface< + ::AccountId, + ::ProxyType, + BlockNumberFor, + >>::RemoteHash; + type RemoteProxyTypeOf = <>::RemoteProxy as RemoteProxyInterface< + ::AccountId, + ::ProxyType, + BlockNumberFor, + >>::RemoteProxyType; + type WeightInfoOf = >::WeightInfo; + + #[pallet::pallet] + pub struct Pallet(_); + + /// Stores the last [`Config::MaxStorageRootsToKeep`] block to storage root mappings of the + /// target chain. + #[pallet::storage] + pub type BlockToRoot, I: 'static = ()> = StorageValue< + _, + BoundedVec<(RemoteBlockNumberOf, RemoteHashOf), T::MaxStorageRootsToKeep>, + ValueQuery, + >; + + /// Configuration trait. + #[pallet::config] + pub trait Config: frame_system::Config + pallet_proxy::Config { + /// The maximum number of storage roots to keep. + /// + /// The storage roots are used to validate the remote proofs. The more we keep in storage, + /// the older the proof can be. This is not only seen as a maximum number, but also as the + /// maximum difference between the latest and the oldest storage root stored. This means + /// that if the chain for example did not progress for `MaxStorageRootsToKeep` blocks, only + /// the latest added storage root will be available for validating proofs. + type MaxStorageRootsToKeep: Get; + + /// The interface for interacting with the remote proxy. + type RemoteProxy: RemoteProxyInterface< + Self::AccountId, + Self::ProxyType, + BlockNumberFor, + >; + + /// Weight information for extrinsics in this pallet. + type WeightInfo: WeightInfo; + } + + impl, I: 'static> OnSystemEvent for Pallet { + fn on_validation_data(validation_data: &PersistedValidationData) { + let Some((block, hash)) = T::RemoteProxy::block_to_storage_root(validation_data) else { + return; + }; + + // Update the block to root mappings. + BlockToRoot::::mutate(|roots| { + let delete_up_to = + block.clone().saturating_sub(T::MaxStorageRootsToKeep::get().into()); + + while roots.first().is_some_and(|f| f.0 <= delete_up_to) { + roots.remove(0); + } + + // We always remove all the old items before, thus there should always be space in + // the vector. + let _res = roots.try_push((block, hash)); + debug_assert!(_res.is_ok()); + }); + } + + fn on_validation_code_applied() {} + } + + #[pallet::error] + #[derive(PartialEq)] + pub enum Error { + /// The local account id could not converted to the remote account id. + CouldNotConvertLocalToRemoteAccountId, + /// The anchor block of the remote proof is unknown. + UnknownProofAnchorBlock, + /// The proxy definition could not be found in the proof. + InvalidProof, + /// Failed to decode the remote proxy definition from the proof. + ProxyDefinitionDecodingFailed, + /// Announcement, if made at all, was made too recently. + Unannounced, + /// Could not find any matching proxy definition in the proof. + DidNotFindMatchingProxyDefinition, + /// Proxy proof not registered. + ProxyProofNotRegistered, + } + + /// The remote proxy proof to prove the existence of a proxy account. + #[derive(core::fmt::Debug, Clone, Decode, Encode, TypeInfo, PartialEq, Eq)] + pub enum RemoteProxyProof { + /// Assumes the default proxy storage layout. + RelayChain { proof: Vec>, block: RemoteBlockNumber }, + } + + /// The dispatch context to keep track of registered proofs. + #[derive(Default)] + pub(crate) struct RemoteProxyContext { + /// The registered proofs. + pub(crate) proofs: Vec>, + } + + #[pallet::call] + impl, I: 'static> Pallet { + /// Dispatch the given `call` from an account that the sender is authorised on a remote + /// chain. + /// + /// The dispatch origin for this call must be _Signed_. + /// + /// Parameters: + /// - `real`: The account that the proxy will make a call on behalf of. + /// - `force_proxy_type`: Specify the exact proxy type to be used and checked for this call. + /// - `call`: The call to be made by the `real` account. + /// - `proof`: The proof from the remote chain about the existence of the proxy. + #[pallet::call_index(0)] + #[pallet::weight({ + let di = call.get_dispatch_info(); + (WeightInfoOf::::remote_proxy() + // AccountData for inner call origin accountdata. + .saturating_add(T::DbWeight::get().reads_writes(1, 1)) + .saturating_add(di.weight), + di.class) + })] + pub fn remote_proxy( + origin: OriginFor, + real: AccountIdLookupOf, + force_proxy_type: Option, + call: Box<::RuntimeCall>, + proof: RemoteProxyProof>, + ) -> DispatchResult { + let who = ensure_signed(origin)?; + let real = T::Lookup::lookup(real)?; + + Self::do_remote_proxy(who, real, force_proxy_type, *call, proof) + } + + /// Register a given remote proxy proof in the current [`dispatch_context`]. + /// + /// The registered remote proof can then be used later in the same context to execute a + /// remote proxy call. This is for example useful when having a multisig operation. The + /// multisig call can use [`Self::remote_proxy_with_registered_proof`] to get an approval by + /// the members of the multisig. The final execution of the multisig call should be at least + /// a batch of `register_remote_proxy_proof` and the multisig call that uses + /// `remote_proxy_with_registered_proof`. This way the final approver can use a recent proof + /// to prove the existence of the remote proxy. Otherwise it would require the multisig + /// members to approve the call in [`Config::MaxStorageRootsToKeep`] amount of time. + /// + /// It is supported to register multiple proofs, but the proofs need to be consumed in the + /// reverse order as they were registered. Basically this means last in, first out. + /// + /// The [`dispatch_context`] spans the entire lifetime of a transaction and every call in + /// the transaction gets access to the same context. + /// + /// # Example + /// + /// ```ignore + /// batch([ + /// register_remote_proxy_proof, + /// as_multisig(remote_proxy_with_registered_proof(transfer)) + /// ]) + /// ``` + /// + /// As `proofs` can not be verified indefinitely (the time the storage roots are stored is + /// limited) this function provides the possibility to provide a "fresh proof" at time of + /// dispatch. As in the example above, this could be useful for multisig operation that + /// depend on multiple members to approve a certain action, which can take multiple days. + #[pallet::call_index(1)] + #[pallet::weight({(WeightInfoOf::::register_remote_proxy_proof(), DispatchClass::Normal)})] + pub fn register_remote_proxy_proof( + origin: OriginFor, + proof: RemoteProxyProof>, + ) -> DispatchResult { + ensure_signed(origin)?; + + dispatch_context::with_context::>, _>( + |context| { + context.or_default().proofs.push(proof); + }, + ); + + Ok(()) + } + + /// Dispatch the given `call` from an account that the sender is authorised on a remote + /// chain. + /// + /// The dispatch origin for this call must be _Signed_. The difference to + /// [`Self::remote_proxy`] is that the proof nees to registered before using + /// [`Self::register_remote_proxy_proof`] (see for more information). + /// + /// Parameters: + /// - `real`: The account that the proxy will make a call on behalf of. + /// - `force_proxy_type`: Specify the exact proxy type to be used and checked for this call. + /// - `call`: The call to be made by the `real` account. + #[pallet::call_index(2)] + #[pallet::weight({ + let di = call.get_dispatch_info(); + (WeightInfoOf::::remote_proxy_with_registered_proof() + // AccountData for inner call origin accountdata. + .saturating_add(T::DbWeight::get().reads_writes(1, 1)) + .saturating_add(di.weight), + di.class) + })] + pub fn remote_proxy_with_registered_proof( + origin: OriginFor, + real: AccountIdLookupOf, + force_proxy_type: Option, + call: Box<::RuntimeCall>, + ) -> DispatchResult { + let who = ensure_signed(origin)?; + let real = T::Lookup::lookup(real)?; + + let proof = dispatch_context::with_context::< + RemoteProxyContext>, + _, + >(|context| context.or_default().proofs.pop()) + .flatten() + .ok_or(Error::::ProxyProofNotRegistered)?; + + Self::do_remote_proxy(who, real, force_proxy_type, *call, proof) + } + } + + impl, I: 'static> Pallet { + fn do_remote_proxy( + who: T::AccountId, + real: T::AccountId, + force_proxy_type: Option, + call: ::RuntimeCall, + proof: RemoteProxyProof>, + ) -> DispatchResult { + let Some(real_remote) = T::RemoteProxy::local_to_remote_account_id(&real) else { + return Err(Error::::CouldNotConvertLocalToRemoteAccountId.into()); + }; + + let def = match proof { + RemoteProxyProof::RelayChain { proof, block } => { + let roots = BlockToRoot::::get(); + + let Ok(storage_root) = roots + .binary_search_by(|(b, _)| b.cmp(&block)) + .map(|pos| roots[pos].1.clone()) + else { + return Err(Error::::UnknownProofAnchorBlock.into()); + }; + + let key = T::RemoteProxy::proxy_definition_storage_key(&real_remote); + + let db = + sp_trie::StorageProof::new(proof).into_memory_db::>(); + let value = sp_trie::read_trie_value::, _>( + &db, + &storage_root, + &key, + None, + None, + ) + .ok() + .flatten() + .ok_or(Error::::InvalidProof)?; + + let proxy_definitions = alloc::vec::Vec::< + ProxyDefinition< + RemoteAccountIdOf, + RemoteProxyTypeOf, + RemoteBlockNumberOf, + >, + >::decode(&mut &value[..]) + .map_err(|_| Error::::ProxyDefinitionDecodingFailed)?; + + let f = |x: &ProxyDefinition< + T::AccountId, + T::ProxyType, + BlockNumberFor, + >| + -> bool { + x.delegate == who && + force_proxy_type.as_ref().map_or(true, |y| &x.proxy_type == y) + }; + + proxy_definitions + .into_iter() + .filter_map(T::RemoteProxy::remote_to_local_proxy_defintion) + .find(f) + .ok_or(Error::::DidNotFindMatchingProxyDefinition)? + }, + }; + + ensure!(def.delay.is_zero(), Error::::Unannounced); + + Self::do_proxy(def, real, call); + + Ok(()) + } + + // TODO: Make upstream public and use that one. + fn do_proxy( + def: ProxyDefinition>, + real: T::AccountId, + call: ::RuntimeCall, + ) { + use frame_support::traits::{InstanceFilter as _, OriginTrait as _}; + // This is a freshly authenticated new account, the origin restrictions doesn't apply. + let mut origin: T::RuntimeOrigin = frame_system::RawOrigin::Signed(real).into(); + origin.add_filter(move |c: &::RuntimeCall| { + let c = ::RuntimeCall::from_ref(c); + // We make sure the proxy call does not modify proxies. + match c.is_sub_type() { + // Proxy call cannot add or remove a proxy with more permissions than it already + // has. + Some(pallet_proxy::Call::add_proxy { ref proxy_type, .. }) | + Some(pallet_proxy::Call::remove_proxy { ref proxy_type, .. }) + if !def.proxy_type.is_superset(proxy_type) => + false, + // Proxy call cannot remove all proxies or kill pure proxies unless it has full + // permissions. + Some(pallet_proxy::Call::remove_proxies { .. }) | + Some(pallet_proxy::Call::kill_pure { .. }) + if def.proxy_type != T::ProxyType::default() => + false, + _ => def.proxy_type.filter(c), + } + }); + let e = call.dispatch(origin); + frame_system::Pallet::::deposit_event( + ::RuntimeEvent::from( + pallet_proxy::Event::ProxyExecuted { + result: e.map(|_| ()).map_err(|e| e.error), + }, + ), + ); + } + } +} diff --git a/pallets/remote-proxy/src/tests.rs b/pallets/remote-proxy/src/tests.rs new file mode 100644 index 0000000000..d859c84eeb --- /dev/null +++ b/pallets/remote-proxy/src/tests.rs @@ -0,0 +1,637 @@ +// Copyright (C) Polkadot Fellows. +// This file is part of Polkadot. + +// Polkadot 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. + +// Polkadot 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 Polkadot. If not, see . + +// Tests for Remote Proxy Pallet + +use super::*; +use crate as remote_proxy; +use codec::Decode; +use cumulus_pallet_parachain_system::OnSystemEvent; +use frame_support::{ + assert_err, assert_ok, construct_runtime, derive_impl, + traits::{Contains, Currency}, +}; +use frame_system::Call as SystemCall; +use pallet_balances::Call as BalancesCall; +use pallet_proxy::{Error as ProxyError, Event as ProxyEvent}; +use pallet_utility::Call as UtilityCall; +use sp_core::{ConstU32, ConstU64, H256}; +use sp_io::TestExternalities; +use sp_runtime::{ + traits::{BlakeTwo256, Dispatchable}, + BoundedVec, BuildStorage, +}; + +type Block = frame_system::mocking::MockBlock; + +construct_runtime!( + pub struct Test { + System: frame_system, + Balances: pallet_balances, + Proxy: pallet_proxy, + Utility: pallet_utility, + RemoteProxy: remote_proxy, + } +); + +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] +impl frame_system::Config for Test { + type Block = Block; + type BaseCallFilter = BaseFilter; + type AccountData = pallet_balances::AccountData; +} + +#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig)] +impl pallet_balances::Config for Test { + type ReserveIdentifier = [u8; 8]; + type AccountStore = System; +} + +impl pallet_utility::Config for Test { + type RuntimeEvent = RuntimeEvent; + type RuntimeCall = RuntimeCall; + type PalletsOrigin = OriginCaller; + type WeightInfo = (); +} + +#[derive( + Copy, + Clone, + Eq, + PartialEq, + Ord, + PartialOrd, + Encode, + Decode, + Debug, + MaxEncodedLen, + scale_info::TypeInfo, +)] +pub enum ProxyType { + Any, + JustTransfer, + JustUtility, +} +impl Default for ProxyType { + fn default() -> Self { + Self::Any + } +} +impl frame_support::traits::InstanceFilter for ProxyType { + fn filter(&self, c: &RuntimeCall) -> bool { + match self { + ProxyType::Any => true, + ProxyType::JustTransfer => { + matches!( + c, + RuntimeCall::Balances(pallet_balances::Call::transfer_allow_death { .. }) + ) + }, + ProxyType::JustUtility => matches!(c, RuntimeCall::Utility { .. }), + } + } + fn is_superset(&self, o: &Self) -> bool { + self == &ProxyType::Any || self == o + } +} +pub struct BaseFilter; +impl Contains for BaseFilter { + fn contains(c: &RuntimeCall) -> bool { + match *c { + // Remark is used as a no-op call in the benchmarking + RuntimeCall::System(SystemCall::remark { .. }) => true, + RuntimeCall::System(_) => false, + _ => true, + } + } +} +impl pallet_proxy::Config for Test { + type RuntimeEvent = RuntimeEvent; + type RuntimeCall = RuntimeCall; + type Currency = Balances; + type ProxyType = ProxyType; + type ProxyDepositBase = ConstU64<1>; + type ProxyDepositFactor = ConstU64<1>; + type MaxProxies = ConstU32<4>; + type WeightInfo = (); + type CallHasher = BlakeTwo256; + type MaxPending = ConstU32<2>; + type AnnouncementDepositBase = ConstU64<1>; + type AnnouncementDepositFactor = ConstU64<1>; +} + +pub struct RemoteProxyImpl; + +impl crate::RemoteProxyInterface for RemoteProxyImpl { + type RemoteAccountId = u64; + type RemoteProxyType = ProxyType; + type RemoteBlockNumber = u64; + type RemoteHash = H256; + type RemoteHasher = BlakeTwo256; + + fn block_to_storage_root( + validation_data: &PersistedValidationData, + ) -> Option<(Self::RemoteBlockNumber, ::Out)> { + Some((validation_data.relay_parent_number as _, validation_data.relay_parent_storage_root)) + } + + fn local_to_remote_account_id(local: &u64) -> Option { + Some(*local) + } + + fn remote_to_local_proxy_defintion( + remote: ProxyDefinition< + Self::RemoteAccountId, + Self::RemoteProxyType, + Self::RemoteBlockNumber, + >, + ) -> Option> { + Some(remote) + } + + #[cfg(feature = "runtime-benchmarks")] + fn create_remote_proxy_proof( + caller: &u64, + proxy: &u64, + ) -> (RemoteProxyProof, u64, H256) { + use sp_trie::TrieMut; + + let (mut db, mut root) = sp_trie::MemoryDB::::default_with_root(); + let mut trie = + sp_trie::TrieDBMutBuilder::>::new(&mut db, &mut root).build(); + + let proxy_definition = vec![ProxyDefinition:: { + delegate: caller.clone(), + proxy_type: ProxyType::default(), + delay: 0, + }]; + + trie.insert(&Self::proxy_definition_storage_key(proxy), &proxy_definition.encode()) + .unwrap(); + drop(trie); + + ( + RemoteProxyProof::RelayChain { + proof: db.drain().into_values().map(|d| d.0).collect(), + block: 1, + }, + 1, + root, + ) + } +} + +impl Config for Test { + type MaxStorageRootsToKeep = ConstU32<10>; + type RemoteProxy = RemoteProxyImpl; + type WeightInfo = (); +} + +pub fn new_test_ext() -> TestExternalities { + let mut t = frame_system::GenesisConfig::::default().build_storage().unwrap(); + pallet_balances::GenesisConfig:: { + balances: vec![(1, 10), (2, 10), (3, 10), (4, 10), (5, 3)], + } + .assimilate_storage(&mut t) + .unwrap(); + let mut ext = TestExternalities::new(t); + ext.execute_with(|| System::set_block_number(1)); + ext +} + +fn call_transfer(dest: u64, value: u64) -> RuntimeCall { + RuntimeCall::Balances(BalancesCall::transfer_allow_death { dest, value }) +} + +#[test] +fn remote_proxy_works() { + let mut ext = new_test_ext(); + + let anon = ext.execute_with(|| { + Balances::make_free_balance_be(&1, 11); // An extra one for the ED. + assert_ok!(Proxy::create_pure(RuntimeOrigin::signed(1), ProxyType::Any, 0, 0)); + let anon = Proxy::pure_account(&1, &ProxyType::Any, 0, None); + System::assert_last_event( + ProxyEvent::PureCreated { + pure: anon, + who: 1, + proxy_type: ProxyType::Any, + disambiguation_index: 0, + } + .into(), + ); + anon + }); + + let proof = sp_state_machine::prove_read( + ext.as_backend(), + [pallet_proxy::Proxies::::hashed_key_for(anon)], + ) + .unwrap(); + let root = *ext.as_backend().root(); + + new_test_ext().execute_with(|| { + let call = Box::new(call_transfer(6, 1)); + assert_ok!(Balances::transfer_allow_death(RuntimeOrigin::signed(3), anon, 5)); + assert_eq!(Balances::free_balance(6), 0); + assert_err!( + Proxy::proxy(RuntimeOrigin::signed(1), anon, None, call.clone()), + ProxyError::::NotProxy + ); + assert_eq!(Balances::free_balance(6), 0); + + RemoteProxy::on_validation_data(&PersistedValidationData { + parent_head: vec![].into(), + relay_parent_number: 1, + relay_parent_storage_root: root, + max_pov_size: 5000000, + }); + assert_ok!(RemoteProxy::remote_proxy( + RuntimeOrigin::signed(1), + anon, + None, + call.clone(), + RemoteProxyProof::RelayChain { + proof: proof.clone().into_iter_nodes().collect(), + block: 1 + } + )); + + System::assert_last_event(ProxyEvent::ProxyExecuted { result: Ok(()) }.into()); + assert_eq!(Balances::free_balance(6), 1); + + assert_err!( + RemoteProxy::remote_proxy( + RuntimeOrigin::signed(1), + anon, + None, + call.clone(), + RemoteProxyProof::RelayChain { proof: proof.into_iter_nodes().collect(), block: 2 } + ), + Error::::UnknownProofAnchorBlock + ); + + assert_err!( + RemoteProxy::remote_proxy( + RuntimeOrigin::signed(1), + anon, + None, + call, + RemoteProxyProof::RelayChain { proof: Vec::new(), block: 1 } + ), + Error::::InvalidProof + ); + }); +} + +#[test] +fn remote_proxy_register_works() { + let mut ext = new_test_ext(); + + let anon = ext.execute_with(|| { + Balances::make_free_balance_be(&1, 11); // An extra one for the ED. + assert_ok!(Proxy::create_pure(RuntimeOrigin::signed(1), ProxyType::Any, 0, 0)); + let anon = Proxy::pure_account(&1, &ProxyType::Any, 0, None); + System::assert_last_event( + ProxyEvent::PureCreated { + pure: anon, + who: 1, + proxy_type: ProxyType::Any, + disambiguation_index: 0, + } + .into(), + ); + anon + }); + + let proof = sp_state_machine::prove_read( + ext.as_backend(), + [pallet_proxy::Proxies::::hashed_key_for(anon)], + ) + .unwrap(); + let root = *ext.as_backend().root(); + + new_test_ext().execute_with(|| { + let call = Box::new(call_transfer(6, 1)); + assert_ok!(Balances::transfer_allow_death(RuntimeOrigin::signed(3), anon, 5)); + assert_eq!(Balances::free_balance(6), 0); + assert_err!( + Proxy::proxy(RuntimeOrigin::signed(1), anon, None, call.clone()), + ProxyError::::NotProxy + ); + assert_eq!(Balances::free_balance(6), 0); + + RemoteProxy::on_validation_data(&PersistedValidationData { + parent_head: vec![].into(), + relay_parent_number: 1, + relay_parent_storage_root: root, + max_pov_size: 5000000, + }); + assert_ok!(RuntimeCall::from(UtilityCall::batch { + calls: vec![ + crate::Call::register_remote_proxy_proof { + proof: RemoteProxyProof::RelayChain { + proof: proof.clone().into_iter_nodes().collect(), + block: 1 + } + } + .into(), + crate::Call::remote_proxy_with_registered_proof { + real: anon, + force_proxy_type: None, + call: call.clone(), + } + .into() + ] + }) + .dispatch(RuntimeOrigin::signed(1))); + + System::assert_has_event(ProxyEvent::ProxyExecuted { result: Ok(()) }.into()); + System::reset_events(); + assert_eq!(Balances::free_balance(6), 1); + + assert_ok!(RuntimeCall::from(UtilityCall::batch { + calls: vec![ + crate::Call::register_remote_proxy_proof { + proof: RemoteProxyProof::RelayChain { + proof: proof.clone().into_iter_nodes().collect(), + block: 1 + } + } + .into(), + UtilityCall::batch { + calls: vec![crate::Call::remote_proxy_with_registered_proof { + real: anon, + force_proxy_type: None, + call: call.clone(), + } + .into()] + } + .into() + ] + }) + .dispatch(RuntimeOrigin::signed(1))); + + System::assert_has_event(ProxyEvent::ProxyExecuted { result: Ok(()) }.into()); + assert_eq!(Balances::free_balance(6), 2); + + assert_err!( + RuntimeCall::from(UtilityCall::batch_all { + calls: vec![ + crate::Call::register_remote_proxy_proof { + proof: RemoteProxyProof::RelayChain { + proof: proof.clone().into_iter_nodes().collect(), + block: 1 + } + } + .into(), + crate::Call::remote_proxy_with_registered_proof { + real: anon, + force_proxy_type: None, + call: call.clone(), + } + .into(), + crate::Call::remote_proxy_with_registered_proof { + real: anon, + force_proxy_type: None, + call: call.clone(), + } + .into() + ] + }) + .dispatch(RuntimeOrigin::signed(1)) + .map_err(|e| e.error), + Error::::ProxyProofNotRegistered + ); + }); +} + +#[test] +fn remote_proxy_multiple_register_works() { + let mut ext = new_test_ext(); + + let (anon, anon2) = ext.execute_with(|| { + Balances::make_free_balance_be(&1, 11); // An extra one for the ED. + assert_ok!(Proxy::create_pure(RuntimeOrigin::signed(1), ProxyType::Any, 0, 0)); + let anon = Proxy::pure_account(&1, &ProxyType::Any, 0, None); + System::assert_last_event( + ProxyEvent::PureCreated { + pure: anon, + who: 1, + proxy_type: ProxyType::Any, + disambiguation_index: 0, + } + .into(), + ); + + Balances::make_free_balance_be(&2, 11); // An extra one for the ED. + assert_ok!(Proxy::create_pure(RuntimeOrigin::signed(1), ProxyType::Any, 0, 1)); + let anon2 = Proxy::pure_account(&1, &ProxyType::Any, 1, None); + System::assert_last_event( + ProxyEvent::PureCreated { + pure: anon2, + who: 1, + proxy_type: ProxyType::Any, + disambiguation_index: 1, + } + .into(), + ); + + (anon, anon2) + }); + + let proof = sp_state_machine::prove_read( + ext.as_backend(), + [pallet_proxy::Proxies::::hashed_key_for(anon)], + ) + .unwrap(); + + let proof2 = sp_state_machine::prove_read( + ext.as_backend(), + [pallet_proxy::Proxies::::hashed_key_for(anon2)], + ) + .unwrap(); + + let root = *ext.as_backend().root(); + + new_test_ext().execute_with(|| { + let call = Box::new(call_transfer(6, 1)); + assert_ok!(Balances::transfer_allow_death(RuntimeOrigin::signed(3), anon, 5)); + assert_ok!(Balances::transfer_allow_death(RuntimeOrigin::signed(4), anon2, 5)); + assert_eq!(Balances::free_balance(6), 0); + assert_err!( + Proxy::proxy(RuntimeOrigin::signed(1), anon, None, call.clone()), + ProxyError::::NotProxy + ); + assert_eq!(Balances::free_balance(6), 0); + + RemoteProxy::on_validation_data(&PersistedValidationData { + parent_head: vec![].into(), + relay_parent_number: 1, + relay_parent_storage_root: root, + max_pov_size: 5000000, + }); + assert_ok!(RuntimeCall::from(UtilityCall::batch { + calls: vec![ + crate::Call::register_remote_proxy_proof { + proof: RemoteProxyProof::RelayChain { + proof: proof.clone().into_iter_nodes().collect(), + block: 1 + } + } + .into(), + crate::Call::remote_proxy_with_registered_proof { + real: anon, + force_proxy_type: None, + call: call.clone(), + } + .into(), + crate::Call::register_remote_proxy_proof { + proof: RemoteProxyProof::RelayChain { + proof: proof2.clone().into_iter_nodes().collect(), + block: 1 + } + } + .into(), + crate::Call::remote_proxy_with_registered_proof { + real: anon2, + force_proxy_type: None, + call: call.clone(), + } + .into() + ] + }) + .dispatch(RuntimeOrigin::signed(1))); + + System::assert_has_event(ProxyEvent::ProxyExecuted { result: Ok(()) }.into()); + System::reset_events(); + assert_eq!(Balances::free_balance(6), 2); + + assert_ok!(RuntimeCall::from(UtilityCall::batch { + calls: vec![ + crate::Call::register_remote_proxy_proof { + proof: RemoteProxyProof::RelayChain { + proof: proof.clone().into_iter_nodes().collect(), + block: 1 + } + } + .into(), + crate::Call::register_remote_proxy_proof { + proof: RemoteProxyProof::RelayChain { + proof: proof2.clone().into_iter_nodes().collect(), + block: 1 + } + } + .into(), + crate::Call::remote_proxy_with_registered_proof { + real: anon2, + force_proxy_type: None, + call: call.clone(), + } + .into(), + crate::Call::remote_proxy_with_registered_proof { + real: anon, + force_proxy_type: None, + call: call.clone(), + } + .into() + ] + }) + .dispatch(RuntimeOrigin::signed(1))); + + System::assert_has_event(ProxyEvent::ProxyExecuted { result: Ok(()) }.into()); + System::reset_events(); + assert_eq!(Balances::free_balance(6), 4); + + assert_err!( + RuntimeCall::from(UtilityCall::batch_all { + calls: vec![ + crate::Call::register_remote_proxy_proof { + proof: RemoteProxyProof::RelayChain { + proof: proof.clone().into_iter_nodes().collect(), + block: 1 + } + } + .into(), + crate::Call::register_remote_proxy_proof { + proof: RemoteProxyProof::RelayChain { + proof: proof2.clone().into_iter_nodes().collect(), + block: 1 + } + } + .into(), + crate::Call::remote_proxy_with_registered_proof { + real: anon, + force_proxy_type: None, + call: call.clone(), + } + .into(), + ] + }) + .dispatch(RuntimeOrigin::signed(1)) + .map_err(|e| e.error), + Error::::InvalidProof + ); + }); +} + +#[test] +fn clean_up_works_and_old_blocks_are_rejected() { + new_test_ext().execute_with(|| { + let root = H256::zero(); + let call = Box::new(call_transfer(6, 1)); + + BlockToRoot::::set(BoundedVec::truncate_from(vec![ + (0, root), + (10, root), + (20, root), + (29, root), + ])); + + RemoteProxy::on_validation_data(&PersistedValidationData { + parent_head: vec![].into(), + relay_parent_number: 30, + relay_parent_storage_root: root, + max_pov_size: 5000000, + }); + BlockToRoot::::get() + .iter() + .for_each(|(b, _)| assert!(*b == 29 || *b == 30)); + assert_err!( + RemoteProxy::remote_proxy( + RuntimeOrigin::signed(1), + 1000, + None, + call.clone(), + RemoteProxyProof::RelayChain { proof: vec![], block: 5 } + ), + Error::::UnknownProofAnchorBlock + ); + + for i in 31u32..=40u32 { + RemoteProxy::on_validation_data(&PersistedValidationData { + parent_head: vec![].into(), + relay_parent_number: dbg!(i), + relay_parent_storage_root: root, + max_pov_size: 5000000, + }); + } + + BlockToRoot::::get() + .iter() + .for_each(|(b, _)| assert!(*b >= 31 && *b <= 40)); + }); +} diff --git a/pallets/remote-proxy/src/weight.rs b/pallets/remote-proxy/src/weight.rs new file mode 100644 index 0000000000..2c0f67d735 --- /dev/null +++ b/pallets/remote-proxy/src/weight.rs @@ -0,0 +1,38 @@ +// Copyright (C) Polkadot Fellows. +// This file is part of Polkadot. + +// Polkadot 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. + +// Polkadot 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 Polkadot. If not, see . + +use frame_support::weights::Weight; + +/// Weight functions needed for `pallet_remote_proxy`. +pub trait WeightInfo { + fn remote_proxy_with_registered_proof() -> Weight; + fn register_remote_proxy_proof() -> Weight; + fn remote_proxy() -> Weight; +} + +impl WeightInfo for () { + fn remote_proxy_with_registered_proof() -> Weight { + Weight::MAX + } + + fn register_remote_proxy_proof() -> Weight { + Weight::MAX + } + + fn remote_proxy() -> Weight { + Weight::MAX + } +} diff --git a/relay/kusama/Cargo.toml b/relay/kusama/Cargo.toml index bbb220140d..05a3dc9f05 100644 --- a/relay/kusama/Cargo.toml +++ b/relay/kusama/Cargo.toml @@ -212,6 +212,7 @@ std = [ "sp-storage/std", "sp-tracing/std", "sp-transaction-pool/std", + "sp-trie/std", "sp-version/std", "substrate-wasm-builder", "xcm-builder/std", @@ -225,6 +226,7 @@ runtime-benchmarks = [ "frame-support/runtime-benchmarks", "frame-system-benchmarking/runtime-benchmarks", "frame-system/runtime-benchmarks", + "kusama-runtime-constants/runtime-benchmarks", "pallet-asset-rate/runtime-benchmarks", "pallet-babe/runtime-benchmarks", "pallet-bags-list/runtime-benchmarks", diff --git a/relay/kusama/constants/Cargo.toml b/relay/kusama/constants/Cargo.toml index 1c27740baf..d696045292 100644 --- a/relay/kusama/constants/Cargo.toml +++ b/relay/kusama/constants/Cargo.toml @@ -8,6 +8,8 @@ license.workspace = true [dependencies] smallvec = { workspace = true } +codec = { workspace = true } +scale-info = { workspace = true } frame-support = { workspace = true } polkadot-primitives = { workspace = true } @@ -15,18 +17,33 @@ polkadot-runtime-common = { workspace = true } sp-runtime = { workspace = true } sp-weights = { workspace = true } sp-core = { workspace = true } +sp-trie = { workspace = true, optional = true } +pallet-remote-proxy = { workspace = true } xcm-builder = { workspace = true } [features] default = ["std"] std = [ + "codec/std", "frame-support/std", + "pallet-remote-proxy/std", "polkadot-primitives/std", "polkadot-runtime-common/std", + "scale-info/std", "sp-core/std", "sp-runtime/std", + "sp-trie?/std", "sp-weights/std", "xcm-builder/std", ] fast-runtime = [] +runtime-benchmarks = [ + "frame-support/runtime-benchmarks", + "pallet-remote-proxy/runtime-benchmarks", + "polkadot-primitives/runtime-benchmarks", + "polkadot-runtime-common/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", + "sp-trie", + "xcm-builder/runtime-benchmarks", +] diff --git a/relay/kusama/constants/src/lib.rs b/relay/kusama/constants/src/lib.rs index 1c7049fead..76bfea46a9 100644 --- a/relay/kusama/constants/src/lib.rs +++ b/relay/kusama/constants/src/lib.rs @@ -16,6 +16,8 @@ #![cfg_attr(not(feature = "std"), no_std)] +extern crate alloc; + pub mod weights; /// Money matters. @@ -134,6 +136,130 @@ pub mod system_parachain { /// Kusama Treasury pallet instance. pub const TREASURY_PALLET_ID: u8 = 18; +pub mod proxy { + use pallet_remote_proxy::ProxyDefinition; + use polkadot_primitives::{AccountId, BlakeTwo256, BlockNumber, Hash}; + use sp_runtime::traits::Convert; + + /// The type used to represent the kinds of proxying allowed. + #[derive( + Copy, + Clone, + Eq, + PartialEq, + Ord, + PartialOrd, + codec::Encode, + codec::Decode, + codec::MaxEncodedLen, + core::fmt::Debug, + scale_info::TypeInfo, + Default, + )] + pub enum ProxyType { + #[codec(index = 0)] + #[default] + Any, + #[codec(index = 1)] + NonTransfer, + #[codec(index = 2)] + Governance, + #[codec(index = 3)] + Staking, + // Index 4 skipped. Formerly `IdentityJudgement`. + #[codec(index = 5)] + CancelProxy, + #[codec(index = 6)] + Auction, + #[codec(index = 7)] + Society, + #[codec(index = 8)] + NominationPools, + #[codec(index = 9)] + Spokesperson, + #[codec(index = 10)] + ParaRegistration, + } + + /// Remote proxy interface that uses the relay chain as remote location. + pub struct RemoteProxyInterface( + core::marker::PhantomData<(LocalProxyType, ProxyDefinitionConverter)>, + ); + + impl< + LocalProxyType, + ProxyDefinitionConverter: Convert< + ProxyDefinition, + Option>, + >, + > pallet_remote_proxy::RemoteProxyInterface + for RemoteProxyInterface + { + type RemoteAccountId = AccountId; + + type RemoteProxyType = ProxyType; + + type RemoteBlockNumber = BlockNumber; + + type RemoteHash = Hash; + + type RemoteHasher = BlakeTwo256; + + fn block_to_storage_root( + validation_data: &polkadot_primitives::PersistedValidationData, + ) -> Option<(Self::RemoteBlockNumber, ::Out)> { + Some((validation_data.relay_parent_number, validation_data.relay_parent_storage_root)) + } + + fn local_to_remote_account_id(local: &AccountId) -> Option { + Some(local.clone()) + } + + fn remote_to_local_proxy_defintion( + remote: ProxyDefinition< + Self::RemoteAccountId, + Self::RemoteProxyType, + Self::RemoteBlockNumber, + >, + ) -> Option> { + ProxyDefinitionConverter::convert(remote) + } + + #[cfg(feature = "runtime-benchmarks")] + fn create_remote_proxy_proof( + caller: &AccountId, + proxy: &AccountId, + ) -> (pallet_remote_proxy::RemoteProxyProof, BlockNumber, Hash) { + use codec::Encode; + use sp_trie::TrieMut; + + let (mut db, mut root) = sp_trie::MemoryDB::::default_with_root(); + let mut trie = + sp_trie::TrieDBMutBuilder::>::new(&mut db, &mut root).build(); + + let proxy_definition = + alloc::vec![ProxyDefinition:: { + delegate: caller.clone(), + proxy_type: ProxyType::default(), + delay: 0, + }]; + + trie.insert(&Self::proxy_definition_storage_key(proxy), &proxy_definition.encode()) + .unwrap(); + drop(trie); + + ( + pallet_remote_proxy::RemoteProxyProof::RelayChain { + proof: db.drain().into_values().map(|d| d.0).collect(), + block: 1, + }, + 1, + root, + ) + } + } +} + #[cfg(test)] mod tests { use super::{ diff --git a/relay/kusama/src/lib.rs b/relay/kusama/src/lib.rs index d9865b92cf..a94a576212 100644 --- a/relay/kusama/src/lib.rs +++ b/relay/kusama/src/lib.rs @@ -28,7 +28,7 @@ use frame_support::{ traits::{EnsureOrigin, EnsureOriginWithArg, OnRuntimeUpgrade}, weights::constants::{WEIGHT_PROOF_SIZE_PER_KB, WEIGHT_REF_TIME_PER_MICROS}, }; -use kusama_runtime_constants::system_parachain::coretime::TIMESLICE_PERIOD; +use kusama_runtime_constants::{proxy::ProxyType, system_parachain::coretime::TIMESLICE_PERIOD}; use pallet_nis::WithMaximumOf; use polkadot_primitives::{ slashing, AccountId, AccountIndex, ApprovalVotingParams, Balance, BlockNumber, CandidateEvent, @@ -1165,7 +1165,10 @@ parameter_types! { pub const MaxPending: u16 = 32; } -/// The type used to represent the kinds of proxying allowed. +/// Transparent wrapper around the actual [`ProxyType`]. +/// +/// This is done to have [`ProxyType`] declared in a different crate (constants) and being able to +/// implement [`InstanceFilter`] in this crate. #[derive( Copy, Clone, @@ -1177,41 +1180,21 @@ parameter_types! { Decode, RuntimeDebug, MaxEncodedLen, - TypeInfo, + Default, )] -pub enum ProxyType { - #[codec(index = 0)] - Any, - #[codec(index = 1)] - NonTransfer, - #[codec(index = 2)] - Governance, - #[codec(index = 3)] - Staking, - // Index 4 skipped. Formerly `IdentityJudgement`. - #[codec(index = 5)] - CancelProxy, - #[codec(index = 6)] - Auction, - #[codec(index = 7)] - Society, - #[codec(index = 8)] - NominationPools, - #[codec(index = 9)] - Spokesperson, - #[codec(index = 10)] - ParaRegistration, -} - -impl Default for ProxyType { - fn default() -> Self { - Self::Any +pub struct TransparentProxyType(ProxyType); + +impl scale_info::TypeInfo for TransparentProxyType { + type Identity = ::Identity; + + fn type_info() -> scale_info::Type { + ProxyType::type_info() } } -impl InstanceFilter for ProxyType { +impl InstanceFilter for TransparentProxyType { fn filter(&self, c: &RuntimeCall) -> bool { - match self { + match self.0 { ProxyType::Any => true, ProxyType::NonTransfer => matches!( c, @@ -1317,7 +1300,7 @@ impl InstanceFilter for ProxyType { } } fn is_superset(&self, o: &Self) -> bool { - match (self, o) { + match (self.0, o.0) { (x, y) if x == y => true, (ProxyType::Any, _) => true, (_, ProxyType::Any) => false, @@ -1331,7 +1314,7 @@ impl pallet_proxy::Config for Runtime { type RuntimeEvent = RuntimeEvent; type RuntimeCall = RuntimeCall; type Currency = Balances; - type ProxyType = ProxyType; + type ProxyType = TransparentProxyType; type ProxyDepositBase = ProxyDepositBase; type ProxyDepositFactor = ProxyDepositFactor; type MaxProxies = MaxProxies; diff --git a/relay/polkadot/Cargo.toml b/relay/polkadot/Cargo.toml index 823bcf41a3..9ff7367362 100644 --- a/relay/polkadot/Cargo.toml +++ b/relay/polkadot/Cargo.toml @@ -214,6 +214,7 @@ std = [ "sp-storage/std", "sp-tracing/std", "sp-transaction-pool/std", + "sp-trie/std", "sp-version/std", "substrate-wasm-builder", "xcm-builder/std", @@ -266,6 +267,7 @@ runtime-benchmarks = [ "polkadot-parachain-primitives/runtime-benchmarks", "polkadot-primitives/runtime-benchmarks", "polkadot-runtime-common/runtime-benchmarks", + "polkadot-runtime-constants/runtime-benchmarks", "runtime-parachains/runtime-benchmarks", "sp-runtime/runtime-benchmarks", "sp-staking/runtime-benchmarks", diff --git a/relay/polkadot/constants/Cargo.toml b/relay/polkadot/constants/Cargo.toml index 645d40e475..708a983635 100644 --- a/relay/polkadot/constants/Cargo.toml +++ b/relay/polkadot/constants/Cargo.toml @@ -8,25 +8,42 @@ license.workspace = true [dependencies] smallvec = { workspace = true } +codec = { workspace = true } +scale-info = { workspace = true } frame-support = { workspace = true } +pallet-remote-proxy = { workspace = true } polkadot-primitives = { workspace = true } polkadot-runtime-common = { workspace = true } sp-runtime = { workspace = true } sp-weights = { workspace = true } sp-core = { workspace = true } +sp-trie = { workspace = true, optional = true } xcm-builder = { workspace = true } [features] default = ["std"] std = [ + "codec/std", "frame-support/std", + "pallet-remote-proxy/std", "polkadot-primitives/std", "polkadot-runtime-common/std", + "scale-info/std", "sp-core/std", "sp-runtime/std", + "sp-trie?/std", "sp-weights/std", "xcm-builder/std", ] fast-runtime = [] +runtime-benchmarks = [ + "frame-support/runtime-benchmarks", + "pallet-remote-proxy/runtime-benchmarks", + "polkadot-primitives/runtime-benchmarks", + "polkadot-runtime-common/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", + "sp-trie", + "xcm-builder/runtime-benchmarks", +] diff --git a/relay/polkadot/constants/src/lib.rs b/relay/polkadot/constants/src/lib.rs index 3449d98884..7b5162e595 100644 --- a/relay/polkadot/constants/src/lib.rs +++ b/relay/polkadot/constants/src/lib.rs @@ -16,6 +16,8 @@ #![cfg_attr(not(feature = "std"), no_std)] +extern crate alloc; + pub mod weights; pub use self::currency::DOLLARS; @@ -152,13 +154,128 @@ pub mod system_parachain { /// Polkadot Treasury pallet instance. pub const TREASURY_PALLET_ID: u8 = 19; +pub mod proxy { + use pallet_remote_proxy::ProxyDefinition; + use polkadot_primitives::{AccountId, BlakeTwo256, BlockNumber, Hash}; + use sp_runtime::traits::Convert; + + /// The type used to represent the kinds of proxying allowed. + #[derive( + Copy, + Clone, + Eq, + PartialEq, + Ord, + PartialOrd, + codec::Encode, + codec::Decode, + core::fmt::Debug, + codec::MaxEncodedLen, + scale_info::TypeInfo, + Default, + )] + pub enum ProxyType { + #[default] + Any = 0, + NonTransfer = 1, + Governance = 2, + Staking = 3, + // Skip 4 as it is now removed (was SudoBalances) + // Skip 5 as it was IdentityJudgement + CancelProxy = 6, + Auction = 7, + NominationPools = 8, + ParaRegistration = 9, + } + + /// Remote proxy interface that uses the relay chain as remote location. + pub struct RemoteProxyInterface( + core::marker::PhantomData<(LocalProxyType, ProxyDefinitionConverter)>, + ); + + impl< + LocalProxyType, + ProxyDefinitionConverter: Convert< + ProxyDefinition, + Option>, + >, + > pallet_remote_proxy::RemoteProxyInterface + for RemoteProxyInterface + { + type RemoteAccountId = AccountId; + + type RemoteProxyType = ProxyType; + + type RemoteBlockNumber = BlockNumber; + + type RemoteHash = Hash; + + type RemoteHasher = BlakeTwo256; + + fn block_to_storage_root( + validation_data: &polkadot_primitives::PersistedValidationData, + ) -> Option<(Self::RemoteBlockNumber, ::Out)> { + Some((validation_data.relay_parent_number, validation_data.relay_parent_storage_root)) + } + + fn local_to_remote_account_id(local: &AccountId) -> Option { + Some(local.clone()) + } + + fn remote_to_local_proxy_defintion( + remote: ProxyDefinition< + Self::RemoteAccountId, + Self::RemoteProxyType, + Self::RemoteBlockNumber, + >, + ) -> Option> { + ProxyDefinitionConverter::convert(remote) + } + + #[cfg(feature = "runtime-benchmarks")] + fn create_remote_proxy_proof( + caller: &AccountId, + proxy: &AccountId, + ) -> (pallet_remote_proxy::RemoteProxyProof, BlockNumber, Hash) { + use codec::Encode; + use sp_trie::TrieMut; + + let (mut db, mut root) = sp_trie::MemoryDB::::default_with_root(); + let mut trie = + sp_trie::TrieDBMutBuilder::>::new(&mut db, &mut root).build(); + + let proxy_definition = + alloc::vec![ProxyDefinition:: { + delegate: caller.clone(), + proxy_type: ProxyType::default(), + delay: 0, + }]; + + trie.insert(&Self::proxy_definition_storage_key(proxy), &proxy_definition.encode()) + .unwrap(); + drop(trie); + + ( + pallet_remote_proxy::RemoteProxyProof::RelayChain { + proof: db.drain().into_values().map(|d| d.0).collect(), + block: 1, + }, + 1, + root, + ) + } + } +} + #[cfg(test)] mod tests { use super::{ currency::{CENTS, DOLLARS, MILLICENTS}, fee::WeightToFee, + proxy::ProxyType, }; use crate::weights::ExtrinsicBaseWeight; + use codec::{Decode, Encode}; use frame_support::weights::WeightToFee as WeightToFeeT; use polkadot_runtime_common::MAXIMUM_BLOCK_WEIGHT; @@ -180,4 +297,32 @@ mod tests { let y = CENTS / 10; assert!(x.max(y) - x.min(y) < MILLICENTS); } + + #[derive( + Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, sp_runtime::RuntimeDebug, + )] + pub enum OldProxyType { + Any, + NonTransfer, + Governance, + Staking, + SudoBalances, + IdentityJudgement, + } + + #[test] + fn proxy_type_decodes_correctly() { + for (i, j) in vec![ + (OldProxyType::Any, ProxyType::Any), + (OldProxyType::NonTransfer, ProxyType::NonTransfer), + (OldProxyType::Governance, ProxyType::Governance), + (OldProxyType::Staking, ProxyType::Staking), + ] + .into_iter() + { + assert_eq!(i.encode(), j.encode()); + } + assert!(ProxyType::decode(&mut &OldProxyType::SudoBalances.encode()[..]).is_err()); + assert!(ProxyType::decode(&mut &OldProxyType::IdentityJudgement.encode()[..]).is_err()); + } } diff --git a/relay/polkadot/src/lib.rs b/relay/polkadot/src/lib.rs index 46bff87120..7e2582c3ca 100644 --- a/relay/polkadot/src/lib.rs +++ b/relay/polkadot/src/lib.rs @@ -128,7 +128,7 @@ pub use sp_runtime::BuildStorage; /// Constant values used within the runtime. use polkadot_runtime_constants::{ - currency::*, fee::*, system_parachain, time::*, TREASURY_PALLET_ID, + currency::*, fee::*, proxy::ProxyType, system_parachain, time::*, TREASURY_PALLET_ID, }; // Weights used in the runtime. @@ -984,7 +984,10 @@ parameter_types! { pub const MaxPending: u16 = 32; } -/// The type used to represent the kinds of proxying allowed. +/// Transparent wrapper around the actual [`ProxyType`]. +/// +/// This is done to have [`ProxyType`] declared in a different crate (constants) and being able to +/// implement [`InstanceFilter`] in this crate. #[derive( Copy, Clone, @@ -996,60 +999,21 @@ parameter_types! { Decode, RuntimeDebug, MaxEncodedLen, - scale_info::TypeInfo, + Default, )] -pub enum ProxyType { - Any = 0, - NonTransfer = 1, - Governance = 2, - Staking = 3, - // Skip 4 as it is now removed (was SudoBalances) - // Skip 5 as it was IdentityJudgement - CancelProxy = 6, - Auction = 7, - NominationPools = 8, - ParaRegistration = 9, -} +pub struct TransparentProxyType(T); -#[cfg(test)] -mod proxy_type_tests { - use super::*; +impl scale_info::TypeInfo for TransparentProxyType { + type Identity = T::Identity; - #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, RuntimeDebug)] - pub enum OldProxyType { - Any, - NonTransfer, - Governance, - Staking, - SudoBalances, - IdentityJudgement, - } - - #[test] - fn proxy_type_decodes_correctly() { - for (i, j) in vec![ - (OldProxyType::Any, ProxyType::Any), - (OldProxyType::NonTransfer, ProxyType::NonTransfer), - (OldProxyType::Governance, ProxyType::Governance), - (OldProxyType::Staking, ProxyType::Staking), - ] - .into_iter() - { - assert_eq!(i.encode(), j.encode()); - } - assert!(ProxyType::decode(&mut &OldProxyType::SudoBalances.encode()[..]).is_err()); - assert!(ProxyType::decode(&mut &OldProxyType::IdentityJudgement.encode()[..]).is_err()); + fn type_info() -> scale_info::Type { + T::type_info() } } -impl Default for ProxyType { - fn default() -> Self { - Self::Any - } -} -impl InstanceFilter for ProxyType { +impl InstanceFilter for TransparentProxyType { fn filter(&self, c: &RuntimeCall) -> bool { - match self { + match self.0 { ProxyType::Any => true, ProxyType::NonTransfer => matches!( c, @@ -1134,8 +1098,9 @@ impl InstanceFilter for ProxyType { ), } } + fn is_superset(&self, o: &Self) -> bool { - match (self, o) { + match (self.0, o.0) { (x, y) if x == y => true, (ProxyType::Any, _) => true, (_, ProxyType::Any) => false, @@ -1149,7 +1114,7 @@ impl pallet_proxy::Config for Runtime { type RuntimeEvent = RuntimeEvent; type RuntimeCall = RuntimeCall; type Currency = Balances; - type ProxyType = ProxyType; + type ProxyType = TransparentProxyType; type ProxyDepositBase = ProxyDepositBase; type ProxyDepositFactor = ProxyDepositFactor; type MaxProxies = MaxProxies; diff --git a/system-parachains/asset-hubs/asset-hub-kusama/Cargo.toml b/system-parachains/asset-hubs/asset-hub-kusama/Cargo.toml index 637720d361..8dc09251b7 100644 --- a/system-parachains/asset-hubs/asset-hub-kusama/Cargo.toml +++ b/system-parachains/asset-hubs/asset-hub-kusama/Cargo.toml @@ -21,7 +21,7 @@ bp-asset-hub-polkadot = { workspace = true } bp-bridge-hub-kusama = { workspace = true } bp-bridge-hub-polkadot = { workspace = true } kusama-runtime-constants = { workspace = true } -polkadot-runtime-constants = { workspace = true } +pallet-remote-proxy = { workspace = true } # Substrate frame-benchmarking = { optional = true, workspace = true } @@ -132,6 +132,7 @@ runtime-benchmarks = [ "frame-support/runtime-benchmarks", "frame-system-benchmarking/runtime-benchmarks", "frame-system/runtime-benchmarks", + "kusama-runtime-constants/runtime-benchmarks", "pallet-asset-conversion/runtime-benchmarks", "pallet-assets/runtime-benchmarks", "pallet-balances/runtime-benchmarks", @@ -141,6 +142,7 @@ runtime-benchmarks = [ "pallet-nft-fractionalization/runtime-benchmarks", "pallet-nfts/runtime-benchmarks", "pallet-proxy/runtime-benchmarks", + "pallet-remote-proxy/runtime-benchmarks", "pallet-state-trie-migration/runtime-benchmarks", "pallet-timestamp/runtime-benchmarks", "pallet-uniques/runtime-benchmarks", @@ -154,6 +156,7 @@ runtime-benchmarks = [ "polkadot-runtime-common/runtime-benchmarks", "snowbridge-router-primitives/runtime-benchmarks", "sp-runtime/runtime-benchmarks", + "system-parachains-constants/runtime-benchmarks", "xcm-builder/runtime-benchmarks", "xcm-executor/runtime-benchmarks", "xcm-runtime-apis/runtime-benchmarks", @@ -179,6 +182,7 @@ try-runtime = [ "pallet-nft-fractionalization/try-runtime", "pallet-nfts/try-runtime", "pallet-proxy/try-runtime", + "pallet-remote-proxy/try-runtime", "pallet-session/try-runtime", "pallet-state-trie-migration/try-runtime", "pallet-timestamp/try-runtime", @@ -230,6 +234,7 @@ std = [ "pallet-nfts-runtime-api/std", "pallet-nfts/std", "pallet-proxy/std", + "pallet-remote-proxy/std", "pallet-session/std", "pallet-state-trie-migration/std", "pallet-timestamp/std", @@ -246,7 +251,6 @@ std = [ "polkadot-core-primitives/std", "polkadot-parachain-primitives/std", "polkadot-runtime-common/std", - "polkadot-runtime-constants/std", "primitive-types/std", "scale-info/std", "serde_json/std", diff --git a/system-parachains/asset-hubs/asset-hub-kusama/src/lib.rs b/system-parachains/asset-hubs/asset-hub-kusama/src/lib.rs index 89a9e96e72..7d3bb5724a 100644 --- a/system-parachains/asset-hubs/asset-hub-kusama/src/lib.rs +++ b/system-parachains/asset-hubs/asset-hub-kusama/src/lib.rs @@ -38,12 +38,15 @@ use assets_common::{ }; use cumulus_pallet_parachain_system::RelayNumberMonotonicallyIncreases; use cumulus_primitives_core::{AggregateMessageOrigin, ParaId}; +use kusama_runtime_constants::time::MINUTES as RC_MINUTES; +use pallet_proxy::ProxyDefinition; use sp_api::impl_runtime_apis; use sp_core::{crypto::KeyTypeId, OpaqueMetadata}; use sp_runtime::{ create_runtime_str, generic, impl_opaque_keys, traits::{ - AccountIdConversion, AccountIdLookup, BlakeTwo256, Block as BlockT, ConvertInto, Verify, + AccountIdConversion, AccountIdLookup, BlakeTwo256, Block as BlockT, Convert, ConvertInto, + Verify, }, transaction_validity::{TransactionSource, TransactionValidity}, ApplyExtrinsicResult, Perbill, Permill, @@ -526,9 +529,11 @@ parameter_types! { RuntimeDebug, MaxEncodedLen, scale_info::TypeInfo, + Default, )] pub enum ProxyType { /// Fully permissioned proxy. Can execute any call on behalf of _proxied_. + #[default] Any, /// Can execute any call that does not transfer funds or assets. NonTransfer, @@ -543,11 +548,6 @@ pub enum ProxyType { /// Collator selection proxy. Can execute calls related to collator selection mechanism. Collator, } -impl Default for ProxyType { - fn default() -> Self { - Self::Any - } -} impl InstanceFilter for ProxyType { fn filter(&self, c: &RuntimeCall) -> bool { @@ -690,7 +690,7 @@ parameter_types! { impl cumulus_pallet_parachain_system::Config for Runtime { type RuntimeEvent = RuntimeEvent; - type OnSystemEvent = (); + type OnSystemEvent = RemoteProxyRelayChain; type SelfParaId = parachain_info::Pallet; type DmpQueue = frame_support::traits::EnqueueWithOrigin; type ReservedDmpWeight = ReservedDmpWeight; @@ -973,6 +973,52 @@ impl pallet_xcm_bridge_hub_router::Config for Runti cumulus_pallet_xcmp_queue::bridging::InAndOutXcmpChannelStatusProvider; } +/// Converts from the relay chain proxy type to the local proxy type. +pub struct RelayChainToLocalProxyTypeConverter; + +impl + Convert< + ProxyDefinition, + Option>, + > for RelayChainToLocalProxyTypeConverter +{ + fn convert( + a: ProxyDefinition, + ) -> Option> { + let proxy_type = match a.proxy_type { + kusama_runtime_constants::proxy::ProxyType::Any => ProxyType::Any, + kusama_runtime_constants::proxy::ProxyType::NonTransfer => ProxyType::NonTransfer, + kusama_runtime_constants::proxy::ProxyType::CancelProxy => ProxyType::CancelProxy, + // Proxy types that are not supported on AH. + kusama_runtime_constants::proxy::ProxyType::Governance | + kusama_runtime_constants::proxy::ProxyType::Staking | + kusama_runtime_constants::proxy::ProxyType::Auction | + kusama_runtime_constants::proxy::ProxyType::Spokesperson | + kusama_runtime_constants::proxy::ProxyType::NominationPools | + kusama_runtime_constants::proxy::ProxyType::Society | + kusama_runtime_constants::proxy::ProxyType::ParaRegistration => return None, + }; + + Some(ProxyDefinition { + delegate: a.delegate, + proxy_type, + // Delays are currently not supported by the remote proxy pallet, but should be + // converted in the future to the block time used by the local proxy pallet. + delay: a.delay, + }) + } +} + +impl pallet_remote_proxy::Config for Runtime { + // The time between creating a proof and using the proof in a transaction. + type MaxStorageRootsToKeep = ConstU32<{ RC_MINUTES }>; + type RemoteProxy = kusama_runtime_constants::proxy::RemoteProxyInterface< + ProxyType, + RelayChainToLocalProxyTypeConverter, + >; + type WeightInfo = weights::pallet_remote_proxy::WeightInfo; +} + // Create the runtime by composing the FRAME pallets that were previously configured. construct_runtime!( pub enum Runtime @@ -1009,6 +1055,7 @@ construct_runtime!( Utility: pallet_utility = 40, Multisig: pallet_multisig = 41, Proxy: pallet_proxy = 42, + RemoteProxyRelayChain: pallet_remote_proxy = 43, // The main stage. Assets: pallet_assets:: = 50, @@ -1088,6 +1135,7 @@ mod benches { [pallet_nft_fractionalization, NftFractionalization] [pallet_nfts, Nfts] [pallet_proxy, Proxy] + [pallet_remote_proxy, RemoteProxyRelayChain] [pallet_session, SessionBench::] [pallet_uniques, Uniques] [pallet_utility, Utility] diff --git a/system-parachains/asset-hubs/asset-hub-kusama/src/weights/mod.rs b/system-parachains/asset-hubs/asset-hub-kusama/src/weights/mod.rs index 4a18c22aca..16aa737336 100644 --- a/system-parachains/asset-hubs/asset-hub-kusama/src/weights/mod.rs +++ b/system-parachains/asset-hubs/asset-hub-kusama/src/weights/mod.rs @@ -30,6 +30,7 @@ pub mod pallet_multisig; pub mod pallet_nft_fractionalization; pub mod pallet_nfts; pub mod pallet_proxy; +pub mod pallet_remote_proxy; pub mod pallet_session; pub mod pallet_timestamp; pub mod pallet_uniques; diff --git a/system-parachains/asset-hubs/asset-hub-kusama/src/weights/pallet_remote_proxy.rs b/system-parachains/asset-hubs/asset-hub-kusama/src/weights/pallet_remote_proxy.rs new file mode 100644 index 0000000000..b3d34884c9 --- /dev/null +++ b/system-parachains/asset-hubs/asset-hub-kusama/src/weights/pallet_remote_proxy.rs @@ -0,0 +1,80 @@ +// Copyright (C) Parity Technologies and the various Polkadot contributors, see Contributions.md +// for a list of specific contributors. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Autogenerated weights for `pallet_remote_proxy` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2025-01-31, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `ggwpez-ref-hw`, CPU: `AMD EPYC 7232P 8-Core Processor` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("./asset-hub-kusama-chain-spec.json")`, DB CACHE: 1024 + +// Executed Command: +// ./target/production/polkadot-parachain +// benchmark +// pallet +// --chain=./asset-hub-kusama-chain-spec.json +// --steps=50 +// --repeat=20 +// --pallet=pallet_remote_proxy +// --extrinsic=* +// --wasm-execution=compiled +// --heap-pages=4096 +// --output=./asset-hub-kusama-weights/ +// --header=./file_header.txt + +#![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 for `pallet_remote_proxy`. +pub struct WeightInfo(PhantomData); +impl pallet_remote_proxy::WeightInfo for WeightInfo { + /// Storage: `RemoteProxyRelayChain::BlockToRoot` (r:1 w:0) + /// Proof: `RemoteProxyRelayChain::BlockToRoot` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) + fn remote_proxy() -> Weight { + // Proof Size summary in bytes: + // Measured: `73` + // Estimated: `3509` + // Minimum execution time: 23_880_000 picoseconds. + Weight::from_parts(24_480_000, 0) + .saturating_add(Weight::from_parts(0, 3509)) + .saturating_add(T::DbWeight::get().reads(1)) + } + fn register_remote_proxy_proof() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 4_540_000 picoseconds. + Weight::from_parts(4_680_000, 0) + .saturating_add(Weight::from_parts(0, 0)) + } + /// Storage: `RemoteProxyRelayChain::BlockToRoot` (r:1 w:0) + /// Proof: `RemoteProxyRelayChain::BlockToRoot` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`) + fn remote_proxy_with_registered_proof() -> Weight { + // Proof Size summary in bytes: + // Measured: `73` + // Estimated: `3509` + // Minimum execution time: 23_950_000 picoseconds. + Weight::from_parts(24_310_000, 0) + .saturating_add(Weight::from_parts(0, 3509)) + .saturating_add(T::DbWeight::get().reads(1)) + } +} diff --git a/system-parachains/asset-hubs/asset-hub-kusama/src/xcm_config.rs b/system-parachains/asset-hubs/asset-hub-kusama/src/xcm_config.rs index 998c59b739..5bd9fdf870 100644 --- a/system-parachains/asset-hubs/asset-hub-kusama/src/xcm_config.rs +++ b/system-parachains/asset-hubs/asset-hub-kusama/src/xcm_config.rs @@ -546,7 +546,7 @@ pub mod bridging { 2, [ GlobalConsensus(PolkadotNetwork::get()), - Parachain(polkadot_runtime_constants::system_parachain::ASSET_HUB_ID), + Parachain(kusama_runtime_constants::system_parachain::ASSET_HUB_ID), ], ); diff --git a/system-parachains/asset-hubs/asset-hub-polkadot/Cargo.toml b/system-parachains/asset-hubs/asset-hub-polkadot/Cargo.toml index 1f207ce943..a5407b281b 100644 --- a/system-parachains/asset-hubs/asset-hub-polkadot/Cargo.toml +++ b/system-parachains/asset-hubs/asset-hub-polkadot/Cargo.toml @@ -125,6 +125,7 @@ runtime-benchmarks = [ "frame-system-benchmarking/runtime-benchmarks", "frame-system/runtime-benchmarks", "hex-literal", + "kusama-runtime-constants/runtime-benchmarks", "pallet-asset-conversion/runtime-benchmarks", "pallet-assets/runtime-benchmarks", "pallet-balances/runtime-benchmarks", @@ -143,8 +144,10 @@ runtime-benchmarks = [ "parachains-common/runtime-benchmarks", "polkadot-parachain-primitives/runtime-benchmarks", "polkadot-runtime-common/runtime-benchmarks", + "polkadot-runtime-constants/runtime-benchmarks", "snowbridge-router-primitives/runtime-benchmarks", "sp-runtime/runtime-benchmarks", + "system-parachains-constants/runtime-benchmarks", "xcm-builder/runtime-benchmarks", "xcm-executor/runtime-benchmarks", "xcm-runtime-apis/runtime-benchmarks", diff --git a/system-parachains/bridge-hubs/bridge-hub-kusama/Cargo.toml b/system-parachains/bridge-hubs/bridge-hub-kusama/Cargo.toml index e61468c8de..99449a393d 100644 --- a/system-parachains/bridge-hubs/bridge-hub-kusama/Cargo.toml +++ b/system-parachains/bridge-hubs/bridge-hub-kusama/Cargo.toml @@ -216,6 +216,7 @@ runtime-benchmarks = [ "frame-support/runtime-benchmarks", "frame-system-benchmarking/runtime-benchmarks", "frame-system/runtime-benchmarks", + "kusama-runtime-constants/runtime-benchmarks", "pallet-balances/runtime-benchmarks", "pallet-bridge-grandpa/runtime-benchmarks", "pallet-bridge-messages/runtime-benchmarks", @@ -232,7 +233,9 @@ runtime-benchmarks = [ "parachains-common/runtime-benchmarks", "polkadot-parachain-primitives/runtime-benchmarks", "polkadot-runtime-common/runtime-benchmarks", + "polkadot-runtime-constants/runtime-benchmarks", "sp-runtime/runtime-benchmarks", + "system-parachains-constants/runtime-benchmarks", "xcm-builder/runtime-benchmarks", "xcm-executor/runtime-benchmarks", "xcm-runtime-apis/runtime-benchmarks", diff --git a/system-parachains/bridge-hubs/bridge-hub-polkadot/Cargo.toml b/system-parachains/bridge-hubs/bridge-hub-polkadot/Cargo.toml index 75598dc389..babee6733f 100644 --- a/system-parachains/bridge-hubs/bridge-hub-polkadot/Cargo.toml +++ b/system-parachains/bridge-hubs/bridge-hub-polkadot/Cargo.toml @@ -240,6 +240,7 @@ runtime-benchmarks = [ "frame-support/runtime-benchmarks", "frame-system-benchmarking/runtime-benchmarks", "frame-system/runtime-benchmarks", + "kusama-runtime-constants/runtime-benchmarks", "pallet-balances/runtime-benchmarks", "pallet-bridge-grandpa/runtime-benchmarks", "pallet-bridge-messages/runtime-benchmarks", @@ -256,6 +257,7 @@ runtime-benchmarks = [ "parachains-common/runtime-benchmarks", "polkadot-parachain-primitives/runtime-benchmarks", "polkadot-runtime-common/runtime-benchmarks", + "polkadot-runtime-constants/runtime-benchmarks", "snowbridge-core/runtime-benchmarks", "snowbridge-pallet-ethereum-client-fixtures/runtime-benchmarks", "snowbridge-pallet-ethereum-client/runtime-benchmarks", @@ -266,6 +268,7 @@ runtime-benchmarks = [ "snowbridge-runtime-common/runtime-benchmarks", "snowbridge-runtime-test-common/runtime-benchmarks", "sp-runtime/runtime-benchmarks", + "system-parachains-constants/runtime-benchmarks", "xcm-builder/runtime-benchmarks", "xcm-executor/runtime-benchmarks", "xcm-runtime-apis/runtime-benchmarks", diff --git a/system-parachains/collectives/collectives-polkadot/Cargo.toml b/system-parachains/collectives/collectives-polkadot/Cargo.toml index 87aeeac0f1..9ecc69471e 100644 --- a/system-parachains/collectives/collectives-polkadot/Cargo.toml +++ b/system-parachains/collectives/collectives-polkadot/Cargo.toml @@ -128,7 +128,9 @@ runtime-benchmarks = [ "parachains-common/runtime-benchmarks", "polkadot-parachain-primitives/runtime-benchmarks", "polkadot-runtime-common/runtime-benchmarks", + "polkadot-runtime-constants/runtime-benchmarks", "sp-runtime/runtime-benchmarks", + "system-parachains-constants/runtime-benchmarks", "xcm-builder/runtime-benchmarks", "xcm-executor/runtime-benchmarks", "xcm-runtime-apis/runtime-benchmarks", diff --git a/system-parachains/constants/Cargo.toml b/system-parachains/constants/Cargo.toml index 17582f7785..050a1a9553 100644 --- a/system-parachains/constants/Cargo.toml +++ b/system-parachains/constants/Cargo.toml @@ -35,3 +35,11 @@ std = [ "sp-std/std", "xcm/std", ] +runtime-benchmarks = [ + "frame-support/runtime-benchmarks", + "kusama-runtime-constants/runtime-benchmarks", + "parachains-common/runtime-benchmarks", + "polkadot-primitives/runtime-benchmarks", + "polkadot-runtime-constants/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", +] diff --git a/system-parachains/coretime/coretime-kusama/Cargo.toml b/system-parachains/coretime/coretime-kusama/Cargo.toml index 618dbf9276..80198bc89e 100644 --- a/system-parachains/coretime/coretime-kusama/Cargo.toml +++ b/system-parachains/coretime/coretime-kusama/Cargo.toml @@ -161,6 +161,7 @@ runtime-benchmarks = [ "frame-support/runtime-benchmarks", "frame-system-benchmarking/runtime-benchmarks", "frame-system/runtime-benchmarks", + "kusama-runtime-constants/runtime-benchmarks", "pallet-balances/runtime-benchmarks", "pallet-broker/runtime-benchmarks", "pallet-collator-selection/runtime-benchmarks", @@ -175,6 +176,7 @@ runtime-benchmarks = [ "polkadot-parachain-primitives/runtime-benchmarks", "polkadot-runtime-common/runtime-benchmarks", "sp-runtime/runtime-benchmarks", + "system-parachains-constants/runtime-benchmarks", "xcm-builder/runtime-benchmarks", "xcm-executor/runtime-benchmarks", "xcm-runtime-apis/runtime-benchmarks", diff --git a/system-parachains/coretime/coretime-polkadot/Cargo.toml b/system-parachains/coretime/coretime-polkadot/Cargo.toml index 91ef384c04..ed605b911e 100644 --- a/system-parachains/coretime/coretime-polkadot/Cargo.toml +++ b/system-parachains/coretime/coretime-polkadot/Cargo.toml @@ -176,7 +176,9 @@ runtime-benchmarks = [ "parachains-common/runtime-benchmarks", "polkadot-parachain-primitives/runtime-benchmarks", "polkadot-runtime-common/runtime-benchmarks", + "polkadot-runtime-constants/runtime-benchmarks", "sp-runtime/runtime-benchmarks", + "system-parachains-constants/runtime-benchmarks", "xcm-builder/runtime-benchmarks", "xcm-executor/runtime-benchmarks", "xcm-runtime-apis/runtime-benchmarks", diff --git a/system-parachains/encointer/Cargo.toml b/system-parachains/encointer/Cargo.toml index a6888efe7a..7ff94568cc 100644 --- a/system-parachains/encointer/Cargo.toml +++ b/system-parachains/encointer/Cargo.toml @@ -127,6 +127,7 @@ runtime-benchmarks = [ "frame-support/runtime-benchmarks", "frame-system-benchmarking/runtime-benchmarks", "frame-system/runtime-benchmarks", + "kusama-runtime-constants/runtime-benchmarks", "pallet-asset-tx-payment/runtime-benchmarks", "pallet-balances/runtime-benchmarks", "pallet-collator-selection/runtime-benchmarks", @@ -153,6 +154,7 @@ runtime-benchmarks = [ "polkadot-primitives/runtime-benchmarks", "polkadot-runtime-common/runtime-benchmarks", "sp-runtime/runtime-benchmarks", + "system-parachains-constants/runtime-benchmarks", "xcm-builder/runtime-benchmarks", "xcm-executor/runtime-benchmarks", "xcm-runtime-apis/runtime-benchmarks", diff --git a/system-parachains/gluttons/glutton-kusama/Cargo.toml b/system-parachains/gluttons/glutton-kusama/Cargo.toml index d3aa109541..4a33ce1444 100644 --- a/system-parachains/gluttons/glutton-kusama/Cargo.toml +++ b/system-parachains/gluttons/glutton-kusama/Cargo.toml @@ -67,6 +67,7 @@ runtime-benchmarks = [ "pallet-sudo?/runtime-benchmarks", "parachains-common/runtime-benchmarks", "sp-runtime/runtime-benchmarks", + "system-parachains-constants/runtime-benchmarks", "xcm-builder/runtime-benchmarks", "xcm-executor/runtime-benchmarks", ] diff --git a/system-parachains/people/people-kusama/Cargo.toml b/system-parachains/people/people-kusama/Cargo.toml index 01005c4ad0..891dd6297c 100644 --- a/system-parachains/people/people-kusama/Cargo.toml +++ b/system-parachains/people/people-kusama/Cargo.toml @@ -161,6 +161,7 @@ runtime-benchmarks = [ "frame-support/runtime-benchmarks", "frame-system-benchmarking/runtime-benchmarks", "frame-system/runtime-benchmarks", + "kusama-runtime-constants/runtime-benchmarks", "pallet-balances/runtime-benchmarks", "pallet-collator-selection/runtime-benchmarks", "pallet-identity/runtime-benchmarks", @@ -176,6 +177,7 @@ runtime-benchmarks = [ "polkadot-primitives/runtime-benchmarks", "polkadot-runtime-common/runtime-benchmarks", "sp-runtime/runtime-benchmarks", + "system-parachains-constants/runtime-benchmarks", "xcm-builder/runtime-benchmarks", "xcm-executor/runtime-benchmarks", "xcm-runtime-apis/runtime-benchmarks", diff --git a/system-parachains/people/people-polkadot/Cargo.toml b/system-parachains/people/people-polkadot/Cargo.toml index 6826c46ac1..362ce2a1e1 100644 --- a/system-parachains/people/people-polkadot/Cargo.toml +++ b/system-parachains/people/people-polkadot/Cargo.toml @@ -172,7 +172,9 @@ runtime-benchmarks = [ "parachains-common/runtime-benchmarks", "polkadot-parachain-primitives/runtime-benchmarks", "polkadot-runtime-common/runtime-benchmarks", + "polkadot-runtime-constants/runtime-benchmarks", "sp-runtime/runtime-benchmarks", + "system-parachains-constants/runtime-benchmarks", "xcm-builder/runtime-benchmarks", "xcm-executor/runtime-benchmarks", "xcm-runtime-apis/runtime-benchmarks",