mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-13 07:01:05 +00:00
babe: support online configuration upgrades (#5514)
* babe: support online configuration upgrades * Switch to use NextConfigDescriptor instead of changing runtime interface * Fix tests * epoch-changes: map function that allows converting with different epoch types * Add migration script for the epoch config change * Fix migration tests * Fix migration: Epoch should be EpochV0 * Update client/consensus/babe/src/lib.rs Co-Authored-By: André Silva <123550+andresilva@users.noreply.github.com> * Fix new epochChanges version * Fix unused imports Co-authored-by: André Silva <123550+andresilva@users.noreply.github.com>
This commit is contained in:
@@ -59,11 +59,13 @@
|
||||
#![forbid(unsafe_code)]
|
||||
#![warn(missing_docs)]
|
||||
pub use sp_consensus_babe::{
|
||||
BabeApi, ConsensusLog, BABE_ENGINE_ID, SlotNumber, BabeConfiguration,
|
||||
BabeApi, ConsensusLog, BABE_ENGINE_ID, SlotNumber,
|
||||
BabeEpochConfiguration, BabeGenesisConfiguration,
|
||||
AuthorityId, AuthorityPair, AuthoritySignature,
|
||||
BabeAuthorityWeight, VRF_OUTPUT_LENGTH,
|
||||
digests::{
|
||||
CompatibleDigestItem, NextEpochDescriptor, PreDigest, PrimaryPreDigest, SecondaryPreDigest,
|
||||
CompatibleDigestItem, NextEpochDescriptor, NextConfigDescriptor,
|
||||
PreDigest, PrimaryPreDigest, SecondaryPreDigest,
|
||||
},
|
||||
};
|
||||
pub use sp_consensus::SyncOracle;
|
||||
@@ -118,36 +120,43 @@ use sp_api::ApiExt;
|
||||
|
||||
mod aux_schema;
|
||||
mod verification;
|
||||
mod migration;
|
||||
pub mod authorship;
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
/// BABE epoch information
|
||||
#[derive(Decode, Encode, Default, PartialEq, Eq, Clone, Debug)]
|
||||
#[derive(Decode, Encode, PartialEq, Eq, Clone, Debug)]
|
||||
pub struct Epoch {
|
||||
/// The epoch index
|
||||
/// The epoch index.
|
||||
pub epoch_index: u64,
|
||||
/// The starting slot of the epoch,
|
||||
/// The starting slot of the epoch.
|
||||
pub start_slot: SlotNumber,
|
||||
/// The duration of this epoch
|
||||
/// The duration of this epoch.
|
||||
pub duration: SlotNumber,
|
||||
/// The authorities and their weights
|
||||
/// The authorities and their weights.
|
||||
pub authorities: Vec<(AuthorityId, BabeAuthorityWeight)>,
|
||||
/// Randomness for this epoch
|
||||
/// Randomness for this epoch.
|
||||
pub randomness: [u8; VRF_OUTPUT_LENGTH],
|
||||
/// Configuration of the epoch.
|
||||
pub config: BabeEpochConfiguration,
|
||||
}
|
||||
|
||||
impl EpochT for Epoch {
|
||||
type NextEpochDescriptor = NextEpochDescriptor;
|
||||
type NextEpochDescriptor = (NextEpochDescriptor, BabeEpochConfiguration);
|
||||
type SlotNumber = SlotNumber;
|
||||
|
||||
fn increment(&self, descriptor: NextEpochDescriptor) -> Epoch {
|
||||
fn increment(
|
||||
&self,
|
||||
(descriptor, config): (NextEpochDescriptor, BabeEpochConfiguration)
|
||||
) -> Epoch {
|
||||
Epoch {
|
||||
epoch_index: self.epoch_index + 1,
|
||||
start_slot: self.start_slot + self.duration,
|
||||
duration: self.duration,
|
||||
authorities: descriptor.authorities,
|
||||
randomness: descriptor.randomness,
|
||||
config,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -160,6 +169,27 @@ impl EpochT for Epoch {
|
||||
}
|
||||
}
|
||||
|
||||
impl Epoch {
|
||||
/// Create the genesis epoch (epoch #0). This is defined to start at the slot of
|
||||
/// the first block, so that has to be provided.
|
||||
pub fn genesis(
|
||||
genesis_config: &BabeGenesisConfiguration,
|
||||
slot_number: SlotNumber
|
||||
) -> Epoch {
|
||||
Epoch {
|
||||
epoch_index: 0,
|
||||
start_slot: slot_number,
|
||||
duration: genesis_config.epoch_length,
|
||||
authorities: genesis_config.genesis_authorities.clone(),
|
||||
randomness: genesis_config.randomness.clone(),
|
||||
config: BabeEpochConfiguration {
|
||||
c: genesis_config.c,
|
||||
secondary_slots: genesis_config.secondary_slots,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(derive_more::Display, Debug)]
|
||||
enum Error<B: BlockT> {
|
||||
#[display(fmt = "Multiple BABE pre-runtime digests, rejecting!")]
|
||||
@@ -168,6 +198,8 @@ enum Error<B: BlockT> {
|
||||
NoPreRuntimeDigest,
|
||||
#[display(fmt = "Multiple BABE epoch change digests, rejecting!")]
|
||||
MultipleEpochChangeDigests,
|
||||
#[display(fmt = "Multiple BABE config change digests, rejecting!")]
|
||||
MultipleConfigChangeDigests,
|
||||
#[display(fmt = "Could not extract timestamp and slot: {:?}", _0)]
|
||||
Extraction(sp_consensus::Error),
|
||||
#[display(fmt = "Could not fetch epoch at {:?}", _0)]
|
||||
@@ -200,6 +232,8 @@ enum Error<B: BlockT> {
|
||||
FetchParentHeader(sp_blockchain::Error),
|
||||
#[display(fmt = "Expected epoch change to happen at {:?}, s{}", _0, _1)]
|
||||
ExpectedEpochChange(B::Hash, u64),
|
||||
#[display(fmt = "Unexpected config change")]
|
||||
UnexpectedConfigChange,
|
||||
#[display(fmt = "Unexpected epoch change")]
|
||||
UnexpectedEpochChange,
|
||||
#[display(fmt = "Parent block of {} has no associated weight", _0)]
|
||||
@@ -236,7 +270,7 @@ pub static INTERMEDIATE_KEY: &[u8] = b"babe1";
|
||||
// and `super::babe::Config` can be eliminated.
|
||||
// https://github.com/paritytech/substrate/issues/2434
|
||||
#[derive(Clone)]
|
||||
pub struct Config(sc_consensus_slots::SlotDuration<BabeConfiguration>);
|
||||
pub struct Config(sc_consensus_slots::SlotDuration<BabeGenesisConfiguration>);
|
||||
|
||||
impl Config {
|
||||
/// Either fetch the slot duration from disk or compute it from the genesis
|
||||
@@ -253,24 +287,12 @@ impl Config {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Create the genesis epoch (epoch #0). This is defined to start at the slot of
|
||||
/// the first block, so that has to be provided.
|
||||
pub fn genesis_epoch(&self, slot_number: SlotNumber) -> Epoch {
|
||||
Epoch {
|
||||
epoch_index: 0,
|
||||
start_slot: slot_number,
|
||||
duration: self.epoch_length,
|
||||
authorities: self.genesis_authorities.clone(),
|
||||
randomness: self.randomness.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Deref for Config {
|
||||
type Target = BabeConfiguration;
|
||||
type Target = BabeGenesisConfiguration;
|
||||
|
||||
fn deref(&self) -> &BabeConfiguration {
|
||||
fn deref(&self) -> &BabeGenesisConfiguration {
|
||||
&*self.0
|
||||
}
|
||||
}
|
||||
@@ -428,7 +450,7 @@ impl<B, C, E, I, Error, SO> sc_consensus_slots::SimpleSlotWorker<B> for BabeWork
|
||||
|
||||
fn authorities_len(&self, epoch_descriptor: &Self::EpochData) -> Option<usize> {
|
||||
self.epoch_changes.lock()
|
||||
.viable_epoch(&epoch_descriptor, |slot| self.config.genesis_epoch(slot))
|
||||
.viable_epoch(&epoch_descriptor, |slot| Epoch::genesis(&self.config, slot))
|
||||
.map(|epoch| epoch.as_ref().authorities.len())
|
||||
}
|
||||
|
||||
@@ -443,9 +465,8 @@ impl<B, C, E, I, Error, SO> sc_consensus_slots::SimpleSlotWorker<B> for BabeWork
|
||||
slot_number,
|
||||
self.epoch_changes.lock().viable_epoch(
|
||||
&epoch_descriptor,
|
||||
|slot| self.config.genesis_epoch(slot)
|
||||
|slot| Epoch::genesis(&self.config, slot)
|
||||
)?.as_ref(),
|
||||
&*self.config,
|
||||
&self.keystore,
|
||||
);
|
||||
|
||||
@@ -599,6 +620,24 @@ fn find_next_epoch_digest<B: BlockT>(header: &B::Header)
|
||||
Ok(epoch_digest)
|
||||
}
|
||||
|
||||
/// Extract the BABE config change digest from the given header, if it exists.
|
||||
fn find_next_config_digest<B: BlockT>(header: &B::Header)
|
||||
-> Result<Option<NextConfigDescriptor>, Error<B>>
|
||||
where DigestItemFor<B>: CompatibleDigestItem,
|
||||
{
|
||||
let mut config_digest: Option<_> = None;
|
||||
for log in header.digest().logs() {
|
||||
trace!(target: "babe", "Checking log {:?}, looking for epoch change digest.", log);
|
||||
let log = log.try_to::<ConsensusLog>(OpaqueDigestItemId::Consensus(&BABE_ENGINE_ID));
|
||||
match (log, config_digest.is_some()) {
|
||||
(Some(ConsensusLog::NextConfigData(_)), true) => return Err(babe_err(Error::MultipleConfigChangeDigests)),
|
||||
(Some(ConsensusLog::NextConfigData(config)), false) => config_digest = Some(config),
|
||||
_ => trace!(target: "babe", "Ignoring digest not meant for us"),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(config_digest)
|
||||
}
|
||||
|
||||
#[derive(Default, Clone)]
|
||||
struct TimeSource(Arc<Mutex<(Option<Duration>, Vec<(Instant, u64)>)>>);
|
||||
@@ -726,7 +765,7 @@ impl<Block, Client> Verifier<Block> for BabeVerifier<Block, Client> where
|
||||
.ok_or_else(|| Error::<Block>::FetchEpoch(parent_hash))?;
|
||||
let viable_epoch = epoch_changes.viable_epoch(
|
||||
&epoch_descriptor,
|
||||
|slot| self.config.genesis_epoch(slot)
|
||||
|slot| Epoch::genesis(&self.config, slot)
|
||||
).ok_or_else(|| Error::<Block>::FetchEpoch(parent_hash))?;
|
||||
|
||||
// We add one to the current slot to allow for some small drift.
|
||||
@@ -736,7 +775,6 @@ impl<Block, Client> Verifier<Block> for BabeVerifier<Block, Client> where
|
||||
pre_digest: Some(pre_digest.clone()),
|
||||
slot_now: slot_now + 1,
|
||||
epoch: viable_epoch.as_ref(),
|
||||
config: &self.config,
|
||||
};
|
||||
|
||||
match verification::check_header::<Block>(v_params)? {
|
||||
@@ -958,19 +996,32 @@ impl<Block, Client, Inner> BlockImport<Block> for BabeBlockImport<Block, Client,
|
||||
// search for this all the time so we can reject unexpected announcements.
|
||||
let next_epoch_digest = find_next_epoch_digest::<Block>(&block.header)
|
||||
.map_err(|e| ConsensusError::ClientImport(e.to_string()))?;
|
||||
let next_config_digest = find_next_config_digest::<Block>(&block.header)
|
||||
.map_err(|e| ConsensusError::ClientImport(e.to_string()))?;
|
||||
|
||||
match (first_in_epoch, next_epoch_digest.is_some()) {
|
||||
(true, true) => {},
|
||||
(false, false) => {},
|
||||
(true, false) => {
|
||||
match (first_in_epoch, next_epoch_digest.is_some(), next_config_digest.is_some()) {
|
||||
(true, true, _) => {},
|
||||
(false, false, false) => {},
|
||||
(false, false, true) => {
|
||||
return Err(
|
||||
ConsensusError::ClientImport(
|
||||
babe_err(Error::<Block>::UnexpectedConfigChange).into(),
|
||||
)
|
||||
)
|
||||
},
|
||||
(true, false, _) => {
|
||||
return Err(
|
||||
ConsensusError::ClientImport(
|
||||
babe_err(Error::<Block>::ExpectedEpochChange(hash, slot_number)).into(),
|
||||
)
|
||||
);
|
||||
)
|
||||
},
|
||||
(false, true) => {
|
||||
return Err(ConsensusError::ClientImport(Error::<Block>::UnexpectedEpochChange.into()));
|
||||
(false, true, _) => {
|
||||
return Err(
|
||||
ConsensusError::ClientImport(
|
||||
babe_err(Error::<Block>::UnexpectedEpochChange).into(),
|
||||
)
|
||||
)
|
||||
},
|
||||
}
|
||||
|
||||
@@ -985,11 +1036,15 @@ impl<Block, Client, Inner> BlockImport<Block> for BabeBlockImport<Block, Client,
|
||||
|
||||
let viable_epoch = epoch_changes.viable_epoch(
|
||||
&epoch_descriptor,
|
||||
|slot| self.config.genesis_epoch(slot),
|
||||
|slot| Epoch::genesis(&self.config, slot)
|
||||
).ok_or_else(|| {
|
||||
ConsensusError::ClientImport(Error::<Block>::FetchEpoch(parent_hash).into())
|
||||
})?;
|
||||
|
||||
let epoch_config = next_config_digest.unwrap_or_else(
|
||||
|| viable_epoch.as_ref().config.clone()
|
||||
);
|
||||
|
||||
// restrict info logging during initial sync to avoid spam
|
||||
let log_level = if block.origin == BlockOrigin::NetworkInitialSync {
|
||||
log::Level::Debug
|
||||
@@ -1006,7 +1061,7 @@ impl<Block, Client, Inner> BlockImport<Block> for BabeBlockImport<Block, Client,
|
||||
viable_epoch.as_ref().start_slot,
|
||||
);
|
||||
|
||||
let next_epoch = viable_epoch.increment(next_epoch_descriptor);
|
||||
let next_epoch = viable_epoch.increment((next_epoch_descriptor, epoch_config));
|
||||
|
||||
log!(target: "babe",
|
||||
log_level,
|
||||
@@ -1152,7 +1207,7 @@ pub fn block_import<Client, Block: BlockT, I>(
|
||||
) -> ClientResult<(BabeBlockImport<Block, Client, I>, BabeLink<Block>)> where
|
||||
Client: AuxStore + HeaderBackend<Block> + HeaderMetadata<Block, Error = sp_blockchain::Error>,
|
||||
{
|
||||
let epoch_changes = aux_schema::load_epoch_changes::<Block, _>(&*client)?;
|
||||
let epoch_changes = aux_schema::load_epoch_changes::<Block, _>(&*client, &config)?;
|
||||
let link = BabeLink {
|
||||
epoch_changes: epoch_changes.clone(),
|
||||
time_source: Default::default(),
|
||||
@@ -1245,13 +1300,12 @@ pub mod test_helpers {
|
||||
&parent.hash(),
|
||||
parent.number().clone(),
|
||||
slot_number,
|
||||
|slot| link.config.genesis_epoch(slot),
|
||||
|slot| Epoch::genesis(&link.config, slot),
|
||||
).unwrap().unwrap();
|
||||
|
||||
authorship::claim_slot(
|
||||
slot_number,
|
||||
&epoch,
|
||||
&link.config,
|
||||
keystore,
|
||||
).map(|(digest, _)| digest)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user