From 7faa277b68d38fdb02775019b223f741b4eba068 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Mon, 22 Aug 2022 17:35:56 +0200 Subject: [PATCH 001/100] Add safe-mode Signed-off-by: Oliver Tale-Yazdi --- Cargo.lock | 8 +++++++ Cargo.toml | 1 + frame/safe-mode/Cargo.toml | 34 +++++++++++++++++++++++++++++ frame/safe-mode/src/benchmarking.rs | 18 +++++++++++++++ frame/safe-mode/src/lib.rs | 24 ++++++++++++++++++++ frame/safe-mode/src/mock.rs | 18 +++++++++++++++ frame/safe-mode/src/tests.rs | 18 +++++++++++++++ 7 files changed, 121 insertions(+) create mode 100644 frame/safe-mode/Cargo.toml create mode 100644 frame/safe-mode/src/benchmarking.rs create mode 100644 frame/safe-mode/src/lib.rs create mode 100644 frame/safe-mode/src/mock.rs create mode 100644 frame/safe-mode/src/tests.rs diff --git a/Cargo.lock b/Cargo.lock index 060cd960ee985..a1b92199bb583 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6132,6 +6132,14 @@ dependencies = [ "sp-std", ] +[[package]] +name = "pallet-safe-mode" +version = "4.0.0-dev" +dependencies = [ + "frame-support", + "frame-system", +] + [[package]] name = "pallet-scheduler" version = "4.0.0-dev" diff --git a/Cargo.toml b/Cargo.toml index e2907716ca9f2..99d3299c009f1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -122,6 +122,7 @@ members = [ "frame/recovery", "frame/referenda", "frame/remark", + "frame/safe-mode", "frame/scheduler", "frame/scored-pool", "frame/session", diff --git a/frame/safe-mode/Cargo.toml b/frame/safe-mode/Cargo.toml new file mode 100644 index 0000000000000..641c9b305d5e3 --- /dev/null +++ b/frame/safe-mode/Cargo.toml @@ -0,0 +1,34 @@ +[package] +name = "pallet-safe-mode" +version = "4.0.0-dev" +authors = ["Parity Technologies "] +edition = "2021" +license = "Apache-2.0" +homepage = "https://substrate.io" +repository = "https://github.com/paritytech/substrate/" +description = "FRAME safe mode pallet" +readme = "README.md" + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } +frame-system = { version = "4.0.0-dev", default-features = false, path = "../system" } + +[dev-dependencies] + +[features] +default = ["std"] +std = [ + "frame-support/std", + "frame-system/std", +] +runtime-benchmarks = [ + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", +] +try-runtime = [ + "frame-support/try-runtime", + "frame-system/try-runtime", +] diff --git a/frame/safe-mode/src/benchmarking.rs b/frame/safe-mode/src/benchmarking.rs new file mode 100644 index 0000000000000..08f16881d7231 --- /dev/null +++ b/frame/safe-mode/src/benchmarking.rs @@ -0,0 +1,18 @@ +// This file is part of Substrate. + +// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![cfg(feature = "runtime-benchmarks")] diff --git a/frame/safe-mode/src/lib.rs b/frame/safe-mode/src/lib.rs new file mode 100644 index 0000000000000..6b0ff9f7cec43 --- /dev/null +++ b/frame/safe-mode/src/lib.rs @@ -0,0 +1,24 @@ +// This file is part of Substrate. + +// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Ensure we're `no_std` when compiling for Wasm. +#![cfg_attr(not(feature = "std"), no_std)] + +mod benchmarking; +mod mock; +mod tests; + diff --git a/frame/safe-mode/src/mock.rs b/frame/safe-mode/src/mock.rs new file mode 100644 index 0000000000000..95c25725a4f6d --- /dev/null +++ b/frame/safe-mode/src/mock.rs @@ -0,0 +1,18 @@ +// This file is part of Substrate. + +// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![cfg(any(test, feature = "runtime-benchmarks"))] diff --git a/frame/safe-mode/src/tests.rs b/frame/safe-mode/src/tests.rs new file mode 100644 index 0000000000000..ee33686e4a39f --- /dev/null +++ b/frame/safe-mode/src/tests.rs @@ -0,0 +1,18 @@ +// This file is part of Substrate. + +// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![cfg(test)] From e2f3efeabe6cf9a5a4d4f8b5fa109176a4fbfae8 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Tue, 23 Aug 2022 12:36:44 +0200 Subject: [PATCH 002/100] Update pallet Signed-off-by: Oliver Tale-Yazdi --- frame/safe-mode/Cargo.toml | 8 ++ frame/safe-mode/src/lib.rs | 195 +++++++++++++++++++++++++++++++++++++ 2 files changed, 203 insertions(+) diff --git a/frame/safe-mode/Cargo.toml b/frame/safe-mode/Cargo.toml index 641c9b305d5e3..7b6e8f9b7fdee 100644 --- a/frame/safe-mode/Cargo.toml +++ b/frame/safe-mode/Cargo.toml @@ -13,16 +13,24 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] +codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +scale-info = { version = "2.0.1", default-features = false, features = ["derive"] } + frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } frame-system = { version = "4.0.0-dev", default-features = false, path = "../system" } +sp-std = { version = "4.0.0", default-features = false, path = "../../primitives/std" } + [dev-dependencies] [features] default = ["std"] std = [ + "codec/std", + "scale-info/std", "frame-support/std", "frame-system/std", + "sp-std/std", ] runtime-benchmarks = [ "frame-support/runtime-benchmarks", diff --git a/frame/safe-mode/src/lib.rs b/frame/safe-mode/src/lib.rs index 6b0ff9f7cec43..06c0753952568 100644 --- a/frame/safe-mode/src/lib.rs +++ b/frame/safe-mode/src/lib.rs @@ -18,7 +18,202 @@ // Ensure we're `no_std` when compiling for Wasm. #![cfg_attr(not(feature = "std"), no_std)] +use frame_support::{ + pallet_prelude::*, + traits::{CallMetadata, Contains, GetCallMetadata}, +}; +use frame_system::pallet_prelude::OriginFor; +use sp_std::convert::TryInto; + mod benchmarking; mod mock; mod tests; +pub use pallet::*; + +pub(crate) type PalletNameOf = BoundedVec::MaxNameLen>; +pub(crate) type ExtrinsicNameOf = BoundedVec::MaxNameLen>; + +#[frame_support::pallet] +pub mod pallet { + use super::*; + + #[pallet::pallet] + #[pallet::generate_store(pub(super) trait Store)] + pub struct Pallet(PhantomData); + + #[pallet::config] + pub trait Config: frame_system::Config { + /// The overarching event type. + type Event: From> + IsType<::Event>; + + /// The only origin that can ban calls. + type BanOrigin: EnsureOrigin; + + /// The only origin that can un-ban calls. + type UnBanOrigin: EnsureOrigin; + + /// Pallets that are safe and can never be banned. + /// + /// The safe-mode pallet is assumed to be safe itself and does not need to be added here. + type SafePallets: Contains>; + + /// Maximum length for pallet- and extrinsic-names. + /// + /// Too long names will not be truncated but handled like + /// [`Self::BanTooLongNames`] specifies. + #[pallet::constant] + type MaxNameLen: Get; + + /// Defines if extrinsics and pallets with too long names should be treated as banned. + /// + /// `true` means that overflowing names will be handled as banned. + /// Setting this to `true` ensures that all extrinsics that + /// are callable, are also ban-able. + /// Otherwise there could be a situation where an extrinsic + /// is callable but not ban-able, which would could be exploited. + #[pallet::constant] + type BanTooLongNames: Get; + + // Weight information for extrinsics in this pallet. + //type WeightInfo: WeightInfo; + } + + #[pallet::error] + pub enum Error { + /// The call is banned. + IsBanned, + + /// The call is not banned. + IsNotBanned, + + /// The call is listed as safe and cannot be banned. + IsSafe, + } + + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event { + /// The call was banned. + CallBanned(PalletNameOf, ExtrinsicNameOf), + + /// The call was un-banned. + CallUnBanned(PalletNameOf, ExtrinsicNameOf), + } + + /// The set of calls that are explicitly banned. + #[pallet::storage] + pub type BannedCalls = + StorageMap<_, Blake2_128Concat, (PalletNameOf, ExtrinsicNameOf), (), OptionQuery>; + + #[pallet::call] + impl Pallet { + /// Ban a call. + /// + /// Can only be called by [`Config::BanOrigin`]. + /// Emits an [`Event::CallBanned`] event on success. + #[pallet::weight(0)] + pub fn ban_call( + origin: OriginFor, + pallet: PalletNameOf, + extrinsic: ExtrinsicNameOf, + ) -> DispatchResult { + T::BanOrigin::ensure_origin(origin)?; + + Self::ensure_can_ban(&pallet, &extrinsic)?; + BannedCalls::::insert((&pallet, &extrinsic), ()); + Self::deposit_event(Event::CallBanned(pallet, extrinsic)); + + Ok(()) + } + + /// Un-ban a call. + /// + /// Can only be called by [`Config::UnBanOrigin`]. + /// Emits an [`Event::CallUnBanned`] event on success. + #[pallet::weight(0)] + pub fn unban_call( + origin: OriginFor, + pallet: PalletNameOf, + extrinsic: ExtrinsicNameOf, + ) -> DispatchResult { + T::UnBanOrigin::ensure_origin(origin)?; + + Self::ensure_can_unban(&pallet, &extrinsic)?; + BannedCalls::::remove((&pallet, &extrinsic)); + Self::deposit_event(Event::CallUnBanned(pallet, extrinsic)); + + Ok(()) + } + } +} + +impl Pallet { + /// Return whether the call is banned. + pub fn is_banned_unbound(pallet: Vec, extrinsic: Vec) -> bool { + let pallet = PalletNameOf::::try_from(pallet); + let extrinsic = ExtrinsicNameOf::::try_from(extrinsic); + + match (pallet, extrinsic) { + (Ok(pallet), Ok(extrinsic)) => Self::is_banned(&pallet, &extrinsic), + _ => T::BanTooLongNames::get(), + } + } + + /// Return whether the call is banned. + pub fn is_banned(pallet: &PalletNameOf, extrinsic: &ExtrinsicNameOf) -> bool { + /* + TODO: These checks should be superfluous since we use `ensure_can_ban`. + // The safe-mode pallet is never banned. + if pallet.as_ref() == ::name().as_bytes().to_vec() { + return false + } + // Any `safe` pallet is never banned. + if T::SafePallets::contains(&pallet) { + return false + } + */ + >::contains_key((pallet, extrinsic)) + } + + /// Ensure that the call can be banned. + pub fn ensure_can_ban( + pallet: &PalletNameOf, + extrinsic: &ExtrinsicNameOf, + ) -> Result<(), Error> { + if pallet.as_ref() == ::name().as_bytes().to_vec() { + return Err(Error::::IsSafe) + } + if T::SafePallets::contains(&pallet) { + return Err(Error::::IsSafe) + } + if Self::is_banned(pallet, extrinsic) { + return Err(Error::::IsBanned) + } + Ok(()) + } + + /// Ensure that the call can be un-banned. + pub fn ensure_can_unban( + pallet: &PalletNameOf, + extrinsic: &ExtrinsicNameOf, + ) -> Result<(), Error> { + if Self::is_banned(pallet, extrinsic) { + // SAFETY: Everything that is banned, can be un-banned. + Ok(()) + } else { + Err(Error::IsNotBanned) + } + } +} + +impl Contains for Pallet +where + ::Call: GetCallMetadata, +{ + /// Returns whether the call is allowed to be called. + fn contains(call: &T::Call) -> bool { + let CallMetadata { pallet_name, function_name } = call.get_call_metadata(); + !Pallet::::is_banned_unbound(pallet_name.into(), function_name.into()) + } +} From 630cf4a26a611add834c4404b850d7c274a2e4bc Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Tue, 23 Aug 2022 12:37:03 +0200 Subject: [PATCH 003/100] Add to kitchensink-runtime Signed-off-by: Oliver Tale-Yazdi --- Cargo.lock | 4 ++++ bin/node/runtime/Cargo.toml | 4 ++++ bin/node/runtime/src/lib.rs | 22 ++++++++++++++++++---- 3 files changed, 26 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a1b92199bb583..fe7d6cfd38c15 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3376,6 +3376,7 @@ dependencies = [ "pallet-recovery", "pallet-referenda", "pallet-remark", + "pallet-safe-mode", "pallet-scheduler", "pallet-session", "pallet-session-benchmarking", @@ -6138,6 +6139,9 @@ version = "4.0.0-dev" dependencies = [ "frame-support", "frame-system", + "parity-scale-codec", + "scale-info", + "sp-std", ] [[package]] diff --git a/bin/node/runtime/Cargo.toml b/bin/node/runtime/Cargo.toml index 10b15b6ec554d..2f51d8d016b59 100644 --- a/bin/node/runtime/Cargo.toml +++ b/bin/node/runtime/Cargo.toml @@ -108,6 +108,7 @@ pallet-transaction-storage = { version = "4.0.0-dev", default-features = false, pallet-uniques = { version = "4.0.0-dev", default-features = false, path = "../../../frame/uniques" } pallet-vesting = { version = "4.0.0-dev", default-features = false, path = "../../../frame/vesting" } pallet-whitelist = { version = "4.0.0-dev", default-features = false, path = "../../../frame/whitelist" } +pallet-safe-mode = { version = "4.0.0-dev", default-features = false, path = "../../../frame/safe-mode" } [build-dependencies] substrate-wasm-builder = { version = "5.0.0-dev", path = "../../../utils/wasm-builder" } @@ -148,6 +149,7 @@ std = [ "pallet-nomination-pools/std", "pallet-nomination-pools-runtime-api/std", "pallet-identity/std", + "pallet-safe-mode/std", "pallet-scheduler/std", "node-primitives/std", "sp-offchain/std", @@ -229,6 +231,7 @@ runtime-benchmarks = [ "pallet-referenda/runtime-benchmarks", "pallet-recovery/runtime-benchmarks", "pallet-remark/runtime-benchmarks", + "pallet-safe-mode/runtime-benchmarks", "pallet-session-benchmarking", "pallet-society/runtime-benchmarks", "pallet-staking/runtime-benchmarks", @@ -278,6 +281,7 @@ try-runtime = [ "pallet-randomness-collective-flip/try-runtime", "pallet-recovery/try-runtime", "pallet-referenda/try-runtime", + "pallet-safe-mode/try-runtime", "pallet-scheduler/try-runtime", "pallet-session/try-runtime", "pallet-society/try-runtime", diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index ff793a49b5ce6..afedbc7b66366 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -31,9 +31,9 @@ use frame_support::{ pallet_prelude::Get, parameter_types, traits::{ - AsEnsureOriginWithArg, ConstU128, ConstU16, ConstU32, Currency, EitherOfDiverse, - EqualPrivilegeOnly, Everything, Imbalance, InstanceFilter, KeyOwnerProofSystem, - LockIdentifier, Nothing, OnUnbalanced, U128CurrencyToVote, + AsEnsureOriginWithArg, ConstBool, ConstU128, ConstU16, ConstU32, Currency, EitherOfDiverse, + EqualPrivilegeOnly, Imbalance, InstanceFilter, KeyOwnerProofSystem, LockIdentifier, + Nothing, OnUnbalanced, U128CurrencyToVote, }, weights::{ constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND}, @@ -199,8 +199,21 @@ parameter_types! { const_assert!(NORMAL_DISPATCH_RATIO.deconstruct() >= AVERAGE_ON_INITIALIZE_RATIO.deconstruct()); +impl pallet_safe_mode::Config for Runtime { + type Event = Event; + // TODO: add some safe pallet like governance + type SafePallets = Nothing; + type BanOrigin = EnsureRoot; + type UnBanOrigin = EnsureRoot; + type MaxNameLen = ConstU32<256>; + type BanTooLongNames = ConstBool; +} + impl frame_system::Config for Runtime { - type BaseCallFilter = Everything; + // Directly using the `SafeMode` pallet here implies that there is no general + // filter and everything that is not banned, is allowed. + // Otherwise you can compose them: `TheseExcept`. + type BaseCallFilter = SafeMode; type BlockWeights = RuntimeBlockWeights; type BlockLength = RuntimeBlockLength; type DbWeight = RocksDbWeight; @@ -1641,6 +1654,7 @@ construct_runtime!( NominationPools: pallet_nomination_pools, RankedPolls: pallet_referenda::, RankedCollective: pallet_ranked_collective, + SafeMode: pallet_safe_mode, } ); From e29a2ea8b513f4bb7c2a6335995ab37c34570e51 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Tue, 23 Aug 2022 12:49:15 +0200 Subject: [PATCH 004/100] Spelling Signed-off-by: Oliver Tale-Yazdi --- bin/node/runtime/src/lib.rs | 4 ++-- frame/safe-mode/Cargo.toml | 4 +--- frame/safe-mode/src/lib.rs | 46 ++++++++++++------------------------- 3 files changed, 18 insertions(+), 36 deletions(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index afedbc7b66366..a8f35066c5ed0 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -201,10 +201,10 @@ const_assert!(NORMAL_DISPATCH_RATIO.deconstruct() >= AVERAGE_ON_INITIALIZE_RATIO impl pallet_safe_mode::Config for Runtime { type Event = Event; - // TODO: add some safe pallet like governance + // TODO: add some safe pallets like governance. type SafePallets = Nothing; type BanOrigin = EnsureRoot; - type UnBanOrigin = EnsureRoot; + type UnbanOrigin = EnsureRoot; type MaxNameLen = ConstU32<256>; type BanTooLongNames = ConstBool; } diff --git a/frame/safe-mode/Cargo.toml b/frame/safe-mode/Cargo.toml index 7b6e8f9b7fdee..7a763030900e0 100644 --- a/frame/safe-mode/Cargo.toml +++ b/frame/safe-mode/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" license = "Apache-2.0" homepage = "https://substrate.io" repository = "https://github.com/paritytech/substrate/" -description = "FRAME safe mode pallet" +description = "FRAME safe-mode pallet" readme = "README.md" [package.metadata.docs.rs] @@ -15,10 +15,8 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } scale-info = { version = "2.0.1", default-features = false, features = ["derive"] } - frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } frame-system = { version = "4.0.0-dev", default-features = false, path = "../system" } - sp-std = { version = "4.0.0", default-features = false, path = "../../primitives/std" } [dev-dependencies] diff --git a/frame/safe-mode/src/lib.rs b/frame/safe-mode/src/lib.rs index 06c0753952568..5606cca8f0b93 100644 --- a/frame/safe-mode/src/lib.rs +++ b/frame/safe-mode/src/lib.rs @@ -15,15 +15,14 @@ // See the License for the specific language governing permissions and // limitations under the License. -// Ensure we're `no_std` when compiling for Wasm. #![cfg_attr(not(feature = "std"), no_std)] use frame_support::{ pallet_prelude::*, traits::{CallMetadata, Contains, GetCallMetadata}, }; -use frame_system::pallet_prelude::OriginFor; -use sp_std::convert::TryInto; +use frame_system::pallet_prelude::*; +use sp_std::{convert::TryInto, prelude::*}; mod benchmarking; mod mock; @@ -51,11 +50,11 @@ pub mod pallet { type BanOrigin: EnsureOrigin; /// The only origin that can un-ban calls. - type UnBanOrigin: EnsureOrigin; + type UnbanOrigin: EnsureOrigin; /// Pallets that are safe and can never be banned. /// - /// The safe-mode pallet is assumed to be safe itself and does not need to be added here. + /// The safe-mode pallet is always assumed to be safe itself. type SafePallets: Contains>; /// Maximum length for pallet- and extrinsic-names. @@ -65,12 +64,11 @@ pub mod pallet { #[pallet::constant] type MaxNameLen: Get; - /// Defines if extrinsics and pallets with too long names should be treated as banned. + /// Specifies if extrinsics and pallets with too long names should be treated as banned. /// - /// `true` means that overflowing names will be handled as banned. - /// Setting this to `true` ensures that all extrinsics that + /// Setting this to `true` ensures that all calls that /// are callable, are also ban-able. - /// Otherwise there could be a situation where an extrinsic + /// Otherwise there could be a situation where a call /// is callable but not ban-able, which would could be exploited. #[pallet::constant] type BanTooLongNames: Get; @@ -83,22 +81,19 @@ pub mod pallet { pub enum Error { /// The call is banned. IsBanned, - /// The call is not banned. IsNotBanned, - - /// The call is listed as safe and cannot be banned. + /// The call is listed as safe. IsSafe, } #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event { - /// The call was banned. + /// This call got banned. CallBanned(PalletNameOf, ExtrinsicNameOf), - - /// The call was un-banned. - CallUnBanned(PalletNameOf, ExtrinsicNameOf), + /// This call got un-banned. + CallUnbanned(PalletNameOf, ExtrinsicNameOf), } /// The set of calls that are explicitly banned. @@ -129,19 +124,19 @@ pub mod pallet { /// Un-ban a call. /// - /// Can only be called by [`Config::UnBanOrigin`]. - /// Emits an [`Event::CallUnBanned`] event on success. + /// Can only be called by [`Config::UnbanOrigin`]. + /// Emits an [`Event::CallUnbanned`] event on success. #[pallet::weight(0)] pub fn unban_call( origin: OriginFor, pallet: PalletNameOf, extrinsic: ExtrinsicNameOf, ) -> DispatchResult { - T::UnBanOrigin::ensure_origin(origin)?; + T::UnbanOrigin::ensure_origin(origin)?; Self::ensure_can_unban(&pallet, &extrinsic)?; BannedCalls::::remove((&pallet, &extrinsic)); - Self::deposit_event(Event::CallUnBanned(pallet, extrinsic)); + Self::deposit_event(Event::CallUnbanned(pallet, extrinsic)); Ok(()) } @@ -162,17 +157,6 @@ impl Pallet { /// Return whether the call is banned. pub fn is_banned(pallet: &PalletNameOf, extrinsic: &ExtrinsicNameOf) -> bool { - /* - TODO: These checks should be superfluous since we use `ensure_can_ban`. - // The safe-mode pallet is never banned. - if pallet.as_ref() == ::name().as_bytes().to_vec() { - return false - } - // Any `safe` pallet is never banned. - if T::SafePallets::contains(&pallet) { - return false - } - */ >::contains_key((pallet, extrinsic)) } From 50a5ddd293e05e5e36a34104e7ab5cb76bc5e0d8 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Tue, 23 Aug 2022 17:07:52 +0200 Subject: [PATCH 005/100] Rename to tx-pause Signed-off-by: Oliver Tale-Yazdi --- Cargo.lock | 24 ++-- Cargo.toml | 2 +- bin/node/runtime/Cargo.toml | 8 +- bin/node/runtime/src/lib.rs | 12 +- frame/{safe-mode => tx-pause}/Cargo.toml | 4 +- .../src/benchmarking.rs | 0 frame/{safe-mode => tx-pause}/src/lib.rs | 108 +++++++++--------- frame/{safe-mode => tx-pause}/src/mock.rs | 0 frame/{safe-mode => tx-pause}/src/tests.rs | 0 9 files changed, 79 insertions(+), 79 deletions(-) rename frame/{safe-mode => tx-pause}/Cargo.toml (94%) rename frame/{safe-mode => tx-pause}/src/benchmarking.rs (100%) rename frame/{safe-mode => tx-pause}/src/lib.rs (60%) rename frame/{safe-mode => tx-pause}/src/mock.rs (100%) rename frame/{safe-mode => tx-pause}/src/tests.rs (100%) diff --git a/Cargo.lock b/Cargo.lock index fe7d6cfd38c15..f9b85545ff227 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3376,7 +3376,6 @@ dependencies = [ "pallet-recovery", "pallet-referenda", "pallet-remark", - "pallet-safe-mode", "pallet-scheduler", "pallet-session", "pallet-session-benchmarking", @@ -3391,6 +3390,7 @@ dependencies = [ "pallet-transaction-payment-rpc-runtime-api", "pallet-transaction-storage", "pallet-treasury", + "pallet-tx-pause", "pallet-uniques", "pallet-utility", "pallet-vesting", @@ -6133,17 +6133,6 @@ dependencies = [ "sp-std", ] -[[package]] -name = "pallet-safe-mode" -version = "4.0.0-dev" -dependencies = [ - "frame-support", - "frame-system", - "parity-scale-codec", - "scale-info", - "sp-std", -] - [[package]] name = "pallet-scheduler" version = "4.0.0-dev" @@ -6457,6 +6446,17 @@ dependencies = [ "sp-std", ] +[[package]] +name = "pallet-tx-pause" +version = "4.0.0-dev" +dependencies = [ + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-std", +] + [[package]] name = "pallet-uniques" version = "4.0.0-dev" diff --git a/Cargo.toml b/Cargo.toml index 99d3299c009f1..6aaf35b65c4b7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -122,7 +122,7 @@ members = [ "frame/recovery", "frame/referenda", "frame/remark", - "frame/safe-mode", + "frame/tx-pause", "frame/scheduler", "frame/scored-pool", "frame/session", diff --git a/bin/node/runtime/Cargo.toml b/bin/node/runtime/Cargo.toml index 2f51d8d016b59..51c098600087f 100644 --- a/bin/node/runtime/Cargo.toml +++ b/bin/node/runtime/Cargo.toml @@ -108,7 +108,7 @@ pallet-transaction-storage = { version = "4.0.0-dev", default-features = false, pallet-uniques = { version = "4.0.0-dev", default-features = false, path = "../../../frame/uniques" } pallet-vesting = { version = "4.0.0-dev", default-features = false, path = "../../../frame/vesting" } pallet-whitelist = { version = "4.0.0-dev", default-features = false, path = "../../../frame/whitelist" } -pallet-safe-mode = { version = "4.0.0-dev", default-features = false, path = "../../../frame/safe-mode" } +pallet-tx-pause = { version = "4.0.0-dev", default-features = false, path = "../../../frame/tx-pause" } [build-dependencies] substrate-wasm-builder = { version = "5.0.0-dev", path = "../../../utils/wasm-builder" } @@ -149,7 +149,7 @@ std = [ "pallet-nomination-pools/std", "pallet-nomination-pools-runtime-api/std", "pallet-identity/std", - "pallet-safe-mode/std", + "pallet-tx-pause/std", "pallet-scheduler/std", "node-primitives/std", "sp-offchain/std", @@ -231,7 +231,7 @@ runtime-benchmarks = [ "pallet-referenda/runtime-benchmarks", "pallet-recovery/runtime-benchmarks", "pallet-remark/runtime-benchmarks", - "pallet-safe-mode/runtime-benchmarks", + "pallet-tx-pause/runtime-benchmarks", "pallet-session-benchmarking", "pallet-society/runtime-benchmarks", "pallet-staking/runtime-benchmarks", @@ -281,7 +281,7 @@ try-runtime = [ "pallet-randomness-collective-flip/try-runtime", "pallet-recovery/try-runtime", "pallet-referenda/try-runtime", - "pallet-safe-mode/try-runtime", + "pallet-tx-pause/try-runtime", "pallet-scheduler/try-runtime", "pallet-session/try-runtime", "pallet-society/try-runtime", diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index a8f35066c5ed0..966cc5c2a9eb6 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -199,21 +199,21 @@ parameter_types! { const_assert!(NORMAL_DISPATCH_RATIO.deconstruct() >= AVERAGE_ON_INITIALIZE_RATIO.deconstruct()); -impl pallet_safe_mode::Config for Runtime { +impl pallet_tx_pause::Config for Runtime { type Event = Event; // TODO: add some safe pallets like governance. type SafePallets = Nothing; type BanOrigin = EnsureRoot; - type UnbanOrigin = EnsureRoot; + type UnpauseOrigin = EnsureRoot; type MaxNameLen = ConstU32<256>; type BanTooLongNames = ConstBool; } impl frame_system::Config for Runtime { - // Directly using the `SafeMode` pallet here implies that there is no general + // Directly using the `TxPause` pallet here implies that there is no general // filter and everything that is not banned, is allowed. - // Otherwise you can compose them: `TheseExcept`. - type BaseCallFilter = SafeMode; + // Otherwise you can compose them: `TheseExcept`. + type BaseCallFilter = TxPause; type BlockWeights = RuntimeBlockWeights; type BlockLength = RuntimeBlockLength; type DbWeight = RocksDbWeight; @@ -1654,7 +1654,7 @@ construct_runtime!( NominationPools: pallet_nomination_pools, RankedPolls: pallet_referenda::, RankedCollective: pallet_ranked_collective, - SafeMode: pallet_safe_mode, + TxPause: pallet_tx_pause, } ); diff --git a/frame/safe-mode/Cargo.toml b/frame/tx-pause/Cargo.toml similarity index 94% rename from frame/safe-mode/Cargo.toml rename to frame/tx-pause/Cargo.toml index 7a763030900e0..0e0a0e969f7ea 100644 --- a/frame/safe-mode/Cargo.toml +++ b/frame/tx-pause/Cargo.toml @@ -1,12 +1,12 @@ [package] -name = "pallet-safe-mode" +name = "pallet-tx-pause" version = "4.0.0-dev" authors = ["Parity Technologies "] edition = "2021" license = "Apache-2.0" homepage = "https://substrate.io" repository = "https://github.com/paritytech/substrate/" -description = "FRAME safe-mode pallet" +description = "FRAME tx-pause pallet" readme = "README.md" [package.metadata.docs.rs] diff --git a/frame/safe-mode/src/benchmarking.rs b/frame/tx-pause/src/benchmarking.rs similarity index 100% rename from frame/safe-mode/src/benchmarking.rs rename to frame/tx-pause/src/benchmarking.rs diff --git a/frame/safe-mode/src/lib.rs b/frame/tx-pause/src/lib.rs similarity index 60% rename from frame/safe-mode/src/lib.rs rename to frame/tx-pause/src/lib.rs index 5606cca8f0b93..139761d3204c5 100644 --- a/frame/safe-mode/src/lib.rs +++ b/frame/tx-pause/src/lib.rs @@ -46,32 +46,32 @@ pub mod pallet { /// The overarching event type. type Event: From> + IsType<::Event>; - /// The only origin that can ban calls. - type BanOrigin: EnsureOrigin; + /// The only origin that can pause calls. + type PauseOrigin: EnsureOrigin; - /// The only origin that can un-ban calls. - type UnbanOrigin: EnsureOrigin; + /// The only origin that can un-pause calls. + type UnpauseOrigin: EnsureOrigin; - /// Pallets that are safe and can never be banned. + /// Pallets that are safe and can never be paused. /// - /// The safe-mode pallet is always assumed to be safe itself. + /// The tx-pause pallet is always assumed to be safe itself. type SafePallets: Contains>; /// Maximum length for pallet- and extrinsic-names. /// /// Too long names will not be truncated but handled like - /// [`Self::BanTooLongNames`] specifies. + /// [`Self::PauseTooLongNames`] specifies. #[pallet::constant] type MaxNameLen: Get; - /// Specifies if extrinsics and pallets with too long names should be treated as banned. + /// Specifies if extrinsics and pallets with too long names should be treated as paused. /// /// Setting this to `true` ensures that all calls that - /// are callable, are also ban-able. + /// are callable, are also pause-able. /// Otherwise there could be a situation where a call - /// is callable but not ban-able, which would could be exploited. + /// is callable but not pause-able, which would could be exploited. #[pallet::constant] - type BanTooLongNames: Get; + type PauseTooLongNames: Get; // Weight information for extrinsics in this pallet. //type WeightInfo: WeightInfo; @@ -79,10 +79,10 @@ pub mod pallet { #[pallet::error] pub enum Error { - /// The call is banned. - IsBanned, - /// The call is not banned. - IsNotBanned, + /// The call is paused. + IsPaused, + /// The call is not paused. + IsNotPaused, /// The call is listed as safe. IsSafe, } @@ -90,53 +90,53 @@ pub mod pallet { #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event { - /// This call got banned. - CallBanned(PalletNameOf, ExtrinsicNameOf), - /// This call got un-banned. - CallUnbanned(PalletNameOf, ExtrinsicNameOf), + /// This call got paused. + CallPaused(PalletNameOf, ExtrinsicNameOf), + /// This call got un-paused. + CallUnpaused(PalletNameOf, ExtrinsicNameOf), } - /// The set of calls that are explicitly banned. + /// The set of calls that are explicitly paused. #[pallet::storage] - pub type BannedCalls = + pub type PausedCalls = StorageMap<_, Blake2_128Concat, (PalletNameOf, ExtrinsicNameOf), (), OptionQuery>; #[pallet::call] impl Pallet { - /// Ban a call. + /// Pause a call. /// - /// Can only be called by [`Config::BanOrigin`]. - /// Emits an [`Event::CallBanned`] event on success. + /// Can only be called by [`Config::PauseOrigin`]. + /// Emits an [`Event::CallPaused`] event on success. #[pallet::weight(0)] - pub fn ban_call( + pub fn pause_call( origin: OriginFor, pallet: PalletNameOf, extrinsic: ExtrinsicNameOf, ) -> DispatchResult { - T::BanOrigin::ensure_origin(origin)?; + T::PauseOrigin::ensure_origin(origin)?; - Self::ensure_can_ban(&pallet, &extrinsic)?; - BannedCalls::::insert((&pallet, &extrinsic), ()); - Self::deposit_event(Event::CallBanned(pallet, extrinsic)); + Self::ensure_can_pause(&pallet, &extrinsic)?; + PausedCalls::::insert((&pallet, &extrinsic), ()); + Self::deposit_event(Event::CallPaused(pallet, extrinsic)); Ok(()) } - /// Un-ban a call. + /// Un-pause a call. /// - /// Can only be called by [`Config::UnbanOrigin`]. - /// Emits an [`Event::CallUnbanned`] event on success. + /// Can only be called by [`Config::UnpauseOrigin`]. + /// Emits an [`Event::CallUnpaused`] event on success. #[pallet::weight(0)] - pub fn unban_call( + pub fn unpause_call( origin: OriginFor, pallet: PalletNameOf, extrinsic: ExtrinsicNameOf, ) -> DispatchResult { - T::UnbanOrigin::ensure_origin(origin)?; + T::UnpauseOrigin::ensure_origin(origin)?; - Self::ensure_can_unban(&pallet, &extrinsic)?; - BannedCalls::::remove((&pallet, &extrinsic)); - Self::deposit_event(Event::CallUnbanned(pallet, extrinsic)); + Self::ensure_can_unpause(&pallet, &extrinsic)?; + PausedCalls::::remove((&pallet, &extrinsic)); + Self::deposit_event(Event::CallUnpaused(pallet, extrinsic)); Ok(()) } @@ -144,24 +144,24 @@ pub mod pallet { } impl Pallet { - /// Return whether the call is banned. - pub fn is_banned_unbound(pallet: Vec, extrinsic: Vec) -> bool { + /// Return whether the call is paused. + pub fn is_paused_unbound(pallet: Vec, extrinsic: Vec) -> bool { let pallet = PalletNameOf::::try_from(pallet); let extrinsic = ExtrinsicNameOf::::try_from(extrinsic); match (pallet, extrinsic) { - (Ok(pallet), Ok(extrinsic)) => Self::is_banned(&pallet, &extrinsic), - _ => T::BanTooLongNames::get(), + (Ok(pallet), Ok(extrinsic)) => Self::is_paused(&pallet, &extrinsic), + _ => T::PauseTooLongNames::get(), } } - /// Return whether the call is banned. - pub fn is_banned(pallet: &PalletNameOf, extrinsic: &ExtrinsicNameOf) -> bool { - >::contains_key((pallet, extrinsic)) + /// Return whether the call is paused. + pub fn is_paused(pallet: &PalletNameOf, extrinsic: &ExtrinsicNameOf) -> bool { + >::contains_key((pallet, extrinsic)) } - /// Ensure that the call can be banned. - pub fn ensure_can_ban( + /// Ensure that the call can be paused. + pub fn ensure_can_pause( pallet: &PalletNameOf, extrinsic: &ExtrinsicNameOf, ) -> Result<(), Error> { @@ -171,22 +171,22 @@ impl Pallet { if T::SafePallets::contains(&pallet) { return Err(Error::::IsSafe) } - if Self::is_banned(pallet, extrinsic) { - return Err(Error::::IsBanned) + if Self::is_paused(pallet, extrinsic) { + return Err(Error::::IsPaused) } Ok(()) } - /// Ensure that the call can be un-banned. - pub fn ensure_can_unban( + /// Ensure that the call can be un-paused. + pub fn ensure_can_unpause( pallet: &PalletNameOf, extrinsic: &ExtrinsicNameOf, ) -> Result<(), Error> { - if Self::is_banned(pallet, extrinsic) { - // SAFETY: Everything that is banned, can be un-banned. + if Self::is_paused(pallet, extrinsic) { + // SAFETY: Everything that is paused, can be un-paused. Ok(()) } else { - Err(Error::IsNotBanned) + Err(Error::IsNotPaused) } } } @@ -198,6 +198,6 @@ where /// Returns whether the call is allowed to be called. fn contains(call: &T::Call) -> bool { let CallMetadata { pallet_name, function_name } = call.get_call_metadata(); - !Pallet::::is_banned_unbound(pallet_name.into(), function_name.into()) + !Pallet::::is_paused_unbound(pallet_name.into(), function_name.into()) } } diff --git a/frame/safe-mode/src/mock.rs b/frame/tx-pause/src/mock.rs similarity index 100% rename from frame/safe-mode/src/mock.rs rename to frame/tx-pause/src/mock.rs diff --git a/frame/safe-mode/src/tests.rs b/frame/tx-pause/src/tests.rs similarity index 100% rename from frame/safe-mode/src/tests.rs rename to frame/tx-pause/src/tests.rs From 00955c1874ca564751aa557db74f189b7be4e24b Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Thu, 25 Aug 2022 14:21:03 +0200 Subject: [PATCH 006/100] Add SafeMode pallet Signed-off-by: Oliver Tale-Yazdi --- Cargo.lock | 13 ++ Cargo.toml | 1 + bin/node/runtime/Cargo.toml | 6 +- bin/node/runtime/src/lib.rs | 39 ++++-- frame/safe-mode/Cargo.toml | 42 ++++++ frame/safe-mode/src/benchmarking.rs | 18 +++ frame/safe-mode/src/lib.rs | 203 ++++++++++++++++++++++++++++ frame/safe-mode/src/mock.rs | 18 +++ frame/safe-mode/src/tests.rs | 18 +++ frame/tx-pause/Cargo.toml | 2 +- frame/tx-pause/src/lib.rs | 37 ++--- 11 files changed, 367 insertions(+), 30 deletions(-) create mode 100644 frame/safe-mode/Cargo.toml create mode 100644 frame/safe-mode/src/benchmarking.rs create mode 100644 frame/safe-mode/src/lib.rs create mode 100644 frame/safe-mode/src/mock.rs create mode 100644 frame/safe-mode/src/tests.rs diff --git a/Cargo.lock b/Cargo.lock index f9b85545ff227..399e48c60b256 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3376,6 +3376,7 @@ dependencies = [ "pallet-recovery", "pallet-referenda", "pallet-remark", + "pallet-safe-mode", "pallet-scheduler", "pallet-session", "pallet-session-benchmarking", @@ -6133,6 +6134,18 @@ dependencies = [ "sp-std", ] +[[package]] +name = "pallet-safe-mode" +version = "4.0.0-dev" +dependencies = [ + "frame-support", + "frame-system", + "parity-scale-codec", + "scale-info", + "sp-runtime", + "sp-std", +] + [[package]] name = "pallet-scheduler" version = "4.0.0-dev" diff --git a/Cargo.toml b/Cargo.toml index 6aaf35b65c4b7..94e229419283f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -123,6 +123,7 @@ members = [ "frame/referenda", "frame/remark", "frame/tx-pause", + "frame/safe-mode", "frame/scheduler", "frame/scored-pool", "frame/session", diff --git a/bin/node/runtime/Cargo.toml b/bin/node/runtime/Cargo.toml index 51c098600087f..9d884ad115353 100644 --- a/bin/node/runtime/Cargo.toml +++ b/bin/node/runtime/Cargo.toml @@ -109,6 +109,7 @@ pallet-uniques = { version = "4.0.0-dev", default-features = false, path = "../. pallet-vesting = { version = "4.0.0-dev", default-features = false, path = "../../../frame/vesting" } pallet-whitelist = { version = "4.0.0-dev", default-features = false, path = "../../../frame/whitelist" } pallet-tx-pause = { version = "4.0.0-dev", default-features = false, path = "../../../frame/tx-pause" } +pallet-safe-mode = { version = "4.0.0-dev", default-features = false, path = "../../../frame/safe-mode" } [build-dependencies] substrate-wasm-builder = { version = "5.0.0-dev", path = "../../../utils/wasm-builder" } @@ -150,6 +151,7 @@ std = [ "pallet-nomination-pools-runtime-api/std", "pallet-identity/std", "pallet-tx-pause/std", + "pallet-safe-mode/std", "pallet-scheduler/std", "node-primitives/std", "sp-offchain/std", @@ -232,6 +234,7 @@ runtime-benchmarks = [ "pallet-recovery/runtime-benchmarks", "pallet-remark/runtime-benchmarks", "pallet-tx-pause/runtime-benchmarks", + "pallet-safe-mode/runtime-benchmarks", "pallet-session-benchmarking", "pallet-society/runtime-benchmarks", "pallet-staking/runtime-benchmarks", @@ -281,7 +284,6 @@ try-runtime = [ "pallet-randomness-collective-flip/try-runtime", "pallet-recovery/try-runtime", "pallet-referenda/try-runtime", - "pallet-tx-pause/try-runtime", "pallet-scheduler/try-runtime", "pallet-session/try-runtime", "pallet-society/try-runtime", @@ -290,6 +292,8 @@ try-runtime = [ "pallet-sudo/try-runtime", "pallet-timestamp/try-runtime", "pallet-tips/try-runtime", + "pallet-tx-pause/try-runtime", + "pallet-safe-mode/try-runtime", "pallet-transaction-payment/try-runtime", "pallet-treasury/try-runtime", "pallet-uniques/try-runtime", diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 966cc5c2a9eb6..faac5b830af66 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -31,9 +31,9 @@ use frame_support::{ pallet_prelude::Get, parameter_types, traits::{ - AsEnsureOriginWithArg, ConstBool, ConstU128, ConstU16, ConstU32, Currency, EitherOfDiverse, - EqualPrivilegeOnly, Imbalance, InstanceFilter, KeyOwnerProofSystem, LockIdentifier, - Nothing, OnUnbalanced, U128CurrencyToVote, + AsEnsureOriginWithArg, ConstBool, ConstU128, ConstU16, ConstU32, Contains, Currency, InsideBoth, + EitherOfDiverse, EqualPrivilegeOnly, Imbalance, InstanceFilter, KeyOwnerProofSystem, + LockIdentifier, Nothing, OnUnbalanced, PalletInfoAccess, U128CurrencyToVote, }, weights::{ constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND}, @@ -199,21 +199,37 @@ parameter_types! { const_assert!(NORMAL_DISPATCH_RATIO.deconstruct() >= AVERAGE_ON_INITIALIZE_RATIO.deconstruct()); +pub struct UnpausablePallets(); +impl Contains> for UnpausablePallets { + fn contains(pallet: &pallet_tx_pause::PalletNameOf) -> bool { + pallet.as_ref() == + as PalletInfoAccess>::name() + .as_bytes() + .to_vec() + } +} + impl pallet_tx_pause::Config for Runtime { type Event = Event; - // TODO: add some safe pallets like governance. - type SafePallets = Nothing; - type BanOrigin = EnsureRoot; + type UnpausablePallets = UnpausablePallets; + type PauseOrigin = EnsureRoot; type UnpauseOrigin = EnsureRoot; type MaxNameLen = ConstU32<256>; - type BanTooLongNames = ConstBool; + type PauseTooLongNames = ConstBool; +} + +impl pallet_safe_mode::Config for Runtime { + type Event = Event; + type SafeModeFilter = Nothing; // TODO add TxPause pallet + type EnableDuration = ConstU32<{ 2 * DAYS }>; + type ExtendDuration = ConstU32<{ 1 * DAYS }>; + type EnableOrigin = EnsureRoot; + type ExtendOrigin = EnsureRoot; + type PreemptiveDisableOrigin = EnsureRoot; } impl frame_system::Config for Runtime { - // Directly using the `TxPause` pallet here implies that there is no general - // filter and everything that is not banned, is allowed. - // Otherwise you can compose them: `TheseExcept`. - type BaseCallFilter = TxPause; + type BaseCallFilter = InsideBoth; type BlockWeights = RuntimeBlockWeights; type BlockLength = RuntimeBlockLength; type DbWeight = RocksDbWeight; @@ -1655,6 +1671,7 @@ construct_runtime!( RankedPolls: pallet_referenda::, RankedCollective: pallet_ranked_collective, TxPause: pallet_tx_pause, + SafeMode: pallet_safe_mode, } ); diff --git a/frame/safe-mode/Cargo.toml b/frame/safe-mode/Cargo.toml new file mode 100644 index 0000000000000..7ad7e83e1caf8 --- /dev/null +++ b/frame/safe-mode/Cargo.toml @@ -0,0 +1,42 @@ +[package] +name = "pallet-safe-mode" +version = "4.0.0-dev" +authors = ["Parity Technologies "] +edition = "2021" +license = "Apache-2.0" +homepage = "https://substrate.io" +repository = "https://github.com/paritytech/substrate/" +description = "FRAME safe-mode pallet" +readme = "README.md" + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +scale-info = { version = "2.0.1", default-features = false, features = ["derive"] } +frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } +frame-system = { version = "4.0.0-dev", default-features = false, path = "../system" } +sp-runtime = { version = "6.0.0", default-features = false, path = "../../primitives/runtime" } +sp-std = { version = "4.0.0", default-features = false, path = "../../primitives/std" } + +[dev-dependencies] + +[features] +default = ["std"] +std = [ + "codec/std", + "scale-info/std", + "frame-support/std", + "frame-system/std", + "sp-runtime/std", + "sp-std/std", +] +runtime-benchmarks = [ + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", +] +try-runtime = [ + "frame-support/try-runtime", + "frame-system/try-runtime", +] diff --git a/frame/safe-mode/src/benchmarking.rs b/frame/safe-mode/src/benchmarking.rs new file mode 100644 index 0000000000000..08f16881d7231 --- /dev/null +++ b/frame/safe-mode/src/benchmarking.rs @@ -0,0 +1,18 @@ +// This file is part of Substrate. + +// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![cfg(feature = "runtime-benchmarks")] diff --git a/frame/safe-mode/src/lib.rs b/frame/safe-mode/src/lib.rs new file mode 100644 index 0000000000000..0638031e77c21 --- /dev/null +++ b/frame/safe-mode/src/lib.rs @@ -0,0 +1,203 @@ +// This file is part of Substrate. + +// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![cfg_attr(not(feature = "std"), no_std)] + +use frame_support::{ + pallet_prelude::*, + traits::{CallMetadata, Contains, GetCallMetadata, PalletInfoAccess}, +}; +use frame_system::pallet_prelude::*; +use sp_runtime::traits::Saturating; +use sp_std::{convert::TryInto, prelude::*}; + +mod benchmarking; +mod mock; +mod tests; + +pub use pallet::*; + +#[frame_support::pallet] +pub mod pallet { + use super::*; + + #[pallet::pallet] + #[pallet::generate_store(pub(super) trait Store)] + pub struct Pallet(PhantomData); + + #[pallet::config] + pub trait Config: frame_system::Config { + /// The overarching event type. + type Event: From> + IsType<::Event>; + + /// Contains all calls that can be dispatched when the safe-mode is enabled. + /// + /// The `SafeMode` pallet is always included and does not need to be added. + type SafeModeFilter: Contains; + + /// How long the safe-mode will stay active when enabled with [`Pallet::enable`]. + #[pallet::constant] + type EnableDuration: Get; + + /// How much the safe-mode can be extended by each [`Pallet::extend`] call. + /// + /// This does not impose a hard limit as the safe-mode can be extended multiple times. + #[pallet::constant] + type ExtendDuration: Get; + + type EnableOrigin: EnsureOrigin; + type ExtendOrigin: EnsureOrigin; + type PreemptiveDisableOrigin: EnsureOrigin; + + // Weight information for extrinsics in this pallet. + //type WeightInfo: WeightInfo; + } + + #[pallet::error] + pub enum Error { + /// The safe-mode is (already) enabled. + IsEnabled, + + /// The safe-mode is (already) disabled. + IsDisabled, + + /// No permission to disable the safe-mode (yet). + NoPermission, + } + + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event { + /// The safe-mode was enabled until inclusively this block. + Enabled(T::BlockNumber), + + /// The safe-mode was extended until inclusively this block. + Extended(T::BlockNumber), + + /// The safe-mode was disabled. + Disabled, + } + + /// Contains the last block number that the safe-mode will stay enabled. + /// + /// This is set to `None` if the safe-mode is disabled. + /// In any block after this block number is reached, + /// the safe-mode can be disabled permissionlessly by any-one. + #[pallet::storage] + pub type Enabled = StorageValue<_, T::BlockNumber, OptionQuery>; + + #[pallet::call] + impl Pallet { + /// Enable the safe-mode. + #[pallet::weight(0)] + pub fn enable(origin: OriginFor) -> DispatchResult { + T::EnableOrigin::ensure_origin(origin)?; + + ensure!(!Enabled::::exists(), Error::::IsEnabled); + let limit = + >::block_number().saturating_add(T::EnableDuration::get()); + Enabled::::put(limit); + Self::deposit_event(Event::Enabled(limit)); + + Ok(()) + } + + /// Extends the safe-mode duration. + /// + /// Can only be called by the [`Config::ExtendOrigin`] origin. + /// The safe-mode must already be enabled for this to succeed. + /// + /// NOTE: This extends the *original* safe-mode duration. + /// The extension period can be less than [`Config::ExtendDuration`] + /// if the safe-mode period already ran out. + /// So to say: even a delayed call to `extend` will never result in + /// an extension period of more than [`Config::ExtendDuration`] blocks. + #[pallet::weight(0)] + pub fn extend(origin: OriginFor) -> DispatchResult { + T::ExtendOrigin::ensure_origin(origin)?; + + let limit = Enabled::::take() + .ok_or(Error::::IsDisabled)? + .saturating_add(T::ExtendDuration::get()); + Enabled::::put(limit); + Self::deposit_event(Event::::Extended(limit)); + + Ok(()) + } + + /// Disable the safe-mode. + /// + /// Can be called either by + /// [`Config::PreemptiveDisableOrigin`] at any time + /// or + /// anyone after block number [`Enabled`]+1 is reached. + #[pallet::weight(0)] + pub fn disable(origin: OriginFor) -> DispatchResult { + let can_preempt = if T::PreemptiveDisableOrigin::ensure_origin(origin.clone()).is_ok() { + true + } else { + ensure_signed(origin)?; + false + }; + + let limit = Enabled::::take().ok_or(Error::::IsDisabled)?; + + if can_preempt || >::block_number() > limit { + Enabled::::kill(); + Self::deposit_event(Event::Disabled); + Ok(()) + } else { + Err(Error::::NoPermission.into()) + } + } + } +} + +impl Pallet { + /// Return whether the `safe-mode` is currently enabled. + pub fn is_enabled() -> bool { + Enabled::::exists() + } + + /// Return whether this call is allowed to be dispatched. + pub fn can_dispatch(call: &T::Call) -> bool + where + T::Call: GetCallMetadata, + { + // The `SafeMode` pallet can always be dispatched. + let CallMetadata { pallet_name, .. } = call.get_call_metadata(); + if pallet_name == as PalletInfoAccess>::name() { + return true + } + + if Self::is_enabled() { + T::SafeModeFilter::contains(call) + } else { + true + } + } +} + +impl Contains for Pallet +where + T::Call: GetCallMetadata, +{ + /// Return whether this call is allowed to be dispatched. + fn contains(call: &T::Call) -> bool { + Pallet::::can_dispatch(call) + } +} diff --git a/frame/safe-mode/src/mock.rs b/frame/safe-mode/src/mock.rs new file mode 100644 index 0000000000000..95c25725a4f6d --- /dev/null +++ b/frame/safe-mode/src/mock.rs @@ -0,0 +1,18 @@ +// This file is part of Substrate. + +// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![cfg(any(test, feature = "runtime-benchmarks"))] diff --git a/frame/safe-mode/src/tests.rs b/frame/safe-mode/src/tests.rs new file mode 100644 index 0000000000000..ee33686e4a39f --- /dev/null +++ b/frame/safe-mode/src/tests.rs @@ -0,0 +1,18 @@ +// This file is part of Substrate. + +// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![cfg(test)] diff --git a/frame/tx-pause/Cargo.toml b/frame/tx-pause/Cargo.toml index 0e0a0e969f7ea..6b001ae9c386f 100644 --- a/frame/tx-pause/Cargo.toml +++ b/frame/tx-pause/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" license = "Apache-2.0" homepage = "https://substrate.io" repository = "https://github.com/paritytech/substrate/" -description = "FRAME tx-pause pallet" +description = "FRAME transaction pause pallet" readme = "README.md" [package.metadata.docs.rs] diff --git a/frame/tx-pause/src/lib.rs b/frame/tx-pause/src/lib.rs index 139761d3204c5..254e733e6d617 100644 --- a/frame/tx-pause/src/lib.rs +++ b/frame/tx-pause/src/lib.rs @@ -30,8 +30,8 @@ mod tests; pub use pallet::*; -pub(crate) type PalletNameOf = BoundedVec::MaxNameLen>; -pub(crate) type ExtrinsicNameOf = BoundedVec::MaxNameLen>; +pub type PalletNameOf = BoundedVec::MaxNameLen>; +pub type ExtrinsicNameOf = BoundedVec::MaxNameLen>; #[frame_support::pallet] pub mod pallet { @@ -55,7 +55,7 @@ pub mod pallet { /// Pallets that are safe and can never be paused. /// /// The tx-pause pallet is always assumed to be safe itself. - type SafePallets: Contains>; + type UnpausablePallets: Contains>; /// Maximum length for pallet- and extrinsic-names. /// @@ -79,12 +79,14 @@ pub mod pallet { #[pallet::error] pub enum Error { - /// The call is paused. + /// The call is (already) paused. IsPaused, - /// The call is not paused. - IsNotPaused, - /// The call is listed as safe. - IsSafe, + + /// The call is (already) unpaused. + IsUnpaused, + + /// The call is listed as safe and cannot be paused. + IsUnpausable, } #[pallet::event] @@ -144,7 +146,7 @@ pub mod pallet { } impl Pallet { - /// Return whether the call is paused. + /// Return whether this call is paused. pub fn is_paused_unbound(pallet: Vec, extrinsic: Vec) -> bool { let pallet = PalletNameOf::::try_from(pallet); let extrinsic = ExtrinsicNameOf::::try_from(extrinsic); @@ -155,21 +157,22 @@ impl Pallet { } } - /// Return whether the call is paused. + /// Return whether this call is paused. pub fn is_paused(pallet: &PalletNameOf, extrinsic: &ExtrinsicNameOf) -> bool { >::contains_key((pallet, extrinsic)) } - /// Ensure that the call can be paused. + /// Ensure that this call can be paused. pub fn ensure_can_pause( pallet: &PalletNameOf, extrinsic: &ExtrinsicNameOf, ) -> Result<(), Error> { + // The `TxPause` pallet can never be paused. if pallet.as_ref() == ::name().as_bytes().to_vec() { - return Err(Error::::IsSafe) + return Err(Error::::IsUnpausable) } - if T::SafePallets::contains(&pallet) { - return Err(Error::::IsSafe) + if T::UnpausablePallets::contains(&pallet) { + return Err(Error::::IsUnpausable) } if Self::is_paused(pallet, extrinsic) { return Err(Error::::IsPaused) @@ -177,7 +180,7 @@ impl Pallet { Ok(()) } - /// Ensure that the call can be un-paused. + /// Ensure that this call can be un-paused. pub fn ensure_can_unpause( pallet: &PalletNameOf, extrinsic: &ExtrinsicNameOf, @@ -186,7 +189,7 @@ impl Pallet { // SAFETY: Everything that is paused, can be un-paused. Ok(()) } else { - Err(Error::IsNotPaused) + Err(Error::IsUnpaused) } } } @@ -195,7 +198,7 @@ impl Contains for Pallet where ::Call: GetCallMetadata, { - /// Returns whether the call is allowed to be called. + /// Return whether the call is allowed to be dispatched. fn contains(call: &T::Call) -> bool { let CallMetadata { pallet_name, function_name } = call.get_call_metadata(); !Pallet::::is_paused_unbound(pallet_name.into(), function_name.into()) From 2b4549bcbcaa22cc4f4609e0b51916f783f2d67e Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Mon, 29 Aug 2022 12:28:15 +0200 Subject: [PATCH 007/100] fmt Signed-off-by: Oliver Tale-Yazdi --- bin/node/runtime/src/lib.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index faac5b830af66..04b0014f0442a 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -31,9 +31,10 @@ use frame_support::{ pallet_prelude::Get, parameter_types, traits::{ - AsEnsureOriginWithArg, ConstBool, ConstU128, ConstU16, ConstU32, Contains, Currency, InsideBoth, - EitherOfDiverse, EqualPrivilegeOnly, Imbalance, InstanceFilter, KeyOwnerProofSystem, - LockIdentifier, Nothing, OnUnbalanced, PalletInfoAccess, U128CurrencyToVote, + AsEnsureOriginWithArg, ConstBool, ConstU128, ConstU16, ConstU32, Contains, Currency, + EitherOfDiverse, EqualPrivilegeOnly, Imbalance, InsideBoth, InstanceFilter, + KeyOwnerProofSystem, LockIdentifier, Nothing, OnUnbalanced, PalletInfoAccess, + U128CurrencyToVote, }, weights::{ constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND}, From 59a492a7ae70c2af2247a8f10a7a2211ee243f5e Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Mon, 29 Aug 2022 12:28:31 +0200 Subject: [PATCH 008/100] =?UTF-8?q?Automatically=20disable=20safe-mode=20i?= =?UTF-8?q?n=20on=5Finit=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Oliver Tale-Yazdi --- frame/safe-mode/src/lib.rs | 94 ++++++++++++++++++++++---------------- 1 file changed, 55 insertions(+), 39 deletions(-) diff --git a/frame/safe-mode/src/lib.rs b/frame/safe-mode/src/lib.rs index 0638031e77c21..b09d8aaa4b43b 100644 --- a/frame/safe-mode/src/lib.rs +++ b/frame/safe-mode/src/lib.rs @@ -19,10 +19,10 @@ use frame_support::{ pallet_prelude::*, - traits::{CallMetadata, Contains, GetCallMetadata, PalletInfoAccess}, + traits::{CallMetadata, Contains, Defensive, GetCallMetadata, PalletInfoAccess}, }; use frame_system::pallet_prelude::*; -use sp_runtime::traits::Saturating; +use sp_runtime::traits::{ Saturating}; use sp_std::{convert::TryInto, prelude::*}; mod benchmarking; @@ -46,7 +46,7 @@ pub mod pallet { /// Contains all calls that can be dispatched when the safe-mode is enabled. /// - /// The `SafeMode` pallet is always included and does not need to be added. + /// The `SafeMode` pallet is always included and does not need to be added here. type SafeModeFilter: Contains; /// How long the safe-mode will stay active when enabled with [`Pallet::enable`]. @@ -74,35 +74,47 @@ pub mod pallet { /// The safe-mode is (already) disabled. IsDisabled, - - /// No permission to disable the safe-mode (yet). - NoPermission, } #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event { - /// The safe-mode was enabled until inclusively this block. + /// The safe-mode was enabled until inclusively this \[block number\]. Enabled(T::BlockNumber), - /// The safe-mode was extended until inclusively this block. + /// The safe-mode was extended until inclusively this \[block number\]. Extended(T::BlockNumber), - /// The safe-mode was disabled. - Disabled, + /// The safe-mode was disabled for a specific \[reason\]. + Disabled(DisableReason), + } + + /// The reason why the safe-mode was disabled. + #[derive(Copy, Clone, PartialEq, Eq, RuntimeDebug, Encode, Decode, TypeInfo, MaxEncodedLen)] + pub enum DisableReason { + /// The safe-mode was automatically disabled after `EnableDuration` had passed. + Timeout, + + /// The safe-mode was preemptively disabled by the + /// [`Config::PreemptiveDisableOrigin`] origin. + Preemptive, } /// Contains the last block number that the safe-mode will stay enabled. /// /// This is set to `None` if the safe-mode is disabled. - /// In any block after this block number is reached, - /// the safe-mode can be disabled permissionlessly by any-one. + /// The safe-mode is automatically disabled when the current block number is greater than this. #[pallet::storage] pub type Enabled = StorageValue<_, T::BlockNumber, OptionQuery>; #[pallet::call] impl Pallet { /// Enable the safe-mode. + /// + /// Enables the safe-mode for [`Config::EnableDuration`] blocks. + /// Can only be called by the [`Config::EnableOrigin`] origin. + /// Errors if the safe-mode is already enabled. + /// Emits an [`Event::Enabled`] event on success. #[pallet::weight(0)] pub fn enable(origin: OriginFor) -> DispatchResult { T::EnableOrigin::ensure_origin(origin)?; @@ -116,16 +128,10 @@ pub mod pallet { Ok(()) } - /// Extends the safe-mode duration. + /// Extend the safe-mode. /// - /// Can only be called by the [`Config::ExtendOrigin`] origin. /// The safe-mode must already be enabled for this to succeed. - /// - /// NOTE: This extends the *original* safe-mode duration. - /// The extension period can be less than [`Config::ExtendDuration`] - /// if the safe-mode period already ran out. - /// So to say: even a delayed call to `extend` will never result in - /// an extension period of more than [`Config::ExtendDuration`] blocks. + /// Can only be called by the [`Config::ExtendOrigin`] origin. #[pallet::weight(0)] pub fn extend(origin: OriginFor) -> DispatchResult { T::ExtendOrigin::ensure_origin(origin)?; @@ -141,33 +147,43 @@ pub mod pallet { /// Disable the safe-mode. /// - /// Can be called either by - /// [`Config::PreemptiveDisableOrigin`] at any time - /// or - /// anyone after block number [`Enabled`]+1 is reached. + /// Can be called either by the [`Config::PreemptiveDisableOrigin`] origin at any time. + /// Will be automatically called after the safe-mode period ran out. #[pallet::weight(0)] pub fn disable(origin: OriginFor) -> DispatchResult { - let can_preempt = if T::PreemptiveDisableOrigin::ensure_origin(origin.clone()).is_ok() { - true - } else { - ensure_signed(origin)?; - false - }; - - let limit = Enabled::::take().ok_or(Error::::IsDisabled)?; - - if can_preempt || >::block_number() > limit { - Enabled::::kill(); - Self::deposit_event(Event::Disabled); - Ok(()) - } else { - Err(Error::::NoPermission.into()) + T::PreemptiveDisableOrigin::ensure_origin(origin.clone())?; + + Self::do_disable(DisableReason::Preemptive) + } + } + + #[pallet::hooks] + impl Hooks> for Pallet { + /// Automatically disables the safe-mode when the period ran out. + /// + /// Bypasses any call filters to avoid getting rejected by them. + fn on_initialize(current: T::BlockNumber) -> Weight { + match Enabled::::get() { + Some(limit) if current > limit => { + let _ = Self::do_disable(DisableReason::Timeout).defensive_proof("Must be disabled; qed"); + T::DbWeight::get().reads_writes(1, 1) + }, + _ => T::DbWeight::get().reads(1), // TODO benchmark } } } } impl Pallet { + /// Logic of the [`crate::Pallet::disable`] extrinsic. + /// + /// Does not check the origin. Errors if the safe-mode is already disabled. + pub fn do_disable(reason: DisableReason) -> DispatchResult { + let _limit = Enabled::::take().ok_or(Error::::IsDisabled)?; + Self::deposit_event(Event::Disabled(reason)); + Ok(()) + } + /// Return whether the `safe-mode` is currently enabled. pub fn is_enabled() -> bool { Enabled::::exists() From c2140af25ef9b77daa16a766b5280fe0f9a644f0 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Mon, 29 Aug 2022 13:52:42 +0200 Subject: [PATCH 009/100] Add permissionless enable+extend Signed-off-by: Oliver Tale-Yazdi --- frame/safe-mode/src/lib.rs | 131 +++++++++++++++++++++++++++---------- 1 file changed, 98 insertions(+), 33 deletions(-) diff --git a/frame/safe-mode/src/lib.rs b/frame/safe-mode/src/lib.rs index b09d8aaa4b43b..9f8ecea405875 100644 --- a/frame/safe-mode/src/lib.rs +++ b/frame/safe-mode/src/lib.rs @@ -19,10 +19,13 @@ use frame_support::{ pallet_prelude::*, - traits::{CallMetadata, Contains, Defensive, GetCallMetadata, PalletInfoAccess}, + traits::{ + CallMetadata, Contains, Currency, Defensive, GetCallMetadata, PalletInfoAccess, + ReservableCurrency, + }, }; use frame_system::pallet_prelude::*; -use sp_runtime::traits::{ Saturating}; +use sp_runtime::traits::Saturating; use sp_std::{convert::TryInto, prelude::*}; mod benchmarking; @@ -31,6 +34,9 @@ mod tests; pub use pallet::*; +type BalanceOf = + <::Currency as Currency<::AccountId>>::Balance; + #[frame_support::pallet] pub mod pallet { use super::*; @@ -49,19 +55,26 @@ pub mod pallet { /// The `SafeMode` pallet is always included and does not need to be added here. type SafeModeFilter: Contains; + /// Currency type for this pallet. + type Currency: ReservableCurrency; + /// How long the safe-mode will stay active when enabled with [`Pallet::enable`]. #[pallet::constant] type EnableDuration: Get; - /// How much the safe-mode can be extended by each [`Pallet::extend`] call. /// /// This does not impose a hard limit as the safe-mode can be extended multiple times. #[pallet::constant] type ExtendDuration: Get; + #[pallet::constant] + type EnableStakeAmount: Get>>; + #[pallet::constant] + type ExtendStakeAmount: Get>>; + type EnableOrigin: EnsureOrigin; type ExtendOrigin: EnsureOrigin; - type PreemptiveDisableOrigin: EnsureOrigin; + type DisableOrigin: EnsureOrigin; // Weight information for extrinsics in this pallet. //type WeightInfo: WeightInfo; @@ -74,6 +87,8 @@ pub mod pallet { /// The safe-mode is (already) disabled. IsDisabled, + + CannotStake, } #[pallet::event] @@ -95,9 +110,8 @@ pub mod pallet { /// The safe-mode was automatically disabled after `EnableDuration` had passed. Timeout, - /// The safe-mode was preemptively disabled by the - /// [`Config::PreemptiveDisableOrigin`] origin. - Preemptive, + /// The safe-mode was forcefully disabled by the [`Config::DisableOrigin`] origin. + Force, } /// Contains the last block number that the safe-mode will stay enabled. @@ -107,53 +121,69 @@ pub mod pallet { #[pallet::storage] pub type Enabled = StorageValue<_, T::BlockNumber, OptionQuery>; + #[pallet::storage] + pub type Stakes = + StorageMap<_, Blake2_128Concat, T::AccountId, BalanceOf, ValueQuery>; + #[pallet::call] impl Pallet { - /// Enable the safe-mode. + /// Enable the safe-mode permissionlessly for [`Config::ExtendDuration`] blocks. + /// + /// Reserves `EnableStakeAmount` from the caller's account. + /// Errors if the safe-mode is already enabled. + /// Can be permanently disabled by configuring `EnableStakeAmount` to `None`. + #[pallet::weight(0)] + pub fn enable(origin: OriginFor) -> DispatchResult { + let who = ensure_signed(origin)?; + + Self::do_enable(Some(who)) + } + + /// Enable the safe-mode by force for [`Config::EnableDuration`] blocks. /// - /// Enables the safe-mode for [`Config::EnableDuration`] blocks. /// Can only be called by the [`Config::EnableOrigin`] origin. /// Errors if the safe-mode is already enabled. /// Emits an [`Event::Enabled`] event on success. #[pallet::weight(0)] - pub fn enable(origin: OriginFor) -> DispatchResult { + pub fn force_enable(origin: OriginFor) -> DispatchResult { T::EnableOrigin::ensure_origin(origin)?; - ensure!(!Enabled::::exists(), Error::::IsEnabled); - let limit = - >::block_number().saturating_add(T::EnableDuration::get()); - Enabled::::put(limit); - Self::deposit_event(Event::Enabled(limit)); + Self::do_enable(None) + } - Ok(()) + /// Extend the safe-mode permissionlessly for [`Config::ExtendDuration`] blocks. + /// + /// Reserves `ExtendStakeAmount` from the caller's account. + /// Errors if the safe-mode is disabled. + /// Can be permanently disabled by configuring `ExtendStakeAmount` to `None`. + #[pallet::weight(0)] + pub fn extend(origin: OriginFor) -> DispatchResult { + let who = ensure_signed(origin)?; + + Self::do_extend(Some(who)) } - /// Extend the safe-mode. + /// Extend the safe-mode by force for [`Config::ExtendDuration`] blocks. /// - /// The safe-mode must already be enabled for this to succeed. + /// Errors if the safe-mode is disabled. /// Can only be called by the [`Config::ExtendOrigin`] origin. #[pallet::weight(0)] - pub fn extend(origin: OriginFor) -> DispatchResult { + pub fn force_extend(origin: OriginFor) -> DispatchResult { T::ExtendOrigin::ensure_origin(origin)?; - let limit = Enabled::::take() - .ok_or(Error::::IsDisabled)? - .saturating_add(T::ExtendDuration::get()); - Enabled::::put(limit); - Self::deposit_event(Event::::Extended(limit)); - - Ok(()) + Self::do_extend(None) } - /// Disable the safe-mode. + /// Disable the safe-mode by force. /// - /// Can be called either by the [`Config::PreemptiveDisableOrigin`] origin at any time. /// Will be automatically called after the safe-mode period ran out. + /// Errors if the safe-mode is disabled. + /// Can only be called by the [`Config::DisableOrigin`] origin. #[pallet::weight(0)] - pub fn disable(origin: OriginFor) -> DispatchResult { - T::PreemptiveDisableOrigin::ensure_origin(origin.clone())?; + pub fn force_disable(origin: OriginFor) -> DispatchResult { + T::DisableOrigin::ensure_origin(origin.clone())?; - Self::do_disable(DisableReason::Preemptive) + Self::do_disable(DisableReason::Force) } } @@ -165,7 +195,8 @@ pub mod pallet { fn on_initialize(current: T::BlockNumber) -> Weight { match Enabled::::get() { Some(limit) if current > limit => { - let _ = Self::do_disable(DisableReason::Timeout).defensive_proof("Must be disabled; qed"); + let _ = Self::do_disable(DisableReason::Timeout) + .defensive_proof("Must be disabled; qed"); T::DbWeight::get().reads_writes(1, 1) }, _ => T::DbWeight::get().reads(1), // TODO benchmark @@ -175,7 +206,41 @@ pub mod pallet { } impl Pallet { - /// Logic of the [`crate::Pallet::disable`] extrinsic. + pub fn do_enable(who: Option) -> DispatchResult { + if let Some(who) = who { + let stake = T::EnableStakeAmount::get().ok_or(Error::::CannotStake)?; + Self::reserve(who, stake)?; + } + + ensure!(!Enabled::::exists(), Error::::IsEnabled); + let limit = + >::block_number().saturating_add(T::EnableDuration::get()); + Enabled::::put(limit); + Self::deposit_event(Event::Enabled(limit)); + Ok(()) + } + + pub fn do_extend(who: Option) -> DispatchResult { + if let Some(who) = who { + let stake = T::ExtendStakeAmount::get().ok_or(Error::::CannotStake)?; + Self::reserve(who, stake)?; + } + + let limit = Enabled::::take() + .ok_or(Error::::IsDisabled)? + .saturating_add(T::ExtendDuration::get()); + Enabled::::put(limit); + Self::deposit_event(Event::::Extended(limit)); + Ok(()) + } + + fn reserve(who: T::AccountId, stake: BalanceOf) -> DispatchResult { + T::Currency::reserve(&who, stake)?; + Stakes::::mutate(&who, |s| s.saturating_accrue(stake)); + Ok(()) + } + + /// Logic of the [`crate::Pallet::force_disable`] extrinsic. /// /// Does not check the origin. Errors if the safe-mode is already disabled. pub fn do_disable(reason: DisableReason) -> DispatchResult { From b740f4fbd37395f5a6d13dc59ad5e02568e8a08a Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Mon, 29 Aug 2022 14:32:53 +0200 Subject: [PATCH 010/100] Add repay+slash stake methods Signed-off-by: Oliver Tale-Yazdi --- frame/safe-mode/src/lib.rs | 62 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 59 insertions(+), 3 deletions(-) diff --git a/frame/safe-mode/src/lib.rs b/frame/safe-mode/src/lib.rs index 9f8ecea405875..58473a291d335 100644 --- a/frame/safe-mode/src/lib.rs +++ b/frame/safe-mode/src/lib.rs @@ -75,6 +75,7 @@ pub mod pallet { type EnableOrigin: EnsureOrigin; type ExtendOrigin: EnsureOrigin; type DisableOrigin: EnsureOrigin; + type RepayOrigin: EnsureOrigin; // Weight information for extrinsics in this pallet. //type WeightInfo: WeightInfo; @@ -89,6 +90,7 @@ pub mod pallet { IsDisabled, CannotStake, + NotStaked, } #[pallet::event] @@ -102,6 +104,9 @@ pub mod pallet { /// The safe-mode was disabled for a specific \[reason\]. Disabled(DisableReason), + + StakeRepaid(T::AccountId, BalanceOf), + StakeSlashed(T::AccountId, BalanceOf), } /// The reason why the safe-mode was disabled. @@ -121,9 +126,17 @@ pub mod pallet { #[pallet::storage] pub type Enabled = StorageValue<_, T::BlockNumber, OptionQuery>; + // Maps (account, block number) to the amount of stake that was staked at that block. #[pallet::storage] - pub type Stakes = - StorageMap<_, Blake2_128Concat, T::AccountId, BalanceOf, ValueQuery>; + pub type Stakes = StorageDoubleMap< + _, + Twox64Concat, + T::AccountId, + Blake2_128Concat, + T::BlockNumber, + BalanceOf, + OptionQuery, + >; #[pallet::call] impl Pallet { @@ -185,6 +198,30 @@ pub mod pallet { Self::do_disable(DisableReason::Force) } + + /// Repay an honest account that put the chain into safe-mode earlier. + #[pallet::weight(0)] + pub fn repay_stake( + origin: OriginFor, + account: T::AccountId, + block: T::BlockNumber, + ) -> DispatchResult { + T::RepayOrigin::ensure_origin(origin.clone())?; + + Self::do_repay_stake(account, block) + } + + /// Slash a dishonest account that put the chain into safe-mode earlier. + #[pallet::weight(0)] + pub fn slash_stake( + origin: OriginFor, + account: T::AccountId, + block: T::BlockNumber, + ) -> DispatchResult { + T::RepayOrigin::ensure_origin(origin.clone())?; + + Self::do_slash_stake(account, block) + } } #[pallet::hooks] @@ -236,7 +273,8 @@ impl Pallet { fn reserve(who: T::AccountId, stake: BalanceOf) -> DispatchResult { T::Currency::reserve(&who, stake)?; - Stakes::::mutate(&who, |s| s.saturating_accrue(stake)); + let block = >::block_number(); + Stakes::::mutate(&who, &block, |s| s.unwrap_or_default().saturating_accrue(stake)); Ok(()) } @@ -249,6 +287,24 @@ impl Pallet { Ok(()) } + pub fn do_repay_stake(account: T::AccountId, block: T::BlockNumber) -> DispatchResult { + ensure!(!Self::is_enabled(), Error::::IsEnabled); + let stake = Stakes::::take(&account, block).ok_or(Error::::NotStaked)?; + + T::Currency::unreserve(&account, stake); + Self::deposit_event(Event::::StakeRepaid(account, stake)); + Ok(()) + } + + pub fn do_slash_stake(account: T::AccountId, block: T::BlockNumber) -> DispatchResult { + ensure!(!Self::is_enabled(), Error::::IsEnabled); + let stake = Stakes::::take(&account, block).ok_or(Error::::NotStaked)?; + + T::Currency::slash_reserved(&account, stake); + Self::deposit_event(Event::::StakeSlashed(account, stake)); + Ok(()) + } + /// Return whether the `safe-mode` is currently enabled. pub fn is_enabled() -> bool { Enabled::::exists() From 13f2ef6322c1087fbcd789988bdc1c42ae279e6d Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Tue, 6 Sep 2022 13:28:47 +0200 Subject: [PATCH 011/100] Add docs Signed-off-by: Oliver Tale-Yazdi --- frame/safe-mode/src/lib.rs | 59 ++++++++++++++++++++++++++------------ 1 file changed, 40 insertions(+), 19 deletions(-) diff --git a/frame/safe-mode/src/lib.rs b/frame/safe-mode/src/lib.rs index 58473a291d335..fee5d8916c9fa 100644 --- a/frame/safe-mode/src/lib.rs +++ b/frame/safe-mode/src/lib.rs @@ -89,7 +89,10 @@ pub mod pallet { /// The safe-mode is (already) disabled. IsDisabled, - CannotStake, + /// A value that is required for the extrinsic was not configured. + NotConfigured, + + /// There is no balance staked. NotStaked, } @@ -105,7 +108,10 @@ pub mod pallet { /// The safe-mode was disabled for a specific \[reason\]. Disabled(DisableReason), + /// An account got repaid its stake. \[account, amount\] StakeRepaid(T::AccountId, BalanceOf), + + /// An account got slashed its stake. \[account, amount\] StakeSlashed(T::AccountId, BalanceOf), } @@ -126,7 +132,7 @@ pub mod pallet { #[pallet::storage] pub type Enabled = StorageValue<_, T::BlockNumber, OptionQuery>; - // Maps (account, block number) to the amount of stake that was staked at that block. + /// Holds the stake that was reserved from a user at a specific block number. #[pallet::storage] pub type Stakes = StorageDoubleMap< _, @@ -140,7 +146,7 @@ pub mod pallet { #[pallet::call] impl Pallet { - /// Enable the safe-mode permissionlessly for [`Config::ExtendDuration`] blocks. + /// Enable the safe-mode permissionlessly for [`Config::EnableDuration`] blocks. /// /// Reserves `EnableStakeAmount` from the caller's account. /// Errors if the safe-mode is already enabled. @@ -200,6 +206,9 @@ pub mod pallet { } /// Repay an honest account that put the chain into safe-mode earlier. + /// + /// Errors if the safe-mode is already enabled. + /// Emits a [`Event::StakeRepaid`] event on success. #[pallet::weight(0)] pub fn repay_stake( origin: OriginFor, @@ -212,6 +221,9 @@ pub mod pallet { } /// Slash a dishonest account that put the chain into safe-mode earlier. + /// + /// Errors if the safe-mode is already enabled. + /// Emits a [`Event::StakeSlashed`] event on success. #[pallet::weight(0)] pub fn slash_stake( origin: OriginFor, @@ -243,9 +255,10 @@ pub mod pallet { } impl Pallet { - pub fn do_enable(who: Option) -> DispatchResult { + /// Logic for the [`crate::Pallet::enable`] and [`crate::Pallet::force_enable`] calls. + fn do_enable(who: Option) -> DispatchResult { if let Some(who) = who { - let stake = T::EnableStakeAmount::get().ok_or(Error::::CannotStake)?; + let stake = T::EnableStakeAmount::get().ok_or(Error::::NotConfigured)?; Self::reserve(who, stake)?; } @@ -257,9 +270,10 @@ impl Pallet { Ok(()) } - pub fn do_extend(who: Option) -> DispatchResult { + /// Logic for the [`crate::Pallet::extend`] and [`crate::Pallet::force_extend`] calls. + fn do_extend(who: Option) -> DispatchResult { if let Some(who) = who { - let stake = T::ExtendStakeAmount::get().ok_or(Error::::CannotStake)?; + let stake = T::ExtendStakeAmount::get().ok_or(Error::::NotConfigured)?; Self::reserve(who, stake)?; } @@ -271,23 +285,19 @@ impl Pallet { Ok(()) } - fn reserve(who: T::AccountId, stake: BalanceOf) -> DispatchResult { - T::Currency::reserve(&who, stake)?; - let block = >::block_number(); - Stakes::::mutate(&who, &block, |s| s.unwrap_or_default().saturating_accrue(stake)); - Ok(()) - } - - /// Logic of the [`crate::Pallet::force_disable`] extrinsic. + /// Logic for the [`crate::Pallet::force_disable`] call. /// /// Does not check the origin. Errors if the safe-mode is already disabled. - pub fn do_disable(reason: DisableReason) -> DispatchResult { + fn do_disable(reason: DisableReason) -> DispatchResult { let _limit = Enabled::::take().ok_or(Error::::IsDisabled)?; Self::deposit_event(Event::Disabled(reason)); Ok(()) } - pub fn do_repay_stake(account: T::AccountId, block: T::BlockNumber) -> DispatchResult { + /// Logic for the [`crate::Pallet::repay_stake`] call. + /// + /// Does not check the origin. Errors if the safe-mode is enabled. + fn do_repay_stake(account: T::AccountId, block: T::BlockNumber) -> DispatchResult { ensure!(!Self::is_enabled(), Error::::IsEnabled); let stake = Stakes::::take(&account, block).ok_or(Error::::NotStaked)?; @@ -296,7 +306,10 @@ impl Pallet { Ok(()) } - pub fn do_slash_stake(account: T::AccountId, block: T::BlockNumber) -> DispatchResult { + /// Logic for the [`crate::Pallet::slash_stake`] call. + /// + /// Does not check the origin. Errors if the safe-mode is enabled. + fn do_slash_stake(account: T::AccountId, block: T::BlockNumber) -> DispatchResult { ensure!(!Self::is_enabled(), Error::::IsEnabled); let stake = Stakes::::take(&account, block).ok_or(Error::::NotStaked)?; @@ -305,6 +318,14 @@ impl Pallet { Ok(()) } + /// Reserve `stake` amount from `who` and store it in `Stakes`. + fn reserve(who: T::AccountId, stake: BalanceOf) -> DispatchResult { + T::Currency::reserve(&who, stake)?; + let block = >::block_number(); + Stakes::::mutate(&who, &block, |s| s.unwrap_or_default().saturating_accrue(stake)); + Ok(()) + } + /// Return whether the `safe-mode` is currently enabled. pub fn is_enabled() -> bool { Enabled::::exists() @@ -315,8 +336,8 @@ impl Pallet { where T::Call: GetCallMetadata, { - // The `SafeMode` pallet can always be dispatched. let CallMetadata { pallet_name, .. } = call.get_call_metadata(); + // The `SafeMode` pallet can always be dispatched. if pallet_name == as PalletInfoAccess>::name() { return true } From 808425837302ea529485e6805d062fb245c702e4 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Tue, 6 Sep 2022 13:29:53 +0200 Subject: [PATCH 012/100] Fix stakes storage Signed-off-by: Oliver Tale-Yazdi --- frame/safe-mode/src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/frame/safe-mode/src/lib.rs b/frame/safe-mode/src/lib.rs index fee5d8916c9fa..e96482cd69b1e 100644 --- a/frame/safe-mode/src/lib.rs +++ b/frame/safe-mode/src/lib.rs @@ -322,7 +322,8 @@ impl Pallet { fn reserve(who: T::AccountId, stake: BalanceOf) -> DispatchResult { T::Currency::reserve(&who, stake)?; let block = >::block_number(); - Stakes::::mutate(&who, &block, |s| s.unwrap_or_default().saturating_accrue(stake)); + let current_stake = Stakes::::get(&who, block).unwrap_or_default(); + Stakes::::insert(&who, block, current_stake.saturating_add(stake)); Ok(()) } From 93acbaec84ddf5494479b7f6ed3bdbb030ec476d Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Wed, 7 Sep 2022 14:33:54 +0200 Subject: [PATCH 013/100] Genesis config for safe-mode pallet Signed-off-by: Oliver Tale-Yazdi --- bin/node/runtime/src/lib.rs | 2 +- frame/safe-mode/src/lib.rs | 26 ++++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index e521ca13143ab..006a1642ab78c 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -200,7 +200,7 @@ parameter_types! { const_assert!(NORMAL_DISPATCH_RATIO.deconstruct() >= AVERAGE_ON_INITIALIZE_RATIO.deconstruct()); -pub struct UnpausablePallets(); +pub struct UnpausablePallets; impl Contains> for UnpausablePallets { fn contains(pallet: &pallet_tx_pause::PalletNameOf) -> bool { pallet.as_ref() == diff --git a/frame/safe-mode/src/lib.rs b/frame/safe-mode/src/lib.rs index e96482cd69b1e..c70a843ef96a1 100644 --- a/frame/safe-mode/src/lib.rs +++ b/frame/safe-mode/src/lib.rs @@ -144,6 +144,32 @@ pub mod pallet { OptionQuery, >; + /// Configure the initial state of this pallet in the genesis block. + #[pallet::genesis_config] + pub struct GenesisConfig { + /// The blocknumber up to which inclusively the safe-mode will be enabled. + pub enabled: Option, + pub _phantom: PhantomData, + } + + #[cfg(feature = "std")] + impl Default for GenesisConfig { + // NOTE: `derive(Default)` does not work together with `#[pallet::genesis_config]`. + // We therefore need to add a trivial default impl. + fn default() -> Self { + Self { enabled: None, _phantom: PhantomData } + } + } + + #[pallet::genesis_build] + impl GenesisBuild for GenesisConfig { + fn build(&self) { + if let Some(block) = self.enabled { + Enabled::::put(block); + } + } + } + #[pallet::call] impl Pallet { /// Enable the safe-mode permissionlessly for [`Config::EnableDuration`] blocks. From 8806aa735a3440f9aeac5a4c9ab0c836a1010ca7 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Wed, 7 Sep 2022 14:45:19 +0200 Subject: [PATCH 014/100] Genesis config for safe-mode pallet Signed-off-by: Oliver Tale-Yazdi --- frame/tx-pause/src/lib.rs | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/frame/tx-pause/src/lib.rs b/frame/tx-pause/src/lib.rs index 254e733e6d617..6f18f1b221594 100644 --- a/frame/tx-pause/src/lib.rs +++ b/frame/tx-pause/src/lib.rs @@ -103,6 +103,32 @@ pub mod pallet { pub type PausedCalls = StorageMap<_, Blake2_128Concat, (PalletNameOf, ExtrinsicNameOf), (), OptionQuery>; + /// Configure the initial state of this pallet in the genesis block. + #[pallet::genesis_config] + pub struct GenesisConfig { + /// The initially paused calls. + pub paused: Vec<(PalletNameOf, ExtrinsicNameOf)>, + pub _phantom: PhantomData, + } + + #[cfg(feature = "std")] + impl Default for GenesisConfig { + // NOTE: `derive(Default)` does not work together with `#[pallet::genesis_config]`. + // We therefore need to add a trivial default impl. + fn default() -> Self { + Self { paused: Default::default(), _phantom: PhantomData } + } + } + + #[pallet::genesis_build] + impl GenesisBuild for GenesisConfig { + fn build(&self) { + for (pallet, extrinsic) in &self.paused { + PausedCalls::::insert((pallet, extrinsic), ()); + } + } + } + #[pallet::call] impl Pallet { /// Pause a call. From 956bb708ee78d6550992491cb26afa7822a1cb66 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Wed, 7 Sep 2022 14:48:27 +0200 Subject: [PATCH 015/100] Rename ExtrinsicName to FunctionName Signed-off-by: Oliver Tale-Yazdi --- frame/tx-pause/src/lib.rs | 58 +++++++++++++++++++-------------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/frame/tx-pause/src/lib.rs b/frame/tx-pause/src/lib.rs index 6f18f1b221594..394bbaaac3506 100644 --- a/frame/tx-pause/src/lib.rs +++ b/frame/tx-pause/src/lib.rs @@ -31,7 +31,7 @@ mod tests; pub use pallet::*; pub type PalletNameOf = BoundedVec::MaxNameLen>; -pub type ExtrinsicNameOf = BoundedVec::MaxNameLen>; +pub type FunctionNameOf = BoundedVec::MaxNameLen>; #[frame_support::pallet] pub mod pallet { @@ -57,14 +57,14 @@ pub mod pallet { /// The tx-pause pallet is always assumed to be safe itself. type UnpausablePallets: Contains>; - /// Maximum length for pallet- and extrinsic-names. + /// Maximum length for pallet- and function-names. /// /// Too long names will not be truncated but handled like /// [`Self::PauseTooLongNames`] specifies. #[pallet::constant] type MaxNameLen: Get; - /// Specifies if extrinsics and pallets with too long names should be treated as paused. + /// Specifies if functions and pallets with too long names should be treated as paused. /// /// Setting this to `true` ensures that all calls that /// are callable, are also pause-able. @@ -79,10 +79,10 @@ pub mod pallet { #[pallet::error] pub enum Error { - /// The call is (already) paused. + /// The call is (already or still) paused. IsPaused, - /// The call is (already) unpaused. + /// The call is (already or still) unpaused. IsUnpaused, /// The call is listed as safe and cannot be paused. @@ -93,21 +93,21 @@ pub mod pallet { #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event { /// This call got paused. - CallPaused(PalletNameOf, ExtrinsicNameOf), + CallPaused(PalletNameOf, FunctionNameOf), /// This call got un-paused. - CallUnpaused(PalletNameOf, ExtrinsicNameOf), + CallUnpaused(PalletNameOf, FunctionNameOf), } /// The set of calls that are explicitly paused. #[pallet::storage] pub type PausedCalls = - StorageMap<_, Blake2_128Concat, (PalletNameOf, ExtrinsicNameOf), (), OptionQuery>; + StorageMap<_, Blake2_128Concat, (PalletNameOf, FunctionNameOf), (), OptionQuery>; /// Configure the initial state of this pallet in the genesis block. #[pallet::genesis_config] pub struct GenesisConfig { /// The initially paused calls. - pub paused: Vec<(PalletNameOf, ExtrinsicNameOf)>, + pub paused: Vec<(PalletNameOf, FunctionNameOf)>, pub _phantom: PhantomData, } @@ -123,8 +123,8 @@ pub mod pallet { #[pallet::genesis_build] impl GenesisBuild for GenesisConfig { fn build(&self) { - for (pallet, extrinsic) in &self.paused { - PausedCalls::::insert((pallet, extrinsic), ()); + for (pallet, function) in &self.paused { + PausedCalls::::insert((pallet, function), ()); } } } @@ -139,13 +139,13 @@ pub mod pallet { pub fn pause_call( origin: OriginFor, pallet: PalletNameOf, - extrinsic: ExtrinsicNameOf, + function: FunctionNameOf, ) -> DispatchResult { T::PauseOrigin::ensure_origin(origin)?; - Self::ensure_can_pause(&pallet, &extrinsic)?; - PausedCalls::::insert((&pallet, &extrinsic), ()); - Self::deposit_event(Event::CallPaused(pallet, extrinsic)); + Self::ensure_can_pause(&pallet, &function)?; + PausedCalls::::insert((&pallet, &function), ()); + Self::deposit_event(Event::CallPaused(pallet, function)); Ok(()) } @@ -158,13 +158,13 @@ pub mod pallet { pub fn unpause_call( origin: OriginFor, pallet: PalletNameOf, - extrinsic: ExtrinsicNameOf, + function: FunctionNameOf, ) -> DispatchResult { T::UnpauseOrigin::ensure_origin(origin)?; - Self::ensure_can_unpause(&pallet, &extrinsic)?; - PausedCalls::::remove((&pallet, &extrinsic)); - Self::deposit_event(Event::CallUnpaused(pallet, extrinsic)); + Self::ensure_can_unpause(&pallet, &function)?; + PausedCalls::::remove((&pallet, &function)); + Self::deposit_event(Event::CallUnpaused(pallet, function)); Ok(()) } @@ -173,25 +173,25 @@ pub mod pallet { impl Pallet { /// Return whether this call is paused. - pub fn is_paused_unbound(pallet: Vec, extrinsic: Vec) -> bool { + pub fn is_paused_unbound(pallet: Vec, function: Vec) -> bool { let pallet = PalletNameOf::::try_from(pallet); - let extrinsic = ExtrinsicNameOf::::try_from(extrinsic); + let function = FunctionNameOf::::try_from(function); - match (pallet, extrinsic) { - (Ok(pallet), Ok(extrinsic)) => Self::is_paused(&pallet, &extrinsic), + match (pallet, function) { + (Ok(pallet), Ok(function)) => Self::is_paused(&pallet, &function), _ => T::PauseTooLongNames::get(), } } /// Return whether this call is paused. - pub fn is_paused(pallet: &PalletNameOf, extrinsic: &ExtrinsicNameOf) -> bool { - >::contains_key((pallet, extrinsic)) + pub fn is_paused(pallet: &PalletNameOf, function: &FunctionNameOf) -> bool { + >::contains_key((pallet, function)) } /// Ensure that this call can be paused. pub fn ensure_can_pause( pallet: &PalletNameOf, - extrinsic: &ExtrinsicNameOf, + function: &FunctionNameOf, ) -> Result<(), Error> { // The `TxPause` pallet can never be paused. if pallet.as_ref() == ::name().as_bytes().to_vec() { @@ -200,7 +200,7 @@ impl Pallet { if T::UnpausablePallets::contains(&pallet) { return Err(Error::::IsUnpausable) } - if Self::is_paused(pallet, extrinsic) { + if Self::is_paused(pallet, function) { return Err(Error::::IsPaused) } Ok(()) @@ -209,9 +209,9 @@ impl Pallet { /// Ensure that this call can be un-paused. pub fn ensure_can_unpause( pallet: &PalletNameOf, - extrinsic: &ExtrinsicNameOf, + function: &FunctionNameOf, ) -> Result<(), Error> { - if Self::is_paused(pallet, extrinsic) { + if Self::is_paused(pallet, function) { // SAFETY: Everything that is paused, can be un-paused. Ok(()) } else { From e3b388b131c642d943857804b813eaedebf6e1c9 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Tue, 13 Sep 2022 19:57:44 +0200 Subject: [PATCH 016/100] Origin variable duration Signed-off-by: Oliver Tale-Yazdi --- frame/safe-mode/src/lib.rs | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/frame/safe-mode/src/lib.rs b/frame/safe-mode/src/lib.rs index c70a843ef96a1..0191abf3289be 100644 --- a/frame/safe-mode/src/lib.rs +++ b/frame/safe-mode/src/lib.rs @@ -72,8 +72,8 @@ pub mod pallet { #[pallet::constant] type ExtendStakeAmount: Get>>; - type EnableOrigin: EnsureOrigin; - type ExtendOrigin: EnsureOrigin; + type EnableOrigin: EnsureOrigin; + type ExtendOrigin: EnsureOrigin; type DisableOrigin: EnsureOrigin; type RepayOrigin: EnsureOrigin; @@ -181,7 +181,7 @@ pub mod pallet { pub fn enable(origin: OriginFor) -> DispatchResult { let who = ensure_signed(origin)?; - Self::do_enable(Some(who)) + Self::do_enable(Some(who), T::EnableDuration::get()) } /// Enable the safe-mode by force for [`Config::EnableDuration`] blocks. @@ -191,9 +191,9 @@ pub mod pallet { /// Emits an [`Event::Enabled`] event on success. #[pallet::weight(0)] pub fn force_enable(origin: OriginFor) -> DispatchResult { - T::EnableOrigin::ensure_origin(origin)?; + let duration = T::EnableOrigin::ensure_origin(origin)?; - Self::do_enable(None) + Self::do_enable(None, duration) } /// Extend the safe-mode permissionlessly for [`Config::ExtendDuration`] blocks. @@ -205,7 +205,7 @@ pub mod pallet { pub fn extend(origin: OriginFor) -> DispatchResult { let who = ensure_signed(origin)?; - Self::do_extend(Some(who)) + Self::do_extend(Some(who), T::ExtendDuration::get()) } /// Extend the safe-mode by force for [`Config::ExtendDuration`] blocks. @@ -214,9 +214,9 @@ pub mod pallet { /// Can only be called by the [`Config::ExtendOrigin`] origin. #[pallet::weight(0)] pub fn force_extend(origin: OriginFor) -> DispatchResult { - T::ExtendOrigin::ensure_origin(origin)?; + let duration = T::ExtendOrigin::ensure_origin(origin)?; - Self::do_extend(None) + Self::do_extend(None, duration) } /// Disable the safe-mode by force. @@ -282,30 +282,27 @@ pub mod pallet { impl Pallet { /// Logic for the [`crate::Pallet::enable`] and [`crate::Pallet::force_enable`] calls. - fn do_enable(who: Option) -> DispatchResult { + fn do_enable(who: Option, duration: T::BlockNumber) -> DispatchResult { if let Some(who) = who { let stake = T::EnableStakeAmount::get().ok_or(Error::::NotConfigured)?; Self::reserve(who, stake)?; } ensure!(!Enabled::::exists(), Error::::IsEnabled); - let limit = - >::block_number().saturating_add(T::EnableDuration::get()); + let limit = >::block_number().saturating_add(duration); Enabled::::put(limit); Self::deposit_event(Event::Enabled(limit)); Ok(()) } /// Logic for the [`crate::Pallet::extend`] and [`crate::Pallet::force_extend`] calls. - fn do_extend(who: Option) -> DispatchResult { + fn do_extend(who: Option, duration: T::BlockNumber) -> DispatchResult { if let Some(who) = who { let stake = T::ExtendStakeAmount::get().ok_or(Error::::NotConfigured)?; Self::reserve(who, stake)?; } - let limit = Enabled::::take() - .ok_or(Error::::IsDisabled)? - .saturating_add(T::ExtendDuration::get()); + let limit = Enabled::::take().ok_or(Error::::IsDisabled)?.saturating_add(duration); Enabled::::put(limit); Self::deposit_event(Event::::Extended(limit)); Ok(()) From 57219d4948f641ac482ad5a50adf7adf58ee167e Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Thu, 15 Sep 2022 12:57:27 +0200 Subject: [PATCH 017/100] Rename FunctionName -> CallName Signed-off-by: Oliver Tale-Yazdi --- frame/tx-pause/src/lib.rs | 48 +++++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/frame/tx-pause/src/lib.rs b/frame/tx-pause/src/lib.rs index 394bbaaac3506..e4c90028dc860 100644 --- a/frame/tx-pause/src/lib.rs +++ b/frame/tx-pause/src/lib.rs @@ -31,7 +31,7 @@ mod tests; pub use pallet::*; pub type PalletNameOf = BoundedVec::MaxNameLen>; -pub type FunctionNameOf = BoundedVec::MaxNameLen>; +pub type CallNameOf = BoundedVec::MaxNameLen>; #[frame_support::pallet] pub mod pallet { @@ -93,21 +93,21 @@ pub mod pallet { #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event { /// This call got paused. - CallPaused(PalletNameOf, FunctionNameOf), + CallPaused(PalletNameOf, CallNameOf), /// This call got un-paused. - CallUnpaused(PalletNameOf, FunctionNameOf), + CallUnpaused(PalletNameOf, CallNameOf), } /// The set of calls that are explicitly paused. #[pallet::storage] pub type PausedCalls = - StorageMap<_, Blake2_128Concat, (PalletNameOf, FunctionNameOf), (), OptionQuery>; + StorageMap<_, Blake2_128Concat, (PalletNameOf, CallNameOf), (), OptionQuery>; /// Configure the initial state of this pallet in the genesis block. #[pallet::genesis_config] pub struct GenesisConfig { /// The initially paused calls. - pub paused: Vec<(PalletNameOf, FunctionNameOf)>, + pub paused: Vec<(PalletNameOf, CallNameOf)>, pub _phantom: PhantomData, } @@ -139,13 +139,13 @@ pub mod pallet { pub fn pause_call( origin: OriginFor, pallet: PalletNameOf, - function: FunctionNameOf, + call: CallNameOf, ) -> DispatchResult { T::PauseOrigin::ensure_origin(origin)?; - Self::ensure_can_pause(&pallet, &function)?; - PausedCalls::::insert((&pallet, &function), ()); - Self::deposit_event(Event::CallPaused(pallet, function)); + Self::ensure_can_pause(&pallet, &call)?; + PausedCalls::::insert((&pallet, &call), ()); + Self::deposit_event(Event::CallPaused(pallet, call)); Ok(()) } @@ -158,13 +158,13 @@ pub mod pallet { pub fn unpause_call( origin: OriginFor, pallet: PalletNameOf, - function: FunctionNameOf, + call: CallNameOf, ) -> DispatchResult { T::UnpauseOrigin::ensure_origin(origin)?; - Self::ensure_can_unpause(&pallet, &function)?; - PausedCalls::::remove((&pallet, &function)); - Self::deposit_event(Event::CallUnpaused(pallet, function)); + Self::ensure_can_unpause(&pallet, &call)?; + PausedCalls::::remove((&pallet, &call)); + Self::deposit_event(Event::CallUnpaused(pallet, call)); Ok(()) } @@ -173,25 +173,25 @@ pub mod pallet { impl Pallet { /// Return whether this call is paused. - pub fn is_paused_unbound(pallet: Vec, function: Vec) -> bool { - let pallet = PalletNameOf::::try_from(pallet); - let function = FunctionNameOf::::try_from(function); + pub fn is_paused_unbound(pallet: Vec, call: Vec) -> bool { + let pallet = PalletNameOf::::try_from(call); + let call = CallNameOf::::try_from(call); - match (pallet, function) { - (Ok(pallet), Ok(function)) => Self::is_paused(&pallet, &function), + match (pallet, call) { + (Ok(pallet), Ok(call)) => Self::is_paused(&pallet, &call), _ => T::PauseTooLongNames::get(), } } /// Return whether this call is paused. - pub fn is_paused(pallet: &PalletNameOf, function: &FunctionNameOf) -> bool { - >::contains_key((pallet, function)) + pub fn is_paused(pallet: &PalletNameOf, call: &CallNameOf) -> bool { + >::contains_key((pallet, call)) } /// Ensure that this call can be paused. pub fn ensure_can_pause( pallet: &PalletNameOf, - function: &FunctionNameOf, + call: &CallNameOf, ) -> Result<(), Error> { // The `TxPause` pallet can never be paused. if pallet.as_ref() == ::name().as_bytes().to_vec() { @@ -200,7 +200,7 @@ impl Pallet { if T::UnpausablePallets::contains(&pallet) { return Err(Error::::IsUnpausable) } - if Self::is_paused(pallet, function) { + if Self::is_paused(pallet, call) { return Err(Error::::IsPaused) } Ok(()) @@ -209,9 +209,9 @@ impl Pallet { /// Ensure that this call can be un-paused. pub fn ensure_can_unpause( pallet: &PalletNameOf, - function: &FunctionNameOf, + call: &CallNameOf, ) -> Result<(), Error> { - if Self::is_paused(pallet, function) { + if Self::is_paused(pallet, call) { // SAFETY: Everything that is paused, can be un-paused. Ok(()) } else { From 3e421f2508dabd802105b7268bdf02de45dd30a4 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Thu, 15 Sep 2022 13:29:42 +0200 Subject: [PATCH 018/100] Rename and docs Signed-off-by: Oliver Tale-Yazdi --- bin/node/runtime/Cargo.toml | 4 +-- frame/safe-mode/src/lib.rs | 53 ++++++++++++++++++++++++++----------- 2 files changed, 39 insertions(+), 18 deletions(-) diff --git a/bin/node/runtime/Cargo.toml b/bin/node/runtime/Cargo.toml index 82ffea2dd9576..f51680e78bf99 100644 --- a/bin/node/runtime/Cargo.toml +++ b/bin/node/runtime/Cargo.toml @@ -235,7 +235,7 @@ runtime-benchmarks = [ "pallet-remark/runtime-benchmarks", "pallet-tx-pause/runtime-benchmarks", "pallet-safe-mode/runtime-benchmarks", - "pallet-session-benchmarking", + "pallet-session-benchmarking/runtime-benchmarks", "pallet-society/runtime-benchmarks", "pallet-staking/runtime-benchmarks", "pallet-state-trie-migration/runtime-benchmarks", @@ -247,7 +247,7 @@ runtime-benchmarks = [ "pallet-uniques/runtime-benchmarks", "pallet-vesting/runtime-benchmarks", "pallet-whitelist/runtime-benchmarks", - "frame-system-benchmarking", + "frame-system-benchmarking/runtime-benchmarks", "hex-literal", ] try-runtime = [ diff --git a/frame/safe-mode/src/lib.rs b/frame/safe-mode/src/lib.rs index 0191abf3289be..b7d7356ddb3ce 100644 --- a/frame/safe-mode/src/lib.rs +++ b/frame/safe-mode/src/lib.rs @@ -50,7 +50,7 @@ pub mod pallet { /// The overarching event type. type Event: From> + IsType<::Event>; - /// Contains all calls that can be dispatched when the safe-mode is enabled. + /// Contains all calls that can be dispatched even when the safe-mode is enabled. /// /// The `SafeMode` pallet is always included and does not need to be added here. type SafeModeFilter: Contains; @@ -61,20 +61,39 @@ pub mod pallet { /// How long the safe-mode will stay active when enabled with [`Pallet::enable`]. #[pallet::constant] type EnableDuration: Get; - /// How much the safe-mode can be extended by each [`Pallet::extend`] call. + + /// For how many blocks the safe-mode can be extended by each [`Pallet::extend`] call. /// /// This does not impose a hard limit as the safe-mode can be extended multiple times. #[pallet::constant] type ExtendDuration: Get; + /// The amount that will be reserved upon calling [`Pallet::enable`]. + /// + /// `None` disables the possibility of permissionlessly enabling the safe-mode. #[pallet::constant] type EnableStakeAmount: Get>>; + + /// The amount that will be reserved upon calling [`Pallet::extend`]. + /// + /// `None` disables the possibility of permissionlessly extending the safe-mode. #[pallet::constant] type ExtendStakeAmount: Get>>; - type EnableOrigin: EnsureOrigin; - type ExtendOrigin: EnsureOrigin; - type DisableOrigin: EnsureOrigin; + /// The origin that can call [`Pallet::force_enable`]. + /// + /// The `Success` value is the number of blocks that this origin can enter into safe-mode. + type ForceEnableOrigin: EnsureOrigin; + + /// The origin that can call [`Pallet::force_extend`]. + /// + /// The `Success` value is the number of blocks that this origin can extend the safe-mode. + type ForceExtendOrigin: EnsureOrigin; + + /// The origin that can call [`Pallet::force_disable`]. + type ForceDisableOrigin: EnsureOrigin; + + /// The origin that can call [`Pallet::repay_stake`] and [`Pallet::slash_stake`]. type RepayOrigin: EnsureOrigin; // Weight information for extrinsics in this pallet. @@ -83,10 +102,10 @@ pub mod pallet { #[pallet::error] pub enum Error { - /// The safe-mode is (already) enabled. + /// The safe-mode is (already or still) enabled. IsEnabled, - /// The safe-mode is (already) disabled. + /// The safe-mode is (already or still) disabled. IsDisabled, /// A value that is required for the extrinsic was not configured. @@ -99,10 +118,10 @@ pub mod pallet { #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event { - /// The safe-mode was enabled until inclusively this \[block number\]. + /// The safe-mode was enabled until inclusively this \[block\]. Enabled(T::BlockNumber), - /// The safe-mode was extended until inclusively this \[block number\]. + /// The safe-mode was extended until inclusively this \[block\]. Extended(T::BlockNumber), /// The safe-mode was disabled for a specific \[reason\]. @@ -118,10 +137,10 @@ pub mod pallet { /// The reason why the safe-mode was disabled. #[derive(Copy, Clone, PartialEq, Eq, RuntimeDebug, Encode, Decode, TypeInfo, MaxEncodedLen)] pub enum DisableReason { - /// The safe-mode was automatically disabled after `EnableDuration` had passed. + /// The safe-mode was automatically disabled after its duration ran out. Timeout, - /// The safe-mode was forcefully disabled by the [`Config::DisableOrigin`] origin. + /// The safe-mode was forcefully disabled by [`Pallet::force_disable`]. Force, } @@ -184,11 +203,11 @@ pub mod pallet { Self::do_enable(Some(who), T::EnableDuration::get()) } - /// Enable the safe-mode by force for [`Config::EnableDuration`] blocks. + /// Enable the safe-mode by force for a per-origin configured number of blocks. /// - /// Can only be called by the [`Config::EnableOrigin`] origin. /// Errors if the safe-mode is already enabled. /// Emits an [`Event::Enabled`] event on success. + /// Can only be called by the [`Config::EnableOrigin`] origin. #[pallet::weight(0)] pub fn force_enable(origin: OriginFor) -> DispatchResult { let duration = T::EnableOrigin::ensure_origin(origin)?; @@ -208,7 +227,7 @@ pub mod pallet { Self::do_extend(Some(who), T::ExtendDuration::get()) } - /// Extend the safe-mode by force for [`Config::ExtendDuration`] blocks. + /// Extend the safe-mode by force a per-origin configured number of blocks. /// /// Errors if the safe-mode is disabled. /// Can only be called by the [`Config::ExtendOrigin`] origin. @@ -235,6 +254,7 @@ pub mod pallet { /// /// Errors if the safe-mode is already enabled. /// Emits a [`Event::StakeRepaid`] event on success. + /// Can only be called by the [`Config::RepayOrigin`] origin. #[pallet::weight(0)] pub fn repay_stake( origin: OriginFor, @@ -250,6 +270,7 @@ pub mod pallet { /// /// Errors if the safe-mode is already enabled. /// Emits a [`Event::StakeSlashed`] event on success. + /// Can only be called by the [`Config::RepayOrigin`] origin. #[pallet::weight(0)] pub fn slash_stake( origin: OriginFor, @@ -356,7 +377,7 @@ impl Pallet { } /// Return whether this call is allowed to be dispatched. - pub fn can_dispatch(call: &T::Call) -> bool + pub fn is_allowed(call: &T::Call) -> bool where T::Call: GetCallMetadata, { @@ -380,6 +401,6 @@ where { /// Return whether this call is allowed to be dispatched. fn contains(call: &T::Call) -> bool { - Pallet::::can_dispatch(call) + Pallet::::is_allowed(call) } } From 379b82f0f4397fee148d9c924eeadccdda21bb84 Mon Sep 17 00:00:00 2001 From: Dan Shields <35669742+NukeManDan@users.noreply.github.com> Date: Wed, 21 Sep 2022 14:34:50 -0600 Subject: [PATCH 019/100] Pallet safe mode tests (#12148) * Add safe-mode mock runtime * Add safe-mode tests * Add ForceEnable- and ForceExtendOrigin * Start dummy benchmarks Co-authored-by: Oliver Tale-Yazdi --- Cargo.lock | 4 + bin/node/cli/src/chain_spec.rs | 2 + bin/node/runtime/src/lib.rs | 19 +- frame/remark/src/tests.rs | 1 - frame/safe-mode/Cargo.toml | 9 +- frame/safe-mode/src/benchmarking.rs | 77 +++++++ frame/safe-mode/src/lib.rs | 21 +- frame/safe-mode/src/mock.rs | 279 +++++++++++++++++++++++- frame/safe-mode/src/tests.rs | 319 +++++++++++++++++++++++++++- frame/safe-mode/src/weights.rs | 82 +++++++ 10 files changed, 800 insertions(+), 13 deletions(-) create mode 100644 frame/safe-mode/src/weights.rs diff --git a/Cargo.lock b/Cargo.lock index 5e318dab087d1..4f5eae9c31b12 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6180,10 +6180,14 @@ dependencies = [ name = "pallet-safe-mode" version = "4.0.0-dev" dependencies = [ + "frame-benchmarking", "frame-support", "frame-system", + "pallet-balances", "parity-scale-codec", "scale-info", + "sp-core", + "sp-io", "sp-runtime", "sp-std", ] diff --git a/bin/node/cli/src/chain_spec.rs b/bin/node/cli/src/chain_spec.rs index 77e2f73dd6e18..b549f5f6ed0c7 100644 --- a/bin/node/cli/src/chain_spec.rs +++ b/bin/node/cli/src/chain_spec.rs @@ -365,6 +365,8 @@ pub fn testnet_genesis( transaction_storage: Default::default(), transaction_payment: Default::default(), alliance: Default::default(), + safe_mode: Default::default(), + tx_pause: Default::default(), alliance_motion: Default::default(), nomination_pools: NominationPoolsConfig { min_create_bond: 10 * DOLLARS, diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 735e8bff551d4..edf4873a50d0c 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -220,14 +220,26 @@ impl pallet_tx_pause::Config for Runtime { type PauseTooLongNames = ConstBool; } +parameter_types! { + // signed config + pub const EnableStakeAmount: Balance = 1 * DOLLARS; //TODO This needs to be something sensible for the implications of enablement! + pub const ExtendStakeAmount: Balance = 1 * DOLLARS; //TODO This needs to be something sensible for the implications of enablement! + pub BlockHeight: BlockNumber = System::block_number(); // TODO ensure this plus config below is correct +} + impl pallet_safe_mode::Config for Runtime { type Event = Event; + type Currency = Balances; type SafeModeFilter = Nothing; // TODO add TxPause pallet type EnableDuration = ConstU32<{ 2 * DAYS }>; type ExtendDuration = ConstU32<{ 1 * DAYS }>; - type EnableOrigin = EnsureRoot; - type ExtendOrigin = EnsureRoot; - type PreemptiveDisableOrigin = EnsureRoot; + type EnableOrigin = EnsureRootWithSuccess; + type ExtendOrigin = EnsureRootWithSuccess; + type DisableOrigin = EnsureRoot; + type RepayOrigin = EnsureRoot; + type EnableStakeAmount = EnableStakeAmount; + type ExtendStakeAmount = ExtendStakeAmount; + type WeightInfo = pallet_safe_mode::weights::SubstrateWeight; } impl frame_system::Config for Runtime { @@ -1795,6 +1807,7 @@ mod benches { [pallet_utility, Utility] [pallet_vesting, Vesting] [pallet_whitelist, Whitelist] + [pallet_safe_mode, SafeMode] ); } diff --git a/frame/remark/src/tests.rs b/frame/remark/src/tests.rs index 2278e3817b48a..bbee84282d212 100644 --- a/frame/remark/src/tests.rs +++ b/frame/remark/src/tests.rs @@ -20,7 +20,6 @@ use super::{Error, Event, Pallet as Remark}; use crate::mock::*; use frame_support::{assert_noop, assert_ok}; -use frame_system::RawOrigin; #[test] fn generates_event() { diff --git a/frame/safe-mode/Cargo.toml b/frame/safe-mode/Cargo.toml index 7ad7e83e1caf8..2ec371617b8cc 100644 --- a/frame/safe-mode/Cargo.toml +++ b/frame/safe-mode/Cargo.toml @@ -14,25 +14,32 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } -scale-info = { version = "2.0.1", default-features = false, features = ["derive"] } +frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../benchmarking" } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } frame-system = { version = "4.0.0-dev", default-features = false, path = "../system" } +scale-info = { version = "2.0.1", default-features = false, features = ["derive"] } sp-runtime = { version = "6.0.0", default-features = false, path = "../../primitives/runtime" } sp-std = { version = "4.0.0", default-features = false, path = "../../primitives/std" } [dev-dependencies] +sp-core = { version = "6.0.0", path = "../../primitives/core" } +sp-std = { version = "4.0.0", path = "../../primitives/std" } +sp-io = { version = "6.0.0", path = "../../primitives/io" } +pallet-balances = { version = "4.0.0-dev", path = "../balances" } [features] default = ["std"] std = [ "codec/std", "scale-info/std", + "frame-benchmarking/std", "frame-support/std", "frame-system/std", "sp-runtime/std", "sp-std/std", ] runtime-benchmarks = [ + "frame-benchmarking/runtime-benchmarks", "frame-support/runtime-benchmarks", "frame-system/runtime-benchmarks", ] diff --git a/frame/safe-mode/src/benchmarking.rs b/frame/safe-mode/src/benchmarking.rs index 08f16881d7231..6028bd816fa74 100644 --- a/frame/safe-mode/src/benchmarking.rs +++ b/frame/safe-mode/src/benchmarking.rs @@ -16,3 +16,80 @@ // limitations under the License. #![cfg(feature = "runtime-benchmarks")] + +use super::{Call as SafeModeCall, Pallet as SafeMode, *}; + +use frame_benchmarking::{benchmarks, whitelisted_caller}; +use frame_support::traits::Currency; +use frame_system::{Pallet as System, RawOrigin}; +use sp_runtime::traits::Bounded; + +benchmarks! { + enable { + let caller: T::AccountId = whitelisted_caller(); + let origin = RawOrigin::Signed(caller.clone()); + T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); + }: enable(origin) + verify { + assert_eq!( + SafeMode::::enabled().unwrap(), + System::::block_number() + T::EnableDuration::get() + ); + } + +// force_enable { +// /* code to set the initial state */ +// }: { +// /* code to test the function benchmarked */ +// } +// verify { +// /* optional verification */ +// } + +// extend { +// /* code to set the initial state */ +// }: { +// /* code to test the function benchmarked */ +// } +// verify { +// /* optional verification */ +// } + +// force_extend { +// /* code to set the initial state */ +// }: { +// /* code to test the function benchmarked */ +// } +// verify { +// /* optional verification */ +// } + +// force_disable { +// /* code to set the initial state */ +// }: { +// /* code to test the function benchmarked */ +// } +// verify { +// /* optional verification */ +// } + +// repay_stake { +// /* code to set the initial state */ +// }: { +// /* code to test the function benchmarked */ +// } +// verify { +// /* optional verification */ +// } + +// slash_stake { +// /* code to set the initial state */ +// }: { +// /* code to test the function benchmarked */ +// } +// verify { +// /* optional verification */ +// } + + impl_benchmark_test_suite!(SafeMode, crate::mock::new_test_ext(), crate::mock::Test); +} \ No newline at end of file diff --git a/frame/safe-mode/src/lib.rs b/frame/safe-mode/src/lib.rs index 234d20057d766..0d315f45a902b 100644 --- a/frame/safe-mode/src/lib.rs +++ b/frame/safe-mode/src/lib.rs @@ -17,22 +17,28 @@ #![cfg_attr(not(feature = "std"), no_std)] +#[cfg(feature = "runtime-benchmarks")] +mod benchmarking; +#[cfg(test)] +pub mod mock; +#[cfg(test)] +mod tests; +pub mod weights; + use frame_support::{ pallet_prelude::*, traits::{ CallMetadata, Contains, Currency, Defensive, GetCallMetadata, PalletInfoAccess, ReservableCurrency, }, + weights::Weight, }; use frame_system::pallet_prelude::*; use sp_runtime::traits::Saturating; use sp_std::{convert::TryInto, prelude::*}; -mod benchmarking; -mod mock; -mod tests; - pub use pallet::*; +pub use weights::*; type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; @@ -97,7 +103,7 @@ pub mod pallet { type RepayOrigin: EnsureOrigin; // Weight information for extrinsics in this pallet. - //type WeightInfo: WeightInfo; + type WeightInfo: WeightInfo; } #[pallet::error] @@ -149,10 +155,12 @@ pub mod pallet { /// This is set to `None` if the safe-mode is disabled. /// The safe-mode is automatically disabled when the current block number is greater than this. #[pallet::storage] + #[pallet::getter(fn enabled)] pub type Enabled = StorageValue<_, T::BlockNumber, OptionQuery>; /// Holds the stake that was reserved from a user at a specific block number. #[pallet::storage] + #[pallet::getter(fn stakes)] pub type Stakes = StorageDoubleMap< _, Twox64Concat, @@ -196,7 +204,8 @@ pub mod pallet { /// Reserves `EnableStakeAmount` from the caller's account. /// Errors if the safe-mode is already enabled. /// Can be permanently disabled by configuring `EnableStakeAmount` to `None`. - #[pallet::weight(0)] + #[pallet::weight(T::WeightInfo::enable())] + // #[pallet::weight(0)] pub fn enable(origin: OriginFor) -> DispatchResult { let who = ensure_signed(origin)?; diff --git a/frame/safe-mode/src/mock.rs b/frame/safe-mode/src/mock.rs index 95c25725a4f6d..04ab0024e492c 100644 --- a/frame/safe-mode/src/mock.rs +++ b/frame/safe-mode/src/mock.rs @@ -15,4 +15,281 @@ // See the License for the specific language governing permissions and // limitations under the License. -#![cfg(any(test, feature = "runtime-benchmarks"))] +//! Test utilities for safe mode pallet. + +use super::*; +use crate as pallet_safe_mode; + +use frame_support::{ + parameter_types, + traits::{Everything, InsideBoth, SortedMembers}, +}; +use frame_system::{EnsureSignedBy, RawOrigin}; +use sp_core::H256; +use sp_runtime::{ + testing::Header, + traits::{BlakeTwo256, IdentityLookup}, + BuildStorage, +}; + +parameter_types! { + pub const BlockHashCount: u64 = 250; +} +impl frame_system::Config for Test { + type BaseCallFilter = InsideBoth; + type BlockWeights = (); + type BlockLength = (); + type Origin = Origin; + type Call = Call; + type Index = u64; + type BlockNumber = u64; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = u64; + type Lookup = IdentityLookup; + type Header = Header; + type Event = Event; + type BlockHashCount = BlockHashCount; + type DbWeight = (); + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type SS58Prefix = (); + type OnSetCode = (); + type MaxConsumers = frame_support::traits::ConstU32<16>; +} + +parameter_types! { + pub const ExistentialDeposit: u64 = 1; + pub const MaxLocks: u32 = 10; +} +impl pallet_balances::Config for Test { + type Balance = u64; + type DustRemoval = (); + type Event = Event; + type ExistentialDeposit = ExistentialDeposit; + type AccountStore = System; + type WeightInfo = (); + type MaxLocks = MaxLocks; + type MaxReserves = (); + type ReserveIdentifier = [u8; 8]; +} + +/// Filter to block balance pallet calls +pub struct MockSafeModeFilter; +impl Contains for MockSafeModeFilter { + fn contains(call: &Call) -> bool { + match call { + Call::System(_) | Call::SafeMode(_) => true, + Call::Balances(_) => false, + } + } +} + +/// An origin that can enable the safe-mode by force. +pub enum ForceEnableOrigin { + Weak, + Medium, + Strong, +} + +/// An origin that can extend the safe-mode by force. +pub enum ForceExtendOrigin { + Weak, + Medium, + Strong, +} + +impl ForceEnableOrigin { + /// The duration of how long the safe-mode will be enabled. + pub fn duration(&self) -> u64 { + match self { + Self::Weak => 5, + Self::Medium => 7, + Self::Strong => 11, + } + } + + /// Account id of the origin. + pub const fn acc(&self) -> u64 { + match self { + Self::Weak => 100, + Self::Medium => 101, + Self::Strong => 102, + } + } + + /// Signed origin. + pub fn signed(&self) -> ::Origin { + RawOrigin::Signed(self.acc()).into() + } +} + +impl ForceExtendOrigin { + /// The duration of how long the safe-mode will be extended. + pub fn duration(&self) -> u64 { + match self { + Self::Weak => 13, + Self::Medium => 17, + Self::Strong => 19, + } + } + + /// Account id of the origin. + pub const fn acc(&self) -> u64 { + match self { + Self::Weak => 200, + Self::Medium => 201, + Self::Strong => 202, + } + } + + /// Signed origin. + pub fn signed(&self) -> ::Origin { + RawOrigin::Signed(self.acc()).into() + } +} + +impl, O>> + From> + std::fmt::Debug> EnsureOrigin + for ForceEnableOrigin +{ + type Success = u64; + + fn try_origin(o: O) -> Result { + o.into().and_then(|o| match o { + RawOrigin::Signed(acc) if acc == ForceEnableOrigin::Weak.acc() => + Ok(ForceEnableOrigin::Weak.duration()), + RawOrigin::Signed(acc) if acc == ForceEnableOrigin::Medium.acc() => + Ok(ForceEnableOrigin::Medium.duration()), + RawOrigin::Signed(acc) if acc == ForceEnableOrigin::Strong.acc() => + Ok(ForceEnableOrigin::Strong.duration()), + r => Err(O::from(r)), + }) + } +} + +impl, O>> + From> + std::fmt::Debug> EnsureOrigin + for ForceExtendOrigin +{ + type Success = u64; + + fn try_origin(o: O) -> Result { + o.into().and_then(|o| match o { + RawOrigin::Signed(acc) if acc == ForceExtendOrigin::Weak.acc() => + Ok(ForceExtendOrigin::Weak.duration()), + RawOrigin::Signed(acc) if acc == ForceExtendOrigin::Medium.acc() => + Ok(ForceExtendOrigin::Medium.duration()), + RawOrigin::Signed(acc) if acc == ForceExtendOrigin::Strong.acc() => + Ok(ForceExtendOrigin::Strong.duration()), + r => Err(O::from(r)), + }) + } +} + +parameter_types! { + pub const EnableDuration: u64 = 3; + pub const ExtendDuration: u64 = 30; + pub const EnableStakeAmount: u64 = 100; //TODO This needs to be something sensible for the implications of enablement! + pub const ExtendStakeAmount: u64 = 100; //TODO This needs to be something sensible for the implications of enablement! + pub const DisableOrigin: u64 =3; + pub const RepayOrigin: u64 = 4; +} + +// Required impl to use some ::get() in tests +impl SortedMembers for DisableOrigin { + fn sorted_members() -> Vec { + vec![Self::get()] + } + #[cfg(feature = "runtime-benchmarks")] + fn add(_m: &u64) {} +} +impl SortedMembers for RepayOrigin { + fn sorted_members() -> Vec { + vec![Self::get()] + } + #[cfg(feature = "runtime-benchmarks")] + fn add(_m: &u64) {} +} + +impl Config for Test { + type Event = Event; + type Currency = Balances; + type SafeModeFilter = MockSafeModeFilter; + type EnableDuration = EnableDuration; + type ExtendDuration = ExtendDuration; + type EnableOrigin = ForceEnableOrigin; + type ExtendOrigin = ForceExtendOrigin; + type DisableOrigin = EnsureSignedBy; + type RepayOrigin = EnsureSignedBy; + type EnableStakeAmount = EnableStakeAmount; + type ExtendStakeAmount = ExtendStakeAmount; + type WeightInfo = (); +} + +type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; +type Block = frame_system::mocking::MockBlock; + +frame_support::construct_runtime!( + pub enum Test where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic, + { + System: frame_system, + Balances: pallet_balances, + SafeMode: pallet_safe_mode, + } +); + +pub fn new_test_ext() -> sp_io::TestExternalities { + let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap(); + + pallet_balances::GenesisConfig:: { + balances: vec![(0, 1234), (1, 5678), (2, 5678), (3, 5678), (4, 5678)], /* The 0 account + * is NOT a special + * origin, the + * rest may be. */ + } + .assimilate_storage(&mut t) + .unwrap(); + + // TODO requires a GenesisConfig impl + // GenesisBuild::::assimilate_storage( + // &pallet_safe_mode::GenesisConfig { + // enabled: None, + // stakes: None, + // }, + // &mut t, + // ) + // .unwrap(); + + let mut ext = sp_io::TestExternalities::new(t); + ext.execute_with(|| { + System::set_block_number(1); + }); + ext +} + +#[cfg(feature = "runtime-benchmarks")] +pub fn new_bench_ext() -> sp_io::TestExternalities { + GenesisConfig::default().build_storage().unwrap().into() +} + +pub fn next_block() { + SafeMode::on_finalize(System::block_number()); + Balances::on_finalize(System::block_number()); + System::on_finalize(System::block_number()); + System::set_block_number(System::block_number() + 1); + System::on_initialize(System::block_number()); + Balances::on_initialize(System::block_number()); + SafeMode::on_initialize(System::block_number()); +} + +pub fn run_to(n: u64) { + while System::block_number() < n { + next_block(); + } +} diff --git a/frame/safe-mode/src/tests.rs b/frame/safe-mode/src/tests.rs index ee33686e4a39f..4ccf4e8cd6411 100644 --- a/frame/safe-mode/src/tests.rs +++ b/frame/safe-mode/src/tests.rs @@ -15,4 +15,321 @@ // See the License for the specific language governing permissions and // limitations under the License. -#![cfg(test)] +//! Test utilities for the safe mode pallet. + +use super::*; +use crate::mock::{Call, *}; + +use frame_support::{assert_err, assert_noop, assert_ok, dispatch::Dispatchable}; + +// GENERAL FAIL/NEGATIVE TESTS --------------------- + +#[test] +fn fails_to_filter_calls_to_safe_mode_pallet() { + new_test_ext().execute_with(|| { + assert_ok!(SafeMode::enable(Origin::signed(0))); + let enabled_at_block = System::block_number(); + let call = Call::Balances(pallet_balances::Call::transfer { dest: 1, value: 1 }); + + assert_err!( + call.clone().dispatch(Origin::signed(0)), + frame_system::Error::::CallFiltered + ); + // TODO ^^^ consider refactor to throw a safe mode error, not generic `CallFiltered` + + next_block(); + assert_ok!(SafeMode::extend(Origin::signed(0))); + assert_ok!(SafeMode::force_extend(ForceExtendOrigin::Weak.signed())); + assert_err!( + call.clone().dispatch(Origin::signed(0)), + frame_system::Error::::CallFiltered + ); + assert_ok!(SafeMode::force_disable(Origin::signed(mock::DisableOrigin::get()))); + assert_ok!(SafeMode::repay_stake( + Origin::signed(mock::RepayOrigin::get()), + 0, + enabled_at_block + )); + + next_block(); + assert_ok!(SafeMode::enable(Origin::signed(0))); + assert_err!( + call.clone().dispatch(Origin::signed(0)), + frame_system::Error::::CallFiltered + ); + assert_ok!(SafeMode::force_disable(Origin::signed(mock::DisableOrigin::get()))); + assert_ok!(SafeMode::slash_stake( + Origin::signed(mock::RepayOrigin::get()), + 0, + enabled_at_block + 2 + )); + }); +} + +#[test] +fn fails_to_extend_if_not_enabled() { + new_test_ext().execute_with(|| { + assert_eq!(SafeMode::enabled(), None); + assert_noop!(SafeMode::extend(Origin::signed(2)), Error::::IsDisabled); + }); +} + +// GENERAL SUCCESS/POSITIVE TESTS --------------------- + +#[test] +fn can_automatically_disable_after_timeout() { + new_test_ext().execute_with(|| { + let enabled_at_block = System::block_number(); + assert_ok!(SafeMode::force_enable(ForceEnableOrigin::Weak.signed())); + run_to(ForceEnableOrigin::Weak.duration() + enabled_at_block + 1); + SafeMode::on_initialize(System::block_number()); + assert_eq!(SafeMode::enabled(), None); + }); +} + +#[test] +fn can_filter_balance_calls_when_enabled() { + new_test_ext().execute_with(|| { + let call = Call::Balances(pallet_balances::Call::transfer { dest: 1, value: 1 }); + + assert_ok!(call.clone().dispatch(Origin::signed(0))); + assert_ok!(SafeMode::enable(Origin::signed(0))); + assert_err!( + call.clone().dispatch(Origin::signed(0)), + frame_system::Error::::CallFiltered + ); + // TODO ^^^ consider refactor to throw a safe mode error, not generic `CallFiltered` + }); +} + +// SIGNED ORIGIN CALL TESTS --------------------- + +#[test] +fn can_enable_with_signed_origin() { + new_test_ext().execute_with(|| { + assert_ok!(SafeMode::enable(Origin::signed(0))); + assert_eq!( + SafeMode::enabled().unwrap(), + System::block_number() + mock::EnableDuration::get() + ); + assert_eq!(Balances::reserved_balance(0), mock::EnableStakeAmount::get()); + assert_noop!(SafeMode::enable(Origin::signed(0)), Error::::IsEnabled); + // Assert the stake. + assert_eq!(Stakes::::get(0, 1), Some(mock::EnableStakeAmount::get())); + }); +} + +#[test] +fn can_extend_with_signed_origin() { + new_test_ext().execute_with(|| { + assert_ok!(SafeMode::enable(Origin::signed(0))); + assert_ok!(SafeMode::extend(Origin::signed(0))); + assert_eq!( + SafeMode::enabled().unwrap(), + System::block_number() + mock::EnableDuration::get() + mock::ExtendDuration::get() + ); + assert_eq!( + Balances::reserved_balance(0), + mock::EnableStakeAmount::get() + mock::ExtendStakeAmount::get() + ); + }); +} + +#[test] +fn fails_signed_origin_when_explicit_origin_required() { + new_test_ext().execute_with(|| { + assert_eq!(SafeMode::enabled(), None); + let enabled_at_block = System::block_number(); + + assert_err!(SafeMode::force_enable(Origin::signed(0)), DispatchError::BadOrigin); + assert_err!(SafeMode::force_extend(Origin::signed(0)), DispatchError::BadOrigin); + assert_err!(SafeMode::force_disable(Origin::signed(0)), DispatchError::BadOrigin); + assert_err!( + SafeMode::slash_stake(Origin::signed(0), 0, enabled_at_block), + DispatchError::BadOrigin + ); + assert_err!( + SafeMode::repay_stake(Origin::signed(0), 0, enabled_at_block), + DispatchError::BadOrigin + ); + }); +} + +// CONFIGURED ORIGIN CALL TESTS --------------------- + +#[test] +fn fails_force_disable_if_not_enabled() { + new_test_ext().execute_with(|| { + assert_noop!( + SafeMode::force_disable(Origin::signed(mock::DisableOrigin::get())), + Error::::IsDisabled + ); + assert_noop!( + SafeMode::force_disable(Origin::signed(mock::DisableOrigin::get())), + Error::::IsDisabled + ); + }); +} + +#[test] +fn can_force_enable_with_config_origin() { + new_test_ext().execute_with(|| { + assert_ok!(SafeMode::force_enable(ForceEnableOrigin::Weak.signed())); + assert_eq!( + SafeMode::enabled().unwrap(), + System::block_number() + ForceEnableOrigin::Weak.duration() + ); + assert_noop!( + SafeMode::force_enable(ForceEnableOrigin::Weak.signed()), + Error::::IsEnabled + ); + assert_eq!(Balances::reserved_balance(ForceEnableOrigin::Weak.acc()), 0); + }); +} + +#[test] +fn can_force_disable_with_config_origin() { + new_test_ext().execute_with(|| { + assert_eq!(SafeMode::enabled(), None); + assert_err!( + SafeMode::force_disable(Origin::signed(mock::DisableOrigin::get())), + Error::::IsDisabled + ); + assert_ok!(SafeMode::force_enable(ForceEnableOrigin::Weak.signed())); + assert_eq!(Balances::reserved_balance(ForceEnableOrigin::Weak.acc()), 0); + assert_ok!(SafeMode::force_disable(Origin::signed(mock::DisableOrigin::get()))); + }); +} + +#[test] +fn can_force_extend_with_config_origin() { + new_test_ext().execute_with(|| { + // Enable by `Weak` and extended by `Medium`. + assert_ok!(SafeMode::force_enable(ForceEnableOrigin::Weak.signed())); + assert_eq!( + SafeMode::enabled().unwrap(), + System::block_number() + ForceEnableOrigin::Weak.duration() + ); + assert_ok!(SafeMode::force_extend(ForceExtendOrigin::Medium.signed())); + assert_eq!( + SafeMode::enabled().unwrap(), + System::block_number() + + ForceEnableOrigin::Weak.duration() + + ForceExtendOrigin::Medium.duration() + ); + assert_eq!(Balances::reserved_balance(ForceEnableOrigin::Weak.acc()), 0); + assert_eq!(Balances::reserved_balance(mock::ExtendDuration::get()), 0); + }); +} + +#[test] +fn can_repay_stake_with_config_origin() { + new_test_ext().execute_with(|| { + let enabled_at_block = System::block_number(); + assert_ok!(SafeMode::enable(Origin::signed(0))); + assert_err!( + SafeMode::repay_stake(Origin::signed(mock::RepayOrigin::get()), 0, enabled_at_block), + Error::::IsEnabled + ); + run_to(mock::EnableDuration::get() + enabled_at_block + 1); + SafeMode::on_initialize(System::block_number()); + assert_ok!(SafeMode::repay_stake( + Origin::signed(mock::RepayOrigin::get()), + 0, + enabled_at_block + )); + // TODO: test accounting is correct + }); +} + +#[test] +fn can_slash_stake_with_config_origin() { + new_test_ext().execute_with(|| { + let enabled_at_block = System::block_number(); + assert_ok!(SafeMode::enable(Origin::signed(0))); + assert_err!( + SafeMode::slash_stake(Origin::signed(mock::RepayOrigin::get()), 0, enabled_at_block), + Error::::IsEnabled + ); + run_to(mock::EnableDuration::get() + enabled_at_block + 1); + SafeMode::on_initialize(System::block_number()); + assert_ok!(SafeMode::slash_stake( + Origin::signed(mock::RepayOrigin::get()), + 0, + enabled_at_block + )); + // TODO: test accounting is correct + }); +} + +#[test] +fn fails_when_explicit_origin_required() { + new_test_ext().execute_with(|| { + assert_eq!(SafeMode::enabled(), None); + let enabled_at_block = System::block_number(); + + assert_err!( + SafeMode::force_extend(ForceEnableOrigin::Weak.signed()), + DispatchError::BadOrigin + ); + assert_err!( + SafeMode::force_disable(ForceEnableOrigin::Weak.signed()), + DispatchError::BadOrigin + ); + assert_err!( + SafeMode::slash_stake(ForceEnableOrigin::Weak.signed(), 0, enabled_at_block), + DispatchError::BadOrigin + ); + assert_err!( + SafeMode::repay_stake(ForceEnableOrigin::Weak.signed(), 0, enabled_at_block), + DispatchError::BadOrigin + ); + + assert_err!( + SafeMode::force_enable(ForceExtendOrigin::Weak.signed()), + DispatchError::BadOrigin + ); + assert_err!( + SafeMode::force_disable(ForceExtendOrigin::Weak.signed()), + DispatchError::BadOrigin + ); + assert_err!( + SafeMode::slash_stake(ForceExtendOrigin::Weak.signed(), 0, enabled_at_block), + DispatchError::BadOrigin + ); + assert_err!( + SafeMode::repay_stake(ForceExtendOrigin::Weak.signed(), 0, enabled_at_block), + DispatchError::BadOrigin + ); + + assert_err!( + SafeMode::force_enable(Origin::signed(mock::DisableOrigin::get())), + DispatchError::BadOrigin + ); + assert_err!( + SafeMode::force_extend(Origin::signed(mock::DisableOrigin::get())), + DispatchError::BadOrigin + ); + assert_err!( + SafeMode::slash_stake(Origin::signed(mock::DisableOrigin::get()), 0, enabled_at_block), + DispatchError::BadOrigin + ); + assert_err!( + SafeMode::repay_stake(Origin::signed(mock::DisableOrigin::get()), 0, enabled_at_block), + DispatchError::BadOrigin + ); + + assert_err!( + SafeMode::force_enable(Origin::signed(mock::RepayOrigin::get())), + DispatchError::BadOrigin + ); + assert_err!( + SafeMode::force_extend(Origin::signed(mock::RepayOrigin::get())), + DispatchError::BadOrigin + ); + assert_err!( + SafeMode::force_disable(Origin::signed(mock::RepayOrigin::get())), + DispatchError::BadOrigin + ); + }); +} diff --git a/frame/safe-mode/src/weights.rs b/frame/safe-mode/src/weights.rs new file mode 100644 index 0000000000000..c96de6f978947 --- /dev/null +++ b/frame/safe-mode/src/weights.rs @@ -0,0 +1,82 @@ +// This file is part of Substrate. + +// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Autogenerated weights for pallet_safe_mode +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2022-09-15, STEPS: `1`, REPEAT: 1, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! HOSTNAME: `pop-os`, CPU: `12th Gen Intel(R) Core(TM) i7-12700H` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 + +// Executed Command: +// ./target/release/substrate +// benchmark +// pallet +// --steps +// 1 +// --repeat +// 1 +// --extrinsic +// * +// --execution +// wasm +// --wasm-execution +// compiled +// --heap-pages +// 4096 +// --pallet +// pallet_safe_mode +// --chain +// dev +// --output +// frame/safe-mode/src/weights.rs +// --template=./.maintain/frame-weight-template.hbs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use sp_std::marker::PhantomData; + +/// Weight functions needed for pallet_safe_mode. +pub trait WeightInfo { + fn enable() -> Weight; +} + +/// Weights for pallet_safe_mode using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl WeightInfo for SubstrateWeight { + // Storage: SafeMode Stakes (r:1 w:1) + // Storage: SafeMode Enabled (r:1 w:1) + fn enable() -> Weight { + Weight::from_ref_time(51_688_000 as u64) + .saturating_add(T::DbWeight::get().reads(2 as u64)) + .saturating_add(T::DbWeight::get().writes(2 as u64)) + } +} + +// For backwards compatibility and tests +impl WeightInfo for () { + // Storage: SafeMode Stakes (r:1 w:1) + // Storage: SafeMode Enabled (r:1 w:1) + fn enable() -> Weight { + Weight::from_ref_time(51_688_000 as u64) + .saturating_add(RocksDbWeight::get().reads(2 as u64)) + .saturating_add(RocksDbWeight::get().writes(2 as u64)) + } +} From 7f8688e7755267db7457330d08e7e5dcd3ff4d09 Mon Sep 17 00:00:00 2001 From: Dan Shields <35669742+NukeManDan@users.noreply.github.com> Date: Wed, 21 Sep 2022 15:05:03 -0600 Subject: [PATCH 020/100] Tests for `pallet-tx-pause` (#12259) * mock added * tests added * dummy benchmarks started --- Cargo.lock | 5 + bin/node/cli/src/chain_spec.rs | 2 + bin/node/runtime/src/lib.rs | 9 ++ bin/node/testing/src/genesis.rs | 2 + frame/tx-pause/Cargo.toml | 13 ++- frame/tx-pause/src/benchmarking.rs | 40 ++++++++ frame/tx-pause/src/lib.rs | 20 ++-- frame/tx-pause/src/mock.rs | 159 ++++++++++++++++++++++++++++- frame/tx-pause/src/tests.rs | 149 +++++++++++++++++++++++++++ frame/tx-pause/src/weights.rs | 93 +++++++++++++++++ 10 files changed, 483 insertions(+), 9 deletions(-) create mode 100644 frame/tx-pause/src/weights.rs diff --git a/Cargo.lock b/Cargo.lock index 4f5eae9c31b12..1e624fe65b33d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6509,10 +6509,15 @@ dependencies = [ name = "pallet-tx-pause" version = "4.0.0-dev" dependencies = [ + "frame-benchmarking", "frame-support", "frame-system", + "pallet-balances", "parity-scale-codec", "scale-info", + "sp-core", + "sp-io", + "sp-runtime", "sp-std", ] diff --git a/bin/node/cli/src/chain_spec.rs b/bin/node/cli/src/chain_spec.rs index b549f5f6ed0c7..61e051120ffbe 100644 --- a/bin/node/cli/src/chain_spec.rs +++ b/bin/node/cli/src/chain_spec.rs @@ -373,6 +373,8 @@ pub fn testnet_genesis( min_join_bond: 1 * DOLLARS, ..Default::default() }, + safe_mode: Default::default(), + tx_pause: Default::default(), } } diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index edf4873a50d0c..ff377c6e6ebc2 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -218,6 +218,14 @@ impl pallet_tx_pause::Config for Runtime { type UnpauseOrigin = EnsureRoot; type MaxNameLen = ConstU32<256>; type PauseTooLongNames = ConstBool; + type WeightInfo = pallet_tx_pause::weights::SubstrateWeight; +} + +parameter_types! { + // signed config + pub const EnableStakeAmount: Balance = 1 * DOLLARS; //TODO This needs to be something sensible for the implications of enablement! + pub const ExtendStakeAmount: Balance = 1 * DOLLARS; //TODO This needs to be something sensible for the implications of enablement! + pub BlockHeight: BlockNumber = System::block_number(); // TODO ensure this plus config below is correct } parameter_types! { @@ -1807,6 +1815,7 @@ mod benches { [pallet_utility, Utility] [pallet_vesting, Vesting] [pallet_whitelist, Whitelist] + [pallet_tx_pause, TxPause] [pallet_safe_mode, SafeMode] ); } diff --git a/bin/node/testing/src/genesis.rs b/bin/node/testing/src/genesis.rs index 1eb7318db52da..2c857bcba01fc 100644 --- a/bin/node/testing/src/genesis.rs +++ b/bin/node/testing/src/genesis.rs @@ -95,5 +95,7 @@ pub fn config_endowed(code: Option<&[u8]>, extra_endowed: Vec) -> Gen alliance: Default::default(), alliance_motion: Default::default(), nomination_pools: Default::default(), + safe_mode: Default::default(), + tx_pause: Default::default(), } } diff --git a/frame/tx-pause/Cargo.toml b/frame/tx-pause/Cargo.toml index 6b001ae9c386f..da32e18ef387c 100644 --- a/frame/tx-pause/Cargo.toml +++ b/frame/tx-pause/Cargo.toml @@ -14,23 +14,34 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } -scale-info = { version = "2.0.1", default-features = false, features = ["derive"] } +frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../benchmarking" } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } frame-system = { version = "4.0.0-dev", default-features = false, path = "../system" } +scale-info = { version = "2.0.1", default-features = false, features = ["derive"] } +sp-runtime = { version = "6.0.0", default-features = false, path = "../../primitives/runtime" } sp-std = { version = "4.0.0", default-features = false, path = "../../primitives/std" } + [dev-dependencies] +sp-core = { version = "6.0.0", path = "../../primitives/core" } +sp-std = { version = "4.0.0", path = "../../primitives/std" } +sp-io = { version = "6.0.0", path = "../../primitives/io" } +pallet-balances = { version = "4.0.0-dev", path = "../balances" } + [features] default = ["std"] std = [ "codec/std", "scale-info/std", + "frame-benchmarking/std", "frame-support/std", "frame-system/std", + "sp-runtime/std", "sp-std/std", ] runtime-benchmarks = [ + "frame-benchmarking/runtime-benchmarks", "frame-support/runtime-benchmarks", "frame-system/runtime-benchmarks", ] diff --git a/frame/tx-pause/src/benchmarking.rs b/frame/tx-pause/src/benchmarking.rs index 08f16881d7231..d605e8fef826a 100644 --- a/frame/tx-pause/src/benchmarking.rs +++ b/frame/tx-pause/src/benchmarking.rs @@ -16,3 +16,43 @@ // limitations under the License. #![cfg(feature = "runtime-benchmarks")] + +use super::{Pallet as TxPause, *}; + +use frame_benchmarking::benchmarks; +use frame_support::traits::UnfilteredDispatchable; + +benchmarks! { + pause_call { + let pallet: PalletNameOf = b"SomePalletName".to_vec().try_into().unwrap(); + let function: FunctionNameOf = b"some_fn_name".to_vec().try_into().unwrap(); + let origin = T::PauseOrigin::successful_origin(); + let call = Call::::pause_call { pallet: pallet.clone(), function: function.clone() }; + + }: { call.dispatch_bypass_filter(origin)?} + verify { + assert![TxPause::::paused_calls((pallet.clone(),function.clone())).is_some()] + } + + unpause_call { + let pallet: PalletNameOf = b"SomePalletName".to_vec().try_into().unwrap(); + let function: FunctionNameOf = b"some_fn_name".to_vec().try_into().unwrap(); + let pause_origin = T::PauseOrigin::successful_origin(); + + // Set + TxPause::::pause_call( + pause_origin, + pallet.clone(), + function.clone(), + )?; + + let unpause_origin = T::UnpauseOrigin::successful_origin(); + let call = Call::::unpause_call { pallet: pallet.clone(), function: function.clone() }; + + }: { call.dispatch_bypass_filter(unpause_origin)?} + verify { + assert![TxPause::::paused_calls((pallet.clone(),function.clone())).is_none()] + } + + impl_benchmark_test_suite!(TxPause, crate::mock::new_test_ext(), crate::mock::Test); +} diff --git a/frame/tx-pause/src/lib.rs b/frame/tx-pause/src/lib.rs index e4c90028dc860..cfa31d3d8d6cb 100644 --- a/frame/tx-pause/src/lib.rs +++ b/frame/tx-pause/src/lib.rs @@ -17,6 +17,14 @@ #![cfg_attr(not(feature = "std"), no_std)] +#[cfg(feature = "runtime-benchmarks")] +mod benchmarking; +#[cfg(test)] +pub mod mock; +#[cfg(test)] +mod tests; +pub mod weights; + use frame_support::{ pallet_prelude::*, traits::{CallMetadata, Contains, GetCallMetadata}, @@ -24,11 +32,8 @@ use frame_support::{ use frame_system::pallet_prelude::*; use sp_std::{convert::TryInto, prelude::*}; -mod benchmarking; -mod mock; -mod tests; - pub use pallet::*; +pub use weights::*; pub type PalletNameOf = BoundedVec::MaxNameLen>; pub type CallNameOf = BoundedVec::MaxNameLen>; @@ -74,7 +79,7 @@ pub mod pallet { type PauseTooLongNames: Get; // Weight information for extrinsics in this pallet. - //type WeightInfo: WeightInfo; + type WeightInfo: WeightInfo; } #[pallet::error] @@ -100,6 +105,7 @@ pub mod pallet { /// The set of calls that are explicitly paused. #[pallet::storage] + #[pallet::getter(fn paused_calls)] pub type PausedCalls = StorageMap<_, Blake2_128Concat, (PalletNameOf, CallNameOf), (), OptionQuery>; @@ -135,7 +141,7 @@ pub mod pallet { /// /// Can only be called by [`Config::PauseOrigin`]. /// Emits an [`Event::CallPaused`] event on success. - #[pallet::weight(0)] + #[pallet::weight(T::WeightInfo::pause_call())] pub fn pause_call( origin: OriginFor, pallet: PalletNameOf, @@ -154,7 +160,7 @@ pub mod pallet { /// /// Can only be called by [`Config::UnpauseOrigin`]. /// Emits an [`Event::CallUnpaused`] event on success. - #[pallet::weight(0)] + #[pallet::weight(T::WeightInfo::unpause_call())] pub fn unpause_call( origin: OriginFor, pallet: PalletNameOf, diff --git a/frame/tx-pause/src/mock.rs b/frame/tx-pause/src/mock.rs index 95c25725a4f6d..a3a128893f348 100644 --- a/frame/tx-pause/src/mock.rs +++ b/frame/tx-pause/src/mock.rs @@ -15,4 +15,161 @@ // See the License for the specific language governing permissions and // limitations under the License. -#![cfg(any(test, feature = "runtime-benchmarks"))] +//! Test utilities for transaction pause (tx pause) pallet. + +use super::*; +use crate as pallet_tx_pause; + +use frame_support::{ + parameter_types, + traits::{Everything, InsideBoth, SortedMembers}, +}; +use frame_system::EnsureSignedBy; +use sp_core::H256; +use sp_runtime::{ + testing::Header, + traits::{BlakeTwo256, IdentityLookup}, +}; + +parameter_types! { + pub const BlockHashCount: u64 = 250; +} +impl frame_system::Config for Test { + type BaseCallFilter = InsideBoth; + type BlockWeights = (); + type BlockLength = (); + type Origin = Origin; + type Call = Call; + type Index = u64; + type BlockNumber = u64; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = u64; + type Lookup = IdentityLookup; + type Header = Header; + type Event = Event; + type BlockHashCount = BlockHashCount; + type DbWeight = (); + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type SS58Prefix = (); + type OnSetCode = (); + type MaxConsumers = frame_support::traits::ConstU32<16>; +} + +parameter_types! { + pub const ExistentialDeposit: u64 = 1; + pub const MaxLocks: u32 = 10; +} +impl pallet_balances::Config for Test { + type Balance = u64; + type DustRemoval = (); + type Event = Event; + type ExistentialDeposit = ExistentialDeposit; + type AccountStore = System; + type WeightInfo = (); + type MaxLocks = MaxLocks; + type MaxReserves = (); + type ReserveIdentifier = [u8; 8]; +} + +parameter_types! { + pub const PauseOrigin: u64 = 1; + pub const UnpauseOrigin: u64 = 2; + pub const MaxNameLen: u32 = 50; + pub const PauseTooLongNames: bool = false; +} + +pub struct MockUnpausablePallets; + +impl Contains> for MockUnpausablePallets { + fn contains(pallet: &PalletNameOf) -> bool { + let unpausables: Vec> = + vec![b"UnpausablePallet".to_vec().try_into().unwrap()]; + + unpausables.iter().any(|i| i == pallet) + } +} +// Required impl to use some ::get() in tests +impl SortedMembers for PauseOrigin { + fn sorted_members() -> Vec { + vec![Self::get()] + } + #[cfg(feature = "runtime-benchmarks")] + fn add(_m: &u64) {} +} +impl SortedMembers for UnpauseOrigin { + fn sorted_members() -> Vec { + vec![Self::get()] + } + #[cfg(feature = "runtime-benchmarks")] + fn add(_m: &u64) {} +} + +impl Config for Test { + type Event = Event; + type PauseOrigin = EnsureSignedBy; + type UnpauseOrigin = EnsureSignedBy; + type UnpausablePallets = MockUnpausablePallets; + type MaxNameLen = MaxNameLen; + type PauseTooLongNames = PauseTooLongNames; + type WeightInfo = (); +} + +type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; +type Block = frame_system::mocking::MockBlock; + +frame_support::construct_runtime!( + pub enum Test where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic, + { + System: frame_system, + Balances: pallet_balances, + TxPause: pallet_tx_pause, + } +); + +pub fn new_test_ext() -> sp_io::TestExternalities { + let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap(); + + pallet_balances::GenesisConfig:: { + // The 0 account is NOT a special origin. The rest may be: + balances: vec![(0, 1234), (1, 5678), (2, 5678), (3, 5678), (4, 5678)], + } + .assimilate_storage(&mut t) + .unwrap(); + + GenesisBuild::::assimilate_storage( + &pallet_tx_pause::GenesisConfig { paused: vec![], _phantom: Default::default() }, + &mut t, + ) + .unwrap(); + + let mut ext = sp_io::TestExternalities::new(t); + ext.execute_with(|| { + System::set_block_number(1); + }); + ext +} + +pub fn next_block() { + TxPause::on_finalize(System::block_number()); + Balances::on_finalize(System::block_number()); + System::on_finalize(System::block_number()); + System::set_block_number(System::block_number() + 1); + System::on_initialize(System::block_number()); + Balances::on_initialize(System::block_number()); + TxPause::on_initialize(System::block_number()); +} + +pub fn run_to(n: u64) { + while System::block_number() < n { + next_block(); + } +} diff --git a/frame/tx-pause/src/tests.rs b/frame/tx-pause/src/tests.rs index ee33686e4a39f..30d9ec7600bfc 100644 --- a/frame/tx-pause/src/tests.rs +++ b/frame/tx-pause/src/tests.rs @@ -16,3 +16,152 @@ // limitations under the License. #![cfg(test)] + +use super::*; +use crate::mock::{Call, *}; + +use frame_support::{assert_err, assert_noop, assert_ok, dispatch::Dispatchable}; + +// GENERAL SUCCESS/POSITIVE TESTS --------------------- + +#[test] +fn can_set_arbitrary_pause() { + new_test_ext().execute_with(|| { + assert_ok!(TxPause::pause_call( + Origin::signed(mock::PauseOrigin::get()), + name(b"SomePallet"), + name(b"some_function"), + )); + }); +} + +#[test] +fn can_pause_system_call() { + new_test_ext().execute_with(|| { + let call = Call::System(frame_system::Call::remark { remark: vec![] }); + + assert_ok!(TxPause::pause_call( + Origin::signed(mock::PauseOrigin::get()), + name(b"System"), + name(b"remark"), + )); + + assert_err!( + call.clone().dispatch(Origin::signed(0)), + frame_system::Error::::CallFiltered + ); + }); +} + +#[test] +fn can_pause_specific_call() { + new_test_ext().execute_with(|| { + let call_paused = Call::Balances(pallet_balances::Call::transfer { dest: 1, value: 1 }); + let call_not_paused = + Call::Balances(pallet_balances::Call::transfer_keep_alive { dest: 1, value: 1 }); + + assert_ok!(TxPause::pause_call( + Origin::signed(mock::PauseOrigin::get()), + name(b"Balances"), + name(b"transfer"), + )); + + assert_err!( + call_paused.clone().dispatch(Origin::signed(0)), + frame_system::Error::::CallFiltered + ); + assert_ok!(call_not_paused.clone().dispatch(Origin::signed(0))); + }); +} + +#[test] +fn can_unpause_specific_call() { + new_test_ext().execute_with(|| { + let call_paused = Call::Balances(pallet_balances::Call::transfer { dest: 1, value: 1 }); + + assert_ok!(TxPause::pause_call( + Origin::signed(mock::PauseOrigin::get()), + name(b"Balances"), + name(b"transfer"), + )); + assert_err!( + call_paused.clone().dispatch(Origin::signed(0)), + frame_system::Error::::CallFiltered + ); + + assert_ok!(TxPause::unpause_call( + Origin::signed(mock::UnpauseOrigin::get()), + name(b"Balances"), + name(b"transfer"), + )); + assert_ok!(call_paused.clone().dispatch(Origin::signed(0))); + }); +} + +// GENERAL FAIL/NEGATIVE TESTS --------------------- + +#[test] +fn fails_to_pause_self() { + new_test_ext().execute_with(|| { + assert_noop!( + TxPause::pause_call( + Origin::signed(mock::PauseOrigin::get()), + name(b"TxPause"), + name(b"should_not_matter"), + ), + Error::::IsUnpausable + ); + }); +} + +#[test] +fn fails_to_pause_unpausable_pallet() { + new_test_ext().execute_with(|| { + assert_noop!( + TxPause::pause_call( + Origin::signed(mock::PauseOrigin::get()), + name(b"UnpausablePallet"), + name(b"should_not_matter"), + ), + Error::::IsUnpausable + ); + }); +} + +#[test] +fn fails_to_pause_already_paused_pallet() { + new_test_ext().execute_with(|| { + assert_ok!(TxPause::pause_call( + Origin::signed(mock::PauseOrigin::get()), + name(b"SomePallet"), + name(b"some_function"), + )); + + assert_noop!( + TxPause::pause_call( + Origin::signed(mock::PauseOrigin::get()), + name(b"SomePallet"), + name(b"some_function"), + ), + Error::::IsPaused + ); + }); +} + +#[test] +fn fails_to_unpause_not_paused_pallet() { + new_test_ext().execute_with(|| { + assert_noop!( + TxPause::unpause_call( + Origin::signed(mock::UnpauseOrigin::get()), + name(b"SomePallet"), + name(b"some_function"), + ), + Error::::IsUnpaused + ); + }); +} + +fn name(bytes: &[u8]) -> BoundedVec { + bytes.to_vec().try_into().unwrap() +} diff --git a/frame/tx-pause/src/weights.rs b/frame/tx-pause/src/weights.rs new file mode 100644 index 0000000000000..10a296924b660 --- /dev/null +++ b/frame/tx-pause/src/weights.rs @@ -0,0 +1,93 @@ +// This file is part of Substrate. + +// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Autogenerated weights for pallet_tx_pause +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2022-09-19, STEPS: `1`, REPEAT: 1, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! HOSTNAME: `pop-os`, CPU: `12th Gen Intel(R) Core(TM) i7-12700H` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 + +// Executed Command: +// ./target/release/substrate +// benchmark +// pallet +// --steps +// 1 +// --repeat +// 1 +// --extrinsic +// * +// --execution +// wasm +// --wasm-execution +// compiled +// --heap-pages +// 4096 +// --pallet +// pallet_tx_pause +// --chain +// dev +// --output +// frame/tx-pause/src/weights.rs +// --template=./.maintain/frame-weight-template.hbs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use sp_std::marker::PhantomData; + +/// Weight functions needed for pallet_tx_pause. +pub trait WeightInfo { + fn pause_call() -> Weight; + fn unpause_call() -> Weight; +} + +/// Weights for pallet_tx_pause using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl WeightInfo for SubstrateWeight { + // Storage: TxPause PausedCalls (r:1 w:1) + fn pause_call() -> Weight { + Weight::from_ref_time(55_270_000 as u64) + .saturating_add(T::DbWeight::get().reads(1 as u64)) + .saturating_add(T::DbWeight::get().writes(1 as u64)) + } + // Storage: TxPause PausedCalls (r:1 w:1) + fn unpause_call() -> Weight { + Weight::from_ref_time(55_290_000 as u64) + .saturating_add(T::DbWeight::get().reads(1 as u64)) + .saturating_add(T::DbWeight::get().writes(1 as u64)) + } +} + +// For backwards compatibility and tests +impl WeightInfo for () { + // Storage: TxPause PausedCalls (r:1 w:1) + fn pause_call() -> Weight { + Weight::from_ref_time(55_270_000 as u64) + .saturating_add(RocksDbWeight::get().reads(1 as u64)) + .saturating_add(RocksDbWeight::get().writes(1 as u64)) + } + // Storage: TxPause PausedCalls (r:1 w:1) + fn unpause_call() -> Weight { + Weight::from_ref_time(55_290_000 as u64) + .saturating_add(RocksDbWeight::get().reads(1 as u64)) + .saturating_add(RocksDbWeight::get().writes(1 as u64)) + } +} From b70395784e6afe58f9d30ff11e802104926ff176 Mon Sep 17 00:00:00 2001 From: Dan Shields Date: Wed, 21 Sep 2022 16:39:24 -0600 Subject: [PATCH 021/100] rename to active/inactive tests broken, in progress --- bin/node/runtime/src/lib.rs | 10 +- frame/safe-mode/src/benchmarking.rs | 16 +-- frame/safe-mode/src/lib.rs | 204 ++++++++++++++++------------ frame/safe-mode/src/mock.rs | 67 +++++---- frame/safe-mode/src/tests.rs | 176 ++++++++++++------------ frame/safe-mode/src/weights.rs | 6 +- 6 files changed, 250 insertions(+), 229 deletions(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index ff377c6e6ebc2..e6ef73a5a3269 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -223,14 +223,14 @@ impl pallet_tx_pause::Config for Runtime { parameter_types! { // signed config - pub const EnableStakeAmount: Balance = 1 * DOLLARS; //TODO This needs to be something sensible for the implications of enablement! + pub const ActivateStakeAmount: Balance = 1 * DOLLARS; //TODO This needs to be something sensible for the implications of enablement! pub const ExtendStakeAmount: Balance = 1 * DOLLARS; //TODO This needs to be something sensible for the implications of enablement! pub BlockHeight: BlockNumber = System::block_number(); // TODO ensure this plus config below is correct } parameter_types! { // signed config - pub const EnableStakeAmount: Balance = 1 * DOLLARS; //TODO This needs to be something sensible for the implications of enablement! + pub const ActivateStakeAmount: Balance = 1 * DOLLARS; //TODO This needs to be something sensible for the implications of enablement! pub const ExtendStakeAmount: Balance = 1 * DOLLARS; //TODO This needs to be something sensible for the implications of enablement! pub BlockHeight: BlockNumber = System::block_number(); // TODO ensure this plus config below is correct } @@ -239,13 +239,13 @@ impl pallet_safe_mode::Config for Runtime { type Event = Event; type Currency = Balances; type SafeModeFilter = Nothing; // TODO add TxPause pallet - type EnableDuration = ConstU32<{ 2 * DAYS }>; + type ActivateDuration = ConstU32<{ 2 * DAYS }>; type ExtendDuration = ConstU32<{ 1 * DAYS }>; type EnableOrigin = EnsureRootWithSuccess; type ExtendOrigin = EnsureRootWithSuccess; - type DisableOrigin = EnsureRoot; + type DeactivateOrigin = EnsureRoot; type RepayOrigin = EnsureRoot; - type EnableStakeAmount = EnableStakeAmount; + type ActivateStakeAmount = ActivateStakeAmount; type ExtendStakeAmount = ExtendStakeAmount; type WeightInfo = pallet_safe_mode::weights::SubstrateWeight; } diff --git a/frame/safe-mode/src/benchmarking.rs b/frame/safe-mode/src/benchmarking.rs index 6028bd816fa74..8fb175e8dd141 100644 --- a/frame/safe-mode/src/benchmarking.rs +++ b/frame/safe-mode/src/benchmarking.rs @@ -25,19 +25,19 @@ use frame_system::{Pallet as System, RawOrigin}; use sp_runtime::traits::Bounded; benchmarks! { - enable { + activate { let caller: T::AccountId = whitelisted_caller(); - let origin = RawOrigin::Signed(caller.clone()); + let origin = RawOrigin::Signed(caller.clone()); T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); - }: enable(origin) + }: activate(origin) verify { assert_eq!( - SafeMode::::enabled().unwrap(), - System::::block_number() + T::EnableDuration::get() + SafeMode::::activated().unwrap(), + System::::block_number() + T::ActivateDuration::get() ); } -// force_enable { +// force_activate { // /* code to set the initial state */ // }: { // /* code to test the function benchmarked */ @@ -64,7 +64,7 @@ benchmarks! { // /* optional verification */ // } -// force_disable { +// force_inactivate { // /* code to set the initial state */ // }: { // /* code to test the function benchmarked */ @@ -92,4 +92,4 @@ benchmarks! { // } impl_benchmark_test_suite!(SafeMode, crate::mock::new_test_ext(), crate::mock::Test); -} \ No newline at end of file +} diff --git a/frame/safe-mode/src/lib.rs b/frame/safe-mode/src/lib.rs index 0d315f45a902b..b2e305f3a74b2 100644 --- a/frame/safe-mode/src/lib.rs +++ b/frame/safe-mode/src/lib.rs @@ -56,17 +56,17 @@ pub mod pallet { /// The overarching event type. type RuntimeEvent: From> + IsType<::RuntimeEvent>; - /// Contains all calls that can be dispatched even when the safe-mode is enabled. + /// Currency type for this pallet. + type Currency: ReservableCurrency; + + /// Contains all calls that can be dispatched even when the safe-mode is activated. /// /// The `SafeMode` pallet is always included and does not need to be added here. type SafeModeFilter: Contains; - /// Currency type for this pallet. - type Currency: ReservableCurrency; - - /// How long the safe-mode will stay active when enabled with [`Pallet::enable`]. + /// How long the safe-mode will stay active when activated with [`Pallet::activate`]. #[pallet::constant] - type EnableDuration: Get; + type ActivateDuration: Get; /// For how many blocks the safe-mode can be extended by each [`Pallet::extend`] call. /// @@ -74,30 +74,30 @@ pub mod pallet { #[pallet::constant] type ExtendDuration: Get; - /// The amount that will be reserved upon calling [`Pallet::enable`]. + /// The amount that will be reserved upon calling [`Pallet::activate`]. /// - /// `None` disables the possibility of permissionlessly enabling the safe-mode. + /// `None` disallows permissionlessly enabling the safe-mode. #[pallet::constant] - type EnableStakeAmount: Get>>; + type ActivateStakeAmount: Get>>; /// The amount that will be reserved upon calling [`Pallet::extend`]. /// - /// `None` disables the possibility of permissionlessly extending the safe-mode. + /// `None` disallows permissionlessly extending the safe-mode. #[pallet::constant] type ExtendStakeAmount: Get>>; - /// The origin that can call [`Pallet::force_enable`]. + /// The origin that can call [`Pallet::force_activate`]. /// - /// The `Success` value is the number of blocks that this origin can enter into safe-mode. - type ForceEnableOrigin: EnsureOrigin; + /// The `Success` value is the number of blocks that this origin can activate safe-mode for. + type ForceActivateOrigin: EnsureOrigin; /// The origin that can call [`Pallet::force_extend`]. /// /// The `Success` value is the number of blocks that this origin can extend the safe-mode. type ForceExtendOrigin: EnsureOrigin; - /// The origin that can call [`Pallet::force_disable`]. - type ForceDisableOrigin: EnsureOrigin; + /// The origin that can call [`Pallet::force_inactivate`]. + type ForceInactivateOrigin: EnsureOrigin; /// The origin that can call [`Pallet::repay_stake`] and [`Pallet::slash_stake`]. type RepayOrigin: EnsureOrigin; @@ -108,11 +108,11 @@ pub mod pallet { #[pallet::error] pub enum Error { - /// The safe-mode is (already or still) enabled. - IsEnabled, + /// The safe-mode is (already or still) active. + IsActive, - /// The safe-mode is (already or still) disabled. - IsDisabled, + /// The safe-mode is (already or still) inactive. + IsInactive, /// A value that is required for the extrinsic was not configured. NotConfigured, @@ -124,14 +124,14 @@ pub mod pallet { #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event { - /// The safe-mode was enabled until inclusively this \[block\]. - Enabled(T::BlockNumber), + /// The safe-mode was activated until inclusively this \[block\]. + Activated(T::BlockNumber), /// The safe-mode was extended until inclusively this \[block\]. Extended(T::BlockNumber), - /// The safe-mode was disabled for a specific \[reason\]. - Disabled(DisableReason), + /// Exited safe-mode for a specific \[reason\]. + Exited(ExitReason), /// An account got repaid its stake. \[account, amount\] StakeRepaid(T::AccountId, BalanceOf), @@ -140,23 +140,23 @@ pub mod pallet { StakeSlashed(T::AccountId, BalanceOf), } - /// The reason why the safe-mode was disabled. + /// The reason why the safe-mode was exited. #[derive(Copy, Clone, PartialEq, Eq, RuntimeDebug, Encode, Decode, TypeInfo, MaxEncodedLen)] - pub enum DisableReason { - /// The safe-mode was automatically disabled after its duration ran out. + pub enum ExitReason { + /// The safe-mode was automatically exited after its duration ran out. Timeout, - /// The safe-mode was forcefully disabled by [`Pallet::force_disable`]. + /// The safe-mode was forcefully exited by [`Pallet::force_inactivate`]. Force, } - /// Contains the last block number that the safe-mode will stay enabled. + /// Contains the last block number that the safe-mode will stay activated. /// - /// This is set to `None` if the safe-mode is disabled. - /// The safe-mode is automatically disabled when the current block number is greater than this. + /// This is set to `None` if the safe-mode is inactive. + /// The safe-mode is automatically inactivated when the current block number is greater than this. #[pallet::storage] - #[pallet::getter(fn enabled)] - pub type Enabled = StorageValue<_, T::BlockNumber, OptionQuery>; + #[pallet::getter(fn active)] + pub type ActiveUntil = StorageValue<_, T::BlockNumber, OptionQuery>; /// Holds the stake that was reserved from a user at a specific block number. #[pallet::storage] @@ -174,8 +174,7 @@ pub mod pallet { /// Configure the initial state of this pallet in the genesis block. #[pallet::genesis_config] pub struct GenesisConfig { - /// The blocknumber up to which inclusively the safe-mode will be enabled. - pub enabled: Option, + pub active: Option, pub _phantom: PhantomData, } @@ -184,51 +183,61 @@ pub mod pallet { // NOTE: `derive(Default)` does not work together with `#[pallet::genesis_config]`. // We therefore need to add a trivial default impl. fn default() -> Self { - Self { enabled: None, _phantom: PhantomData } + Self { active: None, _phantom: PhantomData } } } #[pallet::genesis_build] impl GenesisBuild for GenesisConfig { fn build(&self) { - if let Some(block) = self.enabled { - Enabled::::put(block); + if let Some(block) = self.active { + ActiveUntil::::put(block); } } } #[pallet::call] impl Pallet { - /// Enable the safe-mode permissionlessly for [`Config::EnableDuration`] blocks. + /// Activate safe-mode permissionlessly for [`Config::ActivateDuration`] blocks. /// - /// Reserves `EnableStakeAmount` from the caller's account. - /// Errors if the safe-mode is already enabled. - /// Can be permanently disabled by configuring `EnableStakeAmount` to `None`. - #[pallet::weight(T::WeightInfo::enable())] + /// Reserves [`Config::ActivateStakeAmount`] from the caller's account. + /// Emits an [`Event::Activated`] event on success. + /// Errors with [`Error::IsActive`] if the safe-mode is already activated. + /// + /// ### Safety + /// + /// Can be permanently disabled by configuring [`Config::ActivateStakeAmount`] to `None`. + #[pallet::weight(T::WeightInfo::activate())] // #[pallet::weight(0)] - pub fn enable(origin: OriginFor) -> DispatchResult { + pub fn activate(origin: OriginFor) -> DispatchResult { let who = ensure_signed(origin)?; - Self::do_enable(Some(who), T::EnableDuration::get()) + Self::do_activate(Some(who), T::ActivateDuration::get()) } - /// Enable the safe-mode by force for a per-origin configured number of blocks. + /// Activate safe-mode by force for a per-origin configured number of blocks. /// - /// Errors if the safe-mode is already enabled. - /// Emits an [`Event::Enabled`] event on success. - /// Can only be called by the [`Config::ForceEnableOrigin`] origin. + /// Emits an [`Event::Activated`] event on success. + /// Errors with [`Error::IsActive`] if the safe-mode is already activated. + /// + /// ### Safety + /// + /// Can only be called by the [`Config::ForceActivateOrigin`] origin. #[pallet::weight(0)] - pub fn force_enable(origin: OriginFor) -> DispatchResult { - let duration = T::ForceEnableOrigin::ensure_origin(origin)?; + pub fn force_activate(origin: OriginFor) -> DispatchResult { + let duration = T::ForceActivateOrigin::ensure_origin(origin)?; - Self::do_enable(None, duration) + Self::do_activate(None, duration) } /// Extend the safe-mode permissionlessly for [`Config::ExtendDuration`] blocks. /// - /// Reserves `ExtendStakeAmount` from the caller's account. - /// Errors if the safe-mode is disabled. - /// Can be permanently disabled by configuring `ExtendStakeAmount` to `None`. + /// Reserves [`Config::ExtendStakeAmount`] from the caller's account. + /// Errors with [`Error::IsInactive`] if the safe-mode is active. + /// + /// ### Safety + /// + /// Can be permanently disabled by configuring [`Config::ActivateStakeAmount`] to `None`. #[pallet::weight(0)] pub fn extend(origin: OriginFor) -> DispatchResult { let who = ensure_signed(origin)?; @@ -238,7 +247,10 @@ pub mod pallet { /// Extend the safe-mode by force a per-origin configured number of blocks. /// - /// Errors if the safe-mode is disabled. + /// Errors with [`Error::IsInactive`] if the safe-mode is inactive. + /// + /// ### Safety + /// /// Can only be called by the [`Config::ForceExtendOrigin`] origin. #[pallet::weight(0)] pub fn force_extend(origin: OriginFor) -> DispatchResult { @@ -247,22 +259,28 @@ pub mod pallet { Self::do_extend(None, duration) } - /// Disable the safe-mode by force. + /// Inactivate safe-mode by force. /// - /// Will be automatically called after the safe-mode period ran out. - /// Errors if the safe-mode is disabled. - /// Can only be called by the [`Config::ForceDisableOrigin`] origin. + /// Note: safe-mode will be automatically inactivated by [`Pallet::on_initialize`] hook after the block height is greater than [`ActiveUntil`] found in storage. + /// Errors with [`Error::IsInactive`] if the safe-mode is inactive. + /// + /// ### Safety + /// + /// Can only be called by the [`Config::ForceInactivateOrigin`] origin. #[pallet::weight(0)] - pub fn force_disable(origin: OriginFor) -> DispatchResult { - T::ForceDisableOrigin::ensure_origin(origin.clone())?; + pub fn force_inactivate(origin: OriginFor) -> DispatchResult { + T::ForceInactivateOrigin::ensure_origin(origin.clone())?; - Self::do_disable(DisableReason::Force) + Self::do_inactivate(ExitReason::Force) } - /// Repay an honest account that put the chain into safe-mode earlier. + /// Repay an honest account that activated safe-mode earlier. /// - /// Errors if the safe-mode is already enabled. /// Emits a [`Event::StakeRepaid`] event on success. + /// Errors if the safe-mode is already activated. + /// + /// ### Safety + /// /// Can only be called by the [`Config::RepayOrigin`] origin. #[pallet::weight(0)] pub fn repay_stake( @@ -277,8 +295,11 @@ pub mod pallet { /// Slash a dishonest account that put the chain into safe-mode earlier. /// - /// Errors if the safe-mode is already enabled. + /// Errors if the safe-mode is already activated. /// Emits a [`Event::StakeSlashed`] event on success. + /// + /// ### Safety + /// /// Can only be called by the [`Config::RepayOrigin`] origin. #[pallet::weight(0)] pub fn slash_stake( @@ -294,14 +315,14 @@ pub mod pallet { #[pallet::hooks] impl Hooks> for Pallet { - /// Automatically disables the safe-mode when the period ran out. + /// Automatically inactivates the safe-mode when the period ran out. /// /// Bypasses any call filters to avoid getting rejected by them. fn on_initialize(current: T::BlockNumber) -> Weight { - match Enabled::::get() { + match ActiveUntil::::get() { Some(limit) if current > limit => { - let _ = Self::do_disable(DisableReason::Timeout) - .defensive_proof("Must be disabled; qed"); + let _ = Self::do_inactivate(ExitReason::Timeout) + .defensive_proof("Must be inactive; qed"); T::DbWeight::get().reads_writes(1, 1) }, _ => T::DbWeight::get().reads(1), // TODO benchmark @@ -311,17 +332,17 @@ pub mod pallet { } impl Pallet { - /// Logic for the [`crate::Pallet::enable`] and [`crate::Pallet::force_enable`] calls. - fn do_enable(who: Option, duration: T::BlockNumber) -> DispatchResult { + /// Logic for the [`crate::Pallet::activate`] and [`crate::Pallet::force_activate`] calls. + fn do_activate(who: Option, duration: T::BlockNumber) -> DispatchResult { if let Some(who) = who { - let stake = T::EnableStakeAmount::get().ok_or(Error::::NotConfigured)?; + let stake = T::ActivateStakeAmount::get().ok_or(Error::::NotConfigured)?; Self::reserve(who, stake)?; } - ensure!(!Enabled::::exists(), Error::::IsEnabled); + ensure!(!ActiveUntil::::exists(), Error::::IsActive); let limit = >::block_number().saturating_add(duration); - Enabled::::put(limit); - Self::deposit_event(Event::Enabled(limit)); + ActiveUntil::::put(limit); + Self::deposit_event(Event::Activated(limit)); Ok(()) } @@ -332,26 +353,28 @@ impl Pallet { Self::reserve(who, stake)?; } - let limit = Enabled::::take().ok_or(Error::::IsDisabled)?.saturating_add(duration); - Enabled::::put(limit); + let limit = ActiveUntil::::take().ok_or(Error::::IsInactive)?.saturating_add(duration); + ActiveUntil::::put(limit); Self::deposit_event(Event::::Extended(limit)); Ok(()) } - /// Logic for the [`crate::Pallet::force_disable`] call. + /// Logic for the [`crate::Pallet::force_inactivate`] call. /// - /// Does not check the origin. Errors if the safe-mode is already disabled. - fn do_disable(reason: DisableReason) -> DispatchResult { - let _limit = Enabled::::take().ok_or(Error::::IsDisabled)?; - Self::deposit_event(Event::Disabled(reason)); + /// Errors if the safe-mode is already inactive. + /// Does not check the origin. + fn do_inactivate(reason: ExitReason) -> DispatchResult { + let _limit = ActiveUntil::::take().ok_or(Error::::IsInactive)?; + Self::deposit_event(Event::Exited(reason)); Ok(()) } /// Logic for the [`crate::Pallet::repay_stake`] call. /// - /// Does not check the origin. Errors if the safe-mode is enabled. + /// Errors if the safe-mode is active. + /// Does not check the origin. fn do_repay_stake(account: T::AccountId, block: T::BlockNumber) -> DispatchResult { - ensure!(!Self::is_enabled(), Error::::IsEnabled); + ensure!(!Self::is_activated(), Error::::IsActive); let stake = Stakes::::take(&account, block).ok_or(Error::::NotStaked)?; T::Currency::unreserve(&account, stake); @@ -361,9 +384,10 @@ impl Pallet { /// Logic for the [`crate::Pallet::slash_stake`] call. /// - /// Does not check the origin. Errors if the safe-mode is enabled. + /// Errors if the safe-mode is activated. + /// Does not check the origin. fn do_slash_stake(account: T::AccountId, block: T::BlockNumber) -> DispatchResult { - ensure!(!Self::is_enabled(), Error::::IsEnabled); + ensure!(!Self::is_activated(), Error::::IsActive); let stake = Stakes::::take(&account, block).ok_or(Error::::NotStaked)?; T::Currency::slash_reserved(&account, stake); @@ -380,9 +404,9 @@ impl Pallet { Ok(()) } - /// Return whether the `safe-mode` is currently enabled. - pub fn is_enabled() -> bool { - Enabled::::exists() + /// Return whether the `safe-mode` is currently activated. + pub fn is_activated() -> bool { + ActiveUntil::::exists() } /// Return whether this call is allowed to be dispatched. @@ -396,7 +420,7 @@ impl Pallet { return true } - if Self::is_enabled() { + if Self::is_activated() { T::SafeModeFilter::contains(call) } else { true diff --git a/frame/safe-mode/src/mock.rs b/frame/safe-mode/src/mock.rs index 04ab0024e492c..ab3b717d99005 100644 --- a/frame/safe-mode/src/mock.rs +++ b/frame/safe-mode/src/mock.rs @@ -90,7 +90,7 @@ impl Contains for MockSafeModeFilter { } /// An origin that can enable the safe-mode by force. -pub enum ForceEnableOrigin { +pub enum ForceActivateOrigin { Weak, Medium, Strong, @@ -103,8 +103,8 @@ pub enum ForceExtendOrigin { Strong, } -impl ForceEnableOrigin { - /// The duration of how long the safe-mode will be enabled. +impl ForceActivateOrigin { + /// The duration of how long the safe-mode will be activated. pub fn duration(&self) -> u64 { match self { Self::Weak => 5, @@ -154,18 +154,18 @@ impl ForceExtendOrigin { } impl, O>> + From> + std::fmt::Debug> EnsureOrigin - for ForceEnableOrigin + for ForceActivateOrigin { type Success = u64; fn try_origin(o: O) -> Result { o.into().and_then(|o| match o { - RawOrigin::Signed(acc) if acc == ForceEnableOrigin::Weak.acc() => - Ok(ForceEnableOrigin::Weak.duration()), - RawOrigin::Signed(acc) if acc == ForceEnableOrigin::Medium.acc() => - Ok(ForceEnableOrigin::Medium.duration()), - RawOrigin::Signed(acc) if acc == ForceEnableOrigin::Strong.acc() => - Ok(ForceEnableOrigin::Strong.duration()), + RawOrigin::Signed(acc) if acc == ForceActivateOrigin::Weak.acc() => + Ok(ForceActivateOrigin::Weak.duration()), + RawOrigin::Signed(acc) if acc == ForceActivateOrigin::Medium.acc() => + Ok(ForceActivateOrigin::Medium.duration()), + RawOrigin::Signed(acc) if acc == ForceActivateOrigin::Strong.acc() => + Ok(ForceActivateOrigin::Strong.duration()), r => Err(O::from(r)), }) } @@ -190,16 +190,16 @@ impl, O>> + From> + std::fmt::Debug } parameter_types! { - pub const EnableDuration: u64 = 3; + pub const ActivateDuration: u64 = 3; pub const ExtendDuration: u64 = 30; - pub const EnableStakeAmount: u64 = 100; //TODO This needs to be something sensible for the implications of enablement! - pub const ExtendStakeAmount: u64 = 100; //TODO This needs to be something sensible for the implications of enablement! - pub const DisableOrigin: u64 =3; + pub const ActivateStakeAmount: u64 = 100; + pub const ExtendStakeAmount: u64 = 100; + pub const DeactivateOrigin: u64 =3; pub const RepayOrigin: u64 = 4; } // Required impl to use some ::get() in tests -impl SortedMembers for DisableOrigin { +impl SortedMembers for DeactivateOrigin { fn sorted_members() -> Vec { vec![Self::get()] } @@ -215,17 +215,17 @@ impl SortedMembers for RepayOrigin { } impl Config for Test { - type Event = Event; + type RuntimeEvent = RuntimeEvent; type Currency = Balances; type SafeModeFilter = MockSafeModeFilter; - type EnableDuration = EnableDuration; + type ActivateDuration = ActivateDuration; type ExtendDuration = ExtendDuration; - type EnableOrigin = ForceEnableOrigin; - type ExtendOrigin = ForceExtendOrigin; - type DisableOrigin = EnsureSignedBy; - type RepayOrigin = EnsureSignedBy; - type EnableStakeAmount = EnableStakeAmount; + type ActivateStakeAmount = ActivateStakeAmount; + type ForceActivateOrigin = ForceActivateOrigin; + type ForceExtendOrigin = ForceExtendOrigin; type ExtendStakeAmount = ExtendStakeAmount; + type ForceInactivateOrigin = EnsureSignedBy; + type RepayOrigin = EnsureSignedBy; type WeightInfo = (); } @@ -248,23 +248,20 @@ pub fn new_test_ext() -> sp_io::TestExternalities { let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap(); pallet_balances::GenesisConfig:: { - balances: vec![(0, 1234), (1, 5678), (2, 5678), (3, 5678), (4, 5678)], /* The 0 account - * is NOT a special - * origin, the - * rest may be. */ + // The 0 account is NOT a special origin, the rest may be. + balances: vec![(0, 1234), (1, 5678), (2, 5678), (3, 5678), (4, 5678)], } .assimilate_storage(&mut t) .unwrap(); - // TODO requires a GenesisConfig impl - // GenesisBuild::::assimilate_storage( - // &pallet_safe_mode::GenesisConfig { - // enabled: None, - // stakes: None, - // }, - // &mut t, - // ) - // .unwrap(); + GenesisBuild::::assimilate_storage( + &pallet_safe_mode::GenesisConfig { + active: None, + _phantom: Default::default(), + }, + &mut t, + ) + .unwrap(); let mut ext = sp_io::TestExternalities::new(t); ext.execute_with(|| { diff --git a/frame/safe-mode/src/tests.rs b/frame/safe-mode/src/tests.rs index 4ccf4e8cd6411..1ea23295ee7b8 100644 --- a/frame/safe-mode/src/tests.rs +++ b/frame/safe-mode/src/tests.rs @@ -27,8 +27,8 @@ use frame_support::{assert_err, assert_noop, assert_ok, dispatch::Dispatchable}; #[test] fn fails_to_filter_calls_to_safe_mode_pallet() { new_test_ext().execute_with(|| { - assert_ok!(SafeMode::enable(Origin::signed(0))); - let enabled_at_block = System::block_number(); + assert_ok!(SafeMode::activate(Origin::signed(0))); + let activated_at_block = System::block_number(); let call = Call::Balances(pallet_balances::Call::transfer { dest: 1, value: 1 }); assert_err!( @@ -44,56 +44,56 @@ fn fails_to_filter_calls_to_safe_mode_pallet() { call.clone().dispatch(Origin::signed(0)), frame_system::Error::::CallFiltered ); - assert_ok!(SafeMode::force_disable(Origin::signed(mock::DisableOrigin::get()))); + assert_ok!(SafeMode::force_inactivate(Origin::signed(mock::DeactivateOrigin::get()))); assert_ok!(SafeMode::repay_stake( Origin::signed(mock::RepayOrigin::get()), 0, - enabled_at_block + activated_at_block )); next_block(); - assert_ok!(SafeMode::enable(Origin::signed(0))); + assert_ok!(SafeMode::activate(Origin::signed(0))); assert_err!( call.clone().dispatch(Origin::signed(0)), frame_system::Error::::CallFiltered ); - assert_ok!(SafeMode::force_disable(Origin::signed(mock::DisableOrigin::get()))); + assert_ok!(SafeMode::force_inactivate(Origin::signed(mock::DeactivateOrigin::get()))); assert_ok!(SafeMode::slash_stake( Origin::signed(mock::RepayOrigin::get()), 0, - enabled_at_block + 2 + activated_at_block + 2 )); }); } #[test] -fn fails_to_extend_if_not_enabled() { +fn fails_to_extend_if_not_activated() { new_test_ext().execute_with(|| { - assert_eq!(SafeMode::enabled(), None); - assert_noop!(SafeMode::extend(Origin::signed(2)), Error::::IsDisabled); + assert_eq!(SafeMode::activated(), None); + assert_noop!(SafeMode::extend(Origin::signed(2)), Error::::IsInactive); }); } // GENERAL SUCCESS/POSITIVE TESTS --------------------- #[test] -fn can_automatically_disable_after_timeout() { +fn can_automatically_inactivate_after_timeout() { new_test_ext().execute_with(|| { - let enabled_at_block = System::block_number(); - assert_ok!(SafeMode::force_enable(ForceEnableOrigin::Weak.signed())); - run_to(ForceEnableOrigin::Weak.duration() + enabled_at_block + 1); + let activated_at_block = System::block_number(); + assert_ok!(SafeMode::force_activate(ForceActivateOrigin::Weak.signed())); + run_to(ForceActivateOrigin::Weak.duration() + activated_at_block + 1); SafeMode::on_initialize(System::block_number()); - assert_eq!(SafeMode::enabled(), None); + assert_eq!(SafeMode::activated(), None); }); } #[test] -fn can_filter_balance_calls_when_enabled() { +fn can_filter_balance_calls_when_activated() { new_test_ext().execute_with(|| { let call = Call::Balances(pallet_balances::Call::transfer { dest: 1, value: 1 }); assert_ok!(call.clone().dispatch(Origin::signed(0))); - assert_ok!(SafeMode::enable(Origin::signed(0))); + assert_ok!(SafeMode::activate(Origin::signed(0))); assert_err!( call.clone().dispatch(Origin::signed(0)), frame_system::Error::::CallFiltered @@ -105,32 +105,32 @@ fn can_filter_balance_calls_when_enabled() { // SIGNED ORIGIN CALL TESTS --------------------- #[test] -fn can_enable_with_signed_origin() { +fn can_activate_with_signed_origin() { new_test_ext().execute_with(|| { - assert_ok!(SafeMode::enable(Origin::signed(0))); + assert_ok!(SafeMode::activate(Origin::signed(0))); assert_eq!( - SafeMode::enabled().unwrap(), - System::block_number() + mock::EnableDuration::get() + SafeMode::activated().unwrap(), + System::block_number() + mock::ActivateDuration::get() ); - assert_eq!(Balances::reserved_balance(0), mock::EnableStakeAmount::get()); - assert_noop!(SafeMode::enable(Origin::signed(0)), Error::::IsEnabled); + assert_eq!(Balances::reserved_balance(0), mock::ActivateStakeAmount::get()); + assert_noop!(SafeMode::activate(Origin::signed(0)), Error::::IsActive); // Assert the stake. - assert_eq!(Stakes::::get(0, 1), Some(mock::EnableStakeAmount::get())); + assert_eq!(Stakes::::get(0, 1), Some(mock::ActivateStakeAmount::get())); }); } #[test] fn can_extend_with_signed_origin() { new_test_ext().execute_with(|| { - assert_ok!(SafeMode::enable(Origin::signed(0))); + assert_ok!(SafeMode::activate(Origin::signed(0))); assert_ok!(SafeMode::extend(Origin::signed(0))); assert_eq!( - SafeMode::enabled().unwrap(), - System::block_number() + mock::EnableDuration::get() + mock::ExtendDuration::get() + SafeMode::activated().unwrap(), + System::block_number() + mock::ActivateDuration::get() + mock::ExtendDuration::get() ); assert_eq!( Balances::reserved_balance(0), - mock::EnableStakeAmount::get() + mock::ExtendStakeAmount::get() + mock::ActivateStakeAmount::get() + mock::ExtendStakeAmount::get() ); }); } @@ -138,18 +138,18 @@ fn can_extend_with_signed_origin() { #[test] fn fails_signed_origin_when_explicit_origin_required() { new_test_ext().execute_with(|| { - assert_eq!(SafeMode::enabled(), None); - let enabled_at_block = System::block_number(); + assert_eq!(SafeMode::activated(), None); + let activated_at_block = System::block_number(); - assert_err!(SafeMode::force_enable(Origin::signed(0)), DispatchError::BadOrigin); + assert_err!(SafeMode::force_activate(Origin::signed(0)), DispatchError::BadOrigin); assert_err!(SafeMode::force_extend(Origin::signed(0)), DispatchError::BadOrigin); - assert_err!(SafeMode::force_disable(Origin::signed(0)), DispatchError::BadOrigin); + assert_err!(SafeMode::force_inactivate(Origin::signed(0)), DispatchError::BadOrigin); assert_err!( - SafeMode::slash_stake(Origin::signed(0), 0, enabled_at_block), + SafeMode::slash_stake(Origin::signed(0), 0, activated_at_block), DispatchError::BadOrigin ); assert_err!( - SafeMode::repay_stake(Origin::signed(0), 0, enabled_at_block), + SafeMode::repay_stake(Origin::signed(0), 0, activated_at_block), DispatchError::BadOrigin ); }); @@ -158,66 +158,66 @@ fn fails_signed_origin_when_explicit_origin_required() { // CONFIGURED ORIGIN CALL TESTS --------------------- #[test] -fn fails_force_disable_if_not_enabled() { +fn fails_force_inactivate_if_not_activated() { new_test_ext().execute_with(|| { assert_noop!( - SafeMode::force_disable(Origin::signed(mock::DisableOrigin::get())), - Error::::IsDisabled + SafeMode::force_inactivate(Origin::signed(mock::DeactivateOrigin::get())), + Error::::IsInactive ); assert_noop!( - SafeMode::force_disable(Origin::signed(mock::DisableOrigin::get())), - Error::::IsDisabled + SafeMode::force_inactivate(Origin::signed(mock::DeactivateOrigin::get())), + Error::::IsInactive ); }); } #[test] -fn can_force_enable_with_config_origin() { +fn can_force_activate_with_config_origin() { new_test_ext().execute_with(|| { - assert_ok!(SafeMode::force_enable(ForceEnableOrigin::Weak.signed())); + assert_ok!(SafeMode::force_activate(ForceActivateOrigin::Weak.signed())); assert_eq!( - SafeMode::enabled().unwrap(), - System::block_number() + ForceEnableOrigin::Weak.duration() + SafeMode::activated().unwrap(), + System::block_number() + ForceActivateOrigin::Weak.duration() ); assert_noop!( - SafeMode::force_enable(ForceEnableOrigin::Weak.signed()), - Error::::IsEnabled + SafeMode::force_activate(ForceActivateOrigin::Weak.signed()), + Error::::IsActive ); - assert_eq!(Balances::reserved_balance(ForceEnableOrigin::Weak.acc()), 0); + assert_eq!(Balances::reserved_balance(ForceActivateOrigin::Weak.acc()), 0); }); } #[test] -fn can_force_disable_with_config_origin() { +fn can_force_inactivate_with_config_origin() { new_test_ext().execute_with(|| { - assert_eq!(SafeMode::enabled(), None); + assert_eq!(SafeMode::activated(), None); assert_err!( - SafeMode::force_disable(Origin::signed(mock::DisableOrigin::get())), - Error::::IsDisabled + SafeMode::force_inactivate(Origin::signed(mock::DeactivateOrigin::get())), + Error::::IsInactive ); - assert_ok!(SafeMode::force_enable(ForceEnableOrigin::Weak.signed())); - assert_eq!(Balances::reserved_balance(ForceEnableOrigin::Weak.acc()), 0); - assert_ok!(SafeMode::force_disable(Origin::signed(mock::DisableOrigin::get()))); + assert_ok!(SafeMode::force_activate(ForceActivateOrigin::Weak.signed())); + assert_eq!(Balances::reserved_balance(ForceActivateOrigin::Weak.acc()), 0); + assert_ok!(SafeMode::force_inactivate(Origin::signed(mock::DeactivateOrigin::get()))); }); } #[test] fn can_force_extend_with_config_origin() { new_test_ext().execute_with(|| { - // Enable by `Weak` and extended by `Medium`. - assert_ok!(SafeMode::force_enable(ForceEnableOrigin::Weak.signed())); + // Activated by `Weak` and extended by `Medium`. + assert_ok!(SafeMode::force_activate(ForceActivateOrigin::Weak.signed())); assert_eq!( - SafeMode::enabled().unwrap(), - System::block_number() + ForceEnableOrigin::Weak.duration() + SafeMode::activated().unwrap(), + System::block_number() + ForceActivateOrigin::Weak.duration() ); assert_ok!(SafeMode::force_extend(ForceExtendOrigin::Medium.signed())); assert_eq!( - SafeMode::enabled().unwrap(), + SafeMode::activated().unwrap(), System::block_number() + - ForceEnableOrigin::Weak.duration() + + ForceActivateOrigin::Weak.duration() + ForceExtendOrigin::Medium.duration() ); - assert_eq!(Balances::reserved_balance(ForceEnableOrigin::Weak.acc()), 0); + assert_eq!(Balances::reserved_balance(ForceActivateOrigin::Weak.acc()), 0); assert_eq!(Balances::reserved_balance(mock::ExtendDuration::get()), 0); }); } @@ -225,18 +225,18 @@ fn can_force_extend_with_config_origin() { #[test] fn can_repay_stake_with_config_origin() { new_test_ext().execute_with(|| { - let enabled_at_block = System::block_number(); - assert_ok!(SafeMode::enable(Origin::signed(0))); + let activated_at_block = System::block_number(); + assert_ok!(SafeMode::activate(Origin::signed(0))); assert_err!( - SafeMode::repay_stake(Origin::signed(mock::RepayOrigin::get()), 0, enabled_at_block), - Error::::IsEnabled + SafeMode::repay_stake(Origin::signed(mock::RepayOrigin::get()), 0, activated_at_block), + Error::::IsActive ); - run_to(mock::EnableDuration::get() + enabled_at_block + 1); + run_to(mock::ActivateDuration::get() + activated_at_block + 1); SafeMode::on_initialize(System::block_number()); assert_ok!(SafeMode::repay_stake( Origin::signed(mock::RepayOrigin::get()), 0, - enabled_at_block + activated_at_block )); // TODO: test accounting is correct }); @@ -245,18 +245,18 @@ fn can_repay_stake_with_config_origin() { #[test] fn can_slash_stake_with_config_origin() { new_test_ext().execute_with(|| { - let enabled_at_block = System::block_number(); - assert_ok!(SafeMode::enable(Origin::signed(0))); + let activated_at_block = System::block_number(); + assert_ok!(SafeMode::activate(Origin::signed(0))); assert_err!( - SafeMode::slash_stake(Origin::signed(mock::RepayOrigin::get()), 0, enabled_at_block), - Error::::IsEnabled + SafeMode::slash_stake(Origin::signed(mock::RepayOrigin::get()), 0, activated_at_block), + Error::::IsActive ); - run_to(mock::EnableDuration::get() + enabled_at_block + 1); + run_to(mock::ActivateDuration::get() + activated_at_block + 1); SafeMode::on_initialize(System::block_number()); assert_ok!(SafeMode::slash_stake( Origin::signed(mock::RepayOrigin::get()), 0, - enabled_at_block + activated_at_block )); // TODO: test accounting is correct }); @@ -265,62 +265,62 @@ fn can_slash_stake_with_config_origin() { #[test] fn fails_when_explicit_origin_required() { new_test_ext().execute_with(|| { - assert_eq!(SafeMode::enabled(), None); - let enabled_at_block = System::block_number(); + assert_eq!(SafeMode::activated(), None); + let activated_at_block = System::block_number(); assert_err!( - SafeMode::force_extend(ForceEnableOrigin::Weak.signed()), + SafeMode::force_extend(ForceActivateOrigin::Weak.signed()), DispatchError::BadOrigin ); assert_err!( - SafeMode::force_disable(ForceEnableOrigin::Weak.signed()), + SafeMode::force_inactivate(ForceActivateOrigin::Weak.signed()), DispatchError::BadOrigin ); assert_err!( - SafeMode::slash_stake(ForceEnableOrigin::Weak.signed(), 0, enabled_at_block), + SafeMode::slash_stake(ForceActivateOrigin::Weak.signed(), 0, activated_at_block), DispatchError::BadOrigin ); assert_err!( - SafeMode::repay_stake(ForceEnableOrigin::Weak.signed(), 0, enabled_at_block), + SafeMode::repay_stake(ForceActivateOrigin::Weak.signed(), 0, activated_at_block), DispatchError::BadOrigin ); assert_err!( - SafeMode::force_enable(ForceExtendOrigin::Weak.signed()), + SafeMode::force_activate(ForceExtendOrigin::Weak.signed()), DispatchError::BadOrigin ); assert_err!( - SafeMode::force_disable(ForceExtendOrigin::Weak.signed()), + SafeMode::force_inactivate(ForceExtendOrigin::Weak.signed()), DispatchError::BadOrigin ); assert_err!( - SafeMode::slash_stake(ForceExtendOrigin::Weak.signed(), 0, enabled_at_block), + SafeMode::slash_stake(ForceExtendOrigin::Weak.signed(), 0, activated_at_block), DispatchError::BadOrigin ); assert_err!( - SafeMode::repay_stake(ForceExtendOrigin::Weak.signed(), 0, enabled_at_block), + SafeMode::repay_stake(ForceExtendOrigin::Weak.signed(), 0, activated_at_block), DispatchError::BadOrigin ); assert_err!( - SafeMode::force_enable(Origin::signed(mock::DisableOrigin::get())), + SafeMode::force_activate(Origin::signed(mock::DeactivateOrigin::get())), DispatchError::BadOrigin ); assert_err!( - SafeMode::force_extend(Origin::signed(mock::DisableOrigin::get())), + SafeMode::force_extend(Origin::signed(mock::DeactivateOrigin::get())), DispatchError::BadOrigin ); assert_err!( - SafeMode::slash_stake(Origin::signed(mock::DisableOrigin::get()), 0, enabled_at_block), + SafeMode::slash_stake(Origin::signed(mock::DeactivateOrigin::get()), 0, activated_at_block), DispatchError::BadOrigin ); assert_err!( - SafeMode::repay_stake(Origin::signed(mock::DisableOrigin::get()), 0, enabled_at_block), + SafeMode::repay_stake(Origin::signed(mock::DeactivateOrigin::get()), 0, activated_at_block), DispatchError::BadOrigin ); assert_err!( - SafeMode::force_enable(Origin::signed(mock::RepayOrigin::get())), + SafeMode::force_activate(Origin::signed(mock::RepayOrigin::get())), DispatchError::BadOrigin ); assert_err!( @@ -328,7 +328,7 @@ fn fails_when_explicit_origin_required() { DispatchError::BadOrigin ); assert_err!( - SafeMode::force_disable(Origin::signed(mock::RepayOrigin::get())), + SafeMode::force_inactivate(Origin::signed(mock::RepayOrigin::get())), DispatchError::BadOrigin ); }); diff --git a/frame/safe-mode/src/weights.rs b/frame/safe-mode/src/weights.rs index c96de6f978947..dc1b64d905c6f 100644 --- a/frame/safe-mode/src/weights.rs +++ b/frame/safe-mode/src/weights.rs @@ -55,7 +55,7 @@ use sp_std::marker::PhantomData; /// Weight functions needed for pallet_safe_mode. pub trait WeightInfo { - fn enable() -> Weight; + fn activate() -> Weight; } /// Weights for pallet_safe_mode using the Substrate node and recommended hardware. @@ -63,7 +63,7 @@ pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { // Storage: SafeMode Stakes (r:1 w:1) // Storage: SafeMode Enabled (r:1 w:1) - fn enable() -> Weight { + fn activate() -> Weight { Weight::from_ref_time(51_688_000 as u64) .saturating_add(T::DbWeight::get().reads(2 as u64)) .saturating_add(T::DbWeight::get().writes(2 as u64)) @@ -74,7 +74,7 @@ impl WeightInfo for SubstrateWeight { impl WeightInfo for () { // Storage: SafeMode Stakes (r:1 w:1) // Storage: SafeMode Enabled (r:1 w:1) - fn enable() -> Weight { + fn activate() -> Weight { Weight::from_ref_time(51_688_000 as u64) .saturating_add(RocksDbWeight::get().reads(2 as u64)) .saturating_add(RocksDbWeight::get().writes(2 as u64)) From 722c4f62bf4bd3913abe5a214816b791d927bba3 Mon Sep 17 00:00:00 2001 From: Dan Shields Date: Thu, 22 Sep 2022 08:29:16 -0600 Subject: [PATCH 022/100] Runtime types, fix tests --- frame/safe-mode/src/lib.rs | 45 +++++++++++++++++++----------------- frame/safe-mode/src/mock.rs | 22 ++++++++---------- frame/safe-mode/src/tests.rs | 38 ++++++++++++++++++------------ frame/tx-pause/src/lib.rs | 10 ++++---- frame/tx-pause/src/mock.rs | 8 +++---- frame/tx-pause/src/tests.rs | 12 ++++++---- 6 files changed, 72 insertions(+), 63 deletions(-) diff --git a/frame/safe-mode/src/lib.rs b/frame/safe-mode/src/lib.rs index b2e305f3a74b2..db0ae1d2d5650 100644 --- a/frame/safe-mode/src/lib.rs +++ b/frame/safe-mode/src/lib.rs @@ -153,9 +153,10 @@ pub mod pallet { /// Contains the last block number that the safe-mode will stay activated. /// /// This is set to `None` if the safe-mode is inactive. - /// The safe-mode is automatically inactivated when the current block number is greater than this. + /// The safe-mode is automatically inactivated when the current block number is greater than + /// this. #[pallet::storage] - #[pallet::getter(fn active)] + #[pallet::getter(fn active_until)] pub type ActiveUntil = StorageValue<_, T::BlockNumber, OptionQuery>; /// Holds the stake that was reserved from a user at a specific block number. @@ -203,9 +204,9 @@ pub mod pallet { /// Reserves [`Config::ActivateStakeAmount`] from the caller's account. /// Emits an [`Event::Activated`] event on success. /// Errors with [`Error::IsActive`] if the safe-mode is already activated. - /// + /// /// ### Safety - /// + /// /// Can be permanently disabled by configuring [`Config::ActivateStakeAmount`] to `None`. #[pallet::weight(T::WeightInfo::activate())] // #[pallet::weight(0)] @@ -219,9 +220,9 @@ pub mod pallet { /// /// Emits an [`Event::Activated`] event on success. /// Errors with [`Error::IsActive`] if the safe-mode is already activated. - /// + /// /// ### Safety - /// + /// /// Can only be called by the [`Config::ForceActivateOrigin`] origin. #[pallet::weight(0)] pub fn force_activate(origin: OriginFor) -> DispatchResult { @@ -234,9 +235,9 @@ pub mod pallet { /// /// Reserves [`Config::ExtendStakeAmount`] from the caller's account. /// Errors with [`Error::IsInactive`] if the safe-mode is active. - /// + /// /// ### Safety - /// + /// /// Can be permanently disabled by configuring [`Config::ActivateStakeAmount`] to `None`. #[pallet::weight(0)] pub fn extend(origin: OriginFor) -> DispatchResult { @@ -248,9 +249,9 @@ pub mod pallet { /// Extend the safe-mode by force a per-origin configured number of blocks. /// /// Errors with [`Error::IsInactive`] if the safe-mode is inactive. - /// + /// /// ### Safety - /// + /// /// Can only be called by the [`Config::ForceExtendOrigin`] origin. #[pallet::weight(0)] pub fn force_extend(origin: OriginFor) -> DispatchResult { @@ -261,11 +262,12 @@ pub mod pallet { /// Inactivate safe-mode by force. /// - /// Note: safe-mode will be automatically inactivated by [`Pallet::on_initialize`] hook after the block height is greater than [`ActiveUntil`] found in storage. - /// Errors with [`Error::IsInactive`] if the safe-mode is inactive. - /// + /// Note: safe-mode will be automatically inactivated by [`Pallet::on_initialize`] hook + /// after the block height is greater than [`ActiveUntil`] found in storage. Errors with + /// [`Error::IsInactive`] if the safe-mode is inactive. + /// /// ### Safety - /// + /// /// Can only be called by the [`Config::ForceInactivateOrigin`] origin. #[pallet::weight(0)] pub fn force_inactivate(origin: OriginFor) -> DispatchResult { @@ -278,9 +280,9 @@ pub mod pallet { /// /// Emits a [`Event::StakeRepaid`] event on success. /// Errors if the safe-mode is already activated. - /// - /// ### Safety - /// + /// + /// ### Safety + /// /// Can only be called by the [`Config::RepayOrigin`] origin. #[pallet::weight(0)] pub fn repay_stake( @@ -297,9 +299,9 @@ pub mod pallet { /// /// Errors if the safe-mode is already activated. /// Emits a [`Event::StakeSlashed`] event on success. - /// + /// /// ### Safety - /// + /// /// Can only be called by the [`Config::RepayOrigin`] origin. #[pallet::weight(0)] pub fn slash_stake( @@ -353,7 +355,8 @@ impl Pallet { Self::reserve(who, stake)?; } - let limit = ActiveUntil::::take().ok_or(Error::::IsInactive)?.saturating_add(duration); + let limit = + ActiveUntil::::take().ok_or(Error::::IsInactive)?.saturating_add(duration); ActiveUntil::::put(limit); Self::deposit_event(Event::::Extended(limit)); Ok(()) @@ -385,7 +388,7 @@ impl Pallet { /// Logic for the [`crate::Pallet::slash_stake`] call. /// /// Errors if the safe-mode is activated. - /// Does not check the origin. + /// Does not check the origin. fn do_slash_stake(account: T::AccountId, block: T::BlockNumber) -> DispatchResult { ensure!(!Self::is_activated(), Error::::IsActive); let stake = Stakes::::take(&account, block).ok_or(Error::::NotStaked)?; diff --git a/frame/safe-mode/src/mock.rs b/frame/safe-mode/src/mock.rs index ab3b717d99005..35066c9f250aa 100644 --- a/frame/safe-mode/src/mock.rs +++ b/frame/safe-mode/src/mock.rs @@ -29,7 +29,6 @@ use sp_core::H256; use sp_runtime::{ testing::Header, traits::{BlakeTwo256, IdentityLookup}, - BuildStorage, }; parameter_types! { @@ -40,7 +39,7 @@ impl frame_system::Config for Test { type BlockWeights = (); type BlockLength = (); type Origin = Origin; - type Call = Call; + type RuntimeCall = RuntimeCall; type Index = u64; type BlockNumber = u64; type Hash = H256; @@ -48,7 +47,7 @@ impl frame_system::Config for Test { type AccountId = u64; type Lookup = IdentityLookup; type Header = Header; - type Event = Event; + type RuntimeEvent = RuntimeEvent; type BlockHashCount = BlockHashCount; type DbWeight = (); type Version = (); @@ -69,7 +68,7 @@ parameter_types! { impl pallet_balances::Config for Test { type Balance = u64; type DustRemoval = (); - type Event = Event; + type RuntimeEvent = RuntimeEvent; type ExistentialDeposit = ExistentialDeposit; type AccountStore = System; type WeightInfo = (); @@ -80,11 +79,11 @@ impl pallet_balances::Config for Test { /// Filter to block balance pallet calls pub struct MockSafeModeFilter; -impl Contains for MockSafeModeFilter { - fn contains(call: &Call) -> bool { +impl Contains for MockSafeModeFilter { + fn contains(call: &RuntimeCall) -> bool { match call { - Call::System(_) | Call::SafeMode(_) => true, - Call::Balances(_) => false, + RuntimeCall::System(_) | RuntimeCall::SafeMode(_) => true, + RuntimeCall::Balances(_) => false, } } } @@ -249,16 +248,13 @@ pub fn new_test_ext() -> sp_io::TestExternalities { pallet_balances::GenesisConfig:: { // The 0 account is NOT a special origin, the rest may be. - balances: vec![(0, 1234), (1, 5678), (2, 5678), (3, 5678), (4, 5678)], + balances: vec![(0, 1234), (1, 5678), (2, 5678), (3, 5678), (4, 5678)], } .assimilate_storage(&mut t) .unwrap(); GenesisBuild::::assimilate_storage( - &pallet_safe_mode::GenesisConfig { - active: None, - _phantom: Default::default(), - }, + &pallet_safe_mode::GenesisConfig { active: None, _phantom: Default::default() }, &mut t, ) .unwrap(); diff --git a/frame/safe-mode/src/tests.rs b/frame/safe-mode/src/tests.rs index 1ea23295ee7b8..2e75f40e0f2c3 100644 --- a/frame/safe-mode/src/tests.rs +++ b/frame/safe-mode/src/tests.rs @@ -18,7 +18,7 @@ //! Test utilities for the safe mode pallet. use super::*; -use crate::mock::{Call, *}; +use crate::mock::{RuntimeCall, *}; use frame_support::{assert_err, assert_noop, assert_ok, dispatch::Dispatchable}; @@ -29,7 +29,7 @@ fn fails_to_filter_calls_to_safe_mode_pallet() { new_test_ext().execute_with(|| { assert_ok!(SafeMode::activate(Origin::signed(0))); let activated_at_block = System::block_number(); - let call = Call::Balances(pallet_balances::Call::transfer { dest: 1, value: 1 }); + let call = RuntimeCall::Balances(pallet_balances::Call::transfer { dest: 1, value: 1 }); assert_err!( call.clone().dispatch(Origin::signed(0)), @@ -69,7 +69,7 @@ fn fails_to_filter_calls_to_safe_mode_pallet() { #[test] fn fails_to_extend_if_not_activated() { new_test_ext().execute_with(|| { - assert_eq!(SafeMode::activated(), None); + assert_eq!(SafeMode::active_until(), None); assert_noop!(SafeMode::extend(Origin::signed(2)), Error::::IsInactive); }); } @@ -83,14 +83,14 @@ fn can_automatically_inactivate_after_timeout() { assert_ok!(SafeMode::force_activate(ForceActivateOrigin::Weak.signed())); run_to(ForceActivateOrigin::Weak.duration() + activated_at_block + 1); SafeMode::on_initialize(System::block_number()); - assert_eq!(SafeMode::activated(), None); + assert_eq!(SafeMode::active_until(), None); }); } #[test] fn can_filter_balance_calls_when_activated() { new_test_ext().execute_with(|| { - let call = Call::Balances(pallet_balances::Call::transfer { dest: 1, value: 1 }); + let call = RuntimeCall::Balances(pallet_balances::Call::transfer { dest: 1, value: 1 }); assert_ok!(call.clone().dispatch(Origin::signed(0))); assert_ok!(SafeMode::activate(Origin::signed(0))); @@ -109,7 +109,7 @@ fn can_activate_with_signed_origin() { new_test_ext().execute_with(|| { assert_ok!(SafeMode::activate(Origin::signed(0))); assert_eq!( - SafeMode::activated().unwrap(), + SafeMode::active_until().unwrap(), System::block_number() + mock::ActivateDuration::get() ); assert_eq!(Balances::reserved_balance(0), mock::ActivateStakeAmount::get()); @@ -125,7 +125,7 @@ fn can_extend_with_signed_origin() { assert_ok!(SafeMode::activate(Origin::signed(0))); assert_ok!(SafeMode::extend(Origin::signed(0))); assert_eq!( - SafeMode::activated().unwrap(), + SafeMode::active_until().unwrap(), System::block_number() + mock::ActivateDuration::get() + mock::ExtendDuration::get() ); assert_eq!( @@ -138,7 +138,7 @@ fn can_extend_with_signed_origin() { #[test] fn fails_signed_origin_when_explicit_origin_required() { new_test_ext().execute_with(|| { - assert_eq!(SafeMode::activated(), None); + assert_eq!(SafeMode::active_until(), None); let activated_at_block = System::block_number(); assert_err!(SafeMode::force_activate(Origin::signed(0)), DispatchError::BadOrigin); @@ -176,7 +176,7 @@ fn can_force_activate_with_config_origin() { new_test_ext().execute_with(|| { assert_ok!(SafeMode::force_activate(ForceActivateOrigin::Weak.signed())); assert_eq!( - SafeMode::activated().unwrap(), + SafeMode::active_until().unwrap(), System::block_number() + ForceActivateOrigin::Weak.duration() ); assert_noop!( @@ -190,7 +190,7 @@ fn can_force_activate_with_config_origin() { #[test] fn can_force_inactivate_with_config_origin() { new_test_ext().execute_with(|| { - assert_eq!(SafeMode::activated(), None); + assert_eq!(SafeMode::active_until(), None); assert_err!( SafeMode::force_inactivate(Origin::signed(mock::DeactivateOrigin::get())), Error::::IsInactive @@ -207,12 +207,12 @@ fn can_force_extend_with_config_origin() { // Activated by `Weak` and extended by `Medium`. assert_ok!(SafeMode::force_activate(ForceActivateOrigin::Weak.signed())); assert_eq!( - SafeMode::activated().unwrap(), + SafeMode::active_until().unwrap(), System::block_number() + ForceActivateOrigin::Weak.duration() ); assert_ok!(SafeMode::force_extend(ForceExtendOrigin::Medium.signed())); assert_eq!( - SafeMode::activated().unwrap(), + SafeMode::active_until().unwrap(), System::block_number() + ForceActivateOrigin::Weak.duration() + ForceExtendOrigin::Medium.duration() @@ -265,7 +265,7 @@ fn can_slash_stake_with_config_origin() { #[test] fn fails_when_explicit_origin_required() { new_test_ext().execute_with(|| { - assert_eq!(SafeMode::activated(), None); + assert_eq!(SafeMode::active_until(), None); let activated_at_block = System::block_number(); assert_err!( @@ -311,11 +311,19 @@ fn fails_when_explicit_origin_required() { DispatchError::BadOrigin ); assert_err!( - SafeMode::slash_stake(Origin::signed(mock::DeactivateOrigin::get()), 0, activated_at_block), + SafeMode::slash_stake( + Origin::signed(mock::DeactivateOrigin::get()), + 0, + activated_at_block + ), DispatchError::BadOrigin ); assert_err!( - SafeMode::repay_stake(Origin::signed(mock::DeactivateOrigin::get()), 0, activated_at_block), + SafeMode::repay_stake( + Origin::signed(mock::DeactivateOrigin::get()), + 0, + activated_at_block + ), DispatchError::BadOrigin ); diff --git a/frame/tx-pause/src/lib.rs b/frame/tx-pause/src/lib.rs index cfa31d3d8d6cb..86e1a1426c7db 100644 --- a/frame/tx-pause/src/lib.rs +++ b/frame/tx-pause/src/lib.rs @@ -49,7 +49,7 @@ pub mod pallet { #[pallet::config] pub trait Config: frame_system::Config { /// The overarching event type. - type Event: From> + IsType<::Event>; + type RuntimeEvent: From> + IsType<::RuntimeEvent>; /// The only origin that can pause calls. type PauseOrigin: EnsureOrigin; @@ -180,7 +180,7 @@ pub mod pallet { impl Pallet { /// Return whether this call is paused. pub fn is_paused_unbound(pallet: Vec, call: Vec) -> bool { - let pallet = PalletNameOf::::try_from(call); + let pallet = PalletNameOf::::try_from(pallet); let call = CallNameOf::::try_from(call); match (pallet, call) { @@ -226,12 +226,12 @@ impl Pallet { } } -impl Contains for Pallet +impl Contains for Pallet where - ::Call: GetCallMetadata, + ::RuntimeCall: GetCallMetadata, { /// Return whether the call is allowed to be dispatched. - fn contains(call: &T::Call) -> bool { + fn contains(call: &T::RuntimeCall) -> bool { let CallMetadata { pallet_name, function_name } = call.get_call_metadata(); !Pallet::::is_paused_unbound(pallet_name.into(), function_name.into()) } diff --git a/frame/tx-pause/src/mock.rs b/frame/tx-pause/src/mock.rs index a3a128893f348..1ed00c9805dba 100644 --- a/frame/tx-pause/src/mock.rs +++ b/frame/tx-pause/src/mock.rs @@ -39,7 +39,7 @@ impl frame_system::Config for Test { type BlockWeights = (); type BlockLength = (); type Origin = Origin; - type Call = Call; + type RuntimeCall = RuntimeCall; type Index = u64; type BlockNumber = u64; type Hash = H256; @@ -47,7 +47,7 @@ impl frame_system::Config for Test { type AccountId = u64; type Lookup = IdentityLookup; type Header = Header; - type Event = Event; + type RuntimeEvent = RuntimeEvent; type BlockHashCount = BlockHashCount; type DbWeight = (); type Version = (); @@ -68,7 +68,7 @@ parameter_types! { impl pallet_balances::Config for Test { type Balance = u64; type DustRemoval = (); - type Event = Event; + type RuntimeEvent = RuntimeEvent; type ExistentialDeposit = ExistentialDeposit; type AccountStore = System; type WeightInfo = (); @@ -111,7 +111,7 @@ impl SortedMembers for UnpauseOrigin { } impl Config for Test { - type Event = Event; + type RuntimeEvent = RuntimeEvent; type PauseOrigin = EnsureSignedBy; type UnpauseOrigin = EnsureSignedBy; type UnpausablePallets = MockUnpausablePallets; diff --git a/frame/tx-pause/src/tests.rs b/frame/tx-pause/src/tests.rs index 30d9ec7600bfc..f474d11eeb838 100644 --- a/frame/tx-pause/src/tests.rs +++ b/frame/tx-pause/src/tests.rs @@ -18,7 +18,7 @@ #![cfg(test)] use super::*; -use crate::mock::{Call, *}; +use crate::mock::{RuntimeCall, *}; use frame_support::{assert_err, assert_noop, assert_ok, dispatch::Dispatchable}; @@ -38,7 +38,7 @@ fn can_set_arbitrary_pause() { #[test] fn can_pause_system_call() { new_test_ext().execute_with(|| { - let call = Call::System(frame_system::Call::remark { remark: vec![] }); + let call = RuntimeCall::System(frame_system::Call::remark { remark: vec![] }); assert_ok!(TxPause::pause_call( Origin::signed(mock::PauseOrigin::get()), @@ -56,9 +56,10 @@ fn can_pause_system_call() { #[test] fn can_pause_specific_call() { new_test_ext().execute_with(|| { - let call_paused = Call::Balances(pallet_balances::Call::transfer { dest: 1, value: 1 }); + let call_paused = + RuntimeCall::Balances(pallet_balances::Call::transfer { dest: 1, value: 1 }); let call_not_paused = - Call::Balances(pallet_balances::Call::transfer_keep_alive { dest: 1, value: 1 }); + RuntimeCall::Balances(pallet_balances::Call::transfer_keep_alive { dest: 1, value: 1 }); assert_ok!(TxPause::pause_call( Origin::signed(mock::PauseOrigin::get()), @@ -77,7 +78,8 @@ fn can_pause_specific_call() { #[test] fn can_unpause_specific_call() { new_test_ext().execute_with(|| { - let call_paused = Call::Balances(pallet_balances::Call::transfer { dest: 1, value: 1 }); + let call_paused = + RuntimeCall::Balances(pallet_balances::Call::transfer { dest: 1, value: 1 }); assert_ok!(TxPause::pause_call( Origin::signed(mock::PauseOrigin::get()), From 1983a4da9b0b5f0d73041d3c5b1636ecdf62096b Mon Sep 17 00:00:00 2001 From: Dan Shields <35669742+NukeManDan@users.noreply.github.com> Date: Tue, 11 Oct 2022 18:40:16 -0600 Subject: [PATCH 023/100] WIP safe mode and tx pause {continued} (#12371) * test coverage on safe mode and tx pause * benchmarks & tests * tests passing, use FullNameOf to check tx-pause unfilterables * naming updates Signed-off-by: Oliver Tale-Yazdi Co-authored-by: Oliver Tale-Yazdi --- Cargo.lock | 4 + bin/node/cli/src/chain_spec.rs | 2 - bin/node/runtime/src/lib.rs | 204 +++++++++++++++++---- frame/safe-mode/Cargo.toml | 13 +- frame/safe-mode/src/benchmarking.rs | 192 +++++++++++++------- frame/safe-mode/src/lib.rs | 211 ++++++++++++---------- frame/safe-mode/src/mock.rs | 122 ++++++++++--- frame/safe-mode/src/tests.rs | 265 ++++++++++++++++++++++------ frame/safe-mode/src/weights.rs | 106 ++++++++++- frame/tx-pause/Cargo.toml | 12 +- frame/tx-pause/src/benchmarking.rs | 59 +++---- frame/tx-pause/src/lib.rs | 94 ++++++---- frame/tx-pause/src/mock.rs | 100 +++++++++-- frame/tx-pause/src/tests.rs | 115 +++++++----- frame/tx-pause/src/weights.rs | 10 +- 15 files changed, 1102 insertions(+), 407 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1e624fe65b33d..e98d42f5076bd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6184,6 +6184,8 @@ dependencies = [ "frame-support", "frame-system", "pallet-balances", + "pallet-proxy", + "pallet-utility", "parity-scale-codec", "scale-info", "sp-core", @@ -6513,6 +6515,8 @@ dependencies = [ "frame-support", "frame-system", "pallet-balances", + "pallet-proxy", + "pallet-utility", "parity-scale-codec", "scale-info", "sp-core", diff --git a/bin/node/cli/src/chain_spec.rs b/bin/node/cli/src/chain_spec.rs index 61e051120ffbe..b549f5f6ed0c7 100644 --- a/bin/node/cli/src/chain_spec.rs +++ b/bin/node/cli/src/chain_spec.rs @@ -373,8 +373,6 @@ pub fn testnet_genesis( min_join_bond: 1 * DOLLARS, ..Default::default() }, - safe_mode: Default::default(), - tx_pause: Default::default(), } } diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index e6ef73a5a3269..d20f10014f3d1 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -33,9 +33,8 @@ use frame_support::{ parameter_types, traits::{ AsEnsureOriginWithArg, ConstBool, ConstU128, ConstU16, ConstU32, Contains, Currency, - EitherOfDiverse, EqualPrivilegeOnly, Imbalance, InsideBoth, InstanceFilter, - KeyOwnerProofSystem, LockIdentifier, Nothing, OnUnbalanced, PalletInfoAccess, - U128CurrencyToVote, + EitherOfDiverse, EnsureOrigin, EqualPrivilegeOnly, Imbalance, InsideBoth, InstanceFilter, + KeyOwnerProofSystem, LockIdentifier, Nothing, OnUnbalanced, U128CurrencyToVote, }, weights::{ constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND}, @@ -45,7 +44,7 @@ use frame_support::{ }; use frame_system::{ limits::{BlockLength, BlockWeights}, - EnsureRoot, EnsureRootWithSuccess, EnsureSigned, + EnsureRoot, EnsureRootWithSuccess, EnsureSigned, RawOrigin, }; pub use node_primitives::{AccountId, Signature}; use node_primitives::{AccountIndex, Balance, BlockNumber, Hash, Index, Moment}; @@ -201,57 +200,189 @@ parameter_types! { const_assert!(NORMAL_DISPATCH_RATIO.deconstruct() >= AVERAGE_ON_INITIALIZE_RATIO.deconstruct()); -pub struct UnpausablePallets; -impl Contains> for UnpausablePallets { - fn contains(pallet: &pallet_tx_pause::PalletNameOf) -> bool { - pallet.as_ref() == - as PalletInfoAccess>::name() - .as_bytes() - .to_vec() +/// Filter to block balance pallet calls +/// Used for both SafeMode and TxPause pallets +/// Therefor we include both so they cannot affect each other +pub struct UnfilterableCalls; +impl Contains for UnfilterableCalls { + fn contains(call: &RuntimeCall) -> bool { + match call { + RuntimeCall::System(_) | RuntimeCall::SafeMode(_) | RuntimeCall::TxPause(_) => true, + RuntimeCall::Balances(_) => false, + _ => false, + } + } +} + +use pallet_tx_pause::FullNameOf; +pub struct UnfilterableCallNames; +/// Make Balances::transfer_keep_alive unfilterable, accept all others. +impl Contains> for UnfilterableCallNames { + fn contains(full_name: &FullNameOf) -> bool { + let unpausables: Vec> = vec![( + b"Balances".to_vec().try_into().unwrap(), + Some(b"transfer_keep_alive".to_vec().try_into().unwrap()), + )]; + + for unpausable_call in unpausables { + let (pallet_name, maybe_call_name) = full_name; + if pallet_name == &unpausable_call.0 { + if unpausable_call.1.is_none() { + return true + } + return maybe_call_name == &unpausable_call.1 + } + } + + false } } impl pallet_tx_pause::Config for Runtime { - type Event = Event; - type UnpausablePallets = UnpausablePallets; + type RuntimeEvent = RuntimeEvent; + type RuntimeCall = RuntimeCall; type PauseOrigin = EnsureRoot; type UnpauseOrigin = EnsureRoot; + type UnfilterableCallNames = UnfilterableCallNames; type MaxNameLen = ConstU32<256>; type PauseTooLongNames = ConstBool; type WeightInfo = pallet_tx_pause::weights::SubstrateWeight; } -parameter_types! { - // signed config - pub const ActivateStakeAmount: Balance = 1 * DOLLARS; //TODO This needs to be something sensible for the implications of enablement! - pub const ExtendStakeAmount: Balance = 1 * DOLLARS; //TODO This needs to be something sensible for the implications of enablement! - pub BlockHeight: BlockNumber = System::block_number(); // TODO ensure this plus config below is correct +/// An origin that can enable the safe-mode by force. +pub enum ForceActivateOrigin { + Weak, + Medium, + Strong, +} + +/// An origin that can extend the safe-mode by force. +pub enum ForceExtendOrigin { + Weak, + Medium, + Strong, +} + +impl ForceActivateOrigin { + /// The duration of how long the safe-mode will be activated. + pub fn duration(&self) -> u32 { + match self { + Self::Weak => 5, + Self::Medium => 7, + Self::Strong => 11, + } + } + + /// Account id of the origin. + pub fn acc(&self) -> AccountId { + match self { + Self::Weak => sp_core::ed25519::Public::from_raw([0; 32]).into(), + Self::Medium => sp_core::ed25519::Public::from_raw([1; 32]).into(), + Self::Strong => sp_core::ed25519::Public::from_raw([2; 32]).into(), + } + } + + /// Signed origin. + pub fn signed(&self) -> ::Origin { + RawOrigin::Signed(self.acc()).into() + } +} + +impl ForceExtendOrigin { + /// The duration of how long the safe-mode will be extended. + pub fn duration(&self) -> u32 { + match self { + Self::Weak => 13, + Self::Medium => 17, + Self::Strong => 19, + } + } + + /// Account id of the origin. + pub fn acc(&self) -> AccountId { + match self { + Self::Weak => sp_core::ed25519::Public::from_raw([0; 32]).into(), + Self::Medium => sp_core::ed25519::Public::from_raw([1; 32]).into(), + Self::Strong => sp_core::ed25519::Public::from_raw([2; 32]).into(), + } + } + + /// Signed origin. + pub fn signed(&self) -> ::Origin { + RawOrigin::Signed(self.acc()).into() + } +} + +impl, O>> + From>> EnsureOrigin + for ForceActivateOrigin +{ + type Success = u32; + + fn try_origin(o: O) -> Result { + o.into().and_then(|o| match o { + RawOrigin::Signed(acc) if acc == ForceActivateOrigin::Weak.acc() => + Ok(ForceActivateOrigin::Weak.duration()), + RawOrigin::Signed(acc) if acc == ForceActivateOrigin::Medium.acc() => + Ok(ForceActivateOrigin::Medium.duration()), + RawOrigin::Signed(acc) if acc == ForceActivateOrigin::Strong.acc() => + Ok(ForceActivateOrigin::Strong.duration()), + r => Err(O::from(r)), + }) + } + + #[cfg(feature = "runtime-benchmarks")] + fn successful_origin() -> O { + O::from(RawOrigin::Signed(ForceActivateOrigin::Strong.acc())) + } +} + +impl, O>> + From>> EnsureOrigin + for ForceExtendOrigin +{ + type Success = u32; + + fn try_origin(o: O) -> Result { + o.into().and_then(|o| match o { + RawOrigin::Signed(acc) if acc == ForceExtendOrigin::Weak.acc() => + Ok(ForceExtendOrigin::Weak.duration()), + RawOrigin::Signed(acc) if acc == ForceExtendOrigin::Medium.acc() => + Ok(ForceExtendOrigin::Medium.duration()), + RawOrigin::Signed(acc) if acc == ForceExtendOrigin::Strong.acc() => + Ok(ForceExtendOrigin::Strong.duration()), + r => Err(O::from(r)), + }) + } + + #[cfg(feature = "runtime-benchmarks")] + fn successful_origin() -> O { + O::from(RawOrigin::Signed(ForceActivateOrigin::Strong.acc())) + } } parameter_types! { - // signed config - pub const ActivateStakeAmount: Balance = 1 * DOLLARS; //TODO This needs to be something sensible for the implications of enablement! - pub const ExtendStakeAmount: Balance = 1 * DOLLARS; //TODO This needs to be something sensible for the implications of enablement! - pub BlockHeight: BlockNumber = System::block_number(); // TODO ensure this plus config below is correct + pub const SignedActivationDuration: u32 = 3; + pub const SignedExtendDuration: u32 = 30; + pub const ActivateReservationAmount: Balance = 10 * DOLLARS; //TODO This needs to be something sensible for the implications of enablement! + pub const ExtendReservationAmount: Balance = 10 * DOLLARS; //TODO This needs to be something sensible for the implications of enablement! } impl pallet_safe_mode::Config for Runtime { - type Event = Event; + type RuntimeEvent = RuntimeEvent; type Currency = Balances; - type SafeModeFilter = Nothing; // TODO add TxPause pallet - type ActivateDuration = ConstU32<{ 2 * DAYS }>; - type ExtendDuration = ConstU32<{ 1 * DAYS }>; - type EnableOrigin = EnsureRootWithSuccess; - type ExtendOrigin = EnsureRootWithSuccess; - type DeactivateOrigin = EnsureRoot; - type RepayOrigin = EnsureRoot; - type ActivateStakeAmount = ActivateStakeAmount; - type ExtendStakeAmount = ExtendStakeAmount; + type UnfilterableCalls = UnfilterableCalls; + type SignedActivationDuration = ConstU32<{ 2 * DAYS }>; + type ActivateReservationAmount = ActivateReservationAmount; + type SignedExtendDuration = ConstU32<{ 1 * DAYS }>; + type ExtendReservationAmount = ExtendReservationAmount; + type ForceActivateOrigin = ForceActivateOrigin; + type ForceExtendOrigin = ForceExtendOrigin; + type ForceDeactivateOrigin = EnsureRoot; + type RepayOrigin = EnsureRoot; type WeightInfo = pallet_safe_mode::weights::SubstrateWeight; } impl frame_system::Config for Runtime { - type BaseCallFilter = InsideBoth; + type BaseCallFilter = InsideBoth; // TODO consider Exclude or NotInside for UnfilterableCalls -> see TheseExcept ) type BlockWeights = RuntimeBlockWeights; type BlockLength = RuntimeBlockLength; type DbWeight = RocksDbWeight; @@ -312,6 +443,11 @@ parameter_types! { pub const AnnouncementDepositFactor: Balance = deposit(0, 66); } +// pub enum PausePresets { +// ..., // todo + +// } + /// The type used to represent the kinds of proxying allowed. #[derive( Copy, @@ -482,7 +618,7 @@ parameter_types! { impl pallet_balances::Config for Runtime { type MaxLocks = MaxLocks; type MaxReserves = MaxReserves; - type ReserveIdentifier = [u8; 8]; + type ReserveIdentifier = Self::BlockNumber; type Balance = Balance; type DustRemoval = (); type RuntimeEvent = RuntimeEvent; diff --git a/frame/safe-mode/Cargo.toml b/frame/safe-mode/Cargo.toml index 2ec371617b8cc..ea1f77133be16 100644 --- a/frame/safe-mode/Cargo.toml +++ b/frame/safe-mode/Cargo.toml @@ -20,12 +20,17 @@ frame-system = { version = "4.0.0-dev", default-features = false, path = "../sys scale-info = { version = "2.0.1", default-features = false, features = ["derive"] } sp-runtime = { version = "6.0.0", default-features = false, path = "../../primitives/runtime" } sp-std = { version = "4.0.0", default-features = false, path = "../../primitives/std" } +pallet-balances = { version = "4.0.0-dev", path = "../balances", default-features = false, optional = true } +pallet-utility = { version = "4.0.0-dev", path = "../utility", default-features = false, optional = true } +pallet-proxy = { version = "4.0.0-dev", path = "../proxy", default-features = false, optional = true } [dev-dependencies] sp-core = { version = "6.0.0", path = "../../primitives/core" } sp-std = { version = "4.0.0", path = "../../primitives/std" } sp-io = { version = "6.0.0", path = "../../primitives/io" } pallet-balances = { version = "4.0.0-dev", path = "../balances" } +pallet-utility = { version = "4.0.0-dev", path = "../utility" } +pallet-proxy = { version = "4.0.0-dev", path = "../proxy" } [features] default = ["std"] @@ -33,8 +38,11 @@ std = [ "codec/std", "scale-info/std", "frame-benchmarking/std", - "frame-support/std", "frame-system/std", + "frame-support/std", + "pallet-balances?/std", + "pallet-utility?/std", + "pallet-proxy?/std", "sp-runtime/std", "sp-std/std", ] @@ -42,6 +50,9 @@ runtime-benchmarks = [ "frame-benchmarking/runtime-benchmarks", "frame-support/runtime-benchmarks", "frame-system/runtime-benchmarks", + "pallet-balances/runtime-benchmarks", + "pallet-utility/runtime-benchmarks", + "pallet-proxy/runtime-benchmarks", ] try-runtime = [ "frame-support/try-runtime", diff --git a/frame/safe-mode/src/benchmarking.rs b/frame/safe-mode/src/benchmarking.rs index 8fb175e8dd141..0c43a4b30a2d3 100644 --- a/frame/safe-mode/src/benchmarking.rs +++ b/frame/safe-mode/src/benchmarking.rs @@ -7,7 +7,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -17,79 +17,139 @@ #![cfg(feature = "runtime-benchmarks")] -use super::{Call as SafeModeCall, Pallet as SafeMode, *}; +use super::{Pallet as SafeMode, *}; use frame_benchmarking::{benchmarks, whitelisted_caller}; -use frame_support::traits::Currency; +use frame_support::traits::{Currency, UnfilteredDispatchable}; use frame_system::{Pallet as System, RawOrigin}; use sp_runtime::traits::Bounded; benchmarks! { - activate { + activate { let caller: T::AccountId = whitelisted_caller(); - let origin = RawOrigin::Signed(caller.clone()); + let origin = RawOrigin::Signed(caller.clone()); T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); - }: activate(origin) - verify { + }: _(origin) + verify { assert_eq!( - SafeMode::::activated().unwrap(), - System::::block_number() + T::ActivateDuration::get() + SafeMode::::active_until().unwrap(), + System::::block_number() + T::SignedActivationDuration::get() ); - } - -// force_activate { -// /* code to set the initial state */ -// }: { -// /* code to test the function benchmarked */ -// } -// verify { -// /* optional verification */ -// } - -// extend { -// /* code to set the initial state */ -// }: { -// /* code to test the function benchmarked */ -// } -// verify { -// /* optional verification */ -// } - -// force_extend { -// /* code to set the initial state */ -// }: { -// /* code to test the function benchmarked */ -// } -// verify { -// /* optional verification */ -// } - -// force_inactivate { -// /* code to set the initial state */ -// }: { -// /* code to test the function benchmarked */ -// } -// verify { -// /* optional verification */ -// } - -// repay_stake { -// /* code to set the initial state */ -// }: { -// /* code to test the function benchmarked */ -// } -// verify { -// /* optional verification */ -// } - -// slash_stake { -// /* code to set the initial state */ -// }: { -// /* code to test the function benchmarked */ -// } -// verify { -// /* optional verification */ -// } - - impl_benchmark_test_suite!(SafeMode, crate::mock::new_test_ext(), crate::mock::Test); + } + + force_activate { + let force_origin = T::ForceActivateOrigin::successful_origin(); + let duration = T::ForceActivateOrigin::ensure_origin(force_origin.clone()).unwrap(); + let call = Call::::force_activate {}; + }: { call.dispatch_bypass_filter(force_origin)? } + verify { + assert_eq!( + SafeMode::::active_until().unwrap(), + System::::block_number() + + duration + ); + } + + extend { + let caller: T::AccountId = whitelisted_caller(); + let origin = RawOrigin::Signed(caller.clone()); + T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); + + assert!(SafeMode::::activate(origin.clone().into()).is_ok()); + }: _(origin) + verify { + assert_eq!( + SafeMode::::active_until().unwrap(), + System::::block_number() + T::SignedActivationDuration::get() + T::SignedExtendDuration::get() + ); + } + + force_extend { + let caller: T::AccountId = whitelisted_caller(); + let origin = RawOrigin::Signed(caller.clone()); + T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); + + assert!(SafeMode::::activate(origin.clone().into()).is_ok()); + + let force_origin = T::ForceExtendOrigin::successful_origin(); + let extension = T::ForceExtendOrigin::ensure_origin(force_origin.clone()).unwrap(); + let call = Call::::force_extend {}; + }: { call.dispatch_bypass_filter(force_origin)? } + verify { + assert_eq!( + SafeMode::::active_until().unwrap(), + System::::block_number() + T::SignedActivationDuration::get() + extension + ); + } + + force_deactivate { + let caller: T::AccountId = whitelisted_caller(); + let origin = RawOrigin::Signed(caller.clone()); + T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); + + assert!(SafeMode::::activate(origin.clone().into()).is_ok()); + + let force_origin = T::ForceDeactivateOrigin::successful_origin(); + let call = Call::::force_deactivate {}; + }: { call.dispatch_bypass_filter(force_origin)? } + verify { + assert_eq!( + SafeMode::::active_until(), + None + ); + } + + release_reservation { + let caller: T::AccountId = whitelisted_caller(); + let origin = RawOrigin::Signed(caller.clone()); + T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); + + let activated_at_block: T::BlockNumber = System::::block_number(); + assert!(SafeMode::::activate(origin.clone().into()).is_ok()); + let current_reservation = Reservations::::get(&caller, activated_at_block).unwrap_or_default(); + assert_eq!( + T::Currency::free_balance(&caller), + BalanceOf::::max_value() - T::ActivateReservationAmount::get().unwrap() + ); + + let force_origin = T::ForceDeactivateOrigin::successful_origin(); + assert!(SafeMode::::force_deactivate(force_origin.clone()).is_ok()); + + let repay_origin = T::RepayOrigin::successful_origin(); + let call = Call::::release_reservation { account: caller.clone(), block: activated_at_block.clone()}; + }: { call.dispatch_bypass_filter(repay_origin)? } + verify { + assert_eq!( + T::Currency::free_balance(&caller), + BalanceOf::::max_value() + ); + } + + slash_reservation { + let caller: T::AccountId = whitelisted_caller(); + let origin = RawOrigin::Signed(caller.clone()); + T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); + + let activated_at_block: T::BlockNumber = System::::block_number(); + assert!(SafeMode::::activate(origin.clone().into()).is_ok()); + let current_reservation = Reservations::::get(&caller, activated_at_block).unwrap_or_default(); + assert_eq!( + T::Currency::free_balance(&caller), + BalanceOf::::max_value() - T::ActivateReservationAmount::get().unwrap() + ); + + let force_origin = T::ForceDeactivateOrigin::successful_origin(); + assert!(SafeMode::::force_deactivate(force_origin.clone()).is_ok()); + + let repay_origin = T::RepayOrigin::successful_origin(); + let call = Call::::slash_reservation { account: caller.clone(), block: activated_at_block.clone()}; + }: { call.dispatch_bypass_filter(repay_origin)? } + verify { + assert_eq!( + T::Currency::free_balance(&caller), + BalanceOf::::max_value() - T::ActivateReservationAmount::get().unwrap() + ); + } + + impl_benchmark_test_suite!(SafeMode, crate::mock::new_test_ext(), crate::mock::Test); } diff --git a/frame/safe-mode/src/lib.rs b/frame/safe-mode/src/lib.rs index db0ae1d2d5650..bcf05934aa34c 100644 --- a/frame/safe-mode/src/lib.rs +++ b/frame/safe-mode/src/lib.rs @@ -17,7 +17,6 @@ #![cfg_attr(not(feature = "std"), no_std)] -#[cfg(feature = "runtime-benchmarks")] mod benchmarking; #[cfg(test)] pub mod mock; @@ -28,8 +27,8 @@ pub mod weights; use frame_support::{ pallet_prelude::*, traits::{ - CallMetadata, Contains, Currency, Defensive, GetCallMetadata, PalletInfoAccess, - ReservableCurrency, + CallMetadata, Contains, Currency, Defensive, GetCallMetadata, NamedReservableCurrency, + PalletInfoAccess, }, weights::Weight, }; @@ -56,50 +55,56 @@ pub mod pallet { /// The overarching event type. type RuntimeEvent: From> + IsType<::RuntimeEvent>; - /// Currency type for this pallet. - type Currency: ReservableCurrency; + /// Currency type for this pallet, used for reservations. + type Currency: NamedReservableCurrency< + Self::AccountId, + ReserveIdentifier = Self::BlockNumber, + >; - /// Contains all calls that can be dispatched even when the safe-mode is activated. + /// Contains all runtime calls in any pallet that can be dispatched even while the safe-mode + /// is activated. /// - /// The `SafeMode` pallet is always included and does not need to be added here. - type SafeModeFilter: Contains; + /// The safe-mode pallet cannot disable it's own calls, and does not need to be explicitly + /// added here. + type UnfilterableCalls: Contains; /// How long the safe-mode will stay active when activated with [`Pallet::activate`]. #[pallet::constant] - type ActivateDuration: Get; + type SignedActivationDuration: Get; /// For how many blocks the safe-mode can be extended by each [`Pallet::extend`] call. /// /// This does not impose a hard limit as the safe-mode can be extended multiple times. #[pallet::constant] - type ExtendDuration: Get; + type SignedExtendDuration: Get; /// The amount that will be reserved upon calling [`Pallet::activate`]. /// /// `None` disallows permissionlessly enabling the safe-mode. #[pallet::constant] - type ActivateStakeAmount: Get>>; + type ActivateReservationAmount: Get>>; /// The amount that will be reserved upon calling [`Pallet::extend`]. /// /// `None` disallows permissionlessly extending the safe-mode. #[pallet::constant] - type ExtendStakeAmount: Get>>; + type ExtendReservationAmount: Get>>; - /// The origin that can call [`Pallet::force_activate`]. + /// The origin that may call [`Pallet::force_activate`]. /// /// The `Success` value is the number of blocks that this origin can activate safe-mode for. type ForceActivateOrigin: EnsureOrigin; - /// The origin that can call [`Pallet::force_extend`]. + /// The origin that may call [`Pallet::force_extend`]. /// /// The `Success` value is the number of blocks that this origin can extend the safe-mode. type ForceExtendOrigin: EnsureOrigin; - /// The origin that can call [`Pallet::force_inactivate`]. - type ForceInactivateOrigin: EnsureOrigin; + /// The origin that may call [`Pallet::force_activate`]. + type ForceDeactivateOrigin: EnsureOrigin; - /// The origin that can call [`Pallet::repay_stake`] and [`Pallet::slash_stake`]. + /// The origin that may call [`Pallet::release_reservation`] and + /// [`Pallet::slash_reservation`]. type RepayOrigin: EnsureOrigin; // Weight information for extrinsics in this pallet. @@ -117,27 +122,28 @@ pub mod pallet { /// A value that is required for the extrinsic was not configured. NotConfigured, - /// There is no balance staked. - NotStaked, + /// There is no balance reserved. + NoReservation, } #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event { /// The safe-mode was activated until inclusively this \[block\]. - Activated(T::BlockNumber), + Activated { block: T::BlockNumber }, /// The safe-mode was extended until inclusively this \[block\]. - Extended(T::BlockNumber), + Extended { block: T::BlockNumber }, /// Exited safe-mode for a specific \[reason\]. - Exited(ExitReason), + Exited { reason: ExitReason }, - /// An account got repaid its stake. \[account, amount\] - StakeRepaid(T::AccountId, BalanceOf), + /// An account had reserve repaid previously reserved at a block. \[block, account, + /// amount\] + ReservationRepaid { block: T::BlockNumber, account: T::AccountId, amount: BalanceOf }, - /// An account got slashed its stake. \[account, amount\] - StakeSlashed(T::AccountId, BalanceOf), + /// An account had reserve slashed previously reserved at a block. \[account, amount\] + ReservationSlashed { block: T::BlockNumber, account: T::AccountId, amount: BalanceOf }, } /// The reason why the safe-mode was exited. @@ -146,23 +152,23 @@ pub mod pallet { /// The safe-mode was automatically exited after its duration ran out. Timeout, - /// The safe-mode was forcefully exited by [`Pallet::force_inactivate`]. + /// The safe-mode was forcefully exited by [`Pallet::force_deactivate`]. Force, } /// Contains the last block number that the safe-mode will stay activated. /// /// This is set to `None` if the safe-mode is inactive. - /// The safe-mode is automatically inactivated when the current block number is greater than + /// The safe-mode is automatically deactivated when the current block number is greater than /// this. #[pallet::storage] #[pallet::getter(fn active_until)] pub type ActiveUntil = StorageValue<_, T::BlockNumber, OptionQuery>; - /// Holds the stake that was reserved from a user at a specific block number. + /// Holds the reserve that was reserved from a user at a specific block number. #[pallet::storage] - #[pallet::getter(fn stakes)] - pub type Stakes = StorageDoubleMap< + #[pallet::getter(fn reserves)] + pub type Reservations = StorageDoubleMap< _, Twox64Concat, T::AccountId, @@ -199,21 +205,23 @@ pub mod pallet { #[pallet::call] impl Pallet { - /// Activate safe-mode permissionlessly for [`Config::ActivateDuration`] blocks. + /// Activate safe-mode permissionlessly for [`Config::SignedActivationDuration`] blocks. /// - /// Reserves [`Config::ActivateStakeAmount`] from the caller's account. + /// Reserves [`Config::ActivateReservationAmount`] from the caller's account. /// Emits an [`Event::Activated`] event on success. /// Errors with [`Error::IsActive`] if the safe-mode is already activated. + /// Errors with [`Error::NotConfigured`] if the reservation amount is `None` . /// /// ### Safety /// - /// Can be permanently disabled by configuring [`Config::ActivateStakeAmount`] to `None`. + /// This may be called by any signed origin with [`Config::ActivateReservationAmount`] free + /// currency to reserve. This call can be disabled for all origins by configuring + /// [`Config::ActivateReservationAmount`] to `None`. #[pallet::weight(T::WeightInfo::activate())] - // #[pallet::weight(0)] pub fn activate(origin: OriginFor) -> DispatchResult { let who = ensure_signed(origin)?; - Self::do_activate(Some(who), T::ActivateDuration::get()) + Self::do_activate(Some(who), T::SignedActivationDuration::get()) } /// Activate safe-mode by force for a per-origin configured number of blocks. @@ -224,106 +232,119 @@ pub mod pallet { /// ### Safety /// /// Can only be called by the [`Config::ForceActivateOrigin`] origin. - #[pallet::weight(0)] + #[pallet::weight(T::WeightInfo::force_activate())] pub fn force_activate(origin: OriginFor) -> DispatchResult { let duration = T::ForceActivateOrigin::ensure_origin(origin)?; Self::do_activate(None, duration) } - /// Extend the safe-mode permissionlessly for [`Config::ExtendDuration`] blocks. + /// Extend the safe-mode permissionlessly for [`Config::SignedExtendDuration`] blocks. /// - /// Reserves [`Config::ExtendStakeAmount`] from the caller's account. + /// Reserves [`Config::ExtendReservationAmount`] from the caller's account. + /// Emits an [`Event::Extended`] event on success. /// Errors with [`Error::IsInactive`] if the safe-mode is active. + /// Errors with [`Error::NotConfigured`] if the reservation amount is `None` . /// /// ### Safety /// - /// Can be permanently disabled by configuring [`Config::ActivateStakeAmount`] to `None`. - #[pallet::weight(0)] + /// This may be called by any signed origin with [`Config::ExtendReservationAmount`] free + /// currency to reserve. This call can be disabled for all origins by configuring + /// [`Config::ExtendReservationAmount`] to `None`. + #[pallet::weight(T::WeightInfo::extend())] pub fn extend(origin: OriginFor) -> DispatchResult { let who = ensure_signed(origin)?; - Self::do_extend(Some(who), T::ExtendDuration::get()) + Self::do_extend(Some(who), T::SignedExtendDuration::get()) } /// Extend the safe-mode by force a per-origin configured number of blocks. /// + /// Emits an [`Event::Extended`] event on success. /// Errors with [`Error::IsInactive`] if the safe-mode is inactive. /// /// ### Safety /// /// Can only be called by the [`Config::ForceExtendOrigin`] origin. - #[pallet::weight(0)] + #[pallet::weight(T::WeightInfo::force_extend())] pub fn force_extend(origin: OriginFor) -> DispatchResult { let duration = T::ForceExtendOrigin::ensure_origin(origin)?; Self::do_extend(None, duration) } - /// Inactivate safe-mode by force. + /// Deactivate safe-mode by force. + /// + /// Emits an [`Event::Exited`] with [`ExitReason::Force`] event on success. + /// Errors with [`Error::IsInactive`] if the safe-mode is inactive. + /// + /// Note: `safe-mode` will be automatically deactivated by [`Pallet::on_initialize`] hook + /// after the block height is greater than [`ActiveUntil`] found in storage. + /// Emits an [`Event::Exited`] with [`ExitReason::Timeout`] event on hook. /// - /// Note: safe-mode will be automatically inactivated by [`Pallet::on_initialize`] hook - /// after the block height is greater than [`ActiveUntil`] found in storage. Errors with - /// [`Error::IsInactive`] if the safe-mode is inactive. /// /// ### Safety /// - /// Can only be called by the [`Config::ForceInactivateOrigin`] origin. - #[pallet::weight(0)] - pub fn force_inactivate(origin: OriginFor) -> DispatchResult { - T::ForceInactivateOrigin::ensure_origin(origin.clone())?; + /// Can only be called by the [`Config::ForceDeactivateOrigin`] origin. + #[pallet::weight(T::WeightInfo::force_deactivate())] + pub fn force_deactivate(origin: OriginFor) -> DispatchResult { + T::ForceDeactivateOrigin::ensure_origin(origin)?; - Self::do_inactivate(ExitReason::Force) + Self::do_deactivate(ExitReason::Force) } - /// Repay an honest account that activated safe-mode earlier. + /// Release a currency reservation for an account that activated safe-mode at a specific + /// block earlier. This cannot be called while safe-mode is active. /// - /// Emits a [`Event::StakeRepaid`] event on success. - /// Errors if the safe-mode is already activated. + /// Emits a [`Event::ReservationRepaid`] event on success. + /// Errors with [`Error::IsActive`] if the safe-mode presently activated. + /// Errors with [`Error::NoReservation`] if the payee has no named reserved currency at the + /// block specified. /// /// ### Safety /// /// Can only be called by the [`Config::RepayOrigin`] origin. - #[pallet::weight(0)] - pub fn repay_stake( + #[pallet::weight(T::WeightInfo::release_reservation())] + pub fn release_reservation( origin: OriginFor, account: T::AccountId, block: T::BlockNumber, ) -> DispatchResult { - T::RepayOrigin::ensure_origin(origin.clone())?; + T::RepayOrigin::ensure_origin(origin)?; - Self::do_repay_stake(account, block) + Self::do_release_reservation(account, block) } - /// Slash a dishonest account that put the chain into safe-mode earlier. + /// Slash a reservation for an account that activated or extended safe-mode at a specific + /// block earlier. This cannot be called while safe-mode is active. /// - /// Errors if the safe-mode is already activated. - /// Emits a [`Event::StakeSlashed`] event on success. + /// Emits a [`Event::ReservationSlashed`] event on success. + /// Errors with [`Error::IsActive`] if the safe-mode presently activated. /// /// ### Safety /// /// Can only be called by the [`Config::RepayOrigin`] origin. - #[pallet::weight(0)] - pub fn slash_stake( + #[pallet::weight(T::WeightInfo::slash_reservation())] + pub fn slash_reservation( origin: OriginFor, account: T::AccountId, block: T::BlockNumber, ) -> DispatchResult { - T::RepayOrigin::ensure_origin(origin.clone())?; + T::RepayOrigin::ensure_origin(origin)?; - Self::do_slash_stake(account, block) + Self::do_slash_reservation(account, block) } } #[pallet::hooks] impl Hooks> for Pallet { - /// Automatically inactivates the safe-mode when the period ran out. + /// Automatically deactivates the safe-mode when the period runs out. /// /// Bypasses any call filters to avoid getting rejected by them. fn on_initialize(current: T::BlockNumber) -> Weight { match ActiveUntil::::get() { Some(limit) if current > limit => { - let _ = Self::do_inactivate(ExitReason::Timeout) + let _ = Self::do_deactivate(ExitReason::Timeout) .defensive_proof("Must be inactive; qed"); T::DbWeight::get().reads_writes(1, 1) }, @@ -337,73 +358,77 @@ impl Pallet { /// Logic for the [`crate::Pallet::activate`] and [`crate::Pallet::force_activate`] calls. fn do_activate(who: Option, duration: T::BlockNumber) -> DispatchResult { if let Some(who) = who { - let stake = T::ActivateStakeAmount::get().ok_or(Error::::NotConfigured)?; - Self::reserve(who, stake)?; + let reserve = T::ActivateReservationAmount::get().ok_or(Error::::NotConfigured)?; + Self::reserve(who, reserve)?; } ensure!(!ActiveUntil::::exists(), Error::::IsActive); let limit = >::block_number().saturating_add(duration); ActiveUntil::::put(limit); - Self::deposit_event(Event::Activated(limit)); + Self::deposit_event(Event::Activated { block: limit }); Ok(()) } /// Logic for the [`crate::Pallet::extend`] and [`crate::Pallet::force_extend`] calls. fn do_extend(who: Option, duration: T::BlockNumber) -> DispatchResult { if let Some(who) = who { - let stake = T::ExtendStakeAmount::get().ok_or(Error::::NotConfigured)?; - Self::reserve(who, stake)?; + let reserve = T::ExtendReservationAmount::get().ok_or(Error::::NotConfigured)?; + Self::reserve(who, reserve)?; } let limit = ActiveUntil::::take().ok_or(Error::::IsInactive)?.saturating_add(duration); ActiveUntil::::put(limit); - Self::deposit_event(Event::::Extended(limit)); + Self::deposit_event(Event::::Extended { block: limit }); Ok(()) } - /// Logic for the [`crate::Pallet::force_inactivate`] call. + /// Logic for the [`crate::Pallet::force_deactivate`] call. /// /// Errors if the safe-mode is already inactive. /// Does not check the origin. - fn do_inactivate(reason: ExitReason) -> DispatchResult { + fn do_deactivate(reason: ExitReason) -> DispatchResult { let _limit = ActiveUntil::::take().ok_or(Error::::IsInactive)?; - Self::deposit_event(Event::Exited(reason)); + Self::deposit_event(Event::Exited { reason }); Ok(()) } - /// Logic for the [`crate::Pallet::repay_stake`] call. + /// Logic for the [`crate::Pallet::release_reservation`] call. /// /// Errors if the safe-mode is active. /// Does not check the origin. - fn do_repay_stake(account: T::AccountId, block: T::BlockNumber) -> DispatchResult { + fn do_release_reservation(account: T::AccountId, block: T::BlockNumber) -> DispatchResult { ensure!(!Self::is_activated(), Error::::IsActive); - let stake = Stakes::::take(&account, block).ok_or(Error::::NotStaked)?; + let reserve = Reservations::::take(&account, block).ok_or(Error::::NoReservation)?; - T::Currency::unreserve(&account, stake); - Self::deposit_event(Event::::StakeRepaid(account, stake)); + T::Currency::unreserve_named(&block, &account, reserve); + Self::deposit_event(Event::::ReservationRepaid { block, account, amount: reserve }); Ok(()) } - /// Logic for the [`crate::Pallet::slash_stake`] call. + /// Logic for the [`crate::Pallet::slash_reservation`] call. /// /// Errors if the safe-mode is activated. /// Does not check the origin. - fn do_slash_stake(account: T::AccountId, block: T::BlockNumber) -> DispatchResult { + fn do_slash_reservation(account: T::AccountId, block: T::BlockNumber) -> DispatchResult { ensure!(!Self::is_activated(), Error::::IsActive); - let stake = Stakes::::take(&account, block).ok_or(Error::::NotStaked)?; + let reserve = Reservations::::take(&account, block).ok_or(Error::::NoReservation)?; - T::Currency::slash_reserved(&account, stake); - Self::deposit_event(Event::::StakeSlashed(account, stake)); + T::Currency::slash_reserved_named(&block, &account, reserve); + Self::deposit_event(Event::::ReservationSlashed { block, account, amount: reserve }); Ok(()) } - /// Reserve `stake` amount from `who` and store it in `Stakes`. - fn reserve(who: T::AccountId, stake: BalanceOf) -> DispatchResult { - T::Currency::reserve(&who, stake)?; + /// Reserve `reserve` amount from `who` and store it in `Reservations`. + fn reserve(who: T::AccountId, reserve: BalanceOf) -> DispatchResult { let block = >::block_number(); - let current_stake = Stakes::::get(&who, block).unwrap_or_default(); - Stakes::::insert(&who, block, current_stake.saturating_add(stake)); + T::Currency::reserve_named(&block, &who, reserve)?; + let current_reservation = Reservations::::get(&who, block).unwrap_or_default(); + // Reservation is mapped to the block that an extrinsic calls activate or extend, + // therefore we prevent abuse in a block by adding to present value in the same block. TODO: + // should we? Why not just fail? Calls in other blocks to activate or extend are stored in a + // new Reservations item. + Reservations::::insert(&who, block, current_reservation.saturating_add(reserve)); Ok(()) } @@ -424,7 +449,7 @@ impl Pallet { } if Self::is_activated() { - T::SafeModeFilter::contains(call) + T::UnfilterableCalls::contains(call) } else { true } diff --git a/frame/safe-mode/src/mock.rs b/frame/safe-mode/src/mock.rs index 35066c9f250aa..8d3c823e44fb1 100644 --- a/frame/safe-mode/src/mock.rs +++ b/frame/safe-mode/src/mock.rs @@ -22,7 +22,7 @@ use crate as pallet_safe_mode; use frame_support::{ parameter_types, - traits::{Everything, InsideBoth, SortedMembers}, + traits::{ConstU64, Everything, InsideBoth, InstanceFilter, SortedMembers}, }; use frame_system::{EnsureSignedBy, RawOrigin}; use sp_core::H256; @@ -63,7 +63,7 @@ impl frame_system::Config for Test { parameter_types! { pub const ExistentialDeposit: u64 = 1; - pub const MaxLocks: u32 = 10; + pub const MaxReserves: u32 = 10; } impl pallet_balances::Config for Test { type Balance = u64; @@ -72,18 +72,77 @@ impl pallet_balances::Config for Test { type ExistentialDeposit = ExistentialDeposit; type AccountStore = System; type WeightInfo = (); - type MaxLocks = MaxLocks; - type MaxReserves = (); - type ReserveIdentifier = [u8; 8]; + type MaxLocks = (); + type MaxReserves = MaxReserves; + type ReserveIdentifier = Self::BlockNumber; } -/// Filter to block balance pallet calls -pub struct MockSafeModeFilter; -impl Contains for MockSafeModeFilter { +impl pallet_utility::Config for Test { + type RuntimeEvent = RuntimeEvent; + type RuntimeCall = RuntimeCall; + type PalletsOrigin = OriginCaller; + type WeightInfo = (); +} + +#[derive( + Copy, + Clone, + Eq, + PartialEq, + Ord, + PartialOrd, + Encode, + Decode, + RuntimeDebug, + MaxEncodedLen, + scale_info::TypeInfo, +)] +pub enum ProxyType { + Any, + JustTransfer, + JustUtility, +} +impl Default for ProxyType { + fn default() -> Self { + Self::Any + } +} +impl InstanceFilter for ProxyType { + fn filter(&self, c: &RuntimeCall) -> bool { + match self { + ProxyType::Any => true, + ProxyType::JustTransfer => { + matches!(c, RuntimeCall::Balances(pallet_balances::Call::transfer { .. })) + }, + ProxyType::JustUtility => matches!(c, RuntimeCall::Utility { .. }), + } + } + fn is_superset(&self, o: &Self) -> bool { + self == &ProxyType::Any || self == o + } +} +impl pallet_proxy::Config for Test { + type RuntimeEvent = RuntimeEvent; + type RuntimeCall = RuntimeCall; + type Currency = Balances; + type ProxyType = ProxyType; + type ProxyDepositBase = ConstU64<1>; + type ProxyDepositFactor = ConstU64<1>; + type MaxProxies = ConstU32<4>; + type WeightInfo = (); + type CallHasher = BlakeTwo256; + type MaxPending = ConstU32<2>; + type AnnouncementDepositBase = ConstU64<1>; + type AnnouncementDepositFactor = ConstU64<1>; +} + +/// Filter to allow all everything except balance calls +pub struct UnfilterableCalls; +impl Contains for UnfilterableCalls { fn contains(call: &RuntimeCall) -> bool { match call { - RuntimeCall::System(_) | RuntimeCall::SafeMode(_) => true, RuntimeCall::Balances(_) => false, + _ => true, } } } @@ -152,7 +211,7 @@ impl ForceExtendOrigin { } } -impl, O>> + From> + std::fmt::Debug> EnsureOrigin +impl, O>> + From>> EnsureOrigin for ForceActivateOrigin { type Success = u64; @@ -168,9 +227,14 @@ impl, O>> + From> + std::fmt::Debug r => Err(O::from(r)), }) } + + #[cfg(feature = "runtime-benchmarks")] + fn successful_origin() -> O { + O::from(RawOrigin::Signed(ForceActivateOrigin::Strong.acc())) + } } -impl, O>> + From> + std::fmt::Debug> EnsureOrigin +impl, O>> + From>> EnsureOrigin for ForceExtendOrigin { type Success = u64; @@ -186,19 +250,24 @@ impl, O>> + From> + std::fmt::Debug r => Err(O::from(r)), }) } + + #[cfg(feature = "runtime-benchmarks")] + fn successful_origin() -> O { + O::from(RawOrigin::Signed(ForceExtendOrigin::Strong.acc())) + } } parameter_types! { - pub const ActivateDuration: u64 = 3; - pub const ExtendDuration: u64 = 30; - pub const ActivateStakeAmount: u64 = 100; - pub const ExtendStakeAmount: u64 = 100; - pub const DeactivateOrigin: u64 =3; + pub const SignedActivationDuration: u64 = 3; + pub const SignedExtendDuration: u64 = 30; + pub const ActivateReservationAmount: u64 = 100; + pub const ExtendReservationAmount: u64 = 100; + pub const ForceDeactivateOrigin: u64 = 3; pub const RepayOrigin: u64 = 4; } // Required impl to use some ::get() in tests -impl SortedMembers for DeactivateOrigin { +impl SortedMembers for ForceDeactivateOrigin { fn sorted_members() -> Vec { vec![Self::get()] } @@ -216,14 +285,14 @@ impl SortedMembers for RepayOrigin { impl Config for Test { type RuntimeEvent = RuntimeEvent; type Currency = Balances; - type SafeModeFilter = MockSafeModeFilter; - type ActivateDuration = ActivateDuration; - type ExtendDuration = ExtendDuration; - type ActivateStakeAmount = ActivateStakeAmount; + type UnfilterableCalls = UnfilterableCalls; + type SignedActivationDuration = SignedActivationDuration; + type ActivateReservationAmount = ActivateReservationAmount; + type SignedExtendDuration = SignedExtendDuration; + type ExtendReservationAmount = ExtendReservationAmount; type ForceActivateOrigin = ForceActivateOrigin; type ForceExtendOrigin = ForceExtendOrigin; - type ExtendStakeAmount = ExtendStakeAmount; - type ForceInactivateOrigin = EnsureSignedBy; + type ForceDeactivateOrigin = EnsureSignedBy; type RepayOrigin = EnsureSignedBy; type WeightInfo = (); } @@ -239,6 +308,8 @@ frame_support::construct_runtime!( { System: frame_system, Balances: pallet_balances, + Utility: pallet_utility, + Proxy: pallet_proxy, SafeMode: pallet_safe_mode, } ); @@ -266,11 +337,6 @@ pub fn new_test_ext() -> sp_io::TestExternalities { ext } -#[cfg(feature = "runtime-benchmarks")] -pub fn new_bench_ext() -> sp_io::TestExternalities { - GenesisConfig::default().build_storage().unwrap().into() -} - pub fn next_block() { SafeMode::on_finalize(System::block_number()); Balances::on_finalize(System::block_number()); diff --git a/frame/safe-mode/src/tests.rs b/frame/safe-mode/src/tests.rs index 2e75f40e0f2c3..54771712a994c 100644 --- a/frame/safe-mode/src/tests.rs +++ b/frame/safe-mode/src/tests.rs @@ -29,10 +29,9 @@ fn fails_to_filter_calls_to_safe_mode_pallet() { new_test_ext().execute_with(|| { assert_ok!(SafeMode::activate(Origin::signed(0))); let activated_at_block = System::block_number(); - let call = RuntimeCall::Balances(pallet_balances::Call::transfer { dest: 1, value: 1 }); assert_err!( - call.clone().dispatch(Origin::signed(0)), + call_transfer().dispatch(Origin::signed(0)), frame_system::Error::::CallFiltered ); // TODO ^^^ consider refactor to throw a safe mode error, not generic `CallFiltered` @@ -41,11 +40,11 @@ fn fails_to_filter_calls_to_safe_mode_pallet() { assert_ok!(SafeMode::extend(Origin::signed(0))); assert_ok!(SafeMode::force_extend(ForceExtendOrigin::Weak.signed())); assert_err!( - call.clone().dispatch(Origin::signed(0)), + call_transfer().dispatch(Origin::signed(0)), frame_system::Error::::CallFiltered ); - assert_ok!(SafeMode::force_inactivate(Origin::signed(mock::DeactivateOrigin::get()))); - assert_ok!(SafeMode::repay_stake( + assert_ok!(SafeMode::force_deactivate(Origin::signed(mock::ForceDeactivateOrigin::get()))); + assert_ok!(SafeMode::release_reservation( Origin::signed(mock::RepayOrigin::get()), 0, activated_at_block @@ -54,11 +53,11 @@ fn fails_to_filter_calls_to_safe_mode_pallet() { next_block(); assert_ok!(SafeMode::activate(Origin::signed(0))); assert_err!( - call.clone().dispatch(Origin::signed(0)), + call_transfer().dispatch(Origin::signed(0)), frame_system::Error::::CallFiltered ); - assert_ok!(SafeMode::force_inactivate(Origin::signed(mock::DeactivateOrigin::get()))); - assert_ok!(SafeMode::slash_stake( + assert_ok!(SafeMode::force_deactivate(Origin::signed(mock::ForceDeactivateOrigin::get()))); + assert_ok!(SafeMode::slash_reservation( Origin::signed(mock::RepayOrigin::get()), 0, activated_at_block + 2 @@ -66,6 +65,14 @@ fn fails_to_filter_calls_to_safe_mode_pallet() { }); } +#[test] +fn fails_to_activate_if_activated() { + new_test_ext().execute_with(|| { + assert_ok!(SafeMode::activate(Origin::signed(0))); + assert_noop!(SafeMode::activate(Origin::signed(2)), Error::::IsActive); + }); +} + #[test] fn fails_to_extend_if_not_activated() { new_test_ext().execute_with(|| { @@ -74,15 +81,42 @@ fn fails_to_extend_if_not_activated() { }); } +#[test] +fn fails_to_release_reservations_with_wrong_block() { + new_test_ext().execute_with(|| { + let activated_at_block = System::block_number(); + assert_ok!(SafeMode::activate(Origin::signed(0))); + run_to(mock::SignedActivationDuration::get() + activated_at_block + 1); + + assert_err!( + SafeMode::release_reservation( + Origin::signed(mock::RepayOrigin::get()), + 0, + activated_at_block + 1 + ), + Error::::NoReservation + ); + + assert_err!( + SafeMode::slash_reservation( + Origin::signed(mock::RepayOrigin::get()), + 0, + activated_at_block + 1 + ), + Error::::NoReservation + ); + }); +} + // GENERAL SUCCESS/POSITIVE TESTS --------------------- #[test] -fn can_automatically_inactivate_after_timeout() { +fn can_automatically_deactivate_after_timeout() { new_test_ext().execute_with(|| { let activated_at_block = System::block_number(); assert_ok!(SafeMode::force_activate(ForceActivateOrigin::Weak.signed())); run_to(ForceActivateOrigin::Weak.duration() + activated_at_block + 1); - SafeMode::on_initialize(System::block_number()); + assert_eq!(SafeMode::active_until(), None); }); } @@ -90,18 +124,84 @@ fn can_automatically_inactivate_after_timeout() { #[test] fn can_filter_balance_calls_when_activated() { new_test_ext().execute_with(|| { - let call = RuntimeCall::Balances(pallet_balances::Call::transfer { dest: 1, value: 1 }); - - assert_ok!(call.clone().dispatch(Origin::signed(0))); + assert_ok!(call_transfer().dispatch(Origin::signed(0))); assert_ok!(SafeMode::activate(Origin::signed(0))); assert_err!( - call.clone().dispatch(Origin::signed(0)), + call_transfer().dispatch(Origin::signed(0)), frame_system::Error::::CallFiltered ); // TODO ^^^ consider refactor to throw a safe mode error, not generic `CallFiltered` }); } +#[test] +fn can_filter_balance_in_batch_when_activated() { + new_test_ext().execute_with(|| { + let batch_call = + RuntimeCall::Utility(pallet_utility::Call::batch { calls: vec![call_transfer()] }); + + assert_ok!(batch_call.clone().dispatch(Origin::signed(0))); + + assert_ok!(SafeMode::activate(Origin::signed(0))); + + assert_ok!(batch_call.clone().dispatch(Origin::signed(0))); + System::assert_last_event( + pallet_utility::Event::BatchInterrupted { + index: 0, + error: frame_system::Error::::CallFiltered.into(), + } + .into(), + ); + }); +} + +#[test] +fn can_filter_balance_in_proxy_when_activated() { + new_test_ext().execute_with(|| { + assert_ok!(Proxy::add_proxy(Origin::signed(1), 2, ProxyType::JustTransfer, 0)); + + assert_ok!(Proxy::proxy(Origin::signed(2), 1, None, Box::new(call_transfer()))); + System::assert_last_event(pallet_proxy::Event::ProxyExecuted { result: Ok(()) }.into()); + + assert_ok!(SafeMode::force_activate(ForceActivateOrigin::Weak.signed())); + + assert_ok!(Proxy::proxy(Origin::signed(2), 1, None, Box::new(call_transfer()))); + System::assert_last_event( + pallet_proxy::Event::ProxyExecuted { + result: DispatchError::from(frame_system::Error::::CallFiltered).into(), + } + .into(), + ); + }); +} + +#[test] +fn can_repay_independent_reservations_by_block() { + new_test_ext().execute_with(|| { + let activated_at_block_0 = System::block_number(); + assert_ok!(SafeMode::activate(Origin::signed(0))); + run_to(mock::SignedActivationDuration::get() + activated_at_block_0 + 1); + + let activated_at_block_1 = System::block_number(); + assert_ok!(SafeMode::activate(Origin::signed(0))); + run_to(mock::SignedActivationDuration::get() + activated_at_block_1 + 1); + + assert_ok!(SafeMode::release_reservation( + Origin::signed(mock::RepayOrigin::get()), + 0, + activated_at_block_0 + )); + assert_eq!(Balances::free_balance(&0), 1234 - mock::ActivateReservationAmount::get()); // accounts set in mock genesis + + assert_ok!(SafeMode::slash_reservation( + Origin::signed(mock::RepayOrigin::get()), + 0, + activated_at_block_1 + )); + assert_eq!(Balances::total_balance(&0), 1234 - mock::ActivateReservationAmount::get()); // accounts set in mock genesis + }); +} + // SIGNED ORIGIN CALL TESTS --------------------- #[test] @@ -110,12 +210,12 @@ fn can_activate_with_signed_origin() { assert_ok!(SafeMode::activate(Origin::signed(0))); assert_eq!( SafeMode::active_until().unwrap(), - System::block_number() + mock::ActivateDuration::get() + System::block_number() + mock::SignedActivationDuration::get() ); - assert_eq!(Balances::reserved_balance(0), mock::ActivateStakeAmount::get()); + assert_eq!(Balances::reserved_balance(0), mock::ActivateReservationAmount::get()); assert_noop!(SafeMode::activate(Origin::signed(0)), Error::::IsActive); // Assert the stake. - assert_eq!(Stakes::::get(0, 1), Some(mock::ActivateStakeAmount::get())); + assert_eq!(Reservations::::get(0, 1), Some(mock::ActivateReservationAmount::get())); }); } @@ -126,11 +226,13 @@ fn can_extend_with_signed_origin() { assert_ok!(SafeMode::extend(Origin::signed(0))); assert_eq!( SafeMode::active_until().unwrap(), - System::block_number() + mock::ActivateDuration::get() + mock::ExtendDuration::get() + System::block_number() + + mock::SignedActivationDuration::get() + + mock::SignedExtendDuration::get() ); assert_eq!( Balances::reserved_balance(0), - mock::ActivateStakeAmount::get() + mock::ExtendStakeAmount::get() + mock::ActivateReservationAmount::get() + mock::ExtendReservationAmount::get() ); }); } @@ -143,13 +245,13 @@ fn fails_signed_origin_when_explicit_origin_required() { assert_err!(SafeMode::force_activate(Origin::signed(0)), DispatchError::BadOrigin); assert_err!(SafeMode::force_extend(Origin::signed(0)), DispatchError::BadOrigin); - assert_err!(SafeMode::force_inactivate(Origin::signed(0)), DispatchError::BadOrigin); + assert_err!(SafeMode::force_deactivate(Origin::signed(0)), DispatchError::BadOrigin); assert_err!( - SafeMode::slash_stake(Origin::signed(0), 0, activated_at_block), + SafeMode::slash_reservation(Origin::signed(0), 0, activated_at_block), DispatchError::BadOrigin ); assert_err!( - SafeMode::repay_stake(Origin::signed(0), 0, activated_at_block), + SafeMode::release_reservation(Origin::signed(0), 0, activated_at_block), DispatchError::BadOrigin ); }); @@ -158,14 +260,14 @@ fn fails_signed_origin_when_explicit_origin_required() { // CONFIGURED ORIGIN CALL TESTS --------------------- #[test] -fn fails_force_inactivate_if_not_activated() { +fn fails_force_deactivate_if_not_activated() { new_test_ext().execute_with(|| { assert_noop!( - SafeMode::force_inactivate(Origin::signed(mock::DeactivateOrigin::get())), + SafeMode::force_deactivate(Origin::signed(mock::ForceDeactivateOrigin::get())), Error::::IsInactive ); assert_noop!( - SafeMode::force_inactivate(Origin::signed(mock::DeactivateOrigin::get())), + SafeMode::force_deactivate(Origin::signed(mock::ForceDeactivateOrigin::get())), Error::::IsInactive ); }); @@ -188,16 +290,16 @@ fn can_force_activate_with_config_origin() { } #[test] -fn can_force_inactivate_with_config_origin() { +fn can_force_deactivate_with_config_origin() { new_test_ext().execute_with(|| { assert_eq!(SafeMode::active_until(), None); assert_err!( - SafeMode::force_inactivate(Origin::signed(mock::DeactivateOrigin::get())), + SafeMode::force_deactivate(Origin::signed(mock::ForceDeactivateOrigin::get())), Error::::IsInactive ); assert_ok!(SafeMode::force_activate(ForceActivateOrigin::Weak.signed())); assert_eq!(Balances::reserved_balance(ForceActivateOrigin::Weak.acc()), 0); - assert_ok!(SafeMode::force_inactivate(Origin::signed(mock::DeactivateOrigin::get()))); + assert_ok!(SafeMode::force_deactivate(Origin::signed(mock::ForceDeactivateOrigin::get()))); }); } @@ -218,47 +320,94 @@ fn can_force_extend_with_config_origin() { ForceExtendOrigin::Medium.duration() ); assert_eq!(Balances::reserved_balance(ForceActivateOrigin::Weak.acc()), 0); - assert_eq!(Balances::reserved_balance(mock::ExtendDuration::get()), 0); + assert_eq!(Balances::reserved_balance(mock::SignedExtendDuration::get()), 0); }); } #[test] -fn can_repay_stake_with_config_origin() { +fn can_release_reservation_with_config_origin() { new_test_ext().execute_with(|| { let activated_at_block = System::block_number(); assert_ok!(SafeMode::activate(Origin::signed(0))); assert_err!( - SafeMode::repay_stake(Origin::signed(mock::RepayOrigin::get()), 0, activated_at_block), + SafeMode::release_reservation( + Origin::signed(mock::RepayOrigin::get()), + 0, + activated_at_block + ), Error::::IsActive ); - run_to(mock::ActivateDuration::get() + activated_at_block + 1); - SafeMode::on_initialize(System::block_number()); - assert_ok!(SafeMode::repay_stake( + run_to(mock::SignedActivationDuration::get() + activated_at_block + 1); + + assert_ok!(SafeMode::release_reservation( Origin::signed(mock::RepayOrigin::get()), 0, activated_at_block )); - // TODO: test accounting is correct + assert_eq!(Balances::free_balance(&0), 1234); // accounts set in mock genesis + + Balances::make_free_balance_be(&0, 1234); + let activated_and_extended_at_block = System::block_number(); + assert_ok!(SafeMode::activate(Origin::signed(0))); + assert_ok!(SafeMode::extend(Origin::signed(0))); + run_to( + mock::SignedActivationDuration::get() + + mock::SignedExtendDuration::get() + + activated_and_extended_at_block + + 1, + ); + + assert_ok!(SafeMode::release_reservation( + Origin::signed(mock::RepayOrigin::get()), + 0, + activated_and_extended_at_block + )); + assert_eq!(Balances::free_balance(&0), 1234); // accounts set in mock genesis }); } #[test] -fn can_slash_stake_with_config_origin() { +fn can_slash_reservation_with_config_origin() { new_test_ext().execute_with(|| { let activated_at_block = System::block_number(); assert_ok!(SafeMode::activate(Origin::signed(0))); assert_err!( - SafeMode::slash_stake(Origin::signed(mock::RepayOrigin::get()), 0, activated_at_block), + SafeMode::slash_reservation( + Origin::signed(mock::RepayOrigin::get()), + 0, + activated_at_block + ), Error::::IsActive ); - run_to(mock::ActivateDuration::get() + activated_at_block + 1); - SafeMode::on_initialize(System::block_number()); - assert_ok!(SafeMode::slash_stake( + run_to(mock::SignedActivationDuration::get() + activated_at_block + 1); + + assert_ok!(SafeMode::slash_reservation( Origin::signed(mock::RepayOrigin::get()), 0, activated_at_block )); - // TODO: test accounting is correct + assert_eq!(Balances::free_balance(&0), 1234 - mock::ActivateReservationAmount::get()); // accounts set in mock genesis + + Balances::make_free_balance_be(&0, 1234); + let activated_and_extended_at_block = System::block_number(); + assert_ok!(SafeMode::activate(Origin::signed(0))); + assert_ok!(SafeMode::extend(Origin::signed(0))); + run_to( + mock::SignedActivationDuration::get() + + mock::SignedExtendDuration::get() + + activated_and_extended_at_block + + 1, + ); + + assert_ok!(SafeMode::slash_reservation( + Origin::signed(mock::RepayOrigin::get()), + 0, + activated_and_extended_at_block + )); + assert_eq!( + Balances::free_balance(&0), + 1234 - mock::ActivateReservationAmount::get() - mock::ExtendReservationAmount::get() + ); // accounts set in mock genesis }); } @@ -273,15 +422,19 @@ fn fails_when_explicit_origin_required() { DispatchError::BadOrigin ); assert_err!( - SafeMode::force_inactivate(ForceActivateOrigin::Weak.signed()), + SafeMode::force_deactivate(ForceActivateOrigin::Weak.signed()), DispatchError::BadOrigin ); assert_err!( - SafeMode::slash_stake(ForceActivateOrigin::Weak.signed(), 0, activated_at_block), + SafeMode::slash_reservation(ForceActivateOrigin::Weak.signed(), 0, activated_at_block), DispatchError::BadOrigin ); assert_err!( - SafeMode::repay_stake(ForceActivateOrigin::Weak.signed(), 0, activated_at_block), + SafeMode::release_reservation( + ForceActivateOrigin::Weak.signed(), + 0, + activated_at_block + ), DispatchError::BadOrigin ); @@ -290,37 +443,37 @@ fn fails_when_explicit_origin_required() { DispatchError::BadOrigin ); assert_err!( - SafeMode::force_inactivate(ForceExtendOrigin::Weak.signed()), + SafeMode::force_deactivate(ForceExtendOrigin::Weak.signed()), DispatchError::BadOrigin ); assert_err!( - SafeMode::slash_stake(ForceExtendOrigin::Weak.signed(), 0, activated_at_block), + SafeMode::slash_reservation(ForceExtendOrigin::Weak.signed(), 0, activated_at_block), DispatchError::BadOrigin ); assert_err!( - SafeMode::repay_stake(ForceExtendOrigin::Weak.signed(), 0, activated_at_block), + SafeMode::release_reservation(ForceExtendOrigin::Weak.signed(), 0, activated_at_block), DispatchError::BadOrigin ); assert_err!( - SafeMode::force_activate(Origin::signed(mock::DeactivateOrigin::get())), + SafeMode::force_activate(Origin::signed(mock::ForceDeactivateOrigin::get())), DispatchError::BadOrigin ); assert_err!( - SafeMode::force_extend(Origin::signed(mock::DeactivateOrigin::get())), + SafeMode::force_extend(Origin::signed(mock::ForceDeactivateOrigin::get())), DispatchError::BadOrigin ); assert_err!( - SafeMode::slash_stake( - Origin::signed(mock::DeactivateOrigin::get()), + SafeMode::slash_reservation( + Origin::signed(mock::ForceDeactivateOrigin::get()), 0, activated_at_block ), DispatchError::BadOrigin ); assert_err!( - SafeMode::repay_stake( - Origin::signed(mock::DeactivateOrigin::get()), + SafeMode::release_reservation( + Origin::signed(mock::ForceDeactivateOrigin::get()), 0, activated_at_block ), @@ -336,8 +489,12 @@ fn fails_when_explicit_origin_required() { DispatchError::BadOrigin ); assert_err!( - SafeMode::force_inactivate(Origin::signed(mock::RepayOrigin::get())), + SafeMode::force_deactivate(Origin::signed(mock::RepayOrigin::get())), DispatchError::BadOrigin ); }); } + +fn call_transfer() -> RuntimeCall { + RuntimeCall::Balances(pallet_balances::Call::transfer { dest: 1, value: 1 }) +} diff --git a/frame/safe-mode/src/weights.rs b/frame/safe-mode/src/weights.rs index dc1b64d905c6f..d24f31f944d3c 100644 --- a/frame/safe-mode/src/weights.rs +++ b/frame/safe-mode/src/weights.rs @@ -56,27 +56,129 @@ use sp_std::marker::PhantomData; /// Weight functions needed for pallet_safe_mode. pub trait WeightInfo { fn activate() -> Weight; + fn force_activate() -> Weight; + fn extend() -> Weight; + fn force_extend() -> Weight; + fn force_deactivate() -> Weight; + fn release_reservation() -> Weight; + fn slash_reservation() -> Weight; } /// Weights for pallet_safe_mode using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { - // Storage: SafeMode Stakes (r:1 w:1) + // Storage: SafeMode Reservations (r:1 w:1) // Storage: SafeMode Enabled (r:1 w:1) fn activate() -> Weight { Weight::from_ref_time(51_688_000 as u64) .saturating_add(T::DbWeight::get().reads(2 as u64)) .saturating_add(T::DbWeight::get().writes(2 as u64)) } + + // Storage: SafeMode Reservations (r:1 w:1) + // Storage: SafeMode Enabled (r:1 w:1) + fn force_activate() -> Weight { + Weight::from_ref_time(51_688_000 as u64) + .saturating_add(T::DbWeight::get().reads(2 as u64)) + .saturating_add(T::DbWeight::get().writes(2 as u64)) + } + + // Storage: SafeMode Reservations (r:1 w:1) + // Storage: SafeMode Enabled (r:1 w:1) + fn extend() -> Weight { + Weight::from_ref_time(51_688_000 as u64) + .saturating_add(T::DbWeight::get().reads(2 as u64)) + .saturating_add(T::DbWeight::get().writes(2 as u64)) + } + + // Storage: SafeMode Reservations (r:1 w:1) + // Storage: SafeMode Enabled (r:1 w:1) + fn force_extend() -> Weight { + Weight::from_ref_time(51_688_000 as u64) + .saturating_add(T::DbWeight::get().reads(2 as u64)) + .saturating_add(T::DbWeight::get().writes(2 as u64)) + } + + // Storage: SafeMode Reservations (r:1 w:1) + // Storage: SafeMode Enabled (r:1 w:1) + fn force_deactivate() -> Weight { + Weight::from_ref_time(51_688_000 as u64) + .saturating_add(T::DbWeight::get().reads(2 as u64)) + .saturating_add(T::DbWeight::get().writes(2 as u64)) + } + + // Storage: SafeMode Reservations (r:1 w:1) + // Storage: SafeMode Enabled (r:1 w:1) + fn release_reservation() -> Weight { + Weight::from_ref_time(51_688_000 as u64) + .saturating_add(T::DbWeight::get().reads(2 as u64)) + .saturating_add(T::DbWeight::get().writes(2 as u64)) + } + + // Storage: SafeMode Reservations (r:1 w:1) + // Storage: SafeMode Enabled (r:1 w:1) + fn slash_reservation() -> Weight { + Weight::from_ref_time(51_688_000 as u64) + .saturating_add(T::DbWeight::get().reads(2 as u64)) + .saturating_add(T::DbWeight::get().writes(2 as u64)) + } } // For backwards compatibility and tests impl WeightInfo for () { - // Storage: SafeMode Stakes (r:1 w:1) + // Storage: SafeMode Reservations (r:1 w:1) // Storage: SafeMode Enabled (r:1 w:1) fn activate() -> Weight { Weight::from_ref_time(51_688_000 as u64) .saturating_add(RocksDbWeight::get().reads(2 as u64)) .saturating_add(RocksDbWeight::get().writes(2 as u64)) } + + // Storage: SafeMode Reservations (r:1 w:1) + // Storage: SafeMode Enabled (r:1 w:1) + fn force_activate() -> Weight { + Weight::from_ref_time(51_688_000 as u64) + .saturating_add(RocksDbWeight::get().reads(2 as u64)) + .saturating_add(RocksDbWeight::get().writes(2 as u64)) + } + + // Storage: SafeMode Reservations (r:1 w:1) + // Storage: SafeMode Enabled (r:1 w:1) + fn extend() -> Weight { + Weight::from_ref_time(51_688_000 as u64) + .saturating_add(RocksDbWeight::get().reads(2 as u64)) + .saturating_add(RocksDbWeight::get().writes(2 as u64)) + } + + // Storage: SafeMode Reservations (r:1 w:1) + // Storage: SafeMode Enabled (r:1 w:1) + fn force_extend() -> Weight { + Weight::from_ref_time(51_688_000 as u64) + .saturating_add(RocksDbWeight::get().reads(2 as u64)) + .saturating_add(RocksDbWeight::get().writes(2 as u64)) + } + + // Storage: SafeMode Reservations (r:1 w:1) + // Storage: SafeMode Enabled (r:1 w:1) + fn force_deactivate() -> Weight { + Weight::from_ref_time(51_688_000 as u64) + .saturating_add(RocksDbWeight::get().reads(2 as u64)) + .saturating_add(RocksDbWeight::get().writes(2 as u64)) + } + + // Storage: SafeMode Reservations (r:1 w:1) + // Storage: SafeMode Enabled (r:1 w:1) + fn release_reservation() -> Weight { + Weight::from_ref_time(51_688_000 as u64) + .saturating_add(RocksDbWeight::get().reads(2 as u64)) + .saturating_add(RocksDbWeight::get().writes(2 as u64)) + } + + // Storage: SafeMode Reservations (r:1 w:1) + // Storage: SafeMode Enabled (r:1 w:1) + fn slash_reservation() -> Weight { + Weight::from_ref_time(51_688_000 as u64) + .saturating_add(RocksDbWeight::get().reads(2 as u64)) + .saturating_add(RocksDbWeight::get().writes(2 as u64)) + } } diff --git a/frame/tx-pause/Cargo.toml b/frame/tx-pause/Cargo.toml index da32e18ef387c..0fe99c20353b4 100644 --- a/frame/tx-pause/Cargo.toml +++ b/frame/tx-pause/Cargo.toml @@ -20,13 +20,17 @@ frame-system = { version = "4.0.0-dev", default-features = false, path = "../sys scale-info = { version = "2.0.1", default-features = false, features = ["derive"] } sp-runtime = { version = "6.0.0", default-features = false, path = "../../primitives/runtime" } sp-std = { version = "4.0.0", default-features = false, path = "../../primitives/std" } - +pallet-balances = { version = "4.0.0-dev", path = "../balances", default-features = false, optional = true } +pallet-utility = { version = "4.0.0-dev", path = "../utility", default-features = false, optional = true } +pallet-proxy = { version = "4.0.0-dev", path = "../proxy", default-features = false, optional = true } [dev-dependencies] sp-core = { version = "6.0.0", path = "../../primitives/core" } sp-std = { version = "4.0.0", path = "../../primitives/std" } sp-io = { version = "6.0.0", path = "../../primitives/io" } pallet-balances = { version = "4.0.0-dev", path = "../balances" } +pallet-utility = { version = "4.0.0-dev", path = "../utility" } +pallet-proxy = { version = "4.0.0-dev", path = "../proxy" } [features] @@ -37,6 +41,9 @@ std = [ "frame-benchmarking/std", "frame-support/std", "frame-system/std", + "pallet-balances?/std", + "pallet-utility?/std", + "pallet-proxy?/std", "sp-runtime/std", "sp-std/std", ] @@ -44,6 +51,9 @@ runtime-benchmarks = [ "frame-benchmarking/runtime-benchmarks", "frame-support/runtime-benchmarks", "frame-system/runtime-benchmarks", + "pallet-balances/runtime-benchmarks", + "pallet-utility/runtime-benchmarks", + "pallet-proxy/runtime-benchmarks", ] try-runtime = [ "frame-support/try-runtime", diff --git a/frame/tx-pause/src/benchmarking.rs b/frame/tx-pause/src/benchmarking.rs index d605e8fef826a..6ee76448130d6 100644 --- a/frame/tx-pause/src/benchmarking.rs +++ b/frame/tx-pause/src/benchmarking.rs @@ -20,39 +20,38 @@ use super::{Pallet as TxPause, *}; use frame_benchmarking::benchmarks; -use frame_support::traits::UnfilteredDispatchable; benchmarks! { - pause_call { - let pallet: PalletNameOf = b"SomePalletName".to_vec().try_into().unwrap(); - let function: FunctionNameOf = b"some_fn_name".to_vec().try_into().unwrap(); - let origin = T::PauseOrigin::successful_origin(); - let call = Call::::pause_call { pallet: pallet.clone(), function: function.clone() }; + pause_call { + let pallet_name: PalletNameOf = b"SomePalletName".to_vec().try_into().unwrap(); + let call_name: CallNameOf = b"some_call_name".to_vec().try_into().unwrap(); + let origin = T::PauseOrigin::successful_origin(); + let call = Call::::pause_call { pallet_name: pallet_name.clone(), call_name: call_name.clone() }; - }: { call.dispatch_bypass_filter(origin)?} - verify { - assert![TxPause::::paused_calls((pallet.clone(),function.clone())).is_some()] - } + }: _(origin, pallet_name.clone(), call_name.clone()) + verify { + assert!(TxPause::::paused_calls((pallet_name.clone(), call_name.clone())).is_some()) + } unpause_call { - let pallet: PalletNameOf = b"SomePalletName".to_vec().try_into().unwrap(); - let function: FunctionNameOf = b"some_fn_name".to_vec().try_into().unwrap(); - let pause_origin = T::PauseOrigin::successful_origin(); - - // Set - TxPause::::pause_call( - pause_origin, - pallet.clone(), - function.clone(), - )?; - - let unpause_origin = T::UnpauseOrigin::successful_origin(); - let call = Call::::unpause_call { pallet: pallet.clone(), function: function.clone() }; - - }: { call.dispatch_bypass_filter(unpause_origin)?} - verify { - assert![TxPause::::paused_calls((pallet.clone(),function.clone())).is_none()] - } - - impl_benchmark_test_suite!(TxPause, crate::mock::new_test_ext(), crate::mock::Test); + let pallet_name: PalletNameOf = b"SomePalletName".to_vec().try_into().unwrap(); + let call_name: CallNameOf = b"some_call_name".to_vec().try_into().unwrap(); + let pause_origin = T::PauseOrigin::successful_origin(); + + // Set + TxPause::::pause_call( + pause_origin, + pallet_name.clone(), + call_name.clone(), + )?; + + let unpause_origin = T::UnpauseOrigin::successful_origin(); + let call = Call::::unpause_call { pallet_name: pallet_name.clone(), call_name: call_name.clone() }; + + }: _(unpause_origin, pallet_name.clone(), call_name.clone()) + verify { + assert!(TxPause::::paused_calls((pallet_name.clone(), call_name.clone())).is_none()) + } + + impl_benchmark_test_suite!(TxPause, crate::mock::new_test_ext(), crate::mock::Test); } diff --git a/frame/tx-pause/src/lib.rs b/frame/tx-pause/src/lib.rs index 86e1a1426c7db..0808d2ab5240a 100644 --- a/frame/tx-pause/src/lib.rs +++ b/frame/tx-pause/src/lib.rs @@ -17,7 +17,6 @@ #![cfg_attr(not(feature = "std"), no_std)] -#[cfg(feature = "runtime-benchmarks")] mod benchmarking; #[cfg(test)] pub mod mock; @@ -26,10 +25,12 @@ mod tests; pub mod weights; use frame_support::{ + dispatch::GetDispatchInfo, pallet_prelude::*, - traits::{CallMetadata, Contains, GetCallMetadata}, + traits::{CallMetadata, Contains, GetCallMetadata, IsSubType, IsType}, }; use frame_system::pallet_prelude::*; +use sp_runtime::{traits::Dispatchable, DispatchResult}; use sp_std::{convert::TryInto, prelude::*}; pub use pallet::*; @@ -37,6 +38,7 @@ pub use weights::*; pub type PalletNameOf = BoundedVec::MaxNameLen>; pub type CallNameOf = BoundedVec::MaxNameLen>; +pub type FullNameOf = (PalletNameOf, Option>); #[frame_support::pallet] pub mod pallet { @@ -51,18 +53,28 @@ pub mod pallet { /// The overarching event type. type RuntimeEvent: From> + IsType<::RuntimeEvent>; + /// The overarching call type. + type RuntimeCall: Parameter + + Dispatchable + + GetDispatchInfo + + GetCallMetadata + + From> + + IsSubType> + + IsType<::RuntimeCall>; + /// The only origin that can pause calls. type PauseOrigin: EnsureOrigin; /// The only origin that can un-pause calls. type UnpauseOrigin: EnsureOrigin; - /// Pallets that are safe and can never be paused. + /// Contains all calls that cannot be paused. /// - /// The tx-pause pallet is always assumed to be safe itself. - type UnpausablePallets: Contains>; + /// The `TxMode` pallet cannot pause it's own calls, and does not need to be explicitly + /// added here. + type UnfilterableCallNames: Contains>; - /// Maximum length for pallet- and function-names. + /// Maximum length for pallet and call SCALE encoded string names. /// /// Too long names will not be truncated but handled like /// [`Self::PauseTooLongNames`] specifies. @@ -92,15 +104,18 @@ pub mod pallet { /// The call is listed as safe and cannot be paused. IsUnpausable, + + // The call does not exist in the runtime. + NoSuchCall, } #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event { - /// This call got paused. - CallPaused(PalletNameOf, CallNameOf), - /// This call got un-paused. - CallUnpaused(PalletNameOf, CallNameOf), + /// This call is now paused. \[pallet_name, call_name\] + CallPaused { pallet_name: PalletNameOf, call_name: CallNameOf }, + /// This call is now unpaused. \[pallet_name, call_name\] + CallUnpaused { pallet_name: PalletNameOf, call_name: CallNameOf }, } /// The set of calls that are explicitly paused. @@ -129,8 +144,8 @@ pub mod pallet { #[pallet::genesis_build] impl GenesisBuild for GenesisConfig { fn build(&self) { - for (pallet, function) in &self.paused { - PausedCalls::::insert((pallet, function), ()); + for (pallet_name, call_name) in &self.paused { + PausedCalls::::insert((pallet_name, call_name), ()); } } } @@ -144,14 +159,14 @@ pub mod pallet { #[pallet::weight(T::WeightInfo::pause_call())] pub fn pause_call( origin: OriginFor, - pallet: PalletNameOf, - call: CallNameOf, + pallet_name: PalletNameOf, + call_name: CallNameOf, ) -> DispatchResult { T::PauseOrigin::ensure_origin(origin)?; - Self::ensure_can_pause(&pallet, &call)?; - PausedCalls::::insert((&pallet, &call), ()); - Self::deposit_event(Event::CallPaused(pallet, call)); + Self::ensure_can_pause(&pallet_name, &call_name)?; + PausedCalls::::insert((&pallet_name, &call_name), ()); + Self::deposit_event(Event::CallPaused { pallet_name, call_name }); Ok(()) } @@ -163,14 +178,14 @@ pub mod pallet { #[pallet::weight(T::WeightInfo::unpause_call())] pub fn unpause_call( origin: OriginFor, - pallet: PalletNameOf, - call: CallNameOf, + pallet_name: PalletNameOf, + call_name: CallNameOf, ) -> DispatchResult { T::UnpauseOrigin::ensure_origin(origin)?; - Self::ensure_can_unpause(&pallet, &call)?; - PausedCalls::::remove((&pallet, &call)); - Self::deposit_event(Event::CallUnpaused(pallet, call)); + Self::ensure_can_unpause(&pallet_name, &call_name)?; + PausedCalls::::remove((&pallet_name, &call_name)); + Self::deposit_event(Event::CallUnpaused { pallet_name, call_name }); Ok(()) } @@ -179,34 +194,35 @@ pub mod pallet { impl Pallet { /// Return whether this call is paused. - pub fn is_paused_unbound(pallet: Vec, call: Vec) -> bool { - let pallet = PalletNameOf::::try_from(pallet); - let call = CallNameOf::::try_from(call); + pub fn is_paused_unbound(pallet_name: Vec, call_name: Vec) -> bool { + let pallet_name = PalletNameOf::::try_from(pallet_name); + let call_name = CallNameOf::::try_from(call_name); - match (pallet, call) { - (Ok(pallet), Ok(call)) => Self::is_paused(&pallet, &call), + match (pallet_name, call_name) { + (Ok(pallet_name), Ok(call_name)) => Self::is_paused(&pallet_name, &call_name), _ => T::PauseTooLongNames::get(), } } /// Return whether this call is paused. - pub fn is_paused(pallet: &PalletNameOf, call: &CallNameOf) -> bool { - >::contains_key((pallet, call)) + pub fn is_paused(pallet_name: &PalletNameOf, call_name: &CallNameOf) -> bool { + >::contains_key((pallet_name, call_name)) } /// Ensure that this call can be paused. pub fn ensure_can_pause( - pallet: &PalletNameOf, - call: &CallNameOf, + pallet_name: &PalletNameOf, + call_name: &CallNameOf, ) -> Result<(), Error> { // The `TxPause` pallet can never be paused. - if pallet.as_ref() == ::name().as_bytes().to_vec() { + if pallet_name.as_ref() == ::name().as_bytes().to_vec() { return Err(Error::::IsUnpausable) } - if T::UnpausablePallets::contains(&pallet) { + let full_name: FullNameOf = (pallet_name.clone(), Some(call_name.clone())); + if T::UnfilterableCallNames::contains(&full_name) { return Err(Error::::IsUnpausable) } - if Self::is_paused(pallet, call) { + if Self::is_paused(&pallet_name, &call_name) { return Err(Error::::IsPaused) } Ok(()) @@ -214,10 +230,10 @@ impl Pallet { /// Ensure that this call can be un-paused. pub fn ensure_can_unpause( - pallet: &PalletNameOf, - call: &CallNameOf, + pallet_name: &PalletNameOf, + call_name: &CallNameOf, ) -> Result<(), Error> { - if Self::is_paused(pallet, call) { + if Self::is_paused(&pallet_name, &call_name) { // SAFETY: Everything that is paused, can be un-paused. Ok(()) } else { @@ -226,12 +242,12 @@ impl Pallet { } } -impl Contains for Pallet +impl Contains<::RuntimeCall> for Pallet where ::RuntimeCall: GetCallMetadata, { /// Return whether the call is allowed to be dispatched. - fn contains(call: &T::RuntimeCall) -> bool { + fn contains(call: &::RuntimeCall) -> bool { let CallMetadata { pallet_name, function_name } = call.get_call_metadata(); !Pallet::::is_paused_unbound(pallet_name.into(), function_name.into()) } diff --git a/frame/tx-pause/src/mock.rs b/frame/tx-pause/src/mock.rs index 1ed00c9805dba..d7fe7af6cede4 100644 --- a/frame/tx-pause/src/mock.rs +++ b/frame/tx-pause/src/mock.rs @@ -22,7 +22,7 @@ use crate as pallet_tx_pause; use frame_support::{ parameter_types, - traits::{Everything, InsideBoth, SortedMembers}, + traits::{ConstU64, Everything, InsideBoth, InstanceFilter, SortedMembers}, }; use frame_system::EnsureSignedBy; use sp_core::H256; @@ -77,6 +77,65 @@ impl pallet_balances::Config for Test { type ReserveIdentifier = [u8; 8]; } +impl pallet_utility::Config for Test { + type RuntimeEvent = RuntimeEvent; + type RuntimeCall = RuntimeCall; + type PalletsOrigin = OriginCaller; + type WeightInfo = (); +} + +#[derive( + Copy, + Clone, + Eq, + PartialEq, + Ord, + PartialOrd, + Encode, + Decode, + RuntimeDebug, + MaxEncodedLen, + scale_info::TypeInfo, +)] +pub enum ProxyType { + Any, + JustTransfer, + JustUtility, +} +impl Default for ProxyType { + fn default() -> Self { + Self::Any + } +} +impl InstanceFilter for ProxyType { + fn filter(&self, c: &RuntimeCall) -> bool { + match self { + ProxyType::Any => true, + ProxyType::JustTransfer => { + matches!(c, RuntimeCall::Balances(pallet_balances::Call::transfer { .. })) + }, + ProxyType::JustUtility => matches!(c, RuntimeCall::Utility { .. }), + } + } + fn is_superset(&self, o: &Self) -> bool { + self == &ProxyType::Any || self == o + } +} +impl pallet_proxy::Config for Test { + type RuntimeEvent = RuntimeEvent; + type RuntimeCall = RuntimeCall; + type Currency = Balances; + type ProxyType = ProxyType; + type ProxyDepositBase = ConstU64<1>; + type ProxyDepositFactor = ConstU64<1>; + type MaxProxies = ConstU32<4>; + type WeightInfo = (); + type CallHasher = BlakeTwo256; + type MaxPending = ConstU32<2>; + type AnnouncementDepositBase = ConstU64<1>; + type AnnouncementDepositFactor = ConstU64<1>; +} + parameter_types! { pub const PauseOrigin: u64 = 1; pub const UnpauseOrigin: u64 = 2; @@ -84,16 +143,34 @@ parameter_types! { pub const PauseTooLongNames: bool = false; } -pub struct MockUnpausablePallets; - -impl Contains> for MockUnpausablePallets { - fn contains(pallet: &PalletNameOf) -> bool { - let unpausables: Vec> = - vec![b"UnpausablePallet".to_vec().try_into().unwrap()]; - - unpausables.iter().any(|i| i == pallet) +#[derive(Copy, Clone, Encode, Decode, RuntimeDebug, MaxEncodedLen, scale_info::TypeInfo)] +pub struct UnfilterableCallNames; + +/// Make Balances::transfer_keep_alive and all DummyPallet calls unfilterable, accept all others. +impl Contains> for UnfilterableCallNames { + fn contains(full_name: &FullNameOf) -> bool { + let unpausables: Vec> = vec![ + ( + b"Balances".to_vec().try_into().unwrap(), + Some(b"transfer_keep_alive".to_vec().try_into().unwrap()), + ), + (b"DummyPallet".to_vec().try_into().unwrap(), None), + ]; + + for unpausable_call in unpausables { + let (pallet_name, maybe_call_name) = full_name; + if pallet_name == &unpausable_call.0 { + if unpausable_call.1.is_none() { + return true + } + return maybe_call_name == &unpausable_call.1 + } + } + + false } } + // Required impl to use some ::get() in tests impl SortedMembers for PauseOrigin { fn sorted_members() -> Vec { @@ -112,9 +189,10 @@ impl SortedMembers for UnpauseOrigin { impl Config for Test { type RuntimeEvent = RuntimeEvent; + type RuntimeCall = RuntimeCall; type PauseOrigin = EnsureSignedBy; type UnpauseOrigin = EnsureSignedBy; - type UnpausablePallets = MockUnpausablePallets; + type UnfilterableCallNames = UnfilterableCallNames; type MaxNameLen = MaxNameLen; type PauseTooLongNames = PauseTooLongNames; type WeightInfo = (); @@ -131,6 +209,8 @@ frame_support::construct_runtime!( { System: frame_system, Balances: pallet_balances, + Utility: pallet_utility, + Proxy: pallet_proxy, TxPause: pallet_tx_pause, } ); diff --git a/frame/tx-pause/src/tests.rs b/frame/tx-pause/src/tests.rs index f474d11eeb838..493de946492e6 100644 --- a/frame/tx-pause/src/tests.rs +++ b/frame/tx-pause/src/tests.rs @@ -25,41 +25,51 @@ use frame_support::{assert_err, assert_noop, assert_ok, dispatch::Dispatchable}; // GENERAL SUCCESS/POSITIVE TESTS --------------------- #[test] -fn can_set_arbitrary_pause() { +fn can_pause_specific_call() { new_test_ext().execute_with(|| { + assert_ok!(call_transfer(1, 1).dispatch(Origin::signed(0))); + assert_ok!(TxPause::pause_call( Origin::signed(mock::PauseOrigin::get()), - name(b"SomePallet"), - name(b"some_function"), + name(b"Balances"), + name(b"transfer"), )); + + assert_err!( + call_transfer(2, 1).dispatch(Origin::signed(2)), + frame_system::Error::::CallFiltered + ); + assert_ok!(call_transfer_keep_alive(3, 1).dispatch(Origin::signed(3))); }); } #[test] -fn can_pause_system_call() { +fn can_unpause_specific_call() { new_test_ext().execute_with(|| { - let call = RuntimeCall::System(frame_system::Call::remark { remark: vec![] }); - assert_ok!(TxPause::pause_call( Origin::signed(mock::PauseOrigin::get()), - name(b"System"), - name(b"remark"), + name(b"Balances"), + name(b"transfer"), )); - assert_err!( - call.clone().dispatch(Origin::signed(0)), + call_transfer(2, 1).dispatch(Origin::signed(2)), frame_system::Error::::CallFiltered ); + + assert_ok!(TxPause::unpause_call( + Origin::signed(mock::UnpauseOrigin::get()), + name(b"Balances"), + name(b"transfer"), + )); + assert_ok!(call_transfer(4, 1).dispatch(Origin::signed(0))); }); } #[test] -fn can_pause_specific_call() { +fn can_filter_balance_in_batch_when_paused() { new_test_ext().execute_with(|| { - let call_paused = - RuntimeCall::Balances(pallet_balances::Call::transfer { dest: 1, value: 1 }); - let call_not_paused = - RuntimeCall::Balances(pallet_balances::Call::transfer_keep_alive { dest: 1, value: 1 }); + let batch_call = + RuntimeCall::Utility(pallet_utility::Call::batch { calls: vec![call_transfer(1, 1)] }); assert_ok!(TxPause::pause_call( Origin::signed(mock::PauseOrigin::get()), @@ -67,36 +77,35 @@ fn can_pause_specific_call() { name(b"transfer"), )); - assert_err!( - call_paused.clone().dispatch(Origin::signed(0)), - frame_system::Error::::CallFiltered + assert_ok!(batch_call.clone().dispatch(Origin::signed(0))); + System::assert_last_event( + pallet_utility::Event::BatchInterrupted { + index: 0, + error: frame_system::Error::::CallFiltered.into(), + } + .into(), ); - assert_ok!(call_not_paused.clone().dispatch(Origin::signed(0))); }); } #[test] -fn can_unpause_specific_call() { +fn can_filter_balance_in_proxy_when_paused() { new_test_ext().execute_with(|| { - let call_paused = - RuntimeCall::Balances(pallet_balances::Call::transfer { dest: 1, value: 1 }); - assert_ok!(TxPause::pause_call( Origin::signed(mock::PauseOrigin::get()), name(b"Balances"), name(b"transfer"), )); - assert_err!( - call_paused.clone().dispatch(Origin::signed(0)), - frame_system::Error::::CallFiltered - ); - assert_ok!(TxPause::unpause_call( - Origin::signed(mock::UnpauseOrigin::get()), - name(b"Balances"), - name(b"transfer"), - )); - assert_ok!(call_paused.clone().dispatch(Origin::signed(0))); + assert_ok!(Proxy::add_proxy(Origin::signed(1), 2, ProxyType::JustTransfer, 0)); + + assert_ok!(Proxy::proxy(Origin::signed(2), 1, None, Box::new(call_transfer(1, 1)))); + System::assert_last_event( + pallet_proxy::Event::ProxyExecuted { + result: DispatchError::from(frame_system::Error::::CallFiltered).into(), + } + .into(), + ); }); } @@ -109,7 +118,7 @@ fn fails_to_pause_self() { TxPause::pause_call( Origin::signed(mock::PauseOrigin::get()), name(b"TxPause"), - name(b"should_not_matter"), + name(b"pause_call") ), Error::::IsUnpausable ); @@ -122,8 +131,22 @@ fn fails_to_pause_unpausable_pallet() { assert_noop!( TxPause::pause_call( Origin::signed(mock::PauseOrigin::get()), - name(b"UnpausablePallet"), - name(b"should_not_matter"), + name(b"DummyPallet"), + name(b"any-call") + ), + Error::::IsUnpausable + ); + }); +} + +#[test] +fn fails_to_pause_unpausable_call() { + new_test_ext().execute_with(|| { + assert_noop!( + TxPause::pause_call( + Origin::signed(mock::PauseOrigin::get()), + name(b"Balances"), + name(b"transfer_keep_alive"), ), Error::::IsUnpausable ); @@ -135,15 +158,15 @@ fn fails_to_pause_already_paused_pallet() { new_test_ext().execute_with(|| { assert_ok!(TxPause::pause_call( Origin::signed(mock::PauseOrigin::get()), - name(b"SomePallet"), - name(b"some_function"), + name(b"Balances"), + name(b"transfer"), )); assert_noop!( TxPause::pause_call( Origin::signed(mock::PauseOrigin::get()), - name(b"SomePallet"), - name(b"some_function"), + name(b"Balances"), + name(b"transfer"), ), Error::::IsPaused ); @@ -156,8 +179,8 @@ fn fails_to_unpause_not_paused_pallet() { assert_noop!( TxPause::unpause_call( Origin::signed(mock::UnpauseOrigin::get()), - name(b"SomePallet"), - name(b"some_function"), + name(b"Balances"), + name(b"transfer"), ), Error::::IsUnpaused ); @@ -167,3 +190,11 @@ fn fails_to_unpause_not_paused_pallet() { fn name(bytes: &[u8]) -> BoundedVec { bytes.to_vec().try_into().unwrap() } + +fn call_transfer(dest: u64, value: u64) -> RuntimeCall { + RuntimeCall::Balances(pallet_balances::Call::transfer { dest, value }) +} + +fn call_transfer_keep_alive(dest: u64, value: u64) -> RuntimeCall { + RuntimeCall::Balances(pallet_balances::Call::transfer_keep_alive { dest, value }) +} diff --git a/frame/tx-pause/src/weights.rs b/frame/tx-pause/src/weights.rs index 10a296924b660..bd1d7de74c64b 100644 --- a/frame/tx-pause/src/weights.rs +++ b/frame/tx-pause/src/weights.rs @@ -18,7 +18,7 @@ //! Autogenerated weights for pallet_tx_pause //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-09-19, STEPS: `1`, REPEAT: 1, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2022-10-11, STEPS: `1`, REPEAT: 1, LOW RANGE: `[]`, HIGH RANGE: `[]` //! HOSTNAME: `pop-os`, CPU: `12th Gen Intel(R) Core(TM) i7-12700H` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 @@ -64,13 +64,13 @@ pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { // Storage: TxPause PausedCalls (r:1 w:1) fn pause_call() -> Weight { - Weight::from_ref_time(55_270_000 as u64) + Weight::from_ref_time(38_905_000 as u64) .saturating_add(T::DbWeight::get().reads(1 as u64)) .saturating_add(T::DbWeight::get().writes(1 as u64)) } // Storage: TxPause PausedCalls (r:1 w:1) fn unpause_call() -> Weight { - Weight::from_ref_time(55_290_000 as u64) + Weight::from_ref_time(32_892_000 as u64) .saturating_add(T::DbWeight::get().reads(1 as u64)) .saturating_add(T::DbWeight::get().writes(1 as u64)) } @@ -80,13 +80,13 @@ impl WeightInfo for SubstrateWeight { impl WeightInfo for () { // Storage: TxPause PausedCalls (r:1 w:1) fn pause_call() -> Weight { - Weight::from_ref_time(55_270_000 as u64) + Weight::from_ref_time(38_905_000 as u64) .saturating_add(RocksDbWeight::get().reads(1 as u64)) .saturating_add(RocksDbWeight::get().writes(1 as u64)) } // Storage: TxPause PausedCalls (r:1 w:1) fn unpause_call() -> Weight { - Weight::from_ref_time(55_290_000 as u64) + Weight::from_ref_time(32_892_000 as u64) .saturating_add(RocksDbWeight::get().reads(1 as u64)) .saturating_add(RocksDbWeight::get().writes(1 as u64)) } From 3a0bec0846a87071aed49d5daca6a15f11145c44 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Wed, 12 Oct 2022 15:22:33 +0200 Subject: [PATCH 024/100] Set block number Signed-off-by: Oliver Tale-Yazdi --- frame/safe-mode/src/benchmarking.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/frame/safe-mode/src/benchmarking.rs b/frame/safe-mode/src/benchmarking.rs index 0c43a4b30a2d3..5c670e65526b0 100644 --- a/frame/safe-mode/src/benchmarking.rs +++ b/frame/safe-mode/src/benchmarking.rs @@ -55,6 +55,7 @@ benchmarks! { let origin = RawOrigin::Signed(caller.clone()); T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); + System::::set_block_number(1u32.into()); assert!(SafeMode::::activate(origin.clone().into()).is_ok()); }: _(origin) verify { From 89023790a66450a7139cb1ded629b1353cb43693 Mon Sep 17 00:00:00 2001 From: Dan Shields Date: Wed, 12 Oct 2022 09:18:50 -0600 Subject: [PATCH 025/100] dummy weights generated, safe mode --- frame/safe-mode/src/benchmarking.rs | 1 + frame/safe-mode/src/weights.rs | 134 +++++++++++++--------------- 2 files changed, 63 insertions(+), 72 deletions(-) diff --git a/frame/safe-mode/src/benchmarking.rs b/frame/safe-mode/src/benchmarking.rs index 5c670e65526b0..efbf786534baf 100644 --- a/frame/safe-mode/src/benchmarking.rs +++ b/frame/safe-mode/src/benchmarking.rs @@ -70,6 +70,7 @@ benchmarks! { let origin = RawOrigin::Signed(caller.clone()); T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); + System::::set_block_number(1u32.into()); assert!(SafeMode::::activate(origin.clone().into()).is_ok()); let force_origin = T::ForceExtendOrigin::successful_origin(); diff --git a/frame/safe-mode/src/weights.rs b/frame/safe-mode/src/weights.rs index d24f31f944d3c..034dff48bcf29 100644 --- a/frame/safe-mode/src/weights.rs +++ b/frame/safe-mode/src/weights.rs @@ -18,8 +18,8 @@ //! Autogenerated weights for pallet_safe_mode //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-09-15, STEPS: `1`, REPEAT: 1, LOW RANGE: `[]`, HIGH RANGE: `[]` -//! HOSTNAME: `pop-os`, CPU: `12th Gen Intel(R) Core(TM) i7-12700H` +//! DATE: 2022-10-12, STEPS: `1`, REPEAT: 1, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! HOSTNAME: `pop-os`, CPU: `AMD Ryzen 7 1800X Eight-Core Processor` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: @@ -67,118 +67,108 @@ pub trait WeightInfo { /// Weights for pallet_safe_mode using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { + // Storage: Balances Reserves (r:1 w:1) // Storage: SafeMode Reservations (r:1 w:1) - // Storage: SafeMode Enabled (r:1 w:1) + // Storage: SafeMode ActiveUntil (r:1 w:1) fn activate() -> Weight { - Weight::from_ref_time(51_688_000 as u64) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) + Weight::from_ref_time(83_536_000 as u64) + .saturating_add(T::DbWeight::get().reads(3 as u64)) + .saturating_add(T::DbWeight::get().writes(3 as u64)) } - - // Storage: SafeMode Reservations (r:1 w:1) - // Storage: SafeMode Enabled (r:1 w:1) + // Storage: SafeMode ActiveUntil (r:1 w:1) fn force_activate() -> Weight { - Weight::from_ref_time(51_688_000 as u64) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) + Weight::from_ref_time(44_232_000 as u64) + .saturating_add(T::DbWeight::get().reads(1 as u64)) + .saturating_add(T::DbWeight::get().writes(1 as u64)) } - + // Storage: Balances Reserves (r:1 w:1) // Storage: SafeMode Reservations (r:1 w:1) - // Storage: SafeMode Enabled (r:1 w:1) + // Storage: SafeMode ActiveUntil (r:1 w:1) fn extend() -> Weight { - Weight::from_ref_time(51_688_000 as u64) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) + Weight::from_ref_time(85_289_000 as u64) + .saturating_add(T::DbWeight::get().reads(3 as u64)) + .saturating_add(T::DbWeight::get().writes(3 as u64)) } - - // Storage: SafeMode Reservations (r:1 w:1) - // Storage: SafeMode Enabled (r:1 w:1) + // Storage: SafeMode ActiveUntil (r:1 w:1) fn force_extend() -> Weight { - Weight::from_ref_time(51_688_000 as u64) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) + Weight::from_ref_time(42_209_000 as u64) + .saturating_add(T::DbWeight::get().reads(1 as u64)) + .saturating_add(T::DbWeight::get().writes(1 as u64)) } - - // Storage: SafeMode Reservations (r:1 w:1) - // Storage: SafeMode Enabled (r:1 w:1) + // Storage: SafeMode ActiveUntil (r:1 w:1) fn force_deactivate() -> Weight { - Weight::from_ref_time(51_688_000 as u64) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) + Weight::from_ref_time(37_661_000 as u64) + .saturating_add(T::DbWeight::get().reads(1 as u64)) + .saturating_add(T::DbWeight::get().writes(1 as u64)) } - + // Storage: SafeMode ActiveUntil (r:1 w:0) // Storage: SafeMode Reservations (r:1 w:1) - // Storage: SafeMode Enabled (r:1 w:1) + // Storage: Balances Reserves (r:1 w:1) fn release_reservation() -> Weight { - Weight::from_ref_time(51_688_000 as u64) - .saturating_add(T::DbWeight::get().reads(2 as u64)) + Weight::from_ref_time(76_964_000 as u64) + .saturating_add(T::DbWeight::get().reads(3 as u64)) .saturating_add(T::DbWeight::get().writes(2 as u64)) } - + // Storage: SafeMode ActiveUntil (r:1 w:0) // Storage: SafeMode Reservations (r:1 w:1) - // Storage: SafeMode Enabled (r:1 w:1) + // Storage: Balances Reserves (r:1 w:1) fn slash_reservation() -> Weight { - Weight::from_ref_time(51_688_000 as u64) - .saturating_add(T::DbWeight::get().reads(2 as u64)) + Weight::from_ref_time(88_074_000 as u64) + .saturating_add(T::DbWeight::get().reads(3 as u64)) .saturating_add(T::DbWeight::get().writes(2 as u64)) } } // For backwards compatibility and tests impl WeightInfo for () { + // Storage: Balances Reserves (r:1 w:1) // Storage: SafeMode Reservations (r:1 w:1) - // Storage: SafeMode Enabled (r:1 w:1) + // Storage: SafeMode ActiveUntil (r:1 w:1) fn activate() -> Weight { - Weight::from_ref_time(51_688_000 as u64) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) + Weight::from_ref_time(83_536_000 as u64) + .saturating_add(RocksDbWeight::get().reads(3 as u64)) + .saturating_add(RocksDbWeight::get().writes(3 as u64)) } - - // Storage: SafeMode Reservations (r:1 w:1) - // Storage: SafeMode Enabled (r:1 w:1) + // Storage: SafeMode ActiveUntil (r:1 w:1) fn force_activate() -> Weight { - Weight::from_ref_time(51_688_000 as u64) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) + Weight::from_ref_time(44_232_000 as u64) + .saturating_add(RocksDbWeight::get().reads(1 as u64)) + .saturating_add(RocksDbWeight::get().writes(1 as u64)) } - + // Storage: Balances Reserves (r:1 w:1) // Storage: SafeMode Reservations (r:1 w:1) - // Storage: SafeMode Enabled (r:1 w:1) + // Storage: SafeMode ActiveUntil (r:1 w:1) fn extend() -> Weight { - Weight::from_ref_time(51_688_000 as u64) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) + Weight::from_ref_time(85_289_000 as u64) + .saturating_add(RocksDbWeight::get().reads(3 as u64)) + .saturating_add(RocksDbWeight::get().writes(3 as u64)) } - - // Storage: SafeMode Reservations (r:1 w:1) - // Storage: SafeMode Enabled (r:1 w:1) + // Storage: SafeMode ActiveUntil (r:1 w:1) fn force_extend() -> Weight { - Weight::from_ref_time(51_688_000 as u64) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) + Weight::from_ref_time(42_209_000 as u64) + .saturating_add(RocksDbWeight::get().reads(1 as u64)) + .saturating_add(RocksDbWeight::get().writes(1 as u64)) } - - // Storage: SafeMode Reservations (r:1 w:1) - // Storage: SafeMode Enabled (r:1 w:1) + // Storage: SafeMode ActiveUntil (r:1 w:1) fn force_deactivate() -> Weight { - Weight::from_ref_time(51_688_000 as u64) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) + Weight::from_ref_time(37_661_000 as u64) + .saturating_add(RocksDbWeight::get().reads(1 as u64)) + .saturating_add(RocksDbWeight::get().writes(1 as u64)) } - + // Storage: SafeMode ActiveUntil (r:1 w:0) // Storage: SafeMode Reservations (r:1 w:1) - // Storage: SafeMode Enabled (r:1 w:1) + // Storage: Balances Reserves (r:1 w:1) fn release_reservation() -> Weight { - Weight::from_ref_time(51_688_000 as u64) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) + Weight::from_ref_time(76_964_000 as u64) + .saturating_add(RocksDbWeight::get().reads(3 as u64)) .saturating_add(RocksDbWeight::get().writes(2 as u64)) } - + // Storage: SafeMode ActiveUntil (r:1 w:0) // Storage: SafeMode Reservations (r:1 w:1) - // Storage: SafeMode Enabled (r:1 w:1) + // Storage: Balances Reserves (r:1 w:1) fn slash_reservation() -> Weight { - Weight::from_ref_time(51_688_000 as u64) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) + Weight::from_ref_time(88_074_000 as u64) + .saturating_add(RocksDbWeight::get().reads(3 as u64)) .saturating_add(RocksDbWeight::get().writes(2 as u64)) } } From 3ef4045d19716346fb86f36616ff973219d90603 Mon Sep 17 00:00:00 2001 From: Dan Shields Date: Thu, 13 Oct 2022 13:16:54 -0600 Subject: [PATCH 026/100] add repay_reservation call with RepaymentDelay per #10033 feature requirements --- bin/node/runtime/src/lib.rs | 16 +-- frame/safe-mode/src/benchmarking.rs | 49 +++++++-- frame/safe-mode/src/lib.rs | 141 +++++++++++++++++-------- frame/safe-mode/src/mock.rs | 16 +-- frame/safe-mode/src/tests.rs | 155 ++++++++++++++++------------ frame/safe-mode/src/weights.rs | 47 ++++++--- 6 files changed, 277 insertions(+), 147 deletions(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index d20f10014f3d1..7a8b8b77ba157 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -360,24 +360,26 @@ impl, O>> + From>> Ensu } parameter_types! { - pub const SignedActivationDuration: u32 = 3; - pub const SignedExtendDuration: u32 = 30; - pub const ActivateReservationAmount: Balance = 10 * DOLLARS; //TODO This needs to be something sensible for the implications of enablement! - pub const ExtendReservationAmount: Balance = 10 * DOLLARS; //TODO This needs to be something sensible for the implications of enablement! + pub const SignedActivationDuration: u32 = 10; + pub const ExtendDuration: u32 = 20; + pub const ActivateReservationAmount: Balance = 10 * DOLLARS; + pub const ExtendReservationAmount: Balance = 15 * DOLLARS; + pub const ReleaseDelay: u32 = 15; } impl pallet_safe_mode::Config for Runtime { type RuntimeEvent = RuntimeEvent; type Currency = Balances; type UnfilterableCalls = UnfilterableCalls; - type SignedActivationDuration = ConstU32<{ 2 * DAYS }>; + type ActivationDuration = ConstU32<{ 2 * DAYS }>; type ActivateReservationAmount = ActivateReservationAmount; - type SignedExtendDuration = ConstU32<{ 1 * DAYS }>; + type ExtendDuration = ConstU32<{ 1 * DAYS }>; type ExtendReservationAmount = ExtendReservationAmount; type ForceActivateOrigin = ForceActivateOrigin; type ForceExtendOrigin = ForceExtendOrigin; type ForceDeactivateOrigin = EnsureRoot; - type RepayOrigin = EnsureRoot; + type ForceReservationOrigin = EnsureRoot; + type ReleaseDelay = ReleaseDelay; type WeightInfo = pallet_safe_mode::weights::SubstrateWeight; } diff --git a/frame/safe-mode/src/benchmarking.rs b/frame/safe-mode/src/benchmarking.rs index efbf786534baf..6866fe4bb8f45 100644 --- a/frame/safe-mode/src/benchmarking.rs +++ b/frame/safe-mode/src/benchmarking.rs @@ -22,7 +22,7 @@ use super::{Pallet as SafeMode, *}; use frame_benchmarking::{benchmarks, whitelisted_caller}; use frame_support::traits::{Currency, UnfilteredDispatchable}; use frame_system::{Pallet as System, RawOrigin}; -use sp_runtime::traits::Bounded; +use sp_runtime::traits::{Bounded, One}; benchmarks! { activate { @@ -33,7 +33,7 @@ benchmarks! { verify { assert_eq!( SafeMode::::active_until().unwrap(), - System::::block_number() + T::SignedActivationDuration::get() + System::::block_number() + T::ActivationDuration::get() ); } @@ -61,7 +61,7 @@ benchmarks! { verify { assert_eq!( SafeMode::::active_until().unwrap(), - System::::block_number() + T::SignedActivationDuration::get() + T::SignedExtendDuration::get() + System::::block_number() + T::ActivationDuration::get() + T::ExtendDuration::get() ); } @@ -80,7 +80,7 @@ benchmarks! { verify { assert_eq!( SafeMode::::active_until().unwrap(), - System::::block_number() + T::SignedActivationDuration::get() + extension + System::::block_number() + T::ActivationDuration::get() + extension ); } @@ -117,9 +117,42 @@ benchmarks! { let force_origin = T::ForceDeactivateOrigin::successful_origin(); assert!(SafeMode::::force_deactivate(force_origin.clone()).is_ok()); - let repay_origin = T::RepayOrigin::successful_origin(); + System::::set_block_number(System::::block_number() + One::one()); + System::::on_initialize(System::::block_number()); + SafeMode::::on_initialize(System::::block_number()); + let call = Call::::release_reservation { account: caller.clone(), block: activated_at_block.clone()}; - }: { call.dispatch_bypass_filter(repay_origin)? } + }: { call.dispatch_bypass_filter(origin.into())? } + verify { + assert_eq!( + T::Currency::free_balance(&caller), + BalanceOf::::max_value() + ); + } + + force_release_reservation { + let caller: T::AccountId = whitelisted_caller(); + let origin = RawOrigin::Signed(caller.clone()); + T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); + + let activated_at_block: T::BlockNumber = System::::block_number(); + assert!(SafeMode::::activate(origin.clone().into()).is_ok()); + let current_reservation = Reservations::::get(&caller, activated_at_block).unwrap_or_default(); + assert_eq!( + T::Currency::free_balance(&caller), + BalanceOf::::max_value() - T::ActivateReservationAmount::get().unwrap() + ); + + let force_origin = T::ForceDeactivateOrigin::successful_origin(); + assert!(SafeMode::::force_deactivate(force_origin.clone()).is_ok()); + + System::::set_block_number(System::::block_number() + One::one()); + System::::on_initialize(System::::block_number()); + SafeMode::::on_initialize(System::::block_number()); + + let release_origin = T::ForceReservationOrigin::successful_origin(); + let call = Call::::force_release_reservation { account: caller.clone(), block: activated_at_block.clone()}; + }: { call.dispatch_bypass_filter(release_origin)? } verify { assert_eq!( T::Currency::free_balance(&caller), @@ -143,9 +176,9 @@ benchmarks! { let force_origin = T::ForceDeactivateOrigin::successful_origin(); assert!(SafeMode::::force_deactivate(force_origin.clone()).is_ok()); - let repay_origin = T::RepayOrigin::successful_origin(); + let release_origin = T::ForceReservationOrigin::successful_origin(); let call = Call::::slash_reservation { account: caller.clone(), block: activated_at_block.clone()}; - }: { call.dispatch_bypass_filter(repay_origin)? } + }: { call.dispatch_bypass_filter(release_origin)? } verify { assert_eq!( T::Currency::free_balance(&caller), diff --git a/frame/safe-mode/src/lib.rs b/frame/safe-mode/src/lib.rs index bcf05934aa34c..85ae3d23c99d3 100644 --- a/frame/safe-mode/src/lib.rs +++ b/frame/safe-mode/src/lib.rs @@ -62,21 +62,21 @@ pub mod pallet { >; /// Contains all runtime calls in any pallet that can be dispatched even while the safe-mode - /// is activated. + /// is active. /// /// The safe-mode pallet cannot disable it's own calls, and does not need to be explicitly /// added here. type UnfilterableCalls: Contains; - /// How long the safe-mode will stay active when activated with [`Pallet::activate`]. + /// How long the safe-mode will stay active with [`Pallet::activate`]. #[pallet::constant] - type SignedActivationDuration: Get; + type ActivationDuration: Get; /// For how many blocks the safe-mode can be extended by each [`Pallet::extend`] call. /// /// This does not impose a hard limit as the safe-mode can be extended multiple times. #[pallet::constant] - type SignedExtendDuration: Get; + type ExtendDuration: Get; /// The amount that will be reserved upon calling [`Pallet::activate`]. /// @@ -103,9 +103,20 @@ pub mod pallet { /// The origin that may call [`Pallet::force_activate`]. type ForceDeactivateOrigin: EnsureOrigin; - /// The origin that may call [`Pallet::release_reservation`] and + /// The origin that may call [`Pallet::force_release_reservation`] and /// [`Pallet::slash_reservation`]. - type RepayOrigin: EnsureOrigin; + type ForceReservationOrigin: EnsureOrigin; + + /// The minimal duration a deposit will remain reserved after safe-mode is activated or + /// extended, unless [`Pallet::force_release_reservation`] is successfully dispatched + /// sooner. + /// + /// Every reservation is tied to a specific activation or extension, thus each reservation + /// can be release independently after the delay for it has passed. + /// + /// `None` disallows permissionlessly releasing the safe-mode reservations. + #[pallet::constant] + type ReleaseDelay: Get>; // Weight information for extrinsics in this pallet. type WeightInfo: WeightInfo; @@ -124,6 +135,9 @@ pub mod pallet { /// There is no balance reserved. NoReservation, + + /// This reservation cannot be released yet. + CannotReleaseYet, } #[pallet::event] @@ -138,12 +152,13 @@ pub mod pallet { /// Exited safe-mode for a specific \[reason\]. Exited { reason: ExitReason }, - /// An account had reserve repaid previously reserved at a block. \[block, account, - /// amount\] - ReservationRepaid { block: T::BlockNumber, account: T::AccountId, amount: BalanceOf }, + /// An account had a reserve released that was reserved at a specific block. \[account, + /// block, amount\] + ReservationReleased { account: T::AccountId, block: T::BlockNumber, amount: BalanceOf }, - /// An account had reserve slashed previously reserved at a block. \[account, amount\] - ReservationSlashed { block: T::BlockNumber, account: T::AccountId, amount: BalanceOf }, + /// An account had reserve slashed that was reserved at a specific block. \[account, block, + /// amount\] + ReservationSlashed { account: T::AccountId, block: T::BlockNumber, amount: BalanceOf }, } /// The reason why the safe-mode was exited. @@ -205,7 +220,7 @@ pub mod pallet { #[pallet::call] impl Pallet { - /// Activate safe-mode permissionlessly for [`Config::SignedActivationDuration`] blocks. + /// Activate safe-mode permissionlessly for [`Config::ActivationDuration`] blocks. /// /// Reserves [`Config::ActivateReservationAmount`] from the caller's account. /// Emits an [`Event::Activated`] event on success. @@ -221,7 +236,7 @@ pub mod pallet { pub fn activate(origin: OriginFor) -> DispatchResult { let who = ensure_signed(origin)?; - Self::do_activate(Some(who), T::SignedActivationDuration::get()) + Self::do_activate(Some(who), T::ActivationDuration::get()) } /// Activate safe-mode by force for a per-origin configured number of blocks. @@ -239,7 +254,7 @@ pub mod pallet { Self::do_activate(None, duration) } - /// Extend the safe-mode permissionlessly for [`Config::SignedExtendDuration`] blocks. + /// Extend the safe-mode permissionlessly for [`Config::ExtendDuration`] blocks. /// /// Reserves [`Config::ExtendReservationAmount`] from the caller's account. /// Emits an [`Event::Extended`] event on success. @@ -255,7 +270,7 @@ pub mod pallet { pub fn extend(origin: OriginFor) -> DispatchResult { let who = ensure_signed(origin)?; - Self::do_extend(Some(who), T::SignedExtendDuration::get()) + Self::do_extend(Some(who), T::ExtendDuration::get()) } /// Extend the safe-mode by force a per-origin configured number of blocks. @@ -293,46 +308,72 @@ pub mod pallet { Self::do_deactivate(ExitReason::Force) } - /// Release a currency reservation for an account that activated safe-mode at a specific + /// Slash a reservation for an account that activated or extended safe-mode at a specific /// block earlier. This cannot be called while safe-mode is active. /// - /// Emits a [`Event::ReservationRepaid`] event on success. - /// Errors with [`Error::IsActive`] if the safe-mode presently activated. - /// Errors with [`Error::NoReservation`] if the payee has no named reserved currency at the + /// Emits a [`Event::ReservationSlashed`] event on success. + /// Errors with [`Error::IsActive`] if the safe-mode is active. + /// + /// ### Safety + /// + /// Can only be called by the [`Config::ForceReservationOrigin`] origin. + #[pallet::weight(T::WeightInfo::slash_reservation())] + pub fn slash_reservation( + origin: OriginFor, + account: T::AccountId, + block: T::BlockNumber, + ) -> DispatchResult { + T::ForceReservationOrigin::ensure_origin(origin)?; + + Self::do_slash(account, block) + } + + /// Release a currency reservation for an account that activated safe-mode at a specific + /// block earlier. This cannot be called while safe-mode is active and not until the + /// [`Config::ReleaseDelay`] block height is passed. + /// + /// Emits a [`Event::ReservationReleased`] event on success. + /// Errors with [`Error::IsActive`] if the safe-mode is active. + /// Errors with [`Error::CannotReleaseYet`] if the [`Config::ReleaseDelay`] . + /// Errors with [`Error::NoReservation`] if the payee has no reserved currency at the /// block specified. /// /// ### Safety /// - /// Can only be called by the [`Config::RepayOrigin`] origin. + /// This may be called by any signed origin. + /// This call can be disabled for all origins by configuring + /// [`Config::ReleaseDelay`] to `None`. #[pallet::weight(T::WeightInfo::release_reservation())] pub fn release_reservation( origin: OriginFor, account: T::AccountId, block: T::BlockNumber, ) -> DispatchResult { - T::RepayOrigin::ensure_origin(origin)?; + let who = ensure_signed(origin)?; - Self::do_release_reservation(account, block) + Self::do_release(Some(who), account, block) } - /// Slash a reservation for an account that activated or extended safe-mode at a specific + /// Release a currency reservation for an account that activated safe-mode at a specific /// block earlier. This cannot be called while safe-mode is active. /// - /// Emits a [`Event::ReservationSlashed`] event on success. - /// Errors with [`Error::IsActive`] if the safe-mode presently activated. + /// Emits a [`Event::ReservationReleased`] event on success. + /// Errors with [`Error::IsActive`] if the safe-mode is active. + /// Errors with [`Error::NoReservation`] if the payee has no reserved currency at the + /// block specified. /// /// ### Safety /// - /// Can only be called by the [`Config::RepayOrigin`] origin. - #[pallet::weight(T::WeightInfo::slash_reservation())] - pub fn slash_reservation( + /// Can only be called by the [`Config::ForceReservationOrigin`] origin. + #[pallet::weight(T::WeightInfo::force_release_reservation())] + pub fn force_release_reservation( origin: OriginFor, account: T::AccountId, block: T::BlockNumber, ) -> DispatchResult { - T::RepayOrigin::ensure_origin(origin)?; + T::ForceReservationOrigin::ensure_origin(origin)?; - Self::do_slash_reservation(account, block) + Self::do_release(None, account, block) } } @@ -393,25 +434,39 @@ impl Pallet { Ok(()) } - /// Logic for the [`crate::Pallet::release_reservation`] call. + /// Logic for the [`crate::Pallet::release_reservation`] and + /// [`crate::Pallet::force_release_reservation`] calls. /// - /// Errors if the safe-mode is active. - /// Does not check the origin. - fn do_release_reservation(account: T::AccountId, block: T::BlockNumber) -> DispatchResult { - ensure!(!Self::is_activated(), Error::::IsActive); - let reserve = Reservations::::take(&account, block).ok_or(Error::::NoReservation)?; + /// Errors if the safe-mode is active with [`Error::IsActive`]. + /// Errors if release is called too soon by anyone but [`Config::ForceReservationOrigin`] with + /// [`Error::CannotReleaseYet`]. Does not check the origin. + fn do_release( + who: Option, + account: T::AccountId, + block: T::BlockNumber, + ) -> DispatchResult { + ensure!(!Self::is_active(), Error::::IsActive); + + let reserve = Reservations::::get(&account, &block).ok_or(Error::::NoReservation)?; + + if who.is_some() { + let delay = T::ReleaseDelay::get().ok_or(Error::::NotConfigured)?; + let now = >::block_number(); + ensure!(now > (block + delay), Error::::CannotReleaseYet); + } + Reservations::::remove(&account, &block); T::Currency::unreserve_named(&block, &account, reserve); - Self::deposit_event(Event::::ReservationRepaid { block, account, amount: reserve }); + Self::deposit_event(Event::::ReservationReleased { block, account, amount: reserve }); Ok(()) } /// Logic for the [`crate::Pallet::slash_reservation`] call. /// - /// Errors if the safe-mode is activated. + /// Errors if the safe-mode is active with [`Error::IsActive`]. /// Does not check the origin. - fn do_slash_reservation(account: T::AccountId, block: T::BlockNumber) -> DispatchResult { - ensure!(!Self::is_activated(), Error::::IsActive); + fn do_slash(account: T::AccountId, block: T::BlockNumber) -> DispatchResult { + ensure!(!Self::is_active(), Error::::IsActive); let reserve = Reservations::::take(&account, block).ok_or(Error::::NoReservation)?; T::Currency::slash_reserved_named(&block, &account, reserve); @@ -432,8 +487,8 @@ impl Pallet { Ok(()) } - /// Return whether the `safe-mode` is currently activated. - pub fn is_activated() -> bool { + /// Return whether the `safe-mode` is active. + pub fn is_active() -> bool { ActiveUntil::::exists() } @@ -448,7 +503,7 @@ impl Pallet { return true } - if Self::is_activated() { + if Self::is_active() { T::UnfilterableCalls::contains(call) } else { true diff --git a/frame/safe-mode/src/mock.rs b/frame/safe-mode/src/mock.rs index 8d3c823e44fb1..a51628ceea819 100644 --- a/frame/safe-mode/src/mock.rs +++ b/frame/safe-mode/src/mock.rs @@ -258,12 +258,13 @@ impl, O>> + From>> EnsureOrigin } parameter_types! { - pub const SignedActivationDuration: u64 = 3; - pub const SignedExtendDuration: u64 = 30; + pub const ActivationDuration: u64 = 3; + pub const ExtendDuration: u64 = 30; pub const ActivateReservationAmount: u64 = 100; pub const ExtendReservationAmount: u64 = 100; pub const ForceDeactivateOrigin: u64 = 3; - pub const RepayOrigin: u64 = 4; + pub const ForceReservationOrigin: u64 = 4; + pub const ReleaseDelay: u64 = 2; } // Required impl to use some ::get() in tests @@ -274,7 +275,7 @@ impl SortedMembers for ForceDeactivateOrigin { #[cfg(feature = "runtime-benchmarks")] fn add(_m: &u64) {} } -impl SortedMembers for RepayOrigin { +impl SortedMembers for ForceReservationOrigin { fn sorted_members() -> Vec { vec![Self::get()] } @@ -286,14 +287,15 @@ impl Config for Test { type RuntimeEvent = RuntimeEvent; type Currency = Balances; type UnfilterableCalls = UnfilterableCalls; - type SignedActivationDuration = SignedActivationDuration; + type ActivationDuration = ActivationDuration; type ActivateReservationAmount = ActivateReservationAmount; - type SignedExtendDuration = SignedExtendDuration; + type ExtendDuration = ExtendDuration; type ExtendReservationAmount = ExtendReservationAmount; type ForceActivateOrigin = ForceActivateOrigin; type ForceExtendOrigin = ForceExtendOrigin; type ForceDeactivateOrigin = EnsureSignedBy; - type RepayOrigin = EnsureSignedBy; + type ForceReservationOrigin = EnsureSignedBy; + type ReleaseDelay = ReleaseDelay; type WeightInfo = (); } diff --git a/frame/safe-mode/src/tests.rs b/frame/safe-mode/src/tests.rs index 54771712a994c..784cc896db91e 100644 --- a/frame/safe-mode/src/tests.rs +++ b/frame/safe-mode/src/tests.rs @@ -44,8 +44,8 @@ fn fails_to_filter_calls_to_safe_mode_pallet() { frame_system::Error::::CallFiltered ); assert_ok!(SafeMode::force_deactivate(Origin::signed(mock::ForceDeactivateOrigin::get()))); - assert_ok!(SafeMode::release_reservation( - Origin::signed(mock::RepayOrigin::get()), + assert_ok!(SafeMode::force_release_reservation( + Origin::signed(mock::ForceReservationOrigin::get()), 0, activated_at_block )); @@ -58,7 +58,7 @@ fn fails_to_filter_calls_to_safe_mode_pallet() { ); assert_ok!(SafeMode::force_deactivate(Origin::signed(mock::ForceDeactivateOrigin::get()))); assert_ok!(SafeMode::slash_reservation( - Origin::signed(mock::RepayOrigin::get()), + Origin::signed(mock::ForceReservationOrigin::get()), 0, activated_at_block + 2 )); @@ -82,15 +82,15 @@ fn fails_to_extend_if_not_activated() { } #[test] -fn fails_to_release_reservations_with_wrong_block() { +fn fails_to_force_release_reservations_with_wrong_block() { new_test_ext().execute_with(|| { let activated_at_block = System::block_number(); assert_ok!(SafeMode::activate(Origin::signed(0))); - run_to(mock::SignedActivationDuration::get() + activated_at_block + 1); + run_to(mock::ActivationDuration::get() + activated_at_block + 1); assert_err!( - SafeMode::release_reservation( - Origin::signed(mock::RepayOrigin::get()), + SafeMode::force_release_reservation( + Origin::signed(mock::ForceReservationOrigin::get()), 0, activated_at_block + 1 ), @@ -99,7 +99,7 @@ fn fails_to_release_reservations_with_wrong_block() { assert_err!( SafeMode::slash_reservation( - Origin::signed(mock::RepayOrigin::get()), + Origin::signed(mock::ForceReservationOrigin::get()), 0, activated_at_block + 1 ), @@ -108,6 +108,21 @@ fn fails_to_release_reservations_with_wrong_block() { }); } +#[test] +fn fails_to_release_reservations_too_early() { + new_test_ext().execute_with(|| { + let activated_at_block = System::block_number(); + assert_ok!(SafeMode::activate(Origin::signed(0))); + assert_ok!(SafeMode::force_deactivate(Origin::signed(mock::ForceDeactivateOrigin::get()))); + assert_err!( + SafeMode::release_reservation(Origin::signed(2), 0, activated_at_block), + Error::::CannotReleaseYet + ); + run_to(activated_at_block + mock::ReleaseDelay::get() + 1); + assert_ok!(SafeMode::release_reservation(Origin::signed(2), 0, activated_at_block)); + }); +} + // GENERAL SUCCESS/POSITIVE TESTS --------------------- #[test] @@ -176,41 +191,12 @@ fn can_filter_balance_in_proxy_when_activated() { } #[test] -fn can_repay_independent_reservations_by_block() { - new_test_ext().execute_with(|| { - let activated_at_block_0 = System::block_number(); - assert_ok!(SafeMode::activate(Origin::signed(0))); - run_to(mock::SignedActivationDuration::get() + activated_at_block_0 + 1); - - let activated_at_block_1 = System::block_number(); - assert_ok!(SafeMode::activate(Origin::signed(0))); - run_to(mock::SignedActivationDuration::get() + activated_at_block_1 + 1); - - assert_ok!(SafeMode::release_reservation( - Origin::signed(mock::RepayOrigin::get()), - 0, - activated_at_block_0 - )); - assert_eq!(Balances::free_balance(&0), 1234 - mock::ActivateReservationAmount::get()); // accounts set in mock genesis - - assert_ok!(SafeMode::slash_reservation( - Origin::signed(mock::RepayOrigin::get()), - 0, - activated_at_block_1 - )); - assert_eq!(Balances::total_balance(&0), 1234 - mock::ActivateReservationAmount::get()); // accounts set in mock genesis - }); -} - -// SIGNED ORIGIN CALL TESTS --------------------- - -#[test] -fn can_activate_with_signed_origin() { +fn can_activate() { new_test_ext().execute_with(|| { assert_ok!(SafeMode::activate(Origin::signed(0))); assert_eq!( SafeMode::active_until().unwrap(), - System::block_number() + mock::SignedActivationDuration::get() + System::block_number() + mock::ActivationDuration::get() ); assert_eq!(Balances::reserved_balance(0), mock::ActivateReservationAmount::get()); assert_noop!(SafeMode::activate(Origin::signed(0)), Error::::IsActive); @@ -220,15 +206,13 @@ fn can_activate_with_signed_origin() { } #[test] -fn can_extend_with_signed_origin() { +fn can_extend() { new_test_ext().execute_with(|| { assert_ok!(SafeMode::activate(Origin::signed(0))); assert_ok!(SafeMode::extend(Origin::signed(0))); assert_eq!( SafeMode::active_until().unwrap(), - System::block_number() + - mock::SignedActivationDuration::get() + - mock::SignedExtendDuration::get() + System::block_number() + mock::ActivationDuration::get() + mock::ExtendDuration::get() ); assert_eq!( Balances::reserved_balance(0), @@ -237,6 +221,39 @@ fn can_extend_with_signed_origin() { }); } +#[test] +fn can_release_independent_reservations_by_block() { + new_test_ext().execute_with(|| { + let activated_at_block_0 = System::block_number(); + assert_ok!(SafeMode::activate(Origin::signed(0))); + + run_to( + mock::ActivationDuration::get() + mock::ReleaseDelay::get() + activated_at_block_0 + 1, + ); + + let activated_at_block_1 = System::block_number(); + assert_ok!(SafeMode::activate(Origin::signed(0))); + + assert_eq!(Balances::free_balance(&0), 1234 - (2 * mock::ActivateReservationAmount::get())); // accounts set in mock genesis + + assert_ok!(SafeMode::force_deactivate(Origin::signed(mock::ForceDeactivateOrigin::get()))); + + assert_ok!(SafeMode::release_reservation(Origin::signed(2), 0, activated_at_block_0)); + assert_err!( + SafeMode::release_reservation(Origin::signed(2), 0, activated_at_block_1), + Error::::CannotReleaseYet + ); + assert_eq!(Balances::free_balance(&0), 1234 - mock::ActivateReservationAmount::get()); // accounts set in mock genesis + + run_to( + mock::ActivationDuration::get() + mock::ReleaseDelay::get() + activated_at_block_1 + 1, + ); + + assert_ok!(SafeMode::release_reservation(Origin::signed(2), 0, activated_at_block_1)); + assert_eq!(Balances::total_balance(&0), 1234); // accounts set in mock genesis + }); +} + #[test] fn fails_signed_origin_when_explicit_origin_required() { new_test_ext().execute_with(|| { @@ -251,7 +268,7 @@ fn fails_signed_origin_when_explicit_origin_required() { DispatchError::BadOrigin ); assert_err!( - SafeMode::release_reservation(Origin::signed(0), 0, activated_at_block), + SafeMode::force_release_reservation(Origin::signed(0), 0, activated_at_block), DispatchError::BadOrigin ); }); @@ -320,27 +337,27 @@ fn can_force_extend_with_config_origin() { ForceExtendOrigin::Medium.duration() ); assert_eq!(Balances::reserved_balance(ForceActivateOrigin::Weak.acc()), 0); - assert_eq!(Balances::reserved_balance(mock::SignedExtendDuration::get()), 0); + assert_eq!(Balances::reserved_balance(mock::ExtendDuration::get()), 0); }); } #[test] -fn can_release_reservation_with_config_origin() { +fn can_force_release_reservation_with_config_origin() { new_test_ext().execute_with(|| { let activated_at_block = System::block_number(); assert_ok!(SafeMode::activate(Origin::signed(0))); assert_err!( - SafeMode::release_reservation( - Origin::signed(mock::RepayOrigin::get()), + SafeMode::force_release_reservation( + Origin::signed(mock::ForceReservationOrigin::get()), 0, activated_at_block ), Error::::IsActive ); - run_to(mock::SignedActivationDuration::get() + activated_at_block + 1); + run_to(mock::ActivationDuration::get() + activated_at_block + 1); - assert_ok!(SafeMode::release_reservation( - Origin::signed(mock::RepayOrigin::get()), + assert_ok!(SafeMode::force_release_reservation( + Origin::signed(mock::ForceReservationOrigin::get()), 0, activated_at_block )); @@ -351,14 +368,14 @@ fn can_release_reservation_with_config_origin() { assert_ok!(SafeMode::activate(Origin::signed(0))); assert_ok!(SafeMode::extend(Origin::signed(0))); run_to( - mock::SignedActivationDuration::get() + - mock::SignedExtendDuration::get() + + mock::ActivationDuration::get() + + mock::ExtendDuration::get() + activated_and_extended_at_block + 1, ); - assert_ok!(SafeMode::release_reservation( - Origin::signed(mock::RepayOrigin::get()), + assert_ok!(SafeMode::force_release_reservation( + Origin::signed(mock::ForceReservationOrigin::get()), 0, activated_and_extended_at_block )); @@ -373,16 +390,16 @@ fn can_slash_reservation_with_config_origin() { assert_ok!(SafeMode::activate(Origin::signed(0))); assert_err!( SafeMode::slash_reservation( - Origin::signed(mock::RepayOrigin::get()), + Origin::signed(mock::ForceReservationOrigin::get()), 0, activated_at_block ), Error::::IsActive ); - run_to(mock::SignedActivationDuration::get() + activated_at_block + 1); + run_to(mock::ActivationDuration::get() + activated_at_block + 1); assert_ok!(SafeMode::slash_reservation( - Origin::signed(mock::RepayOrigin::get()), + Origin::signed(mock::ForceReservationOrigin::get()), 0, activated_at_block )); @@ -393,14 +410,14 @@ fn can_slash_reservation_with_config_origin() { assert_ok!(SafeMode::activate(Origin::signed(0))); assert_ok!(SafeMode::extend(Origin::signed(0))); run_to( - mock::SignedActivationDuration::get() + - mock::SignedExtendDuration::get() + + mock::ActivationDuration::get() + + mock::ExtendDuration::get() + activated_and_extended_at_block + 1, ); assert_ok!(SafeMode::slash_reservation( - Origin::signed(mock::RepayOrigin::get()), + Origin::signed(mock::ForceReservationOrigin::get()), 0, activated_and_extended_at_block )); @@ -430,7 +447,7 @@ fn fails_when_explicit_origin_required() { DispatchError::BadOrigin ); assert_err!( - SafeMode::release_reservation( + SafeMode::force_release_reservation( ForceActivateOrigin::Weak.signed(), 0, activated_at_block @@ -451,7 +468,11 @@ fn fails_when_explicit_origin_required() { DispatchError::BadOrigin ); assert_err!( - SafeMode::release_reservation(ForceExtendOrigin::Weak.signed(), 0, activated_at_block), + SafeMode::force_release_reservation( + ForceExtendOrigin::Weak.signed(), + 0, + activated_at_block + ), DispatchError::BadOrigin ); @@ -472,7 +493,7 @@ fn fails_when_explicit_origin_required() { DispatchError::BadOrigin ); assert_err!( - SafeMode::release_reservation( + SafeMode::force_release_reservation( Origin::signed(mock::ForceDeactivateOrigin::get()), 0, activated_at_block @@ -481,15 +502,15 @@ fn fails_when_explicit_origin_required() { ); assert_err!( - SafeMode::force_activate(Origin::signed(mock::RepayOrigin::get())), + SafeMode::force_activate(Origin::signed(mock::ForceReservationOrigin::get())), DispatchError::BadOrigin ); assert_err!( - SafeMode::force_extend(Origin::signed(mock::RepayOrigin::get())), + SafeMode::force_extend(Origin::signed(mock::ForceReservationOrigin::get())), DispatchError::BadOrigin ); assert_err!( - SafeMode::force_deactivate(Origin::signed(mock::RepayOrigin::get())), + SafeMode::force_deactivate(Origin::signed(mock::ForceReservationOrigin::get())), DispatchError::BadOrigin ); }); diff --git a/frame/safe-mode/src/weights.rs b/frame/safe-mode/src/weights.rs index 034dff48bcf29..2acef30841642 100644 --- a/frame/safe-mode/src/weights.rs +++ b/frame/safe-mode/src/weights.rs @@ -18,7 +18,7 @@ //! Autogenerated weights for pallet_safe_mode //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-10-12, STEPS: `1`, REPEAT: 1, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2022-10-13, STEPS: `1`, REPEAT: 1, LOW RANGE: `[]`, HIGH RANGE: `[]` //! HOSTNAME: `pop-os`, CPU: `AMD Ryzen 7 1800X Eight-Core Processor` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 @@ -60,6 +60,7 @@ pub trait WeightInfo { fn extend() -> Weight; fn force_extend() -> Weight; fn force_deactivate() -> Weight; + fn force_release_reservation() -> Weight; fn release_reservation() -> Weight; fn slash_reservation() -> Weight; } @@ -71,13 +72,13 @@ impl WeightInfo for SubstrateWeight { // Storage: SafeMode Reservations (r:1 w:1) // Storage: SafeMode ActiveUntil (r:1 w:1) fn activate() -> Weight { - Weight::from_ref_time(83_536_000 as u64) + Weight::from_ref_time(95_729_000 as u64) .saturating_add(T::DbWeight::get().reads(3 as u64)) .saturating_add(T::DbWeight::get().writes(3 as u64)) } // Storage: SafeMode ActiveUntil (r:1 w:1) fn force_activate() -> Weight { - Weight::from_ref_time(44_232_000 as u64) + Weight::from_ref_time(50_724_000 as u64) .saturating_add(T::DbWeight::get().reads(1 as u64)) .saturating_add(T::DbWeight::get().writes(1 as u64)) } @@ -85,27 +86,35 @@ impl WeightInfo for SubstrateWeight { // Storage: SafeMode Reservations (r:1 w:1) // Storage: SafeMode ActiveUntil (r:1 w:1) fn extend() -> Weight { - Weight::from_ref_time(85_289_000 as u64) + Weight::from_ref_time(87_614_000 as u64) .saturating_add(T::DbWeight::get().reads(3 as u64)) .saturating_add(T::DbWeight::get().writes(3 as u64)) } // Storage: SafeMode ActiveUntil (r:1 w:1) fn force_extend() -> Weight { - Weight::from_ref_time(42_209_000 as u64) + Weight::from_ref_time(46_246_000 as u64) .saturating_add(T::DbWeight::get().reads(1 as u64)) .saturating_add(T::DbWeight::get().writes(1 as u64)) } // Storage: SafeMode ActiveUntil (r:1 w:1) fn force_deactivate() -> Weight { - Weight::from_ref_time(37_661_000 as u64) + Weight::from_ref_time(37_891_000 as u64) .saturating_add(T::DbWeight::get().reads(1 as u64)) .saturating_add(T::DbWeight::get().writes(1 as u64)) } // Storage: SafeMode ActiveUntil (r:1 w:0) // Storage: SafeMode Reservations (r:1 w:1) // Storage: Balances Reserves (r:1 w:1) + fn force_release_reservation() -> Weight { + Weight::from_ref_time(80_290_000 as u64) + .saturating_add(T::DbWeight::get().reads(3 as u64)) + .saturating_add(T::DbWeight::get().writes(2 as u64)) + } + // Storage: SafeMode ActiveUntil (r:1 w:0) + // Storage: SafeMode Reservations (r:1 w:1) + // Storage: Balances Reserves (r:1 w:1) fn release_reservation() -> Weight { - Weight::from_ref_time(76_964_000 as u64) + Weight::from_ref_time(80_290_000 as u64) .saturating_add(T::DbWeight::get().reads(3 as u64)) .saturating_add(T::DbWeight::get().writes(2 as u64)) } @@ -113,7 +122,7 @@ impl WeightInfo for SubstrateWeight { // Storage: SafeMode Reservations (r:1 w:1) // Storage: Balances Reserves (r:1 w:1) fn slash_reservation() -> Weight { - Weight::from_ref_time(88_074_000 as u64) + Weight::from_ref_time(110_417_000 as u64) .saturating_add(T::DbWeight::get().reads(3 as u64)) .saturating_add(T::DbWeight::get().writes(2 as u64)) } @@ -125,13 +134,13 @@ impl WeightInfo for () { // Storage: SafeMode Reservations (r:1 w:1) // Storage: SafeMode ActiveUntil (r:1 w:1) fn activate() -> Weight { - Weight::from_ref_time(83_536_000 as u64) + Weight::from_ref_time(95_729_000 as u64) .saturating_add(RocksDbWeight::get().reads(3 as u64)) .saturating_add(RocksDbWeight::get().writes(3 as u64)) } // Storage: SafeMode ActiveUntil (r:1 w:1) fn force_activate() -> Weight { - Weight::from_ref_time(44_232_000 as u64) + Weight::from_ref_time(50_724_000 as u64) .saturating_add(RocksDbWeight::get().reads(1 as u64)) .saturating_add(RocksDbWeight::get().writes(1 as u64)) } @@ -139,27 +148,35 @@ impl WeightInfo for () { // Storage: SafeMode Reservations (r:1 w:1) // Storage: SafeMode ActiveUntil (r:1 w:1) fn extend() -> Weight { - Weight::from_ref_time(85_289_000 as u64) + Weight::from_ref_time(87_614_000 as u64) .saturating_add(RocksDbWeight::get().reads(3 as u64)) .saturating_add(RocksDbWeight::get().writes(3 as u64)) } // Storage: SafeMode ActiveUntil (r:1 w:1) fn force_extend() -> Weight { - Weight::from_ref_time(42_209_000 as u64) + Weight::from_ref_time(46_246_000 as u64) .saturating_add(RocksDbWeight::get().reads(1 as u64)) .saturating_add(RocksDbWeight::get().writes(1 as u64)) } // Storage: SafeMode ActiveUntil (r:1 w:1) fn force_deactivate() -> Weight { - Weight::from_ref_time(37_661_000 as u64) + Weight::from_ref_time(37_891_000 as u64) .saturating_add(RocksDbWeight::get().reads(1 as u64)) .saturating_add(RocksDbWeight::get().writes(1 as u64)) } // Storage: SafeMode ActiveUntil (r:1 w:0) // Storage: SafeMode Reservations (r:1 w:1) + // Storage: Balances Reserves (r:1 w:1) + fn force_release_reservation() -> Weight { + Weight::from_ref_time(80_290_000 as u64) + .saturating_add(RocksDbWeight::get().reads(3 as u64)) + .saturating_add(RocksDbWeight::get().writes(2 as u64)) + } + // Storage: SafeMode ActiveUntil (r:1 w:0) +// Storage: SafeMode Reservations (r:1 w:1) // Storage: Balances Reserves (r:1 w:1) fn release_reservation() -> Weight { - Weight::from_ref_time(76_964_000 as u64) + Weight::from_ref_time(80_290_000 as u64) .saturating_add(RocksDbWeight::get().reads(3 as u64)) .saturating_add(RocksDbWeight::get().writes(2 as u64)) } @@ -167,7 +184,7 @@ impl WeightInfo for () { // Storage: SafeMode Reservations (r:1 w:1) // Storage: Balances Reserves (r:1 w:1) fn slash_reservation() -> Weight { - Weight::from_ref_time(88_074_000 as u64) + Weight::from_ref_time(110_417_000 as u64) .saturating_add(RocksDbWeight::get().reads(3 as u64)) .saturating_add(RocksDbWeight::get().writes(2 as u64)) } From 9c46c46dab3f066de4a5e8fc96aadf2a8c73970e Mon Sep 17 00:00:00 2001 From: Dan Shields Date: Sat, 15 Oct 2022 13:37:10 -0600 Subject: [PATCH 027/100] make call name optional to allow pausing pallets, handle `Contains` correctly for this throughout, doc comments started --- bin/node/runtime/src/lib.rs | 16 +-- frame/safe-mode/src/lib.rs | 4 +- frame/safe-mode/src/mock.rs | 6 +- frame/tx-pause/src/benchmarking.rs | 24 ++--- frame/tx-pause/src/lib.rs | 159 +++++++++++++++++++++++------ frame/tx-pause/src/mock.rs | 18 ++-- frame/tx-pause/src/tests.rs | 89 ++++++++++++---- frame/tx-pause/src/weights.rs | 12 +-- 8 files changed, 234 insertions(+), 94 deletions(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 7a8b8b77ba157..60e1bc447f607 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -203,8 +203,8 @@ const_assert!(NORMAL_DISPATCH_RATIO.deconstruct() >= AVERAGE_ON_INITIALIZE_RATIO /// Filter to block balance pallet calls /// Used for both SafeMode and TxPause pallets /// Therefor we include both so they cannot affect each other -pub struct UnfilterableCalls; -impl Contains for UnfilterableCalls { +pub struct WhitelistCalls; +impl Contains for WhitelistCalls { fn contains(call: &RuntimeCall) -> bool { match call { RuntimeCall::System(_) | RuntimeCall::SafeMode(_) | RuntimeCall::TxPause(_) => true, @@ -215,9 +215,9 @@ impl Contains for UnfilterableCalls { } use pallet_tx_pause::FullNameOf; -pub struct UnfilterableCallNames; -/// Make Balances::transfer_keep_alive unfilterable, accept all others. -impl Contains> for UnfilterableCallNames { +pub struct WhitelistCallNames; +/// Whitelist `Balances::transfer_keep_alive`, all others are pauseable. +impl Contains> for WhitelistCallNames { fn contains(full_name: &FullNameOf) -> bool { let unpausables: Vec> = vec![( b"Balances".to_vec().try_into().unwrap(), @@ -243,7 +243,7 @@ impl pallet_tx_pause::Config for Runtime { type RuntimeCall = RuntimeCall; type PauseOrigin = EnsureRoot; type UnpauseOrigin = EnsureRoot; - type UnfilterableCallNames = UnfilterableCallNames; + type WhitelistCallNames = WhitelistCallNames; type MaxNameLen = ConstU32<256>; type PauseTooLongNames = ConstBool; type WeightInfo = pallet_tx_pause::weights::SubstrateWeight; @@ -370,7 +370,7 @@ parameter_types! { impl pallet_safe_mode::Config for Runtime { type RuntimeEvent = RuntimeEvent; type Currency = Balances; - type UnfilterableCalls = UnfilterableCalls; + type WhitelistCalls = WhitelistCalls; type ActivationDuration = ConstU32<{ 2 * DAYS }>; type ActivateReservationAmount = ActivateReservationAmount; type ExtendDuration = ConstU32<{ 1 * DAYS }>; @@ -384,7 +384,7 @@ impl pallet_safe_mode::Config for Runtime { } impl frame_system::Config for Runtime { - type BaseCallFilter = InsideBoth; // TODO consider Exclude or NotInside for UnfilterableCalls -> see TheseExcept ) + type BaseCallFilter = InsideBoth; // TODO consider Exclude or NotInside for WhitelistCalls -> see TheseExcept ) type BlockWeights = RuntimeBlockWeights; type BlockLength = RuntimeBlockLength; type DbWeight = RocksDbWeight; diff --git a/frame/safe-mode/src/lib.rs b/frame/safe-mode/src/lib.rs index 85ae3d23c99d3..d6d79e18e5564 100644 --- a/frame/safe-mode/src/lib.rs +++ b/frame/safe-mode/src/lib.rs @@ -66,7 +66,7 @@ pub mod pallet { /// /// The safe-mode pallet cannot disable it's own calls, and does not need to be explicitly /// added here. - type UnfilterableCalls: Contains; + type WhitelistCalls: Contains; /// How long the safe-mode will stay active with [`Pallet::activate`]. #[pallet::constant] @@ -504,7 +504,7 @@ impl Pallet { } if Self::is_active() { - T::UnfilterableCalls::contains(call) + T::WhitelistCalls::contains(call) } else { true } diff --git a/frame/safe-mode/src/mock.rs b/frame/safe-mode/src/mock.rs index a51628ceea819..098b25479bc97 100644 --- a/frame/safe-mode/src/mock.rs +++ b/frame/safe-mode/src/mock.rs @@ -137,8 +137,8 @@ impl pallet_proxy::Config for Test { } /// Filter to allow all everything except balance calls -pub struct UnfilterableCalls; -impl Contains for UnfilterableCalls { +pub struct WhitelistCalls; +impl Contains for WhitelistCalls { fn contains(call: &RuntimeCall) -> bool { match call { RuntimeCall::Balances(_) => false, @@ -286,7 +286,7 @@ impl SortedMembers for ForceReservationOrigin { impl Config for Test { type RuntimeEvent = RuntimeEvent; type Currency = Balances; - type UnfilterableCalls = UnfilterableCalls; + type WhitelistCalls = WhitelistCalls; type ActivationDuration = ActivationDuration; type ActivateReservationAmount = ActivateReservationAmount; type ExtendDuration = ExtendDuration; diff --git a/frame/tx-pause/src/benchmarking.rs b/frame/tx-pause/src/benchmarking.rs index 6ee76448130d6..ed1c21946e0b2 100644 --- a/frame/tx-pause/src/benchmarking.rs +++ b/frame/tx-pause/src/benchmarking.rs @@ -22,35 +22,35 @@ use super::{Pallet as TxPause, *}; use frame_benchmarking::benchmarks; benchmarks! { - pause_call { + pause { let pallet_name: PalletNameOf = b"SomePalletName".to_vec().try_into().unwrap(); - let call_name: CallNameOf = b"some_call_name".to_vec().try_into().unwrap(); + let maybe_call_name: Option> = Some(b"some_call_name".to_vec().try_into().unwrap()); let origin = T::PauseOrigin::successful_origin(); - let call = Call::::pause_call { pallet_name: pallet_name.clone(), call_name: call_name.clone() }; + let call = Call::::pause { pallet_name: pallet_name.clone(), maybe_call_name: maybe_call_name.clone() }; - }: _(origin, pallet_name.clone(), call_name.clone()) + }: _(origin, pallet_name.clone(), maybe_call_name.clone()) verify { - assert!(TxPause::::paused_calls((pallet_name.clone(), call_name.clone())).is_some()) + assert!(TxPause::::paused_calls((pallet_name.clone(), maybe_call_name.clone())).is_some()) } - unpause_call { + unpause { let pallet_name: PalletNameOf = b"SomePalletName".to_vec().try_into().unwrap(); - let call_name: CallNameOf = b"some_call_name".to_vec().try_into().unwrap(); + let maybe_call_name: Option> = Some(b"some_call_name".to_vec().try_into().unwrap()); let pause_origin = T::PauseOrigin::successful_origin(); // Set - TxPause::::pause_call( + TxPause::::pause( pause_origin, pallet_name.clone(), - call_name.clone(), + maybe_call_name.clone(), )?; let unpause_origin = T::UnpauseOrigin::successful_origin(); - let call = Call::::unpause_call { pallet_name: pallet_name.clone(), call_name: call_name.clone() }; + let call = Call::::unpause { pallet_name: pallet_name.clone(), maybe_call_name: maybe_call_name.clone() }; - }: _(unpause_origin, pallet_name.clone(), call_name.clone()) + }: _(unpause_origin, pallet_name.clone(), maybe_call_name.clone()) verify { - assert!(TxPause::::paused_calls((pallet_name.clone(), call_name.clone())).is_none()) + assert!(TxPause::::paused_calls((pallet_name.clone(), maybe_call_name.clone())).is_none()) } impl_benchmark_test_suite!(TxPause, crate::mock::new_test_ext(), crate::mock::Test); diff --git a/frame/tx-pause/src/lib.rs b/frame/tx-pause/src/lib.rs index 0808d2ab5240a..48304f7bae602 100644 --- a/frame/tx-pause/src/lib.rs +++ b/frame/tx-pause/src/lib.rs @@ -15,6 +15,67 @@ // See the License for the specific language governing permissions and // limitations under the License. +//! # Transaction Pause Pallet +//! +//! The Transaction Pause pallet provides a dynamic call filter that can be controlled with +//! extrinsics. This pallet may be used to disable dispatch of specific calls within a runtime. +//! +//! - [`Config`] +//! - [`Call`] +//! - [`Pallet`] +//! +//! ## Overview +//! +//! The Transaction Pause pallet provides functions for: +//! +//! - Setting a dynamic list of [`FullNameOf`] items that are matched against to filter these calls. +//! - Setting [`Config::WhitelistCallNames`] that cannot be paused by this pallet. +//! - Repatriating a reserved balance to a beneficiary account that exists. +//! - Transferring a balance between accounts (when not reserved). +//! - Slashing an account balance. +//! - Account creation and removal. +//! - Managing total issuance. +//! - Setting and managing locks. +//! +//! Can also be used as call-filter by the runtime together with the SafeMode +//! +//! TODO expand an link to safe mode in docs. +//! +//! ### Terminology +//! +//! - **Pause**: The ability to dispatch of a specific call becomes disabled. +//! - **Unpause**: The ability to dispatch of a specific call becomes enabled, if it was paused. +//! +//! ## Interface +//! +//! ### Dispatchable Functions +//! +//! - `pause` - Pause a pallet or optionally a specific call within a pallet. +//! - `unpause` - Unpause a pallet or optionally a specific call within a pallet. +//! +//! ## Usage +//! +//! The following examples show how to use the Transaction Pause pallet in your custom pallet. +//! TODO check doc links +//! ### Example within a runtime's [`pallet-frame-system::BaseCallFilter`] configuration: +//! +//! ```ignore +//! impl frame_system::Config for Runtime { +//! … +//! type BaseCallFilter = InsideBoth>; +//! … +//! } +//! ``` +//! +//! ## Genesis config +//! +//! The Transaction Pause pallet may be configured to pause calls on genesis with it's +//! [`GenesisConfig`]. +//! +//! ## Assumptions +//! +//! * TODO + #![cfg_attr(not(feature = "std"), no_std)] mod benchmarking; @@ -36,8 +97,13 @@ use sp_std::{convert::TryInto, prelude::*}; pub use pallet::*; pub use weights::*; +/// The stringy name of a pallet from [`GetCallMetadata`] for [`Config::RuntimeCall`] variants. pub type PalletNameOf = BoundedVec::MaxNameLen>; +/// The stringy name of a call (within a pallet) from [`GetCallMetadata`] for +/// [`Config::RuntimeCall`] variants. pub type CallNameOf = BoundedVec::MaxNameLen>; +/// A sully specified pallet ([`PalletNameOf`]) and optional call ([`CallNameOf`]) +/// to partially or fully specify an item a variant of a [`Config::RuntimeCall`]. pub type FullNameOf = (PalletNameOf, Option>); #[frame_support::pallet] @@ -72,7 +138,7 @@ pub mod pallet { /// /// The `TxMode` pallet cannot pause it's own calls, and does not need to be explicitly /// added here. - type UnfilterableCallNames: Contains>; + type WhitelistCallNames: Contains>; /// Maximum length for pallet and call SCALE encoded string names. /// @@ -105,15 +171,19 @@ pub mod pallet { /// The call is listed as safe and cannot be paused. IsUnpausable, - // The call does not exist in the runtime. - NoSuchCall, + // The pallet or call does not exist in the runtime. + NotFound, } #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event { + /// This pallet's calls are now paused. \[pallet_name\] + PalletPaused { pallet_name: PalletNameOf }, /// This call is now paused. \[pallet_name, call_name\] CallPaused { pallet_name: PalletNameOf, call_name: CallNameOf }, + /// This pallet's calls are now unpaused. \[pallet_name\] + PalletUnpaused { pallet_name: PalletNameOf }, /// This call is now unpaused. \[pallet_name, call_name\] CallUnpaused { pallet_name: PalletNameOf, call_name: CallNameOf }, } @@ -122,13 +192,13 @@ pub mod pallet { #[pallet::storage] #[pallet::getter(fn paused_calls)] pub type PausedCalls = - StorageMap<_, Blake2_128Concat, (PalletNameOf, CallNameOf), (), OptionQuery>; + StorageMap<_, Blake2_128Concat, (PalletNameOf, Option>), (), OptionQuery>; /// Configure the initial state of this pallet in the genesis block. #[pallet::genesis_config] pub struct GenesisConfig { /// The initially paused calls. - pub paused: Vec<(PalletNameOf, CallNameOf)>, + pub paused: Vec<(PalletNameOf, Option>)>, pub _phantom: PhantomData, } @@ -144,8 +214,8 @@ pub mod pallet { #[pallet::genesis_build] impl GenesisBuild for GenesisConfig { fn build(&self) { - for (pallet_name, call_name) in &self.paused { - PausedCalls::::insert((pallet_name, call_name), ()); + for (pallet_name, maybe_call_name) in &self.paused { + PausedCalls::::insert((pallet_name, maybe_call_name), ()); } } } @@ -156,17 +226,21 @@ pub mod pallet { /// /// Can only be called by [`Config::PauseOrigin`]. /// Emits an [`Event::CallPaused`] event on success. - #[pallet::weight(T::WeightInfo::pause_call())] - pub fn pause_call( + #[pallet::weight(T::WeightInfo::pause())] + pub fn pause( origin: OriginFor, pallet_name: PalletNameOf, - call_name: CallNameOf, + maybe_call_name: Option>, ) -> DispatchResult { T::PauseOrigin::ensure_origin(origin)?; - Self::ensure_can_pause(&pallet_name, &call_name)?; - PausedCalls::::insert((&pallet_name, &call_name), ()); - Self::deposit_event(Event::CallPaused { pallet_name, call_name }); + Self::ensure_can_pause(&pallet_name, &maybe_call_name)?; + PausedCalls::::insert((&pallet_name, &maybe_call_name), ()); + if let Some(call_name) = maybe_call_name { + Self::deposit_event(Event::CallPaused { pallet_name, call_name }); + } else { + Self::deposit_event(Event::PalletPaused { pallet_name }); + } Ok(()) } @@ -175,17 +249,21 @@ pub mod pallet { /// /// Can only be called by [`Config::UnpauseOrigin`]. /// Emits an [`Event::CallUnpaused`] event on success. - #[pallet::weight(T::WeightInfo::unpause_call())] - pub fn unpause_call( + #[pallet::weight(T::WeightInfo::unpause())] + pub fn unpause( origin: OriginFor, pallet_name: PalletNameOf, - call_name: CallNameOf, + maybe_call_name: Option>, ) -> DispatchResult { T::UnpauseOrigin::ensure_origin(origin)?; - Self::ensure_can_unpause(&pallet_name, &call_name)?; - PausedCalls::::remove((&pallet_name, &call_name)); - Self::deposit_event(Event::CallUnpaused { pallet_name, call_name }); + Self::ensure_can_unpause(&pallet_name, &maybe_call_name)?; + PausedCalls::::remove((&pallet_name, &maybe_call_name)); + if let Some(call_name) = maybe_call_name { + Self::deposit_event(Event::CallUnpaused { pallet_name, call_name }); + } else { + Self::deposit_event(Event::PalletUnpaused { pallet_name }); + } Ok(()) } @@ -193,47 +271,62 @@ pub mod pallet { } impl Pallet { - /// Return whether this call is paused. + /// Return whether this pallet or call is paused. pub fn is_paused_unbound(pallet_name: Vec, call_name: Vec) -> bool { let pallet_name = PalletNameOf::::try_from(pallet_name); let call_name = CallNameOf::::try_from(call_name); match (pallet_name, call_name) { - (Ok(pallet_name), Ok(call_name)) => Self::is_paused(&pallet_name, &call_name), + (Ok(pallet_name), Ok(call_name)) => Self::is_paused(&pallet_name, &Some(call_name)), _ => T::PauseTooLongNames::get(), } } - /// Return whether this call is paused. - pub fn is_paused(pallet_name: &PalletNameOf, call_name: &CallNameOf) -> bool { - >::contains_key((pallet_name, call_name)) + /// Return whether this pallet or call is paused. + pub fn is_paused( + pallet_name: &PalletNameOf, + maybe_call_name: &Option>, + ) -> bool { + if >::contains_key(>::from((pallet_name.clone(), None))) { + if T::WhitelistCallNames::contains(&>::from(( + pallet_name.clone(), + maybe_call_name.clone(), + ))) { + return false + } + return true + }; + >::contains_key(>::from(( + pallet_name.clone(), + maybe_call_name.clone(), + ))) } - /// Ensure that this call can be paused. + /// Ensure that this pallet or call can be paused. pub fn ensure_can_pause( pallet_name: &PalletNameOf, - call_name: &CallNameOf, + maybe_call_name: &Option>, ) -> Result<(), Error> { // The `TxPause` pallet can never be paused. if pallet_name.as_ref() == ::name().as_bytes().to_vec() { return Err(Error::::IsUnpausable) } - let full_name: FullNameOf = (pallet_name.clone(), Some(call_name.clone())); - if T::UnfilterableCallNames::contains(&full_name) { - return Err(Error::::IsUnpausable) - } - if Self::is_paused(&pallet_name, &call_name) { + if Self::is_paused(&pallet_name.clone(), &maybe_call_name.clone()) { return Err(Error::::IsPaused) } + let full_name: FullNameOf = (pallet_name.clone(), maybe_call_name.clone()); + if T::WhitelistCallNames::contains(&full_name) { + return Err(Error::::IsUnpausable) + } Ok(()) } /// Ensure that this call can be un-paused. pub fn ensure_can_unpause( pallet_name: &PalletNameOf, - call_name: &CallNameOf, + maybe_call_name: &Option>, ) -> Result<(), Error> { - if Self::is_paused(&pallet_name, &call_name) { + if Self::is_paused(&pallet_name, &maybe_call_name) { // SAFETY: Everything that is paused, can be un-paused. Ok(()) } else { diff --git a/frame/tx-pause/src/mock.rs b/frame/tx-pause/src/mock.rs index d7fe7af6cede4..e54ae3823ae96 100644 --- a/frame/tx-pause/src/mock.rs +++ b/frame/tx-pause/src/mock.rs @@ -144,10 +144,11 @@ parameter_types! { } #[derive(Copy, Clone, Encode, Decode, RuntimeDebug, MaxEncodedLen, scale_info::TypeInfo)] -pub struct UnfilterableCallNames; +pub struct WhitelistCallNames; -/// Make Balances::transfer_keep_alive and all DummyPallet calls unfilterable, accept all others. -impl Contains> for UnfilterableCallNames { +/// Contains used by `BaseCallFiler` so this impl whitelists `Balances::transfer_keep_alive` +/// and all DummyPallet calls. All others may be paused. +impl Contains> for WhitelistCallNames { fn contains(full_name: &FullNameOf) -> bool { let unpausables: Vec> = vec![ ( @@ -159,11 +160,12 @@ impl Contains> for UnfilterableCallNames { for unpausable_call in unpausables { let (pallet_name, maybe_call_name) = full_name; - if pallet_name == &unpausable_call.0 { - if unpausable_call.1.is_none() { - return true + if unpausable_call.1.is_none() { + return &unpausable_call.0 == pallet_name + } else { + if &unpausable_call.0 == pallet_name { + return &unpausable_call.1 == maybe_call_name } - return maybe_call_name == &unpausable_call.1 } } @@ -192,7 +194,7 @@ impl Config for Test { type RuntimeCall = RuntimeCall; type PauseOrigin = EnsureSignedBy; type UnpauseOrigin = EnsureSignedBy; - type UnfilterableCallNames = UnfilterableCallNames; + type WhitelistCallNames = WhitelistCallNames; type MaxNameLen = MaxNameLen; type PauseTooLongNames = PauseTooLongNames; type WeightInfo = (); diff --git a/frame/tx-pause/src/tests.rs b/frame/tx-pause/src/tests.rs index 493de946492e6..4e786f2aa3665 100644 --- a/frame/tx-pause/src/tests.rs +++ b/frame/tx-pause/src/tests.rs @@ -29,10 +29,10 @@ fn can_pause_specific_call() { new_test_ext().execute_with(|| { assert_ok!(call_transfer(1, 1).dispatch(Origin::signed(0))); - assert_ok!(TxPause::pause_call( + assert_ok!(TxPause::pause( Origin::signed(mock::PauseOrigin::get()), name(b"Balances"), - name(b"transfer"), + Some(name(b"transfer")), )); assert_err!( @@ -43,23 +43,45 @@ fn can_pause_specific_call() { }); } +#[test] +fn can_pause_all_calls_in_pallet_except_on_whitelist() { + new_test_ext().execute_with(|| { + assert_ok!(call_transfer(1, 1).dispatch(Origin::signed(0))); + + let batch_call = + RuntimeCall::Utility(pallet_utility::Call::batch { calls: vec![call_transfer(1, 1)] }); + assert_ok!(batch_call.clone().dispatch(Origin::signed(0))); + + assert_ok!(TxPause::pause( + Origin::signed(mock::PauseOrigin::get()), + name(b"Utility"), + None, + )); + + assert_err!( + batch_call.clone().dispatch(Origin::signed(0)), + frame_system::Error::::CallFiltered + ); + }); +} + #[test] fn can_unpause_specific_call() { new_test_ext().execute_with(|| { - assert_ok!(TxPause::pause_call( + assert_ok!(TxPause::pause( Origin::signed(mock::PauseOrigin::get()), name(b"Balances"), - name(b"transfer"), + Some(name(b"transfer")), )); assert_err!( call_transfer(2, 1).dispatch(Origin::signed(2)), frame_system::Error::::CallFiltered ); - assert_ok!(TxPause::unpause_call( + assert_ok!(TxPause::unpause( Origin::signed(mock::UnpauseOrigin::get()), name(b"Balances"), - name(b"transfer"), + Some(name(b"transfer")), )); assert_ok!(call_transfer(4, 1).dispatch(Origin::signed(0))); }); @@ -71,10 +93,10 @@ fn can_filter_balance_in_batch_when_paused() { let batch_call = RuntimeCall::Utility(pallet_utility::Call::batch { calls: vec![call_transfer(1, 1)] }); - assert_ok!(TxPause::pause_call( + assert_ok!(TxPause::pause( Origin::signed(mock::PauseOrigin::get()), name(b"Balances"), - name(b"transfer"), + Some(name(b"transfer")), )); assert_ok!(batch_call.clone().dispatch(Origin::signed(0))); @@ -91,10 +113,10 @@ fn can_filter_balance_in_batch_when_paused() { #[test] fn can_filter_balance_in_proxy_when_paused() { new_test_ext().execute_with(|| { - assert_ok!(TxPause::pause_call( + assert_ok!(TxPause::pause( Origin::signed(mock::PauseOrigin::get()), name(b"Balances"), - name(b"transfer"), + Some(name(b"transfer")), )); assert_ok!(Proxy::add_proxy(Origin::signed(1), 2, ProxyType::JustTransfer, 0)); @@ -115,10 +137,10 @@ fn can_filter_balance_in_proxy_when_paused() { fn fails_to_pause_self() { new_test_ext().execute_with(|| { assert_noop!( - TxPause::pause_call( + TxPause::pause( Origin::signed(mock::PauseOrigin::get()), name(b"TxPause"), - name(b"pause_call") + Some(name(b"pause")), ), Error::::IsUnpausable ); @@ -129,24 +151,47 @@ fn fails_to_pause_self() { fn fails_to_pause_unpausable_pallet() { new_test_ext().execute_with(|| { assert_noop!( - TxPause::pause_call( + TxPause::pause( Origin::signed(mock::PauseOrigin::get()), name(b"DummyPallet"), - name(b"any-call") + Some(name(b"any-call")), ), Error::::IsUnpausable ); }); } +#[test] +fn fails_to_pause_unpausable_call_when_pallet_is_paused() { + new_test_ext().execute_with(|| { + assert_ok!(call_transfer(1, 1).dispatch(Origin::signed(0))); + + let batch_call = + RuntimeCall::Utility(pallet_utility::Call::batch { calls: vec![call_transfer(1, 1)] }); + assert_ok!(batch_call.clone().dispatch(Origin::signed(0))); + + assert_ok!(TxPause::pause( + Origin::signed(mock::PauseOrigin::get()), + name(b"Balances"), + None, + )); + + assert_ok!(call_transfer_keep_alive(3, 1).dispatch(Origin::signed(3))); + assert_err!( + call_transfer(2, 1).dispatch(Origin::signed(0)), + frame_system::Error::::CallFiltered + ); + }); +} + #[test] fn fails_to_pause_unpausable_call() { new_test_ext().execute_with(|| { assert_noop!( - TxPause::pause_call( + TxPause::pause( Origin::signed(mock::PauseOrigin::get()), name(b"Balances"), - name(b"transfer_keep_alive"), + Some(name(b"transfer_keep_alive")), ), Error::::IsUnpausable ); @@ -156,17 +201,17 @@ fn fails_to_pause_unpausable_call() { #[test] fn fails_to_pause_already_paused_pallet() { new_test_ext().execute_with(|| { - assert_ok!(TxPause::pause_call( + assert_ok!(TxPause::pause( Origin::signed(mock::PauseOrigin::get()), name(b"Balances"), - name(b"transfer"), + Some(name(b"transfer")), )); assert_noop!( - TxPause::pause_call( + TxPause::pause( Origin::signed(mock::PauseOrigin::get()), name(b"Balances"), - name(b"transfer"), + Some(name(b"transfer")), ), Error::::IsPaused ); @@ -177,10 +222,10 @@ fn fails_to_pause_already_paused_pallet() { fn fails_to_unpause_not_paused_pallet() { new_test_ext().execute_with(|| { assert_noop!( - TxPause::unpause_call( + TxPause::unpause( Origin::signed(mock::UnpauseOrigin::get()), name(b"Balances"), - name(b"transfer"), + Some(name(b"transfer")), ), Error::::IsUnpaused ); diff --git a/frame/tx-pause/src/weights.rs b/frame/tx-pause/src/weights.rs index bd1d7de74c64b..21fee4495f4d8 100644 --- a/frame/tx-pause/src/weights.rs +++ b/frame/tx-pause/src/weights.rs @@ -55,21 +55,21 @@ use sp_std::marker::PhantomData; /// Weight functions needed for pallet_tx_pause. pub trait WeightInfo { - fn pause_call() -> Weight; - fn unpause_call() -> Weight; + fn pause() -> Weight; + fn unpause() -> Weight; } /// Weights for pallet_tx_pause using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { // Storage: TxPause PausedCalls (r:1 w:1) - fn pause_call() -> Weight { + fn pause() -> Weight { Weight::from_ref_time(38_905_000 as u64) .saturating_add(T::DbWeight::get().reads(1 as u64)) .saturating_add(T::DbWeight::get().writes(1 as u64)) } // Storage: TxPause PausedCalls (r:1 w:1) - fn unpause_call() -> Weight { + fn unpause() -> Weight { Weight::from_ref_time(32_892_000 as u64) .saturating_add(T::DbWeight::get().reads(1 as u64)) .saturating_add(T::DbWeight::get().writes(1 as u64)) @@ -79,13 +79,13 @@ impl WeightInfo for SubstrateWeight { // For backwards compatibility and tests impl WeightInfo for () { // Storage: TxPause PausedCalls (r:1 w:1) - fn pause_call() -> Weight { + fn pause() -> Weight { Weight::from_ref_time(38_905_000 as u64) .saturating_add(RocksDbWeight::get().reads(1 as u64)) .saturating_add(RocksDbWeight::get().writes(1 as u64)) } // Storage: TxPause PausedCalls (r:1 w:1) - fn unpause_call() -> Weight { + fn unpause() -> Weight { Weight::from_ref_time(32_892_000 as u64) .saturating_add(RocksDbWeight::get().reads(1 as u64)) .saturating_add(RocksDbWeight::get().writes(1 as u64)) From b8a4fcb5cf7625f97f689ec7f65ed48b97e48a0d Mon Sep 17 00:00:00 2001 From: Dan Shields Date: Sat, 15 Oct 2022 17:17:06 -0600 Subject: [PATCH 028/100] move to full_name notation for all interfaces, last commit introduced 1 more storage read. --- frame/tx-pause/src/benchmarking.rs | 30 ++++++----- frame/tx-pause/src/lib.rs | 87 +++++++++++------------------- frame/tx-pause/src/tests.rs | 62 ++++++++++----------- frame/tx-pause/src/weights.rs | 26 ++++----- 4 files changed, 90 insertions(+), 115 deletions(-) diff --git a/frame/tx-pause/src/benchmarking.rs b/frame/tx-pause/src/benchmarking.rs index ed1c21946e0b2..6fc20a96302a5 100644 --- a/frame/tx-pause/src/benchmarking.rs +++ b/frame/tx-pause/src/benchmarking.rs @@ -23,35 +23,39 @@ use frame_benchmarking::benchmarks; benchmarks! { pause { - let pallet_name: PalletNameOf = b"SomePalletName".to_vec().try_into().unwrap(); - let maybe_call_name: Option> = Some(b"some_call_name".to_vec().try_into().unwrap()); + let full_name: FullNameOf = (name::(b"SomePalletName"), Some(name::(b"SomePalletName"))); + // let pallet_name: PalletNameOf = name::(b"SomePalletName"); + // let maybe_call_name: Option> = Some(name::(b"some_call_name")); let origin = T::PauseOrigin::successful_origin(); - let call = Call::::pause { pallet_name: pallet_name.clone(), maybe_call_name: maybe_call_name.clone() }; + // let call = Call::::pause { full_name: full_name.clone() }; + // let call = Call::::pause { pallet_name: pallet_name.clone(), maybe_call_name: maybe_call_name.clone() }; - }: _(origin, pallet_name.clone(), maybe_call_name.clone()) + }: _(origin, full_name.clone()) verify { - assert!(TxPause::::paused_calls((pallet_name.clone(), maybe_call_name.clone())).is_some()) + assert!(TxPause::::paused_calls(full_name.clone()).is_some()) } unpause { - let pallet_name: PalletNameOf = b"SomePalletName".to_vec().try_into().unwrap(); - let maybe_call_name: Option> = Some(b"some_call_name".to_vec().try_into().unwrap()); + let full_name: FullNameOf = (name::(b"SomePalletName"), Some(name::(b"SomePalletName"))); let pause_origin = T::PauseOrigin::successful_origin(); - // Set TxPause::::pause( pause_origin, - pallet_name.clone(), - maybe_call_name.clone(), + full_name.clone(), )?; let unpause_origin = T::UnpauseOrigin::successful_origin(); - let call = Call::::unpause { pallet_name: pallet_name.clone(), maybe_call_name: maybe_call_name.clone() }; + // let call = Call::::unpause { pallet_name: pallet_name.clone(), maybe_call_name: maybe_call_name.clone() }; - }: _(unpause_origin, pallet_name.clone(), maybe_call_name.clone()) + }: _(unpause_origin, full_name.clone()) verify { - assert!(TxPause::::paused_calls((pallet_name.clone(), maybe_call_name.clone())).is_none()) + assert!(TxPause::::paused_calls(full_name.clone()).is_none()) + } impl_benchmark_test_suite!(TxPause, crate::mock::new_test_ext(), crate::mock::Test); } + +pub fn name(bytes: &[u8]) -> BoundedVec { + bytes.to_vec().try_into().unwrap() +} diff --git a/frame/tx-pause/src/lib.rs b/frame/tx-pause/src/lib.rs index 48304f7bae602..1ef2fcdf316d7 100644 --- a/frame/tx-pause/src/lib.rs +++ b/frame/tx-pause/src/lib.rs @@ -178,27 +178,23 @@ pub mod pallet { #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event { - /// This pallet's calls are now paused. \[pallet_name\] - PalletPaused { pallet_name: PalletNameOf }, - /// This call is now paused. \[pallet_name, call_name\] - CallPaused { pallet_name: PalletNameOf, call_name: CallNameOf }, - /// This pallet's calls are now unpaused. \[pallet_name\] - PalletUnpaused { pallet_name: PalletNameOf }, - /// This call is now unpaused. \[pallet_name, call_name\] - CallUnpaused { pallet_name: PalletNameOf, call_name: CallNameOf }, + /// This pallet, or a specific call is now paused. \[pallet_name, Option\] + SomethingPaused { full_name: FullNameOf }, + /// This pallet, or a specific call is now unpaused. \[pallet_name, Option\] + SomethingUnpaused { full_name: FullNameOf }, } /// The set of calls that are explicitly paused. #[pallet::storage] #[pallet::getter(fn paused_calls)] pub type PausedCalls = - StorageMap<_, Blake2_128Concat, (PalletNameOf, Option>), (), OptionQuery>; + StorageMap<_, Blake2_128Concat, FullNameOf, (), OptionQuery>; /// Configure the initial state of this pallet in the genesis block. #[pallet::genesis_config] pub struct GenesisConfig { /// The initially paused calls. - pub paused: Vec<(PalletNameOf, Option>)>, + pub paused: Vec>, pub _phantom: PhantomData, } @@ -225,22 +221,14 @@ pub mod pallet { /// Pause a call. /// /// Can only be called by [`Config::PauseOrigin`]. - /// Emits an [`Event::CallPaused`] event on success. + /// Emits an [`Event::SomethingPaused`] event on success. #[pallet::weight(T::WeightInfo::pause())] - pub fn pause( - origin: OriginFor, - pallet_name: PalletNameOf, - maybe_call_name: Option>, - ) -> DispatchResult { + pub fn pause(origin: OriginFor, full_name: FullNameOf) -> DispatchResult { T::PauseOrigin::ensure_origin(origin)?; - Self::ensure_can_pause(&pallet_name, &maybe_call_name)?; - PausedCalls::::insert((&pallet_name, &maybe_call_name), ()); - if let Some(call_name) = maybe_call_name { - Self::deposit_event(Event::CallPaused { pallet_name, call_name }); - } else { - Self::deposit_event(Event::PalletPaused { pallet_name }); - } + Self::ensure_can_pause(&full_name)?; + PausedCalls::::insert(&full_name, ()); + Self::deposit_event(Event::SomethingPaused { full_name }); Ok(()) } @@ -248,23 +236,14 @@ pub mod pallet { /// Un-pause a call. /// /// Can only be called by [`Config::UnpauseOrigin`]. - /// Emits an [`Event::CallUnpaused`] event on success. + /// Emits an [`Event::SomethingUnpaused`] event on success. #[pallet::weight(T::WeightInfo::unpause())] - pub fn unpause( - origin: OriginFor, - pallet_name: PalletNameOf, - maybe_call_name: Option>, - ) -> DispatchResult { + pub fn unpause(origin: OriginFor, full_name: FullNameOf) -> DispatchResult { T::UnpauseOrigin::ensure_origin(origin)?; - Self::ensure_can_unpause(&pallet_name, &maybe_call_name)?; - PausedCalls::::remove((&pallet_name, &maybe_call_name)); - if let Some(call_name) = maybe_call_name { - Self::deposit_event(Event::CallUnpaused { pallet_name, call_name }); - } else { - Self::deposit_event(Event::PalletUnpaused { pallet_name }); - } - + Self::ensure_can_unpause(&full_name)?; + PausedCalls::::remove(&full_name); + Self::deposit_event(Event::SomethingUnpaused { full_name }); Ok(()) } } @@ -277,23 +256,26 @@ impl Pallet { let call_name = CallNameOf::::try_from(call_name); match (pallet_name, call_name) { - (Ok(pallet_name), Ok(call_name)) => Self::is_paused(&pallet_name, &Some(call_name)), + (Ok(pallet_name), Ok(call_name)) => Self::is_paused(&&>::from(( + pallet_name.clone(), + Some(call_name.clone()), + ))), _ => T::PauseTooLongNames::get(), } } - /// Return whether this pallet or call is paused. - pub fn is_paused( - pallet_name: &PalletNameOf, - maybe_call_name: &Option>, - ) -> bool { + /// Return whether this pallet or call is paused + pub fn is_paused(full_name: &FullNameOf) -> bool { + let (pallet_name, maybe_call_name) = full_name; if >::contains_key(>::from((pallet_name.clone(), None))) { + // SAFETY: Everything that is whitelisted cannot be paused, + // including calls within paused pallets. if T::WhitelistCallNames::contains(&>::from(( pallet_name.clone(), maybe_call_name.clone(), ))) { return false - } + }; return true }; >::contains_key(>::from(( @@ -303,18 +285,14 @@ impl Pallet { } /// Ensure that this pallet or call can be paused. - pub fn ensure_can_pause( - pallet_name: &PalletNameOf, - maybe_call_name: &Option>, - ) -> Result<(), Error> { + pub fn ensure_can_pause(full_name: &FullNameOf) -> Result<(), Error> { // The `TxPause` pallet can never be paused. - if pallet_name.as_ref() == ::name().as_bytes().to_vec() { + if full_name.0.as_ref() == ::name().as_bytes().to_vec() { return Err(Error::::IsUnpausable) } - if Self::is_paused(&pallet_name.clone(), &maybe_call_name.clone()) { + if Self::is_paused(&full_name) { return Err(Error::::IsPaused) } - let full_name: FullNameOf = (pallet_name.clone(), maybe_call_name.clone()); if T::WhitelistCallNames::contains(&full_name) { return Err(Error::::IsUnpausable) } @@ -322,11 +300,8 @@ impl Pallet { } /// Ensure that this call can be un-paused. - pub fn ensure_can_unpause( - pallet_name: &PalletNameOf, - maybe_call_name: &Option>, - ) -> Result<(), Error> { - if Self::is_paused(&pallet_name, &maybe_call_name) { + pub fn ensure_can_unpause(full_name: &FullNameOf) -> Result<(), Error> { + if Self::is_paused(&full_name) { // SAFETY: Everything that is paused, can be un-paused. Ok(()) } else { diff --git a/frame/tx-pause/src/tests.rs b/frame/tx-pause/src/tests.rs index 4e786f2aa3665..d490db7b64585 100644 --- a/frame/tx-pause/src/tests.rs +++ b/frame/tx-pause/src/tests.rs @@ -31,8 +31,7 @@ fn can_pause_specific_call() { assert_ok!(TxPause::pause( Origin::signed(mock::PauseOrigin::get()), - name(b"Balances"), - Some(name(b"transfer")), + full_name::(b"Balances", Some(b"transfer")) )); assert_err!( @@ -54,9 +53,8 @@ fn can_pause_all_calls_in_pallet_except_on_whitelist() { assert_ok!(TxPause::pause( Origin::signed(mock::PauseOrigin::get()), - name(b"Utility"), - None, - )); + full_name::(b"Utility", None) + ),); assert_err!( batch_call.clone().dispatch(Origin::signed(0)), @@ -70,8 +68,7 @@ fn can_unpause_specific_call() { new_test_ext().execute_with(|| { assert_ok!(TxPause::pause( Origin::signed(mock::PauseOrigin::get()), - name(b"Balances"), - Some(name(b"transfer")), + full_name::(b"Balances", Some(b"transfer")), )); assert_err!( call_transfer(2, 1).dispatch(Origin::signed(2)), @@ -80,8 +77,7 @@ fn can_unpause_specific_call() { assert_ok!(TxPause::unpause( Origin::signed(mock::UnpauseOrigin::get()), - name(b"Balances"), - Some(name(b"transfer")), + full_name::(b"Balances", Some(b"transfer")), )); assert_ok!(call_transfer(4, 1).dispatch(Origin::signed(0))); }); @@ -95,8 +91,7 @@ fn can_filter_balance_in_batch_when_paused() { assert_ok!(TxPause::pause( Origin::signed(mock::PauseOrigin::get()), - name(b"Balances"), - Some(name(b"transfer")), + full_name::(b"Balances", Some(b"transfer")), )); assert_ok!(batch_call.clone().dispatch(Origin::signed(0))); @@ -115,8 +110,7 @@ fn can_filter_balance_in_proxy_when_paused() { new_test_ext().execute_with(|| { assert_ok!(TxPause::pause( Origin::signed(mock::PauseOrigin::get()), - name(b"Balances"), - Some(name(b"transfer")), + full_name::(b"Balances", Some(b"transfer")), )); assert_ok!(Proxy::add_proxy(Origin::signed(1), 2, ProxyType::JustTransfer, 0)); @@ -139,8 +133,7 @@ fn fails_to_pause_self() { assert_noop!( TxPause::pause( Origin::signed(mock::PauseOrigin::get()), - name(b"TxPause"), - Some(name(b"pause")), + full_name::(b"TxPause", Some(b"pause")), ), Error::::IsUnpausable ); @@ -153,8 +146,7 @@ fn fails_to_pause_unpausable_pallet() { assert_noop!( TxPause::pause( Origin::signed(mock::PauseOrigin::get()), - name(b"DummyPallet"), - Some(name(b"any-call")), + full_name::(b"DummyPallet", Some(b"any_call")), ), Error::::IsUnpausable ); @@ -172,8 +164,7 @@ fn fails_to_pause_unpausable_call_when_pallet_is_paused() { assert_ok!(TxPause::pause( Origin::signed(mock::PauseOrigin::get()), - name(b"Balances"), - None, + full_name::(b"Balances", None), )); assert_ok!(call_transfer_keep_alive(3, 1).dispatch(Origin::signed(3))); @@ -190,8 +181,7 @@ fn fails_to_pause_unpausable_call() { assert_noop!( TxPause::pause( Origin::signed(mock::PauseOrigin::get()), - name(b"Balances"), - Some(name(b"transfer_keep_alive")), + full_name::(b"Balances", Some(b"transfer_keep_alive")), ), Error::::IsUnpausable ); @@ -203,15 +193,13 @@ fn fails_to_pause_already_paused_pallet() { new_test_ext().execute_with(|| { assert_ok!(TxPause::pause( Origin::signed(mock::PauseOrigin::get()), - name(b"Balances"), - Some(name(b"transfer")), + full_name::(b"Balances", Some(b"transfer")), )); assert_noop!( TxPause::pause( Origin::signed(mock::PauseOrigin::get()), - name(b"Balances"), - Some(name(b"transfer")), + full_name::(b"Balances", Some(b"transfer")), ), Error::::IsPaused ); @@ -224,22 +212,30 @@ fn fails_to_unpause_not_paused_pallet() { assert_noop!( TxPause::unpause( Origin::signed(mock::UnpauseOrigin::get()), - name(b"Balances"), - Some(name(b"transfer")), + full_name::(b"Balances", Some(b"transfer_keep_alive")), ), Error::::IsUnpaused ); }); } -fn name(bytes: &[u8]) -> BoundedVec { - bytes.to_vec().try_into().unwrap() -} - -fn call_transfer(dest: u64, value: u64) -> RuntimeCall { +pub fn call_transfer(dest: u64, value: u64) -> RuntimeCall { RuntimeCall::Balances(pallet_balances::Call::transfer { dest, value }) } -fn call_transfer_keep_alive(dest: u64, value: u64) -> RuntimeCall { +pub fn call_transfer_keep_alive(dest: u64, value: u64) -> RuntimeCall { RuntimeCall::Balances(pallet_balances::Call::transfer_keep_alive { dest, value }) } + +pub fn full_name( + pallet_name_bytes: &[u8], + maybe_call_name_bytes: Option<&[u8]>, +) -> FullNameOf { + match maybe_call_name_bytes { + Some(call_name_bytes) => >::from(( + pallet_name_bytes.to_vec().try_into().unwrap(), + Some(call_name_bytes.to_vec().try_into().unwrap()), + )), + None => >::from((pallet_name_bytes.to_vec().try_into().unwrap(), None)), + } +} diff --git a/frame/tx-pause/src/weights.rs b/frame/tx-pause/src/weights.rs index 21fee4495f4d8..a4b408eef321e 100644 --- a/frame/tx-pause/src/weights.rs +++ b/frame/tx-pause/src/weights.rs @@ -18,7 +18,7 @@ //! Autogenerated weights for pallet_tx_pause //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-10-11, STEPS: `1`, REPEAT: 1, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2022-10-15, STEPS: `1`, REPEAT: 1, LOW RANGE: `[]`, HIGH RANGE: `[]` //! HOSTNAME: `pop-os`, CPU: `12th Gen Intel(R) Core(TM) i7-12700H` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 @@ -62,32 +62,32 @@ pub trait WeightInfo { /// Weights for pallet_tx_pause using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { - // Storage: TxPause PausedCalls (r:1 w:1) + // Storage: TxPause PausedCalls (r:2 w:1) fn pause() -> Weight { - Weight::from_ref_time(38_905_000 as u64) - .saturating_add(T::DbWeight::get().reads(1 as u64)) + Weight::from_ref_time(61_745_000 as u64) + .saturating_add(T::DbWeight::get().reads(2 as u64)) .saturating_add(T::DbWeight::get().writes(1 as u64)) } - // Storage: TxPause PausedCalls (r:1 w:1) + // Storage: TxPause PausedCalls (r:2 w:1) fn unpause() -> Weight { - Weight::from_ref_time(32_892_000 as u64) - .saturating_add(T::DbWeight::get().reads(1 as u64)) + Weight::from_ref_time(55_117_000 as u64) + .saturating_add(T::DbWeight::get().reads(2 as u64)) .saturating_add(T::DbWeight::get().writes(1 as u64)) } } // For backwards compatibility and tests impl WeightInfo for () { - // Storage: TxPause PausedCalls (r:1 w:1) + // Storage: TxPause PausedCalls (r:2 w:1) fn pause() -> Weight { - Weight::from_ref_time(38_905_000 as u64) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) + Weight::from_ref_time(61_745_000 as u64) + .saturating_add(RocksDbWeight::get().reads(2 as u64)) .saturating_add(RocksDbWeight::get().writes(1 as u64)) } - // Storage: TxPause PausedCalls (r:1 w:1) + // Storage: TxPause PausedCalls (r:2 w:1) fn unpause() -> Weight { - Weight::from_ref_time(32_892_000 as u64) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) + Weight::from_ref_time(55_117_000 as u64) + .saturating_add(RocksDbWeight::get().reads(2 as u64)) .saturating_add(RocksDbWeight::get().writes(1 as u64)) } } From f39cc9d31c34755f5c89f67b74b9d4687728496b Mon Sep 17 00:00:00 2001 From: Dan Shields Date: Sat, 15 Oct 2022 17:32:07 -0600 Subject: [PATCH 029/100] refactor is_paused --- frame/tx-pause/src/lib.rs | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/frame/tx-pause/src/lib.rs b/frame/tx-pause/src/lib.rs index 1ef2fcdf316d7..f4a1696634c65 100644 --- a/frame/tx-pause/src/lib.rs +++ b/frame/tx-pause/src/lib.rs @@ -267,21 +267,20 @@ impl Pallet { /// Return whether this pallet or call is paused pub fn is_paused(full_name: &FullNameOf) -> bool { let (pallet_name, maybe_call_name) = full_name; + // SAFETY: Everything that is whitelisted cannot be paused, + // including calls within paused pallets. + if T::WhitelistCallNames::contains(&>::from(( + pallet_name.clone(), + maybe_call_name.clone(), + ))) { + return false + }; + // Check is pallet is paused. if >::contains_key(>::from((pallet_name.clone(), None))) { - // SAFETY: Everything that is whitelisted cannot be paused, - // including calls within paused pallets. - if T::WhitelistCallNames::contains(&>::from(( - pallet_name.clone(), - maybe_call_name.clone(), - ))) { - return false - }; return true }; - >::contains_key(>::from(( - pallet_name.clone(), - maybe_call_name.clone(), - ))) + // Check if call in a pallet is paused + >::contains_key(full_name) } /// Ensure that this pallet or call can be paused. From 92771786a4f0d56887a606e474fe7369d1369129 Mon Sep 17 00:00:00 2001 From: Dan Shields Date: Mon, 31 Oct 2022 19:20:23 +0100 Subject: [PATCH 030/100] safe math on safe mode --- frame/safe-mode/src/benchmarking.rs | 2 +- frame/safe-mode/src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frame/safe-mode/src/benchmarking.rs b/frame/safe-mode/src/benchmarking.rs index 6866fe4bb8f45..d2b51c5474189 100644 --- a/frame/safe-mode/src/benchmarking.rs +++ b/frame/safe-mode/src/benchmarking.rs @@ -117,7 +117,7 @@ benchmarks! { let force_origin = T::ForceDeactivateOrigin::successful_origin(); assert!(SafeMode::::force_deactivate(force_origin.clone()).is_ok()); - System::::set_block_number(System::::block_number() + One::one()); + System::::set_block_number(System::::block_number() + T::ReleaseDelay::get().unwrap() + One::one()); System::::on_initialize(System::::block_number()); SafeMode::::on_initialize(System::::block_number()); diff --git a/frame/safe-mode/src/lib.rs b/frame/safe-mode/src/lib.rs index d6d79e18e5564..1c82aea55b12d 100644 --- a/frame/safe-mode/src/lib.rs +++ b/frame/safe-mode/src/lib.rs @@ -452,7 +452,7 @@ impl Pallet { if who.is_some() { let delay = T::ReleaseDelay::get().ok_or(Error::::NotConfigured)?; let now = >::block_number(); - ensure!(now > (block + delay), Error::::CannotReleaseYet); + ensure!(now > (block.saturating_add(delay)), Error::::CannotReleaseYet); } Reservations::::remove(&account, &block); From 31167d738e9992ecef33a23dcf121689e6e4c462 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Mon, 6 Feb 2023 19:25:47 +0100 Subject: [PATCH 031/100] Make stuff compile Signed-off-by: Oliver Tale-Yazdi --- Cargo.lock | 38 +++++++ bin/node/runtime/src/lib.rs | 16 +-- frame/safe-mode/Cargo.toml | 9 +- frame/safe-mode/src/benchmarking.rs | 20 ++-- frame/safe-mode/src/lib.rs | 16 ++- frame/safe-mode/src/mock.rs | 14 +-- frame/safe-mode/src/tests.rs | 152 ++++++++++++++++------------ frame/tx-pause/Cargo.toml | 10 +- frame/tx-pause/src/benchmarking.rs | 11 +- frame/tx-pause/src/lib.rs | 8 +- frame/tx-pause/src/mock.rs | 2 +- frame/tx-pause/src/tests.rs | 56 +++++----- 12 files changed, 209 insertions(+), 143 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c870fb0a930ac..69a40ff0013d4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3546,6 +3546,7 @@ dependencies = [ "pallet-referenda", "pallet-remark", "pallet-root-testing", + "pallet-safe-mode", "pallet-scheduler", "pallet-session", "pallet-session-benchmarking", @@ -3560,6 +3561,7 @@ dependencies = [ "pallet-transaction-payment-rpc-runtime-api", "pallet-transaction-storage", "pallet-treasury", + "pallet-tx-pause", "pallet-uniques", "pallet-utility", "pallet-vesting", @@ -6318,6 +6320,24 @@ dependencies = [ "sp-std", ] +[[package]] +name = "pallet-safe-mode" +version = "4.0.0-dev" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "pallet-balances", + "pallet-proxy", + "pallet-utility", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + [[package]] name = "pallet-scheduler" version = "4.0.0-dev" @@ -6634,6 +6654,24 @@ dependencies = [ "sp-std", ] +[[package]] +name = "pallet-tx-pause" +version = "4.0.0-dev" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "pallet-balances", + "pallet-proxy", + "pallet-utility", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + [[package]] name = "pallet-uniques" version = "4.0.0-dev" diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index d9d32f61e8aef..e09948a3b67bc 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -29,13 +29,13 @@ use frame_election_provider_support::{ use frame_support::{ construct_runtime, dispatch::DispatchClass, - pallet_prelude::Get, + pallet_prelude::EnsureOrigin, parameter_types, traits::{ fungible::ItemOf, AsEnsureOriginWithArg, ConstBool, ConstU128, ConstU16, ConstU32, - Currency, EitherOfDiverse, EqualPrivilegeOnly, Everything, Imbalance, InstanceFilter, - KeyOwnerProofSystem, LockIdentifier, Nothing, OnUnbalanced, U128CurrencyToVote, - WithdrawReasons, + Contains, Currency, EitherOfDiverse, EqualPrivilegeOnly, Imbalance, InsideBoth, + InstanceFilter, KeyOwnerProofSystem, LockIdentifier, Nothing, OnUnbalanced, + U128CurrencyToVote, WithdrawReasons, }, weights::{ constants::{ @@ -70,7 +70,7 @@ use sp_runtime::{ curve::PiecewiseLinear, generic, impl_opaque_keys, traits::{ - self, BlakeTwo256, Block as BlockT, Bounded, ConvertInto, NumberFor, OpaqueKeys, + self, BlakeTwo256, Block as BlockT, Bounded, ConvertInto, Get, NumberFor, OpaqueKeys, SaturatedConversion, StaticLookup, }, transaction_validity::{TransactionPriority, TransactionSource, TransactionValidity}, @@ -289,7 +289,7 @@ impl ForceActivateOrigin { } /// Signed origin. - pub fn signed(&self) -> ::Origin { + pub fn signed(&self) -> ::RuntimeOrigin { RawOrigin::Signed(self.acc()).into() } } @@ -314,7 +314,7 @@ impl ForceExtendOrigin { } /// Signed origin. - pub fn signed(&self) -> ::Origin { + pub fn signed(&self) -> ::RuntimeOrigin { RawOrigin::Signed(self.acc()).into() } } @@ -1689,7 +1689,7 @@ parameter_types! { pub const ThawThrottle: (Perquintill, BlockNumber) = (Perquintill::from_percent(25), 5); pub Target: Perquintill = Perquintill::zero(); pub const NisPalletId: PalletId = PalletId(*b"py/nis "); - pub const NisReserveId: [u8; 8] = *b"py/nis "; + pub const NisReserveId: u32 = 123; // FAIL-CI } impl pallet_nis::Config for Runtime { diff --git a/frame/safe-mode/Cargo.toml b/frame/safe-mode/Cargo.toml index ea1f77133be16..2c980c0674bc7 100644 --- a/frame/safe-mode/Cargo.toml +++ b/frame/safe-mode/Cargo.toml @@ -18,16 +18,15 @@ frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } frame-system = { version = "4.0.0-dev", default-features = false, path = "../system" } scale-info = { version = "2.0.1", default-features = false, features = ["derive"] } -sp-runtime = { version = "6.0.0", default-features = false, path = "../../primitives/runtime" } -sp-std = { version = "4.0.0", default-features = false, path = "../../primitives/std" } +sp-runtime = { version = "7.0.0", default-features = false, path = "../../primitives/runtime" } +sp-std = { version = "5.0.0", default-features = false, path = "../../primitives/std" } pallet-balances = { version = "4.0.0-dev", path = "../balances", default-features = false, optional = true } pallet-utility = { version = "4.0.0-dev", path = "../utility", default-features = false, optional = true } pallet-proxy = { version = "4.0.0-dev", path = "../proxy", default-features = false, optional = true } [dev-dependencies] -sp-core = { version = "6.0.0", path = "../../primitives/core" } -sp-std = { version = "4.0.0", path = "../../primitives/std" } -sp-io = { version = "6.0.0", path = "../../primitives/io" } +sp-core = { version = "7.0.0", path = "../../primitives/core" } +sp-io = { version = "7.0.0", path = "../../primitives/io" } pallet-balances = { version = "4.0.0-dev", path = "../balances" } pallet-utility = { version = "4.0.0-dev", path = "../utility" } pallet-proxy = { version = "4.0.0-dev", path = "../proxy" } diff --git a/frame/safe-mode/src/benchmarking.rs b/frame/safe-mode/src/benchmarking.rs index d2b51c5474189..aebca74eda7f1 100644 --- a/frame/safe-mode/src/benchmarking.rs +++ b/frame/safe-mode/src/benchmarking.rs @@ -19,7 +19,7 @@ use super::{Pallet as SafeMode, *}; -use frame_benchmarking::{benchmarks, whitelisted_caller}; +use frame_benchmarking::{benchmarks, whitelisted_caller, BenchmarkError}; use frame_support::traits::{Currency, UnfilteredDispatchable}; use frame_system::{Pallet as System, RawOrigin}; use sp_runtime::traits::{Bounded, One}; @@ -38,7 +38,7 @@ benchmarks! { } force_activate { - let force_origin = T::ForceActivateOrigin::successful_origin(); + let force_origin = T::ForceActivateOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; let duration = T::ForceActivateOrigin::ensure_origin(force_origin.clone()).unwrap(); let call = Call::::force_activate {}; }: { call.dispatch_bypass_filter(force_origin)? } @@ -73,7 +73,7 @@ benchmarks! { System::::set_block_number(1u32.into()); assert!(SafeMode::::activate(origin.clone().into()).is_ok()); - let force_origin = T::ForceExtendOrigin::successful_origin(); + let force_origin = T::ForceExtendOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; let extension = T::ForceExtendOrigin::ensure_origin(force_origin.clone()).unwrap(); let call = Call::::force_extend {}; }: { call.dispatch_bypass_filter(force_origin)? } @@ -91,7 +91,7 @@ benchmarks! { assert!(SafeMode::::activate(origin.clone().into()).is_ok()); - let force_origin = T::ForceDeactivateOrigin::successful_origin(); + let force_origin = T::ForceDeactivateOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; let call = Call::::force_deactivate {}; }: { call.dispatch_bypass_filter(force_origin)? } verify { @@ -114,7 +114,7 @@ benchmarks! { BalanceOf::::max_value() - T::ActivateReservationAmount::get().unwrap() ); - let force_origin = T::ForceDeactivateOrigin::successful_origin(); + let force_origin = T::ForceDeactivateOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; assert!(SafeMode::::force_deactivate(force_origin.clone()).is_ok()); System::::set_block_number(System::::block_number() + T::ReleaseDelay::get().unwrap() + One::one()); @@ -143,14 +143,15 @@ benchmarks! { BalanceOf::::max_value() - T::ActivateReservationAmount::get().unwrap() ); - let force_origin = T::ForceDeactivateOrigin::successful_origin(); + // TODO + let force_origin = T::ForceDeactivateOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; assert!(SafeMode::::force_deactivate(force_origin.clone()).is_ok()); System::::set_block_number(System::::block_number() + One::one()); System::::on_initialize(System::::block_number()); SafeMode::::on_initialize(System::::block_number()); - let release_origin = T::ForceReservationOrigin::successful_origin(); + let release_origin = T::ForceReservationOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; let call = Call::::force_release_reservation { account: caller.clone(), block: activated_at_block.clone()}; }: { call.dispatch_bypass_filter(release_origin)? } verify { @@ -173,10 +174,11 @@ benchmarks! { BalanceOf::::max_value() - T::ActivateReservationAmount::get().unwrap() ); - let force_origin = T::ForceDeactivateOrigin::successful_origin(); + // TODO + let force_origin = T::ForceDeactivateOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; assert!(SafeMode::::force_deactivate(force_origin.clone()).is_ok()); - let release_origin = T::ForceReservationOrigin::successful_origin(); + let release_origin = T::ForceReservationOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; let call = Call::::slash_reservation { account: caller.clone(), block: activated_at_block.clone()}; }: { call.dispatch_bypass_filter(release_origin)? } verify { diff --git a/frame/safe-mode/src/lib.rs b/frame/safe-mode/src/lib.rs index 1c82aea55b12d..5d6e552b76b85 100644 --- a/frame/safe-mode/src/lib.rs +++ b/frame/safe-mode/src/lib.rs @@ -93,19 +93,19 @@ pub mod pallet { /// The origin that may call [`Pallet::force_activate`]. /// /// The `Success` value is the number of blocks that this origin can activate safe-mode for. - type ForceActivateOrigin: EnsureOrigin; + type ForceActivateOrigin: EnsureOrigin; /// The origin that may call [`Pallet::force_extend`]. /// /// The `Success` value is the number of blocks that this origin can extend the safe-mode. - type ForceExtendOrigin: EnsureOrigin; + type ForceExtendOrigin: EnsureOrigin; /// The origin that may call [`Pallet::force_activate`]. - type ForceDeactivateOrigin: EnsureOrigin; + type ForceDeactivateOrigin: EnsureOrigin; /// The origin that may call [`Pallet::force_release_reservation`] and /// [`Pallet::slash_reservation`]. - type ForceReservationOrigin: EnsureOrigin; + type ForceReservationOrigin: EnsureOrigin; /// The minimal duration a deposit will remain reserved after safe-mode is activated or /// extended, unless [`Pallet::force_release_reservation`] is successfully dispatched @@ -232,6 +232,7 @@ pub mod pallet { /// This may be called by any signed origin with [`Config::ActivateReservationAmount`] free /// currency to reserve. This call can be disabled for all origins by configuring /// [`Config::ActivateReservationAmount`] to `None`. + #[pallet::call_index(0)] #[pallet::weight(T::WeightInfo::activate())] pub fn activate(origin: OriginFor) -> DispatchResult { let who = ensure_signed(origin)?; @@ -247,6 +248,7 @@ pub mod pallet { /// ### Safety /// /// Can only be called by the [`Config::ForceActivateOrigin`] origin. + #[pallet::call_index(1)] #[pallet::weight(T::WeightInfo::force_activate())] pub fn force_activate(origin: OriginFor) -> DispatchResult { let duration = T::ForceActivateOrigin::ensure_origin(origin)?; @@ -266,6 +268,7 @@ pub mod pallet { /// This may be called by any signed origin with [`Config::ExtendReservationAmount`] free /// currency to reserve. This call can be disabled for all origins by configuring /// [`Config::ExtendReservationAmount`] to `None`. + #[pallet::call_index(2)] #[pallet::weight(T::WeightInfo::extend())] pub fn extend(origin: OriginFor) -> DispatchResult { let who = ensure_signed(origin)?; @@ -281,6 +284,7 @@ pub mod pallet { /// ### Safety /// /// Can only be called by the [`Config::ForceExtendOrigin`] origin. + #[pallet::call_index(3)] #[pallet::weight(T::WeightInfo::force_extend())] pub fn force_extend(origin: OriginFor) -> DispatchResult { let duration = T::ForceExtendOrigin::ensure_origin(origin)?; @@ -301,6 +305,7 @@ pub mod pallet { /// ### Safety /// /// Can only be called by the [`Config::ForceDeactivateOrigin`] origin. + #[pallet::call_index(4)] #[pallet::weight(T::WeightInfo::force_deactivate())] pub fn force_deactivate(origin: OriginFor) -> DispatchResult { T::ForceDeactivateOrigin::ensure_origin(origin)?; @@ -317,6 +322,7 @@ pub mod pallet { /// ### Safety /// /// Can only be called by the [`Config::ForceReservationOrigin`] origin. + #[pallet::call_index(5)] #[pallet::weight(T::WeightInfo::slash_reservation())] pub fn slash_reservation( origin: OriginFor, @@ -343,6 +349,7 @@ pub mod pallet { /// This may be called by any signed origin. /// This call can be disabled for all origins by configuring /// [`Config::ReleaseDelay`] to `None`. + #[pallet::call_index(6)] #[pallet::weight(T::WeightInfo::release_reservation())] pub fn release_reservation( origin: OriginFor, @@ -365,6 +372,7 @@ pub mod pallet { /// ### Safety /// /// Can only be called by the [`Config::ForceReservationOrigin`] origin. + #[pallet::call_index(7)] #[pallet::weight(T::WeightInfo::force_release_reservation())] pub fn force_release_reservation( origin: OriginFor, diff --git a/frame/safe-mode/src/mock.rs b/frame/safe-mode/src/mock.rs index 098b25479bc97..23cd524f20bee 100644 --- a/frame/safe-mode/src/mock.rs +++ b/frame/safe-mode/src/mock.rs @@ -38,7 +38,7 @@ impl frame_system::Config for Test { type BaseCallFilter = InsideBoth; type BlockWeights = (); type BlockLength = (); - type Origin = Origin; + type RuntimeOrigin = RuntimeOrigin; type RuntimeCall = RuntimeCall; type Index = u64; type BlockNumber = u64; @@ -181,7 +181,7 @@ impl ForceActivateOrigin { } /// Signed origin. - pub fn signed(&self) -> ::Origin { + pub fn signed(&self) -> ::RuntimeOrigin { RawOrigin::Signed(self.acc()).into() } } @@ -206,7 +206,7 @@ impl ForceExtendOrigin { } /// Signed origin. - pub fn signed(&self) -> ::Origin { + pub fn signed(&self) -> ::RuntimeOrigin { RawOrigin::Signed(self.acc()).into() } } @@ -229,8 +229,8 @@ impl, O>> + From>> EnsureOrigin } #[cfg(feature = "runtime-benchmarks")] - fn successful_origin() -> O { - O::from(RawOrigin::Signed(ForceActivateOrigin::Strong.acc())) + fn try_successful_origin() -> Result { + Ok(O::from(RawOrigin::Signed(ForceActivateOrigin::Strong.acc()))) } } @@ -252,8 +252,8 @@ impl, O>> + From>> EnsureOrigin } #[cfg(feature = "runtime-benchmarks")] - fn successful_origin() -> O { - O::from(RawOrigin::Signed(ForceExtendOrigin::Strong.acc())) + fn try_successful_origin() -> Result { + Ok(O::from(RawOrigin::Signed(ForceExtendOrigin::Strong.acc()))) } } diff --git a/frame/safe-mode/src/tests.rs b/frame/safe-mode/src/tests.rs index 784cc896db91e..950faf13d9543 100644 --- a/frame/safe-mode/src/tests.rs +++ b/frame/safe-mode/src/tests.rs @@ -27,38 +27,42 @@ use frame_support::{assert_err, assert_noop, assert_ok, dispatch::Dispatchable}; #[test] fn fails_to_filter_calls_to_safe_mode_pallet() { new_test_ext().execute_with(|| { - assert_ok!(SafeMode::activate(Origin::signed(0))); + assert_ok!(SafeMode::activate(RuntimeOrigin::signed(0))); let activated_at_block = System::block_number(); assert_err!( - call_transfer().dispatch(Origin::signed(0)), + call_transfer().dispatch(RuntimeOrigin::signed(0)), frame_system::Error::::CallFiltered ); // TODO ^^^ consider refactor to throw a safe mode error, not generic `CallFiltered` next_block(); - assert_ok!(SafeMode::extend(Origin::signed(0))); + assert_ok!(SafeMode::extend(RuntimeOrigin::signed(0))); assert_ok!(SafeMode::force_extend(ForceExtendOrigin::Weak.signed())); assert_err!( - call_transfer().dispatch(Origin::signed(0)), + call_transfer().dispatch(RuntimeOrigin::signed(0)), frame_system::Error::::CallFiltered ); - assert_ok!(SafeMode::force_deactivate(Origin::signed(mock::ForceDeactivateOrigin::get()))); + assert_ok!(SafeMode::force_deactivate(RuntimeOrigin::signed( + mock::ForceDeactivateOrigin::get() + ))); assert_ok!(SafeMode::force_release_reservation( - Origin::signed(mock::ForceReservationOrigin::get()), + RuntimeOrigin::signed(mock::ForceReservationOrigin::get()), 0, activated_at_block )); next_block(); - assert_ok!(SafeMode::activate(Origin::signed(0))); + assert_ok!(SafeMode::activate(RuntimeOrigin::signed(0))); assert_err!( - call_transfer().dispatch(Origin::signed(0)), + call_transfer().dispatch(RuntimeOrigin::signed(0)), frame_system::Error::::CallFiltered ); - assert_ok!(SafeMode::force_deactivate(Origin::signed(mock::ForceDeactivateOrigin::get()))); + assert_ok!(SafeMode::force_deactivate(RuntimeOrigin::signed( + mock::ForceDeactivateOrigin::get() + ))); assert_ok!(SafeMode::slash_reservation( - Origin::signed(mock::ForceReservationOrigin::get()), + RuntimeOrigin::signed(mock::ForceReservationOrigin::get()), 0, activated_at_block + 2 )); @@ -68,8 +72,8 @@ fn fails_to_filter_calls_to_safe_mode_pallet() { #[test] fn fails_to_activate_if_activated() { new_test_ext().execute_with(|| { - assert_ok!(SafeMode::activate(Origin::signed(0))); - assert_noop!(SafeMode::activate(Origin::signed(2)), Error::::IsActive); + assert_ok!(SafeMode::activate(RuntimeOrigin::signed(0))); + assert_noop!(SafeMode::activate(RuntimeOrigin::signed(2)), Error::::IsActive); }); } @@ -77,7 +81,7 @@ fn fails_to_activate_if_activated() { fn fails_to_extend_if_not_activated() { new_test_ext().execute_with(|| { assert_eq!(SafeMode::active_until(), None); - assert_noop!(SafeMode::extend(Origin::signed(2)), Error::::IsInactive); + assert_noop!(SafeMode::extend(RuntimeOrigin::signed(2)), Error::::IsInactive); }); } @@ -85,12 +89,12 @@ fn fails_to_extend_if_not_activated() { fn fails_to_force_release_reservations_with_wrong_block() { new_test_ext().execute_with(|| { let activated_at_block = System::block_number(); - assert_ok!(SafeMode::activate(Origin::signed(0))); + assert_ok!(SafeMode::activate(RuntimeOrigin::signed(0))); run_to(mock::ActivationDuration::get() + activated_at_block + 1); assert_err!( SafeMode::force_release_reservation( - Origin::signed(mock::ForceReservationOrigin::get()), + RuntimeOrigin::signed(mock::ForceReservationOrigin::get()), 0, activated_at_block + 1 ), @@ -99,7 +103,7 @@ fn fails_to_force_release_reservations_with_wrong_block() { assert_err!( SafeMode::slash_reservation( - Origin::signed(mock::ForceReservationOrigin::get()), + RuntimeOrigin::signed(mock::ForceReservationOrigin::get()), 0, activated_at_block + 1 ), @@ -112,14 +116,16 @@ fn fails_to_force_release_reservations_with_wrong_block() { fn fails_to_release_reservations_too_early() { new_test_ext().execute_with(|| { let activated_at_block = System::block_number(); - assert_ok!(SafeMode::activate(Origin::signed(0))); - assert_ok!(SafeMode::force_deactivate(Origin::signed(mock::ForceDeactivateOrigin::get()))); + assert_ok!(SafeMode::activate(RuntimeOrigin::signed(0))); + assert_ok!(SafeMode::force_deactivate(RuntimeOrigin::signed( + mock::ForceDeactivateOrigin::get() + ))); assert_err!( - SafeMode::release_reservation(Origin::signed(2), 0, activated_at_block), + SafeMode::release_reservation(RuntimeOrigin::signed(2), 0, activated_at_block), Error::::CannotReleaseYet ); run_to(activated_at_block + mock::ReleaseDelay::get() + 1); - assert_ok!(SafeMode::release_reservation(Origin::signed(2), 0, activated_at_block)); + assert_ok!(SafeMode::release_reservation(RuntimeOrigin::signed(2), 0, activated_at_block)); }); } @@ -139,10 +145,10 @@ fn can_automatically_deactivate_after_timeout() { #[test] fn can_filter_balance_calls_when_activated() { new_test_ext().execute_with(|| { - assert_ok!(call_transfer().dispatch(Origin::signed(0))); - assert_ok!(SafeMode::activate(Origin::signed(0))); + assert_ok!(call_transfer().dispatch(RuntimeOrigin::signed(0))); + assert_ok!(SafeMode::activate(RuntimeOrigin::signed(0))); assert_err!( - call_transfer().dispatch(Origin::signed(0)), + call_transfer().dispatch(RuntimeOrigin::signed(0)), frame_system::Error::::CallFiltered ); // TODO ^^^ consider refactor to throw a safe mode error, not generic `CallFiltered` @@ -155,11 +161,11 @@ fn can_filter_balance_in_batch_when_activated() { let batch_call = RuntimeCall::Utility(pallet_utility::Call::batch { calls: vec![call_transfer()] }); - assert_ok!(batch_call.clone().dispatch(Origin::signed(0))); + assert_ok!(batch_call.clone().dispatch(RuntimeOrigin::signed(0))); - assert_ok!(SafeMode::activate(Origin::signed(0))); + assert_ok!(SafeMode::activate(RuntimeOrigin::signed(0))); - assert_ok!(batch_call.clone().dispatch(Origin::signed(0))); + assert_ok!(batch_call.clone().dispatch(RuntimeOrigin::signed(0))); System::assert_last_event( pallet_utility::Event::BatchInterrupted { index: 0, @@ -173,14 +179,14 @@ fn can_filter_balance_in_batch_when_activated() { #[test] fn can_filter_balance_in_proxy_when_activated() { new_test_ext().execute_with(|| { - assert_ok!(Proxy::add_proxy(Origin::signed(1), 2, ProxyType::JustTransfer, 0)); + assert_ok!(Proxy::add_proxy(RuntimeOrigin::signed(1), 2, ProxyType::JustTransfer, 0)); - assert_ok!(Proxy::proxy(Origin::signed(2), 1, None, Box::new(call_transfer()))); + assert_ok!(Proxy::proxy(RuntimeOrigin::signed(2), 1, None, Box::new(call_transfer()))); System::assert_last_event(pallet_proxy::Event::ProxyExecuted { result: Ok(()) }.into()); assert_ok!(SafeMode::force_activate(ForceActivateOrigin::Weak.signed())); - assert_ok!(Proxy::proxy(Origin::signed(2), 1, None, Box::new(call_transfer()))); + assert_ok!(Proxy::proxy(RuntimeOrigin::signed(2), 1, None, Box::new(call_transfer()))); System::assert_last_event( pallet_proxy::Event::ProxyExecuted { result: DispatchError::from(frame_system::Error::::CallFiltered).into(), @@ -193,13 +199,13 @@ fn can_filter_balance_in_proxy_when_activated() { #[test] fn can_activate() { new_test_ext().execute_with(|| { - assert_ok!(SafeMode::activate(Origin::signed(0))); + assert_ok!(SafeMode::activate(RuntimeOrigin::signed(0))); assert_eq!( SafeMode::active_until().unwrap(), System::block_number() + mock::ActivationDuration::get() ); assert_eq!(Balances::reserved_balance(0), mock::ActivateReservationAmount::get()); - assert_noop!(SafeMode::activate(Origin::signed(0)), Error::::IsActive); + assert_noop!(SafeMode::activate(RuntimeOrigin::signed(0)), Error::::IsActive); // Assert the stake. assert_eq!(Reservations::::get(0, 1), Some(mock::ActivateReservationAmount::get())); }); @@ -208,8 +214,8 @@ fn can_activate() { #[test] fn can_extend() { new_test_ext().execute_with(|| { - assert_ok!(SafeMode::activate(Origin::signed(0))); - assert_ok!(SafeMode::extend(Origin::signed(0))); + assert_ok!(SafeMode::activate(RuntimeOrigin::signed(0))); + assert_ok!(SafeMode::extend(RuntimeOrigin::signed(0))); assert_eq!( SafeMode::active_until().unwrap(), System::block_number() + mock::ActivationDuration::get() + mock::ExtendDuration::get() @@ -225,22 +231,28 @@ fn can_extend() { fn can_release_independent_reservations_by_block() { new_test_ext().execute_with(|| { let activated_at_block_0 = System::block_number(); - assert_ok!(SafeMode::activate(Origin::signed(0))); + assert_ok!(SafeMode::activate(RuntimeOrigin::signed(0))); run_to( mock::ActivationDuration::get() + mock::ReleaseDelay::get() + activated_at_block_0 + 1, ); let activated_at_block_1 = System::block_number(); - assert_ok!(SafeMode::activate(Origin::signed(0))); + assert_ok!(SafeMode::activate(RuntimeOrigin::signed(0))); assert_eq!(Balances::free_balance(&0), 1234 - (2 * mock::ActivateReservationAmount::get())); // accounts set in mock genesis - assert_ok!(SafeMode::force_deactivate(Origin::signed(mock::ForceDeactivateOrigin::get()))); + assert_ok!(SafeMode::force_deactivate(RuntimeOrigin::signed( + mock::ForceDeactivateOrigin::get() + ))); - assert_ok!(SafeMode::release_reservation(Origin::signed(2), 0, activated_at_block_0)); + assert_ok!(SafeMode::release_reservation( + RuntimeOrigin::signed(2), + 0, + activated_at_block_0 + )); assert_err!( - SafeMode::release_reservation(Origin::signed(2), 0, activated_at_block_1), + SafeMode::release_reservation(RuntimeOrigin::signed(2), 0, activated_at_block_1), Error::::CannotReleaseYet ); assert_eq!(Balances::free_balance(&0), 1234 - mock::ActivateReservationAmount::get()); // accounts set in mock genesis @@ -249,7 +261,11 @@ fn can_release_independent_reservations_by_block() { mock::ActivationDuration::get() + mock::ReleaseDelay::get() + activated_at_block_1 + 1, ); - assert_ok!(SafeMode::release_reservation(Origin::signed(2), 0, activated_at_block_1)); + assert_ok!(SafeMode::release_reservation( + RuntimeOrigin::signed(2), + 0, + activated_at_block_1 + )); assert_eq!(Balances::total_balance(&0), 1234); // accounts set in mock genesis }); } @@ -260,15 +276,15 @@ fn fails_signed_origin_when_explicit_origin_required() { assert_eq!(SafeMode::active_until(), None); let activated_at_block = System::block_number(); - assert_err!(SafeMode::force_activate(Origin::signed(0)), DispatchError::BadOrigin); - assert_err!(SafeMode::force_extend(Origin::signed(0)), DispatchError::BadOrigin); - assert_err!(SafeMode::force_deactivate(Origin::signed(0)), DispatchError::BadOrigin); + assert_err!(SafeMode::force_activate(RuntimeOrigin::signed(0)), DispatchError::BadOrigin); + assert_err!(SafeMode::force_extend(RuntimeOrigin::signed(0)), DispatchError::BadOrigin); + assert_err!(SafeMode::force_deactivate(RuntimeOrigin::signed(0)), DispatchError::BadOrigin); assert_err!( - SafeMode::slash_reservation(Origin::signed(0), 0, activated_at_block), + SafeMode::slash_reservation(RuntimeOrigin::signed(0), 0, activated_at_block), DispatchError::BadOrigin ); assert_err!( - SafeMode::force_release_reservation(Origin::signed(0), 0, activated_at_block), + SafeMode::force_release_reservation(RuntimeOrigin::signed(0), 0, activated_at_block), DispatchError::BadOrigin ); }); @@ -280,11 +296,11 @@ fn fails_signed_origin_when_explicit_origin_required() { fn fails_force_deactivate_if_not_activated() { new_test_ext().execute_with(|| { assert_noop!( - SafeMode::force_deactivate(Origin::signed(mock::ForceDeactivateOrigin::get())), + SafeMode::force_deactivate(RuntimeOrigin::signed(mock::ForceDeactivateOrigin::get())), Error::::IsInactive ); assert_noop!( - SafeMode::force_deactivate(Origin::signed(mock::ForceDeactivateOrigin::get())), + SafeMode::force_deactivate(RuntimeOrigin::signed(mock::ForceDeactivateOrigin::get())), Error::::IsInactive ); }); @@ -311,12 +327,14 @@ fn can_force_deactivate_with_config_origin() { new_test_ext().execute_with(|| { assert_eq!(SafeMode::active_until(), None); assert_err!( - SafeMode::force_deactivate(Origin::signed(mock::ForceDeactivateOrigin::get())), + SafeMode::force_deactivate(RuntimeOrigin::signed(mock::ForceDeactivateOrigin::get())), Error::::IsInactive ); assert_ok!(SafeMode::force_activate(ForceActivateOrigin::Weak.signed())); assert_eq!(Balances::reserved_balance(ForceActivateOrigin::Weak.acc()), 0); - assert_ok!(SafeMode::force_deactivate(Origin::signed(mock::ForceDeactivateOrigin::get()))); + assert_ok!(SafeMode::force_deactivate(RuntimeOrigin::signed( + mock::ForceDeactivateOrigin::get() + ))); }); } @@ -345,10 +363,10 @@ fn can_force_extend_with_config_origin() { fn can_force_release_reservation_with_config_origin() { new_test_ext().execute_with(|| { let activated_at_block = System::block_number(); - assert_ok!(SafeMode::activate(Origin::signed(0))); + assert_ok!(SafeMode::activate(RuntimeOrigin::signed(0))); assert_err!( SafeMode::force_release_reservation( - Origin::signed(mock::ForceReservationOrigin::get()), + RuntimeOrigin::signed(mock::ForceReservationOrigin::get()), 0, activated_at_block ), @@ -357,7 +375,7 @@ fn can_force_release_reservation_with_config_origin() { run_to(mock::ActivationDuration::get() + activated_at_block + 1); assert_ok!(SafeMode::force_release_reservation( - Origin::signed(mock::ForceReservationOrigin::get()), + RuntimeOrigin::signed(mock::ForceReservationOrigin::get()), 0, activated_at_block )); @@ -365,8 +383,8 @@ fn can_force_release_reservation_with_config_origin() { Balances::make_free_balance_be(&0, 1234); let activated_and_extended_at_block = System::block_number(); - assert_ok!(SafeMode::activate(Origin::signed(0))); - assert_ok!(SafeMode::extend(Origin::signed(0))); + assert_ok!(SafeMode::activate(RuntimeOrigin::signed(0))); + assert_ok!(SafeMode::extend(RuntimeOrigin::signed(0))); run_to( mock::ActivationDuration::get() + mock::ExtendDuration::get() + @@ -375,7 +393,7 @@ fn can_force_release_reservation_with_config_origin() { ); assert_ok!(SafeMode::force_release_reservation( - Origin::signed(mock::ForceReservationOrigin::get()), + RuntimeOrigin::signed(mock::ForceReservationOrigin::get()), 0, activated_and_extended_at_block )); @@ -387,10 +405,10 @@ fn can_force_release_reservation_with_config_origin() { fn can_slash_reservation_with_config_origin() { new_test_ext().execute_with(|| { let activated_at_block = System::block_number(); - assert_ok!(SafeMode::activate(Origin::signed(0))); + assert_ok!(SafeMode::activate(RuntimeOrigin::signed(0))); assert_err!( SafeMode::slash_reservation( - Origin::signed(mock::ForceReservationOrigin::get()), + RuntimeOrigin::signed(mock::ForceReservationOrigin::get()), 0, activated_at_block ), @@ -399,7 +417,7 @@ fn can_slash_reservation_with_config_origin() { run_to(mock::ActivationDuration::get() + activated_at_block + 1); assert_ok!(SafeMode::slash_reservation( - Origin::signed(mock::ForceReservationOrigin::get()), + RuntimeOrigin::signed(mock::ForceReservationOrigin::get()), 0, activated_at_block )); @@ -407,8 +425,8 @@ fn can_slash_reservation_with_config_origin() { Balances::make_free_balance_be(&0, 1234); let activated_and_extended_at_block = System::block_number(); - assert_ok!(SafeMode::activate(Origin::signed(0))); - assert_ok!(SafeMode::extend(Origin::signed(0))); + assert_ok!(SafeMode::activate(RuntimeOrigin::signed(0))); + assert_ok!(SafeMode::extend(RuntimeOrigin::signed(0))); run_to( mock::ActivationDuration::get() + mock::ExtendDuration::get() + @@ -417,7 +435,7 @@ fn can_slash_reservation_with_config_origin() { ); assert_ok!(SafeMode::slash_reservation( - Origin::signed(mock::ForceReservationOrigin::get()), + RuntimeOrigin::signed(mock::ForceReservationOrigin::get()), 0, activated_and_extended_at_block )); @@ -477,16 +495,16 @@ fn fails_when_explicit_origin_required() { ); assert_err!( - SafeMode::force_activate(Origin::signed(mock::ForceDeactivateOrigin::get())), + SafeMode::force_activate(RuntimeOrigin::signed(mock::ForceDeactivateOrigin::get())), DispatchError::BadOrigin ); assert_err!( - SafeMode::force_extend(Origin::signed(mock::ForceDeactivateOrigin::get())), + SafeMode::force_extend(RuntimeOrigin::signed(mock::ForceDeactivateOrigin::get())), DispatchError::BadOrigin ); assert_err!( SafeMode::slash_reservation( - Origin::signed(mock::ForceDeactivateOrigin::get()), + RuntimeOrigin::signed(mock::ForceDeactivateOrigin::get()), 0, activated_at_block ), @@ -494,7 +512,7 @@ fn fails_when_explicit_origin_required() { ); assert_err!( SafeMode::force_release_reservation( - Origin::signed(mock::ForceDeactivateOrigin::get()), + RuntimeOrigin::signed(mock::ForceDeactivateOrigin::get()), 0, activated_at_block ), @@ -502,15 +520,15 @@ fn fails_when_explicit_origin_required() { ); assert_err!( - SafeMode::force_activate(Origin::signed(mock::ForceReservationOrigin::get())), + SafeMode::force_activate(RuntimeOrigin::signed(mock::ForceReservationOrigin::get())), DispatchError::BadOrigin ); assert_err!( - SafeMode::force_extend(Origin::signed(mock::ForceReservationOrigin::get())), + SafeMode::force_extend(RuntimeOrigin::signed(mock::ForceReservationOrigin::get())), DispatchError::BadOrigin ); assert_err!( - SafeMode::force_deactivate(Origin::signed(mock::ForceReservationOrigin::get())), + SafeMode::force_deactivate(RuntimeOrigin::signed(mock::ForceReservationOrigin::get())), DispatchError::BadOrigin ); }); diff --git a/frame/tx-pause/Cargo.toml b/frame/tx-pause/Cargo.toml index 0fe99c20353b4..8143e74ff4ffb 100644 --- a/frame/tx-pause/Cargo.toml +++ b/frame/tx-pause/Cargo.toml @@ -18,21 +18,19 @@ frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } frame-system = { version = "4.0.0-dev", default-features = false, path = "../system" } scale-info = { version = "2.0.1", default-features = false, features = ["derive"] } -sp-runtime = { version = "6.0.0", default-features = false, path = "../../primitives/runtime" } -sp-std = { version = "4.0.0", default-features = false, path = "../../primitives/std" } +sp-runtime = { version = "7.0.0", default-features = false, path = "../../primitives/runtime" } +sp-std = { version = "5.0.0", default-features = false, path = "../../primitives/std" } pallet-balances = { version = "4.0.0-dev", path = "../balances", default-features = false, optional = true } pallet-utility = { version = "4.0.0-dev", path = "../utility", default-features = false, optional = true } pallet-proxy = { version = "4.0.0-dev", path = "../proxy", default-features = false, optional = true } [dev-dependencies] -sp-core = { version = "6.0.0", path = "../../primitives/core" } -sp-std = { version = "4.0.0", path = "../../primitives/std" } -sp-io = { version = "6.0.0", path = "../../primitives/io" } +sp-core = { version = "7.0.0", path = "../../primitives/core" } +sp-io = { version = "7.0.0", path = "../../primitives/io" } pallet-balances = { version = "4.0.0-dev", path = "../balances" } pallet-utility = { version = "4.0.0-dev", path = "../utility" } pallet-proxy = { version = "4.0.0-dev", path = "../proxy" } - [features] default = ["std"] std = [ diff --git a/frame/tx-pause/src/benchmarking.rs b/frame/tx-pause/src/benchmarking.rs index 6fc20a96302a5..77e2158778d4f 100644 --- a/frame/tx-pause/src/benchmarking.rs +++ b/frame/tx-pause/src/benchmarking.rs @@ -26,28 +26,29 @@ benchmarks! { let full_name: FullNameOf = (name::(b"SomePalletName"), Some(name::(b"SomePalletName"))); // let pallet_name: PalletNameOf = name::(b"SomePalletName"); // let maybe_call_name: Option> = Some(name::(b"some_call_name")); - let origin = T::PauseOrigin::successful_origin(); + let origin = T::PauseOrigin::try_successful_origin().expect("Tx-pause pallet does not make sense without pause origin"); // let call = Call::::pause { full_name: full_name.clone() }; // let call = Call::::pause { pallet_name: pallet_name.clone(), maybe_call_name: maybe_call_name.clone() }; - }: _(origin, full_name.clone()) + }: _(origin, full_name.clone()) verify { assert!(TxPause::::paused_calls(full_name.clone()).is_some()) } unpause { let full_name: FullNameOf = (name::(b"SomePalletName"), Some(name::(b"SomePalletName"))); - let pause_origin = T::PauseOrigin::successful_origin(); + // tODO + let pause_origin = T::PauseOrigin::try_successful_origin().expect("Tx-pause pallet does not make sense without pause origin"); TxPause::::pause( pause_origin, full_name.clone(), )?; - let unpause_origin = T::UnpauseOrigin::successful_origin(); + let unpause_origin = T::UnpauseOrigin::try_successful_origin().expect("Tx-pause pallet does not make sense without un-pause origin"); // let call = Call::::unpause { pallet_name: pallet_name.clone(), maybe_call_name: maybe_call_name.clone() }; - }: _(unpause_origin, full_name.clone()) + }: _(unpause_origin, full_name.clone()) verify { assert!(TxPause::::paused_calls(full_name.clone()).is_none()) diff --git a/frame/tx-pause/src/lib.rs b/frame/tx-pause/src/lib.rs index f4a1696634c65..a3f7bb790f3ad 100644 --- a/frame/tx-pause/src/lib.rs +++ b/frame/tx-pause/src/lib.rs @@ -121,7 +121,7 @@ pub mod pallet { /// The overarching call type. type RuntimeCall: Parameter - + Dispatchable + + Dispatchable + GetDispatchInfo + GetCallMetadata + From> @@ -129,10 +129,10 @@ pub mod pallet { + IsType<::RuntimeCall>; /// The only origin that can pause calls. - type PauseOrigin: EnsureOrigin; + type PauseOrigin: EnsureOrigin; /// The only origin that can un-pause calls. - type UnpauseOrigin: EnsureOrigin; + type UnpauseOrigin: EnsureOrigin; /// Contains all calls that cannot be paused. /// @@ -222,6 +222,7 @@ pub mod pallet { /// /// Can only be called by [`Config::PauseOrigin`]. /// Emits an [`Event::SomethingPaused`] event on success. + #[pallet::call_index(0)] #[pallet::weight(T::WeightInfo::pause())] pub fn pause(origin: OriginFor, full_name: FullNameOf) -> DispatchResult { T::PauseOrigin::ensure_origin(origin)?; @@ -237,6 +238,7 @@ pub mod pallet { /// /// Can only be called by [`Config::UnpauseOrigin`]. /// Emits an [`Event::SomethingUnpaused`] event on success. + #[pallet::call_index(1)] #[pallet::weight(T::WeightInfo::unpause())] pub fn unpause(origin: OriginFor, full_name: FullNameOf) -> DispatchResult { T::UnpauseOrigin::ensure_origin(origin)?; diff --git a/frame/tx-pause/src/mock.rs b/frame/tx-pause/src/mock.rs index e54ae3823ae96..a9294b6ac4f77 100644 --- a/frame/tx-pause/src/mock.rs +++ b/frame/tx-pause/src/mock.rs @@ -38,7 +38,7 @@ impl frame_system::Config for Test { type BaseCallFilter = InsideBoth; type BlockWeights = (); type BlockLength = (); - type Origin = Origin; + type RuntimeOrigin = RuntimeOrigin; type RuntimeCall = RuntimeCall; type Index = u64; type BlockNumber = u64; diff --git a/frame/tx-pause/src/tests.rs b/frame/tx-pause/src/tests.rs index d490db7b64585..983d5cf50421c 100644 --- a/frame/tx-pause/src/tests.rs +++ b/frame/tx-pause/src/tests.rs @@ -27,37 +27,37 @@ use frame_support::{assert_err, assert_noop, assert_ok, dispatch::Dispatchable}; #[test] fn can_pause_specific_call() { new_test_ext().execute_with(|| { - assert_ok!(call_transfer(1, 1).dispatch(Origin::signed(0))); + assert_ok!(call_transfer(1, 1).dispatch(RuntimeOrigin::signed(0))); assert_ok!(TxPause::pause( - Origin::signed(mock::PauseOrigin::get()), + RuntimeOrigin::signed(mock::PauseOrigin::get()), full_name::(b"Balances", Some(b"transfer")) )); assert_err!( - call_transfer(2, 1).dispatch(Origin::signed(2)), + call_transfer(2, 1).dispatch(RuntimeOrigin::signed(2)), frame_system::Error::::CallFiltered ); - assert_ok!(call_transfer_keep_alive(3, 1).dispatch(Origin::signed(3))); + assert_ok!(call_transfer_keep_alive(3, 1).dispatch(RuntimeOrigin::signed(3))); }); } #[test] fn can_pause_all_calls_in_pallet_except_on_whitelist() { new_test_ext().execute_with(|| { - assert_ok!(call_transfer(1, 1).dispatch(Origin::signed(0))); + assert_ok!(call_transfer(1, 1).dispatch(RuntimeOrigin::signed(0))); let batch_call = RuntimeCall::Utility(pallet_utility::Call::batch { calls: vec![call_transfer(1, 1)] }); - assert_ok!(batch_call.clone().dispatch(Origin::signed(0))); + assert_ok!(batch_call.clone().dispatch(RuntimeOrigin::signed(0))); assert_ok!(TxPause::pause( - Origin::signed(mock::PauseOrigin::get()), + RuntimeOrigin::signed(mock::PauseOrigin::get()), full_name::(b"Utility", None) ),); assert_err!( - batch_call.clone().dispatch(Origin::signed(0)), + batch_call.clone().dispatch(RuntimeOrigin::signed(0)), frame_system::Error::::CallFiltered ); }); @@ -67,19 +67,19 @@ fn can_pause_all_calls_in_pallet_except_on_whitelist() { fn can_unpause_specific_call() { new_test_ext().execute_with(|| { assert_ok!(TxPause::pause( - Origin::signed(mock::PauseOrigin::get()), + RuntimeOrigin::signed(mock::PauseOrigin::get()), full_name::(b"Balances", Some(b"transfer")), )); assert_err!( - call_transfer(2, 1).dispatch(Origin::signed(2)), + call_transfer(2, 1).dispatch(RuntimeOrigin::signed(2)), frame_system::Error::::CallFiltered ); assert_ok!(TxPause::unpause( - Origin::signed(mock::UnpauseOrigin::get()), + RuntimeOrigin::signed(mock::UnpauseOrigin::get()), full_name::(b"Balances", Some(b"transfer")), )); - assert_ok!(call_transfer(4, 1).dispatch(Origin::signed(0))); + assert_ok!(call_transfer(4, 1).dispatch(RuntimeOrigin::signed(0))); }); } @@ -90,11 +90,11 @@ fn can_filter_balance_in_batch_when_paused() { RuntimeCall::Utility(pallet_utility::Call::batch { calls: vec![call_transfer(1, 1)] }); assert_ok!(TxPause::pause( - Origin::signed(mock::PauseOrigin::get()), + RuntimeOrigin::signed(mock::PauseOrigin::get()), full_name::(b"Balances", Some(b"transfer")), )); - assert_ok!(batch_call.clone().dispatch(Origin::signed(0))); + assert_ok!(batch_call.clone().dispatch(RuntimeOrigin::signed(0))); System::assert_last_event( pallet_utility::Event::BatchInterrupted { index: 0, @@ -109,13 +109,13 @@ fn can_filter_balance_in_batch_when_paused() { fn can_filter_balance_in_proxy_when_paused() { new_test_ext().execute_with(|| { assert_ok!(TxPause::pause( - Origin::signed(mock::PauseOrigin::get()), + RuntimeOrigin::signed(mock::PauseOrigin::get()), full_name::(b"Balances", Some(b"transfer")), )); - assert_ok!(Proxy::add_proxy(Origin::signed(1), 2, ProxyType::JustTransfer, 0)); + assert_ok!(Proxy::add_proxy(RuntimeOrigin::signed(1), 2, ProxyType::JustTransfer, 0)); - assert_ok!(Proxy::proxy(Origin::signed(2), 1, None, Box::new(call_transfer(1, 1)))); + assert_ok!(Proxy::proxy(RuntimeOrigin::signed(2), 1, None, Box::new(call_transfer(1, 1)))); System::assert_last_event( pallet_proxy::Event::ProxyExecuted { result: DispatchError::from(frame_system::Error::::CallFiltered).into(), @@ -132,7 +132,7 @@ fn fails_to_pause_self() { new_test_ext().execute_with(|| { assert_noop!( TxPause::pause( - Origin::signed(mock::PauseOrigin::get()), + RuntimeOrigin::signed(mock::PauseOrigin::get()), full_name::(b"TxPause", Some(b"pause")), ), Error::::IsUnpausable @@ -145,7 +145,7 @@ fn fails_to_pause_unpausable_pallet() { new_test_ext().execute_with(|| { assert_noop!( TxPause::pause( - Origin::signed(mock::PauseOrigin::get()), + RuntimeOrigin::signed(mock::PauseOrigin::get()), full_name::(b"DummyPallet", Some(b"any_call")), ), Error::::IsUnpausable @@ -156,20 +156,20 @@ fn fails_to_pause_unpausable_pallet() { #[test] fn fails_to_pause_unpausable_call_when_pallet_is_paused() { new_test_ext().execute_with(|| { - assert_ok!(call_transfer(1, 1).dispatch(Origin::signed(0))); + assert_ok!(call_transfer(1, 1).dispatch(RuntimeOrigin::signed(0))); let batch_call = RuntimeCall::Utility(pallet_utility::Call::batch { calls: vec![call_transfer(1, 1)] }); - assert_ok!(batch_call.clone().dispatch(Origin::signed(0))); + assert_ok!(batch_call.clone().dispatch(RuntimeOrigin::signed(0))); assert_ok!(TxPause::pause( - Origin::signed(mock::PauseOrigin::get()), + RuntimeOrigin::signed(mock::PauseOrigin::get()), full_name::(b"Balances", None), )); - assert_ok!(call_transfer_keep_alive(3, 1).dispatch(Origin::signed(3))); + assert_ok!(call_transfer_keep_alive(3, 1).dispatch(RuntimeOrigin::signed(3))); assert_err!( - call_transfer(2, 1).dispatch(Origin::signed(0)), + call_transfer(2, 1).dispatch(RuntimeOrigin::signed(0)), frame_system::Error::::CallFiltered ); }); @@ -180,7 +180,7 @@ fn fails_to_pause_unpausable_call() { new_test_ext().execute_with(|| { assert_noop!( TxPause::pause( - Origin::signed(mock::PauseOrigin::get()), + RuntimeOrigin::signed(mock::PauseOrigin::get()), full_name::(b"Balances", Some(b"transfer_keep_alive")), ), Error::::IsUnpausable @@ -192,13 +192,13 @@ fn fails_to_pause_unpausable_call() { fn fails_to_pause_already_paused_pallet() { new_test_ext().execute_with(|| { assert_ok!(TxPause::pause( - Origin::signed(mock::PauseOrigin::get()), + RuntimeOrigin::signed(mock::PauseOrigin::get()), full_name::(b"Balances", Some(b"transfer")), )); assert_noop!( TxPause::pause( - Origin::signed(mock::PauseOrigin::get()), + RuntimeOrigin::signed(mock::PauseOrigin::get()), full_name::(b"Balances", Some(b"transfer")), ), Error::::IsPaused @@ -211,7 +211,7 @@ fn fails_to_unpause_not_paused_pallet() { new_test_ext().execute_with(|| { assert_noop!( TxPause::unpause( - Origin::signed(mock::UnpauseOrigin::get()), + RuntimeOrigin::signed(mock::UnpauseOrigin::get()), full_name::(b"Balances", Some(b"transfer_keep_alive")), ), Error::::IsUnpaused From ec7aff6b303325b96a4f6fe9cf39ed253399d04b Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Mon, 6 Feb 2023 19:51:21 +0100 Subject: [PATCH 032/100] Compile Signed-off-by: Oliver Tale-Yazdi --- bin/node/runtime/src/lib.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index e09948a3b67bc..b981c6ac24480 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -337,8 +337,8 @@ impl, O>> + From>> Ensu } #[cfg(feature = "runtime-benchmarks")] - fn successful_origin() -> O { - O::from(RawOrigin::Signed(ForceActivateOrigin::Strong.acc())) + fn try_successful_origin() -> Result { + Ok(O::from(RawOrigin::Signed(ForceActivateOrigin::Strong.acc()))) } } @@ -360,8 +360,8 @@ impl, O>> + From>> Ensu } #[cfg(feature = "runtime-benchmarks")] - fn successful_origin() -> O { - O::from(RawOrigin::Signed(ForceActivateOrigin::Strong.acc())) + fn try_successful_origin() -> Result { + Ok(O::from(RawOrigin::Signed(ForceActivateOrigin::Strong.acc()))) } } From a53608c7fc5414914f6707c200cc61c0052ab13e Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Wed, 1 Mar 2023 17:41:29 +0100 Subject: [PATCH 033/100] Cleanup & renames Signed-off-by: Oliver Tale-Yazdi --- bin/node/runtime/src/lib.rs | 40 +-- frame/safe-mode/src/benchmarking.rs | 225 ++++++++++------ frame/safe-mode/src/lib.rs | 295 ++++++++++----------- frame/safe-mode/src/mock.rs | 48 ++-- frame/safe-mode/src/tests.rs | 356 +++++++++++++++++-------- frame/safe-mode/src/weights.rs | 393 ++++++++++++++++++---------- 6 files changed, 832 insertions(+), 525 deletions(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index b981c6ac24480..4e307630b4b9e 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -209,8 +209,8 @@ const_assert!(NORMAL_DISPATCH_RATIO.deconstruct() >= AVERAGE_ON_INITIALIZE_RATIO /// Filter to block balance pallet calls /// Used for both SafeMode and TxPause pallets /// Therefor we include both so they cannot affect each other -pub struct WhitelistCalls; -impl Contains for WhitelistCalls { +pub struct WhitelistedCalls; +impl Contains for WhitelistedCalls { fn contains(call: &RuntimeCall) -> bool { match call { RuntimeCall::System(_) | RuntimeCall::SafeMode(_) | RuntimeCall::TxPause(_) => true, @@ -256,7 +256,7 @@ impl pallet_tx_pause::Config for Runtime { } /// An origin that can enable the safe-mode by force. -pub enum ForceActivateOrigin { +pub enum ForceEnterOrigin { Weak, Medium, Strong, @@ -269,7 +269,7 @@ pub enum ForceExtendOrigin { Strong, } -impl ForceActivateOrigin { +impl ForceEnterOrigin { /// The duration of how long the safe-mode will be activated. pub fn duration(&self) -> u32 { match self { @@ -320,25 +320,25 @@ impl ForceExtendOrigin { } impl, O>> + From>> EnsureOrigin - for ForceActivateOrigin + for ForceEnterOrigin { type Success = u32; fn try_origin(o: O) -> Result { o.into().and_then(|o| match o { - RawOrigin::Signed(acc) if acc == ForceActivateOrigin::Weak.acc() => - Ok(ForceActivateOrigin::Weak.duration()), - RawOrigin::Signed(acc) if acc == ForceActivateOrigin::Medium.acc() => - Ok(ForceActivateOrigin::Medium.duration()), - RawOrigin::Signed(acc) if acc == ForceActivateOrigin::Strong.acc() => - Ok(ForceActivateOrigin::Strong.duration()), + RawOrigin::Signed(acc) if acc == ForceEnterOrigin::Weak.acc() => + Ok(ForceEnterOrigin::Weak.duration()), + RawOrigin::Signed(acc) if acc == ForceEnterOrigin::Medium.acc() => + Ok(ForceEnterOrigin::Medium.duration()), + RawOrigin::Signed(acc) if acc == ForceEnterOrigin::Strong.acc() => + Ok(ForceEnterOrigin::Strong.duration()), r => Err(O::from(r)), }) } #[cfg(feature = "runtime-benchmarks")] fn try_successful_origin() -> Result { - Ok(O::from(RawOrigin::Signed(ForceActivateOrigin::Strong.acc()))) + Ok(O::from(RawOrigin::Signed(ForceEnterOrigin::Strong.acc()))) } } @@ -361,12 +361,12 @@ impl, O>> + From>> Ensu #[cfg(feature = "runtime-benchmarks")] fn try_successful_origin() -> Result { - Ok(O::from(RawOrigin::Signed(ForceActivateOrigin::Strong.acc()))) + Ok(O::from(RawOrigin::Signed(ForceEnterOrigin::Strong.acc()))) } } parameter_types! { - pub const SignedActivationDuration: u32 = 10; + pub const SignedEnterDuration: u32 = 10; pub const ExtendDuration: u32 = 20; pub const ActivateReservationAmount: Balance = 10 * DOLLARS; pub const ExtendReservationAmount: Balance = 15 * DOLLARS; @@ -376,21 +376,21 @@ parameter_types! { impl pallet_safe_mode::Config for Runtime { type RuntimeEvent = RuntimeEvent; type Currency = Balances; - type WhitelistCalls = WhitelistCalls; - type ActivationDuration = ConstU32<{ 2 * DAYS }>; + type WhitelistedCalls = WhitelistedCalls; + type EnterDuration = ConstU32<{ 2 * DAYS }>; type ActivateReservationAmount = ActivateReservationAmount; type ExtendDuration = ConstU32<{ 1 * DAYS }>; type ExtendReservationAmount = ExtendReservationAmount; - type ForceActivateOrigin = ForceActivateOrigin; + type ForceEnterOrigin = ForceEnterOrigin; type ForceExtendOrigin = ForceExtendOrigin; - type ForceDeactivateOrigin = EnsureRoot; - type ForceReservationOrigin = EnsureRoot; + type ForceExitOrigin = EnsureRoot; + type ReservationSlashOrigin = EnsureRoot; type ReleaseDelay = ReleaseDelay; type WeightInfo = pallet_safe_mode::weights::SubstrateWeight; } impl frame_system::Config for Runtime { - type BaseCallFilter = InsideBoth; // TODO consider Exclude or NotInside for WhitelistCalls -> see TheseExcept ) + type BaseCallFilter = InsideBoth; // TODO consider Exclude or NotInside for WhitelistedCalls -> see TheseExcept ) type BlockWeights = RuntimeBlockWeights; type BlockLength = RuntimeBlockLength; type DbWeight = RocksDbWeight; diff --git a/frame/safe-mode/src/benchmarking.rs b/frame/safe-mode/src/benchmarking.rs index aebca74eda7f1..754061c7f3453 100644 --- a/frame/safe-mode/src/benchmarking.rs +++ b/frame/safe-mode/src/benchmarking.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); @@ -19,169 +19,238 @@ use super::{Pallet as SafeMode, *}; -use frame_benchmarking::{benchmarks, whitelisted_caller, BenchmarkError}; +use frame_benchmarking::v2::*; use frame_support::traits::{Currency, UnfilteredDispatchable}; use frame_system::{Pallet as System, RawOrigin}; -use sp_runtime::traits::{Bounded, One}; +use sp_runtime::traits::{Bounded, One, Zero}; -benchmarks! { - activate { +#[benchmarks] +mod benchmarks { + use super::*; + + /// `on_initialize` exiting since the until block is in the past. + #[benchmark] + fn on_initialize_exit() { + EnteredUntil::::put(&T::BlockNumber::zero()); + assert!(SafeMode::::is_entered()); + + #[block] + { + SafeMode::::on_initialize(1u32.into()); + } + } + + /// `on_initialize` doing nothing. + #[benchmark] + fn on_initialize_noop() { + #[block] + { + SafeMode::::on_initialize(1u32.into()); + } + } + + #[benchmark] + fn enter() { let caller: T::AccountId = whitelisted_caller(); let origin = RawOrigin::Signed(caller.clone()); T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); - }: _(origin) - verify { + + #[extrinsic_call] + _(origin); + assert_eq!( SafeMode::::active_until().unwrap(), - System::::block_number() + T::ActivationDuration::get() + System::::block_number() + T::EnterDuration::get() ); } - force_activate { - let force_origin = T::ForceActivateOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; - let duration = T::ForceActivateOrigin::ensure_origin(force_origin.clone()).unwrap(); - let call = Call::::force_activate {}; - }: { call.dispatch_bypass_filter(force_origin)? } - verify { - assert_eq!( - SafeMode::::active_until().unwrap(), - System::::block_number() + - duration - ); + #[benchmark] + fn force_enter() { + let force_origin = + T::ForceEnterOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; + let duration = T::ForceEnterOrigin::ensure_origin(force_origin.clone()).unwrap(); + let call = Call::::force_enter {}; + + #[block] + { + call.dispatch_bypass_filter(force_origin)?; + } + + assert_eq!(SafeMode::::active_until().unwrap(), System::::block_number() + duration); } - extend { + #[benchmark] + fn extend() { let caller: T::AccountId = whitelisted_caller(); let origin = RawOrigin::Signed(caller.clone()); T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); System::::set_block_number(1u32.into()); - assert!(SafeMode::::activate(origin.clone().into()).is_ok()); - }: _(origin) - verify { + assert!(SafeMode::::enter(origin.clone().into()).is_ok()); + + #[extrinsic_call] + _(origin); + assert_eq!( SafeMode::::active_until().unwrap(), - System::::block_number() + T::ActivationDuration::get() + T::ExtendDuration::get() + System::::block_number() + T::EnterDuration::get() + T::ExtendDuration::get() ); } - force_extend { + #[benchmark] + fn force_extend() -> Result<(), BenchmarkError> { let caller: T::AccountId = whitelisted_caller(); let origin = RawOrigin::Signed(caller.clone()); T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); System::::set_block_number(1u32.into()); - assert!(SafeMode::::activate(origin.clone().into()).is_ok()); + assert!(SafeMode::::enter(origin.clone().into()).is_ok()); - let force_origin = T::ForceExtendOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; + let force_origin = T::ForceExtendOrigin::try_successful_origin() + .map_err(|_| BenchmarkError::Weightless)?; let extension = T::ForceExtendOrigin::ensure_origin(force_origin.clone()).unwrap(); let call = Call::::force_extend {}; - }: { call.dispatch_bypass_filter(force_origin)? } - verify { + + #[block] + { + call.dispatch_bypass_filter(force_origin)?; + } + assert_eq!( SafeMode::::active_until().unwrap(), - System::::block_number() + T::ActivationDuration::get() + extension + System::::block_number() + T::EnterDuration::get() + extension ); } - force_deactivate { + #[benchmark] + fn force_exit() { let caller: T::AccountId = whitelisted_caller(); let origin = RawOrigin::Signed(caller.clone()); T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); - assert!(SafeMode::::activate(origin.clone().into()).is_ok()); + assert!(SafeMode::::enter(origin.clone().into()).is_ok()); - let force_origin = T::ForceDeactivateOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; - let call = Call::::force_deactivate {}; - }: { call.dispatch_bypass_filter(force_origin)? } - verify { - assert_eq!( - SafeMode::::active_until(), - None - ); + let force_origin = + T::ForceExitOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; + let call = Call::::force_exit {}; + + #[block] + { + call.dispatch_bypass_filter(force_origin)?; + } + + assert_eq!(SafeMode::::active_until(), None); } - release_reservation { + #[benchmark] + fn release_reservation() -> Result<(), BenchmarkError> { let caller: T::AccountId = whitelisted_caller(); let origin = RawOrigin::Signed(caller.clone()); T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); - let activated_at_block: T::BlockNumber = System::::block_number(); - assert!(SafeMode::::activate(origin.clone().into()).is_ok()); - let current_reservation = Reservations::::get(&caller, activated_at_block).unwrap_or_default(); + let enterd_at_block: T::BlockNumber = System::::block_number(); + assert!(SafeMode::::enter(origin.clone().into()).is_ok()); + let current_reservation = + Reservations::::get(&caller, enterd_at_block).unwrap_or_default(); assert_eq!( T::Currency::free_balance(&caller), BalanceOf::::max_value() - T::ActivateReservationAmount::get().unwrap() ); - let force_origin = T::ForceDeactivateOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; - assert!(SafeMode::::force_deactivate(force_origin.clone()).is_ok()); + let force_origin = + T::ForceExitOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; + assert!(SafeMode::::force_exit(force_origin.clone()).is_ok()); - System::::set_block_number(System::::block_number() + T::ReleaseDelay::get().unwrap() + One::one()); + System::::set_block_number( + System::::block_number() + T::ReleaseDelay::get().unwrap() + One::one(), + ); System::::on_initialize(System::::block_number()); SafeMode::::on_initialize(System::::block_number()); - let call = Call::::release_reservation { account: caller.clone(), block: activated_at_block.clone()}; - }: { call.dispatch_bypass_filter(origin.into())? } - verify { - assert_eq!( - T::Currency::free_balance(&caller), - BalanceOf::::max_value() - ); + let call = Call::::release_reservation { + account: caller.clone(), + block: enterd_at_block.clone(), + }; + + #[block] + { + call.dispatch_bypass_filter(origin.into())?; + } + + assert_eq!(T::Currency::free_balance(&caller), BalanceOf::::max_value()); } - force_release_reservation { + #[benchmark] + fn force_release_reservation() -> Result<(), BenchmarkError> { let caller: T::AccountId = whitelisted_caller(); let origin = RawOrigin::Signed(caller.clone()); T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); - let activated_at_block: T::BlockNumber = System::::block_number(); - assert!(SafeMode::::activate(origin.clone().into()).is_ok()); - let current_reservation = Reservations::::get(&caller, activated_at_block).unwrap_or_default(); + let enterd_at_block: T::BlockNumber = System::::block_number(); + assert!(SafeMode::::enter(origin.clone().into()).is_ok()); + let current_reservation = + Reservations::::get(&caller, enterd_at_block).unwrap_or_default(); assert_eq!( T::Currency::free_balance(&caller), BalanceOf::::max_value() - T::ActivateReservationAmount::get().unwrap() ); // TODO - let force_origin = T::ForceDeactivateOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; - assert!(SafeMode::::force_deactivate(force_origin.clone()).is_ok()); + let force_origin = + T::ForceExitOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; + assert!(SafeMode::::force_exit(force_origin.clone()).is_ok()); System::::set_block_number(System::::block_number() + One::one()); System::::on_initialize(System::::block_number()); SafeMode::::on_initialize(System::::block_number()); - let release_origin = T::ForceReservationOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; - let call = Call::::force_release_reservation { account: caller.clone(), block: activated_at_block.clone()}; - }: { call.dispatch_bypass_filter(release_origin)? } - verify { - assert_eq!( - T::Currency::free_balance(&caller), - BalanceOf::::max_value() - ); + let release_origin = T::ReservationSlashOrigin::try_successful_origin() + .map_err(|_| BenchmarkError::Weightless)?; + let call = Call::::force_release_reservation { + account: caller.clone(), + block: enterd_at_block.clone(), + }; + + #[block] + { + call.dispatch_bypass_filter(release_origin)?; + } + + assert_eq!(T::Currency::free_balance(&caller), BalanceOf::::max_value()); } - slash_reservation { + #[benchmark] + fn force_slash_reservation() -> Result<(), BenchmarkError> { let caller: T::AccountId = whitelisted_caller(); let origin = RawOrigin::Signed(caller.clone()); T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); - let activated_at_block: T::BlockNumber = System::::block_number(); - assert!(SafeMode::::activate(origin.clone().into()).is_ok()); - let current_reservation = Reservations::::get(&caller, activated_at_block).unwrap_or_default(); + let enterd_at_block: T::BlockNumber = System::::block_number(); + assert!(SafeMode::::enter(origin.clone().into()).is_ok()); + let current_reservation = + Reservations::::get(&caller, enterd_at_block).unwrap_or_default(); assert_eq!( T::Currency::free_balance(&caller), BalanceOf::::max_value() - T::ActivateReservationAmount::get().unwrap() ); // TODO - let force_origin = T::ForceDeactivateOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; - assert!(SafeMode::::force_deactivate(force_origin.clone()).is_ok()); + let force_origin = + T::ForceExitOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; + assert!(SafeMode::::force_exit(force_origin.clone()).is_ok()); + + let release_origin = T::ReservationSlashOrigin::try_successful_origin() + .map_err(|_| BenchmarkError::Weightless)?; + let call = Call::::force_slash_reservation { + account: caller.clone(), + block: enterd_at_block.clone(), + }; + + #[block] + { + call.dispatch_bypass_filter(release_origin)?; + } - let release_origin = T::ForceReservationOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; - let call = Call::::slash_reservation { account: caller.clone(), block: activated_at_block.clone()}; - }: { call.dispatch_bypass_filter(release_origin)? } - verify { assert_eq!( T::Currency::free_balance(&caller), BalanceOf::::max_value() - T::ActivateReservationAmount::get().unwrap() diff --git a/frame/safe-mode/src/lib.rs b/frame/safe-mode/src/lib.rs index 5d6e552b76b85..737a710c43653 100644 --- a/frame/safe-mode/src/lib.rs +++ b/frame/safe-mode/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); @@ -62,15 +62,15 @@ pub mod pallet { >; /// Contains all runtime calls in any pallet that can be dispatched even while the safe-mode - /// is active. + /// is entered. /// /// The safe-mode pallet cannot disable it's own calls, and does not need to be explicitly /// added here. - type WhitelistCalls: Contains; + type WhitelistedCalls: Contains; - /// How long the safe-mode will stay active with [`Pallet::activate`]. + /// For how many blocks the safe-mode will be entered by [`Pallet::enter`]. #[pallet::constant] - type ActivationDuration: Get; + type EnterDuration: Get; /// For how many blocks the safe-mode can be extended by each [`Pallet::extend`] call. /// @@ -78,7 +78,7 @@ pub mod pallet { #[pallet::constant] type ExtendDuration: Get; - /// The amount that will be reserved upon calling [`Pallet::activate`]. + /// The amount that will be reserved upon calling [`Pallet::enter`]. /// /// `None` disallows permissionlessly enabling the safe-mode. #[pallet::constant] @@ -90,29 +90,28 @@ pub mod pallet { #[pallet::constant] type ExtendReservationAmount: Get>>; - /// The origin that may call [`Pallet::force_activate`]. + /// The origin that may call [`Pallet::force_enter`]. /// - /// The `Success` value is the number of blocks that this origin can activate safe-mode for. - type ForceActivateOrigin: EnsureOrigin; + /// The `Success` value is the number of blocks that this origin can enter safe-mode for. + type ForceEnterOrigin: EnsureOrigin; /// The origin that may call [`Pallet::force_extend`]. /// /// The `Success` value is the number of blocks that this origin can extend the safe-mode. type ForceExtendOrigin: EnsureOrigin; - /// The origin that may call [`Pallet::force_activate`]. - type ForceDeactivateOrigin: EnsureOrigin; + /// The origin that may call [`Pallet::force_enter`]. + type ForceExitOrigin: EnsureOrigin; /// The origin that may call [`Pallet::force_release_reservation`] and /// [`Pallet::slash_reservation`]. - type ForceReservationOrigin: EnsureOrigin; + type ReservationSlashOrigin: EnsureOrigin; - /// The minimal duration a deposit will remain reserved after safe-mode is activated or - /// extended, unless [`Pallet::force_release_reservation`] is successfully dispatched - /// sooner. + /// The minimal duration a deposit will remain reserved after safe-mode is entered or + /// extended, unless [`Pallet::force_release_reservation`] is successfully called sooner. /// /// Every reservation is tied to a specific activation or extension, thus each reservation - /// can be release independently after the delay for it has passed. + /// can be released independently after the delay for it has passed. /// /// `None` disallows permissionlessly releasing the safe-mode reservations. #[pallet::constant] @@ -124,13 +123,13 @@ pub mod pallet { #[pallet::error] pub enum Error { - /// The safe-mode is (already or still) active. - IsActive, + /// The safe-mode is (already or still) entered. + Entered, - /// The safe-mode is (already or still) inactive. - IsInactive, + /// The safe-mode is (already or still) exited. + Exited, - /// A value that is required for the extrinsic was not configured. + /// This functionality of the pallet is disabled by the configuration. NotConfigured, /// There is no balance reserved. @@ -143,51 +142,49 @@ pub mod pallet { #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event { - /// The safe-mode was activated until inclusively this \[block\]. - Activated { block: T::BlockNumber }, + /// The safe-mode was entered until inclusively this block. + Entered { until: T::BlockNumber }, - /// The safe-mode was extended until inclusively this \[block\]. - Extended { block: T::BlockNumber }, + /// The safe-mode was extended until inclusively this block. + Extended { until: T::BlockNumber }, - /// Exited safe-mode for a specific \[reason\]. + /// Exited the safe-mode for a specific reason. Exited { reason: ExitReason }, - /// An account had a reserve released that was reserved at a specific block. \[account, - /// block, amount\] + /// An account had a reserve released that was reserved at a specific block. ReservationReleased { account: T::AccountId, block: T::BlockNumber, amount: BalanceOf }, - /// An account had reserve slashed that was reserved at a specific block. \[account, block, - /// amount\] + /// An account had reserve slashed that was reserved at a specific block. ReservationSlashed { account: T::AccountId, block: T::BlockNumber, amount: BalanceOf }, } - /// The reason why the safe-mode was exited. + /// The reason why the safe-mode was deactivated. #[derive(Copy, Clone, PartialEq, Eq, RuntimeDebug, Encode, Decode, TypeInfo, MaxEncodedLen)] pub enum ExitReason { - /// The safe-mode was automatically exited after its duration ran out. + /// The safe-mode was automatically deactivated after it's duration ran out. Timeout, - /// The safe-mode was forcefully exited by [`Pallet::force_deactivate`]. + /// The safe-mode was forcefully deactivated by [`Pallet::force_exit`]. Force, } - /// Contains the last block number that the safe-mode will stay activated. + /// Contains the last block number that the safe-mode will stay enter in. /// /// This is set to `None` if the safe-mode is inactive. /// The safe-mode is automatically deactivated when the current block number is greater than /// this. #[pallet::storage] #[pallet::getter(fn active_until)] - pub type ActiveUntil = StorageValue<_, T::BlockNumber, OptionQuery>; + pub type EnteredUntil = StorageValue<_, T::BlockNumber, OptionQuery>; - /// Holds the reserve that was reserved from a user at a specific block number. + /// Holds the reserve that was taken from an account at a specific block number. #[pallet::storage] #[pallet::getter(fn reserves)] pub type Reservations = StorageDoubleMap< _, Twox64Concat, T::AccountId, - Blake2_128Concat, + Twox64Concat, T::BlockNumber, BalanceOf, OptionQuery, @@ -196,7 +193,7 @@ pub mod pallet { /// Configure the initial state of this pallet in the genesis block. #[pallet::genesis_config] pub struct GenesisConfig { - pub active: Option, + pub entered_until: Option, pub _phantom: PhantomData, } @@ -205,26 +202,26 @@ pub mod pallet { // NOTE: `derive(Default)` does not work together with `#[pallet::genesis_config]`. // We therefore need to add a trivial default impl. fn default() -> Self { - Self { active: None, _phantom: PhantomData } + Self { entered_until: None, _phantom: PhantomData } } } #[pallet::genesis_build] impl GenesisBuild for GenesisConfig { fn build(&self) { - if let Some(block) = self.active { - ActiveUntil::::put(block); + if let Some(block) = self.entered_until { + EnteredUntil::::put(block); } } } #[pallet::call] impl Pallet { - /// Activate safe-mode permissionlessly for [`Config::ActivationDuration`] blocks. + /// Enter safe-mode permissionlessly for [`Config::EnterDuration`] blocks. /// /// Reserves [`Config::ActivateReservationAmount`] from the caller's account. - /// Emits an [`Event::Activated`] event on success. - /// Errors with [`Error::IsActive`] if the safe-mode is already activated. + /// Emits an [`Event::Entered`] event on success. + /// Errors with [`Error::Entered`] if the safe-mode is already entered. /// Errors with [`Error::NotConfigured`] if the reservation amount is `None` . /// /// ### Safety @@ -233,38 +230,35 @@ pub mod pallet { /// currency to reserve. This call can be disabled for all origins by configuring /// [`Config::ActivateReservationAmount`] to `None`. #[pallet::call_index(0)] - #[pallet::weight(T::WeightInfo::activate())] - pub fn activate(origin: OriginFor) -> DispatchResult { + #[pallet::weight(T::WeightInfo::enter())] + pub fn enter(origin: OriginFor) -> DispatchResult { let who = ensure_signed(origin)?; - Self::do_activate(Some(who), T::ActivationDuration::get()) + Self::do_enter(Some(who), T::EnterDuration::get()) } - /// Activate safe-mode by force for a per-origin configured number of blocks. - /// - /// Emits an [`Event::Activated`] event on success. - /// Errors with [`Error::IsActive`] if the safe-mode is already activated. + /// Enter safe-mode by force for a per-origin configured number of blocks. /// - /// ### Safety + /// Emits an [`Event::Entered`] event on success. + /// Errors with [`Error::Entered`] if the safe-mode is already entered. /// - /// Can only be called by the [`Config::ForceActivateOrigin`] origin. + /// Can only be called by the [`Config::ForceEnterOrigin`] origin. #[pallet::call_index(1)] - #[pallet::weight(T::WeightInfo::force_activate())] - pub fn force_activate(origin: OriginFor) -> DispatchResult { - let duration = T::ForceActivateOrigin::ensure_origin(origin)?; + #[pallet::weight(T::WeightInfo::force_enter())] + pub fn force_enter(origin: OriginFor) -> DispatchResult { + let duration = T::ForceEnterOrigin::ensure_origin(origin)?; - Self::do_activate(None, duration) + Self::do_enter(None, duration) } /// Extend the safe-mode permissionlessly for [`Config::ExtendDuration`] blocks. /// + /// This accumulates on top of the current remaining duration. /// Reserves [`Config::ExtendReservationAmount`] from the caller's account. /// Emits an [`Event::Extended`] event on success. - /// Errors with [`Error::IsInactive`] if the safe-mode is active. + /// Errors with [`Error::Exited`] if the safe-mode is entered. /// Errors with [`Error::NotConfigured`] if the reservation amount is `None` . /// - /// ### Safety - /// /// This may be called by any signed origin with [`Config::ExtendReservationAmount`] free /// currency to reserve. This call can be disabled for all origins by configuring /// [`Config::ExtendReservationAmount`] to `None`. @@ -276,12 +270,10 @@ pub mod pallet { Self::do_extend(Some(who), T::ExtendDuration::get()) } - /// Extend the safe-mode by force a per-origin configured number of blocks. + /// Extend the safe-mode by force for a per-origin configured number of blocks. /// /// Emits an [`Event::Extended`] event on success. - /// Errors with [`Error::IsInactive`] if the safe-mode is inactive. - /// - /// ### Safety + /// Errors with [`Error::Exited`] if the safe-mode is inactive. /// /// Can only be called by the [`Config::ForceExtendOrigin`] origin. #[pallet::call_index(3)] @@ -292,63 +284,60 @@ pub mod pallet { Self::do_extend(None, duration) } - /// Deactivate safe-mode by force. + /// Exit safe-mode by force. /// /// Emits an [`Event::Exited`] with [`ExitReason::Force`] event on success. - /// Errors with [`Error::IsInactive`] if the safe-mode is inactive. + /// Errors with [`Error::Exited`] if the safe-mode is inactive. /// /// Note: `safe-mode` will be automatically deactivated by [`Pallet::on_initialize`] hook - /// after the block height is greater than [`ActiveUntil`] found in storage. + /// after the block height is greater than [`EnteredUntil`] found in storage. /// Emits an [`Event::Exited`] with [`ExitReason::Timeout`] event on hook. /// /// /// ### Safety /// - /// Can only be called by the [`Config::ForceDeactivateOrigin`] origin. + /// Can only be called by the [`Config::ForceExitOrigin`] origin. #[pallet::call_index(4)] - #[pallet::weight(T::WeightInfo::force_deactivate())] - pub fn force_deactivate(origin: OriginFor) -> DispatchResult { - T::ForceDeactivateOrigin::ensure_origin(origin)?; + #[pallet::weight(T::WeightInfo::force_exit())] + pub fn force_exit(origin: OriginFor) -> DispatchResult { + T::ForceExitOrigin::ensure_origin(origin)?; Self::do_deactivate(ExitReason::Force) } - /// Slash a reservation for an account that activated or extended safe-mode at a specific - /// block earlier. This cannot be called while safe-mode is active. + /// Slash a reservation for an account that entered or extended safe-mode at a specific + /// block earlier. /// - /// Emits a [`Event::ReservationSlashed`] event on success. - /// Errors with [`Error::IsActive`] if the safe-mode is active. + /// This can be called while safe-mode is entered. /// - /// ### Safety + /// Emits a [`Event::ReservationSlashed`] event on success. + /// Errors with [`Error::Entered`] if the safe-mode is entered. /// - /// Can only be called by the [`Config::ForceReservationOrigin`] origin. + /// Can only be called by the [`Config::ReservationSlashOrigin`] origin. #[pallet::call_index(5)] - #[pallet::weight(T::WeightInfo::slash_reservation())] - pub fn slash_reservation( + #[pallet::weight(T::WeightInfo::force_slash_reservation())] + pub fn force_slash_reservation( origin: OriginFor, account: T::AccountId, block: T::BlockNumber, ) -> DispatchResult { - T::ForceReservationOrigin::ensure_origin(origin)?; + T::ReservationSlashOrigin::ensure_origin(origin)?; - Self::do_slash(account, block) + Self::do_force_slash(account, block) } - /// Release a currency reservation for an account that activated safe-mode at a specific - /// block earlier. This cannot be called while safe-mode is active and not until the + /// Permissionlessly release a reservation for an account that entered safe-mode at a + /// specific block earlier. + /// + /// The call can be completely disabled by setting [`Config::ReleaseDelay`] to `None`. + /// This cannot be called while safe-mode is entered and not until the /// [`Config::ReleaseDelay`] block height is passed. /// /// Emits a [`Event::ReservationReleased`] event on success. - /// Errors with [`Error::IsActive`] if the safe-mode is active. + /// Errors with [`Error::Entered`] if the safe-mode is entered. /// Errors with [`Error::CannotReleaseYet`] if the [`Config::ReleaseDelay`] . /// Errors with [`Error::NoReservation`] if the payee has no reserved currency at the /// block specified. - /// - /// ### Safety - /// - /// This may be called by any signed origin. - /// This call can be disabled for all origins by configuring - /// [`Config::ReleaseDelay`] to `None`. #[pallet::call_index(6)] #[pallet::weight(T::WeightInfo::release_reservation())] pub fn release_reservation( @@ -356,22 +345,22 @@ pub mod pallet { account: T::AccountId, block: T::BlockNumber, ) -> DispatchResult { - let who = ensure_signed(origin)?; + ensure_signed(origin)?; - Self::do_release(Some(who), account, block) + Self::do_release(false, account, block) } - /// Release a currency reservation for an account that activated safe-mode at a specific - /// block earlier. This cannot be called while safe-mode is active. + /// Force to release a reservation for an account that entered safe-mode at a specific + /// block earlier. + /// + /// This can be called while safe-mode is still entered. /// /// Emits a [`Event::ReservationReleased`] event on success. - /// Errors with [`Error::IsActive`] if the safe-mode is active. + /// Errors with [`Error::Entered`] if the safe-mode is entered. /// Errors with [`Error::NoReservation`] if the payee has no reserved currency at the /// block specified. /// - /// ### Safety - /// - /// Can only be called by the [`Config::ForceReservationOrigin`] origin. + /// Can only be called by the [`Config::ReservationSlashOrigin`] origin. #[pallet::call_index(7)] #[pallet::weight(T::WeightInfo::force_release_reservation())] pub fn force_release_reservation( @@ -379,65 +368,66 @@ pub mod pallet { account: T::AccountId, block: T::BlockNumber, ) -> DispatchResult { - T::ForceReservationOrigin::ensure_origin(origin)?; + T::ReservationSlashOrigin::ensure_origin(origin)?; - Self::do_release(None, account, block) + Self::do_release(true, account, block) } } #[pallet::hooks] impl Hooks> for Pallet { - /// Automatically deactivates the safe-mode when the period runs out. + /// Automatically exits the safe-mode when the period runs out. /// /// Bypasses any call filters to avoid getting rejected by them. fn on_initialize(current: T::BlockNumber) -> Weight { - match ActiveUntil::::get() { + match EnteredUntil::::get() { Some(limit) if current > limit => { - let _ = Self::do_deactivate(ExitReason::Timeout) - .defensive_proof("Must be inactive; qed"); - T::DbWeight::get().reads_writes(1, 1) + let _ = + Self::do_deactivate(ExitReason::Timeout).defensive_proof("Must exit; qed"); + T::WeightInfo::on_initialize_exit() }, - _ => T::DbWeight::get().reads(1), // TODO benchmark + _ => T::WeightInfo::on_initialize_noop(), } } } } impl Pallet { - /// Logic for the [`crate::Pallet::activate`] and [`crate::Pallet::force_activate`] calls. - fn do_activate(who: Option, duration: T::BlockNumber) -> DispatchResult { + /// Logic for the [`crate::Pallet::enter`] and [`crate::Pallet::force_enter`] calls. + fn do_enter(who: Option, duration: T::BlockNumber) -> DispatchResult { + ensure!(!EnteredUntil::::exists(), Error::::Entered); + if let Some(who) = who { let reserve = T::ActivateReservationAmount::get().ok_or(Error::::NotConfigured)?; Self::reserve(who, reserve)?; } - ensure!(!ActiveUntil::::exists(), Error::::IsActive); - let limit = >::block_number().saturating_add(duration); - ActiveUntil::::put(limit); - Self::deposit_event(Event::Activated { block: limit }); + let until = >::block_number().saturating_add(duration); + EnteredUntil::::put(until); + Self::deposit_event(Event::Entered { until }); Ok(()) } /// Logic for the [`crate::Pallet::extend`] and [`crate::Pallet::force_extend`] calls. fn do_extend(who: Option, duration: T::BlockNumber) -> DispatchResult { + let mut until = EnteredUntil::::get().ok_or(Error::::Exited)?; + if let Some(who) = who { let reserve = T::ExtendReservationAmount::get().ok_or(Error::::NotConfigured)?; Self::reserve(who, reserve)?; } - let limit = - ActiveUntil::::take().ok_or(Error::::IsInactive)?.saturating_add(duration); - ActiveUntil::::put(limit); - Self::deposit_event(Event::::Extended { block: limit }); + until.saturating_accrue(duration); + EnteredUntil::::put(until); + Self::deposit_event(Event::::Extended { until }); Ok(()) } - /// Logic for the [`crate::Pallet::force_deactivate`] call. + /// Logic for the [`crate::Pallet::force_exit`] call. /// - /// Errors if the safe-mode is already inactive. - /// Does not check the origin. + /// Errors if the safe-mode is already exited. fn do_deactivate(reason: ExitReason) -> DispatchResult { - let _limit = ActiveUntil::::take().ok_or(Error::::IsInactive)?; + let _until = EnteredUntil::::take().ok_or(Error::::Exited)?; Self::deposit_event(Event::Exited { reason }); Ok(()) } @@ -445,59 +435,46 @@ impl Pallet { /// Logic for the [`crate::Pallet::release_reservation`] and /// [`crate::Pallet::force_release_reservation`] calls. /// - /// Errors if the safe-mode is active with [`Error::IsActive`]. - /// Errors if release is called too soon by anyone but [`Config::ForceReservationOrigin`] with - /// [`Error::CannotReleaseYet`]. Does not check the origin. - fn do_release( - who: Option, - account: T::AccountId, - block: T::BlockNumber, - ) -> DispatchResult { - ensure!(!Self::is_active(), Error::::IsActive); - - let reserve = Reservations::::get(&account, &block).ok_or(Error::::NoReservation)?; - - if who.is_some() { + /// Errors if the safe-mode is entered with [`Error::Entered`] when `force` is `false`. + /// Errors if release is called too soon by anyone but [`Config::ReservationSlashOrigin`] with + /// [`Error::CannotReleaseYet`]. + fn do_release(force: bool, account: T::AccountId, block: T::BlockNumber) -> DispatchResult { + let amount = Reservations::::take(&account, &block).ok_or(Error::::NoReservation)?; + + if !force { + ensure!(!Self::is_entered(), Error::::Entered); + let delay = T::ReleaseDelay::get().ok_or(Error::::NotConfigured)?; let now = >::block_number(); ensure!(now > (block.saturating_add(delay)), Error::::CannotReleaseYet); } - Reservations::::remove(&account, &block); - T::Currency::unreserve_named(&block, &account, reserve); - Self::deposit_event(Event::::ReservationReleased { block, account, amount: reserve }); + T::Currency::unreserve_named(&block, &account, amount); + Self::deposit_event(Event::::ReservationReleased { block, account, amount }); Ok(()) } /// Logic for the [`crate::Pallet::slash_reservation`] call. - /// - /// Errors if the safe-mode is active with [`Error::IsActive`]. - /// Does not check the origin. - fn do_slash(account: T::AccountId, block: T::BlockNumber) -> DispatchResult { - ensure!(!Self::is_active(), Error::::IsActive); - let reserve = Reservations::::take(&account, block).ok_or(Error::::NoReservation)?; - - T::Currency::slash_reserved_named(&block, &account, reserve); - Self::deposit_event(Event::::ReservationSlashed { block, account, amount: reserve }); + fn do_force_slash(account: T::AccountId, block: T::BlockNumber) -> DispatchResult { + let amount = Reservations::::take(&account, block).ok_or(Error::::NoReservation)?; + + T::Currency::slash_reserved_named(&block, &account, amount); + Self::deposit_event(Event::::ReservationSlashed { block, account, amount }); Ok(()) } - /// Reserve `reserve` amount from `who` and store it in `Reservations`. - fn reserve(who: T::AccountId, reserve: BalanceOf) -> DispatchResult { + /// Reserve `amount` from `who` and store it in `Reservations`. + fn reserve(who: T::AccountId, amount: BalanceOf) -> DispatchResult { let block = >::block_number(); - T::Currency::reserve_named(&block, &who, reserve)?; + T::Currency::reserve_named(&block, &who, amount)?; let current_reservation = Reservations::::get(&who, block).unwrap_or_default(); - // Reservation is mapped to the block that an extrinsic calls activate or extend, - // therefore we prevent abuse in a block by adding to present value in the same block. TODO: - // should we? Why not just fail? Calls in other blocks to activate or extend are stored in a - // new Reservations item. - Reservations::::insert(&who, block, current_reservation.saturating_add(reserve)); + Reservations::::insert(&who, block, current_reservation.saturating_add(amount)); Ok(()) } - /// Return whether the `safe-mode` is active. - pub fn is_active() -> bool { - ActiveUntil::::exists() + /// Return whether the `safe-mode` is entered. + pub fn is_entered() -> bool { + EnteredUntil::::exists() } /// Return whether this call is allowed to be dispatched. @@ -506,13 +483,13 @@ impl Pallet { T::RuntimeCall: GetCallMetadata, { let CallMetadata { pallet_name, .. } = call.get_call_metadata(); - // The `SafeMode` pallet can always be dispatched. + // The `SafeMode` pallet is always allowed. if pallet_name == as PalletInfoAccess>::name() { return true } - if Self::is_active() { - T::WhitelistCalls::contains(call) + if Self::is_entered() { + T::WhitelistedCalls::contains(call) } else { true } diff --git a/frame/safe-mode/src/mock.rs b/frame/safe-mode/src/mock.rs index 23cd524f20bee..9a0969c0517f5 100644 --- a/frame/safe-mode/src/mock.rs +++ b/frame/safe-mode/src/mock.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); @@ -137,8 +137,8 @@ impl pallet_proxy::Config for Test { } /// Filter to allow all everything except balance calls -pub struct WhitelistCalls; -impl Contains for WhitelistCalls { +pub struct WhitelistedCalls; +impl Contains for WhitelistedCalls { fn contains(call: &RuntimeCall) -> bool { match call { RuntimeCall::Balances(_) => false, @@ -148,7 +148,7 @@ impl Contains for WhitelistCalls { } /// An origin that can enable the safe-mode by force. -pub enum ForceActivateOrigin { +pub enum ForceEnterOrigin { Weak, Medium, Strong, @@ -161,7 +161,7 @@ pub enum ForceExtendOrigin { Strong, } -impl ForceActivateOrigin { +impl ForceEnterOrigin { /// The duration of how long the safe-mode will be activated. pub fn duration(&self) -> u64 { match self { @@ -212,25 +212,25 @@ impl ForceExtendOrigin { } impl, O>> + From>> EnsureOrigin - for ForceActivateOrigin + for ForceEnterOrigin { type Success = u64; fn try_origin(o: O) -> Result { o.into().and_then(|o| match o { - RawOrigin::Signed(acc) if acc == ForceActivateOrigin::Weak.acc() => - Ok(ForceActivateOrigin::Weak.duration()), - RawOrigin::Signed(acc) if acc == ForceActivateOrigin::Medium.acc() => - Ok(ForceActivateOrigin::Medium.duration()), - RawOrigin::Signed(acc) if acc == ForceActivateOrigin::Strong.acc() => - Ok(ForceActivateOrigin::Strong.duration()), + RawOrigin::Signed(acc) if acc == ForceEnterOrigin::Weak.acc() => + Ok(ForceEnterOrigin::Weak.duration()), + RawOrigin::Signed(acc) if acc == ForceEnterOrigin::Medium.acc() => + Ok(ForceEnterOrigin::Medium.duration()), + RawOrigin::Signed(acc) if acc == ForceEnterOrigin::Strong.acc() => + Ok(ForceEnterOrigin::Strong.duration()), r => Err(O::from(r)), }) } #[cfg(feature = "runtime-benchmarks")] fn try_successful_origin() -> Result { - Ok(O::from(RawOrigin::Signed(ForceActivateOrigin::Strong.acc()))) + Ok(O::from(RawOrigin::Signed(ForceEnterOrigin::Strong.acc()))) } } @@ -258,24 +258,24 @@ impl, O>> + From>> EnsureOrigin } parameter_types! { - pub const ActivationDuration: u64 = 3; + pub const EnterDuration: u64 = 3; pub const ExtendDuration: u64 = 30; pub const ActivateReservationAmount: u64 = 100; pub const ExtendReservationAmount: u64 = 100; - pub const ForceDeactivateOrigin: u64 = 3; - pub const ForceReservationOrigin: u64 = 4; + pub const ForceExitOrigin: u64 = 3; + pub const ReservationSlashOrigin: u64 = 4; pub const ReleaseDelay: u64 = 2; } // Required impl to use some ::get() in tests -impl SortedMembers for ForceDeactivateOrigin { +impl SortedMembers for ForceExitOrigin { fn sorted_members() -> Vec { vec![Self::get()] } #[cfg(feature = "runtime-benchmarks")] fn add(_m: &u64) {} } -impl SortedMembers for ForceReservationOrigin { +impl SortedMembers for ReservationSlashOrigin { fn sorted_members() -> Vec { vec![Self::get()] } @@ -286,15 +286,15 @@ impl SortedMembers for ForceReservationOrigin { impl Config for Test { type RuntimeEvent = RuntimeEvent; type Currency = Balances; - type WhitelistCalls = WhitelistCalls; - type ActivationDuration = ActivationDuration; + type WhitelistedCalls = WhitelistedCalls; + type EnterDuration = EnterDuration; type ActivateReservationAmount = ActivateReservationAmount; type ExtendDuration = ExtendDuration; type ExtendReservationAmount = ExtendReservationAmount; - type ForceActivateOrigin = ForceActivateOrigin; + type ForceEnterOrigin = ForceEnterOrigin; type ForceExtendOrigin = ForceExtendOrigin; - type ForceDeactivateOrigin = EnsureSignedBy; - type ForceReservationOrigin = EnsureSignedBy; + type ForceExitOrigin = EnsureSignedBy; + type ReservationSlashOrigin = EnsureSignedBy; type ReleaseDelay = ReleaseDelay; type WeightInfo = (); } @@ -327,7 +327,7 @@ pub fn new_test_ext() -> sp_io::TestExternalities { .unwrap(); GenesisBuild::::assimilate_storage( - &pallet_safe_mode::GenesisConfig { active: None, _phantom: Default::default() }, + &pallet_safe_mode::GenesisConfig { entered_until: None, _phantom: Default::default() }, &mut t, ) .unwrap(); diff --git a/frame/safe-mode/src/tests.rs b/frame/safe-mode/src/tests.rs index 950faf13d9543..44073a5d57f31 100644 --- a/frame/safe-mode/src/tests.rs +++ b/frame/safe-mode/src/tests.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); @@ -21,13 +21,36 @@ use super::*; use crate::mock::{RuntimeCall, *}; use frame_support::{assert_err, assert_noop, assert_ok, dispatch::Dispatchable}; +use sp_runtime::TransactionOutcome; + +/// Do something hypothetically by rolling back any changes afterwards. +/// +/// Returns the original result of the closure. +macro_rules! hypothetically { + ( $e:expr ) => { + frame_support::storage::transactional::with_transaction( + || -> TransactionOutcome> { + sp_runtime::TransactionOutcome::Rollback(Ok($e)) + }, + ) + .expect("Always returning Ok; qed") + }; +} + +/// Assert something to be [*hypothetically*] `Ok`. +macro_rules! hypothetically_ok { + ($e:expr $(, $args:expr)* $(,)?) => { + let result = hypothetically!($e); + assert_ok!(result $(, $args)*); + }; +} // GENERAL FAIL/NEGATIVE TESTS --------------------- #[test] fn fails_to_filter_calls_to_safe_mode_pallet() { new_test_ext().execute_with(|| { - assert_ok!(SafeMode::activate(RuntimeOrigin::signed(0))); + assert_ok!(SafeMode::enter(RuntimeOrigin::signed(0))); let activated_at_block = System::block_number(); assert_err!( @@ -43,26 +66,22 @@ fn fails_to_filter_calls_to_safe_mode_pallet() { call_transfer().dispatch(RuntimeOrigin::signed(0)), frame_system::Error::::CallFiltered ); - assert_ok!(SafeMode::force_deactivate(RuntimeOrigin::signed( - mock::ForceDeactivateOrigin::get() - ))); + assert_ok!(SafeMode::force_exit(RuntimeOrigin::signed(mock::ForceExitOrigin::get()))); assert_ok!(SafeMode::force_release_reservation( - RuntimeOrigin::signed(mock::ForceReservationOrigin::get()), + RuntimeOrigin::signed(mock::ReservationSlashOrigin::get()), 0, activated_at_block )); next_block(); - assert_ok!(SafeMode::activate(RuntimeOrigin::signed(0))); + assert_ok!(SafeMode::enter(RuntimeOrigin::signed(0))); assert_err!( call_transfer().dispatch(RuntimeOrigin::signed(0)), frame_system::Error::::CallFiltered ); - assert_ok!(SafeMode::force_deactivate(RuntimeOrigin::signed( - mock::ForceDeactivateOrigin::get() - ))); - assert_ok!(SafeMode::slash_reservation( - RuntimeOrigin::signed(mock::ForceReservationOrigin::get()), + assert_ok!(SafeMode::force_exit(RuntimeOrigin::signed(mock::ForceExitOrigin::get()))); + assert_ok!(SafeMode::force_slash_reservation( + RuntimeOrigin::signed(mock::ReservationSlashOrigin::get()), 0, activated_at_block + 2 )); @@ -72,8 +91,8 @@ fn fails_to_filter_calls_to_safe_mode_pallet() { #[test] fn fails_to_activate_if_activated() { new_test_ext().execute_with(|| { - assert_ok!(SafeMode::activate(RuntimeOrigin::signed(0))); - assert_noop!(SafeMode::activate(RuntimeOrigin::signed(2)), Error::::IsActive); + assert_ok!(SafeMode::enter(RuntimeOrigin::signed(0))); + assert_noop!(SafeMode::enter(RuntimeOrigin::signed(2)), Error::::Entered); }); } @@ -81,7 +100,7 @@ fn fails_to_activate_if_activated() { fn fails_to_extend_if_not_activated() { new_test_ext().execute_with(|| { assert_eq!(SafeMode::active_until(), None); - assert_noop!(SafeMode::extend(RuntimeOrigin::signed(2)), Error::::IsInactive); + assert_noop!(SafeMode::extend(RuntimeOrigin::signed(2)), Error::::Exited); }); } @@ -89,12 +108,12 @@ fn fails_to_extend_if_not_activated() { fn fails_to_force_release_reservations_with_wrong_block() { new_test_ext().execute_with(|| { let activated_at_block = System::block_number(); - assert_ok!(SafeMode::activate(RuntimeOrigin::signed(0))); - run_to(mock::ActivationDuration::get() + activated_at_block + 1); + assert_ok!(SafeMode::enter(RuntimeOrigin::signed(0))); + run_to(mock::EnterDuration::get() + activated_at_block + 1); assert_err!( SafeMode::force_release_reservation( - RuntimeOrigin::signed(mock::ForceReservationOrigin::get()), + RuntimeOrigin::signed(mock::ReservationSlashOrigin::get()), 0, activated_at_block + 1 ), @@ -102,8 +121,8 @@ fn fails_to_force_release_reservations_with_wrong_block() { ); assert_err!( - SafeMode::slash_reservation( - RuntimeOrigin::signed(mock::ForceReservationOrigin::get()), + SafeMode::force_slash_reservation( + RuntimeOrigin::signed(mock::ReservationSlashOrigin::get()), 0, activated_at_block + 1 ), @@ -116,10 +135,8 @@ fn fails_to_force_release_reservations_with_wrong_block() { fn fails_to_release_reservations_too_early() { new_test_ext().execute_with(|| { let activated_at_block = System::block_number(); - assert_ok!(SafeMode::activate(RuntimeOrigin::signed(0))); - assert_ok!(SafeMode::force_deactivate(RuntimeOrigin::signed( - mock::ForceDeactivateOrigin::get() - ))); + assert_ok!(SafeMode::enter(RuntimeOrigin::signed(0))); + assert_ok!(SafeMode::force_exit(RuntimeOrigin::signed(mock::ForceExitOrigin::get()))); assert_err!( SafeMode::release_reservation(RuntimeOrigin::signed(2), 0, activated_at_block), Error::::CannotReleaseYet @@ -135,8 +152,8 @@ fn fails_to_release_reservations_too_early() { fn can_automatically_deactivate_after_timeout() { new_test_ext().execute_with(|| { let activated_at_block = System::block_number(); - assert_ok!(SafeMode::force_activate(ForceActivateOrigin::Weak.signed())); - run_to(ForceActivateOrigin::Weak.duration() + activated_at_block + 1); + assert_ok!(SafeMode::force_enter(ForceEnterOrigin::Weak.signed())); + run_to(ForceEnterOrigin::Weak.duration() + activated_at_block + 1); assert_eq!(SafeMode::active_until(), None); }); @@ -146,7 +163,7 @@ fn can_automatically_deactivate_after_timeout() { fn can_filter_balance_calls_when_activated() { new_test_ext().execute_with(|| { assert_ok!(call_transfer().dispatch(RuntimeOrigin::signed(0))); - assert_ok!(SafeMode::activate(RuntimeOrigin::signed(0))); + assert_ok!(SafeMode::enter(RuntimeOrigin::signed(0))); assert_err!( call_transfer().dispatch(RuntimeOrigin::signed(0)), frame_system::Error::::CallFiltered @@ -163,7 +180,7 @@ fn can_filter_balance_in_batch_when_activated() { assert_ok!(batch_call.clone().dispatch(RuntimeOrigin::signed(0))); - assert_ok!(SafeMode::activate(RuntimeOrigin::signed(0))); + assert_ok!(SafeMode::enter(RuntimeOrigin::signed(0))); assert_ok!(batch_call.clone().dispatch(RuntimeOrigin::signed(0))); System::assert_last_event( @@ -184,7 +201,7 @@ fn can_filter_balance_in_proxy_when_activated() { assert_ok!(Proxy::proxy(RuntimeOrigin::signed(2), 1, None, Box::new(call_transfer()))); System::assert_last_event(pallet_proxy::Event::ProxyExecuted { result: Ok(()) }.into()); - assert_ok!(SafeMode::force_activate(ForceActivateOrigin::Weak.signed())); + assert_ok!(SafeMode::force_enter(ForceEnterOrigin::Weak.signed())); assert_ok!(Proxy::proxy(RuntimeOrigin::signed(2), 1, None, Box::new(call_transfer()))); System::assert_last_event( @@ -199,13 +216,13 @@ fn can_filter_balance_in_proxy_when_activated() { #[test] fn can_activate() { new_test_ext().execute_with(|| { - assert_ok!(SafeMode::activate(RuntimeOrigin::signed(0))); + assert_ok!(SafeMode::enter(RuntimeOrigin::signed(0))); assert_eq!( SafeMode::active_until().unwrap(), - System::block_number() + mock::ActivationDuration::get() + System::block_number() + mock::EnterDuration::get() ); assert_eq!(Balances::reserved_balance(0), mock::ActivateReservationAmount::get()); - assert_noop!(SafeMode::activate(RuntimeOrigin::signed(0)), Error::::IsActive); + assert_noop!(SafeMode::enter(RuntimeOrigin::signed(0)), Error::::Entered); // Assert the stake. assert_eq!(Reservations::::get(0, 1), Some(mock::ActivateReservationAmount::get())); }); @@ -214,11 +231,11 @@ fn can_activate() { #[test] fn can_extend() { new_test_ext().execute_with(|| { - assert_ok!(SafeMode::activate(RuntimeOrigin::signed(0))); + assert_ok!(SafeMode::enter(RuntimeOrigin::signed(0))); assert_ok!(SafeMode::extend(RuntimeOrigin::signed(0))); assert_eq!( SafeMode::active_until().unwrap(), - System::block_number() + mock::ActivationDuration::get() + mock::ExtendDuration::get() + System::block_number() + mock::EnterDuration::get() + mock::ExtendDuration::get() ); assert_eq!( Balances::reserved_balance(0), @@ -227,24 +244,37 @@ fn can_extend() { }); } +#[test] +fn can_extend_twice_in_same_block() { + new_test_ext().execute_with(|| { + assert_ok!(SafeMode::enter(RuntimeOrigin::signed(0))); + assert_ok!(SafeMode::extend(RuntimeOrigin::signed(0))); + assert_ok!(SafeMode::extend(RuntimeOrigin::signed(0))); + assert_eq!( + SafeMode::active_until().unwrap(), + System::block_number() + mock::EnterDuration::get() + mock::ExtendDuration::get() * 2 + ); + assert_eq!( + Balances::reserved_balance(0), + mock::ActivateReservationAmount::get() + mock::ExtendReservationAmount::get() * 2 + ); + }); +} + #[test] fn can_release_independent_reservations_by_block() { new_test_ext().execute_with(|| { let activated_at_block_0 = System::block_number(); - assert_ok!(SafeMode::activate(RuntimeOrigin::signed(0))); + assert_ok!(SafeMode::enter(RuntimeOrigin::signed(0))); - run_to( - mock::ActivationDuration::get() + mock::ReleaseDelay::get() + activated_at_block_0 + 1, - ); + run_to(mock::EnterDuration::get() + mock::ReleaseDelay::get() + activated_at_block_0 + 1); let activated_at_block_1 = System::block_number(); - assert_ok!(SafeMode::activate(RuntimeOrigin::signed(0))); + assert_ok!(SafeMode::enter(RuntimeOrigin::signed(0))); assert_eq!(Balances::free_balance(&0), 1234 - (2 * mock::ActivateReservationAmount::get())); // accounts set in mock genesis - assert_ok!(SafeMode::force_deactivate(RuntimeOrigin::signed( - mock::ForceDeactivateOrigin::get() - ))); + assert_ok!(SafeMode::force_exit(RuntimeOrigin::signed(mock::ForceExitOrigin::get()))); assert_ok!(SafeMode::release_reservation( RuntimeOrigin::signed(2), @@ -257,9 +287,7 @@ fn can_release_independent_reservations_by_block() { ); assert_eq!(Balances::free_balance(&0), 1234 - mock::ActivateReservationAmount::get()); // accounts set in mock genesis - run_to( - mock::ActivationDuration::get() + mock::ReleaseDelay::get() + activated_at_block_1 + 1, - ); + run_to(mock::EnterDuration::get() + mock::ReleaseDelay::get() + activated_at_block_1 + 1); assert_ok!(SafeMode::release_reservation( RuntimeOrigin::signed(2), @@ -276,11 +304,11 @@ fn fails_signed_origin_when_explicit_origin_required() { assert_eq!(SafeMode::active_until(), None); let activated_at_block = System::block_number(); - assert_err!(SafeMode::force_activate(RuntimeOrigin::signed(0)), DispatchError::BadOrigin); + assert_err!(SafeMode::force_enter(RuntimeOrigin::signed(0)), DispatchError::BadOrigin); assert_err!(SafeMode::force_extend(RuntimeOrigin::signed(0)), DispatchError::BadOrigin); - assert_err!(SafeMode::force_deactivate(RuntimeOrigin::signed(0)), DispatchError::BadOrigin); + assert_err!(SafeMode::force_exit(RuntimeOrigin::signed(0)), DispatchError::BadOrigin); assert_err!( - SafeMode::slash_reservation(RuntimeOrigin::signed(0), 0, activated_at_block), + SafeMode::force_slash_reservation(RuntimeOrigin::signed(0), 0, activated_at_block), DispatchError::BadOrigin ); assert_err!( @@ -296,12 +324,12 @@ fn fails_signed_origin_when_explicit_origin_required() { fn fails_force_deactivate_if_not_activated() { new_test_ext().execute_with(|| { assert_noop!( - SafeMode::force_deactivate(RuntimeOrigin::signed(mock::ForceDeactivateOrigin::get())), - Error::::IsInactive + SafeMode::force_exit(RuntimeOrigin::signed(mock::ForceExitOrigin::get())), + Error::::Exited ); assert_noop!( - SafeMode::force_deactivate(RuntimeOrigin::signed(mock::ForceDeactivateOrigin::get())), - Error::::IsInactive + SafeMode::force_exit(RuntimeOrigin::signed(mock::ForceExitOrigin::get())), + Error::::Exited ); }); } @@ -309,16 +337,16 @@ fn fails_force_deactivate_if_not_activated() { #[test] fn can_force_activate_with_config_origin() { new_test_ext().execute_with(|| { - assert_ok!(SafeMode::force_activate(ForceActivateOrigin::Weak.signed())); + assert_ok!(SafeMode::force_enter(ForceEnterOrigin::Weak.signed())); assert_eq!( SafeMode::active_until().unwrap(), - System::block_number() + ForceActivateOrigin::Weak.duration() + System::block_number() + ForceEnterOrigin::Weak.duration() ); assert_noop!( - SafeMode::force_activate(ForceActivateOrigin::Weak.signed()), - Error::::IsActive + SafeMode::force_enter(ForceEnterOrigin::Weak.signed()), + Error::::Entered ); - assert_eq!(Balances::reserved_balance(ForceActivateOrigin::Weak.acc()), 0); + assert_eq!(Balances::reserved_balance(ForceEnterOrigin::Weak.acc()), 0); }); } @@ -327,14 +355,12 @@ fn can_force_deactivate_with_config_origin() { new_test_ext().execute_with(|| { assert_eq!(SafeMode::active_until(), None); assert_err!( - SafeMode::force_deactivate(RuntimeOrigin::signed(mock::ForceDeactivateOrigin::get())), - Error::::IsInactive - ); - assert_ok!(SafeMode::force_activate(ForceActivateOrigin::Weak.signed())); - assert_eq!(Balances::reserved_balance(ForceActivateOrigin::Weak.acc()), 0); - assert_ok!(SafeMode::force_deactivate(RuntimeOrigin::signed( - mock::ForceDeactivateOrigin::get() - ))); + SafeMode::force_exit(RuntimeOrigin::signed(mock::ForceExitOrigin::get())), + Error::::Exited + ); + assert_ok!(SafeMode::force_enter(ForceEnterOrigin::Weak.signed())); + assert_eq!(Balances::reserved_balance(ForceEnterOrigin::Weak.acc()), 0); + assert_ok!(SafeMode::force_exit(RuntimeOrigin::signed(mock::ForceExitOrigin::get()))); }); } @@ -342,19 +368,19 @@ fn can_force_deactivate_with_config_origin() { fn can_force_extend_with_config_origin() { new_test_ext().execute_with(|| { // Activated by `Weak` and extended by `Medium`. - assert_ok!(SafeMode::force_activate(ForceActivateOrigin::Weak.signed())); + assert_ok!(SafeMode::force_enter(ForceEnterOrigin::Weak.signed())); assert_eq!( SafeMode::active_until().unwrap(), - System::block_number() + ForceActivateOrigin::Weak.duration() + System::block_number() + ForceEnterOrigin::Weak.duration() ); assert_ok!(SafeMode::force_extend(ForceExtendOrigin::Medium.signed())); assert_eq!( SafeMode::active_until().unwrap(), System::block_number() + - ForceActivateOrigin::Weak.duration() + + ForceEnterOrigin::Weak.duration() + ForceExtendOrigin::Medium.duration() ); - assert_eq!(Balances::reserved_balance(ForceActivateOrigin::Weak.acc()), 0); + assert_eq!(Balances::reserved_balance(ForceEnterOrigin::Weak.acc()), 0); assert_eq!(Balances::reserved_balance(mock::ExtendDuration::get()), 0); }); } @@ -363,19 +389,16 @@ fn can_force_extend_with_config_origin() { fn can_force_release_reservation_with_config_origin() { new_test_ext().execute_with(|| { let activated_at_block = System::block_number(); - assert_ok!(SafeMode::activate(RuntimeOrigin::signed(0))); - assert_err!( - SafeMode::force_release_reservation( - RuntimeOrigin::signed(mock::ForceReservationOrigin::get()), - 0, - activated_at_block - ), - Error::::IsActive - ); - run_to(mock::ActivationDuration::get() + activated_at_block + 1); + assert_ok!(SafeMode::enter(RuntimeOrigin::signed(0))); + hypothetically_ok!(SafeMode::force_release_reservation( + RuntimeOrigin::signed(mock::ReservationSlashOrigin::get()), + 0, + activated_at_block + ),); + run_to(mock::EnterDuration::get() + activated_at_block + 1); assert_ok!(SafeMode::force_release_reservation( - RuntimeOrigin::signed(mock::ForceReservationOrigin::get()), + RuntimeOrigin::signed(mock::ReservationSlashOrigin::get()), 0, activated_at_block )); @@ -383,17 +406,17 @@ fn can_force_release_reservation_with_config_origin() { Balances::make_free_balance_be(&0, 1234); let activated_and_extended_at_block = System::block_number(); - assert_ok!(SafeMode::activate(RuntimeOrigin::signed(0))); + assert_ok!(SafeMode::enter(RuntimeOrigin::signed(0))); assert_ok!(SafeMode::extend(RuntimeOrigin::signed(0))); run_to( - mock::ActivationDuration::get() + + mock::EnterDuration::get() + mock::ExtendDuration::get() + activated_and_extended_at_block + 1, ); assert_ok!(SafeMode::force_release_reservation( - RuntimeOrigin::signed(mock::ForceReservationOrigin::get()), + RuntimeOrigin::signed(mock::ReservationSlashOrigin::get()), 0, activated_and_extended_at_block )); @@ -402,22 +425,123 @@ fn can_force_release_reservation_with_config_origin() { } #[test] -fn can_slash_reservation_with_config_origin() { +fn can_release_reservation_while_entered() { new_test_ext().execute_with(|| { - let activated_at_block = System::block_number(); - assert_ok!(SafeMode::activate(RuntimeOrigin::signed(0))); + assert_eq!(System::block_number(), 1); + assert_ok!(SafeMode::enter(RuntimeOrigin::signed(0))); + assert!(SafeMode::is_entered()); + + assert_eq!(Balances::free_balance(&0), 1234 - mock::ActivateReservationAmount::get()); + + // We could slash in the same block or any later. + for i in 0..mock::EnterDuration::get() + 10 { + run_to(i); + hypothetically_ok!(SafeMode::force_release_reservation( + RuntimeOrigin::signed(mock::ReservationSlashOrigin::get()), + 0, + 1 + )); + } + // Now once we slash once + assert_ok!(SafeMode::force_release_reservation( + RuntimeOrigin::signed(mock::ReservationSlashOrigin::get()), + 0, + 1 + ),); + assert_eq!(Balances::free_balance(&0), 1234); + // ... it wont work ever again. assert_err!( - SafeMode::slash_reservation( - RuntimeOrigin::signed(mock::ForceReservationOrigin::get()), + SafeMode::force_release_reservation( + RuntimeOrigin::signed(mock::ReservationSlashOrigin::get()), 0, - activated_at_block + 1 + ), + Error::::NoReservation + ); + }); +} + +#[test] +fn can_slash_reservation_while_entered() { + new_test_ext().execute_with(|| { + assert_eq!(System::block_number(), 1); + assert_ok!(SafeMode::enter(RuntimeOrigin::signed(0))); + assert!(SafeMode::is_entered()); + + // We could slash in the same block or any later. + for i in 0..mock::EnterDuration::get() + 10 { + run_to(i); + hypothetically_ok!(SafeMode::force_slash_reservation( + RuntimeOrigin::signed(mock::ReservationSlashOrigin::get()), + 0, + 1 + )); + } + // Now once we slash once + assert_ok!(SafeMode::force_slash_reservation( + RuntimeOrigin::signed(mock::ReservationSlashOrigin::get()), + 0, + 1 + ),); + // ... it wont work ever again. + assert_err!( + SafeMode::force_slash_reservation( + RuntimeOrigin::signed(mock::ReservationSlashOrigin::get()), + 0, + 1 + ), + Error::::NoReservation + ); + }); +} + +#[test] +fn can_slash_reservation_from_extend_block() { + new_test_ext().execute_with(|| { + assert_ok!(SafeMode::enter(RuntimeOrigin::signed(0))); + assert_ok!(SafeMode::extend(RuntimeOrigin::signed(0))); + assert_eq!( + Balances::free_balance(&0), + 1234 - mock::ActivateReservationAmount::get() - mock::ExtendReservationAmount::get() + ); + + // Now once we slash once since the enter and extend are treated as one reservation. + assert_ok!(SafeMode::force_slash_reservation( + RuntimeOrigin::signed(mock::ReservationSlashOrigin::get()), + 0, + 1 + ),); + assert_eq!( + Balances::free_balance(&0), + 1234 - mock::ExtendReservationAmount::get() - mock::ActivateReservationAmount::get() + ); + + // But never again. + assert_err!( + SafeMode::force_slash_reservation( + RuntimeOrigin::signed(mock::ReservationSlashOrigin::get()), + 0, + 1 ), - Error::::IsActive + Error::::NoReservation ); - run_to(mock::ActivationDuration::get() + activated_at_block + 1); + }); +} + +#[test] +fn can_slash_reservation_with_config_origin() { + new_test_ext().execute_with(|| { + let activated_at_block = System::block_number(); + assert_ok!(SafeMode::enter(RuntimeOrigin::signed(0))); + hypothetically_ok!(SafeMode::force_slash_reservation( + RuntimeOrigin::signed(mock::ReservationSlashOrigin::get()), + 0, + activated_at_block + ),); + run_to(mock::EnterDuration::get() + activated_at_block + 1); - assert_ok!(SafeMode::slash_reservation( - RuntimeOrigin::signed(mock::ForceReservationOrigin::get()), + assert_ok!(SafeMode::force_slash_reservation( + RuntimeOrigin::signed(mock::ReservationSlashOrigin::get()), 0, activated_at_block )); @@ -425,17 +549,17 @@ fn can_slash_reservation_with_config_origin() { Balances::make_free_balance_be(&0, 1234); let activated_and_extended_at_block = System::block_number(); - assert_ok!(SafeMode::activate(RuntimeOrigin::signed(0))); + assert_ok!(SafeMode::enter(RuntimeOrigin::signed(0))); assert_ok!(SafeMode::extend(RuntimeOrigin::signed(0))); run_to( - mock::ActivationDuration::get() + + mock::EnterDuration::get() + mock::ExtendDuration::get() + activated_and_extended_at_block + 1, ); - assert_ok!(SafeMode::slash_reservation( - RuntimeOrigin::signed(mock::ForceReservationOrigin::get()), + assert_ok!(SafeMode::force_slash_reservation( + RuntimeOrigin::signed(mock::ReservationSlashOrigin::get()), 0, activated_and_extended_at_block )); @@ -453,20 +577,24 @@ fn fails_when_explicit_origin_required() { let activated_at_block = System::block_number(); assert_err!( - SafeMode::force_extend(ForceActivateOrigin::Weak.signed()), + SafeMode::force_extend(ForceEnterOrigin::Weak.signed()), DispatchError::BadOrigin ); assert_err!( - SafeMode::force_deactivate(ForceActivateOrigin::Weak.signed()), + SafeMode::force_exit(ForceEnterOrigin::Weak.signed()), DispatchError::BadOrigin ); assert_err!( - SafeMode::slash_reservation(ForceActivateOrigin::Weak.signed(), 0, activated_at_block), + SafeMode::force_slash_reservation( + ForceEnterOrigin::Weak.signed(), + 0, + activated_at_block + ), DispatchError::BadOrigin ); assert_err!( SafeMode::force_release_reservation( - ForceActivateOrigin::Weak.signed(), + ForceEnterOrigin::Weak.signed(), 0, activated_at_block ), @@ -474,15 +602,19 @@ fn fails_when_explicit_origin_required() { ); assert_err!( - SafeMode::force_activate(ForceExtendOrigin::Weak.signed()), + SafeMode::force_enter(ForceExtendOrigin::Weak.signed()), DispatchError::BadOrigin ); assert_err!( - SafeMode::force_deactivate(ForceExtendOrigin::Weak.signed()), + SafeMode::force_exit(ForceExtendOrigin::Weak.signed()), DispatchError::BadOrigin ); assert_err!( - SafeMode::slash_reservation(ForceExtendOrigin::Weak.signed(), 0, activated_at_block), + SafeMode::force_slash_reservation( + ForceExtendOrigin::Weak.signed(), + 0, + activated_at_block + ), DispatchError::BadOrigin ); assert_err!( @@ -495,16 +627,16 @@ fn fails_when_explicit_origin_required() { ); assert_err!( - SafeMode::force_activate(RuntimeOrigin::signed(mock::ForceDeactivateOrigin::get())), + SafeMode::force_enter(RuntimeOrigin::signed(mock::ForceExitOrigin::get())), DispatchError::BadOrigin ); assert_err!( - SafeMode::force_extend(RuntimeOrigin::signed(mock::ForceDeactivateOrigin::get())), + SafeMode::force_extend(RuntimeOrigin::signed(mock::ForceExitOrigin::get())), DispatchError::BadOrigin ); assert_err!( - SafeMode::slash_reservation( - RuntimeOrigin::signed(mock::ForceDeactivateOrigin::get()), + SafeMode::force_slash_reservation( + RuntimeOrigin::signed(mock::ForceExitOrigin::get()), 0, activated_at_block ), @@ -512,7 +644,7 @@ fn fails_when_explicit_origin_required() { ); assert_err!( SafeMode::force_release_reservation( - RuntimeOrigin::signed(mock::ForceDeactivateOrigin::get()), + RuntimeOrigin::signed(mock::ForceExitOrigin::get()), 0, activated_at_block ), @@ -520,15 +652,15 @@ fn fails_when_explicit_origin_required() { ); assert_err!( - SafeMode::force_activate(RuntimeOrigin::signed(mock::ForceReservationOrigin::get())), + SafeMode::force_enter(RuntimeOrigin::signed(mock::ReservationSlashOrigin::get())), DispatchError::BadOrigin ); assert_err!( - SafeMode::force_extend(RuntimeOrigin::signed(mock::ForceReservationOrigin::get())), + SafeMode::force_extend(RuntimeOrigin::signed(mock::ReservationSlashOrigin::get())), DispatchError::BadOrigin ); assert_err!( - SafeMode::force_deactivate(RuntimeOrigin::signed(mock::ForceReservationOrigin::get())), + SafeMode::force_exit(RuntimeOrigin::signed(mock::ReservationSlashOrigin::get())), DispatchError::BadOrigin ); }); diff --git a/frame/safe-mode/src/weights.rs b/frame/safe-mode/src/weights.rs index 2acef30841642..46e78521b5ae0 100644 --- a/frame/safe-mode/src/weights.rs +++ b/frame/safe-mode/src/weights.rs @@ -1,13 +1,13 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) 2023 Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -18,33 +18,26 @@ //! Autogenerated weights for pallet_safe_mode //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-10-13, STEPS: `1`, REPEAT: 1, LOW RANGE: `[]`, HIGH RANGE: `[]` -//! HOSTNAME: `pop-os`, CPU: `AMD Ryzen 7 1800X Eight-Core Processor` +//! DATE: 2023-03-01, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `i9`, CPU: `13th Gen Intel(R) Core(TM) i9-13900K` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: // ./target/release/substrate // benchmark // pallet -// --steps -// 1 -// --repeat -// 1 -// --extrinsic -// * -// --execution -// wasm -// --wasm-execution -// compiled -// --heap-pages -// 4096 -// --pallet -// pallet_safe_mode -// --chain -// dev -// --output -// frame/safe-mode/src/weights.rs -// --template=./.maintain/frame-weight-template.hbs +// --chain=dev +// --steps=50 +// --repeat=20 +// --pallet=pallet-safe-mode +// --extrinsic=* +// --execution=wasm +// --wasm-execution=compiled +// --heap-pages=4096 +// --output=./frame/safe-mode/src/weights.rs +// --header=./HEADER-APACHE2 +// --template=.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -55,137 +48,273 @@ use sp_std::marker::PhantomData; /// Weight functions needed for pallet_safe_mode. pub trait WeightInfo { - fn activate() -> Weight; - fn force_activate() -> Weight; + fn on_initialize_exit() -> Weight; + fn on_initialize_noop() -> Weight; + fn enter() -> Weight; + fn force_enter() -> Weight; fn extend() -> Weight; fn force_extend() -> Weight; - fn force_deactivate() -> Weight; - fn force_release_reservation() -> Weight; + fn force_exit() -> Weight; fn release_reservation() -> Weight; - fn slash_reservation() -> Weight; + fn force_release_reservation() -> Weight; + fn force_slash_reservation() -> Weight; } /// Weights for pallet_safe_mode using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { - // Storage: Balances Reserves (r:1 w:1) - // Storage: SafeMode Reservations (r:1 w:1) - // Storage: SafeMode ActiveUntil (r:1 w:1) - fn activate() -> Weight { - Weight::from_ref_time(95_729_000 as u64) - .saturating_add(T::DbWeight::get().reads(3 as u64)) - .saturating_add(T::DbWeight::get().writes(3 as u64)) - } - // Storage: SafeMode ActiveUntil (r:1 w:1) - fn force_activate() -> Weight { - Weight::from_ref_time(50_724_000 as u64) - .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) - } - // Storage: Balances Reserves (r:1 w:1) - // Storage: SafeMode Reservations (r:1 w:1) - // Storage: SafeMode ActiveUntil (r:1 w:1) + /// Storage: SafeMode EnteredUntil (r:1 w:1) + /// Proof: SafeMode EnteredUntil (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + fn on_initialize_exit() -> Weight { + // Proof Size summary in bytes: + // Measured: `1141` + // Estimated: `499` + // Minimum execution time: 10_678 nanoseconds. + Weight::from_parts(11_199_000, 499) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: SafeMode EnteredUntil (r:1 w:0) + /// Proof: SafeMode EnteredUntil (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + fn on_initialize_noop() -> Weight { + // Proof Size summary in bytes: + // Measured: `676` + // Estimated: `499` + // Minimum execution time: 2_574 nanoseconds. + Weight::from_parts(2_747_000, 499) + .saturating_add(T::DbWeight::get().reads(1_u64)) + } + /// Storage: SafeMode EnteredUntil (r:1 w:1) + /// Proof: SafeMode EnteredUntil (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Balances Reserves (r:1 w:1) + /// Proof: Balances Reserves (max_values: None, max_size: Some(1049), added: 3524, mode: MaxEncodedLen) + /// Storage: SafeMode Reservations (r:1 w:1) + /// Proof: SafeMode Reservations (max_values: None, max_size: Some(68), added: 2543, mode: MaxEncodedLen) + fn enter() -> Weight { + // Proof Size summary in bytes: + // Measured: `1895` + // Estimated: `6566` + // Minimum execution time: 29_221 nanoseconds. + Weight::from_parts(31_037_000, 6566) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + /// Storage: SafeMode EnteredUntil (r:1 w:1) + /// Proof: SafeMode EnteredUntil (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + fn force_enter() -> Weight { + // Proof Size summary in bytes: + // Measured: `1258` + // Estimated: `499` + // Minimum execution time: 12_753 nanoseconds. + Weight::from_parts(12_939_000, 499) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: SafeMode EnteredUntil (r:1 w:1) + /// Proof: SafeMode EnteredUntil (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Balances Reserves (r:1 w:1) + /// Proof: Balances Reserves (max_values: None, max_size: Some(1049), added: 3524, mode: MaxEncodedLen) + /// Storage: SafeMode Reservations (r:1 w:1) + /// Proof: SafeMode Reservations (max_values: None, max_size: Some(68), added: 2543, mode: MaxEncodedLen) fn extend() -> Weight { - Weight::from_ref_time(87_614_000 as u64) - .saturating_add(T::DbWeight::get().reads(3 as u64)) - .saturating_add(T::DbWeight::get().writes(3 as u64)) + // Proof Size summary in bytes: + // Measured: `2228` + // Estimated: `6566` + // Minimum execution time: 33_757 nanoseconds. + Weight::from_parts(35_096_000, 6566) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) } - // Storage: SafeMode ActiveUntil (r:1 w:1) + /// Storage: SafeMode EnteredUntil (r:1 w:1) + /// Proof: SafeMode EnteredUntil (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) fn force_extend() -> Weight { - Weight::from_ref_time(46_246_000 as u64) - .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) - } - // Storage: SafeMode ActiveUntil (r:1 w:1) - fn force_deactivate() -> Weight { - Weight::from_ref_time(37_891_000 as u64) - .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) - } - // Storage: SafeMode ActiveUntil (r:1 w:0) - // Storage: SafeMode Reservations (r:1 w:1) - // Storage: Balances Reserves (r:1 w:1) - fn force_release_reservation() -> Weight { - Weight::from_ref_time(80_290_000 as u64) - .saturating_add(T::DbWeight::get().reads(3 as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) + // Proof Size summary in bytes: + // Measured: `1444` + // Estimated: `499` + // Minimum execution time: 15_521 nanoseconds. + Weight::from_parts(16_387_000, 499) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) + } + /// Storage: SafeMode EnteredUntil (r:1 w:1) + /// Proof: SafeMode EnteredUntil (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + fn force_exit() -> Weight { + // Proof Size summary in bytes: + // Measured: `1319` + // Estimated: `499` + // Minimum execution time: 13_464 nanoseconds. + Weight::from_parts(14_153_000, 499) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) } - // Storage: SafeMode ActiveUntil (r:1 w:0) - // Storage: SafeMode Reservations (r:1 w:1) - // Storage: Balances Reserves (r:1 w:1) + /// Storage: SafeMode Reservations (r:1 w:1) + /// Proof: SafeMode Reservations (max_values: None, max_size: Some(68), added: 2543, mode: MaxEncodedLen) + /// Storage: SafeMode EnteredUntil (r:1 w:0) + /// Proof: SafeMode EnteredUntil (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Balances Reserves (r:1 w:1) + /// Proof: Balances Reserves (max_values: None, max_size: Some(1049), added: 3524, mode: MaxEncodedLen) fn release_reservation() -> Weight { - Weight::from_ref_time(80_290_000 as u64) - .saturating_add(T::DbWeight::get().reads(3 as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) - } - // Storage: SafeMode ActiveUntil (r:1 w:0) - // Storage: SafeMode Reservations (r:1 w:1) - // Storage: Balances Reserves (r:1 w:1) - fn slash_reservation() -> Weight { - Weight::from_ref_time(110_417_000 as u64) - .saturating_add(T::DbWeight::get().reads(3 as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) + // Proof Size summary in bytes: + // Measured: `2080` + // Estimated: `6566` + // Minimum execution time: 30_614 nanoseconds. + Weight::from_parts(32_080_000, 6566) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: SafeMode Reservations (r:1 w:1) + /// Proof: SafeMode Reservations (max_values: None, max_size: Some(68), added: 2543, mode: MaxEncodedLen) + /// Storage: Balances Reserves (r:1 w:1) + /// Proof: Balances Reserves (max_values: None, max_size: Some(1049), added: 3524, mode: MaxEncodedLen) + fn force_release_reservation() -> Weight { + // Proof Size summary in bytes: + // Measured: `2080` + // Estimated: `6067` + // Minimum execution time: 28_752 nanoseconds. + Weight::from_parts(30_362_000, 6067) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + /// Storage: SafeMode Reservations (r:1 w:1) + /// Proof: SafeMode Reservations (max_values: None, max_size: Some(68), added: 2543, mode: MaxEncodedLen) + /// Storage: Balances Reserves (r:1 w:1) + /// Proof: Balances Reserves (max_values: None, max_size: Some(1049), added: 3524, mode: MaxEncodedLen) + fn force_slash_reservation() -> Weight { + // Proof Size summary in bytes: + // Measured: `2083` + // Estimated: `6067` + // Minimum execution time: 33_964 nanoseconds. + Weight::from_parts(35_626_000, 6067) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) } } // For backwards compatibility and tests impl WeightInfo for () { - // Storage: Balances Reserves (r:1 w:1) - // Storage: SafeMode Reservations (r:1 w:1) - // Storage: SafeMode ActiveUntil (r:1 w:1) - fn activate() -> Weight { - Weight::from_ref_time(95_729_000 as u64) - .saturating_add(RocksDbWeight::get().reads(3 as u64)) - .saturating_add(RocksDbWeight::get().writes(3 as u64)) - } - // Storage: SafeMode ActiveUntil (r:1 w:1) - fn force_activate() -> Weight { - Weight::from_ref_time(50_724_000 as u64) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) - } - // Storage: Balances Reserves (r:1 w:1) - // Storage: SafeMode Reservations (r:1 w:1) - // Storage: SafeMode ActiveUntil (r:1 w:1) + /// Storage: SafeMode EnteredUntil (r:1 w:1) + /// Proof: SafeMode EnteredUntil (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + fn on_initialize_exit() -> Weight { + // Proof Size summary in bytes: + // Measured: `1141` + // Estimated: `499` + // Minimum execution time: 10_678 nanoseconds. + Weight::from_parts(11_199_000, 499) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: SafeMode EnteredUntil (r:1 w:0) + /// Proof: SafeMode EnteredUntil (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + fn on_initialize_noop() -> Weight { + // Proof Size summary in bytes: + // Measured: `676` + // Estimated: `499` + // Minimum execution time: 2_574 nanoseconds. + Weight::from_parts(2_747_000, 499) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + } + /// Storage: SafeMode EnteredUntil (r:1 w:1) + /// Proof: SafeMode EnteredUntil (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Balances Reserves (r:1 w:1) + /// Proof: Balances Reserves (max_values: None, max_size: Some(1049), added: 3524, mode: MaxEncodedLen) + /// Storage: SafeMode Reservations (r:1 w:1) + /// Proof: SafeMode Reservations (max_values: None, max_size: Some(68), added: 2543, mode: MaxEncodedLen) + fn enter() -> Weight { + // Proof Size summary in bytes: + // Measured: `1895` + // Estimated: `6566` + // Minimum execution time: 29_221 nanoseconds. + Weight::from_parts(31_037_000, 6566) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } + /// Storage: SafeMode EnteredUntil (r:1 w:1) + /// Proof: SafeMode EnteredUntil (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + fn force_enter() -> Weight { + // Proof Size summary in bytes: + // Measured: `1258` + // Estimated: `499` + // Minimum execution time: 12_753 nanoseconds. + Weight::from_parts(12_939_000, 499) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: SafeMode EnteredUntil (r:1 w:1) + /// Proof: SafeMode EnteredUntil (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Balances Reserves (r:1 w:1) + /// Proof: Balances Reserves (max_values: None, max_size: Some(1049), added: 3524, mode: MaxEncodedLen) + /// Storage: SafeMode Reservations (r:1 w:1) + /// Proof: SafeMode Reservations (max_values: None, max_size: Some(68), added: 2543, mode: MaxEncodedLen) fn extend() -> Weight { - Weight::from_ref_time(87_614_000 as u64) - .saturating_add(RocksDbWeight::get().reads(3 as u64)) - .saturating_add(RocksDbWeight::get().writes(3 as u64)) + // Proof Size summary in bytes: + // Measured: `2228` + // Estimated: `6566` + // Minimum execution time: 33_757 nanoseconds. + Weight::from_parts(35_096_000, 6566) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) } - // Storage: SafeMode ActiveUntil (r:1 w:1) + /// Storage: SafeMode EnteredUntil (r:1 w:1) + /// Proof: SafeMode EnteredUntil (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) fn force_extend() -> Weight { - Weight::from_ref_time(46_246_000 as u64) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) - } - // Storage: SafeMode ActiveUntil (r:1 w:1) - fn force_deactivate() -> Weight { - Weight::from_ref_time(37_891_000 as u64) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) - } - // Storage: SafeMode ActiveUntil (r:1 w:0) - // Storage: SafeMode Reservations (r:1 w:1) - // Storage: Balances Reserves (r:1 w:1) - fn force_release_reservation() -> Weight { - Weight::from_ref_time(80_290_000 as u64) - .saturating_add(RocksDbWeight::get().reads(3 as u64)) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) + // Proof Size summary in bytes: + // Measured: `1444` + // Estimated: `499` + // Minimum execution time: 15_521 nanoseconds. + Weight::from_parts(16_387_000, 499) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) + } + /// Storage: SafeMode EnteredUntil (r:1 w:1) + /// Proof: SafeMode EnteredUntil (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + fn force_exit() -> Weight { + // Proof Size summary in bytes: + // Measured: `1319` + // Estimated: `499` + // Minimum execution time: 13_464 nanoseconds. + Weight::from_parts(14_153_000, 499) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) } - // Storage: SafeMode ActiveUntil (r:1 w:0) -// Storage: SafeMode Reservations (r:1 w:1) - // Storage: Balances Reserves (r:1 w:1) + /// Storage: SafeMode Reservations (r:1 w:1) + /// Proof: SafeMode Reservations (max_values: None, max_size: Some(68), added: 2543, mode: MaxEncodedLen) + /// Storage: SafeMode EnteredUntil (r:1 w:0) + /// Proof: SafeMode EnteredUntil (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: Balances Reserves (r:1 w:1) + /// Proof: Balances Reserves (max_values: None, max_size: Some(1049), added: 3524, mode: MaxEncodedLen) fn release_reservation() -> Weight { - Weight::from_ref_time(80_290_000 as u64) - .saturating_add(RocksDbWeight::get().reads(3 as u64)) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) - } - // Storage: SafeMode ActiveUntil (r:1 w:0) - // Storage: SafeMode Reservations (r:1 w:1) - // Storage: Balances Reserves (r:1 w:1) - fn slash_reservation() -> Weight { - Weight::from_ref_time(110_417_000 as u64) - .saturating_add(RocksDbWeight::get().reads(3 as u64)) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) + // Proof Size summary in bytes: + // Measured: `2080` + // Estimated: `6566` + // Minimum execution time: 30_614 nanoseconds. + Weight::from_parts(32_080_000, 6566) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + } + /// Storage: SafeMode Reservations (r:1 w:1) + /// Proof: SafeMode Reservations (max_values: None, max_size: Some(68), added: 2543, mode: MaxEncodedLen) + /// Storage: Balances Reserves (r:1 w:1) + /// Proof: Balances Reserves (max_values: None, max_size: Some(1049), added: 3524, mode: MaxEncodedLen) + fn force_release_reservation() -> Weight { + // Proof Size summary in bytes: + // Measured: `2080` + // Estimated: `6067` + // Minimum execution time: 28_752 nanoseconds. + Weight::from_parts(30_362_000, 6067) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + } + /// Storage: SafeMode Reservations (r:1 w:1) + /// Proof: SafeMode Reservations (max_values: None, max_size: Some(68), added: 2543, mode: MaxEncodedLen) + /// Storage: Balances Reserves (r:1 w:1) + /// Proof: Balances Reserves (max_values: None, max_size: Some(1049), added: 3524, mode: MaxEncodedLen) + fn force_slash_reservation() -> Weight { + // Proof Size summary in bytes: + // Measured: `2083` + // Estimated: `6067` + // Minimum execution time: 33_964 nanoseconds. + Weight::from_parts(35_626_000, 6067) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) } } From f868b5d38fe28569461a1df4cb1fa7ba491a841e Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Wed, 1 Mar 2023 19:46:31 +0100 Subject: [PATCH 034/100] Cleanup TxPause pallet Signed-off-by: Oliver Tale-Yazdi --- frame/tx-pause/src/lib.rs | 56 +++++++++++++++---------------------- frame/tx-pause/src/mock.rs | 28 +++++-------------- frame/tx-pause/src/tests.rs | 50 +++++++++++++++------------------ 3 files changed, 52 insertions(+), 82 deletions(-) diff --git a/frame/tx-pause/src/lib.rs b/frame/tx-pause/src/lib.rs index a3f7bb790f3ad..b78e12d3fce17 100644 --- a/frame/tx-pause/src/lib.rs +++ b/frame/tx-pause/src/lib.rs @@ -102,9 +102,10 @@ pub type PalletNameOf = BoundedVec::MaxNameLen>; /// The stringy name of a call (within a pallet) from [`GetCallMetadata`] for /// [`Config::RuntimeCall`] variants. pub type CallNameOf = BoundedVec::MaxNameLen>; + /// A sully specified pallet ([`PalletNameOf`]) and optional call ([`CallNameOf`]) /// to partially or fully specify an item a variant of a [`Config::RuntimeCall`]. -pub type FullNameOf = (PalletNameOf, Option>); +pub type FullNameOf = (PalletNameOf, CallNameOf); #[frame_support::pallet] pub mod pallet { @@ -169,7 +170,7 @@ pub mod pallet { IsUnpaused, /// The call is listed as safe and cannot be paused. - IsUnpausable, + Unpausable, // The pallet or call does not exist in the runtime. NotFound, @@ -178,10 +179,10 @@ pub mod pallet { #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event { - /// This pallet, or a specific call is now paused. \[pallet_name, Option\] - SomethingPaused { full_name: FullNameOf }, - /// This pallet, or a specific call is now unpaused. \[pallet_name, Option\] - SomethingUnpaused { full_name: FullNameOf }, + /// This pallet, or a specific call is now paused. + CallPaused { full_name: FullNameOf }, + /// This pallet, or a specific call is now unpaused. + CallUnpaused { full_name: FullNameOf }, } /// The set of calls that are explicitly paused. @@ -221,7 +222,7 @@ pub mod pallet { /// Pause a call. /// /// Can only be called by [`Config::PauseOrigin`]. - /// Emits an [`Event::SomethingPaused`] event on success. + /// Emits an [`Event::CallPaused`] event on success. #[pallet::call_index(0)] #[pallet::weight(T::WeightInfo::pause())] pub fn pause(origin: OriginFor, full_name: FullNameOf) -> DispatchResult { @@ -229,7 +230,7 @@ pub mod pallet { Self::ensure_can_pause(&full_name)?; PausedCalls::::insert(&full_name, ()); - Self::deposit_event(Event::SomethingPaused { full_name }); + Self::deposit_event(Event::CallPaused { full_name }); Ok(()) } @@ -237,7 +238,7 @@ pub mod pallet { /// Un-pause a call. /// /// Can only be called by [`Config::UnpauseOrigin`]. - /// Emits an [`Event::SomethingUnpaused`] event on success. + /// Emits an [`Event::CallUnpaused`] event on success. #[pallet::call_index(1)] #[pallet::weight(T::WeightInfo::unpause())] pub fn unpause(origin: OriginFor, full_name: FullNameOf) -> DispatchResult { @@ -245,43 +246,31 @@ pub mod pallet { Self::ensure_can_unpause(&full_name)?; PausedCalls::::remove(&full_name); - Self::deposit_event(Event::SomethingUnpaused { full_name }); + Self::deposit_event(Event::CallUnpaused { full_name }); Ok(()) } } } impl Pallet { - /// Return whether this pallet or call is paused. + /// Return whether this call is paused. pub fn is_paused_unbound(pallet_name: Vec, call_name: Vec) -> bool { let pallet_name = PalletNameOf::::try_from(pallet_name); let call_name = CallNameOf::::try_from(call_name); match (pallet_name, call_name) { - (Ok(pallet_name), Ok(call_name)) => Self::is_paused(&&>::from(( - pallet_name.clone(), - Some(call_name.clone()), - ))), + (Ok(pallet_name), Ok(call_name)) => + Self::is_paused(&&>::from((pallet_name.clone(), call_name.clone()))), _ => T::PauseTooLongNames::get(), } } - /// Return whether this pallet or call is paused + /// Return whether this call is paused. pub fn is_paused(full_name: &FullNameOf) -> bool { - let (pallet_name, maybe_call_name) = full_name; - // SAFETY: Everything that is whitelisted cannot be paused, - // including calls within paused pallets. - if T::WhitelistCallNames::contains(&>::from(( - pallet_name.clone(), - maybe_call_name.clone(), - ))) { + if T::WhitelistCallNames::contains(full_name) { return false - }; - // Check is pallet is paused. - if >::contains_key(>::from((pallet_name.clone(), None))) { - return true - }; - // Check if call in a pallet is paused + } + >::contains_key(full_name) } @@ -289,14 +278,15 @@ impl Pallet { pub fn ensure_can_pause(full_name: &FullNameOf) -> Result<(), Error> { // The `TxPause` pallet can never be paused. if full_name.0.as_ref() == ::name().as_bytes().to_vec() { - return Err(Error::::IsUnpausable) + return Err(Error::::Unpausable) + } + + if T::WhitelistCallNames::contains(&full_name) { + return Err(Error::::Unpausable) } if Self::is_paused(&full_name) { return Err(Error::::IsPaused) } - if T::WhitelistCallNames::contains(&full_name) { - return Err(Error::::IsUnpausable) - } Ok(()) } diff --git a/frame/tx-pause/src/mock.rs b/frame/tx-pause/src/mock.rs index a9294b6ac4f77..67f9d2fcb585e 100644 --- a/frame/tx-pause/src/mock.rs +++ b/frame/tx-pause/src/mock.rs @@ -146,30 +146,16 @@ parameter_types! { #[derive(Copy, Clone, Encode, Decode, RuntimeDebug, MaxEncodedLen, scale_info::TypeInfo)] pub struct WhitelistCallNames; -/// Contains used by `BaseCallFiler` so this impl whitelists `Balances::transfer_keep_alive` -/// and all DummyPallet calls. All others may be paused. +/// Contains used by `BaseCallFiler` so this impl whitelists `Balances::transfer_keep_alive`. All +/// others may be paused. impl Contains> for WhitelistCallNames { fn contains(full_name: &FullNameOf) -> bool { - let unpausables: Vec> = vec![ - ( - b"Balances".to_vec().try_into().unwrap(), - Some(b"transfer_keep_alive".to_vec().try_into().unwrap()), - ), - (b"DummyPallet".to_vec().try_into().unwrap(), None), - ]; + let unpausables: Vec> = vec![( + b"Balances".to_vec().try_into().unwrap(), + b"transfer_keep_alive".to_vec().try_into().unwrap(), + )]; - for unpausable_call in unpausables { - let (pallet_name, maybe_call_name) = full_name; - if unpausable_call.1.is_none() { - return &unpausable_call.0 == pallet_name - } else { - if &unpausable_call.0 == pallet_name { - return &unpausable_call.1 == maybe_call_name - } - } - } - - false + unpausables.contains(full_name) } } diff --git a/frame/tx-pause/src/tests.rs b/frame/tx-pause/src/tests.rs index 983d5cf50421c..08029ecb8aeb0 100644 --- a/frame/tx-pause/src/tests.rs +++ b/frame/tx-pause/src/tests.rs @@ -31,7 +31,7 @@ fn can_pause_specific_call() { assert_ok!(TxPause::pause( RuntimeOrigin::signed(mock::PauseOrigin::get()), - full_name::(b"Balances", Some(b"transfer")) + full_name::(b"Balances", b"transfer") )); assert_err!( @@ -53,7 +53,7 @@ fn can_pause_all_calls_in_pallet_except_on_whitelist() { assert_ok!(TxPause::pause( RuntimeOrigin::signed(mock::PauseOrigin::get()), - full_name::(b"Utility", None) + full_name::(b"Utility", b"batch") ),); assert_err!( @@ -68,7 +68,7 @@ fn can_unpause_specific_call() { new_test_ext().execute_with(|| { assert_ok!(TxPause::pause( RuntimeOrigin::signed(mock::PauseOrigin::get()), - full_name::(b"Balances", Some(b"transfer")), + full_name::(b"Balances", b"transfer"), )); assert_err!( call_transfer(2, 1).dispatch(RuntimeOrigin::signed(2)), @@ -77,7 +77,7 @@ fn can_unpause_specific_call() { assert_ok!(TxPause::unpause( RuntimeOrigin::signed(mock::UnpauseOrigin::get()), - full_name::(b"Balances", Some(b"transfer")), + full_name::(b"Balances", b"transfer"), )); assert_ok!(call_transfer(4, 1).dispatch(RuntimeOrigin::signed(0))); }); @@ -91,7 +91,7 @@ fn can_filter_balance_in_batch_when_paused() { assert_ok!(TxPause::pause( RuntimeOrigin::signed(mock::PauseOrigin::get()), - full_name::(b"Balances", Some(b"transfer")), + full_name::(b"Balances", b"transfer"), )); assert_ok!(batch_call.clone().dispatch(RuntimeOrigin::signed(0))); @@ -110,7 +110,7 @@ fn can_filter_balance_in_proxy_when_paused() { new_test_ext().execute_with(|| { assert_ok!(TxPause::pause( RuntimeOrigin::signed(mock::PauseOrigin::get()), - full_name::(b"Balances", Some(b"transfer")), + full_name::(b"Balances", b"transfer"), )); assert_ok!(Proxy::add_proxy(RuntimeOrigin::signed(1), 2, ProxyType::JustTransfer, 0)); @@ -133,9 +133,9 @@ fn fails_to_pause_self() { assert_noop!( TxPause::pause( RuntimeOrigin::signed(mock::PauseOrigin::get()), - full_name::(b"TxPause", Some(b"pause")), + full_name::(b"TxPause", b"pause"), ), - Error::::IsUnpausable + Error::::Unpausable ); }); } @@ -146,15 +146,15 @@ fn fails_to_pause_unpausable_pallet() { assert_noop!( TxPause::pause( RuntimeOrigin::signed(mock::PauseOrigin::get()), - full_name::(b"DummyPallet", Some(b"any_call")), + full_name::(b"Balances", b"transfer_keep_alive"), ), - Error::::IsUnpausable + Error::::Unpausable ); }); } #[test] -fn fails_to_pause_unpausable_call_when_pallet_is_paused() { +fn fails_to_pause_unpausable_call_when_other_call_is_paused() { new_test_ext().execute_with(|| { assert_ok!(call_transfer(1, 1).dispatch(RuntimeOrigin::signed(0))); @@ -164,7 +164,7 @@ fn fails_to_pause_unpausable_call_when_pallet_is_paused() { assert_ok!(TxPause::pause( RuntimeOrigin::signed(mock::PauseOrigin::get()), - full_name::(b"Balances", None), + full_name::(b"Balances", b"transfer"), )); assert_ok!(call_transfer_keep_alive(3, 1).dispatch(RuntimeOrigin::signed(3))); @@ -181,9 +181,9 @@ fn fails_to_pause_unpausable_call() { assert_noop!( TxPause::pause( RuntimeOrigin::signed(mock::PauseOrigin::get()), - full_name::(b"Balances", Some(b"transfer_keep_alive")), + full_name::(b"Balances", b"transfer_keep_alive"), ), - Error::::IsUnpausable + Error::::Unpausable ); }); } @@ -193,13 +193,13 @@ fn fails_to_pause_already_paused_pallet() { new_test_ext().execute_with(|| { assert_ok!(TxPause::pause( RuntimeOrigin::signed(mock::PauseOrigin::get()), - full_name::(b"Balances", Some(b"transfer")), + full_name::(b"Balances", b"transfer"), )); assert_noop!( TxPause::pause( RuntimeOrigin::signed(mock::PauseOrigin::get()), - full_name::(b"Balances", Some(b"transfer")), + full_name::(b"Balances", b"transfer"), ), Error::::IsPaused ); @@ -212,7 +212,7 @@ fn fails_to_unpause_not_paused_pallet() { assert_noop!( TxPause::unpause( RuntimeOrigin::signed(mock::UnpauseOrigin::get()), - full_name::(b"Balances", Some(b"transfer_keep_alive")), + full_name::(b"Balances", b"transfer_keep_alive"), ), Error::::IsUnpaused ); @@ -227,15 +227,9 @@ pub fn call_transfer_keep_alive(dest: u64, value: u64) -> RuntimeCall { RuntimeCall::Balances(pallet_balances::Call::transfer_keep_alive { dest, value }) } -pub fn full_name( - pallet_name_bytes: &[u8], - maybe_call_name_bytes: Option<&[u8]>, -) -> FullNameOf { - match maybe_call_name_bytes { - Some(call_name_bytes) => >::from(( - pallet_name_bytes.to_vec().try_into().unwrap(), - Some(call_name_bytes.to_vec().try_into().unwrap()), - )), - None => >::from((pallet_name_bytes.to_vec().try_into().unwrap(), None)), - } +pub fn full_name(pallet_name: &[u8], call_name: &[u8]) -> FullNameOf { + >::from(( + pallet_name.to_vec().try_into().unwrap(), + call_name.to_vec().try_into().unwrap(), + )) } From eb598311261f56ec3ec93ed642c42f85de60dcbb Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Thu, 2 Mar 2023 15:15:35 +0100 Subject: [PATCH 035/100] Fix benches Signed-off-by: Oliver Tale-Yazdi --- frame/tx-pause/src/benchmarking.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frame/tx-pause/src/benchmarking.rs b/frame/tx-pause/src/benchmarking.rs index 77e2158778d4f..687daba672ef3 100644 --- a/frame/tx-pause/src/benchmarking.rs +++ b/frame/tx-pause/src/benchmarking.rs @@ -23,7 +23,7 @@ use frame_benchmarking::benchmarks; benchmarks! { pause { - let full_name: FullNameOf = (name::(b"SomePalletName"), Some(name::(b"SomePalletName"))); + let full_name: FullNameOf = (name::(b"SomePalletName"), name::(b"SomePalletName")); // let pallet_name: PalletNameOf = name::(b"SomePalletName"); // let maybe_call_name: Option> = Some(name::(b"some_call_name")); let origin = T::PauseOrigin::try_successful_origin().expect("Tx-pause pallet does not make sense without pause origin"); @@ -36,7 +36,7 @@ benchmarks! { } unpause { - let full_name: FullNameOf = (name::(b"SomePalletName"), Some(name::(b"SomePalletName"))); + let full_name: FullNameOf = (name::(b"SomePalletName"), name::(b"SomePalletName")); // tODO let pause_origin = T::PauseOrigin::try_successful_origin().expect("Tx-pause pallet does not make sense without pause origin"); From cf3d749f4f2762946f0a8f63c1dec93806e06812 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Thu, 2 Mar 2023 15:15:43 +0100 Subject: [PATCH 036/100] fmt Signed-off-by: Oliver Tale-Yazdi --- frame/tx-pause/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/tx-pause/src/lib.rs b/frame/tx-pause/src/lib.rs index b78e12d3fce17..63002682bccbb 100644 --- a/frame/tx-pause/src/lib.rs +++ b/frame/tx-pause/src/lib.rs @@ -270,7 +270,7 @@ impl Pallet { if T::WhitelistCallNames::contains(full_name) { return false } - + >::contains_key(full_name) } From 7a5b0981d510cc60fe30ad2a851e789fb65c7e72 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Sat, 25 Mar 2023 19:31:15 +0100 Subject: [PATCH 037/100] Refactor to fungibles::* and rename stuf Signed-off-by: Oliver Tale-Yazdi --- Cargo.lock | 38 ++++++ bin/node/runtime/src/lib.rs | 16 +-- frame/benchmarking/src/lib.rs | 2 +- frame/safe-mode/src/benchmarking.rs | 100 +++++++++------- frame/safe-mode/src/lib.rs | 117 ++++++++++-------- frame/safe-mode/src/mock.rs | 34 ++++-- frame/safe-mode/src/tests.rs | 176 ++++++++++++---------------- frame/safe-mode/src/weights.rs | 18 +-- frame/tx-pause/src/benchmarking.rs | 9 +- frame/tx-pause/src/lib.rs | 50 ++++---- frame/tx-pause/src/mock.rs | 10 +- frame/tx-pause/src/weights.rs | 8 +- 12 files changed, 318 insertions(+), 260 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 33bca4005483c..312e838e31479 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3665,6 +3665,7 @@ dependencies = [ "pallet-referenda", "pallet-remark", "pallet-root-testing", + "pallet-safe-mode", "pallet-salary", "pallet-scheduler", "pallet-session", @@ -3681,6 +3682,7 @@ dependencies = [ "pallet-transaction-payment-rpc-runtime-api", "pallet-transaction-storage", "pallet-treasury", + "pallet-tx-pause", "pallet-uniques", "pallet-utility", "pallet-vesting", @@ -6625,6 +6627,24 @@ dependencies = [ "sp-std", ] +[[package]] +name = "pallet-safe-mode" +version = "4.0.0-dev" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "pallet-balances", + "pallet-proxy", + "pallet-utility", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + [[package]] name = "pallet-salary" version = "4.0.0-dev" @@ -6967,6 +6987,24 @@ dependencies = [ "sp-std", ] +[[package]] +name = "pallet-tx-pause" +version = "4.0.0-dev" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "pallet-balances", + "pallet-proxy", + "pallet-utility", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + [[package]] name = "pallet-uniques" version = "4.0.0-dev" diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 9e0a9aab83a6b..8a4f2d1269ce9 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -229,9 +229,9 @@ impl Contains for WhitelistedCalls { } use pallet_tx_pause::FullNameOf; -pub struct WhitelistCallNames; +pub struct WhitelistedCalls; /// Whitelist `Balances::transfer_keep_alive`, all others are pauseable. -impl Contains> for WhitelistCallNames { +impl Contains> for WhitelistedCalls { fn contains(full_name: &FullNameOf) -> bool { let unpausables: Vec> = vec![( b"Balances".to_vec().try_into().unwrap(), @@ -257,7 +257,7 @@ impl pallet_tx_pause::Config for Runtime { type RuntimeCall = RuntimeCall; type PauseOrigin = EnsureRoot; type UnpauseOrigin = EnsureRoot; - type WhitelistCallNames = WhitelistCallNames; + type WhitelistedCalls = WhitelistedCalls; type MaxNameLen = ConstU32<256>; type PauseTooLongNames = ConstBool; type WeightInfo = pallet_tx_pause::weights::SubstrateWeight; @@ -376,8 +376,8 @@ impl, O>> + From>> Ensu parameter_types! { pub const SignedEnterDuration: u32 = 10; pub const ExtendDuration: u32 = 20; - pub const ActivateReservationAmount: Balance = 10 * DOLLARS; - pub const ExtendReservationAmount: Balance = 15 * DOLLARS; + pub const EnterStakeAmount: Balance = 10 * DOLLARS; + pub const ExtendStakeAmount: Balance = 15 * DOLLARS; pub const ReleaseDelay: u32 = 15; } @@ -386,13 +386,13 @@ impl pallet_safe_mode::Config for Runtime { type Currency = Balances; type WhitelistedCalls = WhitelistedCalls; type EnterDuration = ConstU32<{ 2 * DAYS }>; - type ActivateReservationAmount = ActivateReservationAmount; + type EnterStakeAmount = EnterStakeAmount; type ExtendDuration = ConstU32<{ 1 * DAYS }>; - type ExtendReservationAmount = ExtendReservationAmount; + type ExtendStakeAmount = ExtendStakeAmount; type ForceEnterOrigin = ForceEnterOrigin; type ForceExtendOrigin = ForceExtendOrigin; type ForceExitOrigin = EnsureRoot; - type ReservationSlashOrigin = EnsureRoot; + type StakeSlashOrigin = EnsureRoot; type ReleaseDelay = ReleaseDelay; type WeightInfo = pallet_safe_mode::weights::SubstrateWeight; } diff --git a/frame/benchmarking/src/lib.rs b/frame/benchmarking/src/lib.rs index 7110c378d581e..955acf17a02e5 100644 --- a/frame/benchmarking/src/lib.rs +++ b/frame/benchmarking/src/lib.rs @@ -157,7 +157,7 @@ pub use v1::*; /// _(RawOrigin::Signed(whitelisted_caller()), 0u32.into(), 0); /// ``` /// -/// The underscore will be substituted with the name of the benchmark (i.e. the name of the +/// The underscore will beu substituted with the name of the benchmark (i.e. the name of the /// function in the benchmark function definition). /// /// Regardless of whether `#[extrinsic_call]` or `#[block]` is used, this attribute also serves diff --git a/frame/safe-mode/src/benchmarking.rs b/frame/safe-mode/src/benchmarking.rs index 754061c7f3453..79f98fedcfc10 100644 --- a/frame/safe-mode/src/benchmarking.rs +++ b/frame/safe-mode/src/benchmarking.rs @@ -20,11 +20,13 @@ use super::{Pallet as SafeMode, *}; use frame_benchmarking::v2::*; -use frame_support::traits::{Currency, UnfilteredDispatchable}; +use frame_support::traits::{fungible::Mutate as FunMutate, UnfilteredDispatchable}; use frame_system::{Pallet as System, RawOrigin}; use sp_runtime::traits::{Bounded, One, Zero}; -#[benchmarks] +#[benchmarks( + where T::Currency: FunMutate, +)] mod benchmarks { use super::*; @@ -53,7 +55,7 @@ mod benchmarks { fn enter() { let caller: T::AccountId = whitelisted_caller(); let origin = RawOrigin::Signed(caller.clone()); - T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); + T::Currency::set_balance(&caller, BalanceOf::::max_value() / 2u32.into()); #[extrinsic_call] _(origin); @@ -65,7 +67,7 @@ mod benchmarks { } #[benchmark] - fn force_enter() { + fn force_enter() -> Result<(), BenchmarkError> { let force_origin = T::ForceEnterOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; let duration = T::ForceEnterOrigin::ensure_origin(force_origin.clone()).unwrap(); @@ -77,13 +79,14 @@ mod benchmarks { } assert_eq!(SafeMode::::active_until().unwrap(), System::::block_number() + duration); + Ok(()) } #[benchmark] fn extend() { let caller: T::AccountId = whitelisted_caller(); let origin = RawOrigin::Signed(caller.clone()); - T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); + T::Currency::set_balance(&caller, BalanceOf::::max_value() / 2u32.into()); System::::set_block_number(1u32.into()); assert!(SafeMode::::enter(origin.clone().into()).is_ok()); @@ -101,7 +104,7 @@ mod benchmarks { fn force_extend() -> Result<(), BenchmarkError> { let caller: T::AccountId = whitelisted_caller(); let origin = RawOrigin::Signed(caller.clone()); - T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); + T::Currency::set_balance(&caller, BalanceOf::::max_value() / 2u32.into()); System::::set_block_number(1u32.into()); assert!(SafeMode::::enter(origin.clone().into()).is_ok()); @@ -120,13 +123,14 @@ mod benchmarks { SafeMode::::active_until().unwrap(), System::::block_number() + T::EnterDuration::get() + extension ); + Ok(()) } #[benchmark] - fn force_exit() { + fn force_exit() -> Result<(), BenchmarkError> { let caller: T::AccountId = whitelisted_caller(); let origin = RawOrigin::Signed(caller.clone()); - T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); + T::Currency::set_balance(&caller, BalanceOf::::max_value() / 2u32.into()); assert!(SafeMode::::enter(origin.clone().into()).is_ok()); @@ -140,21 +144,22 @@ mod benchmarks { } assert_eq!(SafeMode::::active_until(), None); + Ok(()) } #[benchmark] - fn release_reservation() -> Result<(), BenchmarkError> { + fn release_stake() -> Result<(), BenchmarkError> { let caller: T::AccountId = whitelisted_caller(); let origin = RawOrigin::Signed(caller.clone()); - T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); + T::Currency::set_balance(&caller, BalanceOf::::max_value() / 2u32.into()); - let enterd_at_block: T::BlockNumber = System::::block_number(); + let entered_at_block: T::BlockNumber = System::::block_number(); assert!(SafeMode::::enter(origin.clone().into()).is_ok()); - let current_reservation = - Reservations::::get(&caller, enterd_at_block).unwrap_or_default(); + let current_stake = Stakes::::get(&caller, entered_at_block).unwrap_or_default(); + assert_eq!(current_stake, T::EnterStakeAmount::get().unwrap()); assert_eq!( - T::Currency::free_balance(&caller), - BalanceOf::::max_value() - T::ActivateReservationAmount::get().unwrap() + T::Currency::balance(&caller), + BalanceOf::::max_value() / 2u32.into() - T::EnterStakeAmount::get().unwrap() ); let force_origin = @@ -167,32 +172,32 @@ mod benchmarks { System::::on_initialize(System::::block_number()); SafeMode::::on_initialize(System::::block_number()); - let call = Call::::release_reservation { - account: caller.clone(), - block: enterd_at_block.clone(), - }; + let call = + Call::::release_stake { account: caller.clone(), block: entered_at_block.clone() }; #[block] { call.dispatch_bypass_filter(origin.into())?; } - assert_eq!(T::Currency::free_balance(&caller), BalanceOf::::max_value()); + assert!(!Stakes::::contains_key(&caller, entered_at_block)); + assert_eq!(T::Currency::balance(&caller), BalanceOf::::max_value() / 2u32.into()); + Ok(()) } #[benchmark] - fn force_release_reservation() -> Result<(), BenchmarkError> { + fn force_release_stake() -> Result<(), BenchmarkError> { let caller: T::AccountId = whitelisted_caller(); let origin = RawOrigin::Signed(caller.clone()); - T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); + T::Currency::set_balance(&caller, BalanceOf::::max_value() / 2u32.into()); - let enterd_at_block: T::BlockNumber = System::::block_number(); + let entered_at_block: T::BlockNumber = System::::block_number(); assert!(SafeMode::::enter(origin.clone().into()).is_ok()); - let current_reservation = - Reservations::::get(&caller, enterd_at_block).unwrap_or_default(); + let current_stake = Stakes::::get(&caller, entered_at_block).unwrap_or_default(); + assert_eq!(current_stake, T::EnterStakeAmount::get().unwrap()); assert_eq!( - T::Currency::free_balance(&caller), - BalanceOf::::max_value() - T::ActivateReservationAmount::get().unwrap() + T::Currency::balance(&caller), + BalanceOf::::max_value() / 2u32.into() - T::EnterStakeAmount::get().unwrap() ); // TODO @@ -204,11 +209,11 @@ mod benchmarks { System::::on_initialize(System::::block_number()); SafeMode::::on_initialize(System::::block_number()); - let release_origin = T::ReservationSlashOrigin::try_successful_origin() - .map_err(|_| BenchmarkError::Weightless)?; - let call = Call::::force_release_reservation { + let release_origin = + T::StakeSlashOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; + let call = Call::::force_release_stake { account: caller.clone(), - block: enterd_at_block.clone(), + block: entered_at_block.clone(), }; #[block] @@ -216,22 +221,25 @@ mod benchmarks { call.dispatch_bypass_filter(release_origin)?; } - assert_eq!(T::Currency::free_balance(&caller), BalanceOf::::max_value()); + assert!(!Stakes::::contains_key(&caller, entered_at_block)); + assert_eq!(T::Currency::balance(&caller), BalanceOf::::max_value() / 2u32.into()); + Ok(()) } #[benchmark] - fn force_slash_reservation() -> Result<(), BenchmarkError> { + fn force_slash_stake() -> Result<(), BenchmarkError> { + // FAIL-CI disable if uncallable let caller: T::AccountId = whitelisted_caller(); let origin = RawOrigin::Signed(caller.clone()); - T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); + T::Currency::set_balance(&caller, BalanceOf::::max_value() / 2u32.into()); - let enterd_at_block: T::BlockNumber = System::::block_number(); + let entered_at_block: T::BlockNumber = System::::block_number(); assert!(SafeMode::::enter(origin.clone().into()).is_ok()); - let current_reservation = - Reservations::::get(&caller, enterd_at_block).unwrap_or_default(); + let current_stake = Stakes::::get(&caller, entered_at_block).unwrap_or_default(); + assert_eq!(current_stake, T::EnterStakeAmount::get().unwrap()); assert_eq!( - T::Currency::free_balance(&caller), - BalanceOf::::max_value() - T::ActivateReservationAmount::get().unwrap() + T::Currency::balance(&caller), + BalanceOf::::max_value() / 2u32.into() - T::EnterStakeAmount::get().unwrap() ); // TODO @@ -239,11 +247,11 @@ mod benchmarks { T::ForceExitOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; assert!(SafeMode::::force_exit(force_origin.clone()).is_ok()); - let release_origin = T::ReservationSlashOrigin::try_successful_origin() - .map_err(|_| BenchmarkError::Weightless)?; - let call = Call::::force_slash_reservation { + let release_origin = + T::StakeSlashOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; + let call = Call::::force_slash_stake { account: caller.clone(), - block: enterd_at_block.clone(), + block: entered_at_block.clone(), }; #[block] @@ -251,10 +259,12 @@ mod benchmarks { call.dispatch_bypass_filter(release_origin)?; } + assert!(!Stakes::::contains_key(&caller, entered_at_block)); assert_eq!( - T::Currency::free_balance(&caller), - BalanceOf::::max_value() - T::ActivateReservationAmount::get().unwrap() + T::Currency::balance(&caller), + BalanceOf::::max_value() / 2u32.into() - T::EnterStakeAmount::get().unwrap() ); + Ok(()) } impl_benchmark_test_suite!(SafeMode, crate::mock::new_test_ext(), crate::mock::Test); diff --git a/frame/safe-mode/src/lib.rs b/frame/safe-mode/src/lib.rs index 737a710c43653..3973fe5fe1cff 100644 --- a/frame/safe-mode/src/lib.rs +++ b/frame/safe-mode/src/lib.rs @@ -27,8 +27,12 @@ pub mod weights; use frame_support::{ pallet_prelude::*, traits::{ - CallMetadata, Contains, Currency, Defensive, GetCallMetadata, NamedReservableCurrency, - PalletInfoAccess, + fungible::{ + hold::{Inspect as FunHoldInspect, Mutate as FunHoldMutate}, + Inspect as FunInspect, + }, + tokens::{Fortitude, Precision}, + CallMetadata, Contains, Defensive, GetCallMetadata, PalletInfoAccess, }, weights::Weight, }; @@ -40,14 +44,20 @@ pub use pallet::*; pub use weights::*; type BalanceOf = - <::Currency as Currency<::AccountId>>::Balance; + <::Currency as FunInspect<::AccountId>>::Balance; + +/// Create a hold reason for a given cause. +pub trait CausalHoldReason { + type Reason: codec::Encode + TypeInfo + 'static; + + fn cause(path: Cause) -> Self::Reason; +} #[frame_support::pallet] pub mod pallet { use super::*; #[pallet::pallet] - #[pallet::generate_store(pub(super) trait Store)] pub struct Pallet(PhantomData); #[pallet::config] @@ -55,10 +65,13 @@ pub mod pallet { /// The overarching event type. type RuntimeEvent: From> + IsType<::RuntimeEvent>; - /// Currency type for this pallet, used for reservations. - type Currency: NamedReservableCurrency< - Self::AccountId, - ReserveIdentifier = Self::BlockNumber, + /// Currency type for this pallet, used for Stakes. + type Currency: FunHoldInspect + FunHoldMutate; + + /// Create a hold reason for a specific cause. + type HoldReason: CausalHoldReason< + Self::BlockNumber, + Reason = >::Reason, >; /// Contains all runtime calls in any pallet that can be dispatched even while the safe-mode @@ -82,13 +95,13 @@ pub mod pallet { /// /// `None` disallows permissionlessly enabling the safe-mode. #[pallet::constant] - type ActivateReservationAmount: Get>>; + type EnterStakeAmount: Get>>; /// The amount that will be reserved upon calling [`Pallet::extend`]. /// /// `None` disallows permissionlessly extending the safe-mode. #[pallet::constant] - type ExtendReservationAmount: Get>>; + type ExtendStakeAmount: Get>>; /// The origin that may call [`Pallet::force_enter`]. /// @@ -103,17 +116,17 @@ pub mod pallet { /// The origin that may call [`Pallet::force_enter`]. type ForceExitOrigin: EnsureOrigin; - /// The origin that may call [`Pallet::force_release_reservation`] and - /// [`Pallet::slash_reservation`]. - type ReservationSlashOrigin: EnsureOrigin; + /// The origin that may call [`Pallet::force_release_stake`] and + /// [`Pallet::slash_stake`]. + type StakeSlashOrigin: EnsureOrigin; /// The minimal duration a deposit will remain reserved after safe-mode is entered or - /// extended, unless [`Pallet::force_release_reservation`] is successfully called sooner. + /// extended, unless [`Pallet::force_release_stake`] is successfully called sooner. /// /// Every reservation is tied to a specific activation or extension, thus each reservation /// can be released independently after the delay for it has passed. /// - /// `None` disallows permissionlessly releasing the safe-mode reservations. + /// `None` disallows permissionlessly releasing the safe-mode Stakes. #[pallet::constant] type ReleaseDelay: Get>; @@ -180,7 +193,7 @@ pub mod pallet { /// Holds the reserve that was taken from an account at a specific block number. #[pallet::storage] #[pallet::getter(fn reserves)] - pub type Reservations = StorageDoubleMap< + pub type Stakes = StorageDoubleMap< _, Twox64Concat, T::AccountId, @@ -219,16 +232,16 @@ pub mod pallet { impl Pallet { /// Enter safe-mode permissionlessly for [`Config::EnterDuration`] blocks. /// - /// Reserves [`Config::ActivateReservationAmount`] from the caller's account. + /// Reserves [`Config::EnterStakeAmount`] from the caller's account. /// Emits an [`Event::Entered`] event on success. /// Errors with [`Error::Entered`] if the safe-mode is already entered. /// Errors with [`Error::NotConfigured`] if the reservation amount is `None` . /// /// ### Safety /// - /// This may be called by any signed origin with [`Config::ActivateReservationAmount`] free + /// This may be called by any signed origin with [`Config::EnterStakeAmount`] free /// currency to reserve. This call can be disabled for all origins by configuring - /// [`Config::ActivateReservationAmount`] to `None`. + /// [`Config::EnterStakeAmount`] to `None`. #[pallet::call_index(0)] #[pallet::weight(T::WeightInfo::enter())] pub fn enter(origin: OriginFor) -> DispatchResult { @@ -254,14 +267,14 @@ pub mod pallet { /// Extend the safe-mode permissionlessly for [`Config::ExtendDuration`] blocks. /// /// This accumulates on top of the current remaining duration. - /// Reserves [`Config::ExtendReservationAmount`] from the caller's account. + /// Reserves [`Config::ExtendStakeAmount`] from the caller's account. /// Emits an [`Event::Extended`] event on success. /// Errors with [`Error::Exited`] if the safe-mode is entered. /// Errors with [`Error::NotConfigured`] if the reservation amount is `None` . /// - /// This may be called by any signed origin with [`Config::ExtendReservationAmount`] free + /// This may be called by any signed origin with [`Config::ExtendStakeAmount`] free /// currency to reserve. This call can be disabled for all origins by configuring - /// [`Config::ExtendReservationAmount`] to `None`. + /// [`Config::ExtendStakeAmount`] to `None`. #[pallet::call_index(2)] #[pallet::weight(T::WeightInfo::extend())] pub fn extend(origin: OriginFor) -> DispatchResult { @@ -313,15 +326,15 @@ pub mod pallet { /// Emits a [`Event::ReservationSlashed`] event on success. /// Errors with [`Error::Entered`] if the safe-mode is entered. /// - /// Can only be called by the [`Config::ReservationSlashOrigin`] origin. + /// Can only be called by the [`Config::StakeSlashOrigin`] origin. #[pallet::call_index(5)] - #[pallet::weight(T::WeightInfo::force_slash_reservation())] - pub fn force_slash_reservation( + #[pallet::weight(T::WeightInfo::force_slash_stake())] + pub fn force_slash_stake( origin: OriginFor, account: T::AccountId, block: T::BlockNumber, ) -> DispatchResult { - T::ReservationSlashOrigin::ensure_origin(origin)?; + T::StakeSlashOrigin::ensure_origin(origin)?; Self::do_force_slash(account, block) } @@ -339,8 +352,8 @@ pub mod pallet { /// Errors with [`Error::NoReservation`] if the payee has no reserved currency at the /// block specified. #[pallet::call_index(6)] - #[pallet::weight(T::WeightInfo::release_reservation())] - pub fn release_reservation( + #[pallet::weight(T::WeightInfo::release_stake())] + pub fn release_stake( origin: OriginFor, account: T::AccountId, block: T::BlockNumber, @@ -360,15 +373,15 @@ pub mod pallet { /// Errors with [`Error::NoReservation`] if the payee has no reserved currency at the /// block specified. /// - /// Can only be called by the [`Config::ReservationSlashOrigin`] origin. + /// Can only be called by the [`Config::StakeSlashOrigin`] origin. #[pallet::call_index(7)] - #[pallet::weight(T::WeightInfo::force_release_reservation())] - pub fn force_release_reservation( + #[pallet::weight(T::WeightInfo::force_release_stake())] + pub fn force_release_stake( origin: OriginFor, account: T::AccountId, block: T::BlockNumber, ) -> DispatchResult { - T::ReservationSlashOrigin::ensure_origin(origin)?; + T::StakeSlashOrigin::ensure_origin(origin)?; Self::do_release(true, account, block) } @@ -398,7 +411,7 @@ impl Pallet { ensure!(!EnteredUntil::::exists(), Error::::Entered); if let Some(who) = who { - let reserve = T::ActivateReservationAmount::get().ok_or(Error::::NotConfigured)?; + let reserve = T::EnterStakeAmount::get().ok_or(Error::::NotConfigured)?; Self::reserve(who, reserve)?; } @@ -413,7 +426,7 @@ impl Pallet { let mut until = EnteredUntil::::get().ok_or(Error::::Exited)?; if let Some(who) = who { - let reserve = T::ExtendReservationAmount::get().ok_or(Error::::NotConfigured)?; + let reserve = T::ExtendStakeAmount::get().ok_or(Error::::NotConfigured)?; Self::reserve(who, reserve)?; } @@ -432,14 +445,14 @@ impl Pallet { Ok(()) } - /// Logic for the [`crate::Pallet::release_reservation`] and - /// [`crate::Pallet::force_release_reservation`] calls. + /// Logic for the [`crate::Pallet::release_stake`] and + /// [`crate::Pallet::force_release_stake`] calls. /// /// Errors if the safe-mode is entered with [`Error::Entered`] when `force` is `false`. - /// Errors if release is called too soon by anyone but [`Config::ReservationSlashOrigin`] with + /// Errors if release is called too soon by anyone but [`Config::StakeSlashOrigin`] with /// [`Error::CannotReleaseYet`]. fn do_release(force: bool, account: T::AccountId, block: T::BlockNumber) -> DispatchResult { - let amount = Reservations::::take(&account, &block).ok_or(Error::::NoReservation)?; + let amount = Stakes::::take(&account, &block).ok_or(Error::::NoReservation)?; if !force { ensure!(!Self::is_entered(), Error::::Entered); @@ -449,26 +462,38 @@ impl Pallet { ensure!(now > (block.saturating_add(delay)), Error::::CannotReleaseYet); } - T::Currency::unreserve_named(&block, &account, amount); + T::Currency::release( + &T::HoldReason::cause(block), + &account, + amount, + Precision::BestEffort, + )?; Self::deposit_event(Event::::ReservationReleased { block, account, amount }); Ok(()) } /// Logic for the [`crate::Pallet::slash_reservation`] call. fn do_force_slash(account: T::AccountId, block: T::BlockNumber) -> DispatchResult { - let amount = Reservations::::take(&account, block).ok_or(Error::::NoReservation)?; - - T::Currency::slash_reserved_named(&block, &account, amount); + let amount = Stakes::::take(&account, block).ok_or(Error::::NoReservation)?; + + // FAIL-CI check these args + T::Currency::burn_held( + &T::HoldReason::cause(block), + &account, + amount, + Precision::BestEffort, + Fortitude::Force, + )?; Self::deposit_event(Event::::ReservationSlashed { block, account, amount }); Ok(()) } - /// Reserve `amount` from `who` and store it in `Reservations`. + /// Reserve `amount` from `who` and store it in `Stakes`. fn reserve(who: T::AccountId, amount: BalanceOf) -> DispatchResult { let block = >::block_number(); - T::Currency::reserve_named(&block, &who, amount)?; - let current_reservation = Reservations::::get(&who, block).unwrap_or_default(); - Reservations::::insert(&who, block, current_reservation.saturating_add(amount)); + T::Currency::hold(&T::HoldReason::cause(block), &who, amount)?; + let current_reservation = Stakes::::get(&who, block).unwrap_or_default(); + Stakes::::insert(&who, block, current_reservation.saturating_add(amount)); Ok(()) } diff --git a/frame/safe-mode/src/mock.rs b/frame/safe-mode/src/mock.rs index 9a0969c0517f5..77b0ce183892b 100644 --- a/frame/safe-mode/src/mock.rs +++ b/frame/safe-mode/src/mock.rs @@ -75,6 +75,10 @@ impl pallet_balances::Config for Test { type MaxLocks = (); type MaxReserves = MaxReserves; type ReserveIdentifier = Self::BlockNumber; + type HoldIdentifier = HoldIdentifier; + type FreezeIdentifier = (); + type MaxHolds = ConstU32<10>; + type MaxFreezes = ConstU32<0>; } impl pallet_utility::Config for Test { @@ -260,10 +264,10 @@ impl, O>> + From>> EnsureOrigin parameter_types! { pub const EnterDuration: u64 = 3; pub const ExtendDuration: u64 = 30; - pub const ActivateReservationAmount: u64 = 100; - pub const ExtendReservationAmount: u64 = 100; + pub const EnterStakeAmount: u64 = 100; + pub const ExtendStakeAmount: u64 = 100; pub const ForceExitOrigin: u64 = 3; - pub const ReservationSlashOrigin: u64 = 4; + pub const StakeSlashOrigin: u64 = 4; pub const ReleaseDelay: u64 = 2; } @@ -275,7 +279,7 @@ impl SortedMembers for ForceExitOrigin { #[cfg(feature = "runtime-benchmarks")] fn add(_m: &u64) {} } -impl SortedMembers for ReservationSlashOrigin { +impl SortedMembers for StakeSlashOrigin { fn sorted_members() -> Vec { vec![Self::get()] } @@ -283,18 +287,34 @@ impl SortedMembers for ReservationSlashOrigin { fn add(_m: &u64) {} } +#[derive( + Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, MaxEncodedLen, Debug, TypeInfo, +)] +pub enum HoldIdentifier { + SafeModeStake { block: u64 }, +} + +impl crate::CausalHoldReason for HoldIdentifier { + type Reason = HoldIdentifier; + + fn cause(block: u64) -> Self::Reason { + Self::SafeModeStake { block } + } +} + impl Config for Test { type RuntimeEvent = RuntimeEvent; type Currency = Balances; + type HoldReason = HoldIdentifier; type WhitelistedCalls = WhitelistedCalls; type EnterDuration = EnterDuration; - type ActivateReservationAmount = ActivateReservationAmount; + type EnterStakeAmount = EnterStakeAmount; type ExtendDuration = ExtendDuration; - type ExtendReservationAmount = ExtendReservationAmount; + type ExtendStakeAmount = ExtendStakeAmount; type ForceEnterOrigin = ForceEnterOrigin; type ForceExtendOrigin = ForceExtendOrigin; type ForceExitOrigin = EnsureSignedBy; - type ReservationSlashOrigin = EnsureSignedBy; + type StakeSlashOrigin = EnsureSignedBy; type ReleaseDelay = ReleaseDelay; type WeightInfo = (); } diff --git a/frame/safe-mode/src/tests.rs b/frame/safe-mode/src/tests.rs index 44073a5d57f31..e9356b515b715 100644 --- a/frame/safe-mode/src/tests.rs +++ b/frame/safe-mode/src/tests.rs @@ -20,7 +20,7 @@ use super::*; use crate::mock::{RuntimeCall, *}; -use frame_support::{assert_err, assert_noop, assert_ok, dispatch::Dispatchable}; +use frame_support::{assert_err, assert_noop, assert_ok, dispatch::Dispatchable, traits::Currency}; use sp_runtime::TransactionOutcome; /// Do something hypothetically by rolling back any changes afterwards. @@ -67,8 +67,8 @@ fn fails_to_filter_calls_to_safe_mode_pallet() { frame_system::Error::::CallFiltered ); assert_ok!(SafeMode::force_exit(RuntimeOrigin::signed(mock::ForceExitOrigin::get()))); - assert_ok!(SafeMode::force_release_reservation( - RuntimeOrigin::signed(mock::ReservationSlashOrigin::get()), + assert_ok!(SafeMode::force_release_stake( + RuntimeOrigin::signed(mock::StakeSlashOrigin::get()), 0, activated_at_block )); @@ -80,8 +80,8 @@ fn fails_to_filter_calls_to_safe_mode_pallet() { frame_system::Error::::CallFiltered ); assert_ok!(SafeMode::force_exit(RuntimeOrigin::signed(mock::ForceExitOrigin::get()))); - assert_ok!(SafeMode::force_slash_reservation( - RuntimeOrigin::signed(mock::ReservationSlashOrigin::get()), + assert_ok!(SafeMode::force_slash_stake( + RuntimeOrigin::signed(mock::StakeSlashOrigin::get()), 0, activated_at_block + 2 )); @@ -105,15 +105,15 @@ fn fails_to_extend_if_not_activated() { } #[test] -fn fails_to_force_release_reservations_with_wrong_block() { +fn fails_to_force_release_stakes_with_wrong_block() { new_test_ext().execute_with(|| { let activated_at_block = System::block_number(); assert_ok!(SafeMode::enter(RuntimeOrigin::signed(0))); run_to(mock::EnterDuration::get() + activated_at_block + 1); assert_err!( - SafeMode::force_release_reservation( - RuntimeOrigin::signed(mock::ReservationSlashOrigin::get()), + SafeMode::force_release_stake( + RuntimeOrigin::signed(mock::StakeSlashOrigin::get()), 0, activated_at_block + 1 ), @@ -121,8 +121,8 @@ fn fails_to_force_release_reservations_with_wrong_block() { ); assert_err!( - SafeMode::force_slash_reservation( - RuntimeOrigin::signed(mock::ReservationSlashOrigin::get()), + SafeMode::force_slash_stake( + RuntimeOrigin::signed(mock::StakeSlashOrigin::get()), 0, activated_at_block + 1 ), @@ -132,17 +132,17 @@ fn fails_to_force_release_reservations_with_wrong_block() { } #[test] -fn fails_to_release_reservations_too_early() { +fn fails_to_release_stakes_too_early() { new_test_ext().execute_with(|| { let activated_at_block = System::block_number(); assert_ok!(SafeMode::enter(RuntimeOrigin::signed(0))); assert_ok!(SafeMode::force_exit(RuntimeOrigin::signed(mock::ForceExitOrigin::get()))); assert_err!( - SafeMode::release_reservation(RuntimeOrigin::signed(2), 0, activated_at_block), + SafeMode::release_stake(RuntimeOrigin::signed(2), 0, activated_at_block), Error::::CannotReleaseYet ); run_to(activated_at_block + mock::ReleaseDelay::get() + 1); - assert_ok!(SafeMode::release_reservation(RuntimeOrigin::signed(2), 0, activated_at_block)); + assert_ok!(SafeMode::release_stake(RuntimeOrigin::signed(2), 0, activated_at_block)); }); } @@ -221,10 +221,10 @@ fn can_activate() { SafeMode::active_until().unwrap(), System::block_number() + mock::EnterDuration::get() ); - assert_eq!(Balances::reserved_balance(0), mock::ActivateReservationAmount::get()); + assert_eq!(Balances::reserved_balance(0), mock::EnterStakeAmount::get()); assert_noop!(SafeMode::enter(RuntimeOrigin::signed(0)), Error::::Entered); // Assert the stake. - assert_eq!(Reservations::::get(0, 1), Some(mock::ActivateReservationAmount::get())); + assert_eq!(Stakes::::get(0, 1), Some(mock::EnterStakeAmount::get())); }); } @@ -239,7 +239,7 @@ fn can_extend() { ); assert_eq!( Balances::reserved_balance(0), - mock::ActivateReservationAmount::get() + mock::ExtendReservationAmount::get() + mock::EnterStakeAmount::get() + mock::ExtendStakeAmount::get() ); }); } @@ -256,13 +256,13 @@ fn can_extend_twice_in_same_block() { ); assert_eq!( Balances::reserved_balance(0), - mock::ActivateReservationAmount::get() + mock::ExtendReservationAmount::get() * 2 + mock::EnterStakeAmount::get() + mock::ExtendStakeAmount::get() * 2 ); }); } #[test] -fn can_release_independent_reservations_by_block() { +fn can_release_independent_stakes_by_block() { new_test_ext().execute_with(|| { let activated_at_block_0 = System::block_number(); assert_ok!(SafeMode::enter(RuntimeOrigin::signed(0))); @@ -272,29 +272,21 @@ fn can_release_independent_reservations_by_block() { let activated_at_block_1 = System::block_number(); assert_ok!(SafeMode::enter(RuntimeOrigin::signed(0))); - assert_eq!(Balances::free_balance(&0), 1234 - (2 * mock::ActivateReservationAmount::get())); // accounts set in mock genesis + assert_eq!(Balances::free_balance(&0), 1234 - (2 * mock::EnterStakeAmount::get())); // accounts set in mock genesis assert_ok!(SafeMode::force_exit(RuntimeOrigin::signed(mock::ForceExitOrigin::get()))); - assert_ok!(SafeMode::release_reservation( - RuntimeOrigin::signed(2), - 0, - activated_at_block_0 - )); + assert_ok!(SafeMode::release_stake(RuntimeOrigin::signed(2), 0, activated_at_block_0)); assert_err!( - SafeMode::release_reservation(RuntimeOrigin::signed(2), 0, activated_at_block_1), + SafeMode::release_stake(RuntimeOrigin::signed(2), 0, activated_at_block_1), Error::::CannotReleaseYet ); - assert_eq!(Balances::free_balance(&0), 1234 - mock::ActivateReservationAmount::get()); // accounts set in mock genesis + assert_eq!(Balances::free_balance(&0), 1234 - mock::EnterStakeAmount::get()); // accounts set in mock genesis run_to(mock::EnterDuration::get() + mock::ReleaseDelay::get() + activated_at_block_1 + 1); - assert_ok!(SafeMode::release_reservation( - RuntimeOrigin::signed(2), - 0, - activated_at_block_1 - )); - assert_eq!(Balances::total_balance(&0), 1234); // accounts set in mock genesis + assert_ok!(SafeMode::release_stake(RuntimeOrigin::signed(2), 0, activated_at_block_1)); + assert_eq!(>::total_balance(&0), 1234); // accounts set in mock genesis }); } @@ -308,11 +300,11 @@ fn fails_signed_origin_when_explicit_origin_required() { assert_err!(SafeMode::force_extend(RuntimeOrigin::signed(0)), DispatchError::BadOrigin); assert_err!(SafeMode::force_exit(RuntimeOrigin::signed(0)), DispatchError::BadOrigin); assert_err!( - SafeMode::force_slash_reservation(RuntimeOrigin::signed(0), 0, activated_at_block), + SafeMode::force_slash_stake(RuntimeOrigin::signed(0), 0, activated_at_block), DispatchError::BadOrigin ); assert_err!( - SafeMode::force_release_reservation(RuntimeOrigin::signed(0), 0, activated_at_block), + SafeMode::force_release_stake(RuntimeOrigin::signed(0), 0, activated_at_block), DispatchError::BadOrigin ); }); @@ -386,19 +378,19 @@ fn can_force_extend_with_config_origin() { } #[test] -fn can_force_release_reservation_with_config_origin() { +fn can_force_release_stake_with_config_origin() { new_test_ext().execute_with(|| { let activated_at_block = System::block_number(); assert_ok!(SafeMode::enter(RuntimeOrigin::signed(0))); - hypothetically_ok!(SafeMode::force_release_reservation( - RuntimeOrigin::signed(mock::ReservationSlashOrigin::get()), + hypothetically_ok!(SafeMode::force_release_stake( + RuntimeOrigin::signed(mock::StakeSlashOrigin::get()), 0, activated_at_block ),); run_to(mock::EnterDuration::get() + activated_at_block + 1); - assert_ok!(SafeMode::force_release_reservation( - RuntimeOrigin::signed(mock::ReservationSlashOrigin::get()), + assert_ok!(SafeMode::force_release_stake( + RuntimeOrigin::signed(mock::StakeSlashOrigin::get()), 0, activated_at_block )); @@ -415,8 +407,8 @@ fn can_force_release_reservation_with_config_origin() { 1, ); - assert_ok!(SafeMode::force_release_reservation( - RuntimeOrigin::signed(mock::ReservationSlashOrigin::get()), + assert_ok!(SafeMode::force_release_stake( + RuntimeOrigin::signed(mock::StakeSlashOrigin::get()), 0, activated_and_extended_at_block )); @@ -425,34 +417,34 @@ fn can_force_release_reservation_with_config_origin() { } #[test] -fn can_release_reservation_while_entered() { +fn can_release_stake_while_entered() { new_test_ext().execute_with(|| { assert_eq!(System::block_number(), 1); assert_ok!(SafeMode::enter(RuntimeOrigin::signed(0))); assert!(SafeMode::is_entered()); - assert_eq!(Balances::free_balance(&0), 1234 - mock::ActivateReservationAmount::get()); + assert_eq!(Balances::free_balance(&0), 1234 - mock::EnterStakeAmount::get()); // We could slash in the same block or any later. for i in 0..mock::EnterDuration::get() + 10 { run_to(i); - hypothetically_ok!(SafeMode::force_release_reservation( - RuntimeOrigin::signed(mock::ReservationSlashOrigin::get()), + hypothetically_ok!(SafeMode::force_release_stake( + RuntimeOrigin::signed(mock::StakeSlashOrigin::get()), 0, 1 )); } // Now once we slash once - assert_ok!(SafeMode::force_release_reservation( - RuntimeOrigin::signed(mock::ReservationSlashOrigin::get()), + assert_ok!(SafeMode::force_release_stake( + RuntimeOrigin::signed(mock::StakeSlashOrigin::get()), 0, 1 ),); assert_eq!(Balances::free_balance(&0), 1234); // ... it wont work ever again. assert_err!( - SafeMode::force_release_reservation( - RuntimeOrigin::signed(mock::ReservationSlashOrigin::get()), + SafeMode::force_release_stake( + RuntimeOrigin::signed(mock::StakeSlashOrigin::get()), 0, 1 ), @@ -462,7 +454,7 @@ fn can_release_reservation_while_entered() { } #[test] -fn can_slash_reservation_while_entered() { +fn can_slash_stake_while_entered() { new_test_ext().execute_with(|| { assert_eq!(System::block_number(), 1); assert_ok!(SafeMode::enter(RuntimeOrigin::signed(0))); @@ -471,81 +463,73 @@ fn can_slash_reservation_while_entered() { // We could slash in the same block or any later. for i in 0..mock::EnterDuration::get() + 10 { run_to(i); - hypothetically_ok!(SafeMode::force_slash_reservation( - RuntimeOrigin::signed(mock::ReservationSlashOrigin::get()), + hypothetically_ok!(SafeMode::force_slash_stake( + RuntimeOrigin::signed(mock::StakeSlashOrigin::get()), 0, 1 )); } // Now once we slash once - assert_ok!(SafeMode::force_slash_reservation( - RuntimeOrigin::signed(mock::ReservationSlashOrigin::get()), + assert_ok!(SafeMode::force_slash_stake( + RuntimeOrigin::signed(mock::StakeSlashOrigin::get()), 0, 1 ),); // ... it wont work ever again. assert_err!( - SafeMode::force_slash_reservation( - RuntimeOrigin::signed(mock::ReservationSlashOrigin::get()), - 0, - 1 - ), + SafeMode::force_slash_stake(RuntimeOrigin::signed(mock::StakeSlashOrigin::get()), 0, 1), Error::::NoReservation ); }); } #[test] -fn can_slash_reservation_from_extend_block() { +fn can_slash_stake_from_extend_block() { new_test_ext().execute_with(|| { assert_ok!(SafeMode::enter(RuntimeOrigin::signed(0))); assert_ok!(SafeMode::extend(RuntimeOrigin::signed(0))); assert_eq!( Balances::free_balance(&0), - 1234 - mock::ActivateReservationAmount::get() - mock::ExtendReservationAmount::get() + 1234 - mock::EnterStakeAmount::get() - mock::ExtendStakeAmount::get() ); - // Now once we slash once since the enter and extend are treated as one reservation. - assert_ok!(SafeMode::force_slash_reservation( - RuntimeOrigin::signed(mock::ReservationSlashOrigin::get()), + // Now once we slash once since the enter and extend are treated as one stake. + assert_ok!(SafeMode::force_slash_stake( + RuntimeOrigin::signed(mock::StakeSlashOrigin::get()), 0, 1 ),); assert_eq!( Balances::free_balance(&0), - 1234 - mock::ExtendReservationAmount::get() - mock::ActivateReservationAmount::get() + 1234 - mock::ExtendStakeAmount::get() - mock::EnterStakeAmount::get() ); // But never again. assert_err!( - SafeMode::force_slash_reservation( - RuntimeOrigin::signed(mock::ReservationSlashOrigin::get()), - 0, - 1 - ), + SafeMode::force_slash_stake(RuntimeOrigin::signed(mock::StakeSlashOrigin::get()), 0, 1), Error::::NoReservation ); }); } #[test] -fn can_slash_reservation_with_config_origin() { +fn can_slash_stake_with_config_origin() { new_test_ext().execute_with(|| { let activated_at_block = System::block_number(); assert_ok!(SafeMode::enter(RuntimeOrigin::signed(0))); - hypothetically_ok!(SafeMode::force_slash_reservation( - RuntimeOrigin::signed(mock::ReservationSlashOrigin::get()), + hypothetically_ok!(SafeMode::force_slash_stake( + RuntimeOrigin::signed(mock::StakeSlashOrigin::get()), 0, activated_at_block ),); run_to(mock::EnterDuration::get() + activated_at_block + 1); - assert_ok!(SafeMode::force_slash_reservation( - RuntimeOrigin::signed(mock::ReservationSlashOrigin::get()), + assert_ok!(SafeMode::force_slash_stake( + RuntimeOrigin::signed(mock::StakeSlashOrigin::get()), 0, activated_at_block )); - assert_eq!(Balances::free_balance(&0), 1234 - mock::ActivateReservationAmount::get()); // accounts set in mock genesis + assert_eq!(Balances::free_balance(&0), 1234 - mock::EnterStakeAmount::get()); // accounts set in mock genesis Balances::make_free_balance_be(&0, 1234); let activated_and_extended_at_block = System::block_number(); @@ -558,14 +542,14 @@ fn can_slash_reservation_with_config_origin() { 1, ); - assert_ok!(SafeMode::force_slash_reservation( - RuntimeOrigin::signed(mock::ReservationSlashOrigin::get()), + assert_ok!(SafeMode::force_slash_stake( + RuntimeOrigin::signed(mock::StakeSlashOrigin::get()), 0, activated_and_extended_at_block )); assert_eq!( Balances::free_balance(&0), - 1234 - mock::ActivateReservationAmount::get() - mock::ExtendReservationAmount::get() + 1234 - mock::EnterStakeAmount::get() - mock::ExtendStakeAmount::get() ); // accounts set in mock genesis }); } @@ -585,19 +569,11 @@ fn fails_when_explicit_origin_required() { DispatchError::BadOrigin ); assert_err!( - SafeMode::force_slash_reservation( - ForceEnterOrigin::Weak.signed(), - 0, - activated_at_block - ), + SafeMode::force_slash_stake(ForceEnterOrigin::Weak.signed(), 0, activated_at_block), DispatchError::BadOrigin ); assert_err!( - SafeMode::force_release_reservation( - ForceEnterOrigin::Weak.signed(), - 0, - activated_at_block - ), + SafeMode::force_release_stake(ForceEnterOrigin::Weak.signed(), 0, activated_at_block), DispatchError::BadOrigin ); @@ -610,19 +586,11 @@ fn fails_when_explicit_origin_required() { DispatchError::BadOrigin ); assert_err!( - SafeMode::force_slash_reservation( - ForceExtendOrigin::Weak.signed(), - 0, - activated_at_block - ), + SafeMode::force_slash_stake(ForceExtendOrigin::Weak.signed(), 0, activated_at_block), DispatchError::BadOrigin ); assert_err!( - SafeMode::force_release_reservation( - ForceExtendOrigin::Weak.signed(), - 0, - activated_at_block - ), + SafeMode::force_release_stake(ForceExtendOrigin::Weak.signed(), 0, activated_at_block), DispatchError::BadOrigin ); @@ -635,7 +603,7 @@ fn fails_when_explicit_origin_required() { DispatchError::BadOrigin ); assert_err!( - SafeMode::force_slash_reservation( + SafeMode::force_slash_stake( RuntimeOrigin::signed(mock::ForceExitOrigin::get()), 0, activated_at_block @@ -643,7 +611,7 @@ fn fails_when_explicit_origin_required() { DispatchError::BadOrigin ); assert_err!( - SafeMode::force_release_reservation( + SafeMode::force_release_stake( RuntimeOrigin::signed(mock::ForceExitOrigin::get()), 0, activated_at_block @@ -652,15 +620,15 @@ fn fails_when_explicit_origin_required() { ); assert_err!( - SafeMode::force_enter(RuntimeOrigin::signed(mock::ReservationSlashOrigin::get())), + SafeMode::force_enter(RuntimeOrigin::signed(mock::StakeSlashOrigin::get())), DispatchError::BadOrigin ); assert_err!( - SafeMode::force_extend(RuntimeOrigin::signed(mock::ReservationSlashOrigin::get())), + SafeMode::force_extend(RuntimeOrigin::signed(mock::StakeSlashOrigin::get())), DispatchError::BadOrigin ); assert_err!( - SafeMode::force_exit(RuntimeOrigin::signed(mock::ReservationSlashOrigin::get())), + SafeMode::force_exit(RuntimeOrigin::signed(mock::StakeSlashOrigin::get())), DispatchError::BadOrigin ); }); diff --git a/frame/safe-mode/src/weights.rs b/frame/safe-mode/src/weights.rs index 46e78521b5ae0..533cba52ba9fc 100644 --- a/frame/safe-mode/src/weights.rs +++ b/frame/safe-mode/src/weights.rs @@ -55,9 +55,9 @@ pub trait WeightInfo { fn extend() -> Weight; fn force_extend() -> Weight; fn force_exit() -> Weight; - fn release_reservation() -> Weight; - fn force_release_reservation() -> Weight; - fn force_slash_reservation() -> Weight; + fn release_stake() -> Weight; + fn force_release_stake() -> Weight; + fn force_slash_stake() -> Weight; } /// Weights for pallet_safe_mode using the Substrate node and recommended hardware. @@ -153,7 +153,7 @@ impl WeightInfo for SubstrateWeight { /// Proof: SafeMode EnteredUntil (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) /// Storage: Balances Reserves (r:1 w:1) /// Proof: Balances Reserves (max_values: None, max_size: Some(1049), added: 3524, mode: MaxEncodedLen) - fn release_reservation() -> Weight { + fn release_stake() -> Weight { // Proof Size summary in bytes: // Measured: `2080` // Estimated: `6566` @@ -166,7 +166,7 @@ impl WeightInfo for SubstrateWeight { /// Proof: SafeMode Reservations (max_values: None, max_size: Some(68), added: 2543, mode: MaxEncodedLen) /// Storage: Balances Reserves (r:1 w:1) /// Proof: Balances Reserves (max_values: None, max_size: Some(1049), added: 3524, mode: MaxEncodedLen) - fn force_release_reservation() -> Weight { + fn force_release_stake() -> Weight { // Proof Size summary in bytes: // Measured: `2080` // Estimated: `6067` @@ -179,7 +179,7 @@ impl WeightInfo for SubstrateWeight { /// Proof: SafeMode Reservations (max_values: None, max_size: Some(68), added: 2543, mode: MaxEncodedLen) /// Storage: Balances Reserves (r:1 w:1) /// Proof: Balances Reserves (max_values: None, max_size: Some(1049), added: 3524, mode: MaxEncodedLen) - fn force_slash_reservation() -> Weight { + fn force_slash_stake() -> Weight { // Proof Size summary in bytes: // Measured: `2083` // Estimated: `6067` @@ -282,7 +282,7 @@ impl WeightInfo for () { /// Proof: SafeMode EnteredUntil (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) /// Storage: Balances Reserves (r:1 w:1) /// Proof: Balances Reserves (max_values: None, max_size: Some(1049), added: 3524, mode: MaxEncodedLen) - fn release_reservation() -> Weight { + fn release_stake() -> Weight { // Proof Size summary in bytes: // Measured: `2080` // Estimated: `6566` @@ -295,7 +295,7 @@ impl WeightInfo for () { /// Proof: SafeMode Reservations (max_values: None, max_size: Some(68), added: 2543, mode: MaxEncodedLen) /// Storage: Balances Reserves (r:1 w:1) /// Proof: Balances Reserves (max_values: None, max_size: Some(1049), added: 3524, mode: MaxEncodedLen) - fn force_release_reservation() -> Weight { + fn force_release_stake() -> Weight { // Proof Size summary in bytes: // Measured: `2080` // Estimated: `6067` @@ -308,7 +308,7 @@ impl WeightInfo for () { /// Proof: SafeMode Reservations (max_values: None, max_size: Some(68), added: 2543, mode: MaxEncodedLen) /// Storage: Balances Reserves (r:1 w:1) /// Proof: Balances Reserves (max_values: None, max_size: Some(1049), added: 3524, mode: MaxEncodedLen) - fn force_slash_reservation() -> Weight { + fn force_slash_stake() -> Weight { // Proof Size summary in bytes: // Measured: `2083` // Estimated: `6067` diff --git a/frame/tx-pause/src/benchmarking.rs b/frame/tx-pause/src/benchmarking.rs index 77e2158778d4f..058f10a32ea83 100644 --- a/frame/tx-pause/src/benchmarking.rs +++ b/frame/tx-pause/src/benchmarking.rs @@ -23,12 +23,8 @@ use frame_benchmarking::benchmarks; benchmarks! { pause { - let full_name: FullNameOf = (name::(b"SomePalletName"), Some(name::(b"SomePalletName"))); - // let pallet_name: PalletNameOf = name::(b"SomePalletName"); - // let maybe_call_name: Option> = Some(name::(b"some_call_name")); + let full_name: FullNameOf = (name::(b"SomePalletName"), name::(b"SomePalletName")); let origin = T::PauseOrigin::try_successful_origin().expect("Tx-pause pallet does not make sense without pause origin"); - // let call = Call::::pause { full_name: full_name.clone() }; - // let call = Call::::pause { pallet_name: pallet_name.clone(), maybe_call_name: maybe_call_name.clone() }; }: _(origin, full_name.clone()) verify { @@ -36,8 +32,7 @@ benchmarks! { } unpause { - let full_name: FullNameOf = (name::(b"SomePalletName"), Some(name::(b"SomePalletName"))); - // tODO + let full_name: FullNameOf = (name::(b"SomePalletName"), name::(b"SomePalletName")); let pause_origin = T::PauseOrigin::try_successful_origin().expect("Tx-pause pallet does not make sense without pause origin"); TxPause::::pause( diff --git a/frame/tx-pause/src/lib.rs b/frame/tx-pause/src/lib.rs index b78e12d3fce17..281884e6edc64 100644 --- a/frame/tx-pause/src/lib.rs +++ b/frame/tx-pause/src/lib.rs @@ -29,7 +29,7 @@ //! The Transaction Pause pallet provides functions for: //! //! - Setting a dynamic list of [`FullNameOf`] items that are matched against to filter these calls. -//! - Setting [`Config::WhitelistCallNames`] that cannot be paused by this pallet. +//! - Setting [`Config::WhitelistedCalls`] that cannot be paused by this pallet. //! - Repatriating a reserved balance to a beneficiary account that exists. //! - Transferring a balance between accounts (when not reserved). //! - Slashing an account balance. @@ -99,6 +99,7 @@ pub use weights::*; /// The stringy name of a pallet from [`GetCallMetadata`] for [`Config::RuntimeCall`] variants. pub type PalletNameOf = BoundedVec::MaxNameLen>; + /// The stringy name of a call (within a pallet) from [`GetCallMetadata`] for /// [`Config::RuntimeCall`] variants. pub type CallNameOf = BoundedVec::MaxNameLen>; @@ -112,7 +113,6 @@ pub mod pallet { use super::*; #[pallet::pallet] - #[pallet::generate_store(pub(super) trait Store)] pub struct Pallet(PhantomData); #[pallet::config] @@ -139,21 +139,20 @@ pub mod pallet { /// /// The `TxMode` pallet cannot pause it's own calls, and does not need to be explicitly /// added here. - type WhitelistCallNames: Contains>; + type WhitelistedCalls: Contains>; /// Maximum length for pallet and call SCALE encoded string names. /// - /// Too long names will not be truncated but handled like - /// [`Self::PauseTooLongNames`] specifies. + /// Too long names will not be truncated but handled like [`Self::PauseTooLongNames`] + /// specifies. #[pallet::constant] type MaxNameLen: Get; /// Specifies if functions and pallets with too long names should be treated as paused. /// - /// Setting this to `true` ensures that all calls that - /// are callable, are also pause-able. - /// Otherwise there could be a situation where a call - /// is callable but not pause-able, which would could be exploited. + /// Setting this to `true` ensures that all calls that are callable, are also pause-able. + /// Otherwise there could be a situation where a call is callable but not pause-able, which + /// would could be exploited. #[pallet::constant] type PauseTooLongNames: Get; @@ -161,6 +160,12 @@ pub mod pallet { type WeightInfo: WeightInfo; } + /// The set of calls that are explicitly paused. + #[pallet::storage] + #[pallet::getter(fn paused_calls)] + pub type PausedCalls = + StorageMap<_, Blake2_128Concat, FullNameOf, (), OptionQuery>; + #[pallet::error] pub enum Error { /// The call is (already or still) paused. @@ -185,12 +190,6 @@ pub mod pallet { CallUnpaused { full_name: FullNameOf }, } - /// The set of calls that are explicitly paused. - #[pallet::storage] - #[pallet::getter(fn paused_calls)] - pub type PausedCalls = - StorageMap<_, Blake2_128Concat, FullNameOf, (), OptionQuery>; - /// Configure the initial state of this pallet in the genesis block. #[pallet::genesis_config] pub struct GenesisConfig { @@ -254,34 +253,33 @@ pub mod pallet { impl Pallet { /// Return whether this call is paused. - pub fn is_paused_unbound(pallet_name: Vec, call_name: Vec) -> bool { - let pallet_name = PalletNameOf::::try_from(pallet_name); - let call_name = CallNameOf::::try_from(call_name); + pub fn is_paused_unbound(pallet: Vec, call: Vec) -> bool { + let pallet = PalletNameOf::::try_from(pallet); + let call = CallNameOf::::try_from(call); - match (pallet_name, call_name) { - (Ok(pallet_name), Ok(call_name)) => - Self::is_paused(&&>::from((pallet_name.clone(), call_name.clone()))), + match (pallet, call) { + (Ok(pallet), Ok(call)) => Self::is_paused(&(pallet, call)), _ => T::PauseTooLongNames::get(), } } /// Return whether this call is paused. pub fn is_paused(full_name: &FullNameOf) -> bool { - if T::WhitelistCallNames::contains(full_name) { + if T::WhitelistedCalls::contains(full_name) { return false } - + >::contains_key(full_name) } - /// Ensure that this pallet or call can be paused. + /// Ensure that this call can be paused. pub fn ensure_can_pause(full_name: &FullNameOf) -> Result<(), Error> { - // The `TxPause` pallet can never be paused. + // SAFETY: The `TxPause` pallet can never pause itself. if full_name.0.as_ref() == ::name().as_bytes().to_vec() { return Err(Error::::Unpausable) } - if T::WhitelistCallNames::contains(&full_name) { + if T::WhitelistedCalls::contains(&full_name) { return Err(Error::::Unpausable) } if Self::is_paused(&full_name) { diff --git a/frame/tx-pause/src/mock.rs b/frame/tx-pause/src/mock.rs index 67f9d2fcb585e..b396380c1cde3 100644 --- a/frame/tx-pause/src/mock.rs +++ b/frame/tx-pause/src/mock.rs @@ -75,6 +75,10 @@ impl pallet_balances::Config for Test { type MaxLocks = MaxLocks; type MaxReserves = (); type ReserveIdentifier = [u8; 8]; + type HoldIdentifier = (); + type FreezeIdentifier = (); + type MaxHolds = ConstU32<0>; + type MaxFreezes = ConstU32<0>; } impl pallet_utility::Config for Test { @@ -144,11 +148,11 @@ parameter_types! { } #[derive(Copy, Clone, Encode, Decode, RuntimeDebug, MaxEncodedLen, scale_info::TypeInfo)] -pub struct WhitelistCallNames; +pub struct WhitelistedCalls; /// Contains used by `BaseCallFiler` so this impl whitelists `Balances::transfer_keep_alive`. All /// others may be paused. -impl Contains> for WhitelistCallNames { +impl Contains> for WhitelistedCalls { fn contains(full_name: &FullNameOf) -> bool { let unpausables: Vec> = vec![( b"Balances".to_vec().try_into().unwrap(), @@ -180,7 +184,7 @@ impl Config for Test { type RuntimeCall = RuntimeCall; type PauseOrigin = EnsureSignedBy; type UnpauseOrigin = EnsureSignedBy; - type WhitelistCallNames = WhitelistCallNames; + type WhitelistedCalls = WhitelistedCalls; type MaxNameLen = MaxNameLen; type PauseTooLongNames = PauseTooLongNames; type WeightInfo = (); diff --git a/frame/tx-pause/src/weights.rs b/frame/tx-pause/src/weights.rs index a4b408eef321e..aa6aa4de3160f 100644 --- a/frame/tx-pause/src/weights.rs +++ b/frame/tx-pause/src/weights.rs @@ -64,13 +64,13 @@ pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { // Storage: TxPause PausedCalls (r:2 w:1) fn pause() -> Weight { - Weight::from_ref_time(61_745_000 as u64) + Weight::from_parts(61_745_000 as u64, 0) .saturating_add(T::DbWeight::get().reads(2 as u64)) .saturating_add(T::DbWeight::get().writes(1 as u64)) } // Storage: TxPause PausedCalls (r:2 w:1) fn unpause() -> Weight { - Weight::from_ref_time(55_117_000 as u64) + Weight::from_parts(55_117_000 as u64, 0) .saturating_add(T::DbWeight::get().reads(2 as u64)) .saturating_add(T::DbWeight::get().writes(1 as u64)) } @@ -80,13 +80,13 @@ impl WeightInfo for SubstrateWeight { impl WeightInfo for () { // Storage: TxPause PausedCalls (r:2 w:1) fn pause() -> Weight { - Weight::from_ref_time(61_745_000 as u64) + Weight::from_parts(61_745_000 as u64, 0) .saturating_add(RocksDbWeight::get().reads(2 as u64)) .saturating_add(RocksDbWeight::get().writes(1 as u64)) } // Storage: TxPause PausedCalls (r:2 w:1) fn unpause() -> Weight { - Weight::from_ref_time(55_117_000 as u64) + Weight::from_parts(55_117_000 as u64, 0) .saturating_add(RocksDbWeight::get().reads(2 as u64)) .saturating_add(RocksDbWeight::get().writes(1 as u64)) } From a8a2948cd5a757e3d47e3d1638647370e3297c21 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Sat, 25 Mar 2023 19:50:03 +0100 Subject: [PATCH 038/100] Make compile Signed-off-by: Oliver Tale-Yazdi --- frame/remark/src/tests.rs | 1 + frame/safe-mode/src/lib.rs | 99 +++++++++++++++--------------------- frame/safe-mode/src/mock.rs | 2 + frame/safe-mode/src/tests.rs | 12 +++-- frame/tx-pause/src/lib.rs | 63 ----------------------- frame/tx-pause/src/mock.rs | 2 + 6 files changed, 54 insertions(+), 125 deletions(-) diff --git a/frame/remark/src/tests.rs b/frame/remark/src/tests.rs index bd86b635ef43b..51fb32b60fc1f 100644 --- a/frame/remark/src/tests.rs +++ b/frame/remark/src/tests.rs @@ -20,6 +20,7 @@ use super::{Error, Event, Pallet as Remark}; use crate::mock::*; use frame_support::{assert_noop, assert_ok}; +use frame_system::RawOrigin; #[test] fn generates_event() { diff --git a/frame/safe-mode/src/lib.rs b/frame/safe-mode/src/lib.rs index 3973fe5fe1cff..94a897b55e963 100644 --- a/frame/safe-mode/src/lib.rs +++ b/frame/safe-mode/src/lib.rs @@ -18,9 +18,7 @@ #![cfg_attr(not(feature = "std"), no_std)] mod benchmarking; -#[cfg(test)] pub mod mock; -#[cfg(test)] mod tests; pub mod weights; @@ -123,7 +121,7 @@ pub mod pallet { /// The minimal duration a deposit will remain reserved after safe-mode is entered or /// extended, unless [`Pallet::force_release_stake`] is successfully called sooner. /// - /// Every reservation is tied to a specific activation or extension, thus each reservation + /// Every stake is tied to a specific activation or extension, thus each stake /// can be released independently after the delay for it has passed. /// /// `None` disallows permissionlessly releasing the safe-mode Stakes. @@ -146,9 +144,9 @@ pub mod pallet { NotConfigured, /// There is no balance reserved. - NoReservation, + NoStake, - /// This reservation cannot be released yet. + /// This stake cannot be released yet. CannotReleaseYet, } @@ -165,10 +163,10 @@ pub mod pallet { Exited { reason: ExitReason }, /// An account had a reserve released that was reserved at a specific block. - ReservationReleased { account: T::AccountId, block: T::BlockNumber, amount: BalanceOf }, + StakeReleased { account: T::AccountId, block: T::BlockNumber, amount: BalanceOf }, /// An account had reserve slashed that was reserved at a specific block. - ReservationSlashed { account: T::AccountId, block: T::BlockNumber, amount: BalanceOf }, + StakeSlashed { account: T::AccountId, block: T::BlockNumber, amount: BalanceOf }, } /// The reason why the safe-mode was deactivated. @@ -235,13 +233,7 @@ pub mod pallet { /// Reserves [`Config::EnterStakeAmount`] from the caller's account. /// Emits an [`Event::Entered`] event on success. /// Errors with [`Error::Entered`] if the safe-mode is already entered. - /// Errors with [`Error::NotConfigured`] if the reservation amount is `None` . - /// - /// ### Safety - /// - /// This may be called by any signed origin with [`Config::EnterStakeAmount`] free - /// currency to reserve. This call can be disabled for all origins by configuring - /// [`Config::EnterStakeAmount`] to `None`. + /// Errors with [`Error::NotConfigured`] if the stake amount is `None`. #[pallet::call_index(0)] #[pallet::weight(T::WeightInfo::enter())] pub fn enter(origin: OriginFor) -> DispatchResult { @@ -270,7 +262,7 @@ pub mod pallet { /// Reserves [`Config::ExtendStakeAmount`] from the caller's account. /// Emits an [`Event::Extended`] event on success. /// Errors with [`Error::Exited`] if the safe-mode is entered. - /// Errors with [`Error::NotConfigured`] if the reservation amount is `None` . + /// Errors with [`Error::NotConfigured`] if the stake amount is `None` . /// /// This may be called by any signed origin with [`Config::ExtendStakeAmount`] free /// currency to reserve. This call can be disabled for all origins by configuring @@ -305,11 +297,6 @@ pub mod pallet { /// Note: `safe-mode` will be automatically deactivated by [`Pallet::on_initialize`] hook /// after the block height is greater than [`EnteredUntil`] found in storage. /// Emits an [`Event::Exited`] with [`ExitReason::Timeout`] event on hook. - /// - /// - /// ### Safety - /// - /// Can only be called by the [`Config::ForceExitOrigin`] origin. #[pallet::call_index(4)] #[pallet::weight(T::WeightInfo::force_exit())] pub fn force_exit(origin: OriginFor) -> DispatchResult { @@ -318,12 +305,12 @@ pub mod pallet { Self::do_deactivate(ExitReason::Force) } - /// Slash a reservation for an account that entered or extended safe-mode at a specific + /// Slash a stake for an account that entered or extended safe-mode at a specific /// block earlier. /// /// This can be called while safe-mode is entered. /// - /// Emits a [`Event::ReservationSlashed`] event on success. + /// Emits a [`Event::StakeSlashed`] event on success. /// Errors with [`Error::Entered`] if the safe-mode is entered. /// /// Can only be called by the [`Config::StakeSlashOrigin`] origin. @@ -339,17 +326,17 @@ pub mod pallet { Self::do_force_slash(account, block) } - /// Permissionlessly release a reservation for an account that entered safe-mode at a + /// Permissionlessly release a stake for an account that entered safe-mode at a /// specific block earlier. /// /// The call can be completely disabled by setting [`Config::ReleaseDelay`] to `None`. /// This cannot be called while safe-mode is entered and not until the /// [`Config::ReleaseDelay`] block height is passed. /// - /// Emits a [`Event::ReservationReleased`] event on success. + /// Emits a [`Event::StakeReleased`] event on success. /// Errors with [`Error::Entered`] if the safe-mode is entered. /// Errors with [`Error::CannotReleaseYet`] if the [`Config::ReleaseDelay`] . - /// Errors with [`Error::NoReservation`] if the payee has no reserved currency at the + /// Errors with [`Error::NoStake`] if the payee has no reserved currency at the /// block specified. #[pallet::call_index(6)] #[pallet::weight(T::WeightInfo::release_stake())] @@ -363,14 +350,14 @@ pub mod pallet { Self::do_release(false, account, block) } - /// Force to release a reservation for an account that entered safe-mode at a specific + /// Force to release a stake for an account that entered safe-mode at a specific /// block earlier. /// /// This can be called while safe-mode is still entered. /// - /// Emits a [`Event::ReservationReleased`] event on success. + /// Emits a [`Event::StakeReleased`] event on success. /// Errors with [`Error::Entered`] if the safe-mode is entered. - /// Errors with [`Error::NoReservation`] if the payee has no reserved currency at the + /// Errors with [`Error::NoStake`] if the payee has no reserved currency at the /// block specified. /// /// Can only be called by the [`Config::StakeSlashOrigin`] origin. @@ -390,16 +377,16 @@ pub mod pallet { #[pallet::hooks] impl Hooks> for Pallet { /// Automatically exits the safe-mode when the period runs out. - /// - /// Bypasses any call filters to avoid getting rejected by them. fn on_initialize(current: T::BlockNumber) -> Weight { - match EnteredUntil::::get() { - Some(limit) if current > limit => { - let _ = - Self::do_deactivate(ExitReason::Timeout).defensive_proof("Must exit; qed"); - T::WeightInfo::on_initialize_exit() - }, - _ => T::WeightInfo::on_initialize_noop(), + let Some(limit) = EnteredUntil::::get() else { + return T::WeightInfo::on_initialize_noop(); + }; + + if current > limit { + let _ = Self::do_deactivate(ExitReason::Timeout).defensive_proof("Must exit; qed"); + T::WeightInfo::on_initialize_exit() + } else { + T::WeightInfo::on_initialize_noop() } } } @@ -408,11 +395,11 @@ pub mod pallet { impl Pallet { /// Logic for the [`crate::Pallet::enter`] and [`crate::Pallet::force_enter`] calls. fn do_enter(who: Option, duration: T::BlockNumber) -> DispatchResult { - ensure!(!EnteredUntil::::exists(), Error::::Entered); + ensure!(!Self::is_entered(), Error::::Entered); if let Some(who) = who { - let reserve = T::EnterStakeAmount::get().ok_or(Error::::NotConfigured)?; - Self::reserve(who, reserve)?; + let amount = T::EnterStakeAmount::get().ok_or(Error::::NotConfigured)?; + Self::hold(who, amount)?; } let until = >::block_number().saturating_add(duration); @@ -426,8 +413,8 @@ impl Pallet { let mut until = EnteredUntil::::get().ok_or(Error::::Exited)?; if let Some(who) = who { - let reserve = T::ExtendStakeAmount::get().ok_or(Error::::NotConfigured)?; - Self::reserve(who, reserve)?; + let amount = T::ExtendStakeAmount::get().ok_or(Error::::NotConfigured)?; + Self::hold(who, amount)?; } until.saturating_accrue(duration); @@ -447,12 +434,8 @@ impl Pallet { /// Logic for the [`crate::Pallet::release_stake`] and /// [`crate::Pallet::force_release_stake`] calls. - /// - /// Errors if the safe-mode is entered with [`Error::Entered`] when `force` is `false`. - /// Errors if release is called too soon by anyone but [`Config::StakeSlashOrigin`] with - /// [`Error::CannotReleaseYet`]. fn do_release(force: bool, account: T::AccountId, block: T::BlockNumber) -> DispatchResult { - let amount = Stakes::::take(&account, &block).ok_or(Error::::NoReservation)?; + let amount = Stakes::::take(&account, &block).ok_or(Error::::NoStake)?; if !force { ensure!(!Self::is_entered(), Error::::Entered); @@ -462,19 +445,19 @@ impl Pallet { ensure!(now > (block.saturating_add(delay)), Error::::CannotReleaseYet); } - T::Currency::release( + let amount = T::Currency::release( &T::HoldReason::cause(block), &account, amount, Precision::BestEffort, )?; - Self::deposit_event(Event::::ReservationReleased { block, account, amount }); + Self::deposit_event(Event::::StakeReleased { block, account, amount }); Ok(()) } - /// Logic for the [`crate::Pallet::slash_reservation`] call. + /// Logic for the [`crate::Pallet::slash_stake`] call. fn do_force_slash(account: T::AccountId, block: T::BlockNumber) -> DispatchResult { - let amount = Stakes::::take(&account, block).ok_or(Error::::NoReservation)?; + let amount = Stakes::::take(&account, block).ok_or(Error::::NoStake)?; // FAIL-CI check these args T::Currency::burn_held( @@ -484,16 +467,18 @@ impl Pallet { Precision::BestEffort, Fortitude::Force, )?; - Self::deposit_event(Event::::ReservationSlashed { block, account, amount }); + Self::deposit_event(Event::::StakeSlashed { block, account, amount }); Ok(()) } - /// Reserve `amount` from `who` and store it in `Stakes`. - fn reserve(who: T::AccountId, amount: BalanceOf) -> DispatchResult { + /// Hold `amount` from `who` and store it in `Stakes`. + fn hold(who: T::AccountId, amount: BalanceOf) -> DispatchResult { let block = >::block_number(); + // Hold for the same reason will increase the amount. T::Currency::hold(&T::HoldReason::cause(block), &who, amount)?; - let current_reservation = Stakes::::get(&who, block).unwrap_or_default(); - Stakes::::insert(&who, block, current_reservation.saturating_add(amount)); + + let current_stake = Stakes::::get(&who, block).unwrap_or_default(); + Stakes::::insert(&who, block, current_stake.saturating_add(amount)); Ok(()) } @@ -508,7 +493,7 @@ impl Pallet { T::RuntimeCall: GetCallMetadata, { let CallMetadata { pallet_name, .. } = call.get_call_metadata(); - // The `SafeMode` pallet is always allowed. + // SAFETY: The `SafeMode` pallet is always allowed. if pallet_name == as PalletInfoAccess>::name() { return true } diff --git a/frame/safe-mode/src/mock.rs b/frame/safe-mode/src/mock.rs index 77b0ce183892b..2d0d48566b59d 100644 --- a/frame/safe-mode/src/mock.rs +++ b/frame/safe-mode/src/mock.rs @@ -17,6 +17,8 @@ //! Test utilities for safe mode pallet. +#![cfg(test)] + use super::*; use crate as pallet_safe_mode; diff --git a/frame/safe-mode/src/tests.rs b/frame/safe-mode/src/tests.rs index e9356b515b715..7329e109b9ac2 100644 --- a/frame/safe-mode/src/tests.rs +++ b/frame/safe-mode/src/tests.rs @@ -17,6 +17,8 @@ //! Test utilities for the safe mode pallet. +#![cfg(test)] + use super::*; use crate::mock::{RuntimeCall, *}; @@ -117,7 +119,7 @@ fn fails_to_force_release_stakes_with_wrong_block() { 0, activated_at_block + 1 ), - Error::::NoReservation + Error::::NoStake ); assert_err!( @@ -126,7 +128,7 @@ fn fails_to_force_release_stakes_with_wrong_block() { 0, activated_at_block + 1 ), - Error::::NoReservation + Error::::NoStake ); }); } @@ -448,7 +450,7 @@ fn can_release_stake_while_entered() { 0, 1 ), - Error::::NoReservation + Error::::NoStake ); }); } @@ -478,7 +480,7 @@ fn can_slash_stake_while_entered() { // ... it wont work ever again. assert_err!( SafeMode::force_slash_stake(RuntimeOrigin::signed(mock::StakeSlashOrigin::get()), 0, 1), - Error::::NoReservation + Error::::NoStake ); }); } @@ -507,7 +509,7 @@ fn can_slash_stake_from_extend_block() { // But never again. assert_err!( SafeMode::force_slash_stake(RuntimeOrigin::signed(mock::StakeSlashOrigin::get()), 0, 1), - Error::::NoReservation + Error::::NoStake ); }); } diff --git a/frame/tx-pause/src/lib.rs b/frame/tx-pause/src/lib.rs index 281884e6edc64..2ac422bbf8c9e 100644 --- a/frame/tx-pause/src/lib.rs +++ b/frame/tx-pause/src/lib.rs @@ -15,73 +15,10 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! # Transaction Pause Pallet -//! -//! The Transaction Pause pallet provides a dynamic call filter that can be controlled with -//! extrinsics. This pallet may be used to disable dispatch of specific calls within a runtime. -//! -//! - [`Config`] -//! - [`Call`] -//! - [`Pallet`] -//! -//! ## Overview -//! -//! The Transaction Pause pallet provides functions for: -//! -//! - Setting a dynamic list of [`FullNameOf`] items that are matched against to filter these calls. -//! - Setting [`Config::WhitelistedCalls`] that cannot be paused by this pallet. -//! - Repatriating a reserved balance to a beneficiary account that exists. -//! - Transferring a balance between accounts (when not reserved). -//! - Slashing an account balance. -//! - Account creation and removal. -//! - Managing total issuance. -//! - Setting and managing locks. -//! -//! Can also be used as call-filter by the runtime together with the SafeMode -//! -//! TODO expand an link to safe mode in docs. -//! -//! ### Terminology -//! -//! - **Pause**: The ability to dispatch of a specific call becomes disabled. -//! - **Unpause**: The ability to dispatch of a specific call becomes enabled, if it was paused. -//! -//! ## Interface -//! -//! ### Dispatchable Functions -//! -//! - `pause` - Pause a pallet or optionally a specific call within a pallet. -//! - `unpause` - Unpause a pallet or optionally a specific call within a pallet. -//! -//! ## Usage -//! -//! The following examples show how to use the Transaction Pause pallet in your custom pallet. -//! TODO check doc links -//! ### Example within a runtime's [`pallet-frame-system::BaseCallFilter`] configuration: -//! -//! ```ignore -//! impl frame_system::Config for Runtime { -//! … -//! type BaseCallFilter = InsideBoth>; -//! … -//! } -//! ``` -//! -//! ## Genesis config -//! -//! The Transaction Pause pallet may be configured to pause calls on genesis with it's -//! [`GenesisConfig`]. -//! -//! ## Assumptions -//! -//! * TODO - #![cfg_attr(not(feature = "std"), no_std)] mod benchmarking; -#[cfg(test)] pub mod mock; -#[cfg(test)] mod tests; pub mod weights; diff --git a/frame/tx-pause/src/mock.rs b/frame/tx-pause/src/mock.rs index b396380c1cde3..f931e27132c96 100644 --- a/frame/tx-pause/src/mock.rs +++ b/frame/tx-pause/src/mock.rs @@ -17,6 +17,8 @@ //! Test utilities for transaction pause (tx pause) pallet. +#![cfg(test)] + use super::*; use crate as pallet_tx_pause; From 2f551fba77ddf874265bbf94f74e9d6bf4a90b68 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Sat, 25 Mar 2023 20:14:26 +0100 Subject: [PATCH 039/100] Fix node config Signed-off-by: Oliver Tale-Yazdi --- bin/node/runtime/src/lib.rs | 99 ++++++++++++++++++------------------- 1 file changed, 47 insertions(+), 52 deletions(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 8a4f2d1269ce9..482b77a625548 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -34,9 +34,10 @@ use frame_support::{ traits::{ fungible::ItemOf, tokens::{nonfungibles_v2::Inspect, GetSalary, PayFromAccount}, - AsEnsureOriginWithArg, ConstBool, ConstU128, ConstU16, ConstU32, Currency, EitherOfDiverse, - EqualPrivilegeOnly, Everything, Imbalance, InstanceFilter, KeyOwnerProofSystem, - LockIdentifier, Nothing, OnUnbalanced, U128CurrencyToVote, WithdrawReasons, + AsEnsureOriginWithArg, ConstBool, ConstU128, ConstU16, ConstU32, Contains, Currency, + EitherOfDiverse, EqualPrivilegeOnly, Imbalance, InsideBoth, InstanceFilter, + KeyOwnerProofSystem, LockIdentifier, Nothing, OnUnbalanced, U128CurrencyToVote, + WithdrawReasons, }, weights::{ constants::{ @@ -59,11 +60,12 @@ use pallet_nis::WithMaximumOf; use pallet_session::historical as pallet_session_historical; pub use pallet_transaction_payment::{CurrencyAdapter, Multiplier, TargetedFeeAdjustment}; use pallet_transaction_payment::{FeeDetails, RuntimeDispatchInfo}; +use pallet_tx_pause::FullNameOf; use scale_info::TypeInfo; use sp_api::impl_runtime_apis; use sp_authority_discovery::AuthorityId as AuthorityDiscoveryId; use sp_consensus_grandpa::AuthorityId as GrandpaId; -use sp_core::{crypto::KeyTypeId, OpaqueMetadata}; +use sp_core::{crypto::KeyTypeId, ed25519::Public, OpaqueMetadata}; use sp_inherents::{CheckInherentsResult, InherentData}; use sp_runtime::{ create_runtime_str, @@ -214,41 +216,30 @@ parameter_types! { const_assert!(NORMAL_DISPATCH_RATIO.deconstruct() >= AVERAGE_ON_INITIALIZE_RATIO.deconstruct()); -/// Filter to block balance pallet calls -/// Used for both SafeMode and TxPause pallets -/// Therefor we include both so they cannot affect each other -pub struct WhitelistedCalls; -impl Contains for WhitelistedCalls { +/// Calls that can bypass the safe-mode pallet. +pub struct SafeModeWhitelistedCalls; +impl Contains for SafeModeWhitelistedCalls { fn contains(call: &RuntimeCall) -> bool { match call { RuntimeCall::System(_) | RuntimeCall::SafeMode(_) | RuntimeCall::TxPause(_) => true, RuntimeCall::Balances(_) => false, + // ... governance et al _ => false, } } } -use pallet_tx_pause::FullNameOf; -pub struct WhitelistedCalls; +/// Calls that cannot be paused by the tx-pause pallet. +pub struct TxPauseWhitelistedCalls; /// Whitelist `Balances::transfer_keep_alive`, all others are pauseable. -impl Contains> for WhitelistedCalls { +impl Contains> for TxPauseWhitelistedCalls { fn contains(full_name: &FullNameOf) -> bool { let unpausables: Vec> = vec![( b"Balances".to_vec().try_into().unwrap(), - Some(b"transfer_keep_alive".to_vec().try_into().unwrap()), + b"transfer_keep_alive".to_vec().try_into().unwrap(), )]; - for unpausable_call in unpausables { - let (pallet_name, maybe_call_name) = full_name; - if pallet_name == &unpausable_call.0 { - if unpausable_call.1.is_none() { - return true - } - return maybe_call_name == &unpausable_call.1 - } - } - - false + unpausables.contains(full_name) } } @@ -257,7 +248,7 @@ impl pallet_tx_pause::Config for Runtime { type RuntimeCall = RuntimeCall; type PauseOrigin = EnsureRoot; type UnpauseOrigin = EnsureRoot; - type WhitelistedCalls = WhitelistedCalls; + type WhitelistedCalls = TxPauseWhitelistedCalls; type MaxNameLen = ConstU32<256>; type PauseTooLongNames = ConstBool; type WeightInfo = pallet_tx_pause::weights::SubstrateWeight; @@ -287,12 +278,12 @@ impl ForceEnterOrigin { } } - /// Account id of the origin. + /// Dummy account id of the origin. pub fn acc(&self) -> AccountId { match self { - Self::Weak => sp_core::ed25519::Public::from_raw([0; 32]).into(), - Self::Medium => sp_core::ed25519::Public::from_raw([1; 32]).into(), - Self::Strong => sp_core::ed25519::Public::from_raw([2; 32]).into(), + Self::Weak => Public::from_raw([0; 32]).into(), + Self::Medium => Public::from_raw([1; 32]).into(), + Self::Strong => Public::from_raw([2; 32]).into(), } } @@ -312,12 +303,12 @@ impl ForceExtendOrigin { } } - /// Account id of the origin. + /// A dummy id for this origin. pub fn acc(&self) -> AccountId { match self { - Self::Weak => sp_core::ed25519::Public::from_raw([0; 32]).into(), - Self::Medium => sp_core::ed25519::Public::from_raw([1; 32]).into(), - Self::Strong => sp_core::ed25519::Public::from_raw([2; 32]).into(), + Self::Weak => Public::from_raw([0; 32]).into(), + Self::Medium => Public::from_raw([1; 32]).into(), + Self::Strong => Public::from_raw([2; 32]).into(), } } @@ -327,19 +318,18 @@ impl ForceExtendOrigin { } } +// FAIL-CI can probably feature gate impl, O>> + From>> EnsureOrigin for ForceEnterOrigin { type Success = u32; fn try_origin(o: O) -> Result { + use ForceEnterOrigin::*; o.into().and_then(|o| match o { - RawOrigin::Signed(acc) if acc == ForceEnterOrigin::Weak.acc() => - Ok(ForceEnterOrigin::Weak.duration()), - RawOrigin::Signed(acc) if acc == ForceEnterOrigin::Medium.acc() => - Ok(ForceEnterOrigin::Medium.duration()), - RawOrigin::Signed(acc) if acc == ForceEnterOrigin::Strong.acc() => - Ok(ForceEnterOrigin::Strong.duration()), + RawOrigin::Signed(acc) if acc == Weak.acc() => Ok(Weak.duration()), + RawOrigin::Signed(acc) if acc == Medium.acc() => Ok(Medium.duration()), + RawOrigin::Signed(acc) if acc == Strong.acc() => Ok(Strong.duration()), r => Err(O::from(r)), }) } @@ -356,13 +346,11 @@ impl, O>> + From>> Ensu type Success = u32; fn try_origin(o: O) -> Result { + use ForceEnterOrigin::*; o.into().and_then(|o| match o { - RawOrigin::Signed(acc) if acc == ForceExtendOrigin::Weak.acc() => - Ok(ForceExtendOrigin::Weak.duration()), - RawOrigin::Signed(acc) if acc == ForceExtendOrigin::Medium.acc() => - Ok(ForceExtendOrigin::Medium.duration()), - RawOrigin::Signed(acc) if acc == ForceExtendOrigin::Strong.acc() => - Ok(ForceExtendOrigin::Strong.duration()), + RawOrigin::Signed(acc) if acc == Weak.acc() => Ok(Weak.duration()), + RawOrigin::Signed(acc) if acc == Medium.acc() => Ok(Medium.duration()), + RawOrigin::Signed(acc) if acc == Strong.acc() => Ok(Strong.duration()), r => Err(O::from(r)), }) } @@ -373,6 +361,14 @@ impl, O>> + From>> Ensu } } +impl pallet_safe_mode::CausalHoldReason for HoldReason { + type Reason = HoldReason; + + fn cause(block: u32) -> Self::Reason { + Self::SafeModeStake { block } + } +} + parameter_types! { pub const SignedEnterDuration: u32 = 10; pub const ExtendDuration: u32 = 20; @@ -384,7 +380,8 @@ parameter_types! { impl pallet_safe_mode::Config for Runtime { type RuntimeEvent = RuntimeEvent; type Currency = Balances; - type WhitelistedCalls = WhitelistedCalls; + type HoldReason = HoldReason; + type WhitelistedCalls = SafeModeWhitelistedCalls; type EnterDuration = ConstU32<{ 2 * DAYS }>; type EnterStakeAmount = EnterStakeAmount; type ExtendDuration = ConstU32<{ 1 * DAYS }>; @@ -398,7 +395,7 @@ impl pallet_safe_mode::Config for Runtime { } impl frame_system::Config for Runtime { - type BaseCallFilter = InsideBoth; // TODO consider Exclude or NotInside for WhitelistedCalls -> see TheseExcept ) + type BaseCallFilter = InsideBoth; type BlockWeights = RuntimeBlockWeights; type BlockLength = RuntimeBlockLength; type DbWeight = RocksDbWeight; @@ -459,11 +456,6 @@ parameter_types! { pub const AnnouncementDepositFactor: Balance = deposit(0, 66); } -// pub enum PausePresets { -// ..., // todo - -// } - /// The type used to represent the kinds of proxying allowed. #[derive( Copy, @@ -628,6 +620,9 @@ parameter_types! { pub enum HoldReason { /// The NIS Pallet has reserved it for a non-fungible receipt. Nis, + SafeModeStake { + block: u32, + }, } impl pallet_balances::Config for Runtime { From 5292a697454ca7777d4f7850035a67b5255952e4 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Sat, 25 Mar 2023 20:22:42 +0100 Subject: [PATCH 040/100] Typos Signed-off-by: Oliver Tale-Yazdi --- bin/node/runtime/src/lib.rs | 2 +- frame/benchmarking/src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 482b77a625548..cc594dfa1c62a 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -628,7 +628,7 @@ pub enum HoldReason { impl pallet_balances::Config for Runtime { type MaxLocks = MaxLocks; type MaxReserves = MaxReserves; - type ReserveIdentifier = Self::BlockNumber; + type ReserveIdentifier = [u8; 8]; type Balance = Balance; type DustRemoval = (); type RuntimeEvent = RuntimeEvent; diff --git a/frame/benchmarking/src/lib.rs b/frame/benchmarking/src/lib.rs index 955acf17a02e5..7110c378d581e 100644 --- a/frame/benchmarking/src/lib.rs +++ b/frame/benchmarking/src/lib.rs @@ -157,7 +157,7 @@ pub use v1::*; /// _(RawOrigin::Signed(whitelisted_caller()), 0u32.into(), 0); /// ``` /// -/// The underscore will beu substituted with the name of the benchmark (i.e. the name of the +/// The underscore will be substituted with the name of the benchmark (i.e. the name of the /// function in the benchmark function definition). /// /// Regardless of whether `#[extrinsic_call]` or `#[block]` is used, this attribute also serves From 6b5c8203f8155f01d3266614ba4027133fb70c61 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Wed, 29 Mar 2023 12:17:44 +0200 Subject: [PATCH 041/100] Remove CausalHoldReason Signed-off-by: Oliver Tale-Yazdi --- bin/node/runtime/src/lib.rs | 16 ++++------------ frame/examples/basic/src/tests.rs | 14 ++++++++++++++ frame/safe-mode/src/lib.rs | 25 ++++++------------------- frame/safe-mode/src/mock.rs | 29 ++++++++++++++--------------- 4 files changed, 38 insertions(+), 46 deletions(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index cc594dfa1c62a..36f912c8d940d 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -361,26 +361,19 @@ impl, O>> + From>> Ensu } } -impl pallet_safe_mode::CausalHoldReason for HoldReason { - type Reason = HoldReason; - - fn cause(block: u32) -> Self::Reason { - Self::SafeModeStake { block } - } -} - parameter_types! { pub const SignedEnterDuration: u32 = 10; pub const ExtendDuration: u32 = 20; pub const EnterStakeAmount: Balance = 10 * DOLLARS; pub const ExtendStakeAmount: Balance = 15 * DOLLARS; pub const ReleaseDelay: u32 = 15; + pub const SafeModeHoldReason: HoldReason = HoldReason::SafeMode; } impl pallet_safe_mode::Config for Runtime { type RuntimeEvent = RuntimeEvent; type Currency = Balances; - type HoldReason = HoldReason; + type HoldReason = SafeModeHoldReason; type WhitelistedCalls = SafeModeWhitelistedCalls; type EnterDuration = ConstU32<{ 2 * DAYS }>; type EnterStakeAmount = EnterStakeAmount; @@ -620,9 +613,8 @@ parameter_types! { pub enum HoldReason { /// The NIS Pallet has reserved it for a non-fungible receipt. Nis, - SafeModeStake { - block: u32, - }, + /// A stake was reserved for a entering or extending the SafeMode. + SafeMode, } impl pallet_balances::Config for Runtime { diff --git a/frame/examples/basic/src/tests.rs b/frame/examples/basic/src/tests.rs index d9a8a4e8e1cdc..10a3528457661 100644 --- a/frame/examples/basic/src/tests.rs +++ b/frame/examples/basic/src/tests.rs @@ -200,3 +200,17 @@ fn weights_work() { // TODO: account for proof size weight assert!(info1.weight.ref_time() > info2.weight.ref_time()); } + +frame_support::parameter_types! { + pub storage Value: u64 = 1; +} + +#[test] +fn storage_consistent() { + new_test_ext().execute_with(|| { + let _g = frame_support::StorageNoopGuard::default(); + + let v = Value::get(); + Value::set(&1); + }); +} diff --git a/frame/safe-mode/src/lib.rs b/frame/safe-mode/src/lib.rs index 94a897b55e963..a6d5272f0a8bc 100644 --- a/frame/safe-mode/src/lib.rs +++ b/frame/safe-mode/src/lib.rs @@ -44,13 +44,6 @@ pub use weights::*; type BalanceOf = <::Currency as FunInspect<::AccountId>>::Balance; -/// Create a hold reason for a given cause. -pub trait CausalHoldReason { - type Reason: codec::Encode + TypeInfo + 'static; - - fn cause(path: Cause) -> Self::Reason; -} - #[frame_support::pallet] pub mod pallet { use super::*; @@ -67,10 +60,8 @@ pub mod pallet { type Currency: FunHoldInspect + FunHoldMutate; /// Create a hold reason for a specific cause. - type HoldReason: CausalHoldReason< - Self::BlockNumber, - Reason = >::Reason, - >; + #[pallet::constant] + type HoldReason: Get<>::Reason>; /// Contains all runtime calls in any pallet that can be dispatched even while the safe-mode /// is entered. @@ -445,12 +436,8 @@ impl Pallet { ensure!(now > (block.saturating_add(delay)), Error::::CannotReleaseYet); } - let amount = T::Currency::release( - &T::HoldReason::cause(block), - &account, - amount, - Precision::BestEffort, - )?; + let amount = + T::Currency::release(&T::HoldReason::get(), &account, amount, Precision::BestEffort)?; Self::deposit_event(Event::::StakeReleased { block, account, amount }); Ok(()) } @@ -461,7 +448,7 @@ impl Pallet { // FAIL-CI check these args T::Currency::burn_held( - &T::HoldReason::cause(block), + &T::HoldReason::get(), &account, amount, Precision::BestEffort, @@ -475,7 +462,7 @@ impl Pallet { fn hold(who: T::AccountId, amount: BalanceOf) -> DispatchResult { let block = >::block_number(); // Hold for the same reason will increase the amount. - T::Currency::hold(&T::HoldReason::cause(block), &who, amount)?; + T::Currency::hold(&T::HoldReason::get(), &who, amount)?; let current_stake = Stakes::::get(&who, block).unwrap_or_default(); Stakes::::insert(&who, block, current_stake.saturating_add(amount)); diff --git a/frame/safe-mode/src/mock.rs b/frame/safe-mode/src/mock.rs index 2d0d48566b59d..68bf57dfd1d84 100644 --- a/frame/safe-mode/src/mock.rs +++ b/frame/safe-mode/src/mock.rs @@ -67,6 +67,16 @@ parameter_types! { pub const ExistentialDeposit: u64 = 1; pub const MaxReserves: u32 = 10; } + +/// Identifies a hold on an account's balance. +#[derive( + Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, MaxEncodedLen, Debug, TypeInfo, +)] +pub enum HoldReason { + /// The safe-mode pallet holds funds since an account either entered or extended the safe-mode. + SafeMode, +} + impl pallet_balances::Config for Test { type Balance = u64; type DustRemoval = (); @@ -77,7 +87,7 @@ impl pallet_balances::Config for Test { type MaxLocks = (); type MaxReserves = MaxReserves; type ReserveIdentifier = Self::BlockNumber; - type HoldIdentifier = HoldIdentifier; + type HoldIdentifier = HoldReason; type FreezeIdentifier = (); type MaxHolds = ConstU32<10>; type MaxFreezes = ConstU32<0>; @@ -289,25 +299,14 @@ impl SortedMembers for StakeSlashOrigin { fn add(_m: &u64) {} } -#[derive( - Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, MaxEncodedLen, Debug, TypeInfo, -)] -pub enum HoldIdentifier { - SafeModeStake { block: u64 }, -} - -impl crate::CausalHoldReason for HoldIdentifier { - type Reason = HoldIdentifier; - - fn cause(block: u64) -> Self::Reason { - Self::SafeModeStake { block } - } +parameter_types! { + pub const SafeModeHoldReason: HoldReason = HoldReason::SafeMode; } impl Config for Test { type RuntimeEvent = RuntimeEvent; type Currency = Balances; - type HoldReason = HoldIdentifier; + type HoldReason = SafeModeHoldReason; type WhitelistedCalls = WhitelistedCalls; type EnterDuration = EnterDuration; type EnterStakeAmount = EnterStakeAmount; From 4aa8f43e47ff12139af2221516cf66f8be3bd7d0 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Wed, 29 Mar 2023 14:27:56 +0200 Subject: [PATCH 042/100] Refactor benchmarks and runtime configs Signed-off-by: Oliver Tale-Yazdi --- Cargo.lock | 1 + bin/node/runtime/src/lib.rs | 139 +++--------------- frame/safe-mode/Cargo.toml | 2 + frame/safe-mode/src/benchmarking.rs | 194 ++++++++++++-------------- frame/safe-mode/src/lib.rs | 80 +++++++---- frame/safe-mode/src/mock.rs | 176 ++++------------------- frame/safe-mode/src/tests.rs | 209 ++++++++++------------------ frame/tx-pause/src/mock.rs | 33 ++--- 8 files changed, 279 insertions(+), 555 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0016eceec81bc..6dbcd32abb502 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6639,6 +6639,7 @@ dependencies = [ "pallet-utility", "parity-scale-codec", "scale-info", + "sp-arithmetic", "sp-core", "sp-io", "sp-runtime", diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 36f912c8d940d..13bd58ba5124a 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -29,7 +29,6 @@ use frame_election_provider_support::{ use frame_support::{ construct_runtime, dispatch::DispatchClass, - pallet_prelude::EnsureOrigin, parameter_types, traits::{ fungible::ItemOf, @@ -49,7 +48,7 @@ use frame_support::{ }; use frame_system::{ limits::{BlockLength, BlockWeights}, - EnsureRoot, EnsureRootWithSuccess, EnsureSigned, EnsureWithSuccess, RawOrigin, + EnsureRoot, EnsureRootWithSuccess, EnsureSigned, EnsureWithSuccess, }; pub use node_primitives::{AccountId, Signature}; use node_primitives::{AccountIndex, Balance, BlockNumber, Hash, Index, Moment}; @@ -65,7 +64,7 @@ use scale_info::TypeInfo; use sp_api::impl_runtime_apis; use sp_authority_discovery::AuthorityId as AuthorityDiscoveryId; use sp_consensus_grandpa::AuthorityId as GrandpaId; -use sp_core::{crypto::KeyTypeId, ed25519::Public, OpaqueMetadata}; +use sp_core::{crypto::KeyTypeId, OpaqueMetadata}; use sp_inherents::{CheckInherentsResult, InherentData}; use sp_runtime::{ create_runtime_str, @@ -254,113 +253,6 @@ impl pallet_tx_pause::Config for Runtime { type WeightInfo = pallet_tx_pause::weights::SubstrateWeight; } -/// An origin that can enable the safe-mode by force. -pub enum ForceEnterOrigin { - Weak, - Medium, - Strong, -} - -/// An origin that can extend the safe-mode by force. -pub enum ForceExtendOrigin { - Weak, - Medium, - Strong, -} - -impl ForceEnterOrigin { - /// The duration of how long the safe-mode will be activated. - pub fn duration(&self) -> u32 { - match self { - Self::Weak => 5, - Self::Medium => 7, - Self::Strong => 11, - } - } - - /// Dummy account id of the origin. - pub fn acc(&self) -> AccountId { - match self { - Self::Weak => Public::from_raw([0; 32]).into(), - Self::Medium => Public::from_raw([1; 32]).into(), - Self::Strong => Public::from_raw([2; 32]).into(), - } - } - - /// Signed origin. - pub fn signed(&self) -> ::RuntimeOrigin { - RawOrigin::Signed(self.acc()).into() - } -} - -impl ForceExtendOrigin { - /// The duration of how long the safe-mode will be extended. - pub fn duration(&self) -> u32 { - match self { - Self::Weak => 13, - Self::Medium => 17, - Self::Strong => 19, - } - } - - /// A dummy id for this origin. - pub fn acc(&self) -> AccountId { - match self { - Self::Weak => Public::from_raw([0; 32]).into(), - Self::Medium => Public::from_raw([1; 32]).into(), - Self::Strong => Public::from_raw([2; 32]).into(), - } - } - - /// Signed origin. - pub fn signed(&self) -> ::RuntimeOrigin { - RawOrigin::Signed(self.acc()).into() - } -} - -// FAIL-CI can probably feature gate -impl, O>> + From>> EnsureOrigin - for ForceEnterOrigin -{ - type Success = u32; - - fn try_origin(o: O) -> Result { - use ForceEnterOrigin::*; - o.into().and_then(|o| match o { - RawOrigin::Signed(acc) if acc == Weak.acc() => Ok(Weak.duration()), - RawOrigin::Signed(acc) if acc == Medium.acc() => Ok(Medium.duration()), - RawOrigin::Signed(acc) if acc == Strong.acc() => Ok(Strong.duration()), - r => Err(O::from(r)), - }) - } - - #[cfg(feature = "runtime-benchmarks")] - fn try_successful_origin() -> Result { - Ok(O::from(RawOrigin::Signed(ForceEnterOrigin::Strong.acc()))) - } -} - -impl, O>> + From>> EnsureOrigin - for ForceExtendOrigin -{ - type Success = u32; - - fn try_origin(o: O) -> Result { - use ForceEnterOrigin::*; - o.into().and_then(|o| match o { - RawOrigin::Signed(acc) if acc == Weak.acc() => Ok(Weak.duration()), - RawOrigin::Signed(acc) if acc == Medium.acc() => Ok(Medium.duration()), - RawOrigin::Signed(acc) if acc == Strong.acc() => Ok(Strong.duration()), - r => Err(O::from(r)), - }) - } - - #[cfg(feature = "runtime-benchmarks")] - fn try_successful_origin() -> Result { - Ok(O::from(RawOrigin::Signed(ForceEnterOrigin::Strong.acc()))) - } -} - parameter_types! { pub const SignedEnterDuration: u32 = 10; pub const ExtendDuration: u32 = 20; @@ -368,6 +260,21 @@ parameter_types! { pub const ExtendStakeAmount: Balance = 15 * DOLLARS; pub const ReleaseDelay: u32 = 15; pub const SafeModeHoldReason: HoldReason = HoldReason::SafeMode; + + pub const ForceEnterWeak: u32 = 3; + pub const ForceEnterStrong: u32 = 5; + + pub const ForceExtendWeak: u32 = 11; + pub const ForceExtendStrong: u32 = 15; + + // NOTE: The account ID maps to the duration. Easy for testing. + pub ForceEnterOrigins: Vec = vec![ForceEnterWeak::get(), ForceEnterStrong::get()]; + pub ForceExtendOrigins: Vec = vec![ForceExtendWeak::get(), ForceExtendStrong::get()]; +} + +frame_support::ord_parameter_types! { + pub const ForceExitOrigin: u32 = 100; + pub const ForceStakeOrigin: u32 = 200; } impl pallet_safe_mode::Config for Runtime { @@ -379,10 +286,10 @@ impl pallet_safe_mode::Config for Runtime { type EnterStakeAmount = EnterStakeAmount; type ExtendDuration = ConstU32<{ 1 * DAYS }>; type ExtendStakeAmount = ExtendStakeAmount; - type ForceEnterOrigin = ForceEnterOrigin; - type ForceExtendOrigin = ForceExtendOrigin; - type ForceExitOrigin = EnsureRoot; - type StakeSlashOrigin = EnsureRoot; + type ForceEnterOrigin = EnsureRootWithSuccess>; + type ForceExtendOrigin = EnsureRootWithSuccess>; + type ForceExitOrigin = EnsureRoot; + type ForceStakeOrigin = EnsureRoot; type ReleaseDelay = ReleaseDelay; type WeightInfo = pallet_safe_mode::weights::SubstrateWeight; } @@ -1770,8 +1677,8 @@ impl pallet_core_fellowship::Config for Runtime { type Balance = Balance; type ParamsOrigin = frame_system::EnsureRoot; type InductOrigin = pallet_core_fellowship::EnsureInducted; - type ApproveOrigin = frame_system::EnsureRootWithSuccess>; - type PromoteOrigin = frame_system::EnsureRootWithSuccess>; + type ApproveOrigin = EnsureRootWithSuccess>; + type PromoteOrigin = EnsureRootWithSuccess>; type EvidenceSize = ConstU32<16_384>; } diff --git a/frame/safe-mode/Cargo.toml b/frame/safe-mode/Cargo.toml index 2c980c0674bc7..b13740162e929 100644 --- a/frame/safe-mode/Cargo.toml +++ b/frame/safe-mode/Cargo.toml @@ -18,6 +18,7 @@ frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } frame-system = { version = "4.0.0-dev", default-features = false, path = "../system" } scale-info = { version = "2.0.1", default-features = false, features = ["derive"] } +sp-arithmetic = { version = "6.0.0", default-features = false, path = "../../primitives/arithmetic" } sp-runtime = { version = "7.0.0", default-features = false, path = "../../primitives/runtime" } sp-std = { version = "5.0.0", default-features = false, path = "../../primitives/std" } pallet-balances = { version = "4.0.0-dev", path = "../balances", default-features = false, optional = true } @@ -42,6 +43,7 @@ std = [ "pallet-balances?/std", "pallet-utility?/std", "pallet-proxy?/std", + "sp-arithmetic/std", "sp-runtime/std", "sp-std/std", ] diff --git a/frame/safe-mode/src/benchmarking.rs b/frame/safe-mode/src/benchmarking.rs index 79f98fedcfc10..c901f18912bd0 100644 --- a/frame/safe-mode/src/benchmarking.rs +++ b/frame/safe-mode/src/benchmarking.rs @@ -30,32 +30,37 @@ use sp_runtime::traits::{Bounded, One, Zero}; mod benchmarks { use super::*; - /// `on_initialize` exiting since the until block is in the past. + /// `on_initialize` doing nothing. #[benchmark] - fn on_initialize_exit() { - EnteredUntil::::put(&T::BlockNumber::zero()); - assert!(SafeMode::::is_entered()); - + fn on_initialize_noop() { #[block] { SafeMode::::on_initialize(1u32.into()); } } - /// `on_initialize` doing nothing. + /// `on_initialize` exiting since the until block is in the past. #[benchmark] - fn on_initialize_noop() { + fn on_initialize_exit() { + EnteredUntil::::put(&T::BlockNumber::zero()); + assert!(SafeMode::::is_entered()); + #[block] { SafeMode::::on_initialize(1u32.into()); } + + assert!(!SafeMode::::is_entered()); } + /// Permissionless enter - if configured. #[benchmark] - fn enter() { + fn enter() -> Result<(), BenchmarkError> { + T::EnterStakeAmount::get().ok_or_else(|| BenchmarkError::Weightless)?; + let caller: T::AccountId = whitelisted_caller(); let origin = RawOrigin::Signed(caller.clone()); - T::Currency::set_balance(&caller, BalanceOf::::max_value() / 2u32.into()); + T::Currency::set_balance(&caller, init_bal::()); #[extrinsic_call] _(origin); @@ -64,12 +69,15 @@ mod benchmarks { SafeMode::::active_until().unwrap(), System::::block_number() + T::EnterDuration::get() ); + Ok(()) } + /// Forceful enter - if configured. #[benchmark] fn force_enter() -> Result<(), BenchmarkError> { let force_origin = T::ForceEnterOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; + let duration = T::ForceEnterOrigin::ensure_origin(force_origin.clone()).unwrap(); let call = Call::::force_enter {}; @@ -82,36 +90,37 @@ mod benchmarks { Ok(()) } + /// Permissionless extend - if configured. #[benchmark] - fn extend() { - let caller: T::AccountId = whitelisted_caller(); - let origin = RawOrigin::Signed(caller.clone()); - T::Currency::set_balance(&caller, BalanceOf::::max_value() / 2u32.into()); + fn extend() -> Result<(), BenchmarkError> { + T::ExtendStakeAmount::get().ok_or_else(|| BenchmarkError::Weightless)?; + + let alice: T::AccountId = whitelisted_caller(); + T::Currency::set_balance(&alice, init_bal::()); System::::set_block_number(1u32.into()); - assert!(SafeMode::::enter(origin.clone().into()).is_ok()); + assert!(SafeMode::::do_enter(None, 1u32.into()).is_ok()); #[extrinsic_call] - _(origin); + _(RawOrigin::Signed(alice)); assert_eq!( SafeMode::::active_until().unwrap(), - System::::block_number() + T::EnterDuration::get() + T::ExtendDuration::get() + System::::block_number() + 1u32.into() + T::ExtendDuration::get() ); + Ok(()) } + /// Forceful extend - if configured. #[benchmark] fn force_extend() -> Result<(), BenchmarkError> { - let caller: T::AccountId = whitelisted_caller(); - let origin = RawOrigin::Signed(caller.clone()); - T::Currency::set_balance(&caller, BalanceOf::::max_value() / 2u32.into()); + let force_origin = T::ForceExtendOrigin::try_successful_origin() + .map_err(|_| BenchmarkError::Weightless)?; System::::set_block_number(1u32.into()); - assert!(SafeMode::::enter(origin.clone().into()).is_ok()); + assert!(SafeMode::::do_enter(None, 1u32.into()).is_ok()); - let force_origin = T::ForceExtendOrigin::try_successful_origin() - .map_err(|_| BenchmarkError::Weightless)?; - let extension = T::ForceExtendOrigin::ensure_origin(force_origin.clone()).unwrap(); + let duration = T::ForceExtendOrigin::ensure_origin(force_origin.clone()).unwrap(); let call = Call::::force_extend {}; #[block] @@ -121,21 +130,18 @@ mod benchmarks { assert_eq!( SafeMode::::active_until().unwrap(), - System::::block_number() + T::EnterDuration::get() + extension + System::::block_number() + 1u32.into() + duration ); Ok(()) } + /// Forceful exit - if configured. #[benchmark] fn force_exit() -> Result<(), BenchmarkError> { - let caller: T::AccountId = whitelisted_caller(); - let origin = RawOrigin::Signed(caller.clone()); - T::Currency::set_balance(&caller, BalanceOf::::max_value() / 2u32.into()); - - assert!(SafeMode::::enter(origin.clone().into()).is_ok()); - let force_origin = T::ForceExitOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; + + assert!(SafeMode::::do_enter(None, 1u32.into()).is_ok()); let call = Call::::force_exit {}; #[block] @@ -147,125 +153,105 @@ mod benchmarks { Ok(()) } + /// Permissionless release of a stake - if configured. #[benchmark] fn release_stake() -> Result<(), BenchmarkError> { - let caller: T::AccountId = whitelisted_caller(); - let origin = RawOrigin::Signed(caller.clone()); - T::Currency::set_balance(&caller, BalanceOf::::max_value() / 2u32.into()); + let delay = T::ReleaseDelay::get().ok_or_else(|| BenchmarkError::Weightless)?; - let entered_at_block: T::BlockNumber = System::::block_number(); - assert!(SafeMode::::enter(origin.clone().into()).is_ok()); - let current_stake = Stakes::::get(&caller, entered_at_block).unwrap_or_default(); - assert_eq!(current_stake, T::EnterStakeAmount::get().unwrap()); - assert_eq!( - T::Currency::balance(&caller), - BalanceOf::::max_value() / 2u32.into() - T::EnterStakeAmount::get().unwrap() - ); + let alice: T::AccountId = whitelisted_caller(); + let origin = RawOrigin::Signed(alice.clone()); - let force_origin = - T::ForceExitOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; - assert!(SafeMode::::force_exit(force_origin.clone()).is_ok()); + T::Currency::set_balance(&alice, init_bal::()); + // Mock the storage. This is needed in case the `EnterStakeAmount` is zero. + let block: T::BlockNumber = 1u32.into(); + let bal: BalanceOf = 1u32.into(); + Stakes::::insert(&alice, &block, &bal); + T::Currency::hold(&T::HoldReason::get(), &alice, bal)?; + EnteredUntil::::put(&block); + assert!(SafeMode::::do_exit(ExitReason::Force).is_ok()); - System::::set_block_number( - System::::block_number() + T::ReleaseDelay::get().unwrap() + One::one(), - ); + System::::set_block_number(delay + One::one() + 2u32.into()); System::::on_initialize(System::::block_number()); SafeMode::::on_initialize(System::::block_number()); - let call = - Call::::release_stake { account: caller.clone(), block: entered_at_block.clone() }; + let call = Call::::release_stake { account: alice.clone(), block: 1u32.into() }; #[block] { - call.dispatch_bypass_filter(origin.into())?; + call.dispatch_bypass_filter(origin.into()).unwrap(); } - assert!(!Stakes::::contains_key(&caller, entered_at_block)); - assert_eq!(T::Currency::balance(&caller), BalanceOf::::max_value() / 2u32.into()); + assert!(!Stakes::::contains_key(&alice, &block)); + assert_eq!(T::Currency::balance(&alice), init_bal::()); Ok(()) } + /// Forceful release of a stake - if configured. #[benchmark] fn force_release_stake() -> Result<(), BenchmarkError> { - let caller: T::AccountId = whitelisted_caller(); - let origin = RawOrigin::Signed(caller.clone()); - T::Currency::set_balance(&caller, BalanceOf::::max_value() / 2u32.into()); + let force_origin = + T::ForceStakeOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; - let entered_at_block: T::BlockNumber = System::::block_number(); - assert!(SafeMode::::enter(origin.clone().into()).is_ok()); - let current_stake = Stakes::::get(&caller, entered_at_block).unwrap_or_default(); - assert_eq!(current_stake, T::EnterStakeAmount::get().unwrap()); - assert_eq!( - T::Currency::balance(&caller), - BalanceOf::::max_value() / 2u32.into() - T::EnterStakeAmount::get().unwrap() - ); + let alice: T::AccountId = whitelisted_caller(); + T::Currency::set_balance(&alice, init_bal::()); - // TODO - let force_origin = - T::ForceExitOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; - assert!(SafeMode::::force_exit(force_origin.clone()).is_ok()); + // Mock the storage. This is needed in case the `EnterStakeAmount` is zero. + let block: T::BlockNumber = 1u32.into(); + let bal: BalanceOf = 1u32.into(); + Stakes::::insert(&alice, &block, &bal); + T::Currency::hold(&T::HoldReason::get(), &alice, bal)?; + EnteredUntil::::put(&block); + + assert_eq!(T::Currency::balance(&alice), init_bal::() - 1u32.into()); + assert!(SafeMode::::do_exit(ExitReason::Force).is_ok()); System::::set_block_number(System::::block_number() + One::one()); System::::on_initialize(System::::block_number()); SafeMode::::on_initialize(System::::block_number()); - let release_origin = - T::StakeSlashOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; - let call = Call::::force_release_stake { - account: caller.clone(), - block: entered_at_block.clone(), - }; + let call = Call::::force_release_stake { account: alice.clone(), block }; #[block] { - call.dispatch_bypass_filter(release_origin)?; + call.dispatch_bypass_filter(force_origin)?; } - assert!(!Stakes::::contains_key(&caller, entered_at_block)); - assert_eq!(T::Currency::balance(&caller), BalanceOf::::max_value() / 2u32.into()); + assert!(!Stakes::::contains_key(&alice, block)); + assert_eq!(T::Currency::balance(&alice), init_bal::()); Ok(()) } #[benchmark] fn force_slash_stake() -> Result<(), BenchmarkError> { - // FAIL-CI disable if uncallable - let caller: T::AccountId = whitelisted_caller(); - let origin = RawOrigin::Signed(caller.clone()); - T::Currency::set_balance(&caller, BalanceOf::::max_value() / 2u32.into()); + let force_origin = + T::ForceStakeOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; - let entered_at_block: T::BlockNumber = System::::block_number(); - assert!(SafeMode::::enter(origin.clone().into()).is_ok()); - let current_stake = Stakes::::get(&caller, entered_at_block).unwrap_or_default(); - assert_eq!(current_stake, T::EnterStakeAmount::get().unwrap()); - assert_eq!( - T::Currency::balance(&caller), - BalanceOf::::max_value() / 2u32.into() - T::EnterStakeAmount::get().unwrap() - ); + let alice: T::AccountId = whitelisted_caller(); + T::Currency::set_balance(&alice, init_bal::()); - // TODO - let force_origin = - T::ForceExitOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; - assert!(SafeMode::::force_exit(force_origin.clone()).is_ok()); + // Mock the storage. This is needed in case the `EnterStakeAmount` is zero. + let block: T::BlockNumber = 1u32.into(); + let bal: BalanceOf = 1u32.into(); + Stakes::::insert(&alice, &block, &bal); + T::Currency::hold(&T::HoldReason::get(), &alice, bal)?; + EnteredUntil::::put(&block); + assert!(SafeMode::::do_exit(ExitReason::Force).is_ok()); - let release_origin = - T::StakeSlashOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; - let call = Call::::force_slash_stake { - account: caller.clone(), - block: entered_at_block.clone(), - }; + let call = Call::::force_slash_stake { account: alice.clone(), block }; #[block] { - call.dispatch_bypass_filter(release_origin)?; + call.dispatch_bypass_filter(force_origin)?; } - assert!(!Stakes::::contains_key(&caller, entered_at_block)); - assert_eq!( - T::Currency::balance(&caller), - BalanceOf::::max_value() / 2u32.into() - T::EnterStakeAmount::get().unwrap() - ); + assert!(!Stakes::::contains_key(&alice, block)); + assert_eq!(T::Currency::balance(&alice), init_bal::() - 1u32.into()); Ok(()) } + fn init_bal() -> BalanceOf { + BalanceOf::::max_value() / 10u32.into() + } + impl_benchmark_test_suite!(SafeMode, crate::mock::new_test_ext(), crate::mock::Test); } diff --git a/frame/safe-mode/src/lib.rs b/frame/safe-mode/src/lib.rs index a6d5272f0a8bc..09c0b900c6108 100644 --- a/frame/safe-mode/src/lib.rs +++ b/frame/safe-mode/src/lib.rs @@ -23,6 +23,7 @@ mod tests; pub mod weights; use frame_support::{ + defensive_assert, pallet_prelude::*, traits::{ fungible::{ @@ -35,6 +36,7 @@ use frame_support::{ weights::Weight, }; use frame_system::pallet_prelude::*; +use sp_arithmetic::traits::Zero; use sp_runtime::traits::Saturating; use sp_std::{convert::TryInto, prelude::*}; @@ -82,13 +84,13 @@ pub mod pallet { /// The amount that will be reserved upon calling [`Pallet::enter`]. /// - /// `None` disallows permissionlessly enabling the safe-mode. + /// `None` disallows permissionlessly enabling the safe-mode and is a sane default. #[pallet::constant] type EnterStakeAmount: Get>>; /// The amount that will be reserved upon calling [`Pallet::extend`]. /// - /// `None` disallows permissionlessly extending the safe-mode. + /// `None` disallows permissionlessly extending the safe-mode and is a sane default. #[pallet::constant] type ExtendStakeAmount: Get>>; @@ -105,17 +107,16 @@ pub mod pallet { /// The origin that may call [`Pallet::force_enter`]. type ForceExitOrigin: EnsureOrigin; - /// The origin that may call [`Pallet::force_release_stake`] and - /// [`Pallet::slash_stake`]. - type StakeSlashOrigin: EnsureOrigin; + /// The only origin that can force to release or slash a stake. + type ForceStakeOrigin: EnsureOrigin; /// The minimal duration a deposit will remain reserved after safe-mode is entered or /// extended, unless [`Pallet::force_release_stake`] is successfully called sooner. /// - /// Every stake is tied to a specific activation or extension, thus each stake - /// can be released independently after the delay for it has passed. + /// Every stake is tied to a specific activation or extension, thus each stake can be + /// released independently after the delay for it has passed. /// - /// `None` disallows permissionlessly releasing the safe-mode Stakes. + /// `None` disallows permissionlessly releasing the safe-mode stakes and is a sane default. #[pallet::constant] type ReleaseDelay: Get>; @@ -137,6 +138,9 @@ pub mod pallet { /// There is no balance reserved. NoStake, + /// The account already has a stake reserved and can therefore not enter or extend again. + AlreadyStaked, + /// This stake cannot be released yet. CannotReleaseYet, } @@ -153,11 +157,14 @@ pub mod pallet { /// Exited the safe-mode for a specific reason. Exited { reason: ExitReason }, - /// An account had a reserve released that was reserved at a specific block. - StakeReleased { account: T::AccountId, block: T::BlockNumber, amount: BalanceOf }, + /// An account reserved funds for either entering or extending the safe-mode. + StakePlaced { account: T::AccountId, amount: BalanceOf }, + + /// An account had a reserve released that was reserved. + StakeReleased { account: T::AccountId, amount: BalanceOf }, - /// An account had reserve slashed that was reserved at a specific block. - StakeSlashed { account: T::AccountId, block: T::BlockNumber, amount: BalanceOf }, + /// An account had reserve slashed that was reserved. + StakeSlashed { account: T::AccountId, amount: BalanceOf }, } /// The reason why the safe-mode was deactivated. @@ -180,6 +187,9 @@ pub mod pallet { pub type EnteredUntil = StorageValue<_, T::BlockNumber, OptionQuery>; /// Holds the reserve that was taken from an account at a specific block number. + /// + /// This helps governance to have an overview of outstanding stakes that should be returned or + /// slashed. #[pallet::storage] #[pallet::getter(fn reserves)] pub type Stakes = StorageDoubleMap< @@ -293,7 +303,7 @@ pub mod pallet { pub fn force_exit(origin: OriginFor) -> DispatchResult { T::ForceExitOrigin::ensure_origin(origin)?; - Self::do_deactivate(ExitReason::Force) + Self::do_exit(ExitReason::Force) } /// Slash a stake for an account that entered or extended safe-mode at a specific @@ -304,7 +314,7 @@ pub mod pallet { /// Emits a [`Event::StakeSlashed`] event on success. /// Errors with [`Error::Entered`] if the safe-mode is entered. /// - /// Can only be called by the [`Config::StakeSlashOrigin`] origin. + /// Can only be called by the [`Config::ForceStakeOrigin`] origin. #[pallet::call_index(5)] #[pallet::weight(T::WeightInfo::force_slash_stake())] pub fn force_slash_stake( @@ -312,7 +322,7 @@ pub mod pallet { account: T::AccountId, block: T::BlockNumber, ) -> DispatchResult { - T::StakeSlashOrigin::ensure_origin(origin)?; + T::ForceStakeOrigin::ensure_origin(origin)?; Self::do_force_slash(account, block) } @@ -351,7 +361,7 @@ pub mod pallet { /// Errors with [`Error::NoStake`] if the payee has no reserved currency at the /// block specified. /// - /// Can only be called by the [`Config::StakeSlashOrigin`] origin. + /// Can only be called by the [`Config::ForceStakeOrigin`] origin. #[pallet::call_index(7)] #[pallet::weight(T::WeightInfo::force_release_stake())] pub fn force_release_stake( @@ -359,7 +369,7 @@ pub mod pallet { account: T::AccountId, block: T::BlockNumber, ) -> DispatchResult { - T::StakeSlashOrigin::ensure_origin(origin)?; + T::ForceStakeOrigin::ensure_origin(origin)?; Self::do_release(true, account, block) } @@ -374,7 +384,7 @@ pub mod pallet { }; if current > limit { - let _ = Self::do_deactivate(ExitReason::Timeout).defensive_proof("Must exit; qed"); + let _ = Self::do_exit(ExitReason::Timeout).defensive_proof("Must exit; qed"); T::WeightInfo::on_initialize_exit() } else { T::WeightInfo::on_initialize_noop() @@ -385,7 +395,7 @@ pub mod pallet { impl Pallet { /// Logic for the [`crate::Pallet::enter`] and [`crate::Pallet::force_enter`] calls. - fn do_enter(who: Option, duration: T::BlockNumber) -> DispatchResult { + pub(crate) fn do_enter(who: Option, duration: T::BlockNumber) -> DispatchResult { ensure!(!Self::is_entered(), Error::::Entered); if let Some(who) = who { @@ -400,7 +410,7 @@ impl Pallet { } /// Logic for the [`crate::Pallet::extend`] and [`crate::Pallet::force_extend`] calls. - fn do_extend(who: Option, duration: T::BlockNumber) -> DispatchResult { + pub(crate) fn do_extend(who: Option, duration: T::BlockNumber) -> DispatchResult { let mut until = EnteredUntil::::get().ok_or(Error::::Exited)?; if let Some(who) = who { @@ -417,7 +427,7 @@ impl Pallet { /// Logic for the [`crate::Pallet::force_exit`] call. /// /// Errors if the safe-mode is already exited. - fn do_deactivate(reason: ExitReason) -> DispatchResult { + pub(crate) fn do_exit(reason: ExitReason) -> DispatchResult { let _until = EnteredUntil::::take().ok_or(Error::::Exited)?; Self::deposit_event(Event::Exited { reason }); Ok(()) @@ -425,7 +435,11 @@ impl Pallet { /// Logic for the [`crate::Pallet::release_stake`] and /// [`crate::Pallet::force_release_stake`] calls. - fn do_release(force: bool, account: T::AccountId, block: T::BlockNumber) -> DispatchResult { + pub(crate) fn do_release( + force: bool, + account: T::AccountId, + block: T::BlockNumber, + ) -> DispatchResult { let amount = Stakes::::take(&account, &block).ok_or(Error::::NoStake)?; if !force { @@ -438,34 +452,40 @@ impl Pallet { let amount = T::Currency::release(&T::HoldReason::get(), &account, amount, Precision::BestEffort)?; - Self::deposit_event(Event::::StakeReleased { block, account, amount }); + Self::deposit_event(Event::::StakeReleased { account, amount }); Ok(()) } /// Logic for the [`crate::Pallet::slash_stake`] call. - fn do_force_slash(account: T::AccountId, block: T::BlockNumber) -> DispatchResult { + pub(crate) fn do_force_slash(account: T::AccountId, block: T::BlockNumber) -> DispatchResult { let amount = Stakes::::take(&account, block).ok_or(Error::::NoStake)?; // FAIL-CI check these args - T::Currency::burn_held( + let burned = T::Currency::burn_held( &T::HoldReason::get(), &account, amount, Precision::BestEffort, Fortitude::Force, )?; - Self::deposit_event(Event::::StakeSlashed { block, account, amount }); + defensive_assert!(burned == amount, "Could not burn the full held amount"); + Self::deposit_event(Event::::StakeSlashed { account, amount }); Ok(()) } - /// Hold `amount` from `who` and store it in `Stakes`. + /// Place a hold for exactly `amount` and store it in `Stakes`. + /// + /// This errors if the account already has a hold for the same reason. fn hold(who: T::AccountId, amount: BalanceOf) -> DispatchResult { let block = >::block_number(); - // Hold for the same reason will increase the amount. + if !T::Currency::balance_on_hold(&T::HoldReason::get(), &who).is_zero() { + return Err(Error::::AlreadyStaked.into()) + } + T::Currency::hold(&T::HoldReason::get(), &who, amount)?; + Stakes::::insert(&who, block, amount); + Self::deposit_event(Event::::StakePlaced { account: who, amount }); - let current_stake = Stakes::::get(&who, block).unwrap_or_default(); - Stakes::::insert(&who, block, current_stake.saturating_add(amount)); Ok(()) } diff --git a/frame/safe-mode/src/mock.rs b/frame/safe-mode/src/mock.rs index 68bf57dfd1d84..2f3596d35445a 100644 --- a/frame/safe-mode/src/mock.rs +++ b/frame/safe-mode/src/mock.rs @@ -24,18 +24,15 @@ use crate as pallet_safe_mode; use frame_support::{ parameter_types, - traits::{ConstU64, Everything, InsideBoth, InstanceFilter, SortedMembers}, + traits::{ConstU64, Everything, InsideBoth, InstanceFilter, IsInVec}, }; -use frame_system::{EnsureSignedBy, RawOrigin}; +use frame_system::EnsureSignedBy; use sp_core::H256; use sp_runtime::{ testing::Header, traits::{BlakeTwo256, IdentityLookup}, }; -parameter_types! { - pub const BlockHashCount: u64 = 250; -} impl frame_system::Config for Test { type BaseCallFilter = InsideBoth; type BlockWeights = (); @@ -50,7 +47,7 @@ impl frame_system::Config for Test { type Lookup = IdentityLookup; type Header = Header; type RuntimeEvent = RuntimeEvent; - type BlockHashCount = BlockHashCount; + type BlockHashCount = ConstU64<250>; type DbWeight = (); type Version = (); type PalletInfo = PalletInfo; @@ -63,11 +60,6 @@ impl frame_system::Config for Test { type MaxConsumers = frame_support::traits::ConstU32<16>; } -parameter_types! { - pub const ExistentialDeposit: u64 = 1; - pub const MaxReserves: u32 = 10; -} - /// Identifies a hold on an account's balance. #[derive( Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, MaxEncodedLen, Debug, TypeInfo, @@ -81,11 +73,11 @@ impl pallet_balances::Config for Test { type Balance = u64; type DustRemoval = (); type RuntimeEvent = RuntimeEvent; - type ExistentialDeposit = ExistentialDeposit; + type ExistentialDeposit = ConstU64<2>; type AccountStore = System; type WeightInfo = (); type MaxLocks = (); - type MaxReserves = MaxReserves; + type MaxReserves = ConstU32<10>; type ReserveIdentifier = Self::BlockNumber; type HoldIdentifier = HoldReason; type FreezeIdentifier = (); @@ -100,6 +92,7 @@ impl pallet_utility::Config for Test { type WeightInfo = (); } +/// Mocked proxies to check that the safe-mode also works with the proxy pallet. #[derive( Copy, Clone, @@ -118,11 +111,13 @@ pub enum ProxyType { JustTransfer, JustUtility, } + impl Default for ProxyType { fn default() -> Self { Self::Any } } + impl InstanceFilter for ProxyType { fn filter(&self, c: &RuntimeCall) -> bool { match self { @@ -137,6 +132,7 @@ impl InstanceFilter for ProxyType { self == &ProxyType::Any || self == o } } + impl pallet_proxy::Config for Test { type RuntimeEvent = RuntimeEvent; type RuntimeCall = RuntimeCall; @@ -152,7 +148,7 @@ impl pallet_proxy::Config for Test { type AnnouncementDepositFactor = ConstU64<1>; } -/// Filter to allow all everything except balance calls +/// The calls that can always bypass the safe-mode. pub struct WhitelistedCalls; impl Contains for WhitelistedCalls { fn contains(call: &RuntimeCall) -> bool { @@ -163,144 +159,28 @@ impl Contains for WhitelistedCalls { } } -/// An origin that can enable the safe-mode by force. -pub enum ForceEnterOrigin { - Weak, - Medium, - Strong, -} - -/// An origin that can extend the safe-mode by force. -pub enum ForceExtendOrigin { - Weak, - Medium, - Strong, -} - -impl ForceEnterOrigin { - /// The duration of how long the safe-mode will be activated. - pub fn duration(&self) -> u64 { - match self { - Self::Weak => 5, - Self::Medium => 7, - Self::Strong => 11, - } - } - - /// Account id of the origin. - pub const fn acc(&self) -> u64 { - match self { - Self::Weak => 100, - Self::Medium => 101, - Self::Strong => 102, - } - } - - /// Signed origin. - pub fn signed(&self) -> ::RuntimeOrigin { - RawOrigin::Signed(self.acc()).into() - } -} - -impl ForceExtendOrigin { - /// The duration of how long the safe-mode will be extended. - pub fn duration(&self) -> u64 { - match self { - Self::Weak => 13, - Self::Medium => 17, - Self::Strong => 19, - } - } - - /// Account id of the origin. - pub const fn acc(&self) -> u64 { - match self { - Self::Weak => 200, - Self::Medium => 201, - Self::Strong => 202, - } - } - - /// Signed origin. - pub fn signed(&self) -> ::RuntimeOrigin { - RawOrigin::Signed(self.acc()).into() - } -} - -impl, O>> + From>> EnsureOrigin - for ForceEnterOrigin -{ - type Success = u64; - - fn try_origin(o: O) -> Result { - o.into().and_then(|o| match o { - RawOrigin::Signed(acc) if acc == ForceEnterOrigin::Weak.acc() => - Ok(ForceEnterOrigin::Weak.duration()), - RawOrigin::Signed(acc) if acc == ForceEnterOrigin::Medium.acc() => - Ok(ForceEnterOrigin::Medium.duration()), - RawOrigin::Signed(acc) if acc == ForceEnterOrigin::Strong.acc() => - Ok(ForceEnterOrigin::Strong.duration()), - r => Err(O::from(r)), - }) - } - - #[cfg(feature = "runtime-benchmarks")] - fn try_successful_origin() -> Result { - Ok(O::from(RawOrigin::Signed(ForceEnterOrigin::Strong.acc()))) - } -} - -impl, O>> + From>> EnsureOrigin - for ForceExtendOrigin -{ - type Success = u64; - - fn try_origin(o: O) -> Result { - o.into().and_then(|o| match o { - RawOrigin::Signed(acc) if acc == ForceExtendOrigin::Weak.acc() => - Ok(ForceExtendOrigin::Weak.duration()), - RawOrigin::Signed(acc) if acc == ForceExtendOrigin::Medium.acc() => - Ok(ForceExtendOrigin::Medium.duration()), - RawOrigin::Signed(acc) if acc == ForceExtendOrigin::Strong.acc() => - Ok(ForceExtendOrigin::Strong.duration()), - r => Err(O::from(r)), - }) - } - - #[cfg(feature = "runtime-benchmarks")] - fn try_successful_origin() -> Result { - Ok(O::from(RawOrigin::Signed(ForceExtendOrigin::Strong.acc()))) - } -} - parameter_types! { - pub const EnterDuration: u64 = 3; + pub const EnterDuration: u64 = 7; pub const ExtendDuration: u64 = 30; pub const EnterStakeAmount: u64 = 100; pub const ExtendStakeAmount: u64 = 100; - pub const ForceExitOrigin: u64 = 3; - pub const StakeSlashOrigin: u64 = 4; - pub const ReleaseDelay: u64 = 2; -} + pub const ReleaseDelay: u64 = 20; + pub const SafeModeHoldReason: HoldReason = HoldReason::SafeMode; -// Required impl to use some ::get() in tests -impl SortedMembers for ForceExitOrigin { - fn sorted_members() -> Vec { - vec![Self::get()] - } - #[cfg(feature = "runtime-benchmarks")] - fn add(_m: &u64) {} -} -impl SortedMembers for StakeSlashOrigin { - fn sorted_members() -> Vec { - vec![Self::get()] - } - #[cfg(feature = "runtime-benchmarks")] - fn add(_m: &u64) {} + pub const ForceEnterWeak: u64 = 3; + pub const ForceEnterStrong: u64 = 5; + + pub const ForceExtendWeak: u64 = 11; + pub const ForceExtendStrong: u64 = 15; + + // NOTE: The account ID maps to the duration. Easy for testing. + pub ForceEnterOrigins: Vec = vec![ForceEnterWeak::get(), ForceEnterStrong::get()]; + pub ForceExtendOrigins: Vec = vec![ForceExtendWeak::get(), ForceExtendStrong::get()]; } -parameter_types! { - pub const SafeModeHoldReason: HoldReason = HoldReason::SafeMode; +frame_support::ord_parameter_types! { + pub const ForceExitOrigin: u64 = 100; + pub const ForceStakeOrigin: u64 = 200; } impl Config for Test { @@ -312,10 +192,10 @@ impl Config for Test { type EnterStakeAmount = EnterStakeAmount; type ExtendDuration = ExtendDuration; type ExtendStakeAmount = ExtendStakeAmount; - type ForceEnterOrigin = ForceEnterOrigin; - type ForceExtendOrigin = ForceExtendOrigin; + type ForceEnterOrigin = EnsureSignedBy, u64>; + type ForceExtendOrigin = EnsureSignedBy, u64>; type ForceExitOrigin = EnsureSignedBy; - type StakeSlashOrigin = EnsureSignedBy; + type ForceStakeOrigin = EnsureSignedBy; type ReleaseDelay = ReleaseDelay; type WeightInfo = (); } diff --git a/frame/safe-mode/src/tests.rs b/frame/safe-mode/src/tests.rs index 7329e109b9ac2..ab3b2f4d6756e 100644 --- a/frame/safe-mode/src/tests.rs +++ b/frame/safe-mode/src/tests.rs @@ -39,7 +39,9 @@ macro_rules! hypothetically { }; } -/// Assert something to be [*hypothetically*] `Ok`. +/// Assert something to be would be [*hypothetically*] `Ok` without actually doing it. +/// +/// Reverts any storage changes made by the closure. macro_rules! hypothetically_ok { ($e:expr $(, $args:expr)* $(,)?) => { let result = hypothetically!($e); @@ -47,8 +49,6 @@ macro_rules! hypothetically_ok { }; } -// GENERAL FAIL/NEGATIVE TESTS --------------------- - #[test] fn fails_to_filter_calls_to_safe_mode_pallet() { new_test_ext().execute_with(|| { @@ -59,18 +59,17 @@ fn fails_to_filter_calls_to_safe_mode_pallet() { call_transfer().dispatch(RuntimeOrigin::signed(0)), frame_system::Error::::CallFiltered ); - // TODO ^^^ consider refactor to throw a safe mode error, not generic `CallFiltered` next_block(); - assert_ok!(SafeMode::extend(RuntimeOrigin::signed(0))); - assert_ok!(SafeMode::force_extend(ForceExtendOrigin::Weak.signed())); + assert_ok!(SafeMode::extend(RuntimeOrigin::signed(1))); + assert_ok!(SafeMode::force_extend(signed(ForceExtendStrong::get()))); assert_err!( call_transfer().dispatch(RuntimeOrigin::signed(0)), frame_system::Error::::CallFiltered ); assert_ok!(SafeMode::force_exit(RuntimeOrigin::signed(mock::ForceExitOrigin::get()))); assert_ok!(SafeMode::force_release_stake( - RuntimeOrigin::signed(mock::StakeSlashOrigin::get()), + RuntimeOrigin::signed(mock::ForceStakeOrigin::get()), 0, activated_at_block )); @@ -83,7 +82,7 @@ fn fails_to_filter_calls_to_safe_mode_pallet() { ); assert_ok!(SafeMode::force_exit(RuntimeOrigin::signed(mock::ForceExitOrigin::get()))); assert_ok!(SafeMode::force_slash_stake( - RuntimeOrigin::signed(mock::StakeSlashOrigin::get()), + RuntimeOrigin::signed(mock::ForceStakeOrigin::get()), 0, activated_at_block + 2 )); @@ -115,7 +114,7 @@ fn fails_to_force_release_stakes_with_wrong_block() { assert_err!( SafeMode::force_release_stake( - RuntimeOrigin::signed(mock::StakeSlashOrigin::get()), + RuntimeOrigin::signed(mock::ForceStakeOrigin::get()), 0, activated_at_block + 1 ), @@ -124,7 +123,7 @@ fn fails_to_force_release_stakes_with_wrong_block() { assert_err!( SafeMode::force_slash_stake( - RuntimeOrigin::signed(mock::StakeSlashOrigin::get()), + RuntimeOrigin::signed(mock::ForceStakeOrigin::get()), 0, activated_at_block + 1 ), @@ -154,8 +153,8 @@ fn fails_to_release_stakes_too_early() { fn can_automatically_deactivate_after_timeout() { new_test_ext().execute_with(|| { let activated_at_block = System::block_number(); - assert_ok!(SafeMode::force_enter(ForceEnterOrigin::Weak.signed())); - run_to(ForceEnterOrigin::Weak.duration() + activated_at_block + 1); + assert_ok!(SafeMode::force_enter(signed(ForceEnterWeak::get()))); + run_to(1 + activated_at_block + ForceEnterWeak::get()); assert_eq!(SafeMode::active_until(), None); }); @@ -203,7 +202,7 @@ fn can_filter_balance_in_proxy_when_activated() { assert_ok!(Proxy::proxy(RuntimeOrigin::signed(2), 1, None, Box::new(call_transfer()))); System::assert_last_event(pallet_proxy::Event::ProxyExecuted { result: Ok(()) }.into()); - assert_ok!(SafeMode::force_enter(ForceEnterOrigin::Weak.signed())); + assert_ok!(SafeMode::force_enter(signed(ForceEnterWeak::get()))); assert_ok!(Proxy::proxy(RuntimeOrigin::signed(2), 1, None, Box::new(call_transfer()))); System::assert_last_event( @@ -231,64 +230,15 @@ fn can_activate() { } #[test] -fn can_extend() { +fn cannot_extend() { new_test_ext().execute_with(|| { assert_ok!(SafeMode::enter(RuntimeOrigin::signed(0))); - assert_ok!(SafeMode::extend(RuntimeOrigin::signed(0))); + assert_err!(SafeMode::extend(RuntimeOrigin::signed(0)), Error::::AlreadyStaked); assert_eq!( SafeMode::active_until().unwrap(), - System::block_number() + mock::EnterDuration::get() + mock::ExtendDuration::get() - ); - assert_eq!( - Balances::reserved_balance(0), - mock::EnterStakeAmount::get() + mock::ExtendStakeAmount::get() - ); - }); -} - -#[test] -fn can_extend_twice_in_same_block() { - new_test_ext().execute_with(|| { - assert_ok!(SafeMode::enter(RuntimeOrigin::signed(0))); - assert_ok!(SafeMode::extend(RuntimeOrigin::signed(0))); - assert_ok!(SafeMode::extend(RuntimeOrigin::signed(0))); - assert_eq!( - SafeMode::active_until().unwrap(), - System::block_number() + mock::EnterDuration::get() + mock::ExtendDuration::get() * 2 - ); - assert_eq!( - Balances::reserved_balance(0), - mock::EnterStakeAmount::get() + mock::ExtendStakeAmount::get() * 2 - ); - }); -} - -#[test] -fn can_release_independent_stakes_by_block() { - new_test_ext().execute_with(|| { - let activated_at_block_0 = System::block_number(); - assert_ok!(SafeMode::enter(RuntimeOrigin::signed(0))); - - run_to(mock::EnterDuration::get() + mock::ReleaseDelay::get() + activated_at_block_0 + 1); - - let activated_at_block_1 = System::block_number(); - assert_ok!(SafeMode::enter(RuntimeOrigin::signed(0))); - - assert_eq!(Balances::free_balance(&0), 1234 - (2 * mock::EnterStakeAmount::get())); // accounts set in mock genesis - - assert_ok!(SafeMode::force_exit(RuntimeOrigin::signed(mock::ForceExitOrigin::get()))); - - assert_ok!(SafeMode::release_stake(RuntimeOrigin::signed(2), 0, activated_at_block_0)); - assert_err!( - SafeMode::release_stake(RuntimeOrigin::signed(2), 0, activated_at_block_1), - Error::::CannotReleaseYet + System::block_number() + mock::EnterDuration::get() ); - assert_eq!(Balances::free_balance(&0), 1234 - mock::EnterStakeAmount::get()); // accounts set in mock genesis - - run_to(mock::EnterDuration::get() + mock::ReleaseDelay::get() + activated_at_block_1 + 1); - - assert_ok!(SafeMode::release_stake(RuntimeOrigin::signed(2), 0, activated_at_block_1)); - assert_eq!(>::total_balance(&0), 1234); // accounts set in mock genesis + assert_eq!(Balances::reserved_balance(0), mock::EnterStakeAmount::get()); }); } @@ -331,16 +281,15 @@ fn fails_force_deactivate_if_not_activated() { #[test] fn can_force_activate_with_config_origin() { new_test_ext().execute_with(|| { - assert_ok!(SafeMode::force_enter(ForceEnterOrigin::Weak.signed())); + assert_ok!(SafeMode::force_enter(signed(ForceEnterStrong::get()))); assert_eq!( SafeMode::active_until().unwrap(), - System::block_number() + ForceEnterOrigin::Weak.duration() + System::block_number() + ForceEnterStrong::get() ); assert_noop!( - SafeMode::force_enter(ForceEnterOrigin::Weak.signed()), + SafeMode::force_enter(signed(ForceEnterStrong::get())), Error::::Entered ); - assert_eq!(Balances::reserved_balance(ForceEnterOrigin::Weak.acc()), 0); }); } @@ -349,12 +298,11 @@ fn can_force_deactivate_with_config_origin() { new_test_ext().execute_with(|| { assert_eq!(SafeMode::active_until(), None); assert_err!( - SafeMode::force_exit(RuntimeOrigin::signed(mock::ForceExitOrigin::get())), + SafeMode::force_exit(RuntimeOrigin::signed(ForceExitOrigin::get())), Error::::Exited ); - assert_ok!(SafeMode::force_enter(ForceEnterOrigin::Weak.signed())); - assert_eq!(Balances::reserved_balance(ForceEnterOrigin::Weak.acc()), 0); - assert_ok!(SafeMode::force_exit(RuntimeOrigin::signed(mock::ForceExitOrigin::get()))); + assert_ok!(SafeMode::force_enter(signed(ForceEnterWeak::get()))); + assert_ok!(SafeMode::force_exit(RuntimeOrigin::signed(ForceExitOrigin::get()))); }); } @@ -362,20 +310,16 @@ fn can_force_deactivate_with_config_origin() { fn can_force_extend_with_config_origin() { new_test_ext().execute_with(|| { // Activated by `Weak` and extended by `Medium`. - assert_ok!(SafeMode::force_enter(ForceEnterOrigin::Weak.signed())); + assert_ok!(SafeMode::force_enter(signed(ForceEnterWeak::get()))); assert_eq!( SafeMode::active_until().unwrap(), - System::block_number() + ForceEnterOrigin::Weak.duration() + System::block_number() + ForceEnterWeak::get() ); - assert_ok!(SafeMode::force_extend(ForceExtendOrigin::Medium.signed())); + assert_ok!(SafeMode::force_extend(signed(ForceExtendWeak::get()))); assert_eq!( SafeMode::active_until().unwrap(), - System::block_number() + - ForceEnterOrigin::Weak.duration() + - ForceExtendOrigin::Medium.duration() + System::block_number() + ForceEnterWeak::get() + ForceExtendWeak::get() ); - assert_eq!(Balances::reserved_balance(ForceEnterOrigin::Weak.acc()), 0); - assert_eq!(Balances::reserved_balance(mock::ExtendDuration::get()), 0); }); } @@ -385,14 +329,14 @@ fn can_force_release_stake_with_config_origin() { let activated_at_block = System::block_number(); assert_ok!(SafeMode::enter(RuntimeOrigin::signed(0))); hypothetically_ok!(SafeMode::force_release_stake( - RuntimeOrigin::signed(mock::StakeSlashOrigin::get()), + RuntimeOrigin::signed(mock::ForceStakeOrigin::get()), 0, activated_at_block ),); run_to(mock::EnterDuration::get() + activated_at_block + 1); assert_ok!(SafeMode::force_release_stake( - RuntimeOrigin::signed(mock::StakeSlashOrigin::get()), + RuntimeOrigin::signed(mock::ForceStakeOrigin::get()), 0, activated_at_block )); @@ -401,7 +345,7 @@ fn can_force_release_stake_with_config_origin() { Balances::make_free_balance_be(&0, 1234); let activated_and_extended_at_block = System::block_number(); assert_ok!(SafeMode::enter(RuntimeOrigin::signed(0))); - assert_ok!(SafeMode::extend(RuntimeOrigin::signed(0))); + assert_ok!(SafeMode::extend(RuntimeOrigin::signed(1))); run_to( mock::EnterDuration::get() + mock::ExtendDuration::get() + @@ -410,7 +354,7 @@ fn can_force_release_stake_with_config_origin() { ); assert_ok!(SafeMode::force_release_stake( - RuntimeOrigin::signed(mock::StakeSlashOrigin::get()), + RuntimeOrigin::signed(mock::ForceStakeOrigin::get()), 0, activated_and_extended_at_block )); @@ -431,14 +375,14 @@ fn can_release_stake_while_entered() { for i in 0..mock::EnterDuration::get() + 10 { run_to(i); hypothetically_ok!(SafeMode::force_release_stake( - RuntimeOrigin::signed(mock::StakeSlashOrigin::get()), + RuntimeOrigin::signed(mock::ForceStakeOrigin::get()), 0, 1 )); } // Now once we slash once assert_ok!(SafeMode::force_release_stake( - RuntimeOrigin::signed(mock::StakeSlashOrigin::get()), + RuntimeOrigin::signed(mock::ForceStakeOrigin::get()), 0, 1 ),); @@ -446,7 +390,7 @@ fn can_release_stake_while_entered() { // ... it wont work ever again. assert_err!( SafeMode::force_release_stake( - RuntimeOrigin::signed(mock::StakeSlashOrigin::get()), + RuntimeOrigin::signed(mock::ForceStakeOrigin::get()), 0, 1 ), @@ -466,20 +410,20 @@ fn can_slash_stake_while_entered() { for i in 0..mock::EnterDuration::get() + 10 { run_to(i); hypothetically_ok!(SafeMode::force_slash_stake( - RuntimeOrigin::signed(mock::StakeSlashOrigin::get()), + RuntimeOrigin::signed(mock::ForceStakeOrigin::get()), 0, 1 )); } // Now once we slash once assert_ok!(SafeMode::force_slash_stake( - RuntimeOrigin::signed(mock::StakeSlashOrigin::get()), + RuntimeOrigin::signed(mock::ForceStakeOrigin::get()), 0, 1 ),); // ... it wont work ever again. assert_err!( - SafeMode::force_slash_stake(RuntimeOrigin::signed(mock::StakeSlashOrigin::get()), 0, 1), + SafeMode::force_slash_stake(RuntimeOrigin::signed(mock::ForceStakeOrigin::get()), 0, 1), Error::::NoStake ); }); @@ -489,26 +433,31 @@ fn can_slash_stake_while_entered() { fn can_slash_stake_from_extend_block() { new_test_ext().execute_with(|| { assert_ok!(SafeMode::enter(RuntimeOrigin::signed(0))); - assert_ok!(SafeMode::extend(RuntimeOrigin::signed(0))); - assert_eq!( - Balances::free_balance(&0), - 1234 - mock::EnterStakeAmount::get() - mock::ExtendStakeAmount::get() - ); + assert_ok!(SafeMode::extend(RuntimeOrigin::signed(1))); + assert_eq!(Balances::free_balance(&0), 1234 - mock::EnterStakeAmount::get()); + assert_eq!(Balances::free_balance(&1), 5678 - mock::ExtendStakeAmount::get()); - // Now once we slash once since the enter and extend are treated as one stake. assert_ok!(SafeMode::force_slash_stake( - RuntimeOrigin::signed(mock::StakeSlashOrigin::get()), + RuntimeOrigin::signed(mock::ForceStakeOrigin::get()), 0, 1 ),); - assert_eq!( - Balances::free_balance(&0), - 1234 - mock::ExtendStakeAmount::get() - mock::EnterStakeAmount::get() - ); + assert_eq!(Balances::free_balance(&0), 1234 - mock::EnterStakeAmount::get()); + + assert_ok!(SafeMode::force_slash_stake( + RuntimeOrigin::signed(mock::ForceStakeOrigin::get()), + 1, + 1 + ),); + assert_eq!(Balances::free_balance(&1), 5678 - mock::ExtendStakeAmount::get()); // But never again. assert_err!( - SafeMode::force_slash_stake(RuntimeOrigin::signed(mock::StakeSlashOrigin::get()), 0, 1), + SafeMode::force_slash_stake(RuntimeOrigin::signed(mock::ForceStakeOrigin::get()), 0, 1), + Error::::NoStake + ); + assert_err!( + SafeMode::force_slash_stake(RuntimeOrigin::signed(mock::ForceStakeOrigin::get()), 1, 1), Error::::NoStake ); }); @@ -520,14 +469,14 @@ fn can_slash_stake_with_config_origin() { let activated_at_block = System::block_number(); assert_ok!(SafeMode::enter(RuntimeOrigin::signed(0))); hypothetically_ok!(SafeMode::force_slash_stake( - RuntimeOrigin::signed(mock::StakeSlashOrigin::get()), + RuntimeOrigin::signed(mock::ForceStakeOrigin::get()), 0, activated_at_block ),); run_to(mock::EnterDuration::get() + activated_at_block + 1); assert_ok!(SafeMode::force_slash_stake( - RuntimeOrigin::signed(mock::StakeSlashOrigin::get()), + RuntimeOrigin::signed(mock::ForceStakeOrigin::get()), 0, activated_at_block )); @@ -536,7 +485,7 @@ fn can_slash_stake_with_config_origin() { Balances::make_free_balance_be(&0, 1234); let activated_and_extended_at_block = System::block_number(); assert_ok!(SafeMode::enter(RuntimeOrigin::signed(0))); - assert_ok!(SafeMode::extend(RuntimeOrigin::signed(0))); + assert_ok!(SafeMode::extend(RuntimeOrigin::signed(1))); run_to( mock::EnterDuration::get() + mock::ExtendDuration::get() + @@ -545,14 +494,12 @@ fn can_slash_stake_with_config_origin() { ); assert_ok!(SafeMode::force_slash_stake( - RuntimeOrigin::signed(mock::StakeSlashOrigin::get()), + RuntimeOrigin::signed(mock::ForceStakeOrigin::get()), 0, activated_and_extended_at_block )); - assert_eq!( - Balances::free_balance(&0), - 1234 - mock::EnterStakeAmount::get() - mock::ExtendStakeAmount::get() - ); // accounts set in mock genesis + assert_eq!(Balances::free_balance(&0), 1234 - mock::EnterStakeAmount::get()); // accounts set in + // mock genesis }); } @@ -562,37 +509,25 @@ fn fails_when_explicit_origin_required() { assert_eq!(SafeMode::active_until(), None); let activated_at_block = System::block_number(); + assert_err!(SafeMode::force_extend(signed(1)), DispatchError::BadOrigin); + assert_err!(SafeMode::force_exit(signed(1)), DispatchError::BadOrigin); assert_err!( - SafeMode::force_extend(ForceEnterOrigin::Weak.signed()), - DispatchError::BadOrigin - ); - assert_err!( - SafeMode::force_exit(ForceEnterOrigin::Weak.signed()), - DispatchError::BadOrigin - ); - assert_err!( - SafeMode::force_slash_stake(ForceEnterOrigin::Weak.signed(), 0, activated_at_block), + SafeMode::force_slash_stake(signed(1), 0, activated_at_block), DispatchError::BadOrigin ); assert_err!( - SafeMode::force_release_stake(ForceEnterOrigin::Weak.signed(), 0, activated_at_block), + SafeMode::force_release_stake(signed(1), 0, activated_at_block), DispatchError::BadOrigin ); + assert_err!(SafeMode::force_enter(signed(1)), DispatchError::BadOrigin); + assert_err!(SafeMode::force_exit(signed(1)), DispatchError::BadOrigin); assert_err!( - SafeMode::force_enter(ForceExtendOrigin::Weak.signed()), + SafeMode::force_slash_stake(signed(1), 0, activated_at_block), DispatchError::BadOrigin ); assert_err!( - SafeMode::force_exit(ForceExtendOrigin::Weak.signed()), - DispatchError::BadOrigin - ); - assert_err!( - SafeMode::force_slash_stake(ForceExtendOrigin::Weak.signed(), 0, activated_at_block), - DispatchError::BadOrigin - ); - assert_err!( - SafeMode::force_release_stake(ForceExtendOrigin::Weak.signed(), 0, activated_at_block), + SafeMode::force_release_stake(signed(1), 0, activated_at_block), DispatchError::BadOrigin ); @@ -622,15 +557,15 @@ fn fails_when_explicit_origin_required() { ); assert_err!( - SafeMode::force_enter(RuntimeOrigin::signed(mock::StakeSlashOrigin::get())), + SafeMode::force_enter(RuntimeOrigin::signed(mock::ForceStakeOrigin::get())), DispatchError::BadOrigin ); assert_err!( - SafeMode::force_extend(RuntimeOrigin::signed(mock::StakeSlashOrigin::get())), + SafeMode::force_extend(RuntimeOrigin::signed(mock::ForceStakeOrigin::get())), DispatchError::BadOrigin ); assert_err!( - SafeMode::force_exit(RuntimeOrigin::signed(mock::StakeSlashOrigin::get())), + SafeMode::force_exit(RuntimeOrigin::signed(mock::ForceStakeOrigin::get())), DispatchError::BadOrigin ); }); @@ -639,3 +574,7 @@ fn fails_when_explicit_origin_required() { fn call_transfer() -> RuntimeCall { RuntimeCall::Balances(pallet_balances::Call::transfer { dest: 1, value: 1 }) } + +fn signed(who: u64) -> RuntimeOrigin { + RuntimeOrigin::signed(who) +} diff --git a/frame/tx-pause/src/mock.rs b/frame/tx-pause/src/mock.rs index f931e27132c96..747b914b9e3e4 100644 --- a/frame/tx-pause/src/mock.rs +++ b/frame/tx-pause/src/mock.rs @@ -24,7 +24,7 @@ use crate as pallet_tx_pause; use frame_support::{ parameter_types, - traits::{ConstU64, Everything, InsideBoth, InstanceFilter, SortedMembers}, + traits::{ConstU64, Everything, InsideBoth, InstanceFilter}, }; use frame_system::EnsureSignedBy; use sp_core::H256; @@ -90,6 +90,7 @@ impl pallet_utility::Config for Test { type WeightInfo = (); } +/// Mocked proxies to check that the tx-pause also works with the proxy pallet. #[derive( Copy, Clone, @@ -108,11 +109,13 @@ pub enum ProxyType { JustTransfer, JustUtility, } + impl Default for ProxyType { fn default() -> Self { Self::Any } } + impl InstanceFilter for ProxyType { fn filter(&self, c: &RuntimeCall) -> bool { match self { @@ -127,6 +130,7 @@ impl InstanceFilter for ProxyType { self == &ProxyType::Any || self == o } } + impl pallet_proxy::Config for Test { type RuntimeEvent = RuntimeEvent; type RuntimeCall = RuntimeCall; @@ -143,17 +147,18 @@ impl pallet_proxy::Config for Test { } parameter_types! { + pub const MaxNameLen: u32 = 50; + pub const PauseTooLongNames: bool = false; +} + +frame_support::ord_parameter_types! { pub const PauseOrigin: u64 = 1; pub const UnpauseOrigin: u64 = 2; - pub const MaxNameLen: u32 = 50; - pub const PauseTooLongNames: bool = false; } +/// The calls that are never allowed to be paused. #[derive(Copy, Clone, Encode, Decode, RuntimeDebug, MaxEncodedLen, scale_info::TypeInfo)] pub struct WhitelistedCalls; - -/// Contains used by `BaseCallFiler` so this impl whitelists `Balances::transfer_keep_alive`. All -/// others may be paused. impl Contains> for WhitelistedCalls { fn contains(full_name: &FullNameOf) -> bool { let unpausables: Vec> = vec![( @@ -165,22 +170,6 @@ impl Contains> for WhitelistedCalls { } } -// Required impl to use some ::get() in tests -impl SortedMembers for PauseOrigin { - fn sorted_members() -> Vec { - vec![Self::get()] - } - #[cfg(feature = "runtime-benchmarks")] - fn add(_m: &u64) {} -} -impl SortedMembers for UnpauseOrigin { - fn sorted_members() -> Vec { - vec![Self::get()] - } - #[cfg(feature = "runtime-benchmarks")] - fn add(_m: &u64) {} -} - impl Config for Test { type RuntimeEvent = RuntimeEvent; type RuntimeCall = RuntimeCall; From 9118a1fc3ddeac743e1026641b78ba236aa219a1 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Wed, 29 Mar 2023 15:43:37 +0200 Subject: [PATCH 043/100] Add traits Signed-off-by: Oliver Tale-Yazdi --- frame/safe-mode/src/lib.rs | 98 +++++++++++++++++++++----- frame/support/src/traits.rs | 3 + frame/support/src/traits/call_pause.rs | 73 +++++++++++++++++++ frame/tx-pause/src/lib.rs | 65 ++++++++++++++--- 4 files changed, 211 insertions(+), 28 deletions(-) create mode 100644 frame/support/src/traits/call_pause.rs diff --git a/frame/safe-mode/src/lib.rs b/frame/safe-mode/src/lib.rs index 09c0b900c6108..747481b29e29d 100644 --- a/frame/safe-mode/src/lib.rs +++ b/frame/safe-mode/src/lib.rs @@ -143,6 +143,9 @@ pub mod pallet { /// This stake cannot be released yet. CannotReleaseYet, + + /// An error from the underlying `Currency`. + CurrencyError, } #[pallet::event] @@ -165,6 +168,16 @@ pub mod pallet { /// An account had reserve slashed that was reserved. StakeSlashed { account: T::AccountId, amount: BalanceOf }, + + /// Could not hold funds for entering or extending the safe-mode. + /// + /// This error comes from the underlying `Currency`. + CannotStake, + + /// Could not release funds for entering or extending the safe-mode. + /// + /// This error comes from the underlying `Currency`. + CannotRelease, } /// The reason why the safe-mode was deactivated. @@ -240,7 +253,7 @@ pub mod pallet { pub fn enter(origin: OriginFor) -> DispatchResult { let who = ensure_signed(origin)?; - Self::do_enter(Some(who), T::EnterDuration::get()) + Self::do_enter(Some(who), T::EnterDuration::get()).map_err(Into::into) } /// Enter safe-mode by force for a per-origin configured number of blocks. @@ -254,7 +267,7 @@ pub mod pallet { pub fn force_enter(origin: OriginFor) -> DispatchResult { let duration = T::ForceEnterOrigin::ensure_origin(origin)?; - Self::do_enter(None, duration) + Self::do_enter(None, duration).map_err(Into::into) } /// Extend the safe-mode permissionlessly for [`Config::ExtendDuration`] blocks. @@ -273,7 +286,7 @@ pub mod pallet { pub fn extend(origin: OriginFor) -> DispatchResult { let who = ensure_signed(origin)?; - Self::do_extend(Some(who), T::ExtendDuration::get()) + Self::do_extend(Some(who), T::ExtendDuration::get()).map_err(Into::into) } /// Extend the safe-mode by force for a per-origin configured number of blocks. @@ -287,7 +300,7 @@ pub mod pallet { pub fn force_extend(origin: OriginFor) -> DispatchResult { let duration = T::ForceExtendOrigin::ensure_origin(origin)?; - Self::do_extend(None, duration) + Self::do_extend(None, duration).map_err(Into::into) } /// Exit safe-mode by force. @@ -303,7 +316,7 @@ pub mod pallet { pub fn force_exit(origin: OriginFor) -> DispatchResult { T::ForceExitOrigin::ensure_origin(origin)?; - Self::do_exit(ExitReason::Force) + Self::do_exit(ExitReason::Force).map_err(Into::into) } /// Slash a stake for an account that entered or extended safe-mode at a specific @@ -324,7 +337,7 @@ pub mod pallet { ) -> DispatchResult { T::ForceStakeOrigin::ensure_origin(origin)?; - Self::do_force_slash(account, block) + Self::do_force_slash(account, block).map_err(Into::into) } /// Permissionlessly release a stake for an account that entered safe-mode at a @@ -348,7 +361,7 @@ pub mod pallet { ) -> DispatchResult { ensure_signed(origin)?; - Self::do_release(false, account, block) + Self::do_release(false, account, block).map_err(Into::into) } /// Force to release a stake for an account that entered safe-mode at a specific @@ -371,7 +384,7 @@ pub mod pallet { ) -> DispatchResult { T::ForceStakeOrigin::ensure_origin(origin)?; - Self::do_release(true, account, block) + Self::do_release(true, account, block).map_err(Into::into) } } @@ -395,7 +408,10 @@ pub mod pallet { impl Pallet { /// Logic for the [`crate::Pallet::enter`] and [`crate::Pallet::force_enter`] calls. - pub(crate) fn do_enter(who: Option, duration: T::BlockNumber) -> DispatchResult { + pub(crate) fn do_enter( + who: Option, + duration: T::BlockNumber, + ) -> Result<(), Error> { ensure!(!Self::is_entered(), Error::::Entered); if let Some(who) = who { @@ -410,7 +426,10 @@ impl Pallet { } /// Logic for the [`crate::Pallet::extend`] and [`crate::Pallet::force_extend`] calls. - pub(crate) fn do_extend(who: Option, duration: T::BlockNumber) -> DispatchResult { + pub(crate) fn do_extend( + who: Option, + duration: T::BlockNumber, + ) -> Result<(), Error> { let mut until = EnteredUntil::::get().ok_or(Error::::Exited)?; if let Some(who) = who { @@ -427,7 +446,7 @@ impl Pallet { /// Logic for the [`crate::Pallet::force_exit`] call. /// /// Errors if the safe-mode is already exited. - pub(crate) fn do_exit(reason: ExitReason) -> DispatchResult { + pub(crate) fn do_exit(reason: ExitReason) -> Result<(), Error> { let _until = EnteredUntil::::take().ok_or(Error::::Exited)?; Self::deposit_event(Event::Exited { reason }); Ok(()) @@ -439,7 +458,7 @@ impl Pallet { force: bool, account: T::AccountId, block: T::BlockNumber, - ) -> DispatchResult { + ) -> Result<(), Error> { let amount = Stakes::::take(&account, &block).ok_or(Error::::NoStake)?; if !force { @@ -451,13 +470,17 @@ impl Pallet { } let amount = - T::Currency::release(&T::HoldReason::get(), &account, amount, Precision::BestEffort)?; + T::Currency::release(&T::HoldReason::get(), &account, amount, Precision::BestEffort) + .map_err(|_| Error::::CurrencyError)?; Self::deposit_event(Event::::StakeReleased { account, amount }); Ok(()) } /// Logic for the [`crate::Pallet::slash_stake`] call. - pub(crate) fn do_force_slash(account: T::AccountId, block: T::BlockNumber) -> DispatchResult { + pub(crate) fn do_force_slash( + account: T::AccountId, + block: T::BlockNumber, + ) -> Result<(), Error> { let amount = Stakes::::take(&account, block).ok_or(Error::::NoStake)?; // FAIL-CI check these args @@ -467,7 +490,8 @@ impl Pallet { amount, Precision::BestEffort, Fortitude::Force, - )?; + ) + .map_err(|_| Error::::CurrencyError)?; defensive_assert!(burned == amount, "Could not burn the full held amount"); Self::deposit_event(Event::::StakeSlashed { account, amount }); Ok(()) @@ -476,13 +500,14 @@ impl Pallet { /// Place a hold for exactly `amount` and store it in `Stakes`. /// /// This errors if the account already has a hold for the same reason. - fn hold(who: T::AccountId, amount: BalanceOf) -> DispatchResult { + fn hold(who: T::AccountId, amount: BalanceOf) -> Result<(), Error> { let block = >::block_number(); if !T::Currency::balance_on_hold(&T::HoldReason::get(), &who).is_zero() { return Err(Error::::AlreadyStaked.into()) } - T::Currency::hold(&T::HoldReason::get(), &who, amount)?; + T::Currency::hold(&T::HoldReason::get(), &who, amount) + .map_err(|_| Error::::CurrencyError)?; Stakes::::insert(&who, block, amount); Self::deposit_event(Event::::StakePlaced { account: who, amount }); @@ -513,7 +538,7 @@ impl Pallet { } } -impl Contains for Pallet +impl Contains for Pallet where T::RuntimeCall: GetCallMetadata, { @@ -522,3 +547,40 @@ where Pallet::::is_allowed(call) } } + +impl frame_support::traits::SafeMode for Pallet { + type BlockNumber = T::BlockNumber; + + fn is_entered() -> bool { + Self::is_entered() + } + + fn remaining() -> Option { + EnteredUntil::::get().map(|until| { + let now = >::block_number(); + until.saturating_sub(now) + }) + } + + fn enter(duration: Self::BlockNumber) -> Result<(), frame_support::traits::SafeModeError> { + Self::do_enter(None, duration).map_err(Into::into) + } + + fn extend(duration: Self::BlockNumber) -> Result<(), frame_support::traits::SafeModeError> { + Self::do_extend(None, duration).map_err(Into::into) + } + + fn exit() -> Result<(), frame_support::traits::SafeModeError> { + Self::do_exit(ExitReason::Force).map_err(Into::into) + } +} + +impl From> for frame_support::traits::SafeModeError { + fn from(err: Error) -> Self { + match err { + Error::::Entered => Self::AlreadyEntered, + Error::::Exited => Self::AlreadyExited, + _ => Self::Unknown, + } + } +} diff --git a/frame/support/src/traits.rs b/frame/support/src/traits.rs index 17d2fb06c8118..5e06fd654aa26 100644 --- a/frame/support/src/traits.rs +++ b/frame/support/src/traits.rs @@ -118,6 +118,9 @@ pub use messages::{ ProcessMessageError, ServiceQueues, TransformOrigin, }; +mod call_pause; +pub use call_pause::{SafeMode, SafeModeError, TransactionPause, TransactionPauseError}; + #[cfg(feature = "try-runtime")] mod try_runtime; #[cfg(feature = "try-runtime")] diff --git a/frame/support/src/traits/call_pause.rs b/frame/support/src/traits/call_pause.rs new file mode 100644 index 0000000000000..aa72078354687 --- /dev/null +++ b/frame/support/src/traits/call_pause.rs @@ -0,0 +1,73 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Traits that can be used to pause calls on a chain. + +pub trait SafeMode { + type BlockNumber; + + /// Whether the safe mode is entered. + fn is_entered() -> bool { + Self::remaining().is_some() + } + + /// How many more blocks the safe mode will stay entered. + /// + /// If this returns `0` then the safe mode will exit in the next block. + fn remaining() -> Option; + + fn enter(duration: Self::BlockNumber) -> Result<(), SafeModeError>; + + fn extend(duration: Self::BlockNumber) -> Result<(), SafeModeError>; + + fn exit() -> Result<(), SafeModeError>; +} + +pub enum SafeModeError { + /// The safe mode is already entered. + AlreadyEntered, + /// The safe mode is already exited. + AlreadyExited, + Unknown, +} + +pub trait TransactionPause { + /// How to unambiguously identify a call. + /// + /// For example `(pallet_index, call_index)`. + type CallIdentifier; + + fn is_paused(call: Self::CallIdentifier) -> bool; + + fn can_pause(call: Self::CallIdentifier) -> bool; + + fn pause(call: Self::CallIdentifier) -> Result<(), TransactionPauseError>; + + fn unpause(call: Self::CallIdentifier) -> Result<(), TransactionPauseError>; +} + +pub enum TransactionPauseError { + /// The call could not be found in the runtime. This is a permanent error. + NotFound, + /// Call cannot be paused. This may or may not resolve in the future. + Unpausable, + /// Call is already paused. + AlreadyPaused, + /// Call is already unpaused. + AlreadyUnpaused, + Unknown, +} diff --git a/frame/tx-pause/src/lib.rs b/frame/tx-pause/src/lib.rs index 2ac422bbf8c9e..47033766680ae 100644 --- a/frame/tx-pause/src/lib.rs +++ b/frame/tx-pause/src/lib.rs @@ -164,11 +164,7 @@ pub mod pallet { pub fn pause(origin: OriginFor, full_name: FullNameOf) -> DispatchResult { T::PauseOrigin::ensure_origin(origin)?; - Self::ensure_can_pause(&full_name)?; - PausedCalls::::insert(&full_name, ()); - Self::deposit_event(Event::CallPaused { full_name }); - - Ok(()) + Self::do_pause(full_name).map_err(Into::into) } /// Un-pause a call. @@ -177,18 +173,31 @@ pub mod pallet { /// Emits an [`Event::CallUnpaused`] event on success. #[pallet::call_index(1)] #[pallet::weight(T::WeightInfo::unpause())] - pub fn unpause(origin: OriginFor, full_name: FullNameOf) -> DispatchResult { + pub fn unpause(origin: OriginFor, ident: FullNameOf) -> DispatchResult { T::UnpauseOrigin::ensure_origin(origin)?; - Self::ensure_can_unpause(&full_name)?; - PausedCalls::::remove(&full_name); - Self::deposit_event(Event::CallUnpaused { full_name }); - Ok(()) + Self::do_unpause(ident).map_err(Into::into) } } } impl Pallet { + pub(crate) fn do_pause(ident: FullNameOf) -> Result<(), Error> { + Self::ensure_can_pause(&ident)?; + PausedCalls::::insert(&ident, ()); + Self::deposit_event(Event::CallPaused { full_name: ident }); + + Ok(()) + } + + pub(crate) fn do_unpause(ident: FullNameOf) -> Result<(), Error> { + Self::ensure_can_unpause(&ident)?; + PausedCalls::::remove(&ident); + Self::deposit_event(Event::CallUnpaused { full_name: ident }); + + Ok(()) + } + /// Return whether this call is paused. pub fn is_paused_unbound(pallet: Vec, call: Vec) -> bool { let pallet = PalletNameOf::::try_from(pallet); @@ -246,3 +255,39 @@ where !Pallet::::is_paused_unbound(pallet_name.into(), function_name.into()) } } + +impl frame_support::traits::TransactionPause for Pallet { + type CallIdentifier = FullNameOf; + + fn is_paused(full_name: Self::CallIdentifier) -> bool { + Self::is_paused(&full_name) + } + + fn can_pause(full_name: Self::CallIdentifier) -> bool { + Self::ensure_can_pause(&full_name).is_ok() + } + + fn pause( + full_name: Self::CallIdentifier, + ) -> Result<(), frame_support::traits::TransactionPauseError> { + Self::do_pause(full_name).map_err(Into::into) + } + + fn unpause( + full_name: Self::CallIdentifier, + ) -> Result<(), frame_support::traits::TransactionPauseError> { + Self::do_unpause(full_name).map_err(Into::into) + } +} + +impl From> for frame_support::traits::TransactionPauseError { + fn from(err: Error) -> Self { + match err { + Error::::NotFound => Self::NotFound, + Error::::Unpausable => Self::Unpausable, + Error::::IsPaused => Self::AlreadyPaused, + Error::::IsUnpaused => Self::AlreadyUnpaused, + _ => Self::Unknown, + } + } +} From ddc94a97d3a5af472eca8da4ffaa05be2de0c228 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Wed, 29 Mar 2023 16:43:57 +0200 Subject: [PATCH 044/100] Remove old code Signed-off-by: Oliver Tale-Yazdi --- bin/node/runtime/src/lib.rs | 15 --------------- frame/examples/basic/src/tests.rs | 14 -------------- frame/support/src/traits/call_pause.rs | 2 +- 3 files changed, 1 insertion(+), 30 deletions(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 13bd58ba5124a..ffb5c98a235f7 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -260,21 +260,6 @@ parameter_types! { pub const ExtendStakeAmount: Balance = 15 * DOLLARS; pub const ReleaseDelay: u32 = 15; pub const SafeModeHoldReason: HoldReason = HoldReason::SafeMode; - - pub const ForceEnterWeak: u32 = 3; - pub const ForceEnterStrong: u32 = 5; - - pub const ForceExtendWeak: u32 = 11; - pub const ForceExtendStrong: u32 = 15; - - // NOTE: The account ID maps to the duration. Easy for testing. - pub ForceEnterOrigins: Vec = vec![ForceEnterWeak::get(), ForceEnterStrong::get()]; - pub ForceExtendOrigins: Vec = vec![ForceExtendWeak::get(), ForceExtendStrong::get()]; -} - -frame_support::ord_parameter_types! { - pub const ForceExitOrigin: u32 = 100; - pub const ForceStakeOrigin: u32 = 200; } impl pallet_safe_mode::Config for Runtime { diff --git a/frame/examples/basic/src/tests.rs b/frame/examples/basic/src/tests.rs index 10a3528457661..d9a8a4e8e1cdc 100644 --- a/frame/examples/basic/src/tests.rs +++ b/frame/examples/basic/src/tests.rs @@ -200,17 +200,3 @@ fn weights_work() { // TODO: account for proof size weight assert!(info1.weight.ref_time() > info2.weight.ref_time()); } - -frame_support::parameter_types! { - pub storage Value: u64 = 1; -} - -#[test] -fn storage_consistent() { - new_test_ext().execute_with(|| { - let _g = frame_support::StorageNoopGuard::default(); - - let v = Value::get(); - Value::set(&1); - }); -} diff --git a/frame/support/src/traits/call_pause.rs b/frame/support/src/traits/call_pause.rs index aa72078354687..53dc426b7e6fc 100644 --- a/frame/support/src/traits/call_pause.rs +++ b/frame/support/src/traits/call_pause.rs @@ -15,7 +15,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Traits that can be used to pause calls on a chain. +//! Traits that can be used to pause calls. pub trait SafeMode { type BlockNumber; From b65d1d5f2fc98ab5acd0310d1df4b6ecdfb23ed7 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Wed, 29 Mar 2023 16:44:13 +0200 Subject: [PATCH 045/100] Cleanup safe-mode benches Signed-off-by: Oliver Tale-Yazdi --- frame/tx-pause/src/benchmarking.rs | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/frame/tx-pause/src/benchmarking.rs b/frame/tx-pause/src/benchmarking.rs index 058f10a32ea83..9f470b74ac780 100644 --- a/frame/tx-pause/src/benchmarking.rs +++ b/frame/tx-pause/src/benchmarking.rs @@ -23,35 +23,31 @@ use frame_benchmarking::benchmarks; benchmarks! { pause { - let full_name: FullNameOf = (name::(b"SomePalletName"), name::(b"SomePalletName")); - let origin = T::PauseOrigin::try_successful_origin().expect("Tx-pause pallet does not make sense without pause origin"); + let origin = T::PauseOrigin::try_successful_origin() + .expect("Tx-pause pallet is not usable without pause origin"); + let full_name = name::(); }: _(origin, full_name.clone()) verify { - assert!(TxPause::::paused_calls(full_name.clone()).is_some()) + assert!(TxPause::::paused_calls(full_name).is_some()) } unpause { - let full_name: FullNameOf = (name::(b"SomePalletName"), name::(b"SomePalletName")); - let pause_origin = T::PauseOrigin::try_successful_origin().expect("Tx-pause pallet does not make sense without pause origin"); - - TxPause::::pause( - pause_origin, - full_name.clone(), - )?; - - let unpause_origin = T::UnpauseOrigin::try_successful_origin().expect("Tx-pause pallet does not make sense without un-pause origin"); - // let call = Call::::unpause { pallet_name: pallet_name.clone(), maybe_call_name: maybe_call_name.clone() }; + let unpause_origin = T::UnpauseOrigin::try_successful_origin() + .expect("Tx-pause pallet is not usable without pause origin"); + let full_name = name::(); + TxPause::::do_pause(full_name.clone()).unwrap(); }: _(unpause_origin, full_name.clone()) verify { - assert!(TxPause::::paused_calls(full_name.clone()).is_none()) - + assert!(TxPause::::paused_calls(full_name).is_none()) } impl_benchmark_test_suite!(TxPause, crate::mock::new_test_ext(), crate::mock::Test); } -pub fn name(bytes: &[u8]) -> BoundedVec { - bytes.to_vec().try_into().unwrap() +/// Longest possible name. +fn name() -> FullNameOf { + let max_len = T::MaxNameLen::get() as usize; + (vec![1; max_len].try_into().unwrap(), vec![1; max_len].try_into().unwrap()) } From 3aa32e2c39c78e6c6b4212a227ccf2f9f58586c0 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Fri, 28 Apr 2023 16:24:40 +0200 Subject: [PATCH 046/100] Update frame/safe-mode/Cargo.toml Co-authored-by: Liam Aharon --- frame/safe-mode/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/safe-mode/Cargo.toml b/frame/safe-mode/Cargo.toml index b13740162e929..a5e765e5b6d0a 100644 --- a/frame/safe-mode/Cargo.toml +++ b/frame/safe-mode/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../benchmarking" } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } frame-system = { version = "4.0.0-dev", default-features = false, path = "../system" } From 2c80c63b1ebf4c6c453543081bc49ebc28ea232d Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Fri, 28 Apr 2023 16:24:51 +0200 Subject: [PATCH 047/100] Update frame/safe-mode/Cargo.toml Co-authored-by: Liam Aharon --- frame/safe-mode/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/safe-mode/Cargo.toml b/frame/safe-mode/Cargo.toml index a5e765e5b6d0a..f92070c235fe6 100644 --- a/frame/safe-mode/Cargo.toml +++ b/frame/safe-mode/Cargo.toml @@ -17,7 +17,7 @@ codec = { package = "parity-scale-codec", version = "3.2.2", default-features = frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../benchmarking" } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } frame-system = { version = "4.0.0-dev", default-features = false, path = "../system" } -scale-info = { version = "2.0.1", default-features = false, features = ["derive"] } +scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } sp-arithmetic = { version = "6.0.0", default-features = false, path = "../../primitives/arithmetic" } sp-runtime = { version = "7.0.0", default-features = false, path = "../../primitives/runtime" } sp-std = { version = "5.0.0", default-features = false, path = "../../primitives/std" } From b79c9b34e6eab0079a54ef0718da03423961c5ec Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Fri, 28 Apr 2023 16:29:16 +0200 Subject: [PATCH 048/100] Docs Signed-off-by: Oliver Tale-Yazdi --- frame/safe-mode/src/lib.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/frame/safe-mode/src/lib.rs b/frame/safe-mode/src/lib.rs index 747481b29e29d..c53e07299ad2c 100644 --- a/frame/safe-mode/src/lib.rs +++ b/frame/safe-mode/src/lib.rs @@ -190,11 +190,11 @@ pub mod pallet { Force, } - /// Contains the last block number that the safe-mode will stay enter in. + /// Contains the last block number that the safe-mode will remain entered in. /// - /// This is set to `None` if the safe-mode is inactive. - /// The safe-mode is automatically deactivated when the current block number is greater than - /// this. + /// Set to `None` when safe-mode is exited. + /// + /// Safe-mode is automatically exited when the current block number exceeds this value. #[pallet::storage] #[pallet::getter(fn active_until)] pub type EnteredUntil = StorageValue<_, T::BlockNumber, OptionQuery>; From defb00b4612b0ebf45fe8d759a306afe7a9b1f3a Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Fri, 28 Apr 2023 16:31:11 +0200 Subject: [PATCH 049/100] Remove getters Signed-off-by: Oliver Tale-Yazdi --- frame/safe-mode/src/lib.rs | 1 - frame/tx-pause/src/lib.rs | 1 - 2 files changed, 2 deletions(-) diff --git a/frame/safe-mode/src/lib.rs b/frame/safe-mode/src/lib.rs index c53e07299ad2c..398ac7f0264e0 100644 --- a/frame/safe-mode/src/lib.rs +++ b/frame/safe-mode/src/lib.rs @@ -196,7 +196,6 @@ pub mod pallet { /// /// Safe-mode is automatically exited when the current block number exceeds this value. #[pallet::storage] - #[pallet::getter(fn active_until)] pub type EnteredUntil = StorageValue<_, T::BlockNumber, OptionQuery>; /// Holds the reserve that was taken from an account at a specific block number. diff --git a/frame/tx-pause/src/lib.rs b/frame/tx-pause/src/lib.rs index 47033766680ae..f0dcd7c48e46d 100644 --- a/frame/tx-pause/src/lib.rs +++ b/frame/tx-pause/src/lib.rs @@ -99,7 +99,6 @@ pub mod pallet { /// The set of calls that are explicitly paused. #[pallet::storage] - #[pallet::getter(fn paused_calls)] pub type PausedCalls = StorageMap<_, Blake2_128Concat, FullNameOf, (), OptionQuery>; From 5881f476024643e8b6c2a03ea682c5392a981e1f Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Fri, 28 Apr 2023 16:31:17 +0200 Subject: [PATCH 050/100] Update Cargo.lock Signed-off-by: Oliver Tale-Yazdi --- Cargo.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6dbcd32abb502..5dc9169fa5928 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9668,9 +9668,9 @@ dependencies = [ [[package]] name = "scale-info" -version = "2.3.1" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "001cf62ece89779fd16105b5f515ad0e5cedcd5440d3dd806bb067978e7c3608" +checksum = "dfdef77228a4c05dc94211441595746732131ad7f6530c6c18f045da7b7ab937" dependencies = [ "bitvec", "cfg-if", @@ -9682,9 +9682,9 @@ dependencies = [ [[package]] name = "scale-info-derive" -version = "2.3.1" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "303959cf613a6f6efd19ed4b4ad5bf79966a13352716299ad532cfb115f4205c" +checksum = "53012eae69e5aa5c14671942a5dd47de59d4cdcff8532a6dd0e081faf1119482" dependencies = [ "proc-macro-crate", "proc-macro2", From 1ab26e48259064380983cc0fb5bb88cbe31ed8cf Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Fri, 28 Apr 2023 14:37:28 +0000 Subject: [PATCH 051/100] Remove phantom Signed-off-by: Oliver Tale-Yazdi --- frame/safe-mode/src/lib.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/frame/safe-mode/src/lib.rs b/frame/safe-mode/src/lib.rs index 398ac7f0264e0..7427fc6f5f3cb 100644 --- a/frame/safe-mode/src/lib.rs +++ b/frame/safe-mode/src/lib.rs @@ -217,16 +217,14 @@ pub mod pallet { /// Configure the initial state of this pallet in the genesis block. #[pallet::genesis_config] pub struct GenesisConfig { - pub entered_until: Option, - pub _phantom: PhantomData, + pub entered_until: Option } - #[cfg(feature = "std")] impl Default for GenesisConfig { // NOTE: `derive(Default)` does not work together with `#[pallet::genesis_config]`. // We therefore need to add a trivial default impl. fn default() -> Self { - Self { entered_until: None, _phantom: PhantomData } + Self { entered_until: None } } } From 53bb17ee5cce84ac75cebe6fcb348d7a3cb68dda Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Fri, 28 Apr 2023 14:38:14 +0000 Subject: [PATCH 052/100] Fix test Signed-off-by: Oliver Tale-Yazdi --- frame/safe-mode/src/lib.rs | 2 +- frame/safe-mode/src/mock.rs | 2 +- frame/safe-mode/src/tests.rs | 20 ++++++++++---------- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/frame/safe-mode/src/lib.rs b/frame/safe-mode/src/lib.rs index 7427fc6f5f3cb..44d3e420a6950 100644 --- a/frame/safe-mode/src/lib.rs +++ b/frame/safe-mode/src/lib.rs @@ -217,7 +217,7 @@ pub mod pallet { /// Configure the initial state of this pallet in the genesis block. #[pallet::genesis_config] pub struct GenesisConfig { - pub entered_until: Option + pub entered_until: Option, } #[cfg(feature = "std")] impl Default for GenesisConfig { diff --git a/frame/safe-mode/src/mock.rs b/frame/safe-mode/src/mock.rs index 2f3596d35445a..fdf8a9026256b 100644 --- a/frame/safe-mode/src/mock.rs +++ b/frame/safe-mode/src/mock.rs @@ -228,7 +228,7 @@ pub fn new_test_ext() -> sp_io::TestExternalities { .unwrap(); GenesisBuild::::assimilate_storage( - &pallet_safe_mode::GenesisConfig { entered_until: None, _phantom: Default::default() }, + &pallet_safe_mode::GenesisConfig { entered_until: None }, &mut t, ) .unwrap(); diff --git a/frame/safe-mode/src/tests.rs b/frame/safe-mode/src/tests.rs index ab3b2f4d6756e..e47b5fa17485d 100644 --- a/frame/safe-mode/src/tests.rs +++ b/frame/safe-mode/src/tests.rs @@ -100,7 +100,7 @@ fn fails_to_activate_if_activated() { #[test] fn fails_to_extend_if_not_activated() { new_test_ext().execute_with(|| { - assert_eq!(SafeMode::active_until(), None); + assert_eq!(EnteredUntil::::get(), None); assert_noop!(SafeMode::extend(RuntimeOrigin::signed(2)), Error::::Exited); }); } @@ -156,7 +156,7 @@ fn can_automatically_deactivate_after_timeout() { assert_ok!(SafeMode::force_enter(signed(ForceEnterWeak::get()))); run_to(1 + activated_at_block + ForceEnterWeak::get()); - assert_eq!(SafeMode::active_until(), None); + assert_eq!(EnteredUntil::::get(), None); }); } @@ -219,7 +219,7 @@ fn can_activate() { new_test_ext().execute_with(|| { assert_ok!(SafeMode::enter(RuntimeOrigin::signed(0))); assert_eq!( - SafeMode::active_until().unwrap(), + EnteredUntil::::get().unwrap(), System::block_number() + mock::EnterDuration::get() ); assert_eq!(Balances::reserved_balance(0), mock::EnterStakeAmount::get()); @@ -235,7 +235,7 @@ fn cannot_extend() { assert_ok!(SafeMode::enter(RuntimeOrigin::signed(0))); assert_err!(SafeMode::extend(RuntimeOrigin::signed(0)), Error::::AlreadyStaked); assert_eq!( - SafeMode::active_until().unwrap(), + EnteredUntil::::get().unwrap(), System::block_number() + mock::EnterDuration::get() ); assert_eq!(Balances::reserved_balance(0), mock::EnterStakeAmount::get()); @@ -245,7 +245,7 @@ fn cannot_extend() { #[test] fn fails_signed_origin_when_explicit_origin_required() { new_test_ext().execute_with(|| { - assert_eq!(SafeMode::active_until(), None); + assert_eq!(EnteredUntil::::get(), None); let activated_at_block = System::block_number(); assert_err!(SafeMode::force_enter(RuntimeOrigin::signed(0)), DispatchError::BadOrigin); @@ -283,7 +283,7 @@ fn can_force_activate_with_config_origin() { new_test_ext().execute_with(|| { assert_ok!(SafeMode::force_enter(signed(ForceEnterStrong::get()))); assert_eq!( - SafeMode::active_until().unwrap(), + EnteredUntil::::get().unwrap(), System::block_number() + ForceEnterStrong::get() ); assert_noop!( @@ -296,7 +296,7 @@ fn can_force_activate_with_config_origin() { #[test] fn can_force_deactivate_with_config_origin() { new_test_ext().execute_with(|| { - assert_eq!(SafeMode::active_until(), None); + assert_eq!(EnteredUntil::::get(), None); assert_err!( SafeMode::force_exit(RuntimeOrigin::signed(ForceExitOrigin::get())), Error::::Exited @@ -312,12 +312,12 @@ fn can_force_extend_with_config_origin() { // Activated by `Weak` and extended by `Medium`. assert_ok!(SafeMode::force_enter(signed(ForceEnterWeak::get()))); assert_eq!( - SafeMode::active_until().unwrap(), + EnteredUntil::::get().unwrap(), System::block_number() + ForceEnterWeak::get() ); assert_ok!(SafeMode::force_extend(signed(ForceExtendWeak::get()))); assert_eq!( - SafeMode::active_until().unwrap(), + EnteredUntil::::get().unwrap(), System::block_number() + ForceEnterWeak::get() + ForceExtendWeak::get() ); }); @@ -506,7 +506,7 @@ fn can_slash_stake_with_config_origin() { #[test] fn fails_when_explicit_origin_required() { new_test_ext().execute_with(|| { - assert_eq!(SafeMode::active_until(), None); + assert_eq!(EnteredUntil::::get(), None); let activated_at_block = System::block_number(); assert_err!(SafeMode::force_extend(signed(1)), DispatchError::BadOrigin); From c2fc849856b7b861d8f441e7972403dcd176bd8f Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Fri, 28 Apr 2023 16:46:46 +0200 Subject: [PATCH 053/100] Remove phantom Signed-off-by: Oliver Tale-Yazdi --- frame/tx-pause/src/lib.rs | 3 +-- frame/tx-pause/src/mock.rs | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/frame/tx-pause/src/lib.rs b/frame/tx-pause/src/lib.rs index f0dcd7c48e46d..67ee25228a5af 100644 --- a/frame/tx-pause/src/lib.rs +++ b/frame/tx-pause/src/lib.rs @@ -131,7 +131,6 @@ pub mod pallet { pub struct GenesisConfig { /// The initially paused calls. pub paused: Vec>, - pub _phantom: PhantomData, } #[cfg(feature = "std")] @@ -139,7 +138,7 @@ pub mod pallet { // NOTE: `derive(Default)` does not work together with `#[pallet::genesis_config]`. // We therefore need to add a trivial default impl. fn default() -> Self { - Self { paused: Default::default(), _phantom: PhantomData } + Self { paused: Default::default() } } } diff --git a/frame/tx-pause/src/mock.rs b/frame/tx-pause/src/mock.rs index 747b914b9e3e4..0c3f3c2a6b63b 100644 --- a/frame/tx-pause/src/mock.rs +++ b/frame/tx-pause/src/mock.rs @@ -209,7 +209,7 @@ pub fn new_test_ext() -> sp_io::TestExternalities { .unwrap(); GenesisBuild::::assimilate_storage( - &pallet_tx_pause::GenesisConfig { paused: vec![], _phantom: Default::default() }, + &pallet_tx_pause::GenesisConfig { paused: vec![] }, &mut t, ) .unwrap(); From 273b7ab970b57753299ab3a2096b82f835a257a3 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Fri, 28 Apr 2023 16:50:44 +0200 Subject: [PATCH 054/100] Apply suggestions from code review Co-authored-by: Liam Aharon --- frame/safe-mode/src/mock.rs | 2 +- frame/tx-pause/Cargo.toml | 2 +- frame/tx-pause/src/lib.rs | 4 ++-- frame/tx-pause/src/mock.rs | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/frame/safe-mode/src/mock.rs b/frame/safe-mode/src/mock.rs index fdf8a9026256b..a97f4ff9a07a6 100644 --- a/frame/safe-mode/src/mock.rs +++ b/frame/safe-mode/src/mock.rs @@ -148,7 +148,7 @@ impl pallet_proxy::Config for Test { type AnnouncementDepositFactor = ConstU64<1>; } -/// The calls that can always bypass the safe-mode. +/// The calls that can always bypass safe-mode. pub struct WhitelistedCalls; impl Contains for WhitelistedCalls { fn contains(call: &RuntimeCall) -> bool { diff --git a/frame/tx-pause/Cargo.toml b/frame/tx-pause/Cargo.toml index 8143e74ff4ffb..a3eafefb4c6b2 100644 --- a/frame/tx-pause/Cargo.toml +++ b/frame/tx-pause/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../benchmarking" } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } frame-system = { version = "4.0.0-dev", default-features = false, path = "../system" } diff --git a/frame/tx-pause/src/lib.rs b/frame/tx-pause/src/lib.rs index 67ee25228a5af..dc4d7b29dae7b 100644 --- a/frame/tx-pause/src/lib.rs +++ b/frame/tx-pause/src/lib.rs @@ -41,7 +41,7 @@ pub type PalletNameOf = BoundedVec::MaxNameLen>; /// [`Config::RuntimeCall`] variants. pub type CallNameOf = BoundedVec::MaxNameLen>; -/// A sully specified pallet ([`PalletNameOf`]) and optional call ([`CallNameOf`]) +/// A fully specified pallet ([`PalletNameOf`]) and optional call ([`CallNameOf`]) /// to partially or fully specify an item a variant of a [`Config::RuntimeCall`]. pub type FullNameOf = (PalletNameOf, CallNameOf); @@ -74,7 +74,7 @@ pub mod pallet { /// Contains all calls that cannot be paused. /// - /// The `TxMode` pallet cannot pause it's own calls, and does not need to be explicitly + /// The `TxMode` pallet cannot pause its own calls, and does not need to be explicitly /// added here. type WhitelistedCalls: Contains>; diff --git a/frame/tx-pause/src/mock.rs b/frame/tx-pause/src/mock.rs index 0c3f3c2a6b63b..a490817c9aa5d 100644 --- a/frame/tx-pause/src/mock.rs +++ b/frame/tx-pause/src/mock.rs @@ -156,7 +156,7 @@ frame_support::ord_parameter_types! { pub const UnpauseOrigin: u64 = 2; } -/// The calls that are never allowed to be paused. +/// Calls that are never allowed to be paused. #[derive(Copy, Clone, Encode, Decode, RuntimeDebug, MaxEncodedLen, scale_info::TypeInfo)] pub struct WhitelistedCalls; impl Contains> for WhitelistedCalls { From f237453dad7f60188da69f2d549f674c13643d03 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Fri, 28 Apr 2023 17:04:14 +0200 Subject: [PATCH 055/100] Use new as Origin benchmarking syntax Signed-off-by: Oliver Tale-Yazdi --- frame/safe-mode/src/benchmarking.rs | 49 +++++++++-------------------- 1 file changed, 15 insertions(+), 34 deletions(-) diff --git a/frame/safe-mode/src/benchmarking.rs b/frame/safe-mode/src/benchmarking.rs index c901f18912bd0..2b0a496338fe3 100644 --- a/frame/safe-mode/src/benchmarking.rs +++ b/frame/safe-mode/src/benchmarking.rs @@ -66,7 +66,7 @@ mod benchmarks { _(origin); assert_eq!( - SafeMode::::active_until().unwrap(), + EnteredUntil::::get().unwrap(), System::::block_number() + T::EnterDuration::get() ); Ok(()) @@ -79,14 +79,11 @@ mod benchmarks { T::ForceEnterOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; let duration = T::ForceEnterOrigin::ensure_origin(force_origin.clone()).unwrap(); - let call = Call::::force_enter {}; - #[block] - { - call.dispatch_bypass_filter(force_origin)?; - } + #[extrinsic_call] + _(force_origin as T::RuntimeOrigin); - assert_eq!(SafeMode::::active_until().unwrap(), System::::block_number() + duration); + assert_eq!(EnteredUntil::::get().unwrap(), System::::block_number() + duration); Ok(()) } @@ -105,7 +102,7 @@ mod benchmarks { _(RawOrigin::Signed(alice)); assert_eq!( - SafeMode::::active_until().unwrap(), + EnteredUntil::::get().unwrap(), System::::block_number() + 1u32.into() + T::ExtendDuration::get() ); Ok(()) @@ -129,7 +126,7 @@ mod benchmarks { } assert_eq!( - SafeMode::::active_until().unwrap(), + EnteredUntil::::get().unwrap(), System::::block_number() + 1u32.into() + duration ); Ok(()) @@ -140,16 +137,12 @@ mod benchmarks { fn force_exit() -> Result<(), BenchmarkError> { let force_origin = T::ForceExitOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; - assert!(SafeMode::::do_enter(None, 1u32.into()).is_ok()); - let call = Call::::force_exit {}; - #[block] - { - call.dispatch_bypass_filter(force_origin)?; - } + #[extrinsic_call] + _(force_origin as T::RuntimeOrigin); - assert_eq!(SafeMode::::active_until(), None); + assert_eq!(EnteredUntil::::get(), None); Ok(()) } @@ -174,12 +167,8 @@ mod benchmarks { System::::on_initialize(System::::block_number()); SafeMode::::on_initialize(System::::block_number()); - let call = Call::::release_stake { account: alice.clone(), block: 1u32.into() }; - - #[block] - { - call.dispatch_bypass_filter(origin.into()).unwrap(); - } + #[extrinsic_call] + _(origin, alice.clone(), 1u32.into()); assert!(!Stakes::::contains_key(&alice, &block)); assert_eq!(T::Currency::balance(&alice), init_bal::()); @@ -209,12 +198,8 @@ mod benchmarks { System::::on_initialize(System::::block_number()); SafeMode::::on_initialize(System::::block_number()); - let call = Call::::force_release_stake { account: alice.clone(), block }; - - #[block] - { - call.dispatch_bypass_filter(force_origin)?; - } + #[extrinsic_call] + _(force_origin as T::RuntimeOrigin, alice.clone(), block); assert!(!Stakes::::contains_key(&alice, block)); assert_eq!(T::Currency::balance(&alice), init_bal::()); @@ -237,12 +222,8 @@ mod benchmarks { EnteredUntil::::put(&block); assert!(SafeMode::::do_exit(ExitReason::Force).is_ok()); - let call = Call::::force_slash_stake { account: alice.clone(), block }; - - #[block] - { - call.dispatch_bypass_filter(force_origin)?; - } + #[extrinsic_call] + _(force_origin as T::RuntimeOrigin, alice.clone(), block); assert!(!Stakes::::contains_key(&alice, block)); assert_eq!(T::Currency::balance(&alice), init_bal::() - 1u32.into()); From f228a19458a3fc66d80016366f0b7c5fae99828e Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Fri, 28 Apr 2023 17:09:23 +0200 Subject: [PATCH 056/100] Docs Signed-off-by: Oliver Tale-Yazdi --- bin/node/runtime/src/lib.rs | 3 --- frame/tx-pause/src/lib.rs | 8 ++++---- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index ef13cbc074ed8..a0031148892d2 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -222,9 +222,6 @@ impl Contains for SafeModeWhitelistedCalls { fn contains(call: &RuntimeCall) -> bool { match call { RuntimeCall::System(_) | RuntimeCall::SafeMode(_) | RuntimeCall::TxPause(_) => true, - RuntimeCall::Balances(_) => false, - // ... governance et al - _ => false, } } } diff --git a/frame/tx-pause/src/lib.rs b/frame/tx-pause/src/lib.rs index dc4d7b29dae7b..76c8ce045c069 100644 --- a/frame/tx-pause/src/lib.rs +++ b/frame/tx-pause/src/lib.rs @@ -85,11 +85,11 @@ pub mod pallet { #[pallet::constant] type MaxNameLen: Get; - /// Specifies if functions and pallets with too long names should be treated as paused. + /// Handles the edge-case when trying to pause a call with a too long name. /// - /// Setting this to `true` ensures that all calls that are callable, are also pause-able. - /// Otherwise there could be a situation where a call is callable but not pause-able, which - /// would could be exploited. + /// `true` ensures that all calls of the runtime can be paused. Otherwise there could be a + /// situation where a call is callable but not pause-able, which would could be exploited. + /// In mostly all situations this can be set to `true` without any downside. #[pallet::constant] type PauseTooLongNames: Get; From ec3baa5e93b896247a015c521e98cb4e1208f6c4 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Fri, 28 Apr 2023 17:21:26 +0200 Subject: [PATCH 057/100] Fix node Signed-off-by: Oliver Tale-Yazdi --- bin/node/runtime/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index a0031148892d2..a2ede0ea787f6 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -222,6 +222,7 @@ impl Contains for SafeModeWhitelistedCalls { fn contains(call: &RuntimeCall) -> bool { match call { RuntimeCall::System(_) | RuntimeCall::SafeMode(_) | RuntimeCall::TxPause(_) => true, + _ => false, } } } From 038451637acdc890d068ea5d485de83eb7abe8fd Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Fri, 28 Apr 2023 17:35:29 +0200 Subject: [PATCH 058/100] Fix tx-pause benches Signed-off-by: Oliver Tale-Yazdi --- frame/tx-pause/src/benchmarking.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frame/tx-pause/src/benchmarking.rs b/frame/tx-pause/src/benchmarking.rs index 9f470b74ac780..1ba72b84357c0 100644 --- a/frame/tx-pause/src/benchmarking.rs +++ b/frame/tx-pause/src/benchmarking.rs @@ -29,7 +29,7 @@ benchmarks! { }: _(origin, full_name.clone()) verify { - assert!(TxPause::::paused_calls(full_name).is_some()) + assert!(PausedCalls::::get(full_name).is_some()) } unpause { @@ -40,7 +40,7 @@ benchmarks! { }: _(unpause_origin, full_name.clone()) verify { - assert!(TxPause::::paused_calls(full_name).is_none()) + assert!(PausedCalls::::get(full_name).is_none()) } impl_benchmark_test_suite!(TxPause, crate::mock::new_test_ext(), crate::mock::Test); From 1dc0a1f7315d724f05a89b8fc9c158a03837f8f1 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Fri, 2 Jun 2023 19:31:18 +0200 Subject: [PATCH 059/100] Renames --- bin/node/runtime/src/lib.rs | 8 +++---- frame/tx-pause/src/benchmarking.rs | 2 +- frame/tx-pause/src/lib.rs | 37 ++++++++++++++++-------------- frame/tx-pause/src/mock.rs | 6 ++--- frame/tx-pause/src/tests.rs | 4 ++-- 5 files changed, 30 insertions(+), 27 deletions(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index a2ede0ea787f6..f48f945a50059 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -59,7 +59,7 @@ use pallet_nis::WithMaximumOf; use pallet_session::historical as pallet_session_historical; pub use pallet_transaction_payment::{CurrencyAdapter, Multiplier, TargetedFeeAdjustment}; use pallet_transaction_payment::{FeeDetails, RuntimeDispatchInfo}; -use pallet_tx_pause::FullNameOf; +use pallet_tx_pause::RuntimeCallNameOf; use scale_info::TypeInfo; use sp_api::impl_runtime_apis; use sp_authority_discovery::AuthorityId as AuthorityDiscoveryId; @@ -230,9 +230,9 @@ impl Contains for SafeModeWhitelistedCalls { /// Calls that cannot be paused by the tx-pause pallet. pub struct TxPauseWhitelistedCalls; /// Whitelist `Balances::transfer_keep_alive`, all others are pauseable. -impl Contains> for TxPauseWhitelistedCalls { - fn contains(full_name: &FullNameOf) -> bool { - let unpausables: Vec> = vec![( +impl Contains> for TxPauseWhitelistedCalls { + fn contains(full_name: &RuntimeCallNameOf) -> bool { + let unpausables: Vec> = vec![( b"Balances".to_vec().try_into().unwrap(), b"transfer_keep_alive".to_vec().try_into().unwrap(), )]; diff --git a/frame/tx-pause/src/benchmarking.rs b/frame/tx-pause/src/benchmarking.rs index 1ba72b84357c0..517de8349c114 100644 --- a/frame/tx-pause/src/benchmarking.rs +++ b/frame/tx-pause/src/benchmarking.rs @@ -47,7 +47,7 @@ benchmarks! { } /// Longest possible name. -fn name() -> FullNameOf { +fn name() -> RuntimeCallNameOf { let max_len = T::MaxNameLen::get() as usize; (vec![1; max_len].try_into().unwrap(), vec![1; max_len].try_into().unwrap()) } diff --git a/frame/tx-pause/src/lib.rs b/frame/tx-pause/src/lib.rs index 76c8ce045c069..b22c2c13e69f5 100644 --- a/frame/tx-pause/src/lib.rs +++ b/frame/tx-pause/src/lib.rs @@ -39,11 +39,11 @@ pub type PalletNameOf = BoundedVec::MaxNameLen>; /// The stringy name of a call (within a pallet) from [`GetCallMetadata`] for /// [`Config::RuntimeCall`] variants. -pub type CallNameOf = BoundedVec::MaxNameLen>; +pub type PalletCallNameOf = BoundedVec::MaxNameLen>; -/// A fully specified pallet ([`PalletNameOf`]) and optional call ([`CallNameOf`]) +/// A fully specified pallet ([`PalletNameOf`]) and optional call ([`PalletCallNameOf`]) /// to partially or fully specify an item a variant of a [`Config::RuntimeCall`]. -pub type FullNameOf = (PalletNameOf, CallNameOf); +pub type RuntimeCallNameOf = (PalletNameOf, PalletCallNameOf); #[frame_support::pallet] pub mod pallet { @@ -76,7 +76,7 @@ pub mod pallet { /// /// The `TxMode` pallet cannot pause its own calls, and does not need to be explicitly /// added here. - type WhitelistedCalls: Contains>; + type WhitelistedCalls: Contains>; /// Maximum length for pallet and call SCALE encoded string names. /// @@ -100,7 +100,7 @@ pub mod pallet { /// The set of calls that are explicitly paused. #[pallet::storage] pub type PausedCalls = - StorageMap<_, Blake2_128Concat, FullNameOf, (), OptionQuery>; + StorageMap<_, Blake2_128Concat, RuntimeCallNameOf, (), OptionQuery>; #[pallet::error] pub enum Error { @@ -121,16 +121,16 @@ pub mod pallet { #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event { /// This pallet, or a specific call is now paused. - CallPaused { full_name: FullNameOf }, + CallPaused { full_name: RuntimeCallNameOf }, /// This pallet, or a specific call is now unpaused. - CallUnpaused { full_name: FullNameOf }, + CallUnpaused { full_name: RuntimeCallNameOf }, } /// Configure the initial state of this pallet in the genesis block. #[pallet::genesis_config] pub struct GenesisConfig { /// The initially paused calls. - pub paused: Vec>, + pub paused: Vec>, } #[cfg(feature = "std")] @@ -159,7 +159,10 @@ pub mod pallet { /// Emits an [`Event::CallPaused`] event on success. #[pallet::call_index(0)] #[pallet::weight(T::WeightInfo::pause())] - pub fn pause(origin: OriginFor, full_name: FullNameOf) -> DispatchResult { + pub fn pause( + origin: OriginFor, + full_name: RuntimeCallNameOf, + ) -> DispatchResult { T::PauseOrigin::ensure_origin(origin)?; Self::do_pause(full_name).map_err(Into::into) @@ -171,7 +174,7 @@ pub mod pallet { /// Emits an [`Event::CallUnpaused`] event on success. #[pallet::call_index(1)] #[pallet::weight(T::WeightInfo::unpause())] - pub fn unpause(origin: OriginFor, ident: FullNameOf) -> DispatchResult { + pub fn unpause(origin: OriginFor, ident: RuntimeCallNameOf) -> DispatchResult { T::UnpauseOrigin::ensure_origin(origin)?; Self::do_unpause(ident).map_err(Into::into) @@ -180,7 +183,7 @@ pub mod pallet { } impl Pallet { - pub(crate) fn do_pause(ident: FullNameOf) -> Result<(), Error> { + pub(crate) fn do_pause(ident: RuntimeCallNameOf) -> Result<(), Error> { Self::ensure_can_pause(&ident)?; PausedCalls::::insert(&ident, ()); Self::deposit_event(Event::CallPaused { full_name: ident }); @@ -188,7 +191,7 @@ impl Pallet { Ok(()) } - pub(crate) fn do_unpause(ident: FullNameOf) -> Result<(), Error> { + pub(crate) fn do_unpause(ident: RuntimeCallNameOf) -> Result<(), Error> { Self::ensure_can_unpause(&ident)?; PausedCalls::::remove(&ident); Self::deposit_event(Event::CallUnpaused { full_name: ident }); @@ -199,7 +202,7 @@ impl Pallet { /// Return whether this call is paused. pub fn is_paused_unbound(pallet: Vec, call: Vec) -> bool { let pallet = PalletNameOf::::try_from(pallet); - let call = CallNameOf::::try_from(call); + let call = PalletCallNameOf::::try_from(call); match (pallet, call) { (Ok(pallet), Ok(call)) => Self::is_paused(&(pallet, call)), @@ -208,7 +211,7 @@ impl Pallet { } /// Return whether this call is paused. - pub fn is_paused(full_name: &FullNameOf) -> bool { + pub fn is_paused(full_name: &RuntimeCallNameOf) -> bool { if T::WhitelistedCalls::contains(full_name) { return false } @@ -217,7 +220,7 @@ impl Pallet { } /// Ensure that this call can be paused. - pub fn ensure_can_pause(full_name: &FullNameOf) -> Result<(), Error> { + pub fn ensure_can_pause(full_name: &RuntimeCallNameOf) -> Result<(), Error> { // SAFETY: The `TxPause` pallet can never pause itself. if full_name.0.as_ref() == ::name().as_bytes().to_vec() { return Err(Error::::Unpausable) @@ -233,7 +236,7 @@ impl Pallet { } /// Ensure that this call can be un-paused. - pub fn ensure_can_unpause(full_name: &FullNameOf) -> Result<(), Error> { + pub fn ensure_can_unpause(full_name: &RuntimeCallNameOf) -> Result<(), Error> { if Self::is_paused(&full_name) { // SAFETY: Everything that is paused, can be un-paused. Ok(()) @@ -255,7 +258,7 @@ where } impl frame_support::traits::TransactionPause for Pallet { - type CallIdentifier = FullNameOf; + type CallIdentifier = RuntimeCallNameOf; fn is_paused(full_name: Self::CallIdentifier) -> bool { Self::is_paused(&full_name) diff --git a/frame/tx-pause/src/mock.rs b/frame/tx-pause/src/mock.rs index a490817c9aa5d..7a54045e457f8 100644 --- a/frame/tx-pause/src/mock.rs +++ b/frame/tx-pause/src/mock.rs @@ -159,9 +159,9 @@ frame_support::ord_parameter_types! { /// Calls that are never allowed to be paused. #[derive(Copy, Clone, Encode, Decode, RuntimeDebug, MaxEncodedLen, scale_info::TypeInfo)] pub struct WhitelistedCalls; -impl Contains> for WhitelistedCalls { - fn contains(full_name: &FullNameOf) -> bool { - let unpausables: Vec> = vec![( +impl Contains> for WhitelistedCalls { + fn contains(full_name: &RuntimeCallNameOf) -> bool { + let unpausables: Vec> = vec![( b"Balances".to_vec().try_into().unwrap(), b"transfer_keep_alive".to_vec().try_into().unwrap(), )]; diff --git a/frame/tx-pause/src/tests.rs b/frame/tx-pause/src/tests.rs index 08029ecb8aeb0..e298b8771915f 100644 --- a/frame/tx-pause/src/tests.rs +++ b/frame/tx-pause/src/tests.rs @@ -227,8 +227,8 @@ pub fn call_transfer_keep_alive(dest: u64, value: u64) -> RuntimeCall { RuntimeCall::Balances(pallet_balances::Call::transfer_keep_alive { dest, value }) } -pub fn full_name(pallet_name: &[u8], call_name: &[u8]) -> FullNameOf { - >::from(( +pub fn full_name(pallet_name: &[u8], call_name: &[u8]) -> RuntimeCallNameOf { + >::from(( pallet_name.to_vec().try_into().unwrap(), call_name.to_vec().try_into().unwrap(), )) From 8d978f26714d0d84d9fb93516278dbe69c2b8743 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Fri, 2 Jun 2023 19:34:15 +0200 Subject: [PATCH 060/100] Remove duplicate test --- frame/tx-pause/src/tests.rs | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/frame/tx-pause/src/tests.rs b/frame/tx-pause/src/tests.rs index e298b8771915f..ca259315726b0 100644 --- a/frame/tx-pause/src/tests.rs +++ b/frame/tx-pause/src/tests.rs @@ -140,19 +140,6 @@ fn fails_to_pause_self() { }); } -#[test] -fn fails_to_pause_unpausable_pallet() { - new_test_ext().execute_with(|| { - assert_noop!( - TxPause::pause( - RuntimeOrigin::signed(mock::PauseOrigin::get()), - full_name::(b"Balances", b"transfer_keep_alive"), - ), - Error::::Unpausable - ); - }); -} - #[test] fn fails_to_pause_unpausable_call_when_other_call_is_paused() { new_test_ext().execute_with(|| { From 4389622102ca5d278fc4c604dd38e55e572e499d Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Fri, 2 Jun 2023 19:34:24 +0200 Subject: [PATCH 061/100] Add docs --- frame/tx-pause/src/lib.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/frame/tx-pause/src/lib.rs b/frame/tx-pause/src/lib.rs index b22c2c13e69f5..de51ba81f0370 100644 --- a/frame/tx-pause/src/lib.rs +++ b/frame/tx-pause/src/lib.rs @@ -200,6 +200,15 @@ impl Pallet { } /// Return whether this call is paused. + pub fn is_paused(full_name: &RuntimeCallNameOf) -> bool { + if T::WhitelistedCalls::contains(full_name) { + return false + } + + >::contains_key(full_name) + } + + /// Same as [`Self::is_paused`] but for non-MEL-bound inputs. pub fn is_paused_unbound(pallet: Vec, call: Vec) -> bool { let pallet = PalletNameOf::::try_from(pallet); let call = PalletCallNameOf::::try_from(call); @@ -210,15 +219,6 @@ impl Pallet { } } - /// Return whether this call is paused. - pub fn is_paused(full_name: &RuntimeCallNameOf) -> bool { - if T::WhitelistedCalls::contains(full_name) { - return false - } - - >::contains_key(full_name) - } - /// Ensure that this call can be paused. pub fn ensure_can_pause(full_name: &RuntimeCallNameOf) -> Result<(), Error> { // SAFETY: The `TxPause` pallet can never pause itself. From 27c803a247426541dc80482226b08185381e610f Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Fri, 2 Jun 2023 19:37:05 +0200 Subject: [PATCH 062/100] docs --- frame/safe-mode/src/lib.rs | 15 ++++++++------- frame/tx-pause/src/lib.rs | 5 +---- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/frame/safe-mode/src/lib.rs b/frame/safe-mode/src/lib.rs index 44d3e420a6950..45cbbb59b9d05 100644 --- a/frame/safe-mode/src/lib.rs +++ b/frame/safe-mode/src/lib.rs @@ -273,7 +273,7 @@ pub mod pallet { /// Reserves [`Config::ExtendStakeAmount`] from the caller's account. /// Emits an [`Event::Extended`] event on success. /// Errors with [`Error::Exited`] if the safe-mode is entered. - /// Errors with [`Error::NotConfigured`] if the stake amount is `None` . + /// Errors with [`Error::NotConfigured`] if the stake amount is `None`. /// /// This may be called by any signed origin with [`Config::ExtendStakeAmount`] free /// currency to reserve. This call can be disabled for all origins by configuring @@ -306,8 +306,9 @@ pub mod pallet { /// Errors with [`Error::Exited`] if the safe-mode is inactive. /// /// Note: `safe-mode` will be automatically deactivated by [`Pallet::on_initialize`] hook - /// after the block height is greater than [`EnteredUntil`] found in storage. - /// Emits an [`Event::Exited`] with [`ExitReason::Timeout`] event on hook. + /// after the block height is greater than the [`EnteredUntil`] storage item. + /// Emits an [`Event::Exited`] with [`ExitReason::Timeout`] event when deactivated in the + /// hook. #[pallet::call_index(4)] #[pallet::weight(T::WeightInfo::force_exit())] pub fn force_exit(origin: OriginFor) -> DispatchResult { @@ -316,13 +317,13 @@ pub mod pallet { Self::do_exit(ExitReason::Force).map_err(Into::into) } - /// Slash a stake for an account that entered or extended safe-mode at a specific - /// block earlier. + /// Slash a stake for an account that entered or extended safe-mode at a given + /// historical block. /// - /// This can be called while safe-mode is entered. + /// This can only be called while safe-mode is entered. /// /// Emits a [`Event::StakeSlashed`] event on success. - /// Errors with [`Error::Entered`] if the safe-mode is entered. + /// Errors with [`Error::Entered`] if safe-mode is entered. /// /// Can only be called by the [`Config::ForceStakeOrigin`] origin. #[pallet::call_index(5)] diff --git a/frame/tx-pause/src/lib.rs b/frame/tx-pause/src/lib.rs index de51ba81f0370..7548f3efee25f 100644 --- a/frame/tx-pause/src/lib.rs +++ b/frame/tx-pause/src/lib.rs @@ -159,10 +159,7 @@ pub mod pallet { /// Emits an [`Event::CallPaused`] event on success. #[pallet::call_index(0)] #[pallet::weight(T::WeightInfo::pause())] - pub fn pause( - origin: OriginFor, - full_name: RuntimeCallNameOf, - ) -> DispatchResult { + pub fn pause(origin: OriginFor, full_name: RuntimeCallNameOf) -> DispatchResult { T::PauseOrigin::ensure_origin(origin)?; Self::do_pause(full_name).map_err(Into::into) From 0079f874182c9fb0f1b461b4efbc2aa92eb51bc8 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Fri, 2 Jun 2023 19:42:17 +0200 Subject: [PATCH 063/100] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Liam Aharon Co-authored-by: Muharem Ismailov Co-authored-by: Gonçalo Pestana --- frame/safe-mode/src/lib.rs | 32 ++++++++++++++++---------------- frame/tx-pause/src/lib.rs | 4 ++-- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/frame/safe-mode/src/lib.rs b/frame/safe-mode/src/lib.rs index 45cbbb59b9d05..444d2089489b4 100644 --- a/frame/safe-mode/src/lib.rs +++ b/frame/safe-mode/src/lib.rs @@ -339,15 +339,15 @@ pub mod pallet { } /// Permissionlessly release a stake for an account that entered safe-mode at a - /// specific block earlier. + /// given historical block. /// /// The call can be completely disabled by setting [`Config::ReleaseDelay`] to `None`. - /// This cannot be called while safe-mode is entered and not until the - /// [`Config::ReleaseDelay`] block height is passed. + /// This cannot be called while safe-mode is entered and not until + /// [`Config::ReleaseDelay`] blocks have passed since safe-mode was entered. /// /// Emits a [`Event::StakeReleased`] event on success. /// Errors with [`Error::Entered`] if the safe-mode is entered. - /// Errors with [`Error::CannotReleaseYet`] if the [`Config::ReleaseDelay`] . + /// Errors with [`Error::CannotReleaseYet`] if [`Config::ReleaseDelay`] block have not passed since safe-mode was entered. /// Errors with [`Error::NoStake`] if the payee has no reserved currency at the /// block specified. #[pallet::call_index(6)] @@ -362,15 +362,15 @@ pub mod pallet { Self::do_release(false, account, block).map_err(Into::into) } - /// Force to release a stake for an account that entered safe-mode at a specific - /// block earlier. + /// Force to release a stake for an account that entered safe-mode at a given + /// historical block. /// /// This can be called while safe-mode is still entered. /// /// Emits a [`Event::StakeReleased`] event on success. - /// Errors with [`Error::Entered`] if the safe-mode is entered. + /// Errors with [`Error::Entered`] if safe-mode is entered. /// Errors with [`Error::NoStake`] if the payee has no reserved currency at the - /// block specified. + /// specified block. /// /// Can only be called by the [`Config::ForceStakeOrigin`] origin. #[pallet::call_index(7)] @@ -388,14 +388,14 @@ pub mod pallet { #[pallet::hooks] impl Hooks> for Pallet { - /// Automatically exits the safe-mode when the period runs out. + /// Automatically exits safe-mode when the current block number is greater than [`EnteredUntil`]. fn on_initialize(current: T::BlockNumber) -> Weight { let Some(limit) = EnteredUntil::::get() else { return T::WeightInfo::on_initialize_noop(); }; if current > limit { - let _ = Self::do_exit(ExitReason::Timeout).defensive_proof("Must exit; qed"); + let _ = Self::do_exit(ExitReason::Timeout).defensive_proof("Only Errors if safe-mode is not entered. Safe-mode has already been checked to be entered; qed"); T::WeightInfo::on_initialize_exit() } else { T::WeightInfo::on_initialize_noop() @@ -443,7 +443,7 @@ impl Pallet { /// Logic for the [`crate::Pallet::force_exit`] call. /// - /// Errors if the safe-mode is already exited. + /// Errors if safe-mode is already exited. pub(crate) fn do_exit(reason: ExitReason) -> Result<(), Error> { let _until = EnteredUntil::::take().ok_or(Error::::Exited)?; Self::deposit_event(Event::Exited { reason }); @@ -464,7 +464,7 @@ impl Pallet { let delay = T::ReleaseDelay::get().ok_or(Error::::NotConfigured)?; let now = >::block_number(); - ensure!(now > (block.saturating_add(delay)), Error::::CannotReleaseYet); + ensure!(now > block.saturating_add(delay), Error::::CannotReleaseYet); } let amount = @@ -497,7 +497,7 @@ impl Pallet { /// Place a hold for exactly `amount` and store it in `Stakes`. /// - /// This errors if the account already has a hold for the same reason. + /// Errors if the account already has a hold for the same reason. fn hold(who: T::AccountId, amount: BalanceOf) -> Result<(), Error> { let block = >::block_number(); if !T::Currency::balance_on_hold(&T::HoldReason::get(), &who).is_zero() { @@ -512,12 +512,12 @@ impl Pallet { Ok(()) } - /// Return whether the `safe-mode` is entered. + /// Return whether `safe-mode` is entered. pub fn is_entered() -> bool { EnteredUntil::::exists() } - /// Return whether this call is allowed to be dispatched. + /// Return whether the given call is allowed to be dispatched. pub fn is_allowed(call: &T::RuntimeCall) -> bool where T::RuntimeCall: GetCallMetadata, @@ -540,7 +540,7 @@ impl Contains for Pallet where T::RuntimeCall: GetCallMetadata, { - /// Return whether this call is allowed to be dispatched. + /// Return whether the given call is allowed to be dispatched. fn contains(call: &T::RuntimeCall) -> bool { Pallet::::is_allowed(call) } diff --git a/frame/tx-pause/src/lib.rs b/frame/tx-pause/src/lib.rs index 7548f3efee25f..53b8876e0ed27 100644 --- a/frame/tx-pause/src/lib.rs +++ b/frame/tx-pause/src/lib.rs @@ -88,7 +88,7 @@ pub mod pallet { /// Handles the edge-case when trying to pause a call with a too long name. /// /// `true` ensures that all calls of the runtime can be paused. Otherwise there could be a - /// situation where a call is callable but not pause-able, which would could be exploited. + /// situation where a call is callable but not pause-able, which could be exploited. /// In mostly all situations this can be set to `true` without any downside. #[pallet::constant] type PauseTooLongNames: Get; @@ -110,7 +110,7 @@ pub mod pallet { /// The call is (already or still) unpaused. IsUnpaused, - /// The call is listed as safe and cannot be paused. + /// The call is whitelisted and cannot be paused. Unpausable, // The pallet or call does not exist in the runtime. From f66cd4a939091344e5a5fef21e7a16e8c620a89a Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Fri, 2 Jun 2023 19:51:59 +0200 Subject: [PATCH 064/100] Cleanup tests --- frame/safe-mode/src/mock.rs | 5 ++++- frame/safe-mode/src/tests.rs | 27 +++++++++++++-------------- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/frame/safe-mode/src/mock.rs b/frame/safe-mode/src/mock.rs index a97f4ff9a07a6..a922b6ffb7b42 100644 --- a/frame/safe-mode/src/mock.rs +++ b/frame/safe-mode/src/mock.rs @@ -217,12 +217,15 @@ frame_support::construct_runtime!( } ); +pub const BAL_ACC0: u64 = 1234; +pub const BAL_ACC1: u64 = 5678; + pub fn new_test_ext() -> sp_io::TestExternalities { let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap(); pallet_balances::GenesisConfig:: { // The 0 account is NOT a special origin, the rest may be. - balances: vec![(0, 1234), (1, 5678), (2, 5678), (3, 5678), (4, 5678)], + balances: vec![(0, BAL_ACC0), (1, BAL_ACC1), (2, 5678), (3, 5678), (4, 5678)], } .assimilate_storage(&mut t) .unwrap(); diff --git a/frame/safe-mode/src/tests.rs b/frame/safe-mode/src/tests.rs index e47b5fa17485d..0c073c498861b 100644 --- a/frame/safe-mode/src/tests.rs +++ b/frame/safe-mode/src/tests.rs @@ -39,7 +39,7 @@ macro_rules! hypothetically { }; } -/// Assert something to be would be [*hypothetically*] `Ok` without actually doing it. +/// Assert something to be [*hypothetically*] `Ok` without actually committing it. /// /// Reverts any storage changes made by the closure. macro_rules! hypothetically_ok { @@ -169,7 +169,6 @@ fn can_filter_balance_calls_when_activated() { call_transfer().dispatch(RuntimeOrigin::signed(0)), frame_system::Error::::CallFiltered ); - // TODO ^^^ consider refactor to throw a safe mode error, not generic `CallFiltered` }); } @@ -340,9 +339,9 @@ fn can_force_release_stake_with_config_origin() { 0, activated_at_block )); - assert_eq!(Balances::free_balance(&0), 1234); // accounts set in mock genesis + assert_eq!(Balances::free_balance(&0), BAL_ACC0); // accounts set in mock genesis - Balances::make_free_balance_be(&0, 1234); + Balances::make_free_balance_be(&0, BAL_ACC0); let activated_and_extended_at_block = System::block_number(); assert_ok!(SafeMode::enter(RuntimeOrigin::signed(0))); assert_ok!(SafeMode::extend(RuntimeOrigin::signed(1))); @@ -358,7 +357,7 @@ fn can_force_release_stake_with_config_origin() { 0, activated_and_extended_at_block )); - assert_eq!(Balances::free_balance(&0), 1234); // accounts set in mock genesis + assert_eq!(Balances::free_balance(&0), BAL_ACC0); // accounts set in mock genesis }); } @@ -369,7 +368,7 @@ fn can_release_stake_while_entered() { assert_ok!(SafeMode::enter(RuntimeOrigin::signed(0))); assert!(SafeMode::is_entered()); - assert_eq!(Balances::free_balance(&0), 1234 - mock::EnterStakeAmount::get()); + assert_eq!(Balances::free_balance(&0), BAL_ACC0 - mock::EnterStakeAmount::get()); // We could slash in the same block or any later. for i in 0..mock::EnterDuration::get() + 10 { @@ -386,7 +385,7 @@ fn can_release_stake_while_entered() { 0, 1 ),); - assert_eq!(Balances::free_balance(&0), 1234); + assert_eq!(Balances::free_balance(&0), BAL_ACC0); // ... it wont work ever again. assert_err!( SafeMode::force_release_stake( @@ -434,22 +433,22 @@ fn can_slash_stake_from_extend_block() { new_test_ext().execute_with(|| { assert_ok!(SafeMode::enter(RuntimeOrigin::signed(0))); assert_ok!(SafeMode::extend(RuntimeOrigin::signed(1))); - assert_eq!(Balances::free_balance(&0), 1234 - mock::EnterStakeAmount::get()); - assert_eq!(Balances::free_balance(&1), 5678 - mock::ExtendStakeAmount::get()); + assert_eq!(Balances::free_balance(&0), BAL_ACC0 - mock::EnterStakeAmount::get()); + assert_eq!(Balances::free_balance(&1), BAL_ACC1 - mock::ExtendStakeAmount::get()); assert_ok!(SafeMode::force_slash_stake( RuntimeOrigin::signed(mock::ForceStakeOrigin::get()), 0, 1 ),); - assert_eq!(Balances::free_balance(&0), 1234 - mock::EnterStakeAmount::get()); + assert_eq!(Balances::free_balance(&0), BAL_ACC0 - mock::EnterStakeAmount::get()); assert_ok!(SafeMode::force_slash_stake( RuntimeOrigin::signed(mock::ForceStakeOrigin::get()), 1, 1 ),); - assert_eq!(Balances::free_balance(&1), 5678 - mock::ExtendStakeAmount::get()); + assert_eq!(Balances::free_balance(&1), BAL_ACC1 - mock::ExtendStakeAmount::get()); // But never again. assert_err!( @@ -480,9 +479,9 @@ fn can_slash_stake_with_config_origin() { 0, activated_at_block )); - assert_eq!(Balances::free_balance(&0), 1234 - mock::EnterStakeAmount::get()); // accounts set in mock genesis + assert_eq!(Balances::free_balance(&0), BAL_ACC0 - mock::EnterStakeAmount::get()); // accounts set in mock genesis - Balances::make_free_balance_be(&0, 1234); + Balances::make_free_balance_be(&0, BAL_ACC0); let activated_and_extended_at_block = System::block_number(); assert_ok!(SafeMode::enter(RuntimeOrigin::signed(0))); assert_ok!(SafeMode::extend(RuntimeOrigin::signed(1))); @@ -498,7 +497,7 @@ fn can_slash_stake_with_config_origin() { 0, activated_and_extended_at_block )); - assert_eq!(Balances::free_balance(&0), 1234 - mock::EnterStakeAmount::get()); // accounts set in + assert_eq!(Balances::free_balance(&0), BAL_ACC0 - mock::EnterStakeAmount::get()); // accounts set in // mock genesis }); } From d7d863c9b7f483b41e8235a1455f54ccfde6d354 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Fri, 2 Jun 2023 19:53:42 +0200 Subject: [PATCH 065/100] docs --- frame/tx-pause/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frame/tx-pause/src/lib.rs b/frame/tx-pause/src/lib.rs index 7548f3efee25f..6bdb10faf64e0 100644 --- a/frame/tx-pause/src/lib.rs +++ b/frame/tx-pause/src/lib.rs @@ -104,10 +104,10 @@ pub mod pallet { #[pallet::error] pub enum Error { - /// The call is (already or still) paused. + /// The call is paused. IsPaused, - /// The call is (already or still) unpaused. + /// The call is unpaused. IsUnpaused, /// The call is listed as safe and cannot be paused. From c37e49724f8e46228e0b9237c245f08b256988f1 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Fri, 2 Jun 2023 19:59:13 +0200 Subject: [PATCH 066/100] Cleanup GenesisConfigs --- frame/safe-mode/src/lib.rs | 10 ++-------- frame/safe-mode/src/tests.rs | 2 +- frame/tx-pause/src/lib.rs | 16 +++++----------- 3 files changed, 8 insertions(+), 20 deletions(-) diff --git a/frame/safe-mode/src/lib.rs b/frame/safe-mode/src/lib.rs index 45cbbb59b9d05..ad023b7376a58 100644 --- a/frame/safe-mode/src/lib.rs +++ b/frame/safe-mode/src/lib.rs @@ -34,6 +34,7 @@ use frame_support::{ CallMetadata, Contains, Defensive, GetCallMetadata, PalletInfoAccess, }, weights::Weight, + DefaultNoBound, }; use frame_system::pallet_prelude::*; use sp_arithmetic::traits::Zero; @@ -216,17 +217,10 @@ pub mod pallet { /// Configure the initial state of this pallet in the genesis block. #[pallet::genesis_config] + #[cfg_attr(feature = "std", derive(DefaultNoBound))] pub struct GenesisConfig { pub entered_until: Option, } - #[cfg(feature = "std")] - impl Default for GenesisConfig { - // NOTE: `derive(Default)` does not work together with `#[pallet::genesis_config]`. - // We therefore need to add a trivial default impl. - fn default() -> Self { - Self { entered_until: None } - } - } #[pallet::genesis_build] impl GenesisBuild for GenesisConfig { diff --git a/frame/safe-mode/src/tests.rs b/frame/safe-mode/src/tests.rs index 0c073c498861b..f3e53ec02d187 100644 --- a/frame/safe-mode/src/tests.rs +++ b/frame/safe-mode/src/tests.rs @@ -498,7 +498,7 @@ fn can_slash_stake_with_config_origin() { activated_and_extended_at_block )); assert_eq!(Balances::free_balance(&0), BAL_ACC0 - mock::EnterStakeAmount::get()); // accounts set in - // mock genesis + // mock genesis }); } diff --git a/frame/tx-pause/src/lib.rs b/frame/tx-pause/src/lib.rs index 6bdb10faf64e0..a675c9854b0e3 100644 --- a/frame/tx-pause/src/lib.rs +++ b/frame/tx-pause/src/lib.rs @@ -26,6 +26,7 @@ use frame_support::{ dispatch::GetDispatchInfo, pallet_prelude::*, traits::{CallMetadata, Contains, GetCallMetadata, IsSubType, IsType}, + DefaultNoBound, }; use frame_system::pallet_prelude::*; use sp_runtime::{traits::Dispatchable, DispatchResult}; @@ -128,25 +129,18 @@ pub mod pallet { /// Configure the initial state of this pallet in the genesis block. #[pallet::genesis_config] + #[cfg_attr(feature = "std", derive(DefaultNoBound))] pub struct GenesisConfig { /// The initially paused calls. pub paused: Vec>, } - #[cfg(feature = "std")] - impl Default for GenesisConfig { - // NOTE: `derive(Default)` does not work together with `#[pallet::genesis_config]`. - // We therefore need to add a trivial default impl. - fn default() -> Self { - Self { paused: Default::default() } - } - } - #[pallet::genesis_build] impl GenesisBuild for GenesisConfig { fn build(&self) { - for (pallet_name, maybe_call_name) in &self.paused { - PausedCalls::::insert((pallet_name, maybe_call_name), ()); + for call in &self.paused { + Pallet::::ensure_can_pause(&call).expect("Genesis data is known good; qed"); + PausedCalls::::insert(&call, ()); } } } From 82a8a068ef018971f3e487e604d0f4959561fae4 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Fri, 2 Jun 2023 20:15:57 +0200 Subject: [PATCH 067/100] Doc traits --- frame/support/src/traits.rs | 7 +- .../traits/{call_pause.rs => safe_mode.rs} | 47 +++++-------- frame/support/src/traits/tx_pause.rs | 66 +++++++++++++++++++ 3 files changed, 89 insertions(+), 31 deletions(-) rename frame/support/src/traits/{call_pause.rs => safe_mode.rs} (60%) create mode 100644 frame/support/src/traits/tx_pause.rs diff --git a/frame/support/src/traits.rs b/frame/support/src/traits.rs index 5e06fd654aa26..daeaf400c65f3 100644 --- a/frame/support/src/traits.rs +++ b/frame/support/src/traits.rs @@ -118,8 +118,11 @@ pub use messages::{ ProcessMessageError, ServiceQueues, TransformOrigin, }; -mod call_pause; -pub use call_pause::{SafeMode, SafeModeError, TransactionPause, TransactionPauseError}; +mod safe_mode; +pub use safe_mode::{SafeMode, SafeModeError}; + +mod tx_pause; +pub use tx_pause::{TransactionPause, TransactionPauseError}; #[cfg(feature = "try-runtime")] mod try_runtime; diff --git a/frame/support/src/traits/call_pause.rs b/frame/support/src/traits/safe_mode.rs similarity index 60% rename from frame/support/src/traits/call_pause.rs rename to frame/support/src/traits/safe_mode.rs index 53dc426b7e6fc..b10b6c3c6faeb 100644 --- a/frame/support/src/traits/call_pause.rs +++ b/frame/support/src/traits/safe_mode.rs @@ -15,9 +15,14 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Traits that can be used to pause calls. +//! Types to put the runtime into safe mode. +/// Can put the runtime into a safe mode. +/// +/// When the runtime entered safe mode, transaction processing for most general transactions is +/// paused. pub trait SafeMode { + /// Block number type. type BlockNumber; /// Whether the safe mode is entered. @@ -27,47 +32,31 @@ pub trait SafeMode { /// How many more blocks the safe mode will stay entered. /// - /// If this returns `0` then the safe mode will exit in the next block. + /// If this returns `0`, then the safe mode will exit in the next block. fn remaining() -> Option; + /// Enter the safe mode for `duration` blocks. + /// + /// Should error when already entered with `AlreadyEntered`. fn enter(duration: Self::BlockNumber) -> Result<(), SafeModeError>; + /// Extend the safe mode for `duration` blocks. + /// + /// Should error when not entered with `AlreadyExited`. fn extend(duration: Self::BlockNumber) -> Result<(), SafeModeError>; + /// Exit the safe mode immediately. + /// + /// This takes effect already in the same block. fn exit() -> Result<(), SafeModeError>; } +/// The error type for [`SafeMode`]. pub enum SafeModeError { /// The safe mode is already entered. AlreadyEntered, /// The safe mode is already exited. AlreadyExited, - Unknown, -} - -pub trait TransactionPause { - /// How to unambiguously identify a call. - /// - /// For example `(pallet_index, call_index)`. - type CallIdentifier; - - fn is_paused(call: Self::CallIdentifier) -> bool; - - fn can_pause(call: Self::CallIdentifier) -> bool; - - fn pause(call: Self::CallIdentifier) -> Result<(), TransactionPauseError>; - - fn unpause(call: Self::CallIdentifier) -> Result<(), TransactionPauseError>; -} - -pub enum TransactionPauseError { - /// The call could not be found in the runtime. This is a permanent error. - NotFound, - /// Call cannot be paused. This may or may not resolve in the future. - Unpausable, - /// Call is already paused. - AlreadyPaused, - /// Call is already unpaused. - AlreadyUnpaused, + /// Unknown error. Unknown, } diff --git a/frame/support/src/traits/tx_pause.rs b/frame/support/src/traits/tx_pause.rs new file mode 100644 index 0000000000000..8593a7871c541 --- /dev/null +++ b/frame/support/src/traits/tx_pause.rs @@ -0,0 +1,66 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Types to pause calls in the runtime. + +/// Can pause specific transactions from being processed. +/// +/// Note that paused transactions will not be queued for later execution. Instead they will be +/// dropped. +pub trait TransactionPause { + /// How to unambiguously identify a call. + /// + /// For example `(pallet_index, call_index)`. + type CallIdentifier; + + /// Whether this call is paused. + fn is_paused(call: Self::CallIdentifier) -> bool; + + /// Whether this call can be paused. + /// + /// This should hold for the current block and may change in the future. + fn can_pause(call: Self::CallIdentifier) -> bool; + + /// Pause this call immediately. + /// + /// This takes effect in the same block and must succeed if `can_pause` returns `true`. + fn pause(call: Self::CallIdentifier) -> Result<(), TransactionPauseError>; + + /// Unpause this call immediately. + /// + /// This takes effect in the same block and must succeed if `is_paused` returns `true`. This + /// invariant is important to not have un-resumable calls. + fn unpause(call: Self::CallIdentifier) -> Result<(), TransactionPauseError>; +} + +/// The error type for [`TransactionPause`]. +pub enum TransactionPauseError { + /// The call could not be found in the runtime. + /// + /// This is a permanent error but could change after a runtime upgrade. + NotFound, + /// Call cannot be paused. + /// + /// This may or may not resolve in a future block. + Unpausable, + /// Call is already paused. + AlreadyPaused, + /// Call is already unpaused. + AlreadyUnpaused, + /// Unknown error. + Unknown, +} From ba81f089ea59b25291b39882f491aeccced4e5c9 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Fri, 2 Jun 2023 20:17:47 +0200 Subject: [PATCH 068/100] Remove PauseTooLongNames --- bin/node/runtime/src/lib.rs | 1 - frame/tx-pause/src/lib.rs | 13 ++----------- frame/tx-pause/src/mock.rs | 2 -- 3 files changed, 2 insertions(+), 14 deletions(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index f48f945a50059..22862008643db 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -248,7 +248,6 @@ impl pallet_tx_pause::Config for Runtime { type UnpauseOrigin = EnsureRoot; type WhitelistedCalls = TxPauseWhitelistedCalls; type MaxNameLen = ConstU32<256>; - type PauseTooLongNames = ConstBool; type WeightInfo = pallet_tx_pause::weights::SubstrateWeight; } diff --git a/frame/tx-pause/src/lib.rs b/frame/tx-pause/src/lib.rs index a675c9854b0e3..73fb27ad679fd 100644 --- a/frame/tx-pause/src/lib.rs +++ b/frame/tx-pause/src/lib.rs @@ -81,19 +81,10 @@ pub mod pallet { /// Maximum length for pallet and call SCALE encoded string names. /// - /// Too long names will not be truncated but handled like [`Self::PauseTooLongNames`] - /// specifies. + /// TOO LONG NAMES WILL BE TREATED AS PAUSED. #[pallet::constant] type MaxNameLen: Get; - /// Handles the edge-case when trying to pause a call with a too long name. - /// - /// `true` ensures that all calls of the runtime can be paused. Otherwise there could be a - /// situation where a call is callable but not pause-able, which would could be exploited. - /// In mostly all situations this can be set to `true` without any downside. - #[pallet::constant] - type PauseTooLongNames: Get; - // Weight information for extrinsics in this pallet. type WeightInfo: WeightInfo; } @@ -206,7 +197,7 @@ impl Pallet { match (pallet, call) { (Ok(pallet), Ok(call)) => Self::is_paused(&(pallet, call)), - _ => T::PauseTooLongNames::get(), + _ => true, } } diff --git a/frame/tx-pause/src/mock.rs b/frame/tx-pause/src/mock.rs index 7a54045e457f8..b50e0cd6a4487 100644 --- a/frame/tx-pause/src/mock.rs +++ b/frame/tx-pause/src/mock.rs @@ -148,7 +148,6 @@ impl pallet_proxy::Config for Test { parameter_types! { pub const MaxNameLen: u32 = 50; - pub const PauseTooLongNames: bool = false; } frame_support::ord_parameter_types! { @@ -177,7 +176,6 @@ impl Config for Test { type UnpauseOrigin = EnsureSignedBy; type WhitelistedCalls = WhitelistedCalls; type MaxNameLen = MaxNameLen; - type PauseTooLongNames = PauseTooLongNames; type WeightInfo = (); } From 1542959a48b58de436d38eeeb569a4ad4333f12f Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Fri, 2 Jun 2023 20:17:55 +0200 Subject: [PATCH 069/100] docs --- frame/safe-mode/src/mock.rs | 2 +- frame/tx-pause/src/mock.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frame/safe-mode/src/mock.rs b/frame/safe-mode/src/mock.rs index a922b6ffb7b42..fe805eecd4989 100644 --- a/frame/safe-mode/src/mock.rs +++ b/frame/safe-mode/src/mock.rs @@ -15,7 +15,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Test utilities for safe mode pallet. +//! Tests and test utilities for safe mode pallet. #![cfg(test)] diff --git a/frame/tx-pause/src/mock.rs b/frame/tx-pause/src/mock.rs index b50e0cd6a4487..eea4d222bdaac 100644 --- a/frame/tx-pause/src/mock.rs +++ b/frame/tx-pause/src/mock.rs @@ -15,7 +15,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Test utilities for transaction pause (tx pause) pallet. +//! Tests and test utilities for transaction pause pallet. #![cfg(test)] From 16b46d6f7413c72961a10e181d6dc814e43295b7 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Fri, 2 Jun 2023 20:23:27 +0200 Subject: [PATCH 070/100] Use V2 benchmarking Signed-off-by: Oliver Tale-Yazdi --- frame/safe-mode/src/benchmarking.rs | 4 +--- frame/safe-mode/src/lib.rs | 9 +++++---- frame/tx-pause/src/benchmarking.rs | 26 ++++++++++++++++---------- frame/tx-pause/src/lib.rs | 2 +- 4 files changed, 23 insertions(+), 18 deletions(-) diff --git a/frame/safe-mode/src/benchmarking.rs b/frame/safe-mode/src/benchmarking.rs index 2b0a496338fe3..d4309b1347593 100644 --- a/frame/safe-mode/src/benchmarking.rs +++ b/frame/safe-mode/src/benchmarking.rs @@ -24,9 +24,7 @@ use frame_support::traits::{fungible::Mutate as FunMutate, UnfilteredDispatchabl use frame_system::{Pallet as System, RawOrigin}; use sp_runtime::traits::{Bounded, One, Zero}; -#[benchmarks( - where T::Currency: FunMutate, -)] +#[benchmarks(where T::Currency: FunMutate)] mod benchmarks { use super::*; diff --git a/frame/safe-mode/src/lib.rs b/frame/safe-mode/src/lib.rs index 18b98be4f5e30..2c644c29c1fec 100644 --- a/frame/safe-mode/src/lib.rs +++ b/frame/safe-mode/src/lib.rs @@ -341,9 +341,9 @@ pub mod pallet { /// /// Emits a [`Event::StakeReleased`] event on success. /// Errors with [`Error::Entered`] if the safe-mode is entered. - /// Errors with [`Error::CannotReleaseYet`] if [`Config::ReleaseDelay`] block have not passed since safe-mode was entered. - /// Errors with [`Error::NoStake`] if the payee has no reserved currency at the - /// block specified. + /// Errors with [`Error::CannotReleaseYet`] if [`Config::ReleaseDelay`] block have not + /// passed since safe-mode was entered. Errors with [`Error::NoStake`] if the payee has no + /// reserved currency at the block specified. #[pallet::call_index(6)] #[pallet::weight(T::WeightInfo::release_stake())] pub fn release_stake( @@ -382,7 +382,8 @@ pub mod pallet { #[pallet::hooks] impl Hooks> for Pallet { - /// Automatically exits safe-mode when the current block number is greater than [`EnteredUntil`]. + /// Automatically exits safe-mode when the current block number is greater than + /// [`EnteredUntil`]. fn on_initialize(current: T::BlockNumber) -> Weight { let Some(limit) = EnteredUntil::::get() else { return T::WeightInfo::on_initialize_noop(); diff --git a/frame/tx-pause/src/benchmarking.rs b/frame/tx-pause/src/benchmarking.rs index 517de8349c114..81595ef9f7280 100644 --- a/frame/tx-pause/src/benchmarking.rs +++ b/frame/tx-pause/src/benchmarking.rs @@ -18,29 +18,35 @@ #![cfg(feature = "runtime-benchmarks")] use super::{Pallet as TxPause, *}; +use frame_benchmarking::v2::*; -use frame_benchmarking::benchmarks; +#[benchmarks] +mod benchmarks { + use super::*; -benchmarks! { - pause { + #[benchmark] + fn pause() { let origin = T::PauseOrigin::try_successful_origin() .expect("Tx-pause pallet is not usable without pause origin"); let full_name = name::(); - }: _(origin, full_name.clone()) - verify { - assert!(PausedCalls::::get(full_name).is_some()) + #[extrinsic_call] + _(origin as T::RuntimeOrigin, full_name.clone()); + + assert!(PausedCalls::::get(full_name).is_some()); } - unpause { + #[benchmark] + fn unpause() { let unpause_origin = T::UnpauseOrigin::try_successful_origin() .expect("Tx-pause pallet is not usable without pause origin"); let full_name = name::(); TxPause::::do_pause(full_name.clone()).unwrap(); - }: _(unpause_origin, full_name.clone()) - verify { - assert!(PausedCalls::::get(full_name).is_none()) + #[extrinsic_call] + _(unpause_origin as T::RuntimeOrigin, full_name.clone()); + + assert!(PausedCalls::::get(full_name).is_none()); } impl_benchmark_test_suite!(TxPause, crate::mock::new_test_ext(), crate::mock::Test); diff --git a/frame/tx-pause/src/lib.rs b/frame/tx-pause/src/lib.rs index 1fa6f41f28d6d..dd7671b5f7d8c 100644 --- a/frame/tx-pause/src/lib.rs +++ b/frame/tx-pause/src/lib.rs @@ -79,7 +79,7 @@ pub mod pallet { /// added here. type WhitelistedCalls: Contains>; - /// Maximum length for pallet and call SCALE encoded string names. + /// Maximum length for pallet name and call name SCALE encoded string names. /// /// TOO LONG NAMES WILL BE TREATED AS PAUSED. #[pallet::constant] From 8915860a6a8c4eb985f2c50b75d360c3805d1815 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Fri, 2 Jun 2023 20:37:18 +0200 Subject: [PATCH 071/100] Use RuntimeHoldReason --- frame/safe-mode/Cargo.toml | 10 +++++----- frame/safe-mode/src/benchmarking.rs | 6 +++--- frame/safe-mode/src/lib.rs | 22 ++++++++++++++-------- frame/safe-mode/src/mock.rs | 4 ++-- frame/tx-pause/Cargo.toml | 8 ++++---- frame/tx-pause/src/mock.rs | 2 +- 6 files changed, 29 insertions(+), 23 deletions(-) diff --git a/frame/safe-mode/Cargo.toml b/frame/safe-mode/Cargo.toml index f92070c235fe6..c2126bbec3650 100644 --- a/frame/safe-mode/Cargo.toml +++ b/frame/safe-mode/Cargo.toml @@ -18,16 +18,16 @@ frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } frame-system = { version = "4.0.0-dev", default-features = false, path = "../system" } scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } -sp-arithmetic = { version = "6.0.0", default-features = false, path = "../../primitives/arithmetic" } -sp-runtime = { version = "7.0.0", default-features = false, path = "../../primitives/runtime" } -sp-std = { version = "5.0.0", default-features = false, path = "../../primitives/std" } +sp-arithmetic = { version = "16.0.0", default-features = false, path = "../../primitives/arithmetic" } +sp-runtime = { version = "24.0.0", default-features = false, path = "../../primitives/runtime" } +sp-std = { version = "8.0.0", default-features = false, path = "../../primitives/std" } pallet-balances = { version = "4.0.0-dev", path = "../balances", default-features = false, optional = true } pallet-utility = { version = "4.0.0-dev", path = "../utility", default-features = false, optional = true } pallet-proxy = { version = "4.0.0-dev", path = "../proxy", default-features = false, optional = true } [dev-dependencies] -sp-core = { version = "7.0.0", path = "../../primitives/core" } -sp-io = { version = "7.0.0", path = "../../primitives/io" } +sp-core = { version = "21.0.0", path = "../../primitives/core" } +sp-io = { version = "23.0.0", path = "../../primitives/io" } pallet-balances = { version = "4.0.0-dev", path = "../balances" } pallet-utility = { version = "4.0.0-dev", path = "../utility" } pallet-proxy = { version = "4.0.0-dev", path = "../proxy" } diff --git a/frame/safe-mode/src/benchmarking.rs b/frame/safe-mode/src/benchmarking.rs index d4309b1347593..2281a163ff2d2 100644 --- a/frame/safe-mode/src/benchmarking.rs +++ b/frame/safe-mode/src/benchmarking.rs @@ -157,7 +157,7 @@ mod benchmarks { let block: T::BlockNumber = 1u32.into(); let bal: BalanceOf = 1u32.into(); Stakes::::insert(&alice, &block, &bal); - T::Currency::hold(&T::HoldReason::get(), &alice, bal)?; + T::Currency::hold(&HoldReason::EnterOrExtend.into(), &alice, bal)?; EnteredUntil::::put(&block); assert!(SafeMode::::do_exit(ExitReason::Force).is_ok()); @@ -186,7 +186,7 @@ mod benchmarks { let block: T::BlockNumber = 1u32.into(); let bal: BalanceOf = 1u32.into(); Stakes::::insert(&alice, &block, &bal); - T::Currency::hold(&T::HoldReason::get(), &alice, bal)?; + T::Currency::hold(&&HoldReason::EnterOrExtend.into(), &alice, bal)?; EnteredUntil::::put(&block); assert_eq!(T::Currency::balance(&alice), init_bal::() - 1u32.into()); @@ -216,7 +216,7 @@ mod benchmarks { let block: T::BlockNumber = 1u32.into(); let bal: BalanceOf = 1u32.into(); Stakes::::insert(&alice, &block, &bal); - T::Currency::hold(&T::HoldReason::get(), &alice, bal)?; + T::Currency::hold(&&HoldReason::EnterOrExtend.into(), &alice, bal)?; EnteredUntil::::put(&block); assert!(SafeMode::::do_exit(ExitReason::Force).is_ok()); diff --git a/frame/safe-mode/src/lib.rs b/frame/safe-mode/src/lib.rs index 2c644c29c1fec..aadb752bf6ff2 100644 --- a/frame/safe-mode/src/lib.rs +++ b/frame/safe-mode/src/lib.rs @@ -60,11 +60,10 @@ pub mod pallet { type RuntimeEvent: From> + IsType<::RuntimeEvent>; /// Currency type for this pallet, used for Stakes. - type Currency: FunHoldInspect + FunHoldMutate; + type Currency: FunHoldInspect + FunHoldMutate; - /// Create a hold reason for a specific cause. - #[pallet::constant] - type HoldReason: Get<>::Reason>; + /// The hold reason when reserving funds for entering or extending the safe-mode. + type RuntimeHoldReason: From; /// Contains all runtime calls in any pallet that can be dispatched even while the safe-mode /// is entered. @@ -231,6 +230,13 @@ pub mod pallet { } } + /// A reason for the pallet placing a hold on funds. + #[pallet::composite_enum] + pub enum HoldReason { + #[codec(index = 0)] + EnterOrExtend, + } + #[pallet::call] impl Pallet { /// Enter safe-mode permissionlessly for [`Config::EnterDuration`] blocks. @@ -463,7 +469,7 @@ impl Pallet { } let amount = - T::Currency::release(&T::HoldReason::get(), &account, amount, Precision::BestEffort) + T::Currency::release(&&HoldReason::EnterOrExtend.into(), &account, amount, Precision::BestEffort) .map_err(|_| Error::::CurrencyError)?; Self::deposit_event(Event::::StakeReleased { account, amount }); Ok(()) @@ -478,7 +484,7 @@ impl Pallet { // FAIL-CI check these args let burned = T::Currency::burn_held( - &T::HoldReason::get(), + &&HoldReason::EnterOrExtend.into(), &account, amount, Precision::BestEffort, @@ -495,11 +501,11 @@ impl Pallet { /// Errors if the account already has a hold for the same reason. fn hold(who: T::AccountId, amount: BalanceOf) -> Result<(), Error> { let block = >::block_number(); - if !T::Currency::balance_on_hold(&T::HoldReason::get(), &who).is_zero() { + if !T::Currency::balance_on_hold(&HoldReason::EnterOrExtend.into(), &who).is_zero() { return Err(Error::::AlreadyStaked.into()) } - T::Currency::hold(&T::HoldReason::get(), &who, amount) + T::Currency::hold(&HoldReason::EnterOrExtend.into(), &who, amount) .map_err(|_| Error::::CurrencyError)?; Stakes::::insert(&who, block, amount); Self::deposit_event(Event::::StakePlaced { account: who, amount }); diff --git a/frame/safe-mode/src/mock.rs b/frame/safe-mode/src/mock.rs index fe805eecd4989..ffb7b921279ec 100644 --- a/frame/safe-mode/src/mock.rs +++ b/frame/safe-mode/src/mock.rs @@ -79,7 +79,7 @@ impl pallet_balances::Config for Test { type MaxLocks = (); type MaxReserves = ConstU32<10>; type ReserveIdentifier = Self::BlockNumber; - type HoldIdentifier = HoldReason; + type RuntimeHoldReason = RuntimeHoldReason; type FreezeIdentifier = (); type MaxHolds = ConstU32<10>; type MaxFreezes = ConstU32<0>; @@ -186,7 +186,7 @@ frame_support::ord_parameter_types! { impl Config for Test { type RuntimeEvent = RuntimeEvent; type Currency = Balances; - type HoldReason = SafeModeHoldReason; + type RuntimeHoldReason = RuntimeHoldReason; type WhitelistedCalls = WhitelistedCalls; type EnterDuration = EnterDuration; type EnterStakeAmount = EnterStakeAmount; diff --git a/frame/tx-pause/Cargo.toml b/frame/tx-pause/Cargo.toml index a3eafefb4c6b2..332c4ca654690 100644 --- a/frame/tx-pause/Cargo.toml +++ b/frame/tx-pause/Cargo.toml @@ -18,15 +18,15 @@ frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } frame-system = { version = "4.0.0-dev", default-features = false, path = "../system" } scale-info = { version = "2.0.1", default-features = false, features = ["derive"] } -sp-runtime = { version = "7.0.0", default-features = false, path = "../../primitives/runtime" } -sp-std = { version = "5.0.0", default-features = false, path = "../../primitives/std" } +sp-runtime = { version = "24.0.0", default-features = false, path = "../../primitives/runtime" } +sp-std = { version = "8.0.0", default-features = false, path = "../../primitives/std" } pallet-balances = { version = "4.0.0-dev", path = "../balances", default-features = false, optional = true } pallet-utility = { version = "4.0.0-dev", path = "../utility", default-features = false, optional = true } pallet-proxy = { version = "4.0.0-dev", path = "../proxy", default-features = false, optional = true } [dev-dependencies] -sp-core = { version = "7.0.0", path = "../../primitives/core" } -sp-io = { version = "7.0.0", path = "../../primitives/io" } +sp-core = { version = "21.0.0", path = "../../primitives/core" } +sp-io = { version = "23.0.0", path = "../../primitives/io" } pallet-balances = { version = "4.0.0-dev", path = "../balances" } pallet-utility = { version = "4.0.0-dev", path = "../utility" } pallet-proxy = { version = "4.0.0-dev", path = "../proxy" } diff --git a/frame/tx-pause/src/mock.rs b/frame/tx-pause/src/mock.rs index eea4d222bdaac..418889879080b 100644 --- a/frame/tx-pause/src/mock.rs +++ b/frame/tx-pause/src/mock.rs @@ -77,8 +77,8 @@ impl pallet_balances::Config for Test { type MaxLocks = MaxLocks; type MaxReserves = (); type ReserveIdentifier = [u8; 8]; - type HoldIdentifier = (); type FreezeIdentifier = (); + type RuntimeHoldReason = RuntimeHoldReason; type MaxHolds = ConstU32<0>; type MaxFreezes = ConstU32<0>; } From d0967a83b7df0c242aa67aef7c3af1e69e372ac9 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Fri, 2 Jun 2023 20:49:43 +0200 Subject: [PATCH 072/100] Fix kitchensink runtime --- bin/node/runtime/src/lib.rs | 17 ++++++++--------- frame/safe-mode/src/lib.rs | 16 +++++++++++----- frame/tx-pause/src/lib.rs | 2 +- 3 files changed, 20 insertions(+), 15 deletions(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index b0372dd9cdad0..07ee79aed7f79 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -256,22 +256,21 @@ impl pallet_tx_pause::Config for Runtime { } parameter_types! { - pub const SignedEnterDuration: u32 = 10; - pub const ExtendDuration: u32 = 20; - pub const EnterStakeAmount: Balance = 10 * DOLLARS; - pub const ExtendStakeAmount: Balance = 15 * DOLLARS; - pub const ReleaseDelay: u32 = 15; - pub const SafeModeHoldReason: HoldReason = HoldReason::SafeMode; + pub const EnterDuration: BlockNumber = 4 * HOURS; + pub const EnterStakeAmount: Balance = 2_000_000 * DOLLARS; + pub const ExtendDuration: BlockNumber = 2 * HOURS; + pub const ExtendStakeAmount: Balance = 1_000_000 * DOLLARS; + pub const ReleaseDelay: u32 = 2 * DAYS; } impl pallet_safe_mode::Config for Runtime { type RuntimeEvent = RuntimeEvent; type Currency = Balances; - type HoldReason = SafeModeHoldReason; + type RuntimeHoldReason = RuntimeHoldReason; type WhitelistedCalls = SafeModeWhitelistedCalls; - type EnterDuration = ConstU32<{ 2 * DAYS }>; + type EnterDuration = EnterDuration; type EnterStakeAmount = EnterStakeAmount; - type ExtendDuration = ConstU32<{ 1 * DAYS }>; + type ExtendDuration = ExtendDuration; type ExtendStakeAmount = ExtendStakeAmount; type ForceEnterOrigin = EnsureRootWithSuccess>; type ForceExtendOrigin = EnsureRootWithSuccess>; diff --git a/frame/safe-mode/src/lib.rs b/frame/safe-mode/src/lib.rs index aadb752bf6ff2..0934384d451fe 100644 --- a/frame/safe-mode/src/lib.rs +++ b/frame/safe-mode/src/lib.rs @@ -60,7 +60,8 @@ pub mod pallet { type RuntimeEvent: From> + IsType<::RuntimeEvent>; /// Currency type for this pallet, used for Stakes. - type Currency: FunHoldInspect + FunHoldMutate; + type Currency: FunHoldInspect + + FunHoldMutate; /// The hold reason when reserving funds for entering or extending the safe-mode. type RuntimeHoldReason: From; @@ -216,7 +217,7 @@ pub mod pallet { /// Configure the initial state of this pallet in the genesis block. #[pallet::genesis_config] - #[cfg_attr(feature = "std", derive(DefaultNoBound))] + #[derive(DefaultNoBound)] pub struct GenesisConfig { pub entered_until: Option, } @@ -233,6 +234,7 @@ pub mod pallet { /// A reason for the pallet placing a hold on funds. #[pallet::composite_enum] pub enum HoldReason { + /// Funds are held for entering or extending the safe-mode. #[codec(index = 0)] EnterOrExtend, } @@ -468,9 +470,13 @@ impl Pallet { ensure!(now > block.saturating_add(delay), Error::::CannotReleaseYet); } - let amount = - T::Currency::release(&&HoldReason::EnterOrExtend.into(), &account, amount, Precision::BestEffort) - .map_err(|_| Error::::CurrencyError)?; + let amount = T::Currency::release( + &&HoldReason::EnterOrExtend.into(), + &account, + amount, + Precision::BestEffort, + ) + .map_err(|_| Error::::CurrencyError)?; Self::deposit_event(Event::::StakeReleased { account, amount }); Ok(()) } diff --git a/frame/tx-pause/src/lib.rs b/frame/tx-pause/src/lib.rs index dd7671b5f7d8c..d499d47a17b92 100644 --- a/frame/tx-pause/src/lib.rs +++ b/frame/tx-pause/src/lib.rs @@ -120,7 +120,7 @@ pub mod pallet { /// Configure the initial state of this pallet in the genesis block. #[pallet::genesis_config] - #[cfg_attr(feature = "std", derive(DefaultNoBound))] + #[derive(DefaultNoBound)] pub struct GenesisConfig { /// The initially paused calls. pub paused: Vec>, From c56f1f91181660253c48fe89386ed167f888ac2e Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Mon, 5 Jun 2023 15:44:51 +0200 Subject: [PATCH 073/100] Fix CI Signed-off-by: Oliver Tale-Yazdi --- bin/node/runtime/src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 07ee79aed7f79..8985dcc036deb 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -64,7 +64,6 @@ use pallet_session::historical as pallet_session_historical; pub use pallet_transaction_payment::{CurrencyAdapter, Multiplier, TargetedFeeAdjustment}; use pallet_transaction_payment::{FeeDetails, RuntimeDispatchInfo}; use pallet_tx_pause::RuntimeCallNameOf; -use scale_info::TypeInfo; use sp_api::impl_runtime_apis; use sp_authority_discovery::AuthorityId as AuthorityDiscoveryId; use sp_consensus_grandpa::AuthorityId as GrandpaId; From 055d46580b4a84976092c0541767f67416e78c20 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Mon, 5 Jun 2023 15:45:05 +0200 Subject: [PATCH 074/100] Fix CI Signed-off-by: Oliver Tale-Yazdi --- frame/safe-mode/src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/frame/safe-mode/src/lib.rs b/frame/safe-mode/src/lib.rs index 0934384d451fe..3d02a2eb1e260 100644 --- a/frame/safe-mode/src/lib.rs +++ b/frame/safe-mode/src/lib.rs @@ -488,7 +488,6 @@ impl Pallet { ) -> Result<(), Error> { let amount = Stakes::::take(&account, block).ok_or(Error::::NoStake)?; - // FAIL-CI check these args let burned = T::Currency::burn_held( &&HoldReason::EnterOrExtend.into(), &account, From 7fbd556d9afebeced9792663bcbb6013c35e40ef Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Mon, 5 Jun 2023 16:04:53 +0200 Subject: [PATCH 075/100] Review Signed-off-by: Oliver Tale-Yazdi --- frame/safe-mode/Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/frame/safe-mode/Cargo.toml b/frame/safe-mode/Cargo.toml index c2126bbec3650..ffdc35766ec73 100644 --- a/frame/safe-mode/Cargo.toml +++ b/frame/safe-mode/Cargo.toml @@ -7,7 +7,6 @@ license = "Apache-2.0" homepage = "https://substrate.io" repository = "https://github.com/paritytech/substrate/" description = "FRAME safe-mode pallet" -readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] From c86894b3253316bee5004d4538221cc66f465e7e Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Mon, 5 Jun 2023 16:12:37 +0200 Subject: [PATCH 076/100] Rename Stake to Deposit Signed-off-by: Oliver Tale-Yazdi --- bin/node/runtime/src/lib.rs | 10 +- frame/safe-mode/src/benchmarking.rs | 40 +++---- frame/safe-mode/src/lib.rs | 116 +++++++++---------- frame/safe-mode/src/mock.rs | 12 +- frame/safe-mode/src/tests.rs | 165 +++++++++++++++------------- frame/safe-mode/src/weights.rs | 18 +-- frame/tx-pause/src/lib.rs | 1 + 7 files changed, 189 insertions(+), 173 deletions(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 8985dcc036deb..76f31b0a6c41a 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -256,9 +256,9 @@ impl pallet_tx_pause::Config for Runtime { parameter_types! { pub const EnterDuration: BlockNumber = 4 * HOURS; - pub const EnterStakeAmount: Balance = 2_000_000 * DOLLARS; + pub const EnterDepositAmount: Balance = 2_000_000 * DOLLARS; pub const ExtendDuration: BlockNumber = 2 * HOURS; - pub const ExtendStakeAmount: Balance = 1_000_000 * DOLLARS; + pub const ExtendDepositAmount: Balance = 1_000_000 * DOLLARS; pub const ReleaseDelay: u32 = 2 * DAYS; } @@ -268,13 +268,13 @@ impl pallet_safe_mode::Config for Runtime { type RuntimeHoldReason = RuntimeHoldReason; type WhitelistedCalls = SafeModeWhitelistedCalls; type EnterDuration = EnterDuration; - type EnterStakeAmount = EnterStakeAmount; + type EnterDepositAmount = EnterDepositAmount; type ExtendDuration = ExtendDuration; - type ExtendStakeAmount = ExtendStakeAmount; + type ExtendDepositAmount = ExtendDepositAmount; type ForceEnterOrigin = EnsureRootWithSuccess>; type ForceExtendOrigin = EnsureRootWithSuccess>; type ForceExitOrigin = EnsureRoot; - type ForceStakeOrigin = EnsureRoot; + type ForceDepositOrigin = EnsureRoot; type ReleaseDelay = ReleaseDelay; type WeightInfo = pallet_safe_mode::weights::SubstrateWeight; } diff --git a/frame/safe-mode/src/benchmarking.rs b/frame/safe-mode/src/benchmarking.rs index 2281a163ff2d2..1553733aaf04c 100644 --- a/frame/safe-mode/src/benchmarking.rs +++ b/frame/safe-mode/src/benchmarking.rs @@ -54,7 +54,7 @@ mod benchmarks { /// Permissionless enter - if configured. #[benchmark] fn enter() -> Result<(), BenchmarkError> { - T::EnterStakeAmount::get().ok_or_else(|| BenchmarkError::Weightless)?; + T::EnterDepositAmount::get().ok_or_else(|| BenchmarkError::Weightless)?; let caller: T::AccountId = whitelisted_caller(); let origin = RawOrigin::Signed(caller.clone()); @@ -88,7 +88,7 @@ mod benchmarks { /// Permissionless extend - if configured. #[benchmark] fn extend() -> Result<(), BenchmarkError> { - T::ExtendStakeAmount::get().ok_or_else(|| BenchmarkError::Weightless)?; + T::ExtendDepositAmount::get().ok_or_else(|| BenchmarkError::Weightless)?; let alice: T::AccountId = whitelisted_caller(); T::Currency::set_balance(&alice, init_bal::()); @@ -144,19 +144,19 @@ mod benchmarks { Ok(()) } - /// Permissionless release of a stake - if configured. + /// Permissionless release of a deposit - if configured. #[benchmark] - fn release_stake() -> Result<(), BenchmarkError> { + fn release_deposit() -> Result<(), BenchmarkError> { let delay = T::ReleaseDelay::get().ok_or_else(|| BenchmarkError::Weightless)?; let alice: T::AccountId = whitelisted_caller(); let origin = RawOrigin::Signed(alice.clone()); T::Currency::set_balance(&alice, init_bal::()); - // Mock the storage. This is needed in case the `EnterStakeAmount` is zero. + // Mock the storage. This is needed in case the `EnterDepositAmount` is zero. let block: T::BlockNumber = 1u32.into(); let bal: BalanceOf = 1u32.into(); - Stakes::::insert(&alice, &block, &bal); + Deposits::::insert(&alice, &block, &bal); T::Currency::hold(&HoldReason::EnterOrExtend.into(), &alice, bal)?; EnteredUntil::::put(&block); assert!(SafeMode::::do_exit(ExitReason::Force).is_ok()); @@ -168,24 +168,24 @@ mod benchmarks { #[extrinsic_call] _(origin, alice.clone(), 1u32.into()); - assert!(!Stakes::::contains_key(&alice, &block)); + assert!(!Deposits::::contains_key(&alice, &block)); assert_eq!(T::Currency::balance(&alice), init_bal::()); Ok(()) } - /// Forceful release of a stake - if configured. + /// Forceful release of a deposit - if configured. #[benchmark] - fn force_release_stake() -> Result<(), BenchmarkError> { - let force_origin = - T::ForceStakeOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; + fn force_release_deposit() -> Result<(), BenchmarkError> { + let force_origin = T::ForceDepositOrigin::try_successful_origin() + .map_err(|_| BenchmarkError::Weightless)?; let alice: T::AccountId = whitelisted_caller(); T::Currency::set_balance(&alice, init_bal::()); - // Mock the storage. This is needed in case the `EnterStakeAmount` is zero. + // Mock the storage. This is needed in case the `EnterDepositAmount` is zero. let block: T::BlockNumber = 1u32.into(); let bal: BalanceOf = 1u32.into(); - Stakes::::insert(&alice, &block, &bal); + Deposits::::insert(&alice, &block, &bal); T::Currency::hold(&&HoldReason::EnterOrExtend.into(), &alice, bal)?; EnteredUntil::::put(&block); @@ -199,23 +199,23 @@ mod benchmarks { #[extrinsic_call] _(force_origin as T::RuntimeOrigin, alice.clone(), block); - assert!(!Stakes::::contains_key(&alice, block)); + assert!(!Deposits::::contains_key(&alice, block)); assert_eq!(T::Currency::balance(&alice), init_bal::()); Ok(()) } #[benchmark] - fn force_slash_stake() -> Result<(), BenchmarkError> { - let force_origin = - T::ForceStakeOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?; + fn force_slash_deposit() -> Result<(), BenchmarkError> { + let force_origin = T::ForceDepositOrigin::try_successful_origin() + .map_err(|_| BenchmarkError::Weightless)?; let alice: T::AccountId = whitelisted_caller(); T::Currency::set_balance(&alice, init_bal::()); - // Mock the storage. This is needed in case the `EnterStakeAmount` is zero. + // Mock the storage. This is needed in case the `EnterDepositAmount` is zero. let block: T::BlockNumber = 1u32.into(); let bal: BalanceOf = 1u32.into(); - Stakes::::insert(&alice, &block, &bal); + Deposits::::insert(&alice, &block, &bal); T::Currency::hold(&&HoldReason::EnterOrExtend.into(), &alice, bal)?; EnteredUntil::::put(&block); assert!(SafeMode::::do_exit(ExitReason::Force).is_ok()); @@ -223,7 +223,7 @@ mod benchmarks { #[extrinsic_call] _(force_origin as T::RuntimeOrigin, alice.clone(), block); - assert!(!Stakes::::contains_key(&alice, block)); + assert!(!Deposits::::contains_key(&alice, block)); assert_eq!(T::Currency::balance(&alice), init_bal::() - 1u32.into()); Ok(()) } diff --git a/frame/safe-mode/src/lib.rs b/frame/safe-mode/src/lib.rs index 3d02a2eb1e260..968f79c8a25c3 100644 --- a/frame/safe-mode/src/lib.rs +++ b/frame/safe-mode/src/lib.rs @@ -16,6 +16,7 @@ // limitations under the License. #![cfg_attr(not(feature = "std"), no_std)] +#![deny(rustdoc::broken_intra_doc_links)] mod benchmarking; pub mod mock; @@ -59,7 +60,7 @@ pub mod pallet { /// The overarching event type. type RuntimeEvent: From> + IsType<::RuntimeEvent>; - /// Currency type for this pallet, used for Stakes. + /// Currency type for this pallet, used for Deposits. type Currency: FunHoldInspect + FunHoldMutate; @@ -87,13 +88,13 @@ pub mod pallet { /// /// `None` disallows permissionlessly enabling the safe-mode and is a sane default. #[pallet::constant] - type EnterStakeAmount: Get>>; + type EnterDepositAmount: Get>>; /// The amount that will be reserved upon calling [`Pallet::extend`]. /// /// `None` disallows permissionlessly extending the safe-mode and is a sane default. #[pallet::constant] - type ExtendStakeAmount: Get>>; + type ExtendDepositAmount: Get>>; /// The origin that may call [`Pallet::force_enter`]. /// @@ -108,16 +109,17 @@ pub mod pallet { /// The origin that may call [`Pallet::force_enter`]. type ForceExitOrigin: EnsureOrigin; - /// The only origin that can force to release or slash a stake. - type ForceStakeOrigin: EnsureOrigin; + /// The only origin that can force to release or slash a deposit. + type ForceDepositOrigin: EnsureOrigin; /// The minimal duration a deposit will remain reserved after safe-mode is entered or - /// extended, unless [`Pallet::force_release_stake`] is successfully called sooner. + /// extended, unless [`Pallet::force_release_deposit`] is successfully called sooner. /// - /// Every stake is tied to a specific activation or extension, thus each stake can be + /// Every deposit is tied to a specific activation or extension, thus each deposit can be /// released independently after the delay for it has passed. /// - /// `None` disallows permissionlessly releasing the safe-mode stakes and is a sane default. + /// `None` disallows permissionlessly releasing the safe-mode deposits and is a sane + /// default. #[pallet::constant] type ReleaseDelay: Get>; @@ -137,12 +139,12 @@ pub mod pallet { NotConfigured, /// There is no balance reserved. - NoStake, + NoDeposit, - /// The account already has a stake reserved and can therefore not enter or extend again. - AlreadyStaked, + /// The account already has a deposit reserved and can therefore not enter or extend again. + AlreadyDeposited, - /// This stake cannot be released yet. + /// This deposit cannot be released yet. CannotReleaseYet, /// An error from the underlying `Currency`. @@ -162,18 +164,18 @@ pub mod pallet { Exited { reason: ExitReason }, /// An account reserved funds for either entering or extending the safe-mode. - StakePlaced { account: T::AccountId, amount: BalanceOf }, + DepositPlaced { account: T::AccountId, amount: BalanceOf }, /// An account had a reserve released that was reserved. - StakeReleased { account: T::AccountId, amount: BalanceOf }, + DepositReleased { account: T::AccountId, amount: BalanceOf }, /// An account had reserve slashed that was reserved. - StakeSlashed { account: T::AccountId, amount: BalanceOf }, + DepositSlashed { account: T::AccountId, amount: BalanceOf }, /// Could not hold funds for entering or extending the safe-mode. /// /// This error comes from the underlying `Currency`. - CannotStake, + CannotDeposit, /// Could not release funds for entering or extending the safe-mode. /// @@ -201,11 +203,11 @@ pub mod pallet { /// Holds the reserve that was taken from an account at a specific block number. /// - /// This helps governance to have an overview of outstanding stakes that should be returned or + /// This helps governance to have an overview of outstanding deposits that should be returned or /// slashed. #[pallet::storage] #[pallet::getter(fn reserves)] - pub type Stakes = StorageDoubleMap< + pub type Deposits = StorageDoubleMap< _, Twox64Concat, T::AccountId, @@ -243,10 +245,10 @@ pub mod pallet { impl Pallet { /// Enter safe-mode permissionlessly for [`Config::EnterDuration`] blocks. /// - /// Reserves [`Config::EnterStakeAmount`] from the caller's account. + /// Reserves [`Config::EnterDepositAmount`] from the caller's account. /// Emits an [`Event::Entered`] event on success. /// Errors with [`Error::Entered`] if the safe-mode is already entered. - /// Errors with [`Error::NotConfigured`] if the stake amount is `None`. + /// Errors with [`Error::NotConfigured`] if the deposit amount is `None`. #[pallet::call_index(0)] #[pallet::weight(T::WeightInfo::enter())] pub fn enter(origin: OriginFor) -> DispatchResult { @@ -272,14 +274,14 @@ pub mod pallet { /// Extend the safe-mode permissionlessly for [`Config::ExtendDuration`] blocks. /// /// This accumulates on top of the current remaining duration. - /// Reserves [`Config::ExtendStakeAmount`] from the caller's account. + /// Reserves [`Config::ExtendDepositAmount`] from the caller's account. /// Emits an [`Event::Extended`] event on success. /// Errors with [`Error::Exited`] if the safe-mode is entered. - /// Errors with [`Error::NotConfigured`] if the stake amount is `None`. + /// Errors with [`Error::NotConfigured`] if the deposit amount is `None`. /// - /// This may be called by any signed origin with [`Config::ExtendStakeAmount`] free + /// This may be called by any signed origin with [`Config::ExtendDepositAmount`] free /// currency to reserve. This call can be disabled for all origins by configuring - /// [`Config::ExtendStakeAmount`] to `None`. + /// [`Config::ExtendDepositAmount`] to `None`. #[pallet::call_index(2)] #[pallet::weight(T::WeightInfo::extend())] pub fn extend(origin: OriginFor) -> DispatchResult { @@ -319,42 +321,42 @@ pub mod pallet { Self::do_exit(ExitReason::Force).map_err(Into::into) } - /// Slash a stake for an account that entered or extended safe-mode at a given + /// Slash a deposit for an account that entered or extended safe-mode at a given /// historical block. /// /// This can only be called while safe-mode is entered. /// - /// Emits a [`Event::StakeSlashed`] event on success. + /// Emits a [`Event::DepositSlashed`] event on success. /// Errors with [`Error::Entered`] if safe-mode is entered. /// - /// Can only be called by the [`Config::ForceStakeOrigin`] origin. + /// Can only be called by the [`Config::ForceDepositOrigin`] origin. #[pallet::call_index(5)] - #[pallet::weight(T::WeightInfo::force_slash_stake())] - pub fn force_slash_stake( + #[pallet::weight(T::WeightInfo::force_slash_deposit())] + pub fn force_slash_deposit( origin: OriginFor, account: T::AccountId, block: T::BlockNumber, ) -> DispatchResult { - T::ForceStakeOrigin::ensure_origin(origin)?; + T::ForceDepositOrigin::ensure_origin(origin)?; - Self::do_force_slash(account, block).map_err(Into::into) + Self::do_force_deposit(account, block).map_err(Into::into) } - /// Permissionlessly release a stake for an account that entered safe-mode at a + /// Permissionlessly release a deposit for an account that entered safe-mode at a /// given historical block. /// /// The call can be completely disabled by setting [`Config::ReleaseDelay`] to `None`. /// This cannot be called while safe-mode is entered and not until /// [`Config::ReleaseDelay`] blocks have passed since safe-mode was entered. /// - /// Emits a [`Event::StakeReleased`] event on success. + /// Emits a [`Event::DepositReleased`] event on success. /// Errors with [`Error::Entered`] if the safe-mode is entered. /// Errors with [`Error::CannotReleaseYet`] if [`Config::ReleaseDelay`] block have not - /// passed since safe-mode was entered. Errors with [`Error::NoStake`] if the payee has no + /// passed since safe-mode was entered. Errors with [`Error::NoDeposit`] if the payee has no /// reserved currency at the block specified. #[pallet::call_index(6)] - #[pallet::weight(T::WeightInfo::release_stake())] - pub fn release_stake( + #[pallet::weight(T::WeightInfo::release_deposit())] + pub fn release_deposit( origin: OriginFor, account: T::AccountId, block: T::BlockNumber, @@ -364,25 +366,25 @@ pub mod pallet { Self::do_release(false, account, block).map_err(Into::into) } - /// Force to release a stake for an account that entered safe-mode at a given + /// Force to release a deposit for an account that entered safe-mode at a given /// historical block. /// /// This can be called while safe-mode is still entered. /// - /// Emits a [`Event::StakeReleased`] event on success. + /// Emits a [`Event::DepositReleased`] event on success. /// Errors with [`Error::Entered`] if safe-mode is entered. - /// Errors with [`Error::NoStake`] if the payee has no reserved currency at the + /// Errors with [`Error::NoDeposit`] if the payee has no reserved currency at the /// specified block. /// - /// Can only be called by the [`Config::ForceStakeOrigin`] origin. + /// Can only be called by the [`Config::ForceDepositOrigin`] origin. #[pallet::call_index(7)] - #[pallet::weight(T::WeightInfo::force_release_stake())] - pub fn force_release_stake( + #[pallet::weight(T::WeightInfo::force_release_deposit())] + pub fn force_release_deposit( origin: OriginFor, account: T::AccountId, block: T::BlockNumber, ) -> DispatchResult { - T::ForceStakeOrigin::ensure_origin(origin)?; + T::ForceDepositOrigin::ensure_origin(origin)?; Self::do_release(true, account, block).map_err(Into::into) } @@ -416,7 +418,7 @@ impl Pallet { ensure!(!Self::is_entered(), Error::::Entered); if let Some(who) = who { - let amount = T::EnterStakeAmount::get().ok_or(Error::::NotConfigured)?; + let amount = T::EnterDepositAmount::get().ok_or(Error::::NotConfigured)?; Self::hold(who, amount)?; } @@ -434,7 +436,7 @@ impl Pallet { let mut until = EnteredUntil::::get().ok_or(Error::::Exited)?; if let Some(who) = who { - let amount = T::ExtendStakeAmount::get().ok_or(Error::::NotConfigured)?; + let amount = T::ExtendDepositAmount::get().ok_or(Error::::NotConfigured)?; Self::hold(who, amount)?; } @@ -453,14 +455,14 @@ impl Pallet { Ok(()) } - /// Logic for the [`crate::Pallet::release_stake`] and - /// [`crate::Pallet::force_release_stake`] calls. + /// Logic for the [`crate::Pallet::release_deposit`] and + /// [`crate::Pallet::force_release_deposit`] calls. pub(crate) fn do_release( force: bool, account: T::AccountId, block: T::BlockNumber, ) -> Result<(), Error> { - let amount = Stakes::::take(&account, &block).ok_or(Error::::NoStake)?; + let amount = Deposits::::take(&account, &block).ok_or(Error::::NoDeposit)?; if !force { ensure!(!Self::is_entered(), Error::::Entered); @@ -477,16 +479,16 @@ impl Pallet { Precision::BestEffort, ) .map_err(|_| Error::::CurrencyError)?; - Self::deposit_event(Event::::StakeReleased { account, amount }); + Self::deposit_event(Event::::DepositReleased { account, amount }); Ok(()) } - /// Logic for the [`crate::Pallet::slash_stake`] call. - pub(crate) fn do_force_slash( + /// Logic for the [`crate::Pallet::slash_deposit`] call. + pub(crate) fn do_force_deposit( account: T::AccountId, block: T::BlockNumber, ) -> Result<(), Error> { - let amount = Stakes::::take(&account, block).ok_or(Error::::NoStake)?; + let amount = Deposits::::take(&account, block).ok_or(Error::::NoDeposit)?; let burned = T::Currency::burn_held( &&HoldReason::EnterOrExtend.into(), @@ -497,23 +499,23 @@ impl Pallet { ) .map_err(|_| Error::::CurrencyError)?; defensive_assert!(burned == amount, "Could not burn the full held amount"); - Self::deposit_event(Event::::StakeSlashed { account, amount }); + Self::deposit_event(Event::::DepositSlashed { account, amount }); Ok(()) } - /// Place a hold for exactly `amount` and store it in `Stakes`. + /// Place a hold for exactly `amount` and store it in `Deposits`. /// /// Errors if the account already has a hold for the same reason. fn hold(who: T::AccountId, amount: BalanceOf) -> Result<(), Error> { let block = >::block_number(); if !T::Currency::balance_on_hold(&HoldReason::EnterOrExtend.into(), &who).is_zero() { - return Err(Error::::AlreadyStaked.into()) + return Err(Error::::AlreadyDeposited.into()) } T::Currency::hold(&HoldReason::EnterOrExtend.into(), &who, amount) .map_err(|_| Error::::CurrencyError)?; - Stakes::::insert(&who, block, amount); - Self::deposit_event(Event::::StakePlaced { account: who, amount }); + Deposits::::insert(&who, block, amount); + Self::deposit_event(Event::::DepositPlaced { account: who, amount }); Ok(()) } diff --git a/frame/safe-mode/src/mock.rs b/frame/safe-mode/src/mock.rs index ffb7b921279ec..5ac58a1c49015 100644 --- a/frame/safe-mode/src/mock.rs +++ b/frame/safe-mode/src/mock.rs @@ -162,8 +162,8 @@ impl Contains for WhitelistedCalls { parameter_types! { pub const EnterDuration: u64 = 7; pub const ExtendDuration: u64 = 30; - pub const EnterStakeAmount: u64 = 100; - pub const ExtendStakeAmount: u64 = 100; + pub const EnterDepositAmount: u64 = 100; + pub const ExtendDepositAmount: u64 = 100; pub const ReleaseDelay: u64 = 20; pub const SafeModeHoldReason: HoldReason = HoldReason::SafeMode; @@ -180,7 +180,7 @@ parameter_types! { frame_support::ord_parameter_types! { pub const ForceExitOrigin: u64 = 100; - pub const ForceStakeOrigin: u64 = 200; + pub const ForceDepositOrigin: u64 = 200; } impl Config for Test { @@ -189,13 +189,13 @@ impl Config for Test { type RuntimeHoldReason = RuntimeHoldReason; type WhitelistedCalls = WhitelistedCalls; type EnterDuration = EnterDuration; - type EnterStakeAmount = EnterStakeAmount; + type EnterDepositAmount = EnterDepositAmount; type ExtendDuration = ExtendDuration; - type ExtendStakeAmount = ExtendStakeAmount; + type ExtendDepositAmount = ExtendDepositAmount; type ForceEnterOrigin = EnsureSignedBy, u64>; type ForceExtendOrigin = EnsureSignedBy, u64>; type ForceExitOrigin = EnsureSignedBy; - type ForceStakeOrigin = EnsureSignedBy; + type ForceDepositOrigin = EnsureSignedBy; type ReleaseDelay = ReleaseDelay; type WeightInfo = (); } diff --git a/frame/safe-mode/src/tests.rs b/frame/safe-mode/src/tests.rs index f3e53ec02d187..edc1eefe8717a 100644 --- a/frame/safe-mode/src/tests.rs +++ b/frame/safe-mode/src/tests.rs @@ -68,8 +68,8 @@ fn fails_to_filter_calls_to_safe_mode_pallet() { frame_system::Error::::CallFiltered ); assert_ok!(SafeMode::force_exit(RuntimeOrigin::signed(mock::ForceExitOrigin::get()))); - assert_ok!(SafeMode::force_release_stake( - RuntimeOrigin::signed(mock::ForceStakeOrigin::get()), + assert_ok!(SafeMode::force_release_deposit( + RuntimeOrigin::signed(mock::ForceDepositOrigin::get()), 0, activated_at_block )); @@ -81,8 +81,8 @@ fn fails_to_filter_calls_to_safe_mode_pallet() { frame_system::Error::::CallFiltered ); assert_ok!(SafeMode::force_exit(RuntimeOrigin::signed(mock::ForceExitOrigin::get()))); - assert_ok!(SafeMode::force_slash_stake( - RuntimeOrigin::signed(mock::ForceStakeOrigin::get()), + assert_ok!(SafeMode::force_slash_deposit( + RuntimeOrigin::signed(mock::ForceDepositOrigin::get()), 0, activated_at_block + 2 )); @@ -106,44 +106,44 @@ fn fails_to_extend_if_not_activated() { } #[test] -fn fails_to_force_release_stakes_with_wrong_block() { +fn fails_to_force_release_deposits_with_wrong_block() { new_test_ext().execute_with(|| { let activated_at_block = System::block_number(); assert_ok!(SafeMode::enter(RuntimeOrigin::signed(0))); run_to(mock::EnterDuration::get() + activated_at_block + 1); assert_err!( - SafeMode::force_release_stake( - RuntimeOrigin::signed(mock::ForceStakeOrigin::get()), + SafeMode::force_release_deposit( + RuntimeOrigin::signed(mock::ForceDepositOrigin::get()), 0, activated_at_block + 1 ), - Error::::NoStake + Error::::NoDeposit ); assert_err!( - SafeMode::force_slash_stake( - RuntimeOrigin::signed(mock::ForceStakeOrigin::get()), + SafeMode::force_slash_deposit( + RuntimeOrigin::signed(mock::ForceDepositOrigin::get()), 0, activated_at_block + 1 ), - Error::::NoStake + Error::::NoDeposit ); }); } #[test] -fn fails_to_release_stakes_too_early() { +fn fails_to_release_deposits_too_early() { new_test_ext().execute_with(|| { let activated_at_block = System::block_number(); assert_ok!(SafeMode::enter(RuntimeOrigin::signed(0))); assert_ok!(SafeMode::force_exit(RuntimeOrigin::signed(mock::ForceExitOrigin::get()))); assert_err!( - SafeMode::release_stake(RuntimeOrigin::signed(2), 0, activated_at_block), + SafeMode::release_deposit(RuntimeOrigin::signed(2), 0, activated_at_block), Error::::CannotReleaseYet ); run_to(activated_at_block + mock::ReleaseDelay::get() + 1); - assert_ok!(SafeMode::release_stake(RuntimeOrigin::signed(2), 0, activated_at_block)); + assert_ok!(SafeMode::release_deposit(RuntimeOrigin::signed(2), 0, activated_at_block)); }); } @@ -221,10 +221,10 @@ fn can_activate() { EnteredUntil::::get().unwrap(), System::block_number() + mock::EnterDuration::get() ); - assert_eq!(Balances::reserved_balance(0), mock::EnterStakeAmount::get()); + assert_eq!(Balances::reserved_balance(0), mock::EnterDepositAmount::get()); assert_noop!(SafeMode::enter(RuntimeOrigin::signed(0)), Error::::Entered); - // Assert the stake. - assert_eq!(Stakes::::get(0, 1), Some(mock::EnterStakeAmount::get())); + // Assert the deposit. + assert_eq!(Deposits::::get(0, 1), Some(mock::EnterDepositAmount::get())); }); } @@ -232,12 +232,12 @@ fn can_activate() { fn cannot_extend() { new_test_ext().execute_with(|| { assert_ok!(SafeMode::enter(RuntimeOrigin::signed(0))); - assert_err!(SafeMode::extend(RuntimeOrigin::signed(0)), Error::::AlreadyStaked); + assert_err!(SafeMode::extend(RuntimeOrigin::signed(0)), Error::::AlreadyDeposited); assert_eq!( EnteredUntil::::get().unwrap(), System::block_number() + mock::EnterDuration::get() ); - assert_eq!(Balances::reserved_balance(0), mock::EnterStakeAmount::get()); + assert_eq!(Balances::reserved_balance(0), mock::EnterDepositAmount::get()); }); } @@ -251,11 +251,11 @@ fn fails_signed_origin_when_explicit_origin_required() { assert_err!(SafeMode::force_extend(RuntimeOrigin::signed(0)), DispatchError::BadOrigin); assert_err!(SafeMode::force_exit(RuntimeOrigin::signed(0)), DispatchError::BadOrigin); assert_err!( - SafeMode::force_slash_stake(RuntimeOrigin::signed(0), 0, activated_at_block), + SafeMode::force_slash_deposit(RuntimeOrigin::signed(0), 0, activated_at_block), DispatchError::BadOrigin ); assert_err!( - SafeMode::force_release_stake(RuntimeOrigin::signed(0), 0, activated_at_block), + SafeMode::force_release_deposit(RuntimeOrigin::signed(0), 0, activated_at_block), DispatchError::BadOrigin ); }); @@ -323,19 +323,19 @@ fn can_force_extend_with_config_origin() { } #[test] -fn can_force_release_stake_with_config_origin() { +fn can_force_release_deposit_with_config_origin() { new_test_ext().execute_with(|| { let activated_at_block = System::block_number(); assert_ok!(SafeMode::enter(RuntimeOrigin::signed(0))); - hypothetically_ok!(SafeMode::force_release_stake( - RuntimeOrigin::signed(mock::ForceStakeOrigin::get()), + hypothetically_ok!(SafeMode::force_release_deposit( + RuntimeOrigin::signed(mock::ForceDepositOrigin::get()), 0, activated_at_block ),); run_to(mock::EnterDuration::get() + activated_at_block + 1); - assert_ok!(SafeMode::force_release_stake( - RuntimeOrigin::signed(mock::ForceStakeOrigin::get()), + assert_ok!(SafeMode::force_release_deposit( + RuntimeOrigin::signed(mock::ForceDepositOrigin::get()), 0, activated_at_block )); @@ -352,8 +352,8 @@ fn can_force_release_stake_with_config_origin() { 1, ); - assert_ok!(SafeMode::force_release_stake( - RuntimeOrigin::signed(mock::ForceStakeOrigin::get()), + assert_ok!(SafeMode::force_release_deposit( + RuntimeOrigin::signed(mock::ForceDepositOrigin::get()), 0, activated_and_extended_at_block )); @@ -362,44 +362,44 @@ fn can_force_release_stake_with_config_origin() { } #[test] -fn can_release_stake_while_entered() { +fn can_release_deposit_while_entered() { new_test_ext().execute_with(|| { assert_eq!(System::block_number(), 1); assert_ok!(SafeMode::enter(RuntimeOrigin::signed(0))); assert!(SafeMode::is_entered()); - assert_eq!(Balances::free_balance(&0), BAL_ACC0 - mock::EnterStakeAmount::get()); + assert_eq!(Balances::free_balance(&0), BAL_ACC0 - mock::EnterDepositAmount::get()); // We could slash in the same block or any later. for i in 0..mock::EnterDuration::get() + 10 { run_to(i); - hypothetically_ok!(SafeMode::force_release_stake( - RuntimeOrigin::signed(mock::ForceStakeOrigin::get()), + hypothetically_ok!(SafeMode::force_release_deposit( + RuntimeOrigin::signed(mock::ForceDepositOrigin::get()), 0, 1 )); } // Now once we slash once - assert_ok!(SafeMode::force_release_stake( - RuntimeOrigin::signed(mock::ForceStakeOrigin::get()), + assert_ok!(SafeMode::force_release_deposit( + RuntimeOrigin::signed(mock::ForceDepositOrigin::get()), 0, 1 ),); assert_eq!(Balances::free_balance(&0), BAL_ACC0); // ... it wont work ever again. assert_err!( - SafeMode::force_release_stake( - RuntimeOrigin::signed(mock::ForceStakeOrigin::get()), + SafeMode::force_release_deposit( + RuntimeOrigin::signed(mock::ForceDepositOrigin::get()), 0, 1 ), - Error::::NoStake + Error::::NoDeposit ); }); } #[test] -fn can_slash_stake_while_entered() { +fn can_slash_deposit_while_entered() { new_test_ext().execute_with(|| { assert_eq!(System::block_number(), 1); assert_ok!(SafeMode::enter(RuntimeOrigin::signed(0))); @@ -408,78 +408,90 @@ fn can_slash_stake_while_entered() { // We could slash in the same block or any later. for i in 0..mock::EnterDuration::get() + 10 { run_to(i); - hypothetically_ok!(SafeMode::force_slash_stake( - RuntimeOrigin::signed(mock::ForceStakeOrigin::get()), + hypothetically_ok!(SafeMode::force_slash_deposit( + RuntimeOrigin::signed(mock::ForceDepositOrigin::get()), 0, 1 )); } // Now once we slash once - assert_ok!(SafeMode::force_slash_stake( - RuntimeOrigin::signed(mock::ForceStakeOrigin::get()), + assert_ok!(SafeMode::force_slash_deposit( + RuntimeOrigin::signed(mock::ForceDepositOrigin::get()), 0, 1 ),); // ... it wont work ever again. assert_err!( - SafeMode::force_slash_stake(RuntimeOrigin::signed(mock::ForceStakeOrigin::get()), 0, 1), - Error::::NoStake + SafeMode::force_slash_deposit( + RuntimeOrigin::signed(mock::ForceDepositOrigin::get()), + 0, + 1 + ), + Error::::NoDeposit ); }); } #[test] -fn can_slash_stake_from_extend_block() { +fn can_slash_deposit_from_extend_block() { new_test_ext().execute_with(|| { assert_ok!(SafeMode::enter(RuntimeOrigin::signed(0))); assert_ok!(SafeMode::extend(RuntimeOrigin::signed(1))); - assert_eq!(Balances::free_balance(&0), BAL_ACC0 - mock::EnterStakeAmount::get()); - assert_eq!(Balances::free_balance(&1), BAL_ACC1 - mock::ExtendStakeAmount::get()); + assert_eq!(Balances::free_balance(&0), BAL_ACC0 - mock::EnterDepositAmount::get()); + assert_eq!(Balances::free_balance(&1), BAL_ACC1 - mock::ExtendDepositAmount::get()); - assert_ok!(SafeMode::force_slash_stake( - RuntimeOrigin::signed(mock::ForceStakeOrigin::get()), + assert_ok!(SafeMode::force_slash_deposit( + RuntimeOrigin::signed(mock::ForceDepositOrigin::get()), 0, 1 ),); - assert_eq!(Balances::free_balance(&0), BAL_ACC0 - mock::EnterStakeAmount::get()); + assert_eq!(Balances::free_balance(&0), BAL_ACC0 - mock::EnterDepositAmount::get()); - assert_ok!(SafeMode::force_slash_stake( - RuntimeOrigin::signed(mock::ForceStakeOrigin::get()), + assert_ok!(SafeMode::force_slash_deposit( + RuntimeOrigin::signed(mock::ForceDepositOrigin::get()), 1, 1 ),); - assert_eq!(Balances::free_balance(&1), BAL_ACC1 - mock::ExtendStakeAmount::get()); + assert_eq!(Balances::free_balance(&1), BAL_ACC1 - mock::ExtendDepositAmount::get()); // But never again. assert_err!( - SafeMode::force_slash_stake(RuntimeOrigin::signed(mock::ForceStakeOrigin::get()), 0, 1), - Error::::NoStake + SafeMode::force_slash_deposit( + RuntimeOrigin::signed(mock::ForceDepositOrigin::get()), + 0, + 1 + ), + Error::::NoDeposit ); assert_err!( - SafeMode::force_slash_stake(RuntimeOrigin::signed(mock::ForceStakeOrigin::get()), 1, 1), - Error::::NoStake + SafeMode::force_slash_deposit( + RuntimeOrigin::signed(mock::ForceDepositOrigin::get()), + 1, + 1 + ), + Error::::NoDeposit ); }); } #[test] -fn can_slash_stake_with_config_origin() { +fn can_slash_deposit_with_config_origin() { new_test_ext().execute_with(|| { let activated_at_block = System::block_number(); assert_ok!(SafeMode::enter(RuntimeOrigin::signed(0))); - hypothetically_ok!(SafeMode::force_slash_stake( - RuntimeOrigin::signed(mock::ForceStakeOrigin::get()), + hypothetically_ok!(SafeMode::force_slash_deposit( + RuntimeOrigin::signed(mock::ForceDepositOrigin::get()), 0, activated_at_block ),); run_to(mock::EnterDuration::get() + activated_at_block + 1); - assert_ok!(SafeMode::force_slash_stake( - RuntimeOrigin::signed(mock::ForceStakeOrigin::get()), + assert_ok!(SafeMode::force_slash_deposit( + RuntimeOrigin::signed(mock::ForceDepositOrigin::get()), 0, activated_at_block )); - assert_eq!(Balances::free_balance(&0), BAL_ACC0 - mock::EnterStakeAmount::get()); // accounts set in mock genesis + assert_eq!(Balances::free_balance(&0), BAL_ACC0 - mock::EnterDepositAmount::get()); // accounts set in mock genesis Balances::make_free_balance_be(&0, BAL_ACC0); let activated_and_extended_at_block = System::block_number(); @@ -492,13 +504,14 @@ fn can_slash_stake_with_config_origin() { 1, ); - assert_ok!(SafeMode::force_slash_stake( - RuntimeOrigin::signed(mock::ForceStakeOrigin::get()), + assert_ok!(SafeMode::force_slash_deposit( + RuntimeOrigin::signed(mock::ForceDepositOrigin::get()), 0, activated_and_extended_at_block )); - assert_eq!(Balances::free_balance(&0), BAL_ACC0 - mock::EnterStakeAmount::get()); // accounts set in - // mock genesis + assert_eq!(Balances::free_balance(&0), BAL_ACC0 - mock::EnterDepositAmount::get()); // accounts set + // in + // mock genesis }); } @@ -511,22 +524,22 @@ fn fails_when_explicit_origin_required() { assert_err!(SafeMode::force_extend(signed(1)), DispatchError::BadOrigin); assert_err!(SafeMode::force_exit(signed(1)), DispatchError::BadOrigin); assert_err!( - SafeMode::force_slash_stake(signed(1), 0, activated_at_block), + SafeMode::force_slash_deposit(signed(1), 0, activated_at_block), DispatchError::BadOrigin ); assert_err!( - SafeMode::force_release_stake(signed(1), 0, activated_at_block), + SafeMode::force_release_deposit(signed(1), 0, activated_at_block), DispatchError::BadOrigin ); assert_err!(SafeMode::force_enter(signed(1)), DispatchError::BadOrigin); assert_err!(SafeMode::force_exit(signed(1)), DispatchError::BadOrigin); assert_err!( - SafeMode::force_slash_stake(signed(1), 0, activated_at_block), + SafeMode::force_slash_deposit(signed(1), 0, activated_at_block), DispatchError::BadOrigin ); assert_err!( - SafeMode::force_release_stake(signed(1), 0, activated_at_block), + SafeMode::force_release_deposit(signed(1), 0, activated_at_block), DispatchError::BadOrigin ); @@ -539,7 +552,7 @@ fn fails_when_explicit_origin_required() { DispatchError::BadOrigin ); assert_err!( - SafeMode::force_slash_stake( + SafeMode::force_slash_deposit( RuntimeOrigin::signed(mock::ForceExitOrigin::get()), 0, activated_at_block @@ -547,7 +560,7 @@ fn fails_when_explicit_origin_required() { DispatchError::BadOrigin ); assert_err!( - SafeMode::force_release_stake( + SafeMode::force_release_deposit( RuntimeOrigin::signed(mock::ForceExitOrigin::get()), 0, activated_at_block @@ -556,15 +569,15 @@ fn fails_when_explicit_origin_required() { ); assert_err!( - SafeMode::force_enter(RuntimeOrigin::signed(mock::ForceStakeOrigin::get())), + SafeMode::force_enter(RuntimeOrigin::signed(mock::ForceDepositOrigin::get())), DispatchError::BadOrigin ); assert_err!( - SafeMode::force_extend(RuntimeOrigin::signed(mock::ForceStakeOrigin::get())), + SafeMode::force_extend(RuntimeOrigin::signed(mock::ForceDepositOrigin::get())), DispatchError::BadOrigin ); assert_err!( - SafeMode::force_exit(RuntimeOrigin::signed(mock::ForceStakeOrigin::get())), + SafeMode::force_exit(RuntimeOrigin::signed(mock::ForceDepositOrigin::get())), DispatchError::BadOrigin ); }); diff --git a/frame/safe-mode/src/weights.rs b/frame/safe-mode/src/weights.rs index 533cba52ba9fc..1e75d6065d5f6 100644 --- a/frame/safe-mode/src/weights.rs +++ b/frame/safe-mode/src/weights.rs @@ -55,9 +55,9 @@ pub trait WeightInfo { fn extend() -> Weight; fn force_extend() -> Weight; fn force_exit() -> Weight; - fn release_stake() -> Weight; - fn force_release_stake() -> Weight; - fn force_slash_stake() -> Weight; + fn release_deposit() -> Weight; + fn force_release_deposit() -> Weight; + fn force_slash_deposit() -> Weight; } /// Weights for pallet_safe_mode using the Substrate node and recommended hardware. @@ -153,7 +153,7 @@ impl WeightInfo for SubstrateWeight { /// Proof: SafeMode EnteredUntil (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) /// Storage: Balances Reserves (r:1 w:1) /// Proof: Balances Reserves (max_values: None, max_size: Some(1049), added: 3524, mode: MaxEncodedLen) - fn release_stake() -> Weight { + fn release_deposit() -> Weight { // Proof Size summary in bytes: // Measured: `2080` // Estimated: `6566` @@ -166,7 +166,7 @@ impl WeightInfo for SubstrateWeight { /// Proof: SafeMode Reservations (max_values: None, max_size: Some(68), added: 2543, mode: MaxEncodedLen) /// Storage: Balances Reserves (r:1 w:1) /// Proof: Balances Reserves (max_values: None, max_size: Some(1049), added: 3524, mode: MaxEncodedLen) - fn force_release_stake() -> Weight { + fn force_release_deposit() -> Weight { // Proof Size summary in bytes: // Measured: `2080` // Estimated: `6067` @@ -179,7 +179,7 @@ impl WeightInfo for SubstrateWeight { /// Proof: SafeMode Reservations (max_values: None, max_size: Some(68), added: 2543, mode: MaxEncodedLen) /// Storage: Balances Reserves (r:1 w:1) /// Proof: Balances Reserves (max_values: None, max_size: Some(1049), added: 3524, mode: MaxEncodedLen) - fn force_slash_stake() -> Weight { + fn force_slash_deposit() -> Weight { // Proof Size summary in bytes: // Measured: `2083` // Estimated: `6067` @@ -282,7 +282,7 @@ impl WeightInfo for () { /// Proof: SafeMode EnteredUntil (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) /// Storage: Balances Reserves (r:1 w:1) /// Proof: Balances Reserves (max_values: None, max_size: Some(1049), added: 3524, mode: MaxEncodedLen) - fn release_stake() -> Weight { + fn release_deposit() -> Weight { // Proof Size summary in bytes: // Measured: `2080` // Estimated: `6566` @@ -295,7 +295,7 @@ impl WeightInfo for () { /// Proof: SafeMode Reservations (max_values: None, max_size: Some(68), added: 2543, mode: MaxEncodedLen) /// Storage: Balances Reserves (r:1 w:1) /// Proof: Balances Reserves (max_values: None, max_size: Some(1049), added: 3524, mode: MaxEncodedLen) - fn force_release_stake() -> Weight { + fn force_release_deposit() -> Weight { // Proof Size summary in bytes: // Measured: `2080` // Estimated: `6067` @@ -308,7 +308,7 @@ impl WeightInfo for () { /// Proof: SafeMode Reservations (max_values: None, max_size: Some(68), added: 2543, mode: MaxEncodedLen) /// Storage: Balances Reserves (r:1 w:1) /// Proof: Balances Reserves (max_values: None, max_size: Some(1049), added: 3524, mode: MaxEncodedLen) - fn force_slash_stake() -> Weight { + fn force_slash_deposit() -> Weight { // Proof Size summary in bytes: // Measured: `2083` // Estimated: `6067` diff --git a/frame/tx-pause/src/lib.rs b/frame/tx-pause/src/lib.rs index d499d47a17b92..d5e687d9708c8 100644 --- a/frame/tx-pause/src/lib.rs +++ b/frame/tx-pause/src/lib.rs @@ -16,6 +16,7 @@ // limitations under the License. #![cfg_attr(not(feature = "std"), no_std)] +#![deny(rustdoc::broken_intra_doc_links)] mod benchmarking; pub mod mock; From 77456adefd553f441545935f0a830e8c4445833b Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Mon, 5 Jun 2023 16:26:59 +0200 Subject: [PATCH 077/100] Add docs Signed-off-by: Oliver Tale-Yazdi --- frame/safe-mode/src/lib.rs | 1 - frame/system/src/lib.rs | 8 ++++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/frame/safe-mode/src/lib.rs b/frame/safe-mode/src/lib.rs index 968f79c8a25c3..539f04666c9e6 100644 --- a/frame/safe-mode/src/lib.rs +++ b/frame/safe-mode/src/lib.rs @@ -206,7 +206,6 @@ pub mod pallet { /// This helps governance to have an overview of outstanding deposits that should be returned or /// slashed. #[pallet::storage] - #[pallet::getter(fn reserves)] pub type Deposits = StorageDoubleMap< _, Twox64Concat, diff --git a/frame/system/src/lib.rs b/frame/system/src/lib.rs index 79cdcf22f55c7..b9e2796eb1b94 100644 --- a/frame/system/src/lib.rs +++ b/frame/system/src/lib.rs @@ -249,6 +249,14 @@ pub mod pallet { /// The basic call filter to use in Origin. All origins are built with this filter as base, /// except Root. + /// + /// This works as a filter for each incoming call. The call needs to pass this filter in + /// order to dispatch. Otherwise it will be rejected with `CallFiltered`. This can be + /// bypassed via `dispatch_bypass_filter` which should only be accessible by root. The + /// filter can be composed of sub-filters by nesting for example + /// [`frame_support::traits::InsideBoth`], [`frame_support::traits::TheseExcept`] or + /// [`frame_support::traits::EverythingBut`] et al. The default would be + /// [`frame_support::traits::Everything`]. #[pallet::no_default] type BaseCallFilter: Contains; From b80ff71c668dff7b3dc5b96895663c3248af2461 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Mon, 5 Jun 2023 16:46:30 +0200 Subject: [PATCH 078/100] Add Notify and test it Signed-off-by: Oliver Tale-Yazdi --- frame/safe-mode/src/lib.rs | 7 ++++++- frame/safe-mode/src/mock.rs | 22 ++++++++++++++++++++-- frame/safe-mode/src/tests.rs | 21 +++++++++++++++++++++ frame/support/src/traits.rs | 2 +- frame/support/src/traits/safe_mode.rs | 14 ++++++++++++++ 5 files changed, 62 insertions(+), 4 deletions(-) diff --git a/frame/safe-mode/src/lib.rs b/frame/safe-mode/src/lib.rs index 539f04666c9e6..d48454dfd0ccb 100644 --- a/frame/safe-mode/src/lib.rs +++ b/frame/safe-mode/src/lib.rs @@ -32,7 +32,7 @@ use frame_support::{ Inspect as FunInspect, }, tokens::{Fortitude, Precision}, - CallMetadata, Contains, Defensive, GetCallMetadata, PalletInfoAccess, + CallMetadata, Contains, Defensive, GetCallMetadata, PalletInfoAccess, SafeModeNotify, }, weights::Weight, DefaultNoBound, @@ -112,6 +112,9 @@ pub mod pallet { /// The only origin that can force to release or slash a deposit. type ForceDepositOrigin: EnsureOrigin; + /// Notifies external logic when the safe-mode is being entered or exited. + type Notify: SafeModeNotify; + /// The minimal duration a deposit will remain reserved after safe-mode is entered or /// extended, unless [`Pallet::force_release_deposit`] is successfully called sooner. /// @@ -424,6 +427,7 @@ impl Pallet { let until = >::block_number().saturating_add(duration); EnteredUntil::::put(until); Self::deposit_event(Event::Entered { until }); + T::Notify::entered(); Ok(()) } @@ -451,6 +455,7 @@ impl Pallet { pub(crate) fn do_exit(reason: ExitReason) -> Result<(), Error> { let _until = EnteredUntil::::take().ok_or(Error::::Exited)?; Self::deposit_event(Event::Exited { reason }); + T::Notify::exited(); Ok(()) } diff --git a/frame/safe-mode/src/mock.rs b/frame/safe-mode/src/mock.rs index 5ac58a1c49015..d31ad8c9f4e00 100644 --- a/frame/safe-mode/src/mock.rs +++ b/frame/safe-mode/src/mock.rs @@ -24,7 +24,7 @@ use crate as pallet_safe_mode; use frame_support::{ parameter_types, - traits::{ConstU64, Everything, InsideBoth, InstanceFilter, IsInVec}, + traits::{ConstU64, Everything, InsideBoth, InstanceFilter, IsInVec, SafeModeNotify}, }; use frame_system::EnsureSignedBy; use sp_core::H256; @@ -161,7 +161,7 @@ impl Contains for WhitelistedCalls { parameter_types! { pub const EnterDuration: u64 = 7; - pub const ExtendDuration: u64 = 30; + pub const ExtendDuration: u64 = 10; pub const EnterDepositAmount: u64 = 100; pub const ExtendDepositAmount: u64 = 100; pub const ReleaseDelay: u64 = 20; @@ -176,6 +176,23 @@ parameter_types! { // NOTE: The account ID maps to the duration. Easy for testing. pub ForceEnterOrigins: Vec = vec![ForceEnterWeak::get(), ForceEnterStrong::get()]; pub ForceExtendOrigins: Vec = vec![ForceExtendWeak::get(), ForceExtendStrong::get()]; + + pub storage Notifications: Vec<(u64, bool)> = vec![]; +} + +pub struct MockedNotify; +impl SafeModeNotify for MockedNotify { + fn entered() { + let mut ns = Notifications::get(); + ns.push((>::block_number(), true)); + Notifications::set(&ns); + } + + fn exited() { + let mut ns = Notifications::get(); + ns.push((>::block_number(), false)); + Notifications::set(&ns); + } } frame_support::ord_parameter_types! { @@ -197,6 +214,7 @@ impl Config for Test { type ForceExitOrigin = EnsureSignedBy; type ForceDepositOrigin = EnsureSignedBy; type ReleaseDelay = ReleaseDelay; + type Notify = MockedNotify; type WeightInfo = (); } diff --git a/frame/safe-mode/src/tests.rs b/frame/safe-mode/src/tests.rs index edc1eefe8717a..b99fb7501a83e 100644 --- a/frame/safe-mode/src/tests.rs +++ b/frame/safe-mode/src/tests.rs @@ -222,12 +222,28 @@ fn can_activate() { System::block_number() + mock::EnterDuration::get() ); assert_eq!(Balances::reserved_balance(0), mock::EnterDepositAmount::get()); + assert_eq!(Notifications::get(), vec![(1, true)]); assert_noop!(SafeMode::enter(RuntimeOrigin::signed(0)), Error::::Entered); + assert_eq!(Notifications::get(), vec![(1, true)]); // Assert the deposit. assert_eq!(Deposits::::get(0, 1), Some(mock::EnterDepositAmount::get())); }); } +#[test] +fn notify_works() { + new_test_ext().execute_with(|| { + assert_ok!(SafeMode::enter(RuntimeOrigin::signed(0))); + assert_eq!(Notifications::get(), vec![(1, true)]); + run_to(10); + assert_eq!(Notifications::get(), vec![(1, true), (9, false)]); + assert_ok!(SafeMode::enter(RuntimeOrigin::signed(1))); + assert_ok!(SafeMode::extend(RuntimeOrigin::signed(2))); + run_to(30); + assert_eq!(Notifications::get(), vec![(1, true), (9, false), (10, true), (28, false)]); + }); +} + #[test] fn cannot_extend() { new_test_ext().execute_with(|| { @@ -238,6 +254,7 @@ fn cannot_extend() { System::block_number() + mock::EnterDuration::get() ); assert_eq!(Balances::reserved_balance(0), mock::EnterDepositAmount::get()); + assert_eq!(Notifications::get(), vec![(1, true)]); }); } @@ -274,6 +291,7 @@ fn fails_force_deactivate_if_not_activated() { SafeMode::force_exit(RuntimeOrigin::signed(mock::ForceExitOrigin::get())), Error::::Exited ); + assert!(Notifications::get().is_empty()); }); } @@ -281,6 +299,7 @@ fn fails_force_deactivate_if_not_activated() { fn can_force_activate_with_config_origin() { new_test_ext().execute_with(|| { assert_ok!(SafeMode::force_enter(signed(ForceEnterStrong::get()))); + assert_eq!(Notifications::get(), vec![(1, true)]); assert_eq!( EnteredUntil::::get().unwrap(), System::block_number() + ForceEnterStrong::get() @@ -289,6 +308,7 @@ fn can_force_activate_with_config_origin() { SafeMode::force_enter(signed(ForceEnterStrong::get())), Error::::Entered ); + assert_eq!(Notifications::get(), vec![(1, true)]); }); } @@ -302,6 +322,7 @@ fn can_force_deactivate_with_config_origin() { ); assert_ok!(SafeMode::force_enter(signed(ForceEnterWeak::get()))); assert_ok!(SafeMode::force_exit(RuntimeOrigin::signed(ForceExitOrigin::get()))); + assert_eq!(Notifications::get(), vec![(1, true), (1, false)]); }); } diff --git a/frame/support/src/traits.rs b/frame/support/src/traits.rs index 0ea0c97ad780e..392e4ce6f8f0f 100644 --- a/frame/support/src/traits.rs +++ b/frame/support/src/traits.rs @@ -117,7 +117,7 @@ pub use messages::{ }; mod safe_mode; -pub use safe_mode::{SafeMode, SafeModeError}; +pub use safe_mode::{SafeMode, SafeModeError, SafeModeNotify}; mod tx_pause; pub use tx_pause::{TransactionPause, TransactionPauseError}; diff --git a/frame/support/src/traits/safe_mode.rs b/frame/support/src/traits/safe_mode.rs index b10b6c3c6faeb..d0be40aa26048 100644 --- a/frame/support/src/traits/safe_mode.rs +++ b/frame/support/src/traits/safe_mode.rs @@ -60,3 +60,17 @@ pub enum SafeModeError { /// Unknown error. Unknown, } + +/// A trait to notify when the runtime enters or exits safe mode. +pub trait SafeModeNotify { + /// Called when the runtime enters safe mode. + fn entered(); + + /// Called when the runtime exits safe mode. + fn exited(); +} + +impl SafeModeNotify for () { + fn entered() {} + fn exited() {} +} From 5ece51a6fd792d6c96cd15eb781651062f645177 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Mon, 5 Jun 2023 16:58:54 +0200 Subject: [PATCH 079/100] Fix kitchensink Signed-off-by: Oliver Tale-Yazdi --- bin/node/runtime/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 76f31b0a6c41a..652e1f1098672 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -276,6 +276,7 @@ impl pallet_safe_mode::Config for Runtime { type ForceExitOrigin = EnsureRoot; type ForceDepositOrigin = EnsureRoot; type ReleaseDelay = ReleaseDelay; + type Notify = (); type WeightInfo = pallet_safe_mode::weights::SubstrateWeight; } From 57dea706455fd0190a1c3b8698d59adfa964bac2 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Mon, 5 Jun 2023 18:51:07 +0200 Subject: [PATCH 080/100] Update frame/safe-mode/src/tests.rs Co-authored-by: Liam Aharon --- frame/safe-mode/src/tests.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/frame/safe-mode/src/tests.rs b/frame/safe-mode/src/tests.rs index b99fb7501a83e..d70be7ccb6633 100644 --- a/frame/safe-mode/src/tests.rs +++ b/frame/safe-mode/src/tests.rs @@ -530,9 +530,8 @@ fn can_slash_deposit_with_config_origin() { 0, activated_and_extended_at_block )); - assert_eq!(Balances::free_balance(&0), BAL_ACC0 - mock::EnterDepositAmount::get()); // accounts set - // in - // mock genesis + // accounts set in mock genesis + assert_eq!(Balances::free_balance(&0), BAL_ACC0 - mock::EnterDepositAmount::get()); }); } From 533bdc9f393f33563afdc5752eed53a7c0a357eb Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Mon, 5 Jun 2023 18:51:16 +0200 Subject: [PATCH 081/100] Update frame/safe-mode/src/tests.rs Co-authored-by: Liam Aharon --- frame/safe-mode/src/tests.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/frame/safe-mode/src/tests.rs b/frame/safe-mode/src/tests.rs index d70be7ccb6633..2af98489bfd8d 100644 --- a/frame/safe-mode/src/tests.rs +++ b/frame/safe-mode/src/tests.rs @@ -512,7 +512,8 @@ fn can_slash_deposit_with_config_origin() { 0, activated_at_block )); - assert_eq!(Balances::free_balance(&0), BAL_ACC0 - mock::EnterDepositAmount::get()); // accounts set in mock genesis + // accounts set in mock genesis + assert_eq!(Balances::free_balance(&0), BAL_ACC0 - mock::EnterDepositAmount::get()); Balances::make_free_balance_be(&0, BAL_ACC0); let activated_and_extended_at_block = System::block_number(); From 2abedb4de9a543637bdba7e2d75b24f1f5c5aaa8 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Mon, 5 Jun 2023 18:51:28 +0200 Subject: [PATCH 082/100] Update frame/support/src/traits/safe_mode.rs Co-authored-by: Liam Aharon --- frame/support/src/traits/safe_mode.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/support/src/traits/safe_mode.rs b/frame/support/src/traits/safe_mode.rs index d0be40aa26048..fad08fc6e4dbd 100644 --- a/frame/support/src/traits/safe_mode.rs +++ b/frame/support/src/traits/safe_mode.rs @@ -25,7 +25,7 @@ pub trait SafeMode { /// Block number type. type BlockNumber; - /// Whether the safe mode is entered. + /// Whether safe mode is entered. fn is_entered() -> bool { Self::remaining().is_some() } From bbde91438390846273400b2da8f19c69eba7865f Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Mon, 5 Jun 2023 18:51:38 +0200 Subject: [PATCH 083/100] Update frame/support/src/traits/safe_mode.rs Co-authored-by: Liam Aharon --- frame/support/src/traits/safe_mode.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/support/src/traits/safe_mode.rs b/frame/support/src/traits/safe_mode.rs index fad08fc6e4dbd..24a73e95a56f9 100644 --- a/frame/support/src/traits/safe_mode.rs +++ b/frame/support/src/traits/safe_mode.rs @@ -30,7 +30,7 @@ pub trait SafeMode { Self::remaining().is_some() } - /// How many more blocks the safe mode will stay entered. + /// How many more blocks safe mode will stay entered. /// /// If this returns `0`, then the safe mode will exit in the next block. fn remaining() -> Option; From e6a86461aae702eed67e1f6a244633f9268d2a66 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Mon, 5 Jun 2023 18:51:50 +0200 Subject: [PATCH 084/100] Update frame/support/src/traits/safe_mode.rs Co-authored-by: Liam Aharon --- frame/support/src/traits/safe_mode.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/support/src/traits/safe_mode.rs b/frame/support/src/traits/safe_mode.rs index 24a73e95a56f9..46ee9c262788a 100644 --- a/frame/support/src/traits/safe_mode.rs +++ b/frame/support/src/traits/safe_mode.rs @@ -32,7 +32,7 @@ pub trait SafeMode { /// How many more blocks safe mode will stay entered. /// - /// If this returns `0`, then the safe mode will exit in the next block. + /// If this returns `0`, then safe mode will exit in the next block. fn remaining() -> Option; /// Enter the safe mode for `duration` blocks. From 05917beb2c4030757e58f3a8ecad43652f3a7b52 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Mon, 5 Jun 2023 18:52:02 +0200 Subject: [PATCH 085/100] Update frame/support/src/traits/tx_pause.rs Co-authored-by: Liam Aharon --- frame/support/src/traits/tx_pause.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/support/src/traits/tx_pause.rs b/frame/support/src/traits/tx_pause.rs index 8593a7871c541..64d2f754f5235 100644 --- a/frame/support/src/traits/tx_pause.rs +++ b/frame/support/src/traits/tx_pause.rs @@ -32,7 +32,7 @@ pub trait TransactionPause { /// Whether this call can be paused. /// - /// This should hold for the current block and may change in the future. + /// This holds for the current block, but may change in the future. fn can_pause(call: Self::CallIdentifier) -> bool; /// Pause this call immediately. From 3de30412b5c032e1338bee94539f7befacbda045 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Mon, 5 Jun 2023 18:52:11 +0200 Subject: [PATCH 086/100] Update frame/tx-pause/src/lib.rs Co-authored-by: Liam Aharon --- frame/tx-pause/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/tx-pause/src/lib.rs b/frame/tx-pause/src/lib.rs index d5e687d9708c8..c66e195460404 100644 --- a/frame/tx-pause/src/lib.rs +++ b/frame/tx-pause/src/lib.rs @@ -123,7 +123,7 @@ pub mod pallet { #[pallet::genesis_config] #[derive(DefaultNoBound)] pub struct GenesisConfig { - /// The initially paused calls. + /// Initially paused calls. pub paused: Vec>, } From b024c1a73b4304797803237dcc64427ca6072ae7 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Mon, 5 Jun 2023 18:52:25 +0200 Subject: [PATCH 087/100] Update frame/tx-pause/src/lib.rs Co-authored-by: Liam Aharon --- frame/tx-pause/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/tx-pause/src/lib.rs b/frame/tx-pause/src/lib.rs index c66e195460404..8b9d21edf1efb 100644 --- a/frame/tx-pause/src/lib.rs +++ b/frame/tx-pause/src/lib.rs @@ -191,7 +191,7 @@ impl Pallet { >::contains_key(full_name) } - /// Same as [`Self::is_paused`] but for non-MEL-bound inputs. + /// Same as [`Self::is_paused`] but for inputs unbound by max-encoded-len. pub fn is_paused_unbound(pallet: Vec, call: Vec) -> bool { let pallet = PalletNameOf::::try_from(pallet); let call = PalletCallNameOf::::try_from(call); From bee80ed00e0629884a357e35f74ef46edc9de3ec Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Mon, 5 Jun 2023 18:52:37 +0200 Subject: [PATCH 088/100] Update frame/tx-pause/src/mock.rs Co-authored-by: Liam Aharon --- frame/tx-pause/src/mock.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/tx-pause/src/mock.rs b/frame/tx-pause/src/mock.rs index 418889879080b..67aeb5417ec69 100644 --- a/frame/tx-pause/src/mock.rs +++ b/frame/tx-pause/src/mock.rs @@ -90,7 +90,7 @@ impl pallet_utility::Config for Test { type WeightInfo = (); } -/// Mocked proxies to check that the tx-pause also works with the proxy pallet. +/// Mocked proxies to check that tx-pause also works with the proxy pallet. #[derive( Copy, Clone, From 55406a7b25b66b6ee7e4171af3300168c39a01a1 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Mon, 5 Jun 2023 18:54:08 +0200 Subject: [PATCH 089/100] Update frame/support/src/traits/safe_mode.rs Co-authored-by: Liam Aharon --- frame/support/src/traits/safe_mode.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/support/src/traits/safe_mode.rs b/frame/support/src/traits/safe_mode.rs index 46ee9c262788a..50d3f853ad33d 100644 --- a/frame/support/src/traits/safe_mode.rs +++ b/frame/support/src/traits/safe_mode.rs @@ -35,7 +35,7 @@ pub trait SafeMode { /// If this returns `0`, then safe mode will exit in the next block. fn remaining() -> Option; - /// Enter the safe mode for `duration` blocks. + /// Enter safe mode for `duration` blocks. /// /// Should error when already entered with `AlreadyEntered`. fn enter(duration: Self::BlockNumber) -> Result<(), SafeModeError>; From 78f541eba20bd9c3e9ffe076bf252cf05b9342b8 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Mon, 5 Jun 2023 19:40:36 +0200 Subject: [PATCH 090/100] Simplify code --- bin/node/runtime/src/lib.rs | 10 ++++------ frame/safe-mode/src/tests.rs | 4 ++-- frame/tx-pause/src/mock.rs | 11 ++++------- 3 files changed, 10 insertions(+), 15 deletions(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 652e1f1098672..0fe2ad79a9605 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -235,12 +235,10 @@ pub struct TxPauseWhitelistedCalls; /// Whitelist `Balances::transfer_keep_alive`, all others are pauseable. impl Contains> for TxPauseWhitelistedCalls { fn contains(full_name: &RuntimeCallNameOf) -> bool { - let unpausables: Vec> = vec![( - b"Balances".to_vec().try_into().unwrap(), - b"transfer_keep_alive".to_vec().try_into().unwrap(), - )]; - - unpausables.contains(full_name) + match (full_name.0.as_slice(), full_name.1.as_slice()) { + (b"Balances", b"transfer_keep_alive") => true, + _ => false, + } } } diff --git a/frame/safe-mode/src/tests.rs b/frame/safe-mode/src/tests.rs index 2af98489bfd8d..4ce9922d3b65c 100644 --- a/frame/safe-mode/src/tests.rs +++ b/frame/safe-mode/src/tests.rs @@ -512,7 +512,7 @@ fn can_slash_deposit_with_config_origin() { 0, activated_at_block )); - // accounts set in mock genesis + // accounts set in mock genesis assert_eq!(Balances::free_balance(&0), BAL_ACC0 - mock::EnterDepositAmount::get()); Balances::make_free_balance_be(&0, BAL_ACC0); @@ -531,7 +531,7 @@ fn can_slash_deposit_with_config_origin() { 0, activated_and_extended_at_block )); - // accounts set in mock genesis + // accounts set in mock genesis assert_eq!(Balances::free_balance(&0), BAL_ACC0 - mock::EnterDepositAmount::get()); }); } diff --git a/frame/tx-pause/src/mock.rs b/frame/tx-pause/src/mock.rs index 67aeb5417ec69..c22045858f3d5 100644 --- a/frame/tx-pause/src/mock.rs +++ b/frame/tx-pause/src/mock.rs @@ -156,16 +156,13 @@ frame_support::ord_parameter_types! { } /// Calls that are never allowed to be paused. -#[derive(Copy, Clone, Encode, Decode, RuntimeDebug, MaxEncodedLen, scale_info::TypeInfo)] pub struct WhitelistedCalls; impl Contains> for WhitelistedCalls { fn contains(full_name: &RuntimeCallNameOf) -> bool { - let unpausables: Vec> = vec![( - b"Balances".to_vec().try_into().unwrap(), - b"transfer_keep_alive".to_vec().try_into().unwrap(), - )]; - - unpausables.contains(full_name) + match (full_name.0.as_slice(), full_name.1.as_slice()) { + (b"Balances", b"transfer_keep_alive") => true, + _ => false, + } } } From 4643c384d7aedd6366b489018ee49ccd2bc3666d Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Mon, 5 Jun 2023 19:50:08 +0200 Subject: [PATCH 091/100] Update frame/support/src/traits/safe_mode.rs Co-authored-by: Liam Aharon --- frame/support/src/traits/safe_mode.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/support/src/traits/safe_mode.rs b/frame/support/src/traits/safe_mode.rs index 50d3f853ad33d..152b38b2d80c1 100644 --- a/frame/support/src/traits/safe_mode.rs +++ b/frame/support/src/traits/safe_mode.rs @@ -40,7 +40,7 @@ pub trait SafeMode { /// Should error when already entered with `AlreadyEntered`. fn enter(duration: Self::BlockNumber) -> Result<(), SafeModeError>; - /// Extend the safe mode for `duration` blocks. + /// Extend safe mode for `duration` blocks. /// /// Should error when not entered with `AlreadyExited`. fn extend(duration: Self::BlockNumber) -> Result<(), SafeModeError>; From f652595879b96cbaef8df72169eb50911b2302da Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Mon, 5 Jun 2023 19:50:17 +0200 Subject: [PATCH 092/100] Update frame/support/src/traits/safe_mode.rs Co-authored-by: Liam Aharon --- frame/support/src/traits/safe_mode.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frame/support/src/traits/safe_mode.rs b/frame/support/src/traits/safe_mode.rs index 152b38b2d80c1..40d287af970a1 100644 --- a/frame/support/src/traits/safe_mode.rs +++ b/frame/support/src/traits/safe_mode.rs @@ -45,7 +45,7 @@ pub trait SafeMode { /// Should error when not entered with `AlreadyExited`. fn extend(duration: Self::BlockNumber) -> Result<(), SafeModeError>; - /// Exit the safe mode immediately. + /// Exit safe mode immediately. /// /// This takes effect already in the same block. fn exit() -> Result<(), SafeModeError>; From 48481d92e3ef219e2ca23638360e3fc99bfed45e Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Mon, 5 Jun 2023 19:50:26 +0200 Subject: [PATCH 093/100] Update frame/support/src/traits/safe_mode.rs Co-authored-by: Liam Aharon --- frame/support/src/traits/safe_mode.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frame/support/src/traits/safe_mode.rs b/frame/support/src/traits/safe_mode.rs index 40d287af970a1..332e28d6e52a9 100644 --- a/frame/support/src/traits/safe_mode.rs +++ b/frame/support/src/traits/safe_mode.rs @@ -53,9 +53,9 @@ pub trait SafeMode { /// The error type for [`SafeMode`]. pub enum SafeModeError { - /// The safe mode is already entered. + /// Safe mode is already entered. AlreadyEntered, - /// The safe mode is already exited. + /// Safe mode is already exited. AlreadyExited, /// Unknown error. Unknown, From a287d99f73a881273ca172f274803d8a8ba14eb7 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Thu, 24 Aug 2023 22:09:34 +0200 Subject: [PATCH 094/100] Fixup merge Signed-off-by: Oliver Tale-Yazdi --- bin/node/runtime/Cargo.toml | 14 ++++---------- frame/safe-mode/Cargo.toml | 15 ++++++--------- frame/tx-pause/Cargo.toml | 13 +++++-------- 3 files changed, 15 insertions(+), 27 deletions(-) diff --git a/bin/node/runtime/Cargo.toml b/bin/node/runtime/Cargo.toml index 0019a92b70a55..ee1cada304c90 100644 --- a/bin/node/runtime/Cargo.toml +++ b/bin/node/runtime/Cargo.toml @@ -193,8 +193,6 @@ std = [ "pallet-nomination-pools/std", "pallet-offences-benchmarking?/std", "pallet-offences/std", - "pallet-tx-pause/std", - "pallet-safe-mode/std", "pallet-preimage/std", "pallet-proxy/std", "pallet-ranked-collective/std", @@ -202,6 +200,7 @@ std = [ "pallet-referenda/std", "pallet-remark/std", "pallet-root-testing/std", + "pallet-safe-mode/std", "pallet-salary/std", "pallet-scheduler/std", "pallet-session-benchmarking?/std", @@ -218,6 +217,7 @@ std = [ "pallet-transaction-payment/std", "pallet-transaction-storage/std", "pallet-treasury/std", + "pallet-tx-pause/std", "pallet-uniques/std", "pallet-utility/std", "pallet-vesting/std", @@ -291,7 +291,6 @@ runtime-benchmarks = [ "pallet-recovery/runtime-benchmarks", "pallet-referenda/runtime-benchmarks", "pallet-remark/runtime-benchmarks", - "pallet-tx-pause/runtime-benchmarks", "pallet-safe-mode/runtime-benchmarks", "pallet-salary/runtime-benchmarks", "pallet-scheduler/runtime-benchmarks", @@ -304,6 +303,7 @@ runtime-benchmarks = [ "pallet-tips/runtime-benchmarks", "pallet-transaction-storage/runtime-benchmarks", "pallet-treasury/runtime-benchmarks", + "pallet-tx-pause/runtime-benchmarks", "pallet-uniques/runtime-benchmarks", "pallet-utility/runtime-benchmarks", "pallet-vesting/runtime-benchmarks", @@ -373,16 +373,10 @@ try-runtime = [ "pallet-sudo/try-runtime", "pallet-timestamp/try-runtime", "pallet-tips/try-runtime", -<<<<<<< HEAD - "pallet-treasury/try-runtime", - "pallet-tx-pause/try-runtime", - "pallet-asset-rate/try-runtime", - "pallet-utility/try-runtime", -======= ->>>>>>> origin/master "pallet-transaction-payment/try-runtime", "pallet-transaction-storage/try-runtime", "pallet-treasury/try-runtime", + "pallet-tx-pause/try-runtime", "pallet-uniques/try-runtime", "pallet-utility/try-runtime", "pallet-vesting/try-runtime", diff --git a/frame/safe-mode/Cargo.toml b/frame/safe-mode/Cargo.toml index ffdc35766ec73..5e60b83b360aa 100644 --- a/frame/safe-mode/Cargo.toml +++ b/frame/safe-mode/Cargo.toml @@ -32,16 +32,16 @@ pallet-utility = { version = "4.0.0-dev", path = "../utility" } pallet-proxy = { version = "4.0.0-dev", path = "../proxy" } [features] -default = ["std"] +default = [ "std" ] std = [ "codec/std", - "scale-info/std", "frame-benchmarking/std", - "frame-system/std", "frame-support/std", + "frame-system/std", "pallet-balances?/std", - "pallet-utility?/std", "pallet-proxy?/std", + "pallet-utility?/std", + "scale-info/std", "sp-arithmetic/std", "sp-runtime/std", "sp-std/std", @@ -51,10 +51,7 @@ runtime-benchmarks = [ "frame-support/runtime-benchmarks", "frame-system/runtime-benchmarks", "pallet-balances/runtime-benchmarks", - "pallet-utility/runtime-benchmarks", "pallet-proxy/runtime-benchmarks", + "pallet-utility/runtime-benchmarks", ] -try-runtime = [ - "frame-support/try-runtime", - "frame-system/try-runtime", -] +try-runtime = [ "frame-support/try-runtime", "frame-system/try-runtime" ] diff --git a/frame/tx-pause/Cargo.toml b/frame/tx-pause/Cargo.toml index 332c4ca654690..d8b3ffe9b8df5 100644 --- a/frame/tx-pause/Cargo.toml +++ b/frame/tx-pause/Cargo.toml @@ -32,16 +32,16 @@ pallet-utility = { version = "4.0.0-dev", path = "../utility" } pallet-proxy = { version = "4.0.0-dev", path = "../proxy" } [features] -default = ["std"] +default = [ "std" ] std = [ "codec/std", - "scale-info/std", "frame-benchmarking/std", "frame-support/std", "frame-system/std", "pallet-balances?/std", - "pallet-utility?/std", "pallet-proxy?/std", + "pallet-utility?/std", + "scale-info/std", "sp-runtime/std", "sp-std/std", ] @@ -50,10 +50,7 @@ runtime-benchmarks = [ "frame-support/runtime-benchmarks", "frame-system/runtime-benchmarks", "pallet-balances/runtime-benchmarks", - "pallet-utility/runtime-benchmarks", "pallet-proxy/runtime-benchmarks", + "pallet-utility/runtime-benchmarks", ] -try-runtime = [ - "frame-support/try-runtime", - "frame-system/try-runtime", -] +try-runtime = [ "frame-support/try-runtime", "frame-system/try-runtime" ] From 80cf83497d4ffc53f2caaadb9fb5ca86c2ff4611 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Thu, 24 Aug 2023 22:22:47 +0200 Subject: [PATCH 095/100] Make stuff compile Signed-off-by: Oliver Tale-Yazdi --- bin/node/runtime/src/lib.rs | 6 ++-- frame/safe-mode/src/benchmarking.rs | 8 ++--- frame/safe-mode/src/lib.rs | 46 ++++++++++++++--------------- frame/safe-mode/src/mock.rs | 26 ++++++---------- frame/tx-pause/src/mock.rs | 5 +--- 5 files changed, 40 insertions(+), 51 deletions(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index ecb8f4c144687..494b1783b0be6 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -37,9 +37,9 @@ use frame_support::{ traits::{ fungible::ItemOf, tokens::{nonfungibles_v2::Inspect, GetSalary, PayFromAccount}, - AsEnsureOriginWithArg, ConstBool, ConstU128, ConstU16, ConstU32, Currency, EitherOfDiverse, Contains, - EqualPrivilegeOnly, Everything, Imbalance, InstanceFilter, KeyOwnerProofSystem, - LockIdentifier, Nothing, OnUnbalanced, WithdrawReasons, + AsEnsureOriginWithArg, ConstBool, ConstU128, ConstU16, ConstU32, Contains, Currency, + EitherOfDiverse, EqualPrivilegeOnly, Everything, Imbalance, InstanceFilter, + KeyOwnerProofSystem, LockIdentifier, Nothing, OnUnbalanced, WithdrawReasons, }, weights::{ constants::{ diff --git a/frame/safe-mode/src/benchmarking.rs b/frame/safe-mode/src/benchmarking.rs index 1553733aaf04c..566150e982afc 100644 --- a/frame/safe-mode/src/benchmarking.rs +++ b/frame/safe-mode/src/benchmarking.rs @@ -40,7 +40,7 @@ mod benchmarks { /// `on_initialize` exiting since the until block is in the past. #[benchmark] fn on_initialize_exit() { - EnteredUntil::::put(&T::BlockNumber::zero()); + EnteredUntil::::put(&BlockNumberFor::::zero()); assert!(SafeMode::::is_entered()); #[block] @@ -154,7 +154,7 @@ mod benchmarks { T::Currency::set_balance(&alice, init_bal::()); // Mock the storage. This is needed in case the `EnterDepositAmount` is zero. - let block: T::BlockNumber = 1u32.into(); + let block: BlockNumberFor = 1u32.into(); let bal: BalanceOf = 1u32.into(); Deposits::::insert(&alice, &block, &bal); T::Currency::hold(&HoldReason::EnterOrExtend.into(), &alice, bal)?; @@ -183,7 +183,7 @@ mod benchmarks { T::Currency::set_balance(&alice, init_bal::()); // Mock the storage. This is needed in case the `EnterDepositAmount` is zero. - let block: T::BlockNumber = 1u32.into(); + let block: BlockNumberFor = 1u32.into(); let bal: BalanceOf = 1u32.into(); Deposits::::insert(&alice, &block, &bal); T::Currency::hold(&&HoldReason::EnterOrExtend.into(), &alice, bal)?; @@ -213,7 +213,7 @@ mod benchmarks { T::Currency::set_balance(&alice, init_bal::()); // Mock the storage. This is needed in case the `EnterDepositAmount` is zero. - let block: T::BlockNumber = 1u32.into(); + let block: BlockNumberFor = 1u32.into(); let bal: BalanceOf = 1u32.into(); Deposits::::insert(&alice, &block, &bal); T::Currency::hold(&&HoldReason::EnterOrExtend.into(), &alice, bal)?; diff --git a/frame/safe-mode/src/lib.rs b/frame/safe-mode/src/lib.rs index d48454dfd0ccb..ff045b964afbb 100644 --- a/frame/safe-mode/src/lib.rs +++ b/frame/safe-mode/src/lib.rs @@ -76,13 +76,13 @@ pub mod pallet { /// For how many blocks the safe-mode will be entered by [`Pallet::enter`]. #[pallet::constant] - type EnterDuration: Get; + type EnterDuration: Get>; /// For how many blocks the safe-mode can be extended by each [`Pallet::extend`] call. /// /// This does not impose a hard limit as the safe-mode can be extended multiple times. #[pallet::constant] - type ExtendDuration: Get; + type ExtendDuration: Get>; /// The amount that will be reserved upon calling [`Pallet::enter`]. /// @@ -99,12 +99,12 @@ pub mod pallet { /// The origin that may call [`Pallet::force_enter`]. /// /// The `Success` value is the number of blocks that this origin can enter safe-mode for. - type ForceEnterOrigin: EnsureOrigin; + type ForceEnterOrigin: EnsureOrigin>; /// The origin that may call [`Pallet::force_extend`]. /// /// The `Success` value is the number of blocks that this origin can extend the safe-mode. - type ForceExtendOrigin: EnsureOrigin; + type ForceExtendOrigin: EnsureOrigin>; /// The origin that may call [`Pallet::force_enter`]. type ForceExitOrigin: EnsureOrigin; @@ -124,7 +124,7 @@ pub mod pallet { /// `None` disallows permissionlessly releasing the safe-mode deposits and is a sane /// default. #[pallet::constant] - type ReleaseDelay: Get>; + type ReleaseDelay: Get>>; // Weight information for extrinsics in this pallet. type WeightInfo: WeightInfo; @@ -158,10 +158,10 @@ pub mod pallet { #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event { /// The safe-mode was entered until inclusively this block. - Entered { until: T::BlockNumber }, + Entered { until: BlockNumberFor }, /// The safe-mode was extended until inclusively this block. - Extended { until: T::BlockNumber }, + Extended { until: BlockNumberFor }, /// Exited the safe-mode for a specific reason. Exited { reason: ExitReason }, @@ -202,7 +202,7 @@ pub mod pallet { /// /// Safe-mode is automatically exited when the current block number exceeds this value. #[pallet::storage] - pub type EnteredUntil = StorageValue<_, T::BlockNumber, OptionQuery>; + pub type EnteredUntil = StorageValue<_, BlockNumberFor, OptionQuery>; /// Holds the reserve that was taken from an account at a specific block number. /// @@ -214,7 +214,7 @@ pub mod pallet { Twox64Concat, T::AccountId, Twox64Concat, - T::BlockNumber, + BlockNumberFor, BalanceOf, OptionQuery, >; @@ -223,11 +223,11 @@ pub mod pallet { #[pallet::genesis_config] #[derive(DefaultNoBound)] pub struct GenesisConfig { - pub entered_until: Option, + pub entered_until: Option>, } #[pallet::genesis_build] - impl GenesisBuild for GenesisConfig { + impl BuildGenesisConfig for GenesisConfig { fn build(&self) { if let Some(block) = self.entered_until { EnteredUntil::::put(block); @@ -337,7 +337,7 @@ pub mod pallet { pub fn force_slash_deposit( origin: OriginFor, account: T::AccountId, - block: T::BlockNumber, + block: BlockNumberFor, ) -> DispatchResult { T::ForceDepositOrigin::ensure_origin(origin)?; @@ -361,7 +361,7 @@ pub mod pallet { pub fn release_deposit( origin: OriginFor, account: T::AccountId, - block: T::BlockNumber, + block: BlockNumberFor, ) -> DispatchResult { ensure_signed(origin)?; @@ -384,7 +384,7 @@ pub mod pallet { pub fn force_release_deposit( origin: OriginFor, account: T::AccountId, - block: T::BlockNumber, + block: BlockNumberFor, ) -> DispatchResult { T::ForceDepositOrigin::ensure_origin(origin)?; @@ -396,7 +396,7 @@ pub mod pallet { impl Hooks> for Pallet { /// Automatically exits safe-mode when the current block number is greater than /// [`EnteredUntil`]. - fn on_initialize(current: T::BlockNumber) -> Weight { + fn on_initialize(current: BlockNumberFor) -> Weight { let Some(limit) = EnteredUntil::::get() else { return T::WeightInfo::on_initialize_noop(); }; @@ -415,7 +415,7 @@ impl Pallet { /// Logic for the [`crate::Pallet::enter`] and [`crate::Pallet::force_enter`] calls. pub(crate) fn do_enter( who: Option, - duration: T::BlockNumber, + duration: BlockNumberFor, ) -> Result<(), Error> { ensure!(!Self::is_entered(), Error::::Entered); @@ -434,7 +434,7 @@ impl Pallet { /// Logic for the [`crate::Pallet::extend`] and [`crate::Pallet::force_extend`] calls. pub(crate) fn do_extend( who: Option, - duration: T::BlockNumber, + duration: BlockNumberFor, ) -> Result<(), Error> { let mut until = EnteredUntil::::get().ok_or(Error::::Exited)?; @@ -464,7 +464,7 @@ impl Pallet { pub(crate) fn do_release( force: bool, account: T::AccountId, - block: T::BlockNumber, + block: BlockNumberFor, ) -> Result<(), Error> { let amount = Deposits::::take(&account, &block).ok_or(Error::::NoDeposit)?; @@ -490,7 +490,7 @@ impl Pallet { /// Logic for the [`crate::Pallet::slash_deposit`] call. pub(crate) fn do_force_deposit( account: T::AccountId, - block: T::BlockNumber, + block: BlockNumberFor, ) -> Result<(), Error> { let amount = Deposits::::take(&account, block).ok_or(Error::::NoDeposit)?; @@ -559,24 +559,24 @@ where } impl frame_support::traits::SafeMode for Pallet { - type BlockNumber = T::BlockNumber; + type BlockNumber = BlockNumberFor; fn is_entered() -> bool { Self::is_entered() } - fn remaining() -> Option { + fn remaining() -> Option> { EnteredUntil::::get().map(|until| { let now = >::block_number(); until.saturating_sub(now) }) } - fn enter(duration: Self::BlockNumber) -> Result<(), frame_support::traits::SafeModeError> { + fn enter(duration: BlockNumberFor) -> Result<(), frame_support::traits::SafeModeError> { Self::do_enter(None, duration).map_err(Into::into) } - fn extend(duration: Self::BlockNumber) -> Result<(), frame_support::traits::SafeModeError> { + fn extend(duration: BlockNumberFor) -> Result<(), frame_support::traits::SafeModeError> { Self::do_extend(None, duration).map_err(Into::into) } diff --git a/frame/safe-mode/src/mock.rs b/frame/safe-mode/src/mock.rs index d31ad8c9f4e00..337b6076f84b9 100644 --- a/frame/safe-mode/src/mock.rs +++ b/frame/safe-mode/src/mock.rs @@ -29,8 +29,8 @@ use frame_support::{ use frame_system::EnsureSignedBy; use sp_core::H256; use sp_runtime::{ - testing::Header, traits::{BlakeTwo256, IdentityLookup}, + BuildStorage, }; impl frame_system::Config for Test { @@ -39,13 +39,12 @@ impl frame_system::Config for Test { type BlockLength = (); type RuntimeOrigin = RuntimeOrigin; type RuntimeCall = RuntimeCall; - type Index = u64; - type BlockNumber = u64; + type Nonce = u64; type Hash = H256; type Hashing = BlakeTwo256; type AccountId = u64; type Lookup = IdentityLookup; - type Header = Header; + type Block = Block; type RuntimeEvent = RuntimeEvent; type BlockHashCount = ConstU64<250>; type DbWeight = (); @@ -78,7 +77,7 @@ impl pallet_balances::Config for Test { type WeightInfo = (); type MaxLocks = (); type MaxReserves = ConstU32<10>; - type ReserveIdentifier = Self::BlockNumber; + type ReserveIdentifier = [u8; 8]; type RuntimeHoldReason = RuntimeHoldReason; type FreezeIdentifier = (); type MaxHolds = ConstU32<10>; @@ -218,14 +217,10 @@ impl Config for Test { type WeightInfo = (); } -type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; type Block = frame_system::mocking::MockBlock; frame_support::construct_runtime!( - pub enum Test where - Block = Block, - NodeBlock = Block, - UncheckedExtrinsic = UncheckedExtrinsic, + pub enum Test { System: frame_system, Balances: pallet_balances, @@ -239,7 +234,7 @@ pub const BAL_ACC0: u64 = 1234; pub const BAL_ACC1: u64 = 5678; pub fn new_test_ext() -> sp_io::TestExternalities { - let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap(); + let mut t = frame_system::GenesisConfig::::default().build_storage().unwrap(); pallet_balances::GenesisConfig:: { // The 0 account is NOT a special origin, the rest may be. @@ -247,12 +242,9 @@ pub fn new_test_ext() -> sp_io::TestExternalities { } .assimilate_storage(&mut t) .unwrap(); - - GenesisBuild::::assimilate_storage( - &pallet_safe_mode::GenesisConfig { entered_until: None }, - &mut t, - ) - .unwrap(); + pallet_safe_mode::GenesisConfig:: { entered_until: None } + .assimilate_storage(&mut t) + .unwrap(); let mut ext = sp_io::TestExternalities::new(t); ext.execute_with(|| { diff --git a/frame/tx-pause/src/mock.rs b/frame/tx-pause/src/mock.rs index c22045858f3d5..cea4380f88042 100644 --- a/frame/tx-pause/src/mock.rs +++ b/frame/tx-pause/src/mock.rs @@ -180,10 +180,7 @@ type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; type Block = frame_system::mocking::MockBlock; frame_support::construct_runtime!( - pub enum Test where - Block = Block, - NodeBlock = Block, - UncheckedExtrinsic = UncheckedExtrinsic, + pub enum Test { System: frame_system, Balances: pallet_balances, From f39ca0a4f30060ae30c8219081151625712f5f92 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Thu, 24 Aug 2023 22:27:23 +0200 Subject: [PATCH 096/100] Make tx-pause compile again Signed-off-by: Oliver Tale-Yazdi --- bin/node/runtime/src/lib.rs | 2 +- frame/tx-pause/src/lib.rs | 2 +- frame/tx-pause/src/mock.rs | 18 +++++++----------- 3 files changed, 9 insertions(+), 13 deletions(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 494b1783b0be6..80bd6480b3f8a 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -38,7 +38,7 @@ use frame_support::{ fungible::ItemOf, tokens::{nonfungibles_v2::Inspect, GetSalary, PayFromAccount}, AsEnsureOriginWithArg, ConstBool, ConstU128, ConstU16, ConstU32, Contains, Currency, - EitherOfDiverse, EqualPrivilegeOnly, Everything, Imbalance, InstanceFilter, + EitherOfDiverse, EqualPrivilegeOnly, Imbalance, InsideBoth, InstanceFilter, KeyOwnerProofSystem, LockIdentifier, Nothing, OnUnbalanced, WithdrawReasons, }, weights::{ diff --git a/frame/tx-pause/src/lib.rs b/frame/tx-pause/src/lib.rs index 8b9d21edf1efb..36147d32a2f0e 100644 --- a/frame/tx-pause/src/lib.rs +++ b/frame/tx-pause/src/lib.rs @@ -128,7 +128,7 @@ pub mod pallet { } #[pallet::genesis_build] - impl GenesisBuild for GenesisConfig { + impl BuildGenesisConfig for GenesisConfig { fn build(&self) { for call in &self.paused { Pallet::::ensure_can_pause(&call).expect("Genesis data is known good; qed"); diff --git a/frame/tx-pause/src/mock.rs b/frame/tx-pause/src/mock.rs index cea4380f88042..70c888f3c38da 100644 --- a/frame/tx-pause/src/mock.rs +++ b/frame/tx-pause/src/mock.rs @@ -29,8 +29,8 @@ use frame_support::{ use frame_system::EnsureSignedBy; use sp_core::H256; use sp_runtime::{ - testing::Header, traits::{BlakeTwo256, IdentityLookup}, + BuildStorage, }; parameter_types! { @@ -42,14 +42,13 @@ impl frame_system::Config for Test { type BlockLength = (); type RuntimeOrigin = RuntimeOrigin; type RuntimeCall = RuntimeCall; - type Index = u64; - type BlockNumber = u64; + type Nonce = u64; type Hash = H256; type Hashing = BlakeTwo256; type AccountId = u64; type Lookup = IdentityLookup; - type Header = Header; type RuntimeEvent = RuntimeEvent; + type Block = Block; type BlockHashCount = BlockHashCount; type DbWeight = (); type Version = (); @@ -176,7 +175,6 @@ impl Config for Test { type WeightInfo = (); } -type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; type Block = frame_system::mocking::MockBlock; frame_support::construct_runtime!( @@ -191,7 +189,7 @@ frame_support::construct_runtime!( ); pub fn new_test_ext() -> sp_io::TestExternalities { - let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap(); + let mut t = frame_system::GenesisConfig::::default().build_storage().unwrap(); pallet_balances::GenesisConfig:: { // The 0 account is NOT a special origin. The rest may be: @@ -200,11 +198,9 @@ pub fn new_test_ext() -> sp_io::TestExternalities { .assimilate_storage(&mut t) .unwrap(); - GenesisBuild::::assimilate_storage( - &pallet_tx_pause::GenesisConfig { paused: vec![] }, - &mut t, - ) - .unwrap(); + pallet_tx_pause::GenesisConfig:: { paused: vec![] } + .assimilate_storage(&mut t) + .unwrap(); let mut ext = sp_io::TestExternalities::new(t); ext.execute_with(|| { From 9259c64761ef303a966c676f9fd45e925aa3f306 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Thu, 24 Aug 2023 22:32:52 +0200 Subject: [PATCH 097/100] Fix features Signed-off-by: Oliver Tale-Yazdi --- frame/safe-mode/Cargo.toml | 9 ++++++++- frame/tx-pause/Cargo.toml | 9 ++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/frame/safe-mode/Cargo.toml b/frame/safe-mode/Cargo.toml index 5e60b83b360aa..e5adc6723a1e8 100644 --- a/frame/safe-mode/Cargo.toml +++ b/frame/safe-mode/Cargo.toml @@ -54,4 +54,11 @@ runtime-benchmarks = [ "pallet-proxy/runtime-benchmarks", "pallet-utility/runtime-benchmarks", ] -try-runtime = [ "frame-support/try-runtime", "frame-system/try-runtime" ] +try-runtime = [ + "frame-support/try-runtime", + "frame-system/try-runtime", + "pallet-balances?/try-runtime", + "pallet-proxy?/try-runtime", + "pallet-utility?/try-runtime", + "sp-runtime/try-runtime", +] diff --git a/frame/tx-pause/Cargo.toml b/frame/tx-pause/Cargo.toml index d8b3ffe9b8df5..7ff27624c999c 100644 --- a/frame/tx-pause/Cargo.toml +++ b/frame/tx-pause/Cargo.toml @@ -53,4 +53,11 @@ runtime-benchmarks = [ "pallet-proxy/runtime-benchmarks", "pallet-utility/runtime-benchmarks", ] -try-runtime = [ "frame-support/try-runtime", "frame-system/try-runtime" ] +try-runtime = [ + "frame-support/try-runtime", + "frame-system/try-runtime", + "pallet-balances?/try-runtime", + "pallet-proxy?/try-runtime", + "pallet-utility?/try-runtime", + "sp-runtime/try-runtime", +] From 3ed9996cc7de4f0ee70990e7a0a5f12cd8601137 Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Thu, 24 Aug 2023 22:48:53 +0200 Subject: [PATCH 098/100] Fix more features Signed-off-by: Oliver Tale-Yazdi --- frame/safe-mode/Cargo.toml | 3 +++ frame/tx-pause/Cargo.toml | 3 +++ 2 files changed, 6 insertions(+) diff --git a/frame/safe-mode/Cargo.toml b/frame/safe-mode/Cargo.toml index e5adc6723a1e8..ca04a3e1b93d3 100644 --- a/frame/safe-mode/Cargo.toml +++ b/frame/safe-mode/Cargo.toml @@ -43,6 +43,8 @@ std = [ "pallet-utility?/std", "scale-info/std", "sp-arithmetic/std", + "sp-core/std", + "sp-io/std", "sp-runtime/std", "sp-std/std", ] @@ -53,6 +55,7 @@ runtime-benchmarks = [ "pallet-balances/runtime-benchmarks", "pallet-proxy/runtime-benchmarks", "pallet-utility/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", ] try-runtime = [ "frame-support/try-runtime", diff --git a/frame/tx-pause/Cargo.toml b/frame/tx-pause/Cargo.toml index 7ff27624c999c..24ac55909ca91 100644 --- a/frame/tx-pause/Cargo.toml +++ b/frame/tx-pause/Cargo.toml @@ -42,6 +42,8 @@ std = [ "pallet-proxy?/std", "pallet-utility?/std", "scale-info/std", + "sp-core/std", + "sp-io/std", "sp-runtime/std", "sp-std/std", ] @@ -52,6 +54,7 @@ runtime-benchmarks = [ "pallet-balances/runtime-benchmarks", "pallet-proxy/runtime-benchmarks", "pallet-utility/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", ] try-runtime = [ "frame-support/try-runtime", From 022f9dac57861e49c7f4a444b6e8801563a8b45d Mon Sep 17 00:00:00 2001 From: command-bot <> Date: Thu, 24 Aug 2023 21:34:17 +0000 Subject: [PATCH 099/100] ".git/.scripts/commands/bench/bench.sh" --subcommand=pallet --runtime=dev --target_dir=substrate --pallet=pallet_safe_mode --- frame/safe-mode/src/weights.rs | 357 +++++++++++++++++---------------- 1 file changed, 179 insertions(+), 178 deletions(-) diff --git a/frame/safe-mode/src/weights.rs b/frame/safe-mode/src/weights.rs index 1e75d6065d5f6..f72bebcab9a4d 100644 --- a/frame/safe-mode/src/weights.rs +++ b/frame/safe-mode/src/weights.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2023 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,41 +15,42 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Autogenerated weights for pallet_safe_mode +//! Autogenerated weights for `pallet_safe_mode` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-03-01, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-08-24, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `i9`, CPU: `13th Gen Intel(R) Core(TM) i9-13900K` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 +//! HOSTNAME: `runner-aahe6cbd-project-145-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("dev")`, DB CACHE: `1024` // Executed Command: -// ./target/release/substrate +// target/production/substrate-node // benchmark // pallet -// --chain=dev // --steps=50 // --repeat=20 -// --pallet=pallet-safe-mode // --extrinsic=* -// --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --output=./frame/safe-mode/src/weights.rs +// --json-file=/builds/parity/mirrors/substrate/.git/.artifacts/bench.json +// --pallet=pallet_safe_mode +// --chain=dev // --header=./HEADER-APACHE2 -// --template=.maintain/frame-weight-template.hbs +// --output=./frame/safe-mode/src/weights.rs +// --template=./.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] #![allow(unused_imports)] +#![allow(missing_docs)] use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; -use sp_std::marker::PhantomData; +use core::marker::PhantomData; -/// Weight functions needed for pallet_safe_mode. +/// Weight functions needed for `pallet_safe_mode`. pub trait WeightInfo { - fn on_initialize_exit() -> Weight; fn on_initialize_noop() -> Weight; + fn on_initialize_exit() -> Weight; fn enter() -> Weight; fn force_enter() -> Weight; fn extend() -> Weight; @@ -60,260 +61,260 @@ pub trait WeightInfo { fn force_slash_deposit() -> Weight; } -/// Weights for pallet_safe_mode using the Substrate node and recommended hardware. +/// Weights for `pallet_safe_mode` using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { - /// Storage: SafeMode EnteredUntil (r:1 w:1) - /// Proof: SafeMode EnteredUntil (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - fn on_initialize_exit() -> Weight { + /// Storage: `SafeMode::EnteredUntil` (r:1 w:0) + /// Proof: `SafeMode::EnteredUntil` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + fn on_initialize_noop() -> Weight { // Proof Size summary in bytes: - // Measured: `1141` - // Estimated: `499` - // Minimum execution time: 10_678 nanoseconds. - Weight::from_parts(11_199_000, 499) + // Measured: `142` + // Estimated: `1489` + // Minimum execution time: 2_500_000 picoseconds. + Weight::from_parts(2_594_000, 1489) .saturating_add(T::DbWeight::get().reads(1_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: SafeMode EnteredUntil (r:1 w:0) - /// Proof: SafeMode EnteredUntil (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - fn on_initialize_noop() -> Weight { + /// Storage: `SafeMode::EnteredUntil` (r:1 w:1) + /// Proof: `SafeMode::EnteredUntil` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + fn on_initialize_exit() -> Weight { // Proof Size summary in bytes: - // Measured: `676` - // Estimated: `499` - // Minimum execution time: 2_574 nanoseconds. - Weight::from_parts(2_747_000, 499) + // Measured: `169` + // Estimated: `1489` + // Minimum execution time: 8_868_000 picoseconds. + Weight::from_parts(9_415_000, 1489) .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: SafeMode EnteredUntil (r:1 w:1) - /// Proof: SafeMode EnteredUntil (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Balances Reserves (r:1 w:1) - /// Proof: Balances Reserves (max_values: None, max_size: Some(1049), added: 3524, mode: MaxEncodedLen) - /// Storage: SafeMode Reservations (r:1 w:1) - /// Proof: SafeMode Reservations (max_values: None, max_size: Some(68), added: 2543, mode: MaxEncodedLen) + /// Storage: `SafeMode::EnteredUntil` (r:1 w:1) + /// Proof: `SafeMode::EnteredUntil` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(85), added: 2560, mode: `MaxEncodedLen`) + /// Storage: `SafeMode::Deposits` (r:0 w:1) + /// Proof: `SafeMode::Deposits` (`max_values`: None, `max_size`: Some(68), added: 2543, mode: `MaxEncodedLen`) fn enter() -> Weight { // Proof Size summary in bytes: - // Measured: `1895` - // Estimated: `6566` - // Minimum execution time: 29_221 nanoseconds. - Weight::from_parts(31_037_000, 6566) - .saturating_add(T::DbWeight::get().reads(3_u64)) + // Measured: `142` + // Estimated: `3550` + // Minimum execution time: 50_541_000 picoseconds. + Weight::from_parts(51_558_000, 3550) + .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } - /// Storage: SafeMode EnteredUntil (r:1 w:1) - /// Proof: SafeMode EnteredUntil (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: `SafeMode::EnteredUntil` (r:1 w:1) + /// Proof: `SafeMode::EnteredUntil` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) fn force_enter() -> Weight { // Proof Size summary in bytes: - // Measured: `1258` - // Estimated: `499` - // Minimum execution time: 12_753 nanoseconds. - Weight::from_parts(12_939_000, 499) + // Measured: `142` + // Estimated: `1489` + // Minimum execution time: 10_489_000 picoseconds. + Weight::from_parts(10_833_000, 1489) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: SafeMode EnteredUntil (r:1 w:1) - /// Proof: SafeMode EnteredUntil (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Balances Reserves (r:1 w:1) - /// Proof: Balances Reserves (max_values: None, max_size: Some(1049), added: 3524, mode: MaxEncodedLen) - /// Storage: SafeMode Reservations (r:1 w:1) - /// Proof: SafeMode Reservations (max_values: None, max_size: Some(68), added: 2543, mode: MaxEncodedLen) + /// Storage: `SafeMode::EnteredUntil` (r:1 w:1) + /// Proof: `SafeMode::EnteredUntil` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(85), added: 2560, mode: `MaxEncodedLen`) + /// Storage: `SafeMode::Deposits` (r:0 w:1) + /// Proof: `SafeMode::Deposits` (`max_values`: None, `max_size`: Some(68), added: 2543, mode: `MaxEncodedLen`) fn extend() -> Weight { // Proof Size summary in bytes: - // Measured: `2228` - // Estimated: `6566` - // Minimum execution time: 33_757 nanoseconds. - Weight::from_parts(35_096_000, 6566) - .saturating_add(T::DbWeight::get().reads(3_u64)) + // Measured: `169` + // Estimated: `3550` + // Minimum execution time: 50_818_000 picoseconds. + Weight::from_parts(51_873_000, 3550) + .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } - /// Storage: SafeMode EnteredUntil (r:1 w:1) - /// Proof: SafeMode EnteredUntil (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: `SafeMode::EnteredUntil` (r:1 w:1) + /// Proof: `SafeMode::EnteredUntil` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) fn force_extend() -> Weight { // Proof Size summary in bytes: - // Measured: `1444` - // Estimated: `499` - // Minimum execution time: 15_521 nanoseconds. - Weight::from_parts(16_387_000, 499) + // Measured: `169` + // Estimated: `1489` + // Minimum execution time: 10_843_000 picoseconds. + Weight::from_parts(11_314_000, 1489) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: SafeMode EnteredUntil (r:1 w:1) - /// Proof: SafeMode EnteredUntil (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: `SafeMode::EnteredUntil` (r:1 w:1) + /// Proof: `SafeMode::EnteredUntil` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) fn force_exit() -> Weight { // Proof Size summary in bytes: - // Measured: `1319` - // Estimated: `499` - // Minimum execution time: 13_464 nanoseconds. - Weight::from_parts(14_153_000, 499) + // Measured: `169` + // Estimated: `1489` + // Minimum execution time: 10_382_000 picoseconds. + Weight::from_parts(10_814_000, 1489) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } - /// Storage: SafeMode Reservations (r:1 w:1) - /// Proof: SafeMode Reservations (max_values: None, max_size: Some(68), added: 2543, mode: MaxEncodedLen) - /// Storage: SafeMode EnteredUntil (r:1 w:0) - /// Proof: SafeMode EnteredUntil (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Balances Reserves (r:1 w:1) - /// Proof: Balances Reserves (max_values: None, max_size: Some(1049), added: 3524, mode: MaxEncodedLen) + /// Storage: `SafeMode::Deposits` (r:1 w:1) + /// Proof: `SafeMode::Deposits` (`max_values`: None, `max_size`: Some(68), added: 2543, mode: `MaxEncodedLen`) + /// Storage: `SafeMode::EnteredUntil` (r:1 w:0) + /// Proof: `SafeMode::EnteredUntil` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(85), added: 2560, mode: `MaxEncodedLen`) fn release_deposit() -> Weight { // Proof Size summary in bytes: - // Measured: `2080` - // Estimated: `6566` - // Minimum execution time: 30_614 nanoseconds. - Weight::from_parts(32_080_000, 6566) + // Measured: `292` + // Estimated: `3550` + // Minimum execution time: 42_828_000 picoseconds. + Weight::from_parts(43_752_000, 3550) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } - /// Storage: SafeMode Reservations (r:1 w:1) - /// Proof: SafeMode Reservations (max_values: None, max_size: Some(68), added: 2543, mode: MaxEncodedLen) - /// Storage: Balances Reserves (r:1 w:1) - /// Proof: Balances Reserves (max_values: None, max_size: Some(1049), added: 3524, mode: MaxEncodedLen) + /// Storage: `SafeMode::Deposits` (r:1 w:1) + /// Proof: `SafeMode::Deposits` (`max_values`: None, `max_size`: Some(68), added: 2543, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(85), added: 2560, mode: `MaxEncodedLen`) fn force_release_deposit() -> Weight { // Proof Size summary in bytes: - // Measured: `2080` - // Estimated: `6067` - // Minimum execution time: 28_752 nanoseconds. - Weight::from_parts(30_362_000, 6067) + // Measured: `292` + // Estimated: `3550` + // Minimum execution time: 40_196_000 picoseconds. + Weight::from_parts(41_298_000, 3550) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } - /// Storage: SafeMode Reservations (r:1 w:1) - /// Proof: SafeMode Reservations (max_values: None, max_size: Some(68), added: 2543, mode: MaxEncodedLen) - /// Storage: Balances Reserves (r:1 w:1) - /// Proof: Balances Reserves (max_values: None, max_size: Some(1049), added: 3524, mode: MaxEncodedLen) + /// Storage: `SafeMode::Deposits` (r:1 w:1) + /// Proof: `SafeMode::Deposits` (`max_values`: None, `max_size`: Some(68), added: 2543, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(85), added: 2560, mode: `MaxEncodedLen`) fn force_slash_deposit() -> Weight { // Proof Size summary in bytes: - // Measured: `2083` - // Estimated: `6067` - // Minimum execution time: 33_964 nanoseconds. - Weight::from_parts(35_626_000, 6067) + // Measured: `292` + // Estimated: `3550` + // Minimum execution time: 33_660_000 picoseconds. + Weight::from_parts(34_426_000, 3550) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } } -// For backwards compatibility and tests +// For backwards compatibility and tests. impl WeightInfo for () { - /// Storage: SafeMode EnteredUntil (r:1 w:1) - /// Proof: SafeMode EnteredUntil (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - fn on_initialize_exit() -> Weight { + /// Storage: `SafeMode::EnteredUntil` (r:1 w:0) + /// Proof: `SafeMode::EnteredUntil` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + fn on_initialize_noop() -> Weight { // Proof Size summary in bytes: - // Measured: `1141` - // Estimated: `499` - // Minimum execution time: 10_678 nanoseconds. - Weight::from_parts(11_199_000, 499) + // Measured: `142` + // Estimated: `1489` + // Minimum execution time: 2_500_000 picoseconds. + Weight::from_parts(2_594_000, 1489) .saturating_add(RocksDbWeight::get().reads(1_u64)) - .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: SafeMode EnteredUntil (r:1 w:0) - /// Proof: SafeMode EnteredUntil (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - fn on_initialize_noop() -> Weight { + /// Storage: `SafeMode::EnteredUntil` (r:1 w:1) + /// Proof: `SafeMode::EnteredUntil` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + fn on_initialize_exit() -> Weight { // Proof Size summary in bytes: - // Measured: `676` - // Estimated: `499` - // Minimum execution time: 2_574 nanoseconds. - Weight::from_parts(2_747_000, 499) + // Measured: `169` + // Estimated: `1489` + // Minimum execution time: 8_868_000 picoseconds. + Weight::from_parts(9_415_000, 1489) .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: SafeMode EnteredUntil (r:1 w:1) - /// Proof: SafeMode EnteredUntil (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Balances Reserves (r:1 w:1) - /// Proof: Balances Reserves (max_values: None, max_size: Some(1049), added: 3524, mode: MaxEncodedLen) - /// Storage: SafeMode Reservations (r:1 w:1) - /// Proof: SafeMode Reservations (max_values: None, max_size: Some(68), added: 2543, mode: MaxEncodedLen) + /// Storage: `SafeMode::EnteredUntil` (r:1 w:1) + /// Proof: `SafeMode::EnteredUntil` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(85), added: 2560, mode: `MaxEncodedLen`) + /// Storage: `SafeMode::Deposits` (r:0 w:1) + /// Proof: `SafeMode::Deposits` (`max_values`: None, `max_size`: Some(68), added: 2543, mode: `MaxEncodedLen`) fn enter() -> Weight { // Proof Size summary in bytes: - // Measured: `1895` - // Estimated: `6566` - // Minimum execution time: 29_221 nanoseconds. - Weight::from_parts(31_037_000, 6566) - .saturating_add(RocksDbWeight::get().reads(3_u64)) + // Measured: `142` + // Estimated: `3550` + // Minimum execution time: 50_541_000 picoseconds. + Weight::from_parts(51_558_000, 3550) + .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } - /// Storage: SafeMode EnteredUntil (r:1 w:1) - /// Proof: SafeMode EnteredUntil (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: `SafeMode::EnteredUntil` (r:1 w:1) + /// Proof: `SafeMode::EnteredUntil` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) fn force_enter() -> Weight { // Proof Size summary in bytes: - // Measured: `1258` - // Estimated: `499` - // Minimum execution time: 12_753 nanoseconds. - Weight::from_parts(12_939_000, 499) + // Measured: `142` + // Estimated: `1489` + // Minimum execution time: 10_489_000 picoseconds. + Weight::from_parts(10_833_000, 1489) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: SafeMode EnteredUntil (r:1 w:1) - /// Proof: SafeMode EnteredUntil (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Balances Reserves (r:1 w:1) - /// Proof: Balances Reserves (max_values: None, max_size: Some(1049), added: 3524, mode: MaxEncodedLen) - /// Storage: SafeMode Reservations (r:1 w:1) - /// Proof: SafeMode Reservations (max_values: None, max_size: Some(68), added: 2543, mode: MaxEncodedLen) + /// Storage: `SafeMode::EnteredUntil` (r:1 w:1) + /// Proof: `SafeMode::EnteredUntil` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(85), added: 2560, mode: `MaxEncodedLen`) + /// Storage: `SafeMode::Deposits` (r:0 w:1) + /// Proof: `SafeMode::Deposits` (`max_values`: None, `max_size`: Some(68), added: 2543, mode: `MaxEncodedLen`) fn extend() -> Weight { // Proof Size summary in bytes: - // Measured: `2228` - // Estimated: `6566` - // Minimum execution time: 33_757 nanoseconds. - Weight::from_parts(35_096_000, 6566) - .saturating_add(RocksDbWeight::get().reads(3_u64)) + // Measured: `169` + // Estimated: `3550` + // Minimum execution time: 50_818_000 picoseconds. + Weight::from_parts(51_873_000, 3550) + .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } - /// Storage: SafeMode EnteredUntil (r:1 w:1) - /// Proof: SafeMode EnteredUntil (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: `SafeMode::EnteredUntil` (r:1 w:1) + /// Proof: `SafeMode::EnteredUntil` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) fn force_extend() -> Weight { // Proof Size summary in bytes: - // Measured: `1444` - // Estimated: `499` - // Minimum execution time: 15_521 nanoseconds. - Weight::from_parts(16_387_000, 499) + // Measured: `169` + // Estimated: `1489` + // Minimum execution time: 10_843_000 picoseconds. + Weight::from_parts(11_314_000, 1489) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: SafeMode EnteredUntil (r:1 w:1) - /// Proof: SafeMode EnteredUntil (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) + /// Storage: `SafeMode::EnteredUntil` (r:1 w:1) + /// Proof: `SafeMode::EnteredUntil` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) fn force_exit() -> Weight { // Proof Size summary in bytes: - // Measured: `1319` - // Estimated: `499` - // Minimum execution time: 13_464 nanoseconds. - Weight::from_parts(14_153_000, 499) + // Measured: `169` + // Estimated: `1489` + // Minimum execution time: 10_382_000 picoseconds. + Weight::from_parts(10_814_000, 1489) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } - /// Storage: SafeMode Reservations (r:1 w:1) - /// Proof: SafeMode Reservations (max_values: None, max_size: Some(68), added: 2543, mode: MaxEncodedLen) - /// Storage: SafeMode EnteredUntil (r:1 w:0) - /// Proof: SafeMode EnteredUntil (max_values: Some(1), max_size: Some(4), added: 499, mode: MaxEncodedLen) - /// Storage: Balances Reserves (r:1 w:1) - /// Proof: Balances Reserves (max_values: None, max_size: Some(1049), added: 3524, mode: MaxEncodedLen) + /// Storage: `SafeMode::Deposits` (r:1 w:1) + /// Proof: `SafeMode::Deposits` (`max_values`: None, `max_size`: Some(68), added: 2543, mode: `MaxEncodedLen`) + /// Storage: `SafeMode::EnteredUntil` (r:1 w:0) + /// Proof: `SafeMode::EnteredUntil` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(85), added: 2560, mode: `MaxEncodedLen`) fn release_deposit() -> Weight { // Proof Size summary in bytes: - // Measured: `2080` - // Estimated: `6566` - // Minimum execution time: 30_614 nanoseconds. - Weight::from_parts(32_080_000, 6566) + // Measured: `292` + // Estimated: `3550` + // Minimum execution time: 42_828_000 picoseconds. + Weight::from_parts(43_752_000, 3550) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } - /// Storage: SafeMode Reservations (r:1 w:1) - /// Proof: SafeMode Reservations (max_values: None, max_size: Some(68), added: 2543, mode: MaxEncodedLen) - /// Storage: Balances Reserves (r:1 w:1) - /// Proof: Balances Reserves (max_values: None, max_size: Some(1049), added: 3524, mode: MaxEncodedLen) + /// Storage: `SafeMode::Deposits` (r:1 w:1) + /// Proof: `SafeMode::Deposits` (`max_values`: None, `max_size`: Some(68), added: 2543, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(85), added: 2560, mode: `MaxEncodedLen`) fn force_release_deposit() -> Weight { // Proof Size summary in bytes: - // Measured: `2080` - // Estimated: `6067` - // Minimum execution time: 28_752 nanoseconds. - Weight::from_parts(30_362_000, 6067) + // Measured: `292` + // Estimated: `3550` + // Minimum execution time: 40_196_000 picoseconds. + Weight::from_parts(41_298_000, 3550) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } - /// Storage: SafeMode Reservations (r:1 w:1) - /// Proof: SafeMode Reservations (max_values: None, max_size: Some(68), added: 2543, mode: MaxEncodedLen) - /// Storage: Balances Reserves (r:1 w:1) - /// Proof: Balances Reserves (max_values: None, max_size: Some(1049), added: 3524, mode: MaxEncodedLen) + /// Storage: `SafeMode::Deposits` (r:1 w:1) + /// Proof: `SafeMode::Deposits` (`max_values`: None, `max_size`: Some(68), added: 2543, mode: `MaxEncodedLen`) + /// Storage: `Balances::Holds` (r:1 w:1) + /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(85), added: 2560, mode: `MaxEncodedLen`) fn force_slash_deposit() -> Weight { // Proof Size summary in bytes: - // Measured: `2083` - // Estimated: `6067` - // Minimum execution time: 33_964 nanoseconds. - Weight::from_parts(35_626_000, 6067) + // Measured: `292` + // Estimated: `3550` + // Minimum execution time: 33_660_000 picoseconds. + Weight::from_parts(34_426_000, 3550) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } From 93d9e44c92d8999a4e8ceb671986b11bc890c80c Mon Sep 17 00:00:00 2001 From: Oliver Tale-Yazdi Date: Thu, 24 Aug 2023 23:51:50 +0200 Subject: [PATCH 100/100] Update weights Signed-off-by: Oliver Tale-Yazdi --- frame/tx-pause/src/weights.rs | 104 +++++++++++++++++++--------------- 1 file changed, 59 insertions(+), 45 deletions(-) diff --git a/frame/tx-pause/src/weights.rs b/frame/tx-pause/src/weights.rs index aa6aa4de3160f..b733e64b159dc 100644 --- a/frame/tx-pause/src/weights.rs +++ b/frame/tx-pause/src/weights.rs @@ -1,13 +1,13 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -15,79 +15,93 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Autogenerated weights for pallet_tx_pause +//! Autogenerated weights for `pallet_tx_pause` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-10-15, STEPS: `1`, REPEAT: 1, LOW RANGE: `[]`, HIGH RANGE: `[]` -//! HOSTNAME: `pop-os`, CPU: `12th Gen Intel(R) Core(TM) i7-12700H` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 +//! DATE: 2023-08-24, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `runner-aahe6cbd-project-145-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("dev")`, DB CACHE: `1024` // Executed Command: -// ./target/release/substrate +// target/production/substrate-node // benchmark // pallet -// --steps -// 1 -// --repeat -// 1 -// --extrinsic -// * -// --execution -// wasm -// --wasm-execution -// compiled -// --heap-pages -// 4096 -// --pallet -// pallet_tx_pause -// --chain -// dev -// --output -// frame/tx-pause/src/weights.rs +// --steps=50 +// --repeat=20 +// --extrinsic=* +// --wasm-execution=compiled +// --heap-pages=4096 +// --json-file=/builds/parity/mirrors/substrate/.git/.artifacts/bench.json +// --pallet=pallet_tx_pause +// --chain=dev +// --header=./HEADER-APACHE2 +// --output=./frame/tx-pause/src/weights.rs // --template=./.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] #![allow(unused_imports)] +#![allow(missing_docs)] use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; -use sp_std::marker::PhantomData; +use core::marker::PhantomData; -/// Weight functions needed for pallet_tx_pause. +/// Weight functions needed for `pallet_tx_pause`. pub trait WeightInfo { fn pause() -> Weight; fn unpause() -> Weight; } -/// Weights for pallet_tx_pause using the Substrate node and recommended hardware. +/// Weights for `pallet_tx_pause` using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { - // Storage: TxPause PausedCalls (r:2 w:1) + /// Storage: `TxPause::PausedCalls` (r:1 w:1) + /// Proof: `TxPause::PausedCalls` (`max_values`: None, `max_size`: Some(532), added: 3007, mode: `MaxEncodedLen`) fn pause() -> Weight { - Weight::from_parts(61_745_000 as u64, 0) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `3` + // Estimated: `3997` + // Minimum execution time: 15_096_000 picoseconds. + Weight::from_parts(15_437_000, 3997) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) } - // Storage: TxPause PausedCalls (r:2 w:1) + /// Storage: `TxPause::PausedCalls` (r:1 w:1) + /// Proof: `TxPause::PausedCalls` (`max_values`: None, `max_size`: Some(532), added: 3007, mode: `MaxEncodedLen`) fn unpause() -> Weight { - Weight::from_parts(55_117_000 as u64, 0) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `565` + // Estimated: `3997` + // Minimum execution time: 21_546_000 picoseconds. + Weight::from_parts(22_178_000, 3997) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(1_u64)) } } -// For backwards compatibility and tests +// For backwards compatibility and tests. impl WeightInfo for () { - // Storage: TxPause PausedCalls (r:2 w:1) + /// Storage: `TxPause::PausedCalls` (r:1 w:1) + /// Proof: `TxPause::PausedCalls` (`max_values`: None, `max_size`: Some(532), added: 3007, mode: `MaxEncodedLen`) fn pause() -> Weight { - Weight::from_parts(61_745_000 as u64, 0) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `3` + // Estimated: `3997` + // Minimum execution time: 15_096_000 picoseconds. + Weight::from_parts(15_437_000, 3997) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) } - // Storage: TxPause PausedCalls (r:2 w:1) + /// Storage: `TxPause::PausedCalls` (r:1 w:1) + /// Proof: `TxPause::PausedCalls` (`max_values`: None, `max_size`: Some(532), added: 3007, mode: `MaxEncodedLen`) fn unpause() -> Weight { - Weight::from_parts(55_117_000 as u64, 0) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) + // Proof Size summary in bytes: + // Measured: `565` + // Estimated: `3997` + // Minimum execution time: 21_546_000 picoseconds. + Weight::from_parts(22_178_000, 3997) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(1_u64)) } }