Offences report system rework (#13425)

* Experiments with common equivocation trait

* Improved equivocation trait

* Fix grandpa equivocation implementation

* Remove some cruft

* Remove some more cruft

* More generic naming

* Simplification of offences manipilation

* More refactory

* Some prograss with the encapsulation of offence report system

* Finally unit type works as a universal null report system

* Align substrate node code

* Further simplification

* Fix test utils

* Remove not required associated type

* Fix benches

* Rollback to prev field name

* Box big params

* Fix typo

* Remove new tag computation

* Remove default implementations

* Better docs

* Return 'Result' instead of bool

* Change offence report system return types

* Some renaming and documentation

* Improve documentation

* More abstract offence report system

* Rename 'consume_evidence' to 'process_evidence'

* Further docs refinements

* Doc for dummy offence report

* Fix rustdoc

* Fix after master merge

* Apply code review suggestions

* Improve docs
This commit is contained in:
Davide Galassi
2023-03-07 21:25:55 +01:00
committed by GitHub
parent e16f15119f
commit 9dd10d131d
18 changed files with 499 additions and 741 deletions
+37 -96
View File
@@ -25,29 +25,25 @@ 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_runtime::{
generic::DigestItem,
traits::{IsMember, One, SaturatedConversion, Saturating, Zero},
ConsensusEngineId, KeyTypeId, Permill,
};
use sp_session::{GetSessionNumber, GetValidatorCount};
use sp_staking::SessionIndex;
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;
use sp_runtime::{
generic::DigestItem,
traits::{IsMember, One, SaturatedConversion, Saturating, Zero},
ConsensusEngineId, Permill,
};
use sp_session::{GetSessionNumber, GetValidatorCount};
use sp_staking::{offence::OffenceReportSystem, SessionIndex};
use sp_std::prelude::*;
pub use sp_consensus_babe::{AuthorityId, PUBLIC_KEY_LENGTH, RANDOMNESS_LENGTH, VRF_OUTPUT_LENGTH};
@@ -64,7 +60,7 @@ mod mock;
#[cfg(all(feature = "std", test))]
mod tests;
pub use equivocation::{BabeEquivocationOffence, EquivocationHandler, HandleEquivocation};
pub use equivocation::{EquivocationOffence, EquivocationReportSystem};
#[allow(deprecated)]
pub use randomness::CurrentBlockRandomness;
pub use randomness::{
@@ -150,35 +146,25 @@ pub mod pallet {
/// initialization.
type DisabledValidators: DisabledValidators;
/// 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: HandleEquivocation<Self>;
/// Helper for weights computations
type WeightInfo: WeightInfo;
/// Max number of authorities allowed
#[pallet::constant]
type MaxAuthorities: Get<u32>;
/// The proof of key ownership, used for validating equivocation reports.
/// The proof must include the session index and validator count of the
/// session at which the equivocation occurred.
type KeyOwnerProof: Parameter + GetSessionNumber + GetValidatorCount;
/// The equivocation handling subsystem, defines methods to check/report an
/// offence and for submitting a transaction to report an equivocation
/// (from an offchain context).
type EquivocationReportSystem: OffenceReportSystem<
Option<Self::AccountId>,
(EquivocationProof<Self::Header>, Self::KeyOwnerProof),
>;
}
#[pallet::error]
@@ -429,8 +415,12 @@ pub mod pallet {
key_owner_proof: T::KeyOwnerProof,
) -> DispatchResultWithPostInfo {
let reporter = ensure_signed(origin)?;
Self::do_report_equivocation(Some(reporter), *equivocation_proof, key_owner_proof)
T::EquivocationReportSystem::process_evidence(
Some(reporter),
(*equivocation_proof, key_owner_proof),
)?;
// Waive the fee since the report is valid and beneficial
Ok(Pays::No.into())
}
/// Report authority equivocation/misbehavior. This method will verify
@@ -451,12 +441,11 @@ pub mod pallet {
key_owner_proof: T::KeyOwnerProof,
) -> DispatchResultWithPostInfo {
ensure_none(origin)?;
Self::do_report_equivocation(
T::HandleEquivocation::block_author(),
*equivocation_proof,
key_owner_proof,
)
T::EquivocationReportSystem::process_evidence(
None,
(*equivocation_proof, key_owner_proof),
)?;
Ok(Pays::No.into())
}
/// Plan an epoch config change. The epoch config change is recorded and will be enacted on
@@ -866,7 +855,7 @@ impl<T: Config> Pallet<T> {
/// This function is only well defined for epochs that actually existed,
/// e.g. if we skipped from epoch 10 to 20 then a call for epoch 15 (which
/// didn't exist) will return an incorrect session index.
fn session_index_for_epoch(epoch_index: u64) -> SessionIndex {
pub(crate) fn session_index_for_epoch(epoch_index: u64) -> SessionIndex {
let skipped_epochs = SkippedEpochs::<T>::get();
match skipped_epochs.binary_search_by_key(&epoch_index, |(epoch_index, _)| *epoch_index) {
// we have an exact match so we just return the given session index
@@ -890,50 +879,6 @@ impl<T: Config> Pallet<T> {
}
}
fn do_report_equivocation(
reporter: Option<T::AccountId>,
equivocation_proof: EquivocationProof<T::Header>,
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::<T>::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::<T>::get()) / T::EpochDuration::get();
// check that the slot number is consistent with the session index
// in the key ownership proof (i.e. slot is for that epoch)
if Self::session_index_for_epoch(epoch_index) != session_index {
return Err(Error::<T>::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::<T>::InvalidKeyOwnershipProof)?;
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)
.map_err(|_| Error::<T>::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
@@ -942,11 +887,7 @@ impl<T: Config> Pallet<T> {
equivocation_proof: EquivocationProof<T::Header>,
key_owner_proof: T::KeyOwnerProof,
) -> Option<()> {
T::HandleEquivocation::submit_unsigned_equivocation_report(
equivocation_proof,
key_owner_proof,
)
.ok()
T::EquivocationReportSystem::publish_evidence((equivocation_proof, key_owner_proof)).ok()
}
}