mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-12 20:31:13 +00:00
Fix light client synchronization on master (#3301)
* value ranges in consensus cache * skip values in cache * read epoch0 + epoch1 data from genesis in babe * sync authorities + session validators at genesis * removed some debug printlns * fixed cache encoding * Revert "skip values in cache" This reverts commit ce451c32823aaa4b67d99ca5b58f1bf3984df4db. * Revert "value ranges in consensus cache" This reverts commit 9062f9434cddd14a01275ddbfcd904b04282e63b. * get rid of cache::AUTHORITIES in Babe * cleaning up * cleaning up * update spec version * lost changes * fixed tests * Update node/runtime/src/lib.rs Co-Authored-By: DemiMarie-parity <48690212+DemiMarie-parity@users.noreply.github.com> * fix once-per-block condition * fix standalone babe + temp_storage in BuildGenesis * fix benhes compilation * fixed comment * re-added light nodes to integration tests * finalize_with_ancestors from extra_requests * post-merge fix * aaand removed debug code * (another one) * fix warn in logs (do not call ForkTree::finalize twice for the same block) * sync digest.next_authorities with actual next authorities * more docs * reverting all commits affecting storage * also remove keys from babe trait * fixed warnings * post-merge fixes * reverted some redundant changes * reverted more changes
This commit is contained in:
committed by
Gavin Wood
parent
d1dde7e087
commit
3825a21bac
@@ -21,20 +21,19 @@
|
||||
#![forbid(unsafe_code, missing_docs)]
|
||||
pub use babe_primitives::*;
|
||||
pub use consensus_common::SyncOracle;
|
||||
use std::{collections::HashMap, sync::Arc, u64, fmt::{Debug, Display}, pin::Pin, time::{Instant, Duration}};
|
||||
use std::{collections::HashMap, sync::Arc, u64, pin::Pin, time::{Instant, Duration}};
|
||||
use babe_primitives;
|
||||
use consensus_common::ImportResult;
|
||||
use consensus_common::import_queue::{
|
||||
BoxJustificationImport, BoxFinalityProofImport,
|
||||
};
|
||||
use consensus_common::well_known_cache_keys::Id as CacheKeyId;
|
||||
use sr_primitives::{generic, generic::{BlockId, OpaqueDigestItemId}, Justification};
|
||||
use sr_primitives::{generic::{BlockId, OpaqueDigestItemId}, Justification};
|
||||
use sr_primitives::traits::{
|
||||
Block as BlockT, Header, DigestItemFor, NumberFor, ProvideRuntimeApi,
|
||||
SimpleBitOps, Zero,
|
||||
Zero,
|
||||
};
|
||||
use keystore::KeyStorePtr;
|
||||
use runtime_support::serde::{Serialize, Deserialize};
|
||||
use codec::{Decode, Encode};
|
||||
use parking_lot::{Mutex, MutexGuard};
|
||||
use primitives::{Blake2Hasher, H256, Pair, Public};
|
||||
@@ -44,8 +43,6 @@ use substrate_telemetry::{
|
||||
telemetry,
|
||||
CONSENSUS_TRACE,
|
||||
CONSENSUS_DEBUG,
|
||||
CONSENSUS_WARN,
|
||||
CONSENSUS_INFO,
|
||||
};
|
||||
use schnorrkel::{
|
||||
keys::Keypair,
|
||||
@@ -72,12 +69,11 @@ use client::{
|
||||
};
|
||||
use fork_tree::ForkTree;
|
||||
use slots::{CheckedHeader, check_equivocation};
|
||||
use futures::{prelude::*, future};
|
||||
use futures::prelude::*;
|
||||
use futures01::Stream as _;
|
||||
use futures_timer::Delay;
|
||||
use log::{error, warn, debug, info, trace};
|
||||
|
||||
use slots::{SlotWorker, SlotData, SlotInfo, SlotCompatible, SignedDuration};
|
||||
use slots::{SlotWorker, SlotData, SlotInfo, SlotCompatible};
|
||||
|
||||
mod aux_schema;
|
||||
#[cfg(test)]
|
||||
@@ -256,7 +252,8 @@ impl<H, B, C, E, I, Error, SO> slots::SimpleSlotWorker<B> for BabeWorker<C, E, I
|
||||
}
|
||||
|
||||
fn epoch_data(&self, block: &B::Hash) -> Result<Self::EpochData, consensus_common::Error> {
|
||||
epoch(self.client.as_ref(), &BlockId::Hash(*block))
|
||||
epoch_from_runtime(self.client.as_ref(), &BlockId::Hash(*block))
|
||||
.ok_or(consensus_common::Error::InvalidAuthoritiesSet)
|
||||
}
|
||||
|
||||
fn authorities_len(&self, epoch_data: &Self::EpochData) -> usize {
|
||||
@@ -397,7 +394,7 @@ fn find_next_epoch_digest<B: BlockT>(header: &B::Header) -> Result<Option<Epoch>
|
||||
/// unsigned. This is required for security and must not be changed.
|
||||
///
|
||||
/// This digest item will always return `Some` when used with `as_babe_pre_digest`.
|
||||
// FIXME #1018 needs misbehavior types. The `transaction_pool` parameter will be
|
||||
// FIXME #1018 needs misbehavior types. The `transaction_pool` parameter will be
|
||||
// used to submit such misbehavior reports.
|
||||
fn check_header<B: BlockT + Sized, C: AuxStore, T>(
|
||||
client: &C,
|
||||
@@ -595,24 +592,51 @@ impl<B: BlockT, C, T> Verifier<B> for BabeVerifier<C, T> where
|
||||
|
||||
let hash = header.hash();
|
||||
let parent_hash = *header.parent_hash();
|
||||
let Epoch { authorities, randomness, epoch_index, .. } =
|
||||
epoch(self.api.as_ref(), &BlockId::Hash(parent_hash))
|
||||
.map_err(|e| format!("Could not fetch epoch at {:?}: {:?}", parent_hash, e))?;
|
||||
|
||||
let epoch = epoch(self.api.as_ref(), &BlockId::Hash(parent_hash))
|
||||
.map_err(|e| format!("Could not fetch epoch at {:?}: {:?}", parent_hash, e))?;
|
||||
let (epoch, maybe_next_epoch) = epoch.deconstruct();
|
||||
let Epoch { authorities, randomness, epoch_index, .. } = epoch;
|
||||
|
||||
// We add one to allow for some small drift.
|
||||
// FIXME #1019 in the future, alter this queue to allow deferring of headers
|
||||
let checked_header = check_header::<B, C, T>(
|
||||
let mut checked_header = check_header::<B, C, T>(
|
||||
&self.api,
|
||||
slot_now + 1,
|
||||
header,
|
||||
header.clone(),
|
||||
hash,
|
||||
&authorities,
|
||||
randomness,
|
||||
epoch_index,
|
||||
self.config.c(),
|
||||
self.transaction_pool.as_ref().map(|x| &**x),
|
||||
)?;
|
||||
);
|
||||
|
||||
// if we have failed to check header using (presumably) current epoch AND we're probably in the next epoch
|
||||
// => check using next epoch
|
||||
// (this is only possible on the light client at epoch#0)
|
||||
if epoch_index == 0 && checked_header.is_err() {
|
||||
if let Some(Epoch { authorities, randomness, epoch_index, .. }) = maybe_next_epoch {
|
||||
let checked_header_next = check_header::<B, C, T>(
|
||||
&self.api,
|
||||
slot_now + 1,
|
||||
header,
|
||||
hash,
|
||||
&authorities,
|
||||
randomness,
|
||||
epoch_index,
|
||||
self.config.c(),
|
||||
self.transaction_pool.as_ref().map(|x| &**x),
|
||||
);
|
||||
|
||||
match checked_header_next {
|
||||
Ok(checked_header_next) => checked_header = Ok(checked_header_next),
|
||||
Err(_) => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let checked_header = checked_header?;
|
||||
match checked_header {
|
||||
CheckedHeader::Checked(pre_header, (pre_digest, seal)) => {
|
||||
let BabePreDigest { slot_number, .. } = pre_digest.as_babe_pre_digest()
|
||||
@@ -665,31 +689,75 @@ impl<B: BlockT, C, T> Verifier<B> for BabeVerifier<C, T> where
|
||||
}
|
||||
}
|
||||
|
||||
/// Regular BABE epoch or spanned genesis epoch.
|
||||
#[derive(Debug, Decode, Encode)]
|
||||
enum MaybeSpanEpoch {
|
||||
/// Genesis entry. Has the data for epoch#0 and epoch#1.
|
||||
Genesis(Epoch, Epoch),
|
||||
/// Regular entry. Has the data for the epoch after next (i.e. current epoch + 2).
|
||||
Regular(Epoch),
|
||||
}
|
||||
|
||||
impl MaybeSpanEpoch {
|
||||
pub fn deconstruct(self) -> (Epoch, Option<Epoch>) {
|
||||
match self {
|
||||
MaybeSpanEpoch::Genesis(epoch0, epoch1) => (epoch0, Some(epoch1)),
|
||||
MaybeSpanEpoch::Regular(epoch) => (epoch, None),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn into_regular(self) -> Option<Epoch> {
|
||||
match self {
|
||||
MaybeSpanEpoch::Regular(epoch) => Some(epoch),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Extract current epoch data from cache and fallback to querying the runtime
|
||||
/// if the cache isn't populated.
|
||||
fn epoch<B, C>(client: &C, at: &BlockId<B>) -> Result<Epoch, ConsensusError> where
|
||||
fn epoch<B, C>(client: &C, at: &BlockId<B>) -> Result<MaybeSpanEpoch, ConsensusError> where
|
||||
B: BlockT,
|
||||
C: ProvideRuntimeApi + ProvideCache<B>,
|
||||
C::Api: BabeApi<B>,
|
||||
{
|
||||
client
|
||||
.cache()
|
||||
.and_then(|cache| cache.get_at(&well_known_cache_keys::EPOCH, at)
|
||||
epoch_from_cache(client, at)
|
||||
.or_else(|| epoch_from_runtime(client, at).map(MaybeSpanEpoch::Regular))
|
||||
.ok_or(consensus_common::Error::InvalidAuthoritiesSet)
|
||||
}
|
||||
|
||||
/// Extract current epoch data from cache.
|
||||
fn epoch_from_cache<B, C>(client: &C, at: &BlockId<B>) -> Option<MaybeSpanEpoch> where
|
||||
B: BlockT,
|
||||
C: ProvideCache<B>,
|
||||
{
|
||||
// the epoch that is BABE-valid at the block is not the epoch that is cache-valid at the block
|
||||
// we need to go back for maximum two steps
|
||||
client.cache()
|
||||
.and_then(|cache| cache
|
||||
.get_at(&well_known_cache_keys::EPOCH, at)
|
||||
.and_then(|v| Decode::decode(&mut &v[..]).ok()))
|
||||
.or_else(|| {
|
||||
if client.runtime_api().has_api::<dyn BabeApi<B>>(at).unwrap_or(false) {
|
||||
let s = BabeApi::epoch(&*client.runtime_api(), at).ok()?;
|
||||
if s.authorities.is_empty() {
|
||||
error!("No authorities!");
|
||||
None
|
||||
} else {
|
||||
Some(s)
|
||||
}
|
||||
} else {
|
||||
error!("bad api!");
|
||||
None
|
||||
}
|
||||
}).ok_or(consensus_common::Error::InvalidAuthoritiesSet)
|
||||
}
|
||||
|
||||
/// Extract current epoch from runtime.
|
||||
fn epoch_from_runtime<B, C>(client: &C, at: &BlockId<B>) -> Option<Epoch> where
|
||||
B: BlockT,
|
||||
C: ProvideRuntimeApi,
|
||||
C::Api: BabeApi<B>,
|
||||
{
|
||||
if client.runtime_api().has_api::<dyn BabeApi<B>>(at).unwrap_or(false) {
|
||||
let s = BabeApi::epoch(&*client.runtime_api(), at).ok()?;
|
||||
if s.authorities.is_empty() {
|
||||
error!("No authorities!");
|
||||
None
|
||||
} else {
|
||||
Some(s)
|
||||
}
|
||||
} else {
|
||||
error!("bad api!");
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// The BABE import queue type.
|
||||
@@ -801,7 +869,7 @@ fn initialize_authorities_cache<B, C>(client: &C) -> Result<(), ConsensusError>
|
||||
|
||||
// check if we already have initialized the cache
|
||||
let genesis_id = BlockId::Number(Zero::zero());
|
||||
let genesis_epoch: Option<Epoch> = cache
|
||||
let genesis_epoch: Option<MaybeSpanEpoch> = cache
|
||||
.get_at(&well_known_cache_keys::EPOCH, &genesis_id)
|
||||
.and_then(|v| Decode::decode(&mut &v[..]).ok());
|
||||
if genesis_epoch.is_some() {
|
||||
@@ -814,7 +882,11 @@ fn initialize_authorities_cache<B, C>(client: &C) -> Result<(), ConsensusError>
|
||||
error,
|
||||
)));
|
||||
|
||||
let genesis_epoch = epoch(client, &genesis_id)?;
|
||||
let epoch0 = epoch_from_runtime(client, &genesis_id).ok_or(consensus_common::Error::InvalidAuthoritiesSet)?;
|
||||
let mut epoch1 = epoch0.clone();
|
||||
epoch1.epoch_index = 1;
|
||||
|
||||
let genesis_epoch = MaybeSpanEpoch::Genesis(epoch0, epoch1);
|
||||
cache.initialize(&well_known_cache_keys::EPOCH, genesis_epoch.encode())
|
||||
.map_err(map_err)
|
||||
}
|
||||
@@ -990,6 +1062,16 @@ impl<B, E, Block, I, RA, PRA> BlockImport<Block> for BabeBlockImport<B, E, Block
|
||||
// this way we can revert it if there's any error
|
||||
let mut old_epoch_changes = None;
|
||||
|
||||
if let Some(enacted_epoch) = enacted_epoch.as_ref() {
|
||||
let enacted_epoch = &enacted_epoch.data;
|
||||
|
||||
// update the current epoch in the client cache
|
||||
new_cache.insert(
|
||||
well_known_cache_keys::EPOCH,
|
||||
MaybeSpanEpoch::Regular(enacted_epoch.clone()).encode(),
|
||||
);
|
||||
}
|
||||
|
||||
if let Some(next_epoch) = next_epoch_digest {
|
||||
if let Some(enacted_epoch) = enacted_epoch {
|
||||
let enacted_epoch = &enacted_epoch.data;
|
||||
@@ -1000,27 +1082,6 @@ impl<B, E, Block, I, RA, PRA> BlockImport<Block> for BabeBlockImport<B, E, Block
|
||||
next_epoch.epoch_index,
|
||||
)));
|
||||
}
|
||||
|
||||
// update the current epoch in the client cache
|
||||
new_cache.insert(
|
||||
well_known_cache_keys::EPOCH,
|
||||
enacted_epoch.encode(),
|
||||
);
|
||||
|
||||
let current_epoch = epoch(&*self.api, &BlockId::Hash(parent_hash))?;
|
||||
|
||||
// if the authorities have changed then we populate the
|
||||
// `AUTHORITIES` key with the enacted epoch, so that the inner
|
||||
// `ImportBlock` can process it (`EPOCH` is specific to BABE).
|
||||
// e.g. in the case of GRANDPA it would require a justification
|
||||
// for the block, expecting that the authorities actually
|
||||
// changed.
|
||||
if current_epoch.authorities != enacted_epoch.authorities {
|
||||
new_cache.insert(
|
||||
well_known_cache_keys::AUTHORITIES,
|
||||
enacted_epoch.encode(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
old_epoch_changes = Some(epoch_changes.clone());
|
||||
@@ -1158,7 +1219,10 @@ pub mod test_helpers {
|
||||
C: ProvideRuntimeApi + ProvideCache<B>,
|
||||
C::Api: BabeApi<B>,
|
||||
{
|
||||
let epoch = epoch(client, at).unwrap();
|
||||
let epoch = match epoch(client, at).unwrap() {
|
||||
MaybeSpanEpoch::Regular(epoch) => epoch,
|
||||
_ => unreachable!("it is always Regular epoch on full nodes"),
|
||||
};
|
||||
|
||||
super::claim_slot(
|
||||
slot_number,
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
// https://github.com/paritytech/substrate/issues/2532
|
||||
#![allow(deprecated)]
|
||||
use super::*;
|
||||
use super::generic::DigestItem;
|
||||
use sr_primitives::generic::{self, DigestItem};
|
||||
|
||||
use babe_primitives::AuthorityPair;
|
||||
use client::{LongestChain, block_builder::BlockBuilder};
|
||||
@@ -341,7 +341,7 @@ fn authorities_call_works() {
|
||||
let client = test_client::new();
|
||||
|
||||
assert_eq!(client.info().chain.best_number, 0);
|
||||
assert_eq!(epoch(&client, &BlockId::Number(0)).unwrap().authorities, vec![
|
||||
assert_eq!(epoch(&client, &BlockId::Number(0)).unwrap().into_regular().unwrap().authorities, vec![
|
||||
(Keyring::Alice.public().into(), 1),
|
||||
(Keyring::Bob.public().into(), 1),
|
||||
(Keyring::Charlie.public().into(), 1),
|
||||
|
||||
Reference in New Issue
Block a user