use crate::justification::{
verification::{Error, JustificationVerifier, PrecommitError},
GrandpaJustification,
};
use crate::justification::verification::{
IterationFlow, JustificationVerificationContext, SignedPrecommit,
};
use sp_consensus_grandpa::AuthorityId;
use sp_runtime::traits::Header as HeaderT;
use sp_std::{collections::btree_set::BTreeSet, prelude::*, vec, vec::Vec};
struct JustificationOptimizer<Header: HeaderT> {
votes: BTreeSet<AuthorityId>,
extra_precommits: Vec<usize>,
duplicate_votes_ancestries_idxs: Vec<usize>,
redundant_votes_ancestries: BTreeSet<Header::Hash>,
}
impl<Header: HeaderT> JustificationOptimizer<Header> {
fn optimize(self, justification: &mut GrandpaJustification<Header>) {
for invalid_precommit_idx in self.extra_precommits.into_iter().rev() {
justification.commit.precommits.remove(invalid_precommit_idx);
}
if !self.duplicate_votes_ancestries_idxs.is_empty() {
for idx in self.duplicate_votes_ancestries_idxs.iter().rev() {
justification.votes_ancestries.swap_remove(*idx);
}
}
if !self.redundant_votes_ancestries.is_empty() {
justification
.votes_ancestries
.retain(|header| !self.redundant_votes_ancestries.contains(&header.hash()))
}
}
}
impl<Header: HeaderT> JustificationVerifier<Header> for JustificationOptimizer<Header> {
fn process_duplicate_votes_ancestries(
&mut self,
duplicate_votes_ancestries: Vec<usize>,
) -> Result<(), Error> {
self.duplicate_votes_ancestries_idxs = duplicate_votes_ancestries.to_vec();
Ok(())
}
fn process_redundant_vote(
&mut self,
precommit_idx: usize,
) -> Result<IterationFlow, PrecommitError> {
self.extra_precommits.push(precommit_idx);
Ok(IterationFlow::Skip)
}
fn process_known_authority_vote(
&mut self,
precommit_idx: usize,
signed: &SignedPrecommit<Header>,
) -> Result<IterationFlow, PrecommitError> {
if self.votes.contains(&signed.id) {
self.extra_precommits.push(precommit_idx);
return Ok(IterationFlow::Skip)
}
Ok(IterationFlow::Run)
}
fn process_unknown_authority_vote(
&mut self,
precommit_idx: usize,
) -> Result<(), PrecommitError> {
self.extra_precommits.push(precommit_idx);
Ok(())
}
fn process_unrelated_ancestry_vote(
&mut self,
precommit_idx: usize,
) -> Result<IterationFlow, PrecommitError> {
self.extra_precommits.push(precommit_idx);
Ok(IterationFlow::Skip)
}
fn process_invalid_signature_vote(
&mut self,
precommit_idx: usize,
) -> Result<(), PrecommitError> {
self.extra_precommits.push(precommit_idx);
Ok(())
}
fn process_valid_vote(&mut self, signed: &SignedPrecommit<Header>) {
self.votes.insert(signed.id.clone());
}
fn process_redundant_votes_ancestries(
&mut self,
redundant_votes_ancestries: BTreeSet<Header::Hash>,
) -> Result<(), Error> {
self.redundant_votes_ancestries = redundant_votes_ancestries;
Ok(())
}
}
pub fn verify_and_optimize_justification<Header: HeaderT>(
finalized_target: (Header::Hash, Header::Number),
context: &JustificationVerificationContext,
justification: &mut GrandpaJustification<Header>,
) -> Result<(), Error> {
let mut optimizer = JustificationOptimizer {
votes: BTreeSet::new(),
extra_precommits: vec![],
duplicate_votes_ancestries_idxs: vec![],
redundant_votes_ancestries: Default::default(),
};
optimizer.verify_justification(finalized_target, context, justification)?;
optimizer.optimize(justification);
Ok(())
}