diff --git a/Cargo.lock b/Cargo.lock index aaa4746d4ca42..3b8ec81c02db8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4871,6 +4871,7 @@ dependencies = [ "pallet-contracts-proc-macro", "pallet-randomness-collective-flip", "pallet-timestamp", + "pallet-utility", "parity-scale-codec", "paste 1.0.4", "pretty_assertions 0.7.2", diff --git a/bin/node-template/pallets/template/src/mock.rs b/bin/node-template/pallets/template/src/mock.rs index 8719bcb4df2d5..9bea61df22edb 100644 --- a/bin/node-template/pallets/template/src/mock.rs +++ b/bin/node-template/pallets/template/src/mock.rs @@ -27,7 +27,7 @@ parameter_types! { } impl system::Config for Test { - type BaseCallFilter = (); + type BaseCallFilter = frame_support::traits::AllowAll; type BlockWeights = (); type BlockLength = (); type DbWeight = (); diff --git a/bin/node-template/runtime/src/lib.rs b/bin/node-template/runtime/src/lib.rs index 0d336622404ca..c92eb8a1aadf8 100644 --- a/bin/node-template/runtime/src/lib.rs +++ b/bin/node-template/runtime/src/lib.rs @@ -146,7 +146,7 @@ parameter_types! { impl frame_system::Config for Runtime { /// The basic call filter to use in dispatchable. - type BaseCallFilter = (); + type BaseCallFilter = frame_support::traits::AllowAll; /// Block & extrinsics weights: base values and limits. type BlockWeights = BlockWeights; /// The maximum length of a block (in bytes). diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 6a25a278f2c78..4c8f1a8298704 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -33,7 +33,7 @@ use frame_support::{ }, traits::{ Currency, Imbalance, KeyOwnerProofSystem, OnUnbalanced, LockIdentifier, - U128CurrencyToVote, + U128CurrencyToVote, AllowAll, DenyAll, }, }; use frame_system::{ @@ -193,7 +193,7 @@ parameter_types! { const_assert!(NORMAL_DISPATCH_RATIO.deconstruct() >= AVERAGE_ON_INITIALIZE_RATIO.deconstruct()); impl frame_system::Config for Runtime { - type BaseCallFilter = (); + type BaseCallFilter = AllowAll; type BlockWeights = RuntimeBlockWeights; type BlockLength = RuntimeBlockLength; type DbWeight = RocksDbWeight; @@ -839,6 +839,14 @@ impl pallet_contracts::Config for Runtime { type Randomness = RandomnessCollectiveFlip; type Currency = Balances; type Event = Event; + type Call = Call; + /// The safest default is to allow no calls at all. + /// + /// Runtimes should whitelist dispatchables that are allowed to be called from contracts + /// and make sure they are stable. Dispatchables exposed to contracts are not allowed to + /// change because that would break already deployed contracts. The `Call` structure itself + /// is not allowed to change the indices of existing pallets, too. + type CallFilter = DenyAll; type RentPayment = (); type SignedClaimHandicap = SignedClaimHandicap; type TombstoneDeposit = TombstoneDeposit; diff --git a/docs/Upgrading-2.0-to-3.0.md b/docs/Upgrading-2.0-to-3.0.md index 46f01ab7824cd..914b7b788d2ea 100644 --- a/docs/Upgrading-2.0-to-3.0.md +++ b/docs/Upgrading-2.0-to-3.0.md @@ -143,7 +143,7 @@ And update the overall definition for weights on frame and a few related types a +const_assert!(NORMAL_DISPATCH_RATIO.deconstruct() >= AVERAGE_ON_INITIALIZE_RATIO.deconstruct()); + +impl frame_system::Config for Runtime { - type BaseCallFilter = (); + type BaseCallFilter = frame_support::traits::AllowAll; + type BlockWeights = RuntimeBlockWeights; + type BlockLength = RuntimeBlockLength; + type DbWeight = RocksDbWeight; diff --git a/frame/assets/src/mock.rs b/frame/assets/src/mock.rs index cf99eed703cdf..429548a5d1c28 100644 --- a/frame/assets/src/mock.rs +++ b/frame/assets/src/mock.rs @@ -43,7 +43,7 @@ parameter_types! { pub const BlockHashCount: u64 = 250; } impl frame_system::Config for Test { - type BaseCallFilter = (); + type BaseCallFilter = frame_support::traits::AllowAll; type BlockWeights = (); type BlockLength = (); type Origin = Origin; diff --git a/frame/atomic-swap/src/tests.rs b/frame/atomic-swap/src/tests.rs index f41874a1eec48..11e74be9b4e7f 100644 --- a/frame/atomic-swap/src/tests.rs +++ b/frame/atomic-swap/src/tests.rs @@ -31,7 +31,7 @@ parameter_types! { frame_system::limits::BlockWeights::simple_max(1024); } impl frame_system::Config for Test { - type BaseCallFilter = (); + type BaseCallFilter = frame_support::traits::AllowAll; type BlockWeights = (); type BlockLength = (); type DbWeight = (); diff --git a/frame/aura/src/mock.rs b/frame/aura/src/mock.rs index 443ac9890ac79..aff6b76a7a49f 100644 --- a/frame/aura/src/mock.rs +++ b/frame/aura/src/mock.rs @@ -48,7 +48,7 @@ parameter_types! { } impl frame_system::Config for Test { - type BaseCallFilter = (); + type BaseCallFilter = frame_support::traits::AllowAll; type BlockWeights = (); type BlockLength = (); type DbWeight = (); diff --git a/frame/authority-discovery/src/lib.rs b/frame/authority-discovery/src/lib.rs index 7edbd8c9a8bd9..1f480926209ec 100644 --- a/frame/authority-discovery/src/lib.rs +++ b/frame/authority-discovery/src/lib.rs @@ -50,7 +50,7 @@ pub mod pallet { Vec, ValueQuery, >; - + #[pallet::storage] #[pallet::getter(fn next_keys)] /// Keys of the next authority set. @@ -212,7 +212,7 @@ mod tests { } impl frame_system::Config for Test { - type BaseCallFilter = (); + type BaseCallFilter = frame_support::traits::AllowAll; type BlockWeights = (); type BlockLength = (); type DbWeight = (); diff --git a/frame/authorship/src/lib.rs b/frame/authorship/src/lib.rs index d40fb93b901a0..de60d1a4caacd 100644 --- a/frame/authorship/src/lib.rs +++ b/frame/authorship/src/lib.rs @@ -440,7 +440,7 @@ mod tests { } impl frame_system::Config for Test { - type BaseCallFilter = (); + type BaseCallFilter = frame_support::traits::AllowAll; type BlockWeights = (); type BlockLength = (); type DbWeight = (); diff --git a/frame/babe/src/mock.rs b/frame/babe/src/mock.rs index 6c1cc89cf1ed0..ea54e9f7cea83 100644 --- a/frame/babe/src/mock.rs +++ b/frame/babe/src/mock.rs @@ -70,7 +70,7 @@ parameter_types! { } impl frame_system::Config for Test { - type BaseCallFilter = (); + type BaseCallFilter = frame_support::traits::AllowAll; type BlockWeights = (); type BlockLength = (); type DbWeight = (); diff --git a/frame/balances/src/tests_composite.rs b/frame/balances/src/tests_composite.rs index 07ec0f377ecfc..1d90b3e70b924 100644 --- a/frame/balances/src/tests_composite.rs +++ b/frame/balances/src/tests_composite.rs @@ -54,7 +54,7 @@ parameter_types! { pub static ExistentialDeposit: u64 = 0; } impl frame_system::Config for Test { - type BaseCallFilter = (); + type BaseCallFilter = frame_support::traits::AllowAll; type BlockWeights = BlockWeights; type BlockLength = (); type DbWeight = (); diff --git a/frame/balances/src/tests_local.rs b/frame/balances/src/tests_local.rs index a6a1a09d9cbfe..36351252b445c 100644 --- a/frame/balances/src/tests_local.rs +++ b/frame/balances/src/tests_local.rs @@ -56,7 +56,7 @@ parameter_types! { pub static ExistentialDeposit: u64 = 0; } impl frame_system::Config for Test { - type BaseCallFilter = (); + type BaseCallFilter = frame_support::traits::AllowAll; type BlockWeights = BlockWeights; type BlockLength = (); type DbWeight = (); diff --git a/frame/balances/src/tests_reentrancy.rs b/frame/balances/src/tests_reentrancy.rs index caca7d78d0ff5..2a3a60dfde842 100644 --- a/frame/balances/src/tests_reentrancy.rs +++ b/frame/balances/src/tests_reentrancy.rs @@ -64,7 +64,7 @@ parameter_types! { pub static ExistentialDeposit: u64 = 0; } impl frame_system::Config for Test { - type BaseCallFilter = (); + type BaseCallFilter = frame_support::traits::AllowAll; type BlockWeights = BlockWeights; type BlockLength = (); type DbWeight = (); diff --git a/frame/benchmarking/src/tests.rs b/frame/benchmarking/src/tests.rs index 0869ae68c7e09..646609c7c1e16 100644 --- a/frame/benchmarking/src/tests.rs +++ b/frame/benchmarking/src/tests.rs @@ -82,7 +82,7 @@ frame_support::construct_runtime!( ); impl frame_system::Config for Test { - type BaseCallFilter = (); + type BaseCallFilter = frame_support::traits::AllowAll; type BlockWeights = (); type BlockLength = (); type DbWeight = (); diff --git a/frame/bounties/src/tests.rs b/frame/bounties/src/tests.rs index 3a53ffd56ac1a..54973bf9b2fda 100644 --- a/frame/bounties/src/tests.rs +++ b/frame/bounties/src/tests.rs @@ -59,7 +59,7 @@ parameter_types! { } impl frame_system::Config for Test { - type BaseCallFilter = (); + type BaseCallFilter = frame_support::traits::AllowAll; type BlockWeights = (); type BlockLength = (); type DbWeight = (); diff --git a/frame/collective/src/lib.rs b/frame/collective/src/lib.rs index a7039887db606..c14ef9df64fee 100644 --- a/frame/collective/src/lib.rs +++ b/frame/collective/src/lib.rs @@ -986,7 +986,7 @@ mod tests { frame_system::limits::BlockWeights::simple_max(1024); } impl frame_system::Config for Test { - type BaseCallFilter = (); + type BaseCallFilter = frame_support::traits::AllowAll; type BlockWeights = (); type BlockLength = (); type DbWeight = (); diff --git a/frame/contracts/CHANGELOG.md b/frame/contracts/CHANGELOG.md index 03945d7b2e346..494c041d1bc84 100644 --- a/frame/contracts/CHANGELOG.md +++ b/frame/contracts/CHANGELOG.md @@ -20,6 +20,9 @@ In other words: Upgrading this pallet will not break pre-existing contracts. ### Added +- Allow contracts to dispatch calls into the runtime (**unstable**) +[#9276](https://github.com/paritytech/substrate/pull/9276) + - New **unstable** version of `seal_call` that offers more features. [#8909](https://github.com/paritytech/substrate/pull/8909) diff --git a/frame/contracts/Cargo.toml b/frame/contracts/Cargo.toml index a0d7da0c5d0c4..9424698091b20 100644 --- a/frame/contracts/Cargo.toml +++ b/frame/contracts/Cargo.toml @@ -48,6 +48,7 @@ wat = "1" pallet-balances = { version = "4.0.0-dev", path = "../balances" } pallet-timestamp = { version = "4.0.0-dev", path = "../timestamp" } pallet-randomness-collective-flip = { version = "4.0.0-dev", path = "../randomness-collective-flip" } +pallet-utility = { version = "4.0.0-dev", path = "../utility" } [features] default = ["std"] diff --git a/frame/contracts/common/src/lib.rs b/frame/contracts/common/src/lib.rs index 04c541a59a39f..098ffd64b8e8e 100644 --- a/frame/contracts/common/src/lib.rs +++ b/frame/contracts/common/src/lib.rs @@ -37,6 +37,15 @@ use serde::{Serialize, Deserialize}; pub struct ContractResult { /// How much gas was consumed during execution. pub gas_consumed: u64, + /// How much gas is required as gas limit in order to execute this call. + /// + /// This value should be used to determine the gas limit for on-chain execution. + /// + /// # Note + /// + /// This can only different from [`Self::gas_consumed`] when weight pre charging + /// is used. Currently, only `seal_call_runtime` makes use of pre charging. + pub gas_required: u64, /// An optional debug message. This message is only filled when explicitly requested /// by the code that calls into the contract. Otherwise it is empty. /// diff --git a/frame/contracts/fixtures/call_runtime.wat b/frame/contracts/fixtures/call_runtime.wat new file mode 100644 index 0000000000000..d5467f6e95e3e --- /dev/null +++ b/frame/contracts/fixtures/call_runtime.wat @@ -0,0 +1,33 @@ +;; This passes its input to `seal_call_runtime` and returns the return value to its caller. +(module + (import "__unstable__" "seal_call_runtime" (func $seal_call_runtime (param i32 i32) (result i32))) + (import "seal0" "seal_input" (func $seal_input (param i32 i32))) + (import "seal0" "seal_return" (func $seal_return (param i32 i32 i32))) + (import "env" "memory" (memory 1 1)) + + ;; 0x1000 = 4k in little endian + ;; size of input buffer + (data (i32.const 0) "\00\10") + + (func (export "call") + ;; Receive the encoded call + (call $seal_input + (i32.const 4) ;; Pointer to the input buffer + (i32.const 0) ;; Size of the length buffer + ) + ;; Just use the call passed as input and store result to memory + (i32.store (i32.const 0) + (call $seal_call_runtime + (i32.const 4) ;; Pointer where the call is stored + (i32.load (i32.const 0)) ;; Size of the call + ) + ) + (call $seal_return + (i32.const 0) ;; flags + (i32.const 0) ;; returned value + (i32.const 4) ;; length of returned value + ) + ) + + (func (export "deploy")) +) diff --git a/frame/contracts/fixtures/call_with_limit.wat b/frame/contracts/fixtures/call_with_limit.wat new file mode 100644 index 0000000000000..abb8708267271 --- /dev/null +++ b/frame/contracts/fixtures/call_with_limit.wat @@ -0,0 +1,37 @@ +;; This expects [account_id, gas_limit] as input and calls the account_id with the supplied gas_limit. +;; It returns the result of the call as output data. +(module + (import "seal0" "seal_input" (func $seal_input (param i32 i32))) + (import "seal0" "seal_call" (func $seal_call (param i32 i32 i64 i32 i32 i32 i32 i32 i32) (result i32))) + (import "seal0" "seal_return" (func $seal_return (param i32 i32 i32))) + (import "env" "memory" (memory 1 1)) + + ;; 0x1000 = 4k in little endian + ;; size of input buffer + (data (i32.const 0) "\00\10") + + (func (export "deploy")) + + (func (export "call") + ;; Receive the encoded call + gas_limit + (call $seal_input + (i32.const 4) ;; Pointer to the input buffer + (i32.const 0) ;; Size of the length buffer + ) + (i32.store + (i32.const 0) + (call $seal_call + (i32.const 4) ;; Pointer to "callee" address. + (i32.const 32) ;; Length of "callee" address. + (i64.load (i32.const 36)) ;; How much gas to devote for the execution. + (i32.const 0) ;; Pointer to the buffer with value to transfer + (i32.const 0) ;; Length of the buffer with value to transfer. + (i32.const 0) ;; Pointer to input data buffer address + (i32.const 0) ;; Length of input data buffer + (i32.const 0xffffffff) ;; u32 max sentinel value: do not copy output + (i32.const 0) ;; Ptr to output buffer len + ) + ) + (call $seal_return (i32.const 0) (i32.const 0) (i32.const 4)) + ) +) diff --git a/frame/contracts/rpc/src/lib.rs b/frame/contracts/rpc/src/lib.rs index 1250d3cb285e7..3b95e98501656 100644 --- a/frame/contracts/rpc/src/lib.rs +++ b/frame/contracts/rpc/src/lib.rs @@ -385,7 +385,8 @@ mod tests { } test(r#"{ "gasConsumed": 5000, - "debugMessage": "0x68656c704f6b", + "gasRequired": 8000, + "debugMessage": "HelloWorld", "result": { "Ok": { "flags": 5, @@ -395,7 +396,8 @@ mod tests { }"#); test(r#"{ "gasConsumed": 3400, - "debugMessage": "0x68656c70457272", + "gasRequired": 5200, + "debugMessage": "HelloWorld", "result": { "Err": "BadOrigin" } @@ -411,7 +413,8 @@ mod tests { } test(r#"{ "gasConsumed": 5000, - "debugMessage": "0x68656c704f6b", + "gasRequired": 8000, + "debugMessage": "HelloWorld", "result": { "Ok": { "result": { @@ -425,7 +428,8 @@ mod tests { }"#); test(r#"{ "gasConsumed": 3400, - "debugMessage": "0x68656c70457272", + "gasRequired": 5200, + "debugMessage": "HelloWorld", "result": { "Err": "BadOrigin" } diff --git a/frame/contracts/src/chain_extension.rs b/frame/contracts/src/chain_extension.rs index e72ab8cf056b0..bb352c3a93d6f 100644 --- a/frame/contracts/src/chain_extension.rs +++ b/frame/contracts/src/chain_extension.rs @@ -57,6 +57,7 @@ use crate::{ Error, wasm::{Runtime, RuntimeCosts}, + gas::ChargedAmount, }; use codec::{Decode, MaxEncodedLen}; use frame_support::weights::Weight; @@ -167,11 +168,22 @@ where /// `weight`. It returns `Err` otherwise. In this case the chain extension should /// abort the execution and pass through the error. /// + /// The returned value can be used to with [`Self::adjust_weight`]. Other than that + /// it has no purpose. + /// /// # Note /// /// Weight is synonymous with gas in substrate. - pub fn charge_weight(&mut self, amount: Weight) -> Result<()> { - self.inner.runtime.charge_gas(RuntimeCosts::ChainExtension(amount)).map(|_| ()) + pub fn charge_weight(&mut self, amount: Weight) -> Result { + self.inner.runtime.charge_gas(RuntimeCosts::ChainExtension(amount)) + } + + /// Adjust a previously charged amount down to its actual amount. + /// + /// This is when a maximum a priori amount was charged and then should be partially + /// refunded to match the actual amount. + pub fn adjust_weight(&mut self, charged: ChargedAmount, actual_weight: Weight) { + self.inner.runtime.adjust_gas(charged, RuntimeCosts::ChainExtension(actual_weight)) } /// Grants access to the execution environment of the current contract call. diff --git a/frame/contracts/src/exec.rs b/frame/contracts/src/exec.rs index 2b595ea6ce8d4..ae1585afbb890 100644 --- a/frame/contracts/src/exec.rs +++ b/frame/contracts/src/exec.rs @@ -28,12 +28,13 @@ use sp_std::{ }; use sp_runtime::{Perbill, traits::{Convert, Saturating}}; use frame_support::{ - dispatch::{DispatchResult, DispatchError}, + dispatch::{DispatchResult, DispatchError, DispatchResultWithPostInfo, Dispatchable}, storage::{with_transaction, TransactionOutcome}, - traits::{ExistenceRequirement, Currency, Time, Randomness, Get}, + traits::{ExistenceRequirement, Currency, Time, Randomness, Get, OriginTrait, Filter}, weights::Weight, ensure, DefaultNoBound, }; +use frame_system::RawOrigin; use pallet_contracts_primitives::{ExecReturnValue}; use smallvec::{SmallVec, Array}; @@ -300,6 +301,9 @@ pub trait Ext: sealing::Sealed { /// /// Returns `true` if debug message recording is enabled. Otherwise `false` is returned. fn append_debug_buffer(&mut self, msg: &str) -> bool; + + /// Call some dispatchable and return the result. + fn call_runtime(&self, call: ::Call) -> DispatchResultWithPostInfo; } /// Describes the different functions that can be exported by an [`Executable`]. @@ -1291,6 +1295,12 @@ where false } } + + fn call_runtime(&self, call: ::Call) -> DispatchResultWithPostInfo { + let mut origin: T::Origin = RawOrigin::Signed(self.address().clone()).into(); + origin.add_filter(T::CallFilter::filter); + call.dispatch(origin) + } } fn deposit_event( @@ -1326,10 +1336,10 @@ mod sealing { mod tests { use super::*; use crate::{ - gas::GasMeter, tests::{ExtBuilder, Test, Event as MetaEvent}, + gas::GasMeter, storage::Storage, tests::{ - ALICE, BOB, CHARLIE, + ALICE, BOB, CHARLIE, Call, TestFilter, ExtBuilder, Test, Event as MetaEvent, test_utils::{place_contract, set_balance, get_balance}, }, exec::ExportedFunction::*, @@ -1337,12 +1347,15 @@ mod tests { }; use codec::{Encode, Decode}; use sp_core::Bytes; - use sp_runtime::DispatchError; + use sp_runtime::{DispatchError, traits::{BadOrigin, Hash}}; use assert_matches::assert_matches; use std::{cell::RefCell, collections::HashMap, rc::Rc}; use pretty_assertions::{assert_eq, assert_ne}; use pallet_contracts_primitives::ReturnFlags; use frame_support::{assert_ok, assert_err}; + use frame_system::{EventRecord, Phase}; + + type System = frame_system::Pallet; type MockStack<'a> = Stack<'a, Test, MockExecutable>; @@ -1353,7 +1366,7 @@ mod tests { } fn events() -> Vec> { - >::events() + System::events() .into_iter() .filter_map(|meta| match meta.event { MetaEvent::Contracts(contract_event) => Some(contract_event), @@ -2503,4 +2516,114 @@ mod tests { ); }); } + + #[test] + fn call_runtime_works() { + let code_hash = MockLoader::insert(Call, |ctx, _| { + let call = Call::System(frame_system::Call::remark_with_event(b"Hello World".to_vec())); + ctx.ext.call_runtime(call).unwrap(); + exec_success() + }); + + ExtBuilder::default().build().execute_with(|| { + let subsistence = Contracts::::subsistence_threshold(); + let schedule = ::Schedule::get(); + let mut gas_meter = GasMeter::::new(GAS_LIMIT); + set_balance(&ALICE, subsistence * 10); + place_contract(&BOB, code_hash); + System::reset_events(); + MockStack::run_call( + ALICE, + BOB, + &mut gas_meter, + &schedule, + 0, + vec![], + None, + ).unwrap(); + + let remark_hash = ::Hashing::hash(b"Hello World"); + assert_eq!(System::events(), vec![ + EventRecord { + phase: Phase::Initialization, + event: MetaEvent::System(frame_system::Event::Remarked(BOB, remark_hash)), + topics: vec![], + }, + ]); + }); + } + + #[test] + fn call_runtime_filter() { + let code_hash = MockLoader::insert(Call, |ctx, _| { + use frame_system::Call as SysCall; + use pallet_balances::Call as BalanceCall; + use pallet_utility::Call as UtilCall; + + // remark should still be allowed + let allowed_call = Call::System(SysCall::remark_with_event(b"Hello".to_vec())); + + // transfers are disallowed by the `TestFiler` (see below) + let forbidden_call = Call::Balances(BalanceCall::transfer(CHARLIE, 22)); + + // simple cases: direct call + assert_err!( + ctx.ext.call_runtime(forbidden_call.clone()), + BadOrigin, + ); + + // as part of a patch: return is OK (but it interrupted the batch) + assert_ok!( + ctx.ext.call_runtime(Call::Utility(UtilCall::batch(vec![ + allowed_call.clone(), forbidden_call, allowed_call + ]))), + ); + + // the transfer wasn't performed + assert_eq!(get_balance(&CHARLIE), 0); + + exec_success() + }); + + TestFilter::set_filter(|call| { + match call { + Call::Balances(pallet_balances::Call::transfer(_, _)) => false, + _ => true, + } + }); + + ExtBuilder::default().build().execute_with(|| { + let subsistence = Contracts::::subsistence_threshold(); + let schedule = ::Schedule::get(); + let mut gas_meter = GasMeter::::new(GAS_LIMIT); + set_balance(&ALICE, subsistence * 10); + place_contract(&BOB, code_hash); + System::reset_events(); + MockStack::run_call( + ALICE, + BOB, + &mut gas_meter, + &schedule, + 0, + vec![], + None, + ).unwrap(); + + let remark_hash = ::Hashing::hash(b"Hello"); + assert_eq!(System::events(), vec![ + EventRecord { + phase: Phase::Initialization, + event: MetaEvent::System(frame_system::Event::Remarked(BOB, remark_hash)), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: MetaEvent::Utility( + pallet_utility::Event::BatchInterrupted(1, BadOrigin.into()), + ), + topics: vec![], + }, + ]); + }); + } } diff --git a/frame/contracts/src/gas.rs b/frame/contracts/src/gas.rs index 34ddb3ceb0434..64f410c4cef2b 100644 --- a/frame/contracts/src/gas.rs +++ b/frame/contracts/src/gas.rs @@ -79,6 +79,8 @@ pub struct GasMeter { gas_limit: Weight, /// Amount of gas left from initial gas limit. Can reach zero. gas_left: Weight, + /// Due to `adjust_gas` and `nested` the `gas_left` can temporarily dip below its final value. + gas_left_lowest: Weight, _phantom: PhantomData, #[cfg(test)] tokens: Vec, @@ -92,6 +94,7 @@ where GasMeter { gas_limit, gas_left: gas_limit, + gas_left_lowest: gas_limit, _phantom: PhantomData, #[cfg(test)] tokens: Vec::new(), @@ -122,6 +125,19 @@ where /// Absorb the remaining gas of a nested meter after we are done using it. pub fn absorb_nested(&mut self, nested: Self) { + if self.gas_left == 0 { + // All of the remaining gas was inherited by the nested gas meter. When absorbing + // we can therefore safely inherit the lowest gas that the nested gas meter experienced + // as long as it is lower than the lowest gas that was experienced by the parent. + // We cannot call `self.gas_left_lowest()` here because in the state that this + // code is run the parent gas meter has `0` gas left. + self.gas_left_lowest = nested.gas_left_lowest().min(self.gas_left_lowest); + } else { + // The nested gas meter was created with a fixed amount that did not consume all of the + // parents (self) gas. The lowest gas that self will experience is when the nested + // gas was pre charged with the fixed amount. + self.gas_left_lowest = self.gas_left_lowest(); + } self.gas_left += nested.gas_left; } @@ -163,12 +179,21 @@ where /// This is when a maximum a priori amount was charged and then should be partially /// refunded to match the actual amount. pub fn adjust_gas>(&mut self, charged_amount: ChargedAmount, token: Tok) { + self.gas_left_lowest = self.gas_left_lowest(); let adjustment = charged_amount.0.saturating_sub(token.weight()); self.gas_left = self.gas_left.saturating_add(adjustment).min(self.gas_limit); } - /// Returns how much gas was used. - pub fn gas_spent(&self) -> Weight { + /// Returns the amount of gas that is required to run the same call. + /// + /// This can be different from `gas_spent` because due to `adjust_gas` the amount of + /// spent gas can temporarily drop and be refunded later. + pub fn gas_required(&self) -> Weight { + self.gas_limit - self.gas_left_lowest() + } + + /// Returns how much gas was spent + pub fn gas_consumed(&self) -> Weight { self.gas_limit - self.gas_left } @@ -179,14 +204,15 @@ where /// Turn this GasMeter into a DispatchResult that contains the actually used gas. pub fn into_dispatch_result( - self, result: Result, + self, + result: Result, base_weight: Weight, ) -> DispatchResultWithPostInfo where E: Into, { let post_info = PostDispatchInfo { - actual_weight: Some(self.gas_spent().saturating_add(base_weight)), + actual_weight: Some(self.gas_consumed().saturating_add(base_weight)), pays_fee: Default::default(), }; @@ -195,6 +221,10 @@ where .map_err(|e| DispatchErrorWithPostInfo { post_info, error: e.into().error }) } + fn gas_left_lowest(&self) -> Weight { + self.gas_left_lowest.min(self.gas_left) + } + #[cfg(test)] pub fn tokens(&self) -> &[ErasedToken] { &self.tokens diff --git a/frame/contracts/src/lib.rs b/frame/contracts/src/lib.rs index 3ac56d8980cb2..116ca6ce18881 100644 --- a/frame/contracts/src/lib.rs +++ b/frame/contracts/src/lib.rs @@ -118,8 +118,9 @@ use sp_runtime::{ Perbill, }; use frame_support::{ - traits::{OnUnbalanced, Currency, Get, Time, Randomness}, - weights::{Weight, PostDispatchInfo, WithPostDispatchInfo}, + traits::{OnUnbalanced, Currency, Get, Time, Randomness, Filter}, + weights::{Weight, PostDispatchInfo, WithPostDispatchInfo, GetDispatchInfo}, + dispatch::Dispatchable, }; use frame_system::Pallet as System; use pallet_contracts_primitives::{ @@ -154,6 +155,41 @@ pub mod pallet { /// The overarching event type. type Event: From> + IsType<::Event>; + /// The overarching call type. + type Call: + Dispatchable + + GetDispatchInfo + + codec::Decode + + IsType<::Call>; + + /// Filter that is applied to calls dispatched by contracts. + /// + /// Use this filter to control which dispatchables are callable by contracts. + /// This is applied in **addition** to [`frame_system::Config::BaseCallFilter`]. + /// It is recommended to treat this as a whitelist. + /// + /// # Subsistence Threshold + /// + /// The runtime **must** make sure that any allowed dispatchable makes sure that the + /// `total_balance` of the contract stays above [`Pallet::subsistence_threshold()`]. + /// Otherwise contracts can clutter the storage with their tombstones without + /// deposting the correct amount of balance. + /// + /// # Stability + /// + /// The runtime **must** make sure that all dispatchables that are callable by + /// contracts remain stable. In addition [`Self::Call`] itself must remain stable. + /// This means that no existing variants are allowed to switch their positions. + /// + /// # Note + /// + /// Note that dispatchables that are called via contracts do not spawn their + /// own wasm instance for each call (as opposed to when called via a transaction). + /// Therefore please make sure to be restrictive about which dispatchables are allowed + /// in order to not introduce a new DoS vector like memory allocation patterns that can + /// be exploited to drive the runtime into a panic. + type CallFilter: Filter<::Call>; + /// Handler for rent payments. type RentPayment: OnUnbalanced>; @@ -658,7 +694,8 @@ where ); ContractExecResult { result: result.map_err(|r| r.error), - gas_consumed: gas_meter.gas_spent(), + gas_consumed: gas_meter.gas_consumed(), + gas_required: gas_meter.gas_required(), debug_message: debug_message.unwrap_or_default(), } } @@ -699,7 +736,8 @@ where Ok(executable) => executable, Err(error) => return ContractInstantiateResult { result: Err(error.into()), - gas_consumed: gas_meter.gas_spent(), + gas_consumed: gas_meter.gas_consumed(), + gas_required: gas_meter.gas_required(), debug_message: Vec::new(), } }; @@ -727,7 +765,8 @@ where }); ContractInstantiateResult { result: result.map_err(|e| e.error), - gas_consumed: gas_meter.gas_spent(), + gas_consumed: gas_meter.gas_consumed(), + gas_required: gas_meter.gas_required(), debug_message: debug_message.unwrap_or_default(), } } diff --git a/frame/contracts/src/tests.rs b/frame/contracts/src/tests.rs index b3ee139008bc5..ea5fbccb0f2a1 100644 --- a/frame/contracts/src/tests.rs +++ b/frame/contracts/src/tests.rs @@ -40,13 +40,14 @@ use sp_io::hashing::blake2_256; use frame_support::{ assert_ok, assert_err, assert_err_ignore_postinfo, parameter_types, assert_storage_noop, - traits::{Currency, ReservableCurrency, OnInitialize}, + traits::{Currency, ReservableCurrency, OnInitialize, Filter}, weights::{Weight, PostDispatchInfo, DispatchClass, constants::WEIGHT_PER_SECOND}, dispatch::DispatchErrorWithPostInfo, storage::child, }; use frame_system::{self as system, EventRecord, Phase}; use pretty_assertions::assert_eq; +use std::cell::RefCell; use crate as pallet_contracts; @@ -63,6 +64,7 @@ frame_support::construct_runtime!( Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, Timestamp: pallet_timestamp::{Pallet, Call, Storage, Inherent}, Randomness: pallet_randomness_collective_flip::{Pallet, Storage}, + Utility: pallet_utility::{Pallet, Call, Storage, Event}, Contracts: pallet_contracts::{Pallet, Call, Storage, Event}, } ); @@ -125,7 +127,7 @@ pub mod test_utils { } thread_local! { - static TEST_EXTENSION: sp_std::cell::RefCell = Default::default(); + static TEST_EXTENSION: RefCell = Default::default(); } pub struct TestExtension { @@ -211,7 +213,7 @@ parameter_types! { pub static ExistentialDeposit: u64 = 0; } impl frame_system::Config for Test { - type BaseCallFilter = (); + type BaseCallFilter = frame_support::traits::AllowAll; type BlockWeights = BlockWeights; type BlockLength = (); type DbWeight = (); @@ -256,6 +258,11 @@ impl pallet_timestamp::Config for Test { type MinimumPeriod = MinimumPeriod; type WeightInfo = (); } +impl pallet_utility::Config for Test { + type Event = Event; + type Call = Call; + type WeightInfo = (); +} parameter_types! { pub const SignedClaimHandicap: u64 = 2; pub const TombstoneDeposit: u64 = 16; @@ -269,9 +276,6 @@ parameter_types! { pub const DeletionWeightLimit: Weight = 500_000_000_000; pub const MaxCodeSize: u32 = 2 * 1024; pub MySchedule: Schedule = >::default(); -} - -parameter_types! { pub const TransactionByteFee: u64 = 0; } @@ -281,11 +285,32 @@ impl Convert> for Test { } } +/// A filter whose filter function can be swapped at runtime. +pub struct TestFilter; + +thread_local! { + static CALL_FILTER: RefCell bool> = RefCell::new(|_| true); +} + +impl TestFilter { + pub fn set_filter(filter: fn(&Call) -> bool) { + CALL_FILTER.with(|fltr| *fltr.borrow_mut() = filter); + } +} + +impl Filter for TestFilter { + fn filter(call: &Call) -> bool { + CALL_FILTER.with(|fltr| fltr.borrow()(call)) + } +} + impl Config for Test { type Time = Timestamp; type Randomness = Randomness; type Currency = Balances; type Event = Event; + type Call = Call; + type CallFilter = TestFilter; type RentPayment = (); type SignedClaimHandicap = SignedClaimHandicap; type TombstoneDeposit = TombstoneDeposit; @@ -2944,8 +2969,8 @@ fn debug_message_invalid_utf8() { } #[test] -fn gas_estimation_correct() { - let (caller_code, caller_hash) = compile_module::("call_return_code").unwrap(); +fn gas_estimation_nested_call_fixed_limit() { + let (caller_code, caller_hash) = compile_module::("call_with_limit").unwrap(); let (callee_code, callee_hash) = compile_module::("dummy").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { let subsistence = Pallet::::subsistence_threshold(); @@ -2976,24 +3001,93 @@ fn gas_estimation_correct() { ); let addr_callee = Contracts::contract_address(&ALICE, &callee_hash, &[1]); + let input: Vec = AsRef::<[u8]>::as_ref(&addr_callee) + .iter() + .cloned() + .chain((GAS_LIMIT / 5).to_le_bytes()) + .collect(); + // Call in order to determine the gas that is required for this call let result = Contracts::bare_call( ALICE, addr_caller.clone(), 0, GAS_LIMIT, - AsRef::<[u8]>::as_ref(&addr_callee).to_vec(), + input.clone(), false, ); - assert_ok!(result.result); + assert_ok!(&result.result); + + assert!(result.gas_required > result.gas_consumed); // Make the same call using the estimated gas. Should succeed. assert_ok!(Contracts::bare_call( ALICE, addr_caller, 0, - result.gas_consumed, - AsRef::<[u8]>::as_ref(&addr_callee).to_vec(), + result.gas_required, + input, + false, + ).result); + }); +} + +#[test] +#[cfg(feature = "unstable-interface")] +fn gas_estimation_call_runtime() { + let (caller_code, caller_hash) = compile_module::("call_runtime").unwrap(); + let (callee_code, callee_hash) = compile_module::("dummy").unwrap(); + ExtBuilder::default().existential_deposit(50).build().execute_with(|| { + let subsistence = Pallet::::subsistence_threshold(); + let _ = Balances::deposit_creating(&ALICE, 1000 * subsistence); + let _ = Balances::deposit_creating(&CHARLIE, 1000 * subsistence); + + assert_ok!( + Contracts::instantiate_with_code( + Origin::signed(ALICE), + subsistence * 100, + GAS_LIMIT, + caller_code, + vec![], + vec![0], + ), + ); + let addr_caller = Contracts::contract_address(&ALICE, &caller_hash, &[0]); + + assert_ok!( + Contracts::instantiate_with_code( + Origin::signed(ALICE), + subsistence * 100, + GAS_LIMIT, + callee_code, + vec![], + vec![1], + ), + ); + let addr_callee = Contracts::contract_address(&ALICE, &callee_hash, &[1]); + + // Call something trivial with a huge gas limit so that we can observe the effects + // of pre-charging. This should create a difference between consumed and required. + let call = Call::Contracts(crate::Call::call(addr_callee, 0, GAS_LIMIT / 3, vec![])); + let result = Contracts::bare_call( + ALICE, + addr_caller.clone(), + 0, + GAS_LIMIT, + call.encode(), + false, + ); + assert_ok!(&result.result); + + assert!(result.gas_required > result.gas_consumed); + + // Make the same call using the required gas. Should succeed. + assert_ok!(Contracts::bare_call( + ALICE, + addr_caller, + 0, + result.gas_required, + call.encode(), false, ).result); }); diff --git a/frame/contracts/src/wasm/mod.rs b/frame/contracts/src/wasm/mod.rs index 03a409bb12fe2..ef45f35d0dae9 100644 --- a/frame/contracts/src/wasm/mod.rs +++ b/frame/contracts/src/wasm/mod.rs @@ -254,18 +254,22 @@ mod tests { rent::RentStatus, tests::{Test, Call, ALICE, BOB}, }; - use std::collections::HashMap; + use std::{ + borrow::BorrowMut, + cell::RefCell, + collections::HashMap, + }; use sp_core::{Bytes, H256}; use hex_literal::hex; use sp_runtime::DispatchError; - use frame_support::{assert_ok, dispatch::DispatchResult, weights::Weight}; + use frame_support::{ + assert_ok, + dispatch::{DispatchResult, DispatchResultWithPostInfo}, + weights::Weight, + }; use assert_matches::assert_matches; use pallet_contracts_primitives::{ExecReturnValue, ReturnFlags}; use pretty_assertions::assert_eq; - use sp_std::borrow::BorrowMut; - - #[derive(Debug, PartialEq, Eq)] - struct DispatchEntry(Call); #[derive(Debug, PartialEq, Eq)] struct RestoreEntry { @@ -313,6 +317,7 @@ mod tests { restores: Vec, // (topics, data) events: Vec<(Vec, Vec)>, + runtime_calls: RefCell>, schedule: Schedule, rent_params: RentParams, gas_meter: GasMeter, @@ -335,6 +340,7 @@ mod tests { transfers: Default::default(), restores: Default::default(), events: Default::default(), + runtime_calls: Default::default(), schedule: Default::default(), rent_params: Default::default(), gas_meter: GasMeter::new(10_000_000_000), @@ -481,6 +487,10 @@ mod tests { self.debug_buffer.extend(msg.as_bytes()); true } + fn call_runtime(&self, call: ::Call) -> DispatchResultWithPostInfo { + self.runtime_calls.borrow_mut().push(call); + Ok(Default::default()) + } } fn execute>( @@ -2160,4 +2170,81 @@ mod tests { }) ); } + + #[cfg(feature = "unstable-interface")] + const CODE_CALL_RUNTIME: &str = r#" +(module + (import "__unstable__" "seal_call_runtime" (func $seal_call_runtime (param i32 i32) (result i32))) + (import "seal0" "seal_input" (func $seal_input (param i32 i32))) + (import "seal0" "seal_return" (func $seal_return (param i32 i32 i32))) + (import "env" "memory" (memory 1 1)) + + ;; 0x1000 = 4k in little endian + ;; size of input buffer + (data (i32.const 0) "\00\10") + + (func (export "call") + ;; Receive the encoded call + (call $seal_input + (i32.const 4) ;; Pointer to the input buffer + (i32.const 0) ;; Size of the length buffer + ) + ;; Just use the call passed as input and store result to memory + (i32.store (i32.const 0) + (call $seal_call_runtime + (i32.const 4) ;; Pointer where the call is stored + (i32.load (i32.const 0)) ;; Size of the call + ) + ) + (call $seal_return + (i32.const 0) ;; flags + (i32.const 0) ;; returned value + (i32.const 4) ;; length of returned value + ) + ) + + (func (export "deploy")) +) +"#; + + #[test] + #[cfg(feature = "unstable-interface")] + fn call_runtime_works() { + use std::convert::TryInto; + let call = Call::System(frame_system::Call::remark(b"Hello World".to_vec())); + let mut ext = MockExt::default(); + let result = execute( + CODE_CALL_RUNTIME, + call.encode(), + &mut ext, + ).unwrap(); + assert_eq!( + *ext.runtime_calls.borrow(), + vec![call], + ); + // 0 = ReturnCode::Success + assert_eq!(u32::from_le_bytes(result.data.0.try_into().unwrap()), 0); + } + + #[test] + #[cfg(feature = "unstable-interface")] + fn call_runtime_panics_on_invalid_call() { + let mut ext = MockExt::default(); + let result = execute( + CODE_CALL_RUNTIME, + vec![0x42], + &mut ext, + ); + assert_eq!( + result, + Err(ExecError { + error: Error::::DecodingFailed.into(), + origin: ErrorOrigin::Caller, + }) + ); + assert_eq!( + *ext.runtime_calls.borrow(), + vec![], + ); + } } diff --git a/frame/contracts/src/wasm/runtime.rs b/frame/contracts/src/wasm/runtime.rs index 8956e3a2b4458..7b6004a84f06b 100644 --- a/frame/contracts/src/wasm/runtime.rs +++ b/frame/contracts/src/wasm/runtime.rs @@ -76,6 +76,9 @@ pub enum ReturnCode { /// recording was disabled. #[cfg(feature = "unstable-interface")] LoggingDisabled = 9, + /// The call dispatched by `seal_call_runtime` was executed but returned an error. + #[cfg(feature = "unstable-interface")] + CallRuntimeReturnedError = 10, } impl ConvertibleToWasm for ReturnCode { @@ -213,6 +216,12 @@ pub enum RuntimeCosts { HashBlake128(u32), /// Weight charged by a chain extension through `seal_call_chain_extension`. ChainExtension(u64), + /// Weight charged for copying data from the sandbox. + #[cfg(feature = "unstable-interface")] + CopyIn(u32), + /// Weight charged for calling into the runtime. + #[cfg(feature = "unstable-interface")] + CallRuntime(Weight), } impl RuntimeCosts { @@ -273,6 +282,10 @@ impl RuntimeCosts { HashBlake128(len) => s.hash_blake2_128 .saturating_add(s.hash_blake2_128_per_byte.saturating_mul(len.into())), ChainExtension(amount) => amount, + #[cfg(feature = "unstable-interface")] + CopyIn(len) => s.return_per_byte.saturating_mul(len.into()), + #[cfg(feature = "unstable-interface")] + CallRuntime(weight) => weight, }; RuntimeToken { #[cfg(test)] @@ -457,6 +470,15 @@ where self.ext.gas_meter().charge(token) } + /// Adjust a previously charged amount down to its actual amount. + /// + /// This is when a maximum a priori amount was charged and then should be partially + /// refunded to match the actual amount. + pub fn adjust_gas(&mut self, charged: ChargedAmount, actual_costs: RuntimeCosts) { + let token = actual_costs.token(&self.ext.schedule().host_fn_weights); + self.ext.gas_meter().adjust_gas(charged, token); + } + /// Read designated chunk from the sandbox memory. /// /// Returns `Err` if one of the following conditions occurs: @@ -797,7 +819,6 @@ where // data passed to the supervisor will lead to a trap. This is not documented explicitly // for every function. define_env!(Env, , - // Account for used gas. Traps if gas used is greater than gas limit. // // NOTE: This is a implementation defined call and is NOT a part of the public API. @@ -1808,4 +1829,58 @@ define_env!(Env, , out_ptr, out_len_ptr, &rent_status, false, already_charged )?) }, + + // Call some dispatchable of the runtime. + // + // This function decodes the passed in data as the overarching `Call` type of the + // runtime and dispatches it. The weight as specified in the runtime is charged + // from the gas meter. Any weight refunds made by the dispatchable are considered. + // + // The filter specified by `Config::CallFilter` is attached to the origin of + // the dispatched call. + // + // # Parameters + // + // - `input_ptr`: the pointer into the linear memory where the input data is placed. + // - `input_len`: the length of the input data in bytes. + // + // # Return Value + // + // Returns `ReturnCode::Success` when the dispatchable was succesfully executed and + // returned `Ok`. When the dispatchable was exeuted but returned an error + // `ReturnCode::CallRuntimeReturnedError` is returned. The full error is not + // provided because it is not guaranteed to be stable. + // + // # Comparison with `ChainExtension` + // + // Just as a chain extension this API allows the runtime to extend the functionality + // of contracts. While making use of this function is generelly easier it cannot be + // used in call cases. Consider writing a chain extension if you need to do perform + // one of the following tasks: + // + // - Return data. + // - Provide functionality **exclusively** to contracts. + // - Provide custom weights. + // - Avoid the need to keep the `Call` data structure stable. + // + // # Unstable + // + // This function is unstable and subject to change (or removal) in the future. Do not + // deploy a contract using it to a production chain. + [__unstable__] seal_call_runtime(ctx, call_ptr: u32, call_len: u32) -> ReturnCode => { + use frame_support::{dispatch::GetDispatchInfo, weights::extract_actual_weight}; + ctx.charge_gas(RuntimeCosts::CopyIn(call_len))?; + let call: ::Call = ctx.read_sandbox_memory_as_unbounded( + call_ptr, call_len + )?; + let dispatch_info = call.get_dispatch_info(); + let charged = ctx.charge_gas(RuntimeCosts::CallRuntime(dispatch_info.weight))?; + let result = ctx.ext.call_runtime(call); + let actual_weight = extract_actual_weight(&result, &dispatch_info); + ctx.adjust_gas(charged, RuntimeCosts::CallRuntime(actual_weight)); + match result { + Ok(_) => Ok(ReturnCode::Success), + Err(_) => Ok(ReturnCode::CallRuntimeReturnedError), + } + }, ); diff --git a/frame/election-provider-multi-phase/src/mock.rs b/frame/election-provider-multi-phase/src/mock.rs index 1b8ee14345852..55fa58590ce71 100644 --- a/frame/election-provider-multi-phase/src/mock.rs +++ b/frame/election-provider-multi-phase/src/mock.rs @@ -199,7 +199,7 @@ pub fn witness() -> SolutionOrSnapshotSize { impl frame_system::Config for Runtime { type SS58Prefix = (); - type BaseCallFilter = (); + type BaseCallFilter = frame_support::traits::AllowAll; type Origin = Origin; type Index = u64; type BlockNumber = u64; diff --git a/frame/elections-phragmen/src/lib.rs b/frame/elections-phragmen/src/lib.rs index 8a1680633ef7b..db4af14328bf1 100644 --- a/frame/elections-phragmen/src/lib.rs +++ b/frame/elections-phragmen/src/lib.rs @@ -1123,7 +1123,7 @@ mod tests { } impl frame_system::Config for Test { - type BaseCallFilter = (); + type BaseCallFilter = frame_support::traits::AllowAll; type BlockWeights = BlockWeights; type BlockLength = (); type DbWeight = (); diff --git a/frame/elections/src/mock.rs b/frame/elections/src/mock.rs index 7eef7f4909982..4df6da829a18e 100644 --- a/frame/elections/src/mock.rs +++ b/frame/elections/src/mock.rs @@ -36,7 +36,7 @@ parameter_types! { frame_system::limits::BlockWeights::simple_max(1024); } impl frame_system::Config for Test { - type BaseCallFilter = (); + type BaseCallFilter = frame_support::traits::AllowAll; type BlockWeights = (); type BlockLength = (); type DbWeight = (); diff --git a/frame/example-offchain-worker/src/tests.rs b/frame/example-offchain-worker/src/tests.rs index ee47aa5629fd9..7d16e59490342 100644 --- a/frame/example-offchain-worker/src/tests.rs +++ b/frame/example-offchain-worker/src/tests.rs @@ -60,7 +60,7 @@ parameter_types! { frame_system::limits::BlockWeights::simple_max(1024); } impl frame_system::Config for Test { - type BaseCallFilter = (); + type BaseCallFilter = frame_support::traits::AllowAll; type BlockWeights = (); type BlockLength = (); type DbWeight = (); diff --git a/frame/example-parallel/src/tests.rs b/frame/example-parallel/src/tests.rs index 56cb73ebb08bb..395290c0bf6e7 100644 --- a/frame/example-parallel/src/tests.rs +++ b/frame/example-parallel/src/tests.rs @@ -44,7 +44,7 @@ parameter_types! { } impl frame_system::Config for Test { - type BaseCallFilter = (); + type BaseCallFilter = frame_support::traits::AllowAll; type Origin = Origin; type Call = Call; type PalletInfo = PalletInfo; diff --git a/frame/example/src/tests.rs b/frame/example/src/tests.rs index c699a0bfad36c..68a9237921805 100644 --- a/frame/example/src/tests.rs +++ b/frame/example/src/tests.rs @@ -54,7 +54,7 @@ parameter_types! { frame_system::limits::BlockWeights::simple_max(1024); } impl frame_system::Config for Test { - type BaseCallFilter = (); + type BaseCallFilter = frame_support::traits::AllowAll; type BlockWeights = (); type BlockLength = (); type DbWeight = (); diff --git a/frame/executive/src/lib.rs b/frame/executive/src/lib.rs index c5f39e14f5fc1..719a94e6fb1b3 100644 --- a/frame/executive/src/lib.rs +++ b/frame/executive/src/lib.rs @@ -694,7 +694,7 @@ mod tests { }; } impl frame_system::Config for Runtime { - type BaseCallFilter = (); + type BaseCallFilter = frame_support::traits::AllowAll; type BlockWeights = BlockWeights; type BlockLength = (); type DbWeight = (); diff --git a/frame/gilt/src/mock.rs b/frame/gilt/src/mock.rs index fb888515496b1..aeff70610d4bb 100644 --- a/frame/gilt/src/mock.rs +++ b/frame/gilt/src/mock.rs @@ -48,7 +48,7 @@ parameter_types! { } impl frame_system::Config for Test { - type BaseCallFilter = (); + type BaseCallFilter = frame_support::traits::AllowAll; type BlockWeights = (); type BlockLength = (); type Origin = Origin; diff --git a/frame/grandpa/src/mock.rs b/frame/grandpa/src/mock.rs index ebe5996c9dab5..768564c30105f 100644 --- a/frame/grandpa/src/mock.rs +++ b/frame/grandpa/src/mock.rs @@ -75,7 +75,7 @@ parameter_types! { } impl frame_system::Config for Test { - type BaseCallFilter = (); + type BaseCallFilter = frame_support::traits::AllowAll; type BlockWeights = (); type BlockLength = (); type DbWeight = (); diff --git a/frame/identity/src/tests.rs b/frame/identity/src/tests.rs index fea83dc3b10aa..7a8bb4fa6d92e 100644 --- a/frame/identity/src/tests.rs +++ b/frame/identity/src/tests.rs @@ -50,7 +50,7 @@ parameter_types! { frame_system::limits::BlockWeights::simple_max(1024); } impl frame_system::Config for Test { - type BaseCallFilter = (); + type BaseCallFilter = frame_support::traits::AllowAll; type BlockWeights = (); type BlockLength = (); type Origin = Origin; diff --git a/frame/im-online/src/mock.rs b/frame/im-online/src/mock.rs index 4bc976476a676..3d7d6d73cd83a 100644 --- a/frame/im-online/src/mock.rs +++ b/frame/im-online/src/mock.rs @@ -118,7 +118,7 @@ parameter_types! { } impl frame_system::Config for Runtime { - type BaseCallFilter = (); + type BaseCallFilter = frame_support::traits::AllowAll; type BlockWeights = (); type BlockLength = (); type DbWeight = (); diff --git a/frame/indices/src/mock.rs b/frame/indices/src/mock.rs index bd9e9c33af25e..46c1d814acb6b 100644 --- a/frame/indices/src/mock.rs +++ b/frame/indices/src/mock.rs @@ -46,7 +46,7 @@ parameter_types! { } impl frame_system::Config for Test { - type BaseCallFilter = (); + type BaseCallFilter = frame_support::traits::AllowAll; type BlockWeights = (); type BlockLength = (); type DbWeight = (); diff --git a/frame/lottery/src/mock.rs b/frame/lottery/src/mock.rs index 07593c17e5086..885e81bb32ea3 100644 --- a/frame/lottery/src/mock.rs +++ b/frame/lottery/src/mock.rs @@ -56,7 +56,7 @@ parameter_types! { } impl frame_system::Config for Test { - type BaseCallFilter = (); + type BaseCallFilter = frame_support::traits::AllowAll; type BlockWeights = (); type BlockLength = (); type DbWeight = (); diff --git a/frame/membership/src/lib.rs b/frame/membership/src/lib.rs index 62c9e5eae1a65..0d95af4e6f4a4 100644 --- a/frame/membership/src/lib.rs +++ b/frame/membership/src/lib.rs @@ -491,7 +491,7 @@ mod tests { } impl frame_system::Config for Test { - type BaseCallFilter = (); + type BaseCallFilter = frame_support::traits::AllowAll; type BlockWeights = (); type BlockLength = (); type DbWeight = (); diff --git a/frame/merkle-mountain-range/src/mock.rs b/frame/merkle-mountain-range/src/mock.rs index 3c8a5d284566f..0d89021ae9668 100644 --- a/frame/merkle-mountain-range/src/mock.rs +++ b/frame/merkle-mountain-range/src/mock.rs @@ -49,7 +49,7 @@ parameter_types! { pub const BlockHashCount: u64 = 250; } impl frame_system::Config for Test { - type BaseCallFilter = (); + type BaseCallFilter = frame_support::traits::AllowAll; type Origin = Origin; type Call = Call; type Index = u64; diff --git a/frame/nicks/src/lib.rs b/frame/nicks/src/lib.rs index 1e0ef90e0a3ac..afdcca7e91a5c 100644 --- a/frame/nicks/src/lib.rs +++ b/frame/nicks/src/lib.rs @@ -275,7 +275,7 @@ mod tests { frame_system::limits::BlockWeights::simple_max(1024); } impl frame_system::Config for Test { - type BaseCallFilter = (); + type BaseCallFilter = frame_support::traits::AllowAll; type BlockWeights = (); type BlockLength = (); type DbWeight = (); diff --git a/frame/node-authorization/src/mock.rs b/frame/node-authorization/src/mock.rs index 3f4f894cdf7e3..e952ed900d4be 100644 --- a/frame/node-authorization/src/mock.rs +++ b/frame/node-authorization/src/mock.rs @@ -48,7 +48,7 @@ parameter_types! { pub const BlockHashCount: u64 = 250; } impl frame_system::Config for Test { - type BaseCallFilter = (); + type BaseCallFilter = frame_support::traits::AllowAll; type DbWeight = (); type BlockWeights = (); type BlockLength = (); diff --git a/frame/offences/benchmarking/src/mock.rs b/frame/offences/benchmarking/src/mock.rs index cd72780ec5ad2..4e7a63c58a40b 100644 --- a/frame/offences/benchmarking/src/mock.rs +++ b/frame/offences/benchmarking/src/mock.rs @@ -43,7 +43,7 @@ parameter_types! { } impl frame_system::Config for Test { - type BaseCallFilter = (); + type BaseCallFilter = frame_support::traits::AllowAll; type BlockWeights = (); type BlockLength = (); type DbWeight = (); diff --git a/frame/offences/src/mock.rs b/frame/offences/src/mock.rs index fff1973e334ea..5818ae71687b2 100644 --- a/frame/offences/src/mock.rs +++ b/frame/offences/src/mock.rs @@ -85,7 +85,7 @@ parameter_types! { frame_system::limits::BlockWeights::simple_max(2 * WEIGHT_PER_SECOND); } impl frame_system::Config for Runtime { - type BaseCallFilter = (); + type BaseCallFilter = frame_support::traits::AllowAll; type BlockWeights = (); type BlockLength = (); type DbWeight = RocksDbWeight; diff --git a/frame/randomness-collective-flip/src/lib.rs b/frame/randomness-collective-flip/src/lib.rs index eaefa9ac86c3b..1ff7d4382da14 100644 --- a/frame/randomness-collective-flip/src/lib.rs +++ b/frame/randomness-collective-flip/src/lib.rs @@ -195,7 +195,7 @@ mod tests { } impl frame_system::Config for Test { - type BaseCallFilter = (); + type BaseCallFilter = frame_support::traits::AllowAll; type BlockWeights = (); type BlockLength = BlockLength; type DbWeight = (); diff --git a/frame/recovery/src/mock.rs b/frame/recovery/src/mock.rs index 6a0abab2bd12b..9139cc12ce54a 100644 --- a/frame/recovery/src/mock.rs +++ b/frame/recovery/src/mock.rs @@ -48,7 +48,7 @@ parameter_types! { } impl frame_system::Config for Test { - type BaseCallFilter = (); + type BaseCallFilter = frame_support::traits::AllowAll; type BlockWeights = (); type BlockLength = (); type DbWeight = (); diff --git a/frame/scored-pool/src/mock.rs b/frame/scored-pool/src/mock.rs index 44a28234a2a82..30dc48dd19d0a 100644 --- a/frame/scored-pool/src/mock.rs +++ b/frame/scored-pool/src/mock.rs @@ -57,7 +57,7 @@ ord_parameter_types! { } impl frame_system::Config for Test { - type BaseCallFilter = (); + type BaseCallFilter = frame_support::traits::AllowAll; type BlockWeights = (); type BlockLength = (); type DbWeight = (); diff --git a/frame/session/benchmarking/src/mock.rs b/frame/session/benchmarking/src/mock.rs index 591e54f067bb5..a3f9b6b447c38 100644 --- a/frame/session/benchmarking/src/mock.rs +++ b/frame/session/benchmarking/src/mock.rs @@ -45,7 +45,7 @@ frame_support::construct_runtime!( ); impl frame_system::Config for Test { - type BaseCallFilter = (); + type BaseCallFilter = frame_support::traits::AllowAll; type BlockWeights = (); type BlockLength = (); type DbWeight = (); diff --git a/frame/session/src/mock.rs b/frame/session/src/mock.rs index 3459ab73d6afe..1462b2326777e 100644 --- a/frame/session/src/mock.rs +++ b/frame/session/src/mock.rs @@ -228,7 +228,7 @@ parameter_types! { } impl frame_system::Config for Test { - type BaseCallFilter = (); + type BaseCallFilter = frame_support::traits::AllowAll; type BlockWeights = (); type BlockLength = (); type DbWeight = (); diff --git a/frame/society/src/mock.rs b/frame/society/src/mock.rs index 5e156caa282eb..18cdda678da6f 100644 --- a/frame/society/src/mock.rs +++ b/frame/society/src/mock.rs @@ -69,7 +69,7 @@ ord_parameter_types! { } impl frame_system::Config for Test { - type BaseCallFilter = (); + type BaseCallFilter = frame_support::traits::AllowAll; type BlockWeights = (); type BlockLength = (); type DbWeight = (); diff --git a/frame/staking/fuzzer/src/mock.rs b/frame/staking/fuzzer/src/mock.rs index 4ac1a10364e6c..98181ca2694d2 100644 --- a/frame/staking/fuzzer/src/mock.rs +++ b/frame/staking/fuzzer/src/mock.rs @@ -42,7 +42,7 @@ frame_support::construct_runtime!( ); impl frame_system::Config for Test { - type BaseCallFilter = (); + type BaseCallFilter = frame_support::traits::AllowAll; type BlockWeights = (); type BlockLength = (); type DbWeight = (); diff --git a/frame/staking/src/mock.rs b/frame/staking/src/mock.rs index e0079cc3f375a..3242a40ccd45e 100644 --- a/frame/staking/src/mock.rs +++ b/frame/staking/src/mock.rs @@ -128,7 +128,7 @@ parameter_types! { } impl frame_system::Config for Test { - type BaseCallFilter = (); + type BaseCallFilter = frame_support::traits::AllowAll; type BlockWeights = (); type BlockLength = (); type DbWeight = RocksDbWeight; diff --git a/frame/support/src/dispatch.rs b/frame/support/src/dispatch.rs index 6f98dee8690be..12c1161a6a6c1 100644 --- a/frame/support/src/dispatch.rs +++ b/frame/support/src/dispatch.rs @@ -2684,7 +2684,7 @@ mod tests { type Origin = OuterOrigin; type AccountId = u32; type Call = (); - type BaseCallFilter = (); + type BaseCallFilter = frame_support::traits::AllowAll; type BlockNumber = u32; type PalletInfo = Self; type DbWeight = (); diff --git a/frame/support/src/traits.rs b/frame/support/src/traits.rs index e8ce07528c8a0..ec47331285ef8 100644 --- a/frame/support/src/traits.rs +++ b/frame/support/src/traits.rs @@ -45,6 +45,7 @@ pub use validation::{ mod filter; pub use filter::{ Filter, FilterStack, FilterStackGuard, ClearFilterGuard, InstanceFilter, IntegrityTest, + AllowAll, DenyAll, }; mod misc; diff --git a/frame/support/src/traits/filter.rs b/frame/support/src/traits/filter.rs index f884a8ece72e5..4b70fa177e5ca 100644 --- a/frame/support/src/traits/filter.rs +++ b/frame/support/src/traits/filter.rs @@ -25,10 +25,20 @@ pub trait Filter { fn filter(_: &T) -> bool; } -impl Filter for () { +/// A [`Filter`] that allows any value. +pub enum AllowAll {} + +/// A [`Filter`] that denies any value. +pub enum DenyAll {} + +impl Filter for AllowAll { fn filter(_: &T) -> bool { true } } +impl Filter for DenyAll { + fn filter(_: &T) -> bool { false } +} + /// Trait to add a constraint onto the filter. pub trait FilterStack: Filter { /// The type used to archive the stack. diff --git a/frame/support/test/tests/construct_runtime.rs b/frame/support/test/tests/construct_runtime.rs index 98d0c45d2425a..dde7f6d53f8ed 100644 --- a/frame/support/test/tests/construct_runtime.rs +++ b/frame/support/test/tests/construct_runtime.rs @@ -225,7 +225,7 @@ pub type BlockNumber = u64; pub type Index = u64; impl system::Config for Runtime { - type BaseCallFilter = (); + type BaseCallFilter = frame_support::traits::AllowAll; type Hash = H256; type Origin = Origin; type BlockNumber = BlockNumber; diff --git a/frame/support/test/tests/instance.rs b/frame/support/test/tests/instance.rs index d952fd82eb0df..7d18a8368edab 100644 --- a/frame/support/test/tests/instance.rs +++ b/frame/support/test/tests/instance.rs @@ -255,7 +255,7 @@ pub type BlockNumber = u64; pub type Index = u64; impl system::Config for Runtime { - type BaseCallFilter= (); + type BaseCallFilter= frame_support::traits::AllowAll; type Hash = H256; type Origin = Origin; type BlockNumber = BlockNumber; diff --git a/frame/support/test/tests/issue2219.rs b/frame/support/test/tests/issue2219.rs index 4525e8c1a1fe2..78a79055a389d 100644 --- a/frame/support/test/tests/issue2219.rs +++ b/frame/support/test/tests/issue2219.rs @@ -158,7 +158,7 @@ pub type Block = generic::Block; pub type UncheckedExtrinsic = generic::UncheckedExtrinsic; impl system::Config for Runtime { - type BaseCallFilter = (); + type BaseCallFilter = frame_support::traits::AllowAll; type Hash = H256; type Origin = Origin; type BlockNumber = BlockNumber; diff --git a/frame/support/test/tests/pallet.rs b/frame/support/test/tests/pallet.rs index 7438cee2bcab9..59ebd2e71e599 100644 --- a/frame/support/test/tests/pallet.rs +++ b/frame/support/test/tests/pallet.rs @@ -448,7 +448,7 @@ frame_support::parameter_types!( ); impl frame_system::Config for Runtime { - type BaseCallFilter = (); + type BaseCallFilter = frame_support::traits::AllowAll; type Origin = Origin; type Index = u64; type BlockNumber = u32; diff --git a/frame/support/test/tests/pallet_compatibility.rs b/frame/support/test/tests/pallet_compatibility.rs index db01d15e5daa9..3c055b9f45aef 100644 --- a/frame/support/test/tests/pallet_compatibility.rs +++ b/frame/support/test/tests/pallet_compatibility.rs @@ -203,7 +203,7 @@ frame_support::parameter_types!( ); impl frame_system::Config for Runtime { - type BaseCallFilter = (); + type BaseCallFilter = frame_support::traits::AllowAll; type Origin = Origin; type Index = u64; type BlockNumber = u32; diff --git a/frame/support/test/tests/pallet_compatibility_instance.rs b/frame/support/test/tests/pallet_compatibility_instance.rs index 63e71c8bf255c..fd5d5fb7fdbbd 100644 --- a/frame/support/test/tests/pallet_compatibility_instance.rs +++ b/frame/support/test/tests/pallet_compatibility_instance.rs @@ -198,7 +198,7 @@ impl frame_system::Config for Runtime { type BlockWeights = (); type BlockLength = (); type DbWeight = (); - type BaseCallFilter = (); + type BaseCallFilter = frame_support::traits::AllowAll; type Origin = Origin; type Index = u64; type BlockNumber = u32; diff --git a/frame/support/test/tests/pallet_instance.rs b/frame/support/test/tests/pallet_instance.rs index ccac97100a4be..11f9497b7bec2 100644 --- a/frame/support/test/tests/pallet_instance.rs +++ b/frame/support/test/tests/pallet_instance.rs @@ -250,7 +250,7 @@ frame_support::parameter_types!( ); impl frame_system::Config for Runtime { - type BaseCallFilter = (); + type BaseCallFilter = frame_support::traits::AllowAll; type Origin = Origin; type Index = u64; type BlockNumber = u32; diff --git a/frame/support/test/tests/pallet_version.rs b/frame/support/test/tests/pallet_version.rs index 5c33d45aea644..ed0bf52a0346f 100644 --- a/frame/support/test/tests/pallet_version.rs +++ b/frame/support/test/tests/pallet_version.rs @@ -144,7 +144,7 @@ frame_support::parameter_types!( ); impl frame_system::Config for Runtime { - type BaseCallFilter = (); + type BaseCallFilter = frame_support::traits::AllowAll; type Origin = Origin; type Index = u64; type BlockNumber = BlockNumber; diff --git a/frame/support/test/tests/pallet_with_name_trait_is_valid.rs b/frame/support/test/tests/pallet_with_name_trait_is_valid.rs index 6f35b122f6399..665bbc2b5c513 100644 --- a/frame/support/test/tests/pallet_with_name_trait_is_valid.rs +++ b/frame/support/test/tests/pallet_with_name_trait_is_valid.rs @@ -126,7 +126,7 @@ mod tests { } impl frame_system::Config for Runtime { - type BaseCallFilter = (); + type BaseCallFilter = frame_support::traits::AllowAll; type Origin = Origin; type Index = u64; type BlockNumber = u64; diff --git a/frame/system/benches/bench.rs b/frame/system/benches/bench.rs index 47980a88164e5..02ea48bdde032 100644 --- a/frame/system/benches/bench.rs +++ b/frame/system/benches/bench.rs @@ -67,7 +67,7 @@ frame_support::parameter_types! { ); } impl system::Config for Runtime { - type BaseCallFilter = (); + type BaseCallFilter = frame_support::traits::AllowAll; type BlockWeights = (); type BlockLength = BlockLength; type DbWeight = (); diff --git a/frame/system/benchmarking/src/mock.rs b/frame/system/benchmarking/src/mock.rs index 253945a598bdc..b375c9fcb5093 100644 --- a/frame/system/benchmarking/src/mock.rs +++ b/frame/system/benchmarking/src/mock.rs @@ -39,7 +39,7 @@ frame_support::construct_runtime!( ); impl frame_system::Config for Test { - type BaseCallFilter = (); + type BaseCallFilter = frame_support::traits::AllowAll; type BlockWeights = (); type BlockLength = (); type DbWeight = (); diff --git a/frame/system/src/mock.rs b/frame/system/src/mock.rs index 0f53532eb8f6b..e9b6fb7d968ec 100644 --- a/frame/system/src/mock.rs +++ b/frame/system/src/mock.rs @@ -85,7 +85,7 @@ impl OnKilledAccount for RecordKilled { } impl Config for Test { - type BaseCallFilter = (); + type BaseCallFilter = frame_support::traits::AllowAll; type BlockWeights = RuntimeBlockWeights; type BlockLength = RuntimeBlockLength; type Origin = Origin; diff --git a/frame/timestamp/src/lib.rs b/frame/timestamp/src/lib.rs index f7dd7378d8ab5..e9b6388340b2f 100644 --- a/frame/timestamp/src/lib.rs +++ b/frame/timestamp/src/lib.rs @@ -326,7 +326,7 @@ mod tests { frame_system::limits::BlockWeights::simple_max(1024); } impl frame_system::Config for Test { - type BaseCallFilter = (); + type BaseCallFilter = frame_support::traits::AllowAll; type BlockWeights = (); type BlockLength = (); type DbWeight = (); diff --git a/frame/tips/src/tests.rs b/frame/tips/src/tests.rs index 6063f0954bd8a..cb58ba6aabd6d 100644 --- a/frame/tips/src/tests.rs +++ b/frame/tips/src/tests.rs @@ -58,7 +58,7 @@ parameter_types! { pub const AvailableBlockRatio: Perbill = Perbill::one(); } impl frame_system::Config for Test { - type BaseCallFilter = (); + type BaseCallFilter = frame_support::traits::AllowAll; type BlockWeights = (); type BlockLength = (); type DbWeight = (); diff --git a/frame/transaction-payment/src/lib.rs b/frame/transaction-payment/src/lib.rs index 416439e7f200c..25fce83e69930 100644 --- a/frame/transaction-payment/src/lib.rs +++ b/frame/transaction-payment/src/lib.rs @@ -715,7 +715,7 @@ mod tests { } impl frame_system::Config for Runtime { - type BaseCallFilter = (); + type BaseCallFilter = frame_support::traits::AllowAll; type BlockWeights = BlockWeights; type BlockLength = (); type DbWeight = (); diff --git a/frame/transaction-storage/src/mock.rs b/frame/transaction-storage/src/mock.rs index 03dacf8a98e89..344d7b7369533 100644 --- a/frame/transaction-storage/src/mock.rs +++ b/frame/transaction-storage/src/mock.rs @@ -51,7 +51,7 @@ parameter_types! { } impl frame_system::Config for Test { - type BaseCallFilter = (); + type BaseCallFilter = frame_support::traits::AllowAll; type BlockWeights = (); type BlockLength = (); type Origin = Origin; diff --git a/frame/treasury/src/tests.rs b/frame/treasury/src/tests.rs index e4b6f2d664fc2..dbd5b22741ba5 100644 --- a/frame/treasury/src/tests.rs +++ b/frame/treasury/src/tests.rs @@ -54,7 +54,7 @@ parameter_types! { frame_system::limits::BlockWeights::simple_max(1024); } impl frame_system::Config for Test { - type BaseCallFilter = (); + type BaseCallFilter = frame_support::traits::AllowAll; type BlockWeights = (); type BlockLength = (); type DbWeight = (); diff --git a/frame/uniques/src/mock.rs b/frame/uniques/src/mock.rs index 336a262358b24..254acd6c419cf 100644 --- a/frame/uniques/src/mock.rs +++ b/frame/uniques/src/mock.rs @@ -43,7 +43,7 @@ parameter_types! { pub const BlockHashCount: u64 = 250; } impl frame_system::Config for Test { - type BaseCallFilter = (); + type BaseCallFilter = frame_support::traits::AllowAll; type BlockWeights = (); type BlockLength = (); type Origin = Origin; diff --git a/frame/vesting/src/mock.rs b/frame/vesting/src/mock.rs index 6fdd44aed140e..45bfb788ba728 100644 --- a/frame/vesting/src/mock.rs +++ b/frame/vesting/src/mock.rs @@ -48,7 +48,7 @@ parameter_types! { impl frame_system::Config for Test { type AccountData = pallet_balances::AccountData; type AccountId = u64; - type BaseCallFilter = (); + type BaseCallFilter = frame_support::traits::AllowAll; type BlockHashCount = BlockHashCount; type BlockLength = (); type BlockNumber = u64; diff --git a/test-utils/runtime/src/lib.rs b/test-utils/runtime/src/lib.rs index a6ba7e39c8958..1023b77939bbc 100644 --- a/test-utils/runtime/src/lib.rs +++ b/test-utils/runtime/src/lib.rs @@ -541,7 +541,7 @@ parameter_types! { } impl frame_system::Config for Runtime { - type BaseCallFilter = (); + type BaseCallFilter = frame_support::traits::AllowAll; type BlockWeights = RuntimeBlockWeights; type BlockLength = RuntimeBlockLength; type Origin = Origin;