feat: initialize Kurdistan SDK - independent fork of Polkadot SDK

This commit is contained in:
2025-12-13 15:44:15 +03:00
commit e4778b4576
6838 changed files with 1847450 additions and 0 deletions
@@ -0,0 +1,47 @@
[package]
name = "pallet-bags-list-remote-tests"
version = "4.0.0-dev"
authors.workspace = true
edition.workspace = true
license = "Apache-2.0"
homepage.workspace = true
repository.workspace = true
description = "FRAME pallet bags list remote test"
publish = false
[lints]
workspace = true
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]
[dependencies]
# frame
frame-election-provider-support = { workspace = true, default-features = true }
frame-support = { workspace = true, default-features = true }
frame-system = { workspace = true, default-features = true }
pallet-bags-list = { features = [
"fuzz",
], workspace = true, default-features = true }
pallet-staking = { workspace = true, default-features = true }
# core
sp-core = { workspace = true, default-features = true }
sp-runtime = { workspace = true, default-features = true }
# utils
remote-externalities = { workspace = true, default-features = true }
# others
log = { workspace = true, default-features = true }
[features]
runtime-benchmarks = [
"frame-election-provider-support/runtime-benchmarks",
"frame-support/runtime-benchmarks",
"frame-system/runtime-benchmarks",
"pallet-bags-list/runtime-benchmarks",
"pallet-staking/runtime-benchmarks",
"remote-externalities/runtime-benchmarks",
"sp-runtime/runtime-benchmarks",
]
@@ -0,0 +1,160 @@
// 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.
//! Utilities for remote-testing pallet-bags-list.
use frame_election_provider_support::ScoreProvider;
use pallet_bags_list::Instance1;
/// A common log target to use.
pub const LOG_TARGET: &str = "runtime::bags-list::remote-tests";
pub mod migration;
pub mod snapshot;
pub mod try_state;
/// A wrapper for a runtime that the functions of this crate expect.
///
/// For example, this can be the `Runtime` type of the Pezkuwi runtime.
pub trait RuntimeT<I: 'static>:
pallet_staking::Config + pallet_bags_list::Config<I> + frame_system::Config
{
}
impl<
I: 'static,
T: pallet_staking::Config + pallet_bags_list::Config<I> + frame_system::Config,
> RuntimeT<I> for T
{
}
fn percent(portion: u32, total: u32) -> f64 {
(portion as f64 / total as f64) * 100f64
}
/// Display the number of nodes in each bag, while identifying those that need a rebag.
pub fn display_and_check_bags<Runtime: RuntimeT<Instance1>>(
currency_unit: u64,
currency_name: &'static str,
) {
use frame_election_provider_support::SortedListProvider;
use frame_support::traits::Get;
let min_nominator_bond = <pallet_staking::MinNominatorBond<Runtime>>::get();
log::info!(target: LOG_TARGET, "min nominator bond is {:?}", min_nominator_bond);
let voter_list_count = <Runtime as pallet_staking::Config>::VoterList::count();
// go through every bag to track the total number of voters within bags and log some info about
// how voters are distributed within the bags.
let mut seen_in_bags = 0;
let mut rebaggable = 0;
let mut active_bags = 0;
for vote_weight_thresh in <Runtime as pallet_bags_list::Config<Instance1>>::BagThresholds::get()
{
let vote_weight_thresh_u64: u64 = (*vote_weight_thresh)
.try_into()
.map_err(|_| "runtime must configure score to at most u64 to use this test")
.unwrap();
// threshold in terms of UNITS (e.g. KSM, HEZ etc)
let vote_weight_thresh_as_unit = vote_weight_thresh_u64 as f64 / currency_unit as f64;
let pretty_thresh = format!("Threshold: {}. {}", vote_weight_thresh_as_unit, currency_name);
let bag = match pallet_bags_list::Pallet::<Runtime, Instance1>::list_bags_get(
*vote_weight_thresh,
) {
Some(bag) => bag,
None => {
log::info!(target: LOG_TARGET, "{} NO VOTERS.", pretty_thresh);
continue;
},
};
active_bags += 1;
for id in bag.std_iter().map(|node| node.std_id().clone()) {
let vote_weight =
<Runtime as pallet_bags_list::Config<Instance1>>::ScoreProvider::score(&id)
.unwrap();
let vote_weight_thresh_u64: u64 = (*vote_weight_thresh)
.try_into()
.map_err(|_| "runtime must configure score to at most u64 to use this test")
.unwrap();
let vote_weight_as_balance: pallet_staking::BalanceOf<Runtime> =
vote_weight_thresh_u64.try_into().map_err(|_| "can't convert").unwrap();
if vote_weight_as_balance < min_nominator_bond {
log::trace!(
target: LOG_TARGET,
"⚠️ {} Account found below min bond: {:?}.",
pretty_thresh,
id
);
}
let node = pallet_bags_list::Node::<Runtime, Instance1>::get(&id)
.expect("node in bag must exist.");
if node.is_misplaced(vote_weight) {
rebaggable += 1;
let notional_bag = pallet_bags_list::notional_bag_for::<Runtime, _>(vote_weight);
let notional_bag_as_u64: u64 = notional_bag
.try_into()
.map_err(|_| "runtime must configure score to at most u64 to use this test")
.unwrap();
log::trace!(
target: LOG_TARGET,
"Account {:?} can be rebagged from {:?} to {:?}",
id,
vote_weight_thresh_as_unit,
notional_bag_as_u64 as f64 / currency_unit as f64
);
}
}
// update our overall counter
let voters_in_bag = bag.std_iter().count() as u32;
seen_in_bags += voters_in_bag;
// percentage of all nominators
let percent_of_voters = percent(voters_in_bag, voter_list_count);
log::info!(
target: LOG_TARGET,
"{} Nominators: {} [%{:.3}]",
pretty_thresh,
voters_in_bag,
percent_of_voters,
);
}
if seen_in_bags != voter_list_count {
log::error!(
target: LOG_TARGET,
"bags list population ({}) not on par whoever is voter_list ({})",
seen_in_bags,
voter_list_count,
)
}
log::info!(
target: LOG_TARGET,
"a total of {} nodes are in {} active bags [{} total bags], {} of which can be rebagged.",
voter_list_count,
active_bags,
<Runtime as pallet_bags_list::Config<Instance1>>::BagThresholds::get().len(),
rebaggable,
);
}
@@ -0,0 +1,67 @@
// Copyright (C) Parity Technologies (UK) Ltd.
// This file is part of Substrate.
// 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.
//! Test to check the migration of the voter bag.
use crate::{RuntimeT, LOG_TARGET};
use frame_support::traits::PalletInfoAccess;
use pallet_staking::Nominators;
use remote_externalities::{Builder, Mode, OnlineConfig};
use sp_runtime::{traits::Block as BlockT, DeserializeOwned};
/// Test voter bags migration. `currency_unit` is the number of planks per the the runtimes `UNITS`
/// (i.e. number of decimal places per HEZ, KSM etc)
pub async fn execute<Runtime, Block>(
currency_unit: u64,
currency_name: &'static str,
ws_url: String,
) where
Runtime: RuntimeT<pallet_bags_list::Instance1>,
Block: BlockT + DeserializeOwned,
Block::Header: DeserializeOwned,
{
let mut ext = Builder::<Block>::new()
.mode(Mode::Online(OnlineConfig {
transport: ws_url.to_string().into(),
pallets: vec![pallet_staking::Pallet::<Runtime>::name().to_string()],
..Default::default()
}))
.build()
.await
.unwrap();
ext.execute_with(|| {
// get the nominator & validator count prior to migrating; these should be invariant.
let pre_migrate_nominator_count = <Nominators<Runtime>>::iter().count() as u32;
log::info!(target: LOG_TARGET, "Nominator count: {}", pre_migrate_nominator_count);
use frame_election_provider_support::SortedListProvider;
// run the actual migration
let moved = <Runtime as pallet_staking::Config>::VoterList::unsafe_regenerate(
pallet_staking::Nominators::<Runtime>::iter().map(|(n, _)| n),
Box::new(|x| Some(pallet_staking::Pallet::<Runtime>::weight_of(x))),
);
log::info!(target: LOG_TARGET, "Moved {} nominators", moved);
let voter_list_len = <Runtime as pallet_staking::Config>::VoterList::iter().count() as u32;
let voter_list_count = <Runtime as pallet_staking::Config>::VoterList::count();
// and confirm it is equal to the length of the `VoterList`.
assert_eq!(pre_migrate_nominator_count, voter_list_len);
assert_eq!(pre_migrate_nominator_count, voter_list_count);
crate::display_and_check_bags::<Runtime>(currency_unit, currency_name);
});
}
@@ -0,0 +1,104 @@
// Copyright (C) Parity Technologies (UK) Ltd.
// This file is part of Substrate.
// 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.
//! Test to execute the snapshot using the voter bag.
use frame_election_provider_support::{
bounds::{CountBound, DataProviderBounds},
SortedListProvider,
};
use frame_support::traits::PalletInfoAccess;
use remote_externalities::{Builder, Mode, OnlineConfig};
use sp_runtime::{
traits::{Block as BlockT, Zero},
DeserializeOwned,
};
/// Execute create a snapshot from pallet-staking.
pub async fn execute<Runtime, Block>(voter_limit: Option<usize>, currency_unit: u64, ws_url: String)
where
Runtime: crate::RuntimeT<pallet_bags_list::Instance1>,
Block: BlockT + DeserializeOwned,
Block::Header: DeserializeOwned,
{
use frame_support::storage::generator::StorageMap;
let mut ext = Builder::<Block>::new()
.mode(Mode::Online(OnlineConfig {
transport: ws_url.to_string().into(),
// NOTE: we don't scrape pallet-staking, this kinda ensures that the source of the data
// is bags-list.
pallets: vec![pallet_bags_list::Pallet::<Runtime, pallet_bags_list::Instance1>::name()
.to_string()],
at: None,
hashed_prefixes: vec![
<pallet_staking::Bonded<Runtime>>::prefix_hash().to_vec(),
<pallet_staking::Ledger<Runtime>>::prefix_hash().to_vec(),
<pallet_staking::Validators<Runtime>>::map_storage_final_prefix(),
<pallet_staking::Nominators<Runtime>>::map_storage_final_prefix(),
],
hashed_keys: vec![
<pallet_staking::Validators<Runtime>>::counter_storage_final_key().to_vec(),
<pallet_staking::Nominators<Runtime>>::counter_storage_final_key().to_vec(),
],
..Default::default()
}))
.build()
.await
.unwrap();
ext.execute_with(|| {
use frame_election_provider_support::ElectionDataProvider;
log::info!(
target: crate::LOG_TARGET,
"{} nodes in bags list.",
<Runtime as pallet_staking::Config>::VoterList::count(),
);
let bounds = match voter_limit {
None => DataProviderBounds::default(),
Some(v) => DataProviderBounds { count: Some(CountBound(v as u32)), size: None },
};
// single page voter snapshot, thus page index == 0.
let voters =
<pallet_staking::Pallet<Runtime> as ElectionDataProvider>::electing_voters(bounds, Zero::zero())
.unwrap();
let mut voters_nominator_only = voters
.iter()
.filter(|(v, _, _)| pallet_staking::Nominators::<Runtime>::contains_key(v))
.cloned()
.collect::<Vec<_>>();
voters_nominator_only.sort_by_key(|(_, w, _)| *w);
let currency_unit = currency_unit as f64;
let min_voter = voters_nominator_only
.first()
.map(|(x, y, _)| (x.clone(), *y as f64 / currency_unit));
let max_voter = voters_nominator_only
.last()
.map(|(x, y, _)| (x.clone(), *y as f64 / currency_unit));
log::info!(
target: crate::LOG_TARGET,
"a snapshot with limit {:?} has been created, {} voters are taken. min nominator: {:?}, max: {:?}",
voter_limit,
voters.len(),
min_voter,
max_voter
);
});
}
@@ -0,0 +1,60 @@
// Copyright (C) Parity Technologies (UK) Ltd.
// This file is part of Substrate.
// 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.
//! Test to execute the sanity-check of the voter bag.
use frame_support::{
storage::generator::StorageMap,
traits::{Get, PalletInfoAccess},
};
use remote_externalities::{Builder, Mode, OnlineConfig};
use sp_runtime::{traits::Block as BlockT, DeserializeOwned};
/// Execute the sanity check of the bags-list.
pub async fn execute<Runtime, Block>(
currency_unit: u64,
currency_name: &'static str,
ws_url: String,
) where
Runtime: crate::RuntimeT<pallet_bags_list::Instance1>,
Block: BlockT + DeserializeOwned,
Block::Header: DeserializeOwned,
{
let mut ext = Builder::<Block>::new()
.mode(Mode::Online(OnlineConfig {
transport: ws_url.to_string().into(),
pallets: vec![pallet_bags_list::Pallet::<Runtime, pallet_bags_list::Instance1>::name()
.to_string()],
hashed_prefixes: vec![
<pallet_staking::Bonded<Runtime>>::prefix_hash().to_vec(),
<pallet_staking::Ledger<Runtime>>::prefix_hash().to_vec(),
],
..Default::default()
}))
.build()
.await
.unwrap();
ext.execute_with(|| {
sp_core::crypto::set_default_ss58_version(Runtime::SS58Prefix::get().try_into().unwrap());
pallet_bags_list::Pallet::<Runtime, pallet_bags_list::Instance1>::do_try_state().unwrap();
log::info!(target: crate::LOG_TARGET, "executed bags-list sanity check with no errors.");
crate::display_and_check_bags::<Runtime>(currency_unit, currency_name);
});
}