Run cargo fmt on the whole code base (#9394)

* Run cargo fmt on the whole code base

* Second run

* Add CI check

* Fix compilation

* More unnecessary braces

* Handle weights

* Use --all

* Use correct attributes...

* Fix UI tests

* AHHHHHHHHH

* 🤦

* Docs

* Fix compilation

* 🤷

* Please stop

* 🤦 x 2

* More

* make rustfmt.toml consistent with polkadot

Co-authored-by: André Silva <andrerfosilva@gmail.com>
This commit is contained in:
Bastian Köcher
2021-07-21 16:32:32 +02:00
committed by GitHub
parent d451c38c1c
commit 7b56ab15b4
1010 changed files with 53339 additions and 51208 deletions
@@ -18,36 +18,37 @@
//! Module implementing the logic for verifying and importing AuRa blocks.
use crate::{AuthorityId, find_pre_digest, slot_author, aura_err, Error, authorities};
use std::{
sync::Arc, marker::PhantomData, hash::Hash, fmt::Debug,
};
use crate::{aura_err, authorities, find_pre_digest, slot_author, AuthorityId, Error};
use codec::{Codec, Decode, Encode};
use log::{debug, info, trace};
use prometheus_endpoint::Registry;
use codec::{Encode, Decode, Codec};
use sp_consensus::{
BlockImport, CanAuthorWith, ForkChoiceStrategy, BlockImportParams,
BlockOrigin, Error as ConsensusError,
import_queue::{
Verifier, BasicQueue, DefaultImportQueue, BoxJustificationImport,
},
};
use sc_client_api::{BlockOf, UsageProvider, backend::AuxStore};
use sp_blockchain::{well_known_cache_keys::{self, Id as CacheKeyId}, ProvideCache, HeaderBackend};
use sc_client_api::{backend::AuxStore, BlockOf, UsageProvider};
use sc_consensus_slots::{check_equivocation, CheckedHeader, InherentDataProviderExt};
use sc_telemetry::{telemetry, TelemetryHandle, CONSENSUS_DEBUG, CONSENSUS_TRACE};
use sp_api::{ApiExt, ProvideRuntimeApi};
use sp_block_builder::BlockBuilder as BlockBuilderApi;
use sp_runtime::{generic::{BlockId, OpaqueDigestItemId}, Justifications};
use sp_runtime::traits::{Block as BlockT, Header, DigestItemFor};
use sp_api::ProvideRuntimeApi;
use sp_blockchain::{
well_known_cache_keys::{self, Id as CacheKeyId},
HeaderBackend, ProvideCache,
};
use sp_consensus::{
import_queue::{BasicQueue, BoxJustificationImport, DefaultImportQueue, Verifier},
BlockImport, BlockImportParams, BlockOrigin, CanAuthorWith, Error as ConsensusError,
ForkChoiceStrategy,
};
use sp_consensus_aura::{
digests::CompatibleDigestItem, inherents::AuraInherentData, AuraApi, ConsensusLog,
AURA_ENGINE_ID,
};
use sp_consensus_slots::Slot;
use sp_core::crypto::Pair;
use sp_inherents::{CreateInherentDataProviders, InherentDataProvider as _};
use sc_telemetry::{telemetry, TelemetryHandle, CONSENSUS_TRACE, CONSENSUS_DEBUG};
use sc_consensus_slots::{CheckedHeader, check_equivocation, InherentDataProviderExt};
use sp_consensus_slots::Slot;
use sp_api::ApiExt;
use sp_consensus_aura::{
digests::CompatibleDigestItem, AuraApi, inherents::AuraInherentData,
ConsensusLog, AURA_ENGINE_ID,
use sp_runtime::{
generic::{BlockId, OpaqueDigestItemId},
traits::{Block as BlockT, DigestItemFor, Header},
Justifications,
};
use std::{fmt::Debug, hash::Hash, marker::PhantomData, sync::Arc};
/// check a header has been signed by the right key. If the slot is too far in the future, an error
/// will be returned. If it's successful, returns the pre-header and the digest item
@@ -61,7 +62,8 @@ fn check_header<C, B: BlockT, P: Pair>(
hash: B::Hash,
authorities: &[AuthorityId<P>],
check_for_equivocation: CheckForEquivocation,
) -> Result<CheckedHeader<B::Header, (Slot, DigestItemFor<B>)>, Error<B>> where
) -> Result<CheckedHeader<B::Header, (Slot, DigestItemFor<B>)>, Error<B>>
where
DigestItemFor<B>: CompatibleDigestItem<P::Signature>,
P::Signature: Codec,
C: sc_client_api::backend::AuxStore,
@@ -69,9 +71,7 @@ fn check_header<C, B: BlockT, P: Pair>(
{
let seal = header.digest_mut().pop().ok_or_else(|| Error::HeaderUnsealed(hash))?;
let sig = seal.as_aura_seal().ok_or_else(|| {
aura_err(Error::HeaderBadSeal(hash))
})?;
let sig = seal.as_aura_seal().ok_or_else(|| aura_err(Error::HeaderBadSeal(hash)))?;
let slot = find_pre_digest::<B, P::Signature>(&header)?;
@@ -81,20 +81,17 @@ fn check_header<C, B: BlockT, P: Pair>(
} else {
// check the signature is valid under the expected authority and
// chain state.
let expected_author = slot_author::<P>(slot, &authorities)
.ok_or_else(|| Error::SlotAuthorNotFound)?;
let expected_author =
slot_author::<P>(slot, &authorities).ok_or_else(|| Error::SlotAuthorNotFound)?;
let pre_hash = header.hash();
if P::verify(&sig, pre_hash.as_ref(), expected_author) {
if check_for_equivocation.check_for_equivocation() {
if let Some(equivocation_proof) = check_equivocation(
client,
slot_now,
slot,
&header,
expected_author,
).map_err(Error::Client)? {
if let Some(equivocation_proof) =
check_equivocation(client, slot_now, slot, &header, expected_author)
.map_err(Error::Client)?
{
info!(
target: "aura",
"Slot author is equivocating at slot {} with headers {:?} and {:?}",
@@ -141,7 +138,8 @@ impl<C, P, CAW, CIDP> AuraVerifier<C, P, CAW, CIDP> {
}
}
impl<C, P, CAW, CIDP> AuraVerifier<C, P, CAW, CIDP> where
impl<C, P, CAW, CIDP> AuraVerifier<C, P, CAW, CIDP>
where
P: Send + Sync + 'static,
CAW: Send + Sync + 'static,
CIDP: Send,
@@ -152,8 +150,10 @@ impl<C, P, CAW, CIDP> AuraVerifier<C, P, CAW, CIDP> where
block_id: BlockId<B>,
inherent_data: sp_inherents::InherentData,
create_inherent_data_providers: CIDP::InherentDataProviders,
) -> Result<(), Error<B>> where
C: ProvideRuntimeApi<B>, C::Api: BlockBuilderApi<B>,
) -> Result<(), Error<B>>
where
C: ProvideRuntimeApi<B>,
C::Api: BlockBuilderApi<B>,
CAW: CanAuthorWith<B>,
CIDP: CreateInherentDataProviders<B, ()>,
{
@@ -167,11 +167,11 @@ impl<C, P, CAW, CIDP> AuraVerifier<C, P, CAW, CIDP> where
return Ok(())
}
let inherent_res = self.client.runtime_api().check_inherents(
&block_id,
block,
inherent_data,
).map_err(|e| Error::Client(e.into()))?;
let inherent_res = self
.client
.runtime_api()
.check_inherents(&block_id, block, inherent_data)
.map_err(|e| Error::Client(e.into()))?;
if !inherent_res.ok() {
for (i, e) in inherent_res.into_errors() {
@@ -187,13 +187,14 @@ impl<C, P, CAW, CIDP> AuraVerifier<C, P, CAW, CIDP> where
}
#[async_trait::async_trait]
impl<B: BlockT, C, P, CAW, CIDP> Verifier<B> for AuraVerifier<C, P, CAW, CIDP> where
C: ProvideRuntimeApi<B> +
Send +
Sync +
sc_client_api::backend::AuxStore +
ProvideCache<B> +
BlockOf,
impl<B: BlockT, C, P, CAW, CIDP> Verifier<B> for AuraVerifier<C, P, CAW, CIDP>
where
C: ProvideRuntimeApi<B>
+ Send
+ Sync
+ sc_client_api::backend::AuxStore
+ ProvideCache<B>
+ BlockOf,
C::Api: BlockBuilderApi<B> + AuraApi<B, AuthorityId<P>> + ApiExt<B>,
DigestItemFor<B>: CompatibleDigestItem<P::Signature>,
P: Pair + Send + Sync + 'static,
@@ -215,15 +216,14 @@ impl<B: BlockT, C, P, CAW, CIDP> Verifier<B> for AuraVerifier<C, P, CAW, CIDP> w
let authorities = authorities(self.client.as_ref(), &BlockId::Hash(parent_hash))
.map_err(|e| format!("Could not fetch authorities at {:?}: {:?}", parent_hash, e))?;
let create_inherent_data_providers = self.create_inherent_data_providers
.create_inherent_data_providers(
parent_hash,
(),
)
let create_inherent_data_providers = self
.create_inherent_data_providers
.create_inherent_data_providers(parent_hash, ())
.await
.map_err(|e| Error::<B>::Client(sp_blockchain::Error::Application(e)))?;
let mut inherent_data = create_inherent_data_providers.create_inherent_data()
let mut inherent_data = create_inherent_data_providers
.create_inherent_data()
.map_err(Error::<B>::Inherent)?;
let slot_now = create_inherent_data_providers.slot();
@@ -238,7 +238,8 @@ impl<B: BlockT, C, P, CAW, CIDP> Verifier<B> for AuraVerifier<C, P, CAW, CIDP> w
hash,
&authorities[..],
self.check_for_equivocation,
).map_err(|e| e.to_string())?;
)
.map_err(|e| e.to_string())?;
match checked_header {
CheckedHeader::Checked(pre_header, (slot, seal)) => {
// if the body is passed through, we need to use the runtime
@@ -250,7 +251,8 @@ impl<B: BlockT, C, P, CAW, CIDP> Verifier<B> for AuraVerifier<C, P, CAW, CIDP> w
inherent_data.aura_replace_inherent_data(slot);
// skip the inherents verification if the runtime API is old.
if self.client
if self
.client
.runtime_api()
.has_api_with::<dyn BlockBuilderApi<B>, _>(
&BlockId::Hash(parent_hash),
@@ -263,7 +265,9 @@ impl<B: BlockT, C, P, CAW, CIDP> Verifier<B> for AuraVerifier<C, P, CAW, CIDP> w
BlockId::Hash(parent_hash),
inherent_data,
create_inherent_data_providers,
).await.map_err(|e| e.to_string())?;
)
.await
.map_err(|e| e.to_string())?;
}
let (_, inner_body) = block.deconstruct();
@@ -279,16 +283,18 @@ impl<B: BlockT, C, P, CAW, CIDP> Verifier<B> for AuraVerifier<C, P, CAW, CIDP> w
);
// Look for an authorities-change log.
let maybe_keys = pre_header.digest()
let maybe_keys = pre_header
.digest()
.logs()
.iter()
.filter_map(|l| l.try_to::<ConsensusLog<AuthorityId<P>>>(
OpaqueDigestItemId::Consensus(&AURA_ENGINE_ID)
))
.filter_map(|l| {
l.try_to::<ConsensusLog<AuthorityId<P>>>(OpaqueDigestItemId::Consensus(
&AURA_ENGINE_ID,
))
})
.find_map(|l| match l {
ConsensusLog::AuthoritiesChange(a) => Some(
vec![(well_known_cache_keys::AUTHORITIES, a.encode())]
),
ConsensusLog::AuthoritiesChange(a) =>
Some(vec![(well_known_cache_keys::AUTHORITIES, a.encode())]),
_ => None,
});
@@ -300,7 +306,7 @@ impl<B: BlockT, C, P, CAW, CIDP> Verifier<B> for AuraVerifier<C, P, CAW, CIDP> w
import_block.post_hash = Some(hash);
Ok((import_block, maybe_keys))
}
},
CheckedHeader::Deferred(a, b) => {
debug!(target: "aura", "Checking {:?} failed; {:?}, {:?}.", hash, a, b);
telemetry!(
@@ -312,7 +318,7 @@ impl<B: BlockT, C, P, CAW, CIDP> Verifier<B> for AuraVerifier<C, P, CAW, CIDP> w
"b" => ?b,
);
Err(format!("Header {:?} rejected: too far in the future", hash))
}
},
}
}
}
@@ -375,8 +381,9 @@ pub fn import_queue<'a, P, Block, I, C, S, CAW, CIDP>(
can_author_with,
check_for_equivocation,
telemetry,
}: ImportQueueParams<'a, Block, I, C, S, CAW, CIDP>
) -> Result<DefaultImportQueue<Block, C>, sp_consensus::Error> where
}: ImportQueueParams<'a, Block, I, C, S, CAW, CIDP>,
) -> Result<DefaultImportQueue<Block, C>, sp_consensus::Error>
where
Block: BlockT,
C::Api: BlockBuilderApi<Block> + AuraApi<Block, AuthorityId<P>> + ApiExt<Block>,
C: 'static
@@ -388,7 +395,7 @@ pub fn import_queue<'a, P, Block, I, C, S, CAW, CIDP>(
+ AuxStore
+ UsageProvider<Block>
+ HeaderBackend<Block>,
I: BlockImport<Block, Error=ConsensusError, Transaction = sp_api::TransactionFor<C, Block>>
I: BlockImport<Block, Error = ConsensusError, Transaction = sp_api::TransactionFor<C, Block>>
+ Send
+ Sync
+ 'static,
@@ -401,23 +408,15 @@ pub fn import_queue<'a, P, Block, I, C, S, CAW, CIDP>(
CIDP: CreateInherentDataProviders<Block, ()> + Sync + Send + 'static,
CIDP::InherentDataProviders: InherentDataProviderExt + Send + Sync,
{
let verifier = build_verifier::<P, _, _, _>(
BuildVerifierParams {
client,
create_inherent_data_providers,
can_author_with,
check_for_equivocation,
telemetry,
},
);
let verifier = build_verifier::<P, _, _, _>(BuildVerifierParams {
client,
create_inherent_data_providers,
can_author_with,
check_for_equivocation,
telemetry,
});
Ok(BasicQueue::new(
verifier,
Box::new(block_import),
justification_import,
spawner,
registry,
))
Ok(BasicQueue::new(verifier, Box::new(block_import), justification_import, spawner, registry))
}
/// Parameters of [`build_verifier`].
@@ -442,7 +441,7 @@ pub fn build_verifier<P, C, CIDP, CAW>(
can_author_with,
check_for_equivocation,
telemetry,
}: BuildVerifierParams<C, CIDP, CAW>
}: BuildVerifierParams<C, CIDP, CAW>,
) -> AuraVerifier<C, P, CAW, CIDP> {
AuraVerifier::<_, P, _, _>::new(
client,
+192 -169
View File
@@ -31,50 +31,53 @@
//! NOTE: Aura itself is designed to be generic over the crypto used.
#![forbid(missing_docs, unsafe_code)]
use std::{
sync::Arc, marker::PhantomData, hash::Hash, fmt::Debug, pin::Pin,
convert::{TryFrom, TryInto},
fmt::Debug,
hash::Hash,
marker::PhantomData,
pin::Pin,
sync::Arc,
};
use futures::prelude::*;
use log::{debug, trace};
use codec::{Encode, Decode, Codec};
use codec::{Codec, Decode, Encode};
use sp_consensus::{
BlockImport, Environment, Proposer, CanAuthorWith, ForkChoiceStrategy, BlockImportParams,
BlockOrigin, Error as ConsensusError, SelectChain, StateAction,
};
use sc_client_api::{backend::AuxStore, BlockOf, UsageProvider};
use sp_blockchain::{Result as CResult, ProvideCache, HeaderBackend};
use sp_core::crypto::Public;
use sp_application_crypto::{AppKey, AppPublic};
use sp_runtime::{generic::BlockId, traits::NumberFor};
use sp_runtime::traits::{Block as BlockT, Header, DigestItemFor, Zero, Member};
use sp_api::ProvideRuntimeApi;
use sp_core::crypto::Pair;
use sp_keystore::{SyncCryptoStorePtr, SyncCryptoStore};
use sp_inherents::CreateInherentDataProviders;
use sc_telemetry::TelemetryHandle;
use sc_consensus_slots::{
SlotInfo, BackoffAuthoringBlocksStrategy, InherentDataProviderExt, StorageChanges,
BackoffAuthoringBlocksStrategy, InherentDataProviderExt, SlotInfo, StorageChanges,
};
use sc_telemetry::TelemetryHandle;
use sp_api::ProvideRuntimeApi;
use sp_application_crypto::{AppKey, AppPublic};
use sp_blockchain::{HeaderBackend, ProvideCache, Result as CResult};
use sp_consensus::{
BlockImport, BlockImportParams, BlockOrigin, CanAuthorWith, Environment,
Error as ConsensusError, ForkChoiceStrategy, Proposer, SelectChain, StateAction,
};
use sp_consensus_slots::Slot;
use sp_core::crypto::{Pair, Public};
use sp_inherents::CreateInherentDataProviders;
use sp_keystore::{SyncCryptoStore, SyncCryptoStorePtr};
use sp_runtime::{
generic::BlockId,
traits::{Block as BlockT, DigestItemFor, Header, Member, NumberFor, Zero},
};
mod import_queue;
pub use sp_consensus_aura::{
ConsensusLog, AuraApi, AURA_ENGINE_ID, digests::CompatibleDigestItem,
inherents::{
InherentType as AuraInherent,
INHERENT_IDENTIFIER, InherentDataProvider,
},
};
pub use sp_consensus::SyncOracle;
pub use import_queue::{
ImportQueueParams, import_queue, CheckForEquivocation,
build_verifier, BuildVerifierParams, AuraVerifier,
build_verifier, import_queue, AuraVerifier, BuildVerifierParams, CheckForEquivocation,
ImportQueueParams,
};
pub use sc_consensus_slots::SlotProportion;
pub use sp_consensus::SyncOracle;
pub use sp_consensus_aura::{
digests::CompatibleDigestItem,
inherents::{InherentDataProvider, InherentType as AuraInherent, INHERENT_IDENTIFIER},
AuraApi, ConsensusLog, AURA_ENGINE_ID,
};
type AuthorityId<P> = <P as Pair>::Public;
@@ -82,7 +85,8 @@ type AuthorityId<P> = <P as Pair>::Public;
pub type SlotDuration = sc_consensus_slots::SlotDuration<sp_consensus_aura::SlotDuration>;
/// Get type of `SlotDuration` for Aura.
pub fn slot_duration<A, B, C>(client: &C) -> CResult<SlotDuration> where
pub fn slot_duration<A, B, C>(client: &C) -> CResult<SlotDuration>
where
A: Codec,
B: BlockT,
C: AuxStore + ProvideRuntimeApi<B> + UsageProvider<B>,
@@ -93,7 +97,9 @@ pub fn slot_duration<A, B, C>(client: &C) -> CResult<SlotDuration> where
/// Get slot author for given block along with authorities.
fn slot_author<P: Pair>(slot: Slot, authorities: &[AuthorityId<P>]) -> Option<&AuthorityId<P>> {
if authorities.is_empty() { return None }
if authorities.is_empty() {
return None
}
let idx = *slot % (authorities.len() as u64);
assert!(
@@ -101,9 +107,10 @@ fn slot_author<P: Pair>(slot: Slot, authorities: &[AuthorityId<P>]) -> Option<&A
"It is impossible to have a vector with length beyond the address space; qed",
);
let current_author = authorities.get(idx as usize)
.expect("authorities not empty; index constrained to list length;\
this is a valid index; qed");
let current_author = authorities.get(idx as usize).expect(
"authorities not empty; index constrained to list length;\
this is a valid index; qed",
);
Some(current_author)
}
@@ -325,9 +332,8 @@ where
type BlockImport = I;
type SyncOracle = SO;
type JustificationSyncLink = L;
type CreateProposer = Pin<Box<
dyn Future<Output = Result<E::Proposer, sp_consensus::Error>> + Send + 'static
>>;
type CreateProposer =
Pin<Box<dyn Future<Output = Result<E::Proposer, sp_consensus::Error>> + Send + 'static>>;
type Proposer = E::Proposer;
type Claim = P::Public;
type EpochData = Vec<AuthorityId<P>>;
@@ -376,22 +382,25 @@ where
slot: Slot,
_claim: &Self::Claim,
) -> Vec<sp_runtime::DigestItem<B::Hash>> {
vec![
<DigestItemFor<B> as CompatibleDigestItem<P::Signature>>::aura_pre_digest(slot),
]
vec![<DigestItemFor<B> as CompatibleDigestItem<P::Signature>>::aura_pre_digest(slot)]
}
fn block_import_params(&self) -> Box<dyn Fn(
B::Header,
&B::Hash,
Vec<B::Extrinsic>,
StorageChanges<sp_api::TransactionFor<C, B>, B>,
Self::Claim,
Self::EpochData,
) -> Result<
sp_consensus::BlockImportParams<B, sp_api::TransactionFor<C, B>>,
sp_consensus::Error> + Send + 'static>
{
fn block_import_params(
&self,
) -> Box<
dyn Fn(
B::Header,
&B::Hash,
Vec<B::Extrinsic>,
StorageChanges<sp_api::TransactionFor<C, B>, B>,
Self::Claim,
Self::EpochData,
) -> Result<
sp_consensus::BlockImportParams<B, sp_api::TransactionFor<C, B>>,
sp_consensus::Error,
> + Send
+ 'static,
> {
let keystore = self.keystore.clone();
Box::new(move |header, header_hash, body, storage_changes, public, _epoch| {
// sign the pre-sealed hash of the block and then
@@ -402,28 +411,28 @@ where
&*keystore,
<AuthorityId<P> as AppKey>::ID,
&public_type_pair,
header_hash.as_ref()
).map_err(|e| sp_consensus::Error::CannotSign(
public.clone(), e.to_string(),
))?
.ok_or_else(|| sp_consensus::Error::CannotSign(
public.clone(), "Could not find key in keystore.".into(),
))?;
let signature = signature.clone().try_into()
.map_err(|_| sp_consensus::Error::InvalidSignature(
signature, public
))?;
header_hash.as_ref(),
)
.map_err(|e| sp_consensus::Error::CannotSign(public.clone(), e.to_string()))?
.ok_or_else(|| {
sp_consensus::Error::CannotSign(
public.clone(),
"Could not find key in keystore.".into(),
)
})?;
let signature = signature
.clone()
.try_into()
.map_err(|_| sp_consensus::Error::InvalidSignature(signature, public))?;
let signature_digest_item = <
DigestItemFor<B> as CompatibleDigestItem<P::Signature>
>::aura_seal(signature);
let signature_digest_item =
<DigestItemFor<B> as CompatibleDigestItem<P::Signature>>::aura_seal(signature);
let mut import_block = BlockImportParams::new(BlockOrigin::Own, header);
import_block.post_digests.push(signature_digest_item);
import_block.body = Some(body);
import_block.state_action = StateAction::ApplyChanges(
sp_consensus::StorageChanges::Changes(storage_changes)
);
import_block.state_action =
StateAction::ApplyChanges(sp_consensus::StorageChanges::Changes(storage_changes));
import_block.fork_choice = Some(ForkChoiceStrategy::LongestChain);
Ok(import_block)
@@ -443,7 +452,7 @@ where
self.client.info().finalized_number,
slot,
self.logging_target(),
);
)
}
}
false
@@ -458,9 +467,11 @@ where
}
fn proposer(&mut self, block: &B::Header) -> Self::CreateProposer {
Box::pin(self.env.init(block).map_err(|e| {
sp_consensus::Error::ClientImport(format!("{:?}", e)).into()
}))
Box::pin(
self.env
.init(block)
.map_err(|e| sp_consensus::Error::ClientImport(format!("{:?}", e)).into()),
)
}
fn telemetry(&self) -> Option<TelemetryHandle> {
@@ -515,7 +526,7 @@ impl<B: BlockT> std::convert::From<Error<B>> for String {
fn find_pre_digest<B: BlockT, Signature: Codec>(header: &B::Header) -> Result<Slot, Error<B>> {
if header.number().is_zero() {
return Ok(0.into());
return Ok(0.into())
}
let mut pre_digest: Option<Slot> = None;
@@ -530,13 +541,15 @@ fn find_pre_digest<B: BlockT, Signature: Codec>(header: &B::Header) -> Result<Sl
pre_digest.ok_or_else(|| aura_err(Error::NoDigestFound))
}
fn authorities<A, B, C>(client: &C, at: &BlockId<B>) -> Result<Vec<A>, ConsensusError> where
fn authorities<A, B, C>(client: &C, at: &BlockId<B>) -> Result<Vec<A>, ConsensusError>
where
A: Codec + Debug,
B: BlockT,
C: ProvideRuntimeApi<B> + BlockOf + ProvideCache<B>,
C::Api: AuraApi<B, A>,
{
client.runtime_api()
client
.runtime_api()
.authorities(at)
.ok()
.ok_or_else(|| sp_consensus::Error::InvalidAuthoritiesSet.into())
@@ -545,26 +558,31 @@ fn authorities<A, B, C>(client: &C, at: &BlockId<B>) -> Result<Vec<A>, Consensus
#[cfg(test)]
mod tests {
use super::*;
use sp_consensus::{
NoNetwork as DummyOracle, Proposal, AlwaysCanAuthor, DisableProofRecording,
import_queue::BoxJustificationImport, SlotData,
};
use sc_network_test::{Block as TestBlock, *};
use sp_runtime::traits::{Block as BlockT, DigestFor};
use sc_network::config::ProtocolConfig;
use parking_lot::Mutex;
use sp_keyring::sr25519::Keyring;
use sc_client_api::BlockchainEvents;
use sp_consensus_aura::sr25519::AuthorityPair;
use sc_consensus_slots::{SimpleSlotWorker, BackoffAuthoringOnFinalizedHeadLagging};
use std::{task::Poll, time::{Instant, Duration}};
use sc_block_builder::BlockBuilderProvider;
use sp_runtime::traits::Header as _;
use substrate_test_runtime_client::{TestClient, runtime::{Header, H256}};
use sc_client_api::BlockchainEvents;
use sc_consensus_slots::{BackoffAuthoringOnFinalizedHeadLagging, SimpleSlotWorker};
use sc_keystore::LocalKeystore;
use sc_network::config::ProtocolConfig;
use sc_network_test::{Block as TestBlock, *};
use sp_application_crypto::key_types::AURA;
use sp_consensus::{
import_queue::BoxJustificationImport, AlwaysCanAuthor, DisableProofRecording,
NoNetwork as DummyOracle, Proposal, SlotData,
};
use sp_consensus_aura::sr25519::AuthorityPair;
use sp_inherents::InherentData;
use sp_keyring::sr25519::Keyring;
use sp_runtime::traits::{Block as BlockT, DigestFor, Header as _};
use sp_timestamp::InherentDataProvider as TimestampInherentDataProvider;
use std::{
task::Poll,
time::{Duration, Instant},
};
use substrate_test_runtime_client::{
runtime::{Header, H256},
TestClient,
};
type Error = sp_blockchain::Error;
@@ -576,19 +594,15 @@ mod tests {
type CreateProposer = futures::future::Ready<Result<DummyProposer, Error>>;
type Error = Error;
fn init(&mut self, parent_header: &<TestBlock as BlockT>::Header)
-> Self::CreateProposer
{
fn init(&mut self, parent_header: &<TestBlock as BlockT>::Header) -> Self::CreateProposer {
futures::future::ready(Ok(DummyProposer(parent_header.number + 1, self.0.clone())))
}
}
impl Proposer<TestBlock> for DummyProposer {
type Error = Error;
type Transaction = sc_client_api::TransactionFor<
substrate_test_runtime_client::Backend,
TestBlock
>;
type Transaction =
sc_client_api::TransactionFor<substrate_test_runtime_client::Backend, TestBlock>;
type Proposal = future::Ready<Result<Proposal<TestBlock, Self::Transaction, ()>, Error>>;
type ProofRecording = DisableProofRecording;
type Proof = ();
@@ -616,11 +630,13 @@ mod tests {
PeersFullClient,
AuthorityPair,
AlwaysCanAuthor,
Box<dyn CreateInherentDataProviders<
TestBlock,
(),
InherentDataProviders = (TimestampInherentDataProvider, InherentDataProvider)
>>
Box<
dyn CreateInherentDataProviders<
TestBlock,
(),
InherentDataProviders = (TimestampInherentDataProvider, InherentDataProvider),
>,
>,
>;
type AuraPeer = Peer<(), PeersClient>;
@@ -635,14 +651,15 @@ mod tests {
/// Create new test network with peers and given config.
fn from_config(_config: &ProtocolConfig) -> Self {
AuraTestNet {
peers: Vec::new(),
}
AuraTestNet { peers: Vec::new() }
}
fn make_verifier(&self, client: PeersClient, _cfg: &ProtocolConfig, _peer_data: &())
-> Self::Verifier
{
fn make_verifier(
&self,
client: PeersClient,
_cfg: &ProtocolConfig,
_peer_data: &(),
) -> Self::Verifier {
match client {
PeersClient::Full(client, _) => {
let slot_duration = slot_duration(&*client).expect("slot duration available");
@@ -668,7 +685,10 @@ mod tests {
}
}
fn make_block_import(&self, client: PeersClient) -> (
fn make_block_import(
&self,
client: PeersClient,
) -> (
BlockImportAdapter<Self::BlockImport>,
Option<BoxJustificationImport<Block>>,
Self::PeerData,
@@ -693,11 +713,7 @@ mod tests {
sp_tracing::try_init_simple();
let net = AuraTestNet::new(3);
let peers = &[
(0, Keyring::Alice),
(1, Keyring::Bob),
(2, Keyring::Charlie),
];
let peers = &[(0, Keyring::Alice), (1, Keyring::Bob), (2, Keyring::Charlie)];
let net = Arc::new(Mutex::new(net));
let mut import_notifications = Vec::new();
@@ -710,9 +726,9 @@ mod tests {
let client = peer.client().as_full().expect("full clients are created").clone();
let select_chain = peer.select_chain().expect("full client has a select chain");
let keystore_path = tempfile::tempdir().expect("Creates keystore path");
let keystore = Arc::new(LocalKeystore::open(keystore_path.path(), None)
.expect("Creates keystore."));
let keystore = Arc::new(
LocalKeystore::open(keystore_path.path(), None).expect("Creates keystore."),
);
SyncCryptoStore::sr25519_generate_new(&*keystore, AURA, Some(&key.to_seed()))
.expect("Creates authority key");
@@ -720,38 +736,46 @@ mod tests {
let environ = DummyFactory(client.clone());
import_notifications.push(
client.import_notification_stream()
.take_while(|n| future::ready(!(n.origin != BlockOrigin::Own && n.header.number() < &5)))
.for_each(move |_| future::ready(()))
client
.import_notification_stream()
.take_while(|n| {
future::ready(!(n.origin != BlockOrigin::Own && n.header.number() < &5))
})
.for_each(move |_| future::ready(())),
);
let slot_duration = slot_duration(&*client).expect("slot duration available");
aura_futures.push(start_aura::<AuthorityPair, _, _, _, _, _, _, _, _, _, _, _>(StartAuraParams {
slot_duration,
block_import: client.clone(),
select_chain,
client,
proposer_factory: environ,
sync_oracle: DummyOracle,
justification_sync_link: (),
create_inherent_data_providers: |_, _| async {
let timestamp = TimestampInherentDataProvider::from_system_time();
let slot = InherentDataProvider::from_timestamp_and_duration(
*timestamp,
Duration::from_secs(6),
);
aura_futures.push(
start_aura::<AuthorityPair, _, _, _, _, _, _, _, _, _, _, _>(StartAuraParams {
slot_duration,
block_import: client.clone(),
select_chain,
client,
proposer_factory: environ,
sync_oracle: DummyOracle,
justification_sync_link: (),
create_inherent_data_providers: |_, _| async {
let timestamp = TimestampInherentDataProvider::from_system_time();
let slot = InherentDataProvider::from_timestamp_and_duration(
*timestamp,
Duration::from_secs(6),
);
Ok((timestamp, slot))
},
force_authoring: false,
backoff_authoring_blocks: Some(BackoffAuthoringOnFinalizedHeadLagging::default()),
keystore,
can_author_with: sp_consensus::AlwaysCanAuthor,
block_proposal_slot_portion: SlotProportion::new(0.5),
max_block_proposal_slot_portion: None,
telemetry: None,
}).expect("Starts aura"));
Ok((timestamp, slot))
},
force_authoring: false,
backoff_authoring_blocks: Some(
BackoffAuthoringOnFinalizedHeadLagging::default(),
),
keystore,
can_author_with: sp_consensus::AlwaysCanAuthor,
block_proposal_slot_portion: SlotProportion::new(0.5),
max_block_proposal_slot_portion: None,
telemetry: None,
})
.expect("Starts aura"),
);
}
futures::executor::block_on(future::select(
@@ -759,10 +783,7 @@ mod tests {
net.lock().poll(cx);
Poll::<()>::Pending
}),
future::select(
future::join_all(aura_futures),
future::join_all(import_notifications)
)
future::select(future::join_all(aura_futures), future::join_all(import_notifications)),
));
}
@@ -771,11 +792,14 @@ mod tests {
let client = substrate_test_runtime_client::new();
assert_eq!(client.chain_info().best_number, 0);
assert_eq!(authorities(&client, &BlockId::Number(0)).unwrap(), vec![
Keyring::Alice.public().into(),
Keyring::Bob.public().into(),
Keyring::Charlie.public().into()
]);
assert_eq!(
authorities(&client, &BlockId::Number(0)).unwrap(),
vec![
Keyring::Alice.public().into(),
Keyring::Bob.public().into(),
Keyring::Charlie.public().into()
]
);
}
#[test]
@@ -785,12 +809,11 @@ mod tests {
let mut authorities = vec![
Keyring::Alice.public().into(),
Keyring::Bob.public().into(),
Keyring::Charlie.public().into()
Keyring::Charlie.public().into(),
];
let keystore_path = tempfile::tempdir().expect("Creates keystore path");
let keystore = LocalKeystore::open(keystore_path.path(), None)
.expect("Creates keystore.");
let keystore = LocalKeystore::open(keystore_path.path(), None).expect("Creates keystore.");
let public = SyncCryptoStore::sr25519_generate_new(&keystore, AuthorityPair::ID, None)
.expect("Key should be created");
authorities.push(public.into());
@@ -822,7 +845,7 @@ mod tests {
H256::from_low_u64_be(0),
H256::from_low_u64_be(0),
Default::default(),
Default::default()
Default::default(),
);
assert!(worker.claim_slot(&head, 0.into(), &authorities).is_none());
assert!(worker.claim_slot(&head, 1.into(), &authorities).is_none());
@@ -839,12 +862,13 @@ mod tests {
let net = AuraTestNet::new(4);
let keystore_path = tempfile::tempdir().expect("Creates keystore path");
let keystore = LocalKeystore::open(keystore_path.path(), None)
.expect("Creates keystore.");
let keystore = LocalKeystore::open(keystore_path.path(), None).expect("Creates keystore.");
SyncCryptoStore::sr25519_generate_new(
&keystore,
AuthorityPair::ID, Some(&Keyring::Alice.to_seed()),
).expect("Key should be created");
AuthorityPair::ID,
Some(&Keyring::Alice.to_seed()),
)
.expect("Key should be created");
let net = Arc::new(Mutex::new(net));
@@ -870,17 +894,16 @@ mod tests {
let head = client.header(&BlockId::Number(0)).unwrap().unwrap();
let res = futures::executor::block_on(worker.on_slot(
SlotInfo {
slot: 0.into(),
timestamp: 0.into(),
ends_at: Instant::now() + Duration::from_secs(100),
inherent_data: InherentData::new(),
duration: Duration::from_millis(1000),
chain_head: head,
block_size_limit: None,
}
)).unwrap();
let res = futures::executor::block_on(worker.on_slot(SlotInfo {
slot: 0.into(),
timestamp: 0.into(),
ends_at: Instant::now() + Duration::from_secs(100),
inherent_data: InherentData::new(),
duration: Duration::from_millis(1000),
chain_head: head,
block_size_limit: None,
}))
.unwrap();
// The returned block should be imported and we should be able to get its header by now.
assert!(client.header(&BlockId::Hash(res.block.hash())).unwrap().is_some());
+43 -75
View File
@@ -18,30 +18,21 @@
//! RPC api for babe.
use sc_consensus_babe::{Epoch, authorship, Config};
use futures::{FutureExt as _, TryFutureExt as _};
use jsonrpc_core::{
Error as RpcError,
futures::future as rpc_future,
};
use jsonrpc_core::{futures::future as rpc_future, Error as RpcError};
use jsonrpc_derive::rpc;
use sc_consensus_babe::{authorship, Config, Epoch};
use sc_consensus_epochs::{descendent_query, Epoch as EpochT, SharedEpochChanges};
use sp_consensus_babe::{
AuthorityId,
BabeApi as BabeRuntimeApi,
digests::PreDigest,
};
use serde::{Deserialize, Serialize};
use sp_core::{
crypto::Public,
};
use sp_application_crypto::AppKey;
use sp_keystore::{SyncCryptoStorePtr, SyncCryptoStore};
use sc_rpc_api::DenyUnsafe;
use sp_api::{ProvideRuntimeApi, BlockId};
use serde::{Deserialize, Serialize};
use sp_api::{BlockId, ProvideRuntimeApi};
use sp_application_crypto::AppKey;
use sp_blockchain::{Error as BlockChainError, HeaderBackend, HeaderMetadata};
use sp_consensus::{Error as ConsensusError, SelectChain};
use sp_consensus_babe::{digests::PreDigest, AuthorityId, BabeApi as BabeRuntimeApi};
use sp_core::crypto::Public;
use sp_keystore::{SyncCryptoStore, SyncCryptoStorePtr};
use sp_runtime::traits::{Block as BlockT, Header as _};
use sp_consensus::{SelectChain, Error as ConsensusError};
use sp_blockchain::{HeaderBackend, HeaderMetadata, Error as BlockChainError};
use std::{collections::HashMap, sync::Arc};
type FutureResult<T> = Box<dyn rpc_future::Future<Item = T, Error = RpcError> + Send>;
@@ -81,14 +72,7 @@ impl<B: BlockT, C, SC> BabeRpcHandler<B, C, SC> {
select_chain: SC,
deny_unsafe: DenyUnsafe,
) -> Self {
Self {
client,
shared_epoch_changes,
keystore,
babe_config,
select_chain,
deny_unsafe,
}
Self { client, shared_epoch_changes, keystore, babe_config, select_chain, deny_unsafe }
}
}
@@ -104,16 +88,10 @@ where
{
fn epoch_authorship(&self) -> FutureResult<HashMap<AuthorityId, EpochAuthorship>> {
if let Err(err) = self.deny_unsafe.check_if_safe() {
return Box::new(rpc_future::err(err.into()));
return Box::new(rpc_future::err(err.into()))
}
let (
babe_config,
keystore,
shared_epoch,
client,
select_chain,
) = (
let (babe_config, keystore, shared_epoch, client, select_chain) = (
self.babe_config.clone(),
self.keystore.clone(),
self.shared_epoch_changes.clone(),
@@ -126,14 +104,9 @@ where
.runtime_api()
.current_epoch_start(&BlockId::Hash(header.hash()))
.map_err(|err| Error::StringError(format!("{:?}", err)))?;
let epoch = epoch_data(
&shared_epoch,
&client,
&babe_config,
*epoch_start,
&select_chain,
)
.await?;
let epoch =
epoch_data(&shared_epoch, &client, &babe_config, *epoch_start, &select_chain)
.await?;
let (epoch_start, epoch_end) = (epoch.start_slot(), epoch.end_slot());
let mut claims: HashMap<AuthorityId, EpochAuthorship> = HashMap::new();
@@ -163,10 +136,10 @@ where
match claim {
PreDigest::Primary { .. } => {
claims.entry(key).or_default().primary.push(slot);
}
},
PreDigest::SecondaryPlain { .. } => {
claims.entry(key).or_default().secondary.push(slot);
}
},
PreDigest::SecondaryVRF { .. } => {
claims.entry(key).or_default().secondary_vrf.push(slot.into());
},
@@ -199,7 +172,7 @@ pub enum Error {
/// Consensus error
Consensus(ConsensusError),
/// Errors that can be formatted as a String
StringError(String)
StringError(String),
}
impl From<Error> for jsonrpc_core::Error {
@@ -226,13 +199,15 @@ where
SC: SelectChain<B>,
{
let parent = select_chain.best_chain().await?;
epoch_changes.shared_data().epoch_data_for_child_of(
descendent_query(&**client),
&parent.hash(),
parent.number().clone(),
slot.into(),
|slot| Epoch::genesis(&babe_config, slot),
)
epoch_changes
.shared_data()
.epoch_data_for_child_of(
descendent_query(&**client),
&parent.hash(),
parent.number().clone(),
slot.into(),
|slot| Epoch::genesis(&babe_config, slot),
)
.map_err(|e| Error::Consensus(ConsensusError::ChainLookup(format!("{:?}", e))))?
.ok_or(Error::Consensus(ConsensusError::InvalidAuthoritiesSet))
}
@@ -240,31 +215,27 @@ where
#[cfg(test)]
mod tests {
use super::*;
use substrate_test_runtime_client::{
runtime::Block,
Backend,
DefaultTestClientBuilderExt,
TestClient,
TestClientBuilderExt,
TestClientBuilder,
};
use sp_application_crypto::AppPair;
use sp_keyring::Sr25519Keyring;
use sp_core::{crypto::key_types::BABE};
use sp_keystore::{SyncCryptoStorePtr, SyncCryptoStore};
use sc_keystore::LocalKeystore;
use sp_application_crypto::AppPair;
use sp_core::crypto::key_types::BABE;
use sp_keyring::Sr25519Keyring;
use sp_keystore::{SyncCryptoStore, SyncCryptoStorePtr};
use substrate_test_runtime_client::{
runtime::Block, Backend, DefaultTestClientBuilderExt, TestClient, TestClientBuilder,
TestClientBuilderExt,
};
use std::sync::Arc;
use sc_consensus_babe::{Config, block_import, AuthorityPair};
use jsonrpc_core::IoHandler;
use sc_consensus_babe::{block_import, AuthorityPair, Config};
use std::sync::Arc;
/// creates keystore backed by a temp file
fn create_temp_keystore<P: AppPair>(
authority: Sr25519Keyring,
) -> (SyncCryptoStorePtr, tempfile::TempDir) {
let keystore_path = tempfile::tempdir().expect("Creates keystore path");
let keystore = Arc::new(LocalKeystore::open(keystore_path.path(), None)
.expect("Creates keystore"));
let keystore =
Arc::new(LocalKeystore::open(keystore_path.path(), None).expect("Creates keystore"));
SyncCryptoStore::sr25519_generate_new(&*keystore, BABE, Some(&authority.to_seed()))
.expect("Creates authority key");
@@ -272,17 +243,14 @@ mod tests {
}
fn test_babe_rpc_handler(
deny_unsafe: DenyUnsafe
deny_unsafe: DenyUnsafe,
) -> BabeRpcHandler<Block, TestClient, sc_consensus::LongestChain<Backend, Block>> {
let builder = TestClientBuilder::new();
let (client, longest_chain) = builder.build_with_longest_chain();
let client = Arc::new(client);
let config = Config::get_or_compute(&*client).expect("config available");
let (_, link) = block_import(
config.clone(),
client.clone(),
client.clone(),
).expect("can initialize block-import");
let (_, link) = block_import(config.clone(), client.clone(), client.clone())
.expect("can initialize block-import");
let epoch_changes = link.epoch_changes().clone();
let keystore = create_temp_keystore::<AuthorityPair>(Sr25519Keyring::Alice).0;
@@ -18,23 +18,17 @@
//! BABE authority selection and slot claiming.
use super::Epoch;
use codec::Encode;
use schnorrkel::{keys::PublicKey, vrf::VRFInOut};
use sp_application_crypto::AppKey;
use sp_consensus_babe::{
BABE_VRF_PREFIX, AuthorityId, BabeAuthorityWeight, make_transcript, make_transcript_data,
Slot,
};
use sp_consensus_babe::digests::{
PreDigest, PrimaryPreDigest, SecondaryPlainPreDigest, SecondaryVRFPreDigest,
digests::{PreDigest, PrimaryPreDigest, SecondaryPlainPreDigest, SecondaryVRFPreDigest},
make_transcript, make_transcript_data, AuthorityId, BabeAuthorityWeight, Slot, BABE_VRF_PREFIX,
};
use sp_consensus_vrf::schnorrkel::{VRFOutput, VRFProof};
use sp_core::{U256, blake2_256, crypto::Public};
use sp_keystore::{SyncCryptoStorePtr, SyncCryptoStore};
use codec::Encode;
use schnorrkel::{
keys::PublicKey,
vrf::VRFInOut,
};
use super::Epoch;
use sp_core::{blake2_256, crypto::Public, U256};
use sp_keystore::{SyncCryptoStore, SyncCryptoStorePtr};
/// Calculates the primary selection threshold for a given authority, taking
/// into account `c` (`1 - c` represents the probability of a slot being empty).
@@ -49,8 +43,7 @@ pub(super) fn calculate_primary_threshold(
let c = c.0 as f64 / c.1 as f64;
let theta =
authorities[authority_index].1 as f64 /
let theta = authorities[authority_index].1 as f64 /
authorities.iter().map(|(_, weight)| weight).sum::<u64>() as f64;
assert!(theta > 0.0, "authority with weight 0.");
@@ -74,14 +67,14 @@ pub(super) fn calculate_primary_threshold(
"returns None when the given value is negative; \
p is defined as `1 - n` where n is defined in (0, 1]; \
p must be a value in [0, 1); \
qed."
qed.",
);
let denom = p.denom().to_biguint().expect(
"returns None when the given value is negative; \
p is defined as `1 - n` where n is defined in (0, 1]; \
p must be a value in [0, 1); \
qed."
qed.",
);
((BigUint::one() << 128) * numer / denom).to_u128().expect(
@@ -108,7 +101,7 @@ pub(super) fn secondary_slot_author(
randomness: [u8; 32],
) -> Option<&AuthorityId> {
if authorities.is_empty() {
return None;
return None
}
let rand = U256::from((randomness, slot).using_encoded(blake2_256));
@@ -116,9 +109,10 @@ pub(super) fn secondary_slot_author(
let authorities_len = U256::from(authorities.len());
let idx = rand % authorities_len;
let expected_author = authorities.get(idx.as_u32() as usize)
.expect("authorities not empty; index constrained to list length; \
this is a valid index; qed");
let expected_author = authorities.get(idx.as_u32() as usize).expect(
"authorities not empty; index constrained to list length; \
this is a valid index; qed",
);
Some(&expected_author.0)
}
@@ -136,23 +130,15 @@ fn claim_secondary_slot(
let Epoch { authorities, randomness, epoch_index, .. } = epoch;
if authorities.is_empty() {
return None;
return None
}
let expected_author = secondary_slot_author(
slot,
authorities,
*randomness,
)?;
let expected_author = secondary_slot_author(slot, authorities, *randomness)?;
for (authority_id, authority_index) in keys {
if authority_id == expected_author {
let pre_digest = if author_secondary_vrf {
let transcript_data = make_transcript_data(
randomness,
slot,
*epoch_index,
);
let transcript_data = make_transcript_data(randomness, slot, *epoch_index);
let result = SyncCryptoStore::sr25519_vrf_sign(
&**keystore,
AuthorityId::ID,
@@ -169,7 +155,10 @@ fn claim_secondary_slot(
} else {
None
}
} else if SyncCryptoStore::has_keys(&**keystore, &[(authority_id.to_raw_vec(), AuthorityId::ID)]) {
} else if SyncCryptoStore::has_keys(
&**keystore,
&[(authority_id.to_raw_vec(), AuthorityId::ID)],
) {
Some(PreDigest::SecondaryPlain(SecondaryPlainPreDigest {
slot,
authority_index: *authority_index as u32,
@@ -179,7 +168,7 @@ fn claim_secondary_slot(
};
if let Some(pre_digest) = pre_digest {
return Some((pre_digest, authority_id.clone()));
return Some((pre_digest, authority_id.clone()))
}
}
}
@@ -196,7 +185,9 @@ pub fn claim_slot(
epoch: &Epoch,
keystore: &SyncCryptoStorePtr,
) -> Option<(PreDigest, AuthorityId)> {
let authorities = epoch.authorities.iter()
let authorities = epoch
.authorities
.iter()
.enumerate()
.map(|(index, a)| (a.0.clone(), index))
.collect::<Vec<_>>();
@@ -211,22 +202,21 @@ pub fn claim_slot_using_keys(
keystore: &SyncCryptoStorePtr,
keys: &[(AuthorityId, usize)],
) -> Option<(PreDigest, AuthorityId)> {
claim_primary_slot(slot, epoch, epoch.config.c, keystore, &keys)
.or_else(|| {
if epoch.config.allowed_slots.is_secondary_plain_slots_allowed() ||
epoch.config.allowed_slots.is_secondary_vrf_slots_allowed()
{
claim_secondary_slot(
slot,
&epoch,
keys,
&keystore,
epoch.config.allowed_slots.is_secondary_vrf_slots_allowed(),
)
} else {
None
}
})
claim_primary_slot(slot, epoch, epoch.config.c, keystore, &keys).or_else(|| {
if epoch.config.allowed_slots.is_secondary_plain_slots_allowed() ||
epoch.config.allowed_slots.is_secondary_vrf_slots_allowed()
{
claim_secondary_slot(
slot,
&epoch,
keys,
&keystore,
epoch.config.allowed_slots.is_secondary_vrf_slots_allowed(),
)
} else {
None
}
})
}
/// Claim a primary slot if it is our turn. Returns `None` if it is not our turn.
@@ -243,16 +233,8 @@ fn claim_primary_slot(
let Epoch { authorities, randomness, epoch_index, .. } = epoch;
for (authority_id, authority_index) in keys {
let transcript = make_transcript(
randomness,
slot,
*epoch_index
);
let transcript_data = make_transcript_data(
randomness,
slot,
*epoch_index
);
let transcript = make_transcript(randomness, slot, *epoch_index);
let transcript_data = make_transcript_data(randomness, slot, *epoch_index);
// Compute the threshold we will use.
//
// We already checked that authorities contains `key.public()`, so it can't
@@ -279,7 +261,7 @@ fn claim_primary_slot(
authority_index: *authority_index as u32,
});
return Some((pre_digest, authority_id.clone()));
return Some((pre_digest, authority_id.clone()))
}
}
}
@@ -290,10 +272,10 @@ fn claim_primary_slot(
#[cfg(test)]
mod tests {
use super::*;
use std::sync::Arc;
use sp_core::{sr25519::Pair, crypto::Pair as _};
use sp_consensus_babe::{AuthorityId, BabeEpochConfiguration, AllowedSlots};
use sc_keystore::LocalKeystore;
use sp_consensus_babe::{AllowedSlots, AuthorityId, BabeEpochConfiguration};
use sp_core::{crypto::Pair as _, sr25519::Pair};
use std::sync::Arc;
#[test]
fn claim_secondary_plain_slot_works() {
@@ -302,7 +284,8 @@ mod tests {
&*keystore,
AuthorityId::ID,
Some(sp_core::crypto::DEV_PHRASE),
).unwrap();
)
.unwrap();
let authorities = vec![
(AuthorityId::from(Pair::generate().0.public()), 5),
@@ -18,15 +18,15 @@
//! Schema for BABE epoch changes in the aux-db.
use log::info;
use codec::{Decode, Encode};
use log::info;
use crate::{migration::EpochV0, Epoch};
use sc_client_api::backend::AuxStore;
use sp_blockchain::{Result as ClientResult, Error as ClientError};
use sp_runtime::traits::Block as BlockT;
use sc_consensus_epochs::{migration::EpochChangesForV0, EpochChangesFor, SharedEpochChanges};
use sp_blockchain::{Error as ClientError, Result as ClientResult};
use sp_consensus_babe::{BabeBlockWeight, BabeGenesisConfiguration};
use sc_consensus_epochs::{EpochChangesFor, SharedEpochChanges, migration::EpochChangesForV0};
use crate::{Epoch, migration::EpochV0};
use sp_runtime::traits::Block as BlockT;
const BABE_EPOCH_CHANGES_VERSION: &[u8] = b"babe_epoch_changes_version";
const BABE_EPOCH_CHANGES_KEY: &[u8] = b"babe_epoch_changes";
@@ -38,16 +38,16 @@ pub fn block_weight_key<H: Encode>(block_hash: H) -> Vec<u8> {
}
fn load_decode<B, T>(backend: &B, key: &[u8]) -> ClientResult<Option<T>>
where
B: AuxStore,
T: Decode,
where
B: AuxStore,
T: Decode,
{
let corrupt = |e: codec::Error| {
ClientError::Backend(format!("BABE DB is corrupted. Decode error: {}", e))
};
match backend.get_aux(key)? {
None => Ok(None),
Some(t) => T::decode(&mut &t[..]).map(Some).map_err(corrupt)
Some(t) => T::decode(&mut &t[..]).map(Some).map_err(corrupt),
}
}
@@ -59,32 +59,26 @@ pub fn load_epoch_changes<Block: BlockT, B: AuxStore>(
let version = load_decode::<_, u32>(backend, BABE_EPOCH_CHANGES_VERSION)?;
let maybe_epoch_changes = match version {
None => load_decode::<_, EpochChangesForV0<Block, EpochV0>>(
backend,
BABE_EPOCH_CHANGES_KEY,
)?.map(|v0| v0.migrate().map(|_, _, epoch| epoch.migrate(config))),
Some(1) => load_decode::<_, EpochChangesFor<Block, EpochV0>>(
backend,
BABE_EPOCH_CHANGES_KEY,
)?.map(|v1| v1.map(|_, _, epoch| epoch.migrate(config))),
Some(BABE_EPOCH_CHANGES_CURRENT_VERSION) => load_decode::<_, EpochChangesFor<Block, Epoch>>(
backend,
BABE_EPOCH_CHANGES_KEY,
)?,
Some(other) => {
return Err(ClientError::Backend(
format!("Unsupported BABE DB version: {:?}", other)
))
},
None =>
load_decode::<_, EpochChangesForV0<Block, EpochV0>>(backend, BABE_EPOCH_CHANGES_KEY)?
.map(|v0| v0.migrate().map(|_, _, epoch| epoch.migrate(config))),
Some(1) =>
load_decode::<_, EpochChangesFor<Block, EpochV0>>(backend, BABE_EPOCH_CHANGES_KEY)?
.map(|v1| v1.map(|_, _, epoch| epoch.migrate(config))),
Some(BABE_EPOCH_CHANGES_CURRENT_VERSION) =>
load_decode::<_, EpochChangesFor<Block, Epoch>>(backend, BABE_EPOCH_CHANGES_KEY)?,
Some(other) =>
return Err(ClientError::Backend(format!("Unsupported BABE DB version: {:?}", other))),
};
let epoch_changes = SharedEpochChanges::<Block, Epoch>::new(maybe_epoch_changes.unwrap_or_else(|| {
info!(
target: "babe",
"👶 Creating empty BABE epoch changes on what appears to be first startup.",
);
EpochChangesFor::<Block, Epoch>::default()
}));
let epoch_changes =
SharedEpochChanges::<Block, Epoch>::new(maybe_epoch_changes.unwrap_or_else(|| {
info!(
target: "babe",
"👶 Creating empty BABE epoch changes on what appears to be first startup.",
);
EpochChangesFor::<Block, Epoch>::default()
}));
// rebalance the tree after deserialization. this isn't strictly necessary
// since the tree is now rebalanced on every update operation. but since the
@@ -99,15 +93,16 @@ pub fn load_epoch_changes<Block: BlockT, B: AuxStore>(
pub(crate) fn write_epoch_changes<Block: BlockT, F, R>(
epoch_changes: &EpochChangesFor<Block, Epoch>,
write_aux: F,
) -> R where
) -> R
where
F: FnOnce(&[(&'static [u8], &[u8])]) -> R,
{
BABE_EPOCH_CHANGES_CURRENT_VERSION.using_encoded(|version| {
let encoded_epoch_changes = epoch_changes.encode();
write_aux(
&[(BABE_EPOCH_CHANGES_KEY, encoded_epoch_changes.as_slice()),
(BABE_EPOCH_CHANGES_VERSION, version)],
)
write_aux(&[
(BABE_EPOCH_CHANGES_KEY, encoded_epoch_changes.as_slice()),
(BABE_EPOCH_CHANGES_VERSION, version),
])
})
}
@@ -116,15 +111,12 @@ pub(crate) fn write_block_weight<H: Encode, F, R>(
block_hash: H,
block_weight: BabeBlockWeight,
write_aux: F,
) -> R where
) -> R
where
F: FnOnce(&[(Vec<u8>, &[u8])]) -> R,
{
let key = block_weight_key(block_hash);
block_weight.using_encoded(|s|
write_aux(
&[(key, s)],
)
)
block_weight.using_encoded(|s| write_aux(&[(key, s)]))
}
/// Load the cumulative chain-weight associated with a block.
@@ -140,13 +132,13 @@ mod test {
use super::*;
use crate::migration::EpochV0;
use fork_tree::ForkTree;
use substrate_test_runtime_client;
use sc_consensus_epochs::{EpochHeader, PersistedEpoch, PersistedEpochHeader};
use sc_network_test::Block as TestBlock;
use sp_consensus::Error as ConsensusError;
use sp_consensus_babe::{AllowedSlots, BabeGenesisConfiguration};
use sp_core::H256;
use sp_runtime::traits::NumberFor;
use sp_consensus_babe::{AllowedSlots, BabeGenesisConfiguration};
use sc_consensus_epochs::{PersistedEpoch, PersistedEpochHeader, EpochHeader};
use sp_consensus::Error as ConsensusError;
use sc_network_test::Block as TestBlock;
use substrate_test_runtime_client;
#[test]
fn load_decode_from_v0_epoch_changes() {
@@ -159,26 +151,30 @@ mod test {
};
let client = substrate_test_runtime_client::new();
let mut v0_tree = ForkTree::<H256, NumberFor<TestBlock>, _>::new();
v0_tree.import::<_, ConsensusError>(
Default::default(),
Default::default(),
PersistedEpoch::Regular(epoch),
&|_, _| Ok(false), // Test is single item only so this can be set to false.
).unwrap();
v0_tree
.import::<_, ConsensusError>(
Default::default(),
Default::default(),
PersistedEpoch::Regular(epoch),
&|_, _| Ok(false), // Test is single item only so this can be set to false.
)
.unwrap();
client.insert_aux(
&[(BABE_EPOCH_CHANGES_KEY,
&EpochChangesForV0::<TestBlock, EpochV0>::from_raw(v0_tree).encode()[..])],
&[],
).unwrap();
client
.insert_aux(
&[(
BABE_EPOCH_CHANGES_KEY,
&EpochChangesForV0::<TestBlock, EpochV0>::from_raw(v0_tree).encode()[..],
)],
&[],
)
.unwrap();
assert_eq!(
load_decode::<_, u32>(&client, BABE_EPOCH_CHANGES_VERSION).unwrap(),
None,
);
assert_eq!(load_decode::<_, u32>(&client, BABE_EPOCH_CHANGES_VERSION).unwrap(), None,);
let epoch_changes = load_epoch_changes::<TestBlock, _>(
&client, &BabeGenesisConfiguration {
&client,
&BabeGenesisConfiguration {
slot_duration: 10,
epoch_length: 4,
c: (3, 10),
@@ -186,10 +182,12 @@ mod test {
randomness: Default::default(),
allowed_slots: AllowedSlots::PrimaryAndSecondaryPlainSlots,
},
).unwrap();
)
.unwrap();
assert!(
epoch_changes.shared_data()
epoch_changes
.shared_data()
.tree()
.iter()
.map(|(_, _, epoch)| epoch.clone())
@@ -200,16 +198,10 @@ mod test {
})],
); // PersistedEpochHeader does not implement Debug, so we use assert! directly.
write_epoch_changes::<TestBlock, _, _>(
&epoch_changes.shared_data(),
|values| {
client.insert_aux(values, &[]).unwrap();
},
);
write_epoch_changes::<TestBlock, _, _>(&epoch_changes.shared_data(), |values| {
client.insert_aux(values, &[]).unwrap();
});
assert_eq!(
load_decode::<_, u32>(&client, BABE_EPOCH_CHANGES_VERSION).unwrap(),
Some(2),
);
assert_eq!(load_decode::<_, u32>(&client, BABE_EPOCH_CHANGES_VERSION).unwrap(), Some(2),);
}
}
File diff suppressed because it is too large Load Diff
@@ -16,12 +16,12 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
use codec::{Encode, Decode};
use sc_consensus_epochs::Epoch as EpochT;
use crate::{
Epoch, AuthorityId, BabeAuthorityWeight, BabeGenesisConfiguration,
BabeEpochConfiguration, VRF_OUTPUT_LENGTH, NextEpochDescriptor,
AuthorityId, BabeAuthorityWeight, BabeEpochConfiguration, BabeGenesisConfiguration, Epoch,
NextEpochDescriptor, VRF_OUTPUT_LENGTH,
};
use codec::{Decode, Encode};
use sc_consensus_epochs::Epoch as EpochT;
use sp_consensus_slots::Slot;
/// BABE epoch information, version 0.
@@ -43,10 +43,7 @@ impl EpochT for EpochV0 {
type NextEpochDescriptor = NextEpochDescriptor;
type Slot = Slot;
fn increment(
&self,
descriptor: NextEpochDescriptor
) -> EpochV0 {
fn increment(&self, descriptor: NextEpochDescriptor) -> EpochV0 {
EpochV0 {
epoch_index: self.epoch_index + 1,
start_slot: self.start_slot + self.duration,
@@ -74,10 +71,7 @@ impl EpochV0 {
duration: self.duration,
authorities: self.authorities,
randomness: self.randomness,
config: BabeEpochConfiguration {
c: config.c,
allowed_slots: config.allowed_slots,
},
config: BabeEpochConfiguration { c: config.c, allowed_slots: config.allowed_slots },
}
}
}
+207 -204
View File
@@ -23,35 +23,33 @@
#![allow(deprecated)]
use super::*;
use authorship::claim_slot;
use sp_core::crypto::Pair;
use sp_keystore::{
SyncCryptoStore,
vrf::make_transcript as transcript_from_data,
use futures::executor::block_on;
use log::debug;
use rand::RngCore;
use rand_chacha::{rand_core::SeedableRng, ChaChaRng};
use sc_block_builder::{BlockBuilder, BlockBuilderProvider};
use sc_client_api::{backend::TransactionFor, BlockchainEvents};
use sc_consensus_slots::BackoffAuthoringOnFinalizedHeadLagging;
use sc_keystore::LocalKeystore;
use sc_network::config::ProtocolConfig;
use sc_network_test::{Block as TestBlock, *};
use sp_application_crypto::key_types::BABE;
use sp_consensus::{
import_queue::{BoxBlockImport, BoxJustificationImport},
AlwaysCanAuthor, DisableProofRecording, NoNetwork as DummyOracle, Proposal,
};
use sp_consensus_babe::{
AuthorityPair, Slot, AllowedSlots, make_transcript, make_transcript_data,
inherents::InherentDataProvider,
inherents::InherentDataProvider, make_transcript, make_transcript_data, AllowedSlots,
AuthorityPair, Slot,
};
use sc_consensus_slots::BackoffAuthoringOnFinalizedHeadLagging;
use sc_block_builder::{BlockBuilder, BlockBuilderProvider};
use sp_consensus::{
NoNetwork as DummyOracle, Proposal, DisableProofRecording, AlwaysCanAuthor,
import_queue::{BoxBlockImport, BoxJustificationImport},
use sp_core::crypto::Pair;
use sp_keystore::{vrf::make_transcript as transcript_from_data, SyncCryptoStore};
use sp_runtime::{
generic::DigestItem,
traits::{Block as BlockT, DigestFor},
};
use sc_network_test::{Block as TestBlock, *};
use sc_network::config::ProtocolConfig;
use sp_runtime::{generic::DigestItem, traits::{Block as BlockT, DigestFor}};
use sc_client_api::{BlockchainEvents, backend::TransactionFor};
use log::debug;
use std::{time::Duration, cell::RefCell, task::Poll};
use rand::RngCore;
use rand_chacha::{
rand_core::SeedableRng, ChaChaRng,
};
use sc_keystore::LocalKeystore;
use sp_application_crypto::key_types::BABE;
use futures::executor::block_on;
use sp_timestamp::InherentDataProvider as TimestampInherentDataProvider;
use std::{cell::RefCell, task::Poll, time::Duration};
type Item = DigestItem<Hash>;
@@ -95,10 +93,7 @@ impl Environment<TestBlock> for DummyFactory {
type Proposer = DummyProposer;
type Error = Error;
fn init(&mut self, parent_header: &<TestBlock as BlockT>::Header)
-> Self::CreateProposer
{
fn init(&mut self, parent_header: &<TestBlock as BlockT>::Header) -> Self::CreateProposer {
let parent_slot = crate::find_pre_digest::<TestBlock>(parent_header)
.expect("parent header has a pre-digest")
.slot();
@@ -113,23 +108,24 @@ impl Environment<TestBlock> for DummyFactory {
}
impl DummyProposer {
fn propose_with(&mut self, pre_digests: DigestFor<TestBlock>)
-> future::Ready<
Result<
Proposal<
TestBlock,
sc_client_api::TransactionFor<substrate_test_runtime_client::Backend, TestBlock>,
()
>,
Error
>
>
{
let block_builder = self.factory.client.new_block_at(
&BlockId::Hash(self.parent_hash),
pre_digests,
false,
).unwrap();
fn propose_with(
&mut self,
pre_digests: DigestFor<TestBlock>,
) -> future::Ready<
Result<
Proposal<
TestBlock,
sc_client_api::TransactionFor<substrate_test_runtime_client::Backend, TestBlock>,
(),
>,
Error,
>,
> {
let block_builder = self
.factory
.client
.new_block_at(&BlockId::Hash(self.parent_hash), pre_digests, false)
.unwrap();
let mut block = match block_builder.build().map_err(|e| e.into()) {
Ok(b) => b.block,
@@ -143,13 +139,14 @@ impl DummyProposer {
// figure out if we should add a consensus digest, since the test runtime
// doesn't.
let epoch_changes = self.factory.epoch_changes.shared_data();
let epoch = epoch_changes.epoch_data_for_child_of(
descendent_query(&*self.factory.client),
&self.parent_hash,
self.parent_number,
this_slot,
|slot| Epoch::genesis(&self.factory.config, slot),
)
let epoch = epoch_changes
.epoch_data_for_child_of(
descendent_query(&*self.factory.client),
&self.parent_hash,
self.parent_number,
this_slot,
|slot| Epoch::genesis(&self.factory.config, slot),
)
.expect("client has data to find epoch")
.expect("can compute epoch for baked block");
@@ -162,7 +159,8 @@ impl DummyProposer {
let digest_data = ConsensusLog::NextEpochData(NextEpochDescriptor {
authorities: epoch.authorities.clone(),
randomness: epoch.randomness.clone(),
}).encode();
})
.encode();
let digest = DigestItem::Consensus(BABE_ENGINE_ID, digest_data);
block.header.digest_mut().push(digest)
}
@@ -176,7 +174,8 @@ impl DummyProposer {
impl Proposer<TestBlock> for DummyProposer {
type Error = Error;
type Transaction = sc_client_api::TransactionFor<substrate_test_runtime_client::Backend, TestBlock>;
type Transaction =
sc_client_api::TransactionFor<substrate_test_runtime_client::Backend, TestBlock>;
type Proposal = future::Ready<Result<Proposal<TestBlock, Self::Transaction, ()>, Error>>;
type ProofRecording = DisableProofRecording;
type Proof = ();
@@ -201,9 +200,9 @@ pub struct PanickingBlockImport<B>(B);
#[async_trait::async_trait]
impl<B: BlockImport<TestBlock>> BlockImport<TestBlock> for PanickingBlockImport<B>
where
B::Transaction: Send,
B: Send,
where
B::Transaction: Send,
B: Send,
{
type Error = B::Error;
type Transaction = B::Transaction;
@@ -233,10 +232,8 @@ pub struct BabeTestNet {
type TestHeader = <TestBlock as BlockT>::Header;
type TestExtrinsic = <TestBlock as BlockT>::Extrinsic;
type TestSelectChain = substrate_test_runtime_client::LongestChain<
substrate_test_runtime_client::Backend,
TestBlock,
>;
type TestSelectChain =
substrate_test_runtime_client::LongestChain<substrate_test_runtime_client::Backend, TestBlock>;
pub struct TestVerifier {
inner: BabeVerifier<
@@ -244,11 +241,13 @@ pub struct TestVerifier {
PeersFullClient,
TestSelectChain,
AlwaysCanAuthor,
Box<dyn CreateInherentDataProviders<
TestBlock,
(),
InherentDataProviders = (TimestampInherentDataProvider, InherentDataProvider)
>>
Box<
dyn CreateInherentDataProviders<
TestBlock,
(),
InherentDataProviders = (TimestampInherentDataProvider, InherentDataProvider),
>,
>,
>,
mutator: Mutator,
}
@@ -274,7 +273,12 @@ impl Verifier<TestBlock> for TestVerifier {
pub struct PeerData {
link: BabeLink<TestBlock>,
block_import: Mutex<
Option<BoxBlockImport<TestBlock, TransactionFor<substrate_test_runtime_client::Backend, TestBlock>>>
Option<
BoxBlockImport<
TestBlock,
TransactionFor<substrate_test_runtime_client::Backend, TestBlock>,
>,
>,
>,
}
@@ -286,32 +290,27 @@ impl TestNetFactory for BabeTestNet {
/// Create new test network with peers and given config.
fn from_config(_config: &ProtocolConfig) -> Self {
debug!(target: "babe", "Creating test network from config");
BabeTestNet {
peers: Vec::new(),
}
BabeTestNet { peers: Vec::new() }
}
fn make_block_import(&self, client: PeersClient)
-> (
BlockImportAdapter<Self::BlockImport>,
Option<BoxJustificationImport<Block>>,
Option<PeerData>,
)
{
fn make_block_import(
&self,
client: PeersClient,
) -> (
BlockImportAdapter<Self::BlockImport>,
Option<BoxJustificationImport<Block>>,
Option<PeerData>,
) {
let client = client.as_full().expect("only full clients are tested");
let config = Config::get_or_compute(&*client).expect("config available");
let (block_import, link) = crate::block_import(
config,
client.clone(),
client.clone(),
).expect("can initialize block-import");
let (block_import, link) = crate::block_import(config, client.clone(), client.clone())
.expect("can initialize block-import");
let block_import = PanickingBlockImport(block_import);
let data_block_import = Mutex::new(
Some(Box::new(block_import.clone()) as BoxBlockImport<_, _>)
);
let data_block_import =
Mutex::new(Some(Box::new(block_import.clone()) as BoxBlockImport<_, _>));
(
BlockImportAdapter::new(block_import),
None,
@@ -324,16 +323,16 @@ impl TestNetFactory for BabeTestNet {
client: PeersClient,
_cfg: &ProtocolConfig,
maybe_link: &Option<PeerData>,
)
-> Self::Verifier
{
) -> Self::Verifier {
use substrate_test_runtime_client::DefaultTestClientBuilderExt;
let client = client.as_full().expect("only full clients are used in test");
trace!(target: "babe", "Creating a verifier");
// ensure block import and verifier are linked correctly.
let data = maybe_link.as_ref().expect("babe link always provided to verifier instantiation");
let data = maybe_link
.as_ref()
.expect("babe link always provided to verifier instantiation");
let (_, longest_chain) = TestClientBuilder::new().build_with_longest_chain();
@@ -369,10 +368,7 @@ impl TestNetFactory for BabeTestNet {
&self.peers
}
fn mut_peers<F: FnOnce(&mut Vec<BabePeer>)>(
&mut self,
closure: F,
) {
fn mut_peers<F: FnOnce(&mut Vec<BabePeer>)>(&mut self, closure: F) {
closure(&mut self.peers);
}
}
@@ -382,9 +378,7 @@ impl TestNetFactory for BabeTestNet {
fn rejects_empty_block() {
sp_tracing::try_init_simple();
let mut net = BabeTestNet::new(3);
let block_builder = |builder: BlockBuilder<_, _, _>| {
builder.build().unwrap().block
};
let block_builder = |builder: BlockBuilder<_, _, _>| builder.build().unwrap().block;
net.mut_peers(|peer| {
peer[0].generate_blocks(1, BlockOrigin::NetworkInitialSync, block_builder);
})
@@ -397,11 +391,7 @@ fn run_one_test(mutator: impl Fn(&mut TestHeader, Stage) + Send + Sync + 'static
MUTATOR.with(|m| *m.borrow_mut() = mutator.clone());
let net = BabeTestNet::new(3);
let peers = &[
(0, "//Alice"),
(1, "//Bob"),
(2, "//Charlie"),
];
let peers = &[(0, "//Alice"), (1, "//Bob"), (2, "//Charlie")];
let net = Arc::new(Mutex::new(net));
let mut import_notifications = Vec::new();
@@ -415,9 +405,10 @@ fn run_one_test(mutator: impl Fn(&mut TestHeader, Stage) + Send + Sync + 'static
let select_chain = peer.select_chain().expect("Full client has select_chain");
let keystore_path = tempfile::tempdir().expect("Creates keystore path");
let keystore: SyncCryptoStorePtr = Arc::new(LocalKeystore::open(keystore_path.path(), None)
.expect("Creates keystore"));
SyncCryptoStore::sr25519_generate_new(&*keystore, BABE, Some(seed)).expect("Generates authority key");
let keystore: SyncCryptoStorePtr =
Arc::new(LocalKeystore::open(keystore_path.path(), None).expect("Creates keystore"));
SyncCryptoStore::sr25519_generate_new(&*keystore, BABE, Some(seed))
.expect("Generates authority key");
keystore_paths.push(keystore_path);
let mut got_own = false;
@@ -435,47 +426,54 @@ fn run_one_test(mutator: impl Fn(&mut TestHeader, Stage) + Send + Sync + 'static
import_notifications.push(
// run each future until we get one of our own blocks with number higher than 5
// that was produced locally.
client.import_notification_stream()
.take_while(move |n| future::ready(n.header.number() < &5 || {
if n.origin == BlockOrigin::Own {
got_own = true;
} else {
got_other = true;
}
client
.import_notification_stream()
.take_while(move |n| {
future::ready(
n.header.number() < &5 || {
if n.origin == BlockOrigin::Own {
got_own = true;
} else {
got_other = true;
}
// continue until we have at least one block of our own
// and one of another peer.
!(got_own && got_other)
}))
.for_each(|_| future::ready(()) )
// continue until we have at least one block of our own
// and one of another peer.
!(got_own && got_other)
},
)
})
.for_each(|_| future::ready(())),
);
babe_futures.push(
start_babe(BabeParams {
block_import: data.block_import.lock().take().expect("import set up during init"),
select_chain,
client,
env: environ,
sync_oracle: DummyOracle,
create_inherent_data_providers: Box::new(|_, _| async {
let timestamp = TimestampInherentDataProvider::from_system_time();
let slot = InherentDataProvider::from_timestamp_and_duration(
*timestamp,
Duration::from_secs(6),
);
babe_futures.push(start_babe(BabeParams {
block_import: data.block_import.lock().take().expect("import set up during init"),
select_chain,
client,
env: environ,
sync_oracle: DummyOracle,
create_inherent_data_providers: Box::new(|_, _| async {
let timestamp = TimestampInherentDataProvider::from_system_time();
let slot = InherentDataProvider::from_timestamp_and_duration(
*timestamp,
Duration::from_secs(6),
);
Ok((timestamp, slot))
}),
force_authoring: false,
backoff_authoring_blocks: Some(BackoffAuthoringOnFinalizedHeadLagging::default()),
babe_link: data.link.clone(),
keystore,
can_author_with: sp_consensus::AlwaysCanAuthor,
justification_sync_link: (),
block_proposal_slot_portion: SlotProportion::new(0.5),
max_block_proposal_slot_portion: None,
telemetry: None,
}).expect("Starts babe"));
Ok((timestamp, slot))
}),
force_authoring: false,
backoff_authoring_blocks: Some(BackoffAuthoringOnFinalizedHeadLagging::default()),
babe_link: data.link.clone(),
keystore,
can_author_with: sp_consensus::AlwaysCanAuthor,
justification_sync_link: (),
block_proposal_slot_portion: SlotProportion::new(0.5),
max_block_proposal_slot_portion: None,
telemetry: None,
})
.expect("Starts babe"),
);
}
block_on(future::select(
futures::future::poll_fn(move |cx| {
@@ -489,7 +487,7 @@ fn run_one_test(mutator: impl Fn(&mut TestHeader, Stage) + Send + Sync + 'static
Poll::<()>::Pending
}),
future::select(future::join_all(import_notifications), future::join_all(babe_futures))
future::select(future::join_all(import_notifications), future::join_all(babe_futures)),
));
}
@@ -503,7 +501,8 @@ fn authoring_blocks() {
fn rejects_missing_inherent_digest() {
run_one_test(|header: &mut TestHeader, stage| {
let v = std::mem::take(&mut header.digest_mut().logs);
header.digest_mut().logs = v.into_iter()
header.digest_mut().logs = v
.into_iter()
.filter(|v| stage == Stage::PostSeal || v.as_babe_pre_digest().is_none())
.collect()
})
@@ -514,7 +513,8 @@ fn rejects_missing_inherent_digest() {
fn rejects_missing_seals() {
run_one_test(|header: &mut TestHeader, stage| {
let v = std::mem::take(&mut header.digest_mut().logs);
header.digest_mut().logs = v.into_iter()
header.digest_mut().logs = v
.into_iter()
.filter(|v| stage == Stage::PreSeal || v.as_babe_seal().is_none())
.collect()
})
@@ -525,7 +525,8 @@ fn rejects_missing_seals() {
fn rejects_missing_consensus_digests() {
run_one_test(|header: &mut TestHeader, stage| {
let v = std::mem::take(&mut header.digest_mut().logs);
header.digest_mut().logs = v.into_iter()
header.digest_mut().logs = v
.into_iter()
.filter(|v| stage == Stage::PostSeal || v.as_next_epoch_descriptor().is_none())
.collect()
});
@@ -560,8 +561,8 @@ fn sig_is_not_pre_digest() {
fn can_author_block() {
sp_tracing::try_init_simple();
let keystore_path = tempfile::tempdir().expect("Creates keystore path");
let keystore: SyncCryptoStorePtr = Arc::new(LocalKeystore::open(keystore_path.path(), None)
.expect("Creates keystore"));
let keystore: SyncCryptoStorePtr =
Arc::new(LocalKeystore::open(keystore_path.path(), None).expect("Creates keystore"));
let public = SyncCryptoStore::sr25519_generate_new(&*keystore, BABE, Some("//Alice"))
.expect("Generates authority pair");
@@ -601,8 +602,8 @@ fn can_author_block() {
None => i += 1,
Some(s) => {
debug!(target: "babe", "Authored block {:?}", s.0);
break;
}
break
},
}
}
}
@@ -622,26 +623,27 @@ fn propose_and_import_block<Transaction: Send + 'static>(
});
let pre_digest = sp_runtime::generic::Digest {
logs: vec![
Item::babe_pre_digest(
PreDigest::SecondaryPlain(SecondaryPlainPreDigest {
authority_index: 0,
slot,
}),
),
],
logs: vec![Item::babe_pre_digest(PreDigest::SecondaryPlain(SecondaryPlainPreDigest {
authority_index: 0,
slot,
}))],
};
let parent_hash = parent.hash();
let mut block = futures::executor::block_on(proposer.propose_with(pre_digest)).unwrap().block;
let epoch_descriptor = proposer_factory.epoch_changes.shared_data().epoch_descriptor_for_child_of(
descendent_query(&*proposer_factory.client),
&parent_hash,
*parent.number(),
slot,
).unwrap().unwrap();
let epoch_descriptor = proposer_factory
.epoch_changes
.shared_data()
.epoch_descriptor_for_child_of(
descendent_query(&*proposer_factory.client),
&parent_hash,
*parent.number(),
slot,
)
.unwrap()
.unwrap();
let seal = {
// sign the pre-sealed hash of the block and then
@@ -706,13 +708,12 @@ fn importing_block_one_sets_genesis_epoch() {
let genesis_epoch = Epoch::genesis(&data.link.config, 999.into());
let epoch_changes = data.link.epoch_changes.shared_data();
let epoch_for_second_block = epoch_changes.epoch_data_for_child_of(
descendent_query(&*client),
&block_hash,
1,
1000.into(),
|slot| Epoch::genesis(&data.link.config, slot),
).unwrap().unwrap();
let epoch_for_second_block = epoch_changes
.epoch_data_for_child_of(descendent_query(&*client), &block_hash, 1, 1000.into(), |slot| {
Epoch::genesis(&data.link.config, slot)
})
.unwrap()
.unwrap();
assert_eq!(epoch_for_second_block, genesis_epoch);
}
@@ -779,16 +780,10 @@ fn importing_epoch_change_block_prunes_tree() {
let fork_3 = propose_and_import_blocks(BlockId::Hash(canon_hashes[18]), 10);
// We should be tracking a total of 9 epochs in the fork tree
assert_eq!(
epoch_changes.shared_data().tree().iter().count(),
9,
);
assert_eq!(epoch_changes.shared_data().tree().iter().count(), 9,);
// And only one root
assert_eq!(
epoch_changes.shared_data().tree().roots().count(),
1,
);
assert_eq!(epoch_changes.shared_data().tree().roots().count(), 1,);
// We finalize block #13 from the canon chain, so on the next epoch
// change the tree should be pruned, to not contain F (#7).
@@ -796,32 +791,47 @@ fn importing_epoch_change_block_prunes_tree() {
propose_and_import_blocks(BlockId::Hash(client.chain_info().best_hash), 7);
// at this point no hashes from the first fork must exist on the tree
assert!(
!epoch_changes.shared_data().tree().iter().map(|(h, _, _)| h).any(|h| fork_1.contains(h)),
);
assert!(!epoch_changes
.shared_data()
.tree()
.iter()
.map(|(h, _, _)| h)
.any(|h| fork_1.contains(h)),);
// but the epoch changes from the other forks must still exist
assert!(
epoch_changes.shared_data().tree().iter().map(|(h, _, _)| h).any(|h| fork_2.contains(h))
);
assert!(epoch_changes
.shared_data()
.tree()
.iter()
.map(|(h, _, _)| h)
.any(|h| fork_2.contains(h)));
assert!(
epoch_changes.shared_data().tree().iter().map(|(h, _, _)| h).any(|h| fork_3.contains(h)),
);
assert!(epoch_changes
.shared_data()
.tree()
.iter()
.map(|(h, _, _)| h)
.any(|h| fork_3.contains(h)),);
// finalizing block #25 from the canon chain should prune out the second fork
client.finalize_block(BlockId::Hash(canon_hashes[24]), None, false).unwrap();
propose_and_import_blocks(BlockId::Hash(client.chain_info().best_hash), 8);
// at this point no hashes from the second fork must exist on the tree
assert!(
!epoch_changes.shared_data().tree().iter().map(|(h, _, _)| h).any(|h| fork_2.contains(h)),
);
assert!(!epoch_changes
.shared_data()
.tree()
.iter()
.map(|(h, _, _)| h)
.any(|h| fork_2.contains(h)),);
// while epoch changes from the last fork should still exist
assert!(
epoch_changes.shared_data().tree().iter().map(|(h, _, _)| h).any(|h| fork_3.contains(h)),
);
assert!(epoch_changes
.shared_data()
.tree()
.iter()
.map(|(h, _, _)| h)
.any(|h| fork_3.contains(h)),);
}
#[test]
@@ -856,20 +866,15 @@ fn verify_slots_are_strictly_increasing() {
// we should fail to import this block since the slot number didn't increase.
// we will panic due to the `PanickingBlockImport` defined above.
propose_and_import_block(
&b1,
Some(999.into()),
&mut proposer_factory,
&mut block_import,
);
propose_and_import_block(&b1, Some(999.into()), &mut proposer_factory, &mut block_import);
}
#[test]
fn babe_transcript_generation_match() {
sp_tracing::try_init_simple();
let keystore_path = tempfile::tempdir().expect("Creates keystore path");
let keystore: SyncCryptoStorePtr = Arc::new(LocalKeystore::open(keystore_path.path(), None)
.expect("Creates keystore"));
let keystore: SyncCryptoStorePtr =
Arc::new(LocalKeystore::open(keystore_path.path(), None).expect("Creates keystore"));
let public = SyncCryptoStore::sr25519_generate_new(&*keystore, BABE, Some("//Alice"))
.expect("Generates authority pair");
@@ -890,9 +895,7 @@ fn babe_transcript_generation_match() {
let test = |t: merlin::Transcript| -> [u8; 16] {
let mut b = [0u8; 16];
t.build_rng()
.finalize(&mut ChaChaRng::from_seed([0u8;32]))
.fill_bytes(&mut b);
t.build_rng().finalize(&mut ChaChaRng::from_seed([0u8; 32])).fill_bytes(&mut b);
b
};
debug_assert!(test(orig_transcript) == test(transcript_from_data(new_transcript)));
@@ -17,18 +17,22 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//! Verification for BABE headers.
use sp_runtime::{traits::Header, traits::DigestItemFor};
use sp_core::{Pair, Public};
use sp_consensus_babe::{make_transcript, AuthoritySignature, AuthorityPair, AuthorityId};
use sp_consensus_babe::digests::{
PreDigest, PrimaryPreDigest, SecondaryPlainPreDigest, SecondaryVRFPreDigest,
CompatibleDigestItem
use super::{
authorship::{calculate_primary_threshold, check_primary_threshold, secondary_slot_author},
babe_err, find_pre_digest, BlockT, Epoch, Error,
};
use sc_consensus_slots::CheckedHeader;
use sp_consensus_slots::Slot;
use log::{debug, trace};
use super::{find_pre_digest, babe_err, Epoch, BlockT, Error};
use super::authorship::{calculate_primary_threshold, check_primary_threshold, secondary_slot_author};
use sc_consensus_slots::CheckedHeader;
use sp_consensus_babe::{
digests::{
CompatibleDigestItem, PreDigest, PrimaryPreDigest, SecondaryPlainPreDigest,
SecondaryVRFPreDigest,
},
make_transcript, AuthorityId, AuthorityPair, AuthoritySignature,
};
use sp_consensus_slots::Slot;
use sp_core::{Pair, Public};
use sp_runtime::traits::{DigestItemFor, Header};
/// BABE verification parameters
pub(super) struct VerificationParams<'a, B: 'a + BlockT> {
@@ -57,26 +61,24 @@ pub(super) struct VerificationParams<'a, B: 'a + BlockT> {
/// with each having different validation logic.
pub(super) fn check_header<B: BlockT + Sized>(
params: VerificationParams<B>,
) -> Result<CheckedHeader<B::Header, VerifiedHeaderInfo<B>>, Error<B>> where
) -> Result<CheckedHeader<B::Header, VerifiedHeaderInfo<B>>, Error<B>>
where
DigestItemFor<B>: CompatibleDigestItem,
{
let VerificationParams {
mut header,
pre_digest,
slot_now,
epoch,
} = params;
let VerificationParams { mut header, pre_digest, slot_now, epoch } = params;
let authorities = &epoch.authorities;
let pre_digest = pre_digest.map(Ok).unwrap_or_else(|| find_pre_digest::<B>(&header))?;
trace!(target: "babe", "Checking header");
let seal = header.digest_mut().pop()
let seal = header
.digest_mut()
.pop()
.ok_or_else(|| babe_err(Error::HeaderUnsealed(header.hash())))?;
let sig = seal.as_babe_seal().ok_or_else(|| {
babe_err(Error::HeaderBadSeal(header.hash()))
})?;
let sig = seal
.as_babe_seal()
.ok_or_else(|| babe_err(Error::HeaderBadSeal(header.hash())))?;
// the pre-hash of the header doesn't include the seal
// and that's what we sign
@@ -84,7 +86,7 @@ pub(super) fn check_header<B: BlockT + Sized>(
if pre_digest.slot() > slot_now {
header.digest_mut().push(seal);
return Ok(CheckedHeader::Deferred(header, pre_digest.slot()));
return Ok(CheckedHeader::Deferred(header, pre_digest.slot()))
}
let author = match authorities.get(pre_digest.authority_index() as usize) {
@@ -100,45 +102,31 @@ pub(super) fn check_header<B: BlockT + Sized>(
primary.slot,
);
check_primary_header::<B>(
pre_hash,
primary,
sig,
&epoch,
epoch.config.c,
)?;
check_primary_header::<B>(pre_hash, primary, sig, &epoch, epoch.config.c)?;
},
PreDigest::SecondaryPlain(secondary) if epoch.config.allowed_slots.is_secondary_plain_slots_allowed() => {
PreDigest::SecondaryPlain(secondary)
if epoch.config.allowed_slots.is_secondary_plain_slots_allowed() =>
{
debug!(target: "babe",
"Verifying secondary plain block #{} at slot: {}",
header.number(),
secondary.slot,
);
check_secondary_plain_header::<B>(
pre_hash,
secondary,
sig,
&epoch,
)?;
},
PreDigest::SecondaryVRF(secondary) if epoch.config.allowed_slots.is_secondary_vrf_slots_allowed() => {
check_secondary_plain_header::<B>(pre_hash, secondary, sig, &epoch)?;
}
PreDigest::SecondaryVRF(secondary)
if epoch.config.allowed_slots.is_secondary_vrf_slots_allowed() =>
{
debug!(target: "babe",
"Verifying secondary VRF block #{} at slot: {}",
header.number(),
secondary.slot,
);
check_secondary_vrf_header::<B>(
pre_hash,
secondary,
sig,
&epoch,
)?;
},
_ => {
return Err(babe_err(Error::SecondarySlotAssignmentsDisabled));
check_secondary_vrf_header::<B>(pre_hash, secondary, sig, &epoch)?;
}
_ => return Err(babe_err(Error::SecondarySlotAssignmentsDisabled)),
}
let info = VerifiedHeaderInfo {
@@ -170,27 +158,20 @@ fn check_primary_header<B: BlockT + Sized>(
if AuthorityPair::verify(&signature, pre_hash, &author) {
let (inout, _) = {
let transcript = make_transcript(
&epoch.randomness,
pre_digest.slot,
epoch.epoch_index,
);
let transcript = make_transcript(&epoch.randomness, pre_digest.slot, epoch.epoch_index);
schnorrkel::PublicKey::from_bytes(author.as_slice()).and_then(|p| {
p.vrf_verify(transcript, &pre_digest.vrf_output, &pre_digest.vrf_proof)
}).map_err(|s| {
babe_err(Error::VRFVerificationFailed(s))
})?
schnorrkel::PublicKey::from_bytes(author.as_slice())
.and_then(|p| {
p.vrf_verify(transcript, &pre_digest.vrf_output, &pre_digest.vrf_proof)
})
.map_err(|s| babe_err(Error::VRFVerificationFailed(s)))?
};
let threshold = calculate_primary_threshold(
c,
&epoch.authorities,
pre_digest.authority_index as usize,
);
let threshold =
calculate_primary_threshold(c, &epoch.authorities, pre_digest.authority_index as usize);
if !check_primary_threshold(&inout, threshold) {
return Err(babe_err(Error::VRFVerificationOfBlockFailed(author.clone(), threshold)));
return Err(babe_err(Error::VRFVerificationOfBlockFailed(author.clone(), threshold)))
}
Ok(())
@@ -211,16 +192,14 @@ fn check_secondary_plain_header<B: BlockT>(
) -> Result<(), Error<B>> {
// check the signature is valid under the expected authority and
// chain state.
let expected_author = secondary_slot_author(
pre_digest.slot,
&epoch.authorities,
epoch.randomness,
).ok_or_else(|| Error::NoSecondaryAuthorExpected)?;
let expected_author =
secondary_slot_author(pre_digest.slot, &epoch.authorities, epoch.randomness)
.ok_or_else(|| Error::NoSecondaryAuthorExpected)?;
let author = &epoch.authorities[pre_digest.authority_index as usize].0;
if expected_author != author {
return Err(Error::InvalidAuthor(expected_author.clone(), author.clone()));
return Err(Error::InvalidAuthor(expected_author.clone(), author.clone()))
}
if AuthorityPair::verify(&signature, pre_hash.as_ref(), author) {
@@ -239,30 +218,22 @@ fn check_secondary_vrf_header<B: BlockT>(
) -> Result<(), Error<B>> {
// check the signature is valid under the expected authority and
// chain state.
let expected_author = secondary_slot_author(
pre_digest.slot,
&epoch.authorities,
epoch.randomness,
).ok_or_else(|| Error::NoSecondaryAuthorExpected)?;
let expected_author =
secondary_slot_author(pre_digest.slot, &epoch.authorities, epoch.randomness)
.ok_or_else(|| Error::NoSecondaryAuthorExpected)?;
let author = &epoch.authorities[pre_digest.authority_index as usize].0;
if expected_author != author {
return Err(Error::InvalidAuthor(expected_author.clone(), author.clone()));
return Err(Error::InvalidAuthor(expected_author.clone(), author.clone()))
}
if AuthorityPair::verify(&signature, pre_hash.as_ref(), author) {
let transcript = make_transcript(
&epoch.randomness,
pre_digest.slot,
epoch.epoch_index,
);
let transcript = make_transcript(&epoch.randomness, pre_digest.slot, epoch.epoch_index);
schnorrkel::PublicKey::from_bytes(author.as_slice()).and_then(|p| {
p.vrf_verify(transcript, &pre_digest.vrf_output, &pre_digest.vrf_proof)
}).map_err(|s| {
babe_err(Error::VRFVerificationFailed(s))
})?;
schnorrkel::PublicKey::from_bytes(author.as_slice())
.and_then(|p| p.vrf_verify(transcript, &pre_digest.vrf_output, &pre_digest.vrf_proof))
.map_err(|s| babe_err(Error::VRFVerificationFailed(s)))?;
Ok(())
} else {
@@ -18,30 +18,26 @@
//! Longest chain implementation
use std::sync::Arc;
use std::marker::PhantomData;
use sc_client_api::backend;
use sp_consensus::{SelectChain, Error as ConsensusError};
use sp_blockchain::{Backend, HeaderBackend};
use sp_consensus::{Error as ConsensusError, SelectChain};
use sp_runtime::{
traits::{NumberFor, Block as BlockT},
generic::BlockId,
traits::{Block as BlockT, NumberFor},
};
use std::{marker::PhantomData, sync::Arc};
/// Implement Longest Chain Select implementation
/// where 'longest' is defined as the highest number of blocks
pub struct LongestChain<B, Block> {
backend: Arc<B>,
_phantom: PhantomData<Block>
_phantom: PhantomData<Block>,
}
impl<B, Block> Clone for LongestChain<B, Block> {
fn clone(&self) -> Self {
let backend = self.backend.clone();
LongestChain {
backend,
_phantom: Default::default()
}
LongestChain { backend, _phantom: Default::default() }
}
}
@@ -52,21 +48,22 @@ where
{
/// Instantiate a new LongestChain for Backend B
pub fn new(backend: Arc<B>) -> Self {
LongestChain {
backend,
_phantom: Default::default(),
}
LongestChain { backend, _phantom: Default::default() }
}
fn best_block_header(&self) -> sp_blockchain::Result<<Block as BlockT>::Header> {
let info = self.backend.blockchain().info();
let import_lock = self.backend.get_import_lock();
let best_hash = self.backend
let best_hash = self
.backend
.blockchain()
.best_containing(info.best_hash, None, import_lock)?
.unwrap_or(info.best_hash);
Ok(self.backend.blockchain().header(BlockId::Hash(best_hash))?
Ok(self
.backend
.blockchain()
.header(BlockId::Hash(best_hash))?
.expect("given block hash was fetched from block in db; qed"))
}
@@ -18,8 +18,8 @@
//! Provides a generic wrapper around shared data. See [`SharedData`] for more information.
use parking_lot::{Condvar, MappedMutexGuard, Mutex, MutexGuard};
use std::sync::Arc;
use parking_lot::{Mutex, MappedMutexGuard, Condvar, MutexGuard};
/// Created by [`SharedDataLocked::release_mutex`].
///
@@ -75,8 +75,7 @@ impl<'a, T> SharedDataLocked<'a, T> {
/// Release the mutex, but keep the shared data locked.
pub fn release_mutex(mut self) -> SharedDataLockedUpgradable<T> {
SharedDataLockedUpgradable {
shared_data: self.shared_data.take()
.expect("`shared_data` is only taken on drop; qed"),
shared_data: self.shared_data.take().expect("`shared_data` is only taken on drop; qed"),
}
}
}
@@ -132,7 +131,7 @@ struct SharedDataInner<T> {
/// # Example
///
/// ```
///# use sc_consensus::shared_data::SharedData;
/// # use sc_consensus::shared_data::SharedData;
///
/// let shared_data = SharedData::new(String::from("hello world"));
///
@@ -174,10 +173,7 @@ pub struct SharedData<T> {
impl<T> Clone for SharedData<T> {
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
cond_var: self.cond_var.clone(),
}
Self { inner: self.inner.clone(), cond_var: self.cond_var.clone() }
}
}
@@ -228,10 +224,7 @@ impl<T> SharedData<T> {
debug_assert!(!guard.locked);
guard.locked = true;
SharedDataLocked {
inner: guard,
shared_data: Some(self.clone()),
}
SharedDataLocked { inner: guard, shared_data: Some(self.clone()) }
}
}
+213 -290
View File
@@ -20,12 +20,16 @@
pub mod migration;
use std::{ops::Add, collections::BTreeMap, borrow::{Borrow, BorrowMut}};
use codec::{Encode, Decode};
use codec::{Decode, Encode};
use fork_tree::ForkTree;
use sc_client_api::utils::is_descendent_of;
use sp_blockchain::{HeaderMetadata, HeaderBackend, Error as ClientError};
use sp_blockchain::{Error as ClientError, HeaderBackend, HeaderMetadata};
use sp_runtime::traits::{Block as BlockT, NumberFor, One, Zero};
use std::{
borrow::{Borrow, BorrowMut},
collections::BTreeMap,
ops::Add,
};
/// A builder for `is_descendent_of` functions.
pub trait IsDescendentOfBuilder<Hash> {
@@ -41,8 +45,7 @@ pub trait IsDescendentOfBuilder<Hash> {
/// details aren't yet stored, but its parent is.
///
/// The format of `current` when `Some` is `(current, current_parent)`.
fn build_is_descendent_of(&self, current: Option<(Hash, Hash)>)
-> Self::IsDescendentOf;
fn build_is_descendent_of(&self, current: Option<(Hash, Hash)>) -> Self::IsDescendentOf;
}
/// Produce a descendent query object given the client.
@@ -55,16 +58,18 @@ pub fn descendent_query<H, Block>(client: &H) -> HeaderBackendDescendentBuilder<
pub struct HeaderBackendDescendentBuilder<H, Block>(H, std::marker::PhantomData<Block>);
impl<'a, H, Block> IsDescendentOfBuilder<Block::Hash>
for HeaderBackendDescendentBuilder<&'a H, Block> where
H: HeaderBackend<Block> + HeaderMetadata<Block, Error=ClientError>,
for HeaderBackendDescendentBuilder<&'a H, Block>
where
H: HeaderBackend<Block> + HeaderMetadata<Block, Error = ClientError>,
Block: BlockT,
{
type Error = ClientError;
type IsDescendentOf = Box<dyn Fn(&Block::Hash, &Block::Hash) -> Result<bool, ClientError> + 'a>;
fn build_is_descendent_of(&self, current: Option<(Block::Hash, Block::Hash)>)
-> Self::IsDescendentOf
{
fn build_is_descendent_of(
&self,
current: Option<(Block::Hash, Block::Hash)>,
) -> Self::IsDescendentOf {
Box::new(is_descendent_of(self.0, current))
}
}
@@ -90,10 +95,7 @@ pub trait Epoch {
impl<'a, E: Epoch> From<&'a E> for EpochHeader<E> {
fn from(epoch: &'a E) -> EpochHeader<E> {
Self {
start_slot: epoch.start_slot(),
end_slot: epoch.end_slot(),
}
Self { start_slot: epoch.start_slot(), end_slot: epoch.end_slot() }
}
}
@@ -109,10 +111,7 @@ pub struct EpochHeader<E: Epoch> {
impl<E: Epoch> Clone for EpochHeader<E> {
fn clone(&self) -> Self {
Self {
start_slot: self.start_slot,
end_slot: self.end_slot,
}
Self { start_slot: self.start_slot, end_slot: self.end_slot }
}
}
@@ -149,7 +148,8 @@ pub enum ViableEpoch<E, ERef = E> {
Signaled(ERef),
}
impl<E, ERef> AsRef<E> for ViableEpoch<E, ERef> where
impl<E, ERef> AsRef<E> for ViableEpoch<E, ERef>
where
ERef: Borrow<E>,
{
fn as_ref(&self) -> &E {
@@ -160,7 +160,8 @@ impl<E, ERef> AsRef<E> for ViableEpoch<E, ERef> where
}
}
impl<E, ERef> AsMut<E> for ViableEpoch<E, ERef> where
impl<E, ERef> AsMut<E> for ViableEpoch<E, ERef>
where
ERef: BorrowMut<E>,
{
fn as_mut(&mut self) -> &mut E {
@@ -171,7 +172,8 @@ impl<E, ERef> AsMut<E> for ViableEpoch<E, ERef> where
}
}
impl<E, ERef> ViableEpoch<E, ERef> where
impl<E, ERef> ViableEpoch<E, ERef>
where
E: Epoch + Clone,
ERef: Borrow<E>,
{
@@ -187,18 +189,14 @@ impl<E, ERef> ViableEpoch<E, ERef> where
/// Get cloned value for the viable epoch.
pub fn into_cloned(self) -> ViableEpoch<E, E> {
match self {
ViableEpoch::UnimportedGenesis(e) =>
ViableEpoch::UnimportedGenesis(e),
ViableEpoch::UnimportedGenesis(e) => ViableEpoch::UnimportedGenesis(e),
ViableEpoch::Signaled(e) => ViableEpoch::Signaled(e.borrow().clone()),
}
}
/// Increment the epoch, yielding an `IncrementedEpoch` to be imported
/// into the fork-tree.
pub fn increment(
&self,
next_descriptor: E::NextEpochDescriptor
) -> IncrementedEpoch<E> {
pub fn increment(&self, next_descriptor: E::NextEpochDescriptor) -> IncrementedEpoch<E> {
let next = self.as_ref().increment(next_descriptor);
let to_persist = match *self {
ViableEpoch::UnimportedGenesis(ref epoch_0) =>
@@ -216,7 +214,7 @@ pub enum ViableEpochDescriptor<Hash, Number, E: Epoch> {
/// The epoch is an unimported genesis, with given start slot number.
UnimportedGenesis(E::Slot),
/// The epoch is signaled and has been imported, with given identifier and header.
Signaled(EpochIdentifier<Hash, Number>, EpochHeader<E>)
Signaled(EpochIdentifier<Hash, Number>, EpochHeader<E>),
}
impl<Hash, Number, E: Epoch> ViableEpochDescriptor<Hash, Number, E> {
@@ -243,8 +241,7 @@ impl<'a, E: Epoch> From<&'a PersistedEpoch<E>> for PersistedEpochHeader<E> {
match epoch {
PersistedEpoch::Genesis(ref epoch_0, ref epoch_1) =>
PersistedEpochHeader::Genesis(epoch_0.into(), epoch_1.into()),
PersistedEpoch::Regular(ref epoch_n) =>
PersistedEpochHeader::Regular(epoch_n.into()),
PersistedEpoch::Regular(ref epoch_n) => PersistedEpochHeader::Regular(epoch_n.into()),
}
}
}
@@ -312,7 +309,8 @@ fn fake_head_hash<H: AsRef<[u8]> + AsMut<[u8]> + Clone>(parent_hash: &H) -> H {
h
}
impl<Hash, Number, E: Epoch> Default for EpochChanges<Hash, Number, E> where
impl<Hash, Number, E: Epoch> Default for EpochChanges<Hash, Number, E>
where
Hash: PartialEq + Ord,
Number: Ord,
{
@@ -321,9 +319,10 @@ impl<Hash, Number, E: Epoch> Default for EpochChanges<Hash, Number, E> where
}
}
impl<Hash, Number, E: Epoch> EpochChanges<Hash, Number, E> where
impl<Hash, Number, E: Epoch> EpochChanges<Hash, Number, E>
where
Hash: PartialEq + Ord + AsRef<[u8]> + AsMut<[u8]> + Copy,
Number: Ord + One + Zero + Add<Output=Number> + Copy,
Number: Ord + One + Zero + Add<Output = Number> + Copy,
{
/// Create a new epoch change.
pub fn new() -> Self {
@@ -337,51 +336,38 @@ impl<Hash, Number, E: Epoch> EpochChanges<Hash, Number, E> where
}
/// Map the epoch changes from one storing data to a different one.
pub fn map<B, F>(self, mut f: F) -> EpochChanges<Hash, Number, B> where
B: Epoch<Slot=E::Slot>,
pub fn map<B, F>(self, mut f: F) -> EpochChanges<Hash, Number, B>
where
B: Epoch<Slot = E::Slot>,
F: FnMut(&Hash, &Number, E) -> B,
{
EpochChanges {
inner: self.inner.map(&mut |_, _, header| {
match header {
PersistedEpochHeader::Genesis(epoch_0, epoch_1) => {
PersistedEpochHeader::Genesis(
EpochHeader {
start_slot: epoch_0.start_slot,
end_slot: epoch_0.end_slot,
},
EpochHeader {
start_slot: epoch_1.start_slot,
end_slot: epoch_1.end_slot,
},
)
},
PersistedEpochHeader::Regular(epoch_n) => {
PersistedEpochHeader::Regular(
EpochHeader {
start_slot: epoch_n.start_slot,
end_slot: epoch_n.end_slot,
},
)
},
}
inner: self.inner.map(&mut |_, _, header| match header {
PersistedEpochHeader::Genesis(epoch_0, epoch_1) => PersistedEpochHeader::Genesis(
EpochHeader { start_slot: epoch_0.start_slot, end_slot: epoch_0.end_slot },
EpochHeader { start_slot: epoch_1.start_slot, end_slot: epoch_1.end_slot },
),
PersistedEpochHeader::Regular(epoch_n) =>
PersistedEpochHeader::Regular(EpochHeader {
start_slot: epoch_n.start_slot,
end_slot: epoch_n.end_slot,
}),
}),
epochs: self.epochs.into_iter().map(|((hash, number), epoch)| {
let bepoch = match epoch {
PersistedEpoch::Genesis(epoch_0, epoch_1) => {
PersistedEpoch::Genesis(
epochs: self
.epochs
.into_iter()
.map(|((hash, number), epoch)| {
let bepoch = match epoch {
PersistedEpoch::Genesis(epoch_0, epoch_1) => PersistedEpoch::Genesis(
f(&hash, &number, epoch_0),
f(&hash, &number, epoch_1),
)
},
PersistedEpoch::Regular(epoch_n) => {
PersistedEpoch::Regular(
f(&hash, &number, epoch_n)
)
},
};
((hash, number), bepoch)
}).collect(),
),
PersistedEpoch::Regular(epoch_n) =>
PersistedEpoch::Regular(f(&hash, &number, epoch_n)),
};
((hash, number), bepoch)
})
.collect(),
}
}
@@ -395,25 +381,17 @@ impl<Hash, Number, E: Epoch> EpochChanges<Hash, Number, E> where
number: Number,
slot: E::Slot,
) -> Result<(), fork_tree::Error<D::Error>> {
let is_descendent_of = descendent_of_builder
.build_is_descendent_of(None);
let is_descendent_of = descendent_of_builder.build_is_descendent_of(None);
let predicate = |epoch: &PersistedEpochHeader<E>| match *epoch {
PersistedEpochHeader::Genesis(_, ref epoch_1) =>
slot >= epoch_1.end_slot,
PersistedEpochHeader::Regular(ref epoch_n) =>
slot >= epoch_n.end_slot,
PersistedEpochHeader::Genesis(_, ref epoch_1) => slot >= epoch_1.end_slot,
PersistedEpochHeader::Regular(ref epoch_n) => slot >= epoch_n.end_slot,
};
// prune any epochs which could not be _live_ as of the children of the
// finalized block, i.e. re-root the fork tree to the oldest ancestor of
// (hash, number) where epoch.end_slot() >= finalized_slot
let removed = self.inner.prune(
hash,
&number,
&is_descendent_of,
&predicate,
)?;
let removed = self.inner.prune(hash, &number, &is_descendent_of, &predicate)?;
for (hash, number, _) in removed {
self.epochs.remove(&(hash, number));
@@ -424,18 +402,18 @@ impl<Hash, Number, E: Epoch> EpochChanges<Hash, Number, E> where
/// Get a reference to an epoch with given identifier.
pub fn epoch(&self, id: &EpochIdentifier<Hash, Number>) -> Option<&E> {
self.epochs.get(&(id.hash, id.number))
.and_then(|v| {
match v {
PersistedEpoch::Genesis(ref epoch_0, _)
if id.position == EpochIdentifierPosition::Genesis0 => Some(epoch_0),
PersistedEpoch::Genesis(_, ref epoch_1)
if id.position == EpochIdentifierPosition::Genesis1 => Some(epoch_1),
PersistedEpoch::Regular(ref epoch_n)
if id.position == EpochIdentifierPosition::Regular => Some(epoch_n),
_ => None,
}
})
self.epochs.get(&(id.hash, id.number)).and_then(|v| match v {
PersistedEpoch::Genesis(ref epoch_0, _)
if id.position == EpochIdentifierPosition::Genesis0 =>
Some(epoch_0),
PersistedEpoch::Genesis(_, ref epoch_1)
if id.position == EpochIdentifierPosition::Genesis1 =>
Some(epoch_1),
PersistedEpoch::Regular(ref epoch_n)
if id.position == EpochIdentifierPosition::Regular =>
Some(epoch_n),
_ => None,
})
}
/// Get a reference to a viable epoch with given descriptor.
@@ -443,33 +421,32 @@ impl<Hash, Number, E: Epoch> EpochChanges<Hash, Number, E> where
&self,
descriptor: &ViableEpochDescriptor<Hash, Number, E>,
make_genesis: G,
) -> Option<ViableEpoch<E, &E>> where
G: FnOnce(E::Slot) -> E
) -> Option<ViableEpoch<E, &E>>
where
G: FnOnce(E::Slot) -> E,
{
match descriptor {
ViableEpochDescriptor::UnimportedGenesis(slot) => {
Some(ViableEpoch::UnimportedGenesis(make_genesis(*slot)))
},
ViableEpochDescriptor::Signaled(identifier, _) => {
self.epoch(&identifier).map(ViableEpoch::Signaled)
},
ViableEpochDescriptor::UnimportedGenesis(slot) =>
Some(ViableEpoch::UnimportedGenesis(make_genesis(*slot))),
ViableEpochDescriptor::Signaled(identifier, _) =>
self.epoch(&identifier).map(ViableEpoch::Signaled),
}
}
/// Get a mutable reference to an epoch with given identifier.
pub fn epoch_mut(&mut self, id: &EpochIdentifier<Hash, Number>) -> Option<&mut E> {
self.epochs.get_mut(&(id.hash, id.number))
.and_then(|v| {
match v {
PersistedEpoch::Genesis(ref mut epoch_0, _)
if id.position == EpochIdentifierPosition::Genesis0 => Some(epoch_0),
PersistedEpoch::Genesis(_, ref mut epoch_1)
if id.position == EpochIdentifierPosition::Genesis1 => Some(epoch_1),
PersistedEpoch::Regular(ref mut epoch_n)
if id.position == EpochIdentifierPosition::Regular => Some(epoch_n),
_ => None,
}
})
self.epochs.get_mut(&(id.hash, id.number)).and_then(|v| match v {
PersistedEpoch::Genesis(ref mut epoch_0, _)
if id.position == EpochIdentifierPosition::Genesis0 =>
Some(epoch_0),
PersistedEpoch::Genesis(_, ref mut epoch_1)
if id.position == EpochIdentifierPosition::Genesis1 =>
Some(epoch_1),
PersistedEpoch::Regular(ref mut epoch_n)
if id.position == EpochIdentifierPosition::Regular =>
Some(epoch_n),
_ => None,
})
}
/// Get a mutable reference to a viable epoch with given descriptor.
@@ -477,16 +454,15 @@ impl<Hash, Number, E: Epoch> EpochChanges<Hash, Number, E> where
&mut self,
descriptor: &ViableEpochDescriptor<Hash, Number, E>,
make_genesis: G,
) -> Option<ViableEpoch<E, &mut E>> where
G: FnOnce(E::Slot) -> E
) -> Option<ViableEpoch<E, &mut E>>
where
G: FnOnce(E::Slot) -> E,
{
match descriptor {
ViableEpochDescriptor::UnimportedGenesis(slot) => {
Some(ViableEpoch::UnimportedGenesis(make_genesis(*slot)))
},
ViableEpochDescriptor::Signaled(identifier, _) => {
self.epoch_mut(&identifier).map(ViableEpoch::Signaled)
},
ViableEpochDescriptor::UnimportedGenesis(slot) =>
Some(ViableEpoch::UnimportedGenesis(make_genesis(*slot))),
ViableEpochDescriptor::Signaled(identifier, _) =>
self.epoch_mut(&identifier).map(ViableEpoch::Signaled),
}
}
@@ -497,18 +473,15 @@ impl<Hash, Number, E: Epoch> EpochChanges<Hash, Number, E> where
pub fn epoch_data<G>(
&self,
descriptor: &ViableEpochDescriptor<Hash, Number, E>,
make_genesis: G
) -> Option<E> where
make_genesis: G,
) -> Option<E>
where
G: FnOnce(E::Slot) -> E,
E: Clone,
{
match descriptor {
ViableEpochDescriptor::UnimportedGenesis(slot) => {
Some(make_genesis(*slot))
},
ViableEpochDescriptor::Signaled(identifier, _) => {
self.epoch(&identifier).cloned()
},
ViableEpochDescriptor::UnimportedGenesis(slot) => Some(make_genesis(*slot)),
ViableEpochDescriptor::Signaled(identifier, _) => self.epoch(&identifier).cloned(),
}
}
@@ -524,7 +497,8 @@ impl<Hash, Number, E: Epoch> EpochChanges<Hash, Number, E> where
parent_number: Number,
slot: E::Slot,
make_genesis: G,
) -> Result<Option<E>, fork_tree::Error<D::Error>> where
) -> Result<Option<E>, fork_tree::Error<D::Error>>
where
G: FnOnce(E::Slot) -> E,
E: Clone,
{
@@ -532,7 +506,7 @@ impl<Hash, Number, E: Epoch> EpochChanges<Hash, Number, E> where
descendent_of_builder,
parent_hash,
parent_number,
slot
slot,
)?;
Ok(descriptor.and_then(|des| self.epoch_data(&des, make_genesis)))
@@ -555,8 +529,8 @@ impl<Hash, Number, E: Epoch> EpochChanges<Hash, Number, E> where
// "descends" from our parent-hash.
let fake_head_hash = fake_head_hash(parent_hash);
let is_descendent_of = descendent_of_builder
.build_is_descendent_of(Some((fake_head_hash, *parent_hash)));
let is_descendent_of =
descendent_of_builder.build_is_descendent_of(Some((fake_head_hash, *parent_hash)));
if parent_number == Zero::zero() {
// need to insert the genesis epoch.
@@ -569,37 +543,41 @@ impl<Hash, Number, E: Epoch> EpochChanges<Hash, Number, E> where
// at epoch_1 -- all we're doing here is figuring out which node
// we need.
let predicate = |epoch: &PersistedEpochHeader<E>| match *epoch {
PersistedEpochHeader::Genesis(ref epoch_0, _) =>
epoch_0.start_slot <= slot,
PersistedEpochHeader::Regular(ref epoch_n) =>
epoch_n.start_slot <= slot,
PersistedEpochHeader::Genesis(ref epoch_0, _) => epoch_0.start_slot <= slot,
PersistedEpochHeader::Regular(ref epoch_n) => epoch_n.start_slot <= slot,
};
self.inner.find_node_where(
&fake_head_hash,
&(parent_number + One::one()),
&is_descendent_of,
&predicate,
)
self.inner
.find_node_where(
&fake_head_hash,
&(parent_number + One::one()),
&is_descendent_of,
&predicate,
)
.map(|n| {
n.map(|node| (match node.data {
// Ok, we found our node.
// and here we figure out which of the internal epochs
// of a genesis node to use based on their start slot.
PersistedEpochHeader::Genesis(ref epoch_0, ref epoch_1) =>
if epoch_1.start_slot <= slot {
(EpochIdentifierPosition::Genesis1, epoch_1.clone())
} else {
(EpochIdentifierPosition::Genesis0, epoch_0.clone())
n.map(|node| {
(
match node.data {
// Ok, we found our node.
// and here we figure out which of the internal epochs
// of a genesis node to use based on their start slot.
PersistedEpochHeader::Genesis(ref epoch_0, ref epoch_1) =>
if epoch_1.start_slot <= slot {
(EpochIdentifierPosition::Genesis1, epoch_1.clone())
} else {
(EpochIdentifierPosition::Genesis0, epoch_0.clone())
},
PersistedEpochHeader::Regular(ref epoch_n) =>
(EpochIdentifierPosition::Regular, epoch_n.clone()),
},
PersistedEpochHeader::Regular(ref epoch_n) =>
(EpochIdentifierPosition::Regular, epoch_n.clone()),
}, node)).map(|((position, header), node)| {
ViableEpochDescriptor::Signaled(EpochIdentifier {
position,
hash: node.hash,
number: node.number
}, header)
node,
)
})
.map(|((position, header), node)| {
ViableEpochDescriptor::Signaled(
EpochIdentifier { position, hash: node.hash, number: node.number },
header,
)
})
})
}
@@ -617,16 +595,11 @@ impl<Hash, Number, E: Epoch> EpochChanges<Hash, Number, E> where
parent_hash: Hash,
epoch: IncrementedEpoch<E>,
) -> Result<(), fork_tree::Error<D::Error>> {
let is_descendent_of = descendent_of_builder
.build_is_descendent_of(Some((hash, parent_hash)));
let is_descendent_of =
descendent_of_builder.build_is_descendent_of(Some((hash, parent_hash)));
let header = PersistedEpochHeader::<E>::from(&epoch.0);
let res = self.inner.import(
hash,
number,
header,
&is_descendent_of,
);
let res = self.inner.import(hash, number, header, &is_descendent_of);
match res {
Ok(_) | Err(fork_tree::Error::Duplicate) => {
@@ -653,8 +626,7 @@ pub type SharedEpochChanges<Block, Epoch> =
#[cfg(test)]
mod tests {
use super::*;
use super::Epoch as EpochT;
use super::{Epoch as EpochT, *};
#[derive(Debug, PartialEq)]
pub struct TestError;
@@ -667,15 +639,14 @@ mod tests {
impl std::error::Error for TestError {}
impl<'a, F: 'a , H: 'a + PartialEq + std::fmt::Debug> IsDescendentOfBuilder<H> for &'a F
where F: Fn(&H, &H) -> Result<bool, TestError>
impl<'a, F: 'a, H: 'a + PartialEq + std::fmt::Debug> IsDescendentOfBuilder<H> for &'a F
where
F: Fn(&H, &H) -> Result<bool, TestError>,
{
type Error = TestError;
type IsDescendentOf = Box<dyn Fn(&H, &H) -> Result<bool, TestError> + 'a>;
fn build_is_descendent_of(&self, current: Option<(H, H)>)
-> Self::IsDescendentOf
{
fn build_is_descendent_of(&self, current: Option<(H, H)>) -> Self::IsDescendentOf {
let f = *self;
Box::new(move |base, head| {
let mut head = head;
@@ -683,7 +654,7 @@ mod tests {
if let Some((ref c_head, ref c_parent)) = current {
if head == c_head {
if base == c_parent {
return Ok(true);
return Ok(true)
} else {
head = c_parent;
}
@@ -709,10 +680,7 @@ mod tests {
type Slot = Slot;
fn increment(&self, _: ()) -> Self {
Epoch {
start_slot: self.start_slot + self.duration,
duration: self.duration,
}
Epoch { start_slot: self.start_slot + self.duration, duration: self.duration }
}
fn end_slot(&self) -> Slot {
@@ -726,7 +694,6 @@ mod tests {
#[test]
fn genesis_epoch_is_created_but_not_imported() {
//
// A - B
// \
// — C
@@ -741,12 +708,10 @@ mod tests {
};
let epoch_changes = EpochChanges::<_, _, Epoch>::new();
let genesis_epoch = epoch_changes.epoch_descriptor_for_child_of(
&is_descendent_of,
b"0",
0,
10101,
).unwrap().unwrap();
let genesis_epoch = epoch_changes
.epoch_descriptor_for_child_of(&is_descendent_of, b"0", 0, 10101)
.unwrap()
.unwrap();
match genesis_epoch {
ViableEpochDescriptor::UnimportedGenesis(slot) => {
@@ -755,12 +720,10 @@ mod tests {
_ => panic!("should be unimported genesis"),
};
let genesis_epoch_2 = epoch_changes.epoch_descriptor_for_child_of(
&is_descendent_of,
b"0",
0,
10102,
).unwrap().unwrap();
let genesis_epoch_2 = epoch_changes
.epoch_descriptor_for_child_of(&is_descendent_of, b"0", 0, 10102)
.unwrap()
.unwrap();
match genesis_epoch_2 {
ViableEpochDescriptor::UnimportedGenesis(slot) => {
@@ -772,7 +735,6 @@ mod tests {
#[test]
fn epoch_changes_between_blocks() {
//
// A - B
// \
// — C
@@ -786,34 +748,23 @@ mod tests {
}
};
let make_genesis = |slot| Epoch {
start_slot: slot,
duration: 100,
};
let make_genesis = |slot| Epoch { start_slot: slot, duration: 100 };
let mut epoch_changes = EpochChanges::<_, _, Epoch>::new();
let genesis_epoch = epoch_changes.epoch_descriptor_for_child_of(
&is_descendent_of,
b"0",
0,
100,
).unwrap().unwrap();
let genesis_epoch = epoch_changes
.epoch_descriptor_for_child_of(&is_descendent_of, b"0", 0, 100)
.unwrap()
.unwrap();
assert_eq!(genesis_epoch, ViableEpochDescriptor::UnimportedGenesis(100));
let import_epoch_1 = epoch_changes
.viable_epoch(&genesis_epoch, &make_genesis)
.unwrap()
.increment(());
let import_epoch_1 =
epoch_changes.viable_epoch(&genesis_epoch, &make_genesis).unwrap().increment(());
let epoch_1 = import_epoch_1.as_ref().clone();
epoch_changes.import(
&is_descendent_of,
*b"A",
1,
*b"0",
import_epoch_1,
).unwrap();
epoch_changes
.import(&is_descendent_of, *b"A", 1, *b"0", import_epoch_1)
.unwrap();
let genesis_epoch = epoch_changes.epoch_data(&genesis_epoch, &make_genesis).unwrap();
assert!(is_descendent_of(b"0", b"A").unwrap());
@@ -823,13 +774,10 @@ mod tests {
{
// x is still within the genesis epoch.
let x = epoch_changes.epoch_data_for_child_of(
&is_descendent_of,
b"A",
1,
end_slot - 1,
&make_genesis,
).unwrap().unwrap();
let x = epoch_changes
.epoch_data_for_child_of(&is_descendent_of, b"A", 1, end_slot - 1, &make_genesis)
.unwrap()
.unwrap();
assert_eq!(x, genesis_epoch);
}
@@ -837,13 +785,10 @@ mod tests {
{
// x is now at the next epoch, because the block is now at the
// start slot of epoch 1.
let x = epoch_changes.epoch_data_for_child_of(
&is_descendent_of,
b"A",
1,
end_slot,
&make_genesis,
).unwrap().unwrap();
let x = epoch_changes
.epoch_data_for_child_of(&is_descendent_of, b"A", 1, end_slot, &make_genesis)
.unwrap()
.unwrap();
assert_eq!(x, epoch_1);
}
@@ -851,13 +796,16 @@ mod tests {
{
// x is now at the next epoch, because the block is now after
// start slot of epoch 1.
let x = epoch_changes.epoch_data_for_child_of(
&is_descendent_of,
b"A",
1,
epoch_1.end_slot() - 1,
&make_genesis,
).unwrap().unwrap();
let x = epoch_changes
.epoch_data_for_child_of(
&is_descendent_of,
b"A",
1,
epoch_1.end_slot() - 1,
&make_genesis,
)
.unwrap()
.unwrap();
assert_eq!(x, epoch_1);
}
@@ -880,90 +828,65 @@ mod tests {
let duration = 100;
let make_genesis = |slot| Epoch {
start_slot: slot,
duration,
};
let make_genesis = |slot| Epoch { start_slot: slot, duration };
let mut epoch_changes = EpochChanges::new();
let next_descriptor = ();
// insert genesis epoch for A
{
let genesis_epoch_a_descriptor = epoch_changes.epoch_descriptor_for_child_of(
&is_descendent_of,
b"0",
0,
100,
).unwrap().unwrap();
let genesis_epoch_a_descriptor = epoch_changes
.epoch_descriptor_for_child_of(&is_descendent_of, b"0", 0, 100)
.unwrap()
.unwrap();
let incremented_epoch = epoch_changes
.viable_epoch(&genesis_epoch_a_descriptor, &make_genesis)
.unwrap()
.increment(next_descriptor.clone());
epoch_changes.import(
&is_descendent_of,
*b"A",
1,
*b"0",
incremented_epoch,
).unwrap();
epoch_changes
.import(&is_descendent_of, *b"A", 1, *b"0", incremented_epoch)
.unwrap();
}
// insert genesis epoch for X
{
let genesis_epoch_x_descriptor = epoch_changes.epoch_descriptor_for_child_of(
&is_descendent_of,
b"0",
0,
1000,
).unwrap().unwrap();
let genesis_epoch_x_descriptor = epoch_changes
.epoch_descriptor_for_child_of(&is_descendent_of, b"0", 0, 1000)
.unwrap()
.unwrap();
let incremented_epoch = epoch_changes
.viable_epoch(&genesis_epoch_x_descriptor, &make_genesis)
.unwrap()
.increment(next_descriptor.clone());
epoch_changes.import(
&is_descendent_of,
*b"X",
1,
*b"0",
incremented_epoch,
).unwrap();
epoch_changes
.import(&is_descendent_of, *b"X", 1, *b"0", incremented_epoch)
.unwrap();
}
// now check that the genesis epochs for our respective block 1s
// respect the chain structure.
{
let epoch_for_a_child = epoch_changes.epoch_data_for_child_of(
&is_descendent_of,
b"A",
1,
101,
&make_genesis,
).unwrap().unwrap();
let epoch_for_a_child = epoch_changes
.epoch_data_for_child_of(&is_descendent_of, b"A", 1, 101, &make_genesis)
.unwrap()
.unwrap();
assert_eq!(epoch_for_a_child, make_genesis(100));
let epoch_for_x_child = epoch_changes.epoch_data_for_child_of(
&is_descendent_of,
b"X",
1,
1001,
&make_genesis,
).unwrap().unwrap();
let epoch_for_x_child = epoch_changes
.epoch_data_for_child_of(&is_descendent_of, b"X", 1, 1001, &make_genesis)
.unwrap()
.unwrap();
assert_eq!(epoch_for_x_child, make_genesis(1000));
let epoch_for_x_child_before_genesis = epoch_changes.epoch_data_for_child_of(
&is_descendent_of,
b"X",
1,
101,
&make_genesis,
).unwrap();
let epoch_for_x_child_before_genesis = epoch_changes
.epoch_data_for_child_of(&is_descendent_of, b"X", 1, 101, &make_genesis)
.unwrap();
// even though there is a genesis epoch at that slot, it's not in
// this chain.
@@ -18,11 +18,11 @@
//! Migration types for epoch changes.
use std::collections::BTreeMap;
use codec::{Encode, Decode};
use crate::{Epoch, EpochChanges, PersistedEpoch, PersistedEpochHeader};
use codec::{Decode, Encode};
use fork_tree::ForkTree;
use sp_runtime::traits::{Block as BlockT, NumberFor};
use crate::{Epoch, EpochChanges, PersistedEpoch, PersistedEpochHeader};
use std::collections::BTreeMap;
/// Legacy definition of epoch changes.
#[derive(Clone, Encode, Decode)]
@@ -31,9 +31,11 @@ pub struct EpochChangesV0<Hash, Number, E: Epoch> {
}
/// Type alias for legacy definition of epoch changes.
pub type EpochChangesForV0<Block, Epoch> = EpochChangesV0<<Block as BlockT>::Hash, NumberFor<Block>, Epoch>;
pub type EpochChangesForV0<Block, Epoch> =
EpochChangesV0<<Block as BlockT>::Hash, NumberFor<Block>, Epoch>;
impl<Hash, Number, E: Epoch> EpochChangesV0<Hash, Number, E> where
impl<Hash, Number, E: Epoch> EpochChangesV0<Hash, Number, E>
where
Hash: PartialEq + Ord + Copy,
Number: Ord + Copy,
{
@@ -19,26 +19,30 @@
//! Extensions for manual seal to produce blocks valid for any runtime.
use super::Error;
use sp_runtime::traits::{Block as BlockT, DigestFor};
use sp_inherents::InherentData;
use sp_consensus::BlockImportParams;
use sp_inherents::InherentData;
use sp_runtime::traits::{Block as BlockT, DigestFor};
pub mod babe;
/// Consensus data provider, manual seal uses this trait object for authoring blocks valid
/// Consensus data provider, manual seal uses this trait object for authoring blocks valid
/// for any runtime.
pub trait ConsensusDataProvider<B: BlockT>: Send + Sync {
/// Block import transaction type
type Transaction;
/// Attempt to create a consensus digest.
fn create_digest(&self, parent: &B::Header, inherents: &InherentData) -> Result<DigestFor<B>, Error>;
fn create_digest(
&self,
parent: &B::Header,
inherents: &InherentData,
) -> Result<DigestFor<B>, Error>;
/// set up the neccessary import params.
fn append_block_import(
&self,
parent: &B::Header,
params: &mut BlockImportParams<B, Self::Transaction>,
inherents: &InherentData
inherents: &InherentData,
) -> Result<(), Error>;
}
@@ -21,30 +21,40 @@
use super::ConsensusDataProvider;
use crate::Error;
use codec::Encode;
use std::{borrow::Cow, sync::{Arc, atomic}, time::SystemTime};
use sc_client_api::{AuxStore, UsageProvider};
use sc_consensus_babe::{
Config, Epoch, authorship, CompatibleDigestItem, BabeIntermediate, INTERMEDIATE_KEY,
find_pre_digest,
authorship, find_pre_digest, BabeIntermediate, CompatibleDigestItem, Config, Epoch,
INTERMEDIATE_KEY,
};
use sc_consensus_epochs::{
descendent_query, EpochHeader, SharedEpochChanges, ViableEpochDescriptor,
};
use sc_consensus_epochs::{SharedEpochChanges, descendent_query, ViableEpochDescriptor, EpochHeader};
use sp_keystore::SyncCryptoStorePtr;
use std::{
borrow::Cow,
sync::{atomic, Arc},
time::SystemTime,
};
use sp_api::{ProvideRuntimeApi, TransactionFor};
use sp_blockchain::{HeaderBackend, HeaderMetadata};
use sp_consensus::{BlockImportParams, BlockOrigin, ForkChoiceStrategy};
use sp_consensus_slots::Slot;
use sp_consensus_babe::{
BabeApi, inherents::BabeInherentData, ConsensusLog, BABE_ENGINE_ID, AuthorityId,
digests::{PreDigest, SecondaryPlainPreDigest, NextEpochDescriptor}, BabeAuthorityWeight,
use sp_consensus::{
import_queue::{CacheKeyId, Verifier},
BlockImportParams, BlockOrigin, ForkChoiceStrategy,
};
use sp_consensus_babe::{
digests::{NextEpochDescriptor, PreDigest, SecondaryPlainPreDigest},
inherents::BabeInherentData,
AuthorityId, BabeApi, BabeAuthorityWeight, ConsensusLog, BABE_ENGINE_ID,
};
use sp_consensus_slots::Slot;
use sp_inherents::{InherentData, InherentDataProvider, InherentIdentifier};
use sp_runtime::{
traits::{DigestItemFor, DigestFor, Block as BlockT, Zero, Header},
generic::{Digest, BlockId}, Justifications,
generic::{BlockId, Digest},
traits::{Block as BlockT, DigestFor, DigestItemFor, Header, Zero},
Justifications,
};
use sp_timestamp::{InherentType, INHERENT_IDENTIFIER, TimestampInherentData};
use sp_consensus::import_queue::{Verifier, CacheKeyId};
use sp_timestamp::{InherentType, TimestampInherentData, INHERENT_IDENTIFIER};
/// Provides BABE-compatible predigests and BlockImportParams.
/// Intended for use with BABE runtimes.
@@ -77,19 +87,16 @@ pub struct BabeVerifier<B: BlockT, C> {
impl<B: BlockT, C> BabeVerifier<B, C> {
/// create a nrew verifier
pub fn new(epoch_changes: SharedEpochChanges<B, Epoch>, client: Arc<C>) -> BabeVerifier<B, C> {
BabeVerifier {
epoch_changes,
client,
}
BabeVerifier { epoch_changes, client }
}
}
/// The verifier for the manual seal engine; instantly finalizes.
#[async_trait::async_trait]
impl<B, C> Verifier<B> for BabeVerifier<B, C>
where
B: BlockT,
C: HeaderBackend<B> + HeaderMetadata<B, Error = sp_blockchain::Error>
where
B: BlockT,
C: HeaderBackend<B> + HeaderMetadata<B, Error = sp_blockchain::Error>,
{
async fn verify(
&mut self,
@@ -107,7 +114,9 @@ impl<B, C> Verifier<B> for BabeVerifier<B, C>
let pre_digest = find_pre_digest::<B>(&header)?;
let parent_hash = header.parent_hash();
let parent = self.client.header(BlockId::Hash(*parent_hash))
let parent = self
.client
.header(BlockId::Hash(*parent_hash))
.ok()
.flatten()
.ok_or_else(|| format!("header for block {} not found", parent_hash))?;
@@ -134,14 +143,14 @@ impl<B, C> Verifier<B> for BabeVerifier<B, C>
}
impl<B, C> BabeConsensusDataProvider<B, C>
where
B: BlockT,
C: AuxStore
+ HeaderBackend<B>
+ ProvideRuntimeApi<B>
+ HeaderMetadata<B, Error = sp_blockchain::Error>
+ UsageProvider<B>,
C::Api: BabeApi<B>,
where
B: BlockT,
C: AuxStore
+ HeaderBackend<B>
+ ProvideRuntimeApi<B>
+ HeaderMetadata<B, Error = sp_blockchain::Error>
+ UsageProvider<B>,
C::Api: BabeApi<B>,
{
pub fn new(
client: Arc<C>,
@@ -155,13 +164,7 @@ impl<B, C> BabeConsensusDataProvider<B, C>
let config = Config::get_or_compute(&*client)?;
Ok(Self {
config,
client,
keystore,
epoch_changes,
authorities,
})
Ok(Self { config, client, keystore, epoch_changes, authorities })
}
fn epoch(&self, parent: &B::Header, slot: Slot) -> Result<Epoch, Error> {
@@ -177,10 +180,7 @@ impl<B, C> BabeConsensusDataProvider<B, C>
.ok_or_else(|| sp_consensus::Error::InvalidAuthoritiesSet)?;
let epoch = epoch_changes
.viable_epoch(
&epoch_descriptor,
|slot| Epoch::genesis(&self.config, slot),
)
.viable_epoch(&epoch_descriptor, |slot| Epoch::genesis(&self.config, slot))
.ok_or_else(|| {
log::info!(target: "babe", "create_digest: no viable_epoch :(");
sp_consensus::Error::InvalidAuthoritiesSet
@@ -191,38 +191,37 @@ impl<B, C> BabeConsensusDataProvider<B, C>
}
impl<B, C> ConsensusDataProvider<B> for BabeConsensusDataProvider<B, C>
where
B: BlockT,
C: AuxStore
+ HeaderBackend<B>
+ HeaderMetadata<B, Error = sp_blockchain::Error>
+ UsageProvider<B>
+ ProvideRuntimeApi<B>,
C::Api: BabeApi<B>,
where
B: BlockT,
C: AuxStore
+ HeaderBackend<B>
+ HeaderMetadata<B, Error = sp_blockchain::Error>
+ UsageProvider<B>
+ ProvideRuntimeApi<B>,
C::Api: BabeApi<B>,
{
type Transaction = TransactionFor<C, B>;
fn create_digest(&self, parent: &B::Header, inherents: &InherentData) -> Result<DigestFor<B>, Error> {
let slot = inherents.babe_inherent_data()?
fn create_digest(
&self,
parent: &B::Header,
inherents: &InherentData,
) -> Result<DigestFor<B>, Error> {
let slot = inherents
.babe_inherent_data()?
.ok_or_else(|| Error::StringError("No babe inherent data".into()))?;
let epoch = self.epoch(parent, slot)?;
// this is a dev node environment, we should always be able to claim a slot.
let logs = if let Some((predigest, _)) = authorship::claim_slot(
slot,
&epoch,
&self.keystore,
) {
vec![
<DigestItemFor<B> as CompatibleDigestItem>::babe_pre_digest(predigest),
]
let logs = if let Some((predigest, _)) =
authorship::claim_slot(slot, &epoch, &self.keystore)
{
vec![<DigestItemFor<B> as CompatibleDigestItem>::babe_pre_digest(predigest)]
} else {
// well we couldn't claim a slot because this is an existing chain and we're not in the authorities.
// we need to tell BabeBlockImport that the epoch has changed, and we put ourselves in the authorities.
let predigest = PreDigest::SecondaryPlain(SecondaryPlainPreDigest {
slot,
authority_index: 0_u32,
});
let predigest =
PreDigest::SecondaryPlain(SecondaryPlainPreDigest { slot, authority_index: 0_u32 });
let mut epoch_changes = self.epoch_changes.shared_data();
let epoch_descriptor = epoch_changes
@@ -232,12 +231,15 @@ impl<B, C> ConsensusDataProvider<B> for BabeConsensusDataProvider<B, C>
parent.number().clone(),
slot,
)
.map_err(|e| Error::StringError(format!("failed to fetch epoch_descriptor: {}", e)))?
.map_err(|e| {
Error::StringError(format!("failed to fetch epoch_descriptor: {}", e))
})?
.ok_or_else(|| sp_consensus::Error::InvalidAuthoritiesSet)?;
match epoch_descriptor {
ViableEpochDescriptor::Signaled(identifier, _epoch_header) => {
let epoch_mut = epoch_changes.epoch_mut(&identifier)
let epoch_mut = epoch_changes
.epoch_mut(&identifier)
.ok_or_else(|| sp_consensus::Error::InvalidAuthoritiesSet)?;
// mutate the current epoch
@@ -251,15 +253,13 @@ impl<B, C> ConsensusDataProvider<B> for BabeConsensusDataProvider<B, C>
vec![
DigestItemFor::<B>::PreRuntime(BABE_ENGINE_ID, predigest.encode()),
DigestItemFor::<B>::Consensus(BABE_ENGINE_ID, next_epoch.encode())
DigestItemFor::<B>::Consensus(BABE_ENGINE_ID, next_epoch.encode()),
]
},
ViableEpochDescriptor::UnimportedGenesis(_) => {
// since this is the genesis, secondary predigest works for now.
vec![
DigestItemFor::<B>::PreRuntime(BABE_ENGINE_ID, predigest.encode()),
]
}
vec![DigestItemFor::<B>::PreRuntime(BABE_ENGINE_ID, predigest.encode())]
},
}
};
@@ -270,9 +270,10 @@ impl<B, C> ConsensusDataProvider<B> for BabeConsensusDataProvider<B, C>
&self,
parent: &B::Header,
params: &mut BlockImportParams<B, Self::Transaction>,
inherents: &InherentData
inherents: &InherentData,
) -> Result<(), Error> {
let slot = inherents.babe_inherent_data()?
let slot = inherents
.babe_inherent_data()?
.ok_or_else(|| Error::StringError("No babe inherent data".into()))?;
let epoch_changes = self.epoch_changes.shared_data();
let mut epoch_descriptor = epoch_changes
@@ -289,27 +290,27 @@ impl<B, C> ConsensusDataProvider<B> for BabeConsensusDataProvider<B, C>
// a quick check to see if we're in the authorities
let epoch = self.epoch(parent, slot)?;
let (authority, _) = self.authorities.first().expect("authorities is non-emptyp; qed");
let has_authority = epoch.authorities.iter()
.find(|(id, _)| *id == *authority)
.is_some();
let has_authority = epoch.authorities.iter().find(|(id, _)| *id == *authority).is_some();
if !has_authority {
log::info!(target: "manual-seal", "authority not found");
let timestamp = inherents.timestamp_inherent_data()?
let timestamp = inherents
.timestamp_inherent_data()?
.ok_or_else(|| Error::StringError("No timestamp inherent data".into()))?;
let slot = *timestamp / self.config.slot_duration;
// manually hard code epoch descriptor
epoch_descriptor = match epoch_descriptor {
ViableEpochDescriptor::Signaled(identifier, _header) => {
ViableEpochDescriptor::Signaled(identifier, _header) =>
ViableEpochDescriptor::Signaled(
identifier,
EpochHeader {
start_slot: slot.into(),
end_slot: (slot * self.config.epoch_length).into(),
},
)
},
_ => unreachable!("we're not in the authorities, so this isn't the genesis epoch; qed")
),
_ => unreachable!(
"we're not in the authorities, so this isn't the genesis epoch; qed"
),
};
}
@@ -326,16 +327,16 @@ impl<B, C> ConsensusDataProvider<B> for BabeConsensusDataProvider<B, C>
/// Mocks the timestamp inherent to always produce the timestamp for the next babe slot.
pub struct SlotTimestampProvider {
time: atomic::AtomicU64,
slot_duration: u64
slot_duration: u64,
}
impl SlotTimestampProvider {
/// Create a new mocked time stamp provider.
pub fn new<B, C>(client: Arc<C>) -> Result<Self, Error>
where
B: BlockT,
C: AuxStore + HeaderBackend<B> + ProvideRuntimeApi<B> + UsageProvider<B>,
C::Api: BabeApi<B>,
where
B: BlockT,
C: AuxStore + HeaderBackend<B> + ProvideRuntimeApi<B> + UsageProvider<B>,
C::Api: BabeApi<B>,
{
let slot_duration = Config::get_or_compute(&*client)?.slot_duration;
let info = client.info();
@@ -355,10 +356,7 @@ impl SlotTimestampProvider {
.as_millis() as u64
};
Ok(Self {
time: atomic::AtomicU64::new(time),
slot_duration,
})
Ok(Self { time: atomic::AtomicU64::new(time), slot_duration })
}
/// Get the current slot number
@@ -369,12 +367,13 @@ impl SlotTimestampProvider {
#[async_trait::async_trait]
impl InherentDataProvider for SlotTimestampProvider {
fn provide_inherent_data(&self, inherent_data: &mut InherentData) -> Result<(), sp_inherents::Error> {
fn provide_inherent_data(
&self,
inherent_data: &mut InherentData,
) -> Result<(), sp_inherents::Error> {
// we update the time here.
let duration: InherentType = self.time.fetch_add(
self.slot_duration,
atomic::Ordering::SeqCst,
).into();
let duration: InherentType =
self.time.fetch_add(self.slot_duration, atomic::Ordering::SeqCst).into();
inherent_data.put_data(INHERENT_IDENTIFIER, &duration)?;
Ok(())
}
@@ -19,10 +19,10 @@
//! A manual sealing engine: the engine listens for rpc calls to seal blocks and create forks.
//! This is suitable for a testing environment.
use sp_consensus::{Error as ConsensusError, ImportResult};
use futures::channel::{mpsc::SendError, oneshot};
use sp_blockchain::Error as BlockchainError;
use sp_consensus::{Error as ConsensusError, ImportResult};
use sp_inherents::Error as InherentsError;
use futures::channel::{oneshot, mpsc::SendError};
/// Error code for rpc
mod codes {
@@ -63,14 +63,14 @@ pub enum Error {
#[display(fmt = "{}", _0)]
#[from(ignore)]
StringError(String),
///send error
/// send error
#[display(fmt = "Consensus process is terminating")]
Canceled(oneshot::Canceled),
///send error
/// send error
#[display(fmt = "Consensus process is terminating")]
SendError(SendError),
/// Some other error.
#[display(fmt="Other error: {}", _0)]
#[display(fmt = "Other error: {}", _0)]
Other(Box<dyn std::error::Error + Send>),
}
@@ -85,7 +85,7 @@ impl Error {
InherentError(_) => codes::INHERENTS_ERROR,
BlockchainError(_) => codes::BLOCKCHAIN_ERROR,
SendError(_) | Canceled(_) => codes::SERVER_SHUTTING_DOWN,
_ => codes::UNKNOWN_ERROR
_ => codes::UNKNOWN_ERROR,
}
}
}
@@ -95,7 +95,7 @@ impl std::convert::From<Error> for jsonrpc_core::Error {
jsonrpc_core::Error {
code: jsonrpc_core::ErrorCode::ServerError(error.to_code()),
message: format!("{}", error),
data: None
data: None,
}
}
}
@@ -19,14 +19,9 @@
//! Block finalization utilities
use crate::rpc;
use sp_runtime::{
Justification,
traits::Block as BlockT,
generic::BlockId,
};
use std::sync::Arc;
use sc_client_api::backend::{Backend as ClientBackend, Finalizer};
use std::marker::PhantomData;
use sp_runtime::{generic::BlockId, traits::Block as BlockT, Justification};
use std::{marker::PhantomData, sync::Arc};
/// params for block finalization.
pub struct FinalizeBlockParams<B: BlockT, F, CB> {
@@ -42,30 +37,23 @@ pub struct FinalizeBlockParams<B: BlockT, F, CB> {
pub _phantom: PhantomData<CB>,
}
/// finalizes a block in the backend with the given params.
pub async fn finalize_block<B, F, CB>(params: FinalizeBlockParams<B, F, CB>)
where
B: BlockT,
F: Finalizer<B, CB>,
CB: ClientBackend<B>,
where
B: BlockT,
F: Finalizer<B, CB>,
CB: ClientBackend<B>,
{
let FinalizeBlockParams {
hash,
mut sender,
justification,
finalizer,
..
} = params;
let FinalizeBlockParams { hash, mut sender, justification, finalizer, .. } = params;
match finalizer.finalize_block(BlockId::Hash(hash), justification, true) {
Err(e) => {
log::warn!("Failed to finalize block {:?}", e);
rpc::send_result(&mut sender, Err(e.into()))
}
},
Ok(()) => {
log::info!("✅ Successfully finalized block: {}", hash);
rpc::send_result(&mut sender, Ok(()))
}
},
}
}
+171 -201
View File
@@ -20,17 +20,17 @@
//! This is suitable for a testing environment.
use futures::prelude::*;
use sp_consensus::{
Environment, Proposer, SelectChain, BlockImport,
ForkChoiceStrategy, BlockImportParams, BlockOrigin,
import_queue::{Verifier, BasicQueue, CacheKeyId, BoxBlockImport},
};
use sp_blockchain::HeaderBackend;
use sp_inherents::CreateInherentDataProviders;
use sp_runtime::{traits::Block as BlockT, Justifications, ConsensusEngineId};
use sc_client_api::backend::{Backend as ClientBackend, Finalizer};
use std::{sync::Arc, marker::PhantomData};
use prometheus_endpoint::Registry;
use sc_client_api::backend::{Backend as ClientBackend, Finalizer};
use sp_blockchain::HeaderBackend;
use sp_consensus::{
import_queue::{BasicQueue, BoxBlockImport, CacheKeyId, Verifier},
BlockImport, BlockImportParams, BlockOrigin, Environment, ForkChoiceStrategy, Proposer,
SelectChain,
};
use sp_inherents::CreateInherentDataProviders;
use sp_runtime::{traits::Block as BlockT, ConsensusEngineId, Justifications};
use std::{marker::PhantomData, sync::Arc};
mod error;
mod finalize_block;
@@ -40,14 +40,14 @@ pub mod consensus;
pub mod rpc;
pub use self::{
error::Error,
consensus::ConsensusDataProvider,
error::Error,
finalize_block::{finalize_block, FinalizeBlockParams},
seal_block::{SealBlockParams, seal_block, MAX_PROPOSAL_DURATION},
rpc::{EngineCommand, CreatedBlock},
rpc::{CreatedBlock, EngineCommand},
seal_block::{seal_block, SealBlockParams, MAX_PROPOSAL_DURATION},
};
use sp_api::{ProvideRuntimeApi, TransactionFor};
use sc_transaction_pool_api::TransactionPool;
use sp_api::{ProvideRuntimeApi, TransactionFor};
/// The `ConsensusEngineId` of Manual Seal.
pub const MANUAL_SEAL_ENGINE_ID: ConsensusEngineId = [b'm', b'a', b'n', b'l'];
@@ -80,17 +80,11 @@ pub fn import_queue<Block, Transaction>(
spawner: &impl sp_core::traits::SpawnEssentialNamed,
registry: Option<&Registry>,
) -> BasicQueue<Block, Transaction>
where
Block: BlockT,
Transaction: Send + Sync + 'static,
where
Block: BlockT,
Transaction: Send + Sync + 'static,
{
BasicQueue::new(
ManualSealVerifier,
block_import,
None,
spawner,
registry,
)
BasicQueue::new(ManualSealVerifier, block_import, None, spawner, registry)
}
/// Params required to start the instant sealing authorship task.
@@ -115,7 +109,8 @@ pub struct ManualSealParams<B: BlockT, BI, E, C: ProvideRuntimeApi<B>, TP, SC, C
pub select_chain: SC,
/// Digest provider for inclusion in blocks.
pub consensus_data_provider: Option<Box<dyn ConsensusDataProvider<B, Transaction = TransactionFor<C, B>>>>,
pub consensus_data_provider:
Option<Box<dyn ConsensusDataProvider<B, Transaction = TransactionFor<C, B>>>>,
/// Something that can create the inherent data providers.
pub create_inherent_data_providers: CIDP,
@@ -139,7 +134,8 @@ pub struct InstantSealParams<B: BlockT, BI, E, C: ProvideRuntimeApi<B>, TP, SC,
pub select_chain: SC,
/// Digest provider for inclusion in blocks.
pub consensus_data_provider: Option<Box<dyn ConsensusDataProvider<B, Transaction = TransactionFor<C, B>>>>,
pub consensus_data_provider:
Option<Box<dyn ConsensusDataProvider<B, Transaction = TransactionFor<C, B>>>>,
/// Something that can create the inherent data providers.
pub create_inherent_data_providers: CIDP,
@@ -156,58 +152,52 @@ pub async fn run_manual_seal<B, BI, CB, E, C, TP, SC, CS, CIDP>(
select_chain,
consensus_data_provider,
create_inherent_data_providers,
}: ManualSealParams<B, BI, E, C, TP, SC, CS, CIDP>
)
where
B: BlockT + 'static,
BI: BlockImport<B, Error = sp_consensus::Error, Transaction = sp_api::TransactionFor<C, B>>
+ Send + Sync + 'static,
C: HeaderBackend<B> + Finalizer<B, CB> + ProvideRuntimeApi<B> + 'static,
CB: ClientBackend<B> + 'static,
E: Environment<B> + 'static,
E::Proposer: Proposer<B, Transaction = TransactionFor<C, B>>,
CS: Stream<Item=EngineCommand<<B as BlockT>::Hash>> + Unpin + 'static,
SC: SelectChain<B> + 'static,
TransactionFor<C, B>: 'static,
TP: TransactionPool<Block = B>,
CIDP: CreateInherentDataProviders<B, ()>,
}: ManualSealParams<B, BI, E, C, TP, SC, CS, CIDP>,
) where
B: BlockT + 'static,
BI: BlockImport<B, Error = sp_consensus::Error, Transaction = sp_api::TransactionFor<C, B>>
+ Send
+ Sync
+ 'static,
C: HeaderBackend<B> + Finalizer<B, CB> + ProvideRuntimeApi<B> + 'static,
CB: ClientBackend<B> + 'static,
E: Environment<B> + 'static,
E::Proposer: Proposer<B, Transaction = TransactionFor<C, B>>,
CS: Stream<Item = EngineCommand<<B as BlockT>::Hash>> + Unpin + 'static,
SC: SelectChain<B> + 'static,
TransactionFor<C, B>: 'static,
TP: TransactionPool<Block = B>,
CIDP: CreateInherentDataProviders<B, ()>,
{
while let Some(command) = commands_stream.next().await {
match command {
EngineCommand::SealNewBlock {
create_empty,
finalize,
parent_hash,
sender,
} => {
seal_block(
SealBlockParams {
sender,
parent_hash,
finalize,
create_empty,
env: &mut env,
select_chain: &select_chain,
block_import: &mut block_import,
consensus_data_provider: consensus_data_provider.as_ref().map(|p| &**p),
pool: pool.clone(),
client: client.clone(),
create_inherent_data_providers: &create_inherent_data_providers,
}
).await;
}
EngineCommand::SealNewBlock { create_empty, finalize, parent_hash, sender } => {
seal_block(SealBlockParams {
sender,
parent_hash,
finalize,
create_empty,
env: &mut env,
select_chain: &select_chain,
block_import: &mut block_import,
consensus_data_provider: consensus_data_provider.as_ref().map(|p| &**p),
pool: pool.clone(),
client: client.clone(),
create_inherent_data_providers: &create_inherent_data_providers,
})
.await;
},
EngineCommand::FinalizeBlock { hash, sender, justification } => {
let justification = justification.map(|j| (MANUAL_SEAL_ENGINE_ID, j));
finalize_block(
FinalizeBlockParams {
hash,
sender,
justification,
finalizer: client.clone(),
_phantom: PhantomData,
}
).await
}
finalize_block(FinalizeBlockParams {
hash,
sender,
justification,
finalizer: client.clone(),
_phantom: PhantomData,
})
.await
},
}
}
}
@@ -224,63 +214,57 @@ pub async fn run_instant_seal<B, BI, CB, E, C, TP, SC, CIDP>(
select_chain,
consensus_data_provider,
create_inherent_data_providers,
}: InstantSealParams<B, BI, E, C, TP, SC, CIDP>
)
where
B: BlockT + 'static,
BI: BlockImport<B, Error = sp_consensus::Error, Transaction = sp_api::TransactionFor<C, B>>
+ Send + Sync + 'static,
C: HeaderBackend<B> + Finalizer<B, CB> + ProvideRuntimeApi<B> + 'static,
CB: ClientBackend<B> + 'static,
E: Environment<B> + 'static,
E::Proposer: Proposer<B, Transaction = TransactionFor<C, B>>,
SC: SelectChain<B> + 'static,
TransactionFor<C, B>: 'static,
TP: TransactionPool<Block = B>,
CIDP: CreateInherentDataProviders<B, ()>,
}: InstantSealParams<B, BI, E, C, TP, SC, CIDP>,
) where
B: BlockT + 'static,
BI: BlockImport<B, Error = sp_consensus::Error, Transaction = sp_api::TransactionFor<C, B>>
+ Send
+ Sync
+ 'static,
C: HeaderBackend<B> + Finalizer<B, CB> + ProvideRuntimeApi<B> + 'static,
CB: ClientBackend<B> + 'static,
E: Environment<B> + 'static,
E::Proposer: Proposer<B, Transaction = TransactionFor<C, B>>,
SC: SelectChain<B> + 'static,
TransactionFor<C, B>: 'static,
TP: TransactionPool<Block = B>,
CIDP: CreateInherentDataProviders<B, ()>,
{
// instant-seal creates blocks as soon as transactions are imported
// into the transaction pool.
let commands_stream = pool.import_notification_stream()
.map(|_| {
EngineCommand::SealNewBlock {
create_empty: false,
finalize: false,
parent_hash: None,
sender: None,
}
});
let commands_stream = pool.import_notification_stream().map(|_| EngineCommand::SealNewBlock {
create_empty: false,
finalize: false,
parent_hash: None,
sender: None,
});
run_manual_seal(
ManualSealParams {
block_import,
env,
client,
pool,
commands_stream,
select_chain,
consensus_data_provider,
create_inherent_data_providers,
}
).await
run_manual_seal(ManualSealParams {
block_import,
env,
client,
pool,
commands_stream,
select_chain,
consensus_data_provider,
create_inherent_data_providers,
})
.await
}
#[cfg(test)]
mod tests {
use super::*;
use substrate_test_runtime_client::{
DefaultTestClientBuilderExt,
TestClientBuilderExt,
AccountKeyring::*,
TestClientBuilder,
};
use sc_transaction_pool::{BasicPool, RevalidationType, Options};
use substrate_test_runtime_transaction_pool::{TestApi, uxt};
use sc_transaction_pool_api::{TransactionPool, MaintainedTransactionPool, TransactionSource};
use sp_runtime::generic::BlockId;
use sp_consensus::ImportedAux;
use sc_basic_authorship::ProposerFactory;
use sc_client_api::BlockBackend;
use sc_transaction_pool::{BasicPool, Options, RevalidationType};
use sc_transaction_pool_api::{MaintainedTransactionPool, TransactionPool, TransactionSource};
use sp_consensus::ImportedAux;
use sp_runtime::generic::BlockId;
use substrate_test_runtime_client::{
AccountKeyring::*, DefaultTestClientBuilderExt, TestClientBuilder, TestClientBuilderExt,
};
use substrate_test_runtime_transaction_pool::{uxt, TestApi};
fn api() -> Arc<TestApi> {
Arc::new(TestApi::empty())
@@ -303,40 +287,32 @@ mod tests {
spawner.clone(),
0,
));
let env = ProposerFactory::new(
spawner.clone(),
client.clone(),
pool.clone(),
None,
None,
);
let env = ProposerFactory::new(spawner.clone(), client.clone(), pool.clone(), None, None);
// this test checks that blocks are created as soon as transactions are imported into the pool.
let (sender, receiver) = futures::channel::oneshot::channel();
let mut sender = Arc::new(Some(sender));
let commands_stream = pool.pool().validated_pool().import_notification_stream()
.map(move |_| {
let commands_stream =
pool.pool().validated_pool().import_notification_stream().map(move |_| {
// we're only going to submit one tx so this fn will only be called once.
let mut_sender = Arc::get_mut(&mut sender).unwrap();
let mut_sender = Arc::get_mut(&mut sender).unwrap();
let sender = std::mem::take(mut_sender);
EngineCommand::SealNewBlock {
create_empty: false,
finalize: true,
parent_hash: None,
sender
sender,
}
});
let future = run_manual_seal(
ManualSealParams {
block_import: client.clone(),
env,
client: client.clone(),
pool: pool.clone(),
commands_stream,
select_chain,
create_inherent_data_providers: |_, _| async { Ok(()) },
consensus_data_provider: None,
}
);
let future = run_manual_seal(ManualSealParams {
block_import: client.clone(),
env,
client: client.clone(),
pool: pool.clone(),
commands_stream,
select_chain,
create_inherent_data_providers: |_, _| async { Ok(()) },
consensus_data_provider: None,
});
std::thread::spawn(|| {
let mut rt = tokio::runtime::Runtime::new().unwrap();
// spawn the background authorship task
@@ -380,27 +356,19 @@ mod tests {
spawner.clone(),
0,
));
let env = ProposerFactory::new(
spawner.clone(),
client.clone(),
pool.clone(),
None,
None,
);
let env = ProposerFactory::new(spawner.clone(), client.clone(), pool.clone(), None, None);
// this test checks that blocks are created as soon as an engine command is sent over the stream.
let (mut sink, commands_stream) = futures::channel::mpsc::channel(1024);
let future = run_manual_seal(
ManualSealParams {
block_import: client.clone(),
env,
client: client.clone(),
pool: pool.clone(),
commands_stream,
select_chain,
consensus_data_provider: None,
create_inherent_data_providers: |_, _| async { Ok(()) },
}
);
let future = run_manual_seal(ManualSealParams {
block_import: client.clone(),
env,
client: client.clone(),
pool: pool.clone(),
commands_stream,
select_chain,
consensus_data_provider: None,
create_inherent_data_providers: |_, _| async { Ok(()) },
});
std::thread::spawn(|| {
let mut rt = tokio::runtime::Runtime::new().unwrap();
// spawn the background authorship task
@@ -416,7 +384,9 @@ mod tests {
sender: Some(tx),
create_empty: false,
finalize: false,
}).await.unwrap();
})
.await
.unwrap();
let created_block = rx.await.unwrap().unwrap();
// assert that the background task returns ok
@@ -439,8 +409,10 @@ mod tests {
sink.send(EngineCommand::FinalizeBlock {
sender: Some(tx),
hash: header.hash(),
justification: None
}).await.unwrap();
justification: None,
})
.await
.unwrap();
// assert that the background task returns ok
assert_eq!(rx.await.unwrap().unwrap(), ());
}
@@ -461,27 +433,19 @@ mod tests {
spawner.clone(),
0,
));
let env = ProposerFactory::new(
spawner.clone(),
client.clone(),
pool.clone(),
None,
None,
);
let env = ProposerFactory::new(spawner.clone(), client.clone(), pool.clone(), None, None);
// this test checks that blocks are created as soon as an engine command is sent over the stream.
let (mut sink, commands_stream) = futures::channel::mpsc::channel(1024);
let future = run_manual_seal(
ManualSealParams {
block_import: client.clone(),
env,
client: client.clone(),
pool: pool.clone(),
commands_stream,
select_chain,
consensus_data_provider: None,
create_inherent_data_providers: |_, _| async { Ok(()) },
}
);
let future = run_manual_seal(ManualSealParams {
block_import: client.clone(),
env,
client: client.clone(),
pool: pool.clone(),
commands_stream,
select_chain,
consensus_data_provider: None,
create_inherent_data_providers: |_, _| async { Ok(()) },
});
std::thread::spawn(|| {
let mut rt = tokio::runtime::Runtime::new().unwrap();
// spawn the background authorship task
@@ -498,7 +462,9 @@ mod tests {
sender: Some(tx),
create_empty: false,
finalize: false,
}).await.unwrap();
})
.await
.unwrap();
let created_block = rx.await.unwrap().unwrap();
pool_api.increment_nonce(Alice.into());
@@ -524,31 +490,35 @@ mod tests {
pool.maintain(sc_transaction_pool_api::ChainEvent::NewBestBlock {
hash: header.hash(),
tree_route: None,
}).await;
})
.await;
let (tx1, rx1) = futures::channel::oneshot::channel();
assert!(sink.send(EngineCommand::SealNewBlock {
parent_hash: Some(created_block.hash),
sender: Some(tx1),
create_empty: false,
finalize: false,
}).await.is_ok());
assert_matches::assert_matches!(
rx1.await.expect("should be no error receiving"),
Ok(_)
);
assert!(sink
.send(EngineCommand::SealNewBlock {
parent_hash: Some(created_block.hash),
sender: Some(tx1),
create_empty: false,
finalize: false,
})
.await
.is_ok());
assert_matches::assert_matches!(rx1.await.expect("should be no error receiving"), Ok(_));
let block = client.block(&BlockId::Number(2)).unwrap().unwrap().block;
pool_api.add_block(block, true);
pool_api.increment_nonce(Alice.into());
assert!(pool.submit_one(&BlockId::Number(1), SOURCE, uxt(Bob, 0)).await.is_ok());
let (tx2, rx2) = futures::channel::oneshot::channel();
assert!(sink.send(EngineCommand::SealNewBlock {
parent_hash: Some(created_block.hash),
sender: Some(tx2),
create_empty: false,
finalize: false,
}).await.is_ok());
assert!(sink
.send(EngineCommand::SealNewBlock {
parent_hash: Some(created_block.hash),
sender: Some(tx2),
create_empty: false,
finalize: false,
})
.await
.is_ok());
let imported = rx2.await.unwrap().unwrap();
// assert that fork block is in the db
assert!(client.header(&BlockId::Hash(imported.hash)).unwrap().is_some())
@@ -18,18 +18,16 @@
//! RPC interface for the `ManualSeal` Engine.
use sp_consensus::ImportedAux;
use jsonrpc_core::Error;
use jsonrpc_derive::rpc;
pub use self::gen_client::Client as ManualSealClient;
use futures::{
channel::{mpsc, oneshot},
TryFutureExt,
FutureExt,
SinkExt
FutureExt, SinkExt, TryFutureExt,
};
use jsonrpc_core::Error;
use jsonrpc_derive::rpc;
use serde::{Deserialize, Serialize};
use sp_consensus::ImportedAux;
use sp_runtime::EncodedJustification;
pub use self::gen_client::Client as ManualSealClient;
/// Future's type for jsonrpc
type FutureResult<T> = Box<dyn jsonrpc_core::futures::Future<Item = T, Error = Error> + Send>;
@@ -63,7 +61,7 @@ pub enum EngineCommand<Hash> {
sender: Sender<()>,
/// finalization justification
justification: Option<EncodedJustification>,
}
},
}
/// RPC trait that provides methods for interacting with the manual-seal authorship task over rpc.
@@ -75,7 +73,7 @@ pub trait ManualSealApi<Hash> {
&self,
create_empty: bool,
finalize: bool,
parent_hash: Option<Hash>
parent_hash: Option<Hash>,
) -> FutureResult<CreatedBlock<Hash>>;
/// Instructs the manual-seal authorship task to finalize a block
@@ -83,7 +81,7 @@ pub trait ManualSealApi<Hash> {
fn finalize_block(
&self,
hash: Hash,
justification: Option<EncodedJustification>
justification: Option<EncodedJustification>,
) -> FutureResult<bool>;
}
@@ -98,7 +96,7 @@ pub struct CreatedBlock<Hash> {
/// hash of the created block.
pub hash: Hash,
/// some extra details about the import operation
pub aux: ImportedAux
pub aux: ImportedAux,
}
impl<Hash> ManualSeal<Hash> {
@@ -113,7 +111,7 @@ impl<Hash: Send + 'static> ManualSealApi<Hash> for ManualSeal<Hash> {
&self,
create_empty: bool,
finalize: bool,
parent_hash: Option<Hash>
parent_hash: Option<Hash>,
) -> FutureResult<CreatedBlock<Hash>> {
let mut sink = self.import_block_channel.clone();
let future = async move {
@@ -126,18 +124,22 @@ impl<Hash: Send + 'static> ManualSealApi<Hash> for ManualSeal<Hash> {
};
sink.send(command).await?;
receiver.await?
}.boxed();
}
.boxed();
Box::new(future.map_err(Error::from).compat())
}
fn finalize_block(&self, hash: Hash, justification: Option<EncodedJustification>) -> FutureResult<bool> {
fn finalize_block(
&self,
hash: Hash,
justification: Option<EncodedJustification>,
) -> FutureResult<bool> {
let mut sink = self.import_block_channel.clone();
let future = async move {
let (sender, receiver) = oneshot::channel();
sink.send(
EngineCommand::FinalizeBlock { hash, sender: Some(sender), justification }
).await?;
sink.send(EngineCommand::FinalizeBlock { hash, sender: Some(sender), justification })
.await?;
receiver.await?.map(|_| true)
};
@@ -150,7 +152,7 @@ impl<Hash: Send + 'static> ManualSealApi<Hash> for ManualSeal<Hash> {
/// to the rpc
pub fn send_result<T: std::fmt::Debug>(
sender: &mut Sender<T>,
result: std::result::Result<T, crate::Error>
result: std::result::Result<T, crate::Error>,
) {
if let Some(sender) = sender.take() {
if let Err(err) = sender.send(result) {
@@ -160,7 +162,7 @@ pub fn send_result<T: std::fmt::Debug>(
// instant seal doesn't report errors over rpc, simply log them.
match result {
Ok(r) => log::info!("Instant Seal success: {:?}", r),
Err(e) => log::error!("Instant Seal encountered an error: {}", e)
Err(e) => log::error!("Instant Seal encountered an error: {}", e),
}
}
}
@@ -18,23 +18,21 @@
//! Block sealing utilities
use crate::{Error, rpc, CreatedBlock, ConsensusDataProvider};
use std::sync::Arc;
use sp_runtime::{
traits::{Block as BlockT, Header as HeaderT},
generic::BlockId,
};
use crate::{rpc, ConsensusDataProvider, CreatedBlock, Error};
use futures::prelude::*;
use sp_consensus::{
self, BlockImport, Environment, Proposer, ForkChoiceStrategy,
BlockImportParams, BlockOrigin, ImportResult, SelectChain, StateAction,
};
use sp_blockchain::HeaderBackend;
use std::collections::HashMap;
use std::time::Duration;
use sp_inherents::{CreateInherentDataProviders, InherentDataProvider};
use sp_api::{ProvideRuntimeApi, TransactionFor};
use sc_transaction_pool_api::TransactionPool;
use sp_api::{ProvideRuntimeApi, TransactionFor};
use sp_blockchain::HeaderBackend;
use sp_consensus::{
self, BlockImport, BlockImportParams, BlockOrigin, Environment, ForkChoiceStrategy,
ImportResult, Proposer, SelectChain, StateAction,
};
use sp_inherents::{CreateInherentDataProviders, InherentDataProvider};
use sp_runtime::{
generic::BlockId,
traits::{Block as BlockT, Header as HeaderT},
};
use std::{collections::HashMap, sync::Arc, time::Duration};
/// max duration for creating a proposal in secs
pub const MAX_PROPOSAL_DURATION: u64 = 10;
@@ -59,7 +57,8 @@ pub struct SealBlockParams<'a, B: BlockT, BI, SC, C: ProvideRuntimeApi<B>, E, TP
/// SelectChain object
pub select_chain: &'a SC,
/// Digest provider for inclusion in blocks.
pub consensus_data_provider: Option<&'a dyn ConsensusDataProvider<B, Transaction = TransactionFor<C, B>>>,
pub consensus_data_provider:
Option<&'a dyn ConsensusDataProvider<B, Transaction = TransactionFor<C, B>>>,
/// block import object
pub block_import: &'a mut BI,
/// Something that can create the inherent data providers.
@@ -97,7 +96,7 @@ pub async fn seal_block<B, BI, SC, C, E, TP, CIDP>(
{
let future = async {
if pool.status().ready == 0 && !create_empty {
return Err(Error::EmptyTransactionPool);
return Err(Error::EmptyTransactionPool)
}
// get the header to build this new block on.
@@ -129,12 +128,15 @@ pub async fn seal_block<B, BI, SC, C, E, TP, CIDP>(
Default::default()
};
let proposal = proposer.propose(
inherent_data.clone(),
digest,
Duration::from_secs(MAX_PROPOSAL_DURATION),
None,
).map_err(|err| Error::StringError(format!("{:?}", err))).await?;
let proposal = proposer
.propose(
inherent_data.clone(),
digest,
Duration::from_secs(MAX_PROPOSAL_DURATION),
None,
)
.map_err(|err| Error::StringError(format!("{:?}", err)))
.await?;
if proposal.block.extrinsics().len() == inherents_len && !create_empty {
return Err(Error::EmptyTransactionPool)
@@ -145,18 +147,17 @@ pub async fn seal_block<B, BI, SC, C, E, TP, CIDP>(
params.body = Some(body);
params.finalized = finalize;
params.fork_choice = Some(ForkChoiceStrategy::LongestChain);
params.state_action = StateAction::ApplyChanges(
sp_consensus::StorageChanges::Changes(proposal.storage_changes)
);
params.state_action = StateAction::ApplyChanges(sp_consensus::StorageChanges::Changes(
proposal.storage_changes,
));
if let Some(digest_provider) = digest_provider {
digest_provider.append_block_import(&parent, &mut params, &inherent_data)?;
}
match block_import.import_block(params, HashMap::new()).await? {
ImportResult::Imported(aux) => {
Ok(CreatedBlock { hash: <B as BlockT>::Header::hash(&header), aux })
},
ImportResult::Imported(aux) =>
Ok(CreatedBlock { hash: <B as BlockT>::Header::hash(&header), aux }),
other => Err(other.into()),
}
};
+88 -107
View File
@@ -41,34 +41,33 @@
mod worker;
pub use crate::worker::{MiningWorker, MiningMetadata, MiningBuild};
pub use crate::worker::{MiningBuild, MiningMetadata, MiningWorker};
use std::{
sync::Arc, borrow::Cow, collections::HashMap, marker::PhantomData,
cmp::Ordering, time::Duration,
};
use codec::{Decode, Encode};
use futures::{Future, StreamExt};
use log::*;
use parking_lot::Mutex;
use sc_client_api::{BlockOf, backend::AuxStore, BlockchainEvents};
use sp_blockchain::{HeaderBackend, ProvideCache, well_known_cache_keys::Id as CacheKeyId};
use sp_block_builder::BlockBuilder as BlockBuilderApi;
use sp_runtime::{Justifications, RuntimeString};
use sp_runtime::generic::{BlockId, Digest, DigestItem};
use sp_runtime::traits::{Block as BlockT, Header as HeaderT};
use prometheus_endpoint::Registry;
use sc_client_api::{self, backend::AuxStore, BlockOf, BlockchainEvents};
use sp_api::ProvideRuntimeApi;
use sp_block_builder::BlockBuilder as BlockBuilderApi;
use sp_blockchain::{well_known_cache_keys::Id as CacheKeyId, HeaderBackend, ProvideCache};
use sp_consensus::{
import_queue::{BasicQueue, BoxBlockImport, BoxJustificationImport, Verifier},
BlockCheckParams, BlockImport, BlockImportParams, BlockOrigin, CanAuthorWith, Environment,
Error as ConsensusError, ForkChoiceStrategy, ImportResult, Proposer, SelectChain, SyncOracle,
};
use sp_consensus_pow::{Seal, TotalDifficulty, POW_ENGINE_ID};
use sp_inherents::{CreateInherentDataProviders, InherentDataProvider};
use sp_consensus::{
BlockImportParams, BlockOrigin, ForkChoiceStrategy, SyncOracle, Environment, Proposer,
SelectChain, Error as ConsensusError, CanAuthorWith, BlockImport, BlockCheckParams, ImportResult,
use sp_runtime::{
generic::{BlockId, Digest, DigestItem},
traits::{Block as BlockT, Header as HeaderT},
Justifications, RuntimeString,
};
use sp_consensus::import_queue::{
BoxBlockImport, BasicQueue, Verifier, BoxJustificationImport,
use std::{
borrow::Cow, cmp::Ordering, collections::HashMap, marker::PhantomData, sync::Arc,
time::Duration,
};
use codec::{Encode, Decode};
use prometheus_endpoint::Registry;
use sc_client_api;
use log::*;
use crate::worker::UntilImportedOrTimeout;
@@ -102,7 +101,7 @@ pub enum Error<B: BlockT> {
CheckInherents(sp_inherents::Error),
#[display(
fmt = "Checking inherents unknown error for identifier: {:?}",
"String::from_utf8_lossy(_0)",
"String::from_utf8_lossy(_0)"
)]
CheckInherentsUnknownError(sp_inherents::InherentIdentifier),
#[display(fmt = "Multiple pre-runtime digests")]
@@ -153,7 +152,8 @@ pub struct PowAux<Difficulty> {
pub total_difficulty: Difficulty,
}
impl<Difficulty> PowAux<Difficulty> where
impl<Difficulty> PowAux<Difficulty>
where
Difficulty: Decode + Default,
{
/// Read the auxiliary from client.
@@ -193,11 +193,7 @@ pub trait PowAlgorithm<B: BlockT> {
/// breaking algorithms will help to protect against selfish mining.
///
/// Returns if the new seal should be considered best block.
fn break_tie(
&self,
_own_seal: &Seal,
_new_seal: &Seal,
) -> bool {
fn break_tie(&self, _own_seal: &Seal, _new_seal: &Seal) -> bool {
false
}
/// Verify that the difficulty is valid against given seal.
@@ -238,7 +234,8 @@ impl<B: BlockT, I: Clone, C, S: Clone, Algorithm: Clone, CAW: Clone, CIDP> Clone
}
}
impl<B, I, C, S, Algorithm, CAW, CIDP> PowBlockImport<B, I, C, S, Algorithm, CAW, CIDP> where
impl<B, I, C, S, Algorithm, CAW, CIDP> PowBlockImport<B, I, C, S, Algorithm, CAW, CIDP>
where
B: BlockT,
I: BlockImport<B, Transaction = sp_api::TransactionFor<C, B>> + Send + Sync,
I::Error: Into<ConsensusError>,
@@ -289,14 +286,15 @@ impl<B, I, C, S, Algorithm, CAW, CIDP> PowBlockImport<B, I, C, S, Algorithm, CAW
return Ok(())
}
let inherent_data = inherent_data_providers.create_inherent_data()
let inherent_data = inherent_data_providers
.create_inherent_data()
.map_err(|e| Error::CreateInherents(e))?;
let inherent_res = self.client.runtime_api().check_inherents(
&block_id,
block,
inherent_data,
).map_err(|e| Error::Client(e.into()))?;
let inherent_res = self
.client
.runtime_api()
.check_inherents(&block_id, block, inherent_data)
.map_err(|e| Error::Client(e.into()))?;
if !inherent_res.ok() {
for (identifier, error) in inherent_res.into_errors() {
@@ -358,20 +356,19 @@ where
self.check_inherents(
check_block.clone(),
BlockId::Hash(parent_hash),
self.create_inherent_data_providers.create_inherent_data_providers(
parent_hash,
(),
).await?,
).await?;
self.create_inherent_data_providers
.create_inherent_data_providers(parent_hash, ())
.await?,
)
.await?;
block.body = Some(check_block.deconstruct().1);
}
let inner_seal = fetch_seal::<B>(block.post_digests.last(), block.header.hash())?;
let intermediate = block.take_intermediate::<PowIntermediate::<Algorithm::Difficulty>>(
INTERMEDIATE_KEY
)?;
let intermediate =
block.take_intermediate::<PowIntermediate<Algorithm::Difficulty>>(INTERMEDIATE_KEY)?;
let difficulty = match intermediate.difficulty {
Some(difficulty) => difficulty,
@@ -401,14 +398,12 @@ where
Ordering::Less => false,
Ordering::Greater => true,
Ordering::Equal => {
let best_inner_seal = fetch_seal::<B>(
best_header.digest().logs.last(),
best_hash,
)?;
let best_inner_seal =
fetch_seal::<B>(best_header.digest().logs.last(), best_hash)?;
self.algorithm.break_tie(&best_inner_seal, &inner_seal)
},
}
},
));
}
@@ -423,35 +418,33 @@ pub struct PowVerifier<B: BlockT, Algorithm> {
}
impl<B: BlockT, Algorithm> PowVerifier<B, Algorithm> {
pub fn new(
algorithm: Algorithm,
) -> Self {
pub fn new(algorithm: Algorithm) -> Self {
Self { algorithm, _marker: PhantomData }
}
fn check_header(
&self,
mut header: B::Header,
) -> Result<(B::Header, DigestItem<B::Hash>), Error<B>> where
) -> Result<(B::Header, DigestItem<B::Hash>), Error<B>>
where
Algorithm: PowAlgorithm<B>,
{
let hash = header.hash();
let (seal, inner_seal) = match header.digest_mut().pop() {
Some(DigestItem::Seal(id, seal)) => {
Some(DigestItem::Seal(id, seal)) =>
if id == POW_ENGINE_ID {
(DigestItem::Seal(id, seal.clone()), seal)
} else {
return Err(Error::WrongEngine(id))
}
},
},
_ => return Err(Error::HeaderUnsealed(hash)),
};
let pre_hash = header.hash();
if !self.algorithm.preliminary_verify(&pre_hash, &inner_seal)?.unwrap_or(true) {
return Err(Error::FailedPreliminaryVerify);
return Err(Error::FailedPreliminaryVerify)
}
Ok((header, seal))
@@ -459,7 +452,8 @@ impl<B: BlockT, Algorithm> PowVerifier<B, Algorithm> {
}
#[async_trait::async_trait]
impl<B: BlockT, Algorithm> Verifier<B> for PowVerifier<B, Algorithm> where
impl<B: BlockT, Algorithm> Verifier<B> for PowVerifier<B, Algorithm>
where
Algorithm: PowAlgorithm<B> + Send + Sync,
Algorithm::Difficulty: 'static + Send,
{
@@ -473,18 +467,15 @@ impl<B: BlockT, Algorithm> Verifier<B> for PowVerifier<B, Algorithm> where
let hash = header.hash();
let (checked_header, seal) = self.check_header(header)?;
let intermediate = PowIntermediate::<Algorithm::Difficulty> {
difficulty: None,
};
let intermediate = PowIntermediate::<Algorithm::Difficulty> { difficulty: None };
let mut import_block = BlockImportParams::new(origin, checked_header);
import_block.post_digests.push(seal);
import_block.body = body;
import_block.justifications = justifications;
import_block.intermediates.insert(
Cow::from(INTERMEDIATE_KEY),
Box::new(intermediate) as Box<_>
);
import_block
.intermediates
.insert(Cow::from(INTERMEDIATE_KEY), Box::new(intermediate) as Box<_>);
import_block.post_hash = Some(hash);
Ok((import_block, None))
@@ -501,10 +492,8 @@ pub fn import_queue<B, Transaction, Algorithm>(
algorithm: Algorithm,
spawner: &impl sp_core::traits::SpawnEssentialNamed,
registry: Option<&Registry>,
) -> Result<
PowImportQueue<B, Transaction>,
sp_consensus::Error
> where
) -> Result<PowImportQueue<B, Transaction>, sp_consensus::Error>
where
B: BlockT,
Transaction: Send + Sync + 'static,
Algorithm: PowAlgorithm<B> + Clone + Send + Sync + 'static,
@@ -512,13 +501,7 @@ pub fn import_queue<B, Transaction, Algorithm>(
{
let verifier = PowVerifier::new(algorithm);
Ok(BasicQueue::new(
verifier,
block_import,
justification_import,
spawner,
registry,
))
Ok(BasicQueue::new(verifier, block_import, justification_import, spawner, registry))
}
/// Start the mining worker for PoW. This function provides the necessary helper functions that can
@@ -573,13 +556,13 @@ where
let task = async move {
loop {
if timer.next().await.is_none() {
break;
break
}
if sync_oracle.is_major_syncing() {
debug!(target: "pow", "Skipping proposal due to sync.");
worker.lock().on_major_syncing();
return;
return
}
let best_header = match select_chain.best_chain().await {
@@ -591,8 +574,8 @@ where
Select best chain error: {:?}",
err
);
return;
}
return
},
};
let best_hash = best_header.hash();
@@ -603,11 +586,11 @@ where
Probably a node update is required!",
err,
);
return;
return
}
if worker.lock().best_hash() == Some(best_hash) {
return;
return
}
// The worker is locked for the duration of the whole proposing period. Within this period,
@@ -622,23 +605,25 @@ where
Fetch difficulty failed: {:?}",
err,
);
return;
return
},
};
let inherent_data_providers =
match create_inherent_data_providers.create_inherent_data_providers(best_hash, ()).await {
Ok(x) => x,
Err(err) => {
warn!(
target: "pow",
"Unable to propose new block for authoring. \
Creating inherent data providers failed: {:?}",
err,
);
return;
},
};
let inherent_data_providers = match create_inherent_data_providers
.create_inherent_data_providers(best_hash, ())
.await
{
Ok(x) => x,
Err(err) => {
warn!(
target: "pow",
"Unable to propose new block for authoring. \
Creating inherent data providers failed: {:?}",
err,
);
return
},
};
let inherent_data = match inherent_data_providers.create_inherent_data() {
Ok(r) => r,
@@ -649,7 +634,7 @@ where
Creating inherent data failed: {:?}",
e,
);
return;
return
},
};
@@ -673,12 +658,10 @@ where
},
};
let proposal = match proposer.propose(
inherent_data,
inherent_digest,
build_time.clone(),
None,
).await {
let proposal = match proposer
.propose(inherent_data, inherent_digest, build_time.clone(), None)
.await
{
Ok(x) => x,
Err(err) => {
warn!(
@@ -714,9 +697,8 @@ fn find_pre_digest<B: BlockT>(header: &B::Header) -> Result<Option<Vec<u8>>, Err
for log in header.digest().logs() {
trace!(target: "pow", "Checking log {:?}, looking for pre runtime digest", log);
match (log, pre_digest.is_some()) {
(DigestItem::PreRuntime(POW_ENGINE_ID, _), true) => {
return Err(Error::MultiplePreRuntimeDigests)
},
(DigestItem::PreRuntime(POW_ENGINE_ID, _), true) =>
return Err(Error::MultiplePreRuntimeDigests),
(DigestItem::PreRuntime(POW_ENGINE_ID, v), false) => {
pre_digest = Some(v.clone());
},
@@ -733,13 +715,12 @@ fn fetch_seal<B: BlockT>(
hash: B::Hash,
) -> Result<Vec<u8>, Error<B>> {
match digest {
Some(DigestItem::Seal(id, seal)) => {
Some(DigestItem::Seal(id, seal)) =>
if id == &POW_ENGINE_ID {
Ok(seal.clone())
} else {
return Err(Error::<B>::WrongEngine(*id).into())
}
},
},
_ => return Err(Error::<B>::HeaderUnsealed(hash).into()),
}
}
+26 -29
View File
@@ -16,20 +16,25 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
use std::{pin::Pin, time::Duration, collections::HashMap, borrow::Cow};
use futures::{
prelude::*,
task::{Context, Poll},
};
use futures_timer::Delay;
use log::*;
use sc_client_api::ImportNotifications;
use sp_consensus::{Proposal, BlockOrigin, BlockImportParams, StorageChanges,
StateAction, import_queue::BoxBlockImport};
use sp_consensus::{
import_queue::BoxBlockImport, BlockImportParams, BlockOrigin, Proposal, StateAction,
StorageChanges,
};
use sp_runtime::{
generic::BlockId,
traits::{Block as BlockT, Header as HeaderT},
DigestItem,
};
use futures::{prelude::*, task::{Context, Poll}};
use futures_timer::Delay;
use log::*;
use std::{borrow::Cow, collections::HashMap, pin::Pin, time::Duration};
use crate::{INTERMEDIATE_KEY, POW_ENGINE_ID, Seal, PowAlgorithm, PowIntermediate};
use crate::{PowAlgorithm, PowIntermediate, Seal, INTERMEDIATE_KEY, POW_ENGINE_ID};
/// Mining metadata. This is the information needed to start an actual mining loop.
#[derive(Clone, Eq, PartialEq)]
@@ -49,7 +54,7 @@ pub struct MiningBuild<
Block: BlockT,
Algorithm: PowAlgorithm<Block>,
C: sp_api::ProvideRuntimeApi<Block>,
Proof
Proof,
> {
/// Mining metadata.
pub metadata: MiningMetadata<Block::Hash, Algorithm::Difficulty>,
@@ -90,10 +95,7 @@ where
self.build = None;
}
pub(crate) fn on_build(
&mut self,
build: MiningBuild<Block, Algorithm, C, Proof>,
) {
pub(crate) fn on_build(&mut self, build: MiningBuild<Block, Algorithm, C, Proof>) {
self.build = Some(build);
}
@@ -137,23 +139,25 @@ where
let mut import_block = BlockImportParams::new(BlockOrigin::Own, header);
import_block.post_digests.push(seal);
import_block.body = Some(body);
import_block.state_action = StateAction::ApplyChanges(
StorageChanges::Changes(build.proposal.storage_changes)
);
import_block.state_action =
StateAction::ApplyChanges(StorageChanges::Changes(build.proposal.storage_changes));
let intermediate = PowIntermediate::<Algorithm::Difficulty> {
difficulty: Some(build.metadata.difficulty),
};
import_block.intermediates.insert(
Cow::from(INTERMEDIATE_KEY),
Box::new(intermediate) as Box<_>,
);
import_block
.intermediates
.insert(Cow::from(INTERMEDIATE_KEY), Box::new(intermediate) as Box<_>);
let header = import_block.post_header();
match self.block_import.import_block(import_block, HashMap::default()).await {
Ok(res) => {
res.handle_justification(&header.hash(), *header.number(), &mut self.justification_sync_link);
res.handle_justification(
&header.hash(),
*header.number(),
&mut self.justification_sync_link,
);
info!(
target: "pow",
@@ -190,15 +194,8 @@ pub struct UntilImportedOrTimeout<Block: BlockT> {
impl<Block: BlockT> UntilImportedOrTimeout<Block> {
/// Create a new stream using the given import notification and timeout duration.
pub fn new(
import_notifications: ImportNotifications<Block>,
timeout: Duration,
) -> Self {
Self {
import_notifications,
timeout,
inner_delay: None,
}
pub fn new(import_notifications: ImportNotifications<Block>, timeout: Duration) -> Self {
Self { import_notifications, timeout, inner_delay: None }
}
}
@@ -18,9 +18,9 @@
//! Schema for slots in the aux-db.
use codec::{Encode, Decode};
use codec::{Decode, Encode};
use sc_client_api::backend::AuxStore;
use sp_blockchain::{Result as ClientResult, Error as ClientError};
use sp_blockchain::{Error as ClientError, Result as ClientResult};
use sp_consensus_slots::{EquivocationProof, Slot};
use sp_runtime::traits::Header;
@@ -33,17 +33,17 @@ pub const MAX_SLOT_CAPACITY: u64 = 1000;
pub const PRUNING_BOUND: u64 = 2 * MAX_SLOT_CAPACITY;
fn load_decode<C, T>(backend: &C, key: &[u8]) -> ClientResult<Option<T>>
where
C: AuxStore,
T: Decode,
where
C: AuxStore,
T: Decode,
{
match backend.get_aux(key)? {
None => Ok(None),
Some(t) => T::decode(&mut &t[..])
.map_err(
|e| ClientError::Backend(format!("Slots DB is corrupted. Decode error: {}", e)),
)
.map(Some)
.map_err(|e| {
ClientError::Backend(format!("Slots DB is corrupted. Decode error: {}", e))
})
.map(Some),
}
}
@@ -57,14 +57,14 @@ pub fn check_equivocation<C, H, P>(
header: &H,
signer: &P,
) -> ClientResult<Option<EquivocationProof<H, P>>>
where
H: Header,
C: AuxStore,
P: Clone + Encode + Decode + PartialEq,
where
H: Header,
C: AuxStore,
P: Clone + Encode + Decode + PartialEq,
{
// We don't check equivocations for old headers out of our capacity.
if slot_now.saturating_sub(*slot) > Slot::from(MAX_SLOT_CAPACITY) {
return Ok(None);
return Ok(None)
}
// Key for this slot.
@@ -72,17 +72,16 @@ pub fn check_equivocation<C, H, P>(
slot.using_encoded(|s| curr_slot_key.extend(s));
// Get headers of this slot.
let mut headers_with_sig = load_decode::<_, Vec<(H, P)>>(backend, &curr_slot_key[..])?
.unwrap_or_else(Vec::new);
let mut headers_with_sig =
load_decode::<_, Vec<(H, P)>>(backend, &curr_slot_key[..])?.unwrap_or_else(Vec::new);
// Get first slot saved.
let slot_header_start = SLOT_HEADER_START.to_vec();
let first_saved_slot = load_decode::<_, Slot>(backend, &slot_header_start[..])?
.unwrap_or(slot);
let first_saved_slot = load_decode::<_, Slot>(backend, &slot_header_start[..])?.unwrap_or(slot);
if slot_now < first_saved_slot {
// The code below assumes that slots will be visited sequentially.
return Ok(None);
return Ok(None)
}
for (prev_header, prev_signer) in headers_with_sig.iter() {
@@ -96,7 +95,7 @@ pub fn check_equivocation<C, H, P>(
offender: signer.clone(),
first_header: prev_header.clone(),
second_header: header.clone(),
}));
}))
} else {
// We don't need to continue in case of duplicated header,
// since it's already saved and a possible equivocation
@@ -135,12 +134,11 @@ pub fn check_equivocation<C, H, P>(
#[cfg(test)]
mod test {
use sp_core::{sr25519, Pair};
use sp_core::hash::H256;
use sp_runtime::testing::{Header as HeaderTest, Digest as DigestTest};
use sp_core::{hash::H256, sr25519, Pair};
use sp_runtime::testing::{Digest as DigestTest, Header as HeaderTest};
use substrate_test_runtime_client;
use super::{MAX_SLOT_CAPACITY, PRUNING_BOUND, check_equivocation};
use super::{check_equivocation, MAX_SLOT_CAPACITY, PRUNING_BOUND};
fn create_header(number: u64) -> HeaderTest {
// so that different headers for the same number get different hashes
@@ -151,7 +149,7 @@ mod test {
number,
state_root: Default::default(),
extrinsics_root: Default::default(),
digest: DigestTest { logs: vec![], },
digest: DigestTest { logs: vec![] },
};
header
@@ -171,79 +169,55 @@ mod test {
let header6 = create_header(3); // @ slot 4
// It's ok to sign same headers.
assert!(
check_equivocation(
&client,
2.into(),
2.into(),
&header1,
&public,
).unwrap().is_none(),
);
assert!(check_equivocation(&client, 2.into(), 2.into(), &header1, &public,)
.unwrap()
.is_none(),);
assert!(
check_equivocation(
&client,
3.into(),
2.into(),
&header1,
&public,
).unwrap().is_none(),
);
assert!(check_equivocation(&client, 3.into(), 2.into(), &header1, &public,)
.unwrap()
.is_none(),);
// But not two different headers at the same slot.
assert!(
check_equivocation(
&client,
4.into(),
2.into(),
&header2,
&public,
).unwrap().is_some(),
);
assert!(check_equivocation(&client, 4.into(), 2.into(), &header2, &public,)
.unwrap()
.is_some(),);
// Different slot is ok.
assert!(
check_equivocation(
&client,
5.into(),
4.into(),
&header3,
&public,
).unwrap().is_none(),
);
assert!(check_equivocation(&client, 5.into(), 4.into(), &header3, &public,)
.unwrap()
.is_none(),);
// Here we trigger pruning and save header 4.
assert!(
check_equivocation(
&client,
(PRUNING_BOUND + 2).into(),
(MAX_SLOT_CAPACITY + 4).into(),
&header4,
&public,
).unwrap().is_none(),
);
assert!(check_equivocation(
&client,
(PRUNING_BOUND + 2).into(),
(MAX_SLOT_CAPACITY + 4).into(),
&header4,
&public,
)
.unwrap()
.is_none(),);
// This fails because header 5 is an equivocation of header 4.
assert!(
check_equivocation(
&client,
(PRUNING_BOUND + 3).into(),
(MAX_SLOT_CAPACITY + 4).into(),
&header5,
&public,
).unwrap().is_some(),
);
assert!(check_equivocation(
&client,
(PRUNING_BOUND + 3).into(),
(MAX_SLOT_CAPACITY + 4).into(),
&header5,
&public,
)
.unwrap()
.is_some(),);
// This is ok because we pruned the corresponding header. Shows that we are pruning.
assert!(
check_equivocation(
&client,
(PRUNING_BOUND + 4).into(),
4.into(),
&header6,
&public,
).unwrap().is_none(),
);
assert!(check_equivocation(
&client,
(PRUNING_BOUND + 4).into(),
4.into(),
&header6,
&public,
)
.unwrap()
.is_none(),);
}
}
+144 -175
View File
@@ -25,19 +25,19 @@
#![forbid(unsafe_code)]
#![warn(missing_docs)]
mod slots;
mod aux_schema;
mod slots;
pub use aux_schema::{check_equivocation, MAX_SLOT_CAPACITY, PRUNING_BOUND};
pub use slots::SlotInfo;
use slots::Slots;
pub use aux_schema::{check_equivocation, MAX_SLOT_CAPACITY, PRUNING_BOUND};
use std::{fmt::Debug, ops::Deref, time::Duration};
use codec::{Decode, Encode};
use futures::{future::Either, Future, TryFutureExt};
use futures_timer::Delay;
use log::{debug, error, info, warn};
use sp_api::{ProvideRuntimeApi, ApiRef};
use sc_telemetry::{telemetry, TelemetryHandle, CONSENSUS_DEBUG, CONSENSUS_INFO, CONSENSUS_WARN};
use sp_api::{ApiRef, ProvideRuntimeApi};
use sp_arithmetic::traits::BaseArithmetic;
use sp_consensus::{
BlockImport, CanAuthorWith, JustificationSyncLink, Proposer, SelectChain, SlotData, SyncOracle,
@@ -46,10 +46,10 @@ use sp_consensus_slots::Slot;
use sp_inherents::CreateInherentDataProviders;
use sp_runtime::{
generic::BlockId,
traits::{Block as BlockT, Header as HeaderT, HashFor, NumberFor}
traits::{Block as BlockT, HashFor, Header as HeaderT, NumberFor},
};
use sc_telemetry::{telemetry, TelemetryHandle, CONSENSUS_DEBUG, CONSENSUS_WARN, CONSENSUS_INFO};
use sp_timestamp::Timestamp;
use std::{fmt::Debug, ops::Deref, time::Duration};
/// The changes that need to applied to the storage to create the state for a block.
///
@@ -76,10 +76,7 @@ pub trait SlotWorker<B: BlockT, Proof> {
///
/// Returns a future that resolves to a [`SlotResult`] iff a block was successfully built in
/// the slot. Otherwise `None` is returned.
async fn on_slot(
&mut self,
slot_info: SlotInfo<B>,
) -> Option<SlotResult<B, Proof>>;
async fn on_slot(&mut self, slot_info: SlotInfo<B>) -> Option<SlotResult<B, Proof>>;
}
/// A skeleton implementation for `SlotWorker` which tries to claim a slot at
@@ -89,7 +86,8 @@ pub trait SlotWorker<B: BlockT, Proof> {
pub trait SimpleSlotWorker<B: BlockT> {
/// A handle to a `BlockImport`.
type BlockImport: BlockImport<B, Transaction = <Self::Proposer as Proposer<B>>::Transaction>
+ Send + 'static;
+ Send
+ 'static;
/// A handle to a `SyncOracle`.
type SyncOracle: SyncOracle;
@@ -100,7 +98,9 @@ pub trait SimpleSlotWorker<B: BlockT> {
/// The type of future resolving to the proposer.
type CreateProposer: Future<Output = Result<Self::Proposer, sp_consensus::Error>>
+ Send + Unpin + 'static;
+ Send
+ Unpin
+ 'static;
/// The type of proposer to use to build blocks.
type Proposer: Proposer<B> + Send;
@@ -139,12 +139,7 @@ pub trait SimpleSlotWorker<B: BlockT> {
/// Notifies the given slot. Similar to `claim_slot`, but will be called no matter whether we
/// need to author blocks or not.
fn notify_slot(
&self,
_header: &B::Header,
_slot: Slot,
_epoch_data: &Self::EpochData,
) {}
fn notify_slot(&self, _header: &B::Header, _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(
@@ -154,18 +149,24 @@ pub trait SimpleSlotWorker<B: BlockT> {
) -> Vec<sp_runtime::DigestItem<B::Hash>>;
/// Returns a function which produces a `BlockImportParams`.
fn block_import_params(&self) -> Box<
fn block_import_params(
&self,
) -> Box<
dyn Fn(
B::Header,
&B::Hash,
Vec<B::Extrinsic>,
StorageChanges<<Self::BlockImport as BlockImport<B>>::Transaction, B>,
Self::Claim,
Self::EpochData,
) -> Result<
sp_consensus::BlockImportParams<B, <Self::BlockImport as BlockImport<B>>::Transaction>,
sp_consensus::Error
> + Send + 'static
B::Header,
&B::Hash,
Vec<B::Extrinsic>,
StorageChanges<<Self::BlockImport as BlockImport<B>>::Transaction, B>,
Self::Claim,
Self::EpochData,
) -> Result<
sp_consensus::BlockImportParams<
B,
<Self::BlockImport as BlockImport<B>>::Transaction,
>,
sp_consensus::Error,
> + Send
+ 'static,
>;
/// Whether to force authoring if offline.
@@ -194,10 +195,7 @@ pub trait SimpleSlotWorker<B: BlockT> {
fn telemetry(&self) -> Option<TelemetryHandle>;
/// Remaining duration for proposing.
fn proposing_remaining_duration(
&self,
slot_info: &SlotInfo<B>,
) -> Duration;
fn proposing_remaining_duration(&self, slot_info: &SlotInfo<B>) -> Duration;
/// Implements [`SlotWorker::on_slot`].
async fn on_slot(
@@ -213,8 +211,7 @@ pub trait SimpleSlotWorker<B: BlockT> {
let proposing_remaining = if proposing_remaining_duration == Duration::default() {
debug!(
target: logging_target,
"Skipping proposal slot {} since there's no time left to propose",
slot,
"Skipping proposal slot {} since there's no time left to propose", slot,
);
return None
@@ -240,8 +237,8 @@ pub trait SimpleSlotWorker<B: BlockT> {
"err" => ?err,
);
return None;
}
return None
},
};
self.notify_slot(&slot_info.chain_head, slot, &epoch_data);
@@ -260,13 +257,13 @@ pub trait SimpleSlotWorker<B: BlockT> {
"authorities_len" => authorities_len,
);
return None;
return None
}
let claim = self.claim_slot(&slot_info.chain_head, slot, &epoch_data)?;
if self.should_backoff(slot, &slot_info.chain_head) {
return None;
return None
}
debug!(
@@ -289,9 +286,7 @@ pub trait SimpleSlotWorker<B: BlockT> {
Err(err) => {
warn!(
target: logging_target,
"Unable to author block in slot {:?}: {:?}",
slot,
err,
"Unable to author block in slot {:?}: {:?}", slot, err,
);
telemetry!(
@@ -303,7 +298,7 @@ pub trait SimpleSlotWorker<B: BlockT> {
);
return None
}
},
};
let logs = self.pre_digest_data(slot, &claim);
@@ -311,34 +306,29 @@ pub trait SimpleSlotWorker<B: BlockT> {
// deadline our production to 98% of the total time left for proposing. As we deadline
// the proposing below to the same total time left, the 2% margin should be enough for
// the result to be returned.
let proposing = proposer.propose(
slot_info.inherent_data,
sp_runtime::generic::Digest {
logs,
},
proposing_remaining_duration.mul_f32(0.98),
None,
).map_err(|e| sp_consensus::Error::ClientImport(format!("{:?}", e)));
let proposing = proposer
.propose(
slot_info.inherent_data,
sp_runtime::generic::Digest { logs },
proposing_remaining_duration.mul_f32(0.98),
None,
)
.map_err(|e| sp_consensus::Error::ClientImport(format!("{:?}", e)));
let proposal = match futures::future::select(proposing, proposing_remaining).await {
Either::Left((Ok(p), _)) => p,
Either::Left((Err(err), _)) => {
warn!(
target: logging_target,
"Proposing failed: {:?}",
err,
);
warn!(target: logging_target, "Proposing failed: {:?}", err,);
return None
},
Either::Right(_) => {
info!(
target: logging_target,
"⌛️ Discarding proposal for slot {}; block production took too long",
slot,
"⌛️ 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")]
#[cfg(build_type = "debug")]
info!(
target: logging_target,
"👉 Recompile your node in `--release` mode to mitigate this problem.",
@@ -373,14 +363,10 @@ pub trait SimpleSlotWorker<B: BlockT> {
) {
Ok(bi) => bi,
Err(err) => {
warn!(
target: logging_target,
"Failed to create block import params: {:?}",
err,
);
warn!(target: logging_target, "Failed to create block import params: {:?}", err,);
return None
}
},
};
info!(
@@ -401,17 +387,14 @@ pub trait SimpleSlotWorker<B: BlockT> {
);
let header = block_import_params.post_header();
match block_import
.import_block(block_import_params, Default::default())
.await
{
match block_import.import_block(block_import_params, Default::default()).await {
Ok(res) => {
res.handle_justification(
&header.hash(),
*header.number(),
self.justification_sync_link(),
);
}
},
Err(err) => {
warn!(
target: logging_target,
@@ -425,18 +408,17 @@ pub trait SimpleSlotWorker<B: BlockT> {
"hash" => ?parent_hash,
"err" => ?err,
);
}
},
}
Some(SlotResult {
block: B::new(header, body),
storage_proof,
})
Some(SlotResult { block: B::new(header, body), storage_proof })
}
}
#[async_trait::async_trait]
impl<B: BlockT, T: SimpleSlotWorker<B> + Send> SlotWorker<B, <T::Proposer as Proposer<B>>::Proof> for T {
impl<B: BlockT, T: SimpleSlotWorker<B> + Send> SlotWorker<B, <T::Proposer as Proposer<B>>::Proof>
for T
{
async fn on_slot(
&mut self,
slot_info: SlotInfo<B>,
@@ -496,8 +478,7 @@ pub async fn start_slot_worker<B, C, W, T, SO, CIDP, CAW, Proof>(
mut sync_oracle: SO,
create_inherent_data_providers: CIDP,
can_author_with: CAW,
)
where
) where
B: BlockT,
C: SelectChain<B>,
W: SlotWorker<B, Proof>,
@@ -509,28 +490,25 @@ where
{
let SlotDuration(slot_duration) = slot_duration;
let mut slots = Slots::new(
slot_duration.slot_duration(),
create_inherent_data_providers,
client,
);
let mut slots =
Slots::new(slot_duration.slot_duration(), create_inherent_data_providers, client);
loop {
let slot_info = match slots.next_slot().await {
Ok(r) => r,
Err(e) => {
warn!(target: "slots", "Error while polling for next slot: {:?}", e);
return;
}
return
},
};
if sync_oracle.is_major_syncing() {
debug!(target: "slots", "Skipping proposal slot due to sync.");
continue;
continue
}
if let Err(err) = can_author_with
.can_author_with(&BlockId::Hash(slot_info.chain_head.hash()))
if let Err(err) =
can_author_with.can_author_with(&BlockId::Hash(slot_info.chain_head.hash()))
{
warn!(
target: "slots",
@@ -559,7 +537,10 @@ pub enum CheckedHeader<H, S> {
#[derive(Debug, thiserror::Error)]
#[allow(missing_docs)]
pub enum Error<T> where T: Debug {
pub enum Error<T>
where
T: Debug,
{
#[error("Slot duration is invalid: {0:?}")]
SlotDurationInvalid(SlotDuration<T>),
}
@@ -591,25 +572,23 @@ impl<T: Clone + Send + Sync + 'static> SlotDuration<T> {
///
/// `slot_key` is marked as `'static`, as it should really be a
/// compile-time constant.
pub fn get_or_compute<B: BlockT, C, CB>(client: &C, cb: CB) -> sp_blockchain::Result<Self> where
pub fn get_or_compute<B: BlockT, C, CB>(client: &C, cb: CB) -> sp_blockchain::Result<Self>
where
C: sc_client_api::backend::AuxStore + sc_client_api::UsageProvider<B>,
C: ProvideRuntimeApi<B>,
CB: FnOnce(ApiRef<C::Api>, &BlockId<B>) -> sp_blockchain::Result<T>,
T: SlotData + Encode + Decode + Debug,
{
let slot_duration = match client.get_aux(T::SLOT_KEY)? {
Some(v) => <T as codec::Decode>::decode(&mut &v[..])
.map(SlotDuration)
.map_err(|_| {
sp_blockchain::Error::Backend({
error!(target: "slots", "slot duration kept in invalid format");
"slot duration kept in invalid format".to_string()
})
}),
Some(v) => <T as codec::Decode>::decode(&mut &v[..]).map(SlotDuration).map_err(|_| {
sp_blockchain::Error::Backend({
error!(target: "slots", "slot duration kept in invalid format");
"slot duration kept in invalid format".to_string()
})
}),
None => {
let best_hash = client.usage_info().chain.best_hash;
let slot_duration =
cb(client.runtime_api(), &BlockId::hash(best_hash))?;
let slot_duration = cb(client.runtime_api(), &BlockId::hash(best_hash))?;
info!(
"⏱ Loaded block-time = {:?} from block {:?}",
@@ -621,11 +600,13 @@ impl<T: Clone + Send + Sync + 'static> SlotDuration<T> {
.using_encoded(|s| client.insert_aux(&[(T::SLOT_KEY, &s[..])], &[]))?;
Ok(SlotDuration(slot_duration))
}
},
}?;
if slot_duration.slot_duration() == Default::default() {
return Err(sp_blockchain::Error::Application(Box::new(Error::SlotDurationInvalid(slot_duration))))
return Err(sp_blockchain::Error::Application(Box::new(Error::SlotDurationInvalid(
slot_duration,
))))
}
Ok(slot_duration)
@@ -687,9 +668,7 @@ pub fn proposing_remaining_duration<Block: BlockT>(
) -> Duration {
use sp_runtime::traits::Zero;
let proposing_duration = slot_info
.duration
.mul_f32(block_proposal_slot_portion.get());
let proposing_duration = slot_info.duration.mul_f32(block_proposal_slot_portion.get());
let slot_remaining = slot_info
.ends_at
@@ -700,7 +679,7 @@ pub fn proposing_remaining_duration<Block: BlockT>(
// If parent is genesis block, we don't require any lenience factor.
if slot_info.chain_head.number().is_zero() {
return proposing_duration;
return proposing_duration
}
let parent_slot = match parent_slot {
@@ -723,9 +702,7 @@ pub fn proposing_remaining_duration<Block: BlockT>(
if let Some(ref max_block_proposal_slot_portion) = max_block_proposal_slot_portion {
std::cmp::min(
lenient_proposing_duration,
slot_info
.duration
.mul_f32(max_block_proposal_slot_portion.get()),
slot_info.duration.mul_f32(max_block_proposal_slot_portion.get()),
)
} else {
lenient_proposing_duration
@@ -853,7 +830,7 @@ impl<N: BaseArithmetic> Default for BackoffAuthoringOnFinalizedHeadLagging<N> {
impl<N> BackoffAuthoringBlocksStrategy<N> for BackoffAuthoringOnFinalizedHeadLagging<N>
where
N: BaseArithmetic + Copy
N: BaseArithmetic + Copy,
{
fn should_backoff(
&self,
@@ -865,12 +842,12 @@ where
) -> bool {
// This should not happen, but we want to keep the previous behaviour if it does.
if slot_now <= chain_head_slot {
return false;
return false
}
let unfinalized_block_length = chain_head_number - finalized_number;
let interval = unfinalized_block_length.saturating_sub(self.unfinalized_slack)
/ self.authoring_bias;
let interval =
unfinalized_block_length.saturating_sub(self.unfinalized_slack) / self.authoring_bias;
let interval = interval.min(self.max_interval);
// We're doing arithmetic between block and slot numbers.
@@ -906,9 +883,9 @@ impl<N> BackoffAuthoringBlocksStrategy<N> for () {
#[cfg(test)]
mod test {
use super::*;
use sp_api::NumberFor;
use std::time::{Duration, Instant};
use substrate_test_runtime_client::runtime::{Block, Header};
use sp_api::NumberFor;
const SLOT_DURATION: Duration = Duration::from_millis(6000);
@@ -945,10 +922,7 @@ mod test {
}
// but we cap it to a maximum of 20 slots
assert_eq!(
super::slot_lenience_linear(1u64.into(), &slot(23)),
Some(SLOT_DURATION * 20),
);
assert_eq!(super::slot_lenience_linear(1u64.into(), &slot(23)), Some(SLOT_DURATION * 20),);
}
#[test]
@@ -1041,7 +1015,15 @@ mod test {
let slot_now = 2;
let should_backoff: Vec<bool> = (slot_now..1000)
.map(|s| strategy.should_backoff(head_number, head_slot.into(), finalized_number, s.into(), "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
@@ -1105,7 +1087,15 @@ 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.into(), finalized_number, s.into(), "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
@@ -1123,11 +1113,7 @@ mod test {
};
let finalized_number = 2;
let mut head_state = HeadState {
head_number: 4,
head_slot: 10,
slot_now: 11,
};
let mut head_state = HeadState { head_number: 4, head_slot: 10, slot_now: 11 };
let should_backoff = |head_state: &HeadState| -> bool {
<dyn BackoffAuthoringBlocksStrategy<NumberFor<Block>>>::should_backoff(
@@ -1155,32 +1141,27 @@ mod test {
// Gradually start to backoff more and more frequently
let expected = [
false, false, false, false, false, // no effect
true, false,
true, false, // 1:1
true, true, false,
true, true, false, // 2:1
true, true, true, false,
true, true, true, false, // 3:1
true, true, true, true, false,
true, true, true, true, false, // 4:1
true, true, true, true, true, false,
true, true, true, true, true, false, // 5:1
true, true, true, true, true, true, false,
true, true, true, true, true, true, false, // 6:1
true, true, true, true, true, true, true, false,
true, true, true, true, true, true, true, false, // 7:1
true, true, true, true, true, true, true, true, false,
true, true, true, true, true, true, true, true, false, // 8:1
true, true, true, true, true, true, true, true, true, false,
true, true, true, true, true, true, true, true, true, false, // 9:1
true, true, true, true, true, true, true, true, true, true, false,
true, true, true, true, true, true, true, true, true, true, false, // 10:1
true, true, true, true, true, true, true, true, true, true, true, false,
true, true, true, true, true, true, true, true, true, true, true, false, // 11:1
true, true, true, true, true, true, true, true, true, true, true, true, false,
true, true, true, true, true, true, true, true, true, true, true, true, false, // 12:1
true, false, true, false, // 1:1
true, true, false, true, true, false, // 2:1
true, true, true, false, true, true, true, false, // 3:1
true, true, true, true, false, true, true, true, true, false, // 4:1
true, true, true, true, true, false, true, true, true, true, true, false, // 5:1
true, true, true, true, true, true, false, true, true, true, true, true, true,
false, // 6:1
true, true, true, true, true, true, true, false, true, true, true, true, true, true,
true, false, // 7:1
true, true, true, true, true, true, true, true, false, true, true, true, true, true,
true, true, true, false, // 8:1
true, true, true, true, true, true, true, true, true, false, true, true, true, true,
true, true, true, true, true, false, // 9:1
true, true, true, true, true, true, true, true, true, true, false, true, true, true,
true, true, true, true, true, true, true, false, // 10:1
true, true, true, true, true, true, true, true, true, true, true, false, true, true,
true, true, true, true, true, true, true, true, true, false, // 11:1
true, true, true, true, true, true, true, true, true, true, true, true, false, true,
true, true, true, true, true, true, true, true, true, true, true, false, // 12:1
true, true, true, true,
];
];
assert_eq!(backoff.as_slice(), &expected[..]);
}
@@ -1195,11 +1176,7 @@ mod test {
let finalized_number = 2;
let starting_slot = 11;
let mut head_state = HeadState {
head_number: 4,
head_slot: 10,
slot_now: starting_slot,
};
let mut head_state = HeadState { head_number: 4, head_slot: 10, slot_now: starting_slot };
let should_backoff = |head_state: &HeadState| -> bool {
<dyn BackoffAuthoringBlocksStrategy<NumberFor<Block>>>::should_backoff(
@@ -1240,30 +1217,22 @@ mod test {
assert_eq!(last_slot - last_two_claimed.next().unwrap(), 92);
assert_eq!(last_slot - last_two_claimed.next().unwrap(), 92 + expected_distance);
let intervals: Vec<_> = slots_claimed
.windows(2)
.map(|x| x[1] - x[0])
.collect();
let intervals: Vec<_> = slots_claimed.windows(2).map(|x| x[1] - x[0]).collect();
// The key thing is that the distance between claimed slots is capped to `max_interval + 1`
// assert_eq!(max_observed_interval, Some(&expected_distance));
assert_eq!(intervals.iter().max(), Some(&expected_distance));
// But lets assert all distances, which we expect to grow linearly until `max_interval + 1`
let expected_intervals: Vec<_> = (0..497)
.map(|i| (i/2).max(1).min(expected_distance) )
.collect();
let expected_intervals: Vec<_> =
(0..497).map(|i| (i / 2).max(1).min(expected_distance)).collect();
assert_eq!(intervals, expected_intervals);
}
fn run_until_max_interval(param: BackoffAuthoringOnFinalizedHeadLagging<u64>) -> (u64, u64) {
let finalized_number = 0;
let mut head_state = HeadState {
head_number: 0,
head_slot: 0,
slot_now: 1,
};
let mut head_state = HeadState { head_number: 0, head_slot: 0, slot_now: 1 };
let should_backoff = |head_state: &HeadState| -> bool {
<dyn BackoffAuthoringBlocksStrategy<NumberFor<Block>>>::should_backoff(
@@ -1277,8 +1246,8 @@ mod test {
};
// Number of blocks until we reach the max interval
let block_for_max_interval
= param.max_interval * param.authoring_bias + param.unfinalized_slack;
let block_for_max_interval =
param.max_interval * param.authoring_bias + param.unfinalized_slack;
while head_state.head_number < block_for_max_interval {
if should_backoff(&head_state) {
@@ -1294,15 +1263,15 @@ mod test {
}
// Denoting
// C: unfinalized_slack
// M: authoring_bias
// X: max_interval
// C: unfinalized_slack
// M: authoring_bias
// X: max_interval
// then the number of slots to reach the max interval can be computed from
// (start_slot + C) + M * sum(n, 1, X)
// (start_slot + C) + M * sum(n, 1, X)
// or
// (start_slot + C) + M * X*(X+1)/2
// (start_slot + C) + M * X*(X+1)/2
fn expected_time_to_reach_max_interval(
param: &BackoffAuthoringOnFinalizedHeadLagging<u64>
param: &BackoffAuthoringOnFinalizedHeadLagging<u64>,
) -> (u64, u64) {
let c = param.unfinalized_slack;
let m = param.authoring_bias;
+12 -17
View File
@@ -20,23 +20,21 @@
//!
//! This is used instead of `futures_timer::Interval` because it was unreliable.
use super::{Slot, InherentDataProviderExt};
use super::{InherentDataProviderExt, Slot};
use sp_consensus::{Error, SelectChain};
use sp_inherents::{InherentData, CreateInherentDataProviders, InherentDataProvider};
use sp_inherents::{CreateInherentDataProviders, InherentData, InherentDataProvider};
use sp_runtime::traits::{Block as BlockT, Header as HeaderT};
use std::time::{Duration, Instant};
use futures_timer::Delay;
use std::time::{Duration, Instant};
/// Returns current duration since unix epoch.
pub fn duration_now() -> Duration {
use std::time::SystemTime;
let now = SystemTime::now();
now.duration_since(SystemTime::UNIX_EPOCH).unwrap_or_else(|e| panic!(
"Current time {:?} is before unix epoch. Something is wrong: {:?}",
now,
e,
))
now.duration_since(SystemTime::UNIX_EPOCH).unwrap_or_else(|e| {
panic!("Current time {:?} is before unix epoch. Something is wrong: {:?}", now, e,)
})
}
/// Returns the duration until the next slot from now.
@@ -104,11 +102,7 @@ pub(crate) struct Slots<Block, C, IDP> {
impl<Block, C, IDP> Slots<Block, C, IDP> {
/// Create a new `Slots` stream.
pub fn new(
slot_duration: Duration,
create_inherent_data_providers: IDP,
client: C,
) -> Self {
pub fn new(slot_duration: Duration, create_inherent_data_providers: IDP, client: C) -> Self {
Slots {
last_slot: 0.into(),
slot_duration,
@@ -135,7 +129,7 @@ where
// schedule wait.
let wait_dur = time_until_next_slot(self.slot_duration);
Some(Delay::new(wait_dur))
}
},
Some(d) => Some(d),
};
@@ -161,11 +155,12 @@ where
);
// Let's try at the next slot..
self.inner_delay.take();
continue;
}
continue
},
};
let inherent_data_providers = self.create_inherent_data_providers
let inherent_data_providers = self
.create_inherent_data_providers
.create_inherent_data_providers(chain_head.hash(), ())
.await?;
+3 -2
View File
@@ -19,7 +19,7 @@
//! Uncles functionality for Substrate.
use sc_client_api::ProvideUncles;
use sp_runtime::{traits::Block as BlockT, generic::BlockId};
use sp_runtime::{generic::BlockId, traits::Block as BlockT};
#[derive(Debug, thiserror::Error)]
pub enum Error<B: BlockT> {
@@ -34,7 +34,8 @@ const MAX_UNCLE_GENERATIONS: u32 = 8;
pub fn create_uncles_inherent_data_provider<B, C>(
client: &C,
parent: B::Hash,
) -> Result<sp_authorship::InherentDataProvider<B::Header>, sc_client_api::blockchain::Error> where
) -> Result<sp_authorship::InherentDataProvider<B::Header>, sc_client_api::blockchain::Error>
where
B: BlockT,
C: ProvideUncles<B>,
{