99 rewards_certificate:: { NotarRewardCertificate , SkipRewardCertificate } ,
1010 vote:: Vote ,
1111 } ,
12- std:: collections:: BTreeMap ,
12+ std:: collections:: HashMap ,
1313} ;
1414
1515/// Builds a signature and bitmap suitable for creating a rewards certificate.
16- fn build_sig_bitmap ( votes : & BTreeMap < u16 , VoteMessage > ) -> Option < ( BLSSignature , Vec < u8 > ) > {
17- let max_rank = votes. last_key_value ( ) . map ( |( rank, _) | rank) . cloned ( ) ?;
16+ fn build_sig_bitmap (
17+ votes : & HashMap < u16 , VoteMessage > ,
18+ max_rank : u16 ,
19+ ) -> Option < ( BLSSignature , Vec < u8 > ) > {
1820 let mut bitmap = BitVec :: repeat ( false , max_rank as usize ) ;
1921 for vote in votes. keys ( ) {
2022 bitmap. set ( * vote as usize , true ) ;
@@ -29,20 +31,35 @@ fn build_sig_bitmap(votes: &BTreeMap<u16, VoteMessage>) -> Option<(BLSSignature,
2931}
3032
3133/// Per slot container for storing notar and skip votes for creating rewards certificates.
32- #[ derive( Default ) ]
3334pub ( super ) struct Entry {
34- /// map from validator rank to the skip vote.
35- skip : BTreeMap < u16 , VoteMessage > ,
36- /// notar votes are indexed by block id as different validators may vote for different blocks.
37- notar : BTreeMap < Hash , BTreeMap < u16 , VoteMessage > > ,
35+ /// Map from validator rank to the skip vote.
36+ skip : HashMap < u16 , VoteMessage > ,
37+ /// Largest ranked validator that voted skip.
38+ skip_max_rank : u16 ,
39+ /// Notar votes are indexed by block id as different validators may vote for different blocks.
40+ /// Per block id, store a map from validator rank to notar vote and also the largest ranked validator that voted notar.
41+ notar : HashMap < Hash , ( HashMap < u16 , VoteMessage > , u16 ) > ,
42+ /// Maximum number of validators for the slot this entry is working on.
43+ max_validators : usize ,
3844}
3945
4046impl Entry {
47+ /// Creates a new instance of [`Entry`].
48+ pub ( super ) fn new ( max_validators : usize ) -> Self {
49+ Self {
50+ skip : HashMap :: with_capacity ( max_validators) ,
51+ skip_max_rank : 0 ,
52+ // under normal operations, all validators should vote for a single block id, still allocate space for a few more to hopefully avoid allocations.
53+ notar : HashMap :: with_capacity ( 5 ) ,
54+ max_validators,
55+ }
56+ }
57+
4158 pub ( super ) fn wants_vote ( & self , vote : & VoteMessage ) -> bool {
4259 match vote. vote {
4360 Vote :: Skip ( _) => !self . skip . contains_key ( & vote. rank ) ,
4461 Vote :: Notarize ( notar) => {
45- let Some ( notar) = self . notar . get ( & notar. block_id ) else {
62+ let Some ( ( notar, _ ) ) = self . notar . get ( & notar. block_id ) else {
4663 return true ;
4764 } ;
4865 !notar. contains_key ( & vote. rank )
@@ -57,13 +74,16 @@ impl Entry {
5774 pub ( super ) fn add_vote ( & mut self , vote : VoteMessage ) {
5875 match vote. vote {
5976 Vote :: Notarize ( notar) => {
60- self . notar
77+ let ( map, max_rank) = self
78+ . notar
6179 . entry ( notar. block_id )
62- . or_default ( )
63- . insert ( vote. rank , vote) ;
80+ . or_insert ( ( HashMap :: with_capacity ( self . max_validators ) , 0 ) ) ;
81+ map. insert ( vote. rank , vote) ;
82+ * max_rank = ( * max_rank) . max ( vote. rank ) ;
6483 }
6584 Vote :: Skip ( _) => {
6685 self . skip . insert ( vote. rank , vote) ;
86+ self . skip_max_rank = self . skip_max_rank . max ( vote. rank ) ;
6787 }
6888 _ => ( ) ,
6989 }
@@ -76,29 +96,31 @@ impl Entry {
7696 Option < SkipRewardCertificate > ,
7797 Option < NotarRewardCertificate > ,
7898 ) {
79- let skip = build_sig_bitmap ( & self . skip ) . map ( |( signature, bitmap) | SkipRewardCertificate {
80- slot,
81- signature,
82- bitmap,
99+ let skip = build_sig_bitmap ( & self . skip , self . skip_max_rank ) . map ( |( signature, bitmap) | {
100+ SkipRewardCertificate {
101+ slot,
102+ signature,
103+ bitmap,
104+ }
83105 } ) ;
84106
85107 // we can only submit one notar rewards certificate but different validators may vote for different blocks and we cannot combine notar votes for different blocks together in one cert.
86108 // pick the block_id with most votes.
87109 // in practice, all validators should have voted for the same block otherwise, we have evidence of leader equivocation.
88110 // TODO: collect metrics for when equivocation is detected.
89111 let mut notar = None ;
90- for ( block_id, map) in & self . notar {
112+ for ( block_id, ( map, max_rank ) ) in & self . notar {
91113 match notar {
92- None => notar = Some ( ( block_id, map) ) ,
93- Some ( ( _, notar_map) ) => {
114+ None => notar = Some ( ( block_id, map, max_rank ) ) ,
115+ Some ( ( _, notar_map, _ ) ) => {
94116 if map. len ( ) > notar_map. len ( ) {
95- notar = Some ( ( block_id, map) ) ;
117+ notar = Some ( ( block_id, map, max_rank ) ) ;
96118 }
97119 }
98120 }
99121 }
100- let notar = notar. and_then ( |( block_id, votes) | {
101- build_sig_bitmap ( votes) . map ( |( signature, bitmap) | NotarRewardCertificate {
122+ let notar = notar. and_then ( |( block_id, votes, max_rank ) | {
123+ build_sig_bitmap ( votes, * max_rank ) . map ( |( signature, bitmap) | NotarRewardCertificate {
102124 slot,
103125 block_id : * block_id,
104126 signature,
0 commit comments