mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-01 01:01:01 +00:00
grandpa: make the VotingRule API async (#8101)
* grandpa: make the VotingRule api async * grandpa: add docs to VotingRuleResult * grandpa: formatting * grandpa: use async blocks Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com> * grandpa: expose VotingRuleResult * grandpa: revert some broken changes to async syntax * grandpa: use finality-grandpa v0.14.0 * grandpa: bump impl_version Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com>
This commit is contained in:
@@ -592,100 +592,6 @@ where
|
||||
fn ancestry(&self, base: Block::Hash, block: Block::Hash) -> Result<Vec<Block::Hash>, GrandpaError> {
|
||||
ancestry(&self.client, base, block)
|
||||
}
|
||||
|
||||
fn best_chain_containing(&self, block: Block::Hash) -> Option<(Block::Hash, NumberFor<Block>)> {
|
||||
// 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;
|
||||
}
|
||||
|
||||
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());
|
||||
let target;
|
||||
|
||||
let 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 = self.client.header(BlockId::Hash(*target_header.parent_hash())).ok()?
|
||||
.expect("Header known to exist after `finality_target` call; qed");
|
||||
}
|
||||
|
||||
target = target_header;
|
||||
&target
|
||||
} else {
|
||||
// otherwise just use the given best as the target
|
||||
&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.
|
||||
self.voting_rule
|
||||
.restrict_vote(&*self.client, &base_header, &best_header, target_header)
|
||||
.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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -733,6 +639,14 @@ where
|
||||
NumberFor<Block>: BlockNumberOps,
|
||||
{
|
||||
type Timer = Pin<Box<dyn Future<Output = Result<(), Self::Error>> + Send + Sync>>;
|
||||
type BestChain = Pin<
|
||||
Box<
|
||||
dyn Future<Output = Result<Option<(Block::Hash, NumberFor<Block>)>, Self::Error>>
|
||||
+ Send
|
||||
+ Sync
|
||||
>,
|
||||
>;
|
||||
|
||||
type Id = AuthorityId;
|
||||
type Signature = AuthoritySignature;
|
||||
|
||||
@@ -747,6 +661,119 @@ where
|
||||
|
||||
type Error = CommandOrError<Block::Hash, NumberFor<Block>>;
|
||||
|
||||
fn best_chain_containing(&self, block: Block::Hash) -> Self::BestChain {
|
||||
let find_best_chain = || {
|
||||
// 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;
|
||||
}
|
||||
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
||||
fn round_data(
|
||||
&self,
|
||||
round: RoundNumber,
|
||||
|
||||
Reference in New Issue
Block a user