mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-20 01:11:08 +00:00
Fix nostd build of several crates (#4060)
Preparation for https://github.com/paritytech/polkadot-sdk/pull/3935 Changes: - Add some `default-features = false` for the case that a crate and that dependency both support nostd builds. - Shuffle files around of some benchmarking-only crates. These conditionally disabled the `cfg_attr` for nostd and pulled in libstd. Example [here](https://github.com/ggwpez/zepter/pull/95). The actual logic is moved into a `inner.rs` to preserve nostd capability of the crate in case the benchmarking feature is disabled. - Add some `use sp_std::vec` where needed. - Remove some `optional = true` in cases where it was not optional. - Removed one superfluous `cfg_attr(not(feature = "std"), no_std..`. All in all this should be logical no-op. --------- Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>
This commit is contained in:
committed by
GitHub
parent
bfbf7f5d6f
commit
7a2c9d4a9a
@@ -0,0 +1,42 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
//! Benchmarking setup for pallet-session.
|
||||||
|
|
||||||
|
use sp_std::{prelude::*, vec};
|
||||||
|
|
||||||
|
use frame_benchmarking::{benchmarks, whitelisted_caller};
|
||||||
|
use frame_system::RawOrigin;
|
||||||
|
use pallet_session::*;
|
||||||
|
use parity_scale_codec::Decode;
|
||||||
|
pub struct Pallet<T: Config>(pallet_session::Pallet<T>);
|
||||||
|
pub trait Config: pallet_session::Config {}
|
||||||
|
|
||||||
|
benchmarks! {
|
||||||
|
set_keys {
|
||||||
|
let caller: T::AccountId = whitelisted_caller();
|
||||||
|
frame_system::Pallet::<T>::inc_providers(&caller);
|
||||||
|
let keys = T::Keys::decode(&mut sp_runtime::traits::TrailingZeroInput::zeroes()).unwrap();
|
||||||
|
let proof: Vec<u8> = vec![0,1,2,3];
|
||||||
|
}: _(RawOrigin::Signed(caller), keys, proof)
|
||||||
|
|
||||||
|
purge_keys {
|
||||||
|
let caller: T::AccountId = whitelisted_caller();
|
||||||
|
frame_system::Pallet::<T>::inc_providers(&caller);
|
||||||
|
let keys = T::Keys::decode(&mut sp_runtime::traits::TrailingZeroInput::zeroes()).unwrap();
|
||||||
|
let proof: Vec<u8> = vec![0,1,2,3];
|
||||||
|
let _t = pallet_session::Pallet::<T>::set_keys(RawOrigin::Signed(caller.clone()).into(), keys, proof);
|
||||||
|
}: _(RawOrigin::Signed(caller))
|
||||||
|
}
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
// This file is part of Substrate.
|
||||||
|
|
||||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
@@ -13,31 +15,13 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
//! Benchmarking setup for pallet-session
|
//! Benchmarks for the Session Pallet.
|
||||||
|
// This is separated into its own crate due to cyclic dependency issues.
|
||||||
|
|
||||||
#![cfg_attr(not(feature = "std"), no_std)]
|
#![cfg_attr(not(feature = "std"), no_std)]
|
||||||
#![cfg(feature = "runtime-benchmarks")]
|
|
||||||
use sp_std::{prelude::*, vec};
|
|
||||||
|
|
||||||
use frame_benchmarking::{benchmarks, whitelisted_caller};
|
#[cfg(feature = "runtime-benchmarks")]
|
||||||
use frame_system::RawOrigin;
|
pub mod inner;
|
||||||
use pallet_session::*;
|
|
||||||
use parity_scale_codec::Decode;
|
|
||||||
pub struct Pallet<T: Config>(pallet_session::Pallet<T>);
|
|
||||||
pub trait Config: pallet_session::Config {}
|
|
||||||
|
|
||||||
benchmarks! {
|
#[cfg(feature = "runtime-benchmarks")]
|
||||||
set_keys {
|
pub use inner::*;
|
||||||
let caller: T::AccountId = whitelisted_caller();
|
|
||||||
frame_system::Pallet::<T>::inc_providers(&caller);
|
|
||||||
let keys = T::Keys::decode(&mut sp_runtime::traits::TrailingZeroInput::zeroes()).unwrap();
|
|
||||||
let proof: Vec<u8> = vec![0,1,2,3];
|
|
||||||
}: _(RawOrigin::Signed(caller), keys, proof)
|
|
||||||
|
|
||||||
purge_keys {
|
|
||||||
let caller: T::AccountId = whitelisted_caller();
|
|
||||||
frame_system::Pallet::<T>::inc_providers(&caller);
|
|
||||||
let keys = T::Keys::decode(&mut sp_runtime::traits::TrailingZeroInput::zeroes()).unwrap();
|
|
||||||
let proof: Vec<u8> = vec![0,1,2,3];
|
|
||||||
let _t = pallet_session::Pallet::<T>::set_keys(RawOrigin::Signed(caller.clone()).into(), keys, proof);
|
|
||||||
}: _(RawOrigin::Signed(caller))
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ pallet-nfts = { path = "../../../../../substrate/frame/nfts", default-features =
|
|||||||
pallet-nfts-runtime-api = { path = "../../../../../substrate/frame/nfts/runtime-api", default-features = false }
|
pallet-nfts-runtime-api = { path = "../../../../../substrate/frame/nfts/runtime-api", default-features = false }
|
||||||
pallet-proxy = { path = "../../../../../substrate/frame/proxy", default-features = false }
|
pallet-proxy = { path = "../../../../../substrate/frame/proxy", default-features = false }
|
||||||
pallet-session = { path = "../../../../../substrate/frame/session", default-features = false }
|
pallet-session = { path = "../../../../../substrate/frame/session", default-features = false }
|
||||||
pallet-state-trie-migration = { path = "../../../../../substrate/frame/state-trie-migration", default-features = false, optional = true }
|
pallet-state-trie-migration = { path = "../../../../../substrate/frame/state-trie-migration", default-features = false }
|
||||||
pallet-timestamp = { path = "../../../../../substrate/frame/timestamp", default-features = false }
|
pallet-timestamp = { path = "../../../../../substrate/frame/timestamp", default-features = false }
|
||||||
pallet-transaction-payment = { path = "../../../../../substrate/frame/transaction-payment", default-features = false }
|
pallet-transaction-payment = { path = "../../../../../substrate/frame/transaction-payment", default-features = false }
|
||||||
pallet-transaction-payment-rpc-runtime-api = { path = "../../../../../substrate/frame/transaction-payment/rpc/runtime-api", default-features = false }
|
pallet-transaction-payment-rpc-runtime-api = { path = "../../../../../substrate/frame/transaction-payment/rpc/runtime-api", default-features = false }
|
||||||
@@ -102,14 +102,6 @@ substrate-wasm-builder = { path = "../../../../../substrate/utils/wasm-builder",
|
|||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["std"]
|
default = ["std"]
|
||||||
# When enabled the `state_version` is set to `1`.
|
|
||||||
# This means that the chain will start using the new state format. The migration is lazy, so
|
|
||||||
# it requires to write a storage value to use the new state format. To migrate all the other
|
|
||||||
# storage values that aren't touched the state migration pallet is added as well.
|
|
||||||
# This pallet will migrate the entire state, controlled through some account.
|
|
||||||
#
|
|
||||||
# This feature should be removed when the main-net will be migrated.
|
|
||||||
state-trie-version-1 = ["pallet-state-trie-migration"]
|
|
||||||
runtime-benchmarks = [
|
runtime-benchmarks = [
|
||||||
"assets-common/runtime-benchmarks",
|
"assets-common/runtime-benchmarks",
|
||||||
"cumulus-pallet-parachain-system/runtime-benchmarks",
|
"cumulus-pallet-parachain-system/runtime-benchmarks",
|
||||||
|
|||||||
@@ -107,7 +107,6 @@ impl_opaque_keys! {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "state-trie-version-1")]
|
|
||||||
#[sp_version::runtime_version]
|
#[sp_version::runtime_version]
|
||||||
pub const VERSION: RuntimeVersion = RuntimeVersion {
|
pub const VERSION: RuntimeVersion = RuntimeVersion {
|
||||||
spec_name: create_runtime_str!("statemine"),
|
spec_name: create_runtime_str!("statemine"),
|
||||||
@@ -120,19 +119,6 @@ pub const VERSION: RuntimeVersion = RuntimeVersion {
|
|||||||
state_version: 1,
|
state_version: 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(not(feature = "state-trie-version-1"))]
|
|
||||||
#[sp_version::runtime_version]
|
|
||||||
pub const VERSION: RuntimeVersion = RuntimeVersion {
|
|
||||||
spec_name: create_runtime_str!("statemine"),
|
|
||||||
impl_name: create_runtime_str!("statemine"),
|
|
||||||
authoring_version: 1,
|
|
||||||
spec_version: 1_010_000,
|
|
||||||
impl_version: 0,
|
|
||||||
apis: RUNTIME_API_VERSIONS,
|
|
||||||
transaction_version: 14,
|
|
||||||
state_version: 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// The version information used to identify this runtime when compiled natively.
|
/// The version information used to identify this runtime when compiled natively.
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
pub fn native_version() -> NativeVersion {
|
pub fn native_version() -> NativeVersion {
|
||||||
@@ -953,7 +939,6 @@ construct_runtime!(
|
|||||||
PoolAssets: pallet_assets::<Instance3> = 55,
|
PoolAssets: pallet_assets::<Instance3> = 55,
|
||||||
AssetConversion: pallet_asset_conversion = 56,
|
AssetConversion: pallet_asset_conversion = 56,
|
||||||
|
|
||||||
#[cfg(feature = "state-trie-version-1")]
|
|
||||||
StateTrieMigration: pallet_state_trie_migration = 70,
|
StateTrieMigration: pallet_state_trie_migration = 70,
|
||||||
|
|
||||||
// TODO: the pallet instance should be removed once all pools have migrated
|
// TODO: the pallet instance should be removed once all pools have migrated
|
||||||
@@ -1695,7 +1680,6 @@ cumulus_pallet_parachain_system::register_validate_block! {
|
|||||||
BlockExecutor = cumulus_pallet_aura_ext::BlockExecutor::<Runtime, Executive>,
|
BlockExecutor = cumulus_pallet_aura_ext::BlockExecutor::<Runtime, Executive>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "state-trie-version-1")]
|
|
||||||
parameter_types! {
|
parameter_types! {
|
||||||
// The deposit configuration for the singed migration. Specially if you want to allow any signed account to do the migration (see `SignedFilter`, these deposits should be high)
|
// The deposit configuration for the singed migration. Specially if you want to allow any signed account to do the migration (see `SignedFilter`, these deposits should be high)
|
||||||
pub const MigrationSignedDepositPerItem: Balance = CENTS;
|
pub const MigrationSignedDepositPerItem: Balance = CENTS;
|
||||||
@@ -1703,7 +1687,6 @@ parameter_types! {
|
|||||||
pub const MigrationMaxKeyLen: u32 = 512;
|
pub const MigrationMaxKeyLen: u32 = 512;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "state-trie-version-1")]
|
|
||||||
impl pallet_state_trie_migration::Config for Runtime {
|
impl pallet_state_trie_migration::Config for Runtime {
|
||||||
type RuntimeEvent = RuntimeEvent;
|
type RuntimeEvent = RuntimeEvent;
|
||||||
type Currency = Balances;
|
type Currency = Balances;
|
||||||
@@ -1721,13 +1704,11 @@ impl pallet_state_trie_migration::Config for Runtime {
|
|||||||
type MaxKeyLen = MigrationMaxKeyLen;
|
type MaxKeyLen = MigrationMaxKeyLen;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "state-trie-version-1")]
|
|
||||||
frame_support::ord_parameter_types! {
|
frame_support::ord_parameter_types! {
|
||||||
pub const MigController: AccountId = AccountId::from(hex_literal::hex!("8458ed39dc4b6f6c7255f7bc42be50c2967db126357c999d44e12ca7ac80dc52"));
|
pub const MigController: AccountId = AccountId::from(hex_literal::hex!("8458ed39dc4b6f6c7255f7bc42be50c2967db126357c999d44e12ca7ac80dc52"));
|
||||||
pub const RootMigController: AccountId = AccountId::from(hex_literal::hex!("8458ed39dc4b6f6c7255f7bc42be50c2967db126357c999d44e12ca7ac80dc52"));
|
pub const RootMigController: AccountId = AccountId::from(hex_literal::hex!("8458ed39dc4b6f6c7255f7bc42be50c2967db126357c999d44e12ca7ac80dc52"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "state-trie-version-1")]
|
|
||||||
#[test]
|
#[test]
|
||||||
fn ensure_key_ss58() {
|
fn ensure_key_ss58() {
|
||||||
use frame_support::traits::SortedMembers;
|
use frame_support::traits::SortedMembers;
|
||||||
|
|||||||
@@ -22,8 +22,8 @@ frame-system-rpc-runtime-api = { path = "../../../../../substrate/frame/system/r
|
|||||||
frame-system-benchmarking = { path = "../../../../../substrate/frame/system/benchmarking", default-features = false, optional = true }
|
frame-system-benchmarking = { path = "../../../../../substrate/frame/system/benchmarking", default-features = false, optional = true }
|
||||||
frame-try-runtime = { path = "../../../../../substrate/frame/try-runtime", default-features = false, optional = true }
|
frame-try-runtime = { path = "../../../../../substrate/frame/try-runtime", default-features = false, optional = true }
|
||||||
pallet-aura = { path = "../../../../../substrate/frame/aura", default-features = false }
|
pallet-aura = { path = "../../../../../substrate/frame/aura", default-features = false }
|
||||||
pallet-glutton = { path = "../../../../../substrate/frame/glutton", default-features = false, optional = true }
|
pallet-glutton = { path = "../../../../../substrate/frame/glutton", default-features = false }
|
||||||
pallet-sudo = { path = "../../../../../substrate/frame/sudo", default-features = false, optional = true }
|
pallet-sudo = { path = "../../../../../substrate/frame/sudo", default-features = false }
|
||||||
pallet-timestamp = { path = "../../../../../substrate/frame/timestamp", default-features = false }
|
pallet-timestamp = { path = "../../../../../substrate/frame/timestamp", default-features = false }
|
||||||
sp-api = { path = "../../../../../substrate/primitives/api", default-features = false }
|
sp-api = { path = "../../../../../substrate/primitives/api", default-features = false }
|
||||||
sp-block-builder = { path = "../../../../../substrate/primitives/block-builder", default-features = false }
|
sp-block-builder = { path = "../../../../../substrate/primitives/block-builder", default-features = false }
|
||||||
|
|||||||
@@ -17,8 +17,8 @@ scale-info = { version = "2.11.1", default-features = false, features = ["derive
|
|||||||
# Substrate
|
# Substrate
|
||||||
sp-core = { path = "../../../substrate/primitives/core", default-features = false }
|
sp-core = { path = "../../../substrate/primitives/core", default-features = false }
|
||||||
sp-inherents = { path = "../../../substrate/primitives/inherents", default-features = false }
|
sp-inherents = { path = "../../../substrate/primitives/inherents", default-features = false }
|
||||||
sp-runtime = { path = "../../../substrate/primitives/runtime", optional = true }
|
sp-runtime = { path = "../../../substrate/primitives/runtime", optional = true, default-features = false }
|
||||||
sp-state-machine = { path = "../../../substrate/primitives/state-machine", optional = true }
|
sp-state-machine = { path = "../../../substrate/primitives/state-machine", optional = true, default-features = false }
|
||||||
sp-std = { path = "../../../substrate/primitives/std", default-features = false }
|
sp-std = { path = "../../../substrate/primitives/std", default-features = false }
|
||||||
sp-trie = { path = "../../../substrate/primitives/trie", default-features = false }
|
sp-trie = { path = "../../../substrate/primitives/trie", default-features = false }
|
||||||
|
|
||||||
@@ -34,6 +34,8 @@ std = [
|
|||||||
"scale-info/std",
|
"scale-info/std",
|
||||||
"sp-core/std",
|
"sp-core/std",
|
||||||
"sp-inherents/std",
|
"sp-inherents/std",
|
||||||
|
"sp-runtime?/std",
|
||||||
|
"sp-state-machine?/std",
|
||||||
"sp-std/std",
|
"sp-std/std",
|
||||||
"sp-trie/std",
|
"sp-trie/std",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ sp-arithmetic = { path = "../../substrate/primitives/arithmetic", default-featur
|
|||||||
sp-authority-discovery = { path = "../../substrate/primitives/authority-discovery", default-features = false, features = ["serde"] }
|
sp-authority-discovery = { path = "../../substrate/primitives/authority-discovery", default-features = false, features = ["serde"] }
|
||||||
sp-consensus-slots = { path = "../../substrate/primitives/consensus/slots", default-features = false, features = ["serde"] }
|
sp-consensus-slots = { path = "../../substrate/primitives/consensus/slots", default-features = false, features = ["serde"] }
|
||||||
sp-io = { path = "../../substrate/primitives/io", default-features = false }
|
sp-io = { path = "../../substrate/primitives/io", default-features = false }
|
||||||
sp-keystore = { path = "../../substrate/primitives/keystore", optional = true }
|
sp-keystore = { path = "../../substrate/primitives/keystore", optional = true, default-features = false }
|
||||||
sp-staking = { path = "../../substrate/primitives/staking", default-features = false, features = ["serde"] }
|
sp-staking = { path = "../../substrate/primitives/staking", default-features = false, features = ["serde"] }
|
||||||
sp-std = { package = "sp-std", path = "../../substrate/primitives/std", default-features = false }
|
sp-std = { package = "sp-std", path = "../../substrate/primitives/std", default-features = false }
|
||||||
|
|
||||||
@@ -53,6 +53,7 @@ std = [
|
|||||||
"sp-consensus-slots/std",
|
"sp-consensus-slots/std",
|
||||||
"sp-io/std",
|
"sp-io/std",
|
||||||
"sp-keystore",
|
"sp-keystore",
|
||||||
|
"sp-keystore?/std",
|
||||||
"sp-staking/std",
|
"sp-staking/std",
|
||||||
"sp-std/std",
|
"sp-std/std",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ sp-runtime = { path = "../../../substrate/primitives/runtime", default-features
|
|||||||
sp-session = { path = "../../../substrate/primitives/session", default-features = false }
|
sp-session = { path = "../../../substrate/primitives/session", default-features = false }
|
||||||
sp-staking = { path = "../../../substrate/primitives/staking", default-features = false, features = ["serde"] }
|
sp-staking = { path = "../../../substrate/primitives/staking", default-features = false, features = ["serde"] }
|
||||||
sp-core = { path = "../../../substrate/primitives/core", default-features = false, features = ["serde"] }
|
sp-core = { path = "../../../substrate/primitives/core", default-features = false, features = ["serde"] }
|
||||||
sp-keystore = { path = "../../../substrate/primitives/keystore", optional = true }
|
sp-keystore = { path = "../../../substrate/primitives/keystore", optional = true, default-features = false }
|
||||||
sp-application-crypto = { path = "../../../substrate/primitives/application-crypto", default-features = false, optional = true }
|
sp-application-crypto = { path = "../../../substrate/primitives/application-crypto", default-features = false, optional = true }
|
||||||
sp-tracing = { path = "../../../substrate/primitives/tracing", default-features = false, optional = true }
|
sp-tracing = { path = "../../../substrate/primitives/tracing", default-features = false, optional = true }
|
||||||
sp-arithmetic = { path = "../../../substrate/primitives/arithmetic", default-features = false }
|
sp-arithmetic = { path = "../../../substrate/primitives/arithmetic", default-features = false }
|
||||||
@@ -108,6 +108,7 @@ std = [
|
|||||||
"sp-core/std",
|
"sp-core/std",
|
||||||
"sp-io/std",
|
"sp-io/std",
|
||||||
"sp-keystore",
|
"sp-keystore",
|
||||||
|
"sp-keystore?/std",
|
||||||
"sp-runtime/std",
|
"sp-runtime/std",
|
||||||
"sp-session/std",
|
"sp-session/std",
|
||||||
"sp-staking/std",
|
"sp-staking/std",
|
||||||
|
|||||||
@@ -14,7 +14,6 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
|
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
#![cfg_attr(not(feature = "std"), no_std)]
|
|
||||||
#![cfg(test)]
|
#![cfg(test)]
|
||||||
|
|
||||||
use codec::Encode;
|
use codec::Encode;
|
||||||
|
|||||||
@@ -0,0 +1,54 @@
|
|||||||
|
title: "Fix nostd build of several crates"
|
||||||
|
|
||||||
|
doc:
|
||||||
|
- audience: Runtime Dev
|
||||||
|
description: |
|
||||||
|
Fixes feature and dependency configuration of several crate. This should allow for better no-std build capabilities.
|
||||||
|
|
||||||
|
crates:
|
||||||
|
- name: cumulus-pallet-session-benchmarking
|
||||||
|
bump: patch
|
||||||
|
- name: asset-hub-rococo-runtime
|
||||||
|
bump: patch
|
||||||
|
- name: glutton-westend-runtime
|
||||||
|
bump: patch
|
||||||
|
- name: cumulus-primitives-parachain-inherent
|
||||||
|
bump: patch
|
||||||
|
- name: polkadot-primitives
|
||||||
|
bump: patch
|
||||||
|
- name: polkadot-runtime-parachains
|
||||||
|
bump: patch
|
||||||
|
- name: xcm-executor-integration-tests
|
||||||
|
bump: patch
|
||||||
|
- name: pallet-atomic-swap
|
||||||
|
bump: patch
|
||||||
|
- name: pallet-election-provider-support-benchmarking
|
||||||
|
bump: patch
|
||||||
|
- name: pallet-dev-mode
|
||||||
|
bump: patch
|
||||||
|
- name: pallet-example-offchain-worker
|
||||||
|
bump: patch
|
||||||
|
- name: pallet-indices
|
||||||
|
bump: patch
|
||||||
|
- name: pallet-nomination-pools
|
||||||
|
bump: patch
|
||||||
|
- name: pallet-nomination-pools-benchmarking
|
||||||
|
bump: patch
|
||||||
|
- name: pallet-offences-benchmarking
|
||||||
|
bump: patch
|
||||||
|
- name: pallet-root-offences
|
||||||
|
bump: patch
|
||||||
|
- name: pallet-session-benchmarking
|
||||||
|
bump: patch
|
||||||
|
- name: frame-system-benchmarking
|
||||||
|
bump: patch
|
||||||
|
- name: sp-consensus-babe
|
||||||
|
bump: patch
|
||||||
|
- name: sp-consensus-babe
|
||||||
|
bump: patch
|
||||||
|
- name: sp-core
|
||||||
|
bump: patch
|
||||||
|
- name: sp-session
|
||||||
|
bump: patch
|
||||||
|
- name: sp-transaction-storage-proof
|
||||||
|
bump: patch
|
||||||
@@ -6,7 +6,7 @@ edition = "2021"
|
|||||||
license = "Apache-2.0"
|
license = "Apache-2.0"
|
||||||
homepage = "paritytech.github.io"
|
homepage = "paritytech.github.io"
|
||||||
repository.workspace = true
|
repository.workspace = true
|
||||||
description = "The single package to get you started with building frame pallets and runtimes"
|
description = "Experimental: The single package to get you started with building frame pallets and runtimes"
|
||||||
publish = false
|
publish = false
|
||||||
|
|
||||||
[lints]
|
[lints]
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ use frame_system::pallet_prelude::BlockNumberFor;
|
|||||||
use scale_info::TypeInfo;
|
use scale_info::TypeInfo;
|
||||||
use sp_io::hashing::blake2_256;
|
use sp_io::hashing::blake2_256;
|
||||||
use sp_runtime::RuntimeDebug;
|
use sp_runtime::RuntimeDebug;
|
||||||
|
use sp_std::vec::Vec;
|
||||||
|
|
||||||
/// Pending atomic swap operation.
|
/// Pending atomic swap operation.
|
||||||
#[derive(Clone, Eq, PartialEq, RuntimeDebugNoBound, Encode, Decode, TypeInfo, MaxEncodedLen)]
|
#[derive(Clone, Eq, PartialEq, RuntimeDebugNoBound, Encode, Decode, TypeInfo, MaxEncodedLen)]
|
||||||
|
|||||||
@@ -0,0 +1,89 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
//! Election provider support pallet benchmarking.
|
||||||
|
//! This is separated into its own crate to avoid bloating the size of the runtime.
|
||||||
|
|
||||||
|
use codec::Decode;
|
||||||
|
use frame_benchmarking::v1::benchmarks;
|
||||||
|
use frame_election_provider_support::{NposSolver, PhragMMS, SequentialPhragmen};
|
||||||
|
use sp_std::vec::Vec;
|
||||||
|
|
||||||
|
pub struct Pallet<T: Config>(frame_system::Pallet<T>);
|
||||||
|
pub trait Config: frame_system::Config {}
|
||||||
|
|
||||||
|
const VOTERS: [u32; 2] = [1_000, 2_000];
|
||||||
|
const TARGETS: [u32; 2] = [500, 1_000];
|
||||||
|
const VOTES_PER_VOTER: [u32; 2] = [5, 16];
|
||||||
|
|
||||||
|
const SEED: u32 = 999;
|
||||||
|
fn set_up_voters_targets<AccountId: Decode + Clone>(
|
||||||
|
voters_len: u32,
|
||||||
|
targets_len: u32,
|
||||||
|
degree: usize,
|
||||||
|
) -> (Vec<(AccountId, u64, impl IntoIterator<Item = AccountId>)>, Vec<AccountId>) {
|
||||||
|
// fill targets.
|
||||||
|
let mut targets = (0..targets_len)
|
||||||
|
.map(|i| frame_benchmarking::account::<AccountId>("Target", i, SEED))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
assert!(targets.len() > degree, "we should always have enough voters to fill");
|
||||||
|
targets.truncate(degree);
|
||||||
|
|
||||||
|
// fill voters.
|
||||||
|
let voters = (0..voters_len)
|
||||||
|
.map(|i| {
|
||||||
|
let voter = frame_benchmarking::account::<AccountId>("Voter", i, SEED);
|
||||||
|
(voter, 1_000, targets.clone())
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
(voters, targets)
|
||||||
|
}
|
||||||
|
|
||||||
|
benchmarks! {
|
||||||
|
phragmen {
|
||||||
|
// number of votes in snapshot.
|
||||||
|
let v in (VOTERS[0]) .. VOTERS[1];
|
||||||
|
// number of targets in snapshot.
|
||||||
|
let t in (TARGETS[0]) .. TARGETS[1];
|
||||||
|
// number of votes per voter (ie the degree).
|
||||||
|
let d in (VOTES_PER_VOTER[0]) .. VOTES_PER_VOTER[1];
|
||||||
|
|
||||||
|
let (voters, targets) = set_up_voters_targets::<T::AccountId>(v, t, d as usize);
|
||||||
|
}: {
|
||||||
|
assert!(
|
||||||
|
SequentialPhragmen::<T::AccountId, sp_runtime::Perbill>
|
||||||
|
::solve(d as usize, targets, voters).is_ok()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
phragmms {
|
||||||
|
// number of votes in snapshot.
|
||||||
|
let v in (VOTERS[0]) .. VOTERS[1];
|
||||||
|
// number of targets in snapshot.
|
||||||
|
let t in (TARGETS[0]) .. TARGETS[1];
|
||||||
|
// number of votes per voter (ie the degree).
|
||||||
|
let d in (VOTES_PER_VOTER[0]) .. VOTES_PER_VOTER[1];
|
||||||
|
|
||||||
|
let (voters, targets) = set_up_voters_targets::<T::AccountId>(v, t, d as usize);
|
||||||
|
}: {
|
||||||
|
assert!(
|
||||||
|
PhragMMS::<T::AccountId, sp_runtime::Perbill>
|
||||||
|
::solve(d as usize, targets, voters).is_ok()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,77 +16,11 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
//! Election provider support pallet benchmarking.
|
//! Election provider support pallet benchmarking.
|
||||||
//! This is separated into its own crate to avoid bloating the size of the runtime.
|
|
||||||
|
|
||||||
#![cfg(feature = "runtime-benchmarks")]
|
|
||||||
#![cfg_attr(not(feature = "std"), no_std)]
|
#![cfg_attr(not(feature = "std"), no_std)]
|
||||||
|
|
||||||
use codec::Decode;
|
#[cfg(feature = "runtime-benchmarks")]
|
||||||
use frame_benchmarking::v1::benchmarks;
|
pub mod inner;
|
||||||
use frame_election_provider_support::{NposSolver, PhragMMS, SequentialPhragmen};
|
|
||||||
use sp_std::vec::Vec;
|
|
||||||
|
|
||||||
pub struct Pallet<T: Config>(frame_system::Pallet<T>);
|
#[cfg(feature = "runtime-benchmarks")]
|
||||||
pub trait Config: frame_system::Config {}
|
pub use inner::*;
|
||||||
|
|
||||||
const VOTERS: [u32; 2] = [1_000, 2_000];
|
|
||||||
const TARGETS: [u32; 2] = [500, 1_000];
|
|
||||||
const VOTES_PER_VOTER: [u32; 2] = [5, 16];
|
|
||||||
|
|
||||||
const SEED: u32 = 999;
|
|
||||||
fn set_up_voters_targets<AccountId: Decode + Clone>(
|
|
||||||
voters_len: u32,
|
|
||||||
targets_len: u32,
|
|
||||||
degree: usize,
|
|
||||||
) -> (Vec<(AccountId, u64, impl IntoIterator<Item = AccountId>)>, Vec<AccountId>) {
|
|
||||||
// fill targets.
|
|
||||||
let mut targets = (0..targets_len)
|
|
||||||
.map(|i| frame_benchmarking::account::<AccountId>("Target", i, SEED))
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
assert!(targets.len() > degree, "we should always have enough voters to fill");
|
|
||||||
targets.truncate(degree);
|
|
||||||
|
|
||||||
// fill voters.
|
|
||||||
let voters = (0..voters_len)
|
|
||||||
.map(|i| {
|
|
||||||
let voter = frame_benchmarking::account::<AccountId>("Voter", i, SEED);
|
|
||||||
(voter, 1_000, targets.clone())
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
(voters, targets)
|
|
||||||
}
|
|
||||||
|
|
||||||
benchmarks! {
|
|
||||||
phragmen {
|
|
||||||
// number of votes in snapshot.
|
|
||||||
let v in (VOTERS[0]) .. VOTERS[1];
|
|
||||||
// number of targets in snapshot.
|
|
||||||
let t in (TARGETS[0]) .. TARGETS[1];
|
|
||||||
// number of votes per voter (ie the degree).
|
|
||||||
let d in (VOTES_PER_VOTER[0]) .. VOTES_PER_VOTER[1];
|
|
||||||
|
|
||||||
let (voters, targets) = set_up_voters_targets::<T::AccountId>(v, t, d as usize);
|
|
||||||
}: {
|
|
||||||
assert!(
|
|
||||||
SequentialPhragmen::<T::AccountId, sp_runtime::Perbill>
|
|
||||||
::solve(d as usize, targets, voters).is_ok()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
phragmms {
|
|
||||||
// number of votes in snapshot.
|
|
||||||
let v in (VOTERS[0]) .. VOTERS[1];
|
|
||||||
// number of targets in snapshot.
|
|
||||||
let t in (TARGETS[0]) .. TARGETS[1];
|
|
||||||
// number of votes per voter (ie the degree).
|
|
||||||
let d in (VOTES_PER_VOTER[0]) .. VOTES_PER_VOTER[1];
|
|
||||||
|
|
||||||
let (voters, targets) = set_up_voters_targets::<T::AccountId>(v, t, d as usize);
|
|
||||||
}: {
|
|
||||||
assert!(
|
|
||||||
PhragMMS::<T::AccountId, sp_runtime::Perbill>
|
|
||||||
::solve(d as usize, targets, voters).is_ok()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -30,6 +30,7 @@
|
|||||||
|
|
||||||
use frame_support::dispatch::DispatchResult;
|
use frame_support::dispatch::DispatchResult;
|
||||||
use frame_system::ensure_signed;
|
use frame_system::ensure_signed;
|
||||||
|
use sp_std::{vec, vec::Vec};
|
||||||
|
|
||||||
// Re-export pallet items so that they can be accessed from the crate namespace.
|
// Re-export pallet items so that they can be accessed from the crate namespace.
|
||||||
pub use pallet::*;
|
pub use pallet::*;
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ frame-support = { path = "../../support", default-features = false }
|
|||||||
frame-system = { path = "../../system", default-features = false }
|
frame-system = { path = "../../system", default-features = false }
|
||||||
sp-core = { path = "../../../primitives/core", default-features = false }
|
sp-core = { path = "../../../primitives/core", default-features = false }
|
||||||
sp-io = { path = "../../../primitives/io", default-features = false }
|
sp-io = { path = "../../../primitives/io", default-features = false }
|
||||||
sp-keystore = { path = "../../../primitives/keystore", optional = true }
|
sp-keystore = { path = "../../../primitives/keystore", optional = true, default-features = false }
|
||||||
sp-runtime = { path = "../../../primitives/runtime", default-features = false }
|
sp-runtime = { path = "../../../primitives/runtime", default-features = false }
|
||||||
sp-std = { path = "../../../primitives/std", default-features = false }
|
sp-std = { path = "../../../primitives/std", default-features = false }
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ frame-support = { path = "../support", default-features = false }
|
|||||||
frame-system = { path = "../system", default-features = false }
|
frame-system = { path = "../system", default-features = false }
|
||||||
sp-core = { path = "../../primitives/core", default-features = false }
|
sp-core = { path = "../../primitives/core", default-features = false }
|
||||||
sp-io = { path = "../../primitives/io", default-features = false }
|
sp-io = { path = "../../primitives/io", default-features = false }
|
||||||
sp-keyring = { path = "../../primitives/keyring", optional = true }
|
sp-keyring = { path = "../../primitives/keyring", optional = true, default-features = false }
|
||||||
sp-runtime = { path = "../../primitives/runtime", default-features = false }
|
sp-runtime = { path = "../../primitives/runtime", default-features = false }
|
||||||
sp-std = { path = "../../primitives/std", default-features = false }
|
sp-std = { path = "../../primitives/std", default-features = false }
|
||||||
|
|
||||||
@@ -42,6 +42,7 @@ std = [
|
|||||||
"sp-core/std",
|
"sp-core/std",
|
||||||
"sp-io/std",
|
"sp-io/std",
|
||||||
"sp-keyring",
|
"sp-keyring",
|
||||||
|
"sp-keyring?/std",
|
||||||
"sp-runtime/std",
|
"sp-runtime/std",
|
||||||
"sp-std/std",
|
"sp-std/std",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -34,8 +34,8 @@ sp-io = { path = "../../primitives/io", default-features = false }
|
|||||||
log = { workspace = true }
|
log = { workspace = true }
|
||||||
|
|
||||||
# Optional: use for testing and/or fuzzing
|
# Optional: use for testing and/or fuzzing
|
||||||
pallet-balances = { path = "../balances", optional = true }
|
pallet-balances = { path = "../balances", optional = true, default-features = false }
|
||||||
sp-tracing = { path = "../../primitives/tracing", optional = true }
|
sp-tracing = { path = "../../primitives/tracing", optional = true, default-features = false }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
pallet-balances = { path = "../balances" }
|
pallet-balances = { path = "../balances" }
|
||||||
|
|||||||
@@ -0,0 +1,846 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
//! Benchmarks for the nomination pools coupled with the staking and bags list pallets.
|
||||||
|
|
||||||
|
use frame_benchmarking::v1::{account, whitelist_account};
|
||||||
|
use frame_election_provider_support::SortedListProvider;
|
||||||
|
use frame_support::{
|
||||||
|
assert_ok, ensure,
|
||||||
|
traits::{
|
||||||
|
fungible::{Inspect, Mutate, Unbalanced},
|
||||||
|
Get,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
use frame_system::RawOrigin as RuntimeOrigin;
|
||||||
|
use pallet_nomination_pools::{
|
||||||
|
BalanceOf, BondExtra, BondedPoolInner, BondedPools, ClaimPermission, ClaimPermissions,
|
||||||
|
Commission, CommissionChangeRate, CommissionClaimPermission, ConfigOp, GlobalMaxCommission,
|
||||||
|
MaxPoolMembers, MaxPoolMembersPerPool, MaxPools, Metadata, MinCreateBond, MinJoinBond,
|
||||||
|
Pallet as Pools, PoolMembers, PoolRoles, PoolState, RewardPools, SubPoolsStorage,
|
||||||
|
};
|
||||||
|
use pallet_staking::MaxNominationsOf;
|
||||||
|
use sp_runtime::{
|
||||||
|
traits::{Bounded, StaticLookup, Zero},
|
||||||
|
Perbill,
|
||||||
|
};
|
||||||
|
use sp_staking::{EraIndex, StakingInterface};
|
||||||
|
use sp_std::{vec, vec::Vec};
|
||||||
|
// `frame_benchmarking::benchmarks!` macro needs this
|
||||||
|
use pallet_nomination_pools::Call;
|
||||||
|
|
||||||
|
type CurrencyOf<T> = <T as pallet_nomination_pools::Config>::Currency;
|
||||||
|
|
||||||
|
const USER_SEED: u32 = 0;
|
||||||
|
const MAX_SPANS: u32 = 100;
|
||||||
|
|
||||||
|
pub(crate) type VoterBagsListInstance = pallet_bags_list::Instance1;
|
||||||
|
pub trait Config:
|
||||||
|
pallet_nomination_pools::Config
|
||||||
|
+ pallet_staking::Config
|
||||||
|
+ pallet_bags_list::Config<VoterBagsListInstance>
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Pallet<T: Config>(Pools<T>);
|
||||||
|
|
||||||
|
fn create_funded_user_with_balance<T: pallet_nomination_pools::Config>(
|
||||||
|
string: &'static str,
|
||||||
|
n: u32,
|
||||||
|
balance: BalanceOf<T>,
|
||||||
|
) -> T::AccountId {
|
||||||
|
let user = account(string, n, USER_SEED);
|
||||||
|
T::Currency::set_balance(&user, balance);
|
||||||
|
user
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a bonded pool account, bonding `balance` and giving the account `balance * 2` free
|
||||||
|
// balance.
|
||||||
|
fn create_pool_account<T: pallet_nomination_pools::Config>(
|
||||||
|
n: u32,
|
||||||
|
balance: BalanceOf<T>,
|
||||||
|
commission: Option<Perbill>,
|
||||||
|
) -> (T::AccountId, T::AccountId) {
|
||||||
|
let ed = CurrencyOf::<T>::minimum_balance();
|
||||||
|
let pool_creator: T::AccountId =
|
||||||
|
create_funded_user_with_balance::<T>("pool_creator", n, ed + balance * 2u32.into());
|
||||||
|
let pool_creator_lookup = T::Lookup::unlookup(pool_creator.clone());
|
||||||
|
|
||||||
|
Pools::<T>::create(
|
||||||
|
RuntimeOrigin::Signed(pool_creator.clone()).into(),
|
||||||
|
balance,
|
||||||
|
pool_creator_lookup.clone(),
|
||||||
|
pool_creator_lookup.clone(),
|
||||||
|
pool_creator_lookup,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
if let Some(c) = commission {
|
||||||
|
let pool_id = pallet_nomination_pools::LastPoolId::<T>::get();
|
||||||
|
Pools::<T>::set_commission(
|
||||||
|
RuntimeOrigin::Signed(pool_creator.clone()).into(),
|
||||||
|
pool_id,
|
||||||
|
Some((c, pool_creator.clone())),
|
||||||
|
)
|
||||||
|
.expect("pool just created, commission can be set by root; qed");
|
||||||
|
}
|
||||||
|
|
||||||
|
let pool_account = pallet_nomination_pools::BondedPools::<T>::iter()
|
||||||
|
.find(|(_, bonded_pool)| bonded_pool.roles.depositor == pool_creator)
|
||||||
|
.map(|(pool_id, _)| Pools::<T>::create_bonded_account(pool_id))
|
||||||
|
.expect("pool_creator created a pool above");
|
||||||
|
|
||||||
|
(pool_creator, pool_account)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn vote_to_balance<T: pallet_nomination_pools::Config>(
|
||||||
|
vote: u64,
|
||||||
|
) -> Result<BalanceOf<T>, &'static str> {
|
||||||
|
vote.try_into().map_err(|_| "could not convert u64 to Balance")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
struct ListScenario<T: pallet_nomination_pools::Config> {
|
||||||
|
/// Stash/Controller that is expected to be moved.
|
||||||
|
origin1: T::AccountId,
|
||||||
|
creator1: T::AccountId,
|
||||||
|
dest_weight: BalanceOf<T>,
|
||||||
|
origin1_member: Option<T::AccountId>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Config> ListScenario<T> {
|
||||||
|
/// An expensive scenario for bags-list implementation:
|
||||||
|
///
|
||||||
|
/// - the node to be updated (r) is the head of a bag that has at least one other node. The bag
|
||||||
|
/// itself will need to be read and written to update its head. The node pointed to by r.next
|
||||||
|
/// will need to be read and written as it will need to have its prev pointer updated. Note
|
||||||
|
/// that there are two other worst case scenarios for bag removal: 1) the node is a tail and
|
||||||
|
/// 2) the node is a middle node with prev and next; all scenarios end up with the same number
|
||||||
|
/// of storage reads and writes.
|
||||||
|
///
|
||||||
|
/// - the destination bag has at least one node, which will need its next pointer updated.
|
||||||
|
pub(crate) fn new(
|
||||||
|
origin_weight: BalanceOf<T>,
|
||||||
|
is_increase: bool,
|
||||||
|
) -> Result<Self, &'static str> {
|
||||||
|
ensure!(!origin_weight.is_zero(), "origin weight must be greater than 0");
|
||||||
|
|
||||||
|
ensure!(
|
||||||
|
pallet_nomination_pools::MaxPools::<T>::get().unwrap_or(0) >= 3,
|
||||||
|
"must allow at least three pools for benchmarks"
|
||||||
|
);
|
||||||
|
|
||||||
|
// Burn the entire issuance.
|
||||||
|
CurrencyOf::<T>::set_total_issuance(Zero::zero());
|
||||||
|
|
||||||
|
// Create accounts with the origin weight
|
||||||
|
let (pool_creator1, pool_origin1) =
|
||||||
|
create_pool_account::<T>(USER_SEED + 1, origin_weight, Some(Perbill::from_percent(50)));
|
||||||
|
|
||||||
|
T::Staking::nominate(
|
||||||
|
&pool_origin1,
|
||||||
|
// NOTE: these don't really need to be validators.
|
||||||
|
vec![account("random_validator", 0, USER_SEED)],
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let (_, pool_origin2) =
|
||||||
|
create_pool_account::<T>(USER_SEED + 2, origin_weight, Some(Perbill::from_percent(50)));
|
||||||
|
|
||||||
|
T::Staking::nominate(
|
||||||
|
&pool_origin2,
|
||||||
|
vec![account("random_validator", 0, USER_SEED)].clone(),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
// Find a destination weight that will trigger the worst case scenario
|
||||||
|
let dest_weight_as_vote = <T as pallet_staking::Config>::VoterList::score_update_worst_case(
|
||||||
|
&pool_origin1,
|
||||||
|
is_increase,
|
||||||
|
);
|
||||||
|
|
||||||
|
let dest_weight: BalanceOf<T> =
|
||||||
|
dest_weight_as_vote.try_into().map_err(|_| "could not convert u64 to Balance")?;
|
||||||
|
|
||||||
|
// Create an account with the worst case destination weight
|
||||||
|
let (_, pool_dest1) =
|
||||||
|
create_pool_account::<T>(USER_SEED + 3, dest_weight, Some(Perbill::from_percent(50)));
|
||||||
|
|
||||||
|
T::Staking::nominate(&pool_dest1, vec![account("random_validator", 0, USER_SEED)])?;
|
||||||
|
|
||||||
|
let weight_of = pallet_staking::Pallet::<T>::weight_of_fn();
|
||||||
|
assert_eq!(vote_to_balance::<T>(weight_of(&pool_origin1)).unwrap(), origin_weight);
|
||||||
|
assert_eq!(vote_to_balance::<T>(weight_of(&pool_origin2)).unwrap(), origin_weight);
|
||||||
|
assert_eq!(vote_to_balance::<T>(weight_of(&pool_dest1)).unwrap(), dest_weight);
|
||||||
|
|
||||||
|
Ok(ListScenario {
|
||||||
|
origin1: pool_origin1,
|
||||||
|
creator1: pool_creator1,
|
||||||
|
dest_weight,
|
||||||
|
origin1_member: None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_joiner(mut self, amount: BalanceOf<T>) -> Self {
|
||||||
|
let amount = MinJoinBond::<T>::get()
|
||||||
|
.max(CurrencyOf::<T>::minimum_balance())
|
||||||
|
// Max `amount` with minimum thresholds for account balance and joining a pool
|
||||||
|
// to ensure 1) the user can be created and 2) can join the pool
|
||||||
|
.max(amount);
|
||||||
|
|
||||||
|
let joiner: T::AccountId = account("joiner", USER_SEED, 0);
|
||||||
|
self.origin1_member = Some(joiner.clone());
|
||||||
|
CurrencyOf::<T>::set_balance(&joiner, amount * 2u32.into());
|
||||||
|
|
||||||
|
let original_bonded = T::Staking::active_stake(&self.origin1).unwrap();
|
||||||
|
|
||||||
|
// Unbond `amount` from the underlying pool account so when the member joins
|
||||||
|
// we will maintain `current_bonded`.
|
||||||
|
T::Staking::unbond(&self.origin1, amount).expect("the pool was created in `Self::new`.");
|
||||||
|
|
||||||
|
// Account pool points for the unbonded balance.
|
||||||
|
BondedPools::<T>::mutate(&1, |maybe_pool| {
|
||||||
|
maybe_pool.as_mut().map(|pool| pool.points -= amount)
|
||||||
|
});
|
||||||
|
|
||||||
|
Pools::<T>::join(RuntimeOrigin::Signed(joiner.clone()).into(), amount, 1).unwrap();
|
||||||
|
|
||||||
|
// check that the vote weight is still the same as the original bonded
|
||||||
|
let weight_of = pallet_staking::Pallet::<T>::weight_of_fn();
|
||||||
|
assert_eq!(vote_to_balance::<T>(weight_of(&self.origin1)).unwrap(), original_bonded);
|
||||||
|
|
||||||
|
// check the member was added correctly
|
||||||
|
let member = PoolMembers::<T>::get(&joiner).unwrap();
|
||||||
|
assert_eq!(member.points, amount);
|
||||||
|
assert_eq!(member.pool_id, 1);
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
frame_benchmarking::benchmarks! {
|
||||||
|
join {
|
||||||
|
let origin_weight = Pools::<T>::depositor_min_bond() * 2u32.into();
|
||||||
|
|
||||||
|
// setup the worst case list scenario.
|
||||||
|
let scenario = ListScenario::<T>::new(origin_weight, true)?;
|
||||||
|
assert_eq!(
|
||||||
|
T::Staking::active_stake(&scenario.origin1).unwrap(),
|
||||||
|
origin_weight
|
||||||
|
);
|
||||||
|
|
||||||
|
let max_additional = scenario.dest_weight - origin_weight;
|
||||||
|
let joiner_free = CurrencyOf::<T>::minimum_balance() + max_additional;
|
||||||
|
|
||||||
|
let joiner: T::AccountId
|
||||||
|
= create_funded_user_with_balance::<T>("joiner", 0, joiner_free);
|
||||||
|
|
||||||
|
whitelist_account!(joiner);
|
||||||
|
}: _(RuntimeOrigin::Signed(joiner.clone()), max_additional, 1)
|
||||||
|
verify {
|
||||||
|
assert_eq!(CurrencyOf::<T>::balance(&joiner), joiner_free - max_additional);
|
||||||
|
assert_eq!(
|
||||||
|
T::Staking::active_stake(&scenario.origin1).unwrap(),
|
||||||
|
scenario.dest_weight
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
bond_extra_transfer {
|
||||||
|
let origin_weight = Pools::<T>::depositor_min_bond() * 2u32.into();
|
||||||
|
let scenario = ListScenario::<T>::new(origin_weight, true)?;
|
||||||
|
let extra = scenario.dest_weight - origin_weight;
|
||||||
|
|
||||||
|
// creator of the src pool will bond-extra, bumping itself to dest bag.
|
||||||
|
|
||||||
|
}: bond_extra(RuntimeOrigin::Signed(scenario.creator1.clone()), BondExtra::FreeBalance(extra))
|
||||||
|
verify {
|
||||||
|
assert!(
|
||||||
|
T::Staking::active_stake(&scenario.origin1).unwrap() >=
|
||||||
|
scenario.dest_weight
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
bond_extra_other {
|
||||||
|
let claimer: T::AccountId = account("claimer", USER_SEED + 4, 0);
|
||||||
|
|
||||||
|
let origin_weight = Pools::<T>::depositor_min_bond() * 2u32.into();
|
||||||
|
let scenario = ListScenario::<T>::new(origin_weight, true)?;
|
||||||
|
let extra = (scenario.dest_weight - origin_weight).max(CurrencyOf::<T>::minimum_balance());
|
||||||
|
|
||||||
|
// set claim preferences to `PermissionlessAll` to any account to bond extra on member's behalf.
|
||||||
|
let _ = Pools::<T>::set_claim_permission(RuntimeOrigin::Signed(scenario.creator1.clone()).into(), ClaimPermission::PermissionlessAll);
|
||||||
|
|
||||||
|
// transfer exactly `extra` to the depositor of the src pool (1),
|
||||||
|
let reward_account1 = Pools::<T>::create_reward_account(1);
|
||||||
|
assert!(extra >= CurrencyOf::<T>::minimum_balance());
|
||||||
|
let _ = CurrencyOf::<T>::mint_into(&reward_account1, extra);
|
||||||
|
|
||||||
|
}: _(RuntimeOrigin::Signed(claimer), T::Lookup::unlookup(scenario.creator1.clone()), BondExtra::Rewards)
|
||||||
|
verify {
|
||||||
|
// commission of 50% deducted here.
|
||||||
|
assert!(
|
||||||
|
T::Staking::active_stake(&scenario.origin1).unwrap() >=
|
||||||
|
scenario.dest_weight / 2u32.into()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
claim_payout {
|
||||||
|
let claimer: T::AccountId = account("claimer", USER_SEED + 4, 0);
|
||||||
|
let commission = Perbill::from_percent(50);
|
||||||
|
let origin_weight = Pools::<T>::depositor_min_bond() * 2u32.into();
|
||||||
|
let ed = CurrencyOf::<T>::minimum_balance();
|
||||||
|
let (depositor, pool_account) = create_pool_account::<T>(0, origin_weight, Some(commission));
|
||||||
|
let reward_account = Pools::<T>::create_reward_account(1);
|
||||||
|
|
||||||
|
// Send funds to the reward account of the pool
|
||||||
|
CurrencyOf::<T>::set_balance(&reward_account, ed + origin_weight);
|
||||||
|
|
||||||
|
// set claim preferences to `PermissionlessAll` so any account can claim rewards on member's
|
||||||
|
// behalf.
|
||||||
|
let _ = Pools::<T>::set_claim_permission(RuntimeOrigin::Signed(depositor.clone()).into(), ClaimPermission::PermissionlessAll);
|
||||||
|
|
||||||
|
// Sanity check
|
||||||
|
assert_eq!(
|
||||||
|
CurrencyOf::<T>::balance(&depositor),
|
||||||
|
origin_weight
|
||||||
|
);
|
||||||
|
|
||||||
|
whitelist_account!(depositor);
|
||||||
|
}:claim_payout_other(RuntimeOrigin::Signed(claimer), depositor.clone())
|
||||||
|
verify {
|
||||||
|
assert_eq!(
|
||||||
|
CurrencyOf::<T>::balance(&depositor),
|
||||||
|
origin_weight + commission * origin_weight
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
CurrencyOf::<T>::balance(&reward_account),
|
||||||
|
ed + commission * origin_weight
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
unbond {
|
||||||
|
// The weight the nominator will start at. The value used here is expected to be
|
||||||
|
// significantly higher than the first position in a list (e.g. the first bag threshold).
|
||||||
|
let origin_weight = Pools::<T>::depositor_min_bond() * 200u32.into();
|
||||||
|
let scenario = ListScenario::<T>::new(origin_weight, false)?;
|
||||||
|
let amount = origin_weight - scenario.dest_weight;
|
||||||
|
|
||||||
|
let scenario = scenario.add_joiner(amount);
|
||||||
|
let member_id = scenario.origin1_member.unwrap().clone();
|
||||||
|
let member_id_lookup = T::Lookup::unlookup(member_id.clone());
|
||||||
|
let all_points = PoolMembers::<T>::get(&member_id).unwrap().points;
|
||||||
|
whitelist_account!(member_id);
|
||||||
|
}: _(RuntimeOrigin::Signed(member_id.clone()), member_id_lookup, all_points)
|
||||||
|
verify {
|
||||||
|
let bonded_after = T::Staking::active_stake(&scenario.origin1).unwrap();
|
||||||
|
// We at least went down to the destination bag
|
||||||
|
assert!(bonded_after <= scenario.dest_weight);
|
||||||
|
let member = PoolMembers::<T>::get(
|
||||||
|
&member_id
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
member.unbonding_eras.keys().cloned().collect::<Vec<_>>(),
|
||||||
|
vec![0 + T::Staking::bonding_duration()]
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
member.unbonding_eras.values().cloned().collect::<Vec<_>>(),
|
||||||
|
vec![all_points]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pool_withdraw_unbonded {
|
||||||
|
let s in 0 .. MAX_SPANS;
|
||||||
|
|
||||||
|
let min_create_bond = Pools::<T>::depositor_min_bond();
|
||||||
|
let (depositor, pool_account) = create_pool_account::<T>(0, min_create_bond, None);
|
||||||
|
|
||||||
|
// Add a new member
|
||||||
|
let min_join_bond = MinJoinBond::<T>::get().max(CurrencyOf::<T>::minimum_balance());
|
||||||
|
let joiner = create_funded_user_with_balance::<T>("joiner", 0, min_join_bond * 2u32.into());
|
||||||
|
Pools::<T>::join(RuntimeOrigin::Signed(joiner.clone()).into(), min_join_bond, 1)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// Sanity check join worked
|
||||||
|
assert_eq!(
|
||||||
|
T::Staking::active_stake(&pool_account).unwrap(),
|
||||||
|
min_create_bond + min_join_bond
|
||||||
|
);
|
||||||
|
assert_eq!(CurrencyOf::<T>::balance(&joiner), min_join_bond);
|
||||||
|
|
||||||
|
// Unbond the new member
|
||||||
|
Pools::<T>::fully_unbond(RuntimeOrigin::Signed(joiner.clone()).into(), joiner.clone()).unwrap();
|
||||||
|
|
||||||
|
// Sanity check that unbond worked
|
||||||
|
assert_eq!(
|
||||||
|
T::Staking::active_stake(&pool_account).unwrap(),
|
||||||
|
min_create_bond
|
||||||
|
);
|
||||||
|
assert_eq!(pallet_staking::Ledger::<T>::get(&pool_account).unwrap().unlocking.len(), 1);
|
||||||
|
// Set the current era
|
||||||
|
pallet_staking::CurrentEra::<T>::put(EraIndex::max_value());
|
||||||
|
|
||||||
|
// Add `s` count of slashing spans to storage.
|
||||||
|
pallet_staking::benchmarking::add_slashing_spans::<T>(&pool_account, s);
|
||||||
|
whitelist_account!(pool_account);
|
||||||
|
}: _(RuntimeOrigin::Signed(pool_account.clone()), 1, s)
|
||||||
|
verify {
|
||||||
|
// The joiners funds didn't change
|
||||||
|
assert_eq!(CurrencyOf::<T>::balance(&joiner), min_join_bond);
|
||||||
|
// The unlocking chunk was removed
|
||||||
|
assert_eq!(pallet_staking::Ledger::<T>::get(pool_account).unwrap().unlocking.len(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
withdraw_unbonded_update {
|
||||||
|
let s in 0 .. MAX_SPANS;
|
||||||
|
|
||||||
|
let min_create_bond = Pools::<T>::depositor_min_bond();
|
||||||
|
let (depositor, pool_account) = create_pool_account::<T>(0, min_create_bond, None);
|
||||||
|
|
||||||
|
// Add a new member
|
||||||
|
let min_join_bond = MinJoinBond::<T>::get().max(CurrencyOf::<T>::minimum_balance());
|
||||||
|
let joiner = create_funded_user_with_balance::<T>("joiner", 0, min_join_bond * 2u32.into());
|
||||||
|
let joiner_lookup = T::Lookup::unlookup(joiner.clone());
|
||||||
|
Pools::<T>::join(RuntimeOrigin::Signed(joiner.clone()).into(), min_join_bond, 1)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// Sanity check join worked
|
||||||
|
assert_eq!(
|
||||||
|
T::Staking::active_stake(&pool_account).unwrap(),
|
||||||
|
min_create_bond + min_join_bond
|
||||||
|
);
|
||||||
|
assert_eq!(CurrencyOf::<T>::balance(&joiner), min_join_bond);
|
||||||
|
|
||||||
|
// Unbond the new member
|
||||||
|
pallet_staking::CurrentEra::<T>::put(0);
|
||||||
|
Pools::<T>::fully_unbond(RuntimeOrigin::Signed(joiner.clone()).into(), joiner.clone()).unwrap();
|
||||||
|
|
||||||
|
// Sanity check that unbond worked
|
||||||
|
assert_eq!(
|
||||||
|
T::Staking::active_stake(&pool_account).unwrap(),
|
||||||
|
min_create_bond
|
||||||
|
);
|
||||||
|
assert_eq!(pallet_staking::Ledger::<T>::get(&pool_account).unwrap().unlocking.len(), 1);
|
||||||
|
|
||||||
|
// Set the current era to ensure we can withdraw unbonded funds
|
||||||
|
pallet_staking::CurrentEra::<T>::put(EraIndex::max_value());
|
||||||
|
|
||||||
|
pallet_staking::benchmarking::add_slashing_spans::<T>(&pool_account, s);
|
||||||
|
whitelist_account!(joiner);
|
||||||
|
}: withdraw_unbonded(RuntimeOrigin::Signed(joiner.clone()), joiner_lookup, s)
|
||||||
|
verify {
|
||||||
|
assert_eq!(
|
||||||
|
CurrencyOf::<T>::balance(&joiner), min_join_bond * 2u32.into()
|
||||||
|
);
|
||||||
|
// The unlocking chunk was removed
|
||||||
|
assert_eq!(pallet_staking::Ledger::<T>::get(&pool_account).unwrap().unlocking.len(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
withdraw_unbonded_kill {
|
||||||
|
let s in 0 .. MAX_SPANS;
|
||||||
|
|
||||||
|
let min_create_bond = Pools::<T>::depositor_min_bond();
|
||||||
|
let (depositor, pool_account) = create_pool_account::<T>(0, min_create_bond, None);
|
||||||
|
let depositor_lookup = T::Lookup::unlookup(depositor.clone());
|
||||||
|
|
||||||
|
// We set the pool to the destroying state so the depositor can leave
|
||||||
|
BondedPools::<T>::try_mutate(&1, |maybe_bonded_pool| {
|
||||||
|
maybe_bonded_pool.as_mut().ok_or(()).map(|bonded_pool| {
|
||||||
|
bonded_pool.state = PoolState::Destroying;
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// Unbond the creator
|
||||||
|
pallet_staking::CurrentEra::<T>::put(0);
|
||||||
|
// Simulate some rewards so we can check if the rewards storage is cleaned up. We check this
|
||||||
|
// here to ensure the complete flow for destroying a pool works - the reward pool account
|
||||||
|
// should never exist by time the depositor withdraws so we test that it gets cleaned
|
||||||
|
// up when unbonding.
|
||||||
|
let reward_account = Pools::<T>::create_reward_account(1);
|
||||||
|
assert!(frame_system::Account::<T>::contains_key(&reward_account));
|
||||||
|
Pools::<T>::fully_unbond(RuntimeOrigin::Signed(depositor.clone()).into(), depositor.clone()).unwrap();
|
||||||
|
|
||||||
|
// Sanity check that unbond worked
|
||||||
|
assert_eq!(
|
||||||
|
T::Staking::active_stake(&pool_account).unwrap(),
|
||||||
|
Zero::zero()
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
CurrencyOf::<T>::balance(&pool_account),
|
||||||
|
min_create_bond
|
||||||
|
);
|
||||||
|
assert_eq!(pallet_staking::Ledger::<T>::get(&pool_account).unwrap().unlocking.len(), 1);
|
||||||
|
|
||||||
|
// Set the current era to ensure we can withdraw unbonded funds
|
||||||
|
pallet_staking::CurrentEra::<T>::put(EraIndex::max_value());
|
||||||
|
|
||||||
|
// Some last checks that storage items we expect to get cleaned up are present
|
||||||
|
assert!(pallet_staking::Ledger::<T>::contains_key(&pool_account));
|
||||||
|
assert!(BondedPools::<T>::contains_key(&1));
|
||||||
|
assert!(SubPoolsStorage::<T>::contains_key(&1));
|
||||||
|
assert!(RewardPools::<T>::contains_key(&1));
|
||||||
|
assert!(PoolMembers::<T>::contains_key(&depositor));
|
||||||
|
assert!(frame_system::Account::<T>::contains_key(&reward_account));
|
||||||
|
|
||||||
|
whitelist_account!(depositor);
|
||||||
|
}: withdraw_unbonded(RuntimeOrigin::Signed(depositor.clone()), depositor_lookup, s)
|
||||||
|
verify {
|
||||||
|
// Pool removal worked
|
||||||
|
assert!(!pallet_staking::Ledger::<T>::contains_key(&pool_account));
|
||||||
|
assert!(!BondedPools::<T>::contains_key(&1));
|
||||||
|
assert!(!SubPoolsStorage::<T>::contains_key(&1));
|
||||||
|
assert!(!RewardPools::<T>::contains_key(&1));
|
||||||
|
assert!(!PoolMembers::<T>::contains_key(&depositor));
|
||||||
|
assert!(!frame_system::Account::<T>::contains_key(&pool_account));
|
||||||
|
assert!(!frame_system::Account::<T>::contains_key(&reward_account));
|
||||||
|
|
||||||
|
// Funds where transferred back correctly
|
||||||
|
assert_eq!(
|
||||||
|
CurrencyOf::<T>::balance(&depositor),
|
||||||
|
// gets bond back + rewards collecting when unbonding
|
||||||
|
min_create_bond * 2u32.into() + CurrencyOf::<T>::minimum_balance()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
create {
|
||||||
|
let min_create_bond = Pools::<T>::depositor_min_bond();
|
||||||
|
let depositor: T::AccountId = account("depositor", USER_SEED, 0);
|
||||||
|
let depositor_lookup = T::Lookup::unlookup(depositor.clone());
|
||||||
|
|
||||||
|
// Give the depositor some balance to bond
|
||||||
|
CurrencyOf::<T>::set_balance(&depositor, min_create_bond * 2u32.into());
|
||||||
|
|
||||||
|
// Make sure no Pools exist at a pre-condition for our verify checks
|
||||||
|
assert_eq!(RewardPools::<T>::count(), 0);
|
||||||
|
assert_eq!(BondedPools::<T>::count(), 0);
|
||||||
|
|
||||||
|
whitelist_account!(depositor);
|
||||||
|
}: _(
|
||||||
|
RuntimeOrigin::Signed(depositor.clone()),
|
||||||
|
min_create_bond,
|
||||||
|
depositor_lookup.clone(),
|
||||||
|
depositor_lookup.clone(),
|
||||||
|
depositor_lookup
|
||||||
|
)
|
||||||
|
verify {
|
||||||
|
assert_eq!(RewardPools::<T>::count(), 1);
|
||||||
|
assert_eq!(BondedPools::<T>::count(), 1);
|
||||||
|
let (_, new_pool) = BondedPools::<T>::iter().next().unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
new_pool,
|
||||||
|
BondedPoolInner {
|
||||||
|
commission: Commission::default(),
|
||||||
|
member_counter: 1,
|
||||||
|
points: min_create_bond,
|
||||||
|
roles: PoolRoles {
|
||||||
|
depositor: depositor.clone(),
|
||||||
|
root: Some(depositor.clone()),
|
||||||
|
nominator: Some(depositor.clone()),
|
||||||
|
bouncer: Some(depositor.clone()),
|
||||||
|
},
|
||||||
|
state: PoolState::Open,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
T::Staking::active_stake(&Pools::<T>::create_bonded_account(1)),
|
||||||
|
Ok(min_create_bond)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
nominate {
|
||||||
|
let n in 1 .. MaxNominationsOf::<T>::get();
|
||||||
|
|
||||||
|
// Create a pool
|
||||||
|
let min_create_bond = Pools::<T>::depositor_min_bond() * 2u32.into();
|
||||||
|
let (depositor, pool_account) = create_pool_account::<T>(0, min_create_bond, None);
|
||||||
|
|
||||||
|
// Create some accounts to nominate. For the sake of benchmarking they don't need to be
|
||||||
|
// actual validators
|
||||||
|
let validators: Vec<_> = (0..n)
|
||||||
|
.map(|i| account("stash", USER_SEED, i))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
whitelist_account!(depositor);
|
||||||
|
}:_(RuntimeOrigin::Signed(depositor.clone()), 1, validators)
|
||||||
|
verify {
|
||||||
|
assert_eq!(RewardPools::<T>::count(), 1);
|
||||||
|
assert_eq!(BondedPools::<T>::count(), 1);
|
||||||
|
let (_, new_pool) = BondedPools::<T>::iter().next().unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
new_pool,
|
||||||
|
BondedPoolInner {
|
||||||
|
commission: Commission::default(),
|
||||||
|
member_counter: 1,
|
||||||
|
points: min_create_bond,
|
||||||
|
roles: PoolRoles {
|
||||||
|
depositor: depositor.clone(),
|
||||||
|
root: Some(depositor.clone()),
|
||||||
|
nominator: Some(depositor.clone()),
|
||||||
|
bouncer: Some(depositor.clone()),
|
||||||
|
},
|
||||||
|
state: PoolState::Open,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
T::Staking::active_stake(&Pools::<T>::create_bonded_account(1)),
|
||||||
|
Ok(min_create_bond)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
set_state {
|
||||||
|
// Create a pool
|
||||||
|
let min_create_bond = Pools::<T>::depositor_min_bond();
|
||||||
|
let (depositor, pool_account) = create_pool_account::<T>(0, min_create_bond, None);
|
||||||
|
BondedPools::<T>::mutate(&1, |maybe_pool| {
|
||||||
|
// Force the pool into an invalid state
|
||||||
|
maybe_pool.as_mut().map(|pool| pool.points = min_create_bond * 10u32.into());
|
||||||
|
});
|
||||||
|
|
||||||
|
let caller = account("caller", 0, USER_SEED);
|
||||||
|
whitelist_account!(caller);
|
||||||
|
}:_(RuntimeOrigin::Signed(caller), 1, PoolState::Destroying)
|
||||||
|
verify {
|
||||||
|
assert_eq!(BondedPools::<T>::get(1).unwrap().state, PoolState::Destroying);
|
||||||
|
}
|
||||||
|
|
||||||
|
set_metadata {
|
||||||
|
let n in 1 .. <T as pallet_nomination_pools::Config>::MaxMetadataLen::get();
|
||||||
|
|
||||||
|
// Create a pool
|
||||||
|
let (depositor, pool_account) = create_pool_account::<T>(0, Pools::<T>::depositor_min_bond() * 2u32.into(), None);
|
||||||
|
|
||||||
|
// Create metadata of the max possible size
|
||||||
|
let metadata: Vec<u8> = (0..n).map(|_| 42).collect();
|
||||||
|
|
||||||
|
whitelist_account!(depositor);
|
||||||
|
}:_(RuntimeOrigin::Signed(depositor), 1, metadata.clone())
|
||||||
|
verify {
|
||||||
|
assert_eq!(Metadata::<T>::get(&1), metadata);
|
||||||
|
}
|
||||||
|
|
||||||
|
set_configs {
|
||||||
|
}:_(
|
||||||
|
RuntimeOrigin::Root,
|
||||||
|
ConfigOp::Set(BalanceOf::<T>::max_value()),
|
||||||
|
ConfigOp::Set(BalanceOf::<T>::max_value()),
|
||||||
|
ConfigOp::Set(u32::MAX),
|
||||||
|
ConfigOp::Set(u32::MAX),
|
||||||
|
ConfigOp::Set(u32::MAX),
|
||||||
|
ConfigOp::Set(Perbill::max_value())
|
||||||
|
) verify {
|
||||||
|
assert_eq!(MinJoinBond::<T>::get(), BalanceOf::<T>::max_value());
|
||||||
|
assert_eq!(MinCreateBond::<T>::get(), BalanceOf::<T>::max_value());
|
||||||
|
assert_eq!(MaxPools::<T>::get(), Some(u32::MAX));
|
||||||
|
assert_eq!(MaxPoolMembers::<T>::get(), Some(u32::MAX));
|
||||||
|
assert_eq!(MaxPoolMembersPerPool::<T>::get(), Some(u32::MAX));
|
||||||
|
assert_eq!(GlobalMaxCommission::<T>::get(), Some(Perbill::max_value()));
|
||||||
|
}
|
||||||
|
|
||||||
|
update_roles {
|
||||||
|
let first_id = pallet_nomination_pools::LastPoolId::<T>::get() + 1;
|
||||||
|
let (root, _) = create_pool_account::<T>(0, Pools::<T>::depositor_min_bond() * 2u32.into(), None);
|
||||||
|
let random: T::AccountId = account("but is anything really random in computers..?", 0, USER_SEED);
|
||||||
|
}:_(
|
||||||
|
RuntimeOrigin::Signed(root.clone()),
|
||||||
|
first_id,
|
||||||
|
ConfigOp::Set(random.clone()),
|
||||||
|
ConfigOp::Set(random.clone()),
|
||||||
|
ConfigOp::Set(random.clone())
|
||||||
|
) verify {
|
||||||
|
assert_eq!(
|
||||||
|
pallet_nomination_pools::BondedPools::<T>::get(first_id).unwrap().roles,
|
||||||
|
pallet_nomination_pools::PoolRoles {
|
||||||
|
depositor: root,
|
||||||
|
nominator: Some(random.clone()),
|
||||||
|
bouncer: Some(random.clone()),
|
||||||
|
root: Some(random),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
chill {
|
||||||
|
// Create a pool
|
||||||
|
let (depositor, pool_account) = create_pool_account::<T>(0, Pools::<T>::depositor_min_bond() * 2u32.into(), None);
|
||||||
|
|
||||||
|
// Nominate with the pool.
|
||||||
|
let validators: Vec<_> = (0..MaxNominationsOf::<T>::get())
|
||||||
|
.map(|i| account("stash", USER_SEED, i))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
assert_ok!(T::Staking::nominate(&pool_account, validators));
|
||||||
|
assert!(T::Staking::nominations(&Pools::<T>::create_bonded_account(1)).is_some());
|
||||||
|
|
||||||
|
whitelist_account!(depositor);
|
||||||
|
}:_(RuntimeOrigin::Signed(depositor.clone()), 1)
|
||||||
|
verify {
|
||||||
|
assert!(T::Staking::nominations(&Pools::<T>::create_bonded_account(1)).is_none());
|
||||||
|
}
|
||||||
|
|
||||||
|
set_commission {
|
||||||
|
// Create a pool - do not set a commission yet.
|
||||||
|
let (depositor, pool_account) = create_pool_account::<T>(0, Pools::<T>::depositor_min_bond() * 2u32.into(), None);
|
||||||
|
// set a max commission
|
||||||
|
Pools::<T>::set_commission_max(RuntimeOrigin::Signed(depositor.clone()).into(), 1u32.into(), Perbill::from_percent(50)).unwrap();
|
||||||
|
// set a change rate
|
||||||
|
Pools::<T>::set_commission_change_rate(RuntimeOrigin::Signed(depositor.clone()).into(), 1u32.into(), CommissionChangeRate {
|
||||||
|
max_increase: Perbill::from_percent(20),
|
||||||
|
min_delay: 0u32.into(),
|
||||||
|
}).unwrap();
|
||||||
|
// set a claim permission to an account.
|
||||||
|
Pools::<T>::set_commission_claim_permission(
|
||||||
|
RuntimeOrigin::Signed(depositor.clone()).into(),
|
||||||
|
1u32.into(),
|
||||||
|
Some(CommissionClaimPermission::Account(depositor.clone()))
|
||||||
|
).unwrap();
|
||||||
|
|
||||||
|
}:_(RuntimeOrigin::Signed(depositor.clone()), 1u32.into(), Some((Perbill::from_percent(20), depositor.clone())))
|
||||||
|
verify {
|
||||||
|
assert_eq!(BondedPools::<T>::get(1).unwrap().commission, Commission {
|
||||||
|
current: Some((Perbill::from_percent(20), depositor.clone())),
|
||||||
|
max: Some(Perbill::from_percent(50)),
|
||||||
|
change_rate: Some(CommissionChangeRate {
|
||||||
|
max_increase: Perbill::from_percent(20),
|
||||||
|
min_delay: 0u32.into()
|
||||||
|
}),
|
||||||
|
throttle_from: Some(1u32.into()),
|
||||||
|
claim_permission: Some(CommissionClaimPermission::Account(depositor)),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
set_commission_max {
|
||||||
|
// Create a pool, setting a commission that will update when max commission is set.
|
||||||
|
let (depositor, pool_account) = create_pool_account::<T>(0, Pools::<T>::depositor_min_bond() * 2u32.into(), Some(Perbill::from_percent(50)));
|
||||||
|
}:_(RuntimeOrigin::Signed(depositor.clone()), 1u32.into(), Perbill::from_percent(50))
|
||||||
|
verify {
|
||||||
|
assert_eq!(
|
||||||
|
BondedPools::<T>::get(1).unwrap().commission, Commission {
|
||||||
|
current: Some((Perbill::from_percent(50), depositor)),
|
||||||
|
max: Some(Perbill::from_percent(50)),
|
||||||
|
change_rate: None,
|
||||||
|
throttle_from: Some(0u32.into()),
|
||||||
|
claim_permission: None,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
set_commission_change_rate {
|
||||||
|
// Create a pool
|
||||||
|
let (depositor, pool_account) = create_pool_account::<T>(0, Pools::<T>::depositor_min_bond() * 2u32.into(), None);
|
||||||
|
}:_(RuntimeOrigin::Signed(depositor.clone()), 1u32.into(), CommissionChangeRate {
|
||||||
|
max_increase: Perbill::from_percent(50),
|
||||||
|
min_delay: 1000u32.into(),
|
||||||
|
})
|
||||||
|
verify {
|
||||||
|
assert_eq!(
|
||||||
|
BondedPools::<T>::get(1).unwrap().commission, Commission {
|
||||||
|
current: None,
|
||||||
|
max: None,
|
||||||
|
change_rate: Some(CommissionChangeRate {
|
||||||
|
max_increase: Perbill::from_percent(50),
|
||||||
|
min_delay: 1000u32.into(),
|
||||||
|
}),
|
||||||
|
throttle_from: Some(1_u32.into()),
|
||||||
|
claim_permission: None,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
set_commission_claim_permission {
|
||||||
|
// Create a pool.
|
||||||
|
let (depositor, pool_account) = create_pool_account::<T>(0, Pools::<T>::depositor_min_bond() * 2u32.into(), None);
|
||||||
|
}:_(RuntimeOrigin::Signed(depositor.clone()), 1u32.into(), Some(CommissionClaimPermission::Account(depositor.clone())))
|
||||||
|
verify {
|
||||||
|
assert_eq!(
|
||||||
|
BondedPools::<T>::get(1).unwrap().commission, Commission {
|
||||||
|
current: None,
|
||||||
|
max: None,
|
||||||
|
change_rate: None,
|
||||||
|
throttle_from: None,
|
||||||
|
claim_permission: Some(CommissionClaimPermission::Account(depositor)),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
set_claim_permission {
|
||||||
|
// Create a pool
|
||||||
|
let min_create_bond = Pools::<T>::depositor_min_bond();
|
||||||
|
let (depositor, pool_account) = create_pool_account::<T>(0, min_create_bond, None);
|
||||||
|
|
||||||
|
// Join pool
|
||||||
|
let min_join_bond = MinJoinBond::<T>::get().max(CurrencyOf::<T>::minimum_balance());
|
||||||
|
let joiner = create_funded_user_with_balance::<T>("joiner", 0, min_join_bond * 4u32.into());
|
||||||
|
let joiner_lookup = T::Lookup::unlookup(joiner.clone());
|
||||||
|
Pools::<T>::join(RuntimeOrigin::Signed(joiner.clone()).into(), min_join_bond, 1)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// Sanity check join worked
|
||||||
|
assert_eq!(
|
||||||
|
T::Staking::active_stake(&pool_account).unwrap(),
|
||||||
|
min_create_bond + min_join_bond
|
||||||
|
);
|
||||||
|
}:_(RuntimeOrigin::Signed(joiner.clone()), ClaimPermission::Permissioned)
|
||||||
|
verify {
|
||||||
|
assert_eq!(ClaimPermissions::<T>::get(joiner), ClaimPermission::Permissioned);
|
||||||
|
}
|
||||||
|
|
||||||
|
claim_commission {
|
||||||
|
let claimer: T::AccountId = account("claimer_member", USER_SEED + 4, 0);
|
||||||
|
let commission = Perbill::from_percent(50);
|
||||||
|
let origin_weight = Pools::<T>::depositor_min_bond() * 2u32.into();
|
||||||
|
let ed = CurrencyOf::<T>::minimum_balance();
|
||||||
|
let (depositor, pool_account) = create_pool_account::<T>(0, origin_weight, Some(commission));
|
||||||
|
let reward_account = Pools::<T>::create_reward_account(1);
|
||||||
|
CurrencyOf::<T>::set_balance(&reward_account, ed + origin_weight);
|
||||||
|
|
||||||
|
// member claims a payout to make some commission available.
|
||||||
|
let _ = Pools::<T>::claim_payout(RuntimeOrigin::Signed(claimer.clone()).into());
|
||||||
|
// set a claim permission to an account.
|
||||||
|
let _ = Pools::<T>::set_commission_claim_permission(
|
||||||
|
RuntimeOrigin::Signed(depositor.clone()).into(),
|
||||||
|
1u32.into(),
|
||||||
|
Some(CommissionClaimPermission::Account(claimer))
|
||||||
|
);
|
||||||
|
whitelist_account!(depositor);
|
||||||
|
}:_(RuntimeOrigin::Signed(depositor.clone()), 1u32.into())
|
||||||
|
verify {
|
||||||
|
assert_eq!(
|
||||||
|
CurrencyOf::<T>::balance(&depositor),
|
||||||
|
origin_weight + commission * origin_weight
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
CurrencyOf::<T>::balance(&reward_account),
|
||||||
|
ed + commission * origin_weight
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
adjust_pool_deposit {
|
||||||
|
// Create a pool
|
||||||
|
let (depositor, _) = create_pool_account::<T>(0, Pools::<T>::depositor_min_bond() * 2u32.into(), None);
|
||||||
|
|
||||||
|
// Remove ed freeze to create a scenario where the ed deposit needs to be adjusted.
|
||||||
|
let _ = Pools::<T>::unfreeze_pool_deposit(&Pools::<T>::create_reward_account(1));
|
||||||
|
assert!(&Pools::<T>::check_ed_imbalance().is_err());
|
||||||
|
|
||||||
|
whitelist_account!(depositor);
|
||||||
|
}:_(RuntimeOrigin::Signed(depositor), 1)
|
||||||
|
verify {
|
||||||
|
assert!(&Pools::<T>::check_ed_imbalance().is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_benchmark_test_suite!(
|
||||||
|
Pallet,
|
||||||
|
crate::mock::new_test_ext(),
|
||||||
|
crate::mock::Runtime
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -17,836 +17,13 @@
|
|||||||
|
|
||||||
//! Benchmarks for the nomination pools coupled with the staking and bags list pallets.
|
//! Benchmarks for the nomination pools coupled with the staking and bags list pallets.
|
||||||
|
|
||||||
#![cfg(feature = "runtime-benchmarks")]
|
|
||||||
#![cfg_attr(not(feature = "std"), no_std)]
|
#![cfg_attr(not(feature = "std"), no_std)]
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(feature = "runtime-benchmarks")]
|
||||||
mod mock;
|
pub mod inner;
|
||||||
|
|
||||||
use frame_benchmarking::v1::{account, whitelist_account};
|
#[cfg(feature = "runtime-benchmarks")]
|
||||||
use frame_election_provider_support::SortedListProvider;
|
pub use inner::*;
|
||||||
use frame_support::{
|
|
||||||
assert_ok, ensure,
|
|
||||||
traits::{
|
|
||||||
fungible::{Inspect, Mutate, Unbalanced},
|
|
||||||
Get,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
use frame_system::RawOrigin as RuntimeOrigin;
|
|
||||||
use pallet_nomination_pools::{
|
|
||||||
BalanceOf, BondExtra, BondedPoolInner, BondedPools, ClaimPermission, ClaimPermissions,
|
|
||||||
Commission, CommissionChangeRate, CommissionClaimPermission, ConfigOp, GlobalMaxCommission,
|
|
||||||
MaxPoolMembers, MaxPoolMembersPerPool, MaxPools, Metadata, MinCreateBond, MinJoinBond,
|
|
||||||
Pallet as Pools, PoolMembers, PoolRoles, PoolState, RewardPools, SubPoolsStorage,
|
|
||||||
};
|
|
||||||
use pallet_staking::MaxNominationsOf;
|
|
||||||
use sp_runtime::{
|
|
||||||
traits::{Bounded, StaticLookup, Zero},
|
|
||||||
Perbill,
|
|
||||||
};
|
|
||||||
use sp_staking::{EraIndex, StakingInterface};
|
|
||||||
use sp_std::{vec, vec::Vec};
|
|
||||||
// `frame_benchmarking::benchmarks!` macro needs this
|
|
||||||
use pallet_nomination_pools::Call;
|
|
||||||
|
|
||||||
type CurrencyOf<T> = <T as pallet_nomination_pools::Config>::Currency;
|
#[cfg(all(feature = "runtime-benchmarks", test))]
|
||||||
|
pub(crate) mod mock;
|
||||||
const USER_SEED: u32 = 0;
|
|
||||||
const MAX_SPANS: u32 = 100;
|
|
||||||
|
|
||||||
type VoterBagsListInstance = pallet_bags_list::Instance1;
|
|
||||||
pub trait Config:
|
|
||||||
pallet_nomination_pools::Config
|
|
||||||
+ pallet_staking::Config
|
|
||||||
+ pallet_bags_list::Config<VoterBagsListInstance>
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Pallet<T: Config>(Pools<T>);
|
|
||||||
|
|
||||||
fn create_funded_user_with_balance<T: pallet_nomination_pools::Config>(
|
|
||||||
string: &'static str,
|
|
||||||
n: u32,
|
|
||||||
balance: BalanceOf<T>,
|
|
||||||
) -> T::AccountId {
|
|
||||||
let user = account(string, n, USER_SEED);
|
|
||||||
T::Currency::set_balance(&user, balance);
|
|
||||||
user
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a bonded pool account, bonding `balance` and giving the account `balance * 2` free
|
|
||||||
// balance.
|
|
||||||
fn create_pool_account<T: pallet_nomination_pools::Config>(
|
|
||||||
n: u32,
|
|
||||||
balance: BalanceOf<T>,
|
|
||||||
commission: Option<Perbill>,
|
|
||||||
) -> (T::AccountId, T::AccountId) {
|
|
||||||
let ed = CurrencyOf::<T>::minimum_balance();
|
|
||||||
let pool_creator: T::AccountId =
|
|
||||||
create_funded_user_with_balance::<T>("pool_creator", n, ed + balance * 2u32.into());
|
|
||||||
let pool_creator_lookup = T::Lookup::unlookup(pool_creator.clone());
|
|
||||||
|
|
||||||
Pools::<T>::create(
|
|
||||||
RuntimeOrigin::Signed(pool_creator.clone()).into(),
|
|
||||||
balance,
|
|
||||||
pool_creator_lookup.clone(),
|
|
||||||
pool_creator_lookup.clone(),
|
|
||||||
pool_creator_lookup,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
if let Some(c) = commission {
|
|
||||||
let pool_id = pallet_nomination_pools::LastPoolId::<T>::get();
|
|
||||||
Pools::<T>::set_commission(
|
|
||||||
RuntimeOrigin::Signed(pool_creator.clone()).into(),
|
|
||||||
pool_id,
|
|
||||||
Some((c, pool_creator.clone())),
|
|
||||||
)
|
|
||||||
.expect("pool just created, commission can be set by root; qed");
|
|
||||||
}
|
|
||||||
|
|
||||||
let pool_account = pallet_nomination_pools::BondedPools::<T>::iter()
|
|
||||||
.find(|(_, bonded_pool)| bonded_pool.roles.depositor == pool_creator)
|
|
||||||
.map(|(pool_id, _)| Pools::<T>::create_bonded_account(pool_id))
|
|
||||||
.expect("pool_creator created a pool above");
|
|
||||||
|
|
||||||
(pool_creator, pool_account)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn vote_to_balance<T: pallet_nomination_pools::Config>(
|
|
||||||
vote: u64,
|
|
||||||
) -> Result<BalanceOf<T>, &'static str> {
|
|
||||||
vote.try_into().map_err(|_| "could not convert u64 to Balance")
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(unused)]
|
|
||||||
struct ListScenario<T: pallet_nomination_pools::Config> {
|
|
||||||
/// Stash/Controller that is expected to be moved.
|
|
||||||
origin1: T::AccountId,
|
|
||||||
creator1: T::AccountId,
|
|
||||||
dest_weight: BalanceOf<T>,
|
|
||||||
origin1_member: Option<T::AccountId>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Config> ListScenario<T> {
|
|
||||||
/// An expensive scenario for bags-list implementation:
|
|
||||||
///
|
|
||||||
/// - the node to be updated (r) is the head of a bag that has at least one other node. The bag
|
|
||||||
/// itself will need to be read and written to update its head. The node pointed to by r.next
|
|
||||||
/// will need to be read and written as it will need to have its prev pointer updated. Note
|
|
||||||
/// that there are two other worst case scenarios for bag removal: 1) the node is a tail and
|
|
||||||
/// 2) the node is a middle node with prev and next; all scenarios end up with the same number
|
|
||||||
/// of storage reads and writes.
|
|
||||||
///
|
|
||||||
/// - the destination bag has at least one node, which will need its next pointer updated.
|
|
||||||
pub(crate) fn new(
|
|
||||||
origin_weight: BalanceOf<T>,
|
|
||||||
is_increase: bool,
|
|
||||||
) -> Result<Self, &'static str> {
|
|
||||||
ensure!(!origin_weight.is_zero(), "origin weight must be greater than 0");
|
|
||||||
|
|
||||||
ensure!(
|
|
||||||
pallet_nomination_pools::MaxPools::<T>::get().unwrap_or(0) >= 3,
|
|
||||||
"must allow at least three pools for benchmarks"
|
|
||||||
);
|
|
||||||
|
|
||||||
// Burn the entire issuance.
|
|
||||||
CurrencyOf::<T>::set_total_issuance(Zero::zero());
|
|
||||||
|
|
||||||
// Create accounts with the origin weight
|
|
||||||
let (pool_creator1, pool_origin1) =
|
|
||||||
create_pool_account::<T>(USER_SEED + 1, origin_weight, Some(Perbill::from_percent(50)));
|
|
||||||
|
|
||||||
T::Staking::nominate(
|
|
||||||
&pool_origin1,
|
|
||||||
// NOTE: these don't really need to be validators.
|
|
||||||
vec![account("random_validator", 0, USER_SEED)],
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let (_, pool_origin2) =
|
|
||||||
create_pool_account::<T>(USER_SEED + 2, origin_weight, Some(Perbill::from_percent(50)));
|
|
||||||
|
|
||||||
T::Staking::nominate(
|
|
||||||
&pool_origin2,
|
|
||||||
vec![account("random_validator", 0, USER_SEED)].clone(),
|
|
||||||
)?;
|
|
||||||
|
|
||||||
// Find a destination weight that will trigger the worst case scenario
|
|
||||||
let dest_weight_as_vote = <T as pallet_staking::Config>::VoterList::score_update_worst_case(
|
|
||||||
&pool_origin1,
|
|
||||||
is_increase,
|
|
||||||
);
|
|
||||||
|
|
||||||
let dest_weight: BalanceOf<T> =
|
|
||||||
dest_weight_as_vote.try_into().map_err(|_| "could not convert u64 to Balance")?;
|
|
||||||
|
|
||||||
// Create an account with the worst case destination weight
|
|
||||||
let (_, pool_dest1) =
|
|
||||||
create_pool_account::<T>(USER_SEED + 3, dest_weight, Some(Perbill::from_percent(50)));
|
|
||||||
|
|
||||||
T::Staking::nominate(&pool_dest1, vec![account("random_validator", 0, USER_SEED)])?;
|
|
||||||
|
|
||||||
let weight_of = pallet_staking::Pallet::<T>::weight_of_fn();
|
|
||||||
assert_eq!(vote_to_balance::<T>(weight_of(&pool_origin1)).unwrap(), origin_weight);
|
|
||||||
assert_eq!(vote_to_balance::<T>(weight_of(&pool_origin2)).unwrap(), origin_weight);
|
|
||||||
assert_eq!(vote_to_balance::<T>(weight_of(&pool_dest1)).unwrap(), dest_weight);
|
|
||||||
|
|
||||||
Ok(ListScenario {
|
|
||||||
origin1: pool_origin1,
|
|
||||||
creator1: pool_creator1,
|
|
||||||
dest_weight,
|
|
||||||
origin1_member: None,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn add_joiner(mut self, amount: BalanceOf<T>) -> Self {
|
|
||||||
let amount = MinJoinBond::<T>::get()
|
|
||||||
.max(CurrencyOf::<T>::minimum_balance())
|
|
||||||
// Max `amount` with minimum thresholds for account balance and joining a pool
|
|
||||||
// to ensure 1) the user can be created and 2) can join the pool
|
|
||||||
.max(amount);
|
|
||||||
|
|
||||||
let joiner: T::AccountId = account("joiner", USER_SEED, 0);
|
|
||||||
self.origin1_member = Some(joiner.clone());
|
|
||||||
CurrencyOf::<T>::set_balance(&joiner, amount * 2u32.into());
|
|
||||||
|
|
||||||
let original_bonded = T::Staking::active_stake(&self.origin1).unwrap();
|
|
||||||
|
|
||||||
// Unbond `amount` from the underlying pool account so when the member joins
|
|
||||||
// we will maintain `current_bonded`.
|
|
||||||
T::Staking::unbond(&self.origin1, amount).expect("the pool was created in `Self::new`.");
|
|
||||||
|
|
||||||
// Account pool points for the unbonded balance.
|
|
||||||
BondedPools::<T>::mutate(&1, |maybe_pool| {
|
|
||||||
maybe_pool.as_mut().map(|pool| pool.points -= amount)
|
|
||||||
});
|
|
||||||
|
|
||||||
Pools::<T>::join(RuntimeOrigin::Signed(joiner.clone()).into(), amount, 1).unwrap();
|
|
||||||
|
|
||||||
// check that the vote weight is still the same as the original bonded
|
|
||||||
let weight_of = pallet_staking::Pallet::<T>::weight_of_fn();
|
|
||||||
assert_eq!(vote_to_balance::<T>(weight_of(&self.origin1)).unwrap(), original_bonded);
|
|
||||||
|
|
||||||
// check the member was added correctly
|
|
||||||
let member = PoolMembers::<T>::get(&joiner).unwrap();
|
|
||||||
assert_eq!(member.points, amount);
|
|
||||||
assert_eq!(member.pool_id, 1);
|
|
||||||
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
frame_benchmarking::benchmarks! {
|
|
||||||
join {
|
|
||||||
let origin_weight = Pools::<T>::depositor_min_bond() * 2u32.into();
|
|
||||||
|
|
||||||
// setup the worst case list scenario.
|
|
||||||
let scenario = ListScenario::<T>::new(origin_weight, true)?;
|
|
||||||
assert_eq!(
|
|
||||||
T::Staking::active_stake(&scenario.origin1).unwrap(),
|
|
||||||
origin_weight
|
|
||||||
);
|
|
||||||
|
|
||||||
let max_additional = scenario.dest_weight - origin_weight;
|
|
||||||
let joiner_free = CurrencyOf::<T>::minimum_balance() + max_additional;
|
|
||||||
|
|
||||||
let joiner: T::AccountId
|
|
||||||
= create_funded_user_with_balance::<T>("joiner", 0, joiner_free);
|
|
||||||
|
|
||||||
whitelist_account!(joiner);
|
|
||||||
}: _(RuntimeOrigin::Signed(joiner.clone()), max_additional, 1)
|
|
||||||
verify {
|
|
||||||
assert_eq!(CurrencyOf::<T>::balance(&joiner), joiner_free - max_additional);
|
|
||||||
assert_eq!(
|
|
||||||
T::Staking::active_stake(&scenario.origin1).unwrap(),
|
|
||||||
scenario.dest_weight
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
bond_extra_transfer {
|
|
||||||
let origin_weight = Pools::<T>::depositor_min_bond() * 2u32.into();
|
|
||||||
let scenario = ListScenario::<T>::new(origin_weight, true)?;
|
|
||||||
let extra = scenario.dest_weight - origin_weight;
|
|
||||||
|
|
||||||
// creator of the src pool will bond-extra, bumping itself to dest bag.
|
|
||||||
|
|
||||||
}: bond_extra(RuntimeOrigin::Signed(scenario.creator1.clone()), BondExtra::FreeBalance(extra))
|
|
||||||
verify {
|
|
||||||
assert!(
|
|
||||||
T::Staking::active_stake(&scenario.origin1).unwrap() >=
|
|
||||||
scenario.dest_weight
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
bond_extra_other {
|
|
||||||
let claimer: T::AccountId = account("claimer", USER_SEED + 4, 0);
|
|
||||||
|
|
||||||
let origin_weight = Pools::<T>::depositor_min_bond() * 2u32.into();
|
|
||||||
let scenario = ListScenario::<T>::new(origin_weight, true)?;
|
|
||||||
let extra = (scenario.dest_weight - origin_weight).max(CurrencyOf::<T>::minimum_balance());
|
|
||||||
|
|
||||||
// set claim preferences to `PermissionlessAll` to any account to bond extra on member's behalf.
|
|
||||||
let _ = Pools::<T>::set_claim_permission(RuntimeOrigin::Signed(scenario.creator1.clone()).into(), ClaimPermission::PermissionlessAll);
|
|
||||||
|
|
||||||
// transfer exactly `extra` to the depositor of the src pool (1),
|
|
||||||
let reward_account1 = Pools::<T>::create_reward_account(1);
|
|
||||||
assert!(extra >= CurrencyOf::<T>::minimum_balance());
|
|
||||||
let _ = CurrencyOf::<T>::mint_into(&reward_account1, extra);
|
|
||||||
|
|
||||||
}: _(RuntimeOrigin::Signed(claimer), T::Lookup::unlookup(scenario.creator1.clone()), BondExtra::Rewards)
|
|
||||||
verify {
|
|
||||||
// commission of 50% deducted here.
|
|
||||||
assert!(
|
|
||||||
T::Staking::active_stake(&scenario.origin1).unwrap() >=
|
|
||||||
scenario.dest_weight / 2u32.into()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
claim_payout {
|
|
||||||
let claimer: T::AccountId = account("claimer", USER_SEED + 4, 0);
|
|
||||||
let commission = Perbill::from_percent(50);
|
|
||||||
let origin_weight = Pools::<T>::depositor_min_bond() * 2u32.into();
|
|
||||||
let ed = CurrencyOf::<T>::minimum_balance();
|
|
||||||
let (depositor, pool_account) = create_pool_account::<T>(0, origin_weight, Some(commission));
|
|
||||||
let reward_account = Pools::<T>::create_reward_account(1);
|
|
||||||
|
|
||||||
// Send funds to the reward account of the pool
|
|
||||||
CurrencyOf::<T>::set_balance(&reward_account, ed + origin_weight);
|
|
||||||
|
|
||||||
// set claim preferences to `PermissionlessAll` so any account can claim rewards on member's
|
|
||||||
// behalf.
|
|
||||||
let _ = Pools::<T>::set_claim_permission(RuntimeOrigin::Signed(depositor.clone()).into(), ClaimPermission::PermissionlessAll);
|
|
||||||
|
|
||||||
// Sanity check
|
|
||||||
assert_eq!(
|
|
||||||
CurrencyOf::<T>::balance(&depositor),
|
|
||||||
origin_weight
|
|
||||||
);
|
|
||||||
|
|
||||||
whitelist_account!(depositor);
|
|
||||||
}:claim_payout_other(RuntimeOrigin::Signed(claimer), depositor.clone())
|
|
||||||
verify {
|
|
||||||
assert_eq!(
|
|
||||||
CurrencyOf::<T>::balance(&depositor),
|
|
||||||
origin_weight + commission * origin_weight
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
CurrencyOf::<T>::balance(&reward_account),
|
|
||||||
ed + commission * origin_weight
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
unbond {
|
|
||||||
// The weight the nominator will start at. The value used here is expected to be
|
|
||||||
// significantly higher than the first position in a list (e.g. the first bag threshold).
|
|
||||||
let origin_weight = Pools::<T>::depositor_min_bond() * 200u32.into();
|
|
||||||
let scenario = ListScenario::<T>::new(origin_weight, false)?;
|
|
||||||
let amount = origin_weight - scenario.dest_weight;
|
|
||||||
|
|
||||||
let scenario = scenario.add_joiner(amount);
|
|
||||||
let member_id = scenario.origin1_member.unwrap().clone();
|
|
||||||
let member_id_lookup = T::Lookup::unlookup(member_id.clone());
|
|
||||||
let all_points = PoolMembers::<T>::get(&member_id).unwrap().points;
|
|
||||||
whitelist_account!(member_id);
|
|
||||||
}: _(RuntimeOrigin::Signed(member_id.clone()), member_id_lookup, all_points)
|
|
||||||
verify {
|
|
||||||
let bonded_after = T::Staking::active_stake(&scenario.origin1).unwrap();
|
|
||||||
// We at least went down to the destination bag
|
|
||||||
assert!(bonded_after <= scenario.dest_weight);
|
|
||||||
let member = PoolMembers::<T>::get(
|
|
||||||
&member_id
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
assert_eq!(
|
|
||||||
member.unbonding_eras.keys().cloned().collect::<Vec<_>>(),
|
|
||||||
vec![0 + T::Staking::bonding_duration()]
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
member.unbonding_eras.values().cloned().collect::<Vec<_>>(),
|
|
||||||
vec![all_points]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
pool_withdraw_unbonded {
|
|
||||||
let s in 0 .. MAX_SPANS;
|
|
||||||
|
|
||||||
let min_create_bond = Pools::<T>::depositor_min_bond();
|
|
||||||
let (depositor, pool_account) = create_pool_account::<T>(0, min_create_bond, None);
|
|
||||||
|
|
||||||
// Add a new member
|
|
||||||
let min_join_bond = MinJoinBond::<T>::get().max(CurrencyOf::<T>::minimum_balance());
|
|
||||||
let joiner = create_funded_user_with_balance::<T>("joiner", 0, min_join_bond * 2u32.into());
|
|
||||||
Pools::<T>::join(RuntimeOrigin::Signed(joiner.clone()).into(), min_join_bond, 1)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// Sanity check join worked
|
|
||||||
assert_eq!(
|
|
||||||
T::Staking::active_stake(&pool_account).unwrap(),
|
|
||||||
min_create_bond + min_join_bond
|
|
||||||
);
|
|
||||||
assert_eq!(CurrencyOf::<T>::balance(&joiner), min_join_bond);
|
|
||||||
|
|
||||||
// Unbond the new member
|
|
||||||
Pools::<T>::fully_unbond(RuntimeOrigin::Signed(joiner.clone()).into(), joiner.clone()).unwrap();
|
|
||||||
|
|
||||||
// Sanity check that unbond worked
|
|
||||||
assert_eq!(
|
|
||||||
T::Staking::active_stake(&pool_account).unwrap(),
|
|
||||||
min_create_bond
|
|
||||||
);
|
|
||||||
assert_eq!(pallet_staking::Ledger::<T>::get(&pool_account).unwrap().unlocking.len(), 1);
|
|
||||||
// Set the current era
|
|
||||||
pallet_staking::CurrentEra::<T>::put(EraIndex::max_value());
|
|
||||||
|
|
||||||
// Add `s` count of slashing spans to storage.
|
|
||||||
pallet_staking::benchmarking::add_slashing_spans::<T>(&pool_account, s);
|
|
||||||
whitelist_account!(pool_account);
|
|
||||||
}: _(RuntimeOrigin::Signed(pool_account.clone()), 1, s)
|
|
||||||
verify {
|
|
||||||
// The joiners funds didn't change
|
|
||||||
assert_eq!(CurrencyOf::<T>::balance(&joiner), min_join_bond);
|
|
||||||
// The unlocking chunk was removed
|
|
||||||
assert_eq!(pallet_staking::Ledger::<T>::get(pool_account).unwrap().unlocking.len(), 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
withdraw_unbonded_update {
|
|
||||||
let s in 0 .. MAX_SPANS;
|
|
||||||
|
|
||||||
let min_create_bond = Pools::<T>::depositor_min_bond();
|
|
||||||
let (depositor, pool_account) = create_pool_account::<T>(0, min_create_bond, None);
|
|
||||||
|
|
||||||
// Add a new member
|
|
||||||
let min_join_bond = MinJoinBond::<T>::get().max(CurrencyOf::<T>::minimum_balance());
|
|
||||||
let joiner = create_funded_user_with_balance::<T>("joiner", 0, min_join_bond * 2u32.into());
|
|
||||||
let joiner_lookup = T::Lookup::unlookup(joiner.clone());
|
|
||||||
Pools::<T>::join(RuntimeOrigin::Signed(joiner.clone()).into(), min_join_bond, 1)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// Sanity check join worked
|
|
||||||
assert_eq!(
|
|
||||||
T::Staking::active_stake(&pool_account).unwrap(),
|
|
||||||
min_create_bond + min_join_bond
|
|
||||||
);
|
|
||||||
assert_eq!(CurrencyOf::<T>::balance(&joiner), min_join_bond);
|
|
||||||
|
|
||||||
// Unbond the new member
|
|
||||||
pallet_staking::CurrentEra::<T>::put(0);
|
|
||||||
Pools::<T>::fully_unbond(RuntimeOrigin::Signed(joiner.clone()).into(), joiner.clone()).unwrap();
|
|
||||||
|
|
||||||
// Sanity check that unbond worked
|
|
||||||
assert_eq!(
|
|
||||||
T::Staking::active_stake(&pool_account).unwrap(),
|
|
||||||
min_create_bond
|
|
||||||
);
|
|
||||||
assert_eq!(pallet_staking::Ledger::<T>::get(&pool_account).unwrap().unlocking.len(), 1);
|
|
||||||
|
|
||||||
// Set the current era to ensure we can withdraw unbonded funds
|
|
||||||
pallet_staking::CurrentEra::<T>::put(EraIndex::max_value());
|
|
||||||
|
|
||||||
pallet_staking::benchmarking::add_slashing_spans::<T>(&pool_account, s);
|
|
||||||
whitelist_account!(joiner);
|
|
||||||
}: withdraw_unbonded(RuntimeOrigin::Signed(joiner.clone()), joiner_lookup, s)
|
|
||||||
verify {
|
|
||||||
assert_eq!(
|
|
||||||
CurrencyOf::<T>::balance(&joiner), min_join_bond * 2u32.into()
|
|
||||||
);
|
|
||||||
// The unlocking chunk was removed
|
|
||||||
assert_eq!(pallet_staking::Ledger::<T>::get(&pool_account).unwrap().unlocking.len(), 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
withdraw_unbonded_kill {
|
|
||||||
let s in 0 .. MAX_SPANS;
|
|
||||||
|
|
||||||
let min_create_bond = Pools::<T>::depositor_min_bond();
|
|
||||||
let (depositor, pool_account) = create_pool_account::<T>(0, min_create_bond, None);
|
|
||||||
let depositor_lookup = T::Lookup::unlookup(depositor.clone());
|
|
||||||
|
|
||||||
// We set the pool to the destroying state so the depositor can leave
|
|
||||||
BondedPools::<T>::try_mutate(&1, |maybe_bonded_pool| {
|
|
||||||
maybe_bonded_pool.as_mut().ok_or(()).map(|bonded_pool| {
|
|
||||||
bonded_pool.state = PoolState::Destroying;
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// Unbond the creator
|
|
||||||
pallet_staking::CurrentEra::<T>::put(0);
|
|
||||||
// Simulate some rewards so we can check if the rewards storage is cleaned up. We check this
|
|
||||||
// here to ensure the complete flow for destroying a pool works - the reward pool account
|
|
||||||
// should never exist by time the depositor withdraws so we test that it gets cleaned
|
|
||||||
// up when unbonding.
|
|
||||||
let reward_account = Pools::<T>::create_reward_account(1);
|
|
||||||
assert!(frame_system::Account::<T>::contains_key(&reward_account));
|
|
||||||
Pools::<T>::fully_unbond(RuntimeOrigin::Signed(depositor.clone()).into(), depositor.clone()).unwrap();
|
|
||||||
|
|
||||||
// Sanity check that unbond worked
|
|
||||||
assert_eq!(
|
|
||||||
T::Staking::active_stake(&pool_account).unwrap(),
|
|
||||||
Zero::zero()
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
CurrencyOf::<T>::balance(&pool_account),
|
|
||||||
min_create_bond
|
|
||||||
);
|
|
||||||
assert_eq!(pallet_staking::Ledger::<T>::get(&pool_account).unwrap().unlocking.len(), 1);
|
|
||||||
|
|
||||||
// Set the current era to ensure we can withdraw unbonded funds
|
|
||||||
pallet_staking::CurrentEra::<T>::put(EraIndex::max_value());
|
|
||||||
|
|
||||||
// Some last checks that storage items we expect to get cleaned up are present
|
|
||||||
assert!(pallet_staking::Ledger::<T>::contains_key(&pool_account));
|
|
||||||
assert!(BondedPools::<T>::contains_key(&1));
|
|
||||||
assert!(SubPoolsStorage::<T>::contains_key(&1));
|
|
||||||
assert!(RewardPools::<T>::contains_key(&1));
|
|
||||||
assert!(PoolMembers::<T>::contains_key(&depositor));
|
|
||||||
assert!(frame_system::Account::<T>::contains_key(&reward_account));
|
|
||||||
|
|
||||||
whitelist_account!(depositor);
|
|
||||||
}: withdraw_unbonded(RuntimeOrigin::Signed(depositor.clone()), depositor_lookup, s)
|
|
||||||
verify {
|
|
||||||
// Pool removal worked
|
|
||||||
assert!(!pallet_staking::Ledger::<T>::contains_key(&pool_account));
|
|
||||||
assert!(!BondedPools::<T>::contains_key(&1));
|
|
||||||
assert!(!SubPoolsStorage::<T>::contains_key(&1));
|
|
||||||
assert!(!RewardPools::<T>::contains_key(&1));
|
|
||||||
assert!(!PoolMembers::<T>::contains_key(&depositor));
|
|
||||||
assert!(!frame_system::Account::<T>::contains_key(&pool_account));
|
|
||||||
assert!(!frame_system::Account::<T>::contains_key(&reward_account));
|
|
||||||
|
|
||||||
// Funds where transferred back correctly
|
|
||||||
assert_eq!(
|
|
||||||
CurrencyOf::<T>::balance(&depositor),
|
|
||||||
// gets bond back + rewards collecting when unbonding
|
|
||||||
min_create_bond * 2u32.into() + CurrencyOf::<T>::minimum_balance()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
create {
|
|
||||||
let min_create_bond = Pools::<T>::depositor_min_bond();
|
|
||||||
let depositor: T::AccountId = account("depositor", USER_SEED, 0);
|
|
||||||
let depositor_lookup = T::Lookup::unlookup(depositor.clone());
|
|
||||||
|
|
||||||
// Give the depositor some balance to bond
|
|
||||||
CurrencyOf::<T>::set_balance(&depositor, min_create_bond * 2u32.into());
|
|
||||||
|
|
||||||
// Make sure no Pools exist at a pre-condition for our verify checks
|
|
||||||
assert_eq!(RewardPools::<T>::count(), 0);
|
|
||||||
assert_eq!(BondedPools::<T>::count(), 0);
|
|
||||||
|
|
||||||
whitelist_account!(depositor);
|
|
||||||
}: _(
|
|
||||||
RuntimeOrigin::Signed(depositor.clone()),
|
|
||||||
min_create_bond,
|
|
||||||
depositor_lookup.clone(),
|
|
||||||
depositor_lookup.clone(),
|
|
||||||
depositor_lookup
|
|
||||||
)
|
|
||||||
verify {
|
|
||||||
assert_eq!(RewardPools::<T>::count(), 1);
|
|
||||||
assert_eq!(BondedPools::<T>::count(), 1);
|
|
||||||
let (_, new_pool) = BondedPools::<T>::iter().next().unwrap();
|
|
||||||
assert_eq!(
|
|
||||||
new_pool,
|
|
||||||
BondedPoolInner {
|
|
||||||
commission: Commission::default(),
|
|
||||||
member_counter: 1,
|
|
||||||
points: min_create_bond,
|
|
||||||
roles: PoolRoles {
|
|
||||||
depositor: depositor.clone(),
|
|
||||||
root: Some(depositor.clone()),
|
|
||||||
nominator: Some(depositor.clone()),
|
|
||||||
bouncer: Some(depositor.clone()),
|
|
||||||
},
|
|
||||||
state: PoolState::Open,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
T::Staking::active_stake(&Pools::<T>::create_bonded_account(1)),
|
|
||||||
Ok(min_create_bond)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
nominate {
|
|
||||||
let n in 1 .. MaxNominationsOf::<T>::get();
|
|
||||||
|
|
||||||
// Create a pool
|
|
||||||
let min_create_bond = Pools::<T>::depositor_min_bond() * 2u32.into();
|
|
||||||
let (depositor, pool_account) = create_pool_account::<T>(0, min_create_bond, None);
|
|
||||||
|
|
||||||
// Create some accounts to nominate. For the sake of benchmarking they don't need to be
|
|
||||||
// actual validators
|
|
||||||
let validators: Vec<_> = (0..n)
|
|
||||||
.map(|i| account("stash", USER_SEED, i))
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
whitelist_account!(depositor);
|
|
||||||
}:_(RuntimeOrigin::Signed(depositor.clone()), 1, validators)
|
|
||||||
verify {
|
|
||||||
assert_eq!(RewardPools::<T>::count(), 1);
|
|
||||||
assert_eq!(BondedPools::<T>::count(), 1);
|
|
||||||
let (_, new_pool) = BondedPools::<T>::iter().next().unwrap();
|
|
||||||
assert_eq!(
|
|
||||||
new_pool,
|
|
||||||
BondedPoolInner {
|
|
||||||
commission: Commission::default(),
|
|
||||||
member_counter: 1,
|
|
||||||
points: min_create_bond,
|
|
||||||
roles: PoolRoles {
|
|
||||||
depositor: depositor.clone(),
|
|
||||||
root: Some(depositor.clone()),
|
|
||||||
nominator: Some(depositor.clone()),
|
|
||||||
bouncer: Some(depositor.clone()),
|
|
||||||
},
|
|
||||||
state: PoolState::Open,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
T::Staking::active_stake(&Pools::<T>::create_bonded_account(1)),
|
|
||||||
Ok(min_create_bond)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
set_state {
|
|
||||||
// Create a pool
|
|
||||||
let min_create_bond = Pools::<T>::depositor_min_bond();
|
|
||||||
let (depositor, pool_account) = create_pool_account::<T>(0, min_create_bond, None);
|
|
||||||
BondedPools::<T>::mutate(&1, |maybe_pool| {
|
|
||||||
// Force the pool into an invalid state
|
|
||||||
maybe_pool.as_mut().map(|pool| pool.points = min_create_bond * 10u32.into());
|
|
||||||
});
|
|
||||||
|
|
||||||
let caller = account("caller", 0, USER_SEED);
|
|
||||||
whitelist_account!(caller);
|
|
||||||
}:_(RuntimeOrigin::Signed(caller), 1, PoolState::Destroying)
|
|
||||||
verify {
|
|
||||||
assert_eq!(BondedPools::<T>::get(1).unwrap().state, PoolState::Destroying);
|
|
||||||
}
|
|
||||||
|
|
||||||
set_metadata {
|
|
||||||
let n in 1 .. <T as pallet_nomination_pools::Config>::MaxMetadataLen::get();
|
|
||||||
|
|
||||||
// Create a pool
|
|
||||||
let (depositor, pool_account) = create_pool_account::<T>(0, Pools::<T>::depositor_min_bond() * 2u32.into(), None);
|
|
||||||
|
|
||||||
// Create metadata of the max possible size
|
|
||||||
let metadata: Vec<u8> = (0..n).map(|_| 42).collect();
|
|
||||||
|
|
||||||
whitelist_account!(depositor);
|
|
||||||
}:_(RuntimeOrigin::Signed(depositor), 1, metadata.clone())
|
|
||||||
verify {
|
|
||||||
assert_eq!(Metadata::<T>::get(&1), metadata);
|
|
||||||
}
|
|
||||||
|
|
||||||
set_configs {
|
|
||||||
}:_(
|
|
||||||
RuntimeOrigin::Root,
|
|
||||||
ConfigOp::Set(BalanceOf::<T>::max_value()),
|
|
||||||
ConfigOp::Set(BalanceOf::<T>::max_value()),
|
|
||||||
ConfigOp::Set(u32::MAX),
|
|
||||||
ConfigOp::Set(u32::MAX),
|
|
||||||
ConfigOp::Set(u32::MAX),
|
|
||||||
ConfigOp::Set(Perbill::max_value())
|
|
||||||
) verify {
|
|
||||||
assert_eq!(MinJoinBond::<T>::get(), BalanceOf::<T>::max_value());
|
|
||||||
assert_eq!(MinCreateBond::<T>::get(), BalanceOf::<T>::max_value());
|
|
||||||
assert_eq!(MaxPools::<T>::get(), Some(u32::MAX));
|
|
||||||
assert_eq!(MaxPoolMembers::<T>::get(), Some(u32::MAX));
|
|
||||||
assert_eq!(MaxPoolMembersPerPool::<T>::get(), Some(u32::MAX));
|
|
||||||
assert_eq!(GlobalMaxCommission::<T>::get(), Some(Perbill::max_value()));
|
|
||||||
}
|
|
||||||
|
|
||||||
update_roles {
|
|
||||||
let first_id = pallet_nomination_pools::LastPoolId::<T>::get() + 1;
|
|
||||||
let (root, _) = create_pool_account::<T>(0, Pools::<T>::depositor_min_bond() * 2u32.into(), None);
|
|
||||||
let random: T::AccountId = account("but is anything really random in computers..?", 0, USER_SEED);
|
|
||||||
}:_(
|
|
||||||
RuntimeOrigin::Signed(root.clone()),
|
|
||||||
first_id,
|
|
||||||
ConfigOp::Set(random.clone()),
|
|
||||||
ConfigOp::Set(random.clone()),
|
|
||||||
ConfigOp::Set(random.clone())
|
|
||||||
) verify {
|
|
||||||
assert_eq!(
|
|
||||||
pallet_nomination_pools::BondedPools::<T>::get(first_id).unwrap().roles,
|
|
||||||
pallet_nomination_pools::PoolRoles {
|
|
||||||
depositor: root,
|
|
||||||
nominator: Some(random.clone()),
|
|
||||||
bouncer: Some(random.clone()),
|
|
||||||
root: Some(random),
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
chill {
|
|
||||||
// Create a pool
|
|
||||||
let (depositor, pool_account) = create_pool_account::<T>(0, Pools::<T>::depositor_min_bond() * 2u32.into(), None);
|
|
||||||
|
|
||||||
// Nominate with the pool.
|
|
||||||
let validators: Vec<_> = (0..MaxNominationsOf::<T>::get())
|
|
||||||
.map(|i| account("stash", USER_SEED, i))
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
assert_ok!(T::Staking::nominate(&pool_account, validators));
|
|
||||||
assert!(T::Staking::nominations(&Pools::<T>::create_bonded_account(1)).is_some());
|
|
||||||
|
|
||||||
whitelist_account!(depositor);
|
|
||||||
}:_(RuntimeOrigin::Signed(depositor.clone()), 1)
|
|
||||||
verify {
|
|
||||||
assert!(T::Staking::nominations(&Pools::<T>::create_bonded_account(1)).is_none());
|
|
||||||
}
|
|
||||||
|
|
||||||
set_commission {
|
|
||||||
// Create a pool - do not set a commission yet.
|
|
||||||
let (depositor, pool_account) = create_pool_account::<T>(0, Pools::<T>::depositor_min_bond() * 2u32.into(), None);
|
|
||||||
// set a max commission
|
|
||||||
Pools::<T>::set_commission_max(RuntimeOrigin::Signed(depositor.clone()).into(), 1u32.into(), Perbill::from_percent(50)).unwrap();
|
|
||||||
// set a change rate
|
|
||||||
Pools::<T>::set_commission_change_rate(RuntimeOrigin::Signed(depositor.clone()).into(), 1u32.into(), CommissionChangeRate {
|
|
||||||
max_increase: Perbill::from_percent(20),
|
|
||||||
min_delay: 0u32.into(),
|
|
||||||
}).unwrap();
|
|
||||||
// set a claim permission to an account.
|
|
||||||
Pools::<T>::set_commission_claim_permission(
|
|
||||||
RuntimeOrigin::Signed(depositor.clone()).into(),
|
|
||||||
1u32.into(),
|
|
||||||
Some(CommissionClaimPermission::Account(depositor.clone()))
|
|
||||||
).unwrap();
|
|
||||||
|
|
||||||
}:_(RuntimeOrigin::Signed(depositor.clone()), 1u32.into(), Some((Perbill::from_percent(20), depositor.clone())))
|
|
||||||
verify {
|
|
||||||
assert_eq!(BondedPools::<T>::get(1).unwrap().commission, Commission {
|
|
||||||
current: Some((Perbill::from_percent(20), depositor.clone())),
|
|
||||||
max: Some(Perbill::from_percent(50)),
|
|
||||||
change_rate: Some(CommissionChangeRate {
|
|
||||||
max_increase: Perbill::from_percent(20),
|
|
||||||
min_delay: 0u32.into()
|
|
||||||
}),
|
|
||||||
throttle_from: Some(1u32.into()),
|
|
||||||
claim_permission: Some(CommissionClaimPermission::Account(depositor)),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
set_commission_max {
|
|
||||||
// Create a pool, setting a commission that will update when max commission is set.
|
|
||||||
let (depositor, pool_account) = create_pool_account::<T>(0, Pools::<T>::depositor_min_bond() * 2u32.into(), Some(Perbill::from_percent(50)));
|
|
||||||
}:_(RuntimeOrigin::Signed(depositor.clone()), 1u32.into(), Perbill::from_percent(50))
|
|
||||||
verify {
|
|
||||||
assert_eq!(
|
|
||||||
BondedPools::<T>::get(1).unwrap().commission, Commission {
|
|
||||||
current: Some((Perbill::from_percent(50), depositor)),
|
|
||||||
max: Some(Perbill::from_percent(50)),
|
|
||||||
change_rate: None,
|
|
||||||
throttle_from: Some(0u32.into()),
|
|
||||||
claim_permission: None,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
set_commission_change_rate {
|
|
||||||
// Create a pool
|
|
||||||
let (depositor, pool_account) = create_pool_account::<T>(0, Pools::<T>::depositor_min_bond() * 2u32.into(), None);
|
|
||||||
}:_(RuntimeOrigin::Signed(depositor.clone()), 1u32.into(), CommissionChangeRate {
|
|
||||||
max_increase: Perbill::from_percent(50),
|
|
||||||
min_delay: 1000u32.into(),
|
|
||||||
})
|
|
||||||
verify {
|
|
||||||
assert_eq!(
|
|
||||||
BondedPools::<T>::get(1).unwrap().commission, Commission {
|
|
||||||
current: None,
|
|
||||||
max: None,
|
|
||||||
change_rate: Some(CommissionChangeRate {
|
|
||||||
max_increase: Perbill::from_percent(50),
|
|
||||||
min_delay: 1000u32.into(),
|
|
||||||
}),
|
|
||||||
throttle_from: Some(1_u32.into()),
|
|
||||||
claim_permission: None,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
set_commission_claim_permission {
|
|
||||||
// Create a pool.
|
|
||||||
let (depositor, pool_account) = create_pool_account::<T>(0, Pools::<T>::depositor_min_bond() * 2u32.into(), None);
|
|
||||||
}:_(RuntimeOrigin::Signed(depositor.clone()), 1u32.into(), Some(CommissionClaimPermission::Account(depositor.clone())))
|
|
||||||
verify {
|
|
||||||
assert_eq!(
|
|
||||||
BondedPools::<T>::get(1).unwrap().commission, Commission {
|
|
||||||
current: None,
|
|
||||||
max: None,
|
|
||||||
change_rate: None,
|
|
||||||
throttle_from: None,
|
|
||||||
claim_permission: Some(CommissionClaimPermission::Account(depositor)),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
set_claim_permission {
|
|
||||||
// Create a pool
|
|
||||||
let min_create_bond = Pools::<T>::depositor_min_bond();
|
|
||||||
let (depositor, pool_account) = create_pool_account::<T>(0, min_create_bond, None);
|
|
||||||
|
|
||||||
// Join pool
|
|
||||||
let min_join_bond = MinJoinBond::<T>::get().max(CurrencyOf::<T>::minimum_balance());
|
|
||||||
let joiner = create_funded_user_with_balance::<T>("joiner", 0, min_join_bond * 4u32.into());
|
|
||||||
let joiner_lookup = T::Lookup::unlookup(joiner.clone());
|
|
||||||
Pools::<T>::join(RuntimeOrigin::Signed(joiner.clone()).into(), min_join_bond, 1)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// Sanity check join worked
|
|
||||||
assert_eq!(
|
|
||||||
T::Staking::active_stake(&pool_account).unwrap(),
|
|
||||||
min_create_bond + min_join_bond
|
|
||||||
);
|
|
||||||
}:_(RuntimeOrigin::Signed(joiner.clone()), ClaimPermission::Permissioned)
|
|
||||||
verify {
|
|
||||||
assert_eq!(ClaimPermissions::<T>::get(joiner), ClaimPermission::Permissioned);
|
|
||||||
}
|
|
||||||
|
|
||||||
claim_commission {
|
|
||||||
let claimer: T::AccountId = account("claimer_member", USER_SEED + 4, 0);
|
|
||||||
let commission = Perbill::from_percent(50);
|
|
||||||
let origin_weight = Pools::<T>::depositor_min_bond() * 2u32.into();
|
|
||||||
let ed = CurrencyOf::<T>::minimum_balance();
|
|
||||||
let (depositor, pool_account) = create_pool_account::<T>(0, origin_weight, Some(commission));
|
|
||||||
let reward_account = Pools::<T>::create_reward_account(1);
|
|
||||||
CurrencyOf::<T>::set_balance(&reward_account, ed + origin_weight);
|
|
||||||
|
|
||||||
// member claims a payout to make some commission available.
|
|
||||||
let _ = Pools::<T>::claim_payout(RuntimeOrigin::Signed(claimer.clone()).into());
|
|
||||||
// set a claim permission to an account.
|
|
||||||
let _ = Pools::<T>::set_commission_claim_permission(
|
|
||||||
RuntimeOrigin::Signed(depositor.clone()).into(),
|
|
||||||
1u32.into(),
|
|
||||||
Some(CommissionClaimPermission::Account(claimer))
|
|
||||||
);
|
|
||||||
whitelist_account!(depositor);
|
|
||||||
}:_(RuntimeOrigin::Signed(depositor.clone()), 1u32.into())
|
|
||||||
verify {
|
|
||||||
assert_eq!(
|
|
||||||
CurrencyOf::<T>::balance(&depositor),
|
|
||||||
origin_weight + commission * origin_weight
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
CurrencyOf::<T>::balance(&reward_account),
|
|
||||||
ed + commission * origin_weight
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
adjust_pool_deposit {
|
|
||||||
// Create a pool
|
|
||||||
let (depositor, _) = create_pool_account::<T>(0, Pools::<T>::depositor_min_bond() * 2u32.into(), None);
|
|
||||||
|
|
||||||
// Remove ed freeze to create a scenario where the ed deposit needs to be adjusted.
|
|
||||||
let _ = Pools::<T>::unfreeze_pool_deposit(&Pools::<T>::create_reward_account(1));
|
|
||||||
assert!(&Pools::<T>::check_ed_imbalance().is_err());
|
|
||||||
|
|
||||||
whitelist_account!(depositor);
|
|
||||||
}:_(RuntimeOrigin::Signed(depositor), 1)
|
|
||||||
verify {
|
|
||||||
assert!(&Pools::<T>::check_ed_imbalance().is_ok());
|
|
||||||
}
|
|
||||||
|
|
||||||
impl_benchmark_test_suite!(
|
|
||||||
Pallet,
|
|
||||||
crate::mock::new_test_ext(),
|
|
||||||
crate::mock::Runtime
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -0,0 +1,250 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
//! Offences pallet benchmarking.
|
||||||
|
|
||||||
|
use sp_std::{prelude::*, vec};
|
||||||
|
|
||||||
|
use frame_benchmarking::v1::{account, benchmarks};
|
||||||
|
use frame_support::traits::{Currency, Get};
|
||||||
|
use frame_system::{Config as SystemConfig, Pallet as System, RawOrigin};
|
||||||
|
|
||||||
|
use sp_runtime::{
|
||||||
|
traits::{Convert, Saturating, StaticLookup},
|
||||||
|
Perbill,
|
||||||
|
};
|
||||||
|
use sp_staking::offence::ReportOffence;
|
||||||
|
|
||||||
|
use pallet_babe::EquivocationOffence as BabeEquivocationOffence;
|
||||||
|
use pallet_balances::Config as BalancesConfig;
|
||||||
|
use pallet_grandpa::{
|
||||||
|
EquivocationOffence as GrandpaEquivocationOffence, TimeSlot as GrandpaTimeSlot,
|
||||||
|
};
|
||||||
|
use pallet_offences::{Config as OffencesConfig, Pallet as Offences};
|
||||||
|
use pallet_session::{
|
||||||
|
historical::{Config as HistoricalConfig, IdentificationTuple},
|
||||||
|
Config as SessionConfig, Pallet as Session, SessionManager,
|
||||||
|
};
|
||||||
|
use pallet_staking::{
|
||||||
|
Config as StakingConfig, Exposure, IndividualExposure, MaxNominationsOf, Pallet as Staking,
|
||||||
|
RewardDestination, ValidatorPrefs,
|
||||||
|
};
|
||||||
|
|
||||||
|
const SEED: u32 = 0;
|
||||||
|
|
||||||
|
const MAX_NOMINATORS: u32 = 100;
|
||||||
|
|
||||||
|
pub struct Pallet<T: Config>(Offences<T>);
|
||||||
|
|
||||||
|
pub trait Config:
|
||||||
|
SessionConfig
|
||||||
|
+ StakingConfig
|
||||||
|
+ OffencesConfig
|
||||||
|
+ HistoricalConfig
|
||||||
|
+ BalancesConfig
|
||||||
|
+ IdTupleConvert<Self>
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A helper trait to make sure we can convert `IdentificationTuple` coming from historical
|
||||||
|
/// and the one required by offences.
|
||||||
|
pub trait IdTupleConvert<T: HistoricalConfig + OffencesConfig> {
|
||||||
|
/// Convert identification tuple from `historical` trait to the one expected by `offences`.
|
||||||
|
fn convert(id: IdentificationTuple<T>) -> <T as OffencesConfig>::IdentificationTuple;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: HistoricalConfig + OffencesConfig> IdTupleConvert<T> for T
|
||||||
|
where
|
||||||
|
<T as OffencesConfig>::IdentificationTuple: From<IdentificationTuple<T>>,
|
||||||
|
{
|
||||||
|
fn convert(id: IdentificationTuple<T>) -> <T as OffencesConfig>::IdentificationTuple {
|
||||||
|
id.into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type LookupSourceOf<T> = <<T as SystemConfig>::Lookup as StaticLookup>::Source;
|
||||||
|
type BalanceOf<T> =
|
||||||
|
<<T as StakingConfig>::Currency as Currency<<T as SystemConfig>::AccountId>>::Balance;
|
||||||
|
|
||||||
|
struct Offender<T: Config> {
|
||||||
|
pub controller: T::AccountId,
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub stash: T::AccountId,
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub nominator_stashes: Vec<T::AccountId>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bond_amount<T: Config>() -> BalanceOf<T> {
|
||||||
|
T::Currency::minimum_balance().saturating_mul(10_000u32.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_offender<T: Config>(n: u32, nominators: u32) -> Result<Offender<T>, &'static str> {
|
||||||
|
let stash: T::AccountId = account("stash", n, SEED);
|
||||||
|
let stash_lookup: LookupSourceOf<T> = T::Lookup::unlookup(stash.clone());
|
||||||
|
let reward_destination = RewardDestination::Staked;
|
||||||
|
let amount = bond_amount::<T>();
|
||||||
|
// add twice as much balance to prevent the account from being killed.
|
||||||
|
let free_amount = amount.saturating_mul(2u32.into());
|
||||||
|
T::Currency::make_free_balance_be(&stash, free_amount);
|
||||||
|
Staking::<T>::bond(
|
||||||
|
RawOrigin::Signed(stash.clone()).into(),
|
||||||
|
amount,
|
||||||
|
reward_destination.clone(),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let validator_prefs =
|
||||||
|
ValidatorPrefs { commission: Perbill::from_percent(50), ..Default::default() };
|
||||||
|
Staking::<T>::validate(RawOrigin::Signed(stash.clone()).into(), validator_prefs)?;
|
||||||
|
|
||||||
|
let mut individual_exposures = vec![];
|
||||||
|
let mut nominator_stashes = vec![];
|
||||||
|
// Create n nominators
|
||||||
|
for i in 0..nominators {
|
||||||
|
let nominator_stash: T::AccountId =
|
||||||
|
account("nominator stash", n * MAX_NOMINATORS + i, SEED);
|
||||||
|
T::Currency::make_free_balance_be(&nominator_stash, free_amount);
|
||||||
|
|
||||||
|
Staking::<T>::bond(
|
||||||
|
RawOrigin::Signed(nominator_stash.clone()).into(),
|
||||||
|
amount,
|
||||||
|
reward_destination.clone(),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let selected_validators: Vec<LookupSourceOf<T>> = vec![stash_lookup.clone()];
|
||||||
|
Staking::<T>::nominate(
|
||||||
|
RawOrigin::Signed(nominator_stash.clone()).into(),
|
||||||
|
selected_validators,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
individual_exposures
|
||||||
|
.push(IndividualExposure { who: nominator_stash.clone(), value: amount });
|
||||||
|
nominator_stashes.push(nominator_stash.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
let exposure = Exposure { total: amount * n.into(), own: amount, others: individual_exposures };
|
||||||
|
let current_era = 0u32;
|
||||||
|
Staking::<T>::add_era_stakers(current_era, stash.clone(), exposure);
|
||||||
|
|
||||||
|
Ok(Offender { controller: stash.clone(), stash, nominator_stashes })
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_offenders<T: Config>(
|
||||||
|
num_offenders: u32,
|
||||||
|
num_nominators: u32,
|
||||||
|
) -> Result<(Vec<IdentificationTuple<T>>, Vec<Offender<T>>), &'static str> {
|
||||||
|
Staking::<T>::new_session(0);
|
||||||
|
|
||||||
|
let mut offenders = vec![];
|
||||||
|
for i in 0..num_offenders {
|
||||||
|
let offender = create_offender::<T>(i + 1, num_nominators)?;
|
||||||
|
offenders.push(offender);
|
||||||
|
}
|
||||||
|
|
||||||
|
Staking::<T>::start_session(0);
|
||||||
|
|
||||||
|
let id_tuples = offenders
|
||||||
|
.iter()
|
||||||
|
.map(|offender| {
|
||||||
|
<T as SessionConfig>::ValidatorIdOf::convert(offender.controller.clone())
|
||||||
|
.expect("failed to get validator id from account id")
|
||||||
|
})
|
||||||
|
.map(|validator_id| {
|
||||||
|
<T as HistoricalConfig>::FullIdentificationOf::convert(validator_id.clone())
|
||||||
|
.map(|full_id| (validator_id, full_id))
|
||||||
|
.expect("failed to convert validator id to full identification")
|
||||||
|
})
|
||||||
|
.collect::<Vec<IdentificationTuple<T>>>();
|
||||||
|
Ok((id_tuples, offenders))
|
||||||
|
}
|
||||||
|
|
||||||
|
benchmarks! {
|
||||||
|
report_offence_grandpa {
|
||||||
|
let n in 0 .. MAX_NOMINATORS.min(MaxNominationsOf::<T>::get());
|
||||||
|
|
||||||
|
// for grandpa equivocation reports the number of reporters
|
||||||
|
// and offenders is always 1
|
||||||
|
let reporters = vec![account("reporter", 1, SEED)];
|
||||||
|
|
||||||
|
// make sure reporters actually get rewarded
|
||||||
|
Staking::<T>::set_slash_reward_fraction(Perbill::one());
|
||||||
|
|
||||||
|
let (mut offenders, raw_offenders) = make_offenders::<T>(1, n)?;
|
||||||
|
let validator_set_count = Session::<T>::validators().len() as u32;
|
||||||
|
|
||||||
|
let offence = GrandpaEquivocationOffence {
|
||||||
|
time_slot: GrandpaTimeSlot { set_id: 0, round: 0 },
|
||||||
|
session_index: 0,
|
||||||
|
validator_set_count,
|
||||||
|
offender: T::convert(offenders.pop().unwrap()),
|
||||||
|
};
|
||||||
|
assert_eq!(System::<T>::event_count(), 0);
|
||||||
|
}: {
|
||||||
|
let _ = Offences::<T>::report_offence(reporters, offence);
|
||||||
|
}
|
||||||
|
verify {
|
||||||
|
// make sure that all slashes have been applied
|
||||||
|
#[cfg(test)]
|
||||||
|
assert_eq!(
|
||||||
|
System::<T>::event_count(), 0
|
||||||
|
+ 1 // offence
|
||||||
|
+ 3 // reporter (reward + endowment)
|
||||||
|
+ 1 // offenders reported
|
||||||
|
+ 3 // offenders slashed
|
||||||
|
+ 1 // offenders chilled
|
||||||
|
+ 3 * n // nominators slashed
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
report_offence_babe {
|
||||||
|
let n in 0 .. MAX_NOMINATORS.min(MaxNominationsOf::<T>::get());
|
||||||
|
|
||||||
|
// for babe equivocation reports the number of reporters
|
||||||
|
// and offenders is always 1
|
||||||
|
let reporters = vec![account("reporter", 1, SEED)];
|
||||||
|
|
||||||
|
// make sure reporters actually get rewarded
|
||||||
|
Staking::<T>::set_slash_reward_fraction(Perbill::one());
|
||||||
|
|
||||||
|
let (mut offenders, raw_offenders) = make_offenders::<T>(1, n)?;
|
||||||
|
let validator_set_count = Session::<T>::validators().len() as u32;
|
||||||
|
|
||||||
|
let offence = BabeEquivocationOffence {
|
||||||
|
slot: 0u64.into(),
|
||||||
|
session_index: 0,
|
||||||
|
validator_set_count,
|
||||||
|
offender: T::convert(offenders.pop().unwrap()),
|
||||||
|
};
|
||||||
|
assert_eq!(System::<T>::event_count(), 0);
|
||||||
|
}: {
|
||||||
|
let _ = Offences::<T>::report_offence(reporters, offence);
|
||||||
|
}
|
||||||
|
verify {
|
||||||
|
// make sure that all slashes have been applied
|
||||||
|
#[cfg(test)]
|
||||||
|
assert_eq!(
|
||||||
|
System::<T>::event_count(), 0
|
||||||
|
+ 1 // offence
|
||||||
|
+ 3 // reporter (reward + endowment)
|
||||||
|
+ 1 // offenders reported
|
||||||
|
+ 3 // offenders slashed
|
||||||
|
+ 1 // offenders chilled
|
||||||
|
+ 3 * n // nominators slashed
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_benchmark_test_suite!(Pallet, crate::mock::new_test_ext(), crate::mock::Test);
|
||||||
|
}
|
||||||
@@ -17,239 +17,13 @@
|
|||||||
|
|
||||||
//! Offences pallet benchmarking.
|
//! Offences pallet benchmarking.
|
||||||
|
|
||||||
#![cfg(feature = "runtime-benchmarks")]
|
|
||||||
#![cfg_attr(not(feature = "std"), no_std)]
|
#![cfg_attr(not(feature = "std"), no_std)]
|
||||||
|
|
||||||
mod mock;
|
#[cfg(feature = "runtime-benchmarks")]
|
||||||
|
pub mod inner;
|
||||||
|
|
||||||
use sp_std::{prelude::*, vec};
|
#[cfg(feature = "runtime-benchmarks")]
|
||||||
|
pub use inner::*;
|
||||||
|
|
||||||
use frame_benchmarking::v1::{account, benchmarks};
|
#[cfg(all(feature = "runtime-benchmarks", test))]
|
||||||
use frame_support::traits::{Currency, Get};
|
pub(crate) mod mock;
|
||||||
use frame_system::{Config as SystemConfig, Pallet as System, RawOrigin};
|
|
||||||
|
|
||||||
use sp_runtime::{
|
|
||||||
traits::{Convert, Saturating, StaticLookup},
|
|
||||||
Perbill,
|
|
||||||
};
|
|
||||||
use sp_staking::offence::ReportOffence;
|
|
||||||
|
|
||||||
use pallet_babe::EquivocationOffence as BabeEquivocationOffence;
|
|
||||||
use pallet_balances::Config as BalancesConfig;
|
|
||||||
use pallet_grandpa::{
|
|
||||||
EquivocationOffence as GrandpaEquivocationOffence, TimeSlot as GrandpaTimeSlot,
|
|
||||||
};
|
|
||||||
use pallet_offences::{Config as OffencesConfig, Pallet as Offences};
|
|
||||||
use pallet_session::{
|
|
||||||
historical::{Config as HistoricalConfig, IdentificationTuple},
|
|
||||||
Config as SessionConfig, Pallet as Session, SessionManager,
|
|
||||||
};
|
|
||||||
use pallet_staking::{
|
|
||||||
Config as StakingConfig, Exposure, IndividualExposure, MaxNominationsOf, Pallet as Staking,
|
|
||||||
RewardDestination, ValidatorPrefs,
|
|
||||||
};
|
|
||||||
|
|
||||||
const SEED: u32 = 0;
|
|
||||||
|
|
||||||
const MAX_NOMINATORS: u32 = 100;
|
|
||||||
|
|
||||||
pub struct Pallet<T: Config>(Offences<T>);
|
|
||||||
|
|
||||||
pub trait Config:
|
|
||||||
SessionConfig
|
|
||||||
+ StakingConfig
|
|
||||||
+ OffencesConfig
|
|
||||||
+ HistoricalConfig
|
|
||||||
+ BalancesConfig
|
|
||||||
+ IdTupleConvert<Self>
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A helper trait to make sure we can convert `IdentificationTuple` coming from historical
|
|
||||||
/// and the one required by offences.
|
|
||||||
pub trait IdTupleConvert<T: HistoricalConfig + OffencesConfig> {
|
|
||||||
/// Convert identification tuple from `historical` trait to the one expected by `offences`.
|
|
||||||
fn convert(id: IdentificationTuple<T>) -> <T as OffencesConfig>::IdentificationTuple;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: HistoricalConfig + OffencesConfig> IdTupleConvert<T> for T
|
|
||||||
where
|
|
||||||
<T as OffencesConfig>::IdentificationTuple: From<IdentificationTuple<T>>,
|
|
||||||
{
|
|
||||||
fn convert(id: IdentificationTuple<T>) -> <T as OffencesConfig>::IdentificationTuple {
|
|
||||||
id.into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type LookupSourceOf<T> = <<T as SystemConfig>::Lookup as StaticLookup>::Source;
|
|
||||||
type BalanceOf<T> =
|
|
||||||
<<T as StakingConfig>::Currency as Currency<<T as SystemConfig>::AccountId>>::Balance;
|
|
||||||
|
|
||||||
struct Offender<T: Config> {
|
|
||||||
pub controller: T::AccountId,
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub stash: T::AccountId,
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub nominator_stashes: Vec<T::AccountId>,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn bond_amount<T: Config>() -> BalanceOf<T> {
|
|
||||||
T::Currency::minimum_balance().saturating_mul(10_000u32.into())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn create_offender<T: Config>(n: u32, nominators: u32) -> Result<Offender<T>, &'static str> {
|
|
||||||
let stash: T::AccountId = account("stash", n, SEED);
|
|
||||||
let stash_lookup: LookupSourceOf<T> = T::Lookup::unlookup(stash.clone());
|
|
||||||
let reward_destination = RewardDestination::Staked;
|
|
||||||
let amount = bond_amount::<T>();
|
|
||||||
// add twice as much balance to prevent the account from being killed.
|
|
||||||
let free_amount = amount.saturating_mul(2u32.into());
|
|
||||||
T::Currency::make_free_balance_be(&stash, free_amount);
|
|
||||||
Staking::<T>::bond(
|
|
||||||
RawOrigin::Signed(stash.clone()).into(),
|
|
||||||
amount,
|
|
||||||
reward_destination.clone(),
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let validator_prefs =
|
|
||||||
ValidatorPrefs { commission: Perbill::from_percent(50), ..Default::default() };
|
|
||||||
Staking::<T>::validate(RawOrigin::Signed(stash.clone()).into(), validator_prefs)?;
|
|
||||||
|
|
||||||
let mut individual_exposures = vec![];
|
|
||||||
let mut nominator_stashes = vec![];
|
|
||||||
// Create n nominators
|
|
||||||
for i in 0..nominators {
|
|
||||||
let nominator_stash: T::AccountId =
|
|
||||||
account("nominator stash", n * MAX_NOMINATORS + i, SEED);
|
|
||||||
T::Currency::make_free_balance_be(&nominator_stash, free_amount);
|
|
||||||
|
|
||||||
Staking::<T>::bond(
|
|
||||||
RawOrigin::Signed(nominator_stash.clone()).into(),
|
|
||||||
amount,
|
|
||||||
reward_destination.clone(),
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let selected_validators: Vec<LookupSourceOf<T>> = vec![stash_lookup.clone()];
|
|
||||||
Staking::<T>::nominate(
|
|
||||||
RawOrigin::Signed(nominator_stash.clone()).into(),
|
|
||||||
selected_validators,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
individual_exposures
|
|
||||||
.push(IndividualExposure { who: nominator_stash.clone(), value: amount });
|
|
||||||
nominator_stashes.push(nominator_stash.clone());
|
|
||||||
}
|
|
||||||
|
|
||||||
let exposure = Exposure { total: amount * n.into(), own: amount, others: individual_exposures };
|
|
||||||
let current_era = 0u32;
|
|
||||||
Staking::<T>::add_era_stakers(current_era, stash.clone(), exposure);
|
|
||||||
|
|
||||||
Ok(Offender { controller: stash.clone(), stash, nominator_stashes })
|
|
||||||
}
|
|
||||||
|
|
||||||
fn make_offenders<T: Config>(
|
|
||||||
num_offenders: u32,
|
|
||||||
num_nominators: u32,
|
|
||||||
) -> Result<(Vec<IdentificationTuple<T>>, Vec<Offender<T>>), &'static str> {
|
|
||||||
Staking::<T>::new_session(0);
|
|
||||||
|
|
||||||
let mut offenders = vec![];
|
|
||||||
for i in 0..num_offenders {
|
|
||||||
let offender = create_offender::<T>(i + 1, num_nominators)?;
|
|
||||||
offenders.push(offender);
|
|
||||||
}
|
|
||||||
|
|
||||||
Staking::<T>::start_session(0);
|
|
||||||
|
|
||||||
let id_tuples = offenders
|
|
||||||
.iter()
|
|
||||||
.map(|offender| {
|
|
||||||
<T as SessionConfig>::ValidatorIdOf::convert(offender.controller.clone())
|
|
||||||
.expect("failed to get validator id from account id")
|
|
||||||
})
|
|
||||||
.map(|validator_id| {
|
|
||||||
<T as HistoricalConfig>::FullIdentificationOf::convert(validator_id.clone())
|
|
||||||
.map(|full_id| (validator_id, full_id))
|
|
||||||
.expect("failed to convert validator id to full identification")
|
|
||||||
})
|
|
||||||
.collect::<Vec<IdentificationTuple<T>>>();
|
|
||||||
Ok((id_tuples, offenders))
|
|
||||||
}
|
|
||||||
|
|
||||||
benchmarks! {
|
|
||||||
report_offence_grandpa {
|
|
||||||
let n in 0 .. MAX_NOMINATORS.min(MaxNominationsOf::<T>::get());
|
|
||||||
|
|
||||||
// for grandpa equivocation reports the number of reporters
|
|
||||||
// and offenders is always 1
|
|
||||||
let reporters = vec![account("reporter", 1, SEED)];
|
|
||||||
|
|
||||||
// make sure reporters actually get rewarded
|
|
||||||
Staking::<T>::set_slash_reward_fraction(Perbill::one());
|
|
||||||
|
|
||||||
let (mut offenders, raw_offenders) = make_offenders::<T>(1, n)?;
|
|
||||||
let validator_set_count = Session::<T>::validators().len() as u32;
|
|
||||||
|
|
||||||
let offence = GrandpaEquivocationOffence {
|
|
||||||
time_slot: GrandpaTimeSlot { set_id: 0, round: 0 },
|
|
||||||
session_index: 0,
|
|
||||||
validator_set_count,
|
|
||||||
offender: T::convert(offenders.pop().unwrap()),
|
|
||||||
};
|
|
||||||
assert_eq!(System::<T>::event_count(), 0);
|
|
||||||
}: {
|
|
||||||
let _ = Offences::<T>::report_offence(reporters, offence);
|
|
||||||
}
|
|
||||||
verify {
|
|
||||||
// make sure that all slashes have been applied
|
|
||||||
#[cfg(test)]
|
|
||||||
assert_eq!(
|
|
||||||
System::<T>::event_count(), 0
|
|
||||||
+ 1 // offence
|
|
||||||
+ 3 // reporter (reward + endowment)
|
|
||||||
+ 1 // offenders reported
|
|
||||||
+ 3 // offenders slashed
|
|
||||||
+ 1 // offenders chilled
|
|
||||||
+ 3 * n // nominators slashed
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
report_offence_babe {
|
|
||||||
let n in 0 .. MAX_NOMINATORS.min(MaxNominationsOf::<T>::get());
|
|
||||||
|
|
||||||
// for babe equivocation reports the number of reporters
|
|
||||||
// and offenders is always 1
|
|
||||||
let reporters = vec![account("reporter", 1, SEED)];
|
|
||||||
|
|
||||||
// make sure reporters actually get rewarded
|
|
||||||
Staking::<T>::set_slash_reward_fraction(Perbill::one());
|
|
||||||
|
|
||||||
let (mut offenders, raw_offenders) = make_offenders::<T>(1, n)?;
|
|
||||||
let validator_set_count = Session::<T>::validators().len() as u32;
|
|
||||||
|
|
||||||
let offence = BabeEquivocationOffence {
|
|
||||||
slot: 0u64.into(),
|
|
||||||
session_index: 0,
|
|
||||||
validator_set_count,
|
|
||||||
offender: T::convert(offenders.pop().unwrap()),
|
|
||||||
};
|
|
||||||
assert_eq!(System::<T>::event_count(), 0);
|
|
||||||
}: {
|
|
||||||
let _ = Offences::<T>::report_offence(reporters, offence);
|
|
||||||
}
|
|
||||||
verify {
|
|
||||||
// make sure that all slashes have been applied
|
|
||||||
#[cfg(test)]
|
|
||||||
assert_eq!(
|
|
||||||
System::<T>::event_count(), 0
|
|
||||||
+ 1 // offence
|
|
||||||
+ 3 // reporter (reward + endowment)
|
|
||||||
+ 1 // offenders reported
|
|
||||||
+ 3 // offenders slashed
|
|
||||||
+ 1 // offenders chilled
|
|
||||||
+ 3 * n // nominators slashed
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
impl_benchmark_test_suite!(Pallet, crate::mock::new_test_ext(), crate::mock::Test);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -17,9 +17,6 @@
|
|||||||
|
|
||||||
//! Mock file for offences benchmarking.
|
//! Mock file for offences benchmarking.
|
||||||
|
|
||||||
#![cfg(test)]
|
|
||||||
|
|
||||||
use super::*;
|
|
||||||
use frame_election_provider_support::{
|
use frame_election_provider_support::{
|
||||||
bounds::{ElectionBounds, ElectionBoundsBuilder},
|
bounds::{ElectionBounds, ElectionBoundsBuilder},
|
||||||
onchain, SequentialPhragmen,
|
onchain, SequentialPhragmen,
|
||||||
@@ -33,7 +30,7 @@ use pallet_session::historical as pallet_session_historical;
|
|||||||
use sp_runtime::{
|
use sp_runtime::{
|
||||||
testing::{Header, UintAuthorityId},
|
testing::{Header, UintAuthorityId},
|
||||||
traits::IdentityLookup,
|
traits::IdentityLookup,
|
||||||
BuildStorage,
|
BuildStorage, Perbill,
|
||||||
};
|
};
|
||||||
|
|
||||||
type AccountId = u64;
|
type AccountId = u64;
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ pallet-staking = { path = "../staking", default-features = false }
|
|||||||
|
|
||||||
frame-support = { path = "../support", default-features = false }
|
frame-support = { path = "../support", default-features = false }
|
||||||
frame-system = { path = "../system", default-features = false }
|
frame-system = { path = "../system", default-features = false }
|
||||||
sp-runtime = { path = "../../primitives/runtime" }
|
sp-runtime = { path = "../../primitives/runtime", default-features = false }
|
||||||
sp-staking = { path = "../../primitives/staking", default-features = false }
|
sp-staking = { path = "../../primitives/staking", default-features = false }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
@@ -34,7 +34,7 @@ pallet-staking-reward-curve = { path = "../staking/reward-curve" }
|
|||||||
|
|
||||||
sp-core = { path = "../../primitives/core" }
|
sp-core = { path = "../../primitives/core" }
|
||||||
sp-io = { path = "../../primitives/io", default-features = false }
|
sp-io = { path = "../../primitives/io", default-features = false }
|
||||||
sp-std = { path = "../../primitives/std", default-features = false }
|
sp-std = { path = "../../primitives/std" }
|
||||||
|
|
||||||
frame-election-provider-support = { path = "../election-provider-support" }
|
frame-election-provider-support = { path = "../election-provider-support" }
|
||||||
|
|
||||||
@@ -74,5 +74,4 @@ std = [
|
|||||||
"sp-io/std",
|
"sp-io/std",
|
||||||
"sp-runtime/std",
|
"sp-runtime/std",
|
||||||
"sp-staking/std",
|
"sp-staking/std",
|
||||||
"sp-std/std",
|
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -27,6 +27,9 @@ mod mock;
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests;
|
mod tests;
|
||||||
|
|
||||||
|
extern crate alloc;
|
||||||
|
|
||||||
|
use alloc::vec::Vec;
|
||||||
use pallet_session::historical::IdentificationTuple;
|
use pallet_session::historical::IdentificationTuple;
|
||||||
use pallet_staking::{BalanceOf, Exposure, ExposureOf, Pallet as Staking};
|
use pallet_staking::{BalanceOf, Exposure, ExposureOf, Pallet as Staking};
|
||||||
use sp_runtime::Perbill;
|
use sp_runtime::Perbill;
|
||||||
@@ -112,7 +115,7 @@ pub mod pallet {
|
|||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(o, _)| OffenceDetails::<T> {
|
.map(|(o, _)| OffenceDetails::<T> {
|
||||||
offender: (o.clone(), Staking::<T>::eras_stakers(now, &o)),
|
offender: (o.clone(), Staking::<T>::eras_stakers(now, &o)),
|
||||||
reporters: vec![],
|
reporters: Default::default(),
|
||||||
})
|
})
|
||||||
.collect())
|
.collect())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,162 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
//! Benchmarks for the Session Pallet.
|
||||||
|
// This is separated into its own crate due to cyclic dependency issues.
|
||||||
|
|
||||||
|
use sp_runtime::traits::{One, StaticLookup, TrailingZeroInput};
|
||||||
|
use sp_std::{prelude::*, vec};
|
||||||
|
|
||||||
|
use codec::Decode;
|
||||||
|
use frame_benchmarking::v1::benchmarks;
|
||||||
|
use frame_support::traits::{Get, KeyOwnerProofSystem, OnInitialize};
|
||||||
|
use frame_system::{pallet_prelude::BlockNumberFor, RawOrigin};
|
||||||
|
use pallet_session::{historical::Pallet as Historical, Pallet as Session, *};
|
||||||
|
use pallet_staking::{
|
||||||
|
benchmarking::create_validator_with_nominators, testing_utils::create_validators,
|
||||||
|
MaxNominationsOf, RewardDestination,
|
||||||
|
};
|
||||||
|
|
||||||
|
const MAX_VALIDATORS: u32 = 1000;
|
||||||
|
|
||||||
|
pub struct Pallet<T: Config>(pallet_session::Pallet<T>);
|
||||||
|
pub trait Config:
|
||||||
|
pallet_session::Config + pallet_session::historical::Config + pallet_staking::Config
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Config> OnInitialize<BlockNumberFor<T>> for Pallet<T> {
|
||||||
|
fn on_initialize(n: BlockNumberFor<T>) -> frame_support::weights::Weight {
|
||||||
|
pallet_session::Pallet::<T>::on_initialize(n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
benchmarks! {
|
||||||
|
set_keys {
|
||||||
|
let n = MaxNominationsOf::<T>::get();
|
||||||
|
let (v_stash, _) = create_validator_with_nominators::<T>(
|
||||||
|
n,
|
||||||
|
MaxNominationsOf::<T>::get(),
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
RewardDestination::Staked,
|
||||||
|
)?;
|
||||||
|
let v_controller = pallet_staking::Pallet::<T>::bonded(&v_stash).ok_or("not stash")?;
|
||||||
|
|
||||||
|
let keys = T::Keys::decode(&mut TrailingZeroInput::zeroes()).unwrap();
|
||||||
|
let proof: Vec<u8> = vec![0,1,2,3];
|
||||||
|
// Whitelist controller account from further DB operations.
|
||||||
|
let v_controller_key = frame_system::Account::<T>::hashed_key_for(&v_controller);
|
||||||
|
frame_benchmarking::benchmarking::add_to_whitelist(v_controller_key.into());
|
||||||
|
}: _(RawOrigin::Signed(v_controller), keys, proof)
|
||||||
|
|
||||||
|
purge_keys {
|
||||||
|
let n = MaxNominationsOf::<T>::get();
|
||||||
|
let (v_stash, _) = create_validator_with_nominators::<T>(
|
||||||
|
n,
|
||||||
|
MaxNominationsOf::<T>::get(),
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
RewardDestination::Staked,
|
||||||
|
)?;
|
||||||
|
let v_controller = pallet_staking::Pallet::<T>::bonded(&v_stash).ok_or("not stash")?;
|
||||||
|
let keys = T::Keys::decode(&mut TrailingZeroInput::zeroes()).unwrap();
|
||||||
|
let proof: Vec<u8> = vec![0,1,2,3];
|
||||||
|
Session::<T>::set_keys(RawOrigin::Signed(v_controller.clone()).into(), keys, proof)?;
|
||||||
|
// Whitelist controller account from further DB operations.
|
||||||
|
let v_controller_key = frame_system::Account::<T>::hashed_key_for(&v_controller);
|
||||||
|
frame_benchmarking::benchmarking::add_to_whitelist(v_controller_key.into());
|
||||||
|
}: _(RawOrigin::Signed(v_controller))
|
||||||
|
|
||||||
|
#[extra]
|
||||||
|
check_membership_proof_current_session {
|
||||||
|
let n in 2 .. MAX_VALIDATORS as u32;
|
||||||
|
|
||||||
|
let (key, key_owner_proof1) = check_membership_proof_setup::<T>(n);
|
||||||
|
let key_owner_proof2 = key_owner_proof1.clone();
|
||||||
|
}: {
|
||||||
|
Historical::<T>::check_proof(key, key_owner_proof1);
|
||||||
|
}
|
||||||
|
verify {
|
||||||
|
assert!(Historical::<T>::check_proof(key, key_owner_proof2).is_some());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[extra]
|
||||||
|
check_membership_proof_historical_session {
|
||||||
|
let n in 2 .. MAX_VALIDATORS as u32;
|
||||||
|
|
||||||
|
let (key, key_owner_proof1) = check_membership_proof_setup::<T>(n);
|
||||||
|
|
||||||
|
// skip to the next session so that the session is historical
|
||||||
|
// and the membership merkle proof must be checked.
|
||||||
|
Session::<T>::rotate_session();
|
||||||
|
|
||||||
|
let key_owner_proof2 = key_owner_proof1.clone();
|
||||||
|
}: {
|
||||||
|
Historical::<T>::check_proof(key, key_owner_proof1);
|
||||||
|
}
|
||||||
|
verify {
|
||||||
|
assert!(Historical::<T>::check_proof(key, key_owner_proof2).is_some());
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_benchmark_test_suite!(Pallet, crate::mock::new_test_ext(), crate::mock::Test, extra = false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets up the benchmark for checking a membership proof. It creates the given
|
||||||
|
/// number of validators, sets random session keys and then creates a membership
|
||||||
|
/// proof for the first authority and returns its key and the proof.
|
||||||
|
fn check_membership_proof_setup<T: Config>(
|
||||||
|
n: u32,
|
||||||
|
) -> ((sp_runtime::KeyTypeId, &'static [u8; 32]), sp_session::MembershipProof) {
|
||||||
|
pallet_staking::ValidatorCount::<T>::put(n);
|
||||||
|
|
||||||
|
// create validators and set random session keys
|
||||||
|
for (n, who) in create_validators::<T>(n, 1000).unwrap().into_iter().enumerate() {
|
||||||
|
use rand::{RngCore, SeedableRng};
|
||||||
|
|
||||||
|
let validator = T::Lookup::lookup(who).unwrap();
|
||||||
|
let controller = pallet_staking::Pallet::<T>::bonded(&validator).unwrap();
|
||||||
|
|
||||||
|
let keys = {
|
||||||
|
let mut keys = [0u8; 128];
|
||||||
|
|
||||||
|
// we keep the keys for the first validator as 0x00000...
|
||||||
|
if n > 0 {
|
||||||
|
let mut rng = rand::rngs::StdRng::seed_from_u64(n as u64);
|
||||||
|
rng.fill_bytes(&mut keys);
|
||||||
|
}
|
||||||
|
|
||||||
|
keys
|
||||||
|
};
|
||||||
|
|
||||||
|
let keys: T::Keys = Decode::decode(&mut &keys[..]).unwrap();
|
||||||
|
let proof: Vec<u8> = vec![];
|
||||||
|
|
||||||
|
Session::<T>::set_keys(RawOrigin::Signed(controller).into(), keys, proof).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
Pallet::<T>::on_initialize(frame_system::pallet_prelude::BlockNumberFor::<T>::one());
|
||||||
|
|
||||||
|
// skip sessions until the new validator set is enacted
|
||||||
|
while Session::<T>::validators().len() < n as usize {
|
||||||
|
Session::<T>::rotate_session();
|
||||||
|
}
|
||||||
|
|
||||||
|
let key = (sp_runtime::KeyTypeId(*b"babe"), &[0u8; 32]);
|
||||||
|
|
||||||
|
(key, Historical::<T>::prove(key).unwrap())
|
||||||
|
}
|
||||||
@@ -15,153 +15,15 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
//! Benchmarks for the Session Pallet.
|
//! Offences pallet benchmarking.
|
||||||
// This is separated into its own crate due to cyclic dependency issues.
|
|
||||||
|
|
||||||
#![cfg(feature = "runtime-benchmarks")]
|
|
||||||
#![cfg_attr(not(feature = "std"), no_std)]
|
#![cfg_attr(not(feature = "std"), no_std)]
|
||||||
|
|
||||||
mod mock;
|
#[cfg(feature = "runtime-benchmarks")]
|
||||||
|
pub mod inner;
|
||||||
|
|
||||||
use sp_runtime::traits::{One, StaticLookup, TrailingZeroInput};
|
#[cfg(feature = "runtime-benchmarks")]
|
||||||
use sp_std::{prelude::*, vec};
|
pub use inner::*;
|
||||||
|
|
||||||
use codec::Decode;
|
#[cfg(all(feature = "runtime-benchmarks", test))]
|
||||||
use frame_benchmarking::v1::benchmarks;
|
pub(crate) mod mock;
|
||||||
use frame_support::traits::{Get, KeyOwnerProofSystem, OnInitialize};
|
|
||||||
use frame_system::{pallet_prelude::BlockNumberFor, RawOrigin};
|
|
||||||
use pallet_session::{historical::Pallet as Historical, Pallet as Session, *};
|
|
||||||
use pallet_staking::{
|
|
||||||
benchmarking::create_validator_with_nominators, testing_utils::create_validators,
|
|
||||||
MaxNominationsOf, RewardDestination,
|
|
||||||
};
|
|
||||||
|
|
||||||
const MAX_VALIDATORS: u32 = 1000;
|
|
||||||
|
|
||||||
pub struct Pallet<T: Config>(pallet_session::Pallet<T>);
|
|
||||||
pub trait Config:
|
|
||||||
pallet_session::Config + pallet_session::historical::Config + pallet_staking::Config
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Config> OnInitialize<BlockNumberFor<T>> for Pallet<T> {
|
|
||||||
fn on_initialize(n: BlockNumberFor<T>) -> frame_support::weights::Weight {
|
|
||||||
pallet_session::Pallet::<T>::on_initialize(n)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
benchmarks! {
|
|
||||||
set_keys {
|
|
||||||
let n = MaxNominationsOf::<T>::get();
|
|
||||||
let (v_stash, _) = create_validator_with_nominators::<T>(
|
|
||||||
n,
|
|
||||||
MaxNominationsOf::<T>::get(),
|
|
||||||
false,
|
|
||||||
true,
|
|
||||||
RewardDestination::Staked,
|
|
||||||
)?;
|
|
||||||
let v_controller = pallet_staking::Pallet::<T>::bonded(&v_stash).ok_or("not stash")?;
|
|
||||||
|
|
||||||
let keys = T::Keys::decode(&mut TrailingZeroInput::zeroes()).unwrap();
|
|
||||||
let proof: Vec<u8> = vec![0,1,2,3];
|
|
||||||
// Whitelist controller account from further DB operations.
|
|
||||||
let v_controller_key = frame_system::Account::<T>::hashed_key_for(&v_controller);
|
|
||||||
frame_benchmarking::benchmarking::add_to_whitelist(v_controller_key.into());
|
|
||||||
}: _(RawOrigin::Signed(v_controller), keys, proof)
|
|
||||||
|
|
||||||
purge_keys {
|
|
||||||
let n = MaxNominationsOf::<T>::get();
|
|
||||||
let (v_stash, _) = create_validator_with_nominators::<T>(
|
|
||||||
n,
|
|
||||||
MaxNominationsOf::<T>::get(),
|
|
||||||
false,
|
|
||||||
true,
|
|
||||||
RewardDestination::Staked,
|
|
||||||
)?;
|
|
||||||
let v_controller = pallet_staking::Pallet::<T>::bonded(&v_stash).ok_or("not stash")?;
|
|
||||||
let keys = T::Keys::decode(&mut TrailingZeroInput::zeroes()).unwrap();
|
|
||||||
let proof: Vec<u8> = vec![0,1,2,3];
|
|
||||||
Session::<T>::set_keys(RawOrigin::Signed(v_controller.clone()).into(), keys, proof)?;
|
|
||||||
// Whitelist controller account from further DB operations.
|
|
||||||
let v_controller_key = frame_system::Account::<T>::hashed_key_for(&v_controller);
|
|
||||||
frame_benchmarking::benchmarking::add_to_whitelist(v_controller_key.into());
|
|
||||||
}: _(RawOrigin::Signed(v_controller))
|
|
||||||
|
|
||||||
#[extra]
|
|
||||||
check_membership_proof_current_session {
|
|
||||||
let n in 2 .. MAX_VALIDATORS as u32;
|
|
||||||
|
|
||||||
let (key, key_owner_proof1) = check_membership_proof_setup::<T>(n);
|
|
||||||
let key_owner_proof2 = key_owner_proof1.clone();
|
|
||||||
}: {
|
|
||||||
Historical::<T>::check_proof(key, key_owner_proof1);
|
|
||||||
}
|
|
||||||
verify {
|
|
||||||
assert!(Historical::<T>::check_proof(key, key_owner_proof2).is_some());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[extra]
|
|
||||||
check_membership_proof_historical_session {
|
|
||||||
let n in 2 .. MAX_VALIDATORS as u32;
|
|
||||||
|
|
||||||
let (key, key_owner_proof1) = check_membership_proof_setup::<T>(n);
|
|
||||||
|
|
||||||
// skip to the next session so that the session is historical
|
|
||||||
// and the membership merkle proof must be checked.
|
|
||||||
Session::<T>::rotate_session();
|
|
||||||
|
|
||||||
let key_owner_proof2 = key_owner_proof1.clone();
|
|
||||||
}: {
|
|
||||||
Historical::<T>::check_proof(key, key_owner_proof1);
|
|
||||||
}
|
|
||||||
verify {
|
|
||||||
assert!(Historical::<T>::check_proof(key, key_owner_proof2).is_some());
|
|
||||||
}
|
|
||||||
|
|
||||||
impl_benchmark_test_suite!(Pallet, crate::mock::new_test_ext(), crate::mock::Test, extra = false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets up the benchmark for checking a membership proof. It creates the given
|
|
||||||
/// number of validators, sets random session keys and then creates a membership
|
|
||||||
/// proof for the first authority and returns its key and the proof.
|
|
||||||
fn check_membership_proof_setup<T: Config>(
|
|
||||||
n: u32,
|
|
||||||
) -> ((sp_runtime::KeyTypeId, &'static [u8; 32]), sp_session::MembershipProof) {
|
|
||||||
pallet_staking::ValidatorCount::<T>::put(n);
|
|
||||||
|
|
||||||
// create validators and set random session keys
|
|
||||||
for (n, who) in create_validators::<T>(n, 1000).unwrap().into_iter().enumerate() {
|
|
||||||
use rand::{RngCore, SeedableRng};
|
|
||||||
|
|
||||||
let validator = T::Lookup::lookup(who).unwrap();
|
|
||||||
let controller = pallet_staking::Pallet::<T>::bonded(&validator).unwrap();
|
|
||||||
|
|
||||||
let keys = {
|
|
||||||
let mut keys = [0u8; 128];
|
|
||||||
|
|
||||||
// we keep the keys for the first validator as 0x00000...
|
|
||||||
if n > 0 {
|
|
||||||
let mut rng = rand::rngs::StdRng::seed_from_u64(n as u64);
|
|
||||||
rng.fill_bytes(&mut keys);
|
|
||||||
}
|
|
||||||
|
|
||||||
keys
|
|
||||||
};
|
|
||||||
|
|
||||||
let keys: T::Keys = Decode::decode(&mut &keys[..]).unwrap();
|
|
||||||
let proof: Vec<u8> = vec![];
|
|
||||||
|
|
||||||
Session::<T>::set_keys(RawOrigin::Signed(controller).into(), keys, proof).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
Pallet::<T>::on_initialize(frame_system::pallet_prelude::BlockNumberFor::<T>::one());
|
|
||||||
|
|
||||||
// skip sessions until the new validator set is enacted
|
|
||||||
while Session::<T>::validators().len() < n as usize {
|
|
||||||
Session::<T>::rotate_session();
|
|
||||||
}
|
|
||||||
|
|
||||||
let key = (sp_runtime::KeyTypeId(*b"babe"), &[0u8; 32]);
|
|
||||||
|
|
||||||
(key, Historical::<T>::prove(key).unwrap())
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -34,9 +34,9 @@
|
|||||||
//!
|
//!
|
||||||
//! See [`polkadot_sdk::frame`](../polkadot_sdk_docs/polkadot_sdk/frame_runtime/index.html).
|
//! See [`polkadot_sdk::frame`](../polkadot_sdk_docs/polkadot_sdk/frame_runtime/index.html).
|
||||||
//!
|
//!
|
||||||
//! ## Warning: Experimental
|
//! ## WARNING: Experimental
|
||||||
//!
|
//!
|
||||||
//! This crate and all of its content is experimental, and should not yet be used in production.
|
//! **This crate and all of its content is experimental, and should not yet be used in production.**
|
||||||
//!
|
//!
|
||||||
//! ## Underlying dependencies
|
//! ## Underlying dependencies
|
||||||
//!
|
//!
|
||||||
|
|||||||
@@ -0,0 +1,230 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
//! Frame System benchmarks.
|
||||||
|
|
||||||
|
use codec::Encode;
|
||||||
|
use frame_benchmarking::v2::*;
|
||||||
|
use frame_support::{dispatch::DispatchClass, storage, traits::Get};
|
||||||
|
use frame_system::{Call, Pallet as System, RawOrigin};
|
||||||
|
use sp_core::storage::well_known_keys;
|
||||||
|
use sp_runtime::traits::Hash;
|
||||||
|
use sp_std::{prelude::*, vec};
|
||||||
|
|
||||||
|
pub struct Pallet<T: Config>(System<T>);
|
||||||
|
pub trait Config: frame_system::Config {
|
||||||
|
/// Adds ability to the Runtime to test against their sample code.
|
||||||
|
///
|
||||||
|
/// Default is `../res/kitchensink_runtime.compact.compressed.wasm`.
|
||||||
|
fn prepare_set_code_data() -> Vec<u8> {
|
||||||
|
include_bytes!("../res/kitchensink_runtime.compact.compressed.wasm").to_vec()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds ability to the Runtime to prepare/initialize before running benchmark `set_code`.
|
||||||
|
fn setup_set_code_requirements(_code: &Vec<u8>) -> Result<(), BenchmarkError> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds ability to the Runtime to do custom validation after benchmark.
|
||||||
|
///
|
||||||
|
/// Default is checking for `CodeUpdated` event .
|
||||||
|
fn verify_set_code() {
|
||||||
|
System::<Self>::assert_last_event(frame_system::Event::<Self>::CodeUpdated.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[benchmarks]
|
||||||
|
mod benchmarks {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[benchmark]
|
||||||
|
fn remark(
|
||||||
|
b: Linear<0, { *T::BlockLength::get().max.get(DispatchClass::Normal) as u32 }>,
|
||||||
|
) -> Result<(), BenchmarkError> {
|
||||||
|
let remark_message = vec![1; b as usize];
|
||||||
|
let caller = whitelisted_caller();
|
||||||
|
|
||||||
|
#[extrinsic_call]
|
||||||
|
remark(RawOrigin::Signed(caller), remark_message);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[benchmark]
|
||||||
|
fn remark_with_event(
|
||||||
|
b: Linear<0, { *T::BlockLength::get().max.get(DispatchClass::Normal) as u32 }>,
|
||||||
|
) -> Result<(), BenchmarkError> {
|
||||||
|
let remark_message = vec![1; b as usize];
|
||||||
|
let caller: T::AccountId = whitelisted_caller();
|
||||||
|
let hash = T::Hashing::hash(&remark_message[..]);
|
||||||
|
|
||||||
|
#[extrinsic_call]
|
||||||
|
remark_with_event(RawOrigin::Signed(caller.clone()), remark_message);
|
||||||
|
|
||||||
|
System::<T>::assert_last_event(
|
||||||
|
frame_system::Event::<T>::Remarked { sender: caller, hash }.into(),
|
||||||
|
);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[benchmark]
|
||||||
|
fn set_heap_pages() -> Result<(), BenchmarkError> {
|
||||||
|
#[extrinsic_call]
|
||||||
|
set_heap_pages(RawOrigin::Root, Default::default());
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[benchmark]
|
||||||
|
fn set_code() -> Result<(), BenchmarkError> {
|
||||||
|
let runtime_blob = T::prepare_set_code_data();
|
||||||
|
T::setup_set_code_requirements(&runtime_blob)?;
|
||||||
|
|
||||||
|
#[extrinsic_call]
|
||||||
|
set_code(RawOrigin::Root, runtime_blob);
|
||||||
|
|
||||||
|
T::verify_set_code();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[benchmark(extra)]
|
||||||
|
fn set_code_without_checks() -> Result<(), BenchmarkError> {
|
||||||
|
// Assume Wasm ~4MB
|
||||||
|
let code = vec![1; 4_000_000 as usize];
|
||||||
|
T::setup_set_code_requirements(&code)?;
|
||||||
|
|
||||||
|
#[block]
|
||||||
|
{
|
||||||
|
System::<T>::set_code_without_checks(RawOrigin::Root.into(), code)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let current_code =
|
||||||
|
storage::unhashed::get_raw(well_known_keys::CODE).ok_or("Code not stored.")?;
|
||||||
|
assert_eq!(current_code.len(), 4_000_000 as usize);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[benchmark(skip_meta)]
|
||||||
|
fn set_storage(i: Linear<0, { 1_000 }>) -> Result<(), BenchmarkError> {
|
||||||
|
// Set up i items to add
|
||||||
|
let mut items = Vec::new();
|
||||||
|
for j in 0..i {
|
||||||
|
let hash = (i, j).using_encoded(T::Hashing::hash).as_ref().to_vec();
|
||||||
|
items.push((hash.clone(), hash.clone()));
|
||||||
|
}
|
||||||
|
|
||||||
|
let items_to_verify = items.clone();
|
||||||
|
|
||||||
|
#[extrinsic_call]
|
||||||
|
set_storage(RawOrigin::Root, items);
|
||||||
|
|
||||||
|
// Verify that they're actually in the storage.
|
||||||
|
for (item, _) in items_to_verify {
|
||||||
|
let value = storage::unhashed::get_raw(&item).ok_or("No value stored")?;
|
||||||
|
assert_eq!(value, *item);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[benchmark(skip_meta)]
|
||||||
|
fn kill_storage(i: Linear<0, { 1_000 }>) -> Result<(), BenchmarkError> {
|
||||||
|
// Add i items to storage
|
||||||
|
let mut items = Vec::with_capacity(i as usize);
|
||||||
|
for j in 0..i {
|
||||||
|
let hash = (i, j).using_encoded(T::Hashing::hash).as_ref().to_vec();
|
||||||
|
storage::unhashed::put_raw(&hash, &hash);
|
||||||
|
items.push(hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify that they're actually in the storage.
|
||||||
|
for item in &items {
|
||||||
|
let value = storage::unhashed::get_raw(item).ok_or("No value stored")?;
|
||||||
|
assert_eq!(value, *item);
|
||||||
|
}
|
||||||
|
|
||||||
|
let items_to_verify = items.clone();
|
||||||
|
|
||||||
|
#[extrinsic_call]
|
||||||
|
kill_storage(RawOrigin::Root, items);
|
||||||
|
|
||||||
|
// Verify that they're not in the storage anymore.
|
||||||
|
for item in items_to_verify {
|
||||||
|
assert!(storage::unhashed::get_raw(&item).is_none());
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[benchmark(skip_meta)]
|
||||||
|
fn kill_prefix(p: Linear<0, { 1_000 }>) -> Result<(), BenchmarkError> {
|
||||||
|
let prefix = p.using_encoded(T::Hashing::hash).as_ref().to_vec();
|
||||||
|
let mut items = Vec::with_capacity(p as usize);
|
||||||
|
// add p items that share a prefix
|
||||||
|
for i in 0..p {
|
||||||
|
let hash = (p, i).using_encoded(T::Hashing::hash).as_ref().to_vec();
|
||||||
|
let key = [&prefix[..], &hash[..]].concat();
|
||||||
|
storage::unhashed::put_raw(&key, &key);
|
||||||
|
items.push(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify that they're actually in the storage.
|
||||||
|
for item in &items {
|
||||||
|
let value = storage::unhashed::get_raw(item).ok_or("No value stored")?;
|
||||||
|
assert_eq!(value, *item);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[extrinsic_call]
|
||||||
|
kill_prefix(RawOrigin::Root, prefix, p);
|
||||||
|
|
||||||
|
// Verify that they're not in the storage anymore.
|
||||||
|
for item in items {
|
||||||
|
assert!(storage::unhashed::get_raw(&item).is_none());
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[benchmark]
|
||||||
|
fn authorize_upgrade() -> Result<(), BenchmarkError> {
|
||||||
|
let runtime_blob = T::prepare_set_code_data();
|
||||||
|
T::setup_set_code_requirements(&runtime_blob)?;
|
||||||
|
let hash = T::Hashing::hash(&runtime_blob);
|
||||||
|
|
||||||
|
#[extrinsic_call]
|
||||||
|
authorize_upgrade(RawOrigin::Root, hash);
|
||||||
|
|
||||||
|
assert!(System::<T>::authorized_upgrade().is_some());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[benchmark]
|
||||||
|
fn apply_authorized_upgrade() -> Result<(), BenchmarkError> {
|
||||||
|
let runtime_blob = T::prepare_set_code_data();
|
||||||
|
T::setup_set_code_requirements(&runtime_blob)?;
|
||||||
|
let hash = T::Hashing::hash(&runtime_blob);
|
||||||
|
// Will be heavier when it needs to do verification (i.e. don't use `...without_checks`).
|
||||||
|
System::<T>::authorize_upgrade(RawOrigin::Root.into(), hash)?;
|
||||||
|
|
||||||
|
#[extrinsic_call]
|
||||||
|
apply_authorized_upgrade(RawOrigin::Root, runtime_blob);
|
||||||
|
|
||||||
|
// Can't check for `CodeUpdated` in parachain upgrades. Just check that the authorization is
|
||||||
|
// gone.
|
||||||
|
assert!(System::<T>::authorized_upgrade().is_none());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_benchmark_test_suite!(Pallet, crate::mock::new_test_ext(), crate::mock::Test);
|
||||||
|
}
|
||||||
@@ -15,221 +15,15 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
// Benchmarks for Utility Pallet
|
//! Frame System benchmarks.
|
||||||
|
|
||||||
#![cfg_attr(not(feature = "std"), no_std)]
|
#![cfg_attr(not(feature = "std"), no_std)]
|
||||||
#![cfg(feature = "runtime-benchmarks")]
|
|
||||||
|
|
||||||
use codec::Encode;
|
#[cfg(feature = "runtime-benchmarks")]
|
||||||
use frame_benchmarking::v2::*;
|
pub mod inner;
|
||||||
use frame_support::{dispatch::DispatchClass, storage, traits::Get};
|
|
||||||
use frame_system::{Call, Pallet as System, RawOrigin};
|
|
||||||
use sp_core::storage::well_known_keys;
|
|
||||||
use sp_runtime::traits::Hash;
|
|
||||||
use sp_std::{prelude::*, vec};
|
|
||||||
|
|
||||||
mod mock;
|
#[cfg(feature = "runtime-benchmarks")]
|
||||||
|
pub use inner::*;
|
||||||
|
|
||||||
pub struct Pallet<T: Config>(System<T>);
|
#[cfg(all(feature = "runtime-benchmarks", test))]
|
||||||
pub trait Config: frame_system::Config {
|
pub(crate) mod mock;
|
||||||
/// Adds ability to the Runtime to test against their sample code.
|
|
||||||
///
|
|
||||||
/// Default is `../res/kitchensink_runtime.compact.compressed.wasm`.
|
|
||||||
fn prepare_set_code_data() -> Vec<u8> {
|
|
||||||
include_bytes!("../res/kitchensink_runtime.compact.compressed.wasm").to_vec()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Adds ability to the Runtime to prepare/initialize before running benchmark `set_code`.
|
|
||||||
fn setup_set_code_requirements(_code: &Vec<u8>) -> Result<(), BenchmarkError> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Adds ability to the Runtime to do custom validation after benchmark.
|
|
||||||
///
|
|
||||||
/// Default is checking for `CodeUpdated` event .
|
|
||||||
fn verify_set_code() {
|
|
||||||
System::<Self>::assert_last_event(frame_system::Event::<Self>::CodeUpdated.into());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[benchmarks]
|
|
||||||
mod benchmarks {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[benchmark]
|
|
||||||
fn remark(
|
|
||||||
b: Linear<0, { *T::BlockLength::get().max.get(DispatchClass::Normal) as u32 }>,
|
|
||||||
) -> Result<(), BenchmarkError> {
|
|
||||||
let remark_message = vec![1; b as usize];
|
|
||||||
let caller = whitelisted_caller();
|
|
||||||
|
|
||||||
#[extrinsic_call]
|
|
||||||
remark(RawOrigin::Signed(caller), remark_message);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[benchmark]
|
|
||||||
fn remark_with_event(
|
|
||||||
b: Linear<0, { *T::BlockLength::get().max.get(DispatchClass::Normal) as u32 }>,
|
|
||||||
) -> Result<(), BenchmarkError> {
|
|
||||||
let remark_message = vec![1; b as usize];
|
|
||||||
let caller: T::AccountId = whitelisted_caller();
|
|
||||||
let hash = T::Hashing::hash(&remark_message[..]);
|
|
||||||
|
|
||||||
#[extrinsic_call]
|
|
||||||
remark_with_event(RawOrigin::Signed(caller.clone()), remark_message);
|
|
||||||
|
|
||||||
System::<T>::assert_last_event(
|
|
||||||
frame_system::Event::<T>::Remarked { sender: caller, hash }.into(),
|
|
||||||
);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[benchmark]
|
|
||||||
fn set_heap_pages() -> Result<(), BenchmarkError> {
|
|
||||||
#[extrinsic_call]
|
|
||||||
set_heap_pages(RawOrigin::Root, Default::default());
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[benchmark]
|
|
||||||
fn set_code() -> Result<(), BenchmarkError> {
|
|
||||||
let runtime_blob = T::prepare_set_code_data();
|
|
||||||
T::setup_set_code_requirements(&runtime_blob)?;
|
|
||||||
|
|
||||||
#[extrinsic_call]
|
|
||||||
set_code(RawOrigin::Root, runtime_blob);
|
|
||||||
|
|
||||||
T::verify_set_code();
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[benchmark(extra)]
|
|
||||||
fn set_code_without_checks() -> Result<(), BenchmarkError> {
|
|
||||||
// Assume Wasm ~4MB
|
|
||||||
let code = vec![1; 4_000_000 as usize];
|
|
||||||
T::setup_set_code_requirements(&code)?;
|
|
||||||
|
|
||||||
#[block]
|
|
||||||
{
|
|
||||||
System::<T>::set_code_without_checks(RawOrigin::Root.into(), code)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
let current_code =
|
|
||||||
storage::unhashed::get_raw(well_known_keys::CODE).ok_or("Code not stored.")?;
|
|
||||||
assert_eq!(current_code.len(), 4_000_000 as usize);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[benchmark(skip_meta)]
|
|
||||||
fn set_storage(i: Linear<0, { 1_000 }>) -> Result<(), BenchmarkError> {
|
|
||||||
// Set up i items to add
|
|
||||||
let mut items = Vec::new();
|
|
||||||
for j in 0..i {
|
|
||||||
let hash = (i, j).using_encoded(T::Hashing::hash).as_ref().to_vec();
|
|
||||||
items.push((hash.clone(), hash.clone()));
|
|
||||||
}
|
|
||||||
|
|
||||||
let items_to_verify = items.clone();
|
|
||||||
|
|
||||||
#[extrinsic_call]
|
|
||||||
set_storage(RawOrigin::Root, items);
|
|
||||||
|
|
||||||
// Verify that they're actually in the storage.
|
|
||||||
for (item, _) in items_to_verify {
|
|
||||||
let value = storage::unhashed::get_raw(&item).ok_or("No value stored")?;
|
|
||||||
assert_eq!(value, *item);
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[benchmark(skip_meta)]
|
|
||||||
fn kill_storage(i: Linear<0, { 1_000 }>) -> Result<(), BenchmarkError> {
|
|
||||||
// Add i items to storage
|
|
||||||
let mut items = Vec::with_capacity(i as usize);
|
|
||||||
for j in 0..i {
|
|
||||||
let hash = (i, j).using_encoded(T::Hashing::hash).as_ref().to_vec();
|
|
||||||
storage::unhashed::put_raw(&hash, &hash);
|
|
||||||
items.push(hash);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify that they're actually in the storage.
|
|
||||||
for item in &items {
|
|
||||||
let value = storage::unhashed::get_raw(item).ok_or("No value stored")?;
|
|
||||||
assert_eq!(value, *item);
|
|
||||||
}
|
|
||||||
|
|
||||||
let items_to_verify = items.clone();
|
|
||||||
|
|
||||||
#[extrinsic_call]
|
|
||||||
kill_storage(RawOrigin::Root, items);
|
|
||||||
|
|
||||||
// Verify that they're not in the storage anymore.
|
|
||||||
for item in items_to_verify {
|
|
||||||
assert!(storage::unhashed::get_raw(&item).is_none());
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[benchmark(skip_meta)]
|
|
||||||
fn kill_prefix(p: Linear<0, { 1_000 }>) -> Result<(), BenchmarkError> {
|
|
||||||
let prefix = p.using_encoded(T::Hashing::hash).as_ref().to_vec();
|
|
||||||
let mut items = Vec::with_capacity(p as usize);
|
|
||||||
// add p items that share a prefix
|
|
||||||
for i in 0..p {
|
|
||||||
let hash = (p, i).using_encoded(T::Hashing::hash).as_ref().to_vec();
|
|
||||||
let key = [&prefix[..], &hash[..]].concat();
|
|
||||||
storage::unhashed::put_raw(&key, &key);
|
|
||||||
items.push(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify that they're actually in the storage.
|
|
||||||
for item in &items {
|
|
||||||
let value = storage::unhashed::get_raw(item).ok_or("No value stored")?;
|
|
||||||
assert_eq!(value, *item);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[extrinsic_call]
|
|
||||||
kill_prefix(RawOrigin::Root, prefix, p);
|
|
||||||
|
|
||||||
// Verify that they're not in the storage anymore.
|
|
||||||
for item in items {
|
|
||||||
assert!(storage::unhashed::get_raw(&item).is_none());
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[benchmark]
|
|
||||||
fn authorize_upgrade() -> Result<(), BenchmarkError> {
|
|
||||||
let runtime_blob = T::prepare_set_code_data();
|
|
||||||
T::setup_set_code_requirements(&runtime_blob)?;
|
|
||||||
let hash = T::Hashing::hash(&runtime_blob);
|
|
||||||
|
|
||||||
#[extrinsic_call]
|
|
||||||
authorize_upgrade(RawOrigin::Root, hash);
|
|
||||||
|
|
||||||
assert!(System::<T>::authorized_upgrade().is_some());
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[benchmark]
|
|
||||||
fn apply_authorized_upgrade() -> Result<(), BenchmarkError> {
|
|
||||||
let runtime_blob = T::prepare_set_code_data();
|
|
||||||
T::setup_set_code_requirements(&runtime_blob)?;
|
|
||||||
let hash = T::Hashing::hash(&runtime_blob);
|
|
||||||
// Will be heavier when it needs to do verification (i.e. don't use `...without_checks`).
|
|
||||||
System::<T>::authorize_upgrade(RawOrigin::Root.into(), hash)?;
|
|
||||||
|
|
||||||
#[extrinsic_call]
|
|
||||||
apply_authorized_upgrade(RawOrigin::Root, runtime_blob);
|
|
||||||
|
|
||||||
// Can't check for `CodeUpdated` in parachain upgrades. Just check that the authorization is
|
|
||||||
// gone.
|
|
||||||
assert!(System::<T>::authorized_upgrade().is_none());
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
impl_benchmark_test_suite!(Pallet, crate::mock::new_test_ext(), crate::mock::Test);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -0,0 +1,50 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
//! Supporting types for try-runtime, testing and dry-running commands.
|
||||||
|
|
||||||
|
pub use frame_support::traits::{TryStateSelect, UpgradeCheckSelect};
|
||||||
|
use frame_support::weights::Weight;
|
||||||
|
|
||||||
|
sp_api::decl_runtime_apis! {
|
||||||
|
/// Runtime api for testing the execution of a runtime upgrade.
|
||||||
|
pub trait TryRuntime {
|
||||||
|
/// dry-run runtime upgrades, returning the total weight consumed.
|
||||||
|
///
|
||||||
|
/// This should do EXACTLY the same operations as the runtime would have done in the case of
|
||||||
|
/// a runtime upgrade (e.g. pallet ordering must be the same)
|
||||||
|
///
|
||||||
|
/// Returns the consumed weight of the migration in case of a successful one, combined with
|
||||||
|
/// the total allowed block weight of the runtime.
|
||||||
|
///
|
||||||
|
/// If `checks` is `true`, `pre_migrate` and `post_migrate` of each migration and
|
||||||
|
/// `try_state` of all pallets will be executed. Else, no. If checks are executed, the PoV
|
||||||
|
/// tracking is likely inaccurate.
|
||||||
|
fn on_runtime_upgrade(checks: UpgradeCheckSelect) -> (Weight, Weight);
|
||||||
|
|
||||||
|
/// Execute the given block, but optionally disable state-root and signature checks.
|
||||||
|
///
|
||||||
|
/// Optionally, a number of `try_state` hooks can also be executed after the block
|
||||||
|
/// execution.
|
||||||
|
fn execute_block(
|
||||||
|
block: Block,
|
||||||
|
state_root_check: bool,
|
||||||
|
signature_check: bool,
|
||||||
|
try_state: TryStateSelect,
|
||||||
|
) -> Weight;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,36 +18,9 @@
|
|||||||
//! Supporting types for try-runtime, testing and dry-running commands.
|
//! Supporting types for try-runtime, testing and dry-running commands.
|
||||||
|
|
||||||
#![cfg_attr(not(feature = "std"), no_std)]
|
#![cfg_attr(not(feature = "std"), no_std)]
|
||||||
#![cfg(feature = "try-runtime")]
|
|
||||||
|
|
||||||
pub use frame_support::traits::{TryStateSelect, UpgradeCheckSelect};
|
#[cfg(feature = "try-runtime")]
|
||||||
use frame_support::weights::Weight;
|
pub mod inner;
|
||||||
|
|
||||||
sp_api::decl_runtime_apis! {
|
#[cfg(feature = "try-runtime")]
|
||||||
/// Runtime api for testing the execution of a runtime upgrade.
|
pub use inner::*;
|
||||||
pub trait TryRuntime {
|
|
||||||
/// dry-run runtime upgrades, returning the total weight consumed.
|
|
||||||
///
|
|
||||||
/// This should do EXACTLY the same operations as the runtime would have done in the case of
|
|
||||||
/// a runtime upgrade (e.g. pallet ordering must be the same)
|
|
||||||
///
|
|
||||||
/// Returns the consumed weight of the migration in case of a successful one, combined with
|
|
||||||
/// the total allowed block weight of the runtime.
|
|
||||||
///
|
|
||||||
/// If `checks` is `true`, `pre_migrate` and `post_migrate` of each migration and
|
|
||||||
/// `try_state` of all pallets will be executed. Else, no. If checks are executed, the PoV
|
|
||||||
/// tracking is likely inaccurate.
|
|
||||||
fn on_runtime_upgrade(checks: UpgradeCheckSelect) -> (Weight, Weight);
|
|
||||||
|
|
||||||
/// Execute the given block, but optionally disable state-root and signature checks.
|
|
||||||
///
|
|
||||||
/// Optionally, a number of `try_state` hooks can also be executed after the block
|
|
||||||
/// execution.
|
|
||||||
fn execute_block(
|
|
||||||
block: Block,
|
|
||||||
state_root_check: bool,
|
|
||||||
signature_check: bool,
|
|
||||||
try_state: TryStateSelect,
|
|
||||||
) -> Weight;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ sp-consensus-slots = { path = "../slots", default-features = false }
|
|||||||
sp-core = { path = "../../core", default-features = false }
|
sp-core = { path = "../../core", default-features = false }
|
||||||
sp-inherents = { path = "../../inherents", default-features = false }
|
sp-inherents = { path = "../../inherents", default-features = false }
|
||||||
sp-runtime = { path = "../../runtime", default-features = false }
|
sp-runtime = { path = "../../runtime", default-features = false }
|
||||||
sp-timestamp = { path = "../../timestamp", optional = true }
|
sp-timestamp = { path = "../../timestamp", optional = true, default-features = false }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["std"]
|
default = ["std"]
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ ss58-registry = { version = "1.34.0", default-features = false }
|
|||||||
sp-std = { path = "../std", default-features = false }
|
sp-std = { path = "../std", default-features = false }
|
||||||
sp-debug-derive = { path = "../debug-derive", default-features = false }
|
sp-debug-derive = { path = "../debug-derive", default-features = false }
|
||||||
sp-storage = { path = "../storage", default-features = false }
|
sp-storage = { path = "../storage", default-features = false }
|
||||||
sp-externalities = { path = "../externalities", optional = true }
|
sp-externalities = { path = "../externalities", optional = true, default-features = false }
|
||||||
futures = { version = "0.3.30", optional = true }
|
futures = { version = "0.3.30", optional = true }
|
||||||
dyn-clonable = { version = "0.9.0", optional = true }
|
dyn-clonable = { version = "0.9.0", optional = true }
|
||||||
thiserror = { optional = true, workspace = true }
|
thiserror = { optional = true, workspace = true }
|
||||||
|
|||||||
@@ -20,9 +20,9 @@ codec = { package = "parity-scale-codec", version = "3.6.1", default-features =
|
|||||||
scale-info = { version = "2.11.1", default-features = false, features = ["derive"] }
|
scale-info = { version = "2.11.1", default-features = false, features = ["derive"] }
|
||||||
sp-api = { path = "../api", default-features = false }
|
sp-api = { path = "../api", default-features = false }
|
||||||
sp-core = { path = "../core", default-features = false }
|
sp-core = { path = "../core", default-features = false }
|
||||||
sp-runtime = { path = "../runtime", optional = true }
|
sp-runtime = { path = "../runtime", optional = true, default-features = false }
|
||||||
sp-staking = { path = "../staking", default-features = false }
|
sp-staking = { path = "../staking", default-features = false }
|
||||||
sp-keystore = { path = "../keystore", optional = true }
|
sp-keystore = { path = "../keystore", optional = true, default-features = false }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["std"]
|
default = ["std"]
|
||||||
|
|||||||
@@ -19,10 +19,10 @@ targets = ["x86_64-unknown-linux-gnu"]
|
|||||||
async-trait = { version = "0.1.79", optional = true }
|
async-trait = { version = "0.1.79", optional = true }
|
||||||
codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = ["derive"] }
|
codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = ["derive"] }
|
||||||
scale-info = { version = "2.11.1", default-features = false, features = ["derive"] }
|
scale-info = { version = "2.11.1", default-features = false, features = ["derive"] }
|
||||||
sp-core = { path = "../core", optional = true }
|
sp-core = { path = "../core", optional = true, default-features = false }
|
||||||
sp-inherents = { path = "../inherents", default-features = false }
|
sp-inherents = { path = "../inherents", default-features = false }
|
||||||
sp-runtime = { path = "../runtime", default-features = false }
|
sp-runtime = { path = "../runtime", default-features = false }
|
||||||
sp-trie = { path = "../trie", optional = true }
|
sp-trie = { path = "../trie", optional = true, default-features = false }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["std"]
|
default = ["std"]
|
||||||
|
|||||||
Reference in New Issue
Block a user