Line data Source code
1 : #include "fd_votor_pool.h"
2 : #include "fd_finality_tracker.h"
3 : #include "fd_parent_ready_tracker.h"
4 : #include "fd_vote_serde.h"
5 :
6 0 : #define PARENT_STATUS_KNOWN (1)
7 0 : #define PARENT_STATUS_CERTIFIED (2)
8 :
9 0 : #define SLASHABLE_OFFENCE_NONE (0)
10 0 : #define SLASHABLE_OFFENCE_NOTAR_DIFFERENT_HASH (1)
11 0 : #define SLASHABLE_OFFENCE_SKIP_AND_NOTARIZE (2)
12 0 : #define SLASHABLE_OFFENCE_SKIP_AND_FINALIZE (3)
13 0 : #define SLASHABLE_OFFENCE_NOTAR_FALLBACK_AND_FINALIZE (4)
14 :
15 0 : #define SAFE_TO_NOTAR_STATUS_SAFE_TO_NOTAR (0)
16 0 : #define SAFE_TO_NOTAR_STATUS_MISSING_BLOCK (1)
17 0 : #define SAFE_TO_NOTAR_STATUS_AWAITING_VOTES (2)
18 :
19 : #define SLOT_STATE_OUT_CERT_MAX (3UL)
20 : #define SLOT_STATE_OUT_EVENT_MAX (3UL)
21 : #define SLOT_STATE_OUT_REPAIR_MAX (3UL)
22 :
23 0 : #define NOTAR_FALLBACK_HASH_MAX (FD_VOTOR_VAT_MAX*FD_VOTOR_NOTAR_FALLBACK_VOTE_MAX)
24 :
25 0 : #define STANDSTILL_FINAL (0UL)
26 0 : #define STANDSTILL_FAST_FINAL (1UL)
27 0 : #define STANDSTILL_NOTAR (2UL)
28 0 : #define STANDSTILL_NOTAR_FALLBACK (3UL)
29 0 : #define STANDSTILL_SKIP (STANDSTILL_NOTAR_FALLBACK+FD_VOTOR_NOTAR_FALLBACK_CERT_MAX)
30 0 : #define STANDSTILL_NOTAR_VOTE (STANDSTILL_SKIP+1UL)
31 0 : #define STANDSTILL_FINAL_VOTE (STANDSTILL_NOTAR_VOTE+1UL)
32 0 : #define STANDSTILL_SKIP_VOTE (STANDSTILL_FINAL_VOTE+1UL)
33 0 : #define STANDSTILL_NOTAR_FALLBACK_VOTE (STANDSTILL_SKIP_VOTE+1UL)
34 0 : #define STANDSTILL_SKIP_FALLBACK_VOTE (STANDSTILL_NOTAR_FALLBACK_VOTE+FD_VOTOR_NOTAR_FALLBACK_VOTE_MAX)
35 :
36 : struct __attribute__((aligned(64UL))) vote_stake_ele {
37 : fd_block_id_t block_id;
38 : uint next;
39 : uint prev;
40 : ulong stake;
41 : };
42 : typedef struct vote_stake_ele vote_stake_ele_t;
43 : FD_STATIC_ASSERT( sizeof(vote_stake_ele_t)==64UL, vote_stake_ele );
44 :
45 : #define POOL_NAME vote_stake_pool
46 0 : #define POOL_T vote_stake_ele_t
47 0 : #define POOL_NEXT next
48 : #define POOL_IDX_T uint
49 : #define POOL_LAZY 1
50 : #include "../../util/tmpl/fd_pool.c"
51 :
52 : #define MAP_NAME vote_stake_map
53 : #define MAP_ELE_T vote_stake_ele_t
54 0 : #define MAP_KEY block_id
55 : #define MAP_KEY_T fd_block_id_t
56 0 : #define MAP_IDX_T uint
57 0 : #define MAP_NEXT next
58 0 : #define MAP_KEY_EQ(k0,k1) (fd_block_id_eq((k0),(k1)))
59 0 : #define MAP_KEY_HASH(key,seed) (fd_hash((seed),(key),sizeof(fd_block_id_t)))
60 : #include "../../util/tmpl/fd_map_chain.c"
61 :
62 : struct parent_status {
63 : fd_block_hash_t hash;
64 : int kind;
65 : };
66 : typedef struct parent_status parent_status_t;
67 :
68 : struct block_hash_set {
69 : fd_block_hash_t hash[ FD_VOTOR_EQVOC_MAX ];
70 : ulong cnt;
71 : };
72 : typedef struct block_hash_set block_hash_set_t;
73 :
74 : union bls_sig_ele {
75 : fd_bls_sig_t sig;
76 : ulong next;
77 : };
78 : typedef union bls_sig_ele bls_sig_ele_t;
79 : FD_STATIC_ASSERT( sizeof(bls_sig_ele_t)==sizeof(fd_bls_sig_t), bls_sig_ele );
80 :
81 : #define POOL_NAME bls_sig_pool
82 0 : #define POOL_T bls_sig_ele_t
83 : #include "../../util/tmpl/fd_pool.c"
84 :
85 : struct slot_votes {
86 : fd_bls_set_t notar_set [ fd_bls_set_word_cnt ];
87 : uint notar_sig_idx [ FD_VOTOR_VAT_MAX ];
88 : uint notar_hash_stake_idx [ FD_VOTOR_VAT_MAX ];
89 : uint notar_hash_stake_head;
90 : ulong notar_hash_stake_cnt;
91 : uint notar_fallback_sig_idx [ FD_VOTOR_VAT_MAX ][ FD_VOTOR_NOTAR_FALLBACK_VOTE_MAX ];
92 : uint notar_fallback_hash_stake_idx[ FD_VOTOR_VAT_MAX ][ FD_VOTOR_NOTAR_FALLBACK_VOTE_MAX ];
93 : uint notar_fallback_hash_stake_head;
94 : ulong notar_fallback_hash_stake_cnt;
95 : uchar notar_fallback_sig_cnt [ FD_VOTOR_VAT_MAX ];
96 : ulong skip_stake;
97 : uint skip_sig_idx [ FD_VOTOR_VAT_MAX ];
98 : fd_bls_agg_t skip_agg;
99 : ulong skip_fallback_stake;
100 : uint skip_fallback_sig_idx [ FD_VOTOR_VAT_MAX ];
101 : fd_bls_agg_t skip_fallback_agg;
102 : ulong final_stake;
103 : uint final_sig_idx [ FD_VOTOR_VAT_MAX ];
104 : fd_bls_agg_t final_agg;
105 : ulong notar_or_skip_stake;
106 : ulong top_notar_stake;
107 : fd_block_hash_t top_notar_hash;
108 : };
109 : typedef struct slot_votes slot_votes_t;
110 :
111 : struct slot_certs {
112 : fd_cert_notar_t notar;
113 : fd_cert_notar_fallback_t notar_fallback[ FD_VOTOR_NOTAR_FALLBACK_CERT_MAX ];
114 : fd_cert_skip_t skip;
115 : fd_cert_fast_final_t fast_final;
116 : fd_cert_final_t final;
117 : uchar notar_fallback_cnt;
118 : };
119 : typedef struct slot_certs slot_certs_t;
120 :
121 : struct slot_state {
122 : slot_votes_t votes;
123 : slot_certs_t certs;
124 :
125 : parent_status_t parents[ FD_VOTOR_EQVOC_MAX ];
126 : ulong parents_cnt;
127 :
128 : block_hash_set_t pending_safe_to_notar;
129 : block_hash_set_t verified_safe_to_notar; /* verified votes alone meet its safe-to-notar threshold */
130 : block_hash_set_t sent_safe_to_notar;
131 : int sent_safe_to_skip;
132 :
133 : ulong slot;
134 : ulong own_rank;
135 : ushort shred_version;
136 : };
137 : typedef struct slot_state slot_state_t;
138 :
139 : struct slot_state_ele {
140 : ulong slot;
141 : ulong next;
142 : slot_state_t slot_state;
143 : };
144 : typedef struct slot_state_ele slot_state_ele_t;
145 :
146 : #define POOL_NAME slot_state_pool
147 0 : #define POOL_T slot_state_ele_t
148 : #include "../../util/tmpl/fd_pool.c"
149 :
150 : #define MAP_NAME slot_state_map
151 : #define MAP_ELE_T slot_state_ele_t
152 0 : #define MAP_KEY slot
153 : #define MAP_KEY_T ulong
154 0 : #define MAP_KEY_EQ(k0,k1) ((*(k0))==(*(k1)))
155 0 : #define MAP_KEY_HASH(key,seed) (fd_ulong_hash((*(key))^(seed)))
156 0 : #define MAP_NEXT next
157 : #include "../../util/tmpl/fd_map_chain.c"
158 :
159 : struct slot_states {
160 : slot_state_ele_t * pool;
161 : slot_state_map_t * map;
162 :
163 : bls_sig_ele_t * bls_sig_pool;
164 : vote_stake_ele_t * notar_hash_stake_pool;
165 : vote_stake_map_t * notar_hash_stake_map;
166 : vote_stake_ele_t * notar_fallback_hash_stake_pool;
167 : vote_stake_map_t * notar_fallback_hash_stake_map;
168 : fd_epoch_info_t const * prev_epoch_info;
169 : ulong prev_epoch_rank;
170 : ulong prev_epoch_slot;
171 : fd_epoch_info_t const * curr_epoch_info;
172 : ulong curr_epoch_rank;
173 : ulong curr_epoch_slot;
174 : fd_epoch_info_t const * next_epoch_info;
175 : ulong next_epoch_rank;
176 : ulong next_epoch_slot;
177 : ulong bls_verify_cnt;
178 : };
179 : typedef struct slot_states slot_states_t;
180 :
181 : static int
182 : block_hash_set_contains( block_hash_set_t const * self,
183 0 : fd_block_hash_t const * block_hash ) {
184 0 : for( ulong i=0UL; i<self->cnt; i++ ) {
185 0 : if( FD_LIKELY( !memcmp( &self->hash[i], block_hash, sizeof(fd_block_hash_t) ) ) ) return 1;
186 0 : }
187 0 : return 0;
188 0 : }
189 :
190 : static void
191 : block_hash_set_insert( block_hash_set_t * self,
192 0 : fd_block_hash_t const * block_hash ) {
193 0 : if( FD_UNLIKELY( block_hash_set_contains( self, block_hash ) ) ) return;
194 0 : FD_TEST( self->cnt < FD_VOTOR_EQVOC_MAX );
195 0 : memcpy( &self->hash[ self->cnt++ ], block_hash, sizeof(fd_block_hash_t) );
196 0 : }
197 :
198 : static void
199 : block_hash_set_remove( block_hash_set_t * self,
200 0 : fd_block_hash_t const * block_hash ) {
201 0 : for( ulong i=0UL; i<self->cnt; i++ ) {
202 0 : if( FD_LIKELY( !memcmp( &self->hash[i], block_hash, sizeof(fd_block_hash_t) ) ) ) {
203 0 : self->cnt--;
204 0 : if( FD_UNLIKELY( i!=self->cnt ) ) memcpy( &self->hash[i], &self->hash[ self->cnt ], sizeof(fd_block_hash_t) );
205 0 : return;
206 0 : }
207 0 : }
208 0 : }
209 :
210 : /* Definition 13. SlotState::is_notar_fallback */
211 :
212 : FD_FN_PURE static int
213 : slot_state_is_notar_fallback( slot_state_t const * self,
214 0 : fd_block_hash_t const * block_hash ) {
215 0 : slot_certs_t const * c = &self->certs;
216 0 : for( ulong i=0UL; i<c->notar_fallback_cnt; i++ ) {
217 0 : if( FD_LIKELY( !memcmp( &c->notar_fallback[i].block_hash, block_hash, sizeof(fd_block_hash_t) ) ) ) return 1;
218 0 : }
219 0 : return 0;
220 0 : }
221 :
222 : /* Definition 13. SlotState::is_notar_fallback_or_stronger */
223 :
224 : FD_FN_PURE static int
225 : slot_state_is_notar_fallback_or_stronger( slot_state_t const * self,
226 0 : fd_block_hash_t const * block_hash ) {
227 0 : int has_notar_cert = self->certs.notar.slot !=ULONG_MAX && !memcmp( &self->certs.notar.block_hash, block_hash, sizeof(fd_block_hash_t) );
228 0 : int has_fast_final_cert = self->certs.fast_final.slot!=ULONG_MAX && !memcmp( &self->certs.fast_final.block_hash, block_hash, sizeof(fd_block_hash_t) );
229 0 : return has_notar_cert || has_fast_final_cert || slot_state_is_notar_fallback( self, block_hash );
230 0 : }
231 :
232 : /* Copies the kind vote sigs of the ranks in set into out[ rank ].
233 : block_hash selects the sig for notar fallback; for notar the caller
234 : filters set by hash. */
235 :
236 : static fd_bls_sig_t *
237 : slot_state_sigs( slot_state_t const * self,
238 : slot_states_t const * states,
239 : uint kind,
240 : fd_block_hash_t const * block_hash,
241 : fd_bls_set_t const * set,
242 0 : fd_bls_sig_t * out ) {
243 0 : slot_votes_t const * votes = &self->votes;
244 0 : vote_stake_ele_t const * notar_fallback_hash_stake_pool = states->notar_fallback_hash_stake_pool;
245 0 : for( ulong rank = fd_bls_set_const_iter_init( set );
246 0 : !fd_bls_set_const_iter_done( rank );
247 0 : rank = fd_bls_set_const_iter_next( set, rank ) ) {
248 0 : ulong idx;
249 0 : switch( kind ) {
250 0 : case FD_VOTE_KIND_NOTAR: idx = votes->notar_sig_idx [ rank ]; break;
251 0 : case FD_VOTE_KIND_SKIP: idx = votes->skip_sig_idx [ rank ]; break;
252 0 : case FD_VOTE_KIND_SKIP_FALLBACK: idx = votes->skip_fallback_sig_idx[ rank ]; break;
253 0 : case FD_VOTE_KIND_FINAL: idx = votes->final_sig_idx [ rank ]; break;
254 0 : case FD_VOTE_KIND_NOTAR_FALLBACK: {
255 0 : ulong j;
256 0 : for( j=0UL; j<votes->notar_fallback_sig_cnt[ rank ]; j++ ) {
257 0 : if( FD_UNLIKELY( !memcmp( &vote_stake_pool_ele_const( notar_fallback_hash_stake_pool, votes->notar_fallback_hash_stake_idx[ rank ][ j ] )->block_id.hash, block_hash, sizeof(fd_block_hash_t) ) ) ) break;
258 0 : }
259 0 : FD_CHECK_CRIT( j<votes->notar_fallback_sig_cnt[ rank ], "invariant violation" );
260 0 : idx = votes->notar_fallback_sig_idx[ rank ][ j ];
261 0 : break;
262 0 : }
263 0 : default: FD_LOG_CRIT(( "unreachable" ));
264 0 : }
265 0 : out[ rank ] = bls_sig_pool_ele_const( states->bls_sig_pool, idx )->sig;
266 0 : }
267 0 : return out;
268 0 : }
269 :
270 : static int
271 : verify_subtract_votes( slot_state_t * self,
272 : slot_states_t * states,
273 : uint kind,
274 : fd_block_hash_t const * block_hash,
275 : fd_bls_agg_t * agg,
276 0 : fd_bls_set_t * bad ) {
277 0 : fd_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 ) );
278 0 : slot_votes_t * votes = &self->votes;
279 0 : vote_stake_map_t * notar_hash_stake_map = states->notar_hash_stake_map;
280 0 : vote_stake_map_t * notar_fallback_hash_stake_map = states->notar_fallback_hash_stake_map;
281 0 : vote_stake_ele_t * notar_hash_stake_pool = states->notar_hash_stake_pool;
282 0 : vote_stake_ele_t * notar_fallback_hash_stake_pool = states->notar_fallback_hash_stake_pool;
283 :
284 0 : fd_bls_sig_t sigs[ FD_VOTOR_VAT_MAX ];
285 0 : fd_bls_set_t set [ fd_bls_set_word_cnt ];
286 0 : switch( kind ) {
287 0 : case FD_VOTE_KIND_SKIP:
288 0 : case FD_VOTE_KIND_SKIP_FALLBACK:
289 0 : case FD_VOTE_KIND_FINAL:
290 0 : slot_state_sigs( self, states, kind, NULL, agg->set, sigs );
291 0 : break;
292 0 : case FD_VOTE_KIND_NOTAR:
293 0 : fd_bls_set_null( set );
294 0 : for( ulong rank = fd_bls_set_const_iter_init( votes->notar_set );
295 0 : !fd_bls_set_const_iter_done( rank );
296 0 : rank = fd_bls_set_const_iter_next( votes->notar_set, rank ) ) {
297 0 : if( FD_LIKELY( !memcmp( &vote_stake_pool_ele( notar_hash_stake_pool, votes->notar_hash_stake_idx[ rank ] )->block_id.hash, block_hash, sizeof(fd_block_hash_t) ) ) ) fd_bls_set_insert( set, rank );
298 0 : }
299 0 : slot_state_sigs( self, states, kind, block_hash, set, sigs );
300 0 : fd_bls_agg_null( agg );
301 0 : fd_bls_agg_construct( agg, epoch_info->pubkeys, sigs, set );
302 0 : break;
303 0 : case FD_VOTE_KIND_NOTAR_FALLBACK:
304 0 : fd_bls_set_null( set );
305 0 : for( ulong rank=0UL; rank<epoch_info->validator_cnt; rank++ ) {
306 0 : for( ulong j=0UL; j<votes->notar_fallback_sig_cnt[ rank ]; j++ ) {
307 0 : if( FD_LIKELY( !memcmp( &vote_stake_pool_ele( notar_fallback_hash_stake_pool, votes->notar_fallback_hash_stake_idx[ rank ][ j ] )->block_id.hash, block_hash, sizeof(fd_block_hash_t) ) ) ) fd_bls_set_insert( set, rank );
308 0 : }
309 0 : }
310 0 : slot_state_sigs( self, states, kind, block_hash, set, sigs );
311 0 : fd_bls_agg_null( agg );
312 0 : fd_bls_agg_construct( agg, epoch_info->pubkeys, sigs, set );
313 0 : break;
314 0 : default: FD_LOG_CRIT(( "unreachable" ));
315 0 : }
316 :
317 0 : uchar msg[ FD_VOTE_SIGNING_SER_MAX ];
318 0 : ulong msg_sz = fd_vote_signing_ser( kind, self->slot, block_hash, self->shred_version, msg );
319 0 : states->bls_verify_cnt++;
320 0 : int err = fd_bls_agg_verify_subtract( agg, msg, msg_sz, epoch_info->pubkeys, sigs, bad );
321 0 : if( FD_LIKELY( !err && fd_bls_set_is_null( bad ) ) ) return 1;
322 :
323 0 : fd_block_id_t key;
324 0 : ulong id = ULONG_MAX;
325 0 : switch( kind ) {
326 0 : case FD_VOTE_KIND_NOTAR: key = fd_block_id( self->slot, block_hash->uc ); id = vote_stake_map_idx_query_const( notar_hash_stake_map, &key, ULONG_MAX, notar_hash_stake_pool ); break;
327 0 : case FD_VOTE_KIND_NOTAR_FALLBACK: key = fd_block_id( self->slot, block_hash->uc ); id = vote_stake_map_idx_query_const( notar_fallback_hash_stake_map, &key, ULONG_MAX, notar_fallback_hash_stake_pool ); break;
328 0 : default: break;
329 0 : }
330 :
331 0 : for( ulong rank = fd_bls_set_const_iter_init( bad );
332 0 : !fd_bls_set_const_iter_done( rank );
333 0 : rank = fd_bls_set_const_iter_next( bad, rank ) ) {
334 0 : ulong stake = fd_epoch_info_validator( epoch_info, rank )->stake;
335 0 : switch( kind ) {
336 0 : case FD_VOTE_KIND_NOTAR:
337 0 : vote_stake_pool_ele( notar_hash_stake_pool, id )->stake -= stake;
338 0 : votes->notar_or_skip_stake -= stake;
339 0 : fd_bls_set_remove( votes->notar_set, rank );
340 0 : bls_sig_pool_idx_release( states->bls_sig_pool, votes->notar_sig_idx[ rank ] );
341 0 : break;
342 0 : case FD_VOTE_KIND_NOTAR_FALLBACK: {
343 0 : ulong j;
344 0 : for( j=0UL; j<votes->notar_fallback_sig_cnt[ rank ]; j++ ) {
345 0 : if( FD_UNLIKELY( votes->notar_fallback_hash_stake_idx[ rank ][ j ]==id ) ) break;
346 0 : }
347 0 : FD_CHECK_CRIT( j<votes->notar_fallback_sig_cnt[ rank ], "invariant violation" );
348 0 : vote_stake_pool_ele( notar_fallback_hash_stake_pool, id )->stake -= stake;
349 0 : bls_sig_pool_idx_release( states->bls_sig_pool, votes->notar_fallback_sig_idx[ rank ][ j ] );
350 0 : votes->notar_fallback_sig_cnt[ rank ]--;
351 0 : votes->notar_fallback_sig_idx [ rank ][ j ] = votes->notar_fallback_sig_idx [ rank ][ votes->notar_fallback_sig_cnt[ rank ] ];
352 0 : votes->notar_fallback_hash_stake_idx[ rank ][ j ] = votes->notar_fallback_hash_stake_idx[ rank ][ votes->notar_fallback_sig_cnt[ rank ] ];
353 0 : break;
354 0 : }
355 0 : case FD_VOTE_KIND_SKIP:
356 0 : votes->skip_stake -= stake;
357 0 : votes->notar_or_skip_stake -= stake;
358 0 : bls_sig_pool_idx_release( states->bls_sig_pool, votes->skip_sig_idx[ rank ] );
359 0 : break;
360 0 : case FD_VOTE_KIND_SKIP_FALLBACK:
361 0 : votes->skip_fallback_stake -= stake;
362 0 : bls_sig_pool_idx_release( states->bls_sig_pool, votes->skip_fallback_sig_idx[ rank ] );
363 0 : break;
364 0 : case FD_VOTE_KIND_FINAL:
365 0 : votes->final_stake -= stake;
366 0 : bls_sig_pool_idx_release( states->bls_sig_pool, votes->final_sig_idx[ rank ] );
367 0 : break;
368 0 : default:
369 0 : FD_LOG_CRIT(( "unreachable" ));
370 0 : }
371 0 : }
372 :
373 0 : if( FD_UNLIKELY( kind==FD_VOTE_KIND_NOTAR && fd_bls_set_is_null( agg->set ) ) ) {
374 0 : uint * link = &votes->notar_hash_stake_head;
375 0 : ulong j;
376 0 : for( j=0UL; j<votes->notar_hash_stake_cnt; j++ ) {
377 0 : if( FD_UNLIKELY( *link==id ) ) break;
378 0 : link = &vote_stake_pool_ele( notar_hash_stake_pool, *link )->prev;
379 0 : }
380 0 : FD_CHECK_CRIT( j<votes->notar_hash_stake_cnt, "invariant violation" );
381 0 : *link = vote_stake_pool_ele( notar_hash_stake_pool, id )->prev;
382 0 : votes->notar_hash_stake_cnt--;
383 0 : vote_stake_map_idx_remove( notar_hash_stake_map, &key, ULONG_MAX, notar_hash_stake_pool );
384 0 : vote_stake_pool_idx_release( notar_hash_stake_pool, id );
385 0 : }
386 0 : if( FD_UNLIKELY( kind==FD_VOTE_KIND_NOTAR_FALLBACK && fd_bls_set_is_null( agg->set ) ) ) {
387 0 : uint * link = &votes->notar_fallback_hash_stake_head;
388 0 : ulong j;
389 0 : for( j=0UL; j<votes->notar_fallback_hash_stake_cnt; j++ ) {
390 0 : if( FD_UNLIKELY( *link==id ) ) break;
391 0 : link = &vote_stake_pool_ele( notar_fallback_hash_stake_pool, *link )->prev;
392 0 : }
393 0 : FD_CHECK_CRIT( j<votes->notar_fallback_hash_stake_cnt, "invariant violation" );
394 0 : *link = vote_stake_pool_ele( notar_fallback_hash_stake_pool, id )->prev;
395 0 : votes->notar_fallback_hash_stake_cnt--;
396 0 : vote_stake_map_idx_remove( notar_fallback_hash_stake_map, &key, ULONG_MAX, notar_fallback_hash_stake_pool );
397 0 : vote_stake_pool_idx_release( notar_fallback_hash_stake_pool, id );
398 0 : }
399 0 : if( FD_UNLIKELY( kind==FD_VOTE_KIND_NOTAR ) ) {
400 0 : votes->top_notar_stake = 0UL;
401 0 : ulong i = votes->notar_hash_stake_head;
402 0 : for( ulong j=0UL; j<votes->notar_hash_stake_cnt; j++ ) {
403 0 : if( FD_LIKELY( vote_stake_pool_ele( notar_hash_stake_pool, i )->stake>votes->top_notar_stake ) ) { /* FIXME slow */
404 0 : votes->top_notar_stake = vote_stake_pool_ele( notar_hash_stake_pool, i )->stake;
405 0 : memcpy( &votes->top_notar_hash, &vote_stake_pool_ele( notar_hash_stake_pool, i )->block_id.hash, sizeof(fd_block_hash_t) );
406 0 : }
407 0 : i = vote_stake_pool_ele( notar_hash_stake_pool, i )->prev;
408 0 : }
409 0 : }
410 0 : return !err;
411 0 : }
412 :
413 : static int
414 : check_safe_to_notar( slot_state_t * self,
415 : slot_states_t * states,
416 : fd_block_hash_t const * block_hash,
417 0 : fd_bls_set_t * bad ) {
418 0 : fd_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 ) );
419 0 : slot_votes_t * votes = &self->votes;
420 0 : vote_stake_map_t * notar_hash_stake_map = states->notar_hash_stake_map;
421 0 : vote_stake_ele_t * notar_hash_stake_pool = states->notar_hash_stake_pool;
422 0 : fd_block_id_t key = fd_block_id( self->slot, block_hash->uc );
423 0 : ulong notar = vote_stake_map_idx_query_const( notar_hash_stake_map, &key, ULONG_MAX, notar_hash_stake_pool );
424 0 : ulong notar_stake = notar!=ULONG_MAX ? vote_stake_pool_ele( notar_hash_stake_pool, notar )->stake : 0UL;
425 0 : ulong skip_stake = votes->skip_stake;
426 :
427 0 : if( FD_UNLIKELY( !fd_epoch_info_is_weakest_quorum( epoch_info, notar_stake ) ) ) {
428 0 : return SAFE_TO_NOTAR_STATUS_AWAITING_VOTES;
429 0 : }
430 0 : if( FD_UNLIKELY( !fd_epoch_info_is_weak_quorum( epoch_info, notar_stake ) && !fd_epoch_info_is_quorum( epoch_info, notar_stake + skip_stake ) ) ) {
431 0 : block_hash_set_insert( &self->pending_safe_to_notar, block_hash );
432 0 : return SAFE_TO_NOTAR_STATUS_AWAITING_VOTES;
433 0 : }
434 :
435 0 : parent_status_t const * parent = NULL;
436 0 : for( ulong i=0UL; i<self->parents_cnt; i++ ) {
437 0 : if( FD_LIKELY( !memcmp( &self->parents[i].hash, block_hash, sizeof(fd_block_hash_t) ) ) ) { parent = &self->parents[i]; break; }
438 0 : }
439 0 : if( FD_UNLIKELY( !parent ) ) return SAFE_TO_NOTAR_STATUS_MISSING_BLOCK;
440 :
441 0 : ulong own = self->own_rank;
442 :
443 0 : int safe_to_notar = 0;
444 0 : if( FD_LIKELY( own!=USHORT_MAX ) ) { /* must be staked */
445 0 : if( FD_UNLIKELY( fd_bls_set_test( votes->skip_agg.set, own ) ) ) safe_to_notar = 1;
446 0 : if( FD_UNLIKELY( fd_bls_set_test( votes->notar_set, own ) ) ) {
447 0 : if( FD_UNLIKELY( !memcmp( &vote_stake_pool_ele( notar_hash_stake_pool, votes->notar_hash_stake_idx[ own ] )->block_id.hash, block_hash, sizeof(fd_block_hash_t) ) ) ) return SAFE_TO_NOTAR_STATUS_AWAITING_VOTES;
448 0 : safe_to_notar = 1;
449 0 : }
450 0 : }
451 0 : if( FD_UNLIKELY( !safe_to_notar ) ) {
452 0 : block_hash_set_insert( &self->pending_safe_to_notar, block_hash );
453 0 : return SAFE_TO_NOTAR_STATUS_AWAITING_VOTES;
454 0 : }
455 :
456 0 : if( FD_LIKELY( !block_hash_set_contains( &self->verified_safe_to_notar, block_hash ) ) ) {
457 0 : fd_bls_agg_t notar_agg[1];
458 0 : fd_bls_set_t bad_notar[ fd_bls_set_word_cnt ];
459 0 : verify_subtract_votes( self, states, FD_VOTE_KIND_NOTAR, block_hash, notar_agg, bad_notar );
460 0 : fd_bls_set_union( bad, bad, bad_notar );
461 0 : if( FD_LIKELY( !fd_bls_set_is_null( votes->skip_agg.set ) ) ) {
462 0 : fd_bls_set_t bad_skip[ fd_bls_set_word_cnt ];
463 0 : verify_subtract_votes( self, states, FD_VOTE_KIND_SKIP, NULL, &votes->skip_agg, bad_skip );
464 0 : fd_bls_set_union( bad, bad, bad_skip );
465 0 : }
466 0 : notar = vote_stake_map_idx_query_const( notar_hash_stake_map, &key, ULONG_MAX, notar_hash_stake_pool );
467 0 : notar_stake = notar!=ULONG_MAX ? vote_stake_pool_ele( notar_hash_stake_pool, notar )->stake : 0UL;
468 0 : skip_stake = votes->skip_stake;
469 0 : if( FD_UNLIKELY( !fd_epoch_info_is_weakest_quorum( epoch_info, notar_stake ) || ( !fd_epoch_info_is_weak_quorum( epoch_info, notar_stake ) && !fd_epoch_info_is_quorum( epoch_info, notar_stake + skip_stake ) ) ) ) {
470 0 : block_hash_set_insert( &self->pending_safe_to_notar, block_hash );
471 0 : return SAFE_TO_NOTAR_STATUS_AWAITING_VOTES;
472 0 : }
473 0 : block_hash_set_insert( &self->verified_safe_to_notar, block_hash );
474 0 : }
475 0 : if( FD_UNLIKELY( parent->kind!=PARENT_STATUS_CERTIFIED ) ) return SAFE_TO_NOTAR_STATUS_AWAITING_VOTES;
476 :
477 0 : block_hash_set_remove( &self->pending_safe_to_notar, block_hash );
478 0 : block_hash_set_insert( &self->sent_safe_to_notar, block_hash );
479 0 : return SAFE_TO_NOTAR_STATUS_SAFE_TO_NOTAR;
480 0 : }
481 :
482 : static int
483 : check_safe_to_skip( slot_state_t * self,
484 : slot_states_t * states,
485 0 : fd_bls_set_t * bad ) {
486 0 : fd_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 ) );
487 0 : slot_votes_t * votes = &self->votes;
488 0 : if( FD_LIKELY( self->sent_safe_to_skip
489 0 : || !fd_epoch_info_is_weak_quorum( epoch_info, votes->notar_or_skip_stake - votes->top_notar_stake )
490 0 : || self->own_rank==USHORT_MAX /* must be staked */ ) ) return 0;
491 0 : if( FD_LIKELY( !fd_bls_set_test( votes->notar_set, self->own_rank ) ) ) return 0;
492 :
493 0 : if( FD_LIKELY( !fd_bls_set_is_null( votes->skip_agg.set ) ) ) {
494 0 : fd_bls_set_t bad_skip[ fd_bls_set_word_cnt ];
495 0 : verify_subtract_votes( self, states, FD_VOTE_KIND_SKIP, NULL, &votes->skip_agg, bad_skip );
496 0 : fd_bls_set_union( bad, bad, bad_skip );
497 0 : }
498 0 : if( FD_LIKELY( fd_epoch_info_is_weak_quorum( epoch_info, votes->skip_stake ) ) ) {
499 0 : self->sent_safe_to_skip = 1;
500 0 : return 1;
501 0 : }
502 :
503 0 : vote_stake_ele_t * notar_hash_stake_pool = states->notar_hash_stake_pool;
504 0 : ulong i = votes->notar_hash_stake_head;
505 0 : for( ulong j=votes->notar_hash_stake_cnt; j>0UL; j-- ) {
506 0 : fd_block_id_t key = vote_stake_pool_ele( notar_hash_stake_pool, i )->block_id;
507 0 : ulong prev = vote_stake_pool_ele( notar_hash_stake_pool, i )->prev;
508 0 : fd_bls_agg_t notar_agg[1];
509 0 : fd_bls_set_t bad_notar[ fd_bls_set_word_cnt ];
510 0 : verify_subtract_votes( self, states, FD_VOTE_KIND_NOTAR, &key.hash, notar_agg, bad_notar );
511 0 : fd_bls_set_union( bad, bad, bad_notar );
512 0 : i = prev;
513 0 : }
514 0 : if( FD_UNLIKELY( !fd_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;
515 0 : self->sent_safe_to_skip = 1;
516 0 : return 1;
517 0 : }
518 :
519 : static int
520 : count_notar_stake( slot_state_t * self,
521 : slot_states_t * states,
522 : fd_vote_notar_t const * vote,
523 : ulong stake,
524 : fd_cert_t * out_cert_events,
525 : ulong * out_cert_event_cnt,
526 : fd_block_id_t * out_safe_to_notar,
527 : ulong * out_safe_to_notar_cnt,
528 : ulong * out_safe_to_skip,
529 : ulong * out_safe_to_skip_cnt,
530 : fd_block_id_t * out_repair_events,
531 : ulong * out_repair_event_cnt,
532 0 : fd_bls_set_t * bad ) {
533 0 : fd_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 ) );
534 0 : ulong slot = vote->slot;
535 0 : fd_block_hash_t const * block_hash = &vote->block_hash;
536 0 : fd_block_id_t key = fd_block_id( self->slot, block_hash->uc );
537 0 : ulong rank = vote->rank;
538 0 : ushort shred_version = vote->shred_version;
539 :
540 0 : slot_votes_t * votes = &self->votes;
541 0 : vote_stake_map_t * notar_hash_stake_map = states->notar_hash_stake_map;
542 0 : vote_stake_map_t * notar_fallback_hash_stake_map = states->notar_fallback_hash_stake_map;
543 0 : vote_stake_ele_t * notar_hash_stake_pool = states->notar_hash_stake_pool;
544 0 : vote_stake_ele_t * notar_fallback_hash_stake_pool = states->notar_fallback_hash_stake_pool;
545 0 : ulong notar = vote_stake_map_idx_query_const( notar_hash_stake_map, &key, ULONG_MAX, notar_hash_stake_pool );
546 0 : if( FD_UNLIKELY( notar==ULONG_MAX ) ) {
547 0 : FD_TEST( vote_stake_pool_free( notar_hash_stake_pool ) );
548 0 : notar = vote_stake_pool_idx_acquire( notar_hash_stake_pool );
549 0 : vote_stake_pool_ele( notar_hash_stake_pool, notar )->block_id = key;
550 0 : vote_stake_pool_ele( notar_hash_stake_pool, notar )->stake = 0UL;
551 0 : vote_stake_pool_ele( notar_hash_stake_pool, notar )->prev = (uint)( votes->notar_hash_stake_cnt ? votes->notar_hash_stake_head : vote_stake_pool_idx_null( notar_hash_stake_pool ) );
552 0 : vote_stake_map_idx_insert( notar_hash_stake_map, notar, notar_hash_stake_pool );
553 0 : votes->notar_hash_stake_head = (uint)notar;
554 0 : votes->notar_hash_stake_cnt++;
555 0 : }
556 0 : votes->notar_hash_stake_idx[ rank ] = (uint)notar;
557 0 : vote_stake_pool_ele( notar_hash_stake_pool, notar )->stake += stake;
558 :
559 0 : ulong notar_stake = vote_stake_pool_ele( notar_hash_stake_pool, notar )->stake;
560 0 : votes->notar_or_skip_stake += stake;
561 0 : if( FD_LIKELY( notar_stake>votes->top_notar_stake ) ) {
562 0 : votes->top_notar_stake = notar_stake;
563 0 : memcpy( &votes->top_notar_hash, block_hash, sizeof(fd_block_hash_t) );
564 0 : }
565 :
566 0 : if( FD_UNLIKELY( !block_hash_set_contains( &self->sent_safe_to_notar, block_hash ) ) ) {
567 0 : switch( check_safe_to_notar( self, states, block_hash, bad ) ) {
568 0 : case SAFE_TO_NOTAR_STATUS_SAFE_TO_NOTAR:
569 0 : out_safe_to_notar[ (*out_safe_to_notar_cnt)++ ] = fd_block_id( slot, block_hash->uc );
570 0 : break;
571 0 : case SAFE_TO_NOTAR_STATUS_MISSING_BLOCK: {
572 0 : ulong j; for( j=0UL; j<*out_repair_event_cnt; j++ ) if( FD_UNLIKELY( !memcmp( &out_repair_events[j].hash, block_hash, sizeof(fd_block_hash_t) ) ) ) break;
573 0 : if( FD_LIKELY( j==*out_repair_event_cnt ) ) out_repair_events[ (*out_repair_event_cnt)++ ] = fd_block_id( slot, block_hash->uc );
574 0 : break;
575 0 : }
576 0 : default: break;
577 0 : }
578 0 : }
579 0 : if( FD_UNLIKELY( check_safe_to_skip( self, states, bad ) ) ) {
580 0 : out_safe_to_skip[ (*out_safe_to_skip_cnt)++ ] = slot;
581 0 : }
582 0 : if( FD_UNLIKELY( !fd_bls_set_is_null( bad ) ) ) {
583 0 : notar = vote_stake_map_idx_query_const( notar_hash_stake_map, &key, ULONG_MAX, notar_hash_stake_pool );
584 0 : if( FD_UNLIKELY( notar==ULONG_MAX || !fd_bls_set_test( votes->notar_set, rank ) ) ) return 0;
585 0 : notar_stake = vote_stake_pool_ele( notar_hash_stake_pool, notar )->stake;
586 0 : }
587 :
588 0 : vote_stake_ele_t const * nf_ele = vote_stake_map_ele_query_const( notar_fallback_hash_stake_map, &key, NULL, notar_fallback_hash_stake_pool );
589 0 : ulong nf_stake = nf_ele ? nf_ele->stake : 0UL;
590 :
591 0 : int notar_verified = 0;
592 0 : int notar_fallback_verified = 0;
593 0 : int newly_notar = 0;
594 0 : fd_bls_agg_t notar_agg[1];
595 0 : fd_bls_agg_t nf_agg[1];
596 :
597 0 : if( FD_UNLIKELY( ( fd_epoch_info_is_quorum ( epoch_info, nf_stake + notar_stake ) && !slot_state_is_notar_fallback_or_stronger( self, block_hash ) ) ||
598 0 : ( fd_epoch_info_is_quorum ( epoch_info, notar_stake ) && self->certs.notar.slot==ULONG_MAX ) ||
599 0 : ( fd_epoch_info_is_strong_quorum( epoch_info, notar_stake ) && self->certs.fast_final.slot==ULONG_MAX ) ) ) {
600 0 : fd_bls_set_t bad_notar[ fd_bls_set_word_cnt ];
601 0 : notar_verified = verify_subtract_votes( self, states, FD_VOTE_KIND_NOTAR, block_hash, notar_agg, bad_notar );
602 0 : fd_bls_set_union( bad, bad, bad_notar );
603 0 : if( FD_UNLIKELY( fd_bls_set_test( bad_notar, rank ) ) ) return 0;
604 :
605 0 : notar_stake = vote_stake_pool_ele( notar_hash_stake_pool, notar )->stake;
606 0 : newly_notar = notar_verified && fd_epoch_info_is_quorum( epoch_info, notar_stake ) && self->certs.notar.slot==ULONG_MAX;
607 0 : if( FD_LIKELY( nf_ele && !newly_notar && !slot_state_is_notar_fallback_or_stronger( self, block_hash ) ) ) {
608 0 : fd_bls_set_t bad_nf[ fd_bls_set_word_cnt ];
609 0 : notar_fallback_verified = verify_subtract_votes( self, states, FD_VOTE_KIND_NOTAR_FALLBACK, block_hash, nf_agg, bad_nf );
610 0 : fd_bls_set_union( bad, bad, bad_nf );
611 0 : }
612 0 : }
613 :
614 0 : if( FD_UNLIKELY( !newly_notar && !slot_state_is_notar_fallback_or_stronger( self, block_hash ) ) ) {
615 0 : fd_cert_notar_fallback_t cert = { .slot = slot, .shred_version = shred_version }; memcpy( &cert.block_hash, block_hash, sizeof(fd_block_hash_t) );
616 :
617 0 : if( FD_LIKELY( notar_verified ) ) {
618 0 : cert.agg_notar = *notar_agg;
619 0 : cert.stake += vote_stake_pool_ele( notar_hash_stake_pool, notar )->stake;
620 0 : }
621 0 : if( FD_LIKELY( notar_fallback_verified ) ) {
622 0 : cert.agg_notar_fallback = *nf_agg;
623 0 : cert.stake += nf_ele->stake;
624 0 : }
625 0 : if( FD_LIKELY( fd_epoch_info_is_quorum( epoch_info, cert.stake ) ) ) {
626 0 : out_cert_events[( *out_cert_event_cnt )++] = ( fd_cert_t ){ .kind = FD_CERT_KIND_NOTAR_FALLBACK, .notar_fallback = cert };
627 0 : }
628 0 : }
629 :
630 0 : if( FD_UNLIKELY( newly_notar ) ) {
631 0 : fd_cert_notar_t cert = { .slot = slot, .shred_version = shred_version, .stake = notar_stake, .agg = *notar_agg }; memcpy( &cert.block_hash, block_hash, sizeof(fd_block_hash_t) );
632 0 : out_cert_events[ (*out_cert_event_cnt)++ ] = (fd_cert_t){ .kind = FD_CERT_KIND_NOTAR, .notar = cert };
633 0 : }
634 :
635 0 : if( FD_UNLIKELY( notar_verified && fd_epoch_info_is_strong_quorum( epoch_info, notar_stake ) && self->certs.fast_final.slot==ULONG_MAX ) ) {
636 0 : fd_cert_fast_final_t cert = { .slot = slot, .shred_version = shred_version, .stake = notar_stake, .agg = *notar_agg }; memcpy( &cert.block_hash, block_hash, sizeof(fd_block_hash_t) );
637 0 : out_cert_events[ (*out_cert_event_cnt)++ ] = (fd_cert_t){ .kind = FD_CERT_KIND_FAST_FINAL, .fast_final = cert };
638 0 : }
639 :
640 0 : return 1;
641 0 : }
642 :
643 : static int
644 : count_notar_fallback_stake( slot_state_t * self,
645 : slot_states_t * states,
646 : fd_vote_notar_fallback_t const * vote,
647 : ulong stake,
648 : fd_cert_t * out_cert_events,
649 : ulong * out_cert_event_cnt,
650 0 : fd_bls_set_t * bad ) {
651 0 : fd_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 ) );
652 0 : ulong slot = vote->slot;
653 0 : fd_block_hash_t const * block_hash = &vote->block_hash;
654 0 : fd_block_id_t key = fd_block_id( self->slot, block_hash->uc );
655 0 : ulong rank = vote->rank;
656 0 : ushort shred_version = vote->shred_version;
657 :
658 0 : slot_votes_t * votes = &self->votes;
659 0 : vote_stake_map_t * notar_hash_stake_map = states->notar_hash_stake_map;
660 0 : vote_stake_map_t * notar_fallback_hash_stake_map = states->notar_fallback_hash_stake_map;
661 0 : vote_stake_ele_t * notar_hash_stake_pool = states->notar_hash_stake_pool;
662 0 : vote_stake_ele_t * notar_fallback_hash_stake_pool = states->notar_fallback_hash_stake_pool;
663 0 : vote_stake_ele_t * nf_ele = vote_stake_map_ele_query( notar_fallback_hash_stake_map, &key, NULL, notar_fallback_hash_stake_pool );
664 0 : if( FD_UNLIKELY( !nf_ele ) ) {
665 0 : FD_TEST( vote_stake_pool_free( notar_fallback_hash_stake_pool ) );
666 0 : nf_ele = vote_stake_pool_ele_acquire( notar_fallback_hash_stake_pool );
667 0 : nf_ele->block_id = key;
668 0 : nf_ele->stake = 0UL;
669 0 : nf_ele->prev = (uint)( votes->notar_fallback_hash_stake_cnt ? votes->notar_fallback_hash_stake_head : vote_stake_pool_idx_null( notar_fallback_hash_stake_pool ) );
670 0 : vote_stake_map_ele_insert( notar_fallback_hash_stake_map, nf_ele, notar_fallback_hash_stake_pool );
671 0 : votes->notar_fallback_hash_stake_head = (uint)vote_stake_pool_idx( notar_fallback_hash_stake_pool, nf_ele );
672 0 : votes->notar_fallback_hash_stake_cnt++;
673 0 : }
674 0 : votes->notar_fallback_hash_stake_idx[ rank ][ votes->notar_fallback_sig_cnt[ rank ]-1UL ] = (uint)vote_stake_pool_idx( notar_fallback_hash_stake_pool, nf_ele );
675 0 : nf_ele->stake += stake;
676 :
677 0 : ulong notar = vote_stake_map_idx_query_const( notar_hash_stake_map, &key, ULONG_MAX, notar_hash_stake_pool );
678 0 : ulong nf_stake = nf_ele->stake;
679 0 : ulong notar_stake = notar!=ULONG_MAX ? vote_stake_pool_ele( notar_hash_stake_pool, notar )->stake : 0UL;
680 0 : if( FD_UNLIKELY( fd_epoch_info_is_quorum( epoch_info, nf_stake + notar_stake ) && !slot_state_is_notar_fallback_or_stronger( self, block_hash ) ) ) {
681 0 : fd_bls_agg_t nf_agg[1];
682 0 : fd_bls_agg_t notar_agg[1];
683 0 : fd_bls_set_t bad_nf[ fd_bls_set_word_cnt ];
684 0 : int nf_verified = verify_subtract_votes( self, states, FD_VOTE_KIND_NOTAR_FALLBACK, block_hash, nf_agg, bad_nf );
685 0 : fd_bls_set_union( bad, bad, bad_nf );
686 0 : if( FD_UNLIKELY( fd_bls_set_test( bad_nf, rank ) ) ) return 0;
687 0 : int notar_verified = 0;
688 0 : if( FD_LIKELY( notar!=ULONG_MAX ) ) {
689 0 : fd_bls_set_t bad_notar[ fd_bls_set_word_cnt ];
690 0 : notar_verified = verify_subtract_votes( self, states, FD_VOTE_KIND_NOTAR, block_hash, notar_agg, bad_notar );
691 0 : fd_bls_set_union( bad, bad, bad_notar );
692 0 : }
693 :
694 0 : fd_cert_notar_fallback_t cert = { .slot = slot, .shred_version = shred_version }; memcpy( &cert.block_hash, block_hash, sizeof(fd_block_hash_t) );
695 :
696 0 : if( FD_LIKELY( notar_verified ) ) {
697 0 : cert.agg_notar = *notar_agg;
698 0 : cert.stake += vote_stake_pool_ele( notar_hash_stake_pool, notar )->stake;
699 0 : }
700 0 : if( FD_LIKELY( nf_verified ) ) {
701 0 : cert.agg_notar_fallback = *nf_agg;
702 0 : cert.stake += nf_ele->stake;
703 0 : }
704 0 : if( FD_LIKELY( fd_epoch_info_is_quorum( epoch_info, cert.stake ) ) ) {
705 0 : out_cert_events[ (*out_cert_event_cnt)++ ] = (fd_cert_t){ .kind = FD_CERT_KIND_NOTAR_FALLBACK, .notar_fallback = cert };
706 0 : }
707 0 : }
708 :
709 0 : return 1;
710 0 : }
711 :
712 : static int
713 : count_skip_stake( slot_state_t * self,
714 : slot_states_t * states,
715 : fd_vote_t const * vote,
716 : ulong stake,
717 : fd_cert_t * out_cert_events,
718 : ulong * out_cert_event_cnt,
719 : fd_block_id_t * out_safe_to_notar,
720 : ulong * out_safe_to_notar_cnt,
721 : ulong * out_safe_to_skip,
722 : ulong * out_safe_to_skip_cnt,
723 : fd_block_id_t * out_repair_events,
724 : ulong * out_repair_event_cnt,
725 0 : fd_bls_set_t * bad ) {
726 0 : fd_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 ) );
727 0 : int fallback = vote->kind==FD_VOTE_KIND_SKIP_FALLBACK;
728 0 : ulong slot = fd_vote_slot( vote );
729 0 : ulong rank = fd_vote_rank( vote );
730 0 : ushort shred_version = fd_vote_shred_version( vote );
731 0 : fd_bls_sig_t const * sig = fd_vote_sig( vote );
732 0 : fd_bls_pub_t const * pub = fd_epoch_info_pubkey( epoch_info, rank );
733 :
734 0 : slot_votes_t * votes = &self->votes;
735 0 : if( FD_UNLIKELY( fallback ) ) { votes->skip_fallback_stake += stake; fd_bls_pub_add( &votes->skip_fallback_agg.pub, &votes->skip_fallback_agg.pub, pub ); fd_bls_sig_add( &votes->skip_fallback_agg.sig, &votes->skip_fallback_agg.sig, sig ); fd_bls_set_insert( votes->skip_fallback_agg.set, rank ); }
736 0 : else { votes->skip_stake += stake; fd_bls_pub_add( &votes->skip_agg.pub, &votes->skip_agg.pub, pub ); fd_bls_sig_add( &votes->skip_agg.sig, &votes->skip_agg.sig, sig ); fd_bls_set_insert( votes->skip_agg.set, rank ); }
737 :
738 0 : block_hash_set_t pending = self->pending_safe_to_notar;
739 0 : for( ulong i=0UL; i<pending.cnt; i++ ) {
740 0 : if( FD_UNLIKELY( block_hash_set_contains( &self->sent_safe_to_notar, &pending.hash[i] ) ) ) continue;
741 0 : switch( check_safe_to_notar( self, states, &pending.hash[i], bad ) ) {
742 0 : case SAFE_TO_NOTAR_STATUS_SAFE_TO_NOTAR:
743 0 : out_safe_to_notar[ (*out_safe_to_notar_cnt)++ ] = fd_block_id( slot, pending.hash[i].uc );
744 0 : break;
745 0 : case SAFE_TO_NOTAR_STATUS_MISSING_BLOCK: {
746 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(fd_block_hash_t) ) ) ) break;
747 0 : if( FD_LIKELY( j==*out_repair_event_cnt ) ) out_repair_events[ (*out_repair_event_cnt)++ ] = fd_block_id( slot, pending.hash[i].uc );
748 0 : break;
749 0 : }
750 0 : default:
751 0 : break;
752 0 : }
753 0 : }
754 :
755 0 : if( FD_UNLIKELY( check_safe_to_skip( self, states, bad ) ) ) {
756 0 : out_safe_to_skip[ (*out_safe_to_skip_cnt)++ ] = slot;
757 0 : }
758 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;
759 :
760 0 : ulong total_skip_stake = votes->skip_stake + votes->skip_fallback_stake;
761 :
762 0 : int skip_verified = 0;
763 0 : int skip_fallback_verified = 0;
764 0 : if( FD_UNLIKELY( fd_epoch_info_is_quorum( epoch_info, total_skip_stake ) && self->certs.skip.slot==ULONG_MAX ) ) {
765 0 : if( FD_LIKELY( !fd_bls_set_is_null( votes->skip_agg.set ) ) ) {
766 0 : fd_bls_set_t bad_skip[ fd_bls_set_word_cnt ];
767 0 : skip_verified = verify_subtract_votes( self, states, FD_VOTE_KIND_SKIP, NULL, &votes->skip_agg, bad_skip );
768 0 : fd_bls_set_union( bad, bad, bad_skip );
769 0 : }
770 0 : if( FD_LIKELY( !fd_bls_set_is_null( votes->skip_fallback_agg.set ) ) ) {
771 0 : fd_bls_set_t bad_sf[ fd_bls_set_word_cnt ];
772 0 : skip_fallback_verified = verify_subtract_votes( self, states, FD_VOTE_KIND_SKIP_FALLBACK, NULL, &votes->skip_fallback_agg, bad_sf );
773 0 : fd_bls_set_union( bad, bad, bad_sf );
774 0 : }
775 0 : if( FD_UNLIKELY( !fd_bls_set_test( fallback ? votes->skip_fallback_agg.set : votes->skip_agg.set, rank ) ) ) return 0;
776 :
777 0 : fd_cert_skip_t cert = { .slot = slot, .shred_version = shred_version };
778 :
779 0 : if( FD_LIKELY( skip_verified ) ) {
780 0 : cert.agg_skip = votes->skip_agg;
781 0 : cert.stake += votes->skip_stake;
782 0 : }
783 0 : if( FD_LIKELY( skip_fallback_verified ) ) {
784 0 : cert.agg_skip_fallback = votes->skip_fallback_agg;
785 0 : cert.stake += votes->skip_fallback_stake;
786 0 : }
787 0 : if( FD_LIKELY( fd_epoch_info_is_quorum( epoch_info, cert.stake ) ) ) {
788 0 : out_cert_events[ (*out_cert_event_cnt)++ ] = (fd_cert_t){ .kind = FD_CERT_KIND_SKIP, .skip = cert };
789 0 : }
790 0 : }
791 :
792 0 : return 1;
793 0 : }
794 :
795 : static int
796 : count_final_stake( slot_state_t * self,
797 : slot_states_t * states,
798 : fd_vote_final_t const * vote,
799 : ulong stake,
800 : fd_cert_t * out_cert_events,
801 : ulong * out_cert_event_cnt,
802 0 : fd_bls_set_t * bad ) {
803 0 : fd_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 ) );
804 0 : ulong slot = vote->slot;
805 0 : ulong rank = vote->rank;
806 0 : ushort shred_version = vote->shred_version;
807 0 : fd_bls_sig_t const * sig = &vote->sig;
808 0 : fd_bls_pub_t const * pub = fd_epoch_info_pubkey( epoch_info, rank );
809 :
810 0 : slot_votes_t * votes = &self->votes;
811 0 : votes->final_stake += stake;
812 0 : fd_bls_pub_add( &votes->final_agg.pub, &votes->final_agg.pub, pub );
813 0 : fd_bls_sig_add( &votes->final_agg.sig, &votes->final_agg.sig, sig );
814 0 : fd_bls_set_insert( votes->final_agg.set, rank );
815 0 : if( FD_UNLIKELY( fd_epoch_info_is_quorum( epoch_info, votes->final_stake ) && self->certs.final.slot==ULONG_MAX ) ) {
816 0 : fd_bls_set_t bad_final[ fd_bls_set_word_cnt ];
817 0 : int final_verified = verify_subtract_votes( self, states, FD_VOTE_KIND_FINAL, NULL, &votes->final_agg, bad_final );
818 0 : fd_bls_set_union( bad, bad, bad_final );
819 0 : if( FD_UNLIKELY( fd_bls_set_test( bad_final, rank ) ) ) return 0;
820 0 : if( FD_UNLIKELY( !final_verified || !fd_epoch_info_is_quorum( epoch_info, votes->final_stake ) ) ) return 1;
821 0 : fd_cert_final_t cert = { .slot = slot, .shred_version = shred_version, .stake = votes->final_stake, .agg = votes->final_agg };
822 0 : out_cert_events[ (*out_cert_event_cnt)++ ] = (fd_cert_t){ .kind = FD_CERT_KIND_FINAL, .final = cert };
823 0 : }
824 :
825 0 : return 1;
826 0 : }
827 :
828 : /* Resets self to an empty slot state. self must be zeroed memory or a
829 : slot state. */
830 :
831 : static void
832 : slot_state_null( slot_state_t * self,
833 0 : slot_states_t * states ) {
834 0 : slot_votes_t * votes = &self->votes;
835 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 ) ) bls_sig_pool_idx_release( states->bls_sig_pool, votes->notar_sig_idx [ rank ] );
836 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 ) ) bls_sig_pool_idx_release( states->bls_sig_pool, votes->skip_sig_idx [ rank ] );
837 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 ) ) bls_sig_pool_idx_release( states->bls_sig_pool, votes->skip_fallback_sig_idx[ rank ] );
838 0 : for( ulong rank = fd_bls_set_const_iter_init( votes->final_agg.set ); !fd_bls_set_const_iter_done( rank ); rank = fd_bls_set_const_iter_next( votes->final_agg.set, rank ) ) bls_sig_pool_idx_release( states->bls_sig_pool, votes->final_sig_idx [ rank ] );
839 0 : for( ulong rank=0UL; rank<FD_VOTOR_VAT_MAX; rank++ ) {
840 0 : for( ulong j=0UL; j<votes->notar_fallback_sig_cnt[ rank ]; j++ ) bls_sig_pool_idx_release( states->bls_sig_pool, votes->notar_fallback_sig_idx[ rank ][ j ] );
841 0 : }
842 0 : ulong notar = votes->notar_hash_stake_head;
843 0 : for( ulong i=0UL; i<votes->notar_hash_stake_cnt; i++ ) {
844 0 : ulong prev = vote_stake_pool_ele( states->notar_hash_stake_pool, notar )->prev;
845 0 : vote_stake_map_idx_remove( states->notar_hash_stake_map, &vote_stake_pool_ele( states->notar_hash_stake_pool, notar )->block_id, ULONG_MAX, states->notar_hash_stake_pool );
846 0 : vote_stake_pool_idx_release( states->notar_hash_stake_pool, notar );
847 0 : notar = prev;
848 0 : }
849 0 : votes->notar_hash_stake_cnt = 0UL;
850 0 : fd_bls_set_null( votes->notar_set );
851 0 : ulong nf_idx = votes->notar_fallback_hash_stake_head;
852 0 : for( ulong i=0UL; i<votes->notar_fallback_hash_stake_cnt; i++ ) {
853 0 : ulong prev = vote_stake_pool_ele( states->notar_fallback_hash_stake_pool, nf_idx )->prev;
854 0 : vote_stake_map_idx_remove( states->notar_fallback_hash_stake_map, &vote_stake_pool_ele( states->notar_fallback_hash_stake_pool, nf_idx )->block_id, ULONG_MAX, states->notar_fallback_hash_stake_pool );
855 0 : vote_stake_pool_idx_release( states->notar_fallback_hash_stake_pool, nf_idx );
856 0 : nf_idx = prev;
857 0 : }
858 0 : votes->notar_fallback_hash_stake_cnt = 0UL;
859 0 : fd_memset( votes->notar_fallback_sig_cnt, 0, sizeof(votes->notar_fallback_sig_cnt) );
860 0 : votes->skip_stake = 0UL;
861 0 : fd_bls_agg_null( &votes->skip_agg );
862 0 : votes->skip_fallback_stake = 0UL;
863 0 : fd_bls_agg_null( &votes->skip_fallback_agg );
864 0 : votes->final_stake = 0UL;
865 0 : fd_bls_agg_null( &votes->final_agg );
866 0 : votes->notar_or_skip_stake = 0UL;
867 0 : votes->top_notar_stake = 0UL;
868 0 : fd_memset( &votes->top_notar_hash, 0, sizeof(fd_block_hash_t) );
869 :
870 0 : self->certs.notar.slot = ULONG_MAX;
871 0 : self->certs.notar_fallback_cnt = 0UL;
872 0 : self->certs.skip.slot = ULONG_MAX;
873 0 : self->certs.fast_final.slot = ULONG_MAX;
874 0 : self->certs.final.slot = ULONG_MAX;
875 :
876 0 : self->parents_cnt = 0UL;
877 0 : self->pending_safe_to_notar.cnt = 0UL;
878 0 : self->verified_safe_to_notar.cnt = 0UL;
879 0 : self->sent_safe_to_notar.cnt = 0UL;
880 0 : self->sent_safe_to_skip = 0;
881 0 : }
882 :
883 : /* Replaces our rank own_rank (USHORT_MAX if unstaked). */
884 :
885 : /* Definition 13. SlotState::add_cert */
886 :
887 : static void
888 : slot_state_add_cert( slot_state_t * self,
889 0 : fd_cert_t const * cert ) {
890 0 : switch( cert->kind ) {
891 0 : case FD_CERT_KIND_NOTAR: {
892 0 : self->certs.notar = cert->notar;
893 0 : break;
894 0 : }
895 0 : case FD_CERT_KIND_NOTAR_FALLBACK: {
896 0 : fd_cert_notar_fallback_t const * n = &cert->notar_fallback;
897 0 : if( FD_LIKELY( !slot_state_is_notar_fallback( self, &n->block_hash ) ) ) {
898 0 : FD_CHECK_CRIT( self->certs.notar_fallback_cnt<FD_VOTOR_NOTAR_FALLBACK_CERT_MAX, "consensus safety violation" ); /* Lemma 48 */
899 0 : self->certs.notar_fallback[ self->certs.notar_fallback_cnt++ ] = *n;
900 0 : }
901 0 : break;
902 0 : }
903 0 : case FD_CERT_KIND_SKIP: {
904 0 : self->certs.skip = cert->skip;
905 0 : break;
906 0 : }
907 0 : case FD_CERT_KIND_FAST_FINAL: {
908 0 : self->certs.fast_final = cert->fast_final;
909 0 : break;
910 0 : }
911 0 : case FD_CERT_KIND_FINAL: {
912 0 : self->certs.final = cert->final;
913 0 : break;
914 0 : }
915 0 : default:
916 0 : FD_LOG_ERR(( "invalid cert kind %u", cert->kind ));
917 0 : }
918 0 : }
919 :
920 : /* Definition 12. SlotState::add_vote */
921 :
922 : static int
923 : slot_state_add_vote( slot_state_t * self,
924 : slot_states_t * states,
925 : fd_vote_t const * vote,
926 : ulong stake,
927 : fd_cert_t * out_cert_events,
928 : ulong * out_cert_event_cnt,
929 : fd_block_id_t * out_safe_to_notar,
930 : ulong * out_safe_to_notar_cnt,
931 : ulong * out_safe_to_skip,
932 : ulong * out_safe_to_skip_cnt,
933 : fd_block_id_t * out_repair_events,
934 : ulong * out_repair_event_cnt,
935 0 : fd_bls_set_t * bad ) {
936 0 : ulong slot = fd_vote_slot( vote );
937 0 : ulong rank = fd_vote_rank( vote );
938 :
939 0 : fd_bls_sig_t const * sig = fd_vote_sig( vote );
940 :
941 0 : FD_TEST( bls_sig_pool_free( states->bls_sig_pool ) );
942 0 : uint sig_idx = (uint)bls_sig_pool_idx_acquire( states->bls_sig_pool );
943 0 : bls_sig_pool_ele( states->bls_sig_pool, sig_idx )->sig = *sig;
944 :
945 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 );
946 0 : self->shred_version = fd_vote_shred_version( vote );
947 0 : int err;
948 0 : switch( vote->kind ) {
949 0 : case FD_VOTE_KIND_NOTAR:
950 0 : self->votes.notar_sig_idx[ rank ] = sig_idx;
951 0 : fd_bls_set_insert( self->votes.notar_set, rank );
952 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 );
953 0 : break;
954 0 : case FD_VOTE_KIND_NOTAR_FALLBACK:
955 0 : FD_TEST( self->votes.notar_fallback_sig_cnt[ rank ]<FD_VOTOR_NOTAR_FALLBACK_VOTE_MAX );
956 0 : self->votes.notar_fallback_sig_idx[ rank ][ self->votes.notar_fallback_sig_cnt[ rank ] ] = sig_idx;
957 0 : self->votes.notar_fallback_sig_cnt[ rank ]++;
958 0 : err = count_notar_fallback_stake( self, states, &vote->notar_fallback, stake, out_cert_events, out_cert_event_cnt, bad );
959 0 : break;
960 0 : case FD_VOTE_KIND_SKIP:
961 0 : self->votes.skip_sig_idx[ rank ] = sig_idx;
962 0 : self->votes.notar_or_skip_stake += stake;
963 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 );
964 0 : break;
965 0 : case FD_VOTE_KIND_SKIP_FALLBACK:
966 0 : self->votes.skip_fallback_sig_idx[ rank ] = sig_idx;
967 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 );
968 0 : break;
969 0 : case FD_VOTE_KIND_FINAL:
970 0 : self->votes.final_sig_idx[ rank ] = sig_idx;
971 0 : err = count_final_stake( self, states, &vote->final, stake, out_cert_events, out_cert_event_cnt, bad );
972 0 : break;
973 0 : default:
974 0 : FD_LOG_CRIT(( "unreachable" ));
975 0 : }
976 :
977 0 : if( FD_UNLIKELY( rank==self->own_rank ) ) {
978 0 : block_hash_set_t pending = self->pending_safe_to_notar;
979 0 : for( ulong i=0UL; i<pending.cnt; i++ ) {
980 0 : if( FD_UNLIKELY( block_hash_set_contains( &self->sent_safe_to_notar, &pending.hash[i] ) ) ) continue;
981 0 : switch( check_safe_to_notar( self, states, &pending.hash[i], bad ) ) {
982 0 : case SAFE_TO_NOTAR_STATUS_SAFE_TO_NOTAR: out_safe_to_notar[ (*out_safe_to_notar_cnt)++ ] = fd_block_id( slot, pending.hash[i].uc ); break;
983 0 : case SAFE_TO_NOTAR_STATUS_MISSING_BLOCK: {
984 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(fd_block_hash_t) ) ) ) break;
985 0 : if( FD_LIKELY( j==*out_repair_event_cnt ) ) out_repair_events[ (*out_repair_event_cnt)++ ] = fd_block_id( slot, pending.hash[i].uc );
986 0 : break;
987 0 : }
988 0 : default: break;
989 0 : }
990 0 : }
991 0 : }
992 :
993 0 : return err;
994 0 : }
995 :
996 : /* Definition 16, SafeToNotar. SlotState::notify_parent_known */
997 :
998 : static void
999 : slot_state_notify_parent_known( slot_state_t * self,
1000 : slot_states_t * states,
1001 : fd_block_hash_t const * block_hash,
1002 0 : fd_bls_set_t * bad ) {
1003 0 : fd_bls_set_null( bad );
1004 0 : for( ulong i=0UL; i<self->parents_cnt; i++ ) {
1005 0 : if( FD_UNLIKELY( !memcmp( &self->parents[i].hash, block_hash, sizeof(fd_block_hash_t) ) ) ) return;
1006 0 : }
1007 0 : FD_TEST( self->parents_cnt < FD_VOTOR_EQVOC_MAX );
1008 0 : parent_status_t * e = &self->parents[ self->parents_cnt++ ];
1009 0 : memcpy( &e->hash, block_hash, sizeof(fd_block_hash_t) );
1010 0 : e->kind = PARENT_STATUS_KNOWN;
1011 :
1012 0 : check_safe_to_notar( self, states, block_hash, bad );
1013 0 : }
1014 :
1015 : /* Definition 16, SafeToNotar. SlotState::notify_parent_certified */
1016 :
1017 : static int
1018 : slot_state_notify_parent_certified( slot_state_t * self,
1019 : slot_states_t * states,
1020 : fd_block_hash_t const * block_hash,
1021 0 : fd_bls_set_t * bad ) {
1022 0 : fd_bls_set_null( bad );
1023 0 : parent_status_t * parent = NULL;
1024 0 : for( ulong i=0UL; i<self->parents_cnt; i++ ) {
1025 0 : if( FD_LIKELY( !memcmp( &self->parents[i].hash, block_hash, sizeof(fd_block_hash_t) ) ) ) { parent = &self->parents[i]; break; }
1026 0 : }
1027 0 : FD_TEST( parent );
1028 0 : parent->kind = PARENT_STATUS_CERTIFIED;
1029 :
1030 0 : if( FD_UNLIKELY( block_hash_set_contains( &self->sent_safe_to_notar, block_hash ) ) ) return 0;
1031 :
1032 0 : switch( check_safe_to_notar( self, states, block_hash, bad ) ) {
1033 0 : case SAFE_TO_NOTAR_STATUS_MISSING_BLOCK: return -1; /* unreachable: this block's parents entry was found above */
1034 0 : case SAFE_TO_NOTAR_STATUS_AWAITING_VOTES: return 0;
1035 0 : case SAFE_TO_NOTAR_STATUS_SAFE_TO_NOTAR: return 1;
1036 0 : default: FD_LOG_CRIT(( "unreachable" ));
1037 0 : }
1038 0 : }
1039 :
1040 : /* SlotState::check_slashable_offence */
1041 :
1042 : FD_FN_PURE static int
1043 : slot_state_check_slashable_offence( slot_state_t const * self,
1044 : slot_states_t const * states,
1045 0 : fd_vote_t const * vote ) {
1046 0 : ulong voter = fd_vote_rank( vote );
1047 0 : slot_votes_t const * votes = &self->votes;
1048 :
1049 0 : switch( vote->kind ) {
1050 0 : case FD_VOTE_KIND_NOTAR: {
1051 0 : if( FD_UNLIKELY( fd_bls_set_test( votes->skip_agg.set, voter ) ) ) {
1052 0 : return SLASHABLE_OFFENCE_SKIP_AND_NOTARIZE;
1053 0 : }
1054 0 : if( FD_UNLIKELY( fd_bls_set_test( votes->notar_set, voter ) ) ) {
1055 0 : if( FD_UNLIKELY( memcmp( &vote_stake_pool_ele_const( states->notar_hash_stake_pool, votes->notar_hash_stake_idx[ voter ] )->block_id.hash, &vote->notar.block_hash, sizeof(fd_block_hash_t) ) ) ) return SLASHABLE_OFFENCE_NOTAR_DIFFERENT_HASH;
1056 0 : }
1057 0 : break;
1058 0 : }
1059 :
1060 0 : case FD_VOTE_KIND_NOTAR_FALLBACK:
1061 0 : if( FD_UNLIKELY( fd_bls_set_test( votes->final_agg.set, voter ) ) ) {
1062 0 : return SLASHABLE_OFFENCE_NOTAR_FALLBACK_AND_FINALIZE;
1063 0 : }
1064 0 : break;
1065 :
1066 0 : case FD_VOTE_KIND_SKIP:
1067 0 : if( FD_UNLIKELY( fd_bls_set_test( votes->final_agg.set, voter ) ) ) {
1068 0 : return SLASHABLE_OFFENCE_SKIP_AND_FINALIZE;
1069 0 : }
1070 0 : if( FD_UNLIKELY( fd_bls_set_test( votes->notar_set, voter ) ) ) return SLASHABLE_OFFENCE_SKIP_AND_NOTARIZE;
1071 0 : break;
1072 :
1073 0 : case FD_VOTE_KIND_SKIP_FALLBACK:
1074 0 : if( FD_UNLIKELY( fd_bls_set_test( votes->final_agg.set, voter ) ) ) {
1075 0 : return SLASHABLE_OFFENCE_SKIP_AND_FINALIZE;
1076 0 : }
1077 0 : break;
1078 :
1079 0 : case FD_VOTE_KIND_FINAL: {
1080 0 : if( FD_UNLIKELY( fd_bls_set_test( votes->skip_agg.set, voter ) || fd_bls_set_test( votes->skip_fallback_agg.set, voter ) ) ) {
1081 0 : return SLASHABLE_OFFENCE_SKIP_AND_FINALIZE;
1082 0 : }
1083 0 : if( FD_UNLIKELY( votes->notar_fallback_sig_cnt[ voter ] ) ) {
1084 0 : return SLASHABLE_OFFENCE_NOTAR_FALLBACK_AND_FINALIZE;
1085 0 : }
1086 0 : break;
1087 0 : }
1088 :
1089 0 : default:
1090 0 : FD_LOG_ERR(( "invalid vote kind %u", vote->kind ));
1091 0 : }
1092 0 : return SLASHABLE_OFFENCE_NONE;
1093 0 : }
1094 :
1095 : /* Definition 12. SlotState::should_ignore_vote */
1096 :
1097 : FD_FN_PURE static int
1098 : slot_state_should_ignore_vote( slot_state_t const * self,
1099 : slot_states_t const * states,
1100 0 : fd_vote_t const * vote ) {
1101 0 : ulong voter = fd_vote_rank( vote );
1102 0 : slot_votes_t const * votes = &self->votes;
1103 0 : vote_stake_ele_t const * notar_hash_stake_pool = states->notar_hash_stake_pool;
1104 0 : vote_stake_ele_t const * notar_fallback_hash_stake_pool = states->notar_fallback_hash_stake_pool;
1105 0 : switch( vote->kind ) {
1106 0 : case FD_VOTE_KIND_NOTAR: {
1107 0 : if( FD_UNLIKELY( fd_bls_set_test( votes->notar_set, voter ) ) ) return 1;
1108 :
1109 0 : fd_block_hash_t const * hash = &vote->notar.block_hash;
1110 0 : for( ulong j=0UL; j<votes->notar_fallback_sig_cnt[ voter ]; j++ ) {
1111 0 : if( FD_UNLIKELY( !memcmp( &vote_stake_pool_ele_const( notar_fallback_hash_stake_pool, votes->notar_fallback_hash_stake_idx[ voter ][ j ] )->block_id.hash, hash, sizeof(fd_block_hash_t) ) ) ) return 1;
1112 0 : }
1113 0 : return 0;
1114 0 : }
1115 0 : case FD_VOTE_KIND_NOTAR_FALLBACK: {
1116 0 : if( FD_UNLIKELY( votes->notar_fallback_sig_cnt[ voter ]>=FD_VOTOR_NOTAR_FALLBACK_VOTE_MAX ) ) return 1;
1117 :
1118 0 : fd_block_hash_t const * hash = &vote->notar_fallback.block_hash;
1119 0 : for( ulong j=0UL; j<votes->notar_fallback_sig_cnt[ voter ]; j++ ) {
1120 0 : if( FD_UNLIKELY( !memcmp( &vote_stake_pool_ele_const( notar_fallback_hash_stake_pool, votes->notar_fallback_hash_stake_idx[ voter ][ j ] )->block_id.hash, hash, sizeof(fd_block_hash_t) ) ) ) return 1;
1121 0 : }
1122 :
1123 0 : return fd_bls_set_test( votes->notar_set, voter ) && !memcmp( &vote_stake_pool_ele_const( notar_hash_stake_pool, votes->notar_hash_stake_idx[ voter ] )->block_id.hash, hash, sizeof(fd_block_hash_t) );
1124 0 : }
1125 0 : case FD_VOTE_KIND_SKIP:
1126 0 : case FD_VOTE_KIND_SKIP_FALLBACK:
1127 0 : return fd_bls_set_test( votes->skip_agg.set, voter ) || fd_bls_set_test( votes->skip_fallback_agg.set, voter );
1128 0 : case FD_VOTE_KIND_FINAL:
1129 0 : return fd_bls_set_test( votes->final_agg.set, voter );
1130 0 : default:
1131 0 : FD_LOG_ERR(( "invalid vote kind %u", vote->kind ));
1132 0 : }
1133 0 : return 0;
1134 0 : }
1135 :
1136 : /* Accessors for reward certs */
1137 :
1138 : static fd_bls_agg_t *
1139 : slot_state_notar_reward_agg( slot_state_t const * self,
1140 : slot_states_t const * states,
1141 0 : fd_bls_agg_t * out ) {
1142 0 : fd_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 ) );
1143 0 : fd_block_id_t key = fd_block_id( self->slot, self->votes.top_notar_hash.uc );
1144 0 : ulong top = vote_stake_map_idx_query_const( states->notar_hash_stake_map, &key, ULONG_MAX, states->notar_hash_stake_pool );
1145 0 : if( FD_UNLIKELY( top==ULONG_MAX ) ) return NULL;
1146 :
1147 0 : fd_bls_set_t set[ fd_bls_set_word_cnt ];
1148 0 : fd_bls_set_null( set );
1149 0 : for( ulong rank = fd_bls_set_const_iter_init( self->votes.notar_set );
1150 0 : !fd_bls_set_const_iter_done( rank );
1151 0 : rank = fd_bls_set_const_iter_next( self->votes.notar_set, rank ) ) {
1152 0 : if( FD_LIKELY( self->votes.notar_hash_stake_idx[ rank ]==top ) ) fd_bls_set_insert( set, rank );
1153 0 : }
1154 0 : fd_bls_sig_t sigs[ FD_VOTOR_VAT_MAX ];
1155 0 : slot_state_sigs( self, states, FD_VOTE_KIND_NOTAR, &key.hash, set, sigs );
1156 0 : fd_bls_agg_null( out );
1157 0 : fd_bls_agg_construct( out, epoch_info->pubkeys, sigs, set );
1158 0 : return out;
1159 0 : }
1160 :
1161 : FD_FN_PURE static fd_bls_agg_t const *
1162 0 : slot_state_skip_reward_agg( slot_state_t const * self ) {
1163 0 : return fd_bls_set_is_null( self->votes.skip_agg.set ) ? NULL : &self->votes.skip_agg;
1164 0 : }
1165 :
1166 : #define QUEUE_NAME pool_events
1167 0 : #define QUEUE_T fd_votor_pool_event_t
1168 : #include "../../util/tmpl/fd_queue_dynamic.c"
1169 :
1170 : /* repair_events are notifications that a block has reached SafeToNotar
1171 : (or stronger) status. It does not necessarily trigger actual repair
1172 : requests if we already have the block... (that is the responsibility
1173 : of the repair tile to answer). */
1174 :
1175 : #define QUEUE_NAME repair_events
1176 0 : #define QUEUE_T fd_block_id_t
1177 : #include "../../util/tmpl/fd_queue_dynamic.c"
1178 :
1179 : /* Protocol bounds (< 20% byzantine stake) on what burst adds push onto
1180 : queues drained to empty before the next add:
1181 :
1182 : - IMPLICITLY_FINALIZED / SKIPPED: once per slot of the window
1183 : - SAFE_TO_NOTAR of waiting children: 3 hashes per child slot (stake
1184 : arithmetic), and correct nodes notar a child only at its parent's
1185 : slot+1 or a window start, for each of up to burst certified
1186 : parents
1187 : - PARENT_READY: once per window start (ready flag), plus one per add
1188 : moving the window
1189 : - per add: 4 of CERT_CREATED, SAFE_TO_NOTAR and SAFE_TO_SKIP
1190 : - repair: 4 block ids per add (3 created certs, 1 missing block) */
1191 :
1192 0 : #define POOL_EVENT_MAX(slot_max,burst) ( (slot_max) \
1193 0 : + 3UL*( ((slot_max)+FD_VOTOR_SLOTS_PER_WINDOW-1UL)/FD_VOTOR_SLOTS_PER_WINDOW + (burst) ) \
1194 0 : + ( ((slot_max)+FD_VOTOR_SLOTS_PER_WINDOW-1UL)/FD_VOTOR_SLOTS_PER_WINDOW + (burst) ) \
1195 0 : + 4UL*(burst) )
1196 0 : #define REPAIR_EVENT_MAX(burst) ( 4UL*(burst) )
1197 :
1198 : struct s2n_waiting_parent_cert_ele {
1199 : fd_block_id_t parent;
1200 : ulong next;
1201 : ulong prev;
1202 : fd_block_id_t child;
1203 : };
1204 : typedef struct s2n_waiting_parent_cert_ele s2n_waiting_parent_cert_ele_t;
1205 :
1206 : #define POOL_NAME s2n_waiting_parent_cert_pool
1207 0 : #define POOL_T s2n_waiting_parent_cert_ele_t
1208 : #include "../../util/tmpl/fd_pool.c"
1209 :
1210 : #define MAP_NAME s2n_waiting_parent_cert_map
1211 0 : #define MAP_ELE_T s2n_waiting_parent_cert_ele_t
1212 0 : #define MAP_KEY parent.slot
1213 0 : #define MAP_KEY_HASH(key,seed) (fd_ulong_hash((*(key))^(seed)))
1214 0 : #define MAP_NEXT next
1215 : #define MAP_MULTI 1
1216 : #define MAP_OPTIMIZE_RANDOM_ACCESS_REMOVAL 1
1217 : #include "../../util/tmpl/fd_map_chain.c"
1218 :
1219 :
1220 : struct s2n_waiting_parent_cert {
1221 : s2n_waiting_parent_cert_ele_t * pool;
1222 : s2n_waiting_parent_cert_map_t * map;
1223 : };
1224 : typedef struct s2n_waiting_parent_cert s2n_waiting_parent_cert_t;
1225 :
1226 : struct __attribute__((aligned(128UL))) fd_votor_pool {
1227 : ulong slot_max;
1228 :
1229 : slot_states_t * slot_states;
1230 : fd_parent_ready_tracker_t * parent_ready_tracker;
1231 : fd_finality_tracker_t * finality_tracker;
1232 : s2n_waiting_parent_cert_t * s2n_waiting_parent_cert;
1233 :
1234 : fd_votor_pool_event_t * pool_events;
1235 : fd_block_id_t * repair_events;
1236 :
1237 : struct {
1238 : fd_parent_ready_t * parent_readys;
1239 : fd_block_id_t * implicitly_finalized;
1240 : ulong * implicitly_skipped;
1241 : } scratch;
1242 : };
1243 :
1244 : ulong
1245 0 : fd_votor_pool_align( void ) {
1246 0 : return alignof(fd_votor_pool_t);
1247 0 : }
1248 :
1249 : ulong
1250 : fd_votor_pool_footprint( ulong slot_max,
1251 0 : ulong burst ) {
1252 0 : if( FD_UNLIKELY( slot_max<FD_VOTOR_SLOTS_PER_WINDOW+FD_VOTOR_REWARD_SLOT_DELTA || slot_max>UINT_MAX/(FD_VOTOR_VAT_MAX*FD_VOTOR_VOTES_PER_SLOT_MAX) ) ) return 0UL;
1253 :
1254 0 : ulong slot_chain_cnt = slot_state_map_chain_cnt_est( slot_max );
1255 0 : ulong sig_max = slot_max*FD_VOTOR_VAT_MAX*FD_VOTOR_VOTES_PER_SLOT_MAX;
1256 0 : ulong notar_max = slot_max*FD_VOTOR_VAT_MAX;
1257 0 : ulong notar_chain_cnt = vote_stake_map_chain_cnt_est( notar_max );
1258 0 : ulong nf_max = slot_max*NOTAR_FALLBACK_HASH_MAX;
1259 0 : ulong nf_chain_cnt = vote_stake_map_chain_cnt_est( nf_max );
1260 0 : ulong s2n_max = slot_max*FD_VOTOR_EQVOC_MAX;
1261 0 : ulong s2n_chain_cnt = s2n_waiting_parent_cert_map_chain_cnt_est( s2n_max );
1262 0 : ulong ready_max = slot_max + FD_VOTOR_SLOTS_PER_WINDOW + 1UL; /* [root-FD_VOTOR_REWARD_SLOT_DELTA-FD_VOTOR_SLOTS_PER_WINDOW, root+slot_max-FD_VOTOR_REWARD_SLOT_DELTA] */
1263 0 : ulong event_max = POOL_EVENT_MAX( slot_max, burst );
1264 0 : ulong repair_max = REPAIR_EVENT_MAX( burst );
1265 :
1266 0 : return FD_LAYOUT_FINI(
1267 0 : FD_LAYOUT_APPEND(
1268 0 : FD_LAYOUT_APPEND(
1269 0 : FD_LAYOUT_APPEND(
1270 0 : FD_LAYOUT_APPEND(
1271 0 : FD_LAYOUT_APPEND(
1272 0 : FD_LAYOUT_APPEND(
1273 0 : FD_LAYOUT_APPEND(
1274 0 : FD_LAYOUT_APPEND(
1275 0 : FD_LAYOUT_APPEND(
1276 0 : FD_LAYOUT_APPEND(
1277 0 : FD_LAYOUT_APPEND(
1278 0 : FD_LAYOUT_APPEND(
1279 0 : FD_LAYOUT_APPEND(
1280 0 : FD_LAYOUT_APPEND(
1281 0 : FD_LAYOUT_APPEND(
1282 0 : FD_LAYOUT_APPEND(
1283 0 : FD_LAYOUT_APPEND(
1284 0 : FD_LAYOUT_APPEND(
1285 0 : FD_LAYOUT_APPEND(
1286 0 : FD_LAYOUT_INIT,
1287 0 : alignof(fd_votor_pool_t), sizeof(fd_votor_pool_t) ),
1288 0 : alignof(slot_states_t), sizeof(slot_states_t) ),
1289 0 : slot_state_pool_align(), slot_state_pool_footprint( slot_max ) ),
1290 0 : slot_state_map_align(), slot_state_map_footprint ( slot_chain_cnt ) ),
1291 0 : bls_sig_pool_align(), bls_sig_pool_footprint( sig_max ) ),
1292 0 : vote_stake_pool_align(), vote_stake_pool_footprint( notar_max ) ),
1293 0 : vote_stake_map_align(), vote_stake_map_footprint ( notar_chain_cnt ) ),
1294 0 : vote_stake_pool_align(), vote_stake_pool_footprint( nf_max ) ),
1295 0 : vote_stake_map_align(), vote_stake_map_footprint ( nf_chain_cnt ) ),
1296 0 : fd_parent_ready_tracker_align(), fd_parent_ready_tracker_footprint( ready_max ) ),
1297 0 : fd_finality_tracker_align(), fd_finality_tracker_footprint( slot_max ) ),
1298 0 : alignof(s2n_waiting_parent_cert_t), sizeof(s2n_waiting_parent_cert_t) ),
1299 0 : s2n_waiting_parent_cert_pool_align(), s2n_waiting_parent_cert_pool_footprint( s2n_max ) ),
1300 0 : s2n_waiting_parent_cert_map_align(), s2n_waiting_parent_cert_map_footprint ( s2n_chain_cnt ) ),
1301 0 : pool_events_align(), pool_events_footprint( event_max ) ),
1302 0 : repair_events_align(), repair_events_footprint( repair_max ) ),
1303 0 : alignof(fd_parent_ready_t), sizeof(fd_parent_ready_t) * slot_max ),
1304 0 : alignof(fd_block_id_t), sizeof(fd_block_id_t) * slot_max ),
1305 0 : alignof(ulong), sizeof(ulong) * slot_max ),
1306 0 : fd_votor_pool_align() );
1307 0 : }
1308 :
1309 : void *
1310 : fd_votor_pool_new( void * mem,
1311 : ulong slot_max,
1312 : ulong burst,
1313 0 : ulong seed ) {
1314 0 : if( FD_UNLIKELY( !mem ) ) {
1315 0 : FD_LOG_WARNING(( "NULL mem" ));
1316 0 : return NULL;
1317 0 : }
1318 0 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)mem, fd_votor_pool_align() ) ) ) {
1319 0 : FD_LOG_WARNING(( "misaligned mem" ));
1320 0 : return NULL;
1321 0 : }
1322 0 : ulong footprint = fd_votor_pool_footprint( slot_max, burst );
1323 0 : if( FD_UNLIKELY( !footprint ) ) {
1324 0 : FD_LOG_WARNING(( "bad slot_max (%lu)", slot_max ));
1325 0 : return NULL;
1326 0 : }
1327 0 : fd_memset( mem, 0, footprint );
1328 :
1329 0 : ulong s2n_max = slot_max*FD_VOTOR_EQVOC_MAX;
1330 0 : ulong slot_chain_cnt = slot_state_map_chain_cnt_est( slot_max );
1331 0 : ulong sig_max = slot_max*FD_VOTOR_VAT_MAX*FD_VOTOR_VOTES_PER_SLOT_MAX;
1332 0 : ulong notar_max = slot_max*FD_VOTOR_VAT_MAX;
1333 0 : ulong notar_chain_cnt = vote_stake_map_chain_cnt_est( notar_max );
1334 0 : ulong nf_max = slot_max*NOTAR_FALLBACK_HASH_MAX;
1335 0 : ulong nf_chain_cnt = vote_stake_map_chain_cnt_est( nf_max );
1336 0 : ulong s2n_chain_cnt = s2n_waiting_parent_cert_map_chain_cnt_est( s2n_max );
1337 0 : ulong ready_max = slot_max + FD_VOTOR_SLOTS_PER_WINDOW + 1UL;
1338 0 : ulong event_max = POOL_EVENT_MAX( slot_max, burst );
1339 0 : ulong repair_max = REPAIR_EVENT_MAX( burst );
1340 :
1341 :
1342 0 : FD_SCRATCH_ALLOC_INIT( l, mem );
1343 0 : fd_votor_pool_t * pool = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_votor_pool_t), sizeof(fd_votor_pool_t) );
1344 0 : void * slot_states = FD_SCRATCH_ALLOC_APPEND( l, alignof(slot_states_t), sizeof(slot_states_t) );
1345 0 : void * slot_state_pool = FD_SCRATCH_ALLOC_APPEND( l, slot_state_pool_align(), slot_state_pool_footprint( slot_max ) );
1346 0 : void * slot_state_map = FD_SCRATCH_ALLOC_APPEND( l, slot_state_map_align(), slot_state_map_footprint ( slot_chain_cnt ) );
1347 0 : void * bls_sig_pool = FD_SCRATCH_ALLOC_APPEND( l, bls_sig_pool_align(), bls_sig_pool_footprint( sig_max ) );
1348 0 : void * notar_hash_stake_pool = FD_SCRATCH_ALLOC_APPEND( l, vote_stake_pool_align(), vote_stake_pool_footprint( notar_max ) );
1349 0 : void * notar_hash_stake_map = FD_SCRATCH_ALLOC_APPEND( l, vote_stake_map_align(), vote_stake_map_footprint ( notar_chain_cnt ) );
1350 0 : void * nf_hash_stake_pool = FD_SCRATCH_ALLOC_APPEND( l, vote_stake_pool_align(), vote_stake_pool_footprint( nf_max ) );
1351 0 : void * nf_hash_stake_map = FD_SCRATCH_ALLOC_APPEND( l, vote_stake_map_align(), vote_stake_map_footprint ( nf_chain_cnt ) );
1352 0 : void * parent_ready_tracker = FD_SCRATCH_ALLOC_APPEND( l, fd_parent_ready_tracker_align(), fd_parent_ready_tracker_footprint( ready_max ) );
1353 0 : void * finality_tracker = FD_SCRATCH_ALLOC_APPEND( l, fd_finality_tracker_align(), fd_finality_tracker_footprint( slot_max ) );
1354 0 : void * s2n_waiting_parent_cert = FD_SCRATCH_ALLOC_APPEND( l, alignof(s2n_waiting_parent_cert_t), sizeof(s2n_waiting_parent_cert_t) );
1355 0 : void * s2n_waiting_parent_cert_pool = FD_SCRATCH_ALLOC_APPEND( l, s2n_waiting_parent_cert_pool_align(), s2n_waiting_parent_cert_pool_footprint( s2n_max ) );
1356 0 : void * s2n_waiting_parent_cert_map = FD_SCRATCH_ALLOC_APPEND( l, s2n_waiting_parent_cert_map_align(), s2n_waiting_parent_cert_map_footprint ( s2n_chain_cnt ) );
1357 0 : void * pool_events = FD_SCRATCH_ALLOC_APPEND( l, pool_events_align(), pool_events_footprint( event_max ) );
1358 0 : void * repair_events = FD_SCRATCH_ALLOC_APPEND( l, repair_events_align(), repair_events_footprint( repair_max ) );
1359 0 : void * parent_ready_scratch = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_parent_ready_t), sizeof(fd_parent_ready_t) * slot_max );
1360 0 : void * implicitly_finalized_scratch = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_block_id_t), sizeof(fd_block_id_t) * slot_max );
1361 0 : void * implicitly_skipped_scratch = FD_SCRATCH_ALLOC_APPEND( l, alignof(ulong), sizeof(ulong) * slot_max );
1362 0 : FD_TEST( FD_SCRATCH_ALLOC_FINI( l, fd_votor_pool_align() ) == (ulong)mem + footprint );
1363 :
1364 0 : pool->slot_states = (slot_states_t *)slot_states;
1365 0 : pool->slot_states->pool = slot_state_pool_join( slot_state_pool_new( slot_state_pool, slot_max ) );
1366 0 : pool->slot_states->map = slot_state_map_join ( slot_state_map_new ( slot_state_map, slot_chain_cnt, seed ) );
1367 0 : pool->slot_states->bls_sig_pool = bls_sig_pool_join ( bls_sig_pool_new ( bls_sig_pool, sig_max ) );
1368 0 : pool->slot_states->notar_hash_stake_pool = vote_stake_pool_join( vote_stake_pool_new( notar_hash_stake_pool, notar_max ) );
1369 0 : pool->slot_states->notar_hash_stake_map = vote_stake_map_join ( vote_stake_map_new ( notar_hash_stake_map, notar_chain_cnt, seed ) );
1370 0 : pool->slot_states->notar_fallback_hash_stake_pool = vote_stake_pool_join( vote_stake_pool_new( nf_hash_stake_pool, nf_max ) );
1371 0 : pool->slot_states->notar_fallback_hash_stake_map = vote_stake_map_join ( vote_stake_map_new ( nf_hash_stake_map, nf_chain_cnt, seed ) );
1372 0 : pool->slot_states->prev_epoch_info = NULL;
1373 0 : pool->slot_states->prev_epoch_rank = USHORT_MAX;
1374 0 : pool->slot_states->prev_epoch_slot = ULONG_MAX;
1375 0 : pool->slot_states->curr_epoch_info = NULL;
1376 0 : pool->slot_states->curr_epoch_rank = USHORT_MAX;
1377 0 : pool->slot_states->curr_epoch_slot = ULONG_MAX;
1378 0 : pool->slot_states->next_epoch_info = NULL;
1379 0 : pool->slot_states->next_epoch_rank = USHORT_MAX;
1380 0 : pool->slot_states->next_epoch_slot = ULONG_MAX;
1381 0 : pool->slot_states->bls_verify_cnt = 0UL;
1382 :
1383 0 : pool->parent_ready_tracker = fd_parent_ready_tracker_join( fd_parent_ready_tracker_new( parent_ready_tracker, ready_max, seed ) );
1384 :
1385 0 : pool->finality_tracker = fd_finality_tracker_join( fd_finality_tracker_new( finality_tracker, slot_max, seed ) );
1386 :
1387 0 : pool->s2n_waiting_parent_cert = (s2n_waiting_parent_cert_t *)s2n_waiting_parent_cert;
1388 0 : pool->s2n_waiting_parent_cert->pool = s2n_waiting_parent_cert_pool_join( s2n_waiting_parent_cert_pool_new( s2n_waiting_parent_cert_pool, s2n_max ) );
1389 0 : pool->s2n_waiting_parent_cert->map = s2n_waiting_parent_cert_map_join ( s2n_waiting_parent_cert_map_new ( s2n_waiting_parent_cert_map, s2n_chain_cnt, seed ) );
1390 :
1391 0 : pool->pool_events = pool_events_join ( pool_events_new ( pool_events, event_max ) );
1392 0 : pool->repair_events = repair_events_join( repair_events_new( repair_events, repair_max ) );
1393 :
1394 0 : pool->slot_max = slot_max;
1395 :
1396 :
1397 0 : pool->scratch.parent_readys = (fd_parent_ready_t *)parent_ready_scratch;
1398 0 : pool->scratch.implicitly_finalized = (fd_block_id_t *)implicitly_finalized_scratch;
1399 0 : pool->scratch.implicitly_skipped = (ulong *)implicitly_skipped_scratch;
1400 :
1401 :
1402 0 : return mem;
1403 0 : }
1404 :
1405 : fd_votor_pool_t *
1406 0 : fd_votor_pool_join( void * mem ) {
1407 0 : fd_votor_pool_t * pool = (fd_votor_pool_t *)mem;
1408 0 : if( FD_UNLIKELY( !pool ) ) {
1409 0 : FD_LOG_WARNING(( "NULL mem" ));
1410 0 : return NULL;
1411 0 : }
1412 0 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)pool, fd_votor_pool_align() ) ) ) {
1413 0 : FD_LOG_WARNING(( "misaligned mem" ));
1414 0 : return NULL;
1415 0 : }
1416 0 : return pool;
1417 0 : }
1418 :
1419 : void *
1420 0 : fd_votor_pool_leave( fd_votor_pool_t const * self ) {
1421 0 : if( FD_UNLIKELY( !self ) ) {
1422 0 : FD_LOG_WARNING(( "NULL pool" ));
1423 0 : return NULL;
1424 0 : }
1425 0 : return (void *)self;
1426 0 : }
1427 :
1428 : void *
1429 0 : fd_votor_pool_delete( void * mem ) {
1430 0 : if( FD_UNLIKELY( !mem ) ) {
1431 0 : FD_LOG_WARNING(( "NULL mem" ));
1432 0 : return NULL;
1433 0 : }
1434 0 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)mem, fd_votor_pool_align() ) ) ) {
1435 0 : FD_LOG_WARNING(( "misaligned mem" ));
1436 0 : return NULL;
1437 0 : }
1438 0 : return mem;
1439 0 : }
1440 :
1441 : FD_FN_PURE fd_votor_pool_metrics_t
1442 0 : fd_votor_pool_metrics( fd_votor_pool_t const * self ) {
1443 0 : return (fd_votor_pool_metrics_t){
1444 0 : .slot_state_pool_used = slot_state_pool_used( self->slot_states->pool ),
1445 0 : .slot_state_pool_free = slot_state_pool_free( self->slot_states->pool ),
1446 0 : .finalized_slot = fd_votor_pool_finalized_slot( self ),
1447 0 : .pool_events_cnt = pool_events_cnt( self->pool_events ),
1448 0 : .bls_verify_cnt = self->slot_states->bls_verify_cnt
1449 0 : };
1450 0 : }
1451 :
1452 : static void
1453 : pool_init( fd_votor_pool_t * self,
1454 0 : fd_block_id_t const * root ) {
1455 0 : fd_finality_tracker_init( self->finality_tracker, root->slot );
1456 0 : self->parent_ready_tracker->root = fd_ulong_sat_sub( root->slot, FD_VOTOR_REWARD_SLOT_DELTA+FD_VOTOR_SLOTS_PER_WINDOW );
1457 0 : }
1458 :
1459 : static void
1460 : pool_push_parent_readys( fd_votor_pool_t * self,
1461 0 : ulong ready_cnt ) {
1462 0 : for( ulong i=0UL; i<ready_cnt; i++ ) {
1463 0 : pool_events_push( self->pool_events, (fd_votor_pool_event_t){ .kind = FD_VOTOR_POOL_EVENT_PARENT_READY, .parent_ready = self->scratch.parent_readys[i] } );
1464 0 : }
1465 0 : }
1466 :
1467 : void
1468 : fd_votor_pool_from_snapshot( fd_votor_pool_t * self,
1469 0 : fd_block_id_t const * root ) {
1470 0 : pool_init( self, root );
1471 0 : pool_push_parent_readys( self, fd_parent_ready_tracker_mark_notar_fallback( self->parent_ready_tracker, root, self->scratch.parent_readys ) );
1472 0 : }
1473 :
1474 : void
1475 : fd_votor_pool_from_genesis( fd_votor_pool_t * self,
1476 0 : fd_block_id_t const * genesis ) {
1477 0 : pool_init( self, genesis );
1478 0 : pool_push_parent_readys( self, fd_parent_ready_tracker_mark_root( self->parent_ready_tracker, genesis, self->scratch.parent_readys ) );
1479 0 : }
1480 :
1481 : static slot_state_t const *
1482 : slot_state_query( fd_votor_pool_t const * self,
1483 0 : ulong slot ) {
1484 0 : slot_state_ele_t const * ele = slot_state_map_ele_query_const( self->slot_states->map, &slot, NULL, self->slot_states->pool );
1485 0 : return ele ? &ele->slot_state : NULL;
1486 0 : }
1487 :
1488 : static slot_state_t *
1489 : slot_state( fd_votor_pool_t * self,
1490 0 : ulong slot ) {
1491 0 : slot_state_ele_t * ele = slot_state_map_ele_query( self->slot_states->map, &slot, NULL, self->slot_states->pool );
1492 0 : if( FD_LIKELY( ele ) ) return &ele->slot_state;
1493 :
1494 0 : slot_states_t * states = self->slot_states;
1495 0 : fd_epoch_info_t const * info = fd_ptr_if ( slot>=states->next_epoch_slot, states->next_epoch_info, fd_ptr_if ( slot>=states->curr_epoch_slot, states->curr_epoch_info, states->prev_epoch_info ) );
1496 0 : ulong rank = fd_ulong_if( slot>=states->next_epoch_slot, states->next_epoch_rank, fd_ulong_if( slot>=states->curr_epoch_slot, states->curr_epoch_rank, states->prev_epoch_rank ) );
1497 :
1498 0 : FD_TEST( info );
1499 0 : FD_TEST( slot_state_pool_free( self->slot_states->pool ) );
1500 :
1501 0 : ele = slot_state_pool_ele_acquire( self->slot_states->pool );
1502 0 : ele->slot = slot;
1503 0 : slot_state_null( &ele->slot_state, states );
1504 0 : ele->slot_state.slot = slot;
1505 0 : ele->slot_state.own_rank = rank;
1506 0 : slot_state_map_ele_insert( self->slot_states->map, ele, self->slot_states->pool );
1507 0 : return &ele->slot_state;
1508 0 : }
1509 :
1510 : static inline fd_finalization_event_t
1511 0 : finalization_event_default( fd_votor_pool_t * self ) {
1512 0 : return fd_finalization_event_default( self->scratch.implicitly_finalized, self->scratch.implicitly_skipped );
1513 0 : }
1514 :
1515 : static void
1516 : handle_finalization( fd_votor_pool_t * self,
1517 0 : fd_finalization_event_t const * event ) {
1518 0 : FD_TEST( pool_events_avail( self->pool_events )>=event->implicitly_finalized_cnt+event->implicitly_skipped_cnt );
1519 0 : for( ulong i=0UL; i<event->implicitly_finalized_cnt; i++ ) pool_events_push( self->pool_events, (fd_votor_pool_event_t){ .kind = FD_VOTOR_POOL_EVENT_IMPLICITLY_FINALIZED, .implicitly_finalized = event->implicitly_finalized[i] } );
1520 0 : for( ulong i=0UL; i<event->implicitly_skipped_cnt; i++ ) pool_events_push( self->pool_events, (fd_votor_pool_event_t){ .kind = FD_VOTOR_POOL_EVENT_IMPLICITLY_SKIPPED, .implicitly_skipped = event->implicitly_skipped [i] } );
1521 0 : ulong first_unpruned_slot = fd_finality_tracker_first_unpruned_slot( self->finality_tracker );
1522 0 : ulong retained_slot = fd_ulong_sat_sub( first_unpruned_slot, FD_VOTOR_REWARD_SLOT_DELTA );
1523 0 : for( ulong slot = fd_ulong_sat_sub( self->parent_ready_tracker->root, FD_VOTOR_REWARD_SLOT_DELTA ); slot<retained_slot; slot++ ) {
1524 0 : slot_state_ele_t * ele = slot_state_map_ele_remove( self->slot_states->map, &slot, NULL, self->slot_states->pool );
1525 0 : if( FD_LIKELY( ele ) ) {
1526 0 : slot_state_null( &ele->slot_state, self->slot_states );
1527 0 : slot_state_pool_ele_release( self->slot_states->pool, ele );
1528 0 : }
1529 0 : s2n_waiting_parent_cert_ele_t * waiter;
1530 0 : while( (waiter = s2n_waiting_parent_cert_map_ele_remove( self->s2n_waiting_parent_cert->map, &slot, NULL, self->s2n_waiting_parent_cert->pool )) ) s2n_waiting_parent_cert_pool_ele_release( self->s2n_waiting_parent_cert->pool, waiter );
1531 0 : }
1532 0 : fd_parent_ready_tracker_prune( self->parent_ready_tracker, fd_ulong_sat_sub( first_unpruned_slot, FD_VOTOR_REWARD_SLOT_DELTA+FD_VOTOR_SLOTS_PER_WINDOW ) );
1533 0 : }
1534 :
1535 : static void
1536 : add_verified_cert( fd_votor_pool_t * self,
1537 : fd_cert_t const * cert,
1538 0 : fd_bls_set_t * bad ) {
1539 0 : ulong slot = fd_cert_slot( cert );
1540 :
1541 0 : slot_state_add_cert( slot_state( self, slot ), cert );
1542 :
1543 0 : switch( cert->kind ) {
1544 0 : case FD_CERT_KIND_FINAL: {
1545 0 : fd_finalization_event_t finalization_event = finalization_event_default( self );
1546 0 : fd_finality_tracker_mark_finalized( self->finality_tracker, slot, &finalization_event );
1547 0 : handle_finalization( self, &finalization_event );
1548 0 : break;
1549 0 : }
1550 0 : case FD_CERT_KIND_FAST_FINAL:
1551 0 : case FD_CERT_KIND_NOTAR:
1552 0 : case FD_CERT_KIND_NOTAR_FALLBACK: {
1553 0 : fd_block_hash_t const * block_hash = fd_cert_block_hash( cert );
1554 0 : fd_block_id_t block_id = fd_block_id( slot, block_hash->uc );
1555 0 : if( FD_UNLIKELY( cert->kind==FD_CERT_KIND_FAST_FINAL ) ) {
1556 0 : fd_finalization_event_t finalization_event = finalization_event_default( self );
1557 0 : fd_finality_tracker_mark_fast_finalized( self->finality_tracker, &block_id, &finalization_event );
1558 0 : handle_finalization( self, &finalization_event );
1559 0 : }
1560 0 : if( FD_LIKELY( cert->kind==FD_CERT_KIND_NOTAR ) ) {
1561 0 : fd_finalization_event_t finalization_event = finalization_event_default( self );
1562 0 : fd_finality_tracker_mark_notarized( self->finality_tracker, &block_id, &finalization_event );
1563 0 : handle_finalization( self, &finalization_event );
1564 0 : }
1565 :
1566 0 : slot_states_t * states = self->slot_states;
1567 0 : s2n_waiting_parent_cert_ele_t * next;
1568 0 : for( s2n_waiting_parent_cert_ele_t * child = s2n_waiting_parent_cert_map_ele_query( self->s2n_waiting_parent_cert->map, &slot, NULL, self->s2n_waiting_parent_cert->pool );
1569 0 : child;
1570 0 : child = next ) {
1571 0 : next = (s2n_waiting_parent_cert_ele_t *)s2n_waiting_parent_cert_map_ele_next_const( child, NULL, self->s2n_waiting_parent_cert->pool );
1572 0 : if( FD_LIKELY( !fd_block_id_eq( &child->parent, &block_id ) ) ) continue;
1573 0 : fd_block_id_t child_id = child->child; /* copy before the release below */
1574 0 : s2n_waiting_parent_cert_map_ele_remove_fast( self->s2n_waiting_parent_cert->map, child, self->s2n_waiting_parent_cert->pool );
1575 0 : s2n_waiting_parent_cert_pool_ele_release( self->s2n_waiting_parent_cert->pool, child );
1576 :
1577 0 : slot_state_t * child_state = slot_state( self, child_id.slot );
1578 0 : fd_bls_set_t bad_child[ fd_bls_set_word_cnt ];
1579 0 : int output = slot_state_notify_parent_certified( child_state, states, &child_id.hash, bad_child );
1580 0 : if( FD_LIKELY( fd_ptr_if( child_id.slot>=states->next_epoch_slot, states->next_epoch_info, fd_ptr_if( child_id.slot>=states->curr_epoch_slot, states->curr_epoch_info, states->prev_epoch_info ) )==fd_ptr_if( slot>=states->next_epoch_slot, states->next_epoch_info, fd_ptr_if( slot>=states->curr_epoch_slot, states->curr_epoch_info, states->prev_epoch_info ) ) ) ) fd_bls_set_union( bad, bad, bad_child );
1581 0 : switch( output ) {
1582 0 : case -1: repair_events_push( self->repair_events, child_id ); break;
1583 0 : case 0: break;
1584 0 : case 1: pool_events_push( self->pool_events, (fd_votor_pool_event_t){ .kind = FD_VOTOR_POOL_EVENT_SAFE_TO_NOTAR, .safe_to_notar = child_id } ); break;
1585 0 : }
1586 0 : }
1587 :
1588 0 : ulong ready_cnt = fd_parent_ready_tracker_mark_notar_fallback( self->parent_ready_tracker, &block_id, self->scratch.parent_readys );
1589 0 : for( ulong i=0UL; i<ready_cnt; i++ ) {
1590 0 : pool_events_push( self->pool_events, (fd_votor_pool_event_t){ .kind = FD_VOTOR_POOL_EVENT_PARENT_READY, .parent_ready = self->scratch.parent_readys[i] } );
1591 0 : }
1592 :
1593 0 : repair_events_push( self->repair_events, block_id );
1594 0 : break;
1595 0 : }
1596 :
1597 0 : case FD_CERT_KIND_SKIP: {
1598 0 : ulong ready_cnt = fd_parent_ready_tracker_mark_skipped( self->parent_ready_tracker, slot, self->scratch.parent_readys );
1599 0 : for( ulong i=0UL; i<ready_cnt; i++ ) {
1600 0 : pool_events_push( self->pool_events, (fd_votor_pool_event_t){ .kind = FD_VOTOR_POOL_EVENT_PARENT_READY, .parent_ready = self->scratch.parent_readys[i] } );
1601 0 : }
1602 0 : break;
1603 0 : }
1604 :
1605 0 : default:
1606 0 : FD_LOG_ERR(( "invalid cert kind %u", cert->kind ));
1607 0 : }
1608 :
1609 0 : fd_votor_pool_event_t event = { .kind = FD_VOTOR_POOL_EVENT_CERT_CREATED, .cert_created = *cert };
1610 0 : pool_events_push( self->pool_events, event );
1611 0 : }
1612 :
1613 : void
1614 : fd_votor_pool_advance_epoch( fd_votor_pool_t * self,
1615 : fd_epoch_info_t const * epoch_info,
1616 : ulong epoch_rank,
1617 0 : ulong epoch_slot ) {
1618 0 : slot_states_t * states = self->slot_states;
1619 0 : if( FD_UNLIKELY( !states->curr_epoch_info ) ) {
1620 0 : states->curr_epoch_info = epoch_info;
1621 0 : states->curr_epoch_rank = epoch_rank;
1622 0 : states->curr_epoch_slot = epoch_slot;
1623 0 : } else if( FD_UNLIKELY( !states->next_epoch_info ) ) {
1624 0 : states->next_epoch_info = epoch_info;
1625 0 : states->next_epoch_rank = epoch_rank;
1626 0 : states->next_epoch_slot = epoch_slot;
1627 0 : } else {
1628 0 : states->prev_epoch_info = states->curr_epoch_info;
1629 0 : states->prev_epoch_slot = states->curr_epoch_slot;
1630 0 : states->prev_epoch_rank = states->curr_epoch_rank;
1631 0 : states->curr_epoch_info = states->next_epoch_info;
1632 0 : states->curr_epoch_slot = states->next_epoch_slot;
1633 0 : states->curr_epoch_rank = states->next_epoch_rank;
1634 0 : states->next_epoch_info = epoch_info;
1635 0 : states->next_epoch_rank = epoch_rank;
1636 0 : states->next_epoch_slot = epoch_slot;
1637 0 : }
1638 0 : }
1639 :
1640 : void
1641 : fd_votor_pool_update_own_rank( fd_votor_pool_t * self,
1642 : ulong epoch_slot,
1643 0 : ulong epoch_rank ) {
1644 0 : slot_states_t * states = self->slot_states;
1645 0 : if ( epoch_slot==states->prev_epoch_slot ) states->prev_epoch_rank = epoch_rank;
1646 0 : else if( epoch_slot==states->curr_epoch_slot ) states->curr_epoch_rank = epoch_rank;
1647 0 : else if( epoch_slot==states->next_epoch_slot ) states->next_epoch_rank = epoch_rank;
1648 0 : else FD_LOG_CRIT(( "no epoch starts at slot %lu", epoch_slot ));
1649 :
1650 0 : slot_state_map_t * map = self->slot_states->map;
1651 0 : slot_state_ele_t * pool = self->slot_states->pool;
1652 0 : for( slot_state_map_iter_t iter = slot_state_map_iter_init( map, pool );
1653 0 : !slot_state_map_iter_done( iter, map, pool );
1654 0 : iter = slot_state_map_iter_next( iter, map, pool ) ) {
1655 0 : slot_state_t * slot_state = &slot_state_map_iter_ele( iter, map, pool )->slot_state;
1656 0 : ulong slot = slot_state->slot;
1657 0 : ulong rank = fd_ulong_if( slot>=states->next_epoch_slot, states->next_epoch_rank, fd_ulong_if( slot>=states->curr_epoch_slot, states->curr_epoch_rank, states->prev_epoch_rank ) );
1658 :
1659 0 : slot_state->own_rank = rank;
1660 0 : }
1661 0 : }
1662 :
1663 : int
1664 : fd_votor_pool_add_block( fd_votor_pool_t * self,
1665 : fd_block_id_t const * block_id,
1666 : fd_block_id_t const * parent_id,
1667 0 : fd_bls_set_t * bad ) {
1668 0 : fd_bls_set_null( bad );
1669 :
1670 0 : ulong slot = block_id->slot;
1671 0 : fd_block_hash_t const * block_hash = &block_id->hash;
1672 0 : ulong parent_slot = parent_id->slot;
1673 0 : fd_block_hash_t const * parent_hash = &parent_id->hash;
1674 :
1675 0 : ulong slot_far_in_future = fd_finality_tracker_first_unpruned_slot( self->finality_tracker ) + self->slot_max - FD_VOTOR_REWARD_SLOT_DELTA;
1676 0 : if( FD_UNLIKELY( slot<fd_finality_tracker_first_unpruned_slot( self->finality_tracker ) ) ) return FD_VOTOR_POOL_ERR_SLOT_TOO_OLD;
1677 0 : if( FD_UNLIKELY( slot>=slot_far_in_future ) ) return FD_VOTOR_POOL_ERR_SLOT_TOO_NEW;
1678 0 : slot_states_t * states = self->slot_states;
1679 0 : if( FD_UNLIKELY( !fd_ptr_if( slot>=states->next_epoch_slot, states->next_epoch_info, fd_ptr_if( slot>=states->curr_epoch_slot, states->curr_epoch_info, states->prev_epoch_info ) ) ) ) return fd_int_if( slot<states->curr_epoch_slot, FD_VOTOR_POOL_ERR_SLOT_TOO_OLD, FD_VOTOR_POOL_ERR_SLOT_TOO_NEW );
1680 :
1681 0 : fd_finalization_event_t finalization_event = finalization_event_default( self );
1682 0 : fd_finality_tracker_add_parent( self->finality_tracker, block_id, parent_id, &finalization_event );
1683 0 : handle_finalization( self, &finalization_event );
1684 0 : if( FD_UNLIKELY( slot<fd_ulong_sat_sub( fd_finality_tracker_first_unpruned_slot( self->finality_tracker ), FD_VOTOR_REWARD_SLOT_DELTA ) ) ) return FD_VOTOR_POOL_SUCCESS; /* pruned by the parent link */
1685 :
1686 0 : slot_state_notify_parent_known( slot_state( self, slot ), states, block_hash, bad );
1687 0 : slot_state_ele_t * parent_state_ = slot_state_map_ele_query( self->slot_states->map, &parent_slot, NULL, self->slot_states->pool );
1688 0 : slot_state_t * parent_state = parent_state_ ? &parent_state_->slot_state : NULL;
1689 0 : if( FD_LIKELY( parent_state && slot_state_is_notar_fallback_or_stronger( parent_state, parent_hash ) ) ) {
1690 0 : fd_bls_set_t bad_certified[ fd_bls_set_word_cnt ];
1691 0 : int output = slot_state_notify_parent_certified( slot_state( self, slot ), states, block_hash, bad_certified );
1692 0 : fd_bls_set_union( bad, bad, bad_certified );
1693 0 : switch( output ) {
1694 0 : case -1: repair_events_push( self->repair_events, *block_id ); return FD_VOTOR_POOL_SUCCESS;
1695 0 : case 0: return FD_VOTOR_POOL_SUCCESS;
1696 0 : case 1: pool_events_push( self->pool_events, (fd_votor_pool_event_t){ .kind = FD_VOTOR_POOL_EVENT_SAFE_TO_NOTAR, .safe_to_notar = *block_id } ); return FD_VOTOR_POOL_SUCCESS;
1697 0 : }
1698 0 : }
1699 :
1700 0 : if( FD_UNLIKELY( parent_slot<fd_ulong_sat_sub( fd_finality_tracker_first_unpruned_slot( self->finality_tracker ), FD_VOTOR_REWARD_SLOT_DELTA ) ) ) return FD_VOTOR_POOL_SUCCESS; /* the parent can no longer certify */
1701 0 : for( s2n_waiting_parent_cert_ele_t const * ele = s2n_waiting_parent_cert_map_ele_query_const( self->s2n_waiting_parent_cert->map, &parent_slot, NULL, self->s2n_waiting_parent_cert->pool );
1702 0 : ele;
1703 0 : ele = s2n_waiting_parent_cert_map_ele_next_const( ele, NULL, self->s2n_waiting_parent_cert->pool ) ) {
1704 0 : if( FD_UNLIKELY( fd_block_id_eq( &ele->child, block_id ) ) ) return FD_VOTOR_POOL_SUCCESS;
1705 0 : }
1706 0 : FD_TEST( s2n_waiting_parent_cert_pool_free( self->s2n_waiting_parent_cert->pool ) );
1707 0 : s2n_waiting_parent_cert_ele_t * ele = s2n_waiting_parent_cert_pool_ele_acquire( self->s2n_waiting_parent_cert->pool );
1708 0 : ele->parent = *parent_id;
1709 0 : ele->child = *block_id;
1710 0 : s2n_waiting_parent_cert_map_ele_insert( self->s2n_waiting_parent_cert->map, ele, self->s2n_waiting_parent_cert->pool );
1711 :
1712 0 : return FD_VOTOR_POOL_SUCCESS;
1713 0 : }
1714 :
1715 : int
1716 : fd_votor_pool_add_cert( fd_votor_pool_t * self,
1717 : fd_cert_t const * cert,
1718 : int verified,
1719 0 : fd_bls_set_t * bad ) {
1720 0 : ulong slot = fd_cert_slot( cert );
1721 0 : fd_bls_set_null( bad );
1722 :
1723 0 : ulong slot_far_in_future = fd_finality_tracker_first_unpruned_slot( self->finality_tracker ) + self->slot_max - FD_VOTOR_REWARD_SLOT_DELTA;
1724 0 : if( FD_UNLIKELY( slot<fd_finality_tracker_first_unpruned_slot( self->finality_tracker ) ) ) return FD_VOTOR_POOL_ERR_SLOT_TOO_OLD;
1725 0 : if( FD_UNLIKELY( slot>=slot_far_in_future ) ) return FD_VOTOR_POOL_ERR_SLOT_TOO_NEW;
1726 :
1727 0 : slot_states_t const * states = self->slot_states;
1728 0 : fd_epoch_info_t const * epoch_info = fd_ptr_if( slot>=states->next_epoch_slot, states->next_epoch_info, fd_ptr_if( slot>=states->curr_epoch_slot, states->curr_epoch_info, states->prev_epoch_info ) );
1729 0 : if( FD_UNLIKELY( !epoch_info ) ) return fd_int_if( slot<states->curr_epoch_slot, FD_VOTOR_POOL_ERR_SLOT_TOO_OLD, FD_VOTOR_POOL_ERR_SLOT_TOO_NEW );
1730 :
1731 0 : slot_state_t * state = slot_state( self, slot );
1732 0 : int duplicate = 0;
1733 0 : switch( cert->kind ) {
1734 0 : case FD_CERT_KIND_FINAL: duplicate = state->certs.final.slot!=ULONG_MAX; break;
1735 0 : case FD_CERT_KIND_FAST_FINAL: duplicate = state->certs.fast_final.slot!=ULONG_MAX; break;
1736 0 : case FD_CERT_KIND_NOTAR: duplicate = state->certs.notar.slot!=ULONG_MAX; break;
1737 0 : case FD_CERT_KIND_NOTAR_FALLBACK: duplicate = slot_state_is_notar_fallback( state, fd_cert_block_hash( cert ) ); break;
1738 0 : case FD_CERT_KIND_SKIP: duplicate = state->certs.skip.slot!=ULONG_MAX; break;
1739 0 : default: FD_LOG_CRIT(( "unreachable" ));
1740 0 : }
1741 0 : if( FD_UNLIKELY( duplicate ) ) return FD_VOTOR_POOL_ERR_DUPLICATE;
1742 :
1743 0 : int err = FD_CERT_VERIFY_SUCCESS;
1744 0 : if( FD_LIKELY( !verified ) ) err = fd_cert_verify( cert, epoch_info );
1745 0 : self->slot_states->bls_verify_cnt += (ulong)( !verified && err!=FD_CERT_VERIFY_ERR_THRESHOLD );
1746 0 : if( FD_UNLIKELY( err ) ) return FD_VOTOR_POOL_ERR_CERT_VERIFY;
1747 :
1748 0 : switch( cert->kind ) { /* a skip cert excludes finalization certs, Lemmas 23 and 28 */
1749 0 : case FD_CERT_KIND_FINAL:
1750 0 : case FD_CERT_KIND_FAST_FINAL: FD_CHECK_CRIT( state->certs.skip.slot==ULONG_MAX, "consensus safety violation" ); break;
1751 0 : case FD_CERT_KIND_NOTAR:
1752 0 : case FD_CERT_KIND_NOTAR_FALLBACK: break;
1753 0 : case FD_CERT_KIND_SKIP: FD_CHECK_CRIT( state->certs.final.slot==ULONG_MAX && state->certs.fast_final.slot==ULONG_MAX, "consensus safety violation" ); break;
1754 0 : default: FD_LOG_CRIT(( "unreachable" ));
1755 0 : }
1756 :
1757 0 : add_verified_cert( self, cert, bad );
1758 0 : return FD_VOTOR_POOL_SUCCESS;
1759 0 : }
1760 :
1761 : int
1762 : fd_votor_pool_add_vote( fd_votor_pool_t * self,
1763 : fd_vote_t const * vote,
1764 : int verified,
1765 : fd_bls_set_t * bad,
1766 0 : uchar * quorum_reached ) {
1767 0 : ulong slot = fd_vote_slot( vote );
1768 0 : fd_bls_set_null( bad );
1769 0 : *quorum_reached = 0;
1770 :
1771 0 : ulong first_unpruned_slot = fd_finality_tracker_first_unpruned_slot( self->finality_tracker );
1772 0 : ulong retained_slot = fd_ulong_sat_sub( first_unpruned_slot, FD_VOTOR_REWARD_SLOT_DELTA );
1773 0 : ulong slot_far_in_future = first_unpruned_slot + self->slot_max - FD_VOTOR_REWARD_SLOT_DELTA;
1774 0 : if( FD_UNLIKELY( slot<retained_slot ) ) return FD_VOTOR_POOL_ERR_SLOT_TOO_OLD;
1775 0 : if( FD_UNLIKELY( slot>=slot_far_in_future ) ) return FD_VOTOR_POOL_ERR_SLOT_TOO_NEW;
1776 0 : slot_states_t * states = self->slot_states;
1777 0 : fd_epoch_info_t const * epoch_info = fd_ptr_if( slot>=states->next_epoch_slot, states->next_epoch_info, fd_ptr_if( slot>=states->curr_epoch_slot, states->curr_epoch_info, states->prev_epoch_info ) );
1778 0 : if( FD_UNLIKELY( !epoch_info ) ) return fd_int_if( slot<states->curr_epoch_slot, FD_VOTOR_POOL_ERR_SLOT_TOO_OLD, FD_VOTOR_POOL_ERR_SLOT_TOO_NEW );
1779 :
1780 0 : ulong voter = fd_vote_rank( vote );
1781 0 : slot_state_t * slot_state_ = slot_state( self, slot );
1782 0 : ulong voter_stake = fd_epoch_info_validator( epoch_info, voter )->stake;
1783 :
1784 0 : if ( FD_UNLIKELY( slot_state_check_slashable_offence( slot_state_, states, vote )!=SLASHABLE_OFFENCE_NONE ) ) {
1785 0 : return FD_VOTOR_POOL_ERR_SLASHABLE;
1786 0 : } else if( FD_UNLIKELY( slot_state_should_ignore_vote( slot_state_, states, vote ) ) ) {
1787 0 : return FD_VOTOR_POOL_ERR_DUPLICATE;
1788 0 : }
1789 0 : if( FD_UNLIKELY( !verified && !fd_bls_sig_validate( fd_vote_sig( vote ) ) ) ) return FD_VOTOR_POOL_ERR_VOTE_VERIFY;
1790 :
1791 0 : fd_cert_t cert_events [ SLOT_STATE_OUT_CERT_MAX ]; ulong cert_event_cnt;
1792 0 : fd_block_id_t safe_to_notar[ SLOT_STATE_OUT_EVENT_MAX ]; ulong safe_to_notar_cnt;
1793 0 : ulong safe_to_skip [ SLOT_STATE_OUT_EVENT_MAX ]; ulong safe_to_skip_cnt;
1794 0 : fd_block_id_t repair_events[ SLOT_STATE_OUT_REPAIR_MAX ]; ulong repair_event_cnt;
1795 0 : slot_state_add_vote( slot_state_, states, vote, voter_stake, cert_events, &cert_event_cnt, safe_to_notar, &safe_to_notar_cnt, safe_to_skip, &safe_to_skip_cnt, repair_events, &repair_event_cnt, bad );
1796 :
1797 0 : for( ulong i=0UL; i<cert_event_cnt; i++ ) { add_verified_cert( self, &cert_events[i], bad ); *quorum_reached = fd_uchar_set_bit( *quorum_reached, (int)cert_events[i].kind ); }
1798 0 : for( ulong i=0UL; i<safe_to_notar_cnt; i++ ) { pool_events_push ( self->pool_events, (fd_votor_pool_event_t){ .kind = FD_VOTOR_POOL_EVENT_SAFE_TO_NOTAR, .safe_to_notar = safe_to_notar[i] } ); *quorum_reached = fd_uchar_set_bit( *quorum_reached, FD_VOTOR_POOL_QUORUM_REACHED_SAFE_TO_NOTAR ); }
1799 0 : for( ulong i=0UL; i<safe_to_skip_cnt; i++ ) { pool_events_push ( self->pool_events, (fd_votor_pool_event_t){ .kind = FD_VOTOR_POOL_EVENT_SAFE_TO_SKIP, .safe_to_skip = safe_to_skip [i] } ); *quorum_reached = fd_uchar_set_bit( *quorum_reached, FD_VOTOR_POOL_QUORUM_REACHED_SAFE_TO_SKIP ); }
1800 0 : for( ulong i=0UL; i<repair_event_cnt; i++ ) { repair_events_push( self->repair_events, repair_events[i] ); }
1801 0 : return FD_VOTOR_POOL_SUCCESS;
1802 0 : }
1803 :
1804 : fd_block_hash_t const *
1805 : fd_votor_pool_agg_rewards( fd_votor_pool_t * self,
1806 : ulong slot,
1807 : fd_bls_agg_t * notar,
1808 : fd_bls_agg_t * skip,
1809 0 : fd_bls_set_t * bad ) {
1810 0 : fd_bls_agg_null( notar );
1811 0 : fd_bls_agg_null( skip );
1812 0 : fd_bls_set_null( bad );
1813 0 : slot_state_t const * state = slot_state_query( self, slot );
1814 0 : if( FD_UNLIKELY( !state ) ) return NULL;
1815 :
1816 0 : slot_states_t * states = self->slot_states;
1817 0 : fd_epoch_info_t const * epoch_info = fd_ptr_if( slot>=states->next_epoch_slot, states->next_epoch_info, fd_ptr_if( slot>=states->curr_epoch_slot, states->curr_epoch_info, states->prev_epoch_info ) );
1818 0 : fd_block_hash_t const * hash = NULL;
1819 0 : fd_bls_sig_t sigs [ FD_VOTOR_VAT_MAX ];
1820 0 : fd_bls_set_t skip_bad[ fd_bls_set_word_cnt ];
1821 0 : uchar msg [ FD_VOTE_SIGNING_SER_MAX ];
1822 0 : ulong msg_sz;
1823 :
1824 0 : if( FD_LIKELY( slot_state_notar_reward_agg( state, states, notar ) ) ) {
1825 0 : hash = &state->votes.top_notar_hash;
1826 0 : slot_state_sigs( state, states, FD_VOTE_KIND_NOTAR, hash, notar->set, sigs );
1827 0 : msg_sz = fd_vote_signing_ser( FD_VOTE_KIND_NOTAR, slot, hash, state->shred_version, msg );
1828 0 : states->bls_verify_cnt++;
1829 0 : if( FD_UNLIKELY( fd_bls_agg_verify_subtract( notar, msg, msg_sz, epoch_info->pubkeys, sigs, bad ) ) ) { fd_bls_agg_null( notar ); hash = NULL; }
1830 0 : }
1831 :
1832 0 : fd_bls_agg_t const * skip_agg = slot_state_skip_reward_agg( state );
1833 0 : if( FD_LIKELY( skip_agg ) ) {
1834 0 : *skip = *skip_agg;
1835 0 : slot_state_sigs( state, states, FD_VOTE_KIND_SKIP, NULL, skip->set, sigs );
1836 0 : msg_sz = fd_vote_signing_ser( FD_VOTE_KIND_SKIP, slot, NULL, state->shred_version, msg );
1837 0 : states->bls_verify_cnt++;
1838 0 : if( FD_UNLIKELY( fd_bls_agg_verify_subtract( skip, msg, msg_sz, epoch_info->pubkeys, sigs, skip_bad ) ) ) fd_bls_agg_null( skip );
1839 0 : fd_bls_set_union( bad, bad, skip_bad );
1840 0 : }
1841 0 : return hash;
1842 0 : }
1843 :
1844 : fd_cert_final_t *
1845 : fd_votor_pool_final_cert( fd_votor_pool_t const * self,
1846 : ulong slot,
1847 0 : fd_cert_final_t * out ) {
1848 0 : slot_state_t const * state = slot_state_query( self, slot );
1849 0 : if( FD_UNLIKELY( !state || state->certs.final.slot==ULONG_MAX ) ) return NULL;
1850 0 : *out = state->certs.final;
1851 0 : return out;
1852 0 : }
1853 :
1854 : fd_cert_notar_t *
1855 : fd_votor_pool_notar_cert( fd_votor_pool_t const * self,
1856 : ulong slot,
1857 0 : fd_cert_notar_t * out ) {
1858 0 : slot_state_t const * state = slot_state_query( self, slot );
1859 0 : if( FD_UNLIKELY( !state || state->certs.notar.slot==ULONG_MAX ) ) return NULL;
1860 0 : *out = state->certs.notar;
1861 0 : return out;
1862 0 : }
1863 :
1864 : fd_votor_pool_standstill_iter_t *
1865 : fd_votor_pool_standstill_iter_init( fd_votor_pool_standstill_iter_t * iter,
1866 0 : fd_votor_pool_t const * self ) {
1867 0 : iter->finalized = fd_votor_pool_finalized_slot( self );
1868 0 : iter->slot = iter->finalized;
1869 0 : iter->idx = 0UL;
1870 0 : return fd_votor_pool_standstill_iter_next( iter, self );
1871 0 : }
1872 :
1873 : fd_votor_pool_standstill_iter_t *
1874 : fd_votor_pool_standstill_iter_next( fd_votor_pool_standstill_iter_t * iter,
1875 0 : fd_votor_pool_t const * self ) {
1876 0 : ulong slot_far_in_future = fd_finality_tracker_first_unpruned_slot( self->finality_tracker ) + self->slot_max - FD_VOTOR_REWARD_SLOT_DELTA;
1877 0 : for( ; iter->slot<slot_far_in_future; iter->slot++, iter->idx = 0UL ) {
1878 0 : slot_state_ele_t const * slot_state_ = slot_state_map_ele_query_const( self->slot_states->map, &iter->slot, NULL, self->slot_states->pool );
1879 0 : if( FD_UNLIKELY( !slot_state_ ) ) continue;
1880 :
1881 0 : slot_certs_t const * certs = &slot_state_->slot_state.certs;
1882 0 : slot_votes_t const * votes = &slot_state_->slot_state.votes;
1883 0 : ulong slot = slot_state_->slot_state.slot;
1884 0 : ulong own_rank = slot_state_->slot_state.own_rank;
1885 0 : ushort shred_version = slot_state_->slot_state.shred_version;
1886 0 : ulong idx = iter->idx;
1887 0 : int finalized = slot==iter->finalized;
1888 0 : int fast_final = certs->fast_final.slot!=ULONG_MAX;
1889 0 : int final = certs->final.slot!=ULONG_MAX && ( !finalized || ( !fast_final && certs->notar.slot!=ULONG_MAX ) );
1890 0 : int notar = certs->notar.slot !=ULONG_MAX && ( !finalized || final );
1891 0 : ulong nf_cert = fd_ulong_max( idx, STANDSTILL_NOTAR_FALLBACK ) - STANDSTILL_NOTAR_FALLBACK;
1892 0 : ulong nf_vote = fd_ulong_max( idx, STANDSTILL_NOTAR_FALLBACK_VOTE ) - STANDSTILL_NOTAR_FALLBACK_VOTE;
1893 :
1894 0 : iter->is_vote = 0;
1895 0 : if( FD_UNLIKELY( idx<=STANDSTILL_FINAL && final ) ) { iter->idx = STANDSTILL_FINAL+1UL; iter->cert = (fd_cert_t){ .kind = FD_CERT_KIND_FINAL, .final = certs->final }; return iter; }
1896 0 : if( FD_UNLIKELY( idx<=STANDSTILL_FAST_FINAL && fast_final ) ) { iter->idx = STANDSTILL_FAST_FINAL+1UL; iter->cert = (fd_cert_t){ .kind = FD_CERT_KIND_FAST_FINAL, .fast_final = certs->fast_final }; return iter; }
1897 0 : if( FD_LIKELY ( idx<=STANDSTILL_NOTAR && notar ) ) { iter->idx = STANDSTILL_NOTAR+1UL; iter->cert = (fd_cert_t){ .kind = FD_CERT_KIND_NOTAR, .notar = certs->notar }; return iter; }
1898 0 : if( FD_UNLIKELY( finalized ) ) continue;
1899 0 : if( FD_UNLIKELY( nf_cert<certs->notar_fallback_cnt ) ) { iter->idx = STANDSTILL_NOTAR_FALLBACK+nf_cert+1UL; iter->cert = (fd_cert_t){ .kind = FD_CERT_KIND_NOTAR_FALLBACK, .notar_fallback = certs->notar_fallback[ nf_cert ] }; return iter; }
1900 0 : if( FD_UNLIKELY( idx<=STANDSTILL_SKIP && certs->skip.slot!=ULONG_MAX ) ) { iter->idx = STANDSTILL_SKIP+1UL; iter->cert = (fd_cert_t){ .kind = FD_CERT_KIND_SKIP, .skip = certs->skip }; return iter; }
1901 :
1902 0 : if( FD_UNLIKELY( own_rank==USHORT_MAX ) ) continue; /* unstaked */
1903 0 : iter->is_vote = 1;
1904 0 : if( FD_UNLIKELY( idx<=STANDSTILL_NOTAR_VOTE && fd_bls_set_test( votes->notar_set, own_rank ) ) ) {
1905 0 : iter->idx = STANDSTILL_NOTAR_VOTE+1UL;
1906 0 : iter->vote = (fd_vote_t){ .kind = FD_VOTE_KIND_NOTAR, .notar = { .slot = slot, .sig = bls_sig_pool_ele_const( self->slot_states->bls_sig_pool, votes->notar_sig_idx[ own_rank ] )->sig, .rank = (ushort)own_rank, .shred_version = shred_version } };
1907 0 : memcpy( &iter->vote.notar.block_hash, &vote_stake_pool_ele_const( self->slot_states->notar_hash_stake_pool, votes->notar_hash_stake_idx[ own_rank ] )->block_id.hash, sizeof(fd_block_hash_t) );
1908 0 : return iter;
1909 0 : }
1910 0 : if( FD_LIKELY ( idx<=STANDSTILL_FINAL_VOTE && fd_bls_set_test( votes->final_agg.set, own_rank ) ) ) { iter->idx = STANDSTILL_FINAL_VOTE+1UL; iter->vote = (fd_vote_t){ .kind = FD_VOTE_KIND_FINAL, .final = { .slot = slot, .sig = bls_sig_pool_ele_const( self->slot_states->bls_sig_pool, votes->final_sig_idx[ own_rank ] )->sig, .rank = (ushort)own_rank, .shred_version = shred_version } }; return iter; }
1911 0 : if( FD_UNLIKELY( idx<=STANDSTILL_SKIP_VOTE && fd_bls_set_test( votes->skip_agg.set, own_rank ) ) ) { iter->idx = STANDSTILL_SKIP_VOTE+1UL; iter->vote = (fd_vote_t){ .kind = FD_VOTE_KIND_SKIP, .skip = { .slot = slot, .sig = bls_sig_pool_ele_const( self->slot_states->bls_sig_pool, votes->skip_sig_idx [ own_rank ] )->sig, .rank = (ushort)own_rank, .shred_version = shred_version } }; return iter; }
1912 0 : if( FD_UNLIKELY( nf_vote<votes->notar_fallback_sig_cnt[ own_rank ] ) ) {
1913 0 : iter->idx = STANDSTILL_NOTAR_FALLBACK_VOTE+nf_vote+1UL;
1914 0 : iter->vote = (fd_vote_t){ .kind = FD_VOTE_KIND_NOTAR_FALLBACK, .notar_fallback = { .slot = slot, .sig = bls_sig_pool_ele_const( self->slot_states->bls_sig_pool, votes->notar_fallback_sig_idx[ own_rank ][ nf_vote ] )->sig, .rank = (ushort)own_rank, .shred_version = shred_version } };
1915 0 : memcpy( &iter->vote.notar_fallback.block_hash, &vote_stake_pool_ele_const( self->slot_states->notar_fallback_hash_stake_pool, votes->notar_fallback_hash_stake_idx[ own_rank ][ nf_vote ] )->block_id.hash, sizeof(fd_block_hash_t) );
1916 0 : return iter;
1917 0 : }
1918 0 : if( FD_UNLIKELY( idx<=STANDSTILL_SKIP_FALLBACK_VOTE && fd_bls_set_test( votes->skip_fallback_agg.set, own_rank ) ) ) { iter->idx = STANDSTILL_SKIP_FALLBACK_VOTE+1UL; iter->vote = (fd_vote_t){ .kind = FD_VOTE_KIND_SKIP_FALLBACK, .skip_fallback = { .slot = slot, .sig = bls_sig_pool_ele_const( self->slot_states->bls_sig_pool, votes->skip_fallback_sig_idx[ own_rank ] )->sig, .rank = (ushort)own_rank, .shred_version = shred_version } }; return iter; }
1919 0 : }
1920 0 : iter->slot = ULONG_MAX;
1921 0 : return iter;
1922 0 : }
1923 :
1924 : FD_FN_PURE int
1925 0 : fd_votor_pool_standstill_iter_done( fd_votor_pool_standstill_iter_t const * iter ) {
1926 0 : return iter->slot==ULONG_MAX;
1927 0 : }
1928 :
1929 : FD_FN_PURE ulong
1930 0 : fd_votor_pool_finalized_slot( fd_votor_pool_t const * self ) {
1931 0 : return fd_finality_tracker_highest_finalized_slot( self->finality_tracker );
1932 0 : }
1933 :
1934 : FD_FN_PURE ulong
1935 0 : fd_votor_pool_highest_parent_ready( fd_votor_pool_t const * self ) {
1936 0 : return self->parent_ready_tracker->highest_ready;
1937 0 : }
1938 :
1939 : FD_FN_PURE fd_block_hash_t const *
1940 0 : fd_votor_pool_finalized_block_hash( fd_votor_pool_t const * self ) {
1941 0 : return fd_finality_tracker_highest_finalized_block_hash( self->finality_tracker );
1942 0 : }
1943 :
1944 : ulong
1945 : fd_votor_pool_parents_ready( fd_votor_pool_t const * self,
1946 : ulong slot,
1947 : fd_block_id_t * out,
1948 0 : ulong out_max ) {
1949 0 : return fd_parent_ready_tracker_parents_ready( self->parent_ready_tracker, slot, out, out_max );
1950 0 : }
1951 :
1952 : FD_FN_PURE fd_block_id_t
1953 : fd_votor_pool_wait_for_parent_ready( fd_votor_pool_t const * self,
1954 0 : ulong slot ) {
1955 0 : return fd_parent_ready_tracker_wait_for_parent_ready( self->parent_ready_tracker, slot );
1956 0 : }
1957 :
1958 : int
1959 : fd_votor_pool_poll_pool_event( fd_votor_pool_t * self,
1960 0 : fd_votor_pool_event_t * event ) {
1961 0 : if( FD_LIKELY( pool_events_empty( self->pool_events ) ) ) return 0;
1962 0 : *event = pool_events_pop( self->pool_events );
1963 0 : if( FD_LIKELY( event->kind!=FD_VOTOR_POOL_EVENT_PARENT_READY ) ) return 1;
1964 0 : fd_parent_ready_state_t * state = fd_parent_ready_state_map_ele_query( self->parent_ready_tracker->states.map, &event->parent_ready.slot, NULL, self->parent_ready_tracker->states.pool );
1965 0 : if( FD_LIKELY( state ) ) state->ready = 0; /* NULL if pruned while queued */
1966 0 : return 1;
1967 0 : }
1968 :
1969 : int
1970 : fd_votor_pool_poll_repair_event( fd_votor_pool_t * self,
1971 0 : fd_block_id_t * block ) {
1972 0 : if( FD_LIKELY( repair_events_empty( self->repair_events ) ) ) return 0;
1973 0 : *block = repair_events_pop( self->repair_events );
1974 0 : return 1;
1975 0 : }
|