make SelectChain async (#9128)

* make SelectChain async

* make JustificationImport async
This commit is contained in:
André Silva
2021-06-20 12:01:09 +01:00
committed by GitHub
parent 650fc2f9c9
commit a26ec52450
18 changed files with 800 additions and 454 deletions
+2
View File
@@ -7208,6 +7208,7 @@ dependencies = [
name = "sc-consensus"
version = "0.9.0"
dependencies = [
"async-trait",
"parking_lot 0.11.1",
"sc-client-api",
"sp-blockchain",
@@ -8707,6 +8708,7 @@ name = "sp-api-test"
version = "2.0.1"
dependencies = [
"criterion",
"futures 0.3.15",
"log",
"parity-scale-codec",
"rustversion",
+30 -21
View File
@@ -93,11 +93,14 @@ impl<B: BlockT, C, SC> BabeRpcHandler<B, C, SC> {
}
impl<B, C, SC> BabeApi for BabeRpcHandler<B, C, SC>
where
B: BlockT,
C: ProvideRuntimeApi<B> + HeaderBackend<B> + HeaderMetadata<B, Error=BlockChainError> + 'static,
C::Api: BabeRuntimeApi<B>,
SC: SelectChain<B> + Clone + 'static,
where
B: BlockT,
C: ProvideRuntimeApi<B>
+ HeaderBackend<B>
+ HeaderMetadata<B, Error = BlockChainError>
+ 'static,
C::Api: BabeRuntimeApi<B>,
SC: SelectChain<B> + Clone + 'static,
{
fn epoch_authorship(&self) -> FutureResult<HashMap<AuthorityId, EpochAuthorship>> {
if let Err(err) = self.deny_unsafe.check_if_safe() {
@@ -118,28 +121,33 @@ impl<B, C, SC> BabeApi for BabeRpcHandler<B, C, SC>
self.select_chain.clone(),
);
let future = async move {
let header = select_chain.best_chain().map_err(Error::Consensus)?;
let epoch_start = client.runtime_api()
let header = select_chain.best_chain().map_err(Error::Consensus).await?;
let epoch_start = client
.runtime_api()
.current_epoch_start(&BlockId::Hash(header.hash()))
.map_err(|err| {
Error::StringError(format!("{:?}", err))
})?;
.map_err(|err| Error::StringError(format!("{:?}", err)))?;
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();
let keys = {
epoch.authorities.iter()
epoch
.authorities
.iter()
.enumerate()
.filter_map(|(i, a)| {
if SyncCryptoStore::has_keys(&*keystore, &[(a.0.to_raw_vec(), AuthorityId::ID)]) {
if SyncCryptoStore::has_keys(
&*keystore,
&[(a.0.to_raw_vec(), AuthorityId::ID)],
) {
Some((a.0.clone(), i))
} else {
None
@@ -167,7 +175,8 @@ impl<B, C, SC> BabeApi for BabeRpcHandler<B, C, SC>
}
Ok(claims)
}.boxed();
}
.boxed();
Box::new(future.compat())
}
@@ -203,20 +212,20 @@ impl From<Error> for jsonrpc_core::Error {
}
}
/// fetches the epoch data for a given slot.
fn epoch_data<B, C, SC>(
/// Fetches the epoch data for a given slot.
async fn epoch_data<B, C, SC>(
epoch_changes: &SharedEpochChanges<B, Epoch>,
client: &Arc<C>,
babe_config: &Config,
slot: u64,
select_chain: &SC,
) -> Result<Epoch, Error>
where
B: BlockT,
C: HeaderBackend<B> + HeaderMetadata<B, Error=BlockChainError> + 'static,
SC: SelectChain<B>,
where
B: BlockT,
C: HeaderBackend<B> + HeaderMetadata<B, Error = BlockChainError> + 'static,
SC: SelectChain<B>,
{
let parent = select_chain.best_chain()?;
let parent = select_chain.best_chain().await?;
epoch_changes.shared_data().epoch_data_for_child_of(
descendent_query(&**client),
&parent.hash(),
+19 -5
View File
@@ -989,7 +989,7 @@ where
Ok(())
}
fn check_and_report_equivocation(
async fn check_and_report_equivocation(
&self,
slot_now: Slot,
slot: Slot,
@@ -1024,6 +1024,7 @@ where
let best_id = self
.select_chain
.best_chain()
.await
.map(|h| BlockId::Hash(h.hash()))
.map_err(|e| Error::Client(e.into()))?;
@@ -1070,13 +1071,26 @@ where
}
}
type BlockVerificationResult<Block> = Result<
(
BlockImportParams<Block, ()>,
Option<Vec<(CacheKeyId, Vec<u8>)>>,
),
String,
>;
#[async_trait::async_trait]
impl<Block, Client, SelectChain, CAW, CIDP> Verifier<Block>
for BabeVerifier<Block, Client, SelectChain, CAW, CIDP>
where
Block: BlockT,
Client: HeaderMetadata<Block, Error = sp_blockchain::Error> + HeaderBackend<Block> + ProvideRuntimeApi<Block>
+ Send + Sync + AuxStore + ProvideCache<Block>,
Client: HeaderMetadata<Block, Error = sp_blockchain::Error>
+ HeaderBackend<Block>
+ ProvideRuntimeApi<Block>
+ Send
+ Sync
+ AuxStore
+ ProvideCache<Block>,
Client::Api: BlockBuilderApi<Block> + BabeApi<Block>,
SelectChain: sp_consensus::SelectChain<Block>,
CAW: CanAuthorWith<Block> + Send + Sync,
@@ -1089,7 +1103,7 @@ where
header: Block::Header,
justifications: Option<Justifications>,
mut body: Option<Vec<Block::Extrinsic>>,
) -> Result<(BlockImportParams<Block, ()>, Option<Vec<(CacheKeyId, Vec<u8>)>>), String> {
) -> BlockVerificationResult<Block> {
trace!(
target: "babe",
"Verifying origin: {:?} header: {:?} justification(s): {:?} body: {:?}",
@@ -1158,7 +1172,7 @@ where
&header,
&verified_info.author,
&origin,
) {
).await {
warn!(target: "babe", "Error checking/reporting BABE equivocation: {:?}", err);
}
@@ -13,6 +13,7 @@ readme = "README.md"
targets = ["x86_64-unknown-linux-gnu"]
[dependencies]
async-trait = "0.1"
sc-client-api = { version = "3.0.0", path = "../../api" }
sp-blockchain = { version = "3.0.0", path = "../../../primitives/blockchain" }
sp-runtime = { version = "3.0.0", path = "../../../primitives/runtime" }
@@ -46,15 +46,15 @@ impl<B, Block> Clone for LongestChain<B, Block> {
}
impl<B, Block> LongestChain<B, Block>
where
B: backend::Backend<Block>,
Block: BlockT,
where
B: backend::Backend<Block>,
Block: BlockT,
{
/// Instantiate a new LongestChain for Backend B
pub fn new(backend: Arc<B>) -> Self {
LongestChain {
backend,
_phantom: Default::default()
_phantom: Default::default(),
}
}
@@ -75,30 +75,30 @@ impl<B, Block> LongestChain<B, Block>
}
}
#[async_trait::async_trait]
impl<B, Block> SelectChain<Block> for LongestChain<B, Block>
where
B: backend::Backend<Block>,
Block: BlockT,
where
B: backend::Backend<Block>,
Block: BlockT,
{
fn leaves(&self) -> Result<Vec<<Block as BlockT>::Hash>, ConsensusError> {
LongestChain::leaves(self)
.map_err(|e| ConsensusError::ChainLookup(e.to_string()).into())
async fn leaves(&self) -> Result<Vec<<Block as BlockT>::Hash>, ConsensusError> {
LongestChain::leaves(self).map_err(|e| ConsensusError::ChainLookup(e.to_string()).into())
}
fn best_chain(&self) -> Result<<Block as BlockT>::Header, ConsensusError>
{
async fn best_chain(&self) -> Result<<Block as BlockT>::Header, ConsensusError> {
LongestChain::best_block_header(&self)
.map_err(|e| ConsensusError::ChainLookup(e.to_string()).into())
}
fn finality_target(
async fn finality_target(
&self,
target_hash: Block::Hash,
maybe_max_number: Option<NumberFor<Block>>
maybe_max_number: Option<NumberFor<Block>>,
) -> Result<Option<Block::Hash>, ConsensusError> {
let import_lock = self.backend.get_import_lock();
self.backend.blockchain().best_containing(target_hash, maybe_max_number, import_lock)
self.backend
.blockchain()
.best_containing(target_hash, maybe_max_number, import_lock)
.map_err(|e| ConsensusError::ChainLookup(e.to_string()).into())
}
}
@@ -80,45 +80,47 @@ pub async fn seal_block<B, BI, SC, C, E, P, CIDP>(
create_inherent_data_providers,
consensus_data_provider: digest_provider,
mut sender,
}: SealBlockParams<'_, B, BI, SC, C, E, P, CIDP>
)
where
B: BlockT,
BI: BlockImport<B, Error = sp_consensus::Error, Transaction = sp_api::TransactionFor<C, B>>
+ Send + Sync + 'static,
C: HeaderBackend<B> + ProvideRuntimeApi<B>,
E: Environment<B>,
E::Proposer: Proposer<B, Transaction = TransactionFor<C, B>>,
P: txpool::ChainApi<Block=B>,
SC: SelectChain<B>,
TransactionFor<C, B>: 'static,
CIDP: CreateInherentDataProviders<B, ()>,
}: SealBlockParams<'_, B, BI, SC, C, E, P, CIDP>,
) where
B: BlockT,
BI: BlockImport<B, Error = sp_consensus::Error, Transaction = sp_api::TransactionFor<C, B>>
+ Send
+ Sync
+ 'static,
C: HeaderBackend<B> + ProvideRuntimeApi<B>,
E: Environment<B>,
E::Proposer: Proposer<B, Transaction = TransactionFor<C, B>>,
P: txpool::ChainApi<Block = B>,
SC: SelectChain<B>,
TransactionFor<C, B>: 'static,
CIDP: CreateInherentDataProviders<B, ()>,
{
let future = async {
if pool.validated_pool().status().ready == 0 && !create_empty {
return Err(Error::EmptyTransactionPool)
return Err(Error::EmptyTransactionPool);
}
// get the header to build this new block on.
// use the parent_hash supplied via `EngineCommand`
// or fetch the best_block.
let parent = match parent_hash {
Some(hash) => {
client.header(BlockId::Hash(hash))?.ok_or_else(|| Error::BlockNotFound(format!("{}", hash)))?
}
None => select_chain.best_chain()?
Some(hash) => client
.header(BlockId::Hash(hash))?
.ok_or_else(|| Error::BlockNotFound(format!("{}", hash)))?,
None => select_chain.best_chain().await?,
};
let inherent_data_providers =
create_inherent_data_providers
.create_inherent_data_providers(parent.hash(), ())
.await
.map_err(|e| Error::Other(e))?;
let inherent_data_providers = create_inherent_data_providers
.create_inherent_data_providers(parent.hash(), ())
.await
.map_err(|e| Error::Other(e))?;
let inherent_data = inherent_data_providers.create_inherent_data()?;
let proposer = env.init(&parent)
.map_err(|err| Error::StringError(format!("{:?}", err))).await?;
let proposer = env
.init(&parent)
.map_err(|err| Error::StringError(format!("{:?}", err)))
.await?;
let inherents_len = inherent_data.len();
let digest = if let Some(digest_provider) = digest_provider {
+8 -4
View File
@@ -341,7 +341,10 @@ where
mut block: BlockImportParams<B, Self::Transaction>,
new_cache: HashMap<CacheKeyId, Vec<u8>>,
) -> Result<ImportResult, Self::Error> {
let best_header = self.select_chain.best_chain()
let best_header = self
.select_chain
.best_chain()
.await
.map_err(|e| format!("Fetch best chain failed via select chain: {:?}", e))?;
let best_hash = best_header.hash();
@@ -543,7 +546,8 @@ pub fn start_mining_worker<Block, C, S, Algorithm, E, SO, L, CIDP, CAW>(
) -> (
Arc<Mutex<MiningWorker<Block, Algorithm, C, L, <E::Proposer as Proposer<Block>>::Proof>>>,
impl Future<Output = ()>,
) where
)
where
Block: BlockT,
C: ProvideRuntimeApi<Block> + BlockchainEvents<Block> + 'static,
S: SelectChain<Block> + 'static,
@@ -578,7 +582,7 @@ pub fn start_mining_worker<Block, C, S, Algorithm, E, SO, L, CIDP, CAW>(
return;
}
let best_header = match select_chain.best_chain() {
let best_header = match select_chain.best_chain().await {
Ok(x) => x,
Err(err) => {
warn!(
@@ -588,7 +592,7 @@ pub fn start_mining_worker<Block, C, S, Algorithm, E, SO, L, CIDP, CAW>(
err
);
return;
},
}
};
let best_hash = best_header.hash();
@@ -151,7 +151,7 @@ where
let ends_at = Instant::now() + ends_in;
let chain_head = match self.client.best_chain() {
let chain_head = match self.client.best_chain().await {
Ok(x) => x,
Err(e) => {
log::warn!(
@@ -34,12 +34,12 @@ use parking_lot::RwLock;
use prometheus_endpoint::{register, Counter, Gauge, PrometheusError, U64};
use sc_client_api::{
backend::{apply_aux, Backend},
backend::{apply_aux, Backend as BackendT},
utils::is_descendent_of,
};
use sc_telemetry::{telemetry, TelemetryHandle, CONSENSUS_DEBUG, CONSENSUS_INFO};
use sp_blockchain::HeaderMetadata;
use sp_consensus::SelectChain;
use sp_consensus::SelectChain as SelectChainT;
use sp_finality_grandpa::{
AuthorityId, AuthoritySignature, Equivocation, EquivocationProof, GrandpaApi, RoundNumber,
SetId, GRANDPA_ENGINE_ID,
@@ -54,7 +54,7 @@ use crate::{
local_authority_id,
notification::GrandpaJustificationSender,
until_imported::UntilVoteTargetImported,
voting_rule::VotingRule,
voting_rule::VotingRule as VotingRuleT,
ClientForGrandpa, CommandOrError, Commit, Config, Error, NewAuthoritySet, Precommit, Prevote,
PrimaryPropose, SignedMessage, VoterCommand,
};
@@ -478,11 +478,11 @@ impl<BE, Block: BlockT, C, N: NetworkT<Block>, SC, VR> Environment<BE, Block, C,
impl<BE, Block, C, N, SC, VR> Environment<BE, Block, C, N, SC, VR>
where
Block: BlockT,
BE: Backend<Block>,
BE: BackendT<Block>,
C: ClientForGrandpa<Block, BE>,
C::Api: GrandpaApi<Block>,
N: NetworkT<Block>,
SC: SelectChain<Block>,
SC: SelectChainT<Block>,
{
/// Report the given equivocation to the GRANDPA runtime module. This method
/// generates a session membership proof of the offender and then submits an
@@ -503,9 +503,12 @@ where
let is_descendent_of = is_descendent_of(&*self.client, None);
let best_header = self.select_chain
.best_chain()
.map_err(|e| Error::Blockchain(e.to_string()))?;
// TODO: add proper async support here
let best_header = futures::executor::block_on(
self.select_chain
.best_chain()
.map_err(|e| Error::Blockchain(e.to_string())),
)?;
let authority_set = self.authority_set.inner();
@@ -581,11 +584,11 @@ impl<BE, Block, C, N, SC, VR> finality_grandpa::Chain<Block::Hash, NumberFor<Blo
for Environment<BE, Block, C, N, SC, VR>
where
Block: BlockT,
BE: Backend<Block>,
BE: BackendT<Block>,
C: ClientForGrandpa<Block, BE>,
N: NetworkT<Block>,
SC: SelectChain<Block>,
VR: VotingRule<Block, C>,
SC: SelectChainT<Block>,
VR: VotingRuleT<Block, C>,
NumberFor<Block>: BlockNumberOps,
{
fn ancestry(
@@ -637,12 +640,12 @@ impl<B, Block, C, N, SC, VR> voter::Environment<Block::Hash, NumberFor<Block>>
for Environment<B, Block, C, N, SC, VR>
where
Block: BlockT,
B: Backend<Block>,
B: BackendT<Block>,
C: ClientForGrandpa<Block, B> + 'static,
C::Api: GrandpaApi<Block>,
N: NetworkT<Block>,
SC: SelectChain<Block>,
VR: VotingRule<Block, C>,
SC: SelectChainT<Block> + 'static,
VR: VotingRuleT<Block, C> + Clone + 'static,
NumberFor<Block>: BlockNumberOps,
{
type Timer = Pin<Box<dyn Future<Output = Result<(), Self::Error>> + Send>>;
@@ -684,116 +687,25 @@ where
type Error = CommandOrError<Block::Hash, NumberFor<Block>>;
fn best_chain_containing(&self, block: Block::Hash) -> Self::BestChain {
let find_best_chain = || {
let client = self.client.clone();
let authority_set = self.authority_set.clone();
let select_chain = self.select_chain.clone();
let voting_rule = self.voting_rule.clone();
let set_id = self.set_id;
Box::pin(async move {
// NOTE: when we finalize an authority set change through the sync protocol the voter is
// signaled asynchronously. therefore the voter could still vote in the next round
// before activating the new set. the `authority_set` is updated immediately thus we
// restrict the voter based on that.
if self.set_id != self.authority_set.set_id() {
return None;
// before activating the new set. the `authority_set` is updated immediately thus
// we restrict the voter based on that.
if set_id != authority_set.set_id() {
return Ok(None);
}
let base_header = match self.client.header(BlockId::Hash(block)).ok()? {
Some(h) => h,
None => {
debug!(target: "afg", "Encountered error finding best chain containing {:?}: couldn't find base block", block);
return None;
}
};
// we refuse to vote beyond the current limit number where transitions are scheduled to
// occur.
// once blocks are finalized that make that transition irrelevant or activate it,
// we will proceed onwards. most of the time there will be no pending transition.
// the limit, if any, is guaranteed to be higher than or equal to the given base number.
let limit = self.authority_set.current_limit(*base_header.number());
debug!(target: "afg", "Finding best chain containing block {:?} with number limit {:?}", block, limit);
match self.select_chain.finality_target(block, None) {
Ok(Some(best_hash)) => {
let best_header = self
.client
.header(BlockId::Hash(best_hash))
.ok()?
.expect("Header known to exist after `finality_target` call; qed");
// check if our vote is currently being limited due to a pending change
let limit = limit.filter(|limit| limit < best_header.number());
if let Some(target_number) = limit {
let mut target_header = best_header.clone();
// walk backwards until we find the target block
loop {
if *target_header.number() < target_number {
unreachable!(
"we are traversing backwards from a known block; \
blocks are stored contiguously; \
qed"
);
}
if *target_header.number() == target_number {
break;
}
target_header = self
.client
.header(BlockId::Hash(*target_header.parent_hash()))
.ok()?
.expect("Header known to exist after `finality_target` call; qed");
}
Some((base_header, best_header, target_header))
} else {
// otherwise just use the given best as the target
Some((base_header, best_header.clone(), best_header))
}
}
Ok(None) => {
debug!(target: "afg", "Encountered error finding best chain containing {:?}: couldn't find target block", block);
None
}
Err(e) => {
debug!(target: "afg", "Encountered error finding best chain containing {:?}: {:?}", block, e);
None
}
}
};
if let Some((base_header, best_header, target_header)) = find_best_chain() {
// restrict vote according to the given voting rule, if the
// voting rule doesn't restrict the vote then we keep the
// previous target.
//
// note that we pass the original `best_header`, i.e. before the
// authority set limit filter, which can be considered a
// mandatory/implicit voting rule.
//
// we also make sure that the restricted vote is higher than the
// round base (i.e. last finalized), otherwise the value
// returned by the given voting rule is ignored and the original
// target is used instead.
let rule_fut = self.voting_rule.restrict_vote(
self.client.clone(),
&base_header,
&best_header,
&target_header,
);
Box::pin(async move {
Ok(rule_fut
.await
.filter(|(_, restricted_number)| {
// we can only restrict votes within the interval [base, target]
restricted_number >= base_header.number()
&& restricted_number < target_header.number()
})
.or_else(|| Some((target_header.hash(), *target_header.number()))))
})
} else {
Box::pin(future::ok(None))
}
best_chain_containing(block, client, authority_set, select_chain, voting_rule)
.await
.map_err(|e| e.into())
})
}
fn round_data(
@@ -1227,6 +1139,111 @@ impl<Block: BlockT> From<GrandpaJustification<Block>> for JustificationOrCommit<
}
}
async fn best_chain_containing<Block, Backend, Client, SelectChain, VotingRule>(
block: Block::Hash,
client: Arc<Client>,
authority_set: SharedAuthoritySet<Block::Hash, NumberFor<Block>>,
select_chain: SelectChain,
voting_rule: VotingRule,
) -> Result<Option<(Block::Hash, NumberFor<Block>)>, Error>
where
Backend: BackendT<Block>,
Block: BlockT,
Client: ClientForGrandpa<Block, Backend>,
SelectChain: SelectChainT<Block> + 'static,
VotingRule: VotingRuleT<Block, Client>,
{
let base_header = match client.header(BlockId::Hash(block))? {
Some(h) => h,
None => {
debug!(target: "afg",
"Encountered error finding best chain containing {:?}: couldn't find base block",
block,
);
return Ok(None);
}
};
// we refuse to vote beyond the current limit number where transitions are scheduled to occur.
// once blocks are finalized that make that transition irrelevant or activate it, we will
// proceed onwards. most of the time there will be no pending transition. the limit, if any, is
// guaranteed to be higher than or equal to the given base number.
let limit = authority_set.current_limit(*base_header.number());
debug!(target: "afg", "Finding best chain containing block {:?} with number limit {:?}", block, limit);
let result = match select_chain.finality_target(block, None).await {
Ok(Some(best_hash)) => {
let best_header = client
.header(BlockId::Hash(best_hash))?
.expect("Header known to exist after `finality_target` call; qed");
// check if our vote is currently being limited due to a pending change
let limit = limit.filter(|limit| limit < best_header.number());
let (base_header, best_header, target_header) = if let Some(target_number) = limit {
let mut target_header = best_header.clone();
// walk backwards until we find the target block
loop {
if *target_header.number() < target_number {
unreachable!(
"we are traversing backwards from a known block; \
blocks are stored contiguously; \
qed"
);
}
if *target_header.number() == target_number {
break;
}
target_header = client
.header(BlockId::Hash(*target_header.parent_hash()))?
.expect("Header known to exist after `finality_target` call; qed");
}
(base_header, best_header, target_header)
} else {
// otherwise just use the given best as the target
(base_header, best_header.clone(), best_header)
};
// restrict vote according to the given voting rule, if the
// voting rule doesn't restrict the vote then we keep the
// previous target.
//
// note that we pass the original `best_header`, i.e. before the
// authority set limit filter, which can be considered a
// mandatory/implicit voting rule.
//
// we also make sure that the restricted vote is higher than the
// round base (i.e. last finalized), otherwise the value
// returned by the given voting rule is ignored and the original
// target is used instead.
voting_rule
.restrict_vote(client.clone(), &base_header, &best_header, &target_header)
.await
.filter(|(_, restricted_number)| {
// we can only restrict votes within the interval [base, target]
restricted_number >= base_header.number() &&
restricted_number < target_header.number()
})
.or_else(|| Some((target_header.hash(), *target_header.number())))
}
Ok(None) => {
debug!(target: "afg", "Encountered error finding best chain containing {:?}: couldn't find target block", block);
None
}
Err(e) => {
debug!(target: "afg", "Encountered error finding best chain containing {:?}: {:?}", block, e);
None
}
};
Ok(result)
}
/// Finalize the given block and apply any authority set changes. If an
/// authority set change is enacted then a justification is created (if not
/// given) and stored with the block when finalizing it.
@@ -1244,7 +1261,7 @@ pub(crate) fn finalize_block<BE, Block, Client>(
) -> Result<(), CommandOrError<Block::Hash, NumberFor<Block>>>
where
Block: BlockT,
BE: Backend<Block>,
BE: BackendT<Block>,
Client: ClientForGrandpa<Block, BE>,
{
// NOTE: lock must be held through writing to DB to avoid race. this lock
@@ -81,6 +81,7 @@ impl<Backend, Block: BlockT, Client, SC: Clone> Clone
}
}
#[async_trait::async_trait]
impl<BE, Block: BlockT, Client, SC> JustificationImport<Block>
for GrandpaBlockImport<BE, Block, Client, SC>
where
@@ -92,22 +93,30 @@ where
{
type Error = ConsensusError;
fn on_start(&mut self) -> Vec<(Block::Hash, NumberFor<Block>)> {
async fn on_start(&mut self) -> Vec<(Block::Hash, NumberFor<Block>)> {
let mut out = Vec::new();
let chain_info = self.inner.info();
// request justifications for all pending changes for which change blocks have already been imported
let authorities = self.authority_set.inner();
for pending_change in authorities.pending_changes() {
let pending_changes: Vec<_> = self
.authority_set
.inner()
.pending_changes()
.cloned()
.collect();
for pending_change in pending_changes {
if pending_change.delay_kind == DelayKind::Finalized &&
pending_change.effective_number() > chain_info.finalized_number &&
pending_change.effective_number() <= chain_info.best_number
{
let effective_block_hash = if !pending_change.delay.is_zero() {
self.select_chain.finality_target(
pending_change.canon_hash,
Some(pending_change.effective_number()),
)
self.select_chain
.finality_target(
pending_change.canon_hash,
Some(pending_change.effective_number()),
)
.await
} else {
Ok(Some(pending_change.canon_hash))
};
@@ -125,7 +134,7 @@ where
out
}
fn import_justification(
async fn import_justification(
&mut self,
hash: Block::Hash,
number: NumberFor<Block>,
+6 -1
View File
@@ -1075,10 +1075,15 @@ impl TestNetFactory for TestNet {
pub struct ForceFinalized(PeersClient);
#[async_trait::async_trait]
impl JustificationImport<Block> for ForceFinalized {
type Error = ConsensusError;
fn import_justification(
async fn on_start(&mut self) -> Vec<(H256, NumberFor<Block>)> {
Vec::new()
}
async fn import_justification(
&mut self,
hash: H256,
_number: NumberFor<Block>,
+3 -2
View File
@@ -561,13 +561,14 @@ mod tests {
client.clone(),
);
let source = sp_runtime::transaction_validity::TransactionSource::External;
let best = longest_chain.best_chain().unwrap();
let best = block_on(longest_chain.best_chain()).unwrap();
let transaction = Transfer {
amount: 5,
nonce: 0,
from: AccountKeyring::Alice.into(),
to: Default::default(),
}.into_signed_tx();
}
.into_signed_tx();
block_on(pool.submit_one(
&BlockId::hash(best.hash()), source, transaction.clone()),
).unwrap();
+476 -218
View File
@@ -337,7 +337,6 @@ fn construct_genesis_with_bad_transaction_should_panic() {
assert!(r.is_err());
}
#[test]
fn client_initializes_from_genesis_ok() {
let client = substrate_test_runtime_client::new();
@@ -450,7 +449,9 @@ fn best_containing_with_genesis_block() {
assert_eq!(
genesis_hash.clone(),
longest_chain_select.finality_target(genesis_hash.clone(), None).unwrap().unwrap()
block_on(longest_chain_select.finality_target(genesis_hash.clone(), None))
.unwrap()
.unwrap(),
);
}
@@ -461,11 +462,17 @@ fn best_containing_with_hash_not_found() {
let (client, longest_chain_select) = TestClientBuilder::new().build_with_longest_chain();
let uninserted_block = client.new_block(Default::default()).unwrap().build().unwrap().block;
let uninserted_block = client
.new_block(Default::default())
.unwrap()
.build()
.unwrap()
.block;
assert_eq!(
None,
longest_chain_select.finality_target(uninserted_block.hash().clone(), None).unwrap()
block_on(longest_chain_select.finality_target(uninserted_block.hash().clone(), None))
.unwrap(),
);
}
@@ -624,18 +631,43 @@ fn best_containing_on_longest_chain_with_single_chain_3_blocks() {
let (mut client, longest_chain_select) = TestClientBuilder::new().build_with_longest_chain();
// G -> A1
let a1 = client.new_block(Default::default()).unwrap().build().unwrap().block;
let a1 = client
.new_block(Default::default())
.unwrap()
.build()
.unwrap()
.block;
block_on(client.import(BlockOrigin::Own, a1.clone())).unwrap();
// A1 -> A2
let a2 = client.new_block(Default::default()).unwrap().build().unwrap().block;
let a2 = client
.new_block(Default::default())
.unwrap()
.build()
.unwrap()
.block;
block_on(client.import(BlockOrigin::Own, a2.clone())).unwrap();
let genesis_hash = client.chain_info().genesis_hash;
assert_eq!(a2.hash(), longest_chain_select.finality_target(genesis_hash, None).unwrap().unwrap());
assert_eq!(a2.hash(), longest_chain_select.finality_target(a1.hash(), None).unwrap().unwrap());
assert_eq!(a2.hash(), longest_chain_select.finality_target(a2.hash(), None).unwrap().unwrap());
assert_eq!(
a2.hash(),
block_on(longest_chain_select.finality_target(genesis_hash, None))
.unwrap()
.unwrap()
);
assert_eq!(
a2.hash(),
block_on(longest_chain_select.finality_target(a1.hash(), None))
.unwrap()
.unwrap()
);
assert_eq!(
a2.hash(),
block_on(longest_chain_select.finality_target(a2.hash(), None))
.unwrap()
.unwrap()
);
}
#[test]
@@ -715,19 +747,19 @@ fn best_containing_on_longest_chain_with_multiple_forks() {
).unwrap().build().unwrap().block;
block_on(client.import(BlockOrigin::Own, b4.clone())).unwrap();
// // B2 -> C3
let mut builder = client.new_block_at(
&BlockId::Hash(b2.hash()),
Default::default(),
false,
).unwrap();
// B2 -> C3
let mut builder = client
.new_block_at(&BlockId::Hash(b2.hash()), Default::default(), false)
.unwrap();
// this push is required as otherwise C3 has the same hash as B3 and won't get imported
builder.push_transfer(Transfer {
from: AccountKeyring::Alice.into(),
to: AccountKeyring::Ferdie.into(),
amount: 1,
nonce: 1,
}).unwrap();
builder
.push_transfer(Transfer {
from: AccountKeyring::Alice.into(),
to: AccountKeyring::Ferdie.into(),
amount: 1,
nonce: 1,
})
.unwrap();
let c3 = builder.build().unwrap().block;
block_on(client.import(BlockOrigin::Own, c3.clone())).unwrap();
@@ -750,7 +782,7 @@ fn best_containing_on_longest_chain_with_multiple_forks() {
assert_eq!(client.chain_info().best_hash, a5.hash());
let genesis_hash = client.chain_info().genesis_hash;
let leaves = longest_chain_select.leaves().unwrap();
let leaves = block_on(longest_chain_select.leaves()).unwrap();
assert!(leaves.contains(&a5.hash()));
assert!(leaves.contains(&b4.hash()));
@@ -759,208 +791,422 @@ fn best_containing_on_longest_chain_with_multiple_forks() {
assert_eq!(leaves.len(), 4);
// search without restriction
assert_eq!(a5.hash(), longest_chain_select.finality_target(
genesis_hash, None).unwrap().unwrap());
assert_eq!(a5.hash(), longest_chain_select.finality_target(
a1.hash(), None).unwrap().unwrap());
assert_eq!(a5.hash(), longest_chain_select.finality_target(
a2.hash(), None).unwrap().unwrap());
assert_eq!(a5.hash(), longest_chain_select.finality_target(
a3.hash(), None).unwrap().unwrap());
assert_eq!(a5.hash(), longest_chain_select.finality_target(
a4.hash(), None).unwrap().unwrap());
assert_eq!(a5.hash(), longest_chain_select.finality_target(
a5.hash(), None).unwrap().unwrap());
assert_eq!(b4.hash(), longest_chain_select.finality_target(
b2.hash(), None).unwrap().unwrap());
assert_eq!(b4.hash(), longest_chain_select.finality_target(
b3.hash(), None).unwrap().unwrap());
assert_eq!(b4.hash(), longest_chain_select.finality_target(
b4.hash(), None).unwrap().unwrap());
assert_eq!(c3.hash(), longest_chain_select.finality_target(
c3.hash(), None).unwrap().unwrap());
assert_eq!(d2.hash(), longest_chain_select.finality_target(
d2.hash(), None).unwrap().unwrap());
assert_eq!(
a5.hash(),
block_on(longest_chain_select.finality_target(genesis_hash, None))
.unwrap()
.unwrap()
);
assert_eq!(
a5.hash(),
block_on(longest_chain_select.finality_target(a1.hash(), None))
.unwrap()
.unwrap()
);
assert_eq!(
a5.hash(),
block_on(longest_chain_select.finality_target(a2.hash(), None))
.unwrap()
.unwrap()
);
assert_eq!(
a5.hash(),
block_on(longest_chain_select.finality_target(a3.hash(), None))
.unwrap()
.unwrap()
);
assert_eq!(
a5.hash(),
block_on(longest_chain_select.finality_target(a4.hash(), None))
.unwrap()
.unwrap()
);
assert_eq!(
a5.hash(),
block_on(longest_chain_select.finality_target(a5.hash(), None))
.unwrap()
.unwrap()
);
assert_eq!(
b4.hash(),
block_on(longest_chain_select.finality_target(b2.hash(), None))
.unwrap()
.unwrap()
);
assert_eq!(
b4.hash(),
block_on(longest_chain_select.finality_target(b3.hash(), None))
.unwrap()
.unwrap()
);
assert_eq!(
b4.hash(),
block_on(longest_chain_select.finality_target(b4.hash(), None))
.unwrap()
.unwrap()
);
assert_eq!(
c3.hash(),
block_on(longest_chain_select.finality_target(c3.hash(), None))
.unwrap()
.unwrap()
);
assert_eq!(
d2.hash(),
block_on(longest_chain_select.finality_target(d2.hash(), None))
.unwrap()
.unwrap()
);
// search only blocks with number <= 5. equivalent to without restriction for this scenario
assert_eq!(a5.hash(), longest_chain_select.finality_target(
genesis_hash, Some(5)).unwrap().unwrap());
assert_eq!(a5.hash(), longest_chain_select.finality_target(
a1.hash(), Some(5)).unwrap().unwrap());
assert_eq!(a5.hash(), longest_chain_select.finality_target(
a2.hash(), Some(5)).unwrap().unwrap());
assert_eq!(a5.hash(), longest_chain_select.finality_target(
a3.hash(), Some(5)).unwrap().unwrap());
assert_eq!(a5.hash(), longest_chain_select.finality_target(
a4.hash(), Some(5)).unwrap().unwrap());
assert_eq!(a5.hash(), longest_chain_select.finality_target(
a5.hash(), Some(5)).unwrap().unwrap());
assert_eq!(b4.hash(), longest_chain_select.finality_target(
b2.hash(), Some(5)).unwrap().unwrap());
assert_eq!(b4.hash(), longest_chain_select.finality_target(
b3.hash(), Some(5)).unwrap().unwrap());
assert_eq!(b4.hash(), longest_chain_select.finality_target(
b4.hash(), Some(5)).unwrap().unwrap());
assert_eq!(c3.hash(), longest_chain_select.finality_target(
c3.hash(), Some(5)).unwrap().unwrap());
assert_eq!(d2.hash(), longest_chain_select.finality_target(
d2.hash(), Some(5)).unwrap().unwrap());
assert_eq!(
a5.hash(),
block_on(longest_chain_select.finality_target(genesis_hash, Some(5)))
.unwrap()
.unwrap()
);
assert_eq!(
a5.hash(),
block_on(longest_chain_select.finality_target(a1.hash(), Some(5)))
.unwrap()
.unwrap()
);
assert_eq!(
a5.hash(),
block_on(longest_chain_select.finality_target(a2.hash(), Some(5)))
.unwrap()
.unwrap()
);
assert_eq!(
a5.hash(),
block_on(longest_chain_select.finality_target(a3.hash(), Some(5)))
.unwrap()
.unwrap()
);
assert_eq!(
a5.hash(),
block_on(longest_chain_select.finality_target(a4.hash(), Some(5)))
.unwrap()
.unwrap()
);
assert_eq!(
a5.hash(),
block_on(longest_chain_select.finality_target(a5.hash(), Some(5)))
.unwrap()
.unwrap()
);
assert_eq!(
b4.hash(),
block_on(longest_chain_select.finality_target(b2.hash(), Some(5)))
.unwrap()
.unwrap()
);
assert_eq!(
b4.hash(),
block_on(longest_chain_select.finality_target(b3.hash(), Some(5)))
.unwrap()
.unwrap()
);
assert_eq!(
b4.hash(),
block_on(longest_chain_select.finality_target(b4.hash(), Some(5)))
.unwrap()
.unwrap()
);
assert_eq!(
c3.hash(),
block_on(longest_chain_select.finality_target(c3.hash(), Some(5)))
.unwrap()
.unwrap()
);
assert_eq!(
d2.hash(),
block_on(longest_chain_select.finality_target(d2.hash(), Some(5)))
.unwrap()
.unwrap()
);
// search only blocks with number <= 4
assert_eq!(a4.hash(), longest_chain_select.finality_target(
genesis_hash, Some(4)).unwrap().unwrap());
assert_eq!(a4.hash(), longest_chain_select.finality_target(
a1.hash(), Some(4)).unwrap().unwrap());
assert_eq!(a4.hash(), longest_chain_select.finality_target(
a2.hash(), Some(4)).unwrap().unwrap());
assert_eq!(a4.hash(), longest_chain_select.finality_target(
a3.hash(), Some(4)).unwrap().unwrap());
assert_eq!(a4.hash(), longest_chain_select.finality_target(
a4.hash(), Some(4)).unwrap().unwrap());
assert_eq!(None, longest_chain_select.finality_target(
a5.hash(), Some(4)).unwrap());
assert_eq!(b4.hash(), longest_chain_select.finality_target(
b2.hash(), Some(4)).unwrap().unwrap());
assert_eq!(b4.hash(), longest_chain_select.finality_target(
b3.hash(), Some(4)).unwrap().unwrap());
assert_eq!(b4.hash(), longest_chain_select.finality_target(
b4.hash(), Some(4)).unwrap().unwrap());
assert_eq!(c3.hash(), longest_chain_select.finality_target(
c3.hash(), Some(4)).unwrap().unwrap());
assert_eq!(d2.hash(), longest_chain_select.finality_target(
d2.hash(), Some(4)).unwrap().unwrap());
// search only blocks with number <= 3
assert_eq!(a3.hash(), longest_chain_select.finality_target(
genesis_hash, Some(3)).unwrap().unwrap());
assert_eq!(a3.hash(), longest_chain_select.finality_target(
a1.hash(), Some(3)).unwrap().unwrap());
assert_eq!(a3.hash(), longest_chain_select.finality_target(
a2.hash(), Some(3)).unwrap().unwrap());
assert_eq!(a3.hash(), longest_chain_select.finality_target(
a3.hash(), Some(3)).unwrap().unwrap());
assert_eq!(None, longest_chain_select.finality_target(
a4.hash(), Some(3)).unwrap());
assert_eq!(None, longest_chain_select.finality_target(
a5.hash(), Some(3)).unwrap());
assert_eq!(b3.hash(), longest_chain_select.finality_target(
b2.hash(), Some(3)).unwrap().unwrap());
assert_eq!(b3.hash(), longest_chain_select.finality_target(
b3.hash(), Some(3)).unwrap().unwrap());
assert_eq!(None, longest_chain_select.finality_target(
b4.hash(), Some(3)).unwrap());
assert_eq!(c3.hash(), longest_chain_select.finality_target(
c3.hash(), Some(3)).unwrap().unwrap());
assert_eq!(d2.hash(), longest_chain_select.finality_target(
d2.hash(), Some(3)).unwrap().unwrap());
// search only blocks with number <= 2
assert_eq!(a2.hash(), longest_chain_select.finality_target(
genesis_hash, Some(2)).unwrap().unwrap());
assert_eq!(a2.hash(), longest_chain_select.finality_target(
a1.hash(), Some(2)).unwrap().unwrap());
assert_eq!(a2.hash(), longest_chain_select.finality_target(
a2.hash(), Some(2)).unwrap().unwrap());
assert_eq!(None, longest_chain_select.finality_target(
a3.hash(), Some(2)).unwrap());
assert_eq!(None, longest_chain_select.finality_target(
a4.hash(), Some(2)).unwrap());
assert_eq!(None, longest_chain_select.finality_target(
a5.hash(), Some(2)).unwrap());
assert_eq!(b2.hash(), longest_chain_select.finality_target(
b2.hash(), Some(2)).unwrap().unwrap());
assert_eq!(None, longest_chain_select.finality_target(
b3.hash(), Some(2)).unwrap());
assert_eq!(None, longest_chain_select.finality_target(
b4.hash(), Some(2)).unwrap());
assert_eq!(None, longest_chain_select.finality_target(
c3.hash(), Some(2)).unwrap());
assert_eq!(d2.hash(), longest_chain_select.finality_target(
d2.hash(), Some(2)).unwrap().unwrap());
// search only blocks with number <= 1
assert_eq!(a1.hash(), longest_chain_select.finality_target(
genesis_hash, Some(1)).unwrap().unwrap());
assert_eq!(a1.hash(), longest_chain_select.finality_target(
a1.hash(), Some(1)).unwrap().unwrap());
assert_eq!(None, longest_chain_select.finality_target(
a2.hash(), Some(1)).unwrap());
assert_eq!(None, longest_chain_select.finality_target(
a3.hash(), Some(1)).unwrap());
assert_eq!(None, longest_chain_select.finality_target(
a4.hash(), Some(1)).unwrap());
assert_eq!(None, longest_chain_select.finality_target(
a5.hash(), Some(1)).unwrap());
assert_eq!(None, longest_chain_select.finality_target(
b2.hash(), Some(1)).unwrap());
assert_eq!(None, longest_chain_select.finality_target(
b3.hash(), Some(1)).unwrap());
assert_eq!(None, longest_chain_select.finality_target(
b4.hash(), Some(1)).unwrap());
assert_eq!(None, longest_chain_select.finality_target(
c3.hash(), Some(1)).unwrap());
assert_eq!(None, longest_chain_select.finality_target(
d2.hash(), Some(1)).unwrap());
// search only blocks with number <= 0
assert_eq!(genesis_hash, longest_chain_select.finality_target(
genesis_hash, Some(0)).unwrap().unwrap());
assert_eq!(None, longest_chain_select.finality_target(
a1.hash(), Some(0)).unwrap());
assert_eq!(None, longest_chain_select.finality_target(
a2.hash(), Some(0)).unwrap());
assert_eq!(None, longest_chain_select.finality_target(
a3.hash(), Some(0)).unwrap());
assert_eq!(None, longest_chain_select.finality_target(
a4.hash(), Some(0)).unwrap());
assert_eq!(None, longest_chain_select.finality_target(
a5.hash(), Some(0)).unwrap());
assert_eq!(None, longest_chain_select.finality_target(
b2.hash(), Some(0)).unwrap());
assert_eq!(None, longest_chain_select.finality_target(
b3.hash(), Some(0)).unwrap());
assert_eq!(None, longest_chain_select.finality_target(
b4.hash(), Some(0)).unwrap());
assert_eq!(
a4.hash(),
block_on(longest_chain_select.finality_target(genesis_hash, Some(4)))
.unwrap()
.unwrap()
);
assert_eq!(
a4.hash(),
block_on(longest_chain_select.finality_target(a1.hash(), Some(4)))
.unwrap()
.unwrap()
);
assert_eq!(
a4.hash(),
block_on(longest_chain_select.finality_target(a2.hash(), Some(4)))
.unwrap()
.unwrap()
);
assert_eq!(
a4.hash(),
block_on(longest_chain_select.finality_target(a3.hash(), Some(4)))
.unwrap()
.unwrap()
);
assert_eq!(
a4.hash(),
block_on(longest_chain_select.finality_target(a4.hash(), Some(4)))
.unwrap()
.unwrap()
);
assert_eq!(
None,
longest_chain_select.finality_target(c3.hash().clone(), Some(0)).unwrap(),
block_on(longest_chain_select.finality_target(a5.hash(), Some(4))).unwrap()
);
assert_eq!(
b4.hash(),
block_on(longest_chain_select.finality_target(b2.hash(), Some(4)))
.unwrap()
.unwrap()
);
assert_eq!(
b4.hash(),
block_on(longest_chain_select.finality_target(b3.hash(), Some(4)))
.unwrap()
.unwrap()
);
assert_eq!(
b4.hash(),
block_on(longest_chain_select.finality_target(b4.hash(), Some(4)))
.unwrap()
.unwrap()
);
assert_eq!(
c3.hash(),
block_on(longest_chain_select.finality_target(c3.hash(), Some(4)))
.unwrap()
.unwrap()
);
assert_eq!(
d2.hash(),
block_on(longest_chain_select.finality_target(d2.hash(), Some(4)))
.unwrap()
.unwrap()
);
// search only blocks with number <= 3
assert_eq!(
a3.hash(),
block_on(longest_chain_select.finality_target(genesis_hash, Some(3)))
.unwrap()
.unwrap()
);
assert_eq!(
a3.hash(),
block_on(longest_chain_select.finality_target(a1.hash(), Some(3)))
.unwrap()
.unwrap()
);
assert_eq!(
a3.hash(),
block_on(longest_chain_select.finality_target(a2.hash(), Some(3)))
.unwrap()
.unwrap()
);
assert_eq!(
a3.hash(),
block_on(longest_chain_select.finality_target(a3.hash(), Some(3)))
.unwrap()
.unwrap()
);
assert_eq!(
None,
block_on(longest_chain_select.finality_target(a4.hash(), Some(3))).unwrap()
);
assert_eq!(
None,
block_on(longest_chain_select.finality_target(a5.hash(), Some(3))).unwrap()
);
assert_eq!(
b3.hash(),
block_on(longest_chain_select.finality_target(b2.hash(), Some(3)))
.unwrap()
.unwrap()
);
assert_eq!(
b3.hash(),
block_on(longest_chain_select.finality_target(b3.hash(), Some(3)))
.unwrap()
.unwrap()
);
assert_eq!(
None,
block_on(longest_chain_select.finality_target(b4.hash(), Some(3))).unwrap()
);
assert_eq!(
c3.hash(),
block_on(longest_chain_select.finality_target(c3.hash(), Some(3)))
.unwrap()
.unwrap()
);
assert_eq!(
d2.hash(),
block_on(longest_chain_select.finality_target(d2.hash(), Some(3)))
.unwrap()
.unwrap()
);
// search only blocks with number <= 2
assert_eq!(
a2.hash(),
block_on(longest_chain_select.finality_target(genesis_hash, Some(2)))
.unwrap()
.unwrap()
);
assert_eq!(
a2.hash(),
block_on(longest_chain_select.finality_target(a1.hash(), Some(2)))
.unwrap()
.unwrap()
);
assert_eq!(
a2.hash(),
block_on(longest_chain_select.finality_target(a2.hash(), Some(2)))
.unwrap()
.unwrap()
);
assert_eq!(
None,
block_on(longest_chain_select.finality_target(a3.hash(), Some(2))).unwrap()
);
assert_eq!(
None,
block_on(longest_chain_select.finality_target(a4.hash(), Some(2))).unwrap()
);
assert_eq!(
None,
block_on(longest_chain_select.finality_target(a5.hash(), Some(2))).unwrap()
);
assert_eq!(
b2.hash(),
block_on(longest_chain_select.finality_target(b2.hash(), Some(2)))
.unwrap()
.unwrap()
);
assert_eq!(
None,
block_on(longest_chain_select.finality_target(b3.hash(), Some(2))).unwrap()
);
assert_eq!(
None,
block_on(longest_chain_select.finality_target(b4.hash(), Some(2))).unwrap()
);
assert_eq!(
None,
block_on(longest_chain_select.finality_target(c3.hash(), Some(2))).unwrap()
);
assert_eq!(
d2.hash(),
block_on(longest_chain_select.finality_target(d2.hash(), Some(2)))
.unwrap()
.unwrap()
);
// search only blocks with number <= 1
assert_eq!(
a1.hash(),
block_on(longest_chain_select.finality_target(genesis_hash, Some(1)))
.unwrap()
.unwrap()
);
assert_eq!(
a1.hash(),
block_on(longest_chain_select.finality_target(a1.hash(), Some(1)))
.unwrap()
.unwrap()
);
assert_eq!(
None,
block_on(longest_chain_select.finality_target(a2.hash(), Some(1))).unwrap()
);
assert_eq!(
None,
block_on(longest_chain_select.finality_target(a3.hash(), Some(1))).unwrap()
);
assert_eq!(
None,
block_on(longest_chain_select.finality_target(a4.hash(), Some(1))).unwrap()
);
assert_eq!(
None,
block_on(longest_chain_select.finality_target(a5.hash(), Some(1))).unwrap()
);
assert_eq!(
None,
longest_chain_select.finality_target(d2.hash().clone(), Some(0)).unwrap(),
block_on(longest_chain_select.finality_target(b2.hash(), Some(1))).unwrap()
);
assert_eq!(
None,
block_on(longest_chain_select.finality_target(b3.hash(), Some(1))).unwrap()
);
assert_eq!(
None,
block_on(longest_chain_select.finality_target(b4.hash(), Some(1))).unwrap()
);
assert_eq!(
None,
block_on(longest_chain_select.finality_target(c3.hash(), Some(1))).unwrap()
);
assert_eq!(
None,
block_on(longest_chain_select.finality_target(d2.hash(), Some(1))).unwrap()
);
// search only blocks with number <= 0
assert_eq!(
genesis_hash,
block_on(longest_chain_select.finality_target(genesis_hash, Some(0)))
.unwrap()
.unwrap()
);
assert_eq!(
None,
block_on(longest_chain_select.finality_target(a1.hash(), Some(0))).unwrap()
);
assert_eq!(
None,
block_on(longest_chain_select.finality_target(a2.hash(), Some(0))).unwrap()
);
assert_eq!(
None,
block_on(longest_chain_select.finality_target(a3.hash(), Some(0))).unwrap()
);
assert_eq!(
None,
block_on(longest_chain_select.finality_target(a4.hash(), Some(0))).unwrap()
);
assert_eq!(
None,
block_on(longest_chain_select.finality_target(a5.hash(), Some(0))).unwrap()
);
assert_eq!(
None,
block_on(longest_chain_select.finality_target(b2.hash(), Some(0))).unwrap()
);
assert_eq!(
None,
block_on(longest_chain_select.finality_target(b3.hash(), Some(0))).unwrap()
);
assert_eq!(
None,
block_on(longest_chain_select.finality_target(b4.hash(), Some(0))).unwrap()
);
assert_eq!(
None,
block_on(longest_chain_select.finality_target(c3.hash().clone(), Some(0))).unwrap(),
);
assert_eq!(
None,
block_on(longest_chain_select.finality_target(d2.hash().clone(), Some(0))).unwrap(),
);
}
@@ -972,18 +1218,30 @@ fn best_containing_on_longest_chain_with_max_depth_higher_than_best() {
let (mut client, longest_chain_select) = TestClientBuilder::new().build_with_longest_chain();
// G -> A1
let a1 = client.new_block(Default::default()).unwrap().build().unwrap().block;
let a1 = client
.new_block(Default::default())
.unwrap()
.build()
.unwrap()
.block;
block_on(client.import(BlockOrigin::Own, a1.clone())).unwrap();
// A1 -> A2
let a2 = client.new_block(Default::default()).unwrap().build().unwrap().block;
let a2 = client
.new_block(Default::default())
.unwrap()
.build()
.unwrap()
.block;
block_on(client.import(BlockOrigin::Own, a2.clone())).unwrap();
let genesis_hash = client.chain_info().genesis_hash;
assert_eq!(
a2.hash(),
longest_chain_select.finality_target(genesis_hash, Some(10)).unwrap().unwrap(),
block_on(longest_chain_select.finality_target(genesis_hash, Some(10)))
.unwrap()
.unwrap(),
);
}
@@ -1181,7 +1439,7 @@ fn finalizing_diverged_block_should_trigger_reorg() {
// `SelectChain` should report B2 as best block though
assert_eq!(
select_chain.best_chain().unwrap().hash(),
block_on(select_chain.best_chain()).unwrap().hash(),
b2.hash(),
);
+3 -2
View File
@@ -27,9 +27,10 @@ rustversion = "1.0.0"
[dev-dependencies]
criterion = "0.3.0"
substrate-test-runtime-client = { version = "2.0.0", path = "../../../test-utils/runtime/client" }
sp-core = { version = "3.0.0", path = "../../core" }
futures = "0.3.9"
log = "0.4.14"
sp-core = { version = "3.0.0", path = "../../core" }
substrate-test-runtime-client = { version = "2.0.0", path = "../../../test-utils/runtime/client" }
[[bench]]
name = "bench"
@@ -160,10 +160,15 @@ fn record_proof_works() {
.build_with_longest_chain();
let block_id = BlockId::Number(client.chain_info().best_number);
let storage_root = longest_chain.best_chain().unwrap().state_root().clone();
let storage_root = futures::executor::block_on(longest_chain.best_chain())
.unwrap()
.state_root()
.clone();
let runtime_code = sp_core::traits::RuntimeCode {
code_fetcher: &sp_core::traits::WrappedRuntimeCode(client.code_at(&block_id).unwrap().into()),
code_fetcher: &sp_core::traits::WrappedRuntimeCode(
client.code_at(&block_id).unwrap().into(),
),
hash: vec![1],
heap_pages: None,
};
@@ -363,15 +363,16 @@ impl<B: BlockT, T, E: std::error::Error + Send + 'static, Transaction> BlockImpo
}
/// Justification import trait
#[async_trait::async_trait]
pub trait JustificationImport<B: BlockT> {
type Error: std::error::Error + Send + 'static;
/// Called by the import queue when it is started. Returns a list of justifications to request
/// from the network.
fn on_start(&mut self) -> Vec<(B::Hash, NumberFor<B>)> { Vec::new() }
async fn on_start(&mut self) -> Vec<(B::Hash, NumberFor<B>)>;
/// Import a Block justification and finalize the given block.
fn import_justification(
async fn import_justification(
&mut self,
hash: B::Hash,
number: NumberFor<B>,
@@ -220,16 +220,16 @@ impl<B: BlockT> BlockImportWorker<B> {
metrics,
};
// Let's initialize `justification_import`
if let Some(justification_import) = worker.justification_import.as_mut() {
for (hash, number) in justification_import.on_start() {
worker.result_sender.request_justification(&hash, number);
}
}
let delay_between_blocks = Duration::default();
let future = async move {
// Let's initialize `justification_import`
if let Some(justification_import) = worker.justification_import.as_mut() {
for (hash, number) in justification_import.on_start().await {
worker.result_sender.request_justification(&hash, number);
}
}
let block_import_process = block_import_process(
block_import,
verifier,
@@ -254,15 +254,18 @@ impl<B: BlockT> BlockImportWorker<B> {
// Make sure to first process all justifications
while let Poll::Ready(justification) = futures::poll!(justification_port.next()) {
match justification {
Some(ImportJustification(who, hash, number, justification)) =>
worker.import_justification(who, hash, number, justification),
Some(ImportJustification(who, hash, number, justification)) => {
worker
.import_justification(who, hash, number, justification)
.await
}
None => {
log::debug!(
target: "block-import",
"Stopping block import because justification channel was closed!",
);
return
},
return;
}
}
}
@@ -278,7 +281,7 @@ impl<B: BlockT> BlockImportWorker<B> {
(future, justification_sender, block_import_sender)
}
fn import_justification(
async fn import_justification(
&mut self,
who: Origin,
hash: B::Hash,
@@ -286,8 +289,11 @@ impl<B: BlockT> BlockImportWorker<B> {
justification: Justification,
) {
let started = wasm_timer::Instant::now();
let success = self.justification_import.as_mut().map(|justification_import| {
justification_import.import_justification(hash, number, justification)
let success = match self.justification_import.as_mut() {
Some(justification_import) => justification_import
.import_justification(hash, number, justification)
.await
.map_err(|e| {
debug!(
target: "sync",
@@ -298,14 +304,19 @@ impl<B: BlockT> BlockImportWorker<B> {
who,
);
e
}).is_ok()
}).unwrap_or(false);
})
.is_ok(),
None => false,
};
if let Some(metrics) = self.metrics.as_ref() {
metrics.justification_import_time.observe(started.elapsed().as_secs_f64());
metrics
.justification_import_time
.observe(started.elapsed().as_secs_f64());
}
self.result_sender.justification_imported(who, &hash, number, success);
self.result_sender
.justification_imported(who, &hash, number, success);
}
}
@@ -472,10 +483,15 @@ mod tests {
}
}
#[async_trait::async_trait]
impl JustificationImport<Block> for () {
type Error = crate::Error;
fn import_justification(
async fn on_start(&mut self) -> Vec<(Hash, BlockNumber)> {
Vec::new()
}
async fn import_justification(
&mut self,
_hash: Hash,
_number: BlockNumber,
@@ -33,23 +33,24 @@ use sp_runtime::traits::{Block as BlockT, NumberFor};
/// some implementations.
///
/// Non-deterministically finalizing chains may only use the `_authoring` functions.
#[async_trait::async_trait]
pub trait SelectChain<Block: BlockT>: Sync + Send + Clone {
/// Get all leaves of the chain: block hashes that have no children currently.
/// Get all leaves of the chain, i.e. block hashes that have no children currently.
/// Leaves that can never be finalized will not be returned.
fn leaves(&self) -> Result<Vec<<Block as BlockT>::Hash>, Error>;
async fn leaves(&self) -> Result<Vec<<Block as BlockT>::Hash>, Error>;
/// Among those `leaves` deterministically pick one chain as the generally
/// best chain to author new blocks upon and probably finalize.
fn best_chain(&self) -> Result<<Block as BlockT>::Header, Error>;
/// best chain to author new blocks upon and probably (but not necessarily)
/// finalize.
async fn best_chain(&self) -> Result<<Block as BlockT>::Header, Error>;
/// Get the best descendent of `target_hash` that we should attempt to
/// finalize next, if any. It is valid to return the given `target_hash`
/// itself if no better descendent exists.
fn finality_target(
async fn finality_target(
&self,
target_hash: <Block as BlockT>::Hash,
_maybe_max_number: Option<NumberFor<Block>>
_maybe_max_number: Option<NumberFor<Block>>,
) -> Result<Option<<Block as BlockT>::Hash>, Error> {
Ok(Some(target_hash))
}