mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-01 02:11:01 +00:00
make SelectChain async (#9128)
* make SelectChain async * make JustificationImport async
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user