mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-30 08:11:03 +00:00
grandpa: report equivocations (#3868)
* session: runtime api for generating session membership proofs * grandpa: add runtime api for creating equivocation report txs * grandpa: submit signed equivocation report transactions * grandpa: use proper equivocation report type * grandpa: report equivocations * grandpa: validate equivocation proof * grandpa: update to finality-grandpa 0.9.1 * grandpa: fix encoding of session membership proof * grandpa: initialize set id session mapping for genesis session * grandpa: fix bug in set_id session validation * fix compilation * cleanup from merge conflicts * cleanup crate tomls * grandpa: refactor equivocation handling to separate trait * node-template: fix compilation * fix test compilation * bump finality-grandpa to v0.10.2 * rpc: fix runtime version test * CHERRY-PICK #4200: Add documentation to SubmitSignedTransaction and actually make it work Squashed commit of the following: commit 4f2cb0b1c588a06f2f3b478bb4b28b5cb29d54b9 Author: Tomasz Drwięga <tomasz@parity.io> Date: Tue Dec 3 16:29:33 2019 +0100 Split the method to avoid confusing type error message. commit c5bf24eeaaf902add89ed1b046b22c4a4aaeb2cd Author: Tomasz Drwięga <tomasz@parity.io> Date: Tue Dec 3 16:19:55 2019 +0100 Make accounts optional, fix logic. commit 97db1ef556e023cf6847e5ffdb036c0e3ea6fb0a Author: Tomasz Drwięga <tomasz@parity.io> Date: Tue Dec 3 10:06:20 2019 +0100 Remove warning. commit 535f5c116d1a2e826eaf90c3f7e6798e443d61d8 Merge: 5162572170f1a5f651Author: Tomasz Drwięga <tomasz@parity.io> Date: Tue Dec 3 07:08:05 2019 +0100 Merge branch 'master' into td-signed-transactions commit 516257217bac89fcebd083712f4ea68b7b23b55a Merge: ac98248c62e68c80c2Author: Tomasz Drwięga <tomasz@parity.io> Date: Mon Dec 2 13:57:25 2019 +0100 Merge branch 'master' into td-signed-transactions commit ac98248c6c56cff381130645a82a13d29933cf83 Author: Tomasz Drwięga <tomasz@parity.io> Date: Mon Nov 25 17:34:52 2019 +0100 Forgotten import. commit 67a3c19031506c28e31c6bc4a90fff62d467dd58 Author: Tomasz Drwięga <tomasz@parity.io> Date: Mon Nov 25 17:32:10 2019 +0100 Fix naming and bounds. commit 93e768ea9df97a4629fca1f9bc4b108fdb33f876 Author: Tomasz Drwięga <tomasz@parity.io> Date: Mon Nov 25 17:01:05 2019 +0100 Add documentation to signed transactions and actually make them work. * grandpa: skip block initialization on report submission method * primitives: allow transaction pool access by default for offchain calls * grandpa: unused parameters * grandpa: remove unused method * grandpa: enable equivocation reporting * grandpa: add workaround for parameter encoding * grandpa: fix localized_payload calls in tests * fix submit_report_equivocation_extrinsic in runtimes * node: fix submit transaction test compilation * node: bump spec_version * rpc: fix api version test * grandpa: allow custom equivocation offence type * grandpa: add test for authorities::next_change_height * grandpa: cleanup report_equivocation function * node: move reporting app crypto to node-primitives * grandpa: move equivocation traits to own module * grandpa: rename app-crypto crate import * grandpa: export equivocation types * node: bump spec_version * grandpa: rename EquivocationReport to EquivocationProof * grandpa: add missing docs to primitives * grandpa: add missing docs to equivocation * node: fix compilation * grandpa: add missing docs to pallet * node: bump spec_version * fix whitespace * grandpa: return error on offence reporting * grandpa: expose session and validator count in proofs through traits * grandpa: use strong key in module KeyOwnerProofSystem * grandpa: move key ownership proof to grandpa runtime api * grandpa: remove unnecessary cloning when checking equivocation proof * grandpa: make report_equivocation a method in Environment * support: implement KeyOwnerProofSystem for () * grandpa: move KeyOwnerProofSystem to module trait * test-utils: fix runtime compilation * grandpa: fix test compilation * grandpa: fix test compilation after merge * grandpa: simplify transaction submission types * grandpa: validate equivocation report in signed extension * client: fix test * node: use ValidateEquivocationReport signed extension * grandpa: expose key ownership proof under opaque type * grandpa: better docs on key ownership proofs * grandpa: add note about signed extension * grandpa: add ValidateEquivocationReport::new * grandpa: remove skip_initialize_block from runtime api * grandpa: use new offchain transaction submission API * grandpa: take set_id in generate_key_ownership_proof * grandpa: update to finality-grandpa v0.12.2 * grandpa: cleanup usages of AuthoritySet::current * grandpa: fix test * grandpa: add mocking utilities for equivocation reporting * grandpa: add test for equivocation reporting * grandpa: move SetIdSession initialization * grandpa: add more tests * node: enable historical session manager * node: bump spec_version * node: use strong key types in KeyOwnerProofSystem definitions * grandpa: export GrandpaEquivocationOffence type
This commit is contained in:
@@ -31,27 +31,65 @@
|
||||
pub use sp_finality_grandpa as fg_primitives;
|
||||
|
||||
use sp_std::prelude::*;
|
||||
use codec::{self as codec, Encode, Decode};
|
||||
use frame_support::{decl_event, decl_storage, decl_module, decl_error, storage};
|
||||
use sp_runtime::{
|
||||
DispatchResult, generic::{DigestItem, OpaqueDigestItemId}, traits::Zero, Perbill,
|
||||
};
|
||||
use sp_staking::{
|
||||
SessionIndex,
|
||||
offence::{Offence, Kind},
|
||||
};
|
||||
use fg_primitives::{
|
||||
GRANDPA_AUTHORITIES_KEY, GRANDPA_ENGINE_ID, ScheduledChange, ConsensusLog, SetId, RoundNumber,
|
||||
};
|
||||
pub use fg_primitives::{AuthorityId, AuthorityList, AuthorityWeight, VersionedAuthorityList};
|
||||
use frame_system::{self as system, ensure_signed, DigestOf};
|
||||
|
||||
use codec::{self as codec, Decode, Encode};
|
||||
pub use fg_primitives::{AuthorityId, AuthorityList, AuthorityWeight, VersionedAuthorityList};
|
||||
use fg_primitives::{
|
||||
ConsensusLog, EquivocationProof, ScheduledChange, SetId, GRANDPA_AUTHORITIES_KEY,
|
||||
GRANDPA_ENGINE_ID,
|
||||
};
|
||||
use frame_support::{
|
||||
decl_error, decl_event, decl_module, decl_storage, storage, traits::KeyOwnerProofSystem,
|
||||
Parameter,
|
||||
};
|
||||
use frame_system::{self as system, ensure_signed, DigestOf};
|
||||
use sp_runtime::{
|
||||
generic::{DigestItem, OpaqueDigestItemId},
|
||||
traits::Zero,
|
||||
DispatchResult, KeyTypeId,
|
||||
};
|
||||
use sp_staking::SessionIndex;
|
||||
|
||||
mod equivocation;
|
||||
mod mock;
|
||||
mod tests;
|
||||
|
||||
pub use equivocation::{
|
||||
EquivocationHandler, GetSessionNumber, GetValidatorCount, GrandpaEquivocationOffence,
|
||||
GrandpaOffence, GrandpaTimeSlot, HandleEquivocation, ValidateEquivocationReport,
|
||||
};
|
||||
|
||||
pub trait Trait: frame_system::Trait {
|
||||
/// The event type of this module.
|
||||
type Event: From<Event> + Into<<Self as frame_system::Trait>::Event>;
|
||||
|
||||
/// The function call.
|
||||
type Call: From<Call<Self>>;
|
||||
|
||||
/// 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 add the `equivocation::ValidateEquivocationReport` signed
|
||||
/// extension to the runtime's `SignedExtra` definition, otherwise
|
||||
/// equivocation reports won't be properly validated.
|
||||
type HandleEquivocation: HandleEquivocation<Self>;
|
||||
}
|
||||
|
||||
/// A stored pending change, old format.
|
||||
@@ -173,7 +211,9 @@ decl_storage! {
|
||||
}
|
||||
add_extra_genesis {
|
||||
config(authorities): AuthorityList;
|
||||
build(|config| Module::<T>::initialize_authorities(&config.authorities))
|
||||
build(|config| {
|
||||
Module::<T>::initialize(&config.authorities)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -183,11 +223,49 @@ decl_module! {
|
||||
|
||||
fn deposit_event() = default;
|
||||
|
||||
/// Report some misbehavior.
|
||||
/// Report voter equivocation/misbehavior. This method will verify the
|
||||
/// equivocation proof and validate the given key ownership proof
|
||||
/// against the extracted offender. If both are valid, the offence
|
||||
/// will be reported.
|
||||
///
|
||||
/// Since the weight is 0 in order to avoid DoS pre-validation is implemented in a
|
||||
/// `SignedExtension`.
|
||||
#[weight = 0]
|
||||
fn report_misbehavior(origin, _report: Vec<u8>) {
|
||||
ensure_signed(origin)?;
|
||||
// FIXME: https://github.com/paritytech/substrate/issues/1112
|
||||
fn report_equivocation(
|
||||
origin,
|
||||
equivocation_proof: EquivocationProof<T::Hash, T::BlockNumber>,
|
||||
key_owner_proof: T::KeyOwnerProof,
|
||||
) {
|
||||
let reporter_id = ensure_signed(origin)?;
|
||||
|
||||
let (session_index, validator_set_count) = (
|
||||
key_owner_proof.session(),
|
||||
key_owner_proof.validator_count(),
|
||||
);
|
||||
|
||||
// we have already checked this proof in `SignedExtension`, we to
|
||||
// check it again to get the full identification of the offender.
|
||||
let offender =
|
||||
T::KeyOwnerProofSystem::check_proof(
|
||||
(fg_primitives::KEY_TYPE, equivocation_proof.offender().clone()),
|
||||
key_owner_proof,
|
||||
).ok_or("Invalid key ownership proof.")?;
|
||||
|
||||
// the set id and round when the offence happened
|
||||
let set_id = equivocation_proof.set_id();
|
||||
let round = equivocation_proof.round();
|
||||
|
||||
// report to the offences module rewarding the sender.
|
||||
T::HandleEquivocation::report_offence(
|
||||
vec![reporter_id],
|
||||
<T::HandleEquivocation as HandleEquivocation<T>>::Offence::new(
|
||||
session_index,
|
||||
validator_set_count,
|
||||
offender,
|
||||
set_id,
|
||||
round,
|
||||
),
|
||||
).map_err(|_| "Duplicate offence report.")?;
|
||||
}
|
||||
|
||||
fn on_finalize(block_number: T::BlockNumber) {
|
||||
@@ -351,7 +429,9 @@ impl<T: Trait> Module<T> {
|
||||
<frame_system::Module<T>>::deposit_log(log.into());
|
||||
}
|
||||
|
||||
fn initialize_authorities(authorities: &AuthorityList) {
|
||||
// Perform module initialization, abstracted so that it can be called either through genesis
|
||||
// config builder or through `on_genesis_session`.
|
||||
fn initialize(authorities: &AuthorityList) {
|
||||
if !authorities.is_empty() {
|
||||
assert!(
|
||||
Self::grandpa_authorities().is_empty(),
|
||||
@@ -359,6 +439,25 @@ impl<T: Trait> Module<T> {
|
||||
);
|
||||
Self::set_grandpa_authorities(authorities);
|
||||
}
|
||||
|
||||
// NOTE: initialize first session of first set. this is necessary
|
||||
// because we only update this `on_new_session` which isn't called
|
||||
// for the genesis session.
|
||||
SetIdSession::insert(0, 0);
|
||||
}
|
||||
|
||||
/// Submits an extrinsic to report an equivocation. This method will sign an
|
||||
/// extrinsic with a call to `report_equivocation` with any reporting keys
|
||||
/// available in the keystore and will push the transaction to the pool.
|
||||
/// Only useful in an offchain context.
|
||||
pub fn submit_report_equivocation_extrinsic(
|
||||
equivocation_proof: EquivocationProof<T::Hash, T::BlockNumber>,
|
||||
key_owner_proof: T::KeyOwnerProof,
|
||||
) -> Option<()> {
|
||||
T::HandleEquivocation::submit_equivocation_report(equivocation_proof, key_owner_proof)
|
||||
.ok()?;
|
||||
|
||||
Some(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -411,7 +510,7 @@ impl<T: Trait> pallet_session::OneSessionHandler<T::AccountId> for Module<T>
|
||||
where I: Iterator<Item=(&'a T::AccountId, AuthorityId)>
|
||||
{
|
||||
let authorities = validators.map(|(_, k)| (k, 1)).collect::<Vec<_>>();
|
||||
Self::initialize_authorities(&authorities);
|
||||
Self::initialize(&authorities);
|
||||
}
|
||||
|
||||
fn on_new_session<'a, I: 'a>(changed: bool, validators: I, _queued_validators: I)
|
||||
@@ -453,56 +552,3 @@ impl<T: Trait> pallet_finality_tracker::OnFinalizationStalled<T::BlockNumber> fo
|
||||
<Stalled<T>>::put((further_wait, median));
|
||||
}
|
||||
}
|
||||
|
||||
/// 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 GrandpaEquivocationOffence<FullIdentification> {
|
||||
/// 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_set_count: u32,
|
||||
/// The authority which produced this equivocation.
|
||||
pub offender: FullIdentification,
|
||||
}
|
||||
|
||||
impl<FullIdentification: Clone> Offence<FullIdentification> for GrandpaEquivocationOffence<FullIdentification> {
|
||||
const ID: Kind = *b"grandpa:equivoca";
|
||||
type TimeSlot = GrandpaTimeSlot;
|
||||
|
||||
fn offenders(&self) -> Vec<FullIdentification> {
|
||||
vec![self.offender.clone()]
|
||||
}
|
||||
|
||||
fn session_index(&self) -> SessionIndex {
|
||||
self.session_index
|
||||
}
|
||||
|
||||
fn validator_set_count(&self) -> u32 {
|
||||
self.validator_set_count
|
||||
}
|
||||
|
||||
fn time_slot(&self) -> Self::TimeSlot {
|
||||
self.time_slot
|
||||
}
|
||||
|
||||
fn slash_fraction(
|
||||
offenders_count: u32,
|
||||
validator_set_count: u32,
|
||||
) -> Perbill {
|
||||
// the formula is min((3k / n)^2, 1)
|
||||
let x = Perbill::from_rational_approximation(3 * offenders_count, validator_set_count);
|
||||
// _ ^ 2
|
||||
x.square()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user