babe: allow skipping over empty epochs (#11727)

* babe: allow skipping epochs in pallet

* babe: detect and skip epochs on client

* babe: cleaner epoch util functions

* babe: add test for runtime handling of skipped epochs

* babe: simpler implementation of client handling of skipped epochs

* babe: test client-side handling of skipped epochs

* babe: add comments on client-side skipped epochs

* babe: remove emptyline

* babe: make it resilient to forks

* babe: typo

* babe: overflow-safe math

* babe: add test for skipping epochs across different forks

* Fix tests

* FMT

Co-authored-by: Bastian Köcher <info@kchr.de>
This commit is contained in:
André Silva
2022-12-24 00:03:31 +00:00
committed by GitHub
parent e053d34cc5
commit 017cf70378
5 changed files with 386 additions and 23 deletions
+29 -20
View File
@@ -588,10 +588,19 @@ impl<T: Config> Pallet<T> {
return
}
// Update epoch index
let epoch_index = EpochIndex::<T>::get()
.checked_add(1)
.expect("epoch indices will never reach 2^64 before the death of the universe; qed");
// Update epoch index.
//
// NOTE: we figure out the epoch index from the slot, which may not
// necessarily be contiguous if the chain was offline for more than
// `T::EpochDuration` slots. When skipping from epoch N to e.g. N+4, we
// will be using the randomness and authorities for that epoch that had
// been previously announced for epoch N+1, and the randomness collected
// during the current epoch (N) will be used for epoch N+5.
let epoch_index = sp_consensus_babe::epoch_index(
CurrentSlot::<T>::get(),
GenesisSlot::<T>::get(),
T::EpochDuration::get(),
);
EpochIndex::<T>::put(epoch_index);
Authorities::<T>::put(authorities);
@@ -638,11 +647,16 @@ impl<T: Config> Pallet<T> {
}
}
/// Finds the start slot of the current epoch. only guaranteed to
/// give correct results after `initialize` of the first block
/// in the chain (as its result is based off of `GenesisSlot`).
/// Finds the start slot of the current epoch.
///
/// Only guaranteed to give correct results after `initialize` of the first
/// block in the chain (as its result is based off of `GenesisSlot`).
pub fn current_epoch_start() -> Slot {
Self::epoch_start(EpochIndex::<T>::get())
sp_consensus_babe::epoch_start_slot(
EpochIndex::<T>::get(),
GenesisSlot::<T>::get(),
T::EpochDuration::get(),
)
}
/// Produces information about the current epoch.
@@ -666,9 +680,15 @@ impl<T: Config> Pallet<T> {
if u64 is not enough we should crash for safety; qed.",
);
let start_slot = sp_consensus_babe::epoch_start_slot(
next_epoch_index,
GenesisSlot::<T>::get(),
T::EpochDuration::get(),
);
Epoch {
epoch_index: next_epoch_index,
start_slot: Self::epoch_start(next_epoch_index),
start_slot,
duration: T::EpochDuration::get(),
authorities: NextAuthorities::<T>::get().to_vec(),
randomness: NextRandomness::<T>::get(),
@@ -680,17 +700,6 @@ impl<T: Config> Pallet<T> {
}
}
fn epoch_start(epoch_index: u64) -> Slot {
// (epoch_index * epoch_duration) + genesis_slot
const PROOF: &str = "slot number is u64; it should relate in some way to wall clock time; \
if u64 is not enough we should crash for safety; qed.";
let epoch_start = epoch_index.checked_mul(T::EpochDuration::get()).expect(PROOF);
epoch_start.checked_add(*GenesisSlot::<T>::get()).expect(PROOF).into()
}
fn deposit_consensus<U: Encode>(new: U) {
let log = DigestItem::Consensus(BABE_ENGINE_ID, new.encode());
<frame_system::Pallet<T>>::deposit_log(log)
+31
View File
@@ -948,3 +948,34 @@ fn generate_equivocation_report_blob() {
println!("equivocation_proof.encode(): {:?}", equivocation_proof.encode());
});
}
#[test]
fn skipping_over_epochs_works() {
let mut ext = new_test_ext(3);
ext.execute_with(|| {
let epoch_duration: u64 = <Test as Config>::EpochDuration::get();
// this sets the genesis slot to 100;
let genesis_slot = 100;
go_to_block(1, genesis_slot);
// we will author all blocks from epoch #0 and arrive at a point where
// we are in epoch #1. we should already have the randomness ready that
// will be used in epoch #2
progress_to_block(epoch_duration + 1);
assert_eq!(EpochIndex::<Test>::get(), 1);
// genesis randomness is an array of zeros
let randomness_for_epoch_2 = NextRandomness::<Test>::get();
assert!(randomness_for_epoch_2 != [0; 32]);
// we will now create a block for a slot that is part of epoch #4.
// we should appropriately increment the epoch index as well as re-use
// the randomness from epoch #2 on epoch #4
go_to_block(System::block_number() + 1, genesis_slot + epoch_duration * 4);
assert_eq!(EpochIndex::<Test>::get(), 4);
assert_eq!(Randomness::<Test>::get(), randomness_for_epoch_2);
});
}