contracts: Allow contracts to dispatch calls into the runtime (#9276)

* contracts: Allow contracts to dispatch calls into the runtime

* Fix RPC tests

* Fix typo

* Replace () by AllowAllFilter and DenyAllFilter

* Add rust doc

* Fixup for `()` removal

* Fix lowest gas calculation

* Rename AllowAllFilter and DenyAllFilter

* Updated changelog
This commit is contained in:
Alexander Theißen
2021-07-12 22:40:27 +02:00
committed by GitHub
parent 2f31602896
commit e01ac8cea0
80 changed files with 674 additions and 107 deletions
+1
View File
@@ -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",
@@ -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 = ();
@@ -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).
+10 -2
View File
@@ -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;
+1 -1
View File
@@ -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;
+1 -1
View File
@@ -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;
+1 -1
View File
@@ -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 = ();
+1 -1
View File
@@ -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 = ();
@@ -50,7 +50,7 @@ pub mod pallet {
Vec<AuthorityId>,
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 = ();
+1 -1
View File
@@ -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 = ();
+1 -1
View File
@@ -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 = ();
@@ -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 = ();
+1 -1
View File
@@ -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 = ();
@@ -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 = ();
+1 -1
View File
@@ -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 = ();
+1 -1
View File
@@ -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 = ();
+1 -1
View File
@@ -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 = ();
+3
View File
@@ -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)
+1
View File
@@ -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"]
@@ -37,6 +37,15 @@ use serde::{Serialize, Deserialize};
pub struct ContractResult<T> {
/// 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.
///
@@ -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"))
)
@@ -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))
)
)
+8 -4
View File
@@ -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"
}
@@ -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<ChargedAmount> {
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.
+129 -6
View File
@@ -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: <Self::T as Config>::Call) -> DispatchResultWithPostInfo;
}
/// Describes the different functions that can be exported by an [`Executable`].
@@ -1291,6 +1295,12 @@ where
false
}
}
fn call_runtime(&self, call: <Self::T as Config>::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<T: Config>(
@@ -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<Test>;
type MockStack<'a> = Stack<'a, Test, MockExecutable>;
@@ -1353,7 +1366,7 @@ mod tests {
}
fn events() -> Vec<Event<Test>> {
<frame_system::Pallet<Test>>::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::<Test>::subsistence_threshold();
let schedule = <Test as Config>::Schedule::get();
let mut gas_meter = GasMeter::<Test>::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 = <Test as frame_system::Config>::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::<Test>::subsistence_threshold();
let schedule = <Test as Config>::Schedule::get();
let mut gas_meter = GasMeter::<Test>::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 = <Test as frame_system::Config>::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![],
},
]);
});
}
}
+34 -4
View File
@@ -79,6 +79,8 @@ pub struct GasMeter<T: Config> {
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<T>,
#[cfg(test)]
tokens: Vec<ErasedToken>,
@@ -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<Tok: Token<T>>(&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<R, E>(
self, result: Result<R, E>,
self,
result: Result<R, E>,
base_weight: Weight,
) -> DispatchResultWithPostInfo
where
E: Into<ExecError>,
{
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
+44 -5
View File
@@ -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<Event<Self>> + IsType<<Self as frame_system::Config>::Event>;
/// The overarching call type.
type Call:
Dispatchable<Origin=Self::Origin, PostInfo=PostDispatchInfo> +
GetDispatchInfo +
codec::Decode +
IsType<<Self as frame_system::Config>::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<<Self as frame_system::Config>::Call>;
/// Handler for rent payments.
type RentPayment: OnUnbalanced<NegativeImbalanceOf<Self>>;
@@ -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(),
}
}
+106 -12
View File
@@ -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<T>, Event<T>},
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<T>},
}
);
@@ -125,7 +127,7 @@ pub mod test_utils {
}
thread_local! {
static TEST_EXTENSION: sp_std::cell::RefCell<TestExtension> = Default::default();
static TEST_EXTENSION: RefCell<TestExtension> = 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<Test> = <Schedule<Test>>::default();
}
parameter_types! {
pub const TransactionByteFee: u64 = 0;
}
@@ -281,11 +285,32 @@ impl Convert<Weight, BalanceOf<Self>> for Test {
}
}
/// A filter whose filter function can be swapped at runtime.
pub struct TestFilter;
thread_local! {
static CALL_FILTER: RefCell<fn(&Call) -> bool> = RefCell::new(|_| true);
}
impl TestFilter {
pub fn set_filter(filter: fn(&Call) -> bool) {
CALL_FILTER.with(|fltr| *fltr.borrow_mut() = filter);
}
}
impl Filter<Call> 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::<Test>("call_return_code").unwrap();
fn gas_estimation_nested_call_fixed_limit() {
let (caller_code, caller_hash) = compile_module::<Test>("call_with_limit").unwrap();
let (callee_code, callee_hash) = compile_module::<Test>("dummy").unwrap();
ExtBuilder::default().existential_deposit(50).build().execute_with(|| {
let subsistence = Pallet::<Test>::subsistence_threshold();
@@ -2976,24 +3001,93 @@ fn gas_estimation_correct() {
);
let addr_callee = Contracts::contract_address(&ALICE, &callee_hash, &[1]);
let input: Vec<u8> = 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::<Test>("call_runtime").unwrap();
let (callee_code, callee_hash) = compile_module::<Test>("dummy").unwrap();
ExtBuilder::default().existential_deposit(50).build().execute_with(|| {
let subsistence = Pallet::<Test>::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);
});
+93 -6
View File
@@ -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<RestoreEntry>,
// (topics, data)
events: Vec<(Vec<H256>, Vec<u8>)>,
runtime_calls: RefCell<Vec<Call>>,
schedule: Schedule<Test>,
rent_params: RentParams<Test>,
gas_meter: GasMeter<Test>,
@@ -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: <Self::T as Config>::Call) -> DispatchResultWithPostInfo {
self.runtime_calls.borrow_mut().push(call);
Ok(Default::default())
}
}
fn execute<E: BorrowMut<MockExt>>(
@@ -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::<Test>::DecodingFailed.into(),
origin: ErrorOrigin::Caller,
})
);
assert_eq!(
*ext.runtime_calls.borrow(),
vec![],
);
}
}
+76 -1
View File
@@ -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, <E: Ext>,
// 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, <E: Ext>,
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: <E::T as Config>::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),
}
},
);
@@ -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;
@@ -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 = ();
+1 -1
View File
@@ -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 = ();
@@ -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 = ();
@@ -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;
+1 -1
View File
@@ -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 = ();
+1 -1
View File
@@ -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 = ();
+1 -1
View File
@@ -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;
+1 -1
View File
@@ -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 = ();
+1 -1
View File
@@ -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;
+1 -1
View File
@@ -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 = ();
+1 -1
View File
@@ -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 = ();
+1 -1
View File
@@ -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 = ();
+1 -1
View File
@@ -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 = ();
@@ -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;
+1 -1
View File
@@ -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 = ();
@@ -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 = ();
@@ -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 = ();
+1 -1
View File
@@ -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;
@@ -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 = ();
+1 -1
View File
@@ -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 = ();
+1 -1
View File
@@ -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 = ();
@@ -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 = ();
+1 -1
View File
@@ -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 = ();
+1 -1
View File
@@ -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 = ();
+1 -1
View File
@@ -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 = ();
+1 -1
View File
@@ -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;
+1 -1
View File
@@ -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 = ();
+1
View File
@@ -45,6 +45,7 @@ pub use validation::{
mod filter;
pub use filter::{
Filter, FilterStack, FilterStackGuard, ClearFilterGuard, InstanceFilter, IntegrityTest,
AllowAll, DenyAll,
};
mod misc;
+11 -1
View File
@@ -25,10 +25,20 @@ pub trait Filter<T> {
fn filter(_: &T) -> bool;
}
impl<T> Filter<T> for () {
/// A [`Filter`] that allows any value.
pub enum AllowAll {}
/// A [`Filter`] that denies any value.
pub enum DenyAll {}
impl<T> Filter<T> for AllowAll {
fn filter(_: &T) -> bool { true }
}
impl<T> Filter<T> for DenyAll {
fn filter(_: &T) -> bool { false }
}
/// Trait to add a constraint onto the filter.
pub trait FilterStack<T>: Filter<T> {
/// The type used to archive the stack.
@@ -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;
@@ -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;
@@ -158,7 +158,7 @@ pub type Block = generic::Block<Header, UncheckedExtrinsic>;
pub type UncheckedExtrinsic = generic::UncheckedExtrinsic<u32, Call, Signature, ()>;
impl system::Config for Runtime {
type BaseCallFilter = ();
type BaseCallFilter = frame_support::traits::AllowAll;
type Hash = H256;
type Origin = Origin;
type BlockNumber = BlockNumber;
+1 -1
View File
@@ -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;
@@ -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;
@@ -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;
@@ -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;
@@ -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;
@@ -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;
+1 -1
View File
@@ -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 = ();
@@ -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 = ();
+1 -1
View File
@@ -85,7 +85,7 @@ impl OnKilledAccount<u64> for RecordKilled {
}
impl Config for Test {
type BaseCallFilter = ();
type BaseCallFilter = frame_support::traits::AllowAll;
type BlockWeights = RuntimeBlockWeights;
type BlockLength = RuntimeBlockLength;
type Origin = Origin;
+1 -1
View File
@@ -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 = ();
+1 -1
View File
@@ -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 = ();
@@ -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 = ();
@@ -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;
+1 -1
View File
@@ -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 = ();
+1 -1
View File
@@ -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;
+1 -1
View File
@@ -48,7 +48,7 @@ parameter_types! {
impl frame_system::Config for Test {
type AccountData = pallet_balances::AccountData<u64>;
type AccountId = u64;
type BaseCallFilter = ();
type BaseCallFilter = frame_support::traits::AllowAll;
type BlockHashCount = BlockHashCount;
type BlockLength = ();
type BlockNumber = u64;
+1 -1
View File
@@ -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;