mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-14 16:51:03 +00:00
Fixing BABE epochs to change between blocks (#3583)
* always fetch epoch from runtime * node integration tests don't test light nodes * give stand-in full node a FULL role * rejig babe APIs * introduce next-epoch-descriptor type * overhaul srml-BABE epoch logic * ensure VRF outputs end up in the right epoch-randomness * rewrite `do_initialize` to remove unnecessary loop * begin accounting for next epoch in epoch function * slots passes header to epoch_data * pass slot_number to SlotWorker::epoch_data * begin extracting epoch-change logic into its own module * aux methods for block weight * aux methods for genesis configuration * comment-out most, refactor header-check pipeline * mostly flesh out verifier again * reinstantiate babe BlockImport implementation * reinstate import-queue instantiation * reintroduce slot-worker implementation * reinstate pretty much all the rest * move fork-choice logic to BlockImport * fix some, but not all errors * patch test-runtime * make is_descendent of slightly more generic * get skeleton compiling when passing is_descendent_of * make descendent-of-builder more succinct * restore ordering of authority_index / slot_number * start fiddling with tests * fix warnings * improve initialization architecture and handle genesis * tests use correct block-import * fix BABE tests * fix some compiler errors * fix node-cli compilation * all crates compile * bump runtime versions and fix some warnings * tweak fork-tree search implementation * do backtracking search in fork-tree * node-cli integration tests now work * fix broken assumption in test_connectivity * babe tests fail for the right reasons. * test genesis epoch logic for epoch_changes * test that epochs can change between blocks * First BABE SRML test * Testing infrastructure for BABE Also includes a trivial additional test. * Apply suggestions from code review Co-Authored-By: Bastian Köcher <bkchr@users.noreply.github.com> * A little more test progress * More work on BABE testing * Try to get the tests working * Implement `UintAuthorityId`-based test mocks * Fix compilation errors * Adjust to upstream changes * Block numbers are ignored in BABE epoch calculation * authority_index() should ignore invalid authorities * Fix compile error * Add tests that session transitions happen * Check if BABE produces logs It currently does not. * Fix test suite This was really nasty, due to a type confusion that showed up as an off-by-1 buffer error. * Add additional tests Most of these were derived from the current output, so they are only useful to guard against regressions. * Make the tests more readable Also bump impl_version. * Fix excessive line width * Remove unused imports * Update srml/babe/src/lib.rs Co-Authored-By: André Silva <andre.beat@gmail.com> * try to fix imports * Fix build errors in test suite * tests did not pass * Try to get at least one digest to be output Currently, the code emits either no digests (if I don’t call `Session::rotate_session()` or two digests (if I do), which is wrong. * More tests They still don’t work, but this should help debugging. * fix silly error * Don’t even try to compile a broken test * remove broken check_epoch test and add one for genesis epoch * Check that the length of the pre-digests is correct * Bump `impl_version` * use epoch_for_descendent_of even for genesis * account for competing block 1s * finish srml-babe docs Co-Authored-By: André Silva <andre.beat@gmail.com> * address grumbles
This commit is contained in:
committed by
GitHub
parent
e6d4a76521
commit
c200ce757b
@@ -17,12 +17,10 @@
|
||||
//! Private implementation details of BABE digests.
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use super::AuthoritySignature;
|
||||
#[cfg(feature = "std")]
|
||||
use super::{BABE_ENGINE_ID, Epoch};
|
||||
use super::{BABE_ENGINE_ID, AuthoritySignature};
|
||||
#[cfg(not(feature = "std"))]
|
||||
use super::{VRF_OUTPUT_LENGTH, VRF_PROOF_LENGTH};
|
||||
use super::{AuthorityIndex, BabeBlockWeight, SlotNumber};
|
||||
use super::{AuthorityId, AuthorityIndex, SlotNumber, BabeAuthorityWeight};
|
||||
#[cfg(feature = "std")]
|
||||
use sr_primitives::{DigestItem, generic::OpaqueDigestItemId};
|
||||
#[cfg(feature = "std")]
|
||||
@@ -35,6 +33,8 @@ use schnorrkel::{
|
||||
SignatureError, errors::MultiSignatureStage,
|
||||
vrf::{VRFProof, VRFOutput, VRF_OUTPUT_LENGTH, VRF_PROOF_LENGTH}
|
||||
};
|
||||
use rstd::vec::Vec;
|
||||
|
||||
|
||||
/// A BABE pre-runtime digest. This contains all data required to validate a
|
||||
/// block and for the BABE runtime module. Slots can be assigned to a primary
|
||||
@@ -52,8 +52,6 @@ pub enum BabePreDigest {
|
||||
authority_index: super::AuthorityIndex,
|
||||
/// Slot number
|
||||
slot_number: SlotNumber,
|
||||
/// Chain weight (measured in number of Primary blocks)
|
||||
weight: BabeBlockWeight,
|
||||
},
|
||||
/// A secondary deterministic slot assignment.
|
||||
Secondary {
|
||||
@@ -61,8 +59,6 @@ pub enum BabePreDigest {
|
||||
authority_index: super::AuthorityIndex,
|
||||
/// Slot number
|
||||
slot_number: SlotNumber,
|
||||
/// Chain weight (measured in number of Primary blocks)
|
||||
weight: BabeBlockWeight,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -84,11 +80,12 @@ impl BabePreDigest {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the weight of the pre digest.
|
||||
pub fn weight(&self) -> BabeBlockWeight {
|
||||
/// Returns the weight _added_ by this digest, not the cumulative weight
|
||||
/// of the chain.
|
||||
pub fn added_weight(&self) -> crate::BabeBlockWeight {
|
||||
match self {
|
||||
BabePreDigest::Primary { weight, .. } => *weight,
|
||||
BabePreDigest::Secondary { weight, .. } => *weight,
|
||||
BabePreDigest::Primary { .. } => 1,
|
||||
BabePreDigest::Secondary { .. } => 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -100,26 +97,29 @@ pub const BABE_VRF_PREFIX: &'static [u8] = b"substrate-babe-vrf";
|
||||
#[derive(Copy, Clone, Encode, Decode)]
|
||||
pub enum RawBabePreDigest {
|
||||
/// A primary VRF-based slot assignment.
|
||||
#[codec(index = "1")]
|
||||
Primary {
|
||||
/// Authority index
|
||||
authority_index: AuthorityIndex,
|
||||
/// Slot number
|
||||
slot_number: SlotNumber,
|
||||
/// Chain weight (measured in number of Primary blocks)
|
||||
weight: BabeBlockWeight,
|
||||
/// VRF output
|
||||
vrf_output: [u8; VRF_OUTPUT_LENGTH],
|
||||
/// VRF proof
|
||||
vrf_proof: [u8; VRF_PROOF_LENGTH],
|
||||
},
|
||||
/// A secondary deterministic slot assignment.
|
||||
#[codec(index = "2")]
|
||||
Secondary {
|
||||
/// Authority index
|
||||
///
|
||||
/// This is not strictly-speaking necessary, since the secondary slots
|
||||
/// are assigned based on slot number and epoch randomness. But including
|
||||
/// it makes things easier for higher-level users of the chain data to
|
||||
/// be aware of the author of a secondary-slot block.
|
||||
authority_index: AuthorityIndex,
|
||||
/// Slot number
|
||||
slot_number: SlotNumber,
|
||||
/// Chain weight (measured in number of Primary blocks)
|
||||
weight: BabeBlockWeight,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -142,25 +142,21 @@ impl Encode for BabePreDigest {
|
||||
vrf_proof,
|
||||
authority_index,
|
||||
slot_number,
|
||||
weight,
|
||||
} => {
|
||||
RawBabePreDigest::Primary {
|
||||
vrf_output: *vrf_output.as_bytes(),
|
||||
vrf_proof: vrf_proof.to_bytes(),
|
||||
authority_index: *authority_index,
|
||||
slot_number: *slot_number,
|
||||
weight: *weight,
|
||||
}
|
||||
},
|
||||
BabePreDigest::Secondary {
|
||||
authority_index,
|
||||
slot_number,
|
||||
weight,
|
||||
} => {
|
||||
RawBabePreDigest::Secondary {
|
||||
authority_index: *authority_index,
|
||||
slot_number: *slot_number,
|
||||
weight: *weight,
|
||||
}
|
||||
},
|
||||
};
|
||||
@@ -176,7 +172,7 @@ impl codec::EncodeLike for BabePreDigest {}
|
||||
impl Decode for BabePreDigest {
|
||||
fn decode<R: Input>(i: &mut R) -> Result<Self, Error> {
|
||||
let pre_digest = match Decode::decode(i)? {
|
||||
RawBabePreDigest::Primary { vrf_output, vrf_proof, authority_index, slot_number, weight } => {
|
||||
RawBabePreDigest::Primary { vrf_output, vrf_proof, authority_index, slot_number } => {
|
||||
// Verify (at compile time) that the sizes in babe_primitives are correct
|
||||
let _: [u8; super::VRF_OUTPUT_LENGTH] = vrf_output;
|
||||
let _: [u8; super::VRF_PROOF_LENGTH] = vrf_proof;
|
||||
@@ -186,11 +182,10 @@ impl Decode for BabePreDigest {
|
||||
vrf_output: VRFOutput::from_bytes(&vrf_output).map_err(convert_error)?,
|
||||
authority_index,
|
||||
slot_number,
|
||||
weight,
|
||||
}
|
||||
},
|
||||
RawBabePreDigest::Secondary { authority_index, slot_number, weight } => {
|
||||
BabePreDigest::Secondary { authority_index, slot_number, weight }
|
||||
RawBabePreDigest::Secondary { authority_index, slot_number } => {
|
||||
BabePreDigest::Secondary { authority_index, slot_number }
|
||||
},
|
||||
};
|
||||
|
||||
@@ -198,6 +193,18 @@ impl Decode for BabePreDigest {
|
||||
}
|
||||
}
|
||||
|
||||
/// Information about the next epoch. This is broadcast in the first block
|
||||
/// of the epoch.
|
||||
#[derive(Decode, Encode, Default, PartialEq, Eq, Clone)]
|
||||
#[cfg_attr(any(feature = "std", test), derive(Debug))]
|
||||
pub struct NextEpochDescriptor {
|
||||
/// The authorities.
|
||||
pub authorities: Vec<(AuthorityId, BabeAuthorityWeight)>,
|
||||
|
||||
/// The value of randomness to use for the slot-assignment.
|
||||
pub randomness: [u8; VRF_OUTPUT_LENGTH],
|
||||
}
|
||||
|
||||
/// A digest item which is usable with BABE consensus.
|
||||
#[cfg(feature = "std")]
|
||||
pub trait CompatibleDigestItem: Sized {
|
||||
@@ -214,7 +221,7 @@ pub trait CompatibleDigestItem: Sized {
|
||||
fn as_babe_seal(&self) -> Option<AuthoritySignature>;
|
||||
|
||||
/// If this item is a BABE epoch, return it.
|
||||
fn as_babe_epoch(&self) -> Option<Epoch>;
|
||||
fn as_next_epoch_descriptor(&self) -> Option<NextEpochDescriptor>;
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
@@ -237,8 +244,12 @@ impl<Hash> CompatibleDigestItem for DigestItem<Hash> where
|
||||
self.try_to(OpaqueDigestItemId::Seal(&BABE_ENGINE_ID))
|
||||
}
|
||||
|
||||
fn as_babe_epoch(&self) -> Option<Epoch> {
|
||||
fn as_next_epoch_descriptor(&self) -> Option<NextEpochDescriptor> {
|
||||
self.try_to(OpaqueDigestItemId::Consensus(&BABE_ENGINE_ID))
|
||||
.and_then(|x: super::ConsensusLog| match x {
|
||||
super::ConsensusLog::NextEpochData(n) => Some(n),
|
||||
_ => None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ use substrate_client::decl_runtime_apis;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
pub use digest::{BabePreDigest, CompatibleDigestItem};
|
||||
pub use digest::{BABE_VRF_PREFIX, RawBabePreDigest};
|
||||
pub use digest::{BABE_VRF_PREFIX, RawBabePreDigest, NextEpochDescriptor};
|
||||
|
||||
mod app {
|
||||
use app_crypto::{app_crypto, key_types::BABE, sr25519};
|
||||
@@ -59,6 +59,11 @@ pub const VRF_PROOF_LENGTH: usize = 64;
|
||||
/// The length of the public key
|
||||
pub const PUBLIC_KEY_LENGTH: usize = 32;
|
||||
|
||||
/// How many blocks to wait before running the median algorithm for relative time
|
||||
/// This will not vary from chain to chain as it is not dependent on slot duration
|
||||
/// or epoch length.
|
||||
pub const MEDIAN_ALGORITHM_CARDINALITY: usize = 1200; // arbitrary suggestion by w3f-research.
|
||||
|
||||
/// The index of an authority.
|
||||
pub type AuthorityIndex = u32;
|
||||
|
||||
@@ -80,33 +85,50 @@ pub struct Epoch {
|
||||
/// The epoch index
|
||||
pub epoch_index: u64,
|
||||
/// The starting slot of the epoch,
|
||||
pub start_slot: u64,
|
||||
pub start_slot: SlotNumber,
|
||||
/// The duration of this epoch
|
||||
pub duration: SlotNumber,
|
||||
/// The authorities and their weights
|
||||
pub authorities: Vec<(AuthorityId, BabeAuthorityWeight)>,
|
||||
/// Randomness for this epoch
|
||||
pub randomness: [u8; VRF_OUTPUT_LENGTH],
|
||||
/// Whether secondary slot assignments should be used during the epoch.
|
||||
pub secondary_slots: bool,
|
||||
}
|
||||
|
||||
impl Epoch {
|
||||
/// "increment" the epoch, with given descriptor for the next.
|
||||
pub fn increment(&self, descriptor: NextEpochDescriptor) -> Epoch {
|
||||
Epoch {
|
||||
epoch_index: self.epoch_index + 1,
|
||||
start_slot: self.start_slot + self.duration,
|
||||
duration: self.duration,
|
||||
authorities: descriptor.authorities,
|
||||
randomness: descriptor.randomness,
|
||||
}
|
||||
}
|
||||
|
||||
/// Produce the "end slot" of the epoch. This is NOT inclusive to the epoch,
|
||||
// i.e. the slots covered by the epoch are `self.start_slot .. self.end_slot()`.
|
||||
pub fn end_slot(&self) -> SlotNumber {
|
||||
self.start_slot + self.duration
|
||||
}
|
||||
}
|
||||
|
||||
/// An consensus log item for BABE.
|
||||
#[derive(Decode, Encode, Clone, PartialEq, Eq)]
|
||||
pub enum ConsensusLog {
|
||||
/// The epoch has changed. This provides information about the
|
||||
/// epoch _after_ next: what slot number it will start at, who are the authorities (and their weights)
|
||||
/// and the next epoch randomness. The information for the _next_ epoch should already
|
||||
/// be available.
|
||||
/// The epoch has changed. This provides information about the _next_
|
||||
/// epoch - information about the _current_ epoch (i.e. the one we've just
|
||||
/// entered) should already be available earlier in the chain.
|
||||
#[codec(index = "1")]
|
||||
NextEpochData(Epoch),
|
||||
NextEpochData(NextEpochDescriptor),
|
||||
/// Disable the authority with given index.
|
||||
#[codec(index = "2")]
|
||||
OnDisabled(AuthorityIndex),
|
||||
}
|
||||
|
||||
/// Configuration data used by the BABE consensus engine.
|
||||
#[derive(Copy, Clone, Hash, PartialEq, Eq, Debug, Encode, Decode)]
|
||||
#[derive(Clone, PartialEq, Eq, Encode, Decode)]
|
||||
#[cfg_attr(any(feature = "std", test), derive(Debug))]
|
||||
pub struct BabeConfiguration {
|
||||
/// The slot duration in milliseconds for BABE. Currently, only
|
||||
/// the value provided by this type at genesis will be used.
|
||||
@@ -114,35 +136,35 @@ pub struct BabeConfiguration {
|
||||
/// Dynamic slot duration may be supported in the future.
|
||||
pub slot_duration: u64,
|
||||
|
||||
/// The duration of epochs in slots.
|
||||
pub epoch_length: SlotNumber,
|
||||
|
||||
/// A constant value that is used in the threshold calculation formula.
|
||||
/// Expressed as a fraction where the first member of the tuple is the
|
||||
/// numerator and the second is the denominator. The fraction should
|
||||
/// Expressed as a rational where the first member of the tuple is the
|
||||
/// numerator and the second is the denominator. The rational should
|
||||
/// represent a value between 0 and 1.
|
||||
/// In the threshold formula calculation, `1 - c` represents the probability
|
||||
/// of a slot being empty.
|
||||
pub c: (u64, u64),
|
||||
|
||||
/// The minimum number of blocks that must be received before running the
|
||||
/// median algorithm to compute the offset between the on-chain time and the
|
||||
/// local time. Currently, only the value provided by this type at genesis
|
||||
/// will be used, but this is subject to change.
|
||||
///
|
||||
/// Blocks less than `self.median_required_blocks` must be generated by an
|
||||
/// *initial validator* ― that is, a node that was a validator at genesis.
|
||||
pub median_required_blocks: u64,
|
||||
/// The authorities for the genesis epoch.
|
||||
pub genesis_authorities: Vec<(AuthorityId, BabeAuthorityWeight)>,
|
||||
|
||||
/// The randomness for the genesis epoch.
|
||||
pub randomness: [u8; VRF_OUTPUT_LENGTH],
|
||||
|
||||
/// Whether this chain should run with secondary slots, which are assigned
|
||||
/// in round-robin manner.
|
||||
pub secondary_slots: bool,
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl slots::SlotData for BabeConfiguration {
|
||||
/// Return the slot duration in milliseconds for BABE. Currently, only
|
||||
/// the value provided by this type at genesis will be used.
|
||||
///
|
||||
/// Dynamic slot duration may be supported in the future.
|
||||
fn slot_duration(&self) -> u64 {
|
||||
self.slot_duration
|
||||
}
|
||||
|
||||
const SLOT_KEY: &'static [u8] = b"babe_bootstrap_data";
|
||||
const SLOT_KEY: &'static [u8] = b"babe_configuration";
|
||||
}
|
||||
|
||||
decl_runtime_apis! {
|
||||
@@ -152,9 +174,6 @@ decl_runtime_apis! {
|
||||
/// only the value provided by this type at genesis will be used.
|
||||
///
|
||||
/// Dynamic configuration may be supported in the future.
|
||||
fn startup_data() -> BabeConfiguration;
|
||||
|
||||
/// Get the current epoch data for Babe.
|
||||
fn epoch() -> Epoch;
|
||||
fn configuration() -> BabeConfiguration;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user