Skip to content

Commit c060d88

Browse files
authored
grandpa: allow noting that the set has stalled (#6725)
* grandpa: remove unused methods to convert digest * grandpa: add root extrinsic for scheduling forced change * grandpa: add benchmark for schedule_forced_change * grandpa: don't take authority weight in schedule_forced_change * grandpa: add const for default forced change delay * grandpa: adjust weights after benchmark on ref hardware * grandpa: fix cleanup of forced changes on standard change application * grandpa: replace schedule_forced_change with note_stalled * grandpa: always trigger a session change when the set is stalled * grandpa: fix bug on set id mutation after failed scheduled change * grandpa: take delay as parameter in note_stalled * grandpa: fix tests * grandpa: fix cleanup of forced changes * grandpa: add test for forced changes cleanup * grandpa: add test for session rotation set id * grandpa: add test for scheduling of forced changes on new session
1 parent a5c319b commit c060d88

File tree

4 files changed

+169
-84
lines changed

4 files changed

+169
-84
lines changed

src/benchmarking.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,9 @@
1919
2020
#![cfg_attr(not(feature = "std"), no_std)]
2121

22-
use super::*;
22+
use super::{*, Module as Grandpa};
2323
use frame_benchmarking::benchmarks;
24+
use frame_system::RawOrigin;
2425
use sp_core::H256;
2526

2627
benchmarks! {
@@ -62,6 +63,15 @@ benchmarks! {
6263
} verify {
6364
assert!(sp_finality_grandpa::check_equivocation_proof(equivocation_proof2));
6465
}
66+
67+
note_stalled {
68+
let delay = 1000.into();
69+
let best_finalized_block_number = 1.into();
70+
71+
}: _(RawOrigin::Root, delay, best_finalized_block_number)
72+
verify {
73+
assert!(Grandpa::<T>::stalled().is_some());
74+
}
6575
}
6676

6777
#[cfg(test)]
@@ -74,6 +84,7 @@ mod tests {
7484
fn test_benchmarks() {
7585
new_test_ext(vec![(1, 1), (2, 1), (3, 1)]).execute_with(|| {
7686
assert_ok!(test_benchmark_check_equivocation_proof::<Test>());
87+
assert_ok!(test_benchmark_note_stalled::<Test>());
7788
})
7889
}
7990

src/lib.rs

Lines changed: 46 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,10 @@ use frame_support::{
4343
decl_error, decl_event, decl_module, decl_storage, storage, traits::KeyOwnerProofSystem,
4444
Parameter,
4545
};
46-
use frame_system::{ensure_none, ensure_signed, DigestOf};
46+
use frame_system::{ensure_none, ensure_root, ensure_signed};
47+
use pallet_finality_tracker::OnFinalizationStalled;
4748
use sp_runtime::{
48-
generic::{DigestItem, OpaqueDigestItemId},
49+
generic::DigestItem,
4950
traits::Zero,
5051
DispatchResult, KeyTypeId,
5152
};
@@ -205,7 +206,7 @@ decl_storage! {
205206
State get(fn state): StoredState<T::BlockNumber> = StoredState::Live;
206207

207208
/// Pending change: (signaled at, scheduled change).
208-
PendingChange: Option<StoredPendingChange<T::BlockNumber>>;
209+
PendingChange get(fn pending_change): Option<StoredPendingChange<T::BlockNumber>>;
209210

210211
/// next block number where we can force a change.
211212
NextForced get(fn next_forced): Option<T::BlockNumber>;
@@ -280,6 +281,24 @@ decl_module! {
280281
)?;
281282
}
282283

284+
/// Note that the current authority set of the GRANDPA finality gadget has
285+
/// stalled. This will trigger a forced authority set change at the beginning
286+
/// of the next session, to be enacted `delay` blocks after that. The delay
287+
/// should be high enough to safely assume that the block signalling the
288+
/// forced change will not be re-orged (e.g. 1000 blocks). The GRANDPA voters
289+
/// will start the new authority set using the given finalized block as base.
290+
/// Only callable by root.
291+
#[weight = weight_for::note_stalled::<T>()]
292+
fn note_stalled(
293+
origin,
294+
delay: T::BlockNumber,
295+
best_finalized_block_number: T::BlockNumber,
296+
) {
297+
ensure_root(origin)?;
298+
299+
Self::on_stalled(delay, best_finalized_block_number)
300+
}
301+
283302
fn on_finalize(block_number: T::BlockNumber) {
284303
// check for scheduled pending authority set changes
285304
if let Some(pending_change) = <PendingChange<T>>::get() {
@@ -295,7 +314,7 @@ decl_module! {
295314
))
296315
} else {
297316
Self::deposit_log(ConsensusLog::ScheduledChange(
298-
ScheduledChange{
317+
ScheduledChange {
299318
delay: pending_change.delay,
300319
next_authorities: pending_change.next_authorities.clone(),
301320
}
@@ -377,6 +396,11 @@ mod weight_for {
377396
// fetching set id -> session index mappings
378397
.saturating_add(T::DbWeight::get().reads(2))
379398
}
399+
400+
pub fn note_stalled<T: super::Trait>() -> Weight {
401+
(3 * WEIGHT_PER_MICROS)
402+
.saturating_add(T::DbWeight::get().writes(1))
403+
}
380404
}
381405

382406
impl<T: Trait> Module<T> {
@@ -580,42 +604,6 @@ impl<T: Trait> Module<T> {
580604
}
581605
}
582606

583-
impl<T: Trait> Module<T> {
584-
/// Attempt to extract a GRANDPA log from a generic digest.
585-
pub fn grandpa_log(digest: &DigestOf<T>) -> Option<ConsensusLog<T::BlockNumber>> {
586-
let id = OpaqueDigestItemId::Consensus(&GRANDPA_ENGINE_ID);
587-
digest.convert_first(|l| l.try_to::<ConsensusLog<T::BlockNumber>>(id))
588-
}
589-
590-
/// Attempt to extract a pending set-change signal from a digest.
591-
pub fn pending_change(digest: &DigestOf<T>)
592-
-> Option<ScheduledChange<T::BlockNumber>>
593-
{
594-
Self::grandpa_log(digest).and_then(|signal| signal.try_into_change())
595-
}
596-
597-
/// Attempt to extract a forced set-change signal from a digest.
598-
pub fn forced_change(digest: &DigestOf<T>)
599-
-> Option<(T::BlockNumber, ScheduledChange<T::BlockNumber>)>
600-
{
601-
Self::grandpa_log(digest).and_then(|signal| signal.try_into_forced_change())
602-
}
603-
604-
/// Attempt to extract a pause signal from a digest.
605-
pub fn pending_pause(digest: &DigestOf<T>)
606-
-> Option<T::BlockNumber>
607-
{
608-
Self::grandpa_log(digest).and_then(|signal| signal.try_into_pause())
609-
}
610-
611-
/// Attempt to extract a resume signal from a digest.
612-
pub fn pending_resume(digest: &DigestOf<T>)
613-
-> Option<T::BlockNumber>
614-
{
615-
Self::grandpa_log(digest).and_then(|signal| signal.try_into_resume())
616-
}
617-
}
618-
619607
impl<T: Trait> sp_runtime::BoundToRuntimeAppPublic for Module<T> {
620608
type Public = AuthorityId;
621609
}
@@ -638,14 +626,26 @@ impl<T: Trait> pallet_session::OneSessionHandler<T::AccountId> for Module<T>
638626
// Always issue a change if `session` says that the validators have changed.
639627
// Even if their session keys are the same as before, the underlying economic
640628
// identities have changed.
641-
let current_set_id = if changed {
629+
let current_set_id = if changed || <Stalled<T>>::exists() {
642630
let next_authorities = validators.map(|(_, k)| (k, 1)).collect::<Vec<_>>();
643-
if let Some((further_wait, median)) = <Stalled<T>>::take() {
644-
let _ = Self::schedule_change(next_authorities, further_wait, Some(median));
631+
632+
let res = if let Some((further_wait, median)) = <Stalled<T>>::take() {
633+
Self::schedule_change(next_authorities, further_wait, Some(median))
634+
} else {
635+
Self::schedule_change(next_authorities, Zero::zero(), None)
636+
};
637+
638+
if res.is_ok() {
639+
CurrentSetId::mutate(|s| {
640+
*s += 1;
641+
*s
642+
})
645643
} else {
646-
let _ = Self::schedule_change(next_authorities, Zero::zero(), None);
644+
// either the session module signalled that the validators have changed
645+
// or the set was stalled. but since we didn't successfully schedule
646+
// an authority set change we do not increment the set id.
647+
Self::current_set_id()
647648
}
648-
CurrentSetId::mutate(|s| { *s += 1; *s })
649649
} else {
650650
// nothing's changed, neither economic conditions nor session keys. update the pointer
651651
// of the current set.
@@ -663,7 +663,7 @@ impl<T: Trait> pallet_session::OneSessionHandler<T::AccountId> for Module<T>
663663
}
664664
}
665665

666-
impl<T: Trait> pallet_finality_tracker::OnFinalizationStalled<T::BlockNumber> for Module<T> {
666+
impl<T: Trait> OnFinalizationStalled<T::BlockNumber> for Module<T> {
667667
fn on_stalled(further_wait: T::BlockNumber, median: T::BlockNumber) {
668668
// when we record old authority sets, we can use `pallet_finality_tracker::median`
669669
// to figure out _who_ failed. until then, we can't meaningfully guard

src/mock.rs

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -365,23 +365,18 @@ pub fn new_test_ext_raw_authorities(authorities: AuthorityList) -> sp_io::TestEx
365365
}
366366

367367
pub fn start_session(session_index: SessionIndex) {
368-
let mut parent_hash = System::parent_hash();
369-
370368
for i in Session::current_index()..session_index {
369+
System::on_finalize(System::block_number());
370+
Session::on_finalize(System::block_number());
371371
Staking::on_finalize(System::block_number());
372-
System::set_block_number((i + 1).into());
373-
Timestamp::set_timestamp(System::block_number() * 6000);
372+
Grandpa::on_finalize(System::block_number());
374373

375-
// In order to be able to use `System::parent_hash()` in the tests
376-
// we need to first get it via `System::finalize` and then set it
377-
// the `System::initialize`. However, it is needed to be taken into
378-
// consideration that finalizing will prune some data in `System`
379-
// storage including old values `BlockHash` if that reaches above
380-
// `BlockHashCount` capacity.
381-
if System::block_number() > 1 {
374+
let parent_hash = if System::block_number() > 1 {
382375
let hdr = System::finalize();
383-
parent_hash = hdr.hash();
384-
}
376+
hdr.hash()
377+
} else {
378+
System::parent_hash()
379+
};
385380

386381
System::initialize(
387382
&(i as u64 + 1),
@@ -390,9 +385,13 @@ pub fn start_session(session_index: SessionIndex) {
390385
&Default::default(),
391386
Default::default(),
392387
);
388+
System::set_block_number((i + 1).into());
389+
Timestamp::set_timestamp(System::block_number() * 6000);
393390

394-
Session::on_initialize(System::block_number());
395391
System::on_initialize(System::block_number());
392+
Session::on_initialize(System::block_number());
393+
Staking::on_initialize(System::block_number());
394+
Grandpa::on_initialize(System::block_number());
396395
}
397396

398397
assert_eq!(Session::current_index(), session_index);

0 commit comments

Comments
 (0)