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
+103
View File
@@ -0,0 +1,103 @@
[package]
name = "pezpallet-grandpa"
version = "28.0.0"
authors.workspace = true
edition.workspace = true
license = "Apache-2.0"
homepage.workspace = true
repository.workspace = true
description = "FRAME pallet for GRANDPA finality gadget"
readme = "README.md"
[lints]
workspace = true
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]
[dependencies]
codec = { features = ["derive"], workspace = true }
pezframe-benchmarking = { optional = true, workspace = true }
pezframe-support = { workspace = true }
pezframe-system = { workspace = true }
log = { workspace = true }
pezpallet-authorship = { workspace = true }
pezpallet-session = { workspace = true }
scale-info = { features = ["derive", "serde"], workspace = true }
pezsp-application-crypto = { features = ["serde"], workspace = true }
pezsp-consensus-grandpa = { features = ["serde"], workspace = true }
pezsp-core = { features = ["serde"], workspace = true }
pezsp-io = { workspace = true }
pezsp-runtime = { features = ["serde"], workspace = true }
pezsp-session = { workspace = true }
pezsp-staking = { features = ["serde"], workspace = true }
[dev-dependencies]
finality-grandpa = { features = [
"derive-codec",
], workspace = true, default-features = true }
pezframe-benchmarking = { workspace = true, default-features = true }
pezframe-election-provider-support = { workspace = true, default-features = true }
pezpallet-balances = { workspace = true, default-features = true }
pezpallet-offences = { workspace = true, default-features = true }
pezpallet-staking = { workspace = true, default-features = true }
pezpallet-staking-reward-curve = { workspace = true, default-features = true }
pezpallet-timestamp = { workspace = true, default-features = true }
pezsp-keyring = { 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-authorship/std",
"pezpallet-balances/std",
"pezpallet-offences/std",
"pezpallet-session/std",
"pezpallet-staking/std",
"pezpallet-timestamp/std",
"scale-info/std",
"pezsp-application-crypto/std",
"pezsp-consensus-grandpa/std",
"pezsp-core/std",
"pezsp-io/std",
"pezsp-runtime/std",
"pezsp-session/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-authorship/runtime-benchmarks",
"pezpallet-balances/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-consensus-grandpa/runtime-benchmarks",
"pezsp-io/runtime-benchmarks",
"pezsp-keyring/runtime-benchmarks",
"pezsp-runtime/runtime-benchmarks",
"pezsp-session/runtime-benchmarks",
"pezsp-staking/runtime-benchmarks",
]
try-runtime = [
"pezframe-election-provider-support/try-runtime",
"pezframe-support/try-runtime",
"pezframe-system/try-runtime",
"pezpallet-authorship/try-runtime",
"pezpallet-balances/try-runtime",
"pezpallet-offences/try-runtime",
"pezpallet-session/try-runtime",
"pezpallet-staking/try-runtime",
"pezpallet-timestamp/try-runtime",
"pezsp-runtime/try-runtime",
]
+12
View File
@@ -0,0 +1,12 @@
GRANDPA Consensus module for runtime.
This manages the GRANDPA authority set ready for the native code.
These authorities are only for GRANDPA finality, not for consensus overall.
In the future, it will also handle misbehavior reports, and on-chain
finality notifications.
For full integration with GRANDPA, the `GrandpaApi` should be implemented.
The necessary items are re-exported via the `fg_primitives` crate.
License: Apache-2.0
@@ -0,0 +1,110 @@
// 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.
//! Benchmarks for the GRANDPA pallet.
use super::*;
use pezframe_benchmarking::v2::*;
use pezframe_system::RawOrigin;
use pezsp_core::H256;
#[benchmarks]
mod benchmarks {
use super::*;
#[benchmark]
fn check_equivocation_proof(x: Linear<0, 1>) {
// NOTE: generated with the test below `test_generate_equivocation_report_blob`.
// the output should be deterministic since the keys we use are static.
// with the current benchmark setup it is not possible to generate this
// programmatically from the benchmark setup.
const EQUIVOCATION_PROOF_BLOB: [u8; 257] = [
1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 136, 220, 52, 23, 213, 5, 142, 196,
180, 80, 62, 12, 18, 234, 26, 10, 137, 190, 32, 15, 233, 137, 34, 66, 61, 67, 52, 1,
79, 166, 176, 238, 207, 48, 195, 55, 171, 225, 252, 130, 161, 56, 151, 29, 193, 32, 25,
157, 249, 39, 80, 193, 214, 96, 167, 147, 25, 130, 45, 42, 64, 208, 182, 164, 10, 0, 0,
0, 0, 0, 0, 0, 234, 236, 231, 45, 70, 171, 135, 246, 136, 153, 38, 167, 91, 134, 150,
242, 215, 83, 56, 238, 16, 119, 55, 170, 32, 69, 255, 248, 164, 20, 57, 50, 122, 115,
135, 96, 80, 203, 131, 232, 73, 23, 149, 86, 174, 59, 193, 92, 121, 76, 154, 211, 44,
96, 10, 84, 159, 133, 211, 56, 103, 0, 59, 2, 96, 20, 69, 2, 32, 179, 16, 184, 108, 76,
215, 64, 195, 78, 143, 73, 177, 139, 20, 144, 98, 231, 41, 117, 255, 220, 115, 41, 59,
27, 75, 56, 10, 0, 0, 0, 0, 0, 0, 0, 128, 179, 250, 48, 211, 76, 10, 70, 74, 230, 219,
139, 96, 78, 88, 112, 33, 170, 44, 184, 59, 200, 155, 143, 128, 40, 222, 179, 210, 190,
84, 16, 182, 21, 34, 94, 28, 193, 163, 226, 51, 251, 134, 233, 187, 121, 63, 157, 240,
165, 203, 92, 16, 146, 120, 190, 229, 251, 129, 29, 45, 32, 29, 6,
];
let equivocation_proof1: pezsp_consensus_grandpa::EquivocationProof<H256, u64> =
Decode::decode(&mut &EQUIVOCATION_PROOF_BLOB[..]).unwrap();
let equivocation_proof2 = equivocation_proof1.clone();
#[block]
{
pezsp_consensus_grandpa::check_equivocation_proof(equivocation_proof1);
}
assert!(pezsp_consensus_grandpa::check_equivocation_proof(equivocation_proof2));
}
#[benchmark]
fn note_stalled() {
let delay = 1000u32.into();
let best_finalized_block_number = 1u32.into();
#[extrinsic_call]
_(RawOrigin::Root, delay, best_finalized_block_number);
assert!(Stalled::<T>::get().is_some());
}
impl_benchmark_test_suite!(
Pallet,
crate::mock::new_test_ext(vec![(1, 1), (2, 1), (3, 1)]),
crate::mock::Test,
);
}
#[cfg(test)]
mod tests {
use super::*;
use crate::mock::*;
#[test]
fn test_generate_equivocation_report_blob() {
let authorities = crate::tests::test_authorities();
let equivocation_authority_index = 0;
let equivocation_key = &authorities[equivocation_authority_index].0;
let equivocation_keyring = extract_keyring(equivocation_key);
new_test_ext_raw_authorities(authorities).execute_with(|| {
start_era(1);
// generate an equivocation proof, with two votes in the same round for
// different block hashes signed by the same key
let equivocation_proof = generate_equivocation_proof(
1,
(1, H256::random(), 10, &equivocation_keyring),
(1, H256::random(), 10, &equivocation_keyring),
);
println!("equivocation_proof: {:?}", equivocation_proof);
println!("equivocation_proof.encode(): {:?}", equivocation_proof.encode());
});
}
}
@@ -0,0 +1,57 @@
// 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.
//! Default weights for the GRANDPA Pallet
//! This file was not auto-generated.
use pezframe_support::weights::{
constants::{RocksDbWeight as DbWeight, WEIGHT_REF_TIME_PER_MICROS, WEIGHT_REF_TIME_PER_NANOS},
Weight,
};
impl crate::WeightInfo for () {
fn report_equivocation(validator_count: u32, max_nominators_per_validator: u32) -> Weight {
// we take the validator set count from the membership proof to
// calculate the weight but we set a floor of 100 validators.
let validator_count = validator_count.max(100) as u64;
// checking membership proof
Weight::from_parts(35u64 * WEIGHT_REF_TIME_PER_MICROS, 0)
.saturating_add(
Weight::from_parts(175u64 * WEIGHT_REF_TIME_PER_NANOS, 0)
.saturating_mul(validator_count),
)
.saturating_add(DbWeight::get().reads(5))
// check equivocation proof
.saturating_add(Weight::from_parts(95u64 * WEIGHT_REF_TIME_PER_MICROS, 0))
// report offence
.saturating_add(Weight::from_parts(110u64 * WEIGHT_REF_TIME_PER_MICROS, 0))
.saturating_add(Weight::from_parts(
25u64 * WEIGHT_REF_TIME_PER_MICROS * max_nominators_per_validator as u64,
0,
))
.saturating_add(DbWeight::get().reads(14 + 3 * max_nominators_per_validator as u64))
.saturating_add(DbWeight::get().writes(10 + 3 * max_nominators_per_validator as u64))
// fetching set id -> session index mappings
.saturating_add(DbWeight::get().reads(2))
}
fn note_stalled() -> Weight {
Weight::from_parts(3u64 * WEIGHT_REF_TIME_PER_MICROS, 0)
.saturating_add(DbWeight::get().writes(1))
}
}
@@ -0,0 +1,291 @@
// 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.
//! An opt-in utility module for reporting equivocations.
//!
//! This module defines an offence type for GRANDPA equivocations
//! and some utility traits to wire together:
//! - a key ownership proof system (e.g. to prove that a given authority was
//! part of a session);
//! - a system for reporting offences;
//! - a system for signing and submitting transactions;
//! - a way to get the current block author;
//!
//! These can be used in an offchain context in order to submit equivocation
//! reporting extrinsics (from the client that's running the GRANDPA protocol).
//! And in a runtime context, so that the GRANDPA module can validate the
//! equivocation proofs in the extrinsic and report the offences.
//!
//! IMPORTANT:
//! When using this module for enabling equivocation reporting it is required
//! that the `ValidateUnsigned` for the GRANDPA pallet is used in the runtime
//! definition.
use alloc::{boxed::Box, vec, vec::Vec};
use codec::{self as codec, Decode, Encode};
use pezframe_support::traits::{Get, KeyOwnerProofSystem};
use pezframe_system::pezpallet_prelude::BlockNumberFor;
use log::{error, info};
use pezsp_consensus_grandpa::{AuthorityId, EquivocationProof, RoundNumber, SetId, KEY_TYPE};
use pezsp_runtime::{
transaction_validity::{
InvalidTransaction, TransactionPriority, TransactionSource, TransactionValidity,
TransactionValidityError, ValidTransaction,
},
DispatchError, KeyTypeId, Perbill,
};
use pezsp_session::{GetSessionNumber, GetValidatorCount};
use pezsp_staking::{
offence::{Kind, Offence, OffenceReportSystem, ReportOffence},
SessionIndex,
};
use super::{Call, Config, Error, Pallet, LOG_TARGET};
/// A round number and set id which point on the time of an offence.
#[derive(Copy, Clone, PartialOrd, Ord, Eq, PartialEq, Encode, Decode)]
pub struct TimeSlot {
// The order of these matters for `derive(Ord)`.
/// Grandpa Set ID.
pub set_id: SetId,
/// Round number.
pub round: RoundNumber,
}
/// GRANDPA equivocation offence report.
pub struct EquivocationOffence<Offender> {
/// Time slot at which this incident happened.
pub time_slot: TimeSlot,
/// The session index in which the incident happened.
pub session_index: SessionIndex,
/// The size of the validator set at the time of the offence.
pub validator_set_count: u32,
/// The authority which produced this equivocation.
pub offender: Offender,
}
impl<Offender: Clone> Offence<Offender> for EquivocationOffence<Offender> {
const ID: Kind = *b"grandpa:equivoca";
type TimeSlot = TimeSlot;
fn offenders(&self) -> Vec<Offender> {
vec![self.offender.clone()]
}
fn session_index(&self) -> SessionIndex {
self.session_index
}
fn validator_set_count(&self) -> u32 {
self.validator_set_count
}
fn time_slot(&self) -> Self::TimeSlot {
self.time_slot
}
// The formula is min((3k / n)^2, 1)
// where k = offenders_number and n = validators_number
fn slash_fraction(&self, offenders_count: u32) -> Perbill {
// Perbill type domain is [0, 1] by definition
Perbill::from_rational(3 * offenders_count, self.validator_set_count).square()
}
}
/// GRANDPA equivocation offence report system.
///
/// This type implements `OffenceReportSystem` such that:
/// - Equivocation reports are published on-chain as unsigned extrinsic via
/// `offchain::CreateTransactionBase`.
/// - On-chain validity checks and processing are mostly delegated to the user provided generic
/// types implementing `KeyOwnerProofSystem` and `ReportOffence` traits.
/// - Offence reporter for unsigned transactions is fetched via the the authorship pallet.
pub struct EquivocationReportSystem<T, R, P, L>(core::marker::PhantomData<(T, R, P, L)>);
impl<T, R, P, L>
OffenceReportSystem<
Option<T::AccountId>,
(EquivocationProof<T::Hash, BlockNumberFor<T>>, T::KeyOwnerProof),
> for EquivocationReportSystem<T, R, P, L>
where
T: Config + pezpallet_authorship::Config + pezframe_system::offchain::CreateBare<Call<T>>,
R: ReportOffence<
T::AccountId,
P::IdentificationTuple,
EquivocationOffence<P::IdentificationTuple>,
>,
P: KeyOwnerProofSystem<(KeyTypeId, AuthorityId), Proof = T::KeyOwnerProof>,
P::IdentificationTuple: Clone,
L: Get<u64>,
{
type Longevity = L;
fn publish_evidence(
evidence: (EquivocationProof<T::Hash, BlockNumberFor<T>>, T::KeyOwnerProof),
) -> Result<(), ()> {
use pezframe_system::offchain::SubmitTransaction;
let (equivocation_proof, key_owner_proof) = evidence;
let call = Call::report_equivocation_unsigned {
equivocation_proof: Box::new(equivocation_proof),
key_owner_proof,
};
let xt = T::create_bare(call.into());
let res = SubmitTransaction::<T, Call<T>>::submit_transaction(xt);
match res {
Ok(_) => info!(target: LOG_TARGET, "Submitted equivocation report"),
Err(e) => error!(target: LOG_TARGET, "Error submitting equivocation report: {:?}", e),
}
res
}
fn check_evidence(
evidence: (EquivocationProof<T::Hash, BlockNumberFor<T>>, T::KeyOwnerProof),
) -> Result<(), TransactionValidityError> {
let (equivocation_proof, key_owner_proof) = evidence;
// Check the membership proof to extract the offender's id
let key = (KEY_TYPE, equivocation_proof.offender().clone());
let offender = P::check_proof(key, key_owner_proof).ok_or(InvalidTransaction::BadProof)?;
// Check if the offence has already been reported, and if so then we can discard the report.
let time_slot =
TimeSlot { set_id: equivocation_proof.set_id(), round: equivocation_proof.round() };
if R::is_known_offence(&[offender], &time_slot) {
Err(InvalidTransaction::Stale.into())
} else {
Ok(())
}
}
fn process_evidence(
reporter: Option<T::AccountId>,
evidence: (EquivocationProof<T::Hash, BlockNumberFor<T>>, T::KeyOwnerProof),
) -> Result<(), DispatchError> {
let (equivocation_proof, key_owner_proof) = evidence;
let reporter = reporter.or_else(|| pezpallet_authorship::Pallet::<T>::author());
let offender = equivocation_proof.offender().clone();
// We check the equivocation within the context of its set id (and
// associated session) and round. We also need to know the validator
// set count when the offence since it is required to calculate the
// slash amount.
let set_id = equivocation_proof.set_id();
let round = equivocation_proof.round();
let session_index = key_owner_proof.session();
let validator_set_count = key_owner_proof.validator_count();
// Validate equivocation proof (check votes are different and signatures are valid).
if !pezsp_consensus_grandpa::check_equivocation_proof(equivocation_proof) {
return Err(Error::<T>::InvalidEquivocationProof.into());
}
// Validate the key ownership proof extracting the id of the offender.
let offender = P::check_proof((KEY_TYPE, offender), key_owner_proof)
.ok_or(Error::<T>::InvalidKeyOwnershipProof)?;
// Fetch the current and previous sets last session index.
// For genesis set there's no previous set.
let previous_set_id_session_index = if set_id != 0 {
let idx = crate::SetIdSession::<T>::get(set_id - 1)
.ok_or(Error::<T>::InvalidEquivocationProof)?;
Some(idx)
} else {
None
};
let set_id_session_index =
crate::SetIdSession::<T>::get(set_id).ok_or(Error::<T>::InvalidEquivocationProof)?;
// Check that the session id for the membership proof is within the
// bounds of the set id reported in the equivocation.
if session_index > set_id_session_index ||
previous_set_id_session_index
.map(|previous_index| session_index <= previous_index)
.unwrap_or(false)
{
return Err(Error::<T>::InvalidEquivocationProof.into());
}
let offence = EquivocationOffence {
time_slot: TimeSlot { set_id, round },
session_index,
offender,
validator_set_count,
};
R::report_offence(reporter.into_iter().collect(), offence)
.map_err(|_| Error::<T>::DuplicateOffenceReport)?;
Ok(())
}
}
/// Methods for the `ValidateUnsigned` implementation:
/// It restricts calls to `report_equivocation_unsigned` to local calls (i.e. extrinsics generated
/// on this node) or that already in a block. This guarantees that only block authors can include
/// unsigned equivocation reports.
impl<T: Config> Pallet<T> {
pub fn validate_unsigned(source: TransactionSource, call: &Call<T>) -> TransactionValidity {
if let Call::report_equivocation_unsigned { equivocation_proof, key_owner_proof } = call {
// discard equivocation report not coming from the local node
match source {
TransactionSource::Local | TransactionSource::InBlock => { /* allowed */ },
_ => {
log::warn!(
target: LOG_TARGET,
"rejecting unsigned report equivocation transaction because it is not local/in-block."
);
return InvalidTransaction::Call.into();
},
}
// Check report validity
let evidence = (*equivocation_proof.clone(), key_owner_proof.clone());
T::EquivocationReportSystem::check_evidence(evidence)?;
let longevity =
<T::EquivocationReportSystem as OffenceReportSystem<_, _>>::Longevity::get();
ValidTransaction::with_tag_prefix("GrandpaEquivocation")
// We assign the maximum priority for any equivocation report.
.priority(TransactionPriority::max_value())
// Only one equivocation report for the same offender at the same slot.
.and_provides((
equivocation_proof.offender().clone(),
equivocation_proof.set_id(),
equivocation_proof.round(),
))
.longevity(longevity)
// We don't propagate this. This can never be included on a remote node.
.propagate(false)
.build()
} else {
InvalidTransaction::Call.into()
}
}
pub fn pre_dispatch(call: &Call<T>) -> Result<(), TransactionValidityError> {
if let Call::report_equivocation_unsigned { equivocation_proof, key_owner_proof } = call {
let evidence = (*equivocation_proof.clone(), key_owner_proof.clone());
T::EquivocationReportSystem::check_evidence(evidence)
} else {
Err(InvalidTransaction::Call.into())
}
}
}
+661
View File
@@ -0,0 +1,661 @@
// 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.
//! GRANDPA Consensus module for runtime.
//!
//! This manages the GRANDPA authority set ready for the native code.
//! These authorities are only for GRANDPA finality, not for consensus overall.
//!
//! In the future, it will also handle misbehavior reports, and on-chain
//! finality notifications.
//!
//! For full integration with GRANDPA, the `GrandpaApi` should be implemented.
//! The necessary items are re-exported via the `fg_primitives` crate.
#![cfg_attr(not(feature = "std"), no_std)]
extern crate alloc;
// Re-export since this is necessary for `impl_apis` in runtime.
pub use pezsp_consensus_grandpa::{
self as fg_primitives, AuthorityId, AuthorityList, AuthorityWeight,
};
use alloc::{boxed::Box, vec::Vec};
use codec::{Decode, Encode, MaxEncodedLen};
use pezframe_support::{
dispatch::{DispatchResultWithPostInfo, Pays},
pezpallet_prelude::Get,
traits::OneSessionHandler,
weights::Weight,
WeakBoundedVec,
};
use pezframe_system::pezpallet_prelude::BlockNumberFor;
use scale_info::TypeInfo;
use pezsp_consensus_grandpa::{
ConsensusLog, EquivocationProof, ScheduledChange, SetId, GRANDPA_ENGINE_ID,
RUNTIME_LOG_TARGET as LOG_TARGET,
};
use pezsp_runtime::{generic::DigestItem, traits::Zero, DispatchResult};
use pezsp_session::{GetSessionNumber, GetValidatorCount};
use pezsp_staking::{offence::OffenceReportSystem, SessionIndex};
mod default_weights;
mod equivocation;
pub mod migrations;
#[cfg(any(feature = "runtime-benchmarks", test))]
mod benchmarking;
#[cfg(all(feature = "std", test))]
mod mock;
#[cfg(all(feature = "std", test))]
mod tests;
pub use equivocation::{EquivocationOffence, EquivocationReportSystem, TimeSlot};
pub use pallet::*;
#[pezframe_support::pallet]
pub mod pallet {
use super::*;
use pezframe_support::{dispatch::DispatchResult, pezpallet_prelude::*};
use pezframe_system::pezpallet_prelude::*;
/// The in-code storage version.
const STORAGE_VERSION: StorageVersion = StorageVersion::new(5);
#[pallet::pallet]
#[pallet::storage_version(STORAGE_VERSION)]
pub struct Pallet<T>(_);
#[pallet::config]
pub trait Config: pezframe_system::Config {
/// The event type of this module.
#[allow(deprecated)]
type RuntimeEvent: From<Event>
+ Into<<Self as pezframe_system::Config>::RuntimeEvent>
+ IsType<<Self as pezframe_system::Config>::RuntimeEvent>;
/// Weights for this pallet.
type WeightInfo: WeightInfo;
/// Max Authorities in use
#[pallet::constant]
type MaxAuthorities: Get<u32>;
/// The maximum number of nominators for each validator.
#[pallet::constant]
type MaxNominators: Get<u32>;
/// The maximum number of entries to keep in the set id to session index mapping.
///
/// Since the `SetIdSession` map is only used for validating equivocations this
/// value should relate to the bonding duration of whatever staking system is
/// being used (if any). If equivocation handling is not enabled then this value
/// can be zero.
#[pallet::constant]
type MaxSetIdSessionEntries: Get<u64>;
/// The proof of key ownership, used for validating equivocation reports
/// The proof include the session index and validator count of the
/// session at which the equivocation occurred.
type KeyOwnerProof: Parameter + GetSessionNumber + GetValidatorCount;
/// The equivocation handling subsystem, defines methods to check/report an
/// offence and for submitting a transaction to report an equivocation
/// (from an offchain context).
type EquivocationReportSystem: OffenceReportSystem<
Option<Self::AccountId>,
(EquivocationProof<Self::Hash, BlockNumberFor<Self>>, Self::KeyOwnerProof),
>;
}
#[pallet::hooks]
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
fn on_finalize(block_number: BlockNumberFor<T>) {
// check for scheduled pending authority set changes
if let Some(pending_change) = PendingChange::<T>::get() {
// emit signal if we're at the block that scheduled the change
if block_number == pending_change.scheduled_at {
let next_authorities = pending_change.next_authorities.to_vec();
if let Some(median) = pending_change.forced {
Self::deposit_log(ConsensusLog::ForcedChange(
median,
ScheduledChange { delay: pending_change.delay, next_authorities },
))
} else {
Self::deposit_log(ConsensusLog::ScheduledChange(ScheduledChange {
delay: pending_change.delay,
next_authorities,
}));
}
}
// enact the change if we've reached the enacting block
if block_number == pending_change.scheduled_at + pending_change.delay {
Authorities::<T>::put(&pending_change.next_authorities);
Self::deposit_event(Event::NewAuthorities {
authority_set: pending_change.next_authorities.into_inner(),
});
PendingChange::<T>::kill();
}
}
// check for scheduled pending state changes
match State::<T>::get() {
StoredState::PendingPause { scheduled_at, delay } => {
// signal change to pause
if block_number == scheduled_at {
Self::deposit_log(ConsensusLog::Pause(delay));
}
// enact change to paused state
if block_number == scheduled_at + delay {
State::<T>::put(StoredState::Paused);
Self::deposit_event(Event::Paused);
}
},
StoredState::PendingResume { scheduled_at, delay } => {
// signal change to resume
if block_number == scheduled_at {
Self::deposit_log(ConsensusLog::Resume(delay));
}
// enact change to live state
if block_number == scheduled_at + delay {
State::<T>::put(StoredState::Live);
Self::deposit_event(Event::Resumed);
}
},
_ => {},
}
}
}
#[pallet::call]
impl<T: Config> Pallet<T> {
/// Report voter equivocation/misbehavior. This method will verify the
/// equivocation proof and validate the given key ownership proof
/// against the extracted offender. If both are valid, the offence
/// will be reported.
#[pallet::call_index(0)]
#[pallet::weight(T::WeightInfo::report_equivocation(
key_owner_proof.validator_count(),
T::MaxNominators::get(),
))]
pub fn report_equivocation(
origin: OriginFor<T>,
equivocation_proof: Box<EquivocationProof<T::Hash, BlockNumberFor<T>>>,
key_owner_proof: T::KeyOwnerProof,
) -> DispatchResultWithPostInfo {
let reporter = ensure_signed(origin)?;
T::EquivocationReportSystem::process_evidence(
Some(reporter),
(*equivocation_proof, key_owner_proof),
)?;
// Waive the fee since the report is valid and beneficial
Ok(Pays::No.into())
}
/// Report voter equivocation/misbehavior. This method will verify the
/// equivocation proof and validate the given key ownership proof
/// against the extracted offender. If both are valid, the offence
/// will be reported.
///
/// This extrinsic must be called unsigned and it is expected that only
/// block authors will call it (validated in `ValidateUnsigned`), as such
/// if the block author is defined it will be defined as the equivocation
/// reporter.
#[pallet::call_index(1)]
#[pallet::weight(T::WeightInfo::report_equivocation(
key_owner_proof.validator_count(),
T::MaxNominators::get(),
))]
pub fn report_equivocation_unsigned(
origin: OriginFor<T>,
equivocation_proof: Box<EquivocationProof<T::Hash, BlockNumberFor<T>>>,
key_owner_proof: T::KeyOwnerProof,
) -> DispatchResultWithPostInfo {
ensure_none(origin)?;
T::EquivocationReportSystem::process_evidence(
None,
(*equivocation_proof, key_owner_proof),
)?;
Ok(Pays::No.into())
}
/// Note that the current authority set of the GRANDPA finality gadget has stalled.
///
/// This will trigger a forced authority set change at the beginning of the next session, to
/// be enacted `delay` blocks after that. The `delay` should be high enough to safely assume
/// that the block signalling the forced change will not be re-orged e.g. 1000 blocks.
/// The block production rate (which may be slowed down because of finality lagging) should
/// be taken into account when choosing the `delay`. The GRANDPA voters based on the new
/// authority will start voting on top of `best_finalized_block_number` for new finalized
/// blocks. `best_finalized_block_number` should be the highest of the latest finalized
/// block of all validators of the new authority set.
///
/// Only callable by root.
#[pallet::call_index(2)]
#[pallet::weight(T::WeightInfo::note_stalled())]
pub fn note_stalled(
origin: OriginFor<T>,
delay: BlockNumberFor<T>,
best_finalized_block_number: BlockNumberFor<T>,
) -> DispatchResult {
ensure_root(origin)?;
Self::on_stalled(delay, best_finalized_block_number);
Ok(())
}
}
#[pallet::event]
#[pallet::generate_deposit(fn deposit_event)]
pub enum Event {
/// New authority set has been applied.
NewAuthorities { authority_set: AuthorityList },
/// Current authority set has been paused.
Paused,
/// Current authority set has been resumed.
Resumed,
}
#[pallet::error]
pub enum Error<T> {
/// Attempt to signal GRANDPA pause when the authority set isn't live
/// (either paused or already pending pause).
PauseFailed,
/// Attempt to signal GRANDPA resume when the authority set isn't paused
/// (either live or already pending resume).
ResumeFailed,
/// Attempt to signal GRANDPA change with one already pending.
ChangePending,
/// Cannot signal forced change so soon after last.
TooSoon,
/// A key ownership proof provided as part of an equivocation report is invalid.
InvalidKeyOwnershipProof,
/// An equivocation proof provided as part of an equivocation report is invalid.
InvalidEquivocationProof,
/// A given equivocation report is valid but already previously reported.
DuplicateOffenceReport,
}
#[pallet::type_value]
pub fn DefaultForState<T: Config>() -> StoredState<BlockNumberFor<T>> {
StoredState::Live
}
/// State of the current authority set.
#[pallet::storage]
pub type State<T: Config> =
StorageValue<_, StoredState<BlockNumberFor<T>>, ValueQuery, DefaultForState<T>>;
/// Pending change: (signaled at, scheduled change).
#[pallet::storage]
pub type PendingChange<T: Config> =
StorageValue<_, StoredPendingChange<BlockNumberFor<T>, T::MaxAuthorities>>;
/// next block number where we can force a change.
#[pallet::storage]
pub type NextForced<T: Config> = StorageValue<_, BlockNumberFor<T>>;
/// `true` if we are currently stalled.
#[pallet::storage]
pub type Stalled<T: Config> = StorageValue<_, (BlockNumberFor<T>, BlockNumberFor<T>)>;
/// The number of changes (both in terms of keys and underlying economic responsibilities)
/// in the "set" of Grandpa validators from genesis.
#[pallet::storage]
pub type CurrentSetId<T: Config> = StorageValue<_, SetId, ValueQuery>;
/// A mapping from grandpa set ID to the index of the *most recent* session for which its
/// members were responsible.
///
/// This is only used for validating equivocation proofs. An equivocation proof must
/// contains a key-ownership proof for a given session, therefore we need a way to tie
/// together sessions and GRANDPA set ids, i.e. we need to validate that a validator
/// was the owner of a given key on a given session, and what the active set ID was
/// during that session.
///
/// TWOX-NOTE: `SetId` is not under user control.
#[pallet::storage]
pub type SetIdSession<T: Config> = StorageMap<_, Twox64Concat, SetId, SessionIndex>;
/// The current list of authorities.
#[pallet::storage]
pub type Authorities<T: Config> =
StorageValue<_, BoundedAuthorityList<T::MaxAuthorities>, ValueQuery>;
#[derive(pezframe_support::DefaultNoBound)]
#[pallet::genesis_config]
pub struct GenesisConfig<T: Config> {
pub authorities: AuthorityList,
#[serde(skip)]
pub _config: core::marker::PhantomData<T>,
}
#[pallet::genesis_build]
impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
fn build(&self) {
CurrentSetId::<T>::put(SetId::default());
Pallet::<T>::initialize(self.authorities.clone())
}
}
#[pallet::validate_unsigned]
impl<T: Config> ValidateUnsigned for Pallet<T> {
type Call = Call<T>;
fn validate_unsigned(source: TransactionSource, call: &Self::Call) -> TransactionValidity {
Self::validate_unsigned(source, call)
}
fn pre_dispatch(call: &Self::Call) -> Result<(), TransactionValidityError> {
Self::pre_dispatch(call)
}
}
}
pub trait WeightInfo {
fn report_equivocation(validator_count: u32, max_nominators_per_validator: u32) -> Weight;
fn note_stalled() -> Weight;
}
/// Bounded version of `AuthorityList`, `Limit` being the bound
pub type BoundedAuthorityList<Limit> = WeakBoundedVec<(AuthorityId, AuthorityWeight), Limit>;
/// A stored pending change.
/// `Limit` is the bound for `next_authorities`
#[derive(Encode, Decode, TypeInfo, MaxEncodedLen)]
#[codec(mel_bound(N: MaxEncodedLen, Limit: Get<u32>))]
#[scale_info(skip_type_params(Limit))]
pub struct StoredPendingChange<N, Limit> {
/// The block number this was scheduled at.
pub scheduled_at: N,
/// The delay in blocks until it will be applied.
pub delay: N,
/// The next authority set, weakly bounded in size by `Limit`.
pub next_authorities: BoundedAuthorityList<Limit>,
/// If defined it means the change was forced and the given block number
/// indicates the median last finalized block when the change was signaled.
pub forced: Option<N>,
}
/// Current state of the GRANDPA authority set. State transitions must happen in
/// the same order of states defined below, e.g. `Paused` implies a prior
/// `PendingPause`.
#[derive(Decode, Encode, TypeInfo, MaxEncodedLen)]
#[cfg_attr(test, derive(Debug, PartialEq))]
pub enum StoredState<N> {
/// The current authority set is live, and GRANDPA is enabled.
Live,
/// There is a pending pause event which will be enacted at the given block
/// height.
PendingPause {
/// Block at which the intention to pause was scheduled.
scheduled_at: N,
/// Number of blocks after which the change will be enacted.
delay: N,
},
/// The current GRANDPA authority set is paused.
Paused,
/// There is a pending resume event which will be enacted at the given block
/// height.
PendingResume {
/// Block at which the intention to resume was scheduled.
scheduled_at: N,
/// Number of blocks after which the change will be enacted.
delay: N,
},
}
impl<T: Config> Pallet<T> {
/// State of the current authority set.
pub fn state() -> StoredState<BlockNumberFor<T>> {
State::<T>::get()
}
/// Pending change: (signaled at, scheduled change).
pub fn pending_change() -> Option<StoredPendingChange<BlockNumberFor<T>, T::MaxAuthorities>> {
PendingChange::<T>::get()
}
/// next block number where we can force a change.
pub fn next_forced() -> Option<BlockNumberFor<T>> {
NextForced::<T>::get()
}
/// `true` if we are currently stalled.
pub fn stalled() -> Option<(BlockNumberFor<T>, BlockNumberFor<T>)> {
Stalled::<T>::get()
}
/// The number of changes (both in terms of keys and underlying economic responsibilities)
/// in the "set" of Grandpa validators from genesis.
pub fn current_set_id() -> SetId {
CurrentSetId::<T>::get()
}
/// A mapping from grandpa set ID to the index of the *most recent* session for which its
/// members were responsible.
///
/// This is only used for validating equivocation proofs. An equivocation proof must
/// contains a key-ownership proof for a given session, therefore we need a way to tie
/// together sessions and GRANDPA set ids, i.e. we need to validate that a validator
/// was the owner of a given key on a given session, and what the active set ID was
/// during that session.
pub fn session_for_set(set_id: SetId) -> Option<SessionIndex> {
SetIdSession::<T>::get(set_id)
}
/// Get the current set of authorities, along with their respective weights.
pub fn grandpa_authorities() -> AuthorityList {
Authorities::<T>::get().into_inner()
}
/// Schedule GRANDPA to pause starting in the given number of blocks.
/// Cannot be done when already paused.
pub fn schedule_pause(in_blocks: BlockNumberFor<T>) -> DispatchResult {
if let StoredState::Live = State::<T>::get() {
let scheduled_at = pezframe_system::Pallet::<T>::block_number();
State::<T>::put(StoredState::PendingPause { delay: in_blocks, scheduled_at });
Ok(())
} else {
Err(Error::<T>::PauseFailed.into())
}
}
/// Schedule a resume of GRANDPA after pausing.
pub fn schedule_resume(in_blocks: BlockNumberFor<T>) -> DispatchResult {
if let StoredState::Paused = State::<T>::get() {
let scheduled_at = pezframe_system::Pallet::<T>::block_number();
State::<T>::put(StoredState::PendingResume { delay: in_blocks, scheduled_at });
Ok(())
} else {
Err(Error::<T>::ResumeFailed.into())
}
}
/// Schedule a change in the authorities.
///
/// The change will be applied at the end of execution of the block
/// `in_blocks` after the current block. This value may be 0, in which
/// case the change is applied at the end of the current block.
///
/// If the `forced` parameter is defined, this indicates that the current
/// set has been synchronously determined to be offline and that after
/// `in_blocks` the given change should be applied. The given block number
/// indicates the median last finalized block number and it should be used
/// as the canon block when starting the new grandpa voter.
///
/// No change should be signaled while any change is pending. Returns
/// an error if a change is already pending.
pub fn schedule_change(
next_authorities: AuthorityList,
in_blocks: BlockNumberFor<T>,
forced: Option<BlockNumberFor<T>>,
) -> DispatchResult {
if !PendingChange::<T>::exists() {
let scheduled_at = pezframe_system::Pallet::<T>::block_number();
if forced.is_some() {
if NextForced::<T>::get().map_or(false, |next| next > scheduled_at) {
return Err(Error::<T>::TooSoon.into());
}
// only allow the next forced change when twice the window has passed since
// this one.
NextForced::<T>::put(scheduled_at + in_blocks * 2u32.into());
}
let next_authorities = WeakBoundedVec::<_, T::MaxAuthorities>::force_from(
next_authorities,
Some(
"Warning: The number of authorities given is too big. \
A runtime configuration adjustment may be needed.",
),
);
PendingChange::<T>::put(StoredPendingChange {
delay: in_blocks,
scheduled_at,
next_authorities,
forced,
});
Ok(())
} else {
Err(Error::<T>::ChangePending.into())
}
}
/// Deposit one of this module's logs.
fn deposit_log(log: ConsensusLog<BlockNumberFor<T>>) {
let log = DigestItem::Consensus(GRANDPA_ENGINE_ID, log.encode());
pezframe_system::Pallet::<T>::deposit_log(log);
}
// Perform module initialization, abstracted so that it can be called either through genesis
// config builder or through `on_genesis_session`.
fn initialize(authorities: AuthorityList) {
if !authorities.is_empty() {
assert!(Self::grandpa_authorities().is_empty(), "Authorities are already initialized!");
Authorities::<T>::put(
&BoundedAuthorityList::<T::MaxAuthorities>::try_from(authorities).expect(
"Grandpa: `Config::MaxAuthorities` is smaller than the number of genesis authorities!",
),
);
}
// NOTE: initialize first session of first set. this is necessary for
// the genesis set and session since we only update the set -> session
// mapping whenever a new session starts, i.e. through `on_new_session`.
SetIdSession::<T>::insert(0, 0);
}
/// Submits an extrinsic to report an equivocation. This method will create
/// an unsigned extrinsic with a call to `report_equivocation_unsigned` and
/// will push the transaction to the pool. Only useful in an offchain
/// context.
pub fn submit_unsigned_equivocation_report(
equivocation_proof: EquivocationProof<T::Hash, BlockNumberFor<T>>,
key_owner_proof: T::KeyOwnerProof,
) -> Option<()> {
T::EquivocationReportSystem::publish_evidence((equivocation_proof, key_owner_proof)).ok()
}
fn on_stalled(further_wait: BlockNumberFor<T>, median: BlockNumberFor<T>) {
// when we record old authority sets we could try to figure out _who_
// failed. until then, we can't meaningfully guard against
// `next == last` the way that normal session changes do.
Stalled::<T>::put((further_wait, median));
}
}
impl<T: Config> pezsp_runtime::BoundToRuntimeAppPublic for Pallet<T> {
type Public = AuthorityId;
}
impl<T: Config> OneSessionHandler<T::AccountId> for Pallet<T>
where
T: pezpallet_session::Config,
{
type Key = AuthorityId;
fn on_genesis_session<'a, I: 'a>(validators: I)
where
I: Iterator<Item = (&'a T::AccountId, AuthorityId)>,
{
let authorities = validators.map(|(_, k)| (k, 1)).collect::<Vec<_>>();
Self::initialize(authorities);
}
fn on_new_session<'a, I: 'a>(changed: bool, validators: I, _queued_validators: I)
where
I: Iterator<Item = (&'a T::AccountId, AuthorityId)>,
{
// Always issue a change if `session` says that the validators have changed.
// Even if their session keys are the same as before, the underlying economic
// identities have changed.
let current_set_id = if changed || Stalled::<T>::exists() {
let next_authorities = validators.map(|(_, k)| (k, 1)).collect::<Vec<_>>();
let res = if let Some((further_wait, median)) = Stalled::<T>::take() {
Self::schedule_change(next_authorities, further_wait, Some(median))
} else {
Self::schedule_change(next_authorities, Zero::zero(), None)
};
if res.is_ok() {
let current_set_id = CurrentSetId::<T>::mutate(|s| {
*s += 1;
*s
});
let max_set_id_session_entries = T::MaxSetIdSessionEntries::get().max(1);
if current_set_id >= max_set_id_session_entries {
SetIdSession::<T>::remove(current_set_id - max_set_id_session_entries);
}
current_set_id
} else {
// either the session module signalled that the validators have changed
// or the set was stalled. but since we didn't successfully schedule
// an authority set change we do not increment the set id.
CurrentSetId::<T>::get()
}
} else {
// nothing's changed, neither economic conditions nor session keys. update the pointer
// of the current set.
CurrentSetId::<T>::get()
};
// update the mapping to note that the current set corresponds to the
// latest equivalent session (i.e. now).
let session_index = pezpallet_session::Pallet::<T>::current_index();
SetIdSession::<T>::insert(current_set_id, &session_index);
}
fn on_disabled(i: u32) {
Self::deposit_log(ConsensusLog::OnDisabled(i as u64))
}
}
@@ -0,0 +1,68 @@
// 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 pezframe_support::{
traits::{Get, OnRuntimeUpgrade},
weights::Weight,
};
use crate::{Config, CurrentSetId, SetIdSession, LOG_TARGET};
pub use v5::MigrateV4ToV5;
/// Version 4.
pub mod v4;
mod v5;
/// This migration will clean up all stale set id -> session entries from the
/// `SetIdSession` storage map, only the latest `max_set_id_session_entries`
/// will be kept.
///
/// This migration should be added with a runtime upgrade that introduces the
/// `MaxSetIdSessionEntries` constant to the pallet (although it could also be
/// done later on).
pub struct CleanupSetIdSessionMap<T>(core::marker::PhantomData<T>);
impl<T: Config> OnRuntimeUpgrade for CleanupSetIdSessionMap<T> {
fn on_runtime_upgrade() -> Weight {
// NOTE: since this migration will loop over all stale entries in the
// map we need to set some cutoff value, otherwise the migration might
// take too long to run. for scenarios where there are that many entries
// to cleanup a multiblock migration will be needed instead.
if CurrentSetId::<T>::get() > 25_000 {
log::warn!(
target: LOG_TARGET,
"CleanupSetIdSessionMap migration was aborted since there are too many entries to cleanup."
);
return T::DbWeight::get().reads(1);
}
cleanup_set_id_sesion_map::<T>()
}
}
fn cleanup_set_id_sesion_map<T: Config>() -> Weight {
let until_set_id = CurrentSetId::<T>::get().saturating_sub(T::MaxSetIdSessionEntries::get());
for set_id in 0..=until_set_id {
SetIdSession::<T>::remove(set_id);
}
T::DbWeight::get()
.reads(1)
.saturating_add(T::DbWeight::get().writes(until_set_id + 1))
}
@@ -0,0 +1,111 @@
// 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 crate::LOG_TARGET;
use pezframe_support::{
traits::{Get, StorageVersion},
weights::Weight,
};
use pezsp_io::hashing::twox_128;
/// The old prefix.
pub const OLD_PREFIX: &[u8] = b"GrandpaFinality";
/// Migrate the entire storage of this pallet to a new prefix.
///
/// This new prefix must be the same as the one set in construct_runtime. For safety, use
/// `PalletInfo` to get it, as:
/// `<Runtime as pezframe_system::Config>::PalletInfo::name::<GrandpaPallet>`.
///
/// The old storage prefix, `GrandpaFinality` is hardcoded in the migration code.
pub fn migrate<T: crate::Config, N: AsRef<str>>(new_pallet_name: N) -> Weight {
if new_pallet_name.as_ref().as_bytes() == OLD_PREFIX {
log::info!(
target: LOG_TARGET,
"New pallet name is equal to the old prefix. No migration needs to be done.",
);
return Weight::zero();
}
let storage_version = StorageVersion::get::<crate::Pallet<T>>();
log::info!(
target: LOG_TARGET,
"Running migration to v3.1 for grandpa with storage version {:?}",
storage_version,
);
if storage_version <= 3 {
log::info!("new prefix: {}", new_pallet_name.as_ref());
pezframe_support::storage::migration::move_pallet(
OLD_PREFIX,
new_pallet_name.as_ref().as_bytes(),
);
StorageVersion::new(4).put::<crate::Pallet<T>>();
<T as pezframe_system::Config>::BlockWeights::get().max_block
} else {
Weight::zero()
}
}
/// Some checks prior to migration. This can be linked to
/// `pezframe_support::traits::OnRuntimeUpgrade::pre_upgrade` for further testing.
///
/// Panics if anything goes wrong.
pub fn pre_migration<T: crate::Config, N: AsRef<str>>(new: N) {
let new = new.as_ref();
log::info!("pre-migration grandpa test with new = {}", new);
// the next key must exist, and start with the hash of `OLD_PREFIX`.
let next_key = pezsp_io::storage::next_key(&twox_128(OLD_PREFIX)).unwrap();
assert!(next_key.starts_with(&twox_128(OLD_PREFIX)));
// The pallet version is already stored using the pallet name
let storage_key = StorageVersion::storage_key::<crate::Pallet<T>>();
// ensure nothing is stored in the new prefix.
assert!(
pezsp_io::storage::next_key(&twox_128(new.as_bytes())).map_or(
// either nothing is there
true,
// or we ensure that it has no common prefix with twox_128(new),
// or isn't the pallet version that is already stored using the pallet name
|next_key| {
!next_key.starts_with(&twox_128(new.as_bytes())) || next_key == storage_key
},
),
"unexpected next_key({}) = {:?}",
new,
pezsp_core::hexdisplay::HexDisplay::from(
&pezsp_io::storage::next_key(&twox_128(new.as_bytes())).unwrap()
),
);
// ensure storage version is 3.
assert_eq!(StorageVersion::get::<crate::Pallet<T>>(), 3);
}
/// Some checks for after migration. This can be linked to
/// `pezframe_support::traits::OnRuntimeUpgrade::post_upgrade` for further testing.
///
/// Panics if anything goes wrong.
pub fn post_migration() {
log::info!("post-migration grandpa");
// Assert that nothing remains at the old prefix
assert!(pezsp_io::storage::next_key(&twox_128(OLD_PREFIX))
.map_or(true, |next_key| !next_key.starts_with(&twox_128(OLD_PREFIX))));
}
@@ -0,0 +1,102 @@
// 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 crate::{BoundedAuthorityList, Pallet};
use alloc::vec::Vec;
use codec::Decode;
use core::marker::PhantomData;
use pezframe_support::{
migrations::VersionedMigration,
storage,
traits::{Get, UncheckedOnRuntimeUpgrade},
weights::Weight,
};
use pezsp_consensus_grandpa::AuthorityList;
const GRANDPA_AUTHORITIES_KEY: &[u8] = b":grandpa_authorities";
fn load_authority_list() -> AuthorityList {
storage::unhashed::get_raw(GRANDPA_AUTHORITIES_KEY).map_or_else(
|| Vec::new(),
|l| <(u8, AuthorityList)>::decode(&mut &l[..]).unwrap_or_default().1,
)
}
/// Actual implementation of [`MigrateV4ToV5`].
pub struct UncheckedMigrateImpl<T>(PhantomData<T>);
impl<T: crate::Config> UncheckedOnRuntimeUpgrade for UncheckedMigrateImpl<T> {
#[cfg(feature = "try-runtime")]
fn pre_upgrade() -> Result<Vec<u8>, pezsp_runtime::TryRuntimeError> {
use codec::Encode;
let authority_list_len = load_authority_list().len() as u32;
if authority_list_len > T::MaxAuthorities::get() {
return Err(
"Grandpa: `Config::MaxAuthorities` is smaller than the actual number of authorities.".into()
);
}
if authority_list_len == 0 {
return Err("Grandpa: Authority list is empty!".into());
}
Ok(authority_list_len.encode())
}
#[cfg(feature = "try-runtime")]
fn post_upgrade(state: Vec<u8>) -> Result<(), pezsp_runtime::TryRuntimeError> {
let len = u32::decode(&mut &state[..]).unwrap();
pezframe_support::ensure!(
len == crate::Pallet::<T>::grandpa_authorities().len() as u32,
"Grandpa: pre-migrated and post-migrated list should have the same length"
);
pezframe_support::ensure!(
load_authority_list().is_empty(),
"Old authority list shouldn't exist anymore"
);
Ok(())
}
fn on_runtime_upgrade() -> Weight {
crate::Authorities::<T>::put(
&BoundedAuthorityList::<T::MaxAuthorities>::force_from(
load_authority_list(),
Some("Grandpa: `Config::MaxAuthorities` is smaller than the actual number of authorities.")
)
);
storage::unhashed::kill(GRANDPA_AUTHORITIES_KEY);
T::DbWeight::get().reads_writes(1, 2)
}
}
/// Migrate the storage from V4 to V5.
///
/// Switches from `GRANDPA_AUTHORITIES_KEY` to a normal FRAME storage item.
pub type MigrateV4ToV5<T> = VersionedMigration<
4,
5,
UncheckedMigrateImpl<T>,
Pallet<T>,
<T as pezframe_system::Config>::DbWeight,
>;
+343
View File
@@ -0,0 +1,343 @@
// 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::{self as pezpallet_grandpa, AuthorityId, AuthorityList, Config, ConsensusLog};
use codec::Encode;
use finality_grandpa;
use pezframe_election_provider_support::{
bounds::{ElectionBounds, ElectionBoundsBuilder},
onchain, SequentialPhragmen,
};
use pezframe_support::{
derive_impl, parameter_types,
traits::{ConstU128, ConstU32, ConstU64, OnFinalize, OnInitialize},
};
use pezpallet_session::historical as pezpallet_session_historical;
use pezsp_consensus_grandpa::{RoundNumber, SetId, GRANDPA_ENGINE_ID};
use pezsp_core::{ConstBool, H256};
use pezsp_keyring::Ed25519Keyring;
use pezsp_runtime::{
curve::PiecewiseLinear,
impl_opaque_keys,
testing::{TestXt, UintAuthorityId},
traits::OpaqueKeys,
BuildStorage, DigestItem, Perbill,
};
use pezsp_staking::{EraIndex, SessionIndex};
type Block = pezframe_system::mocking::MockBlock<Test>;
pezframe_support::construct_runtime!(
pub enum Test
{
System: pezframe_system,
Authorship: pezpallet_authorship,
Timestamp: pezpallet_timestamp,
Balances: pezpallet_balances,
Staking: pezpallet_staking,
Session: pezpallet_session,
Grandpa: pezpallet_grandpa,
Offences: pezpallet_offences,
Historical: pezpallet_session_historical,
}
);
impl_opaque_keys! {
pub struct TestSessionKeys {
pub grandpa_authority: super::Pallet<Test>,
}
}
#[derive_impl(pezframe_system::config_preludes::TestDefaultConfig)]
impl pezframe_system::Config for Test {
type Block = Block;
type AccountData = pezpallet_balances::AccountData<Balance>;
}
impl<C> pezframe_system::offchain::CreateTransactionBase<C> for Test
where
RuntimeCall: From<C>,
{
type RuntimeCall = RuntimeCall;
type Extrinsic = TestXt<RuntimeCall, ()>;
}
impl<C> pezframe_system::offchain::CreateBare<C> for Test
where
RuntimeCall: From<C>,
{
fn create_bare(call: Self::RuntimeCall) -> Self::Extrinsic {
TestXt::new_bare(call)
}
}
parameter_types! {
pub const Period: u64 = 1;
pub const Offset: u64 = 0;
}
/// Custom `SessionHandler` since we use `TestSessionKeys` as `Keys`.
impl pezpallet_session::Config for Test {
type RuntimeEvent = RuntimeEvent;
type ValidatorId = u64;
type ValidatorIdOf = pezsp_runtime::traits::ConvertInto;
type ShouldEndSession = pezpallet_session::PeriodicSessions<ConstU64<1>, ConstU64<0>>;
type NextSessionRotation = pezpallet_session::PeriodicSessions<ConstU64<1>, ConstU64<0>>;
type SessionManager = pezpallet_session::historical::NoteHistoricalRoot<Self, Staking>;
type SessionHandler = <TestSessionKeys as OpaqueKeys>::KeyTypeIdProviders;
type Keys = TestSessionKeys;
type DisablingStrategy = ();
type WeightInfo = ();
type Currency = Balances;
type KeyDeposit = ();
}
impl pezpallet_session::historical::Config for Test {
type RuntimeEvent = RuntimeEvent;
type FullIdentification = ();
type FullIdentificationOf = pezpallet_staking::UnitIdentificationOf<Self>;
}
impl pezpallet_authorship::Config for Test {
type FindAuthor = ();
type EventHandler = ();
}
type Balance = u128;
#[derive_impl(pezpallet_balances::config_preludes::TestDefaultConfig)]
impl pezpallet_balances::Config for Test {
type Balance = Balance;
type ExistentialDeposit = ConstU128<1>;
type AccountStore = System;
}
impl pezpallet_timestamp::Config for Test {
type Moment = u64;
type OnTimestampSet = ();
type MinimumPeriod = ConstU64<3>;
type WeightInfo = ();
}
pezpallet_staking_reward_curve::build! {
const REWARD_CURVE: PiecewiseLinear<'static> = curve!(
min_inflation: 0_025_000u64,
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 SessionsPerEra: SessionIndex = 3;
pub const BondingDuration: EraIndex = 3;
pub const RewardCurve: &'static PiecewiseLinear<'static> = &REWARD_CURVE;
pub static ElectionsBoundsOnChain: ElectionBounds = ElectionBoundsBuilder::default().build();
}
pub struct OnChainSeqPhragmen;
impl onchain::Config for OnChainSeqPhragmen {
type System = Test;
type Solver = SequentialPhragmen<u64, Perbill>;
type DataProvider = Staking;
type WeightInfo = ();
type MaxWinnersPerPage = ConstU32<100>;
type MaxBackersPerWinner = ConstU32<100>;
type Sort = ConstBool<true>;
type Bounds = ElectionsBoundsOnChain;
}
#[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 SessionsPerEra = SessionsPerEra;
type BondingDuration = BondingDuration;
type AdminOrigin = pezframe_system::EnsureRoot<Self::AccountId>;
type SessionInterface = Self;
type UnixTime = pezpallet_timestamp::Pallet<Test>;
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>;
type NominationsQuota = pezpallet_staking::FixedNominationsQuota<16>;
}
impl pezpallet_offences::Config for Test {
type RuntimeEvent = RuntimeEvent;
type IdentificationTuple = pezpallet_session::historical::IdentificationTuple<Self>;
type OnOffenceHandler = Staking;
}
parameter_types! {
pub const ReportLongevity: u64 =
BondingDuration::get() as u64 * SessionsPerEra::get() as u64 * Period::get();
pub const MaxSetIdSessionEntries: u32 = BondingDuration::get() * SessionsPerEra::get();
}
impl Config for Test {
type RuntimeEvent = RuntimeEvent;
type WeightInfo = ();
type MaxAuthorities = ConstU32<100>;
type MaxNominators = ConstU32<1000>;
type MaxSetIdSessionEntries = MaxSetIdSessionEntries;
type KeyOwnerProof = pezsp_session::MembershipProof;
type EquivocationReportSystem =
super::EquivocationReportSystem<Self, Offences, Historical, ReportLongevity>;
}
pub fn grandpa_log(log: ConsensusLog<u64>) -> DigestItem {
DigestItem::Consensus(GRANDPA_ENGINE_ID, log.encode())
}
pub fn to_authorities(vec: Vec<(u64, u64)>) -> AuthorityList {
vec.into_iter()
.map(|(id, weight)| (UintAuthorityId(id).to_public_key::<AuthorityId>(), weight))
.collect()
}
pub fn extract_keyring(id: &AuthorityId) -> Ed25519Keyring {
let mut raw_public = [0; 32];
raw_public.copy_from_slice(id.as_ref());
Ed25519Keyring::from_raw_public(raw_public).unwrap()
}
pub fn new_test_ext(vec: Vec<(u64, u64)>) -> pezsp_io::TestExternalities {
new_test_ext_raw_authorities(to_authorities(vec))
}
pub fn new_test_ext_raw_authorities(authorities: AuthorityList) -> pezsp_io::TestExternalities {
pezsp_tracing::try_init_simple();
let mut t = pezframe_system::GenesisConfig::<Test>::default().build_storage().unwrap();
let balances: Vec<_> = (0..authorities.len()).map(|i| (i as u64, 10_000_000)).collect();
pezpallet_balances::GenesisConfig::<Test> { balances, ..Default::default() }
.assimilate_storage(&mut t)
.unwrap();
// stashes are the index.
let session_keys: Vec<_> = authorities
.iter()
.enumerate()
.map(|(i, (k, _))| {
(
i as u64,
i as u64,
TestSessionKeys { grandpa_authority: AuthorityId::from(k.clone()) },
)
})
.collect();
// NOTE: this will initialize the grandpa authorities
// through OneSessionHandler::on_genesis_session
pezpallet_session::GenesisConfig::<Test> { keys: session_keys, ..Default::default() }
.assimilate_storage(&mut t)
.unwrap();
// controllers are the same as stash
let stakers: Vec<_> = (0..authorities.len())
.map(|i| (i as u64, i as u64, 10_000, pezpallet_staking::StakerStatus::<u64>::Validator))
.collect();
let staking_config = pezpallet_staking::GenesisConfig::<Test> {
stakers,
validator_count: 8,
force_era: pezpallet_staking::Forcing::ForceNew,
minimum_validator_count: 0,
invulnerables: vec![],
..Default::default()
};
staking_config.assimilate_storage(&mut t).unwrap();
t.into()
}
pub fn start_session(session_index: SessionIndex) {
for i in Session::current_index()..session_index {
System::on_finalize(System::block_number());
Session::on_finalize(System::block_number());
Staking::on_finalize(System::block_number());
Grandpa::on_finalize(System::block_number());
let parent_hash = if System::block_number() > 1 {
let hdr = System::finalize();
hdr.hash()
} else {
System::parent_hash()
};
System::reset_events();
System::initialize(&(i as u64 + 1), &parent_hash, &Default::default());
System::set_block_number((i + 1).into());
Timestamp::set_timestamp(System::block_number() * 6000);
System::on_initialize(System::block_number());
Session::on_initialize(System::block_number());
Staking::on_initialize(System::block_number());
Grandpa::on_initialize(System::block_number());
}
assert_eq!(Session::current_index(), session_index);
}
pub fn start_era(era_index: EraIndex) {
start_session((era_index * 3).into());
assert_eq!(pezpallet_staking::CurrentEra::<Test>::get(), Some(era_index));
}
pub fn initialize_block(number: u64, parent_hash: H256) {
System::reset_events();
System::initialize(&number, &parent_hash, &Default::default());
}
pub fn generate_equivocation_proof(
set_id: SetId,
vote1: (RoundNumber, H256, u64, &Ed25519Keyring),
vote2: (RoundNumber, H256, u64, &Ed25519Keyring),
) -> pezsp_consensus_grandpa::EquivocationProof<H256, u64> {
let signed_prevote = |round, hash, number, keyring: &Ed25519Keyring| {
let prevote = finality_grandpa::Prevote { target_hash: hash, target_number: number };
let prevote_msg = finality_grandpa::Message::Prevote(prevote.clone());
let payload = pezsp_consensus_grandpa::localized_payload(round, set_id, &prevote_msg);
let signed = keyring.sign(&payload).into();
(prevote, signed)
};
let (prevote1, signed1) = signed_prevote(vote1.0, vote1.1, vote1.2, vote1.3);
let (prevote2, signed2) = signed_prevote(vote2.0, vote2.1, vote2.2, vote2.3);
pezsp_consensus_grandpa::EquivocationProof::new(
set_id,
pezsp_consensus_grandpa::Equivocation::Prevote(finality_grandpa::Equivocation {
round_number: vote1.0,
identity: vote1.3.public().into(),
first: (prevote1, signed1),
second: (prevote2, signed2),
}),
)
}
+919
View File
@@ -0,0 +1,919 @@
// 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 module.
#![cfg(test)]
use super::{Call, Event, *};
use crate::mock::*;
use fg_primitives::ScheduledChange;
use pezframe_support::{
assert_err, assert_noop, assert_ok,
dispatch::{GetDispatchInfo, Pays},
traits::{Currency, KeyOwnerProofSystem, OnFinalize, OneSessionHandler},
};
use pezframe_system::{EventRecord, Phase};
use pezsp_core::H256;
use pezsp_keyring::Ed25519Keyring;
use pezsp_runtime::testing::Digest;
#[test]
fn authorities_change_logged() {
new_test_ext(vec![(1, 1), (2, 1), (3, 1)]).execute_with(|| {
initialize_block(1, Default::default());
Grandpa::schedule_change(to_authorities(vec![(4, 1), (5, 1), (6, 1)]), 0, None).unwrap();
System::note_finished_extrinsics();
Grandpa::on_finalize(1);
let header = System::finalize();
assert_eq!(
header.digest,
Digest {
logs: vec![grandpa_log(ConsensusLog::ScheduledChange(ScheduledChange {
delay: 0,
next_authorities: to_authorities(vec![(4, 1), (5, 1), (6, 1)])
})),],
}
);
assert_eq!(
System::events(),
vec![EventRecord {
phase: Phase::Finalization,
event: Event::NewAuthorities {
authority_set: to_authorities(vec![(4, 1), (5, 1), (6, 1)])
}
.into(),
topics: vec![],
},]
);
});
}
#[test]
fn authorities_change_logged_after_delay() {
new_test_ext(vec![(1, 1), (2, 1), (3, 1)]).execute_with(|| {
initialize_block(1, Default::default());
Grandpa::schedule_change(to_authorities(vec![(4, 1), (5, 1), (6, 1)]), 1, None).unwrap();
Grandpa::on_finalize(1);
let header = System::finalize();
assert_eq!(
header.digest,
Digest {
logs: vec![grandpa_log(ConsensusLog::ScheduledChange(ScheduledChange {
delay: 1,
next_authorities: to_authorities(vec![(4, 1), (5, 1), (6, 1)])
})),],
}
);
// no change at this height.
assert_eq!(System::events(), vec![]);
initialize_block(2, header.hash());
System::note_finished_extrinsics();
Grandpa::on_finalize(2);
let _header = System::finalize();
assert_eq!(
System::events(),
vec![EventRecord {
phase: Phase::Finalization,
event: Event::NewAuthorities {
authority_set: to_authorities(vec![(4, 1), (5, 1), (6, 1)])
}
.into(),
topics: vec![],
},]
);
});
}
#[test]
fn cannot_schedule_change_when_one_pending() {
new_test_ext(vec![(1, 1), (2, 1), (3, 1)]).execute_with(|| {
initialize_block(1, Default::default());
Grandpa::schedule_change(to_authorities(vec![(4, 1), (5, 1), (6, 1)]), 1, None).unwrap();
assert!(PendingChange::<Test>::exists());
assert_noop!(
Grandpa::schedule_change(to_authorities(vec![(5, 1)]), 1, None),
Error::<Test>::ChangePending
);
Grandpa::on_finalize(1);
let header = System::finalize();
initialize_block(2, header.hash());
assert!(PendingChange::<Test>::exists());
assert_noop!(
Grandpa::schedule_change(to_authorities(vec![(5, 1)]), 1, None),
Error::<Test>::ChangePending
);
Grandpa::on_finalize(2);
let header = System::finalize();
initialize_block(3, header.hash());
assert!(!PendingChange::<Test>::exists());
assert_ok!(Grandpa::schedule_change(to_authorities(vec![(5, 1)]), 1, None));
Grandpa::on_finalize(3);
let _header = System::finalize();
});
}
#[test]
fn dispatch_forced_change() {
new_test_ext(vec![(1, 1), (2, 1), (3, 1)]).execute_with(|| {
initialize_block(1, Default::default());
Grandpa::schedule_change(to_authorities(vec![(4, 1), (5, 1), (6, 1)]), 5, Some(0)).unwrap();
assert!(PendingChange::<Test>::exists());
assert_noop!(
Grandpa::schedule_change(to_authorities(vec![(5, 1)]), 1, Some(0)),
Error::<Test>::ChangePending
);
Grandpa::on_finalize(1);
let mut header = System::finalize();
for i in 2..7 {
initialize_block(i, header.hash());
assert!(PendingChange::<Test>::get().unwrap().forced.is_some());
assert_eq!(NextForced::<Test>::get(), Some(11));
assert_noop!(
Grandpa::schedule_change(to_authorities(vec![(5, 1)]), 1, None),
Error::<Test>::ChangePending
);
assert_noop!(
Grandpa::schedule_change(to_authorities(vec![(5, 1)]), 1, Some(0)),
Error::<Test>::ChangePending
);
Grandpa::on_finalize(i);
header = System::finalize();
}
// change has been applied at the end of block 6.
// add a normal change.
{
initialize_block(7, header.hash());
assert!(!PendingChange::<Test>::exists());
assert_eq!(
Grandpa::grandpa_authorities(),
to_authorities(vec![(4, 1), (5, 1), (6, 1)])
);
assert_ok!(Grandpa::schedule_change(to_authorities(vec![(5, 1)]), 1, None));
Grandpa::on_finalize(7);
header = System::finalize();
}
// run the normal change.
{
initialize_block(8, header.hash());
assert!(PendingChange::<Test>::exists());
assert_eq!(
Grandpa::grandpa_authorities(),
to_authorities(vec![(4, 1), (5, 1), (6, 1)])
);
assert_noop!(
Grandpa::schedule_change(to_authorities(vec![(5, 1)]), 1, None),
Error::<Test>::ChangePending
);
Grandpa::on_finalize(8);
header = System::finalize();
}
// normal change applied. but we can't apply a new forced change for some
// time.
for i in 9..11 {
initialize_block(i, header.hash());
assert!(!PendingChange::<Test>::exists());
assert_eq!(Grandpa::grandpa_authorities(), to_authorities(vec![(5, 1)]));
assert_eq!(NextForced::<Test>::get(), Some(11));
assert_noop!(
Grandpa::schedule_change(to_authorities(vec![(5, 1), (6, 1)]), 5, Some(0)),
Error::<Test>::TooSoon
);
Grandpa::on_finalize(i);
header = System::finalize();
}
{
initialize_block(11, header.hash());
assert!(!PendingChange::<Test>::exists());
assert_ok!(Grandpa::schedule_change(
to_authorities(vec![(5, 1), (6, 1), (7, 1)]),
5,
Some(0)
));
assert_eq!(NextForced::<Test>::get(), Some(21));
Grandpa::on_finalize(11);
header = System::finalize();
}
let _ = header;
});
}
#[test]
fn schedule_pause_only_when_live() {
new_test_ext(vec![(1, 1), (2, 1), (3, 1)]).execute_with(|| {
// we schedule a pause at block 1 with delay of 1
initialize_block(1, Default::default());
Grandpa::schedule_pause(1).unwrap();
// we've switched to the pending pause state
assert_eq!(
State::<Test>::get(),
StoredState::PendingPause { scheduled_at: 1u64, delay: 1 }
);
Grandpa::on_finalize(1);
let _ = System::finalize();
initialize_block(2, Default::default());
// signaling a pause now should fail
assert_noop!(Grandpa::schedule_pause(1), Error::<Test>::PauseFailed);
Grandpa::on_finalize(2);
let _ = System::finalize();
// after finalizing block 2 the set should have switched to paused state
assert_eq!(State::<Test>::get(), StoredState::Paused);
});
}
#[test]
fn schedule_resume_only_when_paused() {
new_test_ext(vec![(1, 1), (2, 1), (3, 1)]).execute_with(|| {
initialize_block(1, Default::default());
// the set is currently live, resuming it is an error
assert_noop!(Grandpa::schedule_resume(1), Error::<Test>::ResumeFailed);
assert_eq!(State::<Test>::get(), StoredState::Live);
// we schedule a pause to be applied instantly
Grandpa::schedule_pause(0).unwrap();
Grandpa::on_finalize(1);
let _ = System::finalize();
assert_eq!(State::<Test>::get(), StoredState::Paused);
// we schedule the set to go back live in 2 blocks
initialize_block(2, Default::default());
Grandpa::schedule_resume(2).unwrap();
Grandpa::on_finalize(2);
let _ = System::finalize();
initialize_block(3, Default::default());
Grandpa::on_finalize(3);
let _ = System::finalize();
initialize_block(4, Default::default());
Grandpa::on_finalize(4);
let _ = System::finalize();
// it should be live at block 4
assert_eq!(State::<Test>::get(), StoredState::Live);
});
}
#[test]
fn time_slot_have_sane_ord() {
// Ensure that `Ord` implementation is sane.
const FIXTURE: &[TimeSlot] = &[
TimeSlot { set_id: 0, round: 0 },
TimeSlot { set_id: 0, round: 1 },
TimeSlot { set_id: 1, round: 0 },
TimeSlot { set_id: 1, round: 1 },
TimeSlot { set_id: 1, round: 2 },
];
assert!(FIXTURE.windows(2).all(|f| f[0] < f[1]));
}
/// Returns a list with 3 authorities with known keys:
/// Alice, Bob and Charlie.
pub fn test_authorities() -> AuthorityList {
let authorities = vec![Ed25519Keyring::Alice, Ed25519Keyring::Bob, Ed25519Keyring::Charlie];
authorities.into_iter().map(|id| (id.public().into(), 1u64)).collect()
}
#[test]
fn report_equivocation_current_set_works() {
let authorities = test_authorities();
new_test_ext_raw_authorities(authorities).execute_with(|| {
assert_eq!(pezpallet_staking::CurrentEra::<Test>::get(), Some(0));
assert_eq!(Session::current_index(), 0);
start_era(1);
let authorities = Grandpa::grandpa_authorities();
let validators = Session::validators();
// make sure that all validators have the same balance
for validator in &validators {
assert_eq!(Balances::total_balance(validator), 10_000_000);
assert_eq!(Staking::slashable_balance_of(validator), 10_000);
assert_eq!(
Staking::eras_stakers(1, &validator),
pezpallet_staking::Exposure { total: 10_000, own: 10_000, others: vec![] },
);
}
let equivocation_authority_index = 0;
let equivocation_key = &authorities[equivocation_authority_index].0;
let equivocation_keyring = extract_keyring(equivocation_key);
let set_id = CurrentSetId::<Test>::get();
// generate an equivocation proof, with two votes in the same round for
// different block hashes signed by the same key
let equivocation_proof = generate_equivocation_proof(
set_id,
(1, H256::random(), 10, &equivocation_keyring),
(1, H256::random(), 10, &equivocation_keyring),
);
// create the key ownership proof
let key_owner_proof =
Historical::prove((pezsp_consensus_grandpa::KEY_TYPE, &equivocation_key)).unwrap();
// report the equivocation and the tx should be dispatched successfully
assert_ok!(Grandpa::report_equivocation_unsigned(
RuntimeOrigin::none(),
Box::new(equivocation_proof),
key_owner_proof,
),);
start_era(2);
// check that the balance of 0-th validator is slashed 100%.
let equivocation_validator_id = validators[equivocation_authority_index];
assert_eq!(Balances::total_balance(&equivocation_validator_id), 10_000_000 - 10_000);
assert_eq!(Staking::slashable_balance_of(&equivocation_validator_id), 0);
assert_eq!(
Staking::eras_stakers(2, &equivocation_validator_id),
pezpallet_staking::Exposure { total: 0, own: 0, others: vec![] },
);
// check that the balances of all other validators are left intact.
for validator in &validators {
if *validator == equivocation_validator_id {
continue;
}
assert_eq!(Balances::total_balance(validator), 10_000_000);
assert_eq!(Staking::slashable_balance_of(validator), 10_000);
assert_eq!(
Staking::eras_stakers(2, &validator),
pezpallet_staking::Exposure { total: 10_000, own: 10_000, others: vec![] },
);
}
});
}
#[test]
fn report_equivocation_old_set_works() {
let authorities = test_authorities();
new_test_ext_raw_authorities(authorities).execute_with(|| {
start_era(1);
let authorities = Grandpa::grandpa_authorities();
let validators = Session::validators();
let equivocation_authority_index = 0;
let equivocation_key = &authorities[equivocation_authority_index].0;
// create the key ownership proof in the "old" set
let key_owner_proof =
Historical::prove((pezsp_consensus_grandpa::KEY_TYPE, &equivocation_key)).unwrap();
start_era(2);
// make sure that all authorities have the same balance
for validator in &validators {
assert_eq!(Balances::total_balance(validator), 10_000_000);
assert_eq!(Staking::slashable_balance_of(validator), 10_000);
assert_eq!(
Staking::eras_stakers(2, &validator),
pezpallet_staking::Exposure { total: 10_000, own: 10_000, others: vec![] },
);
}
let equivocation_keyring = extract_keyring(equivocation_key);
let set_id = CurrentSetId::<Test>::get();
// generate an equivocation proof for the old set,
let equivocation_proof = generate_equivocation_proof(
set_id - 1,
(1, H256::random(), 10, &equivocation_keyring),
(1, H256::random(), 10, &equivocation_keyring),
);
// report the equivocation using the key ownership proof generated on
// the old set, the tx should be dispatched successfully
assert_ok!(Grandpa::report_equivocation_unsigned(
RuntimeOrigin::none(),
Box::new(equivocation_proof),
key_owner_proof,
),);
start_era(3);
// check that the balance of 0-th validator is slashed 100%.
let equivocation_validator_id = validators[equivocation_authority_index];
assert_eq!(Balances::total_balance(&equivocation_validator_id), 10_000_000 - 10_000);
assert_eq!(Staking::slashable_balance_of(&equivocation_validator_id), 0);
assert_eq!(
Staking::eras_stakers(3, &equivocation_validator_id),
pezpallet_staking::Exposure { total: 0, own: 0, others: vec![] },
);
// check that the balances of all other validators are left intact.
for validator in &validators {
if *validator == equivocation_validator_id {
continue;
}
assert_eq!(Balances::total_balance(validator), 10_000_000);
assert_eq!(Staking::slashable_balance_of(validator), 10_000);
assert_eq!(
Staking::eras_stakers(3, &validator),
pezpallet_staking::Exposure { total: 10_000, own: 10_000, others: vec![] },
);
}
});
}
#[test]
fn report_equivocation_invalid_set_id() {
let authorities = test_authorities();
new_test_ext_raw_authorities(authorities).execute_with(|| {
start_era(1);
let authorities = Grandpa::grandpa_authorities();
let equivocation_authority_index = 0;
let equivocation_key = &authorities[equivocation_authority_index].0;
let equivocation_keyring = extract_keyring(equivocation_key);
let key_owner_proof =
Historical::prove((pezsp_consensus_grandpa::KEY_TYPE, &equivocation_key)).unwrap();
let set_id = CurrentSetId::<Test>::get();
// generate an equivocation for a future set
let equivocation_proof = generate_equivocation_proof(
set_id + 1,
(1, H256::random(), 10, &equivocation_keyring),
(1, H256::random(), 10, &equivocation_keyring),
);
// the call for reporting the equivocation should error
assert_err!(
Grandpa::report_equivocation_unsigned(
RuntimeOrigin::none(),
Box::new(equivocation_proof),
key_owner_proof,
),
Error::<Test>::InvalidEquivocationProof,
);
});
}
#[test]
fn report_equivocation_invalid_session() {
let authorities = test_authorities();
new_test_ext_raw_authorities(authorities).execute_with(|| {
start_era(1);
let authorities = Grandpa::grandpa_authorities();
let equivocation_authority_index = 0;
let equivocation_key = &authorities[equivocation_authority_index].0;
let equivocation_keyring = extract_keyring(equivocation_key);
// generate a key ownership proof at set id = 1
let key_owner_proof =
Historical::prove((pezsp_consensus_grandpa::KEY_TYPE, &equivocation_key)).unwrap();
start_era(2);
let set_id = CurrentSetId::<Test>::get();
// generate an equivocation proof at set id = 2
let equivocation_proof = generate_equivocation_proof(
set_id,
(1, H256::random(), 10, &equivocation_keyring),
(1, H256::random(), 10, &equivocation_keyring),
);
// report an equivocation for the current set using an key ownership
// proof from the previous set, the session should be invalid.
assert_err!(
Grandpa::report_equivocation_unsigned(
RuntimeOrigin::none(),
Box::new(equivocation_proof),
key_owner_proof,
),
Error::<Test>::InvalidEquivocationProof,
);
});
}
#[test]
fn report_equivocation_invalid_key_owner_proof() {
let authorities = test_authorities();
new_test_ext_raw_authorities(authorities).execute_with(|| {
start_era(1);
let authorities = Grandpa::grandpa_authorities();
let invalid_owner_authority_index = 1;
let invalid_owner_key = &authorities[invalid_owner_authority_index].0;
// generate a key ownership proof for the authority at index 1
let invalid_key_owner_proof =
Historical::prove((pezsp_consensus_grandpa::KEY_TYPE, &invalid_owner_key)).unwrap();
let equivocation_authority_index = 0;
let equivocation_key = &authorities[equivocation_authority_index].0;
let equivocation_keyring = extract_keyring(equivocation_key);
let set_id = CurrentSetId::<Test>::get();
// generate an equivocation proof for the authority at index 0
let equivocation_proof = generate_equivocation_proof(
set_id,
(1, H256::random(), 10, &equivocation_keyring),
(1, H256::random(), 10, &equivocation_keyring),
);
// we need to start a new era otherwise the key ownership proof won't be
// checked since the authorities are part of the current session
start_era(2);
// report an equivocation for the current set using a key ownership
// proof for a different key than the one in the equivocation proof.
assert_err!(
Grandpa::report_equivocation_unsigned(
RuntimeOrigin::none(),
Box::new(equivocation_proof),
invalid_key_owner_proof,
),
Error::<Test>::InvalidKeyOwnershipProof,
);
});
}
#[test]
fn report_equivocation_invalid_equivocation_proof() {
let authorities = test_authorities();
new_test_ext_raw_authorities(authorities).execute_with(|| {
start_era(1);
let authorities = Grandpa::grandpa_authorities();
let equivocation_authority_index = 0;
let equivocation_key = &authorities[equivocation_authority_index].0;
let equivocation_keyring = extract_keyring(equivocation_key);
// generate a key ownership proof at set id = 1
let key_owner_proof =
Historical::prove((pezsp_consensus_grandpa::KEY_TYPE, &equivocation_key)).unwrap();
let set_id = CurrentSetId::<Test>::get();
let assert_invalid_equivocation_proof = |equivocation_proof| {
assert_err!(
Grandpa::report_equivocation_unsigned(
RuntimeOrigin::none(),
Box::new(equivocation_proof),
key_owner_proof.clone(),
),
Error::<Test>::InvalidEquivocationProof,
);
};
start_era(2);
// both votes target the same block number and hash,
// there is no equivocation.
assert_invalid_equivocation_proof(generate_equivocation_proof(
set_id,
(1, H256::zero(), 10, &equivocation_keyring),
(1, H256::zero(), 10, &equivocation_keyring),
));
// votes targeting different rounds, there is no equivocation.
assert_invalid_equivocation_proof(generate_equivocation_proof(
set_id,
(1, H256::random(), 10, &equivocation_keyring),
(2, H256::random(), 10, &equivocation_keyring),
));
// votes signed with different authority keys
assert_invalid_equivocation_proof(generate_equivocation_proof(
set_id,
(1, H256::random(), 10, &equivocation_keyring),
(1, H256::random(), 10, &Ed25519Keyring::Charlie),
));
// votes signed with a key that isn't part of the authority set
assert_invalid_equivocation_proof(generate_equivocation_proof(
set_id,
(1, H256::random(), 10, &equivocation_keyring),
(1, H256::random(), 10, &Ed25519Keyring::Dave),
));
});
}
#[test]
fn report_equivocation_validate_unsigned_prevents_duplicates() {
use pezsp_runtime::transaction_validity::{
InvalidTransaction, TransactionPriority, TransactionSource, TransactionValidity,
ValidTransaction,
};
let authorities = test_authorities();
new_test_ext_raw_authorities(authorities).execute_with(|| {
start_era(1);
let authorities = Grandpa::grandpa_authorities();
// generate and report an equivocation for the validator at index 0
let equivocation_authority_index = 0;
let equivocation_key = &authorities[equivocation_authority_index].0;
let equivocation_keyring = extract_keyring(equivocation_key);
let set_id = CurrentSetId::<Test>::get();
let equivocation_proof = generate_equivocation_proof(
set_id,
(1, H256::random(), 10, &equivocation_keyring),
(1, H256::random(), 10, &equivocation_keyring),
);
let key_owner_proof =
Historical::prove((pezsp_consensus_grandpa::KEY_TYPE, &equivocation_key)).unwrap();
let call = Call::report_equivocation_unsigned {
equivocation_proof: Box::new(equivocation_proof.clone()),
key_owner_proof: key_owner_proof.clone(),
};
// only local/inblock reports are allowed
assert_eq!(
<Grandpa as pezsp_runtime::traits::ValidateUnsigned>::validate_unsigned(
TransactionSource::External,
&call,
),
InvalidTransaction::Call.into(),
);
// the transaction is valid when passed as local
let tx_tag = (equivocation_key, set_id, 1u64);
assert_eq!(
<Grandpa as pezsp_runtime::traits::ValidateUnsigned>::validate_unsigned(
TransactionSource::Local,
&call,
),
TransactionValidity::Ok(ValidTransaction {
priority: TransactionPriority::max_value(),
requires: vec![],
provides: vec![("GrandpaEquivocation", tx_tag).encode()],
longevity: ReportLongevity::get(),
propagate: false,
})
);
// the pre dispatch checks should also pass
assert_ok!(<Grandpa as pezsp_runtime::traits::ValidateUnsigned>::pre_dispatch(&call));
// we submit the report
Grandpa::report_equivocation_unsigned(
RuntimeOrigin::none(),
Box::new(equivocation_proof),
key_owner_proof,
)
.unwrap();
// the report should now be considered stale and the transaction is invalid
// the check for staleness should be done on both `validate_unsigned` and on `pre_dispatch`
assert_err!(
<Grandpa as pezsp_runtime::traits::ValidateUnsigned>::validate_unsigned(
TransactionSource::Local,
&call,
),
InvalidTransaction::Stale,
);
assert_err!(
<Grandpa as pezsp_runtime::traits::ValidateUnsigned>::pre_dispatch(&call),
InvalidTransaction::Stale,
);
});
}
#[test]
fn on_new_session_doesnt_start_new_set_if_schedule_change_failed() {
new_test_ext(vec![(1, 1), (2, 1), (3, 1)]).execute_with(|| {
assert_eq!(CurrentSetId::<Test>::get(), 0);
// starting a new era should lead to a change in the session
// validators and trigger a new set
start_era(1);
assert_eq!(CurrentSetId::<Test>::get(), 1);
// we schedule a change delayed by 2 blocks, this should make it so that
// when we try to rotate the session at the beginning of the era we will
// fail to schedule a change (there's already one pending), so we should
// not increment the set id.
Grandpa::schedule_change(to_authorities(vec![(1, 1)]), 2, None).unwrap();
start_era(2);
assert_eq!(CurrentSetId::<Test>::get(), 1);
// everything should go back to normal after.
start_era(3);
assert_eq!(CurrentSetId::<Test>::get(), 2);
// session rotation might also fail to schedule a change if it's for a
// forced change (i.e. grandpa is stalled) and it is too soon.
NextForced::<Test>::put(1000);
Stalled::<Test>::put((30, 1));
// NOTE: we cannot go through normal era rotation since having `Stalled`
// defined will also trigger a new set (regardless of whether the
// session validators changed)
Grandpa::on_new_session(true, std::iter::empty(), std::iter::empty());
assert_eq!(CurrentSetId::<Test>::get(), 2);
});
}
#[test]
fn cleans_up_old_set_id_session_mappings() {
new_test_ext(vec![(1, 1), (2, 1), (3, 1)]).execute_with(|| {
let max_set_id_session_entries = MaxSetIdSessionEntries::get();
start_era(max_set_id_session_entries);
// we should have a session id mapping for all the set ids from
// `max_set_id_session_entries` eras we have observed
for i in 1..=max_set_id_session_entries {
assert!(SetIdSession::<Test>::get(i as u64).is_some());
}
start_era(max_set_id_session_entries * 2);
// we should keep tracking the new mappings for new eras
for i in max_set_id_session_entries + 1..=max_set_id_session_entries * 2 {
assert!(SetIdSession::<Test>::get(i as u64).is_some());
}
// but the old ones should have been pruned by now
for i in 1..=max_set_id_session_entries {
assert!(SetIdSession::<Test>::get(i as u64).is_none());
}
});
}
#[test]
fn always_schedules_a_change_on_new_session_when_stalled() {
new_test_ext(vec![(1, 1), (2, 1), (3, 1)]).execute_with(|| {
start_era(1);
assert!(PendingChange::<Test>::get().is_none());
assert_eq!(CurrentSetId::<Test>::get(), 1);
// if the session handler reports no change then we should not schedule
// any pending change
Grandpa::on_new_session(false, std::iter::empty(), std::iter::empty());
assert!(PendingChange::<Test>::get().is_none());
assert_eq!(CurrentSetId::<Test>::get(), 1);
// if grandpa is stalled then we should **always** schedule a forced
// change on a new session
Stalled::<Test>::put((10, 1));
Grandpa::on_new_session(false, std::iter::empty(), std::iter::empty());
assert!(PendingChange::<Test>::get().is_some());
assert!(PendingChange::<Test>::get().unwrap().forced.is_some());
assert_eq!(CurrentSetId::<Test>::get(), 2);
});
}
#[test]
fn report_equivocation_has_valid_weight() {
// the weight depends on the size of the validator set,
// but there's a lower bound of 100 validators.
assert!((1..=100)
.map(|validators| <Test as Config>::WeightInfo::report_equivocation(validators, 1000))
.collect::<Vec<_>>()
.windows(2)
.all(|w| w[0] == w[1]));
// after 100 validators the weight should keep increasing
// with every extra validator.
assert!((100..=1000)
.map(|validators| <Test as Config>::WeightInfo::report_equivocation(validators, 1000))
.collect::<Vec<_>>()
.windows(2)
.all(|w| w[0].ref_time() < w[1].ref_time()));
}
#[test]
fn valid_equivocation_reports_dont_pay_fees() {
let authorities = test_authorities();
new_test_ext_raw_authorities(authorities).execute_with(|| {
start_era(1);
let equivocation_key = &Grandpa::grandpa_authorities()[0].0;
let equivocation_keyring = extract_keyring(equivocation_key);
let set_id = CurrentSetId::<Test>::get();
// generate an equivocation proof.
let equivocation_proof = generate_equivocation_proof(
set_id,
(1, H256::random(), 10, &equivocation_keyring),
(1, H256::random(), 10, &equivocation_keyring),
);
// create the key ownership proof.
let key_owner_proof =
Historical::prove((pezsp_consensus_grandpa::KEY_TYPE, &equivocation_key)).unwrap();
// check the dispatch info for the call.
let info = Call::<Test>::report_equivocation_unsigned {
equivocation_proof: Box::new(equivocation_proof.clone()),
key_owner_proof: key_owner_proof.clone(),
}
.get_dispatch_info();
// it should have non-zero weight and the fee has to be paid.
assert!(info.call_weight.any_gt(Weight::zero()));
assert_eq!(info.pays_fee, Pays::Yes);
// report the equivocation.
let post_info = Grandpa::report_equivocation_unsigned(
RuntimeOrigin::none(),
Box::new(equivocation_proof.clone()),
key_owner_proof.clone(),
)
.unwrap();
// the original weight should be kept, but given that the report
// is valid the fee is waived.
assert!(post_info.actual_weight.is_none());
assert_eq!(post_info.pays_fee, Pays::No);
// report the equivocation again which is invalid now since it is
// duplicate.
let post_info = Grandpa::report_equivocation_unsigned(
RuntimeOrigin::none(),
Box::new(equivocation_proof),
key_owner_proof,
)
.err()
.unwrap()
.post_info;
// the fee is not waived and the original weight is kept.
assert!(post_info.actual_weight.is_none());
assert_eq!(post_info.pays_fee, Pays::Yes);
})
}
+126
View File
@@ -0,0 +1,126 @@
// 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_grandpa`
//!
//! 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_grandpa
// --header=/__w/pezkuwi-sdk/pezkuwi-sdk/bizinikiwi/HEADER-APACHE2
// --output=/__w/pezkuwi-sdk/pezkuwi-sdk/bizinikiwi/pezframe/grandpa/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_grandpa`.
pub trait WeightInfo {
fn check_equivocation_proof(x: u32, ) -> Weight;
fn note_stalled() -> Weight;
}
/// Weights for `pezpallet_grandpa` using the Bizinikiwi node and recommended hardware.
pub struct BizinikiwiWeight<T>(PhantomData<T>);
impl<T: pezframe_system::Config> WeightInfo for BizinikiwiWeight<T> {
/// The range of component `x` is `[0, 1]`.
fn check_equivocation_proof(x: u32, ) -> Weight {
// Proof Size summary in bytes:
// Measured: `0`
// Estimated: `0`
// Minimum execution time: 77_943_000 picoseconds.
Weight::from_parts(78_252_373, 0)
// Standard Error: 17_672
.saturating_add(Weight::from_parts(29_726, 0).saturating_mul(x.into()))
}
/// Storage: `Grandpa::Stalled` (r:0 w:1)
/// Proof: `Grandpa::Stalled` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `MaxEncodedLen`)
fn note_stalled() -> Weight {
// Proof Size summary in bytes:
// Measured: `0`
// Estimated: `0`
// Minimum execution time: 2_006_000 picoseconds.
Weight::from_parts(2_117_000, 0)
.saturating_add(T::DbWeight::get().writes(1_u64))
}
}
// For backwards compatibility and tests.
impl WeightInfo for () {
/// The range of component `x` is `[0, 1]`.
fn check_equivocation_proof(x: u32, ) -> Weight {
// Proof Size summary in bytes:
// Measured: `0`
// Estimated: `0`
// Minimum execution time: 77_943_000 picoseconds.
Weight::from_parts(78_252_373, 0)
// Standard Error: 17_672
.saturating_add(Weight::from_parts(29_726, 0).saturating_mul(x.into()))
}
/// Storage: `Grandpa::Stalled` (r:0 w:1)
/// Proof: `Grandpa::Stalled` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `MaxEncodedLen`)
fn note_stalled() -> Weight {
// Proof Size summary in bytes:
// Measured: `0`
// Estimated: `0`
// Minimum execution time: 2_006_000 picoseconds.
Weight::from_parts(2_117_000, 0)
.saturating_add(RocksDbWeight::get().writes(1_u64))
}
}