Line data Source code
1 : #include "ag_slot_state.h"
2 : #include "ag_vote_serde.h"
3 :
4 0 : #define AG_SAFE_TO_NOTAR_STATUS_SAFE_TO_NOTAR (0)
5 0 : #define AG_SAFE_TO_NOTAR_STATUS_MISSING_BLOCK (1)
6 0 : #define AG_SAFE_TO_NOTAR_STATUS_AWAITING_VOTES (2)
7 :
8 : static int
9 : block_hash_set_contains( ag_block_hash_set_t const * set,
10 0 : ag_block_hash_t const block_hash ) {
11 0 : for( ulong i=0UL; i<set->cnt; i++ ) {
12 0 : if( FD_LIKELY( !memcmp( set->hash[i], block_hash, sizeof(ag_block_hash_t) ) ) ) return 1;
13 0 : }
14 0 : return 0;
15 0 : }
16 :
17 : static void
18 : block_hash_set_insert( ag_block_hash_set_t * set,
19 0 : ag_block_hash_t const block_hash ) {
20 0 : if( FD_UNLIKELY( block_hash_set_contains( set, block_hash ) ) ) return;
21 0 : FD_TEST( set->cnt < AG_EQVOC_BLOCK_HASH_MAX );
22 0 : memcpy( set->hash[ set->cnt++ ], block_hash, sizeof(ag_block_hash_t) );
23 0 : }
24 :
25 : static void
26 : block_hash_set_remove( ag_block_hash_set_t * set,
27 0 : ag_block_hash_t const block_hash ) {
28 0 : for( ulong i=0UL; i<set->cnt; i++ ) {
29 0 : if( FD_LIKELY( !memcmp( set->hash[i], block_hash, sizeof(ag_block_hash_t) ) ) ) {
30 0 : set->cnt--;
31 0 : if( FD_UNLIKELY( i!=set->cnt ) ) memcpy( set->hash[i], set->hash[ set->cnt ], sizeof(ag_block_hash_t) );
32 0 : return;
33 0 : }
34 0 : }
35 0 : }
36 :
37 : static int
38 : verify_subtract_votes( ag_slot_state_t * self,
39 : ag_slot_states_t * states,
40 : uint kind,
41 : ag_block_hash_t const block_hash,
42 : fd_bls_agg_t * agg,
43 0 : fd_bls_set_t * bad ) {
44 0 : ag_epoch_info_t const * epoch_info = fd_ptr_if( self->slot>=states->next_epoch_slot, states->next_epoch_info, fd_ptr_if( self->slot>=states->curr_epoch_slot, states->curr_epoch_info, states->prev_epoch_info ) );
45 0 : ag_slot_votes_t * votes = &self->votes;
46 0 : ag_vote_stake_map_t * notar_hash_stake_map = states->notar_hash_stake_map;
47 0 : ag_vote_stake_map_t * notar_fallback_hash_stake_map = states->notar_fallback_hash_stake_map;
48 0 : ag_vote_stake_ele_t * notar_hash_stake_pool = states->notar_hash_stake_pool;
49 0 : ag_vote_stake_ele_t * notar_fallback_hash_stake_pool = states->notar_fallback_hash_stake_pool;
50 :
51 0 : fd_bls_sig_t sigs[ AG_VAT_MAX ];
52 0 : fd_bls_set_t set [ fd_bls_set_word_cnt ];
53 0 : switch( kind ) {
54 0 : case AG_VOTE_KIND_SKIP:
55 0 : case AG_VOTE_KIND_SKIP_FALLBACK:
56 0 : case AG_VOTE_KIND_FINAL:
57 0 : ag_slot_state_sigs( self, states, kind, NULL, agg->set, sigs );
58 0 : break;
59 0 : case AG_VOTE_KIND_NOTAR:
60 0 : fd_bls_set_null( set );
61 0 : for( ulong rank = fd_bls_set_const_iter_init( votes->notar_set );
62 0 : !fd_bls_set_const_iter_done( rank );
63 0 : rank = fd_bls_set_const_iter_next( votes->notar_set, rank ) ) {
64 0 : if( FD_LIKELY( !memcmp( ag_vote_stake_pool_ele( notar_hash_stake_pool, votes->notar_hash_stake_idx[ rank ] )->block_id.hash, block_hash, sizeof(ag_block_hash_t) ) ) ) fd_bls_set_insert( set, rank );
65 0 : }
66 0 : ag_slot_state_sigs( self, states, kind, block_hash, set, sigs );
67 0 : fd_bls_agg_null( agg );
68 0 : fd_bls_agg_construct( agg, epoch_info->pubkeys, sigs, set );
69 0 : break;
70 0 : case AG_VOTE_KIND_NOTAR_FALLBACK:
71 0 : fd_bls_set_null( set );
72 0 : for( ulong rank=0UL; rank<epoch_info->validator_cnt; rank++ ) {
73 0 : for( ulong j=0UL; j<votes->notar_fallback_sig_cnt[ rank ]; j++ ) {
74 0 : if( FD_LIKELY( !memcmp( ag_vote_stake_pool_ele( notar_fallback_hash_stake_pool, votes->notar_fallback_hash_stake_idx[ rank ][ j ] )->block_id.hash, block_hash, sizeof(ag_block_hash_t) ) ) ) fd_bls_set_insert( set, rank );
75 0 : }
76 0 : }
77 0 : ag_slot_state_sigs( self, states, kind, block_hash, set, sigs );
78 0 : fd_bls_agg_null( agg );
79 0 : fd_bls_agg_construct( agg, epoch_info->pubkeys, sigs, set );
80 0 : break;
81 0 : default: FD_LOG_CRIT(( "unreachable" ));
82 0 : }
83 :
84 0 : uchar msg[ AG_VOTE_SIGNING_SER_MAX ];
85 0 : ulong msg_sz = ag_vote_signing_ser( kind, self->slot, block_hash, self->shred_version, msg );
86 0 : states->bls_verify_cnt++;
87 0 : int err = fd_bls_agg_verify_subtract( agg, msg, msg_sz, epoch_info->pubkeys, sigs, bad );
88 0 : if( FD_LIKELY( !err && fd_bls_set_is_null( bad ) ) ) return 1;
89 :
90 0 : ag_block_id_t key;
91 0 : ulong id = ULONG_MAX;
92 0 : switch( kind ) {
93 0 : case AG_VOTE_KIND_NOTAR: key = ag_block_id( self->slot, block_hash ); id = ag_vote_stake_map_idx_query_const( notar_hash_stake_map, &key, ULONG_MAX, notar_hash_stake_pool ); break;
94 0 : case AG_VOTE_KIND_NOTAR_FALLBACK: key = ag_block_id( self->slot, block_hash ); id = ag_vote_stake_map_idx_query_const( notar_fallback_hash_stake_map, &key, ULONG_MAX, notar_fallback_hash_stake_pool ); break;
95 0 : default: break;
96 0 : }
97 :
98 0 : for( ulong rank = fd_bls_set_const_iter_init( bad );
99 0 : !fd_bls_set_const_iter_done( rank );
100 0 : rank = fd_bls_set_const_iter_next( bad, rank ) ) {
101 0 : ulong stake = ag_epoch_info_validator( epoch_info, rank )->stake;
102 0 : switch( kind ) {
103 0 : case AG_VOTE_KIND_NOTAR:
104 0 : ag_vote_stake_pool_ele( notar_hash_stake_pool, id )->stake -= stake;
105 0 : votes->notar_or_skip_stake -= stake;
106 0 : fd_bls_set_remove( votes->notar_set, rank );
107 0 : fd_bls_sig_pool_idx_release( states->bls_sig_pool, votes->notar_sig_idx[ rank ] );
108 0 : break;
109 0 : case AG_VOTE_KIND_NOTAR_FALLBACK: {
110 0 : ulong j;
111 0 : for( j=0UL; j<votes->notar_fallback_sig_cnt[ rank ]; j++ ) {
112 0 : if( FD_UNLIKELY( votes->notar_fallback_hash_stake_idx[ rank ][ j ]==id ) ) break;
113 0 : }
114 0 : FD_CHECK_CRIT( j<votes->notar_fallback_sig_cnt[ rank ], "invariant violation" );
115 0 : ag_vote_stake_pool_ele( notar_fallback_hash_stake_pool, id )->stake -= stake;
116 0 : fd_bls_sig_pool_idx_release( states->bls_sig_pool, votes->notar_fallback_sig_idx[ rank ][ j ] );
117 0 : votes->notar_fallback_sig_cnt[ rank ]--;
118 0 : votes->notar_fallback_sig_idx [ rank ][ j ] = votes->notar_fallback_sig_idx [ rank ][ votes->notar_fallback_sig_cnt[ rank ] ];
119 0 : votes->notar_fallback_hash_stake_idx[ rank ][ j ] = votes->notar_fallback_hash_stake_idx[ rank ][ votes->notar_fallback_sig_cnt[ rank ] ];
120 0 : break;
121 0 : }
122 0 : case AG_VOTE_KIND_SKIP:
123 0 : votes->skip_stake -= stake;
124 0 : votes->notar_or_skip_stake -= stake;
125 0 : fd_bls_sig_pool_idx_release( states->bls_sig_pool, votes->skip_sig_idx[ rank ] );
126 0 : break;
127 0 : case AG_VOTE_KIND_SKIP_FALLBACK:
128 0 : votes->skip_fallback_stake -= stake;
129 0 : fd_bls_sig_pool_idx_release( states->bls_sig_pool, votes->skip_fallback_sig_idx[ rank ] );
130 0 : break;
131 0 : case AG_VOTE_KIND_FINAL:
132 0 : votes->finalize_stake -= stake;
133 0 : fd_bls_sig_pool_idx_release( states->bls_sig_pool, votes->finalize_sig_idx[ rank ] );
134 0 : break;
135 0 : default:
136 0 : FD_LOG_CRIT(( "unreachable" ));
137 0 : }
138 0 : }
139 :
140 0 : if( FD_UNLIKELY( kind==AG_VOTE_KIND_NOTAR && fd_bls_set_is_null( agg->set ) ) ) {
141 0 : ulong * link = &votes->notar_hash_stake_head;
142 0 : ulong j;
143 0 : for( j=0UL; j<votes->notar_hash_stake_cnt; j++ ) {
144 0 : if( FD_UNLIKELY( *link==id ) ) break;
145 0 : link = &ag_vote_stake_pool_ele( notar_hash_stake_pool, *link )->prev;
146 0 : }
147 0 : FD_CHECK_CRIT( j<votes->notar_hash_stake_cnt, "invariant violation" );
148 0 : *link = ag_vote_stake_pool_ele( notar_hash_stake_pool, id )->prev;
149 0 : votes->notar_hash_stake_cnt--;
150 0 : ag_vote_stake_map_idx_remove( notar_hash_stake_map, &key, ULONG_MAX, notar_hash_stake_pool );
151 0 : ag_vote_stake_pool_idx_release( notar_hash_stake_pool, id );
152 0 : }
153 0 : if( FD_UNLIKELY( kind==AG_VOTE_KIND_NOTAR_FALLBACK && fd_bls_set_is_null( agg->set ) ) ) {
154 0 : ulong * link = &votes->notar_fallback_hash_stake_head;
155 0 : ulong j;
156 0 : for( j=0UL; j<votes->notar_fallback_hash_stake_cnt; j++ ) {
157 0 : if( FD_UNLIKELY( *link==id ) ) break;
158 0 : link = &ag_vote_stake_pool_ele( notar_fallback_hash_stake_pool, *link )->prev;
159 0 : }
160 0 : FD_CHECK_CRIT( j<votes->notar_fallback_hash_stake_cnt, "invariant violation" );
161 0 : *link = ag_vote_stake_pool_ele( notar_fallback_hash_stake_pool, id )->prev;
162 0 : votes->notar_fallback_hash_stake_cnt--;
163 0 : ag_vote_stake_map_idx_remove( notar_fallback_hash_stake_map, &key, ULONG_MAX, notar_fallback_hash_stake_pool );
164 0 : ag_vote_stake_pool_idx_release( notar_fallback_hash_stake_pool, id );
165 0 : }
166 0 : if( FD_UNLIKELY( kind==AG_VOTE_KIND_NOTAR ) ) {
167 0 : votes->top_notar_stake = 0UL;
168 0 : ulong i = votes->notar_hash_stake_head;
169 0 : for( ulong j=0UL; j<votes->notar_hash_stake_cnt; j++ ) {
170 0 : if( FD_LIKELY( ag_vote_stake_pool_ele( notar_hash_stake_pool, i )->stake>votes->top_notar_stake ) ) { /* FIXME slow */
171 0 : votes->top_notar_stake = ag_vote_stake_pool_ele( notar_hash_stake_pool, i )->stake;
172 0 : memcpy( votes->top_notar_hash, ag_vote_stake_pool_ele( notar_hash_stake_pool, i )->block_id.hash, sizeof(ag_block_hash_t) );
173 0 : }
174 0 : i = ag_vote_stake_pool_ele( notar_hash_stake_pool, i )->prev;
175 0 : }
176 0 : }
177 0 : return !err;
178 0 : }
179 :
180 : static int
181 : check_safe_to_notar( ag_slot_state_t * self,
182 : ag_slot_states_t * states,
183 : ag_block_hash_t const block_hash,
184 0 : fd_bls_set_t * bad ) {
185 0 : ag_epoch_info_t const * epoch_info = fd_ptr_if( self->slot>=states->next_epoch_slot, states->next_epoch_info, fd_ptr_if( self->slot>=states->curr_epoch_slot, states->curr_epoch_info, states->prev_epoch_info ) );
186 0 : ag_slot_votes_t * votes = &self->votes;
187 0 : ag_vote_stake_map_t * notar_hash_stake_map = states->notar_hash_stake_map;
188 0 : ag_vote_stake_ele_t * notar_hash_stake_pool = states->notar_hash_stake_pool;
189 0 : ag_block_id_t key = ag_block_id( self->slot, block_hash );
190 0 : ulong notar = ag_vote_stake_map_idx_query_const( notar_hash_stake_map, &key, ULONG_MAX, notar_hash_stake_pool );
191 0 : ulong notar_stake = notar!=ULONG_MAX ? ag_vote_stake_pool_ele( notar_hash_stake_pool, notar )->stake : 0UL;
192 0 : ulong skip_stake = votes->skip_stake;
193 :
194 0 : if( FD_UNLIKELY( !ag_epoch_info_is_weakest_quorum( epoch_info, notar_stake ) ) ) {
195 0 : return AG_SAFE_TO_NOTAR_STATUS_AWAITING_VOTES;
196 0 : }
197 0 : if( FD_UNLIKELY( !ag_epoch_info_is_weak_quorum( epoch_info, notar_stake ) && !ag_epoch_info_is_quorum( epoch_info, notar_stake + skip_stake ) ) ) {
198 0 : block_hash_set_insert( &self->pending_safe_to_notar, block_hash );
199 0 : return AG_SAFE_TO_NOTAR_STATUS_AWAITING_VOTES;
200 0 : }
201 :
202 0 : ag_parent_status_t const * parent = NULL;
203 0 : for( ulong i=0UL; i<self->parents_cnt; i++ ) {
204 0 : if( FD_LIKELY( !memcmp( self->parents[i].hash, block_hash, sizeof(ag_block_hash_t) ) ) ) { parent = &self->parents[i]; break; }
205 0 : }
206 0 : if( FD_UNLIKELY( !parent ) ) return AG_SAFE_TO_NOTAR_STATUS_MISSING_BLOCK;
207 0 : if( FD_UNLIKELY( parent->kind!=AG_PARENT_STATUS_CERTIFIED ) ) return AG_SAFE_TO_NOTAR_STATUS_AWAITING_VOTES;
208 :
209 0 : ulong own = self->own_rank;
210 :
211 0 : int safe_to_notar = 0;
212 0 : if( FD_LIKELY( own!=USHORT_MAX ) ) { /* must be staked */
213 0 : if( FD_UNLIKELY( fd_bls_set_test( votes->skip_agg.set, own ) ) ) safe_to_notar = 1;
214 0 : if( FD_UNLIKELY( fd_bls_set_test( votes->notar_set, own ) ) ) {
215 0 : if( FD_UNLIKELY( !memcmp( ag_vote_stake_pool_ele( notar_hash_stake_pool, votes->notar_hash_stake_idx[ own ] )->block_id.hash, block_hash, sizeof(ag_block_hash_t) ) ) ) return AG_SAFE_TO_NOTAR_STATUS_AWAITING_VOTES;
216 0 : safe_to_notar = 1;
217 0 : }
218 0 : }
219 0 : if( FD_UNLIKELY( !safe_to_notar ) ) {
220 0 : block_hash_set_insert( &self->pending_safe_to_notar, block_hash );
221 0 : return AG_SAFE_TO_NOTAR_STATUS_AWAITING_VOTES;
222 0 : }
223 :
224 0 : fd_bls_agg_t notar_agg[1];
225 0 : fd_bls_set_t bad_notar[ fd_bls_set_word_cnt ];
226 0 : verify_subtract_votes( self, states, AG_VOTE_KIND_NOTAR, block_hash, notar_agg, bad_notar );
227 0 : fd_bls_set_union( bad, bad, bad_notar );
228 0 : if( FD_LIKELY( !fd_bls_set_is_null( votes->skip_agg.set ) ) ) {
229 0 : fd_bls_set_t bad_skip[ fd_bls_set_word_cnt ];
230 0 : verify_subtract_votes( self, states, AG_VOTE_KIND_SKIP, NULL, &votes->skip_agg, bad_skip );
231 0 : fd_bls_set_union( bad, bad, bad_skip );
232 0 : }
233 0 : notar = ag_vote_stake_map_idx_query_const( notar_hash_stake_map, &key, ULONG_MAX, notar_hash_stake_pool );
234 0 : notar_stake = notar!=ULONG_MAX ? ag_vote_stake_pool_ele( notar_hash_stake_pool, notar )->stake : 0UL;
235 0 : skip_stake = votes->skip_stake;
236 0 : if( FD_UNLIKELY( !ag_epoch_info_is_weakest_quorum( epoch_info, notar_stake ) || ( !ag_epoch_info_is_weak_quorum( epoch_info, notar_stake ) && !ag_epoch_info_is_quorum( epoch_info, notar_stake + skip_stake ) ) ) ) {
237 0 : block_hash_set_insert( &self->pending_safe_to_notar, block_hash );
238 0 : return AG_SAFE_TO_NOTAR_STATUS_AWAITING_VOTES;
239 0 : }
240 :
241 0 : block_hash_set_remove( &self->pending_safe_to_notar, block_hash );
242 0 : block_hash_set_insert( &self->sent_safe_to_notar, block_hash );
243 0 : return AG_SAFE_TO_NOTAR_STATUS_SAFE_TO_NOTAR;
244 0 : }
245 :
246 : static int
247 : check_safe_to_skip( ag_slot_state_t * self,
248 : ag_slot_states_t * states,
249 0 : fd_bls_set_t * bad ) {
250 0 : ag_epoch_info_t const * epoch_info = fd_ptr_if( self->slot>=states->next_epoch_slot, states->next_epoch_info, fd_ptr_if( self->slot>=states->curr_epoch_slot, states->curr_epoch_info, states->prev_epoch_info ) );
251 0 : ag_slot_votes_t * votes = &self->votes;
252 0 : if( FD_LIKELY( self->sent_safe_to_skip
253 0 : || !ag_epoch_info_is_weak_quorum( epoch_info, votes->notar_or_skip_stake - votes->top_notar_stake )
254 0 : || self->own_rank==USHORT_MAX /* must be staked */ ) ) return 0;
255 0 : if( FD_LIKELY( !fd_bls_set_test( votes->notar_set, self->own_rank ) ) ) return 0;
256 :
257 0 : ag_vote_stake_ele_t * notar_hash_stake_pool = states->notar_hash_stake_pool;
258 0 : ulong i = votes->notar_hash_stake_head;
259 0 : for( ulong j=votes->notar_hash_stake_cnt; j>0UL; j-- ) {
260 0 : ag_block_id_t key = ag_vote_stake_pool_ele( notar_hash_stake_pool, i )->block_id;
261 0 : ulong prev = ag_vote_stake_pool_ele( notar_hash_stake_pool, i )->prev;
262 0 : fd_bls_agg_t notar_agg[1];
263 0 : fd_bls_set_t bad_notar[ fd_bls_set_word_cnt ];
264 0 : verify_subtract_votes( self, states, AG_VOTE_KIND_NOTAR, key.hash, notar_agg, bad_notar );
265 0 : fd_bls_set_union( bad, bad, bad_notar );
266 0 : i = prev;
267 0 : }
268 0 : if( FD_LIKELY( !fd_bls_set_is_null( votes->skip_agg.set ) ) ) {
269 0 : fd_bls_set_t bad_skip[ fd_bls_set_word_cnt ];
270 0 : verify_subtract_votes( self, states, AG_VOTE_KIND_SKIP, NULL, &votes->skip_agg, bad_skip );
271 0 : fd_bls_set_union( bad, bad, bad_skip );
272 0 : }
273 0 : if( FD_UNLIKELY( !ag_epoch_info_is_weak_quorum( epoch_info, votes->notar_or_skip_stake - votes->top_notar_stake ) || !fd_bls_set_test( votes->notar_set, self->own_rank ) ) ) return 0;
274 0 : self->sent_safe_to_skip = 1;
275 0 : return 1;
276 0 : }
277 :
278 : static int
279 : count_notar_stake( ag_slot_state_t * self,
280 : ag_slot_states_t * states,
281 : ag_vote_notar_t const * vote,
282 : ulong stake,
283 : ag_cert_t * out_cert_events,
284 : ulong * out_cert_event_cnt,
285 : ag_block_id_t * out_safe_to_notar,
286 : ulong * out_safe_to_notar_cnt,
287 : ulong * out_safe_to_skip,
288 : ulong * out_safe_to_skip_cnt,
289 : ag_block_id_t * out_repair_events,
290 : ulong * out_repair_event_cnt,
291 0 : fd_bls_set_t * bad ) {
292 0 : ag_epoch_info_t const * epoch_info = fd_ptr_if( self->slot>=states->next_epoch_slot, states->next_epoch_info, fd_ptr_if( self->slot>=states->curr_epoch_slot, states->curr_epoch_info, states->prev_epoch_info ) );
293 0 : ulong slot = vote->slot;
294 0 : uchar const * block_hash = vote->block_hash;
295 0 : ag_block_id_t key = ag_block_id( self->slot, block_hash );
296 0 : ulong rank = vote->rank;
297 0 : ushort shred_version = vote->shred_version;
298 :
299 0 : ag_slot_votes_t * votes = &self->votes;
300 0 : ag_vote_stake_map_t * notar_hash_stake_map = states->notar_hash_stake_map;
301 0 : ag_vote_stake_map_t * notar_fallback_hash_stake_map = states->notar_fallback_hash_stake_map;
302 0 : ag_vote_stake_ele_t * notar_hash_stake_pool = states->notar_hash_stake_pool;
303 0 : ag_vote_stake_ele_t * notar_fallback_hash_stake_pool = states->notar_fallback_hash_stake_pool;
304 0 : ulong notar = ag_vote_stake_map_idx_query_const( notar_hash_stake_map, &key, ULONG_MAX, notar_hash_stake_pool );
305 0 : if( FD_UNLIKELY( notar==ULONG_MAX ) ) {
306 0 : FD_TEST( ag_vote_stake_pool_free( notar_hash_stake_pool ) );
307 0 : notar = ag_vote_stake_pool_idx_acquire( notar_hash_stake_pool );
308 0 : ag_vote_stake_pool_ele( notar_hash_stake_pool, notar )->block_id = key;
309 0 : ag_vote_stake_pool_ele( notar_hash_stake_pool, notar )->stake = 0UL;
310 0 : ag_vote_stake_pool_ele( notar_hash_stake_pool, notar )->prev = votes->notar_hash_stake_cnt ? votes->notar_hash_stake_head : ag_vote_stake_pool_idx_null( notar_hash_stake_pool );
311 0 : ag_vote_stake_map_idx_insert( notar_hash_stake_map, notar, notar_hash_stake_pool );
312 0 : votes->notar_hash_stake_head = notar;
313 0 : votes->notar_hash_stake_cnt++;
314 0 : }
315 0 : votes->notar_hash_stake_idx[ rank ] = (uint)notar;
316 0 : ag_vote_stake_pool_ele( notar_hash_stake_pool, notar )->stake += stake;
317 :
318 0 : ulong notar_stake = ag_vote_stake_pool_ele( notar_hash_stake_pool, notar )->stake;
319 0 : votes->notar_or_skip_stake += stake;
320 0 : if( FD_LIKELY( notar_stake>votes->top_notar_stake ) ) {
321 0 : votes->top_notar_stake = notar_stake;
322 0 : memcpy( votes->top_notar_hash, block_hash, sizeof(ag_block_hash_t) );
323 0 : }
324 :
325 0 : if( FD_UNLIKELY( !block_hash_set_contains( &self->sent_safe_to_notar, block_hash ) ) ) {
326 0 : switch( check_safe_to_notar( self, states, block_hash, bad ) ) {
327 0 : case AG_SAFE_TO_NOTAR_STATUS_SAFE_TO_NOTAR:
328 0 : out_safe_to_notar[ (*out_safe_to_notar_cnt)++ ] = ag_block_id( slot, block_hash );
329 0 : break;
330 0 : case AG_SAFE_TO_NOTAR_STATUS_MISSING_BLOCK: {
331 0 : ulong j; for( j=0UL; j<*out_repair_event_cnt; j++ ) if( FD_UNLIKELY( !memcmp( out_repair_events[j].hash, block_hash, sizeof(ag_block_hash_t) ) ) ) break;
332 0 : if( FD_LIKELY( j==*out_repair_event_cnt ) ) out_repair_events[ (*out_repair_event_cnt)++ ] = ag_block_id( slot, block_hash );
333 0 : break;
334 0 : }
335 0 : default: break;
336 0 : }
337 0 : }
338 0 : if( FD_UNLIKELY( check_safe_to_skip( self, states, bad ) ) ) {
339 0 : out_safe_to_skip[ (*out_safe_to_skip_cnt)++ ] = slot;
340 0 : }
341 0 : if( FD_UNLIKELY( !fd_bls_set_is_null( bad ) ) ) {
342 0 : notar = ag_vote_stake_map_idx_query_const( notar_hash_stake_map, &key, ULONG_MAX, notar_hash_stake_pool );
343 0 : if( FD_UNLIKELY( notar==ULONG_MAX || !fd_bls_set_test( votes->notar_set, rank ) ) ) return 0;
344 0 : notar_stake = ag_vote_stake_pool_ele( notar_hash_stake_pool, notar )->stake;
345 0 : }
346 :
347 0 : ulong nf = ag_vote_stake_map_idx_query_const( notar_fallback_hash_stake_map, &key, ULONG_MAX, notar_fallback_hash_stake_pool );
348 0 : ulong nf_stake = nf!=ULONG_MAX ? ag_vote_stake_pool_ele( notar_fallback_hash_stake_pool, nf )->stake : 0UL;
349 :
350 0 : int notar_verified = 0;
351 0 : int notar_fallback_verified = 0;
352 0 : fd_bls_agg_t notar_agg[1];
353 0 : fd_bls_agg_t nf_agg[1];
354 :
355 0 : if( FD_UNLIKELY( ( ag_epoch_info_is_quorum ( epoch_info, nf_stake + notar_stake ) && !ag_slot_state_is_notar_fallback( self, block_hash ) ) ||
356 0 : ( ag_epoch_info_is_quorum ( epoch_info, notar_stake ) && self->certs.notar.slot==ULONG_MAX ) ||
357 0 : ( ag_epoch_info_is_strong_quorum( epoch_info, notar_stake ) && self->certs.fast_finalize.slot==ULONG_MAX ) ) ) {
358 0 : fd_bls_set_t bad_notar[ fd_bls_set_word_cnt ];
359 0 : notar_verified = verify_subtract_votes( self, states, AG_VOTE_KIND_NOTAR, block_hash, notar_agg, bad_notar );
360 0 : fd_bls_set_union( bad, bad, bad_notar );
361 0 : if( FD_UNLIKELY( fd_bls_set_test( bad_notar, rank ) ) ) return 0;
362 :
363 0 : notar_stake = ag_vote_stake_pool_ele( notar_hash_stake_pool, notar )->stake;
364 0 : if( FD_LIKELY( nf!=ULONG_MAX && !ag_slot_state_is_notar_fallback( self, block_hash ) ) ) {
365 0 : fd_bls_set_t bad_nf[ fd_bls_set_word_cnt ];
366 0 : notar_fallback_verified = verify_subtract_votes( self, states, AG_VOTE_KIND_NOTAR_FALLBACK, block_hash, nf_agg, bad_nf );
367 0 : fd_bls_set_union( bad, bad, bad_nf );
368 0 : }
369 0 : }
370 :
371 0 : if( FD_UNLIKELY( !ag_slot_state_is_notar_fallback( self, block_hash ) ) ) {
372 0 : ag_cert_notar_fallback_t cert = { .slot = slot, .shred_version = shred_version }; memcpy( cert.block_hash, block_hash, sizeof(ag_block_hash_t) );
373 :
374 0 : if( FD_LIKELY( notar_verified ) ) {
375 0 : cert.agg_notar = *notar_agg;
376 0 : cert.stake += ag_vote_stake_pool_ele( notar_hash_stake_pool, notar )->stake;
377 0 : }
378 0 : if( FD_LIKELY( notar_fallback_verified ) ) {
379 0 : cert.agg_notar_fallback = *nf_agg;
380 0 : cert.stake += ag_vote_stake_pool_ele( notar_fallback_hash_stake_pool, nf )->stake;
381 0 : }
382 0 : if( FD_LIKELY( ag_epoch_info_is_quorum( epoch_info, cert.stake ) ) ) {
383 0 : out_cert_events[( *out_cert_event_cnt )++] = ( ag_cert_t ){ .kind = AG_CERT_KIND_NOTAR_FALLBACK, .notar_fallback = cert };
384 0 : }
385 0 : }
386 :
387 0 : if( FD_UNLIKELY( notar_verified && ag_epoch_info_is_quorum( epoch_info, notar_stake ) && self->certs.notar.slot==ULONG_MAX ) ) {
388 0 : ag_cert_notar_t cert = { .slot = slot, .shred_version = shred_version, .stake = notar_stake, .agg = *notar_agg }; memcpy( cert.block_hash, block_hash, sizeof(ag_block_hash_t) );
389 0 : out_cert_events[ (*out_cert_event_cnt)++ ] = (ag_cert_t){ .kind = AG_CERT_KIND_NOTAR, .notar = cert };
390 0 : }
391 :
392 0 : if( FD_UNLIKELY( notar_verified && ag_epoch_info_is_strong_quorum( epoch_info, notar_stake ) && self->certs.fast_finalize.slot==ULONG_MAX ) ) {
393 0 : ag_cert_fast_final_t cert = { .slot = slot, .shred_version = shred_version, .stake = notar_stake, .agg = *notar_agg }; memcpy( cert.block_hash, block_hash, sizeof(ag_block_hash_t) );
394 0 : out_cert_events[ (*out_cert_event_cnt)++ ] = (ag_cert_t){ .kind = AG_CERT_KIND_FAST_FINAL, .fast_final = cert };
395 0 : }
396 :
397 0 : return 1;
398 0 : }
399 :
400 : static int
401 : count_notar_fallback_stake( ag_slot_state_t * self,
402 : ag_slot_states_t * states,
403 : ag_vote_notar_fallback_t const * vote,
404 : ulong stake,
405 : ag_cert_t * out_cert_events,
406 : ulong * out_cert_event_cnt,
407 0 : fd_bls_set_t * bad ) {
408 0 : ag_epoch_info_t const * epoch_info = fd_ptr_if( self->slot>=states->next_epoch_slot, states->next_epoch_info, fd_ptr_if( self->slot>=states->curr_epoch_slot, states->curr_epoch_info, states->prev_epoch_info ) );
409 0 : ulong slot = vote->slot;
410 0 : uchar const * block_hash = vote->block_hash;
411 0 : ag_block_id_t key = ag_block_id( self->slot, block_hash );
412 0 : ulong rank = vote->rank;
413 0 : ushort shred_version = vote->shred_version;
414 :
415 0 : ag_slot_votes_t * votes = &self->votes;
416 0 : ag_vote_stake_map_t * notar_hash_stake_map = states->notar_hash_stake_map;
417 0 : ag_vote_stake_map_t * notar_fallback_hash_stake_map = states->notar_fallback_hash_stake_map;
418 0 : ag_vote_stake_ele_t * notar_hash_stake_pool = states->notar_hash_stake_pool;
419 0 : ag_vote_stake_ele_t * notar_fallback_hash_stake_pool = states->notar_fallback_hash_stake_pool;
420 0 : ulong nf = ag_vote_stake_map_idx_query_const( notar_fallback_hash_stake_map, &key, ULONG_MAX, notar_fallback_hash_stake_pool );
421 0 : if( FD_UNLIKELY( nf==ULONG_MAX ) ) {
422 0 : FD_TEST( ag_vote_stake_pool_free( notar_fallback_hash_stake_pool ) );
423 0 : nf = ag_vote_stake_pool_idx_acquire( notar_fallback_hash_stake_pool );
424 0 : ag_vote_stake_pool_ele( notar_fallback_hash_stake_pool, nf )->block_id = key;
425 0 : ag_vote_stake_pool_ele( notar_fallback_hash_stake_pool, nf )->stake = 0UL;
426 0 : ag_vote_stake_pool_ele( notar_fallback_hash_stake_pool, nf )->prev = votes->notar_fallback_hash_stake_cnt ? votes->notar_fallback_hash_stake_head : ag_vote_stake_pool_idx_null( notar_fallback_hash_stake_pool );
427 0 : ag_vote_stake_map_idx_insert( notar_fallback_hash_stake_map, nf, notar_fallback_hash_stake_pool );
428 0 : votes->notar_fallback_hash_stake_head = nf;
429 0 : votes->notar_fallback_hash_stake_cnt++;
430 0 : }
431 0 : votes->notar_fallback_hash_stake_idx[ rank ][ votes->notar_fallback_sig_cnt[ rank ]-1UL ] = (uint)nf;
432 0 : ag_vote_stake_pool_ele( notar_fallback_hash_stake_pool, nf )->stake += stake;
433 :
434 0 : ulong notar = ag_vote_stake_map_idx_query_const( notar_hash_stake_map, &key, ULONG_MAX, notar_hash_stake_pool );
435 0 : ulong nf_stake = ag_vote_stake_pool_ele( notar_fallback_hash_stake_pool, nf )->stake;
436 0 : ulong notar_stake = notar!=ULONG_MAX ? ag_vote_stake_pool_ele( notar_hash_stake_pool, notar )->stake : 0UL;
437 0 : if( FD_UNLIKELY( ag_epoch_info_is_quorum( epoch_info, nf_stake + notar_stake ) && !ag_slot_state_is_notar_fallback( self, block_hash ) ) ) {
438 0 : fd_bls_agg_t nf_agg[1];
439 0 : fd_bls_agg_t notar_agg[1];
440 0 : fd_bls_set_t bad_nf[ fd_bls_set_word_cnt ];
441 0 : int nf_verified = verify_subtract_votes( self, states, AG_VOTE_KIND_NOTAR_FALLBACK, block_hash, nf_agg, bad_nf );
442 0 : fd_bls_set_union( bad, bad, bad_nf );
443 0 : if( FD_UNLIKELY( fd_bls_set_test( bad_nf, rank ) ) ) return 0;
444 0 : int notar_verified = 0;
445 0 : if( FD_LIKELY( notar!=ULONG_MAX ) ) {
446 0 : fd_bls_set_t bad_notar[ fd_bls_set_word_cnt ];
447 0 : notar_verified = verify_subtract_votes( self, states, AG_VOTE_KIND_NOTAR, block_hash, notar_agg, bad_notar );
448 0 : fd_bls_set_union( bad, bad, bad_notar );
449 0 : }
450 :
451 0 : ag_cert_notar_fallback_t cert = { .slot = slot, .shred_version = shred_version }; memcpy( cert.block_hash, block_hash, sizeof(ag_block_hash_t) );
452 :
453 0 : if( FD_LIKELY( notar_verified ) ) {
454 0 : cert.agg_notar = *notar_agg;
455 0 : cert.stake += ag_vote_stake_pool_ele( notar_hash_stake_pool, notar )->stake;
456 0 : }
457 0 : if( FD_LIKELY( nf_verified ) ) {
458 0 : cert.agg_notar_fallback = *nf_agg;
459 0 : cert.stake += ag_vote_stake_pool_ele( notar_fallback_hash_stake_pool, nf )->stake;
460 0 : }
461 0 : if( FD_LIKELY( ag_epoch_info_is_quorum( epoch_info, cert.stake ) ) ) {
462 0 : out_cert_events[ (*out_cert_event_cnt)++ ] = (ag_cert_t){ .kind = AG_CERT_KIND_NOTAR_FALLBACK, .notar_fallback = cert };
463 0 : }
464 0 : }
465 :
466 0 : return 1;
467 0 : }
468 :
469 : static int
470 : count_skip_stake( ag_slot_state_t * self,
471 : ag_slot_states_t * states,
472 : ag_vote_t const * vote,
473 : ulong stake,
474 : ag_cert_t * out_cert_events,
475 : ulong * out_cert_event_cnt,
476 : ag_block_id_t * out_safe_to_notar,
477 : ulong * out_safe_to_notar_cnt,
478 : ulong * out_safe_to_skip,
479 : ulong * out_safe_to_skip_cnt,
480 : ag_block_id_t * out_repair_events,
481 : ulong * out_repair_event_cnt,
482 0 : fd_bls_set_t * bad ) {
483 0 : ag_epoch_info_t const * epoch_info = fd_ptr_if( self->slot>=states->next_epoch_slot, states->next_epoch_info, fd_ptr_if( self->slot>=states->curr_epoch_slot, states->curr_epoch_info, states->prev_epoch_info ) );
484 0 : int fallback = vote->kind==AG_VOTE_KIND_SKIP_FALLBACK;
485 0 : ulong slot = ag_vote_slot( vote );
486 0 : ulong rank = ag_vote_rank( vote );
487 0 : ushort shred_version = ag_vote_shred_version( vote );
488 0 : fd_bls_sig_t const * sig = ag_vote_sig( vote );
489 0 : fd_bls_pub_t const * pub = ag_epoch_info_pubkey( epoch_info, rank );
490 :
491 0 : ag_slot_votes_t * votes = &self->votes;
492 0 : if( FD_UNLIKELY( fallback ) ) { votes->skip_fallback_stake += stake; blst_p1_add_or_double_affine( &votes->skip_fallback_agg.pub, &votes->skip_fallback_agg.pub, pub ); blst_p2_add_or_double_affine( &votes->skip_fallback_agg.sig, &votes->skip_fallback_agg.sig, sig ); fd_bls_set_insert( votes->skip_fallback_agg.set, rank ); }
493 0 : else { votes->skip_stake += stake; blst_p1_add_or_double_affine( &votes->skip_agg.pub, &votes->skip_agg.pub, pub ); blst_p2_add_or_double_affine( &votes->skip_agg.sig, &votes->skip_agg.sig, sig ); fd_bls_set_insert( votes->skip_agg.set, rank ); }
494 :
495 0 : ag_block_hash_set_t pending = self->pending_safe_to_notar;
496 0 : for( ulong i=0UL; i<pending.cnt; i++ ) {
497 0 : if( FD_UNLIKELY( block_hash_set_contains( &self->sent_safe_to_notar, pending.hash[i] ) ) ) continue;
498 0 : switch( check_safe_to_notar( self, states, pending.hash[i], bad ) ) {
499 0 : case AG_SAFE_TO_NOTAR_STATUS_SAFE_TO_NOTAR:
500 0 : out_safe_to_notar[ (*out_safe_to_notar_cnt)++ ] = ag_block_id( slot, pending.hash[i] );
501 0 : break;
502 0 : case AG_SAFE_TO_NOTAR_STATUS_MISSING_BLOCK: {
503 0 : ulong j; for( j=0UL; j<*out_repair_event_cnt; j++ ) if( FD_UNLIKELY( !memcmp( out_repair_events[j].hash, pending.hash[i], sizeof(ag_block_hash_t) ) ) ) break;
504 0 : if( FD_LIKELY( j==*out_repair_event_cnt ) ) out_repair_events[ (*out_repair_event_cnt)++ ] = ag_block_id( slot, pending.hash[i] );
505 0 : break;
506 0 : }
507 0 : default:
508 0 : break;
509 0 : }
510 0 : }
511 :
512 0 : if( FD_UNLIKELY( check_safe_to_skip( self, states, bad ) ) ) {
513 0 : out_safe_to_skip[ (*out_safe_to_skip_cnt)++ ] = slot;
514 0 : }
515 0 : if( FD_UNLIKELY( !fd_bls_set_is_null( bad ) && !fd_bls_set_test( fallback ? votes->skip_fallback_agg.set : votes->skip_agg.set, rank ) ) ) return 0;
516 :
517 0 : ulong total_skip_stake = votes->skip_stake + votes->skip_fallback_stake;
518 :
519 0 : int skip_verified = 0;
520 0 : int skip_fallback_verified = 0;
521 0 : if( FD_UNLIKELY( ag_epoch_info_is_quorum( epoch_info, total_skip_stake ) && self->certs.skip.slot==ULONG_MAX ) ) {
522 0 : if( FD_LIKELY( !fd_bls_set_is_null( votes->skip_agg.set ) ) ) {
523 0 : fd_bls_set_t bad_skip[ fd_bls_set_word_cnt ];
524 0 : skip_verified = verify_subtract_votes( self, states, AG_VOTE_KIND_SKIP, NULL, &votes->skip_agg, bad_skip );
525 0 : fd_bls_set_union( bad, bad, bad_skip );
526 0 : }
527 0 : if( FD_LIKELY( !fd_bls_set_is_null( votes->skip_fallback_agg.set ) ) ) {
528 0 : fd_bls_set_t bad_sf[ fd_bls_set_word_cnt ];
529 0 : skip_fallback_verified = verify_subtract_votes( self, states, AG_VOTE_KIND_SKIP_FALLBACK, NULL, &votes->skip_fallback_agg, bad_sf );
530 0 : fd_bls_set_union( bad, bad, bad_sf );
531 0 : }
532 0 : if( FD_UNLIKELY( !fd_bls_set_test( fallback ? votes->skip_fallback_agg.set : votes->skip_agg.set, rank ) ) ) return 0;
533 :
534 0 : ag_cert_skip_t cert = { .slot = slot, .shred_version = shred_version };
535 :
536 0 : if( FD_LIKELY( skip_verified ) ) {
537 0 : cert.agg_skip = votes->skip_agg;
538 0 : cert.stake += votes->skip_stake;
539 0 : }
540 0 : if( FD_LIKELY( skip_fallback_verified ) ) {
541 0 : cert.agg_skip_fallback = votes->skip_fallback_agg;
542 0 : cert.stake += votes->skip_fallback_stake;
543 0 : }
544 0 : if( FD_LIKELY( ag_epoch_info_is_quorum( epoch_info, cert.stake ) ) ) {
545 0 : out_cert_events[ (*out_cert_event_cnt)++ ] = (ag_cert_t){ .kind = AG_CERT_KIND_SKIP, .skip = cert };
546 0 : }
547 0 : }
548 :
549 0 : return 1;
550 0 : }
551 :
552 : static int
553 : count_finalize_stake( ag_slot_state_t * self,
554 : ag_slot_states_t * states,
555 : ag_vote_final_t const * vote,
556 : ulong stake,
557 : ag_cert_t * out_cert_events,
558 : ulong * out_cert_event_cnt,
559 0 : fd_bls_set_t * bad ) {
560 0 : ag_epoch_info_t const * epoch_info = fd_ptr_if( self->slot>=states->next_epoch_slot, states->next_epoch_info, fd_ptr_if( self->slot>=states->curr_epoch_slot, states->curr_epoch_info, states->prev_epoch_info ) );
561 0 : ulong slot = vote->slot;
562 0 : ulong rank = vote->rank;
563 0 : ushort shred_version = vote->shred_version;
564 0 : fd_bls_sig_t const * sig = &vote->sig;
565 0 : fd_bls_pub_t const * pub = ag_epoch_info_pubkey( epoch_info, rank );
566 :
567 0 : ag_slot_votes_t * votes = &self->votes;
568 0 : votes->finalize_stake += stake;
569 0 : blst_p1_add_or_double_affine( &votes->finalize_agg.pub, &votes->finalize_agg.pub, pub );
570 0 : blst_p2_add_or_double_affine( &votes->finalize_agg.sig, &votes->finalize_agg.sig, sig );
571 0 : fd_bls_set_insert( votes->finalize_agg.set, rank );
572 0 : if( FD_UNLIKELY( ag_epoch_info_is_quorum( epoch_info, votes->finalize_stake ) && self->certs.finalize.slot==ULONG_MAX ) ) {
573 0 : fd_bls_set_t bad_final[ fd_bls_set_word_cnt ];
574 0 : int finalize_verified = verify_subtract_votes( self, states, AG_VOTE_KIND_FINAL, NULL, &votes->finalize_agg, bad_final );
575 0 : fd_bls_set_union( bad, bad, bad_final );
576 0 : if( FD_UNLIKELY( fd_bls_set_test( bad_final, rank ) ) ) return 0;
577 0 : if( FD_UNLIKELY( !finalize_verified || !ag_epoch_info_is_quorum( epoch_info, votes->finalize_stake ) ) ) return 1;
578 0 : ag_cert_final_t cert = { .slot = slot, .shred_version = shred_version, .stake = votes->finalize_stake, .agg = votes->finalize_agg };
579 0 : out_cert_events[ (*out_cert_event_cnt)++ ] = (ag_cert_t){ .kind = AG_CERT_KIND_FINAL, .final = cert };
580 0 : }
581 :
582 0 : return 1;
583 0 : }
584 :
585 : void
586 : ag_slot_state_null( ag_slot_state_t * self,
587 0 : ag_slot_states_t * states ) {
588 0 : ag_slot_votes_t * votes = &self->votes;
589 0 : for( ulong rank = fd_bls_set_const_iter_init( votes->notar_set ); !fd_bls_set_const_iter_done( rank ); rank = fd_bls_set_const_iter_next( votes->notar_set, rank ) ) fd_bls_sig_pool_idx_release( states->bls_sig_pool, votes->notar_sig_idx [ rank ] );
590 0 : for( ulong rank = fd_bls_set_const_iter_init( votes->skip_agg.set ); !fd_bls_set_const_iter_done( rank ); rank = fd_bls_set_const_iter_next( votes->skip_agg.set, rank ) ) fd_bls_sig_pool_idx_release( states->bls_sig_pool, votes->skip_sig_idx [ rank ] );
591 0 : for( ulong rank = fd_bls_set_const_iter_init( votes->skip_fallback_agg.set ); !fd_bls_set_const_iter_done( rank ); rank = fd_bls_set_const_iter_next( votes->skip_fallback_agg.set, rank ) ) fd_bls_sig_pool_idx_release( states->bls_sig_pool, votes->skip_fallback_sig_idx[ rank ] );
592 0 : for( ulong rank = fd_bls_set_const_iter_init( votes->finalize_agg.set ); !fd_bls_set_const_iter_done( rank ); rank = fd_bls_set_const_iter_next( votes->finalize_agg.set, rank ) ) fd_bls_sig_pool_idx_release( states->bls_sig_pool, votes->finalize_sig_idx [ rank ] );
593 0 : for( ulong rank=0UL; rank<AG_VAT_MAX; rank++ ) {
594 0 : for( ulong j=0UL; j<votes->notar_fallback_sig_cnt[ rank ]; j++ ) fd_bls_sig_pool_idx_release( states->bls_sig_pool, votes->notar_fallback_sig_idx[ rank ][ j ] );
595 0 : }
596 0 : ulong notar = votes->notar_hash_stake_head;
597 0 : for( ulong i=0UL; i<votes->notar_hash_stake_cnt; i++ ) {
598 0 : ulong prev = ag_vote_stake_pool_ele( states->notar_hash_stake_pool, notar )->prev;
599 0 : ag_vote_stake_map_idx_remove( states->notar_hash_stake_map, &ag_vote_stake_pool_ele( states->notar_hash_stake_pool, notar )->block_id, ULONG_MAX, states->notar_hash_stake_pool );
600 0 : ag_vote_stake_pool_idx_release( states->notar_hash_stake_pool, notar );
601 0 : notar = prev;
602 0 : }
603 0 : votes->notar_hash_stake_cnt = 0UL;
604 0 : fd_bls_set_null( votes->notar_set );
605 0 : ulong nf = votes->notar_fallback_hash_stake_head;
606 0 : for( ulong i=0UL; i<votes->notar_fallback_hash_stake_cnt; i++ ) {
607 0 : ulong prev = ag_vote_stake_pool_ele( states->notar_fallback_hash_stake_pool, nf )->prev;
608 0 : ag_vote_stake_map_idx_remove( states->notar_fallback_hash_stake_map, &ag_vote_stake_pool_ele( states->notar_fallback_hash_stake_pool, nf )->block_id, ULONG_MAX, states->notar_fallback_hash_stake_pool );
609 0 : ag_vote_stake_pool_idx_release( states->notar_fallback_hash_stake_pool, nf );
610 0 : nf = prev;
611 0 : }
612 0 : votes->notar_fallback_hash_stake_cnt = 0UL;
613 0 : fd_memset( votes->notar_fallback_sig_cnt, 0, sizeof(votes->notar_fallback_sig_cnt) );
614 0 : votes->skip_stake = 0UL;
615 0 : fd_bls_agg_null( &votes->skip_agg );
616 0 : votes->skip_fallback_stake = 0UL;
617 0 : fd_bls_agg_null( &votes->skip_fallback_agg );
618 0 : votes->finalize_stake = 0UL;
619 0 : fd_bls_agg_null( &votes->finalize_agg );
620 0 : votes->notar_or_skip_stake = 0UL;
621 0 : votes->top_notar_stake = 0UL;
622 0 : fd_memset( votes->top_notar_hash, 0, sizeof(ag_block_hash_t) );
623 :
624 0 : self->certs.notar.slot = ULONG_MAX;
625 0 : self->certs.notar_fallback_cnt = 0UL;
626 0 : self->certs.skip.slot = ULONG_MAX;
627 0 : self->certs.fast_finalize.slot = ULONG_MAX;
628 0 : self->certs.finalize.slot = ULONG_MAX;
629 :
630 0 : self->parents_cnt = 0UL;
631 0 : self->pending_safe_to_notar.cnt = 0UL;
632 0 : self->sent_safe_to_notar.cnt = 0UL;
633 0 : self->sent_safe_to_skip = 0;
634 0 : }
635 :
636 : void
637 : ag_slot_state_set_own_rank( ag_slot_state_t * self,
638 0 : ulong own_rank ) {
639 0 : self->own_rank = own_rank;
640 0 : }
641 :
642 : FD_FN_PURE int
643 : ag_slot_state_is_notar_fallback( ag_slot_state_t const * self,
644 0 : ag_block_hash_t const block_hash ) {
645 0 : ag_slot_certs_t const * c = &self->certs;
646 0 : for( ulong i=0UL; i<c->notar_fallback_cnt; i++ ) {
647 0 : if( FD_LIKELY( !memcmp( c->notar_fallback[i].block_hash, block_hash, sizeof(ag_block_hash_t) ) ) ) return 1;
648 0 : }
649 0 : return 0;
650 0 : }
651 :
652 : FD_FN_PURE int
653 : ag_slot_state_is_notar_fallback_or_stronger( ag_slot_state_t const * self,
654 0 : ag_block_hash_t const block_hash ) {
655 0 : int has_notar_cert = self->certs.notar.slot !=ULONG_MAX && !memcmp( self->certs.notar.block_hash, block_hash, sizeof(ag_block_hash_t) );
656 0 : int has_fast_final_cert = self->certs.fast_finalize.slot!=ULONG_MAX && !memcmp( self->certs.fast_finalize.block_hash, block_hash, sizeof(ag_block_hash_t) );
657 0 : return has_notar_cert || has_fast_final_cert || ag_slot_state_is_notar_fallback( self, block_hash );
658 0 : }
659 :
660 : void
661 : ag_slot_state_add_cert( ag_slot_state_t * self,
662 0 : ag_cert_t const * cert ) {
663 0 : switch( cert->kind ) {
664 0 : case AG_CERT_KIND_NOTAR: {
665 0 : self->certs.notar = cert->notar;
666 0 : break;
667 0 : }
668 0 : case AG_CERT_KIND_NOTAR_FALLBACK: {
669 0 : ag_cert_notar_fallback_t const * n = &cert->notar_fallback;
670 0 : if( FD_LIKELY( !ag_slot_state_is_notar_fallback( self, n->block_hash ) ) ) {
671 0 : FD_CHECK_CRIT( self->certs.notar_fallback_cnt<AG_NOTAR_FALLBACK_CERT_MAX, "consensus safety violation" ); /* Lemma 48 */
672 0 : self->certs.notar_fallback[ self->certs.notar_fallback_cnt++ ] = *n;
673 0 : }
674 0 : break;
675 0 : }
676 0 : case AG_CERT_KIND_SKIP: {
677 0 : self->certs.skip = cert->skip;
678 0 : break;
679 0 : }
680 0 : case AG_CERT_KIND_FAST_FINAL: {
681 0 : self->certs.fast_finalize = cert->fast_final;
682 0 : break;
683 0 : }
684 0 : case AG_CERT_KIND_FINAL: {
685 0 : self->certs.finalize = cert->final;
686 0 : break;
687 0 : }
688 0 : default:
689 0 : FD_LOG_ERR(( "invalid cert kind %u", cert->kind ));
690 0 : }
691 0 : }
692 :
693 : int
694 : ag_slot_state_add_vote( ag_slot_state_t * self,
695 : ag_slot_states_t * states,
696 : ag_vote_t const * vote,
697 : ulong stake,
698 : ag_cert_t * out_cert_events,
699 : ulong * out_cert_event_cnt,
700 : ag_block_id_t * out_safe_to_notar,
701 : ulong * out_safe_to_notar_cnt,
702 : ulong * out_safe_to_skip,
703 : ulong * out_safe_to_skip_cnt,
704 : ag_block_id_t * out_repair_events,
705 : ulong * out_repair_event_cnt,
706 0 : fd_bls_set_t * bad ) {
707 0 : ulong slot = ag_vote_slot( vote );
708 0 : ulong rank = ag_vote_rank( vote );
709 :
710 0 : fd_bls_sig_t const * sig = ag_vote_sig( vote );
711 :
712 0 : FD_TEST( fd_bls_sig_pool_free( states->bls_sig_pool ) );
713 0 : uint sig_idx = (uint)fd_bls_sig_pool_idx_acquire( states->bls_sig_pool );
714 0 : fd_bls_sig_pool_ele( states->bls_sig_pool, sig_idx )->sig = *sig;
715 :
716 0 : *out_cert_event_cnt = 0UL; *out_safe_to_notar_cnt = 0UL; *out_safe_to_skip_cnt = 0UL; *out_repair_event_cnt = 0UL; fd_bls_set_null( bad );
717 0 : self->shred_version = ag_vote_shred_version( vote );
718 0 : int err;
719 0 : switch( vote->kind ) {
720 0 : case AG_VOTE_KIND_NOTAR:
721 0 : self->votes.notar_sig_idx[ rank ] = sig_idx;
722 0 : fd_bls_set_insert( self->votes.notar_set, rank );
723 0 : err = count_notar_stake( self, states, &vote->notar, stake, out_cert_events, out_cert_event_cnt, out_safe_to_notar, out_safe_to_notar_cnt, out_safe_to_skip, out_safe_to_skip_cnt, out_repair_events, out_repair_event_cnt, bad );
724 0 : break;
725 0 : case AG_VOTE_KIND_NOTAR_FALLBACK:
726 0 : FD_TEST( self->votes.notar_fallback_sig_cnt[ rank ]<AG_NOTAR_FALLBACK_VOTE_MAX );
727 0 : self->votes.notar_fallback_sig_idx[ rank ][ self->votes.notar_fallback_sig_cnt[ rank ] ] = sig_idx;
728 0 : self->votes.notar_fallback_sig_cnt[ rank ]++;
729 0 : err = count_notar_fallback_stake( self, states, &vote->notar_fallback, stake, out_cert_events, out_cert_event_cnt, bad );
730 0 : break;
731 0 : case AG_VOTE_KIND_SKIP:
732 0 : self->votes.skip_sig_idx[ rank ] = sig_idx;
733 0 : self->votes.notar_or_skip_stake += stake;
734 0 : err = count_skip_stake( self, states, vote, stake, out_cert_events, out_cert_event_cnt, out_safe_to_notar, out_safe_to_notar_cnt, out_safe_to_skip, out_safe_to_skip_cnt, out_repair_events, out_repair_event_cnt, bad );
735 0 : break;
736 0 : case AG_VOTE_KIND_SKIP_FALLBACK:
737 0 : self->votes.skip_fallback_sig_idx[ rank ] = sig_idx;
738 0 : err = count_skip_stake( self, states, vote, stake, out_cert_events, out_cert_event_cnt, out_safe_to_notar, out_safe_to_notar_cnt, out_safe_to_skip, out_safe_to_skip_cnt, out_repair_events, out_repair_event_cnt, bad );
739 0 : break;
740 0 : case AG_VOTE_KIND_FINAL:
741 0 : self->votes.finalize_sig_idx[ rank ] = sig_idx;
742 0 : err = count_finalize_stake( self, states, &vote->final, stake, out_cert_events, out_cert_event_cnt, bad );
743 0 : break;
744 0 : default:
745 0 : FD_LOG_CRIT(( "unreachable" ));
746 0 : }
747 :
748 0 : if( FD_UNLIKELY( rank==self->own_rank ) ) {
749 0 : ag_block_hash_set_t pending = self->pending_safe_to_notar;
750 0 : for( ulong i=0UL; i<pending.cnt; i++ ) {
751 0 : if( FD_UNLIKELY( block_hash_set_contains( &self->sent_safe_to_notar, pending.hash[i] ) ) ) continue;
752 0 : switch( check_safe_to_notar( self, states, pending.hash[i], bad ) ) {
753 0 : case AG_SAFE_TO_NOTAR_STATUS_SAFE_TO_NOTAR: out_safe_to_notar[ (*out_safe_to_notar_cnt)++ ] = ag_block_id( slot, pending.hash[i] ); break;
754 0 : case AG_SAFE_TO_NOTAR_STATUS_MISSING_BLOCK: {
755 0 : ulong j; for( j=0UL; j<*out_repair_event_cnt; j++ ) if( FD_UNLIKELY( !memcmp( out_repair_events[j].hash, pending.hash[i], sizeof(ag_block_hash_t) ) ) ) break;
756 0 : if( FD_LIKELY( j==*out_repair_event_cnt ) ) out_repair_events[ (*out_repair_event_cnt)++ ] = ag_block_id( slot, pending.hash[i] );
757 0 : break;
758 0 : }
759 0 : default: break;
760 0 : }
761 0 : }
762 0 : }
763 :
764 0 : return err;
765 0 : }
766 :
767 : void
768 : ag_slot_state_notify_parent_known( ag_slot_state_t * self,
769 0 : ag_block_hash_t const block_hash ) {
770 0 : for( ulong i=0UL; i<self->parents_cnt; i++ ) {
771 0 : if( FD_UNLIKELY( !memcmp( self->parents[i].hash, block_hash, sizeof(ag_block_hash_t) ) ) ) return;
772 0 : }
773 0 : FD_TEST( self->parents_cnt < AG_EQVOC_BLOCK_HASH_MAX );
774 0 : ag_parent_status_t * e = &self->parents[ self->parents_cnt++ ];
775 0 : memcpy( e->hash, block_hash, sizeof(ag_block_hash_t) );
776 0 : e->kind = AG_PARENT_STATUS_KNOWN;
777 0 : }
778 :
779 : int
780 : ag_slot_state_notify_parent_certified( ag_slot_state_t * self,
781 : ag_slot_states_t * states,
782 : ag_block_hash_t const block_hash,
783 0 : fd_bls_set_t * bad ) {
784 0 : fd_bls_set_null( bad );
785 0 : ag_parent_status_t * parent = NULL;
786 0 : for( ulong i=0UL; i<self->parents_cnt; i++ ) {
787 0 : if( FD_LIKELY( !memcmp( self->parents[i].hash, block_hash, sizeof(ag_block_hash_t) ) ) ) { parent = &self->parents[i]; break; }
788 0 : }
789 0 : FD_TEST( parent );
790 0 : parent->kind = AG_PARENT_STATUS_CERTIFIED;
791 :
792 0 : if( FD_UNLIKELY( block_hash_set_contains( &self->sent_safe_to_notar, block_hash ) ) ) return 0;
793 :
794 0 : switch( check_safe_to_notar( self, states, block_hash, bad ) ) {
795 0 : case AG_SAFE_TO_NOTAR_STATUS_MISSING_BLOCK: return -1; /* unreachable: this block's parents entry was found above */
796 0 : case AG_SAFE_TO_NOTAR_STATUS_AWAITING_VOTES: return 0;
797 0 : case AG_SAFE_TO_NOTAR_STATUS_SAFE_TO_NOTAR: return 1;
798 0 : default: FD_LOG_CRIT(( "unreachable" ));
799 0 : }
800 0 : }
801 :
802 : FD_FN_PURE int
803 : ag_slot_state_check_slashable_offence( ag_slot_state_t const * self,
804 : ag_slot_states_t const * states,
805 0 : ag_vote_t const * vote ) {
806 0 : ulong voter = ag_vote_rank( vote );
807 0 : ag_slot_votes_t const * votes = &self->votes;
808 :
809 0 : switch( vote->kind ) {
810 0 : case AG_VOTE_KIND_NOTAR: {
811 0 : if( FD_UNLIKELY( fd_bls_set_test( votes->skip_agg.set, voter ) ) ) {
812 0 : return AG_SLASHABLE_SKIP_AND_NOTARIZE;
813 0 : }
814 0 : if( FD_UNLIKELY( fd_bls_set_test( votes->notar_set, voter ) ) ) {
815 0 : if( FD_UNLIKELY( memcmp( ag_vote_stake_pool_ele_const( states->notar_hash_stake_pool, votes->notar_hash_stake_idx[ voter ] )->block_id.hash, vote->notar.block_hash, sizeof(ag_block_hash_t) ) ) ) return AG_SLASHABLE_NOTAR_DIFFERENT_HASH;
816 0 : }
817 0 : break;
818 0 : }
819 :
820 0 : case AG_VOTE_KIND_NOTAR_FALLBACK:
821 0 : if( FD_UNLIKELY( fd_bls_set_test( votes->finalize_agg.set, voter ) ) ) {
822 0 : return AG_SLASHABLE_NOTAR_FALLBACK_AND_FINALIZE;
823 0 : } else if( FD_UNLIKELY( votes->notar_fallback_sig_cnt[ voter ]>=AG_NOTAR_FALLBACK_VOTE_MAX ) ) {
824 0 : return AG_SLASHABLE_NOTAR_FALLBACK_OVER_THREE;
825 0 : }
826 0 : break;
827 :
828 0 : case AG_VOTE_KIND_SKIP:
829 0 : if( FD_UNLIKELY( fd_bls_set_test( votes->finalize_agg.set, voter ) ) ) {
830 0 : return AG_SLASHABLE_SKIP_AND_FINALIZE;
831 0 : }
832 0 : if( FD_UNLIKELY( fd_bls_set_test( votes->notar_set, voter ) ) ) return AG_SLASHABLE_SKIP_AND_NOTARIZE;
833 0 : break;
834 :
835 0 : case AG_VOTE_KIND_SKIP_FALLBACK:
836 0 : if( FD_UNLIKELY( fd_bls_set_test( votes->finalize_agg.set, voter ) ) ) {
837 0 : return AG_SLASHABLE_SKIP_AND_FINALIZE;
838 0 : }
839 0 : break;
840 :
841 0 : case AG_VOTE_KIND_FINAL: {
842 0 : if( FD_UNLIKELY( fd_bls_set_test( votes->skip_agg.set, voter ) || fd_bls_set_test( votes->skip_fallback_agg.set, voter ) ) ) {
843 0 : return AG_SLASHABLE_SKIP_AND_FINALIZE;
844 0 : }
845 0 : if( FD_UNLIKELY( votes->notar_fallback_sig_cnt[ voter ] ) ) {
846 0 : return AG_SLASHABLE_NOTAR_FALLBACK_AND_FINALIZE;
847 0 : }
848 0 : break;
849 0 : }
850 :
851 0 : default:
852 0 : FD_LOG_ERR(( "invalid vote kind %u", vote->kind ));
853 0 : }
854 0 : return AG_SLASHABLE_NONE;
855 0 : }
856 :
857 : FD_FN_PURE int
858 : ag_slot_state_should_ignore_vote( ag_slot_state_t const * self,
859 : ag_slot_states_t const * states,
860 0 : ag_vote_t const * vote ) {
861 0 : ulong voter = ag_vote_rank( vote );
862 0 : ag_slot_votes_t const * votes = &self->votes;
863 0 : ag_vote_stake_ele_t const * notar_hash_stake_pool = states->notar_hash_stake_pool;
864 0 : ag_vote_stake_ele_t const * notar_fallback_hash_stake_pool = states->notar_fallback_hash_stake_pool;
865 0 : switch( vote->kind ) {
866 0 : case AG_VOTE_KIND_NOTAR: {
867 0 : if( FD_UNLIKELY( fd_bls_set_test( votes->notar_set, voter ) ) ) return 1;
868 :
869 0 : uchar const * hash = vote->notar.block_hash;
870 0 : for( ulong j=0UL; j<votes->notar_fallback_sig_cnt[ voter ]; j++ ) {
871 0 : if( FD_UNLIKELY( !memcmp( ag_vote_stake_pool_ele_const( notar_fallback_hash_stake_pool, votes->notar_fallback_hash_stake_idx[ voter ][ j ] )->block_id.hash, hash, sizeof(ag_block_hash_t) ) ) ) return 1;
872 0 : }
873 0 : return 0;
874 0 : }
875 0 : case AG_VOTE_KIND_NOTAR_FALLBACK: {
876 0 : uchar const * hash = vote->notar_fallback.block_hash;
877 0 : for( ulong j=0UL; j<votes->notar_fallback_sig_cnt[ voter ]; j++ ) {
878 0 : if( FD_UNLIKELY( !memcmp( ag_vote_stake_pool_ele_const( notar_fallback_hash_stake_pool, votes->notar_fallback_hash_stake_idx[ voter ][ j ] )->block_id.hash, hash, sizeof(ag_block_hash_t) ) ) ) return 1;
879 0 : }
880 :
881 0 : return fd_bls_set_test( votes->notar_set, voter ) && !memcmp( ag_vote_stake_pool_ele_const( notar_hash_stake_pool, votes->notar_hash_stake_idx[ voter ] )->block_id.hash, hash, sizeof(ag_block_hash_t) );
882 0 : }
883 0 : case AG_VOTE_KIND_SKIP:
884 0 : case AG_VOTE_KIND_SKIP_FALLBACK:
885 0 : return fd_bls_set_test( votes->skip_agg.set, voter ) || fd_bls_set_test( votes->skip_fallback_agg.set, voter );
886 0 : case AG_VOTE_KIND_FINAL:
887 0 : return fd_bls_set_test( votes->finalize_agg.set, voter );
888 0 : default:
889 0 : FD_LOG_ERR(( "invalid vote kind %u", vote->kind ));
890 0 : }
891 0 : return 0;
892 0 : }
893 :
894 : fd_bls_agg_t *
895 : ag_slot_state_notar_reward_agg( ag_slot_state_t const * self,
896 : ag_slot_states_t const * states,
897 0 : fd_bls_agg_t * out ) {
898 0 : ag_epoch_info_t const * epoch_info = fd_ptr_if( self->slot>=states->next_epoch_slot, states->next_epoch_info, fd_ptr_if( self->slot>=states->curr_epoch_slot, states->curr_epoch_info, states->prev_epoch_info ) );
899 0 : ag_block_id_t key = ag_block_id( self->slot, self->votes.top_notar_hash );
900 0 : ulong top = ag_vote_stake_map_idx_query_const( states->notar_hash_stake_map, &key, ULONG_MAX, states->notar_hash_stake_pool );
901 0 : if( FD_UNLIKELY( top==ULONG_MAX ) ) return NULL;
902 :
903 0 : fd_bls_set_t set[ fd_bls_set_word_cnt ];
904 0 : fd_bls_set_null( set );
905 0 : for( ulong rank = fd_bls_set_const_iter_init( self->votes.notar_set );
906 0 : !fd_bls_set_const_iter_done( rank );
907 0 : rank = fd_bls_set_const_iter_next( self->votes.notar_set, rank ) ) {
908 0 : if( FD_LIKELY( self->votes.notar_hash_stake_idx[ rank ]==top ) ) fd_bls_set_insert( set, rank );
909 0 : }
910 0 : fd_bls_sig_t sigs[ AG_VAT_MAX ];
911 0 : ag_slot_state_sigs( self, states, AG_VOTE_KIND_NOTAR, key.hash, set, sigs );
912 0 : fd_bls_agg_null( out );
913 0 : fd_bls_agg_construct( out, epoch_info->pubkeys, sigs, set );
914 0 : return out;
915 0 : }
916 :
917 : FD_FN_PURE fd_bls_agg_t const *
918 0 : ag_slot_state_skip_reward_agg( ag_slot_state_t const * self ) {
919 0 : return fd_bls_set_is_null( self->votes.skip_agg.set ) ? NULL : &self->votes.skip_agg;
920 0 : }
921 :
922 : fd_bls_sig_t *
923 : ag_slot_state_sigs( ag_slot_state_t const * self,
924 : ag_slot_states_t const * states,
925 : uint kind,
926 : ag_block_hash_t const block_hash,
927 : fd_bls_set_t const * set,
928 0 : fd_bls_sig_t * out ) {
929 0 : ag_slot_votes_t const * votes = &self->votes;
930 0 : ag_vote_stake_ele_t const * notar_fallback_hash_stake_pool = states->notar_fallback_hash_stake_pool;
931 0 : for( ulong rank = fd_bls_set_const_iter_init( set );
932 0 : !fd_bls_set_const_iter_done( rank );
933 0 : rank = fd_bls_set_const_iter_next( set, rank ) ) {
934 0 : ulong idx;
935 0 : switch( kind ) {
936 0 : case AG_VOTE_KIND_NOTAR: idx = votes->notar_sig_idx [ rank ]; break;
937 0 : case AG_VOTE_KIND_SKIP: idx = votes->skip_sig_idx [ rank ]; break;
938 0 : case AG_VOTE_KIND_SKIP_FALLBACK: idx = votes->skip_fallback_sig_idx[ rank ]; break;
939 0 : case AG_VOTE_KIND_FINAL: idx = votes->finalize_sig_idx [ rank ]; break;
940 0 : case AG_VOTE_KIND_NOTAR_FALLBACK: {
941 0 : ulong j;
942 0 : for( j=0UL; j<votes->notar_fallback_sig_cnt[ rank ]; j++ ) {
943 0 : if( FD_UNLIKELY( !memcmp( ag_vote_stake_pool_ele_const( notar_fallback_hash_stake_pool, votes->notar_fallback_hash_stake_idx[ rank ][ j ] )->block_id.hash, block_hash, sizeof(ag_block_hash_t) ) ) ) break;
944 0 : }
945 0 : FD_CHECK_CRIT( j<votes->notar_fallback_sig_cnt[ rank ], "invariant violation" );
946 0 : idx = votes->notar_fallback_sig_idx[ rank ][ j ];
947 0 : break;
948 0 : }
949 0 : default: FD_LOG_CRIT(( "unreachable" ));
950 0 : }
951 0 : out[ rank ] = fd_bls_sig_pool_ele_const( states->bls_sig_pool, idx )->sig;
952 0 : }
953 0 : return out;
954 0 : }
|