feat: Rebrand Polkadot/Substrate references to PezkuwiChain

This commit systematically rebrands various references from Parity Technologies'
Polkadot/Substrate ecosystem to PezkuwiChain within the kurdistan-sdk.

Key changes include:
- Updated external repository URLs (zombienet-sdk, parity-db, parity-scale-codec, wasm-instrument) to point to pezkuwichain forks.
- Modified internal documentation and code comments to reflect PezkuwiChain naming and structure.
- Replaced direct references to  with  or specific paths within the  for XCM, Pezkuwi, and other modules.
- Cleaned up deprecated  issue and PR references in various  and  files, particularly in  and  modules.
- Adjusted image and logo URLs in documentation to point to PezkuwiChain assets.
- Removed or rephrased comments related to external Polkadot/Substrate PRs and issues.

This is a significant step towards fully customizing the SDK for the PezkuwiChain ecosystem.
This commit is contained in:
2025-12-14 00:04:10 +03:00
parent 286de54384
commit 1c0e57d984
9084 changed files with 997839 additions and 997557 deletions
+57
View File
@@ -0,0 +1,57 @@
[package]
name = "pezpallet-offences"
version = "27.0.0"
authors.workspace = true
edition.workspace = true
license = "Apache-2.0"
homepage.workspace = true
repository.workspace = true
description = "FRAME offences pallet"
readme = "README.md"
[lints]
workspace = true
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]
[dependencies]
codec = { features = ["derive"], workspace = true }
pezframe-support = { workspace = true }
pezframe-system = { workspace = true }
log = { workspace = true }
scale-info = { features = ["derive"], workspace = true }
serde = { optional = true, workspace = true, default-features = true }
pezsp-runtime = { workspace = true }
pezsp-staking = { workspace = true }
[dev-dependencies]
pezsp-core = { workspace = true, default-features = true }
pezsp-io = { workspace = true, default-features = true }
[features]
default = ["std"]
std = [
"codec/std",
"pezframe-support/std",
"pezframe-system/std",
"log/std",
"scale-info/std",
"serde",
"pezsp-core/std",
"pezsp-io/std",
"pezsp-runtime/std",
"pezsp-staking/std",
]
runtime-benchmarks = [
"pezframe-support/runtime-benchmarks",
"pezframe-system/runtime-benchmarks",
"pezsp-io/runtime-benchmarks",
"pezsp-runtime/runtime-benchmarks",
"pezsp-staking/runtime-benchmarks",
]
try-runtime = [
"pezframe-support/try-runtime",
"pezframe-system/try-runtime",
"pezsp-runtime/try-runtime",
]
+5
View File
@@ -0,0 +1,5 @@
# Offences Module
Tracks reported offences
License: Apache-2.0
@@ -0,0 +1,83 @@
[package]
name = "pezpallet-offences-benchmarking"
version = "28.0.0"
authors.workspace = true
edition.workspace = true
license = "Apache-2.0"
homepage.workspace = true
repository.workspace = true
description = "FRAME offences pallet benchmarking"
readme = "README.md"
[lints]
workspace = true
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]
[dependencies]
codec = { workspace = true }
pezframe-benchmarking = { workspace = true }
pezframe-election-provider-support = { workspace = true }
pezframe-support = { workspace = true }
pezframe-system = { workspace = true }
log = { workspace = true }
pezpallet-babe = { workspace = true }
pezpallet-balances = { workspace = true }
pezpallet-grandpa = { workspace = true }
pezpallet-im-online = { workspace = true }
pezpallet-offences = { workspace = true }
pezpallet-session = { workspace = true }
pezpallet-staking = { workspace = true }
scale-info = { features = ["derive"], workspace = true }
pezsp-runtime = { workspace = true }
pezsp-staking = { workspace = true }
[dev-dependencies]
pezpallet-staking-reward-curve = { workspace = true, default-features = true }
pezpallet-timestamp = { workspace = true, default-features = true }
pezsp-core = { workspace = true, default-features = true }
pezsp-io = { workspace = true, default-features = true }
pezsp-tracing = { workspace = true, default-features = true }
[features]
default = ["std"]
std = [
"codec/std",
"pezframe-benchmarking/std",
"pezframe-election-provider-support/std",
"pezframe-support/std",
"pezframe-system/std",
"log/std",
"pezpallet-babe/std",
"pezpallet-balances/std",
"pezpallet-grandpa/std",
"pezpallet-im-online/std",
"pezpallet-offences/std",
"pezpallet-session/std",
"pezpallet-staking/std",
"pezpallet-timestamp/std",
"scale-info/std",
"pezsp-core/std",
"pezsp-io/std",
"pezsp-runtime/std",
"pezsp-staking/std",
]
runtime-benchmarks = [
"pezframe-benchmarking/runtime-benchmarks",
"pezframe-election-provider-support/runtime-benchmarks",
"pezframe-support/runtime-benchmarks",
"pezframe-system/runtime-benchmarks",
"pezpallet-babe/runtime-benchmarks",
"pezpallet-balances/runtime-benchmarks",
"pezpallet-grandpa/runtime-benchmarks",
"pezpallet-im-online/runtime-benchmarks",
"pezpallet-offences/runtime-benchmarks",
"pezpallet-session/runtime-benchmarks",
"pezpallet-staking-reward-curve/runtime-benchmarks",
"pezpallet-staking/runtime-benchmarks",
"pezpallet-timestamp/runtime-benchmarks",
"pezsp-io/runtime-benchmarks",
"pezsp-runtime/runtime-benchmarks",
"pezsp-staking/runtime-benchmarks",
]
@@ -0,0 +1,3 @@
Offences pallet benchmarking.
License: Apache-2.0
@@ -0,0 +1,289 @@
// This file is part of Bizinikiwi.
// 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 alloc::{vec, vec::Vec};
use codec::Decode;
use pezframe_benchmarking::v2::*;
use pezframe_support::traits::Get;
use pezframe_system::{Config as SystemConfig, Pallet as System, RawOrigin};
use pezpallet_babe::EquivocationOffence as BabeEquivocationOffence;
use pezpallet_balances::Config as BalancesConfig;
use pezpallet_grandpa::{
EquivocationOffence as GrandpaEquivocationOffence, TimeSlot as GrandpaTimeSlot,
};
use pezpallet_offences::{Config as OffencesConfig, Pallet as Offences};
use pezpallet_session::{
historical::{Config as HistoricalConfig, IdentificationTuple},
Config as SessionConfig, Pallet as Session,
};
use pezpallet_staking::{
Config as StakingConfig, Exposure, IndividualExposure, MaxNominationsOf, Pallet as Staking,
RewardDestination, ValidatorPrefs,
};
use pezsp_runtime::{
traits::{Convert, Saturating, StaticLookup},
Perbill,
};
use pezsp_staking::offence::ReportOffence;
const SEED: u32 = 0;
const MAX_NOMINATORS: u32 = 100;
pub struct Pallet<T: Config>(Offences<T>);
pub trait Config:
SessionConfig<ValidatorId = <Self as pezframe_system::Config>::AccountId>
+ 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>::CurrencyBalance;
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> {
pezpallet_staking::asset::existential_deposit::<T>().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());
pezpallet_staking::asset::set_stakeable_balance::<T>(&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)?;
// set some fake keys for the validators.
let keys =
<T as SessionConfig>::Keys::decode(&mut pezsp_runtime::traits::TrailingZeroInput::zeroes())
.unwrap();
let proof: Vec<u8> = vec![0, 1, 2, 3];
Session::<T>::ensure_can_pay_key_deposit(&stash)?;
Session::<T>::set_keys(RawOrigin::Signed(stash.clone()).into(), keys, proof)?;
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);
pezpallet_staking::asset::set_stakeable_balance::<T>(&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>>, &'static str> {
let mut offenders = vec![];
for i in 0..num_offenders {
let offender = create_offender::<T>(i + 1, num_nominators)?;
// add them to the session validators -- this is needed since `FullIdentificationOf` usually
// checks this.
pezpallet_session::Validators::<T>::mutate(|v| v.push(offender.controller.clone()));
offenders.push(offender);
}
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))
.unwrap()
})
.collect::<Vec<IdentificationTuple<T>>>();
if pezpallet_staking::ActiveEra::<T>::get().is_none() {
pezpallet_staking::ActiveEra::<T>::put(pezpallet_staking::ActiveEraInfo {
index: 0,
start: Some(0),
});
}
Ok(id_tuples)
}
#[cfg(test)]
fn assert_all_slashes_applied<T>(offender_count: usize)
where
T: Config,
<T as pezframe_system::Config>::RuntimeEvent: TryInto<pezpallet_staking::Event<T>>,
<T as pezframe_system::Config>::RuntimeEvent: TryInto<pezpallet_balances::Event<T>>,
<T as pezframe_system::Config>::RuntimeEvent: TryInto<pezpallet_offences::Event>,
<T as pezframe_system::Config>::RuntimeEvent: TryInto<pezframe_system::Event<T>>,
{
// make sure that all slashes have been applied and TotalIssuance adjusted(BurnedDebt).
// deposit to reporter + reporter account endowed.
assert_eq!(System::<T>::read_events_for_pallet::<pezpallet_balances::Event<T>>().len(), 3);
// (n nominators + one validator) * slashed + Slash Reported + Slash Computed
assert_eq!(
System::<T>::read_events_for_pallet::<pezpallet_staking::Event<T>>().len(),
1 * (offender_count + 1) as usize + 1
);
// offence
assert_eq!(System::<T>::read_events_for_pallet::<pezpallet_offences::Event>().len(), 1);
// reporter new account
assert_eq!(System::<T>::read_events_for_pallet::<pezframe_system::Event<T>>().len(), 1);
}
#[benchmarks(
where
<T as pezframe_system::Config>::RuntimeEvent: TryInto<pezpallet_staking::Event<T>>,
<T as pezframe_system::Config>::RuntimeEvent: TryInto<pezpallet_balances::Event<T>>,
<T as pezframe_system::Config>::RuntimeEvent: TryInto<pezpallet_offences::Event>,
<T as pezframe_system::Config>::RuntimeEvent: TryInto<pezframe_system::Event<T>>,
)]
mod benchmarks {
use super::*;
#[benchmark]
pub fn report_offence_grandpa(
n: Linear<0, { MAX_NOMINATORS.min(MaxNominationsOf::<T>::get()) }>,
) -> Result<(), BenchmarkError> {
// 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 = 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);
#[block]
{
let _ = Offences::<T>::report_offence(reporters, offence);
}
#[cfg(test)]
{
assert_all_slashes_applied::<T>(n as usize);
}
Ok(())
}
#[benchmark]
fn report_offence_babe(
n: Linear<0, { MAX_NOMINATORS.min(MaxNominationsOf::<T>::get()) }>,
) -> Result<(), BenchmarkError> {
// 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 = 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);
#[block]
{
let _ = Offences::<T>::report_offence(reporters, offence);
}
#[cfg(test)]
{
assert_all_slashes_applied::<T>(n as usize);
}
Ok(())
}
impl_benchmark_test_suite!(Pallet, crate::mock::new_test_ext(), crate::mock::Test);
}
@@ -0,0 +1,31 @@
// This file is part of Bizinikiwi.
// 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.
#![cfg_attr(not(feature = "std"), no_std)]
extern crate alloc;
#[cfg(feature = "runtime-benchmarks")]
pub mod inner;
#[cfg(feature = "runtime-benchmarks")]
pub use inner::*;
#[cfg(all(feature = "runtime-benchmarks", test))]
pub(crate) mod mock;
@@ -0,0 +1,205 @@
// This file is part of Bizinikiwi.
// 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.
//! Mock file for offences benchmarking.
use pezframe_election_provider_support::{
bounds::{ElectionBounds, ElectionBoundsBuilder},
onchain, SequentialPhragmen,
};
use pezframe_support::{
derive_impl, parameter_types,
traits::{ConstU32, ConstU64},
};
use pezframe_system as system;
use pezpallet_session::historical as pezpallet_session_historical;
use pezsp_runtime::{
testing::{Header, UintAuthorityId},
BuildStorage, KeyTypeId, Perbill,
};
type AccountId = u64;
#[derive_impl(pezframe_system::config_preludes::TestDefaultConfig)]
impl pezframe_system::Config for Test {
type Block = Block;
type AccountData = pezpallet_balances::AccountData<u64>;
}
#[derive_impl(pezpallet_balances::config_preludes::TestDefaultConfig)]
impl pezpallet_balances::Config for Test {
type ExistentialDeposit = ConstU64<10>;
type AccountStore = System;
}
impl pezpallet_timestamp::Config for Test {
type Moment = u64;
type OnTimestampSet = ();
type MinimumPeriod = ConstU64<5>;
type WeightInfo = ();
}
impl pezpallet_session::historical::Config for Test {
type RuntimeEvent = RuntimeEvent;
type FullIdentification = ();
type FullIdentificationOf = pezpallet_staking::UnitIdentificationOf<Self>;
}
pezsp_runtime::impl_opaque_keys! {
pub struct SessionKeys {
pub foo: pezsp_runtime::testing::UintAuthorityId,
}
}
pub struct TestSessionHandler;
impl pezpallet_session::SessionHandler<AccountId> for TestSessionHandler {
// corresponds to the opaque key id above
const KEY_TYPE_IDS: &'static [KeyTypeId] = &[KeyTypeId([100u8, 117u8, 109u8, 121u8])];
fn on_genesis_session<Ks: pezsp_runtime::traits::OpaqueKeys>(_validators: &[(AccountId, Ks)]) {}
fn on_new_session<Ks: pezsp_runtime::traits::OpaqueKeys>(
_: bool,
_: &[(AccountId, Ks)],
_: &[(AccountId, Ks)],
) {
}
fn on_disabled(_: u32) {}
}
parameter_types! {
pub const Period: u64 = 1;
pub const Offset: u64 = 0;
}
impl pezpallet_session::Config for Test {
type SessionManager = pezpallet_session::historical::NoteHistoricalRoot<Test, Staking>;
type Keys = SessionKeys;
type ShouldEndSession = pezpallet_session::PeriodicSessions<Period, Offset>;
type NextSessionRotation = pezpallet_session::PeriodicSessions<Period, Offset>;
type SessionHandler = TestSessionHandler;
type RuntimeEvent = RuntimeEvent;
type ValidatorId = AccountId;
type ValidatorIdOf = pezsp_runtime::traits::ConvertInto;
type DisablingStrategy = ();
type WeightInfo = ();
type Currency = Balances;
type KeyDeposit = ();
}
pezpallet_staking_reward_curve::build! {
const I_NPOS: pezsp_runtime::curve::PiecewiseLinear<'static> = curve!(
min_inflation: 0_025_000,
max_inflation: 0_100_000,
ideal_stake: 0_500_000,
falloff: 0_050_000,
max_piece_count: 40,
test_precision: 0_005_000,
);
}
parameter_types! {
pub const RewardCurve: &'static pezsp_runtime::curve::PiecewiseLinear<'static> = &I_NPOS;
pub static ElectionsBounds: ElectionBounds = ElectionBoundsBuilder::default().build();
pub const Sort: bool = true;
}
pub struct OnChainSeqPhragmen;
impl onchain::Config for OnChainSeqPhragmen {
type System = Test;
type Solver = SequentialPhragmen<AccountId, Perbill>;
type DataProvider = Staking;
type WeightInfo = ();
type MaxWinnersPerPage = ConstU32<100>;
type MaxBackersPerWinner = ConstU32<100>;
type Sort = Sort;
type Bounds = ElectionsBounds;
}
#[derive_impl(pezpallet_staking::config_preludes::TestDefaultConfig)]
impl pezpallet_staking::Config for Test {
type OldCurrency = Balances;
type Currency = Balances;
type CurrencyBalance = <Self as pezpallet_balances::Config>::Balance;
type UnixTime = pezpallet_timestamp::Pallet<Self>;
type AdminOrigin = pezframe_system::EnsureRoot<Self::AccountId>;
type SessionInterface = Self;
type EraPayout = pezpallet_staking::ConvertCurve<RewardCurve>;
type NextNewSession = Session;
type ElectionProvider = onchain::OnChainExecution<OnChainSeqPhragmen>;
type GenesisElectionProvider = Self::ElectionProvider;
type VoterList = pezpallet_staking::UseNominatorsAndValidatorsMap<Self>;
type TargetList = pezpallet_staking::UseValidatorsMap<Self>;
}
impl pezpallet_im_online::Config for Test {
type AuthorityId = UintAuthorityId;
type RuntimeEvent = RuntimeEvent;
type ValidatorSet = Historical;
type NextSessionRotation = pezpallet_session::PeriodicSessions<Period, Offset>;
type ReportUnresponsiveness = Offences;
type UnsignedPriority = ();
type WeightInfo = ();
type MaxKeys = ConstU32<10_000>;
type MaxPeerInHeartbeats = ConstU32<10_000>;
}
impl pezpallet_offences::Config for Test {
type RuntimeEvent = RuntimeEvent;
type IdentificationTuple = pezpallet_session::historical::IdentificationTuple<Self>;
type OnOffenceHandler = Staking;
}
impl<T> pezframe_system::offchain::CreateTransactionBase<T> for Test
where
RuntimeCall: From<T>,
{
type Extrinsic = UncheckedExtrinsic;
type RuntimeCall = RuntimeCall;
}
impl<T> pezframe_system::offchain::CreateBare<T> for Test
where
RuntimeCall: From<T>,
{
fn create_bare(call: Self::RuntimeCall) -> Self::Extrinsic {
UncheckedExtrinsic::new_bare(call)
}
}
impl crate::Config for Test {}
pub type Block = pezsp_runtime::generic::Block<Header, UncheckedExtrinsic>;
pub type UncheckedExtrinsic = pezsp_runtime::generic::UncheckedExtrinsic<u32, RuntimeCall, u64, ()>;
pezframe_support::construct_runtime!(
pub enum Test
{
System: system::{Pallet, Call, Event<T>},
Balances: pezpallet_balances,
Staking: pezpallet_staking,
Session: pezpallet_session,
ImOnline: pezpallet_im_online::{Pallet, Call, Storage, Event<T>, ValidateUnsigned, Config<T>},
Offences: pezpallet_offences::{Pallet, Storage, Event},
Historical: pezpallet_session_historical::{Pallet, Event<T>},
}
);
pub fn new_test_ext() -> pezsp_io::TestExternalities {
pezsp_tracing::try_init_simple();
let t = pezframe_system::GenesisConfig::<Test>::default().build_storage().unwrap();
pezsp_io::TestExternalities::new(t)
}
+254
View File
@@ -0,0 +1,254 @@
// This file is part of Bizinikiwi.
// 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
//!
//! Tracks reported offences
// Ensure we're `no_std` when compiling for Wasm.
#![cfg_attr(not(feature = "std"), no_std)]
pub mod migration;
mod mock;
mod tests;
extern crate alloc;
use alloc::vec::Vec;
use codec::Encode;
use core::marker::PhantomData;
use pezframe_support::weights::Weight;
use pezsp_runtime::{traits::Hash, Perbill};
use pezsp_staking::{
offence::{Kind, Offence, OffenceDetails, OffenceError, OnOffenceHandler, ReportOffence},
SessionIndex,
};
pub use pallet::*;
/// A binary blob which represents a SCALE codec-encoded `O::TimeSlot`.
type OpaqueTimeSlot = Vec<u8>;
/// A type alias for a report identifier.
type ReportIdOf<T> = <T as pezframe_system::Config>::Hash;
const LOG_TARGET: &str = "runtime::offences";
#[pezframe_support::pallet]
pub mod pallet {
use super::*;
use pezframe_support::pezpallet_prelude::*;
const STORAGE_VERSION: StorageVersion = StorageVersion::new(1);
#[pallet::pallet]
#[pallet::storage_version(STORAGE_VERSION)]
#[pallet::without_storage_info]
pub struct Pallet<T>(_);
/// The pallet's config trait.
#[pallet::config]
pub trait Config: pezframe_system::Config {
/// The overarching event type.
#[allow(deprecated)]
type RuntimeEvent: From<Event> + IsType<<Self as pezframe_system::Config>::RuntimeEvent>;
/// Full identification of the validator.
type IdentificationTuple: Parameter;
/// A handler called for every offence report.
type OnOffenceHandler: OnOffenceHandler<Self::AccountId, Self::IdentificationTuple, Weight>;
}
/// The primary structure that holds all offence records keyed by report identifiers.
#[pallet::storage]
pub type Reports<T: Config> = StorageMap<
_,
Twox64Concat,
ReportIdOf<T>,
OffenceDetails<T::AccountId, T::IdentificationTuple>,
>;
/// A vector of reports of the same kind that happened at the same time slot.
#[pallet::storage]
pub type ConcurrentReportsIndex<T: Config> = StorageDoubleMap<
_,
Twox64Concat,
Kind,
Twox64Concat,
OpaqueTimeSlot,
Vec<ReportIdOf<T>>,
ValueQuery,
>;
/// Events type.
#[pallet::event]
#[pallet::generate_deposit(pub(super) fn deposit_event)]
pub enum Event {
/// There is an offence reported of the given `kind` happened at the `session_index` and
/// (kind-specific) time slot. This event is not deposited for duplicate slashes.
/// \[kind, timeslot\].
Offence { kind: Kind, timeslot: OpaqueTimeSlot },
}
}
impl<T, O> ReportOffence<T::AccountId, T::IdentificationTuple, O> for Pallet<T>
where
T: Config,
O: Offence<T::IdentificationTuple>,
{
fn report_offence(reporters: Vec<T::AccountId>, offence: O) -> Result<(), OffenceError> {
let offenders = offence.offenders();
let time_slot = offence.time_slot();
// Go through all offenders in the offence report and find all offenders that were spotted
// in unique reports.
let TriageOutcome { concurrent_offenders } =
match Self::triage_offence_report::<O>(reporters, &time_slot, offenders) {
Some(triage) => triage,
// The report contained only duplicates, so there is no need to slash again.
None => return Err(OffenceError::DuplicateReport),
};
let offenders_count = concurrent_offenders.len() as u32;
// The amount new offenders are slashed
let new_fraction = offence.slash_fraction(offenders_count);
let slash_perbill: Vec<_> = (0..concurrent_offenders.len()).map(|_| new_fraction).collect();
T::OnOffenceHandler::on_offence(
&concurrent_offenders,
&slash_perbill,
offence.session_index(),
);
// Deposit the event.
Self::deposit_event(Event::Offence { kind: O::ID, timeslot: time_slot.encode() });
Ok(())
}
fn is_known_offence(offenders: &[T::IdentificationTuple], time_slot: &O::TimeSlot) -> bool {
let any_unknown = offenders.iter().any(|offender| {
let report_id = Self::report_id::<O>(time_slot, offender);
!<Reports<T>>::contains_key(&report_id)
});
!any_unknown
}
}
impl<T: Config> Pallet<T> {
/// Get the offence details from reports of given ID.
pub fn reports(
report_id: ReportIdOf<T>,
) -> Option<OffenceDetails<T::AccountId, T::IdentificationTuple>> {
Reports::<T>::get(report_id)
}
/// Compute the ID for the given report properties.
///
/// The report id depends on the offence kind, time slot and the id of offender.
fn report_id<O: Offence<T::IdentificationTuple>>(
time_slot: &O::TimeSlot,
offender: &T::IdentificationTuple,
) -> ReportIdOf<T> {
(O::ID, time_slot.encode(), offender).using_encoded(T::Hashing::hash)
}
/// Triages the offence report and returns the set of offenders that was involved in unique
/// reports along with the list of the concurrent offences.
fn triage_offence_report<O: Offence<T::IdentificationTuple>>(
reporters: Vec<T::AccountId>,
time_slot: &O::TimeSlot,
offenders: Vec<T::IdentificationTuple>,
) -> Option<TriageOutcome<T>> {
let mut storage = ReportIndexStorage::<T, O>::load(time_slot);
let mut any_new = false;
for offender in offenders {
let report_id = Self::report_id::<O>(time_slot, &offender);
if !<Reports<T>>::contains_key(&report_id) {
any_new = true;
<Reports<T>>::insert(
&report_id,
OffenceDetails { offender, reporters: reporters.clone() },
);
storage.insert(report_id);
}
}
if any_new {
// Load report details for the all reports happened at the same time.
let concurrent_offenders = storage
.concurrent_reports
.iter()
.filter_map(<Reports<T>>::get)
.collect::<Vec<_>>();
storage.save();
Some(TriageOutcome { concurrent_offenders })
} else {
None
}
}
}
struct TriageOutcome<T: Config> {
/// Other reports for the same report kinds.
concurrent_offenders: Vec<OffenceDetails<T::AccountId, T::IdentificationTuple>>,
}
/// An auxiliary struct for working with storage of indexes localized for a specific offence
/// kind (specified by the `O` type parameter).
///
/// This struct is responsible for aggregating storage writes and the underlying storage should not
/// accessed directly meanwhile.
#[must_use = "The changes are not saved without called `save`"]
struct ReportIndexStorage<T: Config, O: Offence<T::IdentificationTuple>> {
opaque_time_slot: OpaqueTimeSlot,
concurrent_reports: Vec<ReportIdOf<T>>,
_phantom: PhantomData<O>,
}
impl<T: Config, O: Offence<T::IdentificationTuple>> ReportIndexStorage<T, O> {
/// Preload indexes from the storage for the specific `time_slot` and the kind of the offence.
fn load(time_slot: &O::TimeSlot) -> Self {
let opaque_time_slot = time_slot.encode();
let concurrent_reports = <ConcurrentReportsIndex<T>>::get(&O::ID, &opaque_time_slot);
Self { opaque_time_slot, concurrent_reports, _phantom: Default::default() }
}
/// Insert a new report to the index.
fn insert(&mut self, report_id: ReportIdOf<T>) {
// Update the list of concurrent reports.
self.concurrent_reports.push(report_id);
}
/// Dump the indexes to the storage.
fn save(self) {
<ConcurrentReportsIndex<T>>::insert(
&O::ID,
&self.opaque_time_slot,
&self.concurrent_reports,
);
}
}
@@ -0,0 +1,182 @@
// This file is part of Bizinikiwi.
// 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.
use super::{Config, Kind, OffenceDetails, Pallet, Perbill, SessionIndex, LOG_TARGET};
use alloc::vec::Vec;
use pezframe_support::{
pezpallet_prelude::ValueQuery,
storage_alias,
traits::{Get, GetStorageVersion, OnRuntimeUpgrade},
weights::Weight,
Twox64Concat,
};
use pezsp_staking::offence::OnOffenceHandler;
#[cfg(feature = "try-runtime")]
use pezframe_support::ensure;
#[cfg(feature = "try-runtime")]
use pezsp_runtime::TryRuntimeError;
mod v0 {
use super::*;
#[storage_alias]
pub type ReportsByKindIndex<T: Config> = StorageMap<
Pallet<T>,
Twox64Concat,
Kind,
Vec<u8>, // (O::TimeSlot, ReportIdOf<T>)
ValueQuery,
>;
}
pub mod v1 {
use pezframe_support::traits::StorageVersion;
use super::*;
pub struct MigrateToV1<T>(core::marker::PhantomData<T>);
impl<T: Config> OnRuntimeUpgrade for MigrateToV1<T> {
#[cfg(feature = "try-runtime")]
fn pre_upgrade() -> Result<Vec<u8>, TryRuntimeError> {
log::info!(
target: LOG_TARGET,
"Number of reports to refund and delete: {}",
v0::ReportsByKindIndex::<T>::iter_keys().count()
);
Ok(Vec::new())
}
fn on_runtime_upgrade() -> Weight {
if Pallet::<T>::on_chain_storage_version() > 0 {
log::info!(target: LOG_TARGET, "pezpallet_offences::MigrateToV1 should be removed");
return T::DbWeight::get().reads(1);
}
let keys_removed = v0::ReportsByKindIndex::<T>::clear(u32::MAX, None).unique as u64;
StorageVersion::new(1).put::<Pallet<T>>();
// + 1 for reading/writing the new storage version
T::DbWeight::get().reads_writes(keys_removed + 1, keys_removed + 1)
}
#[cfg(feature = "try-runtime")]
fn post_upgrade(_state: Vec<u8>) -> Result<(), TryRuntimeError> {
let onchain = Pallet::<T>::on_chain_storage_version();
ensure!(onchain == 1, "pezpallet_offences::MigrateToV1 needs to be run");
ensure!(
v0::ReportsByKindIndex::<T>::iter_keys().count() == 0,
"there are some dangling reports that need to be destroyed and refunded"
);
Ok(())
}
}
}
/// Type of data stored as a deferred offence
type DeferredOffenceOf<T> = (
Vec<OffenceDetails<<T as pezframe_system::Config>::AccountId, <T as Config>::IdentificationTuple>>,
Vec<Perbill>,
SessionIndex,
);
// Deferred reports that have been rejected by the offence handler and need to be submitted
// at a later time.
#[storage_alias]
type DeferredOffences<T: Config> =
StorageValue<crate::Pallet<T>, Vec<DeferredOffenceOf<T>>, ValueQuery>;
pub fn remove_deferred_storage<T: Config>() -> Weight {
let mut weight = T::DbWeight::get().reads_writes(1, 1);
let deferred = <DeferredOffences<T>>::take();
log::info!(target: LOG_TARGET, "have {} deferred offences, applying.", deferred.len());
for (offences, perbill, session) in deferred.iter() {
let consumed = T::OnOffenceHandler::on_offence(offences, perbill, *session);
weight = weight.saturating_add(consumed);
}
weight
}
#[cfg(test)]
mod test {
use super::*;
use crate::mock::{new_test_ext, with_on_offence_fractions, Runtime as T, KIND};
use codec::Encode;
use pezsp_runtime::Perbill;
use pezsp_staking::offence::OffenceDetails;
#[test]
fn migration_to_v1_works() {
let mut ext = new_test_ext();
ext.execute_with(|| {
<v0::ReportsByKindIndex<T>>::insert(KIND, 2u32.encode());
assert!(<v0::ReportsByKindIndex<T>>::iter_values().count() > 0);
});
ext.commit_all().unwrap();
ext.execute_with(|| {
assert_eq!(
v1::MigrateToV1::<T>::on_runtime_upgrade(),
<T as pezframe_system::Config>::DbWeight::get().reads_writes(2, 2),
);
assert!(<v0::ReportsByKindIndex<T>>::iter_values().count() == 0);
})
}
#[test]
fn should_resubmit_deferred_offences() {
new_test_ext().execute_with(|| {
// given
assert_eq!(<DeferredOffences<T>>::get().len(), 0);
with_on_offence_fractions(|f| {
assert_eq!(f.clone(), vec![]);
});
let offence_details = OffenceDetails::<
<T as pezframe_system::Config>::AccountId,
<T as Config>::IdentificationTuple,
> {
offender: 5,
reporters: vec![],
};
// push deferred offence
<DeferredOffences<T>>::append((
vec![offence_details],
vec![Perbill::from_percent(5 + 1 * 100 / 5)],
1,
));
// when
assert_eq!(
remove_deferred_storage::<T>(),
<T as pezframe_system::Config>::DbWeight::get().reads_writes(1, 1),
);
// then
assert!(!<DeferredOffences<T>>::exists());
with_on_offence_fractions(|f| {
assert_eq!(f.clone(), vec![Perbill::from_percent(5 + 1 * 100 / 5)]);
});
})
}
}
+138
View File
@@ -0,0 +1,138 @@
// This file is part of Bizinikiwi.
// 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.
//! Test utilities
#![cfg(test)]
use crate as offences;
use crate::Config;
use codec::Encode;
use pezframe_support::{
derive_impl, parameter_types,
traits::ConstU32,
weights::{constants::RocksDbWeight, Weight},
};
use pezsp_runtime::{traits::IdentityLookup, BuildStorage, Perbill};
use pezsp_staking::{
offence::{self, Kind, OffenceDetails},
SessionIndex,
};
pub struct OnOffenceHandler;
parameter_types! {
pub static OnOffencePerbill: Vec<Perbill> = Default::default();
pub static OffenceWeight: Weight = Default::default();
}
impl<Reporter, Offender> offence::OnOffenceHandler<Reporter, Offender, Weight>
for OnOffenceHandler
{
fn on_offence(
_offenders: &[OffenceDetails<Reporter, Offender>],
slash_fraction: &[Perbill],
_offence_session: SessionIndex,
) -> Weight {
OnOffencePerbill::mutate(|f| {
*f = slash_fraction.to_vec();
});
OffenceWeight::get()
}
}
pub fn with_on_offence_fractions<R, F: FnOnce(&mut Vec<Perbill>) -> R>(f: F) -> R {
OnOffencePerbill::mutate(|fractions| f(fractions))
}
type Block = pezframe_system::mocking::MockBlock<Runtime>;
pezframe_support::construct_runtime!(
pub enum Runtime {
System: pezframe_system,
Offences: offences,
}
);
#[derive_impl(pezframe_system::config_preludes::TestDefaultConfig)]
impl pezframe_system::Config for Runtime {
type Nonce = u64;
type Lookup = IdentityLookup<Self::AccountId>;
type Block = Block;
type RuntimeEvent = RuntimeEvent;
type DbWeight = RocksDbWeight;
type MaxConsumers = ConstU32<16>;
}
impl Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type IdentificationTuple = u64;
type OnOffenceHandler = OnOffenceHandler;
}
pub fn new_test_ext() -> pezsp_io::TestExternalities {
let t = pezframe_system::GenesisConfig::<Runtime>::default().build_storage().unwrap();
let mut ext = pezsp_io::TestExternalities::new(t);
ext.execute_with(|| System::set_block_number(1));
ext
}
pub const KIND: [u8; 16] = *b"test_report_1234";
/// Returns all offence details for the specific `kind` happened at the specific time slot.
pub fn offence_reports(kind: Kind, time_slot: u128) -> Vec<OffenceDetails<u64, u64>> {
<crate::ConcurrentReportsIndex<Runtime>>::get(&kind, &time_slot.encode())
.into_iter()
.map(|report_id| {
<crate::Reports<Runtime>>::get(&report_id)
.expect("dangling report id is found in ConcurrentReportsIndex")
})
.collect()
}
#[derive(Clone)]
pub struct Offence {
pub validator_set_count: u32,
pub offenders: Vec<u64>,
pub time_slot: u128,
}
impl offence::Offence<u64> for Offence {
const ID: offence::Kind = KIND;
type TimeSlot = u128;
fn offenders(&self) -> Vec<u64> {
self.offenders.clone()
}
fn validator_set_count(&self) -> u32 {
self.validator_set_count
}
fn time_slot(&self) -> u128 {
self.time_slot
}
fn session_index(&self) -> SessionIndex {
1
}
fn slash_fraction(&self, offenders_count: u32) -> Perbill {
Perbill::from_percent(5 + offenders_count * 100 / self.validator_set_count)
}
}
+269
View File
@@ -0,0 +1,269 @@
// This file is part of Bizinikiwi.
// 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.
//! Tests for the offences module.
#![cfg(test)]
use super::*;
use crate::mock::{
new_test_ext, offence_reports, with_on_offence_fractions, Offence, Offences, Runtime,
RuntimeEvent, System, KIND,
};
use pezframe_system::{EventRecord, Phase};
use pezsp_core::H256;
use pezsp_runtime::Perbill;
#[test]
fn should_get_reports_with_storagemap_getter_and_function_getter() {
new_test_ext().execute_with(|| {
// given
let report_id: ReportIdOf<Runtime> = H256::from_low_u64_be(1);
let offence_details = OffenceDetails { offender: 1, reporters: vec![2, 3] };
Reports::<Runtime>::insert(report_id, offence_details.clone());
// when
let stored_offence_details = Offences::reports(report_id);
// then
assert_eq!(stored_offence_details, Some(offence_details.clone()));
// when
let stored_offence_details = Reports::<Runtime>::get(report_id);
// then
assert_eq!(stored_offence_details, Some(offence_details.clone()));
});
}
#[test]
fn should_report_an_authority_and_trigger_on_offence() {
new_test_ext().execute_with(|| {
// given
let time_slot = 42;
assert_eq!(offence_reports(KIND, time_slot), vec![]);
let offence = Offence { validator_set_count: 5, time_slot, offenders: vec![5] };
// when
Offences::report_offence(vec![], offence).unwrap();
// then
with_on_offence_fractions(|f| {
assert_eq!(f.clone(), vec![Perbill::from_percent(25)]);
});
});
}
#[test]
fn should_not_report_the_same_authority_twice_in_the_same_slot() {
new_test_ext().execute_with(|| {
// given
let time_slot = 42;
assert_eq!(offence_reports(KIND, time_slot), vec![]);
let offence = Offence { validator_set_count: 5, time_slot, offenders: vec![5] };
Offences::report_offence(vec![], offence.clone()).unwrap();
with_on_offence_fractions(|f| {
assert_eq!(f.clone(), vec![Perbill::from_percent(25)]);
f.clear();
});
// when
// report for the second time
assert_eq!(Offences::report_offence(vec![], offence), Err(OffenceError::DuplicateReport));
// then
with_on_offence_fractions(|f| {
assert_eq!(f.clone(), vec![]);
});
});
}
#[test]
fn should_report_in_different_time_slot() {
new_test_ext().execute_with(|| {
// given
let time_slot = 42;
assert_eq!(offence_reports(KIND, time_slot), vec![]);
let mut offence = Offence { validator_set_count: 5, time_slot, offenders: vec![5] };
Offences::report_offence(vec![], offence.clone()).unwrap();
with_on_offence_fractions(|f| {
assert_eq!(f.clone(), vec![Perbill::from_percent(25)]);
f.clear();
});
// when
// report for the second time
offence.time_slot += 1;
Offences::report_offence(vec![], offence).unwrap();
// then
with_on_offence_fractions(|f| {
assert_eq!(f.clone(), vec![Perbill::from_percent(25)]);
});
});
}
#[test]
fn should_deposit_event() {
new_test_ext().execute_with(|| {
// given
let time_slot = 42;
assert_eq!(offence_reports(KIND, time_slot), vec![]);
let offence = Offence { validator_set_count: 5, time_slot, offenders: vec![5] };
// when
Offences::report_offence(vec![], offence).unwrap();
// then
assert_eq!(
System::events(),
vec![EventRecord {
phase: Phase::Initialization,
event: RuntimeEvent::Offences(crate::Event::Offence {
kind: KIND,
timeslot: time_slot.encode()
}),
topics: vec![],
}]
);
});
}
#[test]
fn doesnt_deposit_event_for_dups() {
new_test_ext().execute_with(|| {
// given
let time_slot = 42;
assert_eq!(offence_reports(KIND, time_slot), vec![]);
let offence = Offence { validator_set_count: 5, time_slot, offenders: vec![5] };
Offences::report_offence(vec![], offence.clone()).unwrap();
with_on_offence_fractions(|f| {
assert_eq!(f.clone(), vec![Perbill::from_percent(25)]);
f.clear();
});
// when
// report for the second time
assert_eq!(Offences::report_offence(vec![], offence), Err(OffenceError::DuplicateReport));
// then
// there is only one event.
assert_eq!(
System::events(),
vec![EventRecord {
phase: Phase::Initialization,
event: RuntimeEvent::Offences(crate::Event::Offence {
kind: KIND,
timeslot: time_slot.encode()
}),
topics: vec![],
}]
);
});
}
#[test]
fn reports_if_an_offence_is_dup() {
new_test_ext().execute_with(|| {
let time_slot = 42;
assert_eq!(offence_reports(KIND, time_slot), vec![]);
let offence =
|time_slot, offenders| Offence { validator_set_count: 5, time_slot, offenders };
let mut test_offence = offence(time_slot, vec![0]);
// the report for authority 0 at time slot 42 should not be a known
// offence
assert!(!<Offences as ReportOffence<_, _, Offence>>::is_known_offence(
&test_offence.offenders,
&test_offence.time_slot
));
// we report an offence for authority 0 at time slot 42
Offences::report_offence(vec![], test_offence.clone()).unwrap();
// the same report should be a known offence now
assert!(<Offences as ReportOffence<_, _, Offence>>::is_known_offence(
&test_offence.offenders,
&test_offence.time_slot
));
// and reporting it again should yield a duplicate report error
assert_eq!(
Offences::report_offence(vec![], test_offence.clone()),
Err(OffenceError::DuplicateReport)
);
// after adding a new offender to the offence report
test_offence.offenders.push(1);
// it should not be a known offence anymore
assert!(!<Offences as ReportOffence<_, _, Offence>>::is_known_offence(
&test_offence.offenders,
&test_offence.time_slot
));
// and reporting it again should work without any error
assert_eq!(Offences::report_offence(vec![], test_offence.clone()), Ok(()));
// creating a new offence for the same authorities on the next slot
// should be considered a new offence and therefore not known
let test_offence_next_slot = offence(time_slot + 1, vec![0, 1]);
assert!(!<Offences as ReportOffence<_, _, Offence>>::is_known_offence(
&test_offence_next_slot.offenders,
&test_offence_next_slot.time_slot
));
});
}
#[test]
fn should_properly_count_offences() {
// We report two different authorities for the same issue. Ultimately, the 1st authority
// should have `count` equal 2 and the count of the 2nd one should be equal to 1.
new_test_ext().execute_with(|| {
// given
let time_slot = 42;
assert_eq!(offence_reports(KIND, time_slot), vec![]);
let offence1 = Offence { validator_set_count: 5, time_slot, offenders: vec![5] };
let offence2 = Offence { validator_set_count: 5, time_slot, offenders: vec![4] };
Offences::report_offence(vec![], offence1).unwrap();
with_on_offence_fractions(|f| {
assert_eq!(f.clone(), vec![Perbill::from_percent(25)]);
f.clear();
});
// when
// report for the second time
Offences::report_offence(vec![], offence2).unwrap();
// then
// the 1st authority should have count 2 and the 2nd one should be reported only once.
assert_eq!(
offence_reports(KIND, time_slot),
vec![
OffenceDetails { offender: 5, reporters: vec![] },
OffenceDetails { offender: 4, reporters: vec![] },
]
);
});
}
+226
View File
@@ -0,0 +1,226 @@
// This file is part of Bizinikiwi.
// 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.
// This file is part of Bizinikiwi.
// 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.
//! Autogenerated weights for `pezpallet_offences`
//!
//! THIS FILE WAS AUTO-GENERATED USING THE BIZINIKIWI BENCHMARK CLI VERSION 32.0.0
//! DATE: 2025-02-21, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
//! WORST CASE MAP SIZE: `1000000`
//! HOSTNAME: `4563561839a5`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz`
//! WASM-EXECUTION: `Compiled`, CHAIN: `None`, DB CACHE: `1024`
// Executed Command:
// frame-omni-bencher
// v1
// benchmark
// pallet
// --extrinsic=*
// --runtime=target/production/wbuild/kitchensink-runtime/kitchensink_runtime.wasm
// --pallet=pezpallet_offences
// --header=/__w/pezkuwi-sdk/pezkuwi-sdk/bizinikiwi/HEADER-APACHE2
// --output=/__w/pezkuwi-sdk/pezkuwi-sdk/bizinikiwi/pezframe/offences/src/weights.rs
// --wasm-execution=compiled
// --steps=50
// --repeat=20
// --heap-pages=4096
// --template=bizinikiwi/.maintain/frame-weight-template.hbs
// --no-storage-info
// --no-min-squares
// --no-median-slopes
// --genesis-builder-policy=none
// --exclude-pallets=pezpallet_xcm,pezpallet_xcm_benchmarks::fungible,pezpallet_xcm_benchmarks::generic,pezpallet_nomination_pools,pezpallet_remark,pezpallet_transaction_storage,pezpallet_election_provider_multi_block,pezpallet_election_provider_multi_block::signed,pezpallet_election_provider_multi_block::unsigned,pezpallet_election_provider_multi_block::verifier
#![cfg_attr(rustfmt, rustfmt_skip)]
#![allow(unused_parens)]
#![allow(unused_imports)]
#![allow(missing_docs)]
#![allow(dead_code)]
use pezframe_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}};
use core::marker::PhantomData;
/// Weight functions needed for `pezpallet_offences`.
pub trait WeightInfo {
fn report_offence_grandpa(n: u32, ) -> Weight;
fn report_offence_babe(n: u32, ) -> Weight;
}
/// Weights for `pezpallet_offences` using the Bizinikiwi node and recommended hardware.
pub struct BizinikiwiWeight<T>(PhantomData<T>);
impl<T: pezframe_system::Config> WeightInfo for BizinikiwiWeight<T> {
/// Storage: `Offences::ConcurrentReportsIndex` (r:1 w:1)
/// Proof: `Offences::ConcurrentReportsIndex` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `Offences::Reports` (r:1 w:1)
/// Proof: `Offences::Reports` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `Staking::ActiveEra` (r:1 w:0)
/// Proof: `Staking::ActiveEra` (`max_values`: Some(1), `max_size`: Some(13), added: 508, mode: `MaxEncodedLen`)
/// Storage: `Staking::ErasStartSessionIndex` (r:1 w:0)
/// Proof: `Staking::ErasStartSessionIndex` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`)
/// Storage: `Staking::Invulnerables` (r:1 w:0)
/// Proof: `Staking::Invulnerables` (`max_values`: Some(1), `max_size`: Some(641), added: 1136, mode: `MaxEncodedLen`)
/// Storage: `Staking::ErasStakersOverview` (r:1 w:0)
/// Proof: `Staking::ErasStakersOverview` (`max_values`: None, `max_size`: Some(92), added: 2567, mode: `MaxEncodedLen`)
/// Storage: `Session::DisabledValidators` (r:1 w:1)
/// Proof: `Session::DisabledValidators` (`max_values`: Some(1), `max_size`: None, mode: `Measured`)
/// Storage: `Session::Validators` (r:1 w:0)
/// Proof: `Session::Validators` (`max_values`: Some(1), `max_size`: None, mode: `Measured`)
/// Storage: `Staking::ValidatorSlashInEra` (r:1 w:1)
/// Proof: `Staking::ValidatorSlashInEra` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`)
/// Storage: `Staking::OffenceQueue` (r:1 w:1)
/// Proof: `Staking::OffenceQueue` (`max_values`: None, `max_size`: Some(101), added: 2576, mode: `MaxEncodedLen`)
/// Storage: `Staking::OffenceQueueEras` (r:1 w:1)
/// Proof: `Staking::OffenceQueueEras` (`max_values`: Some(1), `max_size`: Some(2690), added: 3185, mode: `MaxEncodedLen`)
/// The range of component `n` is `[0, 16]`.
fn report_offence_grandpa(n: u32, ) -> Weight {
// Proof Size summary in bytes:
// Measured: `424`
// Estimated: `4175 + n * (1 ±0)`
// Minimum execution time: 39_455_000 picoseconds.
Weight::from_parts(41_709_372, 4175)
// Standard Error: 5_442
.saturating_add(Weight::from_parts(438_959, 0).saturating_mul(n.into()))
.saturating_add(T::DbWeight::get().reads(11_u64))
.saturating_add(T::DbWeight::get().writes(6_u64))
.saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into()))
}
/// Storage: `Offences::ConcurrentReportsIndex` (r:1 w:1)
/// Proof: `Offences::ConcurrentReportsIndex` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `Offences::Reports` (r:1 w:1)
/// Proof: `Offences::Reports` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `Staking::ActiveEra` (r:1 w:0)
/// Proof: `Staking::ActiveEra` (`max_values`: Some(1), `max_size`: Some(13), added: 508, mode: `MaxEncodedLen`)
/// Storage: `Staking::ErasStartSessionIndex` (r:1 w:0)
/// Proof: `Staking::ErasStartSessionIndex` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`)
/// Storage: `Staking::Invulnerables` (r:1 w:0)
/// Proof: `Staking::Invulnerables` (`max_values`: Some(1), `max_size`: Some(641), added: 1136, mode: `MaxEncodedLen`)
/// Storage: `Staking::ErasStakersOverview` (r:1 w:0)
/// Proof: `Staking::ErasStakersOverview` (`max_values`: None, `max_size`: Some(92), added: 2567, mode: `MaxEncodedLen`)
/// Storage: `Session::DisabledValidators` (r:1 w:1)
/// Proof: `Session::DisabledValidators` (`max_values`: Some(1), `max_size`: None, mode: `Measured`)
/// Storage: `Session::Validators` (r:1 w:0)
/// Proof: `Session::Validators` (`max_values`: Some(1), `max_size`: None, mode: `Measured`)
/// Storage: `Staking::ValidatorSlashInEra` (r:1 w:1)
/// Proof: `Staking::ValidatorSlashInEra` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`)
/// Storage: `Staking::OffenceQueue` (r:1 w:1)
/// Proof: `Staking::OffenceQueue` (`max_values`: None, `max_size`: Some(101), added: 2576, mode: `MaxEncodedLen`)
/// Storage: `Staking::OffenceQueueEras` (r:1 w:1)
/// Proof: `Staking::OffenceQueueEras` (`max_values`: Some(1), `max_size`: Some(2690), added: 3185, mode: `MaxEncodedLen`)
/// The range of component `n` is `[0, 16]`.
fn report_offence_babe(n: u32, ) -> Weight {
// Proof Size summary in bytes:
// Measured: `424`
// Estimated: `4175 + n * (1 ±0)`
// Minimum execution time: 39_396_000 picoseconds.
Weight::from_parts(41_490_043, 4175)
// Standard Error: 4_716
.saturating_add(Weight::from_parts(433_178, 0).saturating_mul(n.into()))
.saturating_add(T::DbWeight::get().reads(11_u64))
.saturating_add(T::DbWeight::get().writes(6_u64))
.saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into()))
}
}
// For backwards compatibility and tests.
impl WeightInfo for () {
/// Storage: `Offences::ConcurrentReportsIndex` (r:1 w:1)
/// Proof: `Offences::ConcurrentReportsIndex` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `Offences::Reports` (r:1 w:1)
/// Proof: `Offences::Reports` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `Staking::ActiveEra` (r:1 w:0)
/// Proof: `Staking::ActiveEra` (`max_values`: Some(1), `max_size`: Some(13), added: 508, mode: `MaxEncodedLen`)
/// Storage: `Staking::ErasStartSessionIndex` (r:1 w:0)
/// Proof: `Staking::ErasStartSessionIndex` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`)
/// Storage: `Staking::Invulnerables` (r:1 w:0)
/// Proof: `Staking::Invulnerables` (`max_values`: Some(1), `max_size`: Some(641), added: 1136, mode: `MaxEncodedLen`)
/// Storage: `Staking::ErasStakersOverview` (r:1 w:0)
/// Proof: `Staking::ErasStakersOverview` (`max_values`: None, `max_size`: Some(92), added: 2567, mode: `MaxEncodedLen`)
/// Storage: `Session::DisabledValidators` (r:1 w:1)
/// Proof: `Session::DisabledValidators` (`max_values`: Some(1), `max_size`: None, mode: `Measured`)
/// Storage: `Session::Validators` (r:1 w:0)
/// Proof: `Session::Validators` (`max_values`: Some(1), `max_size`: None, mode: `Measured`)
/// Storage: `Staking::ValidatorSlashInEra` (r:1 w:1)
/// Proof: `Staking::ValidatorSlashInEra` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`)
/// Storage: `Staking::OffenceQueue` (r:1 w:1)
/// Proof: `Staking::OffenceQueue` (`max_values`: None, `max_size`: Some(101), added: 2576, mode: `MaxEncodedLen`)
/// Storage: `Staking::OffenceQueueEras` (r:1 w:1)
/// Proof: `Staking::OffenceQueueEras` (`max_values`: Some(1), `max_size`: Some(2690), added: 3185, mode: `MaxEncodedLen`)
/// The range of component `n` is `[0, 16]`.
fn report_offence_grandpa(n: u32, ) -> Weight {
// Proof Size summary in bytes:
// Measured: `424`
// Estimated: `4175 + n * (1 ±0)`
// Minimum execution time: 39_455_000 picoseconds.
Weight::from_parts(41_709_372, 4175)
// Standard Error: 5_442
.saturating_add(Weight::from_parts(438_959, 0).saturating_mul(n.into()))
.saturating_add(RocksDbWeight::get().reads(11_u64))
.saturating_add(RocksDbWeight::get().writes(6_u64))
.saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into()))
}
/// Storage: `Offences::ConcurrentReportsIndex` (r:1 w:1)
/// Proof: `Offences::ConcurrentReportsIndex` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `Offences::Reports` (r:1 w:1)
/// Proof: `Offences::Reports` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// Storage: `Staking::ActiveEra` (r:1 w:0)
/// Proof: `Staking::ActiveEra` (`max_values`: Some(1), `max_size`: Some(13), added: 508, mode: `MaxEncodedLen`)
/// Storage: `Staking::ErasStartSessionIndex` (r:1 w:0)
/// Proof: `Staking::ErasStartSessionIndex` (`max_values`: None, `max_size`: Some(16), added: 2491, mode: `MaxEncodedLen`)
/// Storage: `Staking::Invulnerables` (r:1 w:0)
/// Proof: `Staking::Invulnerables` (`max_values`: Some(1), `max_size`: Some(641), added: 1136, mode: `MaxEncodedLen`)
/// Storage: `Staking::ErasStakersOverview` (r:1 w:0)
/// Proof: `Staking::ErasStakersOverview` (`max_values`: None, `max_size`: Some(92), added: 2567, mode: `MaxEncodedLen`)
/// Storage: `Session::DisabledValidators` (r:1 w:1)
/// Proof: `Session::DisabledValidators` (`max_values`: Some(1), `max_size`: None, mode: `Measured`)
/// Storage: `Session::Validators` (r:1 w:0)
/// Proof: `Session::Validators` (`max_values`: Some(1), `max_size`: None, mode: `Measured`)
/// Storage: `Staking::ValidatorSlashInEra` (r:1 w:1)
/// Proof: `Staking::ValidatorSlashInEra` (`max_values`: None, `max_size`: Some(72), added: 2547, mode: `MaxEncodedLen`)
/// Storage: `Staking::OffenceQueue` (r:1 w:1)
/// Proof: `Staking::OffenceQueue` (`max_values`: None, `max_size`: Some(101), added: 2576, mode: `MaxEncodedLen`)
/// Storage: `Staking::OffenceQueueEras` (r:1 w:1)
/// Proof: `Staking::OffenceQueueEras` (`max_values`: Some(1), `max_size`: Some(2690), added: 3185, mode: `MaxEncodedLen`)
/// The range of component `n` is `[0, 16]`.
fn report_offence_babe(n: u32, ) -> Weight {
// Proof Size summary in bytes:
// Measured: `424`
// Estimated: `4175 + n * (1 ±0)`
// Minimum execution time: 39_396_000 picoseconds.
Weight::from_parts(41_490_043, 4175)
// Standard Error: 4_716
.saturating_add(Weight::from_parts(433_178, 0).saturating_mul(n.into()))
.saturating_add(RocksDbWeight::get().reads(11_u64))
.saturating_add(RocksDbWeight::get().writes(6_u64))
.saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into()))
}
}