babe, grandpa: cleanup stale equivocation reports (#8041)

* grandpa: check equivocation report staleness on `validate_unsigned`

* babe: check equivocation report staleness on `validate_unsigned`

* node: bump spec_version

* babe, grandpa: remove duplicate call destructuring
This commit is contained in:
André Silva
2021-02-04 15:22:11 +00:00
committed by GitHub
parent a32f2b9271
commit 6dea5494f3
5 changed files with 81 additions and 49 deletions
+34 -25
View File
@@ -190,7 +190,7 @@ pub struct GrandpaTimeSlot {
impl<T: Config> frame_support::unsigned::ValidateUnsigned for Module<T> {
type Call = Call<T>;
fn validate_unsigned(source: TransactionSource, call: &Self::Call) -> TransactionValidity {
if let Call::report_equivocation_unsigned(equivocation_proof, _) = call {
if let Call::report_equivocation_unsigned(equivocation_proof, key_owner_proof) = call {
// discard equivocation report not coming from the local node
match source {
TransactionSource::Local | TransactionSource::InBlock => { /* allowed */ }
@@ -204,6 +204,9 @@ impl<T: Config> frame_support::unsigned::ValidateUnsigned for Module<T> {
}
}
// check report staleness
is_known_offence::<T>(equivocation_proof, key_owner_proof)?;
ValidTransaction::with_tag_prefix("GrandpaEquivocation")
// We assign the maximum priority for any equivocation report.
.priority(TransactionPriority::max_value())
@@ -223,36 +226,42 @@ impl<T: Config> frame_support::unsigned::ValidateUnsigned for Module<T> {
fn pre_dispatch(call: &Self::Call) -> Result<(), TransactionValidityError> {
if let Call::report_equivocation_unsigned(equivocation_proof, key_owner_proof) = call {
// 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 =
<T::HandleEquivocation as HandleEquivocation<T>>::Offence::new_time_slot(
equivocation_proof.set_id(),
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(())
}
is_known_offence::<T>(equivocation_proof, key_owner_proof)
} else {
Err(InvalidTransaction::Call.into())
}
}
}
fn is_known_offence<T: Config>(
equivocation_proof: &EquivocationProof<T::Hash, T::BlockNumber>,
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 = <T::HandleEquivocation as HandleEquivocation<T>>::Offence::new_time_slot(
equivocation_proof.set_id(),
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.
#[allow(dead_code)]
pub struct GrandpaEquivocationOffence<FullIdentification> {
+9
View File
@@ -775,6 +775,15 @@ fn report_equivocation_validate_unsigned_prevents_duplicates() {
.unwrap();
// the report should now be considered stale and the transaction is invalid
// the check for staleness should be done on both `validate_unsigned` and on `pre_dispatch`
assert_err!(
<Grandpa as sp_runtime::traits::ValidateUnsigned>::validate_unsigned(
TransactionSource::Local,
&call,
),
InvalidTransaction::Stale,
);
assert_err!(
<Grandpa as sp_runtime::traits::ValidateUnsigned>::pre_dispatch(&call),
InvalidTransaction::Stale,