referrerpolicy=no-referrer-when-downgrade

pallet_bags_list_remote_tests/
lib.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// 	http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18//! Utilities for remote-testing pallet-bags-list.
19
20use frame_election_provider_support::ScoreProvider;
21use pallet_bags_list::Instance1;
22
23/// A common log target to use.
24pub const LOG_TARGET: &str = "runtime::bags-list::remote-tests";
25
26pub mod migration;
27pub mod snapshot;
28pub mod try_state;
29
30/// A wrapper for a runtime that the functions of this crate expect.
31///
32/// For example, this can be the `Runtime` type of the Polkadot runtime.
33pub trait RuntimeT<I: 'static>:
34	pallet_staking::Config + pallet_bags_list::Config<I> + frame_system::Config
35{
36}
37impl<
38		I: 'static,
39		T: pallet_staking::Config + pallet_bags_list::Config<I> + frame_system::Config,
40	> RuntimeT<I> for T
41{
42}
43
44fn percent(portion: u32, total: u32) -> f64 {
45	(portion as f64 / total as f64) * 100f64
46}
47
48/// Display the number of nodes in each bag, while identifying those that need a rebag.
49pub fn display_and_check_bags<Runtime: RuntimeT<Instance1>>(
50	currency_unit: u64,
51	currency_name: &'static str,
52) {
53	use frame_election_provider_support::SortedListProvider;
54	use frame_support::traits::Get;
55
56	let min_nominator_bond = <pallet_staking::MinNominatorBond<Runtime>>::get();
57	log::info!(target: LOG_TARGET, "min nominator bond is {:?}", min_nominator_bond);
58
59	let voter_list_count = <Runtime as pallet_staking::Config>::VoterList::count();
60
61	// go through every bag to track the total number of voters within bags and log some info about
62	// how voters are distributed within the bags.
63	let mut seen_in_bags = 0;
64	let mut rebaggable = 0;
65	let mut active_bags = 0;
66	for vote_weight_thresh in <Runtime as pallet_bags_list::Config<Instance1>>::BagThresholds::get()
67	{
68		let vote_weight_thresh_u64: u64 = (*vote_weight_thresh)
69			.try_into()
70			.map_err(|_| "runtime must configure score to at most u64 to use this test")
71			.unwrap();
72		// threshold in terms of UNITS (e.g. KSM, DOT etc)
73		let vote_weight_thresh_as_unit = vote_weight_thresh_u64 as f64 / currency_unit as f64;
74		let pretty_thresh = format!("Threshold: {}. {}", vote_weight_thresh_as_unit, currency_name);
75
76		let bag = match pallet_bags_list::Pallet::<Runtime, Instance1>::list_bags_get(
77			*vote_weight_thresh,
78		) {
79			Some(bag) => bag,
80			None => {
81				log::info!(target: LOG_TARGET, "{} NO VOTERS.", pretty_thresh);
82				continue
83			},
84		};
85
86		active_bags += 1;
87
88		for id in bag.std_iter().map(|node| node.std_id().clone()) {
89			let vote_weight =
90				<Runtime as pallet_bags_list::Config<Instance1>>::ScoreProvider::score(&id)
91					.unwrap();
92			let vote_weight_thresh_u64: u64 = (*vote_weight_thresh)
93				.try_into()
94				.map_err(|_| "runtime must configure score to at most u64 to use this test")
95				.unwrap();
96			let vote_weight_as_balance: pallet_staking::BalanceOf<Runtime> =
97				vote_weight_thresh_u64.try_into().map_err(|_| "can't convert").unwrap();
98
99			if vote_weight_as_balance < min_nominator_bond {
100				log::trace!(
101					target: LOG_TARGET,
102					"⚠️ {} Account found below min bond: {:?}.",
103					pretty_thresh,
104					id
105				);
106			}
107
108			let node = pallet_bags_list::Node::<Runtime, Instance1>::get(&id)
109				.expect("node in bag must exist.");
110			if node.is_misplaced(vote_weight) {
111				rebaggable += 1;
112				let notional_bag = pallet_bags_list::notional_bag_for::<Runtime, _>(vote_weight);
113				let notional_bag_as_u64: u64 = notional_bag
114					.try_into()
115					.map_err(|_| "runtime must configure score to at most u64 to use this test")
116					.unwrap();
117				log::trace!(
118					target: LOG_TARGET,
119					"Account {:?} can be rebagged from {:?} to {:?}",
120					id,
121					vote_weight_thresh_as_unit,
122					notional_bag_as_u64 as f64 / currency_unit as f64
123				);
124			}
125		}
126
127		// update our overall counter
128		let voters_in_bag = bag.std_iter().count() as u32;
129		seen_in_bags += voters_in_bag;
130
131		// percentage of all nominators
132		let percent_of_voters = percent(voters_in_bag, voter_list_count);
133
134		log::info!(
135			target: LOG_TARGET,
136			"{} Nominators: {} [%{:.3}]",
137			pretty_thresh,
138			voters_in_bag,
139			percent_of_voters,
140		);
141	}
142
143	if seen_in_bags != voter_list_count {
144		log::error!(
145			target: LOG_TARGET,
146			"bags list population ({}) not on par whoever is voter_list ({})",
147			seen_in_bags,
148			voter_list_count,
149		)
150	}
151
152	log::info!(
153		target: LOG_TARGET,
154		"a total of {} nodes are in {} active bags [{} total bags], {} of which can be rebagged.",
155		voter_list_count,
156		active_bags,
157		<Runtime as pallet_bags_list::Config<Instance1>>::BagThresholds::get().len(),
158		rebaggable,
159	);
160}