From d0b6d9526274866e8ceb0ae4449494a41199f705 Mon Sep 17 00:00:00 2001 From: Davide Galassi Date: Mon, 13 Feb 2023 19:09:43 +0100 Subject: [PATCH 01/33] Experiments with common equivocation trait --- frame/babe/src/equivocation.rs | 96 +++------ frame/babe/src/lib.rs | 24 ++- frame/babe/src/mock.rs | 3 +- frame/grandpa/src/equivocation.rs | 279 ++++++++++++------------- frame/grandpa/src/lib.rs | 36 ++-- primitives/staking/src/equivocation.rs | 67 ++++++ primitives/staking/src/lib.rs | 1 + 7 files changed, 261 insertions(+), 245 deletions(-) create mode 100644 primitives/staking/src/equivocation.rs diff --git a/frame/babe/src/equivocation.rs b/frame/babe/src/equivocation.rs index 70f087fd461f9..1995a32b63809 100644 --- a/frame/babe/src/equivocation.rs +++ b/frame/babe/src/equivocation.rs @@ -43,6 +43,7 @@ use sp_runtime::{ DispatchResult, Perbill, }; use sp_staking::{ + equivocation::EquivocationHandler2, offence::{Kind, Offence, OffenceError, ReportOffence}, SessionIndex, }; @@ -50,61 +51,6 @@ use sp_std::prelude::*; use crate::{Call, Config, Pallet, LOG_TARGET}; -/// A trait with utility methods for handling equivocation reports in BABE. -/// The trait provides methods for reporting an offence triggered by a valid -/// equivocation report, checking the current block author (to declare as the -/// reporter), and also for creating and submitting equivocation report -/// extrinsics (useful only in offchain context). -pub trait HandleEquivocation { - /// The longevity, in blocks, that the equivocation report is valid for. When using the staking - /// pallet this should be equal to the bonding duration (in blocks, not eras). - type ReportLongevity: Get; - - /// Report an offence proved by the given reporters. - fn report_offence( - reporters: Vec, - offence: BabeEquivocationOffence, - ) -> Result<(), OffenceError>; - - /// Returns true if all of the offenders at the given time slot have already been reported. - fn is_known_offence(offenders: &[T::KeyOwnerIdentification], time_slot: &Slot) -> bool; - - /// Create and dispatch an equivocation report extrinsic. - fn submit_unsigned_equivocation_report( - equivocation_proof: EquivocationProof, - key_owner_proof: T::KeyOwnerProof, - ) -> DispatchResult; - - /// Fetch the current block author id, if defined. - fn block_author() -> Option; -} - -impl HandleEquivocation for () { - type ReportLongevity = (); - - fn report_offence( - _reporters: Vec, - _offence: BabeEquivocationOffence, - ) -> Result<(), OffenceError> { - Ok(()) - } - - fn is_known_offence(_offenders: &[T::KeyOwnerIdentification], _time_slot: &Slot) -> bool { - true - } - - fn submit_unsigned_equivocation_report( - _equivocation_proof: EquivocationProof, - _key_owner_proof: T::KeyOwnerProof, - ) -> DispatchResult { - Ok(()) - } - - fn block_author() -> Option { - None - } -} - /// Generic equivocation handler. This type implements `HandleEquivocation` /// using existing subsystems that are part of frame (type bounds described /// below) and will dispatch to them directly, it's only purpose is to wire all @@ -119,12 +65,16 @@ impl Default for EquivocationHandler { } } -impl HandleEquivocation for EquivocationHandler +impl EquivocationHandler2 for EquivocationHandler where - // We use the authorship pallet to fetch the current block author and use - // `offchain::SendTransactionTypes` for unsigned extrinsic creation and - // submission. + // We use the authorship pallet + // to fetch the current block + // author and use + // `offchain::SendTransactionTypes` + // for unsigned extrinsic + // creation and submission. T: Config + pallet_authorship::Config + frame_system::offchain::SendTransactionTypes>, + // A system for reporting offences after valid equivocation reports are // processed. R: ReportOffence< @@ -138,20 +88,33 @@ where { type ReportLongevity = L; + type AccountId = T::AccountId; + + type KeyOwnerIdentification = T::KeyOwnerIdentification; + + type Offence = BabeEquivocationOffence; + + type EquivocationProof = EquivocationProof; + + type KeyOwnerProof = T::KeyOwnerProof; + fn report_offence( - reporters: Vec, + reporters: Vec, offence: BabeEquivocationOffence, ) -> Result<(), OffenceError> { R::report_offence(reporters, offence) } - fn is_known_offence(offenders: &[T::KeyOwnerIdentification], time_slot: &Slot) -> bool { + fn is_known_offence( + offenders: &[Self::KeyOwnerIdentification], + time_slot: &>::TimeSlot, + ) -> bool { R::is_known_offence(offenders, time_slot) } fn submit_unsigned_equivocation_report( - equivocation_proof: EquivocationProof, - key_owner_proof: T::KeyOwnerProof, + equivocation_proof: Self::EquivocationProof, + key_owner_proof: Self::KeyOwnerProof, ) -> DispatchResult { use frame_system::offchain::SubmitTransaction; @@ -198,7 +161,7 @@ impl Pallet { is_known_offence::(equivocation_proof, key_owner_proof)?; let longevity = - >::ReportLongevity::get(); + ::ReportLongevity::get(); ValidTransaction::with_tag_prefix("BabeEquivocation") // We assign the maximum priority for any equivocation report. @@ -233,9 +196,8 @@ fn is_known_offence( let offender = T::KeyOwnerProofSystem::check_proof(key, key_owner_proof.clone()) .ok_or(InvalidTransaction::BadProof)?; - // check if the offence has already been reported, - // and if so then we can discard the report. - if T::HandleEquivocation::is_known_offence(&[offender], &equivocation_proof.slot) { + // check if the offence has already been reported, and if so then we can discard the report. + if T::HandleEquivocation2::is_known_offence(&[offender], &equivocation_proof.slot) { Err(InvalidTransaction::Stale.into()) } else { Ok(()) diff --git a/frame/babe/src/lib.rs b/frame/babe/src/lib.rs index eb87d65b0549f..ce9ab68a0caed 100644 --- a/frame/babe/src/lib.rs +++ b/frame/babe/src/lib.rs @@ -39,6 +39,7 @@ use sp_runtime::{ ConsensusEngineId, KeyTypeId, Permill, }; use sp_session::{GetSessionNumber, GetValidatorCount}; +use sp_staking::equivocation::EquivocationHandler2; use sp_std::prelude::*; use sp_consensus_babe::{ @@ -63,7 +64,7 @@ mod mock; #[cfg(all(feature = "std", test))] mod tests; -pub use equivocation::{BabeEquivocationOffence, EquivocationHandler, HandleEquivocation}; +pub use equivocation::{BabeEquivocationOffence, EquivocationHandler}; #[allow(deprecated)] pub use randomness::CurrentBlockRandomness; pub use randomness::{ @@ -171,8 +172,15 @@ pub mod pallet { /// NOTE: when enabling equivocation handling (i.e. this type isn't set to /// `()`) you must use this pallet's `ValidateUnsigned` in the runtime /// definition. - type HandleEquivocation: HandleEquivocation; + type HandleEquivocation2: EquivocationHandler2< + AccountId = Self::AccountId, + KeyOwnerProof = Self::KeyOwnerProof, + KeyOwnerIdentification = Self::KeyOwnerIdentification, + Offence = BabeEquivocationOffence, + EquivocationProof = EquivocationProof, + >; + /// Helper for weights computations type WeightInfo: WeightInfo; /// Max number of authorities allowed @@ -439,7 +447,7 @@ pub mod pallet { ensure_none(origin)?; Self::do_report_equivocation( - T::HandleEquivocation::block_author(), + T::HandleEquivocation2::block_author(), *equivocation_proof, key_owner_proof, ) @@ -849,12 +857,7 @@ impl Pallet { let offence = BabeEquivocationOffence { slot, validator_set_count, offender, session_index }; - let reporters = match reporter { - Some(id) => vec![id], - None => vec![], - }; - - T::HandleEquivocation::report_offence(reporters, offence) + T::HandleEquivocation2::report_offence(reporter.into_iter().collect(), offence) .map_err(|_| Error::::DuplicateOffenceReport)?; // waive the fee since the report is valid and beneficial @@ -869,7 +872,8 @@ impl Pallet { equivocation_proof: EquivocationProof, key_owner_proof: T::KeyOwnerProof, ) -> Option<()> { - T::HandleEquivocation::submit_unsigned_equivocation_report( + // TODO: this should be submitted anyway...??? + T::HandleEquivocation2::submit_unsigned_equivocation_report( equivocation_proof, key_owner_proof, ) diff --git a/frame/babe/src/mock.rs b/frame/babe/src/mock.rs index b09dc29f6d60a..c64fc873b9ead 100644 --- a/frame/babe/src/mock.rs +++ b/frame/babe/src/mock.rs @@ -234,8 +234,7 @@ impl Config for Test { AuthorityId, )>>::IdentificationTuple; - type HandleEquivocation = - super::EquivocationHandler; + type HandleEquivocation2 = super::EquivocationHandler; type WeightInfo = (); type MaxAuthorities = ConstU32<10>; diff --git a/frame/grandpa/src/equivocation.rs b/frame/grandpa/src/equivocation.rs index 23801a4982a82..740e62e03832b 100644 --- a/frame/grandpa/src/equivocation.rs +++ b/frame/grandpa/src/equivocation.rs @@ -48,141 +48,166 @@ use sp_runtime::{ DispatchResult, Perbill, }; use sp_staking::{ + equivocation::EquivocationHandler2, offence::{Kind, Offence, OffenceError, ReportOffence}, SessionIndex, }; use super::{Call, Config, Pallet, LOG_TARGET}; -/// A trait with utility methods for handling equivocation reports in GRANDPA. -/// The offence type is generic, and the trait provides , reporting an offence -/// triggered by a valid equivocation report, and also for creating and -/// submitting equivocation report extrinsics (useful only in offchain context). -pub trait HandleEquivocation { - /// The offence type used for reporting offences on valid equivocation reports. - type Offence: GrandpaOffence; - - /// The longevity, in blocks, that the equivocation report is valid for. When using the staking - /// pallet this should be equal to the bonding duration (in blocks, not eras). - type ReportLongevity: Get; - - /// Report an offence proved by the given reporters. - fn report_offence( - reporters: Vec, - offence: Self::Offence, - ) -> Result<(), OffenceError>; - - /// Returns true if all of the offenders at the given time slot have already been reported. - fn is_known_offence( - offenders: &[T::KeyOwnerIdentification], - time_slot: &>::TimeSlot, - ) -> bool; - - /// Create and dispatch an equivocation report extrinsic. - fn submit_unsigned_equivocation_report( - equivocation_proof: EquivocationProof, - key_owner_proof: T::KeyOwnerProof, - ) -> DispatchResult; - - /// Fetch the current block author id, if defined. - fn block_author() -> Option; -} - -impl HandleEquivocation for () { - type Offence = GrandpaEquivocationOffence; - type ReportLongevity = (); - - fn report_offence( - _reporters: Vec, - _offence: GrandpaEquivocationOffence, - ) -> Result<(), OffenceError> { - Ok(()) - } - - fn is_known_offence( - _offenders: &[T::KeyOwnerIdentification], - _time_slot: &GrandpaTimeSlot, - ) -> bool { - true - } - - fn submit_unsigned_equivocation_report( - _equivocation_proof: EquivocationProof, - _key_owner_proof: T::KeyOwnerProof, - ) -> DispatchResult { - Ok(()) - } - - fn block_author() -> Option { - None - } -} +// /// A trait with utility methods for handling equivocation reports in GRANDPA. +// /// The offence type is generic, and the trait provides , reporting an offence +// /// triggered by a valid equivocation report, and also for creating and +// /// submitting equivocation report extrinsics (useful only in offchain context). +// pub trait HandleEquivocation { +// /// The offence type used for reporting offences on valid equivocation reports. +// type Offence: GrandpaOffence; + +// /// The longevity, in blocks, that the equivocation report is valid for. When using the staking +// /// pallet this should be equal to the bonding duration (in blocks, not eras). +// type ReportLongevity: Get; + +// /// Report an offence proved by the given reporters. +// fn report_offence( +// reporters: Vec, +// offence: Self::Offence, +// ) -> Result<(), OffenceError>; + +// /// Returns true if all of the offenders at the given time slot have already been reported. +// fn is_known_offence( +// offenders: &[T::KeyOwnerIdentification], +// time_slot: &>::TimeSlot, +// ) -> bool; + +// /// Create and dispatch an equivocation report extrinsic. +// fn submit_unsigned_equivocation_report( +// equivocation_proof: EquivocationProof, +// key_owner_proof: T::KeyOwnerProof, +// ) -> DispatchResult; + +// /// Fetch the current block author id, if defined. +// fn block_author() -> Option; +// } /// Generic equivocation handler. This type implements `HandleEquivocation` /// using existing subsystems that are part of frame (type bounds described /// below) and will dispatch to them directly, it's only purpose is to wire all /// subsystems together. -pub struct EquivocationHandler> { - _phantom: sp_std::marker::PhantomData<(I, R, L, O)>, +pub struct EquivocationHandler { + _phantom: sp_std::marker::PhantomData<(T, R, L)>, } -impl Default for EquivocationHandler { +impl Default for EquivocationHandler { fn default() -> Self { Self { _phantom: Default::default() } } } -impl HandleEquivocation for EquivocationHandler +// We use the authorship pallet to fetch the current block author and use +// `offchain::SendTransactionTypes` for unsigned extrinsic creation and +// submission. +impl EquivocationHandler2 for EquivocationHandler where - // We use the authorship pallet to fetch the current block author and use - // `offchain::SendTransactionTypes` for unsigned extrinsic creation and - // submission. T: Config + pallet_authorship::Config + frame_system::offchain::SendTransactionTypes>, - // A system for reporting offences after valid equivocation reports are - // processed. - R: ReportOffence, - // The longevity (in blocks) that the equivocation report is valid for. When using the staking - // pallet this should be the bonding duration. + R: ReportOffence< + T::AccountId, + T::KeyOwnerIdentification, + GrandpaEquivocationOffence, + >, L: Get, - // The offence type that should be used when reporting. - O: GrandpaOffence, { - type Offence = O; type ReportLongevity = L; - fn report_offence(reporters: Vec, offence: O) -> Result<(), OffenceError> { - R::report_offence(reporters, offence) + type AccountId = T::AccountId; + + type KeyOwnerIdentification = T::KeyOwnerIdentification; + + type Offence = GrandpaEquivocationOffence; + + type EquivocationProof = EquivocationProof; + + type KeyOwnerProof = T::KeyOwnerProof; + + fn report_offence( + _reporters: Vec, + _offence: Self::Offence, + ) -> Result<(), OffenceError> { + Ok(()) } - fn is_known_offence(offenders: &[T::KeyOwnerIdentification], time_slot: &O::TimeSlot) -> bool { - R::is_known_offence(offenders, time_slot) + fn is_known_offence( + _offenders: &[Self::KeyOwnerIdentification], + _time_slot: &>::TimeSlot, + ) -> bool { + true } fn submit_unsigned_equivocation_report( - equivocation_proof: EquivocationProof, - key_owner_proof: T::KeyOwnerProof, + _equivocation_proof: Self::EquivocationProof, + _key_owner_proof: Self::KeyOwnerProof, ) -> DispatchResult { - use frame_system::offchain::SubmitTransaction; - - let call = Call::report_equivocation_unsigned { - equivocation_proof: Box::new(equivocation_proof), - key_owner_proof, - }; - - match SubmitTransaction::>::submit_unsigned_transaction(call.into()) { - Ok(()) => log::info!(target: LOG_TARGET, "Submitted GRANDPA equivocation report.",), - Err(e) => - log::error!(target: LOG_TARGET, "Error submitting equivocation report: {:?}", e,), - } - Ok(()) } - fn block_author() -> Option { - >::author() + /// Fetch the current block author id, if defined. + fn block_author() -> Option { + None } } +// impl HandleEquivocation for EquivocationHandler +// where +// // We use the authorship pallet +// // to fetch the current block +// // author and use +// // `offchain::SendTransactionTypes` for unsigned extrinsic creation and +// // submission. +// T: Config + pallet_authorship::Config + frame_system::offchain::SendTransactionTypes>, +// // A system for reporting offences after valid equivocation reports are +// // processed. +// R: ReportOffence, +// // The longevity (in blocks) that the equivocation report is valid for. When using the staking +// // pallet this should be the bonding duration. +// L: Get, +// // The offence type that should be used when reporting. +// O: GrandpaOffence, +// { +// type Offence = O; +// type ReportLongevity = L; + +// fn report_offence(reporters: Vec, offence: O) -> Result<(), OffenceError> { +// R::report_offence(reporters, offence) +// } + +// fn is_known_offence(offenders: &[T::KeyOwnerIdentification], time_slot: &O::TimeSlot) -> bool { +// R::is_known_offence(offenders, time_slot) +// } + +// fn submit_unsigned_equivocation_report( +// equivocation_proof: EquivocationProof, +// key_owner_proof: T::KeyOwnerProof, +// ) -> DispatchResult { +// use frame_system::offchain::SubmitTransaction; + +// let call = Call::report_equivocation_unsigned { +// equivocation_proof: Box::new(equivocation_proof), +// key_owner_proof, +// }; + +// match SubmitTransaction::>::submit_unsigned_transaction(call.into()) { +// Ok(()) => log::info!(target: LOG_TARGET, "Submitted GRANDPA equivocation report.",), +// Err(e) => +// log::error!(target: LOG_TARGET, "Error submitting equivocation report: {:?}", e,), +// } + +// Ok(()) +// } + +// fn block_author() -> Option { +// >::author() +// } +// } + /// 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 GrandpaTimeSlot { @@ -216,8 +241,7 @@ impl Pallet { // check report staleness is_known_offence::(equivocation_proof, key_owner_proof)?; - let longevity = - >::ReportLongevity::get(); + let longevity = ::ReportLongevity::get(); ValidTransaction::with_tag_prefix("GrandpaEquivocation") // We assign the maximum priority for any equivocation report. @@ -258,10 +282,9 @@ fn is_known_offence( // check if the offence has already been reported, // and if so then we can discard the report. - let time_slot = >::Offence::new_time_slot( - equivocation_proof.set_id(), - equivocation_proof.round(), - ); + // let time_slot = >::Offence::new_time_slot( + let time_slot = + GrandpaTimeSlot { set_id: equivocation_proof.set_id(), round: equivocation_proof.round() }; let is_known_offence = T::HandleEquivocation::is_known_offence(&[offender], &time_slot); @@ -280,51 +303,11 @@ pub struct GrandpaEquivocationOffence { /// 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, + pub validator_count: u32, /// The authority which produced this equivocation. pub offender: FullIdentification, } -/// An interface for types that will be used as GRANDPA offences and must also -/// implement the `Offence` trait. This trait provides a constructor that is -/// provided all available data during processing of GRANDPA equivocations. -pub trait GrandpaOffence: Offence { - /// Create a new GRANDPA offence using the given equivocation details. - fn new( - session_index: SessionIndex, - validator_set_count: u32, - offender: FullIdentification, - set_id: SetId, - round: RoundNumber, - ) -> Self; - - /// Create a new GRANDPA offence time slot. - fn new_time_slot(set_id: SetId, round: RoundNumber) -> Self::TimeSlot; -} - -impl GrandpaOffence - for GrandpaEquivocationOffence -{ - fn new( - session_index: SessionIndex, - validator_set_count: u32, - offender: FullIdentification, - set_id: SetId, - round: RoundNumber, - ) -> Self { - GrandpaEquivocationOffence { - session_index, - validator_set_count, - offender, - time_slot: GrandpaTimeSlot { set_id, round }, - } - } - - fn new_time_slot(set_id: SetId, round: RoundNumber) -> Self::TimeSlot { - GrandpaTimeSlot { set_id, round } - } -} - impl Offence for GrandpaEquivocationOffence { @@ -340,7 +323,7 @@ impl Offence } fn validator_set_count(&self) -> u32 { - self.validator_set_count + self.validator_count } fn time_slot(&self) -> Self::TimeSlot { @@ -349,7 +332,7 @@ impl Offence fn slash_fraction(&self, offenders_count: u32) -> Perbill { // the formula is min((3k / n)^2, 1) - let x = Perbill::from_rational(3 * offenders_count, self.validator_set_count); + let x = Perbill::from_rational(3 * offenders_count, self.validator_count); // _ ^ 2 x.square() } diff --git a/frame/grandpa/src/lib.rs b/frame/grandpa/src/lib.rs index ea534947ddd37..1cffceb170263 100644 --- a/frame/grandpa/src/lib.rs +++ b/frame/grandpa/src/lib.rs @@ -50,7 +50,7 @@ use frame_support::{ use scale_info::TypeInfo; use sp_runtime::{generic::DigestItem, traits::Zero, DispatchResult, KeyTypeId}; use sp_session::{GetSessionNumber, GetValidatorCount}; -use sp_staking::SessionIndex; +use sp_staking::{equivocation::EquivocationHandler2, SessionIndex}; mod default_weights; mod equivocation; @@ -63,10 +63,7 @@ mod mock; #[cfg(all(feature = "std", test))] mod tests; -pub use equivocation::{ - EquivocationHandler, GrandpaEquivocationOffence, GrandpaOffence, GrandpaTimeSlot, - HandleEquivocation, -}; +pub use equivocation::{EquivocationHandler, GrandpaEquivocationOffence, GrandpaTimeSlot}; pub use pallet::*; @@ -113,7 +110,13 @@ pub mod pallet { /// NOTE: when enabling equivocation handling (i.e. this type isn't set to /// `()`) you must use this pallet's `ValidateUnsigned` in the runtime /// definition. - type HandleEquivocation: HandleEquivocation; + type HandleEquivocation: EquivocationHandler2< + AccountId = Self::AccountId, + KeyOwnerProof = Self::KeyOwnerProof, + KeyOwnerIdentification = Self::KeyOwnerIdentification, + Offence = GrandpaEquivocationOffence, + EquivocationProof = EquivocationProof, + >; /// Weights for this pallet. type WeightInfo: WeightInfo; @@ -583,18 +586,15 @@ impl Pallet { return Err(Error::::InvalidEquivocationProof.into()) } - // report to the offences module rewarding the sender. - T::HandleEquivocation::report_offence( - reporter.into_iter().collect(), - >::Offence::new( - session_index, - validator_count, - offender, - set_id, - round, - ), - ) - .map_err(|_| Error::::DuplicateOffenceReport)?; + let offence = GrandpaEquivocationOffence { + time_slot: GrandpaTimeSlot { set_id, round }, + session_index, + offender, + validator_count, + }; + + T::HandleEquivocation::report_offence(reporter.into_iter().collect(), offence) + .map_err(|_| Error::::DuplicateOffenceReport)?; // waive the fee since the report is valid and beneficial Ok(Pays::No.into()) diff --git a/primitives/staking/src/equivocation.rs b/primitives/staking/src/equivocation.rs new file mode 100644 index 0000000000000..e3e245ecd5bf1 --- /dev/null +++ b/primitives/staking/src/equivocation.rs @@ -0,0 +1,67 @@ +use crate::offence::{Offence, OffenceError}; +use sp_core::Get; +use sp_runtime::DispatchResult; + +pub trait EquivocationHandler2 { + /// The longevity, in blocks, that the equivocation report is valid for. When using the staking + /// pallet this should be equal to the bonding duration (in blocks, not eras). + type ReportLongevity: Get; + + type AccountId; + + type Offence: Offence; + + type KeyOwnerIdentification; + + type EquivocationProof; + + type KeyOwnerProof; + + /// Report an offence proved by the given reporters. + fn report_offence( + reporters: Vec, + offence: Self::Offence, + ) -> Result<(), OffenceError>; + + /// Returns true if all of the offenders at the given time slot have already been reported. + fn is_known_offence( + offenders: &[Self::KeyOwnerIdentification], + time_slot: &>::TimeSlot, + ) -> bool; + + /// Create and dispatch an equivocation report extrinsic. + fn submit_unsigned_equivocation_report( + equivocation_proof: Self::EquivocationProof, + key_owner_proof: Self::KeyOwnerProof, + ) -> DispatchResult; + + /// Fetch the current block author id, if defined. + fn block_author() -> Option; +} + +// impl HandleEquivocation2 for () { +// type ReportLongevity = (); +// type AccountId = (); + +// fn report_offence( +// _reporters: Vec, +// _offence: BabeEquivocationOffence, +// ) -> Result<(), OffenceError> { +// Ok(()) +// } + +// fn is_known_offence(_offenders: &[T::KeyOwnerIdentification], _time_slot: &Slot) -> bool { +// true +// } + +// fn submit_unsigned_equivocation_report( +// _equivocation_proof: EquivocationProof, +// _key_owner_proof: T::KeyOwnerProof, +// ) -> DispatchResult { +// Ok(()) +// } + +// fn block_author() -> Option { +// None +// } +// } diff --git a/primitives/staking/src/lib.rs b/primitives/staking/src/lib.rs index 9eb4a4890cdf8..ed238053e7272 100644 --- a/primitives/staking/src/lib.rs +++ b/primitives/staking/src/lib.rs @@ -23,6 +23,7 @@ use sp_runtime::{DispatchError, DispatchResult}; use sp_std::{collections::btree_map::BTreeMap, vec::Vec}; +pub mod equivocation; pub mod offence; /// Simple index type with which we can count sessions. From 5754be642f959c6d61e62c4a6d55e0ea59632d28 Mon Sep 17 00:00:00 2001 From: Davide Galassi Date: Wed, 15 Feb 2023 17:55:46 +0100 Subject: [PATCH 02/33] Improved equivocation trait --- frame/babe/src/equivocation.rs | 25 ++++++------------------- frame/babe/src/lib.rs | 10 +++++----- frame/babe/src/mock.rs | 2 +- primitives/staking/src/equivocation.rs | 17 +++++++++++------ 4 files changed, 23 insertions(+), 31 deletions(-) diff --git a/frame/babe/src/equivocation.rs b/frame/babe/src/equivocation.rs index 1995a32b63809..122047f6cb6e2 100644 --- a/frame/babe/src/equivocation.rs +++ b/frame/babe/src/equivocation.rs @@ -43,8 +43,8 @@ use sp_runtime::{ DispatchResult, Perbill, }; use sp_staking::{ - equivocation::EquivocationHandler2, - offence::{Kind, Offence, OffenceError, ReportOffence}, + equivocation::EquivocationHandler as EquivocationHandlerT, + offence::{Kind, Offence, ReportOffence}, SessionIndex, }; use sp_std::prelude::*; @@ -65,7 +65,7 @@ impl Default for EquivocationHandler { } } -impl EquivocationHandler2 for EquivocationHandler +impl EquivocationHandlerT for EquivocationHandler where // We use the authorship pallet // to fetch the current block @@ -98,19 +98,7 @@ where type KeyOwnerProof = T::KeyOwnerProof; - fn report_offence( - reporters: Vec, - offence: BabeEquivocationOffence, - ) -> Result<(), OffenceError> { - R::report_offence(reporters, offence) - } - - fn is_known_offence( - offenders: &[Self::KeyOwnerIdentification], - time_slot: &>::TimeSlot, - ) -> bool { - R::is_known_offence(offenders, time_slot) - } + type ReportOffence = R; fn submit_unsigned_equivocation_report( equivocation_proof: Self::EquivocationProof, @@ -160,8 +148,7 @@ impl Pallet { // check report staleness is_known_offence::(equivocation_proof, key_owner_proof)?; - let longevity = - ::ReportLongevity::get(); + let longevity = ::ReportLongevity::get(); ValidTransaction::with_tag_prefix("BabeEquivocation") // We assign the maximum priority for any equivocation report. @@ -197,7 +184,7 @@ fn is_known_offence( .ok_or(InvalidTransaction::BadProof)?; // check if the offence has already been reported, and if so then we can discard the report. - if T::HandleEquivocation2::is_known_offence(&[offender], &equivocation_proof.slot) { + if T::HandleEquivocation::is_known_offence(&[offender], &equivocation_proof.slot) { Err(InvalidTransaction::Stale.into()) } else { Ok(()) diff --git a/frame/babe/src/lib.rs b/frame/babe/src/lib.rs index ce9ab68a0caed..4eed908507477 100644 --- a/frame/babe/src/lib.rs +++ b/frame/babe/src/lib.rs @@ -39,7 +39,7 @@ use sp_runtime::{ ConsensusEngineId, KeyTypeId, Permill, }; use sp_session::{GetSessionNumber, GetValidatorCount}; -use sp_staking::equivocation::EquivocationHandler2; +use sp_staking::equivocation::EquivocationHandler as EquivocationHandlerT; use sp_std::prelude::*; use sp_consensus_babe::{ @@ -172,7 +172,7 @@ pub mod pallet { /// NOTE: when enabling equivocation handling (i.e. this type isn't set to /// `()`) you must use this pallet's `ValidateUnsigned` in the runtime /// definition. - type HandleEquivocation2: EquivocationHandler2< + type HandleEquivocation: EquivocationHandlerT< AccountId = Self::AccountId, KeyOwnerProof = Self::KeyOwnerProof, KeyOwnerIdentification = Self::KeyOwnerIdentification, @@ -447,7 +447,7 @@ pub mod pallet { ensure_none(origin)?; Self::do_report_equivocation( - T::HandleEquivocation2::block_author(), + T::HandleEquivocation::block_author(), *equivocation_proof, key_owner_proof, ) @@ -857,7 +857,7 @@ impl Pallet { let offence = BabeEquivocationOffence { slot, validator_set_count, offender, session_index }; - T::HandleEquivocation2::report_offence(reporter.into_iter().collect(), offence) + T::HandleEquivocation::report_offence(reporter.into_iter().collect(), offence) .map_err(|_| Error::::DuplicateOffenceReport)?; // waive the fee since the report is valid and beneficial @@ -873,7 +873,7 @@ impl Pallet { key_owner_proof: T::KeyOwnerProof, ) -> Option<()> { // TODO: this should be submitted anyway...??? - T::HandleEquivocation2::submit_unsigned_equivocation_report( + T::HandleEquivocation::submit_unsigned_equivocation_report( equivocation_proof, key_owner_proof, ) diff --git a/frame/babe/src/mock.rs b/frame/babe/src/mock.rs index c64fc873b9ead..ba1c1bcb45d09 100644 --- a/frame/babe/src/mock.rs +++ b/frame/babe/src/mock.rs @@ -234,7 +234,7 @@ impl Config for Test { AuthorityId, )>>::IdentificationTuple; - type HandleEquivocation2 = super::EquivocationHandler; + type HandleEquivocation = super::EquivocationHandler; type WeightInfo = (); type MaxAuthorities = ConstU32<10>; diff --git a/primitives/staking/src/equivocation.rs b/primitives/staking/src/equivocation.rs index e3e245ecd5bf1..1a379898cf96b 100644 --- a/primitives/staking/src/equivocation.rs +++ b/primitives/staking/src/equivocation.rs @@ -1,8 +1,8 @@ -use crate::offence::{Offence, OffenceError}; +use crate::offence::{Offence, OffenceError, ReportOffence}; use sp_core::Get; use sp_runtime::DispatchResult; -pub trait EquivocationHandler2 { +pub trait EquivocationHandler { /// The longevity, in blocks, that the equivocation report is valid for. When using the staking /// pallet this should be equal to the bonding duration (in blocks, not eras). type ReportLongevity: Get; @@ -17,17 +17,21 @@ pub trait EquivocationHandler2 { type KeyOwnerProof; - /// Report an offence proved by the given reporters. + type ReportOffence: ReportOffence; + fn report_offence( reporters: Vec, offence: Self::Offence, - ) -> Result<(), OffenceError>; + ) -> Result<(), OffenceError> { + Self::ReportOffence::report_offence(reporters, offence) + } - /// Returns true if all of the offenders at the given time slot have already been reported. fn is_known_offence( offenders: &[Self::KeyOwnerIdentification], time_slot: &>::TimeSlot, - ) -> bool; + ) -> bool { + Self::ReportOffence::is_known_offence(offenders, time_slot) + } /// Create and dispatch an equivocation report extrinsic. fn submit_unsigned_equivocation_report( @@ -42,6 +46,7 @@ pub trait EquivocationHandler2 { // impl HandleEquivocation2 for () { // type ReportLongevity = (); // type AccountId = (); +// type ReportOffence = (); // fn report_offence( // _reporters: Vec, From 9029a5cb0024831532a02579ae0d55c2a215a8d8 Mon Sep 17 00:00:00 2001 From: Davide Galassi Date: Wed, 15 Feb 2023 19:15:52 +0100 Subject: [PATCH 03/33] Fix grandpa equivocation implementation --- frame/grandpa/src/equivocation.rs | 135 +++++-------------------- frame/grandpa/src/lib.rs | 4 +- frame/grandpa/src/mock.rs | 3 +- primitives/staking/src/equivocation.rs | 4 +- 4 files changed, 29 insertions(+), 117 deletions(-) diff --git a/frame/grandpa/src/equivocation.rs b/frame/grandpa/src/equivocation.rs index 740e62e03832b..b86d67d71feaf 100644 --- a/frame/grandpa/src/equivocation.rs +++ b/frame/grandpa/src/equivocation.rs @@ -48,47 +48,13 @@ use sp_runtime::{ DispatchResult, Perbill, }; use sp_staking::{ - equivocation::EquivocationHandler2, - offence::{Kind, Offence, OffenceError, ReportOffence}, + equivocation::EquivocationHandler as EquivocationHandlerT, + offence::{Kind, Offence, ReportOffence}, SessionIndex, }; use super::{Call, Config, Pallet, LOG_TARGET}; -// /// A trait with utility methods for handling equivocation reports in GRANDPA. -// /// The offence type is generic, and the trait provides , reporting an offence -// /// triggered by a valid equivocation report, and also for creating and -// /// submitting equivocation report extrinsics (useful only in offchain context). -// pub trait HandleEquivocation { -// /// The offence type used for reporting offences on valid equivocation reports. -// type Offence: GrandpaOffence; - -// /// The longevity, in blocks, that the equivocation report is valid for. When using the staking -// /// pallet this should be equal to the bonding duration (in blocks, not eras). -// type ReportLongevity: Get; - -// /// Report an offence proved by the given reporters. -// fn report_offence( -// reporters: Vec, -// offence: Self::Offence, -// ) -> Result<(), OffenceError>; - -// /// Returns true if all of the offenders at the given time slot have already been reported. -// fn is_known_offence( -// offenders: &[T::KeyOwnerIdentification], -// time_slot: &>::TimeSlot, -// ) -> bool; - -// /// Create and dispatch an equivocation report extrinsic. -// fn submit_unsigned_equivocation_report( -// equivocation_proof: EquivocationProof, -// key_owner_proof: T::KeyOwnerProof, -// ) -> DispatchResult; - -// /// Fetch the current block author id, if defined. -// fn block_author() -> Option; -// } - /// Generic equivocation handler. This type implements `HandleEquivocation` /// using existing subsystems that are part of frame (type bounds described /// below) and will dispatch to them directly, it's only purpose is to wire all @@ -106,7 +72,7 @@ impl Default for EquivocationHandler { // We use the authorship pallet to fetch the current block author and use // `offchain::SendTransactionTypes` for unsigned extrinsic creation and // submission. -impl EquivocationHandler2 for EquivocationHandler +impl EquivocationHandlerT for EquivocationHandler where T: Config + pallet_authorship::Config + frame_system::offchain::SendTransactionTypes>, R: ReportOffence< @@ -124,90 +90,37 @@ where type Offence = GrandpaEquivocationOffence; - type EquivocationProof = EquivocationProof; + type EquivocationProof = EquivocationProof; type KeyOwnerProof = T::KeyOwnerProof; - fn report_offence( - _reporters: Vec, - _offence: Self::Offence, - ) -> Result<(), OffenceError> { - Ok(()) - } - - fn is_known_offence( - _offenders: &[Self::KeyOwnerIdentification], - _time_slot: &>::TimeSlot, - ) -> bool { - true - } + type ReportOffence = R; fn submit_unsigned_equivocation_report( - _equivocation_proof: Self::EquivocationProof, - _key_owner_proof: Self::KeyOwnerProof, + equivocation_proof: Self::EquivocationProof, + key_owner_proof: Self::KeyOwnerProof, ) -> DispatchResult { + use frame_system::offchain::SubmitTransaction; + + let call = Call::report_equivocation_unsigned { + equivocation_proof: Box::new(equivocation_proof), + key_owner_proof, + }; + + match SubmitTransaction::>::submit_unsigned_transaction(call.into()) { + Ok(()) => log::info!(target: LOG_TARGET, "Submitted GRANDPA equivocation report.",), + Err(e) => + log::error!(target: LOG_TARGET, "Error submitting equivocation report: {:?}", e,), + } + Ok(()) } - /// Fetch the current block author id, if defined. fn block_author() -> Option { - None + >::author() } } -// impl HandleEquivocation for EquivocationHandler -// where -// // We use the authorship pallet -// // to fetch the current block -// // author and use -// // `offchain::SendTransactionTypes` for unsigned extrinsic creation and -// // submission. -// T: Config + pallet_authorship::Config + frame_system::offchain::SendTransactionTypes>, -// // A system for reporting offences after valid equivocation reports are -// // processed. -// R: ReportOffence, -// // The longevity (in blocks) that the equivocation report is valid for. When using the staking -// // pallet this should be the bonding duration. -// L: Get, -// // The offence type that should be used when reporting. -// O: GrandpaOffence, -// { -// type Offence = O; -// type ReportLongevity = L; - -// fn report_offence(reporters: Vec, offence: O) -> Result<(), OffenceError> { -// R::report_offence(reporters, offence) -// } - -// fn is_known_offence(offenders: &[T::KeyOwnerIdentification], time_slot: &O::TimeSlot) -> bool { -// R::is_known_offence(offenders, time_slot) -// } - -// fn submit_unsigned_equivocation_report( -// equivocation_proof: EquivocationProof, -// key_owner_proof: T::KeyOwnerProof, -// ) -> DispatchResult { -// use frame_system::offchain::SubmitTransaction; - -// let call = Call::report_equivocation_unsigned { -// equivocation_proof: Box::new(equivocation_proof), -// key_owner_proof, -// }; - -// match SubmitTransaction::>::submit_unsigned_transaction(call.into()) { -// Ok(()) => log::info!(target: LOG_TARGET, "Submitted GRANDPA equivocation report.",), -// Err(e) => -// log::error!(target: LOG_TARGET, "Error submitting equivocation report: {:?}", e,), -// } - -// Ok(()) -// } - -// fn block_author() -> Option { -// >::author() -// } -// } - /// 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 GrandpaTimeSlot { @@ -241,7 +154,7 @@ impl Pallet { // check report staleness is_known_offence::(equivocation_proof, key_owner_proof)?; - let longevity = ::ReportLongevity::get(); + let longevity = ::ReportLongevity::get(); ValidTransaction::with_tag_prefix("GrandpaEquivocation") // We assign the maximum priority for any equivocation report. @@ -280,9 +193,7 @@ fn is_known_offence( let offender = T::KeyOwnerProofSystem::check_proof(key, key_owner_proof.clone()) .ok_or(InvalidTransaction::BadProof)?; - // check if the offence has already been reported, - // and if so then we can discard the report. - // let time_slot = >::Offence::new_time_slot( + // Check if the offence has already been reported, and if so then we can discard the report. let time_slot = GrandpaTimeSlot { set_id: equivocation_proof.set_id(), round: equivocation_proof.round() }; diff --git a/frame/grandpa/src/lib.rs b/frame/grandpa/src/lib.rs index 1cffceb170263..8305d188449b5 100644 --- a/frame/grandpa/src/lib.rs +++ b/frame/grandpa/src/lib.rs @@ -50,7 +50,7 @@ use frame_support::{ use scale_info::TypeInfo; use sp_runtime::{generic::DigestItem, traits::Zero, DispatchResult, KeyTypeId}; use sp_session::{GetSessionNumber, GetValidatorCount}; -use sp_staking::{equivocation::EquivocationHandler2, SessionIndex}; +use sp_staking::{equivocation::EquivocationHandler as EquivocationHandlerT, SessionIndex}; mod default_weights; mod equivocation; @@ -110,7 +110,7 @@ pub mod pallet { /// NOTE: when enabling equivocation handling (i.e. this type isn't set to /// `()`) you must use this pallet's `ValidateUnsigned` in the runtime /// definition. - type HandleEquivocation: EquivocationHandler2< + type HandleEquivocation: EquivocationHandlerT< AccountId = Self::AccountId, KeyOwnerProof = Self::KeyOwnerProof, KeyOwnerIdentification = Self::KeyOwnerIdentification, diff --git a/frame/grandpa/src/mock.rs b/frame/grandpa/src/mock.rs index 7d54966a498a6..1441610817384 100644 --- a/frame/grandpa/src/mock.rs +++ b/frame/grandpa/src/mock.rs @@ -235,8 +235,7 @@ impl Config for Test { AuthorityId, )>>::IdentificationTuple; - type HandleEquivocation = - super::EquivocationHandler; + type HandleEquivocation = super::EquivocationHandler; type WeightInfo = (); type MaxAuthorities = ConstU32<100>; diff --git a/primitives/staking/src/equivocation.rs b/primitives/staking/src/equivocation.rs index 1a379898cf96b..dbe83cdd4b258 100644 --- a/primitives/staking/src/equivocation.rs +++ b/primitives/staking/src/equivocation.rs @@ -40,7 +40,9 @@ pub trait EquivocationHandler { ) -> DispatchResult; /// Fetch the current block author id, if defined. - fn block_author() -> Option; + fn block_author() -> Option { + None + } } // impl HandleEquivocation2 for () { From 0e04ca01d291eab3168da0b9d7140af2bbfd096b Mon Sep 17 00:00:00 2001 From: Davide Galassi Date: Thu, 16 Feb 2023 10:37:35 +0100 Subject: [PATCH 04/33] Remove some cruft --- frame/babe/src/equivocation.rs | 13 ++----- frame/grandpa/src/equivocation.rs | 3 +- primitives/staking/src/equivocation.rs | 47 +++++++++++--------------- 3 files changed, 23 insertions(+), 40 deletions(-) diff --git a/frame/babe/src/equivocation.rs b/frame/babe/src/equivocation.rs index 122047f6cb6e2..951e6e9106ade 100644 --- a/frame/babe/src/equivocation.rs +++ b/frame/babe/src/equivocation.rs @@ -65,25 +65,18 @@ impl Default for EquivocationHandler { } } +// We use the authorship pallet to fetch the current block author and use +// `offchain::SendTransactionTypes` for unsigned extrinsic creation and +// submission. impl EquivocationHandlerT for EquivocationHandler where - // We use the authorship pallet - // to fetch the current block - // author and use - // `offchain::SendTransactionTypes` - // for unsigned extrinsic - // creation and submission. T: Config + pallet_authorship::Config + frame_system::offchain::SendTransactionTypes>, - // A system for reporting offences after valid equivocation reports are - // processed. R: ReportOffence< T::AccountId, T::KeyOwnerIdentification, BabeEquivocationOffence, >, - // The longevity (in blocks) that the equivocation report is valid for. When using the staking - // pallet this should be the bonding duration. L: Get, { type ReportLongevity = L; diff --git a/frame/grandpa/src/equivocation.rs b/frame/grandpa/src/equivocation.rs index b86d67d71feaf..25961f4673c36 100644 --- a/frame/grandpa/src/equivocation.rs +++ b/frame/grandpa/src/equivocation.rs @@ -206,8 +206,7 @@ fn is_known_offence( } } -/// A grandpa equivocation offence report. -#[allow(dead_code)] +/// A GRANDPA equivocation offence report. pub struct GrandpaEquivocationOffence { /// Time slot at which this incident happened. pub time_slot: GrandpaTimeSlot, diff --git a/primitives/staking/src/equivocation.rs b/primitives/staking/src/equivocation.rs index dbe83cdd4b258..dd13f1f679f7a 100644 --- a/primitives/staking/src/equivocation.rs +++ b/primitives/staking/src/equivocation.rs @@ -1,6 +1,7 @@ use crate::offence::{Offence, OffenceError, ReportOffence}; use sp_core::Get; -use sp_runtime::DispatchResult; +use sp_runtime::{DispatchError, DispatchResult}; +use sp_std::vec::Vec; pub trait EquivocationHandler { /// The longevity, in blocks, that the equivocation report is valid for. When using the staking @@ -35,9 +36,11 @@ pub trait EquivocationHandler { /// Create and dispatch an equivocation report extrinsic. fn submit_unsigned_equivocation_report( - equivocation_proof: Self::EquivocationProof, - key_owner_proof: Self::KeyOwnerProof, - ) -> DispatchResult; + _equivocation_proof: Self::EquivocationProof, + _key_owner_proof: Self::KeyOwnerProof, + ) -> DispatchResult { + Err(DispatchError::Other("Not implemented")) + } /// Fetch the current block author id, if defined. fn block_author() -> Option { @@ -45,30 +48,18 @@ pub trait EquivocationHandler { } } -// impl HandleEquivocation2 for () { +// impl, Id> EquivocationHandler for () { // type ReportLongevity = (); + // type AccountId = (); -// type ReportOffence = (); - -// fn report_offence( -// _reporters: Vec, -// _offence: BabeEquivocationOffence, -// ) -> Result<(), OffenceError> { -// Ok(()) -// } - -// fn is_known_offence(_offenders: &[T::KeyOwnerIdentification], _time_slot: &Slot) -> bool { -// true -// } - -// fn submit_unsigned_equivocation_report( -// _equivocation_proof: EquivocationProof, -// _key_owner_proof: T::KeyOwnerProof, -// ) -> DispatchResult { -// Ok(()) -// } - -// fn block_author() -> Option { -// None -// } + +// type Offence = T; // Offence; + +// type KeyOwnerIdentification = Id; + +// type EquivocationProof = (); + +// type KeyOwnerProof = (); + +// type ReportOffence = (); // } From d8c259893fe4b8e5425fd34335cc36565cc8dc0a Mon Sep 17 00:00:00 2001 From: Davide Galassi Date: Thu, 16 Feb 2023 10:54:54 +0100 Subject: [PATCH 05/33] Remove some more cruft --- frame/babe/src/equivocation.rs | 29 ++++++++++++++++++-------- frame/grandpa/src/equivocation.rs | 29 ++++++++++++++++++-------- primitives/staking/src/equivocation.rs | 20 ++---------------- 3 files changed, 42 insertions(+), 36 deletions(-) diff --git a/frame/babe/src/equivocation.rs b/frame/babe/src/equivocation.rs index 951e6e9106ade..23bcc436a1de1 100644 --- a/frame/babe/src/equivocation.rs +++ b/frame/babe/src/equivocation.rs @@ -55,15 +55,8 @@ use crate::{Call, Config, Pallet, LOG_TARGET}; /// using existing subsystems that are part of frame (type bounds described /// below) and will dispatch to them directly, it's only purpose is to wire all /// subsystems together. -pub struct EquivocationHandler { - _phantom: sp_std::marker::PhantomData<(I, R, L)>, -} - -impl Default for EquivocationHandler { - fn default() -> Self { - Self { _phantom: Default::default() } - } -} +#[derive(Default)] +pub struct EquivocationHandler(sp_std::marker::PhantomData<(I, R, L)>); // We use the authorship pallet to fetch the current block author and use // `offchain::SendTransactionTypes` for unsigned extrinsic creation and @@ -118,6 +111,24 @@ where } } +struct NullHandler(sp_std::marker::PhantomData); + +impl EquivocationHandlerT for NullHandler { + type ReportLongevity = (); + + type AccountId = T::AccountId; + + type KeyOwnerIdentification = T::KeyOwnerIdentification; + + type Offence = BabeEquivocationOffence; + + type EquivocationProof = EquivocationProof; + + type KeyOwnerProof = T::KeyOwnerProof; + + type ReportOffence = (); +} + /// 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 diff --git a/frame/grandpa/src/equivocation.rs b/frame/grandpa/src/equivocation.rs index 25961f4673c36..8d837cc57d4a1 100644 --- a/frame/grandpa/src/equivocation.rs +++ b/frame/grandpa/src/equivocation.rs @@ -59,15 +59,8 @@ use super::{Call, Config, Pallet, LOG_TARGET}; /// using existing subsystems that are part of frame (type bounds described /// below) and will dispatch to them directly, it's only purpose is to wire all /// subsystems together. -pub struct EquivocationHandler { - _phantom: sp_std::marker::PhantomData<(T, R, L)>, -} - -impl Default for EquivocationHandler { - fn default() -> Self { - Self { _phantom: Default::default() } - } -} +#[derive(Default)] +pub struct EquivocationHandler(sp_std::marker::PhantomData<(T, R, L)>); // We use the authorship pallet to fetch the current block author and use // `offchain::SendTransactionTypes` for unsigned extrinsic creation and @@ -121,6 +114,24 @@ where } } +struct NullHandler(sp_std::marker::PhantomData); + +impl EquivocationHandlerT for NullHandler { + type ReportLongevity = (); + + type AccountId = T::AccountId; + + type KeyOwnerIdentification = T::KeyOwnerIdentification; + + type Offence = GrandpaEquivocationOffence; + + type EquivocationProof = EquivocationProof; + + type KeyOwnerProof = T::KeyOwnerProof; + + type ReportOffence = (); +} + /// 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 GrandpaTimeSlot { diff --git a/primitives/staking/src/equivocation.rs b/primitives/staking/src/equivocation.rs index dd13f1f679f7a..c71bf85569c7c 100644 --- a/primitives/staking/src/equivocation.rs +++ b/primitives/staking/src/equivocation.rs @@ -1,6 +1,6 @@ use crate::offence::{Offence, OffenceError, ReportOffence}; use sp_core::Get; -use sp_runtime::{DispatchError, DispatchResult}; +use sp_runtime::DispatchResult; use sp_std::vec::Vec; pub trait EquivocationHandler { @@ -39,7 +39,7 @@ pub trait EquivocationHandler { _equivocation_proof: Self::EquivocationProof, _key_owner_proof: Self::KeyOwnerProof, ) -> DispatchResult { - Err(DispatchError::Other("Not implemented")) + Ok(()) } /// Fetch the current block author id, if defined. @@ -47,19 +47,3 @@ pub trait EquivocationHandler { None } } - -// impl, Id> EquivocationHandler for () { -// type ReportLongevity = (); - -// type AccountId = (); - -// type Offence = T; // Offence; - -// type KeyOwnerIdentification = Id; - -// type EquivocationProof = (); - -// type KeyOwnerProof = (); - -// type ReportOffence = (); -// } From 1674a48606051aa972d24c1e3dcbea6f13b39cd8 Mon Sep 17 00:00:00 2001 From: Davide Galassi Date: Thu, 16 Feb 2023 11:33:00 +0100 Subject: [PATCH 06/33] More generic naming --- frame/babe/src/equivocation.rs | 21 ++++++++++----------- frame/babe/src/lib.rs | 4 ++-- primitives/staking/src/equivocation.rs | 26 +++++++++++++------------- 3 files changed, 25 insertions(+), 26 deletions(-) diff --git a/frame/babe/src/equivocation.rs b/frame/babe/src/equivocation.rs index 23bcc436a1de1..beed7b7241fa2 100644 --- a/frame/babe/src/equivocation.rs +++ b/frame/babe/src/equivocation.rs @@ -64,7 +64,6 @@ pub struct EquivocationHandler(sp_std::marker::PhantomData<(I, R, L)>); impl EquivocationHandlerT for EquivocationHandler where T: Config + pallet_authorship::Config + frame_system::offchain::SendTransactionTypes>, - R: ReportOffence< T::AccountId, T::KeyOwnerIdentification, @@ -72,13 +71,11 @@ where >, L: Get, { - type ReportLongevity = L; - - type AccountId = T::AccountId; + type ReporterId = T::AccountId; - type KeyOwnerIdentification = T::KeyOwnerIdentification; + type OffenderId = T::KeyOwnerIdentification; - type Offence = BabeEquivocationOffence; + type Offence = BabeEquivocationOffence; type EquivocationProof = EquivocationProof; @@ -86,6 +83,8 @@ where type ReportOffence = R; + type ReportLongevity = L; + fn submit_unsigned_equivocation_report( equivocation_proof: Self::EquivocationProof, key_owner_proof: Self::KeyOwnerProof, @@ -114,19 +113,19 @@ where struct NullHandler(sp_std::marker::PhantomData); impl EquivocationHandlerT for NullHandler { - type ReportLongevity = (); - - type AccountId = T::AccountId; + type ReporterId = T::AccountId; - type KeyOwnerIdentification = T::KeyOwnerIdentification; + type OffenderId = T::KeyOwnerIdentification; - type Offence = BabeEquivocationOffence; + type Offence = BabeEquivocationOffence; type EquivocationProof = EquivocationProof; type KeyOwnerProof = T::KeyOwnerProof; type ReportOffence = (); + + type ReportLongevity = (); } /// Methods for the `ValidateUnsigned` implementation: diff --git a/frame/babe/src/lib.rs b/frame/babe/src/lib.rs index 4eed908507477..2fb2688c8f6a8 100644 --- a/frame/babe/src/lib.rs +++ b/frame/babe/src/lib.rs @@ -173,9 +173,9 @@ pub mod pallet { /// `()`) you must use this pallet's `ValidateUnsigned` in the runtime /// definition. type HandleEquivocation: EquivocationHandlerT< - AccountId = Self::AccountId, + ReporterId = Self::AccountId, + OffenderId = Self::KeyOwnerIdentification, KeyOwnerProof = Self::KeyOwnerProof, - KeyOwnerIdentification = Self::KeyOwnerIdentification, Offence = BabeEquivocationOffence, EquivocationProof = EquivocationProof, >; diff --git a/primitives/staking/src/equivocation.rs b/primitives/staking/src/equivocation.rs index c71bf85569c7c..6b90fc063a201 100644 --- a/primitives/staking/src/equivocation.rs +++ b/primitives/staking/src/equivocation.rs @@ -4,32 +4,32 @@ use sp_runtime::DispatchResult; use sp_std::vec::Vec; pub trait EquivocationHandler { - /// The longevity, in blocks, that the equivocation report is valid for. When using the staking - /// pallet this should be equal to the bonding duration (in blocks, not eras). - type ReportLongevity: Get; - - type AccountId; - - type Offence: Offence; + type ReporterId; - type KeyOwnerIdentification; + type OffenderId; type EquivocationProof; type KeyOwnerProof; - type ReportOffence: ReportOffence; + type Offence: Offence; + + type ReportOffence: ReportOffence; + + /// The longevity, in blocks, that the equivocation report is valid for. When using the staking + /// pallet this should be equal to the bonding duration (in blocks, not eras). + type ReportLongevity: Get; fn report_offence( - reporters: Vec, + reporters: Vec, offence: Self::Offence, ) -> Result<(), OffenceError> { Self::ReportOffence::report_offence(reporters, offence) } fn is_known_offence( - offenders: &[Self::KeyOwnerIdentification], - time_slot: &>::TimeSlot, + offenders: &[Self::OffenderId], + time_slot: &>::TimeSlot, ) -> bool { Self::ReportOffence::is_known_offence(offenders, time_slot) } @@ -43,7 +43,7 @@ pub trait EquivocationHandler { } /// Fetch the current block author id, if defined. - fn block_author() -> Option { + fn block_author() -> Option { None } } From 6f0feffe193d1a0537aa28d17c2d452099179925 Mon Sep 17 00:00:00 2001 From: Davide Galassi Date: Thu, 16 Feb 2023 19:33:25 +0100 Subject: [PATCH 07/33] Simplification of offences manipilation --- frame/babe/src/equivocation.rs | 50 +++++++++------------- frame/babe/src/lib.rs | 24 +++++------ frame/grandpa/src/equivocation.rs | 59 ++++++++++++-------------- frame/grandpa/src/lib.rs | 15 +++---- frame/offences/src/lib.rs | 17 ++++---- frame/offences/src/mock.rs | 16 ++++--- frame/offences/src/tests.rs | 46 ++++++++++---------- frame/staking/src/lib.rs | 13 +++--- primitives/staking/src/equivocation.rs | 33 ++++++-------- primitives/staking/src/offence.rs | 30 +++++++------ 10 files changed, 141 insertions(+), 162 deletions(-) diff --git a/frame/babe/src/equivocation.rs b/frame/babe/src/equivocation.rs index beed7b7241fa2..05942563c0776 100644 --- a/frame/babe/src/equivocation.rs +++ b/frame/babe/src/equivocation.rs @@ -64,20 +64,12 @@ pub struct EquivocationHandler(sp_std::marker::PhantomData<(I, R, L)>); impl EquivocationHandlerT for EquivocationHandler where T: Config + pallet_authorship::Config + frame_system::offchain::SendTransactionTypes>, - R: ReportOffence< - T::AccountId, - T::KeyOwnerIdentification, - BabeEquivocationOffence, - >, + R: ReportOffence>, L: Get, { - type ReporterId = T::AccountId; + type Offence = BabeEquivocationOffence; - type OffenderId = T::KeyOwnerIdentification; - - type Offence = BabeEquivocationOffence; - - type EquivocationProof = EquivocationProof; + type OffenceProof = EquivocationProof; type KeyOwnerProof = T::KeyOwnerProof; @@ -85,8 +77,8 @@ where type ReportLongevity = L; - fn submit_unsigned_equivocation_report( - equivocation_proof: Self::EquivocationProof, + fn submit_offence_proof( + equivocation_proof: Self::OffenceProof, key_owner_proof: Self::KeyOwnerProof, ) -> DispatchResult { use frame_system::offchain::SubmitTransaction; @@ -113,18 +105,10 @@ where struct NullHandler(sp_std::marker::PhantomData); impl EquivocationHandlerT for NullHandler { - type ReporterId = T::AccountId; - - type OffenderId = T::KeyOwnerIdentification; - - type Offence = BabeEquivocationOffence; - - type EquivocationProof = EquivocationProof; - - type KeyOwnerProof = T::KeyOwnerProof; - + type Offence = BabeEquivocationOffence; + type OffenceProof = (); + type KeyOwnerProof = (); type ReportOffence = (); - type ReportLongevity = (); } @@ -197,7 +181,7 @@ fn is_known_offence( /// A BABE equivocation offence report. /// /// When a validator released two or more blocks at the same slot. -pub struct BabeEquivocationOffence { +pub struct BabeEquivocationOffence { /// A babe slot in which this incident happened. pub slot: Slot, /// The session index in which the incident happened. @@ -205,19 +189,25 @@ pub struct BabeEquivocationOffence { /// The size of the validator set at the time of the offence. pub validator_set_count: u32, /// The authority that produced the equivocation. - pub offender: FullIdentification, + pub offender: O, + /// The offence reporter. + pub reporter: Option, } -impl Offence - for BabeEquivocationOffence -{ +impl Offence for BabeEquivocationOffence { const ID: Kind = *b"babe:equivocatio"; type TimeSlot = Slot; + type Offender = O; + type Reporter = R; - fn offenders(&self) -> Vec { + fn offenders(&self) -> Vec { vec![self.offender.clone()] } + fn reporters(&self) -> Vec { + self.reporter.clone().into_iter().collect() + } + fn session_index(&self) -> SessionIndex { self.session_index } diff --git a/frame/babe/src/lib.rs b/frame/babe/src/lib.rs index 2fb2688c8f6a8..d73e98fe74022 100644 --- a/frame/babe/src/lib.rs +++ b/frame/babe/src/lib.rs @@ -173,11 +173,9 @@ pub mod pallet { /// `()`) you must use this pallet's `ValidateUnsigned` in the runtime /// definition. type HandleEquivocation: EquivocationHandlerT< - ReporterId = Self::AccountId, - OffenderId = Self::KeyOwnerIdentification, KeyOwnerProof = Self::KeyOwnerProof, - Offence = BabeEquivocationOffence, - EquivocationProof = EquivocationProof, + Offence = BabeEquivocationOffence, + OffenceProof = EquivocationProof, >; /// Helper for weights computations @@ -854,10 +852,15 @@ impl Pallet { let offender = T::KeyOwnerProofSystem::check_proof(key, key_owner_proof) .ok_or(Error::::InvalidKeyOwnershipProof)?; - let offence = - BabeEquivocationOffence { slot, validator_set_count, offender, session_index }; + let offence = BabeEquivocationOffence { + slot, + validator_set_count, + offender, + session_index, + reporter, + }; - T::HandleEquivocation::report_offence(reporter.into_iter().collect(), offence) + T::HandleEquivocation::report_offence(offence) .map_err(|_| Error::::DuplicateOffenceReport)?; // waive the fee since the report is valid and beneficial @@ -872,12 +875,7 @@ impl Pallet { equivocation_proof: EquivocationProof, key_owner_proof: T::KeyOwnerProof, ) -> Option<()> { - // TODO: this should be submitted anyway...??? - T::HandleEquivocation::submit_unsigned_equivocation_report( - equivocation_proof, - key_owner_proof, - ) - .ok() + T::HandleEquivocation::submit_offence_proof(equivocation_proof, key_owner_proof).ok() } } diff --git a/frame/grandpa/src/equivocation.rs b/frame/grandpa/src/equivocation.rs index 8d837cc57d4a1..5f0914d6dd599 100644 --- a/frame/grandpa/src/equivocation.rs +++ b/frame/grandpa/src/equivocation.rs @@ -68,29 +68,21 @@ pub struct EquivocationHandler(sp_std::marker::PhantomData<(T, R, L)>); impl EquivocationHandlerT for EquivocationHandler where T: Config + pallet_authorship::Config + frame_system::offchain::SendTransactionTypes>, - R: ReportOffence< - T::AccountId, - T::KeyOwnerIdentification, - GrandpaEquivocationOffence, - >, + R: ReportOffence>, L: Get, { - type ReportLongevity = L; - - type AccountId = T::AccountId; - - type KeyOwnerIdentification = T::KeyOwnerIdentification; + type Offence = GrandpaEquivocationOffence; - type Offence = GrandpaEquivocationOffence; - - type EquivocationProof = EquivocationProof; + type OffenceProof = EquivocationProof; type KeyOwnerProof = T::KeyOwnerProof; type ReportOffence = R; - fn submit_unsigned_equivocation_report( - equivocation_proof: Self::EquivocationProof, + type ReportLongevity = L; + + fn submit_offence_proof( + equivocation_proof: Self::OffenceProof, key_owner_proof: Self::KeyOwnerProof, ) -> DispatchResult { use frame_system::offchain::SubmitTransaction; @@ -109,7 +101,7 @@ where Ok(()) } - fn block_author() -> Option { + fn block_author() -> Option { >::author() } } @@ -117,19 +109,14 @@ where struct NullHandler(sp_std::marker::PhantomData); impl EquivocationHandlerT for NullHandler { - type ReportLongevity = (); - - type AccountId = T::AccountId; - - type KeyOwnerIdentification = T::KeyOwnerIdentification; - - type Offence = GrandpaEquivocationOffence; - - type EquivocationProof = EquivocationProof; - - type KeyOwnerProof = T::KeyOwnerProof; + // TODO: is possible to get rid of this? Like defining a default dummy Offence + // OR even better get rid of this EquivocationHandler trait... + type Offence = GrandpaEquivocationOffence; + type OffenceProof = (); + type KeyOwnerProof = (); type ReportOffence = (); + type ReportLongevity = (); } /// A round number and set id which point on the time of an offence. @@ -218,7 +205,7 @@ fn is_known_offence( } /// A GRANDPA equivocation offence report. -pub struct GrandpaEquivocationOffence { +pub struct GrandpaEquivocationOffence { /// Time slot at which this incident happened. pub time_slot: GrandpaTimeSlot, /// The session index in which the incident happened. @@ -226,19 +213,25 @@ pub struct GrandpaEquivocationOffence { /// The size of the validator set at the time of the offence. pub validator_count: u32, /// The authority which produced this equivocation. - pub offender: FullIdentification, + pub offender: O, + /// The offence reporter. + pub reporter: Option, } -impl Offence - for GrandpaEquivocationOffence -{ +impl Offence for GrandpaEquivocationOffence { const ID: Kind = *b"grandpa:equivoca"; type TimeSlot = GrandpaTimeSlot; + type Offender = O; + type Reporter = R; - fn offenders(&self) -> Vec { + fn offenders(&self) -> Vec { vec![self.offender.clone()] } + fn reporters(&self) -> Vec { + self.reporter.clone().into_iter().collect() + } + fn session_index(&self) -> SessionIndex { self.session_index } diff --git a/frame/grandpa/src/lib.rs b/frame/grandpa/src/lib.rs index 8305d188449b5..202f29f0ba5c4 100644 --- a/frame/grandpa/src/lib.rs +++ b/frame/grandpa/src/lib.rs @@ -111,11 +111,9 @@ pub mod pallet { /// `()`) you must use this pallet's `ValidateUnsigned` in the runtime /// definition. type HandleEquivocation: EquivocationHandlerT< - AccountId = Self::AccountId, KeyOwnerProof = Self::KeyOwnerProof, - KeyOwnerIdentification = Self::KeyOwnerIdentification, - Offence = GrandpaEquivocationOffence, - EquivocationProof = EquivocationProof, + Offence = GrandpaEquivocationOffence, + OffenceProof = EquivocationProof, >; /// Weights for this pallet. @@ -591,9 +589,10 @@ impl Pallet { session_index, offender, validator_count, + reporter, }; - T::HandleEquivocation::report_offence(reporter.into_iter().collect(), offence) + T::HandleEquivocation::report_offence(offence) .map_err(|_| Error::::DuplicateOffenceReport)?; // waive the fee since the report is valid and beneficial @@ -608,11 +607,7 @@ impl Pallet { equivocation_proof: EquivocationProof, key_owner_proof: T::KeyOwnerProof, ) -> Option<()> { - T::HandleEquivocation::submit_unsigned_equivocation_report( - equivocation_proof, - key_owner_proof, - ) - .ok() + T::HandleEquivocation::submit_offence_proof(equivocation_proof, key_owner_proof).ok() } fn on_stalled(further_wait: T::BlockNumber, median: T::BlockNumber) { diff --git a/frame/offences/src/lib.rs b/frame/offences/src/lib.rs index 7858b02719c4c..0eb895ce5949b 100644 --- a/frame/offences/src/lib.rs +++ b/frame/offences/src/lib.rs @@ -112,13 +112,14 @@ pub mod pallet { } } -impl> - ReportOffence for Pallet +impl ReportOffence for Pallet where - T::IdentificationTuple: Clone, + T: Config, + O: Offence, { - fn report_offence(reporters: Vec, offence: O) -> Result<(), OffenceError> { + fn report_offence(offence: O) -> Result<(), OffenceError> { let offenders = offence.offenders(); + let reporters = offence.reporters(); let time_slot = offence.time_slot(); // Go through all offenders in the offence report and find all offenders that were spotted @@ -164,7 +165,7 @@ impl Pallet { /// Compute the ID for the given report properties. /// /// The report id depends on the offence kind, time slot and the id of offender. - fn report_id>( + fn report_id( time_slot: &O::TimeSlot, offender: &T::IdentificationTuple, ) -> ReportIdOf { @@ -173,7 +174,7 @@ impl Pallet { /// Triages the offence report and returns the set of offenders that was involved in unique /// reports along with the list of the concurrent offences. - fn triage_offence_report>( + fn triage_offence_report( reporters: Vec, time_slot: &O::TimeSlot, offenders: Vec, @@ -223,13 +224,13 @@ struct TriageOutcome { /// This struct is responsible for aggregating storage writes and the underlying storage should not /// accessed directly meanwhile. #[must_use = "The changes are not saved without called `save`"] -struct ReportIndexStorage> { +struct ReportIndexStorage { opaque_time_slot: OpaqueTimeSlot, concurrent_reports: Vec>, same_kind_reports: Vec<(O::TimeSlot, ReportIdOf)>, } -impl> ReportIndexStorage { +impl ReportIndexStorage { /// Preload indexes from the storage for the specific `time_slot` and the kind of the offence. fn load(time_slot: &O::TimeSlot) -> Self { let opaque_time_slot = time_slot.encode(); diff --git a/frame/offences/src/mock.rs b/frame/offences/src/mock.rs index 63d2b0de34780..72e1736aa79fd 100644 --- a/frame/offences/src/mock.rs +++ b/frame/offences/src/mock.rs @@ -134,20 +134,26 @@ pub fn offence_reports(kind: Kind, time_slot: u128) -> Vec { +pub struct Offence { pub validator_set_count: u32, - pub offenders: Vec, + pub offenders: Vec, pub time_slot: u128, } -impl offence::Offence for Offence { +impl offence::Offence for Offence { const ID: offence::Kind = KIND; type TimeSlot = u128; + type Offender = u64; + type Reporter = u64; - fn offenders(&self) -> Vec { + fn offenders(&self) -> Vec { self.offenders.clone() } + fn reporters(&self) -> Vec { + Default::default() + } + fn validator_set_count(&self) -> u32 { self.validator_set_count } @@ -167,5 +173,5 @@ impl offence::Offence for Offence { /// Create the report id for the given `offender` and `time_slot` combination. pub fn report_id(time_slot: u128, offender: u64) -> H256 { - Offences::report_id::>(&time_slot, &offender) + Offences::report_id::(&time_slot, &offender) } diff --git a/frame/offences/src/tests.rs b/frame/offences/src/tests.rs index 266e05debf050..e2b7dcba589b6 100644 --- a/frame/offences/src/tests.rs +++ b/frame/offences/src/tests.rs @@ -37,7 +37,7 @@ fn should_report_an_authority_and_trigger_on_offence() { let offence = Offence { validator_set_count: 5, time_slot, offenders: vec![5] }; // when - Offences::report_offence(vec![], offence).unwrap(); + Offences::report_offence(offence).unwrap(); // then with_on_offence_fractions(|f| { @@ -54,7 +54,7 @@ fn should_not_report_the_same_authority_twice_in_the_same_slot() { assert_eq!(offence_reports(KIND, time_slot), vec![]); let offence = Offence { validator_set_count: 5, time_slot, offenders: vec![5] }; - Offences::report_offence(vec![], offence.clone()).unwrap(); + Offences::report_offence(offence.clone()).unwrap(); with_on_offence_fractions(|f| { assert_eq!(f.clone(), vec![Perbill::from_percent(25)]); f.clear(); @@ -62,7 +62,7 @@ fn should_not_report_the_same_authority_twice_in_the_same_slot() { // when // report for the second time - assert_eq!(Offences::report_offence(vec![], offence), Err(OffenceError::DuplicateReport)); + assert_eq!(Offences::report_offence(offence), Err(OffenceError::DuplicateReport)); // then with_on_offence_fractions(|f| { @@ -79,7 +79,7 @@ fn should_report_in_different_time_slot() { assert_eq!(offence_reports(KIND, time_slot), vec![]); let mut offence = Offence { validator_set_count: 5, time_slot, offenders: vec![5] }; - Offences::report_offence(vec![], offence.clone()).unwrap(); + Offences::report_offence(offence.clone()).unwrap(); with_on_offence_fractions(|f| { assert_eq!(f.clone(), vec![Perbill::from_percent(25)]); f.clear(); @@ -88,7 +88,7 @@ fn should_report_in_different_time_slot() { // when // report for the second time offence.time_slot += 1; - Offences::report_offence(vec![], offence).unwrap(); + Offences::report_offence(offence).unwrap(); // then with_on_offence_fractions(|f| { @@ -107,7 +107,7 @@ fn should_deposit_event() { let offence = Offence { validator_set_count: 5, time_slot, offenders: vec![5] }; // when - Offences::report_offence(vec![], offence).unwrap(); + Offences::report_offence(offence).unwrap(); // then assert_eq!( @@ -132,7 +132,7 @@ fn doesnt_deposit_event_for_dups() { assert_eq!(offence_reports(KIND, time_slot), vec![]); let offence = Offence { validator_set_count: 5, time_slot, offenders: vec![5] }; - Offences::report_offence(vec![], offence.clone()).unwrap(); + Offences::report_offence(offence.clone()).unwrap(); with_on_offence_fractions(|f| { assert_eq!(f.clone(), vec![Perbill::from_percent(25)]); f.clear(); @@ -140,7 +140,7 @@ fn doesnt_deposit_event_for_dups() { // when // report for the second time - assert_eq!(Offences::report_offence(vec![], offence), Err(OffenceError::DuplicateReport)); + assert_eq!(Offences::report_offence(offence), Err(OffenceError::DuplicateReport)); // then // there is only one event. @@ -160,36 +160,34 @@ fn doesnt_deposit_event_for_dups() { #[test] fn reports_if_an_offence_is_dup() { - type TestOffence = Offence; - new_test_ext().execute_with(|| { let time_slot = 42; assert_eq!(offence_reports(KIND, time_slot), vec![]); let offence = - |time_slot, offenders| TestOffence { validator_set_count: 5, time_slot, offenders }; + |time_slot, offenders| Offence { validator_set_count: 5, time_slot, offenders }; let mut test_offence = offence(time_slot, vec![0]); // the report for authority 0 at time slot 42 should not be a known // offence - assert!(!>::is_known_offence( + assert!(!>::is_known_offence( &test_offence.offenders, &test_offence.time_slot )); // we report an offence for authority 0 at time slot 42 - Offences::report_offence(vec![], test_offence.clone()).unwrap(); + Offences::report_offence(test_offence.clone()).unwrap(); // the same report should be a known offence now - assert!(>::is_known_offence( + assert!(>::is_known_offence( &test_offence.offenders, &test_offence.time_slot )); // and reporting it again should yield a duplicate report error assert_eq!( - Offences::report_offence(vec![], test_offence.clone()), + Offences::report_offence(test_offence.clone()), Err(OffenceError::DuplicateReport) ); @@ -197,18 +195,18 @@ fn reports_if_an_offence_is_dup() { test_offence.offenders.push(1); // it should not be a known offence anymore - assert!(!>::is_known_offence( + assert!(!>::is_known_offence( &test_offence.offenders, &test_offence.time_slot )); // and reporting it again should work without any error - assert_eq!(Offences::report_offence(vec![], test_offence.clone()), Ok(())); + assert_eq!(Offences::report_offence(test_offence.clone()), Ok(())); // creating a new offence for the same authorities on the next slot // should be considered a new offence and thefore not known let test_offence_next_slot = offence(time_slot + 1, vec![0, 1]); - assert!(!>::is_known_offence( + assert!(!>::is_known_offence( &test_offence_next_slot.offenders, &test_offence_next_slot.time_slot )); @@ -226,7 +224,7 @@ fn should_properly_count_offences() { let offence1 = Offence { validator_set_count: 5, time_slot, offenders: vec![5] }; let offence2 = Offence { validator_set_count: 5, time_slot, offenders: vec![4] }; - Offences::report_offence(vec![], offence1).unwrap(); + Offences::report_offence(offence1).unwrap(); with_on_offence_fractions(|f| { assert_eq!(f.clone(), vec![Perbill::from_percent(25)]); f.clear(); @@ -234,7 +232,7 @@ fn should_properly_count_offences() { // when // report for the second time - Offences::report_offence(vec![], offence2).unwrap(); + Offences::report_offence(offence2).unwrap(); // then // the 1st authority should have count 2 and the 2nd one should be reported only once. @@ -263,7 +261,7 @@ fn should_properly_sort_offences() { Offence { validator_set_count: 5, time_slot: time_slot + 1, offenders: vec![6, 7] }; let offence4 = Offence { validator_set_count: 5, time_slot: time_slot - 1, offenders: vec![3] }; - Offences::report_offence(vec![], offence1).unwrap(); + Offences::report_offence(offence1).unwrap(); with_on_offence_fractions(|f| { assert_eq!(f.clone(), vec![Perbill::from_percent(25)]); f.clear(); @@ -271,9 +269,9 @@ fn should_properly_sort_offences() { // when // report for the second time - Offences::report_offence(vec![], offence2).unwrap(); - Offences::report_offence(vec![], offence3).unwrap(); - Offences::report_offence(vec![], offence4).unwrap(); + Offences::report_offence(offence2).unwrap(); + Offences::report_offence(offence3).unwrap(); + Offences::report_offence(offence4).unwrap(); // then let same_kind_reports = Vec::<(u128, sp_core::H256)>::decode( diff --git a/frame/staking/src/lib.rs b/frame/staking/src/lib.rs index 3672056534b75..008066fa8258a 100644 --- a/frame/staking/src/lib.rs +++ b/frame/staking/src/lib.rs @@ -912,20 +912,19 @@ pub struct FilterHistoricalOffences { _inner: sp_std::marker::PhantomData<(T, R)>, } -impl ReportOffence - for FilterHistoricalOffences, R> +impl ReportOffence for FilterHistoricalOffences, R> where T: Config, - R: ReportOffence, - O: Offence, + R: ReportOffence, + O: Offence, { - fn report_offence(reporters: Vec, offence: O) -> Result<(), OffenceError> { + fn report_offence(offence: O) -> Result<(), OffenceError> { // Disallow any slashing from before the current bonding period. let offence_session = offence.session_index(); let bonded_eras = BondedEras::::get(); if bonded_eras.first().filter(|(_, start)| offence_session >= *start).is_some() { - R::report_offence(reporters, offence) + R::report_offence(offence) } else { >::deposit_event(Event::::OldSlashingReportDiscarded { session_index: offence_session, @@ -934,7 +933,7 @@ where } } - fn is_known_offence(offenders: &[Offender], time_slot: &O::TimeSlot) -> bool { + fn is_known_offence(offenders: &[O::Offender], time_slot: &O::TimeSlot) -> bool { R::is_known_offence(offenders, time_slot) } } diff --git a/primitives/staking/src/equivocation.rs b/primitives/staking/src/equivocation.rs index 6b90fc063a201..b9808d3c38393 100644 --- a/primitives/staking/src/equivocation.rs +++ b/primitives/staking/src/equivocation.rs @@ -1,49 +1,42 @@ use crate::offence::{Offence, OffenceError, ReportOffence}; use sp_core::Get; use sp_runtime::DispatchResult; -use sp_std::vec::Vec; pub trait EquivocationHandler { - type ReporterId; + type Offence: Offence; - type OffenderId; - - type EquivocationProof; + type OffenceProof; type KeyOwnerProof; - type Offence: Offence; - - type ReportOffence: ReportOffence; + type ReportOffence: ReportOffence; /// The longevity, in blocks, that the equivocation report is valid for. When using the staking /// pallet this should be equal to the bonding duration (in blocks, not eras). type ReportLongevity: Get; - fn report_offence( - reporters: Vec, - offence: Self::Offence, - ) -> Result<(), OffenceError> { - Self::ReportOffence::report_offence(reporters, offence) + fn report_offence(offence: Self::Offence) -> Result<(), OffenceError> { + Self::ReportOffence::report_offence(offence) } fn is_known_offence( - offenders: &[Self::OffenderId], - time_slot: &>::TimeSlot, + offenders: &[::Offender], + time_slot: &::TimeSlot, ) -> bool { Self::ReportOffence::is_known_offence(offenders, time_slot) } - /// Create and dispatch an equivocation report extrinsic. - fn submit_unsigned_equivocation_report( - _equivocation_proof: Self::EquivocationProof, + /// Create and dispatch an offence report extrinsic. + fn submit_offence_proof( + _offence_proof: Self::OffenceProof, _key_owner_proof: Self::KeyOwnerProof, ) -> DispatchResult { Ok(()) } - /// Fetch the current block author id, if defined. - fn block_author() -> Option { + /// Fetch the current reporter id, if defined. + // TODO: rename to reporter and move to offence trait? + fn block_author() -> Option<::Reporter> { None } } diff --git a/primitives/staking/src/offence.rs b/primitives/staking/src/offence.rs index f6517b9e9028b..13a4d71f031fc 100644 --- a/primitives/staking/src/offence.rs +++ b/primitives/staking/src/offence.rs @@ -18,10 +18,9 @@ //! Common traits and types that are useful for describing offences for usage in environments //! that use staking. -use sp_std::vec::Vec; - use codec::{Decode, Encode}; use sp_runtime::Perbill; +use sp_std::vec::Vec; use crate::SessionIndex; @@ -65,10 +64,16 @@ pub enum DisableStrategy { /// This trait assumes that the offence is legitimate and was validated already. /// /// Examples of offences include: a BABE equivocation or a GRANDPA unjustified vote. -pub trait Offence { +pub trait Offence { /// Identifier which is unique for this kind of an offence. const ID: Kind; + /// Offender Identifier. + type Offender; + + /// Reporter identifier. + type Reporter; + /// A type that represents a point in time on an abstract timescale. /// /// See `Offence::time_slot` for details. The only requirement is that such timescale could be @@ -76,9 +81,10 @@ pub trait Offence { type TimeSlot: Clone + codec::Codec + Ord; /// The list of all offenders involved in this incident. - /// - /// The list has no duplicates, so it is rather a set. - fn offenders(&self) -> Vec; + fn offenders(&self) -> Vec; + + /// The list of all reporters of this incident. + fn reporters(&self) -> Vec; /// The session index that is used for querying the validator set for the `slash_fraction` /// function. @@ -138,22 +144,22 @@ impl sp_runtime::traits::Printable for OffenceError { } /// A trait for decoupling offence reporters from the actual handling of offence reports. -pub trait ReportOffence> { +pub trait ReportOffence { /// Report an `offence` and reward given `reporters`. - fn report_offence(reporters: Vec, offence: O) -> Result<(), OffenceError>; + fn report_offence(offence: O) -> Result<(), OffenceError>; /// Returns true iff all of the given offenders have been previously reported /// at the given time slot. This function is useful to prevent the sending of /// duplicate offence reports. - fn is_known_offence(offenders: &[Offender], time_slot: &O::TimeSlot) -> bool; + fn is_known_offence(offenders: &[O::Offender], time_slot: &O::TimeSlot) -> bool; } -impl> ReportOffence for () { - fn report_offence(_reporters: Vec, _offence: O) -> Result<(), OffenceError> { +impl ReportOffence for () { + fn report_offence(_offence: O) -> Result<(), OffenceError> { Ok(()) } - fn is_known_offence(_offenders: &[Offender], _time_slot: &O::TimeSlot) -> bool { + fn is_known_offence(_offenders: &[O::Offender], _time_slot: &O::TimeSlot) -> bool { true } } From 239bfbbf2ef4e8efd7edba262fd959fe25dd7e16 Mon Sep 17 00:00:00 2001 From: Davide Galassi Date: Fri, 17 Feb 2023 11:21:15 +0100 Subject: [PATCH 08/33] More refactory --- bin/node-template/runtime/src/lib.rs | 2 +- frame/babe/src/equivocation.rs | 10 ++--- frame/babe/src/lib.rs | 13 ++---- frame/grandpa/src/equivocation.rs | 6 +-- frame/grandpa/src/lib.rs | 4 +- frame/im-online/src/lib.rs | 14 ++++--- frame/im-online/src/mock.rs | 8 ++-- frame/im-online/src/tests.rs | 26 +++++------- primitives/staking/src/equivocation.rs | 55 +++++++++++++++++++++++++- 9 files changed, 92 insertions(+), 46 deletions(-) diff --git a/bin/node-template/runtime/src/lib.rs b/bin/node-template/runtime/src/lib.rs index ff9ac66d6afec..58f51cb6458ad 100644 --- a/bin/node-template/runtime/src/lib.rs +++ b/bin/node-template/runtime/src/lib.rs @@ -226,7 +226,7 @@ impl pallet_grandpa::Config for Runtime { GrandpaId, )>>::IdentificationTuple; - type HandleEquivocation = (); + type HandleEquivocation = pallet_grandpa::NullHandler; type WeightInfo = (); type MaxAuthorities = ConstU32<32>; diff --git a/frame/babe/src/equivocation.rs b/frame/babe/src/equivocation.rs index 05942563c0776..0c824058469b0 100644 --- a/frame/babe/src/equivocation.rs +++ b/frame/babe/src/equivocation.rs @@ -64,10 +64,10 @@ pub struct EquivocationHandler(sp_std::marker::PhantomData<(I, R, L)>); impl EquivocationHandlerT for EquivocationHandler where T: Config + pallet_authorship::Config + frame_system::offchain::SendTransactionTypes>, - R: ReportOffence>, + R: ReportOffence>, L: Get, { - type Offence = BabeEquivocationOffence; + type Offence = EquivocationOffence; type OffenceProof = EquivocationProof; @@ -105,7 +105,7 @@ where struct NullHandler(sp_std::marker::PhantomData); impl EquivocationHandlerT for NullHandler { - type Offence = BabeEquivocationOffence; + type Offence = EquivocationOffence; type OffenceProof = (); type KeyOwnerProof = (); type ReportOffence = (); @@ -181,7 +181,7 @@ fn is_known_offence( /// A BABE equivocation offence report. /// /// When a validator released two or more blocks at the same slot. -pub struct BabeEquivocationOffence { +pub struct EquivocationOffence { /// A babe slot in which this incident happened. pub slot: Slot, /// The session index in which the incident happened. @@ -194,7 +194,7 @@ pub struct BabeEquivocationOffence { pub reporter: Option, } -impl Offence for BabeEquivocationOffence { +impl Offence for EquivocationOffence { const ID: Kind = *b"babe:equivocatio"; type TimeSlot = Slot; type Offender = O; diff --git a/frame/babe/src/lib.rs b/frame/babe/src/lib.rs index d73e98fe74022..3561afe95a01f 100644 --- a/frame/babe/src/lib.rs +++ b/frame/babe/src/lib.rs @@ -64,7 +64,7 @@ mod mock; #[cfg(all(feature = "std", test))] mod tests; -pub use equivocation::{BabeEquivocationOffence, EquivocationHandler}; +pub use equivocation::{EquivocationHandler, EquivocationOffence}; #[allow(deprecated)] pub use randomness::CurrentBlockRandomness; pub use randomness::{ @@ -174,7 +174,7 @@ pub mod pallet { /// definition. type HandleEquivocation: EquivocationHandlerT< KeyOwnerProof = Self::KeyOwnerProof, - Offence = BabeEquivocationOffence, + Offence = EquivocationOffence, OffenceProof = EquivocationProof, >; @@ -852,13 +852,8 @@ impl Pallet { let offender = T::KeyOwnerProofSystem::check_proof(key, key_owner_proof) .ok_or(Error::::InvalidKeyOwnershipProof)?; - let offence = BabeEquivocationOffence { - slot, - validator_set_count, - offender, - session_index, - reporter, - }; + let offence = + EquivocationOffence { slot, validator_set_count, offender, session_index, reporter }; T::HandleEquivocation::report_offence(offence) .map_err(|_| Error::::DuplicateOffenceReport)?; diff --git a/frame/grandpa/src/equivocation.rs b/frame/grandpa/src/equivocation.rs index 5f0914d6dd599..6323aa195cd2f 100644 --- a/frame/grandpa/src/equivocation.rs +++ b/frame/grandpa/src/equivocation.rs @@ -106,15 +106,15 @@ where } } -struct NullHandler(sp_std::marker::PhantomData); +pub struct NullHandler(sp_std::marker::PhantomData); impl EquivocationHandlerT for NullHandler { // TODO: is possible to get rid of this? Like defining a default dummy Offence // OR even better get rid of this EquivocationHandler trait... type Offence = GrandpaEquivocationOffence; - type OffenceProof = (); - type KeyOwnerProof = (); + type OffenceProof = EquivocationProof; + type KeyOwnerProof = T::KeyOwnerProof; type ReportOffence = (); type ReportLongevity = (); } diff --git a/frame/grandpa/src/lib.rs b/frame/grandpa/src/lib.rs index 202f29f0ba5c4..a22708861ac3f 100644 --- a/frame/grandpa/src/lib.rs +++ b/frame/grandpa/src/lib.rs @@ -63,7 +63,9 @@ mod mock; #[cfg(all(feature = "std", test))] mod tests; -pub use equivocation::{EquivocationHandler, GrandpaEquivocationOffence, GrandpaTimeSlot}; +pub use equivocation::{ + EquivocationHandler, GrandpaEquivocationOffence, GrandpaTimeSlot, NullHandler, +}; pub use pallet::*; diff --git a/frame/im-online/src/lib.rs b/frame/im-online/src/lib.rs index 8c1f46978e8bc..f88f92b896e50 100644 --- a/frame/im-online/src/lib.rs +++ b/frame/im-online/src/lib.rs @@ -353,8 +353,6 @@ pub mod pallet { /// A type that gives us the ability to submit unresponsiveness offence reports. type ReportUnresponsiveness: ReportOffence< - Self::AccountId, - IdentificationTuple, UnresponsivenessOffence>, >; @@ -909,7 +907,7 @@ impl OneSessionHandler for Pallet { let validator_set_count = keys.len() as u32; let offence = UnresponsivenessOffence { session_index, validator_set_count, offenders }; - if let Err(e) = T::ReportUnresponsiveness::report_offence(vec![], offence) { + if let Err(e) = T::ReportUnresponsiveness::report_offence(offence) { sp_runtime::print(e); } } @@ -935,14 +933,20 @@ pub struct UnresponsivenessOffence { pub offenders: Vec, } -impl Offence for UnresponsivenessOffence { +impl Offence for UnresponsivenessOffence { const ID: Kind = *b"im-online:offlin"; type TimeSlot = SessionIndex; + type Offender = Offender; + type Reporter = (); - fn offenders(&self) -> Vec { + fn offenders(&self) -> Vec { self.offenders.clone() } + fn reporters(&self) -> Vec { + Default::default() + } + fn session_index(&self) -> SessionIndex { self.session_index } diff --git a/frame/im-online/src/mock.rs b/frame/im-online/src/mock.rs index 783e68dfede9f..5e0377a3b6e10 100644 --- a/frame/im-online/src/mock.rs +++ b/frame/im-online/src/mock.rs @@ -88,14 +88,14 @@ type IdentificationTuple = (u64, u64); type Offence = crate::UnresponsivenessOffence; parameter_types! { - pub static Offences: Vec<(Vec, Offence)> = vec![]; + pub static Offences: Vec = vec![]; } /// A mock offence report handler. pub struct OffenceHandler; -impl ReportOffence for OffenceHandler { - fn report_offence(reporters: Vec, offence: Offence) -> Result<(), OffenceError> { - Offences::mutate(|l| l.push((reporters, offence))); +impl ReportOffence for OffenceHandler { + fn report_offence(offence: Offence) -> Result<(), OffenceError> { + Offences::mutate(|l| l.push(offence)); Ok(()) } diff --git a/frame/im-online/src/tests.rs b/frame/im-online/src/tests.rs index 2c026f7176b65..dd5151df87a32 100644 --- a/frame/im-online/src/tests.rs +++ b/frame/im-online/src/tests.rs @@ -79,14 +79,11 @@ fn should_report_offline_validators() { let offences = Offences::take(); assert_eq!( offences, - vec![( - vec![], - UnresponsivenessOffence { - session_index: 2, - validator_set_count: 3, - offenders: vec![(1, 1), (2, 2), (3, 3),], - } - )] + vec![UnresponsivenessOffence { + session_index: 2, + validator_set_count: 3, + offenders: vec![(1, 1), (2, 2), (3, 3),], + }] ); // should not report when heartbeat is sent @@ -99,14 +96,11 @@ fn should_report_offline_validators() { let offences = Offences::take(); assert_eq!( offences, - vec![( - vec![], - UnresponsivenessOffence { - session_index: 3, - validator_set_count: 6, - offenders: vec![(5, 5), (6, 6),], - } - )] + vec![UnresponsivenessOffence { + session_index: 3, + validator_set_count: 6, + offenders: vec![(5, 5), (6, 6),], + }] ); }); } diff --git a/primitives/staking/src/equivocation.rs b/primitives/staking/src/equivocation.rs index b9808d3c38393..0645dbae7c532 100644 --- a/primitives/staking/src/equivocation.rs +++ b/primitives/staking/src/equivocation.rs @@ -1,6 +1,45 @@ -use crate::offence::{Offence, OffenceError, ReportOffence}; +use crate::{ + offence::{Offence, OffenceError, ReportOffence}, + SessionIndex, +}; use sp_core::Get; -use sp_runtime::DispatchResult; +use sp_runtime::{DispatchResult, Perbill}; +use sp_std::vec::Vec; + +// Dummy offence implementation used by the "null" Offence reporter. +// Should remain private... +pub struct NullOffence; + +impl Offence for NullOffence { + const ID: crate::offence::Kind = [0; 16]; + type TimeSlot = (); + type Offender = (); + type Reporter = (); + + fn offenders(&self) -> Vec { + Default::default() + } + + fn reporters(&self) -> Vec { + Default::default() + } + + fn validator_set_count(&self) -> u32 { + 0 + } + + fn time_slot(&self) -> Self::TimeSlot { + () + } + + fn session_index(&self) -> SessionIndex { + 0 + } + + fn slash_fraction(&self, _offenders_count: u32) -> Perbill { + Default::default() + } +} pub trait EquivocationHandler { type Offence: Offence; @@ -40,3 +79,15 @@ pub trait EquivocationHandler { None } } + +impl EquivocationHandler for () { + type Offence = NullOffence; + + type OffenceProof = (); + + type KeyOwnerProof = (); + + type ReportOffence = (); + + type ReportLongevity = (); +} From 4ba1a01025ead6a36b99f963d64c1ac0595dacef Mon Sep 17 00:00:00 2001 From: Davide Galassi Date: Sat, 18 Feb 2023 21:13:13 +0100 Subject: [PATCH 09/33] Some prograss with the encapsulation of offence report system --- bin/node-template/runtime/src/lib.rs | 26 ++-- frame/babe/src/equivocation.rs | 155 +++++++++++-------- frame/babe/src/lib.rs | 120 +++++---------- frame/babe/src/mock.rs | 18 +-- frame/babe/src/tests.rs | 27 ++-- frame/grandpa/src/equivocation.rs | 198 +++++++++++++++---------- frame/grandpa/src/lib.rs | 152 +++++-------------- frame/grandpa/src/mock.rs | 19 +-- frame/grandpa/src/tests.rs | 31 ++-- frame/offences/src/lib.rs | 7 +- frame/offences/src/mock.rs | 5 - frame/offences/src/tests.rs | 42 +++--- frame/staking/src/lib.rs | 8 +- primitives/consensus/slots/src/lib.rs | 2 +- primitives/finality-grandpa/src/lib.rs | 3 + primitives/staking/src/equivocation.rs | 115 +++++++------- primitives/staking/src/offence.rs | 14 +- 17 files changed, 440 insertions(+), 502 deletions(-) diff --git a/bin/node-template/runtime/src/lib.rs b/bin/node-template/runtime/src/lib.rs index 58f51cb6458ad..728add3ade88c 100644 --- a/bin/node-template/runtime/src/lib.rs +++ b/bin/node-template/runtime/src/lib.rs @@ -216,21 +216,25 @@ impl pallet_aura::Config for Runtime { impl pallet_grandpa::Config for Runtime { type RuntimeEvent = RuntimeEvent; - type KeyOwnerProofSystem = (); + type WeightInfo = (); + type MaxAuthorities = ConstU32<32>; + type MaxSetIdSessionEntries = ConstU64<0>; - type KeyOwnerProof = - >::Proof; + type KeyOwnerProof = (); + type EquivocationProof = (); + type OffenceReportSystem = (); - type KeyOwnerIdentification = >::IdentificationTuple; + // type KeyOwnerProofSystem = (); - type HandleEquivocation = pallet_grandpa::NullHandler; + // type KeyOwnerProof = + // >::Proof; - type WeightInfo = (); - type MaxAuthorities = ConstU32<32>; - type MaxSetIdSessionEntries = ConstU64<0>; + // type KeyOwnerIdentification = >::IdentificationTuple; + + // type HandleEquivocation = pallet_grandpa::NullHandler; } impl pallet_timestamp::Config for Runtime { diff --git a/frame/babe/src/equivocation.rs b/frame/babe/src/equivocation.rs index 0c824058469b0..e2e9766e60110 100644 --- a/frame/babe/src/equivocation.rs +++ b/frame/babe/src/equivocation.rs @@ -33,83 +33,132 @@ //! that the `ValidateUnsigned` for the BABE pallet is used in the runtime //! definition. +use codec::Encode; use frame_support::traits::{Get, KeyOwnerProofSystem}; -use sp_consensus_babe::{EquivocationProof, Slot}; +use log::{error, info}; + +use sp_consensus_babe::{AuthorityId, EquivocationProof, Slot, KEY_TYPE}; use sp_runtime::{ transaction_validity::{ InvalidTransaction, TransactionPriority, TransactionSource, TransactionValidity, TransactionValidityError, ValidTransaction, }, - DispatchResult, Perbill, + DispatchResult, KeyTypeId, Perbill, SaturatedConversion, }; +use sp_session::{GetSessionNumber, GetValidatorCount}; use sp_staking::{ - equivocation::EquivocationHandler as EquivocationHandlerT, + equivocation::OffenceReportSystem, offence::{Kind, Offence, ReportOffence}, SessionIndex, }; use sp_std::prelude::*; -use crate::{Call, Config, Pallet, LOG_TARGET}; +use crate::{Call, Config, Error, Pallet, LOG_TARGET}; /// Generic equivocation handler. This type implements `HandleEquivocation` /// using existing subsystems that are part of frame (type bounds described /// below) and will dispatch to them directly, it's only purpose is to wire all /// subsystems together. #[derive(Default)] -pub struct EquivocationHandler(sp_std::marker::PhantomData<(I, R, L)>); +pub struct EquivocationHandler(sp_std::marker::PhantomData<(T, R, P, L)>); // We use the authorship pallet to fetch the current block author and use // `offchain::SendTransactionTypes` for unsigned extrinsic creation and // submission. -impl EquivocationHandlerT for EquivocationHandler +impl OffenceReportSystem for EquivocationHandler where - T: Config + pallet_authorship::Config + frame_system::offchain::SendTransactionTypes>, - R: ReportOffence>, + T: Config::Header>> + + pallet_authorship::Config + + frame_system::offchain::SendTransactionTypes>, + R: ReportOffence>, + P: KeyOwnerProofSystem<(KeyTypeId, AuthorityId), Proof = T::KeyOwnerProof>, + P::IdentificationTuple: Clone, + P::Proof: GetSessionNumber + GetValidatorCount, L: Get, { - type Offence = EquivocationOffence; + type Offence = EquivocationOffence; type OffenceProof = EquivocationProof; type KeyOwnerProof = T::KeyOwnerProof; - type ReportOffence = R; - type ReportLongevity = L; - fn submit_offence_proof( + type Reporter = T::AccountId; + + fn report_evidence( + reporter: Option, equivocation_proof: Self::OffenceProof, key_owner_proof: Self::KeyOwnerProof, ) -> DispatchResult { - use frame_system::offchain::SubmitTransaction; + let reporter = reporter.or_else(|| >::author()); + + let offender = equivocation_proof.offender.clone(); + let slot = equivocation_proof.slot; + + // Validate the equivocation proof (check votes are different and signatures are valid) + if !sp_consensus_babe::check_equivocation_proof(equivocation_proof) { + return Err(Error::::InvalidEquivocationProof.into()) + } - let call = Call::report_equivocation_unsigned { - equivocation_proof: Box::new(equivocation_proof), - key_owner_proof, - }; + let validator_set_count = key_owner_proof.validator_count(); + let session_index = key_owner_proof.session(); - match SubmitTransaction::>::submit_unsigned_transaction(call.into()) { - Ok(()) => log::info!(target: LOG_TARGET, "Submitted BABE equivocation report.",), - Err(e) => - log::error!(target: LOG_TARGET, "Error submitting equivocation report: {:?}", e,), + let epoch_index = (*slot.saturating_sub(crate::GenesisSlot::::get()) / + T::EpochDuration::get()) + .saturated_into::(); + + // Check that the slot number is consistent with the session index + // in the key ownership proof (i.e. slot is for that epoch) + // TODO: this should be part of `check_evidence`... + if epoch_index != session_index { + return Err(Error::::InvalidKeyOwnershipProof.into()) } + // Check the membership proof and extract the offender's id + // TODO: These checks were alread yperformed by check evidence... + let offender = P::check_proof((KEY_TYPE, offender), key_owner_proof) + .ok_or(Error::::InvalidKeyOwnershipProof)?; + + let offence = EquivocationOffence { slot, validator_set_count, offender, session_index }; + + R::report_offence(reporter.into_iter().collect(), offence) + .map_err(|_| Error::::DuplicateOffenceReport)?; + Ok(()) } - fn block_author() -> Option { - >::author() + fn check_evidence( + equivocation_proof: &Self::OffenceProof, + key_owner_proof: &Self::KeyOwnerProof, + ) -> DispatchResult { + // Check the membership proof to extract the offender's id + let key = (sp_consensus_babe::KEY_TYPE, equivocation_proof.offender.clone()); + let offender = P::check_proof(key, key_owner_proof.clone()) + .ok_or(Error::::InvalidKeyOwnershipProof)?; + + // Check if the offence has already been reported, and if so then we can discard the report. + if R::is_known_offence(&[offender], &equivocation_proof.slot) { + Err(Error::::DuplicateOffenceReport.into()) + } else { + Ok(()) + } } -} -struct NullHandler(sp_std::marker::PhantomData); + fn submit_evidence( + equivocation_proof: Self::OffenceProof, + key_owner_proof: Self::KeyOwnerProof, + ) -> bool { + use frame_system::offchain::SubmitTransaction; -impl EquivocationHandlerT for NullHandler { - type Offence = EquivocationOffence; - type OffenceProof = (); - type KeyOwnerProof = (); - type ReportOffence = (); - type ReportLongevity = (); + let call = Call::report_equivocation_unsigned { equivocation_proof, key_owner_proof }; + let res = SubmitTransaction::>::submit_unsigned_transaction(call.into()); + match res { + Ok(()) => info!(target: LOG_TARGET, "Submitted equivocation report."), + Err(e) => error!(target: LOG_TARGET, "Error submitting equivocation report: {:?}", e), + } + res.is_ok() + } } /// Methods for the `ValidateUnsigned` implementation: @@ -132,16 +181,19 @@ impl Pallet { }, } - // check report staleness - is_known_offence::(equivocation_proof, key_owner_proof)?; + // Check report validity + // TODO DAVXY: propagate error + T::OffenceReportSystem::check_evidence(equivocation_proof, key_owner_proof) + .map_err(|_| InvalidTransaction::Stale)?; - let longevity = ::ReportLongevity::get(); + let longevity = ::ReportLongevity::get(); + let tag = equivocation_proof.using_encoded(|bytes| sp_io::hashing::blake2_256(bytes)); ValidTransaction::with_tag_prefix("BabeEquivocation") // 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.slot)) + .and_provides(tag) .longevity(longevity) // We don't propagate this. This can never be included on a remote node. .propagate(false) @@ -153,35 +205,19 @@ impl Pallet { pub fn pre_dispatch(call: &Call) -> Result<(), TransactionValidityError> { if let Call::report_equivocation_unsigned { equivocation_proof, key_owner_proof } = call { - is_known_offence::(equivocation_proof, key_owner_proof) + // TODO DAVXY: propagate error + T::OffenceReportSystem::check_evidence(equivocation_proof, key_owner_proof) + .map_err(|_| TransactionValidityError::Invalid(InvalidTransaction::Stale)) } else { Err(InvalidTransaction::Call.into()) } } } -fn is_known_offence( - equivocation_proof: &EquivocationProof, - key_owner_proof: &T::KeyOwnerProof, -) -> Result<(), TransactionValidityError> { - // check the membership proof to extract the offender's id - let key = (sp_consensus_babe::KEY_TYPE, equivocation_proof.offender.clone()); - - let offender = T::KeyOwnerProofSystem::check_proof(key, key_owner_proof.clone()) - .ok_or(InvalidTransaction::BadProof)?; - - // check if the offence has already been reported, and if so then we can discard the report. - if T::HandleEquivocation::is_known_offence(&[offender], &equivocation_proof.slot) { - Err(InvalidTransaction::Stale.into()) - } else { - Ok(()) - } -} - /// A BABE equivocation offence report. /// /// When a validator released two or more blocks at the same slot. -pub struct EquivocationOffence { +pub struct EquivocationOffence { /// A babe slot in which this incident happened. pub slot: Slot, /// The session index in which the incident happened. @@ -190,24 +226,17 @@ pub struct EquivocationOffence { pub validator_set_count: u32, /// The authority that produced the equivocation. pub offender: O, - /// The offence reporter. - pub reporter: Option, } -impl Offence for EquivocationOffence { +impl Offence for EquivocationOffence { const ID: Kind = *b"babe:equivocatio"; type TimeSlot = Slot; type Offender = O; - type Reporter = R; fn offenders(&self) -> Vec { vec![self.offender.clone()] } - fn reporters(&self) -> Vec { - self.reporter.clone().into_iter().collect() - } - fn session_index(&self) -> SessionIndex { self.session_index } diff --git a/frame/babe/src/lib.rs b/frame/babe/src/lib.rs index 3561afe95a01f..67647e9ba4c1f 100644 --- a/frame/babe/src/lib.rs +++ b/frame/babe/src/lib.rs @@ -25,30 +25,26 @@ use codec::{Decode, Encode}; use frame_support::{ dispatch::{DispatchResultWithPostInfo, Pays}, ensure, - traits::{ - ConstU32, DisabledValidators, FindAuthor, Get, KeyOwnerProofSystem, OnTimestampSet, - OneSessionHandler, - }, + traits::{ConstU32, DisabledValidators, FindAuthor, Get, OnTimestampSet, OneSessionHandler}, weights::Weight, BoundedVec, WeakBoundedVec, }; use sp_application_crypto::ByteArray; +use sp_consensus_babe::{ + digests::{NextConfigDescriptor, NextEpochDescriptor, PreDigest}, + AllowedSlots, BabeAuthorityWeight, BabeEpochConfiguration, ConsensusLog, Epoch, Slot, + BABE_ENGINE_ID, +}; +use sp_consensus_vrf::schnorrkel; use sp_runtime::{ generic::DigestItem, traits::{IsMember, One, SaturatedConversion, Saturating, Zero}, - ConsensusEngineId, KeyTypeId, Permill, + ConsensusEngineId, Permill, }; use sp_session::{GetSessionNumber, GetValidatorCount}; -use sp_staking::equivocation::EquivocationHandler as EquivocationHandlerT; +use sp_staking::equivocation::OffenceReportSystem; use sp_std::prelude::*; -use sp_consensus_babe::{ - digests::{NextConfigDescriptor, NextEpochDescriptor, PreDigest}, - AllowedSlots, BabeAuthorityWeight, BabeEpochConfiguration, ConsensusLog, Epoch, - EquivocationProof, Slot, BABE_ENGINE_ID, -}; -use sp_consensus_vrf::schnorrkel; - pub use sp_consensus_babe::{AuthorityId, PUBLIC_KEY_LENGTH, RANDOMNESS_LENGTH, VRF_OUTPUT_LENGTH}; const LOG_TARGET: &str = "runtime::babe"; @@ -150,21 +146,20 @@ pub mod pallet { /// initialization. type DisabledValidators: DisabledValidators; + /// Helper for weights computations + type WeightInfo: WeightInfo; + + /// Max number of authorities allowed + #[pallet::constant] + type MaxAuthorities: Get; + /// 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 identification of a key owner, used when reporting equivocations. - type KeyOwnerIdentification: Parameter; - - /// A system for proving ownership of keys, i.e. that a given key was part - /// of a validator set, needed for validating equivocation reports. - type KeyOwnerProofSystem: KeyOwnerProofSystem< - (KeyTypeId, AuthorityId), - Proof = Self::KeyOwnerProof, - IdentificationTuple = Self::KeyOwnerIdentification, - >; + /// The equivocation proof + type EquivocationProof: Parameter; /// The equivocation handling subsystem, defines methods to report an /// offence (after the equivocation has been validated) and for submitting a @@ -172,18 +167,11 @@ pub mod pallet { /// NOTE: when enabling equivocation handling (i.e. this type isn't set to /// `()`) you must use this pallet's `ValidateUnsigned` in the runtime /// definition. - type HandleEquivocation: EquivocationHandlerT< + type OffenceReportSystem: OffenceReportSystem< KeyOwnerProof = Self::KeyOwnerProof, - Offence = EquivocationOffence, - OffenceProof = EquivocationProof, + OffenceProof = Self::EquivocationProof, + Reporter = Self::AccountId, >; - - /// Helper for weights computations - type WeightInfo: WeightInfo; - - /// Max number of authorities allowed - #[pallet::constant] - type MaxAuthorities: Get; } #[pallet::error] @@ -417,12 +405,13 @@ pub mod pallet { ))] pub fn report_equivocation( origin: OriginFor, - equivocation_proof: Box>, + equivocation_proof: T::EquivocationProof, key_owner_proof: T::KeyOwnerProof, ) -> DispatchResultWithPostInfo { - let reporter = ensure_signed(origin)?; - - Self::do_report_equivocation(Some(reporter), *equivocation_proof, key_owner_proof) + let reporter = Some(ensure_signed(origin)?); + T::OffenceReportSystem::report_evidence(reporter, equivocation_proof, key_owner_proof)?; + // Waive the fee since the report is valid and beneficial + Ok(Pays::No.into()) } /// Report authority equivocation/misbehavior. This method will verify @@ -439,16 +428,13 @@ pub mod pallet { ))] pub fn report_equivocation_unsigned( origin: OriginFor, - equivocation_proof: Box>, + equivocation_proof: T::EquivocationProof, key_owner_proof: T::KeyOwnerProof, ) -> DispatchResultWithPostInfo { ensure_none(origin)?; - - Self::do_report_equivocation( - T::HandleEquivocation::block_author(), - *equivocation_proof, - key_owner_proof, - ) + T::OffenceReportSystem::report_evidence(None, equivocation_proof, key_owner_proof)?; + // Waive the fee since the report is valid and beneficial + Ok(Pays::No.into()) } /// Plan an epoch config change. The epoch config change is recorded and will be enacted on @@ -822,55 +808,15 @@ impl Pallet { this_randomness } - fn do_report_equivocation( - reporter: Option, - equivocation_proof: EquivocationProof, - key_owner_proof: T::KeyOwnerProof, - ) -> DispatchResultWithPostInfo { - let offender = equivocation_proof.offender.clone(); - let slot = equivocation_proof.slot; - - // validate the equivocation proof - if !sp_consensus_babe::check_equivocation_proof(equivocation_proof) { - return Err(Error::::InvalidEquivocationProof.into()) - } - - let validator_set_count = key_owner_proof.validator_count(); - let session_index = key_owner_proof.session(); - - let epoch_index = (*slot.saturating_sub(GenesisSlot::::get()) / T::EpochDuration::get()) - .saturated_into::(); - - // check that the slot number is consistent with the session index - // in the key ownership proof (i.e. slot is for that epoch) - if epoch_index != session_index { - return Err(Error::::InvalidKeyOwnershipProof.into()) - } - - // check the membership proof and extract the offender's id - let key = (sp_consensus_babe::KEY_TYPE, offender); - let offender = T::KeyOwnerProofSystem::check_proof(key, key_owner_proof) - .ok_or(Error::::InvalidKeyOwnershipProof)?; - - let offence = - EquivocationOffence { slot, validator_set_count, offender, session_index, reporter }; - - T::HandleEquivocation::report_offence(offence) - .map_err(|_| Error::::DuplicateOffenceReport)?; - - // waive the fee since the report is valid and beneficial - Ok(Pays::No.into()) - } - /// 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, + equivocation_proof: T::EquivocationProof, key_owner_proof: T::KeyOwnerProof, - ) -> Option<()> { - T::HandleEquivocation::submit_offence_proof(equivocation_proof, key_owner_proof).ok() + ) -> bool { + T::OffenceReportSystem::submit_evidence(equivocation_proof, key_owner_proof) } } diff --git a/frame/babe/src/mock.rs b/frame/babe/src/mock.rs index ba1c1bcb45d09..be6dbdab2c8e8 100644 --- a/frame/babe/src/mock.rs +++ b/frame/babe/src/mock.rs @@ -223,21 +223,15 @@ impl Config for Test { type ExpectedBlockTime = ConstU64<1>; type EpochChangeTrigger = crate::ExternalTrigger; type DisabledValidators = Session; + type WeightInfo = (); + type MaxAuthorities = ConstU32<10>; - type KeyOwnerProofSystem = Historical; - - type KeyOwnerProof = - >::Proof; - - type KeyOwnerIdentification = >::IdentificationTuple; + type KeyOwnerProof = >::Proof; - type HandleEquivocation = super::EquivocationHandler; + type EquivocationProof = sp_consensus_babe::EquivocationProof
; - type WeightInfo = (); - type MaxAuthorities = ConstU32<10>; + type OffenceReportSystem = + super::EquivocationHandler; } pub fn go_to_block(n: u64, s: u64) { diff --git a/frame/babe/src/tests.rs b/frame/babe/src/tests.rs index 0b8a02547144b..0e86187f26e8c 100644 --- a/frame/babe/src/tests.rs +++ b/frame/babe/src/tests.rs @@ -21,7 +21,7 @@ use super::{Call, *}; use frame_support::{ assert_err, assert_noop, assert_ok, dispatch::{GetDispatchInfo, Pays}, - traits::{Currency, EstimateNextSessionRotation, OnFinalize}, + traits::{Currency, EstimateNextSessionRotation, KeyOwnerProofSystem, OnFinalize}, }; use mock::*; use pallet_session::ShouldEndSession; @@ -465,7 +465,7 @@ fn report_equivocation_current_session_works() { // report the equivocation Babe::report_equivocation_unsigned( RuntimeOrigin::none(), - Box::new(equivocation_proof), + equivocation_proof, key_owner_proof, ) .unwrap(); @@ -537,7 +537,7 @@ fn report_equivocation_old_session_works() { // report the equivocation Babe::report_equivocation_unsigned( RuntimeOrigin::none(), - Box::new(equivocation_proof), + equivocation_proof, key_owner_proof, ) .unwrap(); @@ -589,7 +589,7 @@ fn report_equivocation_invalid_key_owner_proof() { assert_err!( Babe::report_equivocation_unsigned( RuntimeOrigin::none(), - Box::new(equivocation_proof.clone()), + equivocation_proof.clone(), key_owner_proof ), Error::::InvalidKeyOwnershipProof, @@ -609,7 +609,7 @@ fn report_equivocation_invalid_key_owner_proof() { assert_err!( Babe::report_equivocation_unsigned( RuntimeOrigin::none(), - Box::new(equivocation_proof), + equivocation_proof, key_owner_proof, ), Error::::InvalidKeyOwnershipProof, @@ -643,7 +643,7 @@ fn report_equivocation_invalid_equivocation_proof() { assert_err!( Babe::report_equivocation_unsigned( RuntimeOrigin::none(), - Box::new(equivocation_proof), + equivocation_proof, key_owner_proof.clone(), ), Error::::InvalidEquivocationProof, @@ -750,7 +750,7 @@ fn report_equivocation_validate_unsigned_prevents_duplicates() { let key_owner_proof = Historical::prove(key).unwrap(); let inner = Call::report_equivocation_unsigned { - equivocation_proof: Box::new(equivocation_proof.clone()), + equivocation_proof: equivocation_proof.clone(), key_owner_proof: key_owner_proof.clone(), }; @@ -764,7 +764,8 @@ fn report_equivocation_validate_unsigned_prevents_duplicates() { ); // the transaction is valid when passed as local - let tx_tag = (offending_authority_pair.public(), CurrentSlot::::get()); + use codec::Encode; + let tag = equivocation_proof.using_encoded(|bytes| sp_io::hashing::blake2_256(bytes)); assert_eq!( ::validate_unsigned( TransactionSource::Local, @@ -773,7 +774,7 @@ fn report_equivocation_validate_unsigned_prevents_duplicates() { TransactionValidity::Ok(ValidTransaction { priority: TransactionPriority::max_value(), requires: vec![], - provides: vec![("BabeEquivocation", tx_tag).encode()], + provides: vec![("BabeEquivocation", tag).encode()], longevity: ReportLongevity::get(), propagate: false, }) @@ -785,7 +786,7 @@ fn report_equivocation_validate_unsigned_prevents_duplicates() { // we submit the report Babe::report_equivocation_unsigned( RuntimeOrigin::none(), - Box::new(equivocation_proof), + equivocation_proof, key_owner_proof, ) .unwrap(); @@ -846,7 +847,7 @@ fn valid_equivocation_reports_dont_pay_fees() { // check the dispatch info for the call. let info = Call::::report_equivocation_unsigned { - equivocation_proof: Box::new(equivocation_proof.clone()), + equivocation_proof: equivocation_proof.clone(), key_owner_proof: key_owner_proof.clone(), } .get_dispatch_info(); @@ -859,7 +860,7 @@ fn valid_equivocation_reports_dont_pay_fees() { // report the equivocation. let post_info = Babe::report_equivocation_unsigned( RuntimeOrigin::none(), - Box::new(equivocation_proof.clone()), + equivocation_proof.clone(), key_owner_proof.clone(), ) .unwrap(); @@ -873,7 +874,7 @@ fn valid_equivocation_reports_dont_pay_fees() { // duplicate. let post_info = Babe::report_equivocation_unsigned( RuntimeOrigin::none(), - Box::new(equivocation_proof), + equivocation_proof, key_owner_proof, ) .err() diff --git a/frame/grandpa/src/equivocation.rs b/frame/grandpa/src/equivocation.rs index 6323aa195cd2f..748b5b738b6d9 100644 --- a/frame/grandpa/src/equivocation.rs +++ b/frame/grandpa/src/equivocation.rs @@ -35,88 +35,160 @@ //! that the `ValidateUnsigned` for the GRANDPA pallet is used in the runtime //! definition. -use sp_std::prelude::*; - use codec::{self as codec, Decode, Encode}; use frame_support::traits::{Get, KeyOwnerProofSystem}; -use sp_finality_grandpa::{EquivocationProof, RoundNumber, SetId}; +use log::{error, info}; + +use sp_finality_grandpa::{AuthorityId, EquivocationProof, RoundNumber, SetId, KEY_TYPE}; use sp_runtime::{ transaction_validity::{ InvalidTransaction, TransactionPriority, TransactionSource, TransactionValidity, TransactionValidityError, ValidTransaction, }, - DispatchResult, Perbill, + DispatchResult, KeyTypeId, Perbill, }; +use sp_session::{GetSessionNumber, GetValidatorCount}; use sp_staking::{ - equivocation::EquivocationHandler as EquivocationHandlerT, + equivocation::OffenceReportSystem, offence::{Kind, Offence, ReportOffence}, SessionIndex, }; +use sp_std::prelude::*; -use super::{Call, Config, Pallet, LOG_TARGET}; +use super::{Call, Config, Error, Pallet, LOG_TARGET}; /// Generic equivocation handler. This type implements `HandleEquivocation` /// using existing subsystems that are part of frame (type bounds described /// below) and will dispatch to them directly, it's only purpose is to wire all /// subsystems together. #[derive(Default)] -pub struct EquivocationHandler(sp_std::marker::PhantomData<(T, R, L)>); +pub struct EquivocationHandler(sp_std::marker::PhantomData<(T, R, P, L)>); // We use the authorship pallet to fetch the current block author and use // `offchain::SendTransactionTypes` for unsigned extrinsic creation and // submission. -impl EquivocationHandlerT for EquivocationHandler +impl OffenceReportSystem for EquivocationHandler where - T: Config + pallet_authorship::Config + frame_system::offchain::SendTransactionTypes>, - R: ReportOffence>, + T: Config< + EquivocationProof = EquivocationProof< + ::Hash, + ::BlockNumber, + >, + > + pallet_authorship::Config + + frame_system::offchain::SendTransactionTypes>, + R: ReportOffence>, + P: KeyOwnerProofSystem<(KeyTypeId, AuthorityId), Proof = T::KeyOwnerProof>, + P::IdentificationTuple: Clone, + P::Proof: GetSessionNumber + GetValidatorCount, L: Get, { - type Offence = GrandpaEquivocationOffence; + type Offence = EquivocationOffence; type OffenceProof = EquivocationProof; type KeyOwnerProof = T::KeyOwnerProof; - type ReportOffence = R; - type ReportLongevity = L; - fn submit_offence_proof( + type Reporter = T::AccountId; + + fn report_evidence( + reporter: Option, equivocation_proof: Self::OffenceProof, key_owner_proof: Self::KeyOwnerProof, ) -> DispatchResult { - use frame_system::offchain::SubmitTransaction; + let reporter = reporter.or_else(|| >::author()); + + // 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 offender = equivocation_proof.offender().clone(); + let set_id = equivocation_proof.set_id(); + let round = equivocation_proof.round(); + let session_index = key_owner_proof.session(); + let validator_count = key_owner_proof.validator_count(); + + // Validate equivocation proof (check votes are different and signatures are valid). + if !sp_finality_grandpa::check_equivocation_proof(equivocation_proof) { + return Err(Error::::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::::InvalidKeyOwnershipProof)?; - let call = Call::report_equivocation_unsigned { - equivocation_proof: Box::new(equivocation_proof), - key_owner_proof, + // 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::::get(set_id - 1) + .ok_or(Error::::InvalidEquivocationProof)?; + Some(idx) + } else { + None }; - match SubmitTransaction::>::submit_unsigned_transaction(call.into()) { - Ok(()) => log::info!(target: LOG_TARGET, "Submitted GRANDPA equivocation report.",), - Err(e) => - log::error!(target: LOG_TARGET, "Error submitting equivocation report: {:?}", e,), + let set_id_session_index = + crate::SetIdSession::::get(set_id).ok_or(Error::::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::::InvalidEquivocationProof.into()) } + let offence = EquivocationOffence { + time_slot: GrandpaTimeSlot { set_id, round }, + session_index, + offender, + validator_count, + }; + + R::report_offence(reporter.into_iter().collect(), offence) + .map_err(|_| Error::::DuplicateOffenceReport)?; + Ok(()) } - fn block_author() -> Option { - >::author() + fn check_evidence( + equivocation_proof: &Self::OffenceProof, + key_owner_proof: &Self::KeyOwnerProof, + ) -> DispatchResult { + // Check the membership proof to extract the offender's id + let key = (sp_finality_grandpa::KEY_TYPE, equivocation_proof.offender().clone()); + let offender = P::check_proof(key, key_owner_proof.clone()) + .ok_or(Error::::InvalidKeyOwnershipProof)?; + + // Check if the offence has already been reported, and if so then we can discard the report. + let time_slot = GrandpaTimeSlot { + set_id: equivocation_proof.set_id(), + round: equivocation_proof.round(), + }; + if R::is_known_offence(&[offender], &time_slot) { + Err(Error::::DuplicateOffenceReport.into()) + } else { + Ok(()) + } } -} -pub struct NullHandler(sp_std::marker::PhantomData); - -impl EquivocationHandlerT for NullHandler { - // TODO: is possible to get rid of this? Like defining a default dummy Offence - // OR even better get rid of this EquivocationHandler trait... - type Offence = GrandpaEquivocationOffence; + fn submit_evidence( + equivocation_proof: Self::OffenceProof, + key_owner_proof: Self::KeyOwnerProof, + ) -> bool { + use frame_system::offchain::SubmitTransaction; - type OffenceProof = EquivocationProof; - type KeyOwnerProof = T::KeyOwnerProof; - type ReportOffence = (); - type ReportLongevity = (); + let call = Call::report_equivocation_unsigned { equivocation_proof, key_owner_proof }; + let res = SubmitTransaction::>::submit_unsigned_transaction(call.into()); + match res { + Ok(()) => info!(target: LOG_TARGET, "Submitted equivocation report."), + Err(e) => error!(target: LOG_TARGET, "Error submitting equivocation report: {:?}", e), + } + res.is_ok() + } } /// A round number and set id which point on the time of an offence. @@ -149,20 +221,22 @@ impl Pallet { }, } - // check report staleness - is_known_offence::(equivocation_proof, key_owner_proof)?; + // Check report validity + // TODO DAVXY: propagate error + T::OffenceReportSystem::check_evidence(equivocation_proof, key_owner_proof) + .map_err(|_| InvalidTransaction::Stale)?; - let longevity = ::ReportLongevity::get(); + let longevity = ::ReportLongevity::get(); + // TODO DAVXY: is ok the hash of the serialized structure as an identifier? + // Was: (equivocation_proof.offender(), equivocation_proof.set_id(), + // equivocation_proof.round()) Oterwise we're going to introduce tag() + let tag = equivocation_proof.using_encoded(|bytes| sp_io::hashing::blake2_256(bytes)); 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(), - )) + .and_provides(tag) .longevity(longevity) // We don't propagate this. This can never be included on a remote node. .propagate(false) @@ -174,38 +248,17 @@ impl Pallet { pub fn pre_dispatch(call: &Call) -> Result<(), TransactionValidityError> { if let Call::report_equivocation_unsigned { equivocation_proof, key_owner_proof } = call { - is_known_offence::(equivocation_proof, key_owner_proof) + // TODO DAVXY: propagate error + T::OffenceReportSystem::check_evidence(equivocation_proof, key_owner_proof) + .map_err(|_| TransactionValidityError::Invalid(InvalidTransaction::Stale)) } else { Err(InvalidTransaction::Call.into()) } } } -fn is_known_offence( - equivocation_proof: &EquivocationProof, - key_owner_proof: &T::KeyOwnerProof, -) -> Result<(), TransactionValidityError> { - // check the membership proof to extract the offender's id - let key = (sp_finality_grandpa::KEY_TYPE, equivocation_proof.offender().clone()); - - let offender = T::KeyOwnerProofSystem::check_proof(key, key_owner_proof.clone()) - .ok_or(InvalidTransaction::BadProof)?; - - // Check if the offence has already been reported, and if so then we can discard the report. - let time_slot = - GrandpaTimeSlot { set_id: equivocation_proof.set_id(), round: equivocation_proof.round() }; - - let is_known_offence = T::HandleEquivocation::is_known_offence(&[offender], &time_slot); - - if is_known_offence { - Err(InvalidTransaction::Stale.into()) - } else { - Ok(()) - } -} - /// A GRANDPA equivocation offence report. -pub struct GrandpaEquivocationOffence { +pub struct EquivocationOffence { /// Time slot at which this incident happened. pub time_slot: GrandpaTimeSlot, /// The session index in which the incident happened. @@ -214,24 +267,17 @@ pub struct GrandpaEquivocationOffence { pub validator_count: u32, /// The authority which produced this equivocation. pub offender: O, - /// The offence reporter. - pub reporter: Option, } -impl Offence for GrandpaEquivocationOffence { +impl Offence for EquivocationOffence { const ID: Kind = *b"grandpa:equivoca"; type TimeSlot = GrandpaTimeSlot; type Offender = O; - type Reporter = R; fn offenders(&self) -> Vec { vec![self.offender.clone()] } - fn reporters(&self) -> Vec { - self.reporter.clone().into_iter().collect() - } - fn session_index(&self) -> SessionIndex { self.session_index } diff --git a/frame/grandpa/src/lib.rs b/frame/grandpa/src/lib.rs index a22708861ac3f..fcfdcc26a028f 100644 --- a/frame/grandpa/src/lib.rs +++ b/frame/grandpa/src/lib.rs @@ -36,21 +36,21 @@ use sp_std::prelude::*; use codec::{self as codec, Decode, Encode, MaxEncodedLen}; pub use fg_primitives::{AuthorityId, AuthorityList, AuthorityWeight, VersionedAuthorityList}; use fg_primitives::{ - ConsensusLog, EquivocationProof, ScheduledChange, SetId, GRANDPA_AUTHORITIES_KEY, - GRANDPA_ENGINE_ID, RUNTIME_LOG_TARGET as LOG_TARGET, + ConsensusLog, ScheduledChange, SetId, GRANDPA_AUTHORITIES_KEY, GRANDPA_ENGINE_ID, + RUNTIME_LOG_TARGET as LOG_TARGET, }; use frame_support::{ dispatch::{DispatchResultWithPostInfo, Pays}, pallet_prelude::Get, storage, - traits::{KeyOwnerProofSystem, OneSessionHandler}, + traits::OneSessionHandler, weights::Weight, WeakBoundedVec, }; use scale_info::TypeInfo; -use sp_runtime::{generic::DigestItem, traits::Zero, DispatchResult, KeyTypeId}; +use sp_runtime::{generic::DigestItem, traits::Zero, DispatchResult}; use sp_session::{GetSessionNumber, GetValidatorCount}; -use sp_staking::{equivocation::EquivocationHandler as EquivocationHandlerT, SessionIndex}; +use sp_staking::{equivocation::OffenceReportSystem, SessionIndex}; mod default_weights; mod equivocation; @@ -63,9 +63,7 @@ mod mock; #[cfg(all(feature = "std", test))] mod tests; -pub use equivocation::{ - EquivocationHandler, GrandpaEquivocationOffence, GrandpaTimeSlot, NullHandler, -}; +pub use equivocation::{EquivocationHandler, EquivocationOffence, GrandpaTimeSlot}; pub use pallet::*; @@ -90,34 +88,6 @@ pub mod pallet { + Into<::RuntimeEvent> + IsType<::RuntimeEvent>; - /// 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 identification of a key owner, used when reporting equivocations. - type KeyOwnerIdentification: Parameter; - - /// A system for proving ownership of keys, i.e. that a given key was part - /// of a validator set, needed for validating equivocation reports. - type KeyOwnerProofSystem: KeyOwnerProofSystem< - (KeyTypeId, AuthorityId), - Proof = Self::KeyOwnerProof, - IdentificationTuple = Self::KeyOwnerIdentification, - >; - - /// The equivocation handling subsystem, defines methods to report an - /// offence (after the equivocation has been validated) and for submitting a - /// transaction to report an equivocation (from an offchain context). - /// NOTE: when enabling equivocation handling (i.e. this type isn't set to - /// `()`) you must use this pallet's `ValidateUnsigned` in the runtime - /// definition. - type HandleEquivocation: EquivocationHandlerT< - KeyOwnerProof = Self::KeyOwnerProof, - Offence = GrandpaEquivocationOffence, - OffenceProof = EquivocationProof, - >; - /// Weights for this pallet. type WeightInfo: WeightInfo; @@ -133,6 +103,26 @@ pub mod pallet { /// can be zero. #[pallet::constant] type MaxSetIdSessionEntries: Get; + + /// 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 proof + type EquivocationProof: Parameter; + + /// The equivocation handling subsystem, defines methods to report an + /// offence (after the equivocation has been validated) and for submitting a + /// transaction to report an equivocation (from an offchain context). + /// NOTE: when enabling equivocation handling (i.e. this type isn't set to + /// `()`) you must use this pallet's `ValidateUnsigned` in the runtime + /// definition. + type OffenceReportSystem: OffenceReportSystem< + KeyOwnerProof = Self::KeyOwnerProof, + OffenceProof = Self::EquivocationProof, + Reporter = Self::AccountId, + >; } #[pallet::hooks] @@ -209,12 +199,14 @@ pub mod pallet { #[pallet::weight(T::WeightInfo::report_equivocation(key_owner_proof.validator_count()))] pub fn report_equivocation( origin: OriginFor, - equivocation_proof: Box>, + equivocation_proof: T::EquivocationProof, key_owner_proof: T::KeyOwnerProof, ) -> DispatchResultWithPostInfo { - let reporter = ensure_signed(origin)?; + let reporter = Some(ensure_signed(origin)?); - Self::do_report_equivocation(Some(reporter), *equivocation_proof, key_owner_proof) + T::OffenceReportSystem::report_evidence(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 @@ -230,16 +222,14 @@ pub mod pallet { #[pallet::weight(T::WeightInfo::report_equivocation(key_owner_proof.validator_count()))] pub fn report_equivocation_unsigned( origin: OriginFor, - equivocation_proof: Box>, + equivocation_proof: T::EquivocationProof, key_owner_proof: T::KeyOwnerProof, ) -> DispatchResultWithPostInfo { ensure_none(origin)?; - Self::do_report_equivocation( - T::HandleEquivocation::block_author(), - *equivocation_proof, - key_owner_proof, - ) + T::OffenceReportSystem::report_evidence(None, equivocation_proof, key_owner_proof)?; + // Waive the fee since the report is valid and beneficial + Ok(Pays::No.into()) } /// Note that the current authority set of the GRANDPA finality gadget has stalled. @@ -535,81 +525,15 @@ impl Pallet { SetIdSession::::insert(0, 0); } - fn do_report_equivocation( - reporter: Option, - equivocation_proof: EquivocationProof, - key_owner_proof: T::KeyOwnerProof, - ) -> DispatchResultWithPostInfo { - // 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_count = key_owner_proof.validator_count(); - - // validate the key ownership proof extracting the id of the offender. - let offender = T::KeyOwnerProofSystem::check_proof( - (fg_primitives::KEY_TYPE, equivocation_proof.offender().clone()), - key_owner_proof, - ) - .ok_or(Error::::InvalidKeyOwnershipProof)?; - - // validate equivocation proof (check votes are different and - // signatures are valid). - if !sp_finality_grandpa::check_equivocation_proof(equivocation_proof) { - return Err(Error::::InvalidEquivocationProof.into()) - } - - // fetch the current and previous sets last session index. on the - // genesis set there's no previous set. - let previous_set_id_session_index = if set_id == 0 { - None - } else { - let session_index = - Self::session_for_set(set_id - 1).ok_or(Error::::InvalidEquivocationProof)?; - - Some(session_index) - }; - - let set_id_session_index = - Self::session_for_set(set_id).ok_or(Error::::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::::InvalidEquivocationProof.into()) - } - - let offence = GrandpaEquivocationOffence { - time_slot: GrandpaTimeSlot { set_id, round }, - session_index, - offender, - validator_count, - reporter, - }; - - T::HandleEquivocation::report_offence(offence) - .map_err(|_| Error::::DuplicateOffenceReport)?; - - // waive the fee since the report is valid and beneficial - Ok(Pays::No.into()) - } - /// 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, + equivocation_proof: T::EquivocationProof, key_owner_proof: T::KeyOwnerProof, - ) -> Option<()> { - T::HandleEquivocation::submit_offence_proof(equivocation_proof, key_owner_proof).ok() + ) -> bool { + T::OffenceReportSystem::submit_evidence(equivocation_proof, key_owner_proof) } fn on_stalled(further_wait: T::BlockNumber, median: T::BlockNumber) { diff --git a/frame/grandpa/src/mock.rs b/frame/grandpa/src/mock.rs index 1441610817384..513a07f613cd7 100644 --- a/frame/grandpa/src/mock.rs +++ b/frame/grandpa/src/mock.rs @@ -225,21 +225,16 @@ parameter_types! { impl Config for Test { type RuntimeEvent = RuntimeEvent; - type KeyOwnerProofSystem = Historical; - - type KeyOwnerProof = - >::Proof; - - type KeyOwnerIdentification = >::IdentificationTuple; - - type HandleEquivocation = super::EquivocationHandler; - type WeightInfo = (); type MaxAuthorities = ConstU32<100>; type MaxSetIdSessionEntries = MaxSetIdSessionEntries; + + type KeyOwnerProof = >::Proof; + + type EquivocationProof = sp_finality_grandpa::EquivocationProof; + + type OffenceReportSystem = + super::EquivocationHandler; } pub fn grandpa_log(log: ConsensusLog) -> DigestItem { diff --git a/frame/grandpa/src/tests.rs b/frame/grandpa/src/tests.rs index e090dcebb60bf..c04d58f605c9b 100644 --- a/frame/grandpa/src/tests.rs +++ b/frame/grandpa/src/tests.rs @@ -26,7 +26,7 @@ use fg_primitives::ScheduledChange; use frame_support::{ assert_err, assert_noop, assert_ok, dispatch::{GetDispatchInfo, Pays}, - traits::{Currency, OnFinalize, OneSessionHandler}, + traits::{Currency, KeyOwnerProofSystem, OnFinalize, OneSessionHandler}, }; use frame_system::{EventRecord, Phase}; use sp_core::H256; @@ -360,7 +360,7 @@ fn report_equivocation_current_set_works() { // report the equivocation and the tx should be dispatched successfully assert_ok!(Grandpa::report_equivocation_unsigned( RuntimeOrigin::none(), - Box::new(equivocation_proof), + equivocation_proof, key_owner_proof, ),); @@ -438,7 +438,7 @@ fn report_equivocation_old_set_works() { // the old set, the tx should be dispatched successfully assert_ok!(Grandpa::report_equivocation_unsigned( RuntimeOrigin::none(), - Box::new(equivocation_proof), + equivocation_proof, key_owner_proof, ),); @@ -501,7 +501,7 @@ fn report_equivocation_invalid_set_id() { assert_err!( Grandpa::report_equivocation_unsigned( RuntimeOrigin::none(), - Box::new(equivocation_proof), + equivocation_proof, key_owner_proof, ), Error::::InvalidEquivocationProof, @@ -542,7 +542,7 @@ fn report_equivocation_invalid_session() { assert_err!( Grandpa::report_equivocation_unsigned( RuntimeOrigin::none(), - Box::new(equivocation_proof), + equivocation_proof, key_owner_proof, ), Error::::InvalidEquivocationProof, @@ -587,7 +587,7 @@ fn report_equivocation_invalid_key_owner_proof() { assert_err!( Grandpa::report_equivocation_unsigned( RuntimeOrigin::none(), - Box::new(equivocation_proof), + equivocation_proof, invalid_key_owner_proof, ), Error::::InvalidKeyOwnershipProof, @@ -618,7 +618,7 @@ fn report_equivocation_invalid_equivocation_proof() { assert_err!( Grandpa::report_equivocation_unsigned( RuntimeOrigin::none(), - Box::new(equivocation_proof), + equivocation_proof, key_owner_proof.clone(), ), Error::::InvalidEquivocationProof, @@ -688,7 +688,7 @@ fn report_equivocation_validate_unsigned_prevents_duplicates() { Historical::prove((sp_finality_grandpa::KEY_TYPE, &equivocation_key)).unwrap(); let call = Call::report_equivocation_unsigned { - equivocation_proof: Box::new(equivocation_proof.clone()), + equivocation_proof: equivocation_proof.clone(), key_owner_proof: key_owner_proof.clone(), }; @@ -702,7 +702,10 @@ fn report_equivocation_validate_unsigned_prevents_duplicates() { ); // the transaction is valid when passed as local - let tx_tag = (equivocation_key, set_id, 1u64); + //let tx_tag = (equivocation_key, set_id, 1u64); + // TODO DAVXY: confirm that this is equally good... + use codec::Encode; + let tag = equivocation_proof.using_encoded(|bytes| sp_io::hashing::blake2_256(bytes)); assert_eq!( ::validate_unsigned( @@ -712,7 +715,7 @@ fn report_equivocation_validate_unsigned_prevents_duplicates() { TransactionValidity::Ok(ValidTransaction { priority: TransactionPriority::max_value(), requires: vec![], - provides: vec![("GrandpaEquivocation", tx_tag).encode()], + provides: vec![("GrandpaEquivocation", tag).encode()], longevity: ReportLongevity::get(), propagate: false, }) @@ -724,7 +727,7 @@ fn report_equivocation_validate_unsigned_prevents_duplicates() { // we submit the report Grandpa::report_equivocation_unsigned( RuntimeOrigin::none(), - Box::new(equivocation_proof), + equivocation_proof, key_owner_proof, ) .unwrap(); @@ -877,7 +880,7 @@ fn valid_equivocation_reports_dont_pay_fees() { // check the dispatch info for the call. let info = Call::::report_equivocation_unsigned { - equivocation_proof: Box::new(equivocation_proof.clone()), + equivocation_proof: equivocation_proof.clone(), key_owner_proof: key_owner_proof.clone(), } .get_dispatch_info(); @@ -889,7 +892,7 @@ fn valid_equivocation_reports_dont_pay_fees() { // report the equivocation. let post_info = Grandpa::report_equivocation_unsigned( RuntimeOrigin::none(), - Box::new(equivocation_proof.clone()), + equivocation_proof.clone(), key_owner_proof.clone(), ) .unwrap(); @@ -903,7 +906,7 @@ fn valid_equivocation_reports_dont_pay_fees() { // duplicate. let post_info = Grandpa::report_equivocation_unsigned( RuntimeOrigin::none(), - Box::new(equivocation_proof), + equivocation_proof, key_owner_proof, ) .err() diff --git a/frame/offences/src/lib.rs b/frame/offences/src/lib.rs index 0eb895ce5949b..352fa02a18e72 100644 --- a/frame/offences/src/lib.rs +++ b/frame/offences/src/lib.rs @@ -112,14 +112,13 @@ pub mod pallet { } } -impl ReportOffence for Pallet +impl ReportOffence for Pallet where T: Config, - O: Offence, + O: Offence, { - fn report_offence(offence: O) -> Result<(), OffenceError> { + fn report_offence(reporters: Vec, offence: O) -> Result<(), OffenceError> { let offenders = offence.offenders(); - let reporters = offence.reporters(); let time_slot = offence.time_slot(); // Go through all offenders in the offence report and find all offenders that were spotted diff --git a/frame/offences/src/mock.rs b/frame/offences/src/mock.rs index 72e1736aa79fd..1f83bc6b48bd8 100644 --- a/frame/offences/src/mock.rs +++ b/frame/offences/src/mock.rs @@ -144,16 +144,11 @@ impl offence::Offence for Offence { const ID: offence::Kind = KIND; type TimeSlot = u128; type Offender = u64; - type Reporter = u64; fn offenders(&self) -> Vec { self.offenders.clone() } - fn reporters(&self) -> Vec { - Default::default() - } - fn validator_set_count(&self) -> u32 { self.validator_set_count } diff --git a/frame/offences/src/tests.rs b/frame/offences/src/tests.rs index e2b7dcba589b6..d224bf001330e 100644 --- a/frame/offences/src/tests.rs +++ b/frame/offences/src/tests.rs @@ -37,7 +37,7 @@ fn should_report_an_authority_and_trigger_on_offence() { let offence = Offence { validator_set_count: 5, time_slot, offenders: vec![5] }; // when - Offences::report_offence(offence).unwrap(); + Offences::report_offence(vec![], offence).unwrap(); // then with_on_offence_fractions(|f| { @@ -54,7 +54,7 @@ fn should_not_report_the_same_authority_twice_in_the_same_slot() { assert_eq!(offence_reports(KIND, time_slot), vec![]); let offence = Offence { validator_set_count: 5, time_slot, offenders: vec![5] }; - Offences::report_offence(offence.clone()).unwrap(); + Offences::report_offence(vec![], offence.clone()).unwrap(); with_on_offence_fractions(|f| { assert_eq!(f.clone(), vec![Perbill::from_percent(25)]); f.clear(); @@ -62,7 +62,7 @@ fn should_not_report_the_same_authority_twice_in_the_same_slot() { // when // report for the second time - assert_eq!(Offences::report_offence(offence), Err(OffenceError::DuplicateReport)); + assert_eq!(Offences::report_offence(vec![], offence), Err(OffenceError::DuplicateReport)); // then with_on_offence_fractions(|f| { @@ -79,7 +79,7 @@ fn should_report_in_different_time_slot() { assert_eq!(offence_reports(KIND, time_slot), vec![]); let mut offence = Offence { validator_set_count: 5, time_slot, offenders: vec![5] }; - Offences::report_offence(offence.clone()).unwrap(); + Offences::report_offence(vec![], offence.clone()).unwrap(); with_on_offence_fractions(|f| { assert_eq!(f.clone(), vec![Perbill::from_percent(25)]); f.clear(); @@ -88,7 +88,7 @@ fn should_report_in_different_time_slot() { // when // report for the second time offence.time_slot += 1; - Offences::report_offence(offence).unwrap(); + Offences::report_offence(vec![], offence).unwrap(); // then with_on_offence_fractions(|f| { @@ -107,7 +107,7 @@ fn should_deposit_event() { let offence = Offence { validator_set_count: 5, time_slot, offenders: vec![5] }; // when - Offences::report_offence(offence).unwrap(); + Offences::report_offence(vec![], offence).unwrap(); // then assert_eq!( @@ -132,7 +132,7 @@ fn doesnt_deposit_event_for_dups() { assert_eq!(offence_reports(KIND, time_slot), vec![]); let offence = Offence { validator_set_count: 5, time_slot, offenders: vec![5] }; - Offences::report_offence(offence.clone()).unwrap(); + Offences::report_offence(vec![], offence.clone()).unwrap(); with_on_offence_fractions(|f| { assert_eq!(f.clone(), vec![Perbill::from_percent(25)]); f.clear(); @@ -140,7 +140,7 @@ fn doesnt_deposit_event_for_dups() { // when // report for the second time - assert_eq!(Offences::report_offence(offence), Err(OffenceError::DuplicateReport)); + assert_eq!(Offences::report_offence(vec![], offence), Err(OffenceError::DuplicateReport)); // then // there is only one event. @@ -171,23 +171,23 @@ fn reports_if_an_offence_is_dup() { // the report for authority 0 at time slot 42 should not be a known // offence - assert!(!>::is_known_offence( + assert!(!>::is_known_offence( &test_offence.offenders, &test_offence.time_slot )); // we report an offence for authority 0 at time slot 42 - Offences::report_offence(test_offence.clone()).unwrap(); + Offences::report_offence(vec![], test_offence.clone()).unwrap(); // the same report should be a known offence now - assert!(>::is_known_offence( + assert!(>::is_known_offence( &test_offence.offenders, &test_offence.time_slot )); // and reporting it again should yield a duplicate report error assert_eq!( - Offences::report_offence(test_offence.clone()), + Offences::report_offence(vec![], test_offence.clone()), Err(OffenceError::DuplicateReport) ); @@ -195,18 +195,18 @@ fn reports_if_an_offence_is_dup() { test_offence.offenders.push(1); // it should not be a known offence anymore - assert!(!>::is_known_offence( + assert!(!>::is_known_offence( &test_offence.offenders, &test_offence.time_slot )); // and reporting it again should work without any error - assert_eq!(Offences::report_offence(test_offence.clone()), Ok(())); + assert_eq!(Offences::report_offence(vec![], test_offence.clone()), Ok(())); // creating a new offence for the same authorities on the next slot // should be considered a new offence and thefore not known let test_offence_next_slot = offence(time_slot + 1, vec![0, 1]); - assert!(!>::is_known_offence( + assert!(!>::is_known_offence( &test_offence_next_slot.offenders, &test_offence_next_slot.time_slot )); @@ -224,7 +224,7 @@ fn should_properly_count_offences() { let offence1 = Offence { validator_set_count: 5, time_slot, offenders: vec![5] }; let offence2 = Offence { validator_set_count: 5, time_slot, offenders: vec![4] }; - Offences::report_offence(offence1).unwrap(); + Offences::report_offence(vec![], offence1).unwrap(); with_on_offence_fractions(|f| { assert_eq!(f.clone(), vec![Perbill::from_percent(25)]); f.clear(); @@ -232,7 +232,7 @@ fn should_properly_count_offences() { // when // report for the second time - Offences::report_offence(offence2).unwrap(); + Offences::report_offence(vec![], offence2).unwrap(); // then // the 1st authority should have count 2 and the 2nd one should be reported only once. @@ -261,7 +261,7 @@ fn should_properly_sort_offences() { Offence { validator_set_count: 5, time_slot: time_slot + 1, offenders: vec![6, 7] }; let offence4 = Offence { validator_set_count: 5, time_slot: time_slot - 1, offenders: vec![3] }; - Offences::report_offence(offence1).unwrap(); + Offences::report_offence(vec![], offence1).unwrap(); with_on_offence_fractions(|f| { assert_eq!(f.clone(), vec![Perbill::from_percent(25)]); f.clear(); @@ -269,9 +269,9 @@ fn should_properly_sort_offences() { // when // report for the second time - Offences::report_offence(offence2).unwrap(); - Offences::report_offence(offence3).unwrap(); - Offences::report_offence(offence4).unwrap(); + Offences::report_offence(vec![], offence2).unwrap(); + Offences::report_offence(vec![], offence3).unwrap(); + Offences::report_offence(vec![], offence4).unwrap(); // then let same_kind_reports = Vec::<(u128, sp_core::H256)>::decode( diff --git a/frame/staking/src/lib.rs b/frame/staking/src/lib.rs index 008066fa8258a..83f3d22875ca1 100644 --- a/frame/staking/src/lib.rs +++ b/frame/staking/src/lib.rs @@ -912,19 +912,19 @@ pub struct FilterHistoricalOffences { _inner: sp_std::marker::PhantomData<(T, R)>, } -impl ReportOffence for FilterHistoricalOffences, R> +impl ReportOffence for FilterHistoricalOffences, R> where T: Config, - R: ReportOffence, + R: ReportOffence, O: Offence, { - fn report_offence(offence: O) -> Result<(), OffenceError> { + fn report_offence(reporters: Vec, offence: O) -> Result<(), OffenceError> { // Disallow any slashing from before the current bonding period. let offence_session = offence.session_index(); let bonded_eras = BondedEras::::get(); if bonded_eras.first().filter(|(_, start)| offence_session >= *start).is_some() { - R::report_offence(offence) + R::report_offence(reporters, offence) } else { >::deposit_event(Event::::OldSlashingReportDiscarded { session_index: offence_session, diff --git a/primitives/consensus/slots/src/lib.rs b/primitives/consensus/slots/src/lib.rs index 21b3cad1e7167..c218809760a14 100644 --- a/primitives/consensus/slots/src/lib.rs +++ b/primitives/consensus/slots/src/lib.rs @@ -130,7 +130,7 @@ impl SlotDuration { /// produces more than one block on the same slot. The proof of equivocation /// are the given distinct headers that were signed by the validator and which /// include the slot number. -#[derive(Clone, Debug, Decode, Encode, PartialEq, TypeInfo)] +#[derive(Clone, Debug, Decode, Encode, PartialEq, TypeInfo, Eq)] pub struct EquivocationProof { /// Returns the authority id of the equivocator. pub offender: Id, diff --git a/primitives/finality-grandpa/src/lib.rs b/primitives/finality-grandpa/src/lib.rs index f5307ab06bde7..0b5e246e46c10 100644 --- a/primitives/finality-grandpa/src/lib.rs +++ b/primitives/finality-grandpa/src/lib.rs @@ -240,6 +240,9 @@ pub struct EquivocationProof { equivocation: Equivocation, } +// Don't bother the grandpa crate... +impl Eq for EquivocationProof {} + impl EquivocationProof { /// Create a new `EquivocationProof` for the given set id and using the /// given equivocation as proof. diff --git a/primitives/staking/src/equivocation.rs b/primitives/staking/src/equivocation.rs index 0645dbae7c532..342178a2616ad 100644 --- a/primitives/staking/src/equivocation.rs +++ b/primitives/staking/src/equivocation.rs @@ -1,29 +1,71 @@ -use crate::{ - offence::{Offence, OffenceError, ReportOffence}, - SessionIndex, -}; +use crate::{offence::Offence, SessionIndex}; use sp_core::Get; use sp_runtime::{DispatchResult, Perbill}; use sp_std::vec::Vec; -// Dummy offence implementation used by the "null" Offence reporter. -// Should remain private... -pub struct NullOffence; +/// TODO:<<<<<<<<<<<< BETTER DOCS +/// +/// A system capable of construct, report and submit offences. +/// +/// Implementors of this trait provides a wrapper around the lower level rough +/// system in order to provide exitrinsic submission of reports an construction +/// of offences given the offence and offender key ownership proofs. +/// +/// It is assumed that this subsystem takes care of checking key ownership proof +/// before report submission. +pub trait OffenceReportSystem { + /// TODO: DOC + type Offence: Offence; + + /// TODO: DOC + type OffenceProof; + + /// TODO: DOC + type KeyOwnerProof; + + /// Longevity, in blocks, for the report validity. When using the staking + /// pallet this should be equal to the bonding duration (in blocks, not eras). + type ReportLongevity: Get; + + /// Offence reporter type + type Reporter; + + /// Report offence to the `ReportOffence` handler. + fn report_evidence( + _reporter: Option, + _offence_proof: Self::OffenceProof, + _key_owner_proof: Self::KeyOwnerProof, + ) -> DispatchResult { + Ok(()) + } + + /// Check if is a known offence. + fn check_evidence( + _offence_proof: &Self::OffenceProof, + _key_owner_proof: &Self::KeyOwnerProof, + ) -> DispatchResult { + Ok(()) + } + + /// Create and dispatch an offence report extrinsic. + fn submit_evidence( + _offence_proof: Self::OffenceProof, + _key_owner_proof: Self::KeyOwnerProof, + ) -> bool { + true + } +} -impl Offence for NullOffence { +// Dummy offence type meant to be used by the dummy offence report system. +impl Offence for () { const ID: crate::offence::Kind = [0; 16]; type TimeSlot = (); type Offender = (); - type Reporter = (); fn offenders(&self) -> Vec { Default::default() } - fn reporters(&self) -> Vec { - Default::default() - } - fn validator_set_count(&self) -> u32 { 0 } @@ -41,53 +83,16 @@ impl Offence for NullOffence { } } -pub trait EquivocationHandler { - type Offence: Offence; - - type OffenceProof; - - type KeyOwnerProof; - - type ReportOffence: ReportOffence; - - /// The longevity, in blocks, that the equivocation report is valid for. When using the staking - /// pallet this should be equal to the bonding duration (in blocks, not eras). - type ReportLongevity: Get; - - fn report_offence(offence: Self::Offence) -> Result<(), OffenceError> { - Self::ReportOffence::report_offence(offence) - } - - fn is_known_offence( - offenders: &[::Offender], - time_slot: &::TimeSlot, - ) -> bool { - Self::ReportOffence::is_known_offence(offenders, time_slot) - } - - /// Create and dispatch an offence report extrinsic. - fn submit_offence_proof( - _offence_proof: Self::OffenceProof, - _key_owner_proof: Self::KeyOwnerProof, - ) -> DispatchResult { - Ok(()) - } +// Dummy report system. +// Should always give successful results +impl OffenceReportSystem for () { + type Offence = (); - /// Fetch the current reporter id, if defined. - // TODO: rename to reporter and move to offence trait? - fn block_author() -> Option<::Reporter> { - None - } -} - -impl EquivocationHandler for () { - type Offence = NullOffence; + type Reporter = (); type OffenceProof = (); type KeyOwnerProof = (); - type ReportOffence = (); - type ReportLongevity = (); } diff --git a/primitives/staking/src/offence.rs b/primitives/staking/src/offence.rs index 13a4d71f031fc..af52dd03a0ef4 100644 --- a/primitives/staking/src/offence.rs +++ b/primitives/staking/src/offence.rs @@ -71,9 +71,6 @@ pub trait Offence { /// Offender Identifier. type Offender; - /// Reporter identifier. - type Reporter; - /// A type that represents a point in time on an abstract timescale. /// /// See `Offence::time_slot` for details. The only requirement is that such timescale could be @@ -83,9 +80,6 @@ pub trait Offence { /// The list of all offenders involved in this incident. fn offenders(&self) -> Vec; - /// The list of all reporters of this incident. - fn reporters(&self) -> Vec; - /// The session index that is used for querying the validator set for the `slash_fraction` /// function. /// @@ -144,9 +138,9 @@ impl sp_runtime::traits::Printable for OffenceError { } /// A trait for decoupling offence reporters from the actual handling of offence reports. -pub trait ReportOffence { +pub trait ReportOffence { /// Report an `offence` and reward given `reporters`. - fn report_offence(offence: O) -> Result<(), OffenceError>; + fn report_offence(reporters: Vec, offence: O) -> Result<(), OffenceError>; /// Returns true iff all of the given offenders have been previously reported /// at the given time slot. This function is useful to prevent the sending of @@ -154,8 +148,8 @@ pub trait ReportOffence { fn is_known_offence(offenders: &[O::Offender], time_slot: &O::TimeSlot) -> bool; } -impl ReportOffence for () { - fn report_offence(_offence: O) -> Result<(), OffenceError> { +impl ReportOffence for () { + fn report_offence(_reporters: Vec, _offence: O) -> Result<(), OffenceError> { Ok(()) } From 7c1c06d26b2bff07609e45c056826adee534fc3a Mon Sep 17 00:00:00 2001 From: Davide Galassi Date: Mon, 20 Feb 2023 12:05:06 +0100 Subject: [PATCH 10/33] Finally unit type works as a universal null report system --- bin/node-template/runtime/src/lib.rs | 16 +-- frame/babe/src/equivocation.rs | 114 +++++++++++----------- frame/babe/src/lib.rs | 22 +++-- frame/babe/src/mock.rs | 4 +- frame/grandpa/src/equivocation.rs | 128 ++++++++++++------------ frame/grandpa/src/lib.rs | 22 +++-- frame/grandpa/src/mock.rs | 4 +- frame/grandpa/src/tests.rs | 1 - frame/im-online/src/lib.rs | 14 +-- frame/im-online/src/mock.rs | 8 +- frame/im-online/src/tests.rs | 26 +++-- frame/offences/src/lib.rs | 12 +-- frame/offences/src/mock.rs | 5 +- frame/offences/src/tests.rs | 8 +- frame/staking/src/lib.rs | 9 +- primitives/staking/src/equivocation.rs | 98 ------------------- primitives/staking/src/lib.rs | 1 - primitives/staking/src/offence.rs | 130 ++++++++++++++++++++++--- 18 files changed, 315 insertions(+), 307 deletions(-) delete mode 100644 primitives/staking/src/equivocation.rs diff --git a/bin/node-template/runtime/src/lib.rs b/bin/node-template/runtime/src/lib.rs index 728add3ade88c..e359cc445d6a0 100644 --- a/bin/node-template/runtime/src/lib.rs +++ b/bin/node-template/runtime/src/lib.rs @@ -220,21 +220,9 @@ impl pallet_grandpa::Config for Runtime { type MaxAuthorities = ConstU32<32>; type MaxSetIdSessionEntries = ConstU64<0>; - type KeyOwnerProof = (); + type KeyOwnerProof = sp_core::Void; type EquivocationProof = (); - type OffenceReportSystem = (); - - // type KeyOwnerProofSystem = (); - - // type KeyOwnerProof = - // >::Proof; - - // type KeyOwnerIdentification = >::IdentificationTuple; - - // type HandleEquivocation = pallet_grandpa::NullHandler; + type EquivocationReportSystem = (); } impl pallet_timestamp::Config for Runtime { diff --git a/frame/babe/src/equivocation.rs b/frame/babe/src/equivocation.rs index e2e9766e60110..8a6c31b5c21a7 100644 --- a/frame/babe/src/equivocation.rs +++ b/frame/babe/src/equivocation.rs @@ -47,47 +47,87 @@ use sp_runtime::{ }; use sp_session::{GetSessionNumber, GetValidatorCount}; use sp_staking::{ - equivocation::OffenceReportSystem, - offence::{Kind, Offence, ReportOffence}, + offence::{Kind, Offence, OffenceReportSystem, ReportOffence}, SessionIndex, }; use sp_std::prelude::*; use crate::{Call, Config, Error, Pallet, LOG_TARGET}; +/// A BABE equivocation offence report. +/// +/// When a validator released two or more blocks at the same slot. +pub struct EquivocationOffence { + /// A babe slot in which this incident happened. + pub slot: Slot, + /// 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 that produced the equivocation. + pub offender: Offender, +} + +impl Offence for EquivocationOffence { + const ID: Kind = *b"babe:equivocatio"; + type TimeSlot = Slot; + + fn offenders(&self) -> Vec { + 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.slot + } + + fn slash_fraction(&self, offenders_count: u32) -> Perbill { + // the formula is min((3k / n)^2, 1) + let x = Perbill::from_rational(3 * offenders_count, self.validator_set_count); + // _ ^ 2 + x.square() + } +} + /// Generic equivocation handler. This type implements `HandleEquivocation` /// using existing subsystems that are part of frame (type bounds described /// below) and will dispatch to them directly, it's only purpose is to wire all /// subsystems together. #[derive(Default)] -pub struct EquivocationHandler(sp_std::marker::PhantomData<(T, R, P, L)>); +pub struct EquivocationReportSystem(sp_std::marker::PhantomData<(T, R, P, L)>); // We use the authorship pallet to fetch the current block author and use // `offchain::SendTransactionTypes` for unsigned extrinsic creation and // submission. -impl OffenceReportSystem for EquivocationHandler +impl OffenceReportSystem for EquivocationReportSystem where T: Config::Header>> + pallet_authorship::Config + frame_system::offchain::SendTransactionTypes>, - R: ReportOffence>, + R: ReportOffence< + T::AccountId, + P::IdentificationTuple, + EquivocationOffence, + >, P: KeyOwnerProofSystem<(KeyTypeId, AuthorityId), Proof = T::KeyOwnerProof>, P::IdentificationTuple: Clone, - P::Proof: GetSessionNumber + GetValidatorCount, L: Get, { - type Offence = EquivocationOffence; - type OffenceProof = EquivocationProof; - type KeyOwnerProof = T::KeyOwnerProof; + type KeyOwnerProof = P::Proof; type ReportLongevity = L; - type Reporter = T::AccountId; - fn report_evidence( - reporter: Option, + reporter: Option, equivocation_proof: Self::OffenceProof, key_owner_proof: Self::KeyOwnerProof, ) -> DispatchResult { @@ -183,10 +223,11 @@ impl Pallet { // Check report validity // TODO DAVXY: propagate error - T::OffenceReportSystem::check_evidence(equivocation_proof, key_owner_proof) + T::EquivocationReportSystem::check_evidence(equivocation_proof, key_owner_proof) .map_err(|_| InvalidTransaction::Stale)?; - let longevity = ::ReportLongevity::get(); + let longevity = + >::ReportLongevity::get(); let tag = equivocation_proof.using_encoded(|bytes| sp_io::hashing::blake2_256(bytes)); ValidTransaction::with_tag_prefix("BabeEquivocation") @@ -206,53 +247,10 @@ impl Pallet { pub fn pre_dispatch(call: &Call) -> Result<(), TransactionValidityError> { if let Call::report_equivocation_unsigned { equivocation_proof, key_owner_proof } = call { // TODO DAVXY: propagate error - T::OffenceReportSystem::check_evidence(equivocation_proof, key_owner_proof) + T::EquivocationReportSystem::check_evidence(equivocation_proof, key_owner_proof) .map_err(|_| TransactionValidityError::Invalid(InvalidTransaction::Stale)) } else { Err(InvalidTransaction::Call.into()) } } } - -/// A BABE equivocation offence report. -/// -/// When a validator released two or more blocks at the same slot. -pub struct EquivocationOffence { - /// A babe slot in which this incident happened. - pub slot: Slot, - /// 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 that produced the equivocation. - pub offender: O, -} - -impl Offence for EquivocationOffence { - const ID: Kind = *b"babe:equivocatio"; - type TimeSlot = Slot; - type Offender = O; - - fn offenders(&self) -> Vec { - 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.slot - } - - fn slash_fraction(&self, offenders_count: u32) -> Perbill { - // the formula is min((3k / n)^2, 1) - let x = Perbill::from_rational(3 * offenders_count, self.validator_set_count); - // _ ^ 2 - x.square() - } -} diff --git a/frame/babe/src/lib.rs b/frame/babe/src/lib.rs index 67647e9ba4c1f..e11c3b676b8b1 100644 --- a/frame/babe/src/lib.rs +++ b/frame/babe/src/lib.rs @@ -42,7 +42,7 @@ use sp_runtime::{ ConsensusEngineId, Permill, }; use sp_session::{GetSessionNumber, GetValidatorCount}; -use sp_staking::equivocation::OffenceReportSystem; +use sp_staking::offence::OffenceReportSystem; use sp_std::prelude::*; pub use sp_consensus_babe::{AuthorityId, PUBLIC_KEY_LENGTH, RANDOMNESS_LENGTH, VRF_OUTPUT_LENGTH}; @@ -60,7 +60,7 @@ mod mock; #[cfg(all(feature = "std", test))] mod tests; -pub use equivocation::{EquivocationHandler, EquivocationOffence}; +pub use equivocation::{EquivocationOffence, EquivocationReportSystem}; #[allow(deprecated)] pub use randomness::CurrentBlockRandomness; pub use randomness::{ @@ -167,10 +167,10 @@ pub mod pallet { /// NOTE: when enabling equivocation handling (i.e. this type isn't set to /// `()`) you must use this pallet's `ValidateUnsigned` in the runtime /// definition. - type OffenceReportSystem: OffenceReportSystem< + type EquivocationReportSystem: OffenceReportSystem< + Self::AccountId, KeyOwnerProof = Self::KeyOwnerProof, OffenceProof = Self::EquivocationProof, - Reporter = Self::AccountId, >; } @@ -409,7 +409,11 @@ pub mod pallet { key_owner_proof: T::KeyOwnerProof, ) -> DispatchResultWithPostInfo { let reporter = Some(ensure_signed(origin)?); - T::OffenceReportSystem::report_evidence(reporter, equivocation_proof, key_owner_proof)?; + T::EquivocationReportSystem::report_evidence( + reporter, + equivocation_proof, + key_owner_proof, + )?; // Waive the fee since the report is valid and beneficial Ok(Pays::No.into()) } @@ -432,7 +436,11 @@ pub mod pallet { key_owner_proof: T::KeyOwnerProof, ) -> DispatchResultWithPostInfo { ensure_none(origin)?; - T::OffenceReportSystem::report_evidence(None, equivocation_proof, key_owner_proof)?; + T::EquivocationReportSystem::report_evidence( + None, + equivocation_proof, + key_owner_proof, + )?; // Waive the fee since the report is valid and beneficial Ok(Pays::No.into()) } @@ -816,7 +824,7 @@ impl Pallet { equivocation_proof: T::EquivocationProof, key_owner_proof: T::KeyOwnerProof, ) -> bool { - T::OffenceReportSystem::submit_evidence(equivocation_proof, key_owner_proof) + T::EquivocationReportSystem::submit_evidence(equivocation_proof, key_owner_proof) } } diff --git a/frame/babe/src/mock.rs b/frame/babe/src/mock.rs index be6dbdab2c8e8..96ea2a5324b15 100644 --- a/frame/babe/src/mock.rs +++ b/frame/babe/src/mock.rs @@ -230,8 +230,8 @@ impl Config for Test { type EquivocationProof = sp_consensus_babe::EquivocationProof
; - type OffenceReportSystem = - super::EquivocationHandler; + type EquivocationReportSystem = + super::EquivocationReportSystem; } pub fn go_to_block(n: u64, s: u64) { diff --git a/frame/grandpa/src/equivocation.rs b/frame/grandpa/src/equivocation.rs index 748b5b738b6d9..9a3bd452b5981 100644 --- a/frame/grandpa/src/equivocation.rs +++ b/frame/grandpa/src/equivocation.rs @@ -49,25 +49,74 @@ use sp_runtime::{ }; use sp_session::{GetSessionNumber, GetValidatorCount}; use sp_staking::{ - equivocation::OffenceReportSystem, - offence::{Kind, Offence, ReportOffence}, + offence::{Kind, Offence, OffenceReportSystem, ReportOffence}, SessionIndex, }; use sp_std::prelude::*; 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 GrandpaTimeSlot { + // The order of these matters for `derive(Ord)`. + /// Grandpa Set ID. + pub set_id: SetId, + /// Round number. + pub round: RoundNumber, +} + +/// A GRANDPA equivocation offence report. +pub struct EquivocationOffence { + /// Time slot at which this incident happened. + pub time_slot: GrandpaTimeSlot, + /// 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_count: u32, + /// The authority which produced this equivocation. + pub offender: Offender, +} + +impl Offence for EquivocationOffence { + const ID: Kind = *b"grandpa:equivoca"; + type TimeSlot = GrandpaTimeSlot; + + fn offenders(&self) -> Vec { + vec![self.offender.clone()] + } + + fn session_index(&self) -> SessionIndex { + self.session_index + } + + fn validator_set_count(&self) -> u32 { + self.validator_count + } + + fn time_slot(&self) -> Self::TimeSlot { + self.time_slot + } + + fn slash_fraction(&self, offenders_count: u32) -> Perbill { + // the formula is min((3k / n)^2, 1) + let x = Perbill::from_rational(3 * offenders_count, self.validator_count); + // _ ^ 2 + x.square() + } +} + /// Generic equivocation handler. This type implements `HandleEquivocation` /// using existing subsystems that are part of frame (type bounds described /// below) and will dispatch to them directly, it's only purpose is to wire all /// subsystems together. #[derive(Default)] -pub struct EquivocationHandler(sp_std::marker::PhantomData<(T, R, P, L)>); +pub struct EquivocationReportSystem(sp_std::marker::PhantomData<(T, R, P, L)>); // We use the authorship pallet to fetch the current block author and use // `offchain::SendTransactionTypes` for unsigned extrinsic creation and // submission. -impl OffenceReportSystem for EquivocationHandler +impl OffenceReportSystem for EquivocationReportSystem where T: Config< EquivocationProof = EquivocationProof< @@ -76,24 +125,23 @@ where >, > + pallet_authorship::Config + frame_system::offchain::SendTransactionTypes>, - R: ReportOffence>, + R: ReportOffence< + T::AccountId, + P::IdentificationTuple, + EquivocationOffence, + >, P: KeyOwnerProofSystem<(KeyTypeId, AuthorityId), Proof = T::KeyOwnerProof>, P::IdentificationTuple: Clone, - P::Proof: GetSessionNumber + GetValidatorCount, L: Get, { - type Offence = EquivocationOffence; - type OffenceProof = EquivocationProof; type KeyOwnerProof = T::KeyOwnerProof; type ReportLongevity = L; - type Reporter = T::AccountId; - fn report_evidence( - reporter: Option, + reporter: Option, equivocation_proof: Self::OffenceProof, key_owner_proof: Self::KeyOwnerProof, ) -> DispatchResult { @@ -191,16 +239,6 @@ where } } -/// 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 GrandpaTimeSlot { - // The order of these matters for `derive(Ord)`. - /// Grandpa Set ID. - pub set_id: SetId, - /// Round number. - pub round: RoundNumber, -} - /// 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 @@ -223,10 +261,11 @@ impl Pallet { // Check report validity // TODO DAVXY: propagate error - T::OffenceReportSystem::check_evidence(equivocation_proof, key_owner_proof) + T::EquivocationReportSystem::check_evidence(equivocation_proof, key_owner_proof) .map_err(|_| InvalidTransaction::Stale)?; - let longevity = ::ReportLongevity::get(); + let longevity = + >::ReportLongevity::get(); // TODO DAVXY: is ok the hash of the serialized structure as an identifier? // Was: (equivocation_proof.offender(), equivocation_proof.set_id(), // equivocation_proof.round()) Oterwise we're going to introduce tag() @@ -249,51 +288,10 @@ impl Pallet { pub fn pre_dispatch(call: &Call) -> Result<(), TransactionValidityError> { if let Call::report_equivocation_unsigned { equivocation_proof, key_owner_proof } = call { // TODO DAVXY: propagate error - T::OffenceReportSystem::check_evidence(equivocation_proof, key_owner_proof) + T::EquivocationReportSystem::check_evidence(equivocation_proof, key_owner_proof) .map_err(|_| TransactionValidityError::Invalid(InvalidTransaction::Stale)) } else { Err(InvalidTransaction::Call.into()) } } } - -/// A GRANDPA equivocation offence report. -pub struct EquivocationOffence { - /// Time slot at which this incident happened. - pub time_slot: GrandpaTimeSlot, - /// 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_count: u32, - /// The authority which produced this equivocation. - pub offender: O, -} - -impl Offence for EquivocationOffence { - const ID: Kind = *b"grandpa:equivoca"; - type TimeSlot = GrandpaTimeSlot; - type Offender = O; - - fn offenders(&self) -> Vec { - vec![self.offender.clone()] - } - - fn session_index(&self) -> SessionIndex { - self.session_index - } - - fn validator_set_count(&self) -> u32 { - self.validator_count - } - - fn time_slot(&self) -> Self::TimeSlot { - self.time_slot - } - - fn slash_fraction(&self, offenders_count: u32) -> Perbill { - // the formula is min((3k / n)^2, 1) - let x = Perbill::from_rational(3 * offenders_count, self.validator_count); - // _ ^ 2 - x.square() - } -} diff --git a/frame/grandpa/src/lib.rs b/frame/grandpa/src/lib.rs index fcfdcc26a028f..cfc8ced877134 100644 --- a/frame/grandpa/src/lib.rs +++ b/frame/grandpa/src/lib.rs @@ -50,7 +50,7 @@ use frame_support::{ use scale_info::TypeInfo; use sp_runtime::{generic::DigestItem, traits::Zero, DispatchResult}; use sp_session::{GetSessionNumber, GetValidatorCount}; -use sp_staking::{equivocation::OffenceReportSystem, SessionIndex}; +use sp_staking::{offence::OffenceReportSystem, SessionIndex}; mod default_weights; mod equivocation; @@ -63,7 +63,7 @@ mod mock; #[cfg(all(feature = "std", test))] mod tests; -pub use equivocation::{EquivocationHandler, EquivocationOffence, GrandpaTimeSlot}; +pub use equivocation::{EquivocationOffence, EquivocationReportSystem, GrandpaTimeSlot}; pub use pallet::*; @@ -118,10 +118,10 @@ pub mod pallet { /// NOTE: when enabling equivocation handling (i.e. this type isn't set to /// `()`) you must use this pallet's `ValidateUnsigned` in the runtime /// definition. - type OffenceReportSystem: OffenceReportSystem< + type EquivocationReportSystem: OffenceReportSystem< + Self::AccountId, KeyOwnerProof = Self::KeyOwnerProof, OffenceProof = Self::EquivocationProof, - Reporter = Self::AccountId, >; } @@ -204,7 +204,11 @@ pub mod pallet { ) -> DispatchResultWithPostInfo { let reporter = Some(ensure_signed(origin)?); - T::OffenceReportSystem::report_evidence(reporter, equivocation_proof, key_owner_proof)?; + T::EquivocationReportSystem::report_evidence( + reporter, + equivocation_proof, + key_owner_proof, + )?; // Waive the fee since the report is valid and beneficial Ok(Pays::No.into()) } @@ -227,7 +231,11 @@ pub mod pallet { ) -> DispatchResultWithPostInfo { ensure_none(origin)?; - T::OffenceReportSystem::report_evidence(None, equivocation_proof, key_owner_proof)?; + T::EquivocationReportSystem::report_evidence( + None, + equivocation_proof, + key_owner_proof, + )?; // Waive the fee since the report is valid and beneficial Ok(Pays::No.into()) } @@ -533,7 +541,7 @@ impl Pallet { equivocation_proof: T::EquivocationProof, key_owner_proof: T::KeyOwnerProof, ) -> bool { - T::OffenceReportSystem::submit_evidence(equivocation_proof, key_owner_proof) + T::EquivocationReportSystem::submit_evidence(equivocation_proof, key_owner_proof) } fn on_stalled(further_wait: T::BlockNumber, median: T::BlockNumber) { diff --git a/frame/grandpa/src/mock.rs b/frame/grandpa/src/mock.rs index 513a07f613cd7..f92232fd15398 100644 --- a/frame/grandpa/src/mock.rs +++ b/frame/grandpa/src/mock.rs @@ -233,8 +233,8 @@ impl Config for Test { type EquivocationProof = sp_finality_grandpa::EquivocationProof; - type OffenceReportSystem = - super::EquivocationHandler; + type EquivocationReportSystem = + super::EquivocationReportSystem; } pub fn grandpa_log(log: ConsensusLog) -> DigestItem { diff --git a/frame/grandpa/src/tests.rs b/frame/grandpa/src/tests.rs index c04d58f605c9b..f3f521c5335e6 100644 --- a/frame/grandpa/src/tests.rs +++ b/frame/grandpa/src/tests.rs @@ -21,7 +21,6 @@ use super::{Call, Event, *}; use crate::mock::*; -use codec::Encode; use fg_primitives::ScheduledChange; use frame_support::{ assert_err, assert_noop, assert_ok, diff --git a/frame/im-online/src/lib.rs b/frame/im-online/src/lib.rs index f88f92b896e50..8c1f46978e8bc 100644 --- a/frame/im-online/src/lib.rs +++ b/frame/im-online/src/lib.rs @@ -353,6 +353,8 @@ pub mod pallet { /// A type that gives us the ability to submit unresponsiveness offence reports. type ReportUnresponsiveness: ReportOffence< + Self::AccountId, + IdentificationTuple, UnresponsivenessOffence>, >; @@ -907,7 +909,7 @@ impl OneSessionHandler for Pallet { let validator_set_count = keys.len() as u32; let offence = UnresponsivenessOffence { session_index, validator_set_count, offenders }; - if let Err(e) = T::ReportUnresponsiveness::report_offence(offence) { + if let Err(e) = T::ReportUnresponsiveness::report_offence(vec![], offence) { sp_runtime::print(e); } } @@ -933,20 +935,14 @@ pub struct UnresponsivenessOffence { pub offenders: Vec, } -impl Offence for UnresponsivenessOffence { +impl Offence for UnresponsivenessOffence { const ID: Kind = *b"im-online:offlin"; type TimeSlot = SessionIndex; - type Offender = Offender; - type Reporter = (); - fn offenders(&self) -> Vec { + fn offenders(&self) -> Vec { self.offenders.clone() } - fn reporters(&self) -> Vec { - Default::default() - } - fn session_index(&self) -> SessionIndex { self.session_index } diff --git a/frame/im-online/src/mock.rs b/frame/im-online/src/mock.rs index 5e0377a3b6e10..783e68dfede9f 100644 --- a/frame/im-online/src/mock.rs +++ b/frame/im-online/src/mock.rs @@ -88,14 +88,14 @@ type IdentificationTuple = (u64, u64); type Offence = crate::UnresponsivenessOffence; parameter_types! { - pub static Offences: Vec = vec![]; + pub static Offences: Vec<(Vec, Offence)> = vec![]; } /// A mock offence report handler. pub struct OffenceHandler; -impl ReportOffence for OffenceHandler { - fn report_offence(offence: Offence) -> Result<(), OffenceError> { - Offences::mutate(|l| l.push(offence)); +impl ReportOffence for OffenceHandler { + fn report_offence(reporters: Vec, offence: Offence) -> Result<(), OffenceError> { + Offences::mutate(|l| l.push((reporters, offence))); Ok(()) } diff --git a/frame/im-online/src/tests.rs b/frame/im-online/src/tests.rs index dd5151df87a32..2c026f7176b65 100644 --- a/frame/im-online/src/tests.rs +++ b/frame/im-online/src/tests.rs @@ -79,11 +79,14 @@ fn should_report_offline_validators() { let offences = Offences::take(); assert_eq!( offences, - vec![UnresponsivenessOffence { - session_index: 2, - validator_set_count: 3, - offenders: vec![(1, 1), (2, 2), (3, 3),], - }] + vec![( + vec![], + UnresponsivenessOffence { + session_index: 2, + validator_set_count: 3, + offenders: vec![(1, 1), (2, 2), (3, 3),], + } + )] ); // should not report when heartbeat is sent @@ -96,11 +99,14 @@ fn should_report_offline_validators() { let offences = Offences::take(); assert_eq!( offences, - vec![UnresponsivenessOffence { - session_index: 3, - validator_set_count: 6, - offenders: vec![(5, 5), (6, 6),], - }] + vec![( + vec![], + UnresponsivenessOffence { + session_index: 3, + validator_set_count: 6, + offenders: vec![(5, 5), (6, 6),], + } + )] ); }); } diff --git a/frame/offences/src/lib.rs b/frame/offences/src/lib.rs index 352fa02a18e72..f9b7eeda528b1 100644 --- a/frame/offences/src/lib.rs +++ b/frame/offences/src/lib.rs @@ -112,10 +112,10 @@ pub mod pallet { } } -impl ReportOffence for Pallet +impl ReportOffence for Pallet where T: Config, - O: Offence, + O: Offence, { fn report_offence(reporters: Vec, offence: O) -> Result<(), OffenceError> { let offenders = offence.offenders(); @@ -164,7 +164,7 @@ impl Pallet { /// Compute the ID for the given report properties. /// /// The report id depends on the offence kind, time slot and the id of offender. - fn report_id( + fn report_id>( time_slot: &O::TimeSlot, offender: &T::IdentificationTuple, ) -> ReportIdOf { @@ -173,7 +173,7 @@ impl Pallet { /// Triages the offence report and returns the set of offenders that was involved in unique /// reports along with the list of the concurrent offences. - fn triage_offence_report( + fn triage_offence_report>( reporters: Vec, time_slot: &O::TimeSlot, offenders: Vec, @@ -223,13 +223,13 @@ struct TriageOutcome { /// This struct is responsible for aggregating storage writes and the underlying storage should not /// accessed directly meanwhile. #[must_use = "The changes are not saved without called `save`"] -struct ReportIndexStorage { +struct ReportIndexStorage> { opaque_time_slot: OpaqueTimeSlot, concurrent_reports: Vec>, same_kind_reports: Vec<(O::TimeSlot, ReportIdOf)>, } -impl ReportIndexStorage { +impl> ReportIndexStorage { /// Preload indexes from the storage for the specific `time_slot` and the kind of the offence. fn load(time_slot: &O::TimeSlot) -> Self { let opaque_time_slot = time_slot.encode(); diff --git a/frame/offences/src/mock.rs b/frame/offences/src/mock.rs index 1f83bc6b48bd8..7f65a3b7f7836 100644 --- a/frame/offences/src/mock.rs +++ b/frame/offences/src/mock.rs @@ -140,12 +140,11 @@ pub struct Offence { pub time_slot: u128, } -impl offence::Offence for Offence { +impl offence::Offence for Offence { const ID: offence::Kind = KIND; type TimeSlot = u128; - type Offender = u64; - fn offenders(&self) -> Vec { + fn offenders(&self) -> Vec { self.offenders.clone() } diff --git a/frame/offences/src/tests.rs b/frame/offences/src/tests.rs index d224bf001330e..2d07e4c87fb9f 100644 --- a/frame/offences/src/tests.rs +++ b/frame/offences/src/tests.rs @@ -171,7 +171,7 @@ fn reports_if_an_offence_is_dup() { // the report for authority 0 at time slot 42 should not be a known // offence - assert!(!>::is_known_offence( + assert!(!>::is_known_offence( &test_offence.offenders, &test_offence.time_slot )); @@ -180,7 +180,7 @@ fn reports_if_an_offence_is_dup() { Offences::report_offence(vec![], test_offence.clone()).unwrap(); // the same report should be a known offence now - assert!(>::is_known_offence( + assert!(>::is_known_offence( &test_offence.offenders, &test_offence.time_slot )); @@ -195,7 +195,7 @@ fn reports_if_an_offence_is_dup() { test_offence.offenders.push(1); // it should not be a known offence anymore - assert!(!>::is_known_offence( + assert!(!>::is_known_offence( &test_offence.offenders, &test_offence.time_slot )); @@ -206,7 +206,7 @@ fn reports_if_an_offence_is_dup() { // creating a new offence for the same authorities on the next slot // should be considered a new offence and thefore not known let test_offence_next_slot = offence(time_slot + 1, vec![0, 1]); - assert!(!>::is_known_offence( + assert!(!>::is_known_offence( &test_offence_next_slot.offenders, &test_offence_next_slot.time_slot )); diff --git a/frame/staking/src/lib.rs b/frame/staking/src/lib.rs index 83f3d22875ca1..3672056534b75 100644 --- a/frame/staking/src/lib.rs +++ b/frame/staking/src/lib.rs @@ -912,11 +912,12 @@ pub struct FilterHistoricalOffences { _inner: sp_std::marker::PhantomData<(T, R)>, } -impl ReportOffence for FilterHistoricalOffences, R> +impl ReportOffence + for FilterHistoricalOffences, R> where T: Config, - R: ReportOffence, - O: Offence, + R: ReportOffence, + O: Offence, { fn report_offence(reporters: Vec, offence: O) -> Result<(), OffenceError> { // Disallow any slashing from before the current bonding period. @@ -933,7 +934,7 @@ where } } - fn is_known_offence(offenders: &[O::Offender], time_slot: &O::TimeSlot) -> bool { + fn is_known_offence(offenders: &[Offender], time_slot: &O::TimeSlot) -> bool { R::is_known_offence(offenders, time_slot) } } diff --git a/primitives/staking/src/equivocation.rs b/primitives/staking/src/equivocation.rs deleted file mode 100644 index 342178a2616ad..0000000000000 --- a/primitives/staking/src/equivocation.rs +++ /dev/null @@ -1,98 +0,0 @@ -use crate::{offence::Offence, SessionIndex}; -use sp_core::Get; -use sp_runtime::{DispatchResult, Perbill}; -use sp_std::vec::Vec; - -/// TODO:<<<<<<<<<<<< BETTER DOCS -/// -/// A system capable of construct, report and submit offences. -/// -/// Implementors of this trait provides a wrapper around the lower level rough -/// system in order to provide exitrinsic submission of reports an construction -/// of offences given the offence and offender key ownership proofs. -/// -/// It is assumed that this subsystem takes care of checking key ownership proof -/// before report submission. -pub trait OffenceReportSystem { - /// TODO: DOC - type Offence: Offence; - - /// TODO: DOC - type OffenceProof; - - /// TODO: DOC - type KeyOwnerProof; - - /// Longevity, in blocks, for the report validity. When using the staking - /// pallet this should be equal to the bonding duration (in blocks, not eras). - type ReportLongevity: Get; - - /// Offence reporter type - type Reporter; - - /// Report offence to the `ReportOffence` handler. - fn report_evidence( - _reporter: Option, - _offence_proof: Self::OffenceProof, - _key_owner_proof: Self::KeyOwnerProof, - ) -> DispatchResult { - Ok(()) - } - - /// Check if is a known offence. - fn check_evidence( - _offence_proof: &Self::OffenceProof, - _key_owner_proof: &Self::KeyOwnerProof, - ) -> DispatchResult { - Ok(()) - } - - /// Create and dispatch an offence report extrinsic. - fn submit_evidence( - _offence_proof: Self::OffenceProof, - _key_owner_proof: Self::KeyOwnerProof, - ) -> bool { - true - } -} - -// Dummy offence type meant to be used by the dummy offence report system. -impl Offence for () { - const ID: crate::offence::Kind = [0; 16]; - type TimeSlot = (); - type Offender = (); - - fn offenders(&self) -> Vec { - Default::default() - } - - fn validator_set_count(&self) -> u32 { - 0 - } - - fn time_slot(&self) -> Self::TimeSlot { - () - } - - fn session_index(&self) -> SessionIndex { - 0 - } - - fn slash_fraction(&self, _offenders_count: u32) -> Perbill { - Default::default() - } -} - -// Dummy report system. -// Should always give successful results -impl OffenceReportSystem for () { - type Offence = (); - - type Reporter = (); - - type OffenceProof = (); - - type KeyOwnerProof = (); - - type ReportLongevity = (); -} diff --git a/primitives/staking/src/lib.rs b/primitives/staking/src/lib.rs index ed238053e7272..9eb4a4890cdf8 100644 --- a/primitives/staking/src/lib.rs +++ b/primitives/staking/src/lib.rs @@ -23,7 +23,6 @@ use sp_runtime::{DispatchError, DispatchResult}; use sp_std::{collections::btree_map::BTreeMap, vec::Vec}; -pub mod equivocation; pub mod offence; /// Simple index type with which we can count sessions. diff --git a/primitives/staking/src/offence.rs b/primitives/staking/src/offence.rs index af52dd03a0ef4..7f4bad023f327 100644 --- a/primitives/staking/src/offence.rs +++ b/primitives/staking/src/offence.rs @@ -19,7 +19,8 @@ //! that use staking. use codec::{Decode, Encode}; -use sp_runtime::Perbill; +use sp_core::Get; +use sp_runtime::{DispatchResult, Perbill}; use sp_std::vec::Vec; use crate::SessionIndex; @@ -64,13 +65,10 @@ pub enum DisableStrategy { /// This trait assumes that the offence is legitimate and was validated already. /// /// Examples of offences include: a BABE equivocation or a GRANDPA unjustified vote. -pub trait Offence { +pub trait Offence { /// Identifier which is unique for this kind of an offence. const ID: Kind; - /// Offender Identifier. - type Offender; - /// A type that represents a point in time on an abstract timescale. /// /// See `Offence::time_slot` for details. The only requirement is that such timescale could be @@ -78,7 +76,9 @@ pub trait Offence { type TimeSlot: Clone + codec::Codec + Ord; /// The list of all offenders involved in this incident. - fn offenders(&self) -> Vec; + /// + /// The list has no duplicates, so it is rather a set. + fn offenders(&self) -> Vec; /// The session index that is used for querying the validator set for the `slash_fraction` /// function. @@ -138,22 +138,22 @@ impl sp_runtime::traits::Printable for OffenceError { } /// A trait for decoupling offence reporters from the actual handling of offence reports. -pub trait ReportOffence { +pub trait ReportOffence> { /// Report an `offence` and reward given `reporters`. - fn report_offence(reporters: Vec, offence: O) -> Result<(), OffenceError>; + fn report_offence(reporters: Vec, offence: O) -> Result<(), OffenceError>; /// Returns true iff all of the given offenders have been previously reported /// at the given time slot. This function is useful to prevent the sending of /// duplicate offence reports. - fn is_known_offence(offenders: &[O::Offender], time_slot: &O::TimeSlot) -> bool; + fn is_known_offence(offenders: &[Offender], time_slot: &O::TimeSlot) -> bool; } -impl ReportOffence for () { - fn report_offence(_reporters: Vec, _offence: O) -> Result<(), OffenceError> { +impl> ReportOffence for () { + fn report_offence(_reporters: Vec, _offence: O) -> Result<(), OffenceError> { Ok(()) } - fn is_known_offence(_offenders: &[O::Offender], _time_slot: &O::TimeSlot) -> bool { + fn is_known_offence(_offenders: &[Offender], _time_slot: &O::TimeSlot) -> bool { true } } @@ -209,3 +209,109 @@ pub struct OffenceDetails { /// particular reporters. pub reporters: Vec, } + +/// A system capable of construct, report and submit offences. +/// +/// Implementors of this trait provide a wrapper for lower level operations. +/// +/// It is assumed that this subsystem takes care of checking key ownership proof +/// before report submission. +pub trait OffenceReportSystem { + // /// Offence type + // type Offence: Offence; + + /// Offence proof + type OffenceProof; + + /// Offender key ownership proof. + /// This can be used by the offence report system to check for evidence validity. + type KeyOwnerProof; + + /// Longevity, in blocks, for the report validity. When using the staking + /// pallet this should be equal to the bonding duration (in blocks, not eras). + type ReportLongevity: Get; + + /// Report offence to the `ReportOffence` handler. + fn report_evidence( + _reporter: Option, + _offence_proof: Self::OffenceProof, + _key_owner_proof: Self::KeyOwnerProof, + ) -> DispatchResult { + Ok(()) + } + + /// Check if is a known offence. + fn check_evidence( + _offence_proof: &Self::OffenceProof, + _key_owner_proof: &Self::KeyOwnerProof, + ) -> DispatchResult { + Ok(()) + } + + /// Create and dispatch an offence report extrinsic. + fn submit_evidence( + _offence_proof: Self::OffenceProof, + _key_owner_proof: Self::KeyOwnerProof, + ) -> bool { + true + } +} + +// // Dummy offence type meant to be used by the dummy offence report system. +// impl Offence for () { +// const ID: crate::offence::Kind = [0; 16]; +// type TimeSlot = (); +// type Offender = (); + +// fn offenders(&self) -> Vec { +// Default::default() +// } + +// fn validator_set_count(&self) -> u32 { +// Default::default() +// } + +// fn time_slot(&self) -> Self::TimeSlot { +// Default::default() +// } + +// fn session_index(&self) -> SessionIndex { +// Default::default() +// } + +// fn slash_fraction(&self, _offenders_count: u32) -> Perbill { +// Default::default() +// } +// } + +// Dummy report system. +// Should always give successful results +impl OffenceReportSystem for () { + type OffenceProof = (); + + type KeyOwnerProof = sp_core::Void; + + type ReportLongevity = (); + + fn report_evidence( + _reporter: Option, + _offence_proof: Self::OffenceProof, + _key_owner_proof: Self::KeyOwnerProof, + ) -> DispatchResult { + Ok(()) + } + + fn check_evidence( + _offence_proof: &Self::OffenceProof, + _key_owner_proof: &Self::KeyOwnerProof, + ) -> DispatchResult { + Ok(()) + } + + fn submit_evidence( + _offence_proof: Self::OffenceProof, + _key_owner_proof: Self::KeyOwnerProof, + ) -> bool { + true + } +} From 1069c9d46cf182db39e9733f75835bb11dbc59cc Mon Sep 17 00:00:00 2001 From: Davide Galassi Date: Mon, 20 Feb 2023 12:52:22 +0100 Subject: [PATCH 11/33] Align substrate node code --- bin/node/runtime/src/lib.rs | 42 ++++++++----------------------------- frame/babe/src/lib.rs | 3 ++- frame/babe/src/mock.rs | 3 --- frame/grandpa/src/lib.rs | 3 ++- 4 files changed, 13 insertions(+), 38 deletions(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 8f8a7ceef3cfe..5193f415ac97e 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -392,24 +392,13 @@ impl pallet_babe::Config for Runtime { type ExpectedBlockTime = ExpectedBlockTime; type EpochChangeTrigger = pallet_babe::ExternalTrigger; type DisabledValidators = Session; - - type KeyOwnerProofSystem = Historical; - - type KeyOwnerProof = >::Proof; - - type KeyOwnerIdentification = >::IdentificationTuple; - - type HandleEquivocation = - pallet_babe::EquivocationHandler; - type WeightInfo = (); type MaxAuthorities = MaxAuthorities; + type KeyOwnerProof = + >::Proof; + type EquivocationProof = sp_consensus_babe::EquivocationProof
; + type EquivocationReportSystem = + pallet_babe::EquivocationReportSystem; } parameter_types! { @@ -1320,26 +1309,13 @@ parameter_types! { impl pallet_grandpa::Config for Runtime { type RuntimeEvent = RuntimeEvent; - - type KeyOwnerProofSystem = Historical; - - type KeyOwnerProof = - >::Proof; - - type KeyOwnerIdentification = >::IdentificationTuple; - - type HandleEquivocation = pallet_grandpa::EquivocationHandler< - Self::KeyOwnerIdentification, - Offences, - ReportLongevity, - >; - type WeightInfo = (); type MaxAuthorities = MaxAuthorities; type MaxSetIdSessionEntries = MaxSetIdSessionEntries; + type KeyOwnerProof = >::Proof; + type EquivocationProof = fg_primitives::EquivocationProof; + type EquivocationReportSystem = + pallet_grandpa::EquivocationReportSystem; } parameter_types! { diff --git a/frame/babe/src/lib.rs b/frame/babe/src/lib.rs index e11c3b676b8b1..b2ae4e4d1ef66 100644 --- a/frame/babe/src/lib.rs +++ b/frame/babe/src/lib.rs @@ -823,8 +823,9 @@ impl Pallet { pub fn submit_unsigned_equivocation_report( equivocation_proof: T::EquivocationProof, key_owner_proof: T::KeyOwnerProof, - ) -> bool { + ) -> Option<()> { T::EquivocationReportSystem::submit_evidence(equivocation_proof, key_owner_proof) + .then_some(()) } } diff --git a/frame/babe/src/mock.rs b/frame/babe/src/mock.rs index 96ea2a5324b15..7f35283bc80e2 100644 --- a/frame/babe/src/mock.rs +++ b/frame/babe/src/mock.rs @@ -225,11 +225,8 @@ impl Config for Test { type DisabledValidators = Session; type WeightInfo = (); type MaxAuthorities = ConstU32<10>; - type KeyOwnerProof = >::Proof; - type EquivocationProof = sp_consensus_babe::EquivocationProof
; - type EquivocationReportSystem = super::EquivocationReportSystem; } diff --git a/frame/grandpa/src/lib.rs b/frame/grandpa/src/lib.rs index cfc8ced877134..a4c7dcc12ca8e 100644 --- a/frame/grandpa/src/lib.rs +++ b/frame/grandpa/src/lib.rs @@ -540,8 +540,9 @@ impl Pallet { pub fn submit_unsigned_equivocation_report( equivocation_proof: T::EquivocationProof, key_owner_proof: T::KeyOwnerProof, - ) -> bool { + ) -> Option<()> { T::EquivocationReportSystem::submit_evidence(equivocation_proof, key_owner_proof) + .then_some(()) } fn on_stalled(further_wait: T::BlockNumber, median: T::BlockNumber) { From e09666954a1779cda61f68363ba270d49e448e4a Mon Sep 17 00:00:00 2001 From: Davide Galassi Date: Mon, 20 Feb 2023 15:41:40 +0100 Subject: [PATCH 12/33] Further simplification --- bin/node-template/runtime/src/lib.rs | 1 - bin/node/runtime/src/lib.rs | 2 -- frame/babe/src/equivocation.rs | 15 ++++---- frame/babe/src/lib.rs | 15 ++++---- frame/babe/src/mock.rs | 1 - frame/grandpa/src/equivocation.rs | 21 ++++-------- frame/grandpa/src/lib.rs | 15 ++++---- frame/grandpa/src/mock.rs | 2 -- primitives/staking/src/offence.rs | 51 +++++----------------------- 9 files changed, 34 insertions(+), 89 deletions(-) diff --git a/bin/node-template/runtime/src/lib.rs b/bin/node-template/runtime/src/lib.rs index e359cc445d6a0..f8d2a3aaa34f3 100644 --- a/bin/node-template/runtime/src/lib.rs +++ b/bin/node-template/runtime/src/lib.rs @@ -221,7 +221,6 @@ impl pallet_grandpa::Config for Runtime { type MaxSetIdSessionEntries = ConstU64<0>; type KeyOwnerProof = sp_core::Void; - type EquivocationProof = (); type EquivocationReportSystem = (); } diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 5193f415ac97e..76fc6c2b7b428 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -396,7 +396,6 @@ impl pallet_babe::Config for Runtime { type MaxAuthorities = MaxAuthorities; type KeyOwnerProof = >::Proof; - type EquivocationProof = sp_consensus_babe::EquivocationProof
; type EquivocationReportSystem = pallet_babe::EquivocationReportSystem; } @@ -1313,7 +1312,6 @@ impl pallet_grandpa::Config for Runtime { type MaxAuthorities = MaxAuthorities; type MaxSetIdSessionEntries = MaxSetIdSessionEntries; type KeyOwnerProof = >::Proof; - type EquivocationProof = fg_primitives::EquivocationProof; type EquivocationReportSystem = pallet_grandpa::EquivocationReportSystem; } diff --git a/frame/babe/src/equivocation.rs b/frame/babe/src/equivocation.rs index 8a6c31b5c21a7..75092e61198f4 100644 --- a/frame/babe/src/equivocation.rs +++ b/frame/babe/src/equivocation.rs @@ -106,9 +106,10 @@ pub struct EquivocationReportSystem(sp_std::marker::PhantomData<(T, // We use the authorship pallet to fetch the current block author and use // `offchain::SendTransactionTypes` for unsigned extrinsic creation and // submission. -impl OffenceReportSystem for EquivocationReportSystem +impl OffenceReportSystem> + for EquivocationReportSystem where - T: Config::Header>> + T: Config //::Header>> + pallet_authorship::Config + frame_system::offchain::SendTransactionTypes>, R: ReportOffence< @@ -120,15 +121,13 @@ where P::IdentificationTuple: Clone, L: Get, { - type OffenceProof = EquivocationProof; - type KeyOwnerProof = P::Proof; type ReportLongevity = L; fn report_evidence( reporter: Option, - equivocation_proof: Self::OffenceProof, + equivocation_proof: EquivocationProof, key_owner_proof: Self::KeyOwnerProof, ) -> DispatchResult { let reporter = reporter.or_else(|| >::author()); @@ -169,7 +168,7 @@ where } fn check_evidence( - equivocation_proof: &Self::OffenceProof, + equivocation_proof: &EquivocationProof, key_owner_proof: &Self::KeyOwnerProof, ) -> DispatchResult { // Check the membership proof to extract the offender's id @@ -186,7 +185,7 @@ where } fn submit_evidence( - equivocation_proof: Self::OffenceProof, + equivocation_proof: EquivocationProof, key_owner_proof: Self::KeyOwnerProof, ) -> bool { use frame_system::offchain::SubmitTransaction; @@ -227,7 +226,7 @@ impl Pallet { .map_err(|_| InvalidTransaction::Stale)?; let longevity = - >::ReportLongevity::get(); + >::ReportLongevity::get(); let tag = equivocation_proof.using_encoded(|bytes| sp_io::hashing::blake2_256(bytes)); ValidTransaction::with_tag_prefix("BabeEquivocation") diff --git a/frame/babe/src/lib.rs b/frame/babe/src/lib.rs index b2ae4e4d1ef66..87a98482221b1 100644 --- a/frame/babe/src/lib.rs +++ b/frame/babe/src/lib.rs @@ -32,8 +32,8 @@ use frame_support::{ use sp_application_crypto::ByteArray; use sp_consensus_babe::{ digests::{NextConfigDescriptor, NextEpochDescriptor, PreDigest}, - AllowedSlots, BabeAuthorityWeight, BabeEpochConfiguration, ConsensusLog, Epoch, Slot, - BABE_ENGINE_ID, + AllowedSlots, BabeAuthorityWeight, BabeEpochConfiguration, ConsensusLog, Epoch, + EquivocationProof, Slot, BABE_ENGINE_ID, }; use sp_consensus_vrf::schnorrkel; use sp_runtime::{ @@ -158,9 +158,6 @@ pub mod pallet { /// session at which the equivocation occurred. type KeyOwnerProof: Parameter + GetSessionNumber + GetValidatorCount; - /// The equivocation proof - type EquivocationProof: Parameter; - /// The equivocation handling subsystem, defines methods to report an /// offence (after the equivocation has been validated) and for submitting a /// transaction to report an equivocation (from an offchain context). @@ -169,8 +166,8 @@ pub mod pallet { /// definition. type EquivocationReportSystem: OffenceReportSystem< Self::AccountId, + EquivocationProof, KeyOwnerProof = Self::KeyOwnerProof, - OffenceProof = Self::EquivocationProof, >; } @@ -405,7 +402,7 @@ pub mod pallet { ))] pub fn report_equivocation( origin: OriginFor, - equivocation_proof: T::EquivocationProof, + equivocation_proof: EquivocationProof, key_owner_proof: T::KeyOwnerProof, ) -> DispatchResultWithPostInfo { let reporter = Some(ensure_signed(origin)?); @@ -432,7 +429,7 @@ pub mod pallet { ))] pub fn report_equivocation_unsigned( origin: OriginFor, - equivocation_proof: T::EquivocationProof, + equivocation_proof: EquivocationProof, key_owner_proof: T::KeyOwnerProof, ) -> DispatchResultWithPostInfo { ensure_none(origin)?; @@ -821,7 +818,7 @@ impl Pallet { /// will push the transaction to the pool. Only useful in an offchain /// context. pub fn submit_unsigned_equivocation_report( - equivocation_proof: T::EquivocationProof, + equivocation_proof: EquivocationProof, key_owner_proof: T::KeyOwnerProof, ) -> Option<()> { T::EquivocationReportSystem::submit_evidence(equivocation_proof, key_owner_proof) diff --git a/frame/babe/src/mock.rs b/frame/babe/src/mock.rs index 7f35283bc80e2..057e59219155f 100644 --- a/frame/babe/src/mock.rs +++ b/frame/babe/src/mock.rs @@ -226,7 +226,6 @@ impl Config for Test { type WeightInfo = (); type MaxAuthorities = ConstU32<10>; type KeyOwnerProof = >::Proof; - type EquivocationProof = sp_consensus_babe::EquivocationProof
; type EquivocationReportSystem = super::EquivocationReportSystem; } diff --git a/frame/grandpa/src/equivocation.rs b/frame/grandpa/src/equivocation.rs index 9a3bd452b5981..a67a192071c40 100644 --- a/frame/grandpa/src/equivocation.rs +++ b/frame/grandpa/src/equivocation.rs @@ -116,15 +116,10 @@ pub struct EquivocationReportSystem(sp_std::marker::PhantomData<(T, // We use the authorship pallet to fetch the current block author and use // `offchain::SendTransactionTypes` for unsigned extrinsic creation and // submission. -impl OffenceReportSystem for EquivocationReportSystem +impl OffenceReportSystem> + for EquivocationReportSystem where - T: Config< - EquivocationProof = EquivocationProof< - ::Hash, - ::BlockNumber, - >, - > + pallet_authorship::Config - + frame_system::offchain::SendTransactionTypes>, + T: Config + pallet_authorship::Config + frame_system::offchain::SendTransactionTypes>, R: ReportOffence< T::AccountId, P::IdentificationTuple, @@ -134,15 +129,13 @@ where P::IdentificationTuple: Clone, L: Get, { - type OffenceProof = EquivocationProof; - type KeyOwnerProof = T::KeyOwnerProof; type ReportLongevity = L; fn report_evidence( reporter: Option, - equivocation_proof: Self::OffenceProof, + equivocation_proof: EquivocationProof, key_owner_proof: Self::KeyOwnerProof, ) -> DispatchResult { let reporter = reporter.or_else(|| >::author()); @@ -203,7 +196,7 @@ where } fn check_evidence( - equivocation_proof: &Self::OffenceProof, + equivocation_proof: &EquivocationProof, key_owner_proof: &Self::KeyOwnerProof, ) -> DispatchResult { // Check the membership proof to extract the offender's id @@ -224,7 +217,7 @@ where } fn submit_evidence( - equivocation_proof: Self::OffenceProof, + equivocation_proof: EquivocationProof, key_owner_proof: Self::KeyOwnerProof, ) -> bool { use frame_system::offchain::SubmitTransaction; @@ -265,7 +258,7 @@ impl Pallet { .map_err(|_| InvalidTransaction::Stale)?; let longevity = - >::ReportLongevity::get(); + >::ReportLongevity::get(); // TODO DAVXY: is ok the hash of the serialized structure as an identifier? // Was: (equivocation_proof.offender(), equivocation_proof.set_id(), // equivocation_proof.round()) Oterwise we're going to introduce tag() diff --git a/frame/grandpa/src/lib.rs b/frame/grandpa/src/lib.rs index a4c7dcc12ca8e..012b39ea317dc 100644 --- a/frame/grandpa/src/lib.rs +++ b/frame/grandpa/src/lib.rs @@ -36,8 +36,8 @@ use sp_std::prelude::*; use codec::{self as codec, Decode, Encode, MaxEncodedLen}; pub use fg_primitives::{AuthorityId, AuthorityList, AuthorityWeight, VersionedAuthorityList}; use fg_primitives::{ - ConsensusLog, ScheduledChange, SetId, GRANDPA_AUTHORITIES_KEY, GRANDPA_ENGINE_ID, - RUNTIME_LOG_TARGET as LOG_TARGET, + ConsensusLog, EquivocationProof, ScheduledChange, SetId, GRANDPA_AUTHORITIES_KEY, + GRANDPA_ENGINE_ID, RUNTIME_LOG_TARGET as LOG_TARGET, }; use frame_support::{ dispatch::{DispatchResultWithPostInfo, Pays}, @@ -109,9 +109,6 @@ pub mod pallet { /// session at which the equivocation occurred. type KeyOwnerProof: Parameter + GetSessionNumber + GetValidatorCount; - /// The equivocation proof - type EquivocationProof: Parameter; - /// The equivocation handling subsystem, defines methods to report an /// offence (after the equivocation has been validated) and for submitting a /// transaction to report an equivocation (from an offchain context). @@ -120,8 +117,8 @@ pub mod pallet { /// definition. type EquivocationReportSystem: OffenceReportSystem< Self::AccountId, + EquivocationProof, KeyOwnerProof = Self::KeyOwnerProof, - OffenceProof = Self::EquivocationProof, >; } @@ -199,7 +196,7 @@ pub mod pallet { #[pallet::weight(T::WeightInfo::report_equivocation(key_owner_proof.validator_count()))] pub fn report_equivocation( origin: OriginFor, - equivocation_proof: T::EquivocationProof, + equivocation_proof: EquivocationProof, key_owner_proof: T::KeyOwnerProof, ) -> DispatchResultWithPostInfo { let reporter = Some(ensure_signed(origin)?); @@ -226,7 +223,7 @@ pub mod pallet { #[pallet::weight(T::WeightInfo::report_equivocation(key_owner_proof.validator_count()))] pub fn report_equivocation_unsigned( origin: OriginFor, - equivocation_proof: T::EquivocationProof, + equivocation_proof: EquivocationProof, key_owner_proof: T::KeyOwnerProof, ) -> DispatchResultWithPostInfo { ensure_none(origin)?; @@ -538,7 +535,7 @@ impl Pallet { /// will push the transaction to the pool. Only useful in an offchain /// context. pub fn submit_unsigned_equivocation_report( - equivocation_proof: T::EquivocationProof, + equivocation_proof: EquivocationProof, key_owner_proof: T::KeyOwnerProof, ) -> Option<()> { T::EquivocationReportSystem::submit_evidence(equivocation_proof, key_owner_proof) diff --git a/frame/grandpa/src/mock.rs b/frame/grandpa/src/mock.rs index f92232fd15398..e4bd36a6f7c45 100644 --- a/frame/grandpa/src/mock.rs +++ b/frame/grandpa/src/mock.rs @@ -231,8 +231,6 @@ impl Config for Test { type KeyOwnerProof = >::Proof; - type EquivocationProof = sp_finality_grandpa::EquivocationProof; - type EquivocationReportSystem = super::EquivocationReportSystem; } diff --git a/primitives/staking/src/offence.rs b/primitives/staking/src/offence.rs index 7f4bad023f327..6ebc41277c4cc 100644 --- a/primitives/staking/src/offence.rs +++ b/primitives/staking/src/offence.rs @@ -216,13 +216,7 @@ pub struct OffenceDetails { /// /// It is assumed that this subsystem takes care of checking key ownership proof /// before report submission. -pub trait OffenceReportSystem { - // /// Offence type - // type Offence: Offence; - - /// Offence proof - type OffenceProof; - +pub trait OffenceReportSystem { /// Offender key ownership proof. /// This can be used by the offence report system to check for evidence validity. type KeyOwnerProof; @@ -234,7 +228,7 @@ pub trait OffenceReportSystem { /// Report offence to the `ReportOffence` handler. fn report_evidence( _reporter: Option, - _offence_proof: Self::OffenceProof, + _offence_proof: OffenceProof, _key_owner_proof: Self::KeyOwnerProof, ) -> DispatchResult { Ok(()) @@ -242,7 +236,7 @@ pub trait OffenceReportSystem { /// Check if is a known offence. fn check_evidence( - _offence_proof: &Self::OffenceProof, + _offence_proof: &OffenceProof, _key_owner_proof: &Self::KeyOwnerProof, ) -> DispatchResult { Ok(()) @@ -250,66 +244,37 @@ pub trait OffenceReportSystem { /// Create and dispatch an offence report extrinsic. fn submit_evidence( - _offence_proof: Self::OffenceProof, + _offence_proof: OffenceProof, _key_owner_proof: Self::KeyOwnerProof, ) -> bool { true } } -// // Dummy offence type meant to be used by the dummy offence report system. -// impl Offence for () { -// const ID: crate::offence::Kind = [0; 16]; -// type TimeSlot = (); -// type Offender = (); - -// fn offenders(&self) -> Vec { -// Default::default() -// } - -// fn validator_set_count(&self) -> u32 { -// Default::default() -// } - -// fn time_slot(&self) -> Self::TimeSlot { -// Default::default() -// } - -// fn session_index(&self) -> SessionIndex { -// Default::default() -// } - -// fn slash_fraction(&self, _offenders_count: u32) -> Perbill { -// Default::default() -// } -// } - // Dummy report system. // Should always give successful results -impl OffenceReportSystem for () { - type OffenceProof = (); - +impl OffenceReportSystem for () { type KeyOwnerProof = sp_core::Void; type ReportLongevity = (); fn report_evidence( _reporter: Option, - _offence_proof: Self::OffenceProof, + _offence_proof: OffenceProof, _key_owner_proof: Self::KeyOwnerProof, ) -> DispatchResult { Ok(()) } fn check_evidence( - _offence_proof: &Self::OffenceProof, + _offence_proof: &OffenceProof, _key_owner_proof: &Self::KeyOwnerProof, ) -> DispatchResult { Ok(()) } fn submit_evidence( - _offence_proof: Self::OffenceProof, + _offence_proof: OffenceProof, _key_owner_proof: Self::KeyOwnerProof, ) -> bool { true From 09850ee50290ea38ec2a736e13bca00420cb1bb1 Mon Sep 17 00:00:00 2001 From: Davide Galassi Date: Mon, 20 Feb 2023 15:50:27 +0100 Subject: [PATCH 13/33] Fix test utils --- frame/babe/src/equivocation.rs | 2 +- test-utils/runtime/src/lib.rs | 19 ++++--------------- 2 files changed, 5 insertions(+), 16 deletions(-) diff --git a/frame/babe/src/equivocation.rs b/frame/babe/src/equivocation.rs index 75092e61198f4..9fd25285f05bc 100644 --- a/frame/babe/src/equivocation.rs +++ b/frame/babe/src/equivocation.rs @@ -54,7 +54,7 @@ use sp_std::prelude::*; use crate::{Call, Config, Error, Pallet, LOG_TARGET}; -/// A BABE equivocation offence report. +/// BABE equivocation offence report. /// /// When a validator released two or more blocks at the same slot. pub struct EquivocationOffence { diff --git a/test-utils/runtime/src/lib.rs b/test-utils/runtime/src/lib.rs index c1a66eb6acb5c..a05945a6946be 100644 --- a/test-utils/runtime/src/lib.rs +++ b/test-utils/runtime/src/lib.rs @@ -28,7 +28,7 @@ use scale_info::TypeInfo; use sp_std::{marker::PhantomData, prelude::*}; use sp_application_crypto::{ecdsa, ed25519, sr25519, RuntimeAppPublic}; -use sp_core::{offchain::KeyTypeId, OpaqueMetadata, RuntimeDebug}; +use sp_core::{OpaqueMetadata, RuntimeDebug}; use sp_trie::{ trie_types::{TrieDBBuilder, TrieDBMutBuilderV1}, PrefixedMemoryDB, StorageProof, @@ -39,7 +39,7 @@ use cfg_if::cfg_if; use frame_support::{ dispatch::RawOrigin, parameter_types, - traits::{CallerTrait, ConstU32, ConstU64, CrateVersion, KeyOwnerProofSystem}, + traits::{CallerTrait, ConstU32, ConstU64, CrateVersion}, weights::{RuntimeDbWeight, Weight}, }; use frame_system::limits::{BlockLength, BlockWeights}; @@ -659,21 +659,10 @@ impl pallet_babe::Config for Runtime { // pallet_babe::SameAuthoritiesForever. type EpochChangeTrigger = pallet_babe::ExternalTrigger; type DisabledValidators = (); - - type KeyOwnerProofSystem = (); - - type KeyOwnerProof = - >::Proof; - - type KeyOwnerIdentification = >::IdentificationTuple; - - type HandleEquivocation = (); type WeightInfo = (); - type MaxAuthorities = ConstU32<10>; + type KeyOwnerProof = sp_core::Void; + type EquivocationReportSystem = (); } /// Adds one to the given input and returns the final result. From 020e71cf24eab157ab0a14b9c6ccdc810dd3d534 Mon Sep 17 00:00:00 2001 From: Davide Galassi Date: Mon, 20 Feb 2023 16:40:30 +0100 Subject: [PATCH 14/33] Remove not required associated type --- frame/babe/src/equivocation.rs | 18 +++++++--------- frame/babe/src/lib.rs | 2 +- frame/grandpa/src/equivocation.rs | 15 +++++++------- frame/grandpa/src/lib.rs | 2 +- primitives/staking/src/offence.rs | 34 +++++++++++-------------------- 5 files changed, 28 insertions(+), 43 deletions(-) diff --git a/frame/babe/src/equivocation.rs b/frame/babe/src/equivocation.rs index 9fd25285f05bc..aadb88ba8d478 100644 --- a/frame/babe/src/equivocation.rs +++ b/frame/babe/src/equivocation.rs @@ -106,12 +106,10 @@ pub struct EquivocationReportSystem(sp_std::marker::PhantomData<(T, // We use the authorship pallet to fetch the current block author and use // `offchain::SendTransactionTypes` for unsigned extrinsic creation and // submission. -impl OffenceReportSystem> +impl OffenceReportSystem> for EquivocationReportSystem where - T: Config //::Header>> - + pallet_authorship::Config - + frame_system::offchain::SendTransactionTypes>, + T: Config + pallet_authorship::Config + frame_system::offchain::SendTransactionTypes>, R: ReportOffence< T::AccountId, P::IdentificationTuple, @@ -121,14 +119,12 @@ where P::IdentificationTuple: Clone, L: Get, { - type KeyOwnerProof = P::Proof; - - type ReportLongevity = L; + type Longevity = L; fn report_evidence( reporter: Option, equivocation_proof: EquivocationProof, - key_owner_proof: Self::KeyOwnerProof, + key_owner_proof: T::KeyOwnerProof, ) -> DispatchResult { let reporter = reporter.or_else(|| >::author()); @@ -169,7 +165,7 @@ where fn check_evidence( equivocation_proof: &EquivocationProof, - key_owner_proof: &Self::KeyOwnerProof, + key_owner_proof: &T::KeyOwnerProof, ) -> DispatchResult { // Check the membership proof to extract the offender's id let key = (sp_consensus_babe::KEY_TYPE, equivocation_proof.offender.clone()); @@ -186,7 +182,7 @@ where fn submit_evidence( equivocation_proof: EquivocationProof, - key_owner_proof: Self::KeyOwnerProof, + key_owner_proof: T::KeyOwnerProof, ) -> bool { use frame_system::offchain::SubmitTransaction; @@ -226,7 +222,7 @@ impl Pallet { .map_err(|_| InvalidTransaction::Stale)?; let longevity = - >::ReportLongevity::get(); + >::Longevity::get(); let tag = equivocation_proof.using_encoded(|bytes| sp_io::hashing::blake2_256(bytes)); ValidTransaction::with_tag_prefix("BabeEquivocation") diff --git a/frame/babe/src/lib.rs b/frame/babe/src/lib.rs index 87a98482221b1..d9c1bc0514d68 100644 --- a/frame/babe/src/lib.rs +++ b/frame/babe/src/lib.rs @@ -166,8 +166,8 @@ pub mod pallet { /// definition. type EquivocationReportSystem: OffenceReportSystem< Self::AccountId, + Self::KeyOwnerProof, EquivocationProof, - KeyOwnerProof = Self::KeyOwnerProof, >; } diff --git a/frame/grandpa/src/equivocation.rs b/frame/grandpa/src/equivocation.rs index a67a192071c40..243539a9665a1 100644 --- a/frame/grandpa/src/equivocation.rs +++ b/frame/grandpa/src/equivocation.rs @@ -116,7 +116,8 @@ pub struct EquivocationReportSystem(sp_std::marker::PhantomData<(T, // We use the authorship pallet to fetch the current block author and use // `offchain::SendTransactionTypes` for unsigned extrinsic creation and // submission. -impl OffenceReportSystem> +impl + OffenceReportSystem> for EquivocationReportSystem where T: Config + pallet_authorship::Config + frame_system::offchain::SendTransactionTypes>, @@ -129,14 +130,12 @@ where P::IdentificationTuple: Clone, L: Get, { - type KeyOwnerProof = T::KeyOwnerProof; - - type ReportLongevity = L; + type Longevity = L; fn report_evidence( reporter: Option, equivocation_proof: EquivocationProof, - key_owner_proof: Self::KeyOwnerProof, + key_owner_proof: T::KeyOwnerProof, ) -> DispatchResult { let reporter = reporter.or_else(|| >::author()); @@ -197,7 +196,7 @@ where fn check_evidence( equivocation_proof: &EquivocationProof, - key_owner_proof: &Self::KeyOwnerProof, + key_owner_proof: &T::KeyOwnerProof, ) -> DispatchResult { // Check the membership proof to extract the offender's id let key = (sp_finality_grandpa::KEY_TYPE, equivocation_proof.offender().clone()); @@ -218,7 +217,7 @@ where fn submit_evidence( equivocation_proof: EquivocationProof, - key_owner_proof: Self::KeyOwnerProof, + key_owner_proof: T::KeyOwnerProof, ) -> bool { use frame_system::offchain::SubmitTransaction; @@ -258,7 +257,7 @@ impl Pallet { .map_err(|_| InvalidTransaction::Stale)?; let longevity = - >::ReportLongevity::get(); + >::Longevity::get(); // TODO DAVXY: is ok the hash of the serialized structure as an identifier? // Was: (equivocation_proof.offender(), equivocation_proof.set_id(), // equivocation_proof.round()) Oterwise we're going to introduce tag() diff --git a/frame/grandpa/src/lib.rs b/frame/grandpa/src/lib.rs index 012b39ea317dc..e3fdb1c9c5457 100644 --- a/frame/grandpa/src/lib.rs +++ b/frame/grandpa/src/lib.rs @@ -117,8 +117,8 @@ pub mod pallet { /// definition. type EquivocationReportSystem: OffenceReportSystem< Self::AccountId, + Self::KeyOwnerProof, EquivocationProof, - KeyOwnerProof = Self::KeyOwnerProof, >; } diff --git a/primitives/staking/src/offence.rs b/primitives/staking/src/offence.rs index 6ebc41277c4cc..321a4a08c39bd 100644 --- a/primitives/staking/src/offence.rs +++ b/primitives/staking/src/offence.rs @@ -216,20 +216,16 @@ pub struct OffenceDetails { /// /// It is assumed that this subsystem takes care of checking key ownership proof /// before report submission. -pub trait OffenceReportSystem { - /// Offender key ownership proof. - /// This can be used by the offence report system to check for evidence validity. - type KeyOwnerProof; - +pub trait OffenceReportSystem { /// Longevity, in blocks, for the report validity. When using the staking /// pallet this should be equal to the bonding duration (in blocks, not eras). - type ReportLongevity: Get; + type Longevity: Get; /// Report offence to the `ReportOffence` handler. fn report_evidence( _reporter: Option, _offence_proof: OffenceProof, - _key_owner_proof: Self::KeyOwnerProof, + _key_owner_proof: KeyOwnerProof, ) -> DispatchResult { Ok(()) } @@ -237,46 +233,40 @@ pub trait OffenceReportSystem { /// Check if is a known offence. fn check_evidence( _offence_proof: &OffenceProof, - _key_owner_proof: &Self::KeyOwnerProof, + _key_owner_proof: &KeyOwnerProof, ) -> DispatchResult { Ok(()) } /// Create and dispatch an offence report extrinsic. - fn submit_evidence( - _offence_proof: OffenceProof, - _key_owner_proof: Self::KeyOwnerProof, - ) -> bool { + fn submit_evidence(_offence_proof: OffenceProof, _key_owner_proof: KeyOwnerProof) -> bool { true } } // Dummy report system. // Should always give successful results -impl OffenceReportSystem for () { - type KeyOwnerProof = sp_core::Void; - - type ReportLongevity = (); +impl + OffenceReportSystem for () +{ + type Longevity = (); fn report_evidence( _reporter: Option, _offence_proof: OffenceProof, - _key_owner_proof: Self::KeyOwnerProof, + _key_owner_proof: KeyOwnerProof, ) -> DispatchResult { Ok(()) } fn check_evidence( _offence_proof: &OffenceProof, - _key_owner_proof: &Self::KeyOwnerProof, + _key_owner_proof: &KeyOwnerProof, ) -> DispatchResult { Ok(()) } - fn submit_evidence( - _offence_proof: OffenceProof, - _key_owner_proof: Self::KeyOwnerProof, - ) -> bool { + fn submit_evidence(_offence_proof: OffenceProof, _key_owner_proof: KeyOwnerProof) -> bool { true } } From 4155c882bd90b062425d92a6874a26d141f9ca03 Mon Sep 17 00:00:00 2001 From: Davide Galassi Date: Mon, 20 Feb 2023 16:50:48 +0100 Subject: [PATCH 15/33] Fix benches --- frame/offences/benchmarking/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frame/offences/benchmarking/src/lib.rs b/frame/offences/benchmarking/src/lib.rs index b71321906cbdd..6109aee8a7acf 100644 --- a/frame/offences/benchmarking/src/lib.rs +++ b/frame/offences/benchmarking/src/lib.rs @@ -36,9 +36,9 @@ use sp_runtime::{ }; use sp_staking::offence::{Offence, ReportOffence}; -use pallet_babe::BabeEquivocationOffence; +use pallet_babe::EquivocationOffence as BabeEquivocationOffence; use pallet_balances::Config as BalancesConfig; -use pallet_grandpa::{GrandpaEquivocationOffence, GrandpaTimeSlot}; +use pallet_grandpa::{EquivocationOffence as GrandpaEquivocationOffence, GrandpaTimeSlot}; use pallet_im_online::{Config as ImOnlineConfig, Pallet as ImOnline, UnresponsivenessOffence}; use pallet_offences::{Config as OffencesConfig, Pallet as Offences}; use pallet_session::{ From 2a3ed074b2c55b81682812a8a414d52916a25e59 Mon Sep 17 00:00:00 2001 From: Davide Galassi Date: Mon, 20 Feb 2023 17:03:58 +0100 Subject: [PATCH 16/33] Rollback to prev field name --- frame/grandpa/src/equivocation.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/frame/grandpa/src/equivocation.rs b/frame/grandpa/src/equivocation.rs index 243539a9665a1..7a915dce99ff2 100644 --- a/frame/grandpa/src/equivocation.rs +++ b/frame/grandpa/src/equivocation.rs @@ -73,7 +73,7 @@ pub struct EquivocationOffence { /// 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_count: u32, + pub validator_set_count: u32, /// The authority which produced this equivocation. pub offender: Offender, } @@ -91,7 +91,7 @@ impl Offence for EquivocationOffence { } fn validator_set_count(&self) -> u32 { - self.validator_count + self.validator_set_count } fn time_slot(&self) -> Self::TimeSlot { @@ -100,7 +100,7 @@ impl Offence for EquivocationOffence { fn slash_fraction(&self, offenders_count: u32) -> Perbill { // the formula is min((3k / n)^2, 1) - let x = Perbill::from_rational(3 * offenders_count, self.validator_count); + let x = Perbill::from_rational(3 * offenders_count, self.validator_set_count); // _ ^ 2 x.square() } @@ -147,7 +147,7 @@ where let set_id = equivocation_proof.set_id(); let round = equivocation_proof.round(); let session_index = key_owner_proof.session(); - let validator_count = key_owner_proof.validator_count(); + let validator_set_count = key_owner_proof.validator_count(); // Validate equivocation proof (check votes are different and signatures are valid). if !sp_finality_grandpa::check_equivocation_proof(equivocation_proof) { @@ -185,7 +185,7 @@ where time_slot: GrandpaTimeSlot { set_id, round }, session_index, offender, - validator_count, + validator_set_count, }; R::report_offence(reporter.into_iter().collect(), offence) From c8a8eb64ad096233dc1b13f7185e64f693cae5aa Mon Sep 17 00:00:00 2001 From: Davide Galassi Date: Mon, 20 Feb 2023 18:28:30 +0100 Subject: [PATCH 17/33] Box big params --- bin/node/runtime/src/lib.rs | 13 +++++++++---- frame/babe/src/equivocation.rs | 12 +++++++----- frame/babe/src/lib.rs | 12 ++++++------ frame/babe/src/tests.rs | 22 +++++++++++----------- frame/grandpa/src/equivocation.rs | 6 ++++-- frame/grandpa/src/lib.rs | 12 ++++++------ frame/grandpa/src/tests.rs | 22 +++++++++++----------- 7 files changed, 54 insertions(+), 45 deletions(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 3fc8dde736214..dee9263bdec9e 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -111,6 +111,11 @@ mod voter_bags; #[cfg(feature = "std")] include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); +/// Max size for serialized extrinsic params for this testing runtime. +/// This is a quite arbitrary but empirically battle tested value. +#[cfg(test)] +pub const CALL_PARAMS_MAX_SIZE: usize = 208; + /// Wasm binary unwrapped. If built with `SKIP_WASM_BUILD`, the function panics. #[cfg(feature = "std")] pub fn wasm_binary_unwrap() -> &'static [u8] { @@ -2352,10 +2357,10 @@ mod tests { fn call_size() { let size = core::mem::size_of::(); assert!( - size <= 208, - "size of RuntimeCall {} is more than 208 bytes: some calls have too big arguments, use Box to reduce the - size of RuntimeCall. - If the limit is too strong, maybe consider increase the limit to 300.", + size <= CALL_PARAMS_MAX_SIZE, + "size of RuntimeCall {} is more than {CALL_PARAMS_MAX_SIZE} bytes. + Some calls have too big arguments, use Box to reduce the size of RuntimeCall. + If the limit is too strong, maybe consider fine tune the limit.", size, ); } diff --git a/frame/babe/src/equivocation.rs b/frame/babe/src/equivocation.rs index ad0bd7749fb88..786c5e5799021 100644 --- a/frame/babe/src/equivocation.rs +++ b/frame/babe/src/equivocation.rs @@ -100,7 +100,6 @@ impl Offence for EquivocationOffence { /// using existing subsystems that are part of frame (type bounds described /// below) and will dispatch to them directly, it's only purpose is to wire all /// subsystems together. -#[derive(Default)] pub struct EquivocationReportSystem(sp_std::marker::PhantomData<(T, R, P, L)>); // We use the authorship pallet to fetch the current block author and use @@ -142,15 +141,15 @@ where let epoch_index = *slot.saturating_sub(crate::GenesisSlot::::get()) / T::EpochDuration::get(); + // TODO DAVXY: CAN ALL THESE VALIDITY CHECK BE PERFORMED ONLY BY CHECK-EVIDENCE??? + // Check that the slot number is consistent with the session index // in the key ownership proof (i.e. slot is for that epoch) - // TODO: this should be part of `check_evidence`... if Pallet::::session_index_for_epoch(epoch_index) != session_index { return Err(Error::::InvalidKeyOwnershipProof.into()) } // Check the membership proof and extract the offender's id - // TODO: These checks were alread yperformed by check evidence... let offender = P::check_proof((KEY_TYPE, offender), key_owner_proof) .ok_or(Error::::InvalidKeyOwnershipProof)?; @@ -185,7 +184,10 @@ where ) -> bool { use frame_system::offchain::SubmitTransaction; - let call = Call::report_equivocation_unsigned { equivocation_proof, key_owner_proof }; + let call = Call::report_equivocation_unsigned { + equivocation_proof: Box::new(equivocation_proof), + key_owner_proof, + }; let res = SubmitTransaction::>::submit_unsigned_transaction(call.into()); match res { Ok(()) => info!(target: LOG_TARGET, "Submitted equivocation report."), @@ -216,7 +218,7 @@ impl Pallet { } // Check report validity - // TODO DAVXY: propagate error + // TODO DAVXY: propagate check error T::EquivocationReportSystem::check_evidence(equivocation_proof, key_owner_proof) .map_err(|_| InvalidTransaction::Stale)?; diff --git a/frame/babe/src/lib.rs b/frame/babe/src/lib.rs index daa67b71bb623..a82b1b5cc7970 100644 --- a/frame/babe/src/lib.rs +++ b/frame/babe/src/lib.rs @@ -415,13 +415,13 @@ pub mod pallet { ))] pub fn report_equivocation( origin: OriginFor, - equivocation_proof: EquivocationProof, + equivocation_proof: Box>, key_owner_proof: T::KeyOwnerProof, ) -> DispatchResultWithPostInfo { - let reporter = Some(ensure_signed(origin)?); + let reporter = ensure_signed(origin)?; T::EquivocationReportSystem::report_evidence( - reporter, - equivocation_proof, + Some(reporter), + *equivocation_proof, key_owner_proof, )?; // Waive the fee since the report is valid and beneficial @@ -442,13 +442,13 @@ pub mod pallet { ))] pub fn report_equivocation_unsigned( origin: OriginFor, - equivocation_proof: EquivocationProof, + equivocation_proof: Box>, key_owner_proof: T::KeyOwnerProof, ) -> DispatchResultWithPostInfo { ensure_none(origin)?; T::EquivocationReportSystem::report_evidence( None, - equivocation_proof, + *equivocation_proof, key_owner_proof, )?; // Waive the fee since the report is valid and beneficial diff --git a/frame/babe/src/tests.rs b/frame/babe/src/tests.rs index 07a69d8968817..c49cb467d41bf 100644 --- a/frame/babe/src/tests.rs +++ b/frame/babe/src/tests.rs @@ -465,7 +465,7 @@ fn report_equivocation_current_session_works() { // report the equivocation Babe::report_equivocation_unsigned( RuntimeOrigin::none(), - equivocation_proof, + Box::new(equivocation_proof), key_owner_proof, ) .unwrap(); @@ -537,7 +537,7 @@ fn report_equivocation_old_session_works() { // report the equivocation Babe::report_equivocation_unsigned( RuntimeOrigin::none(), - equivocation_proof, + Box::new(equivocation_proof), key_owner_proof, ) .unwrap(); @@ -589,7 +589,7 @@ fn report_equivocation_invalid_key_owner_proof() { assert_err!( Babe::report_equivocation_unsigned( RuntimeOrigin::none(), - equivocation_proof.clone(), + Box::new(equivocation_proof.clone()), key_owner_proof ), Error::::InvalidKeyOwnershipProof, @@ -609,7 +609,7 @@ fn report_equivocation_invalid_key_owner_proof() { assert_err!( Babe::report_equivocation_unsigned( RuntimeOrigin::none(), - equivocation_proof, + Box::new(equivocation_proof), key_owner_proof, ), Error::::InvalidKeyOwnershipProof, @@ -643,7 +643,7 @@ fn report_equivocation_invalid_equivocation_proof() { assert_err!( Babe::report_equivocation_unsigned( RuntimeOrigin::none(), - equivocation_proof, + Box::new(equivocation_proof), key_owner_proof.clone(), ), Error::::InvalidEquivocationProof, @@ -750,7 +750,7 @@ fn report_equivocation_validate_unsigned_prevents_duplicates() { let key_owner_proof = Historical::prove(key).unwrap(); let inner = Call::report_equivocation_unsigned { - equivocation_proof: equivocation_proof.clone(), + equivocation_proof: Box::new(equivocation_proof.clone()), key_owner_proof: key_owner_proof.clone(), }; @@ -786,7 +786,7 @@ fn report_equivocation_validate_unsigned_prevents_duplicates() { // we submit the report Babe::report_equivocation_unsigned( RuntimeOrigin::none(), - equivocation_proof, + Box::new(equivocation_proof), key_owner_proof, ) .unwrap(); @@ -870,7 +870,7 @@ fn report_equivocation_after_skipped_epochs_works() { // between epoch index and session index must be checked. assert!(Babe::report_equivocation_unsigned( RuntimeOrigin::none(), - equivocation_proof, + Box::new(equivocation_proof), key_owner_proof ) .is_ok()); @@ -897,7 +897,7 @@ fn valid_equivocation_reports_dont_pay_fees() { // check the dispatch info for the call. let info = Call::::report_equivocation_unsigned { - equivocation_proof: equivocation_proof.clone(), + equivocation_proof: Box::new(equivocation_proof.clone()), key_owner_proof: key_owner_proof.clone(), } .get_dispatch_info(); @@ -910,7 +910,7 @@ fn valid_equivocation_reports_dont_pay_fees() { // report the equivocation. let post_info = Babe::report_equivocation_unsigned( RuntimeOrigin::none(), - equivocation_proof.clone(), + Box::new(equivocation_proof.clone()), key_owner_proof.clone(), ) .unwrap(); @@ -924,7 +924,7 @@ fn valid_equivocation_reports_dont_pay_fees() { // duplicate. let post_info = Babe::report_equivocation_unsigned( RuntimeOrigin::none(), - equivocation_proof, + Box::new(equivocation_proof), key_owner_proof, ) .err() diff --git a/frame/grandpa/src/equivocation.rs b/frame/grandpa/src/equivocation.rs index 7a915dce99ff2..ea5430a4d6c3d 100644 --- a/frame/grandpa/src/equivocation.rs +++ b/frame/grandpa/src/equivocation.rs @@ -110,7 +110,6 @@ impl Offence for EquivocationOffence { /// using existing subsystems that are part of frame (type bounds described /// below) and will dispatch to them directly, it's only purpose is to wire all /// subsystems together. -#[derive(Default)] pub struct EquivocationReportSystem(sp_std::marker::PhantomData<(T, R, P, L)>); // We use the authorship pallet to fetch the current block author and use @@ -221,7 +220,10 @@ where ) -> bool { use frame_system::offchain::SubmitTransaction; - let call = Call::report_equivocation_unsigned { equivocation_proof, key_owner_proof }; + let call = Call::report_equivocation_unsigned { + equivocation_proof: Box::new(equivocation_proof), + key_owner_proof, + }; let res = SubmitTransaction::>::submit_unsigned_transaction(call.into()); match res { Ok(()) => info!(target: LOG_TARGET, "Submitted equivocation report."), diff --git a/frame/grandpa/src/lib.rs b/frame/grandpa/src/lib.rs index e3fdb1c9c5457..d9072cd846edd 100644 --- a/frame/grandpa/src/lib.rs +++ b/frame/grandpa/src/lib.rs @@ -196,14 +196,14 @@ pub mod pallet { #[pallet::weight(T::WeightInfo::report_equivocation(key_owner_proof.validator_count()))] pub fn report_equivocation( origin: OriginFor, - equivocation_proof: EquivocationProof, + equivocation_proof: Box>, key_owner_proof: T::KeyOwnerProof, ) -> DispatchResultWithPostInfo { - let reporter = Some(ensure_signed(origin)?); + let reporter = ensure_signed(origin)?; T::EquivocationReportSystem::report_evidence( - reporter, - equivocation_proof, + Some(reporter), + *equivocation_proof, key_owner_proof, )?; // Waive the fee since the report is valid and beneficial @@ -223,14 +223,14 @@ pub mod pallet { #[pallet::weight(T::WeightInfo::report_equivocation(key_owner_proof.validator_count()))] pub fn report_equivocation_unsigned( origin: OriginFor, - equivocation_proof: EquivocationProof, + equivocation_proof: Box>, key_owner_proof: T::KeyOwnerProof, ) -> DispatchResultWithPostInfo { ensure_none(origin)?; T::EquivocationReportSystem::report_evidence( None, - equivocation_proof, + *equivocation_proof, key_owner_proof, )?; // Waive the fee since the report is valid and beneficial diff --git a/frame/grandpa/src/tests.rs b/frame/grandpa/src/tests.rs index f3f521c5335e6..6b8c47e98620e 100644 --- a/frame/grandpa/src/tests.rs +++ b/frame/grandpa/src/tests.rs @@ -359,7 +359,7 @@ fn report_equivocation_current_set_works() { // report the equivocation and the tx should be dispatched successfully assert_ok!(Grandpa::report_equivocation_unsigned( RuntimeOrigin::none(), - equivocation_proof, + Box::new(equivocation_proof), key_owner_proof, ),); @@ -437,7 +437,7 @@ fn report_equivocation_old_set_works() { // the old set, the tx should be dispatched successfully assert_ok!(Grandpa::report_equivocation_unsigned( RuntimeOrigin::none(), - equivocation_proof, + Box::new(equivocation_proof), key_owner_proof, ),); @@ -500,7 +500,7 @@ fn report_equivocation_invalid_set_id() { assert_err!( Grandpa::report_equivocation_unsigned( RuntimeOrigin::none(), - equivocation_proof, + Box::new(equivocation_proof), key_owner_proof, ), Error::::InvalidEquivocationProof, @@ -541,7 +541,7 @@ fn report_equivocation_invalid_session() { assert_err!( Grandpa::report_equivocation_unsigned( RuntimeOrigin::none(), - equivocation_proof, + Box::new(equivocation_proof), key_owner_proof, ), Error::::InvalidEquivocationProof, @@ -586,7 +586,7 @@ fn report_equivocation_invalid_key_owner_proof() { assert_err!( Grandpa::report_equivocation_unsigned( RuntimeOrigin::none(), - equivocation_proof, + Box::new(equivocation_proof), invalid_key_owner_proof, ), Error::::InvalidKeyOwnershipProof, @@ -617,7 +617,7 @@ fn report_equivocation_invalid_equivocation_proof() { assert_err!( Grandpa::report_equivocation_unsigned( RuntimeOrigin::none(), - equivocation_proof, + Box::new(equivocation_proof), key_owner_proof.clone(), ), Error::::InvalidEquivocationProof, @@ -687,7 +687,7 @@ fn report_equivocation_validate_unsigned_prevents_duplicates() { Historical::prove((sp_finality_grandpa::KEY_TYPE, &equivocation_key)).unwrap(); let call = Call::report_equivocation_unsigned { - equivocation_proof: equivocation_proof.clone(), + equivocation_proof: Box::new(equivocation_proof.clone()), key_owner_proof: key_owner_proof.clone(), }; @@ -726,7 +726,7 @@ fn report_equivocation_validate_unsigned_prevents_duplicates() { // we submit the report Grandpa::report_equivocation_unsigned( RuntimeOrigin::none(), - equivocation_proof, + Box::new(equivocation_proof), key_owner_proof, ) .unwrap(); @@ -879,7 +879,7 @@ fn valid_equivocation_reports_dont_pay_fees() { // check the dispatch info for the call. let info = Call::::report_equivocation_unsigned { - equivocation_proof: equivocation_proof.clone(), + equivocation_proof: Box::new(equivocation_proof.clone()), key_owner_proof: key_owner_proof.clone(), } .get_dispatch_info(); @@ -891,7 +891,7 @@ fn valid_equivocation_reports_dont_pay_fees() { // report the equivocation. let post_info = Grandpa::report_equivocation_unsigned( RuntimeOrigin::none(), - equivocation_proof.clone(), + Box::new(equivocation_proof.clone()), key_owner_proof.clone(), ) .unwrap(); @@ -905,7 +905,7 @@ fn valid_equivocation_reports_dont_pay_fees() { // duplicate. let post_info = Grandpa::report_equivocation_unsigned( RuntimeOrigin::none(), - equivocation_proof, + Box::new(equivocation_proof), key_owner_proof, ) .err() From 9e959b7b9803d96bd69339383222a2e651c6efd4 Mon Sep 17 00:00:00 2001 From: Davide Galassi Date: Mon, 20 Feb 2023 18:30:57 +0100 Subject: [PATCH 18/33] Fix typo --- bin/node/runtime/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index dee9263bdec9e..d3b37965aff56 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -2359,8 +2359,8 @@ mod tests { assert!( size <= CALL_PARAMS_MAX_SIZE, "size of RuntimeCall {} is more than {CALL_PARAMS_MAX_SIZE} bytes. - Some calls have too big arguments, use Box to reduce the size of RuntimeCall. - If the limit is too strong, maybe consider fine tune the limit.", + Some calls have too big arguments, use Box to reduce the size of RuntimeCall. + If the limit is too strong, maybe consider increase the limit.", size, ); } From 6e7e11dc5f51b4b786f1d30b315fc6593a92f5ce Mon Sep 17 00:00:00 2001 From: Davide Galassi Date: Mon, 20 Feb 2023 18:46:05 +0100 Subject: [PATCH 19/33] Remove new tag computation --- frame/babe/src/equivocation.rs | 4 +--- frame/babe/src/tests.rs | 5 ++--- frame/grandpa/src/equivocation.rs | 10 +++++----- frame/grandpa/src/tests.rs | 7 ++----- 4 files changed, 10 insertions(+), 16 deletions(-) diff --git a/frame/babe/src/equivocation.rs b/frame/babe/src/equivocation.rs index 786c5e5799021..5b2933c74e37e 100644 --- a/frame/babe/src/equivocation.rs +++ b/frame/babe/src/equivocation.rs @@ -33,7 +33,6 @@ //! that the `ValidateUnsigned` for the BABE pallet is used in the runtime //! definition. -use codec::Encode; use frame_support::traits::{Get, KeyOwnerProofSystem}; use log::{error, info}; @@ -224,13 +223,12 @@ impl Pallet { let longevity = >::Longevity::get(); - let tag = equivocation_proof.using_encoded(|bytes| sp_io::hashing::blake2_256(bytes)); ValidTransaction::with_tag_prefix("BabeEquivocation") // 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(tag) + .and_provides((equivocation_proof.offender.clone(), *equivocation_proof.slot)) .longevity(longevity) // We don't propagate this. This can never be included on a remote node. .propagate(false) diff --git a/frame/babe/src/tests.rs b/frame/babe/src/tests.rs index c49cb467d41bf..4096492953169 100644 --- a/frame/babe/src/tests.rs +++ b/frame/babe/src/tests.rs @@ -764,8 +764,7 @@ fn report_equivocation_validate_unsigned_prevents_duplicates() { ); // the transaction is valid when passed as local - use codec::Encode; - let tag = equivocation_proof.using_encoded(|bytes| sp_io::hashing::blake2_256(bytes)); + let tx_tag = (offending_authority_pair.public(), CurrentSlot::::get()); assert_eq!( ::validate_unsigned( TransactionSource::Local, @@ -774,7 +773,7 @@ fn report_equivocation_validate_unsigned_prevents_duplicates() { TransactionValidity::Ok(ValidTransaction { priority: TransactionPriority::max_value(), requires: vec![], - provides: vec![("BabeEquivocation", tag).encode()], + provides: vec![("BabeEquivocation", tx_tag).encode()], longevity: ReportLongevity::get(), propagate: false, }) diff --git a/frame/grandpa/src/equivocation.rs b/frame/grandpa/src/equivocation.rs index ea5430a4d6c3d..86d4a0bacef19 100644 --- a/frame/grandpa/src/equivocation.rs +++ b/frame/grandpa/src/equivocation.rs @@ -260,16 +260,16 @@ impl Pallet { let longevity = >::Longevity::get(); - // TODO DAVXY: is ok the hash of the serialized structure as an identifier? - // Was: (equivocation_proof.offender(), equivocation_proof.set_id(), - // equivocation_proof.round()) Oterwise we're going to introduce tag() - let tag = equivocation_proof.using_encoded(|bytes| sp_io::hashing::blake2_256(bytes)); 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(tag) + .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) diff --git a/frame/grandpa/src/tests.rs b/frame/grandpa/src/tests.rs index 6b8c47e98620e..22c4e31405444 100644 --- a/frame/grandpa/src/tests.rs +++ b/frame/grandpa/src/tests.rs @@ -701,10 +701,7 @@ fn report_equivocation_validate_unsigned_prevents_duplicates() { ); // the transaction is valid when passed as local - //let tx_tag = (equivocation_key, set_id, 1u64); - // TODO DAVXY: confirm that this is equally good... - use codec::Encode; - let tag = equivocation_proof.using_encoded(|bytes| sp_io::hashing::blake2_256(bytes)); + let tx_tag = (equivocation_key, set_id, 1u64); assert_eq!( ::validate_unsigned( @@ -714,7 +711,7 @@ fn report_equivocation_validate_unsigned_prevents_duplicates() { TransactionValidity::Ok(ValidTransaction { priority: TransactionPriority::max_value(), requires: vec![], - provides: vec![("GrandpaEquivocation", tag).encode()], + provides: vec![("GrandpaEquivocation", tx_tag).encode()], longevity: ReportLongevity::get(), propagate: false, }) From 080e82cedcef4e244bec3912056424d3fbd4c14f Mon Sep 17 00:00:00 2001 From: Davide Galassi Date: Tue, 21 Feb 2023 12:03:04 +0100 Subject: [PATCH 20/33] Remove default implementations --- primitives/staking/src/offence.rs | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/primitives/staking/src/offence.rs b/primitives/staking/src/offence.rs index 321a4a08c39bd..c76c06f3719d0 100644 --- a/primitives/staking/src/offence.rs +++ b/primitives/staking/src/offence.rs @@ -226,22 +226,16 @@ pub trait OffenceReportSystem { _reporter: Option, _offence_proof: OffenceProof, _key_owner_proof: KeyOwnerProof, - ) -> DispatchResult { - Ok(()) - } + ) -> DispatchResult; /// Check if is a known offence. fn check_evidence( _offence_proof: &OffenceProof, _key_owner_proof: &KeyOwnerProof, - ) -> DispatchResult { - Ok(()) - } + ) -> DispatchResult; /// Create and dispatch an offence report extrinsic. - fn submit_evidence(_offence_proof: OffenceProof, _key_owner_proof: KeyOwnerProof) -> bool { - true - } + fn submit_evidence(_offence_proof: OffenceProof, _key_owner_proof: KeyOwnerProof) -> bool; } // Dummy report system. From 3966ef77bc12c468f47730f54b6badf80570243b Mon Sep 17 00:00:00 2001 From: Davide Galassi Date: Tue, 21 Feb 2023 12:17:36 +0100 Subject: [PATCH 21/33] Better docs --- primitives/staking/src/offence.rs | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/primitives/staking/src/offence.rs b/primitives/staking/src/offence.rs index c76c06f3719d0..b92ed83fda348 100644 --- a/primitives/staking/src/offence.rs +++ b/primitives/staking/src/offence.rs @@ -212,34 +212,38 @@ pub struct OffenceDetails { /// A system capable of construct, report and submit offences. /// -/// Implementors of this trait provide a wrapper for lower level operations. +/// Implementors of this trait provide a wrapper to some low level operations +/// which mechanics are left open by this trait. That is, implementation details +/// of the provider are left opaque at this level. /// -/// It is assumed that this subsystem takes care of checking key ownership proof -/// before report submission. +/// It is assumed that this subsystem takes care of checking offender key +/// ownership proof before report submission. +/// Key ownership checks are done using the `key_owner_proof` argument which +/// always comes together with the `offence_proof`. pub trait OffenceReportSystem { /// Longevity, in blocks, for the report validity. When using the staking /// pallet this should be equal to the bonding duration (in blocks, not eras). type Longevity: Get; - /// Report offence to the `ReportOffence` handler. + /// Report a valid offence. + /// TODO DAVXY: reported information should have already been checked via `check_evidence`. fn report_evidence( - _reporter: Option, - _offence_proof: OffenceProof, - _key_owner_proof: KeyOwnerProof, + reporter: Option, + offence_proof: OffenceProof, + key_owner_proof: KeyOwnerProof, ) -> DispatchResult; - /// Check if is a known offence. + /// Check for evidence validity. fn check_evidence( - _offence_proof: &OffenceProof, - _key_owner_proof: &KeyOwnerProof, + offence_proof: &OffenceProof, + key_owner_proof: &KeyOwnerProof, ) -> DispatchResult; /// Create and dispatch an offence report extrinsic. - fn submit_evidence(_offence_proof: OffenceProof, _key_owner_proof: KeyOwnerProof) -> bool; + fn submit_evidence(offence_proof: OffenceProof, key_owner_proof: KeyOwnerProof) -> bool; } // Dummy report system. -// Should always give successful results impl OffenceReportSystem for () { From 5587720b7d35a67eb018d6f62bd500044e213b40 Mon Sep 17 00:00:00 2001 From: Davide Galassi Date: Thu, 23 Feb 2023 15:23:03 +0100 Subject: [PATCH 22/33] Return 'Result' instead of bool --- frame/babe/src/equivocation.rs | 4 ++-- frame/babe/src/lib.rs | 3 +-- frame/grandpa/src/equivocation.rs | 4 ++-- frame/grandpa/src/lib.rs | 3 +-- primitives/staking/src/offence.rs | 33 ++++++++++++++++++------------- 5 files changed, 25 insertions(+), 22 deletions(-) diff --git a/frame/babe/src/equivocation.rs b/frame/babe/src/equivocation.rs index 5b2933c74e37e..61a3d653b941e 100644 --- a/frame/babe/src/equivocation.rs +++ b/frame/babe/src/equivocation.rs @@ -180,7 +180,7 @@ where fn submit_evidence( equivocation_proof: EquivocationProof, key_owner_proof: T::KeyOwnerProof, - ) -> bool { + ) -> Result<(), ()> { use frame_system::offchain::SubmitTransaction; let call = Call::report_equivocation_unsigned { @@ -192,7 +192,7 @@ where Ok(()) => info!(target: LOG_TARGET, "Submitted equivocation report."), Err(e) => error!(target: LOG_TARGET, "Error submitting equivocation report: {:?}", e), } - res.is_ok() + res } } diff --git a/frame/babe/src/lib.rs b/frame/babe/src/lib.rs index a82b1b5cc7970..ab8ebf552fd1a 100644 --- a/frame/babe/src/lib.rs +++ b/frame/babe/src/lib.rs @@ -894,8 +894,7 @@ impl Pallet { equivocation_proof: EquivocationProof, key_owner_proof: T::KeyOwnerProof, ) -> Option<()> { - T::EquivocationReportSystem::submit_evidence(equivocation_proof, key_owner_proof) - .then_some(()) + T::EquivocationReportSystem::submit_evidence(equivocation_proof, key_owner_proof).ok() } } diff --git a/frame/grandpa/src/equivocation.rs b/frame/grandpa/src/equivocation.rs index 86d4a0bacef19..5e996e63f92ca 100644 --- a/frame/grandpa/src/equivocation.rs +++ b/frame/grandpa/src/equivocation.rs @@ -217,7 +217,7 @@ where fn submit_evidence( equivocation_proof: EquivocationProof, key_owner_proof: T::KeyOwnerProof, - ) -> bool { + ) -> Result<(), ()> { use frame_system::offchain::SubmitTransaction; let call = Call::report_equivocation_unsigned { @@ -229,7 +229,7 @@ where Ok(()) => info!(target: LOG_TARGET, "Submitted equivocation report."), Err(e) => error!(target: LOG_TARGET, "Error submitting equivocation report: {:?}", e), } - res.is_ok() + res } } diff --git a/frame/grandpa/src/lib.rs b/frame/grandpa/src/lib.rs index d9072cd846edd..1379e2e283317 100644 --- a/frame/grandpa/src/lib.rs +++ b/frame/grandpa/src/lib.rs @@ -538,8 +538,7 @@ impl Pallet { equivocation_proof: EquivocationProof, key_owner_proof: T::KeyOwnerProof, ) -> Option<()> { - T::EquivocationReportSystem::submit_evidence(equivocation_proof, key_owner_proof) - .then_some(()) + T::EquivocationReportSystem::submit_evidence(equivocation_proof, key_owner_proof).ok() } fn on_stalled(further_wait: T::BlockNumber, median: T::BlockNumber) { diff --git a/primitives/staking/src/offence.rs b/primitives/staking/src/offence.rs index b92ed83fda348..462c0f196fff9 100644 --- a/primitives/staking/src/offence.rs +++ b/primitives/staking/src/offence.rs @@ -212,35 +212,37 @@ pub struct OffenceDetails { /// A system capable of construct, report and submit offences. /// -/// Implementors of this trait provide a wrapper to some low level operations +/// Implementors of this trait provide a wrapper for some low level operations /// which mechanics are left open by this trait. That is, implementation details -/// of the provider are left opaque at this level. +/// are left opaque at this level. /// /// It is assumed that this subsystem takes care of checking offender key -/// ownership proof before report submission. -/// Key ownership checks are done using the `key_owner_proof` argument which -/// always comes together with the `offence_proof`. +/// ownership proof during evidence report. Key ownership checks are done using +/// the `key_owner_proof` argument which comes together with the an `offence_proof`. pub trait OffenceReportSystem { - /// Longevity, in blocks, for the report validity. When using the staking - /// pallet this should be equal to the bonding duration (in blocks, not eras). + /// Longevity, in blocks, for the report validity. + /// For example, when using the staking pallet this should be set equal + /// to the bonding durationin blocks, not eras). type Longevity: Get; - /// Report a valid offence. - /// TODO DAVXY: reported information should have already been checked via `check_evidence`. + /// Report offence evidence. fn report_evidence( reporter: Option, offence_proof: OffenceProof, key_owner_proof: KeyOwnerProof, ) -> DispatchResult; - /// Check for evidence validity. + /// Check offence evidence. fn check_evidence( offence_proof: &OffenceProof, key_owner_proof: &KeyOwnerProof, ) -> DispatchResult; - /// Create and dispatch an offence report extrinsic. - fn submit_evidence(offence_proof: OffenceProof, key_owner_proof: KeyOwnerProof) -> bool; + /// Create and submit an offence report extrinsic. + fn submit_evidence( + offence_proof: OffenceProof, + key_owner_proof: KeyOwnerProof, + ) -> Result<(), ()>; } // Dummy report system. @@ -264,7 +266,10 @@ impl Ok(()) } - fn submit_evidence(_offence_proof: OffenceProof, _key_owner_proof: KeyOwnerProof) -> bool { - true + fn submit_evidence( + _offence_proof: OffenceProof, + _key_owner_proof: KeyOwnerProof, + ) -> Result<(), ()> { + Ok(()) } } From 52ee3674b856bfc02b42c7b2d47059235a7fd2b7 Mon Sep 17 00:00:00 2001 From: Davide Galassi Date: Fri, 24 Feb 2023 15:39:08 +0100 Subject: [PATCH 23/33] Change offence report system return types --- frame/babe/src/equivocation.rs | 23 ++++++++--------------- frame/babe/src/lib.rs | 2 +- frame/grandpa/src/equivocation.rs | 20 ++++++++------------ frame/grandpa/src/lib.rs | 2 +- primitives/staking/src/offence.rs | 26 ++++++++++++++------------ 5 files changed, 32 insertions(+), 41 deletions(-) diff --git a/frame/babe/src/equivocation.rs b/frame/babe/src/equivocation.rs index 61a3d653b941e..58fc146a079c8 100644 --- a/frame/babe/src/equivocation.rs +++ b/frame/babe/src/equivocation.rs @@ -42,7 +42,7 @@ use sp_runtime::{ InvalidTransaction, TransactionPriority, TransactionSource, TransactionValidity, TransactionValidityError, ValidTransaction, }, - DispatchResult, KeyTypeId, Perbill, + DispatchError, KeyTypeId, Perbill, }; use sp_session::{GetSessionNumber, GetValidatorCount}; use sp_staking::{ @@ -104,7 +104,7 @@ pub struct EquivocationReportSystem(sp_std::marker::PhantomData<(T, // We use the authorship pallet to fetch the current block author and use // `offchain::SendTransactionTypes` for unsigned extrinsic creation and // submission. -impl OffenceReportSystem> +impl OffenceReportSystem, P::Proof> for EquivocationReportSystem where T: Config + pallet_authorship::Config + frame_system::offchain::SendTransactionTypes>, @@ -123,9 +123,8 @@ where reporter: Option, equivocation_proof: EquivocationProof, key_owner_proof: T::KeyOwnerProof, - ) -> DispatchResult { + ) -> Result<(), DispatchError> { let reporter = reporter.or_else(|| >::author()); - let offender = equivocation_proof.offender.clone(); let slot = equivocation_proof.slot; @@ -140,8 +139,6 @@ where let epoch_index = *slot.saturating_sub(crate::GenesisSlot::::get()) / T::EpochDuration::get(); - // TODO DAVXY: CAN ALL THESE VALIDITY CHECK BE PERFORMED ONLY BY CHECK-EVIDENCE??? - // Check that the slot number is consistent with the session index // in the key ownership proof (i.e. slot is for that epoch) if Pallet::::session_index_for_epoch(epoch_index) != session_index { @@ -163,15 +160,15 @@ where fn check_evidence( equivocation_proof: &EquivocationProof, key_owner_proof: &T::KeyOwnerProof, - ) -> DispatchResult { + ) -> Result<(), TransactionValidityError> { // Check the membership proof to extract the offender's id let key = (sp_consensus_babe::KEY_TYPE, equivocation_proof.offender.clone()); - let offender = P::check_proof(key, key_owner_proof.clone()) - .ok_or(Error::::InvalidKeyOwnershipProof)?; + let offender = + P::check_proof(key, key_owner_proof.clone()).ok_or(InvalidTransaction::BadProof)?; // Check if the offence has already been reported, and if so then we can discard the report. if R::is_known_offence(&[offender], &equivocation_proof.slot) { - Err(Error::::DuplicateOffenceReport.into()) + Err(InvalidTransaction::Stale.into()) } else { Ok(()) } @@ -217,9 +214,7 @@ impl Pallet { } // Check report validity - // TODO DAVXY: propagate check error - T::EquivocationReportSystem::check_evidence(equivocation_proof, key_owner_proof) - .map_err(|_| InvalidTransaction::Stale)?; + T::EquivocationReportSystem::check_evidence(equivocation_proof, key_owner_proof)?; let longevity = >::Longevity::get(); @@ -240,9 +235,7 @@ impl Pallet { pub fn pre_dispatch(call: &Call) -> Result<(), TransactionValidityError> { if let Call::report_equivocation_unsigned { equivocation_proof, key_owner_proof } = call { - // TODO DAVXY: propagate error T::EquivocationReportSystem::check_evidence(equivocation_proof, key_owner_proof) - .map_err(|_| TransactionValidityError::Invalid(InvalidTransaction::Stale)) } else { Err(InvalidTransaction::Call.into()) } diff --git a/frame/babe/src/lib.rs b/frame/babe/src/lib.rs index ab8ebf552fd1a..c048e12f9ee65 100644 --- a/frame/babe/src/lib.rs +++ b/frame/babe/src/lib.rs @@ -166,8 +166,8 @@ pub mod pallet { /// definition. type EquivocationReportSystem: OffenceReportSystem< Self::AccountId, - Self::KeyOwnerProof, EquivocationProof, + Self::KeyOwnerProof, >; } diff --git a/frame/grandpa/src/equivocation.rs b/frame/grandpa/src/equivocation.rs index 5e996e63f92ca..ce7aa158dab52 100644 --- a/frame/grandpa/src/equivocation.rs +++ b/frame/grandpa/src/equivocation.rs @@ -45,7 +45,7 @@ use sp_runtime::{ InvalidTransaction, TransactionPriority, TransactionSource, TransactionValidity, TransactionValidityError, ValidTransaction, }, - DispatchResult, KeyTypeId, Perbill, + DispatchError, KeyTypeId, Perbill, }; use sp_session::{GetSessionNumber, GetValidatorCount}; use sp_staking::{ @@ -116,7 +116,7 @@ pub struct EquivocationReportSystem(sp_std::marker::PhantomData<(T, // `offchain::SendTransactionTypes` for unsigned extrinsic creation and // submission. impl - OffenceReportSystem> + OffenceReportSystem, T::KeyOwnerProof> for EquivocationReportSystem where T: Config + pallet_authorship::Config + frame_system::offchain::SendTransactionTypes>, @@ -135,7 +135,7 @@ where reporter: Option, equivocation_proof: EquivocationProof, key_owner_proof: T::KeyOwnerProof, - ) -> DispatchResult { + ) -> Result<(), DispatchError> { let reporter = reporter.or_else(|| >::author()); // We check the equivocation within the context of its set id (and @@ -196,11 +196,11 @@ where fn check_evidence( equivocation_proof: &EquivocationProof, key_owner_proof: &T::KeyOwnerProof, - ) -> DispatchResult { + ) -> Result<(), TransactionValidityError> { // Check the membership proof to extract the offender's id let key = (sp_finality_grandpa::KEY_TYPE, equivocation_proof.offender().clone()); - let offender = P::check_proof(key, key_owner_proof.clone()) - .ok_or(Error::::InvalidKeyOwnershipProof)?; + let offender = + P::check_proof(key, key_owner_proof.clone()).ok_or(InvalidTransaction::BadProof)?; // Check if the offence has already been reported, and if so then we can discard the report. let time_slot = GrandpaTimeSlot { @@ -208,7 +208,7 @@ where round: equivocation_proof.round(), }; if R::is_known_offence(&[offender], &time_slot) { - Err(Error::::DuplicateOffenceReport.into()) + Err(InvalidTransaction::Stale.into()) } else { Ok(()) } @@ -254,9 +254,7 @@ impl Pallet { } // Check report validity - // TODO DAVXY: propagate error - T::EquivocationReportSystem::check_evidence(equivocation_proof, key_owner_proof) - .map_err(|_| InvalidTransaction::Stale)?; + T::EquivocationReportSystem::check_evidence(equivocation_proof, key_owner_proof)?; let longevity = >::Longevity::get(); @@ -281,9 +279,7 @@ impl Pallet { pub fn pre_dispatch(call: &Call) -> Result<(), TransactionValidityError> { if let Call::report_equivocation_unsigned { equivocation_proof, key_owner_proof } = call { - // TODO DAVXY: propagate error T::EquivocationReportSystem::check_evidence(equivocation_proof, key_owner_proof) - .map_err(|_| TransactionValidityError::Invalid(InvalidTransaction::Stale)) } else { Err(InvalidTransaction::Call.into()) } diff --git a/frame/grandpa/src/lib.rs b/frame/grandpa/src/lib.rs index 1379e2e283317..54a77f2eeb988 100644 --- a/frame/grandpa/src/lib.rs +++ b/frame/grandpa/src/lib.rs @@ -117,8 +117,8 @@ pub mod pallet { /// definition. type EquivocationReportSystem: OffenceReportSystem< Self::AccountId, - Self::KeyOwnerProof, EquivocationProof, + Self::KeyOwnerProof, >; } diff --git a/primitives/staking/src/offence.rs b/primitives/staking/src/offence.rs index 462c0f196fff9..7c3aed6d38d99 100644 --- a/primitives/staking/src/offence.rs +++ b/primitives/staking/src/offence.rs @@ -20,7 +20,7 @@ use codec::{Decode, Encode}; use sp_core::Get; -use sp_runtime::{DispatchResult, Perbill}; +use sp_runtime::{transaction_validity::TransactionValidityError, DispatchError, Perbill}; use sp_std::vec::Vec; use crate::SessionIndex; @@ -219,7 +219,7 @@ pub struct OffenceDetails { /// It is assumed that this subsystem takes care of checking offender key /// ownership proof during evidence report. Key ownership checks are done using /// the `key_owner_proof` argument which comes together with the an `offence_proof`. -pub trait OffenceReportSystem { +pub trait OffenceReportSystem { /// Longevity, in blocks, for the report validity. /// For example, when using the staking pallet this should be set equal /// to the bonding durationin blocks, not eras). @@ -230,13 +230,13 @@ pub trait OffenceReportSystem { reporter: Option, offence_proof: OffenceProof, key_owner_proof: KeyOwnerProof, - ) -> DispatchResult; + ) -> Result<(), DispatchError>; /// Check offence evidence. fn check_evidence( offence_proof: &OffenceProof, key_owner_proof: &KeyOwnerProof, - ) -> DispatchResult; + ) -> Result<(), TransactionValidityError>; /// Create and submit an offence report extrinsic. fn submit_evidence( @@ -246,29 +246,31 @@ pub trait OffenceReportSystem { } // Dummy report system. -impl - OffenceReportSystem for () -{ +// +// `KeyOwnerProof` type has been coercivelly set as `sp_core::Void`, that is a +// type that can't be regularly instantiated. The idea is to prevent the report +// and submission of evidence in cause of usage of this report system. +impl OffenceReportSystem for () { type Longevity = (); fn report_evidence( _reporter: Option, _offence_proof: OffenceProof, - _key_owner_proof: KeyOwnerProof, - ) -> DispatchResult { + _key_owner_proof: sp_core::Void, + ) -> Result<(), DispatchError> { Ok(()) } fn check_evidence( _offence_proof: &OffenceProof, - _key_owner_proof: &KeyOwnerProof, - ) -> DispatchResult { + _key_owner_proof: &sp_core::Void, + ) -> Result<(), TransactionValidityError> { Ok(()) } fn submit_evidence( _offence_proof: OffenceProof, - _key_owner_proof: KeyOwnerProof, + _key_owner_proof: sp_core::Void, ) -> Result<(), ()> { Ok(()) } From 8706a6d11667553d91c5ed8f25b58195654f04b0 Mon Sep 17 00:00:00 2001 From: Davide Galassi Date: Fri, 24 Feb 2023 16:54:57 +0100 Subject: [PATCH 24/33] Some renaming and documentation --- frame/babe/src/equivocation.rs | 72 ++++++++++++++-------------- frame/babe/src/lib.rs | 16 +++---- frame/grandpa/src/equivocation.rs | 80 +++++++++++++++---------------- frame/grandpa/src/lib.rs | 18 +++---- primitives/staking/src/offence.rs | 49 ++++++++++++------- 5 files changed, 121 insertions(+), 114 deletions(-) diff --git a/frame/babe/src/equivocation.rs b/frame/babe/src/equivocation.rs index 58fc146a079c8..fdb68c0ee6e67 100644 --- a/frame/babe/src/equivocation.rs +++ b/frame/babe/src/equivocation.rs @@ -119,7 +119,42 @@ where { type Longevity = L; - fn report_evidence( + fn publish_evidence( + equivocation_proof: EquivocationProof, + key_owner_proof: T::KeyOwnerProof, + ) -> Result<(), ()> { + use frame_system::offchain::SubmitTransaction; + + let call = Call::report_equivocation_unsigned { + equivocation_proof: Box::new(equivocation_proof), + key_owner_proof, + }; + let res = SubmitTransaction::>::submit_unsigned_transaction(call.into()); + 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( + equivocation_proof: &EquivocationProof, + key_owner_proof: &T::KeyOwnerProof, + ) -> Result<(), TransactionValidityError> { + // Check the membership proof to extract the offender's id + let key = (sp_consensus_babe::KEY_TYPE, equivocation_proof.offender.clone()); + let offender = + P::check_proof(key, key_owner_proof.clone()).ok_or(InvalidTransaction::BadProof)?; + + // Check if the offence has already been reported, and if so then we can discard the report. + if R::is_known_offence(&[offender], &equivocation_proof.slot) { + Err(InvalidTransaction::Stale.into()) + } else { + Ok(()) + } + } + + fn consume_evidence( reporter: Option, equivocation_proof: EquivocationProof, key_owner_proof: T::KeyOwnerProof, @@ -156,41 +191,6 @@ where Ok(()) } - - fn check_evidence( - equivocation_proof: &EquivocationProof, - key_owner_proof: &T::KeyOwnerProof, - ) -> Result<(), TransactionValidityError> { - // Check the membership proof to extract the offender's id - let key = (sp_consensus_babe::KEY_TYPE, equivocation_proof.offender.clone()); - let offender = - P::check_proof(key, key_owner_proof.clone()).ok_or(InvalidTransaction::BadProof)?; - - // Check if the offence has already been reported, and if so then we can discard the report. - if R::is_known_offence(&[offender], &equivocation_proof.slot) { - Err(InvalidTransaction::Stale.into()) - } else { - Ok(()) - } - } - - fn submit_evidence( - equivocation_proof: EquivocationProof, - key_owner_proof: T::KeyOwnerProof, - ) -> Result<(), ()> { - use frame_system::offchain::SubmitTransaction; - - let call = Call::report_equivocation_unsigned { - equivocation_proof: Box::new(equivocation_proof), - key_owner_proof, - }; - let res = SubmitTransaction::>::submit_unsigned_transaction(call.into()); - match res { - Ok(()) => info!(target: LOG_TARGET, "Submitted equivocation report."), - Err(e) => error!(target: LOG_TARGET, "Error submitting equivocation report: {:?}", e), - } - res - } } /// Methods for the `ValidateUnsigned` implementation: diff --git a/frame/babe/src/lib.rs b/frame/babe/src/lib.rs index c048e12f9ee65..58adb927a037a 100644 --- a/frame/babe/src/lib.rs +++ b/frame/babe/src/lib.rs @@ -158,12 +158,9 @@ pub mod pallet { /// session at which the equivocation occurred. type KeyOwnerProof: Parameter + GetSessionNumber + GetValidatorCount; - /// The equivocation handling subsystem, defines methods to report an - /// offence (after the equivocation has been validated) and for submitting a - /// transaction to report an equivocation (from an offchain context). - /// NOTE: when enabling equivocation handling (i.e. this type isn't set to - /// `()`) you must use this pallet's `ValidateUnsigned` in the runtime - /// definition. + /// 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< Self::AccountId, EquivocationProof, @@ -419,7 +416,7 @@ pub mod pallet { key_owner_proof: T::KeyOwnerProof, ) -> DispatchResultWithPostInfo { let reporter = ensure_signed(origin)?; - T::EquivocationReportSystem::report_evidence( + T::EquivocationReportSystem::consume_evidence( Some(reporter), *equivocation_proof, key_owner_proof, @@ -446,12 +443,11 @@ pub mod pallet { key_owner_proof: T::KeyOwnerProof, ) -> DispatchResultWithPostInfo { ensure_none(origin)?; - T::EquivocationReportSystem::report_evidence( + T::EquivocationReportSystem::consume_evidence( None, *equivocation_proof, key_owner_proof, )?; - // Waive the fee since the report is valid and beneficial Ok(Pays::No.into()) } @@ -894,7 +890,7 @@ impl Pallet { equivocation_proof: EquivocationProof, key_owner_proof: T::KeyOwnerProof, ) -> Option<()> { - T::EquivocationReportSystem::submit_evidence(equivocation_proof, key_owner_proof).ok() + T::EquivocationReportSystem::publish_evidence(equivocation_proof, key_owner_proof).ok() } } diff --git a/frame/grandpa/src/equivocation.rs b/frame/grandpa/src/equivocation.rs index ce7aa158dab52..8b1fed3177fa9 100644 --- a/frame/grandpa/src/equivocation.rs +++ b/frame/grandpa/src/equivocation.rs @@ -131,7 +131,46 @@ where { type Longevity = L; - fn report_evidence( + fn publish_evidence( + equivocation_proof: EquivocationProof, + key_owner_proof: T::KeyOwnerProof, + ) -> Result<(), ()> { + use frame_system::offchain::SubmitTransaction; + + let call = Call::report_equivocation_unsigned { + equivocation_proof: Box::new(equivocation_proof), + key_owner_proof, + }; + let res = SubmitTransaction::>::submit_unsigned_transaction(call.into()); + 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( + equivocation_proof: &EquivocationProof, + key_owner_proof: &T::KeyOwnerProof, + ) -> Result<(), TransactionValidityError> { + // Check the membership proof to extract the offender's id + let key = (sp_finality_grandpa::KEY_TYPE, equivocation_proof.offender().clone()); + let offender = + P::check_proof(key, key_owner_proof.clone()).ok_or(InvalidTransaction::BadProof)?; + + // Check if the offence has already been reported, and if so then we can discard the report. + let time_slot = GrandpaTimeSlot { + 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 consume_evidence( reporter: Option, equivocation_proof: EquivocationProof, key_owner_proof: T::KeyOwnerProof, @@ -192,45 +231,6 @@ where Ok(()) } - - fn check_evidence( - equivocation_proof: &EquivocationProof, - key_owner_proof: &T::KeyOwnerProof, - ) -> Result<(), TransactionValidityError> { - // Check the membership proof to extract the offender's id - let key = (sp_finality_grandpa::KEY_TYPE, equivocation_proof.offender().clone()); - let offender = - P::check_proof(key, key_owner_proof.clone()).ok_or(InvalidTransaction::BadProof)?; - - // Check if the offence has already been reported, and if so then we can discard the report. - let time_slot = GrandpaTimeSlot { - 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 submit_evidence( - equivocation_proof: EquivocationProof, - key_owner_proof: T::KeyOwnerProof, - ) -> Result<(), ()> { - use frame_system::offchain::SubmitTransaction; - - let call = Call::report_equivocation_unsigned { - equivocation_proof: Box::new(equivocation_proof), - key_owner_proof, - }; - let res = SubmitTransaction::>::submit_unsigned_transaction(call.into()); - match res { - Ok(()) => info!(target: LOG_TARGET, "Submitted equivocation report."), - Err(e) => error!(target: LOG_TARGET, "Error submitting equivocation report: {:?}", e), - } - res - } } /// Methods for the `ValidateUnsigned` implementation: diff --git a/frame/grandpa/src/lib.rs b/frame/grandpa/src/lib.rs index 54a77f2eeb988..1424380804b95 100644 --- a/frame/grandpa/src/lib.rs +++ b/frame/grandpa/src/lib.rs @@ -105,16 +105,13 @@ pub mod pallet { type MaxSetIdSessionEntries: Get; /// The proof of key ownership, used for validating equivocation reports - /// The proof must include the session index and validator count of the + /// 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 report an - /// offence (after the equivocation has been validated) and for submitting a - /// transaction to report an equivocation (from an offchain context). - /// NOTE: when enabling equivocation handling (i.e. this type isn't set to - /// `()`) you must use this pallet's `ValidateUnsigned` in the runtime - /// definition. + /// 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< Self::AccountId, EquivocationProof, @@ -201,7 +198,7 @@ pub mod pallet { ) -> DispatchResultWithPostInfo { let reporter = ensure_signed(origin)?; - T::EquivocationReportSystem::report_evidence( + T::EquivocationReportSystem::consume_evidence( Some(reporter), *equivocation_proof, key_owner_proof, @@ -228,12 +225,11 @@ pub mod pallet { ) -> DispatchResultWithPostInfo { ensure_none(origin)?; - T::EquivocationReportSystem::report_evidence( + T::EquivocationReportSystem::consume_evidence( None, *equivocation_proof, key_owner_proof, )?; - // Waive the fee since the report is valid and beneficial Ok(Pays::No.into()) } @@ -538,7 +534,7 @@ impl Pallet { equivocation_proof: EquivocationProof, key_owner_proof: T::KeyOwnerProof, ) -> Option<()> { - T::EquivocationReportSystem::submit_evidence(equivocation_proof, key_owner_proof).ok() + T::EquivocationReportSystem::publish_evidence(equivocation_proof, key_owner_proof).ok() } fn on_stalled(further_wait: T::BlockNumber, median: T::BlockNumber) { diff --git a/primitives/staking/src/offence.rs b/primitives/staking/src/offence.rs index 7c3aed6d38d99..5577116217dcb 100644 --- a/primitives/staking/src/offence.rs +++ b/primitives/staking/src/offence.rs @@ -210,39 +210,54 @@ pub struct OffenceDetails { pub reporters: Vec, } -/// A system capable of construct, report and submit offences. +/// A system to construct, report and submit offences. /// /// Implementors of this trait provide a wrapper for some low level operations -/// which mechanics are left open by this trait. That is, implementation details -/// are left opaque at this level. +/// which mechanics are left open to the implementation. That is, implementation +/// details are opaque at this level. /// -/// It is assumed that this subsystem takes care of checking offender key +/// It is assumed that this offence subsystem if able of checking offender key /// ownership proof during evidence report. Key ownership checks are done using /// the `key_owner_proof` argument which comes together with the an `offence_proof`. +/// +/// At this level we also don't assume the usage context of this trait, but +/// as a general guideline we provide a typical usage scenario: +/// +/// 1. An equivocation offence is detected by a participant. +/// Offence evidence is submitted onchain via the [`publish_evidence`] method. +/// This will construct and submit an extrinsic transaction containing the offence +/// information. +/// +/// 2. If the extrinsic was unsigned then receivers of the transaction may want to +/// perform a preliminary sanity check before further processing. This is a good +/// place to call the [`check_evidence`] method. +/// +/// 3. Finally the extrinsic is executed on-chain. At this point the user calls +/// the [`consume_evidence`] to digest the offence report and enact the required actions. pub trait OffenceReportSystem { /// Longevity, in blocks, for the report validity. /// For example, when using the staking pallet this should be set equal /// to the bonding durationin blocks, not eras). type Longevity: Get; - /// Report offence evidence. - fn report_evidence( - reporter: Option, + /// Create and submit an offence report. + fn publish_evidence( offence_proof: OffenceProof, key_owner_proof: KeyOwnerProof, - ) -> Result<(), DispatchError>; + ) -> Result<(), ()>; - /// Check offence evidence. + /// Check an offence evidence validity. fn check_evidence( offence_proof: &OffenceProof, key_owner_proof: &KeyOwnerProof, ) -> Result<(), TransactionValidityError>; - /// Create and submit an offence report extrinsic. - fn submit_evidence( + /// Report an offence evidence. + fn consume_evidence( + reporter: Option, offence_proof: OffenceProof, key_owner_proof: KeyOwnerProof, - ) -> Result<(), ()>; + ) -> Result<(), DispatchError>; } // Dummy report system. @@ -253,11 +268,10 @@ pub trait OffenceReportSystem { impl OffenceReportSystem for () { type Longevity = (); - fn report_evidence( - _reporter: Option, + fn publish_evidence( _offence_proof: OffenceProof, _key_owner_proof: sp_core::Void, - ) -> Result<(), DispatchError> { + ) -> Result<(), ()> { Ok(()) } @@ -268,10 +282,11 @@ impl OffenceReportSystem, _offence_proof: OffenceProof, _key_owner_proof: sp_core::Void, - ) -> Result<(), ()> { + ) -> Result<(), DispatchError> { Ok(()) } } From 7bebe8461864ade81651aff53b642c805b338b2d Mon Sep 17 00:00:00 2001 From: Davide Galassi Date: Fri, 24 Feb 2023 17:16:20 +0100 Subject: [PATCH 25/33] Improve documentation --- primitives/staking/src/offence.rs | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/primitives/staking/src/offence.rs b/primitives/staking/src/offence.rs index 5577116217dcb..4bb5e8447a8a0 100644 --- a/primitives/staking/src/offence.rs +++ b/primitives/staking/src/offence.rs @@ -240,19 +240,28 @@ pub trait OffenceReportSystem { /// to the bonding durationin blocks, not eras). type Longevity: Get; - /// Create and submit an offence report. + /// Publish an offence evidence. + /// + /// Common usage: submit the evidence on-chain via some kind of extrinsic. fn publish_evidence( offence_proof: OffenceProof, key_owner_proof: KeyOwnerProof, ) -> Result<(), ()>; - /// Check an offence evidence validity. + /// Check an offence evidence. + /// + /// Common usage: preliminary validity check before execution + /// (e.g. for unsigned extrinsic quick checks). fn check_evidence( offence_proof: &OffenceProof, key_owner_proof: &KeyOwnerProof, ) -> Result<(), TransactionValidityError>; /// Report an offence evidence. + /// + /// Typical usage: enact some form of slashing directly or by forwarding + /// the evidence to a lower level specialized subsystem (e.g. a handler + /// implementing `ReportOffence` trait). fn consume_evidence( reporter: Option, offence_proof: OffenceProof, @@ -260,11 +269,14 @@ pub trait OffenceReportSystem { ) -> Result<(), DispatchError>; } -// Dummy report system. -// -// `KeyOwnerProof` type has been coercivelly set as `sp_core::Void`, that is a -// type that can't be regularly instantiated. The idea is to prevent the report -// and submission of evidence in cause of usage of this report system. +/// Dummy offence report system. +/// +/// `KeyOwnerProof` type is coercivelly set as `sp_core::Void`, i.e. a type that +/// can't be regularly instantiated. The idea is to prevent the creation of offences +/// targeting this subsystem in the first place and thus preventing any spammy behavior. +/// +/// Nevertheless, because of the generic [`Reporter`] and [`OffenceProof`] this dummy +/// system can be used in any place requiring the association with an `OffenceReportSystem`. impl OffenceReportSystem for () { type Longevity = (); From f9a505b7cb80565059169a110474f016d414e912 Mon Sep 17 00:00:00 2001 From: Davide Galassi Date: Fri, 24 Feb 2023 18:35:38 +0100 Subject: [PATCH 26/33] More abstract offence report system --- frame/babe/src/equivocation.rs | 24 +++++----- frame/babe/src/lib.rs | 13 +++--- frame/grandpa/src/equivocation.rs | 32 +++++++------ frame/grandpa/src/lib.rs | 13 +++--- primitives/staking/src/offence.rs | 74 +++++++++++-------------------- 5 files changed, 68 insertions(+), 88 deletions(-) diff --git a/frame/babe/src/equivocation.rs b/frame/babe/src/equivocation.rs index fdb68c0ee6e67..b4643e57bf176 100644 --- a/frame/babe/src/equivocation.rs +++ b/frame/babe/src/equivocation.rs @@ -104,7 +104,8 @@ pub struct EquivocationReportSystem(sp_std::marker::PhantomData<(T, // We use the authorship pallet to fetch the current block author and use // `offchain::SendTransactionTypes` for unsigned extrinsic creation and // submission. -impl OffenceReportSystem, P::Proof> +impl + OffenceReportSystem, (EquivocationProof, T::KeyOwnerProof)> for EquivocationReportSystem where T: Config + pallet_authorship::Config + frame_system::offchain::SendTransactionTypes>, @@ -120,10 +121,10 @@ where type Longevity = L; fn publish_evidence( - equivocation_proof: EquivocationProof, - key_owner_proof: T::KeyOwnerProof, + evidence: (EquivocationProof, T::KeyOwnerProof), ) -> Result<(), ()> { use frame_system::offchain::SubmitTransaction; + let (equivocation_proof, key_owner_proof) = evidence; let call = Call::report_equivocation_unsigned { equivocation_proof: Box::new(equivocation_proof), @@ -138,9 +139,10 @@ where } fn check_evidence( - equivocation_proof: &EquivocationProof, - key_owner_proof: &T::KeyOwnerProof, + evidence: (EquivocationProof, T::KeyOwnerProof), ) -> Result<(), TransactionValidityError> { + let (equivocation_proof, key_owner_proof) = evidence; + // Check the membership proof to extract the offender's id let key = (sp_consensus_babe::KEY_TYPE, equivocation_proof.offender.clone()); let offender = @@ -156,9 +158,9 @@ where fn consume_evidence( reporter: Option, - equivocation_proof: EquivocationProof, - key_owner_proof: T::KeyOwnerProof, + evidence: (EquivocationProof, T::KeyOwnerProof), ) -> Result<(), DispatchError> { + let (equivocation_proof, key_owner_proof) = evidence; let reporter = reporter.or_else(|| >::author()); let offender = equivocation_proof.offender.clone(); let slot = equivocation_proof.slot; @@ -214,10 +216,11 @@ impl Pallet { } // Check report validity - T::EquivocationReportSystem::check_evidence(equivocation_proof, key_owner_proof)?; + let evidence = (*equivocation_proof.clone(), key_owner_proof.clone()); + T::EquivocationReportSystem::check_evidence(evidence)?; let longevity = - >::Longevity::get(); + >::Longevity::get(); ValidTransaction::with_tag_prefix("BabeEquivocation") // We assign the maximum priority for any equivocation report. @@ -235,7 +238,8 @@ impl Pallet { pub fn pre_dispatch(call: &Call) -> Result<(), TransactionValidityError> { if let Call::report_equivocation_unsigned { equivocation_proof, key_owner_proof } = call { - T::EquivocationReportSystem::check_evidence(equivocation_proof, key_owner_proof) + let evidence = (*equivocation_proof.clone(), key_owner_proof.clone()); + T::EquivocationReportSystem::check_evidence(evidence) } else { Err(InvalidTransaction::Call.into()) } diff --git a/frame/babe/src/lib.rs b/frame/babe/src/lib.rs index 58adb927a037a..5f3b74709eb78 100644 --- a/frame/babe/src/lib.rs +++ b/frame/babe/src/lib.rs @@ -162,9 +162,8 @@ pub mod pallet { /// offence and for submitting a transaction to report an equivocation /// (from an offchain context). type EquivocationReportSystem: OffenceReportSystem< - Self::AccountId, - EquivocationProof, - Self::KeyOwnerProof, + Option, + (EquivocationProof, Self::KeyOwnerProof), >; } @@ -418,8 +417,7 @@ pub mod pallet { let reporter = ensure_signed(origin)?; T::EquivocationReportSystem::consume_evidence( Some(reporter), - *equivocation_proof, - key_owner_proof, + (*equivocation_proof, key_owner_proof), )?; // Waive the fee since the report is valid and beneficial Ok(Pays::No.into()) @@ -445,8 +443,7 @@ pub mod pallet { ensure_none(origin)?; T::EquivocationReportSystem::consume_evidence( None, - *equivocation_proof, - key_owner_proof, + (*equivocation_proof, key_owner_proof), )?; Ok(Pays::No.into()) } @@ -890,7 +887,7 @@ impl Pallet { equivocation_proof: EquivocationProof, key_owner_proof: T::KeyOwnerProof, ) -> Option<()> { - T::EquivocationReportSystem::publish_evidence(equivocation_proof, key_owner_proof).ok() + T::EquivocationReportSystem::publish_evidence((equivocation_proof, key_owner_proof)).ok() } } diff --git a/frame/grandpa/src/equivocation.rs b/frame/grandpa/src/equivocation.rs index 8b1fed3177fa9..e9196aeb008b8 100644 --- a/frame/grandpa/src/equivocation.rs +++ b/frame/grandpa/src/equivocation.rs @@ -116,8 +116,10 @@ pub struct EquivocationReportSystem(sp_std::marker::PhantomData<(T, // `offchain::SendTransactionTypes` for unsigned extrinsic creation and // submission. impl - OffenceReportSystem, T::KeyOwnerProof> - for EquivocationReportSystem + OffenceReportSystem< + Option, + (EquivocationProof, T::KeyOwnerProof), + > for EquivocationReportSystem where T: Config + pallet_authorship::Config + frame_system::offchain::SendTransactionTypes>, R: ReportOffence< @@ -132,10 +134,10 @@ where type Longevity = L; fn publish_evidence( - equivocation_proof: EquivocationProof, - key_owner_proof: T::KeyOwnerProof, + evidence: (EquivocationProof, T::KeyOwnerProof), ) -> Result<(), ()> { use frame_system::offchain::SubmitTransaction; + let (equivocation_proof, key_owner_proof) = evidence; let call = Call::report_equivocation_unsigned { equivocation_proof: Box::new(equivocation_proof), @@ -150,13 +152,13 @@ where } fn check_evidence( - equivocation_proof: &EquivocationProof, - key_owner_proof: &T::KeyOwnerProof, + evidence: (EquivocationProof, T::KeyOwnerProof), ) -> Result<(), TransactionValidityError> { + let (equivocation_proof, key_owner_proof) = evidence; + // Check the membership proof to extract the offender's id let key = (sp_finality_grandpa::KEY_TYPE, equivocation_proof.offender().clone()); - let offender = - P::check_proof(key, key_owner_proof.clone()).ok_or(InvalidTransaction::BadProof)?; + 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 = GrandpaTimeSlot { @@ -172,16 +174,16 @@ where fn consume_evidence( reporter: Option, - equivocation_proof: EquivocationProof, - key_owner_proof: T::KeyOwnerProof, + evidence: (EquivocationProof, T::KeyOwnerProof), ) -> Result<(), DispatchError> { + let (equivocation_proof, key_owner_proof) = evidence; let reporter = reporter.or_else(|| >::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 offender = equivocation_proof.offender().clone(); let set_id = equivocation_proof.set_id(); let round = equivocation_proof.round(); let session_index = key_owner_proof.session(); @@ -254,10 +256,11 @@ impl Pallet { } // Check report validity - T::EquivocationReportSystem::check_evidence(equivocation_proof, key_owner_proof)?; + let evidence = (*equivocation_proof.clone(), key_owner_proof.clone()); + T::EquivocationReportSystem::check_evidence(evidence)?; let longevity = - >::Longevity::get(); + >::Longevity::get(); ValidTransaction::with_tag_prefix("GrandpaEquivocation") // We assign the maximum priority for any equivocation report. @@ -279,7 +282,8 @@ impl Pallet { pub fn pre_dispatch(call: &Call) -> Result<(), TransactionValidityError> { if let Call::report_equivocation_unsigned { equivocation_proof, key_owner_proof } = call { - T::EquivocationReportSystem::check_evidence(equivocation_proof, key_owner_proof) + let evidence = (*equivocation_proof.clone(), key_owner_proof.clone()); + T::EquivocationReportSystem::check_evidence(evidence) } else { Err(InvalidTransaction::Call.into()) } diff --git a/frame/grandpa/src/lib.rs b/frame/grandpa/src/lib.rs index 1424380804b95..691fecf38742f 100644 --- a/frame/grandpa/src/lib.rs +++ b/frame/grandpa/src/lib.rs @@ -113,9 +113,8 @@ pub mod pallet { /// offence and for submitting a transaction to report an equivocation /// (from an offchain context). type EquivocationReportSystem: OffenceReportSystem< - Self::AccountId, - EquivocationProof, - Self::KeyOwnerProof, + Option, + (EquivocationProof, Self::KeyOwnerProof), >; } @@ -200,8 +199,7 @@ pub mod pallet { T::EquivocationReportSystem::consume_evidence( Some(reporter), - *equivocation_proof, - key_owner_proof, + (*equivocation_proof, key_owner_proof), )?; // Waive the fee since the report is valid and beneficial Ok(Pays::No.into()) @@ -227,8 +225,7 @@ pub mod pallet { T::EquivocationReportSystem::consume_evidence( None, - *equivocation_proof, - key_owner_proof, + (*equivocation_proof, key_owner_proof), )?; Ok(Pays::No.into()) } @@ -534,7 +531,7 @@ impl Pallet { equivocation_proof: EquivocationProof, key_owner_proof: T::KeyOwnerProof, ) -> Option<()> { - T::EquivocationReportSystem::publish_evidence(equivocation_proof, key_owner_proof).ok() + T::EquivocationReportSystem::publish_evidence((equivocation_proof, key_owner_proof)).ok() } fn on_stalled(further_wait: T::BlockNumber, median: T::BlockNumber) { diff --git a/primitives/staking/src/offence.rs b/primitives/staking/src/offence.rs index 4bb5e8447a8a0..b668adc8aea62 100644 --- a/primitives/staking/src/offence.rs +++ b/primitives/staking/src/offence.rs @@ -210,32 +210,30 @@ pub struct OffenceDetails { pub reporters: Vec, } -/// A system to construct, report and submit offences. +/// An abstract system capable of publish, check and consume offence evidences. /// -/// Implementors of this trait provide a wrapper for some low level operations -/// which mechanics are left open to the implementation. That is, implementation -/// details are opaque at this level. +/// Implementation details are left opaque at this level and we don't assume +/// any specific usage scenario for this trait. The main goal for this trait is +/// to group together some common behaviors required during a typical offence +/// report flow. /// -/// It is assumed that this offence subsystem if able of checking offender key -/// ownership proof during evidence report. Key ownership checks are done using -/// the `key_owner_proof` argument which comes together with the an `offence_proof`. +/// Even tough this trait doesn't assume too much, a general guideline for a typical +/// usage scenario: /// -/// At this level we also don't assume the usage context of this trait, but -/// as a general guideline we provide a typical usage scenario: +/// 1. An offence is detected and an evidence is submitted onchain via the +/// [`publish_evidence`] method. This will construct and submit an extrinsic +/// transaction containing the offence evidence information. /// -/// 1. An equivocation offence is detected by a participant. -/// Offence evidence is submitted onchain via the [`publish_evidence`] method. -/// This will construct and submit an extrinsic transaction containing the offence -/// information. -/// -/// 2. If the extrinsic was unsigned then receivers of the transaction may want to -/// perform a preliminary sanity check before further processing. This is a good +/// 2. If the extrinsic was unsigned the transaction receivers may want to perform +/// some preliminary sanity check before further processing. This is a good /// place to call the [`check_evidence`] method. /// -/// 3. Finally the extrinsic is executed on-chain. At this point the user calls -/// the [`consume_evidence`] to digest the offence report and enact the required actions. -pub trait OffenceReportSystem { - /// Longevity, in blocks, for the report validity. +/// 3. Finally the extrinsic is executed on-chain. At this point the user may wish +/// to call [`consume_evidence`] to digest the offence report and enact the +/// required actions. +pub trait OffenceReportSystem { + /// Longevity, in blocks, for the evidence report validity. + /// /// For example, when using the staking pallet this should be set equal /// to the bonding durationin blocks, not eras). type Longevity: Get; @@ -243,30 +241,20 @@ pub trait OffenceReportSystem { /// Publish an offence evidence. /// /// Common usage: submit the evidence on-chain via some kind of extrinsic. - fn publish_evidence( - offence_proof: OffenceProof, - key_owner_proof: KeyOwnerProof, - ) -> Result<(), ()>; + fn publish_evidence(evidence: Evidence) -> Result<(), ()>; /// Check an offence evidence. /// /// Common usage: preliminary validity check before execution /// (e.g. for unsigned extrinsic quick checks). - fn check_evidence( - offence_proof: &OffenceProof, - key_owner_proof: &KeyOwnerProof, - ) -> Result<(), TransactionValidityError>; + fn check_evidence(evidence: Evidence) -> Result<(), TransactionValidityError>; /// Report an offence evidence. /// - /// Typical usage: enact some form of slashing directly or by forwarding + /// Common usage: enact some form of slashing directly or by forwarding /// the evidence to a lower level specialized subsystem (e.g. a handler /// implementing `ReportOffence` trait). - fn consume_evidence( - reporter: Option, - offence_proof: OffenceProof, - key_owner_proof: KeyOwnerProof, - ) -> Result<(), DispatchError>; + fn consume_evidence(reporter: Reporter, evidence: Evidence) -> Result<(), DispatchError>; } /// Dummy offence report system. @@ -277,28 +265,18 @@ pub trait OffenceReportSystem { /// /// Nevertheless, because of the generic [`Reporter`] and [`OffenceProof`] this dummy /// system can be used in any place requiring the association with an `OffenceReportSystem`. -impl OffenceReportSystem for () { +impl OffenceReportSystem for () { type Longevity = (); - fn publish_evidence( - _offence_proof: OffenceProof, - _key_owner_proof: sp_core::Void, - ) -> Result<(), ()> { + fn publish_evidence(_evidence: Evidence) -> Result<(), ()> { Ok(()) } - fn check_evidence( - _offence_proof: &OffenceProof, - _key_owner_proof: &sp_core::Void, - ) -> Result<(), TransactionValidityError> { + fn check_evidence(_evidence: Evidence) -> Result<(), TransactionValidityError> { Ok(()) } - fn consume_evidence( - _reporter: Option, - _offence_proof: OffenceProof, - _key_owner_proof: sp_core::Void, - ) -> Result<(), DispatchError> { + fn consume_evidence(_reporter: Reporter, _evidence: Evidence) -> Result<(), DispatchError> { Ok(()) } } From aa830118dc40faac16561c2f595de12ab2e253a7 Mon Sep 17 00:00:00 2001 From: Davide Galassi Date: Fri, 24 Feb 2023 18:45:07 +0100 Subject: [PATCH 27/33] Rename 'consume_evidence' to 'process_evidence' --- frame/babe/src/equivocation.rs | 2 +- frame/babe/src/lib.rs | 4 ++-- frame/grandpa/src/equivocation.rs | 2 +- frame/grandpa/src/lib.rs | 4 ++-- primitives/staking/src/offence.rs | 10 +++++----- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/frame/babe/src/equivocation.rs b/frame/babe/src/equivocation.rs index b4643e57bf176..e2369d4f2fca5 100644 --- a/frame/babe/src/equivocation.rs +++ b/frame/babe/src/equivocation.rs @@ -156,7 +156,7 @@ where } } - fn consume_evidence( + fn process_evidence( reporter: Option, evidence: (EquivocationProof, T::KeyOwnerProof), ) -> Result<(), DispatchError> { diff --git a/frame/babe/src/lib.rs b/frame/babe/src/lib.rs index 5f3b74709eb78..8ada302c74752 100644 --- a/frame/babe/src/lib.rs +++ b/frame/babe/src/lib.rs @@ -415,7 +415,7 @@ pub mod pallet { key_owner_proof: T::KeyOwnerProof, ) -> DispatchResultWithPostInfo { let reporter = ensure_signed(origin)?; - T::EquivocationReportSystem::consume_evidence( + T::EquivocationReportSystem::process_evidence( Some(reporter), (*equivocation_proof, key_owner_proof), )?; @@ -441,7 +441,7 @@ pub mod pallet { key_owner_proof: T::KeyOwnerProof, ) -> DispatchResultWithPostInfo { ensure_none(origin)?; - T::EquivocationReportSystem::consume_evidence( + T::EquivocationReportSystem::process_evidence( None, (*equivocation_proof, key_owner_proof), )?; diff --git a/frame/grandpa/src/equivocation.rs b/frame/grandpa/src/equivocation.rs index e9196aeb008b8..c8f87c4ae898b 100644 --- a/frame/grandpa/src/equivocation.rs +++ b/frame/grandpa/src/equivocation.rs @@ -172,7 +172,7 @@ where } } - fn consume_evidence( + fn process_evidence( reporter: Option, evidence: (EquivocationProof, T::KeyOwnerProof), ) -> Result<(), DispatchError> { diff --git a/frame/grandpa/src/lib.rs b/frame/grandpa/src/lib.rs index 691fecf38742f..dd360a2be7840 100644 --- a/frame/grandpa/src/lib.rs +++ b/frame/grandpa/src/lib.rs @@ -197,7 +197,7 @@ pub mod pallet { ) -> DispatchResultWithPostInfo { let reporter = ensure_signed(origin)?; - T::EquivocationReportSystem::consume_evidence( + T::EquivocationReportSystem::process_evidence( Some(reporter), (*equivocation_proof, key_owner_proof), )?; @@ -223,7 +223,7 @@ pub mod pallet { ) -> DispatchResultWithPostInfo { ensure_none(origin)?; - T::EquivocationReportSystem::consume_evidence( + T::EquivocationReportSystem::process_evidence( None, (*equivocation_proof, key_owner_proof), )?; diff --git a/primitives/staking/src/offence.rs b/primitives/staking/src/offence.rs index b668adc8aea62..7c3a42c904af7 100644 --- a/primitives/staking/src/offence.rs +++ b/primitives/staking/src/offence.rs @@ -210,7 +210,7 @@ pub struct OffenceDetails { pub reporters: Vec, } -/// An abstract system capable of publish, check and consume offence evidences. +/// An abstract system capable of publish, check and process offence evidences. /// /// Implementation details are left opaque at this level and we don't assume /// any specific usage scenario for this trait. The main goal for this trait is @@ -229,7 +229,7 @@ pub struct OffenceDetails { /// place to call the [`check_evidence`] method. /// /// 3. Finally the extrinsic is executed on-chain. At this point the user may wish -/// to call [`consume_evidence`] to digest the offence report and enact the +/// to call [`process_evidence`] to digest the offence report and enact the /// required actions. pub trait OffenceReportSystem { /// Longevity, in blocks, for the evidence report validity. @@ -249,12 +249,12 @@ pub trait OffenceReportSystem { /// (e.g. for unsigned extrinsic quick checks). fn check_evidence(evidence: Evidence) -> Result<(), TransactionValidityError>; - /// Report an offence evidence. + /// Process an offence evidence. /// /// Common usage: enact some form of slashing directly or by forwarding /// the evidence to a lower level specialized subsystem (e.g. a handler /// implementing `ReportOffence` trait). - fn consume_evidence(reporter: Reporter, evidence: Evidence) -> Result<(), DispatchError>; + fn process_evidence(reporter: Reporter, evidence: Evidence) -> Result<(), DispatchError>; } /// Dummy offence report system. @@ -276,7 +276,7 @@ impl OffenceReportSystem for () { Ok(()) } - fn consume_evidence(_reporter: Reporter, _evidence: Evidence) -> Result<(), DispatchError> { + fn process_evidence(_reporter: Reporter, _evidence: Evidence) -> Result<(), DispatchError> { Ok(()) } } From 0c5a5362ff0f0abfb8de1e59d14ac157f13dbe38 Mon Sep 17 00:00:00 2001 From: Davide Galassi Date: Fri, 24 Feb 2023 18:55:05 +0100 Subject: [PATCH 28/33] Further docs refinements --- primitives/staking/src/offence.rs | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/primitives/staking/src/offence.rs b/primitives/staking/src/offence.rs index 7c3a42c904af7..529c074051519 100644 --- a/primitives/staking/src/offence.rs +++ b/primitives/staking/src/offence.rs @@ -210,27 +210,26 @@ pub struct OffenceDetails { pub reporters: Vec, } -/// An abstract system capable of publish, check and process offence evidences. +/// An abstract system to publish, check and process offence evidences. /// -/// Implementation details are left opaque at this level and we don't assume -/// any specific usage scenario for this trait. The main goal for this trait is -/// to group together some common behaviors required during a typical offence -/// report flow. +/// Implementation details are left opaque and we don't assume any specific usage +/// scenario for this trait at this level. The main goal is to group together some +/// common actions required during a typical offence report flow. /// -/// Even tough this trait doesn't assume too much, a general guideline for a typical -/// usage scenario: +/// Even though this trait doesn't assume too much, this is a general guideline +/// for a typical usage scenario: /// -/// 1. An offence is detected and an evidence is submitted onchain via the +/// 1. An offence is detected and an evidence is submitted on-chain via the /// [`publish_evidence`] method. This will construct and submit an extrinsic /// transaction containing the offence evidence information. /// -/// 2. If the extrinsic was unsigned the transaction receivers may want to perform -/// some preliminary sanity check before further processing. This is a good +/// 2. If the extrinsic is unsigned then the transaction receiver may want to +/// perform some preliminary checks before further processing. This is a good /// place to call the [`check_evidence`] method. /// -/// 3. Finally the extrinsic is executed on-chain. At this point the user may wish -/// to call [`process_evidence`] to digest the offence report and enact the -/// required actions. +/// 3. Finally the report extrinsic is executed on-chain. This is where the user +/// calls the [`process_evidence`] to consume the offence report and enact any +/// required action. pub trait OffenceReportSystem { /// Longevity, in blocks, for the evidence report validity. /// From 46b9f864a0e659cb2cfd3868ffc6b9b706156688 Mon Sep 17 00:00:00 2001 From: Davide Galassi Date: Fri, 24 Feb 2023 18:57:09 +0100 Subject: [PATCH 29/33] Doc for dummy offence report --- primitives/staking/src/offence.rs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/primitives/staking/src/offence.rs b/primitives/staking/src/offence.rs index 529c074051519..5d6c8c4c1dadb 100644 --- a/primitives/staking/src/offence.rs +++ b/primitives/staking/src/offence.rs @@ -258,12 +258,7 @@ pub trait OffenceReportSystem { /// Dummy offence report system. /// -/// `KeyOwnerProof` type is coercivelly set as `sp_core::Void`, i.e. a type that -/// can't be regularly instantiated. The idea is to prevent the creation of offences -/// targeting this subsystem in the first place and thus preventing any spammy behavior. -/// -/// Nevertheless, because of the generic [`Reporter`] and [`OffenceProof`] this dummy -/// system can be used in any place requiring the association with an `OffenceReportSystem`. +/// Doesn't do anything special and returns `Ok(())` for all the actions. impl OffenceReportSystem for () { type Longevity = (); From 3ff71d14d645c3ed70f26eef4f4126e0c0048423 Mon Sep 17 00:00:00 2001 From: Davide Galassi Date: Fri, 24 Feb 2023 19:10:20 +0100 Subject: [PATCH 30/33] Fix rustdoc --- primitives/staking/src/offence.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/primitives/staking/src/offence.rs b/primitives/staking/src/offence.rs index 42a91ea90d12b..00799d567280d 100644 --- a/primitives/staking/src/offence.rs +++ b/primitives/staking/src/offence.rs @@ -220,16 +220,16 @@ pub struct OffenceDetails { /// for a typical usage scenario: /// /// 1. An offence is detected and an evidence is submitted on-chain via the -/// [`publish_evidence`] method. This will construct and submit an extrinsic -/// transaction containing the offence evidence information. +/// [`OffenceReportSystem::publish_evidence`] method. This will construct +/// and submit an extrinsic transaction containing the offence evidence. /// /// 2. If the extrinsic is unsigned then the transaction receiver may want to /// perform some preliminary checks before further processing. This is a good -/// place to call the [`check_evidence`] method. +/// place to call the [`OffenceReportSystem::check_evidence`] method. /// /// 3. Finally the report extrinsic is executed on-chain. This is where the user -/// calls the [`process_evidence`] to consume the offence report and enact any -/// required action. +/// calls the [`OffenceReportSystem::process_evidence`] to consume the offence +/// report and enact any required action. pub trait OffenceReportSystem { /// Longevity, in blocks, for the evidence report validity. /// From d3362645db84526fb62c035cf60f67c0e7d94504 Mon Sep 17 00:00:00 2001 From: Davide Galassi Date: Tue, 7 Mar 2023 17:29:44 +0100 Subject: [PATCH 31/33] Fix after master merge --- frame/grandpa/src/equivocation.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frame/grandpa/src/equivocation.rs b/frame/grandpa/src/equivocation.rs index 179280ebb5b2f..8074d94ef6e56 100644 --- a/frame/grandpa/src/equivocation.rs +++ b/frame/grandpa/src/equivocation.rs @@ -156,7 +156,7 @@ where let (equivocation_proof, key_owner_proof) = evidence; // Check the membership proof to extract the offender's id - let key = (sp_finality_grandpa::KEY_TYPE, equivocation_proof.offender().clone()); + 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. @@ -189,7 +189,7 @@ where let validator_set_count = key_owner_proof.validator_count(); // Validate equivocation proof (check votes are different and signatures are valid). - if !sp_finality_grandpa::check_equivocation_proof(equivocation_proof) { + if !sp_consensus_grandpa::check_equivocation_proof(equivocation_proof) { return Err(Error::::InvalidEquivocationProof.into()) } From 2e1c3384b857b64161bf503ae77d3d3fd83d488d Mon Sep 17 00:00:00 2001 From: Davide Galassi Date: Tue, 7 Mar 2023 17:57:51 +0100 Subject: [PATCH 32/33] Apply code review suggestions --- frame/babe/src/equivocation.rs | 15 +++++++-------- primitives/staking/src/offence.rs | 2 +- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/frame/babe/src/equivocation.rs b/frame/babe/src/equivocation.rs index 5e0c2f510b1e9..4500ae19a1571 100644 --- a/frame/babe/src/equivocation.rs +++ b/frame/babe/src/equivocation.rs @@ -87,18 +87,17 @@ impl Offence for EquivocationOffence { self.slot } + // The formula is min((3k / n)^2, 1) + // with: k = offenders_number, n = validators_number fn slash_fraction(&self, offenders_count: u32) -> Perbill { - // the formula is min((3k / n)^2, 1) - let x = Perbill::from_rational(3 * offenders_count, self.validator_set_count); - // _ ^ 2 - x.square() + // Perbill type domain is [0, 1] by definition + Perbill::from_rational(3 * offenders_count, self.validator_set_count).square() } } -/// Generic equivocation handler. This type implements `HandleEquivocation` -/// using existing subsystems that are part of frame (type bounds described -/// below) and will dispatch to them directly, it's only purpose is to wire all -/// subsystems together. +/// Babe equivocation offence system. +/// +/// This type implements `OffenceReportSystem` pub struct EquivocationReportSystem(sp_std::marker::PhantomData<(T, R, P, L)>); // We use the authorship pallet to fetch the current block author and use diff --git a/primitives/staking/src/offence.rs b/primitives/staking/src/offence.rs index 00799d567280d..6694c9055d4ff 100644 --- a/primitives/staking/src/offence.rs +++ b/primitives/staking/src/offence.rs @@ -234,7 +234,7 @@ pub trait OffenceReportSystem { /// Longevity, in blocks, for the evidence report validity. /// /// For example, when using the staking pallet this should be set equal - /// to the bonding durationin blocks, not eras). + /// to the bonding duration in blocks, not eras. type Longevity: Get; /// Publish an offence evidence. From 643c7458be8ed860d59ea9ce063803837b2c9a9b Mon Sep 17 00:00:00 2001 From: Davide Galassi Date: Tue, 7 Mar 2023 19:01:45 +0100 Subject: [PATCH 33/33] Improve docs --- frame/babe/src/equivocation.rs | 2 +- frame/grandpa/src/equivocation.rs | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/frame/babe/src/equivocation.rs b/frame/babe/src/equivocation.rs index 4500ae19a1571..ed98385a74474 100644 --- a/frame/babe/src/equivocation.rs +++ b/frame/babe/src/equivocation.rs @@ -88,7 +88,7 @@ impl Offence for EquivocationOffence { } // The formula is min((3k / n)^2, 1) - // with: k = offenders_number, n = validators_number + // 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() diff --git a/frame/grandpa/src/equivocation.rs b/frame/grandpa/src/equivocation.rs index 8074d94ef6e56..6fd12bbdfcdb9 100644 --- a/frame/grandpa/src/equivocation.rs +++ b/frame/grandpa/src/equivocation.rs @@ -97,11 +97,11 @@ impl Offence for EquivocationOffence { 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 { - // the formula is min((3k / n)^2, 1) - let x = Perbill::from_rational(3 * offenders_count, self.validator_set_count); - // _ ^ 2 - x.square() + // Perbill type domain is [0, 1] by definition + Perbill::from_rational(3 * offenders_count, self.validator_set_count).square() } }