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
+94
View File
@@ -0,0 +1,94 @@
[package]
name = "pezpallet-beefy"
version = "28.0.0"
authors.workspace = true
edition.workspace = true
license = "Apache-2.0"
repository.workspace = true
description = "BEEFY FRAME pallet"
homepage.workspace = true
[lints]
workspace = true
[dependencies]
codec = { features = ["derive"], 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 }
serde = { optional = true, workspace = true, default-features = true }
pezsp-consensus-beefy = { features = ["serde"], workspace = true }
pezsp-runtime = { features = ["serde"], workspace = true }
pezsp-session = { workspace = true }
pezsp-staking = { features = ["serde"], workspace = true }
[dev-dependencies]
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-core = { workspace = true, default-features = true }
pezsp-io = { workspace = true, default-features = true }
pezsp-staking = { workspace = true, default-features = true }
pezsp-state-machine = { workspace = true }
pezsp-tracing = { workspace = true, default-features = true }
[features]
default = ["std"]
std = [
"codec/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",
"serde/std",
"pezsp-consensus-beefy/std",
"pezsp-core/std",
"pezsp-io/std",
"pezsp-runtime/std",
"pezsp-session/std",
"pezsp-staking/std",
"pezsp-state-machine/std",
]
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",
]
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-beefy/runtime-benchmarks",
"pezsp-io/runtime-benchmarks",
"pezsp-runtime/runtime-benchmarks",
"pezsp-session/runtime-benchmarks",
"pezsp-staking/runtime-benchmarks",
"pezsp-state-machine/runtime-benchmarks",
]
@@ -0,0 +1,63 @@
// 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 BEEFY 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_voting_equivocation(
votes_count: u32,
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(
(50u64 * WEIGHT_REF_TIME_PER_MICROS).saturating_mul(votes_count as u64),
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 set_new_genesis() -> Weight {
DbWeight::get().writes(1)
}
}
@@ -0,0 +1,403 @@
// 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 BEEFY 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 BEEFY protocol).
//! And in a runtime context, so that the BEEFY pallet 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 BEEFY pallet is used in the runtime
//! definition.
use alloc::{vec, vec::Vec};
use codec::{self as codec, Decode, Encode};
use pezframe_support::traits::{Get, KeyOwnerProofSystem};
use pezframe_system::pezpallet_prelude::{BlockNumberFor, HeaderFor};
use log::{error, info};
use pezsp_consensus_beefy::{
check_commitment_signature, AncestryHelper, DoubleVotingProof, ForkVotingProof,
FutureBlockVotingProof, ValidatorSetId, KEY_TYPE as BEEFY_KEY_TYPE,
};
use pezsp_runtime::{
transaction_validity::{
InvalidTransaction, TransactionPriority, TransactionSource, TransactionValidity,
TransactionValidityError, ValidTransaction,
},
DispatchError, KeyTypeId, Perbill, RuntimeAppPublic,
};
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<N: Copy + Clone + PartialOrd + Ord + Eq + PartialEq + Encode + Decode> {
// The order of these matters for `derive(Ord)`.
/// BEEFY Set ID.
pub set_id: ValidatorSetId,
/// Round number.
pub round: N,
}
/// BEEFY equivocation offence report.
pub struct EquivocationOffence<Offender, N>
where
N: Copy + Clone + PartialOrd + Ord + Eq + PartialEq + Encode + Decode,
{
/// Time slot at which this incident happened.
pub time_slot: TimeSlot<N>,
/// 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,
/// Optional slash fraction
maybe_slash_fraction: Option<Perbill>,
}
impl<Offender: Clone, N> Offence<Offender> for EquivocationOffence<Offender, N>
where
N: Copy + Clone + PartialOrd + Ord + Eq + PartialEq + Encode + Decode,
{
const ID: Kind = *b"beefy:equivocati";
type TimeSlot = TimeSlot<N>;
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
}
fn slash_fraction(&self, offenders_count: u32) -> Perbill {
if let Some(slash_fraction) = self.maybe_slash_fraction {
return slash_fraction;
}
// `Perbill` type domain is [0, 1] by definition
// The formula is min((3k / n)^2, 1)
// where k = offenders_number and n = validators_number
Perbill::from_rational(3 * offenders_count, self.validator_set_count).square()
}
}
/// BEEFY 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 authorship pallet.
pub struct EquivocationReportSystem<T, R, P, L>(core::marker::PhantomData<(T, R, P, L)>);
/// Equivocation evidence convenience alias.
pub enum EquivocationEvidenceFor<T: Config> {
DoubleVotingProof(
DoubleVotingProof<
BlockNumberFor<T>,
T::BeefyId,
<T::BeefyId as RuntimeAppPublic>::Signature,
>,
T::KeyOwnerProof,
),
ForkVotingProof(
ForkVotingProof<
HeaderFor<T>,
T::BeefyId,
<T::AncestryHelper as AncestryHelper<HeaderFor<T>>>::Proof,
>,
T::KeyOwnerProof,
),
FutureBlockVotingProof(FutureBlockVotingProof<BlockNumberFor<T>, T::BeefyId>, T::KeyOwnerProof),
}
impl<T: Config> EquivocationEvidenceFor<T> {
/// Returns the authority id of the equivocator.
fn offender_id(&self) -> &T::BeefyId {
match self {
EquivocationEvidenceFor::DoubleVotingProof(equivocation_proof, _) =>
equivocation_proof.offender_id(),
EquivocationEvidenceFor::ForkVotingProof(equivocation_proof, _) =>
&equivocation_proof.vote.id,
EquivocationEvidenceFor::FutureBlockVotingProof(equivocation_proof, _) =>
&equivocation_proof.vote.id,
}
}
/// Returns the round number at which the equivocation occurred.
fn round_number(&self) -> &BlockNumberFor<T> {
match self {
EquivocationEvidenceFor::DoubleVotingProof(equivocation_proof, _) =>
equivocation_proof.round_number(),
EquivocationEvidenceFor::ForkVotingProof(equivocation_proof, _) =>
&equivocation_proof.vote.commitment.block_number,
EquivocationEvidenceFor::FutureBlockVotingProof(equivocation_proof, _) =>
&equivocation_proof.vote.commitment.block_number,
}
}
/// Returns the set id at which the equivocation occurred.
fn set_id(&self) -> ValidatorSetId {
match self {
EquivocationEvidenceFor::DoubleVotingProof(equivocation_proof, _) =>
equivocation_proof.set_id(),
EquivocationEvidenceFor::ForkVotingProof(equivocation_proof, _) =>
equivocation_proof.vote.commitment.validator_set_id,
EquivocationEvidenceFor::FutureBlockVotingProof(equivocation_proof, _) =>
equivocation_proof.vote.commitment.validator_set_id,
}
}
/// Returns the set id at which the equivocation occurred.
fn key_owner_proof(&self) -> &T::KeyOwnerProof {
match self {
EquivocationEvidenceFor::DoubleVotingProof(_, key_owner_proof) => key_owner_proof,
EquivocationEvidenceFor::ForkVotingProof(_, key_owner_proof) => key_owner_proof,
EquivocationEvidenceFor::FutureBlockVotingProof(_, key_owner_proof) => key_owner_proof,
}
}
fn checked_offender<P>(&self) -> Option<P::IdentificationTuple>
where
P: KeyOwnerProofSystem<(KeyTypeId, T::BeefyId), Proof = T::KeyOwnerProof>,
{
let key = (BEEFY_KEY_TYPE, self.offender_id().clone());
P::check_proof(key, self.key_owner_proof().clone())
}
fn check_equivocation_proof(self) -> Result<(), Error<T>> {
match self {
EquivocationEvidenceFor::DoubleVotingProof(equivocation_proof, _) => {
// Validate equivocation proof (check votes are different and signatures are valid).
if !pezsp_consensus_beefy::check_double_voting_proof(&equivocation_proof) {
return Err(Error::<T>::InvalidDoubleVotingProof);
}
Ok(())
},
EquivocationEvidenceFor::ForkVotingProof(equivocation_proof, _) => {
let ForkVotingProof { vote, ancestry_proof, header } = equivocation_proof;
if !<T::AncestryHelper as AncestryHelper<HeaderFor<T>>>::is_proof_optimal(
&ancestry_proof,
) {
return Err(Error::<T>::InvalidForkVotingProof);
}
let maybe_validation_context = <T::AncestryHelper as AncestryHelper<
HeaderFor<T>,
>>::extract_validation_context(header);
let validation_context = match maybe_validation_context {
Some(validation_context) => validation_context,
None => {
return Err(Error::<T>::InvalidForkVotingProof);
},
};
let is_non_canonical =
<T::AncestryHelper as AncestryHelper<HeaderFor<T>>>::is_non_canonical(
&vote.commitment,
ancestry_proof,
validation_context,
);
if !is_non_canonical {
return Err(Error::<T>::InvalidForkVotingProof);
}
let is_signature_valid =
check_commitment_signature(&vote.commitment, &vote.id, &vote.signature);
if !is_signature_valid {
return Err(Error::<T>::InvalidForkVotingProof);
}
Ok(())
},
EquivocationEvidenceFor::FutureBlockVotingProof(equivocation_proof, _) => {
let FutureBlockVotingProof { vote } = equivocation_proof;
// Check if the commitment actually targets a future block
if vote.commitment.block_number < pezframe_system::Pallet::<T>::block_number() {
return Err(Error::<T>::InvalidFutureBlockVotingProof);
}
let is_signature_valid =
check_commitment_signature(&vote.commitment, &vote.id, &vote.signature);
if !is_signature_valid {
return Err(Error::<T>::InvalidForkVotingProof);
}
Ok(())
},
}
}
fn slash_fraction(&self) -> Option<Perbill> {
match self {
EquivocationEvidenceFor::DoubleVotingProof(_, _) => None,
EquivocationEvidenceFor::ForkVotingProof(_, _) |
EquivocationEvidenceFor::FutureBlockVotingProof(_, _) => Some(Perbill::from_percent(50)),
}
}
}
impl<T, R, P, L> OffenceReportSystem<Option<T::AccountId>, EquivocationEvidenceFor<T>>
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, BlockNumberFor<T>>,
>,
P: KeyOwnerProofSystem<(KeyTypeId, T::BeefyId), Proof = T::KeyOwnerProof>,
P::IdentificationTuple: Clone,
L: Get<u64>,
{
type Longevity = L;
fn publish_evidence(evidence: EquivocationEvidenceFor<T>) -> Result<(), ()> {
use pezframe_system::offchain::SubmitTransaction;
let call: Call<T> = evidence.into();
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: EquivocationEvidenceFor<T>,
) -> Result<(), TransactionValidityError> {
let offender = evidence.checked_offender::<P>().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: evidence.set_id(), round: *evidence.round_number() };
if R::is_known_offence(&[offender], &time_slot) {
Err(InvalidTransaction::Stale.into())
} else {
Ok(())
}
}
fn process_evidence(
reporter: Option<T::AccountId>,
evidence: EquivocationEvidenceFor<T>,
) -> Result<(), DispatchError> {
let maybe_slash_fraction = evidence.slash_fraction();
let reporter = reporter.or_else(|| pezpallet_authorship::Pallet::<T>::author());
// We check the equivocation within the context of its set id (and associated session).
let set_id = evidence.set_id();
let round = *evidence.round_number();
let set_id_session_index = crate::SetIdSession::<T>::get(set_id)
.ok_or(Error::<T>::InvalidEquivocationProofSessionMember)?;
// Check that the session id for the membership proof is within the bounds
// of the set id reported in the equivocation.
let key_owner_proof = evidence.key_owner_proof();
let validator_count = key_owner_proof.validator_count();
let session_index = key_owner_proof.session();
if session_index != set_id_session_index {
return Err(Error::<T>::InvalidEquivocationProofSession.into());
}
// Validate the key ownership proof extracting the id of the offender.
let offender =
evidence.checked_offender::<P>().ok_or(Error::<T>::InvalidKeyOwnershipProof)?;
evidence.check_equivocation_proof()?;
let offence = EquivocationOffence {
time_slot: TimeSlot { set_id, round },
session_index,
validator_set_count: validator_count,
offender,
maybe_slash_fraction,
};
R::report_offence(reporter.into_iter().collect(), offence)
.map_err(|_| Error::<T>::DuplicateOffenceReport.into())
}
}
/// 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 {
// 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();
},
}
let evidence = call.to_equivocation_evidence_for().ok_or(InvalidTransaction::Call)?;
let tag = (evidence.offender_id().clone(), evidence.set_id(), *evidence.round_number());
T::EquivocationReportSystem::check_evidence(evidence)?;
let longevity =
<T::EquivocationReportSystem as OffenceReportSystem<_, _>>::Longevity::get();
ValidTransaction::with_tag_prefix("BeefyEquivocation")
// We assign the maximum priority for any equivocation report.
.priority(TransactionPriority::MAX)
// Only one equivocation report for the same offender at the same slot.
.and_provides(tag)
.longevity(longevity)
// We don't propagate this. This can never be included on a remote node.
.propagate(false)
.build()
}
pub fn pre_dispatch(call: &Call<T>) -> Result<(), TransactionValidityError> {
let evidence = call.to_equivocation_evidence_for().ok_or(InvalidTransaction::Call)?;
T::EquivocationReportSystem::check_evidence(evidence)
}
}
+790
View File
@@ -0,0 +1,790 @@
// 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.
#![cfg_attr(not(feature = "std"), no_std)]
extern crate alloc;
mod default_weights;
mod equivocation;
#[cfg(test)]
mod mock;
#[cfg(test)]
mod tests;
use alloc::{boxed::Box, vec::Vec};
use codec::{Encode, MaxEncodedLen};
use log;
use pezframe_support::{
dispatch::{DispatchResultWithPostInfo, Pays},
pezpallet_prelude::*,
traits::{Get, OneSessionHandler},
weights::{constants::RocksDbWeight as DbWeight, Weight},
BoundedSlice, BoundedVec, Parameter,
};
use pezframe_system::{
ensure_none, ensure_signed,
pezpallet_prelude::{BlockNumberFor, HeaderFor, OriginFor},
};
use pezsp_consensus_beefy::{
AncestryHelper, AncestryHelperWeightInfo, AuthorityIndex, BeefyAuthorityId, ConsensusLog,
DoubleVotingProof, ForkVotingProof, FutureBlockVotingProof, OnNewValidatorSet, ValidatorSet,
BEEFY_ENGINE_ID, GENESIS_AUTHORITY_SET_ID,
};
use pezsp_runtime::{
generic::DigestItem,
traits::{IsMember, Member, One},
RuntimeAppPublic,
};
use pezsp_session::{GetSessionNumber, GetValidatorCount};
use pezsp_staking::{offence::OffenceReportSystem, SessionIndex};
use crate::equivocation::EquivocationEvidenceFor;
pub use crate::equivocation::{EquivocationOffence, EquivocationReportSystem, TimeSlot};
pub use pallet::*;
const LOG_TARGET: &str = "runtime::beefy";
#[pezframe_support::pallet]
pub mod pallet {
use super::*;
use pezframe_system::{ensure_root, pezpallet_prelude::BlockNumberFor};
#[pallet::config]
pub trait Config: pezframe_system::Config {
/// Authority identifier type
type BeefyId: Member
+ Parameter
// todo: use custom signature hashing type instead of hardcoded `Keccak256`
+ BeefyAuthorityId<pezsp_runtime::traits::Keccak256>
+ MaybeSerializeDeserialize
+ MaxEncodedLen;
/// The maximum number of authorities that can be added.
#[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>;
/// A hook to act on the new BEEFY validator set.
///
/// For some applications it might be beneficial to make the BEEFY validator set available
/// externally apart from having it in the storage. For instance you might cache a light
/// weight MMR root over validators and make it available for Light Clients.
type OnNewValidatorSet: OnNewValidatorSet<<Self as Config>::BeefyId>;
/// Hook for checking commitment canonicity.
type AncestryHelper: AncestryHelper<HeaderFor<Self>>
+ AncestryHelperWeightInfo<HeaderFor<Self>>;
/// Weights for this pallet.
type WeightInfo: WeightInfo;
/// The proof of key ownership, used for validating equivocation reports
/// The proof must 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 publish, check and process an equivocation offence.
type EquivocationReportSystem: OffenceReportSystem<
Option<Self::AccountId>,
EquivocationEvidenceFor<Self>,
>;
}
#[pallet::pallet]
pub struct Pallet<T>(_);
/// The current authorities set
#[pallet::storage]
pub type Authorities<T: Config> =
StorageValue<_, BoundedVec<T::BeefyId, T::MaxAuthorities>, ValueQuery>;
/// The current validator set id
#[pallet::storage]
pub type ValidatorSetId<T: Config> =
StorageValue<_, pezsp_consensus_beefy::ValidatorSetId, ValueQuery>;
/// Authorities set scheduled to be used with the next session
#[pallet::storage]
pub type NextAuthorities<T: Config> =
StorageValue<_, BoundedVec<T::BeefyId, T::MaxAuthorities>, ValueQuery>;
/// A mapping from BEEFY 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 BEEFY 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: `ValidatorSetId` is not under user control.
#[pallet::storage]
pub type SetIdSession<T: Config> =
StorageMap<_, Twox64Concat, pezsp_consensus_beefy::ValidatorSetId, SessionIndex>;
/// Block number where BEEFY consensus is enabled/started.
/// By changing this (through privileged `set_new_genesis()`), BEEFY consensus is effectively
/// restarted from the newly set block number.
#[pallet::storage]
pub type GenesisBlock<T: Config> = StorageValue<_, Option<BlockNumberFor<T>>, ValueQuery>;
#[pallet::genesis_config]
pub struct GenesisConfig<T: Config> {
/// Initial set of BEEFY authorities.
pub authorities: Vec<T::BeefyId>,
/// Block number where BEEFY consensus should start.
/// Should match the session where initial authorities are active.
/// *Note:* Ideally use block number where GRANDPA authorities are changed,
/// to guarantee the client gets a finality notification for exactly this block.
pub genesis_block: Option<BlockNumberFor<T>>,
}
impl<T: Config> Default for GenesisConfig<T> {
fn default() -> Self {
// BEEFY genesis will be first BEEFY-MANDATORY block,
// use block number one instead of chain-genesis.
let genesis_block = Some(One::one());
Self { authorities: Vec::new(), genesis_block }
}
}
#[pallet::genesis_build]
impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
fn build(&self) {
Pallet::<T>::initialize(&self.authorities)
// we panic here as runtime maintainers can simply reconfigure genesis and restart
// the chain easily
.expect("Authorities vec too big");
GenesisBlock::<T>::put(&self.genesis_block);
}
}
#[pallet::error]
pub enum Error<T> {
/// A key ownership proof provided as part of an equivocation report is invalid.
InvalidKeyOwnershipProof,
/// A double voting proof provided as part of an equivocation report is invalid.
InvalidDoubleVotingProof,
/// A fork voting proof provided as part of an equivocation report is invalid.
InvalidForkVotingProof,
/// A future block voting proof provided as part of an equivocation report is invalid.
InvalidFutureBlockVotingProof,
/// The session of the equivocation proof is invalid
InvalidEquivocationProofSession,
/// The session of the equivocation proof is not in the mapping (anymore)
InvalidEquivocationProofSessionMember,
/// A given equivocation report is valid but already previously reported.
DuplicateOffenceReport,
/// Submitted configuration is invalid.
InvalidConfiguration,
}
#[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_double_voting(
key_owner_proof.validator_count(),
T::MaxNominators::get(),
))]
pub fn report_double_voting(
origin: OriginFor<T>,
equivocation_proof: Box<
DoubleVotingProof<
BlockNumberFor<T>,
T::BeefyId,
<T::BeefyId as RuntimeAppPublic>::Signature,
>,
>,
key_owner_proof: T::KeyOwnerProof,
) -> DispatchResultWithPostInfo {
let reporter = ensure_signed(origin)?;
T::EquivocationReportSystem::process_evidence(
Some(reporter),
EquivocationEvidenceFor::DoubleVotingProof(*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_double_voting(
key_owner_proof.validator_count(),
T::MaxNominators::get(),
))]
pub fn report_double_voting_unsigned(
origin: OriginFor<T>,
equivocation_proof: Box<
DoubleVotingProof<
BlockNumberFor<T>,
T::BeefyId,
<T::BeefyId as RuntimeAppPublic>::Signature,
>,
>,
key_owner_proof: T::KeyOwnerProof,
) -> DispatchResultWithPostInfo {
ensure_none(origin)?;
T::EquivocationReportSystem::process_evidence(
None,
EquivocationEvidenceFor::DoubleVotingProof(*equivocation_proof, key_owner_proof),
)?;
Ok(Pays::No.into())
}
/// Reset BEEFY consensus by setting a new BEEFY genesis at `delay_in_blocks` blocks in the
/// future.
///
/// Note: `delay_in_blocks` has to be at least 1.
#[pallet::call_index(2)]
#[pallet::weight(<T as Config>::WeightInfo::set_new_genesis())]
pub fn set_new_genesis(
origin: OriginFor<T>,
delay_in_blocks: BlockNumberFor<T>,
) -> DispatchResult {
ensure_root(origin)?;
ensure!(delay_in_blocks >= One::one(), Error::<T>::InvalidConfiguration);
let genesis_block = pezframe_system::Pallet::<T>::block_number() + delay_in_blocks;
GenesisBlock::<T>::put(Some(genesis_block));
Ok(())
}
/// Report fork voting equivocation. 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(3)]
#[pallet::weight(T::WeightInfo::report_fork_voting::<T>(
key_owner_proof.validator_count(),
T::MaxNominators::get(),
&equivocation_proof.ancestry_proof
))]
pub fn report_fork_voting(
origin: OriginFor<T>,
equivocation_proof: Box<
ForkVotingProof<
HeaderFor<T>,
T::BeefyId,
<T::AncestryHelper as AncestryHelper<HeaderFor<T>>>::Proof,
>,
>,
key_owner_proof: T::KeyOwnerProof,
) -> DispatchResultWithPostInfo {
let reporter = ensure_signed(origin)?;
T::EquivocationReportSystem::process_evidence(
Some(reporter),
EquivocationEvidenceFor::ForkVotingProof(*equivocation_proof, key_owner_proof),
)?;
// Waive the fee since the report is valid and beneficial
Ok(Pays::No.into())
}
/// Report fork voting equivocation. 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(4)]
#[pallet::weight(T::WeightInfo::report_fork_voting::<T>(
key_owner_proof.validator_count(),
T::MaxNominators::get(),
&equivocation_proof.ancestry_proof
))]
pub fn report_fork_voting_unsigned(
origin: OriginFor<T>,
equivocation_proof: Box<
ForkVotingProof<
HeaderFor<T>,
T::BeefyId,
<T::AncestryHelper as AncestryHelper<HeaderFor<T>>>::Proof,
>,
>,
key_owner_proof: T::KeyOwnerProof,
) -> DispatchResultWithPostInfo {
ensure_none(origin)?;
T::EquivocationReportSystem::process_evidence(
None,
EquivocationEvidenceFor::ForkVotingProof(*equivocation_proof, key_owner_proof),
)?;
// Waive the fee since the report is valid and beneficial
Ok(Pays::No.into())
}
/// Report future block voting equivocation. 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(5)]
#[pallet::weight(T::WeightInfo::report_future_block_voting(
key_owner_proof.validator_count(),
T::MaxNominators::get(),
))]
pub fn report_future_block_voting(
origin: OriginFor<T>,
equivocation_proof: Box<FutureBlockVotingProof<BlockNumberFor<T>, T::BeefyId>>,
key_owner_proof: T::KeyOwnerProof,
) -> DispatchResultWithPostInfo {
let reporter = ensure_signed(origin)?;
T::EquivocationReportSystem::process_evidence(
Some(reporter),
EquivocationEvidenceFor::FutureBlockVotingProof(
*equivocation_proof,
key_owner_proof,
),
)?;
// Waive the fee since the report is valid and beneficial
Ok(Pays::No.into())
}
/// Report future block voting equivocation. 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(6)]
#[pallet::weight(T::WeightInfo::report_future_block_voting(
key_owner_proof.validator_count(),
T::MaxNominators::get(),
))]
pub fn report_future_block_voting_unsigned(
origin: OriginFor<T>,
equivocation_proof: Box<FutureBlockVotingProof<BlockNumberFor<T>, T::BeefyId>>,
key_owner_proof: T::KeyOwnerProof,
) -> DispatchResultWithPostInfo {
ensure_none(origin)?;
T::EquivocationReportSystem::process_evidence(
None,
EquivocationEvidenceFor::FutureBlockVotingProof(
*equivocation_proof,
key_owner_proof,
),
)?;
// Waive the fee since the report is valid and beneficial
Ok(Pays::No.into())
}
}
#[pallet::hooks]
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
#[cfg(feature = "try-runtime")]
fn try_state(_n: BlockNumberFor<T>) -> Result<(), pezsp_runtime::TryRuntimeError> {
Self::do_try_state()
}
}
#[pallet::validate_unsigned]
impl<T: Config> ValidateUnsigned for Pallet<T> {
type Call = Call<T>;
fn pre_dispatch(call: &Self::Call) -> Result<(), TransactionValidityError> {
Self::pre_dispatch(call)
}
fn validate_unsigned(source: TransactionSource, call: &Self::Call) -> TransactionValidity {
Self::validate_unsigned(source, call)
}
}
impl<T: Config> Call<T> {
pub fn to_equivocation_evidence_for(&self) -> Option<EquivocationEvidenceFor<T>> {
match self {
Call::report_double_voting_unsigned { equivocation_proof, key_owner_proof } =>
Some(EquivocationEvidenceFor::<T>::DoubleVotingProof(
*equivocation_proof.clone(),
key_owner_proof.clone(),
)),
Call::report_fork_voting_unsigned { equivocation_proof, key_owner_proof } =>
Some(EquivocationEvidenceFor::<T>::ForkVotingProof(
*equivocation_proof.clone(),
key_owner_proof.clone(),
)),
_ => None,
}
}
}
impl<T: Config> From<EquivocationEvidenceFor<T>> for Call<T> {
fn from(evidence: EquivocationEvidenceFor<T>) -> Self {
match evidence {
EquivocationEvidenceFor::DoubleVotingProof(equivocation_proof, key_owner_proof) =>
Call::report_double_voting_unsigned {
equivocation_proof: Box::new(equivocation_proof),
key_owner_proof,
},
EquivocationEvidenceFor::ForkVotingProof(equivocation_proof, key_owner_proof) =>
Call::report_fork_voting_unsigned {
equivocation_proof: Box::new(equivocation_proof),
key_owner_proof,
},
EquivocationEvidenceFor::FutureBlockVotingProof(
equivocation_proof,
key_owner_proof,
) => Call::report_future_block_voting_unsigned {
equivocation_proof: Box::new(equivocation_proof),
key_owner_proof,
},
}
}
}
}
#[cfg(any(feature = "try-runtime", test))]
impl<T: Config> Pallet<T> {
/// Ensure the correctness of the state of this pallet.
///
/// This should be valid before or after each state transition of this pallet.
pub fn do_try_state() -> Result<(), pezsp_runtime::TryRuntimeError> {
Self::try_state_authorities()?;
Self::try_state_validators()?;
Ok(())
}
/// # Invariants
///
/// * `Authorities` should not exceed the `MaxAuthorities` capacity.
/// * `NextAuthorities` should not exceed the `MaxAuthorities` capacity.
fn try_state_authorities() -> Result<(), pezsp_runtime::TryRuntimeError> {
if let Some(authorities_len) = <Authorities<T>>::decode_len() {
ensure!(
authorities_len as u32 <= T::MaxAuthorities::get(),
"Authorities number exceeds what the pallet config allows."
);
} else {
return Err(pezsp_runtime::TryRuntimeError::Other(
"Failed to decode length of authorities",
));
}
if let Some(next_authorities_len) = <NextAuthorities<T>>::decode_len() {
ensure!(
next_authorities_len as u32 <= T::MaxAuthorities::get(),
"Next authorities number exceeds what the pallet config allows."
);
} else {
return Err(pezsp_runtime::TryRuntimeError::Other(
"Failed to decode length of next authorities",
));
}
Ok(())
}
/// # Invariants
///
/// `ValidatorSetId` must be present in `SetIdSession`
fn try_state_validators() -> Result<(), pezsp_runtime::TryRuntimeError> {
let validator_set_id = <ValidatorSetId<T>>::get();
ensure!(
SetIdSession::<T>::get(validator_set_id).is_some(),
"Validator set id must be present in SetIdSession"
);
Ok(())
}
}
impl<T: Config> Pallet<T> {
/// Return the current active BEEFY validator set.
pub fn validator_set() -> Option<ValidatorSet<T::BeefyId>> {
let validators: BoundedVec<T::BeefyId, T::MaxAuthorities> = Authorities::<T>::get();
let id: pezsp_consensus_beefy::ValidatorSetId = ValidatorSetId::<T>::get();
ValidatorSet::<T::BeefyId>::new(validators, id)
}
/// Submits an extrinsic to report a double voting equivocation. This method will create
/// an unsigned extrinsic with a call to `report_double_voting_unsigned` and
/// will push the transaction to the pool. Only useful in an offchain context.
pub fn submit_unsigned_double_voting_report(
equivocation_proof: DoubleVotingProof<
BlockNumberFor<T>,
T::BeefyId,
<T::BeefyId as RuntimeAppPublic>::Signature,
>,
key_owner_proof: T::KeyOwnerProof,
) -> Option<()> {
T::EquivocationReportSystem::publish_evidence(EquivocationEvidenceFor::DoubleVotingProof(
equivocation_proof,
key_owner_proof,
))
.ok()
}
/// Submits an extrinsic to report a fork voting equivocation. This method will create
/// an unsigned extrinsic with a call to `report_fork_voting_unsigned` and
/// will push the transaction to the pool. Only useful in an offchain context.
pub fn submit_unsigned_fork_voting_report(
equivocation_proof: ForkVotingProof<
HeaderFor<T>,
T::BeefyId,
<T::AncestryHelper as AncestryHelper<HeaderFor<T>>>::Proof,
>,
key_owner_proof: T::KeyOwnerProof,
) -> Option<()> {
T::EquivocationReportSystem::publish_evidence(EquivocationEvidenceFor::ForkVotingProof(
equivocation_proof,
key_owner_proof,
))
.ok()
}
/// Submits an extrinsic to report a future block voting equivocation. This method will create
/// an unsigned extrinsic with a call to `report_future_block_voting_unsigned` and
/// will push the transaction to the pool. Only useful in an offchain context.
pub fn submit_unsigned_future_block_voting_report(
equivocation_proof: FutureBlockVotingProof<BlockNumberFor<T>, T::BeefyId>,
key_owner_proof: T::KeyOwnerProof,
) -> Option<()> {
T::EquivocationReportSystem::publish_evidence(
EquivocationEvidenceFor::FutureBlockVotingProof(equivocation_proof, key_owner_proof),
)
.ok()
}
fn change_authorities(
new: BoundedVec<T::BeefyId, T::MaxAuthorities>,
queued: BoundedVec<T::BeefyId, T::MaxAuthorities>,
) {
Authorities::<T>::put(&new);
let new_id = ValidatorSetId::<T>::get() + 1u64;
ValidatorSetId::<T>::put(new_id);
NextAuthorities::<T>::put(&queued);
if let Some(validator_set) = ValidatorSet::<T::BeefyId>::new(new, new_id) {
let log = DigestItem::Consensus(
BEEFY_ENGINE_ID,
ConsensusLog::AuthoritiesChange(validator_set.clone()).encode(),
);
pezframe_system::Pallet::<T>::deposit_log(log);
let next_id = new_id + 1;
if let Some(next_validator_set) = ValidatorSet::<T::BeefyId>::new(queued, next_id) {
<T::OnNewValidatorSet as OnNewValidatorSet<_>>::on_new_validator_set(
&validator_set,
&next_validator_set,
);
}
}
}
fn initialize(authorities: &Vec<T::BeefyId>) -> Result<(), ()> {
if authorities.is_empty() {
return Ok(());
}
if !Authorities::<T>::get().is_empty() {
return Err(());
}
let bounded_authorities =
BoundedSlice::<T::BeefyId, T::MaxAuthorities>::try_from(authorities.as_slice())
.map_err(|_| ())?;
let id = GENESIS_AUTHORITY_SET_ID;
Authorities::<T>::put(bounded_authorities);
ValidatorSetId::<T>::put(id);
// Like `pezpallet_session`, initialize the next validator set as well.
NextAuthorities::<T>::put(bounded_authorities);
if let Some(validator_set) = ValidatorSet::<T::BeefyId>::new(authorities.clone(), id) {
let next_id = id + 1;
if let Some(next_validator_set) =
ValidatorSet::<T::BeefyId>::new(authorities.clone(), next_id)
{
<T::OnNewValidatorSet as OnNewValidatorSet<_>>::on_new_validator_set(
&validator_set,
&next_validator_set,
);
}
}
// 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);
Ok(())
}
}
impl<T: Config> pezsp_runtime::BoundToRuntimeAppPublic for Pallet<T> {
type Public = T::BeefyId;
}
impl<T: Config> OneSessionHandler<T::AccountId> for Pallet<T>
where
T: pezpallet_session::Config,
{
type Key = T::BeefyId;
fn on_genesis_session<'a, I: 'a>(validators: I)
where
I: Iterator<Item = (&'a T::AccountId, T::BeefyId)>,
{
let authorities = validators.map(|(_, k)| k).collect::<Vec<_>>();
// we panic here as runtime maintainers can simply reconfigure genesis and restart the
// chain easily
Self::initialize(&authorities).expect("Authorities vec too big");
}
fn on_new_session<'a, I: 'a>(_changed: bool, validators: I, queued_validators: I)
where
I: Iterator<Item = (&'a T::AccountId, T::BeefyId)>,
{
let next_authorities = validators.map(|(_, k)| k).collect::<Vec<_>>();
if next_authorities.len() as u32 > T::MaxAuthorities::get() {
log::error!(
target: LOG_TARGET,
"authorities list {:?} truncated to length {}",
next_authorities,
T::MaxAuthorities::get(),
);
}
let bounded_next_authorities =
BoundedVec::<_, T::MaxAuthorities>::truncate_from(next_authorities);
let next_queued_authorities = queued_validators.map(|(_, k)| k).collect::<Vec<_>>();
if next_queued_authorities.len() as u32 > T::MaxAuthorities::get() {
log::error!(
target: LOG_TARGET,
"queued authorities list {:?} truncated to length {}",
next_queued_authorities,
T::MaxAuthorities::get(),
);
}
let bounded_next_queued_authorities =
BoundedVec::<_, T::MaxAuthorities>::truncate_from(next_queued_authorities);
// Always issue a change on each `session`, even if validator set hasn't changed.
// We want to have at least one BEEFY mandatory block per session.
Self::change_authorities(bounded_next_authorities, bounded_next_queued_authorities);
let validator_set_id = ValidatorSetId::<T>::get();
// Update the mapping for the new set id that corresponds to the latest session (i.e. now).
let session_index = pezpallet_session::Pallet::<T>::current_index();
SetIdSession::<T>::insert(validator_set_id, &session_index);
// Prune old entry if limit reached.
let max_set_id_session_entries = T::MaxSetIdSessionEntries::get().max(1);
if validator_set_id >= max_set_id_session_entries {
SetIdSession::<T>::remove(validator_set_id - max_set_id_session_entries);
}
}
fn on_disabled(i: u32) {
let log = DigestItem::Consensus(
BEEFY_ENGINE_ID,
ConsensusLog::<T::BeefyId>::OnDisabled(i as AuthorityIndex).encode(),
);
pezframe_system::Pallet::<T>::deposit_log(log);
}
}
impl<T: Config> IsMember<T::BeefyId> for Pallet<T> {
fn is_member(authority_id: &T::BeefyId) -> bool {
Authorities::<T>::get().iter().any(|id| id == authority_id)
}
}
pub trait WeightInfo {
fn report_voting_equivocation(
votes_count: u32,
validator_count: u32,
max_nominators_per_validator: u32,
) -> Weight;
fn set_new_genesis() -> Weight;
}
pub(crate) trait WeightInfoExt: WeightInfo {
fn report_double_voting(validator_count: u32, max_nominators_per_validator: u32) -> Weight {
Self::report_voting_equivocation(2, validator_count, max_nominators_per_validator)
}
fn report_fork_voting<T: Config>(
validator_count: u32,
max_nominators_per_validator: u32,
ancestry_proof: &<T::AncestryHelper as AncestryHelper<HeaderFor<T>>>::Proof,
) -> Weight {
<T::AncestryHelper as AncestryHelperWeightInfo<HeaderFor<T>>>::is_proof_optimal(&ancestry_proof)
.saturating_add(<T::AncestryHelper as AncestryHelperWeightInfo<HeaderFor<T>>>::extract_validation_context())
.saturating_add(
<T::AncestryHelper as AncestryHelperWeightInfo<HeaderFor<T>>>::is_non_canonical(
ancestry_proof,
),
)
.saturating_add(Self::report_voting_equivocation(
1,
validator_count,
max_nominators_per_validator,
))
}
fn report_future_block_voting(
validator_count: u32,
max_nominators_per_validator: u32,
) -> Weight {
// checking if the report is for a future block
DbWeight::get()
.reads(1)
// check and report the equivocated vote
.saturating_add(Self::report_voting_equivocation(
1,
validator_count,
max_nominators_per_validator,
))
}
}
impl<T> WeightInfoExt for T where T: WeightInfo {}
+379
View File
@@ -0,0 +1,379 @@
// 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 codec::{Decode, DecodeWithMemTracking, Encode};
use pezframe_election_provider_support::{
bounds::{ElectionBounds, ElectionBoundsBuilder},
onchain, SequentialPhragmen, Weight,
};
use pezframe_support::{
construct_runtime, derive_impl, parameter_types,
traits::{ConstU32, ConstU64, KeyOwnerProofSystem, OnFinalize, OnInitialize},
};
use pezframe_system::pezpallet_prelude::HeaderFor;
use pezpallet_session::historical as pezpallet_session_historical;
use scale_info::TypeInfo;
use pezsp_core::{crypto::KeyTypeId, ConstBool, ConstU128};
use pezsp_runtime::{
app_crypto::ecdsa::Public,
curve::PiecewiseLinear,
impl_opaque_keys,
testing::TestXt,
traits::{Header as HeaderT, OpaqueKeys},
BuildStorage, Perbill,
};
use pezsp_staking::{EraIndex, SessionIndex};
use pezsp_state_machine::BasicExternalities;
use crate as pezpallet_beefy;
pub use pezsp_consensus_beefy::{ecdsa_crypto::AuthorityId as BeefyId, ConsensusLog, BEEFY_ENGINE_ID};
use pezsp_consensus_beefy::{AncestryHelper, AncestryHelperWeightInfo, Commitment};
impl_opaque_keys! {
pub struct MockSessionKeys {
pub dummy: pezpallet_beefy::Pallet<Test>,
}
}
type Block = pezframe_system::mocking::MockBlock<Test>;
construct_runtime!(
pub enum Test
{
System: pezframe_system,
Authorship: pezpallet_authorship,
Timestamp: pezpallet_timestamp,
Balances: pezpallet_balances,
Beefy: pezpallet_beefy,
Staking: pezpallet_staking,
Session: pezpallet_session,
Offences: pezpallet_offences,
Historical: pezpallet_session_historical,
}
);
#[derive_impl(pezframe_system::config_preludes::TestDefaultConfig)]
impl pezframe_system::Config for Test {
type Block = Block;
type AccountData = pezpallet_balances::AccountData<u128>;
}
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)
}
}
#[derive(Clone, Debug, Decode, Encode, PartialEq, TypeInfo)]
pub struct MockAncestryProofContext {
pub is_valid: bool,
}
#[derive(Clone, Debug, Decode, DecodeWithMemTracking, Encode, PartialEq, TypeInfo)]
pub struct MockAncestryProof {
pub is_optimal: bool,
pub is_non_canonical: bool,
}
parameter_types! {
pub const Period: u64 = 1;
pub const ReportLongevity: u64 =
BondingDuration::get() as u64 * SessionsPerEra::get() as u64 * Period::get();
pub const MaxSetIdSessionEntries: u32 = BondingDuration::get() * SessionsPerEra::get();
pub storage AncestryProofContext: Option<MockAncestryProofContext> = Some(
MockAncestryProofContext {
is_valid: true,
}
);
}
pub struct MockAncestryHelper;
impl<Header: HeaderT> AncestryHelper<Header> for MockAncestryHelper {
type Proof = MockAncestryProof;
type ValidationContext = MockAncestryProofContext;
fn is_proof_optimal(proof: &Self::Proof) -> bool {
proof.is_optimal
}
fn extract_validation_context(_header: Header) -> Option<Self::ValidationContext> {
AncestryProofContext::get()
}
fn is_non_canonical(
_commitment: &Commitment<Header::Number>,
proof: Self::Proof,
context: Self::ValidationContext,
) -> bool {
context.is_valid && proof.is_non_canonical
}
}
impl<Header: HeaderT> AncestryHelperWeightInfo<Header> for MockAncestryHelper {
fn is_proof_optimal(_proof: &<Self as AncestryHelper<HeaderFor<Test>>>::Proof) -> Weight {
unimplemented!()
}
fn extract_validation_context() -> Weight {
unimplemented!()
}
fn is_non_canonical(_proof: &<Self as AncestryHelper<HeaderFor<Test>>>::Proof) -> Weight {
unimplemented!()
}
}
impl pezpallet_beefy::Config for Test {
type BeefyId = BeefyId;
type MaxAuthorities = ConstU32<100>;
type MaxNominators = ConstU32<1000>;
type MaxSetIdSessionEntries = MaxSetIdSessionEntries;
type OnNewValidatorSet = ();
type AncestryHelper = MockAncestryHelper;
type WeightInfo = ();
type KeyOwnerProof = <Historical as KeyOwnerProofSystem<(KeyTypeId, BeefyId)>>::Proof;
type EquivocationReportSystem =
super::EquivocationReportSystem<Self, Offences, Historical, ReportLongevity>;
}
parameter_types! {
pub const DisabledValidatorsThreshold: Perbill = Perbill::from_percent(33);
}
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 = <MockSessionKeys as OpaqueKeys>::KeyTypeIdProviders;
type Keys = MockSessionKeys;
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 RuntimeEvent = RuntimeEvent;
type OldCurrency = Balances;
type Currency = Balances;
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>;
}
impl pezpallet_offences::Config for Test {
type RuntimeEvent = RuntimeEvent;
type IdentificationTuple = pezpallet_session::historical::IdentificationTuple<Self>;
type OnOffenceHandler = Staking;
}
#[derive(Default)]
pub struct ExtBuilder {
authorities: Vec<BeefyId>,
}
impl ExtBuilder {
/// Add some AccountIds to insert into `List`.
#[cfg(test)]
pub(crate) fn add_authorities(mut self, ids: Vec<BeefyId>) -> Self {
self.authorities = ids;
self
}
pub fn build(self) -> pezsp_io::TestExternalities {
pezsp_tracing::try_init_simple();
let mut t = pezframe_system::GenesisConfig::<Test>::default().build_storage().unwrap();
let balances: Vec<_> =
(0..self.authorities.len()).map(|i| (i as u64, 10_000_000)).collect();
pezpallet_balances::GenesisConfig::<Test> { balances, ..Default::default() }
.assimilate_storage(&mut t)
.unwrap();
let session_keys: Vec<_> = self
.authorities
.iter()
.enumerate()
.map(|(i, k)| (i as u64, i as u64, MockSessionKeys { dummy: k.clone() }))
.collect();
BasicExternalities::execute_with_storage(&mut t, || {
for (ref id, ..) in &session_keys {
pezframe_system::Pallet::<Test>::inc_providers(id);
}
});
pezpallet_session::GenesisConfig::<Test> { keys: session_keys, ..Default::default() }
.assimilate_storage(&mut t)
.unwrap();
// controllers are same as stash
let stakers: Vec<_> = (0..self.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: 2,
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 build_and_execute(self, test: impl FnOnce() -> ()) {
self.build().execute_with(|| {
test();
Beefy::do_try_state().expect("All invariants must hold after a test");
})
}
}
// Note, that we can't use `UintAuthorityId` here. Reason is that the implementation
// of `to_public_key()` assumes, that a public key is 32 bytes long. This is true for
// ed25519 and sr25519 but *not* for ecdsa. A compressed ecdsa public key is 33 bytes,
// with the first one containing information to reconstruct the uncompressed key.
pub fn mock_beefy_id(id: u8) -> BeefyId {
let mut buf: [u8; 33] = [id; 33];
// Set to something valid.
buf[0] = 0x02;
let pk = Public::from_raw(buf);
BeefyId::from(pk)
}
pub fn mock_authorities(vec: Vec<u8>) -> Vec<BeefyId> {
vec.into_iter().map(|id| mock_beefy_id(id)).collect()
}
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());
Beefy::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());
Beefy::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));
}
File diff suppressed because it is too large Load Diff