Introduce a Slot type (#7997)

* Introduce a `Slot` type

Instead of having some type definition that only was used in half of the
code or directly using `u64`, this adds a new unit type wrapper `Slot`.
This makes it especially easy for the outside api to know what type is
expected/returned.

* Change epoch duratioC

* rename all instances of slot number to slot

* Make the constructor private

Co-authored-by: André Silva <andrerfosilva@gmail.com>
This commit is contained in:
Bastian Köcher
2021-01-28 20:44:22 +01:00
committed by GitHub
parent 6c2dd28dfb
commit b6294418f8
34 changed files with 549 additions and 445 deletions
+64 -58
View File
@@ -41,6 +41,7 @@ use parking_lot::Mutex;
use sp_api::{ProvideRuntimeApi, ApiRef};
use sp_arithmetic::traits::BaseArithmetic;
use sp_consensus::{BlockImport, Proposer, SyncOracle, SelectChain, CanAuthorWith, SlotData, RecordProof};
use sp_consensus_slots::Slot;
use sp_inherents::{InherentData, InherentDataProviders};
use sp_runtime::{
generic::BlockId,
@@ -115,7 +116,7 @@ pub trait SimpleSlotWorker<B: BlockT> {
fn epoch_data(
&self,
header: &B::Header,
slot_number: u64,
slot: Slot,
) -> Result<Self::EpochData, sp_consensus::Error>;
/// Returns the number of authorities given the epoch data.
@@ -126,7 +127,7 @@ pub trait SimpleSlotWorker<B: BlockT> {
fn claim_slot(
&self,
header: &B::Header,
slot_number: u64,
slot: Slot,
epoch_data: &Self::EpochData,
) -> Option<Self::Claim>;
@@ -135,14 +136,14 @@ pub trait SimpleSlotWorker<B: BlockT> {
fn notify_slot(
&self,
_header: &B::Header,
_slot_number: u64,
_slot: Slot,
_epoch_data: &Self::EpochData,
) {}
/// Return the pre digest data to include in a block authored with the given claim.
fn pre_digest_data(
&self,
slot_number: u64,
slot: Slot,
claim: &Self::Claim,
) -> Vec<sp_runtime::DigestItem<B::Hash>>;
@@ -170,7 +171,7 @@ pub trait SimpleSlotWorker<B: BlockT> {
///
/// An example strategy that back offs if the finalized head is lagging too much behind the tip
/// is implemented by [`BackoffAuthoringOnFinalizedHeadLagging`].
fn should_backoff(&self, _slot_number: u64, _chain_head: &B::Header) -> bool {
fn should_backoff(&self, _slot: Slot, _chain_head: &B::Header) -> bool {
false
}
@@ -208,7 +209,7 @@ pub trait SimpleSlotWorker<B: BlockT> {
where
<Self::Proposer as Proposer<B>>::Proposal: Unpin + Send + 'static,
{
let (timestamp, slot_number) = (slot_info.timestamp, slot_info.number);
let (timestamp, slot) = (slot_info.timestamp, slot_info.slot);
let slot_remaining_duration = self.slot_remaining_duration(&slot_info);
let proposing_remaining_duration = self.proposing_remaining_duration(&chain_head, &slot_info);
@@ -218,7 +219,7 @@ pub trait SimpleSlotWorker<B: BlockT> {
debug!(
target: self.logging_target(),
"Skipping proposal slot {} since there's no time left to propose",
slot_number,
slot,
);
return Box::pin(future::ready(None));
@@ -227,7 +228,7 @@ pub trait SimpleSlotWorker<B: BlockT> {
None => Box::new(future::pending()) as Box<_>,
};
let epoch_data = match self.epoch_data(&chain_head, slot_number) {
let epoch_data = match self.epoch_data(&chain_head, slot) {
Ok(epoch_data) => epoch_data,
Err(err) => {
warn!("Unable to fetch epoch data at block {:?}: {:?}", chain_head.hash(), err);
@@ -242,7 +243,7 @@ pub trait SimpleSlotWorker<B: BlockT> {
}
};
self.notify_slot(&chain_head, slot_number, &epoch_data);
self.notify_slot(&chain_head, slot, &epoch_data);
let authorities_len = self.authorities_len(&epoch_data);
@@ -260,38 +261,43 @@ pub trait SimpleSlotWorker<B: BlockT> {
return Box::pin(future::ready(None));
}
let claim = match self.claim_slot(&chain_head, slot_number, &epoch_data) {
let claim = match self.claim_slot(&chain_head, slot, &epoch_data) {
None => return Box::pin(future::ready(None)),
Some(claim) => claim,
};
if self.should_backoff(slot_number, &chain_head) {
if self.should_backoff(slot, &chain_head) {
return Box::pin(future::ready(None));
}
debug!(
target: self.logging_target(),
"Starting authorship at slot {}; timestamp = {}",
slot_number,
slot,
timestamp,
);
telemetry!(CONSENSUS_DEBUG; "slots.starting_authorship";
"slot_num" => slot_number,
telemetry!(
CONSENSUS_DEBUG;
"slots.starting_authorship";
"slot_num" => *slot,
"timestamp" => timestamp,
);
let awaiting_proposer = self.proposer(&chain_head).map_err(move |err| {
warn!("Unable to author block in slot {:?}: {:?}", slot_number, err);
warn!("Unable to author block in slot {:?}: {:?}", slot, err);
telemetry!(CONSENSUS_WARN; "slots.unable_authoring_block";
"slot" => slot_number, "err" => ?err
telemetry!(
CONSENSUS_WARN;
"slots.unable_authoring_block";
"slot" => *slot,
"err" => ?err
);
err
});
let logs = self.pre_digest_data(slot_number, &claim);
let logs = self.pre_digest_data(slot, &claim);
// deadline our production to approx. the end of the slot
let proposing = awaiting_proposer.and_then(move |proposer| proposer.propose(
@@ -307,12 +313,14 @@ pub trait SimpleSlotWorker<B: BlockT> {
futures::future::select(proposing, proposing_remaining).map(move |v| match v {
Either::Left((b, _)) => b.map(|b| (b, claim)),
Either::Right(_) => {
info!("⌛️ Discarding proposal for slot {}; block production took too long", slot_number);
info!("⌛️ Discarding proposal for slot {}; block production took too long", slot);
// If the node was compiled with debug, tell the user to use release optimizations.
#[cfg(build_type="debug")]
info!("👉 Recompile your node in `--release` mode to mitigate this problem.");
telemetry!(CONSENSUS_INFO; "slots.discarding_proposal_took_too_long";
"slot" => slot_number,
telemetry!(
CONSENSUS_INFO;
"slots.discarding_proposal_took_too_long";
"slot" => *slot,
);
Err(sp_consensus::Error::ClientImport("Timeout in the Slots proposer".into()))
@@ -388,7 +396,7 @@ pub trait SlotCompatible {
fn extract_timestamp_and_slot(
&self,
inherent: &InherentData,
) -> Result<(u64, u64, std::time::Duration), sp_consensus::Error>;
) -> Result<(u64, Slot, std::time::Duration), sp_consensus::Error>;
}
/// Start a new slot worker.
@@ -429,12 +437,12 @@ where
return Either::Right(future::ready(Ok(())));
}
let slot_num = slot_info.number;
let slot = slot_info.slot;
let chain_head = match client.best_chain() {
Ok(x) => x,
Err(e) => {
warn!(target: "slots", "Unable to author block in slot {}. \
no best block header: {:?}", slot_num, e);
no best block header: {:?}", slot, e);
return Either::Right(future::ready(Ok(())));
}
};
@@ -444,7 +452,7 @@ where
target: "slots",
"Unable to author block in slot {},. `can_author_with` returned: {} \
Probably a node update is required!",
slot_num,
slot,
err,
);
Either::Right(future::ready(Ok(())))
@@ -465,7 +473,7 @@ where
pub enum CheckedHeader<H, S> {
/// A header which has slot in the future. this is the full header (not stripped)
/// and the slot in which it should be processed.
Deferred(H, u64),
Deferred(H, Slot),
/// A header which is fully checked, including signature. This is the pre-header
/// accompanied by the seal components.
///
@@ -473,8 +481,6 @@ pub enum CheckedHeader<H, S> {
Checked(H, S),
}
#[derive(Debug, thiserror::Error)]
#[allow(missing_docs)]
pub enum Error<T> where T: Debug {
@@ -561,7 +567,7 @@ impl<T: Clone + Send + Sync + 'static> SlotDuration<T> {
/// to parent. If the number of skipped slots is greated than 0 this method will apply
/// an exponential backoff of at most `2^7 * slot_duration`, if no slots were skipped
/// this method will return `None.`
pub fn slot_lenience_exponential(parent_slot: u64, slot_info: &SlotInfo) -> Option<Duration> {
pub fn slot_lenience_exponential(parent_slot: Slot, slot_info: &SlotInfo) -> Option<Duration> {
// never give more than 2^this times the lenience.
const BACKOFF_CAP: u64 = 7;
@@ -574,7 +580,7 @@ pub fn slot_lenience_exponential(parent_slot: u64, slot_info: &SlotInfo) -> Opti
// exponential back-off.
// in normal cases we only attempt to issue blocks up to the end of the slot.
// when the chain has been stalled for a few slots, we give more lenience.
let skipped_slots = slot_info.number.saturating_sub(parent_slot + 1);
let skipped_slots = *slot_info.slot.saturating_sub(parent_slot + 1);
if skipped_slots == 0 {
None
@@ -590,7 +596,7 @@ pub fn slot_lenience_exponential(parent_slot: u64, slot_info: &SlotInfo) -> Opti
/// to parent. If the number of skipped slots is greated than 0 this method will apply
/// a linear backoff of at most `20 * slot_duration`, if no slots were skipped
/// this method will return `None.`
pub fn slot_lenience_linear(parent_slot: u64, slot_info: &SlotInfo) -> Option<Duration> {
pub fn slot_lenience_linear(parent_slot: Slot, slot_info: &SlotInfo) -> Option<Duration> {
// never give more than 20 times more lenience.
const BACKOFF_CAP: u64 = 20;
@@ -600,7 +606,7 @@ pub fn slot_lenience_linear(parent_slot: u64, slot_info: &SlotInfo) -> Option<Du
// linear back-off.
// in normal cases we only attempt to issue blocks up to the end of the slot.
// when the chain has been stalled for a few slots, we give more lenience.
let skipped_slots = slot_info.number.saturating_sub(parent_slot + 1);
let skipped_slots = *slot_info.slot.saturating_sub(parent_slot + 1);
if skipped_slots == 0 {
None
@@ -616,9 +622,9 @@ pub trait BackoffAuthoringBlocksStrategy<N> {
fn should_backoff(
&self,
chain_head_number: N,
chain_head_slot: u64,
chain_head_slot: Slot,
finalized_number: N,
slow_now: u64,
slow_now: Slot,
logging_target: &str,
) -> bool;
}
@@ -663,9 +669,9 @@ where
fn should_backoff(
&self,
chain_head_number: N,
chain_head_slot: u64,
chain_head_slot: Slot,
finalized_number: N,
slot_now: u64,
slot_now: Slot,
logging_target: &str,
) -> bool {
// This should not happen, but we want to keep the previous behaviour if it does.
@@ -683,7 +689,7 @@ where
// If interval is nonzero we backoff if the current slot isn't far enough ahead of the chain
// head.
if slot_now <= chain_head_slot + interval {
if *slot_now <= *chain_head_slot + interval {
info!(
target: logging_target,
"Backing off claiming new slot for block authorship: finality is lagging.",
@@ -699,9 +705,9 @@ impl<N> BackoffAuthoringBlocksStrategy<N> for () {
fn should_backoff(
&self,
_chain_head_number: N,
_chain_head_slot: u64,
_chain_head_slot: Slot,
_finalized_number: N,
_slot_now: u64,
_slot_now: Slot,
_logging_target: &str,
) -> bool {
false
@@ -717,9 +723,9 @@ mod test {
const SLOT_DURATION: Duration = Duration::from_millis(6000);
fn slot(n: u64) -> super::slots::SlotInfo {
fn slot(slot: u64) -> super::slots::SlotInfo {
super::slots::SlotInfo {
number: n,
slot: slot.into(),
duration: SLOT_DURATION.as_millis() as u64,
timestamp: Default::default(),
inherent_data: Default::default(),
@@ -730,20 +736,20 @@ mod test {
#[test]
fn linear_slot_lenience() {
// if no slots are skipped there should be no lenience
assert_eq!(super::slot_lenience_linear(1, &slot(2)), None);
assert_eq!(super::slot_lenience_linear(1.into(), &slot(2)), None);
// otherwise the lenience is incremented linearly with
// the number of skipped slots.
for n in 3..=22 {
assert_eq!(
super::slot_lenience_linear(1, &slot(n)),
super::slot_lenience_linear(1.into(), &slot(n)),
Some(SLOT_DURATION * (n - 2) as u32),
);
}
// but we cap it to a maximum of 20 slots
assert_eq!(
super::slot_lenience_linear(1, &slot(23)),
super::slot_lenience_linear(1.into(), &slot(23)),
Some(SLOT_DURATION * 20),
);
}
@@ -751,24 +757,24 @@ mod test {
#[test]
fn exponential_slot_lenience() {
// if no slots are skipped there should be no lenience
assert_eq!(super::slot_lenience_exponential(1, &slot(2)), None);
assert_eq!(super::slot_lenience_exponential(1.into(), &slot(2)), None);
// otherwise the lenience is incremented exponentially every two slots
for n in 3..=17 {
assert_eq!(
super::slot_lenience_exponential(1, &slot(n)),
super::slot_lenience_exponential(1.into(), &slot(n)),
Some(SLOT_DURATION * 2u32.pow((n / 2 - 1) as u32)),
);
}
// but we cap it to a maximum of 14 slots
assert_eq!(
super::slot_lenience_exponential(1, &slot(18)),
super::slot_lenience_exponential(1.into(), &slot(18)),
Some(SLOT_DURATION * 2u32.pow(7)),
);
assert_eq!(
super::slot_lenience_exponential(1, &slot(19)),
super::slot_lenience_exponential(1.into(), &slot(19)),
Some(SLOT_DURATION * 2u32.pow(7)),
);
}
@@ -808,7 +814,7 @@ mod test {
let slot_now = 2;
let should_backoff: Vec<bool> = (slot_now..1000)
.map(|s| strategy.should_backoff(head_number, head_slot, finalized_number, s, "slots"))
.map(|s| strategy.should_backoff(head_number, head_slot.into(), finalized_number, s.into(), "slots"))
.collect();
// Should always be false, since the head isn't advancing
@@ -833,9 +839,9 @@ mod test {
.map(move |s| {
let b = strategy.should_backoff(
head_number,
head_slot,
head_slot.into(),
finalized_number,
s,
s.into(),
"slots",
);
// Chain is still advancing (by someone else)
@@ -872,7 +878,7 @@ mod test {
let max_interval = strategy.max_interval;
let should_backoff: Vec<bool> = (slot_now..200)
.map(|s| strategy.should_backoff(head_number, head_slot, finalized_number, s, "slots"))
.map(|s| strategy.should_backoff(head_number, head_slot.into(), finalized_number, s.into(), "slots"))
.collect();
// Should backoff (true) until we are `max_interval` number of slots ahead of the chain
@@ -900,9 +906,9 @@ mod test {
<dyn BackoffAuthoringBlocksStrategy<NumberFor<Block>>>::should_backoff(
&param,
head_state.head_number,
head_state.head_slot,
head_state.head_slot.into(),
finalized_number,
head_state.slot_now,
head_state.slot_now.into(),
"slots",
)
};
@@ -972,9 +978,9 @@ mod test {
<dyn BackoffAuthoringBlocksStrategy<NumberFor<Block>>>::should_backoff(
&param,
head_state.head_number,
head_state.head_slot,
head_state.head_slot.into(),
finalized_number,
head_state.slot_now,
head_state.slot_now.into(),
"slots",
)
};
@@ -1036,9 +1042,9 @@ mod test {
<dyn BackoffAuthoringBlocksStrategy<NumberFor<Block>>>::should_backoff(
&param,
head_state.head_number,
head_state.head_slot,
head_state.head_slot.into(),
finalized_number,
head_state.slot_now,
head_state.slot_now.into(),
"slots",
)
};