feat: initialize Kurdistan SDK - independent fork of Polkadot SDK

This commit is contained in:
2025-12-13 15:44:15 +03:00
commit 286de54384
6841 changed files with 1848356 additions and 0 deletions
@@ -0,0 +1,156 @@
// Copyright (C) Parity Technologies (UK) Ltd.
// This file is part of Pezkuwi.
// Pezkuwi is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Pezkuwi is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Pezkuwi. If not, see <http://www.gnu.org/licenses/>.
use bitvec::{bitvec, prelude::Lsb0};
use frame_benchmarking::v2::*;
use pallet_message_queue as mq;
use pezkuwi_primitives::{
CandidateCommitments, CommittedCandidateReceiptV2 as CommittedCandidateReceipt, HrmpChannelId,
OutboundHrmpMessage, SessionIndex,
};
use super::*;
use crate::{
builder::generate_validator_pairs,
configuration,
hrmp::{HrmpChannel, HrmpChannels},
initializer, HeadData, ValidationCode,
};
fn create_candidate_commitments<T: crate::hrmp::pallet::Config>(
para_id: ParaId,
head_data: HeadData,
max_msg_len: usize,
ump_msg_count: u32,
hrmp_msg_count: u32,
code_upgrade: bool,
) -> CandidateCommitments {
let upward_messages = {
let unbounded = create_messages(max_msg_len, ump_msg_count as _);
BoundedVec::truncate_from(unbounded)
};
let horizontal_messages = {
let unbounded = create_messages(max_msg_len, hrmp_msg_count as _);
for n in 0..unbounded.len() {
let channel_id = HrmpChannelId { sender: para_id, recipient: para_id + n as u32 + 1 };
HrmpChannels::<T>::insert(
&channel_id,
HrmpChannel {
sender_deposit: 42,
recipient_deposit: 42,
max_capacity: 10_000_000,
max_total_size: 1_000_000_000,
max_message_size: 10_000_000,
msg_count: 0,
total_size: 0,
mqc_head: None,
},
);
}
let unbounded = unbounded
.into_iter()
.enumerate()
.map(|(n, data)| OutboundHrmpMessage { recipient: para_id + n as u32 + 1, data })
.collect();
BoundedVec::truncate_from(unbounded)
};
let new_validation_code = code_upgrade.then_some(ValidationCode(vec![42_u8; 1024]));
CandidateCommitments::<u32> {
upward_messages,
horizontal_messages,
new_validation_code,
head_data,
processed_downward_messages: 0,
hrmp_watermark: 10,
}
}
fn create_messages(msg_len: usize, n_msgs: usize) -> Vec<Vec<u8>> {
let best_number = 73_u8; // Chuck Norris of numbers
vec![vec![best_number; msg_len]; n_msgs]
}
#[benchmarks(where T: mq::Config + configuration::Config + initializer::Config)]
mod benchmarks {
use super::*;
#[benchmark]
fn enact_candidate(u: Linear<0, 2>, h: Linear<0, 2>, c: Linear<0, 1>) {
let para = 42_u32.into(); // not especially important.
let max_len = mq::MaxMessageLenOf::<T>::get() as usize;
let config = configuration::ActiveConfig::<T>::get();
let n_validators = config.max_validators.unwrap_or(500);
let validators = generate_validator_pairs::<T>(n_validators);
let session = SessionIndex::from(0_u32);
initializer::Pallet::<T>::test_trigger_on_new_session(
false,
session,
validators.iter().map(|(a, v)| (a, v.clone())),
None,
);
let backing_group_size = config.scheduler_params.max_validators_per_core.unwrap_or(5);
let head_data = HeadData(vec![0xFF; 1024]);
let relay_parent_number = BlockNumberFor::<T>::from(10_u32);
let commitments = create_candidate_commitments::<T>(para, head_data, max_len, u, h, c != 0);
let backers = bitvec![u8, Lsb0; 1; backing_group_size as usize];
let availability_votes = bitvec![u8, Lsb0; 1; n_validators as usize];
let core_index = CoreIndex::from(0);
let backing_group = GroupIndex::from(0);
let descriptor = CandidateDescriptor::<T::Hash>::new(
para,
Default::default(),
CoreIndex(0),
1,
Default::default(),
Default::default(),
Default::default(),
Default::default(),
ValidationCode(vec![1, 2, 3]).hash(),
);
let receipt = CommittedCandidateReceipt::<T::Hash> { descriptor, commitments };
Pallet::<T>::receive_upward_messages(para, &vec![vec![0; max_len]; 1]);
#[block]
{
Pallet::<T>::enact_candidate(
relay_parent_number,
receipt,
backers,
availability_votes,
core_index,
backing_group,
);
}
}
impl_benchmark_test_suite! {
Pallet,
crate::mock::new_test_ext(Default::default()),
crate::mock::Test
}
}
@@ -0,0 +1,319 @@
// Copyright (C) Parity Technologies (UK) Ltd.
// This file is part of Pezkuwi.
// Pezkuwi is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Pezkuwi is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
pub use v1::MigrateToV1;
pub mod v0 {
use crate::inclusion::{Config, Pallet};
use bitvec::{order::Lsb0 as BitOrderLsb0, vec::BitVec};
use codec::{Decode, Encode};
use frame_support::{storage_alias, Twox64Concat};
use frame_system::pallet_prelude::BlockNumberFor;
use pezkuwi_primitives::{
AvailabilityBitfield, CandidateCommitments, CandidateDescriptorV2 as CandidateDescriptor,
CandidateHash, CoreIndex, GroupIndex, Id as ParaId, ValidatorIndex,
};
use scale_info::TypeInfo;
#[derive(Encode, Decode, PartialEq, TypeInfo, Clone, Debug)]
pub struct CandidatePendingAvailability<H, N> {
pub core: CoreIndex,
pub hash: CandidateHash,
pub descriptor: CandidateDescriptor<H>,
pub availability_votes: BitVec<u8, BitOrderLsb0>,
pub backers: BitVec<u8, BitOrderLsb0>,
pub relay_parent_number: N,
pub backed_in_number: N,
pub backing_group: GroupIndex,
}
#[derive(Encode, Decode, TypeInfo, Debug, PartialEq)]
pub struct AvailabilityBitfieldRecord<N> {
pub bitfield: AvailabilityBitfield,
pub submitted_at: N,
}
#[storage_alias]
pub type PendingAvailability<T: Config> = StorageMap<
Pallet<T>,
Twox64Concat,
ParaId,
CandidatePendingAvailability<<T as frame_system::Config>::Hash, BlockNumberFor<T>>,
>;
#[storage_alias]
pub type PendingAvailabilityCommitments<T: Config> =
StorageMap<Pallet<T>, Twox64Concat, ParaId, CandidateCommitments>;
#[storage_alias]
pub type AvailabilityBitfields<T: Config> = StorageMap<
Pallet<T>,
Twox64Concat,
ValidatorIndex,
AvailabilityBitfieldRecord<BlockNumberFor<T>>,
>;
}
mod v1 {
use super::v0::{
AvailabilityBitfields, PendingAvailability as V0PendingAvailability,
PendingAvailabilityCommitments as V0PendingAvailabilityCommitments,
};
use crate::inclusion::{
CandidatePendingAvailability as V1CandidatePendingAvailability, Config, Pallet,
PendingAvailability as V1PendingAvailability,
};
use alloc::{collections::vec_deque::VecDeque, vec::Vec};
use frame_support::{traits::UncheckedOnRuntimeUpgrade, weights::Weight};
use sp_core::Get;
#[cfg(feature = "try-runtime")]
use codec::{Decode, Encode};
#[cfg(feature = "try-runtime")]
use frame_support::{
ensure,
traits::{GetStorageVersion, StorageVersion},
};
pub struct VersionUncheckedMigrateToV1<T>(core::marker::PhantomData<T>);
impl<T: Config> UncheckedOnRuntimeUpgrade for VersionUncheckedMigrateToV1<T> {
#[cfg(feature = "try-runtime")]
fn pre_upgrade() -> Result<Vec<u8>, sp_runtime::TryRuntimeError> {
log::trace!(target: crate::inclusion::LOG_TARGET, "Running pre_upgrade() for inclusion MigrateToV1");
let candidates_before_upgrade = V0PendingAvailability::<T>::iter().count();
let commitments_before_upgrade = V0PendingAvailabilityCommitments::<T>::iter().count();
if candidates_before_upgrade != commitments_before_upgrade {
log::warn!(
target: crate::inclusion::LOG_TARGET,
"Number of pending candidates differ from the number of pending commitments. {} vs {}",
candidates_before_upgrade,
commitments_before_upgrade
);
}
Ok((candidates_before_upgrade as u32).encode())
}
fn on_runtime_upgrade() -> Weight {
let mut weight: Weight = Weight::zero();
let v0_candidates: Vec<_> = V0PendingAvailability::<T>::drain().collect();
for (para_id, candidate) in v0_candidates {
let commitments = V0PendingAvailabilityCommitments::<T>::take(para_id);
// One write for each removal (one candidate and one commitment).
weight = weight.saturating_add(T::DbWeight::get().writes(2));
if let Some(commitments) = commitments {
let mut per_para = VecDeque::new();
per_para.push_back(V1CandidatePendingAvailability {
core: candidate.core,
hash: candidate.hash,
descriptor: candidate.descriptor,
availability_votes: candidate.availability_votes,
backers: candidate.backers,
relay_parent_number: candidate.relay_parent_number,
backed_in_number: candidate.backed_in_number,
backing_group: candidate.backing_group,
commitments,
});
V1PendingAvailability::<T>::insert(para_id, per_para);
weight = weight.saturating_add(T::DbWeight::get().writes(1));
}
}
// should've already been drained by the above for loop, but as a sanity check, in case
// there are more commitments than candidates.
// V0PendingAvailabilityCommitments should not contain too many keys so removing
// everything at once should be safe
let res = V0PendingAvailabilityCommitments::<T>::clear(u32::MAX, None);
weight = weight.saturating_add(
T::DbWeight::get().reads_writes(res.loops as u64, res.backend as u64),
);
// AvailabilityBitfields should not contain too many keys so removing everything at once
// should be safe.
let res = AvailabilityBitfields::<T>::clear(u32::MAX, None);
weight = weight.saturating_add(
T::DbWeight::get().reads_writes(res.loops as u64, res.backend as u64),
);
weight
}
#[cfg(feature = "try-runtime")]
fn post_upgrade(state: Vec<u8>) -> Result<(), sp_runtime::TryRuntimeError> {
log::trace!(target: crate::inclusion::LOG_TARGET, "Running post_upgrade() for inclusion MigrateToV1");
ensure!(
Pallet::<T>::on_chain_storage_version() >= StorageVersion::new(1),
"Storage version should be >= 1 after the migration"
);
let candidates_before_upgrade =
u32::decode(&mut &state[..]).expect("Was properly encoded") as usize;
let candidates_after_upgrade = V1PendingAvailability::<T>::iter().fold(
0usize,
|mut acc, (_paraid, para_candidates)| {
acc += para_candidates.len();
acc
},
);
ensure!(
candidates_before_upgrade == candidates_after_upgrade,
"Number of pending candidates should be the same as the one before the upgrade."
);
ensure!(
V0PendingAvailability::<T>::iter().next() == None,
"Pending availability candidates storage v0 should have been removed"
);
ensure!(
V0PendingAvailabilityCommitments::<T>::iter().next() == None,
"Pending availability commitments storage should have been removed"
);
ensure!(
AvailabilityBitfields::<T>::iter().next() == None,
"Availability bitfields storage should have been removed"
);
Ok(())
}
}
/// Migrate to v1 inclusion module storage.
/// - merges the `PendingAvailabilityCommitments` into the `CandidatePendingAvailability`
/// storage
/// - removes the `AvailabilityBitfields` storage, which was never read.
pub type MigrateToV1<T> = frame_support::migrations::VersionedMigration<
0,
1,
VersionUncheckedMigrateToV1<T>,
Pallet<T>,
<T as frame_system::Config>::DbWeight,
>;
}
#[cfg(test)]
mod tests {
use super::{v1::VersionUncheckedMigrateToV1, *};
use crate::{
inclusion::{
CandidatePendingAvailability as V1CandidatePendingAvailability,
PendingAvailability as V1PendingAvailability, *,
},
mock::{new_test_ext, MockGenesisConfig, Test},
};
use frame_support::traits::UncheckedOnRuntimeUpgrade;
use pezkuwi_primitives::{AvailabilityBitfield, Id as ParaId};
use pezkuwi_primitives_test_helpers::{
dummy_candidate_commitments, dummy_candidate_descriptor_v2, dummy_hash,
};
#[test]
fn migrate_to_v1() {
new_test_ext(MockGenesisConfig::default()).execute_with(|| {
// No data to migrate.
assert_eq!(
<VersionUncheckedMigrateToV1<Test> as UncheckedOnRuntimeUpgrade>::on_runtime_upgrade(),
Weight::zero()
);
assert!(V1PendingAvailability::<Test>::iter().next().is_none());
let mut expected = vec![];
for i in 1..5 {
let descriptor = dummy_candidate_descriptor_v2(dummy_hash());
v0::PendingAvailability::<Test>::insert(
ParaId::from(i),
v0::CandidatePendingAvailability {
core: CoreIndex(i),
descriptor: descriptor.clone(),
relay_parent_number: i,
hash: CandidateHash(dummy_hash()),
availability_votes: Default::default(),
backed_in_number: i,
backers: Default::default(),
backing_group: GroupIndex(i),
},
);
v0::PendingAvailabilityCommitments::<Test>::insert(
ParaId::from(i),
dummy_candidate_commitments(HeadData(vec![i as _])),
);
v0::AvailabilityBitfields::<Test>::insert(
ValidatorIndex(i),
v0::AvailabilityBitfieldRecord {
bitfield: AvailabilityBitfield(Default::default()),
submitted_at: i,
},
);
expected.push((
ParaId::from(i),
[V1CandidatePendingAvailability {
core: CoreIndex(i),
descriptor,
relay_parent_number: i,
hash: CandidateHash(dummy_hash()),
availability_votes: Default::default(),
backed_in_number: i,
backers: Default::default(),
backing_group: GroupIndex(i),
commitments: dummy_candidate_commitments(HeadData(vec![i as _])),
}]
.into_iter()
.collect::<VecDeque<_>>(),
));
}
// add some wrong data also, candidates without commitments or commitments without
// candidates.
v0::PendingAvailability::<Test>::insert(
ParaId::from(6),
v0::CandidatePendingAvailability {
core: CoreIndex(6),
descriptor: dummy_candidate_descriptor_v2(dummy_hash()),
relay_parent_number: 6,
hash: CandidateHash(dummy_hash()),
availability_votes: Default::default(),
backed_in_number: 6,
backers: Default::default(),
backing_group: GroupIndex(6),
},
);
v0::PendingAvailabilityCommitments::<Test>::insert(
ParaId::from(7),
dummy_candidate_commitments(HeadData(vec![7 as _])),
);
// For tests, db weight is zero.
assert_eq!(
<VersionUncheckedMigrateToV1<Test> as UncheckedOnRuntimeUpgrade>::on_runtime_upgrade(),
Weight::zero()
);
assert_eq!(v0::PendingAvailabilityCommitments::<Test>::iter().next(), None);
assert_eq!(v0::PendingAvailability::<Test>::iter().next(), None);
assert_eq!(v0::AvailabilityBitfields::<Test>::iter().next(), None);
let mut actual = V1PendingAvailability::<Test>::iter().collect::<Vec<_>>();
actual.sort_by(|(id1, _), (id2, _)| id1.cmp(id2));
expected.sort_by(|(id1, _), (id2, _)| id1.cmp(id2));
assert_eq!(actual, expected);
});
}
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff