@@ -27,7 +27,13 @@ use finality_grandpa::voter_set::VoterSet;
2727use sp_consensus_grandpa:: { AuthorityId , AuthoritySignature , SetId } ;
2828use sp_runtime:: { traits:: Header as HeaderT , RuntimeDebug } ;
2929use sp_std:: {
30- collections:: { btree_map:: BTreeMap , btree_set:: BTreeSet } ,
30+ collections:: {
31+ btree_map:: {
32+ BTreeMap ,
33+ Entry :: { Occupied , Vacant } ,
34+ } ,
35+ btree_set:: BTreeSet ,
36+ } ,
3137 prelude:: * ,
3238} ;
3339
@@ -44,23 +50,40 @@ pub struct AncestryChain<Header: HeaderT> {
4450 /// We expect all forks in the ancestry chain to be descendants of base.
4551 base : HeaderId < Header :: Hash , Header :: Number > ,
4652 /// Header hash => parent header hash mapping.
47- pub parents : BTreeMap < Header :: Hash , Header :: Hash > ,
53+ parents : BTreeMap < Header :: Hash , Header :: Hash > ,
4854 /// Hashes of headers that were not visited by `ancestry()`.
49- pub unvisited : BTreeSet < Header :: Hash > ,
55+ unvisited : BTreeSet < Header :: Hash > ,
5056}
5157
5258impl < Header : HeaderT > AncestryChain < Header > {
53- /// Create new ancestry chain.
54- pub fn new ( justification : & GrandpaJustification < Header > ) -> AncestryChain < Header > {
59+ /// Creates a new instance of `AncestryChain` starting from a `GrandpaJustification`.
60+ ///
61+ /// Returns the `AncestryChain` and a `Vec` containing the `votes_ancestries` entries
62+ /// that were ignored when creating it, because they are duplicates.
63+ pub fn new (
64+ justification : & GrandpaJustification < Header > ,
65+ ) -> ( AncestryChain < Header > , Vec < usize > ) {
5566 let mut parents = BTreeMap :: new ( ) ;
5667 let mut unvisited = BTreeSet :: new ( ) ;
57- for ancestor in & justification. votes_ancestries {
68+ let mut ignored_idxs = Vec :: new ( ) ;
69+ for ( idx, ancestor) in justification. votes_ancestries . iter ( ) . enumerate ( ) {
5870 let hash = ancestor. hash ( ) ;
59- let parent_hash = * ancestor. parent_hash ( ) ;
60- parents. insert ( hash, parent_hash) ;
61- unvisited. insert ( hash) ;
71+ match parents. entry ( hash) {
72+ Occupied ( _) => {
73+ ignored_idxs. push ( idx) ;
74+ } ,
75+ Vacant ( entry) => {
76+ entry. insert ( * ancestor. parent_hash ( ) ) ;
77+ unvisited. insert ( hash) ;
78+ } ,
79+ }
6280 }
63- AncestryChain { base : justification. commit_target_id ( ) , parents, unvisited }
81+ ( AncestryChain { base : justification. commit_target_id ( ) , parents, unvisited } , ignored_idxs)
82+ }
83+
84+ /// Returns the hash of a block's parent if the block is present in the ancestry.
85+ pub fn parent_hash_of ( & self , hash : & Header :: Hash ) -> Option < & Header :: Hash > {
86+ self . parents . get ( hash)
6487 }
6588
6689 /// Returns a route if the precommit target block is a descendant of the `base` block.
@@ -80,7 +103,7 @@ impl<Header: HeaderT> AncestryChain<Header> {
80103 break
81104 }
82105
83- current_hash = match self . parents . get ( & current_hash) {
106+ current_hash = match self . parent_hash_of ( & current_hash) {
84107 Some ( parent_hash) => {
85108 let is_visited_before = self . unvisited . get ( & current_hash) . is_none ( ) ;
86109 if is_visited_before {
@@ -117,6 +140,8 @@ pub enum Error {
117140 InvalidAuthorityList ,
118141 /// Justification is finalizing unexpected header.
119142 InvalidJustificationTarget ,
143+ /// The justification contains duplicate headers in its `votes_ancestries` field.
144+ DuplicateVotesAncestries ,
120145 /// Error validating a precommit
121146 Precommit ( PrecommitError ) ,
122147 /// The cumulative weight of all votes in the justification is not enough to justify commit
@@ -168,6 +193,12 @@ enum IterationFlow {
168193
169194/// Verification callbacks.
170195trait JustificationVerifier < Header : HeaderT > {
196+ /// Called when there are duplicate headers in the votes ancestries.
197+ fn process_duplicate_votes_ancestries (
198+ & mut self ,
199+ duplicate_votes_ancestries : Vec < usize > ,
200+ ) -> Result < ( ) , Error > ;
201+
171202 fn process_redundant_vote (
172203 & mut self ,
173204 precommit_idx : usize ,
@@ -216,10 +247,14 @@ trait JustificationVerifier<Header: HeaderT> {
216247 }
217248
218249 let threshold = context. voter_set . threshold ( ) . get ( ) ;
219- let mut chain = AncestryChain :: new ( justification) ;
250+ let ( mut chain, ignored_idxs ) = AncestryChain :: new ( justification) ;
220251 let mut signature_buffer = Vec :: new ( ) ;
221252 let mut cumulative_weight = 0u64 ;
222253
254+ if !ignored_idxs. is_empty ( ) {
255+ self . process_duplicate_votes_ancestries ( ignored_idxs) ?;
256+ }
257+
223258 for ( precommit_idx, signed) in justification. commit . precommits . iter ( ) . enumerate ( ) {
224259 if cumulative_weight >= threshold {
225260 let action =
0 commit comments