Line data Source code
1 : #include "ag_pool.h"
2 : #include "ag_slot_state.h"
3 : #include "ag_finality_tracker.h"
4 : #include "ag_parent_ready_tracker.h"
5 :
6 : #define QUEUE_NAME pool_events
7 0 : #define QUEUE_T ag_pool_event_t
8 : #include "../../util/tmpl/fd_queue_dynamic.c"
9 :
10 : /* repair_events are notifications that a block has reached SafeToNotar
11 : (or stronger) status. It does not necessarily trigger actual repair
12 : requests if we already have the block... (that is the responsibility
13 : of the repair tile to answer). */
14 :
15 : #define QUEUE_NAME repair_events
16 0 : #define QUEUE_T ag_block_id_t
17 : #include "../../util/tmpl/fd_queue_dynamic.c"
18 :
19 : /* Protocol bounds (< 20% byzantine stake) on what burst adds push onto
20 : queues drained to empty before the next add:
21 :
22 : - IMPLICITLY_FINALIZED / SKIPPED: once per slot of the window
23 : - SAFE_TO_NOTAR of waiting children: 3 hashes per child slot (stake
24 : arithmetic), and correct nodes notar a child only at its parent's
25 : slot+1 or a window start, for each of up to burst certified
26 : parents
27 : - PARENT_READY: once per window start (ready flag), plus one per add
28 : moving the window
29 : - per add: 4 of CERT_CREATED, SAFE_TO_NOTAR and SAFE_TO_SKIP
30 : - repair: 4 block ids per add (3 created certs, 1 missing block) */
31 :
32 0 : #define POOL_EVENT_MAX(slot_max,burst) ( (slot_max) \
33 0 : + 3UL*( ((slot_max)+AG_SLOTS_PER_WINDOW-1UL)/AG_SLOTS_PER_WINDOW + (burst) ) \
34 0 : + ( ((slot_max)+AG_SLOTS_PER_WINDOW-1UL)/AG_SLOTS_PER_WINDOW + (burst) ) \
35 0 : + 4UL*(burst) )
36 0 : #define REPAIR_EVENT_MAX(burst) ( 4UL*(burst) )
37 :
38 : struct slot_state_ele {
39 : ulong slot;
40 : ulong next;
41 : ag_slot_state_t slot_state;
42 : };
43 : typedef struct slot_state_ele slot_state_ele_t;
44 :
45 : #define POOL_NAME slot_state_pool
46 0 : #define POOL_T slot_state_ele_t
47 : #include "../../util/tmpl/fd_pool.c"
48 :
49 : #define MAP_NAME slot_state_map
50 : #define MAP_ELE_T slot_state_ele_t
51 0 : #define MAP_KEY slot
52 : #define MAP_KEY_T ulong
53 0 : #define MAP_KEY_EQ(k0,k1) ((*(k0))==(*(k1)))
54 0 : #define MAP_KEY_HASH(key,seed) (fd_ulong_hash( (*(key)) ^ (seed) ))
55 0 : #define MAP_NEXT next
56 : #include "../../util/tmpl/fd_map_chain.c"
57 :
58 : struct s2n_waiting_parent_cert_ele {
59 : ag_block_id_t parent;
60 : ulong next;
61 : ulong prev;
62 : ag_block_id_t child;
63 : };
64 : typedef struct s2n_waiting_parent_cert_ele s2n_waiting_parent_cert_ele_t;
65 :
66 : #define POOL_NAME s2n_waiting_parent_cert_pool
67 0 : #define POOL_T s2n_waiting_parent_cert_ele_t
68 : #include "../../util/tmpl/fd_pool.c"
69 :
70 : #define MAP_NAME s2n_waiting_parent_cert_map
71 0 : #define MAP_ELE_T s2n_waiting_parent_cert_ele_t
72 0 : #define MAP_KEY parent.slot
73 0 : #define MAP_KEY_HASH(key,seed) (fd_ulong_hash( (*(key)) ^ (seed) ))
74 0 : #define MAP_NEXT next
75 : #define MAP_MULTI 1
76 : #define MAP_OPTIMIZE_RANDOM_ACCESS_REMOVAL 1
77 : #include "../../util/tmpl/fd_map_chain.c"
78 :
79 : struct slot_states {
80 : slot_state_ele_t * pool;
81 : slot_state_map_t * map;
82 : ag_slot_states_t shared;
83 : };
84 : typedef struct slot_states slot_states_t;
85 :
86 : struct s2n_waiting_parent_cert {
87 : s2n_waiting_parent_cert_ele_t * pool;
88 : s2n_waiting_parent_cert_map_t * map;
89 : };
90 : typedef struct s2n_waiting_parent_cert s2n_waiting_parent_cert_t;
91 :
92 : struct __attribute__((aligned(128UL))) ag_pool {
93 : ulong slot_max;
94 :
95 : slot_states_t * slot_states;
96 : ag_parent_ready_tracker_t * parent_ready_tracker;
97 : ag_finality_tracker_t * finality_tracker;
98 : s2n_waiting_parent_cert_t * s2n_waiting_parent_cert;
99 :
100 : ag_pool_event_t * pool_events;
101 : ag_block_id_t * repair_events;
102 :
103 : struct {
104 : ag_parent_ready_t * parent_readys;
105 : ag_block_id_t * implicitly_finalized;
106 : ulong * implicitly_skipped;
107 : } scratch;
108 : };
109 :
110 : ulong
111 0 : ag_pool_align( void ) {
112 0 : return alignof(ag_pool_t);
113 0 : }
114 :
115 : ulong
116 : ag_pool_footprint( ulong slot_max,
117 0 : ulong burst ) {
118 0 : if( FD_UNLIKELY( slot_max<AG_SLOTS_PER_WINDOW+AG_REWARD_SLOT_DELTA || slot_max>UINT_MAX/(AG_VAT_MAX*AG_SLOT_VOTE_SIG_MAX) ) ) return 0UL;
119 :
120 0 : ulong slot_chain_cnt = slot_state_map_chain_cnt_est( slot_max );
121 0 : ulong sig_max = slot_max*AG_VAT_MAX*AG_SLOT_VOTE_SIG_MAX;
122 0 : ulong notar_max = slot_max*AG_VAT_MAX;
123 0 : ulong notar_chain_cnt = ag_vote_stake_map_chain_cnt_est( notar_max );
124 0 : ulong nf_max = slot_max*AG_NOTAR_FALLBACK_HASH_MAX;
125 0 : ulong nf_chain_cnt = ag_vote_stake_map_chain_cnt_est( nf_max );
126 0 : ulong s2n_max = slot_max*AG_EQVOC_BLOCK_HASH_MAX;
127 0 : ulong s2n_chain_cnt = s2n_waiting_parent_cert_map_chain_cnt_est( s2n_max );
128 0 : ulong ready_max = slot_max + AG_SLOTS_PER_WINDOW + 1UL; /* [root-AG_REWARD_SLOT_DELTA-AG_SLOTS_PER_WINDOW, root+slot_max-AG_REWARD_SLOT_DELTA] */
129 0 : ulong event_max = POOL_EVENT_MAX( slot_max, burst );
130 0 : ulong repair_max = REPAIR_EVENT_MAX( burst );
131 :
132 0 : return FD_LAYOUT_FINI(
133 0 : FD_LAYOUT_APPEND(
134 0 : FD_LAYOUT_APPEND(
135 0 : FD_LAYOUT_APPEND(
136 0 : FD_LAYOUT_APPEND(
137 0 : FD_LAYOUT_APPEND(
138 0 : FD_LAYOUT_APPEND(
139 0 : FD_LAYOUT_APPEND(
140 0 : FD_LAYOUT_APPEND(
141 0 : FD_LAYOUT_APPEND(
142 0 : FD_LAYOUT_APPEND(
143 0 : FD_LAYOUT_APPEND(
144 0 : FD_LAYOUT_APPEND(
145 0 : FD_LAYOUT_APPEND(
146 0 : FD_LAYOUT_APPEND(
147 0 : FD_LAYOUT_APPEND(
148 0 : FD_LAYOUT_APPEND(
149 0 : FD_LAYOUT_APPEND(
150 0 : FD_LAYOUT_APPEND(
151 0 : FD_LAYOUT_APPEND(
152 0 : FD_LAYOUT_INIT,
153 0 : alignof(ag_pool_t), sizeof(ag_pool_t) ),
154 0 : alignof(slot_states_t), sizeof(slot_states_t) ),
155 0 : slot_state_pool_align(), slot_state_pool_footprint( slot_max ) ),
156 0 : slot_state_map_align(), slot_state_map_footprint ( slot_chain_cnt ) ),
157 0 : fd_bls_sig_pool_align(), fd_bls_sig_pool_footprint( sig_max ) ),
158 0 : ag_vote_stake_pool_align(), ag_vote_stake_pool_footprint( notar_max ) ),
159 0 : ag_vote_stake_map_align(), ag_vote_stake_map_footprint ( notar_chain_cnt ) ),
160 0 : ag_vote_stake_pool_align(), ag_vote_stake_pool_footprint( nf_max ) ),
161 0 : ag_vote_stake_map_align(), ag_vote_stake_map_footprint ( nf_chain_cnt ) ),
162 0 : ag_parent_ready_tracker_align(), ag_parent_ready_tracker_footprint( ready_max ) ),
163 0 : ag_finality_tracker_align(), ag_finality_tracker_footprint( slot_max ) ),
164 0 : alignof(s2n_waiting_parent_cert_t), sizeof(s2n_waiting_parent_cert_t) ),
165 0 : s2n_waiting_parent_cert_pool_align(), s2n_waiting_parent_cert_pool_footprint( s2n_max ) ),
166 0 : s2n_waiting_parent_cert_map_align(), s2n_waiting_parent_cert_map_footprint ( s2n_chain_cnt ) ),
167 0 : pool_events_align(), pool_events_footprint( event_max ) ),
168 0 : repair_events_align(), repair_events_footprint( repair_max ) ),
169 0 : alignof(ag_parent_ready_t), sizeof(ag_parent_ready_t) * slot_max ),
170 0 : alignof(ag_block_id_t), sizeof(ag_block_id_t) * slot_max ),
171 0 : alignof(ulong), sizeof(ulong) * slot_max ),
172 0 : ag_pool_align() );
173 0 : }
174 :
175 : void *
176 : ag_pool_new( void * mem,
177 : ulong slot_max,
178 : ulong burst,
179 0 : ulong seed ) {
180 0 : if( FD_UNLIKELY( !mem ) ) {
181 0 : FD_LOG_WARNING(( "NULL mem" ));
182 0 : return NULL;
183 0 : }
184 0 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)mem, ag_pool_align() ) ) ) {
185 0 : FD_LOG_WARNING(( "misaligned mem" ));
186 0 : return NULL;
187 0 : }
188 0 : ulong footprint = ag_pool_footprint( slot_max, burst );
189 0 : if( FD_UNLIKELY( !footprint ) ) {
190 0 : FD_LOG_WARNING(( "bad slot_max (%lu)", slot_max ));
191 0 : return NULL;
192 0 : }
193 0 : fd_memset( mem, 0, footprint );
194 :
195 0 : ulong s2n_max = slot_max*AG_EQVOC_BLOCK_HASH_MAX;
196 0 : ulong slot_chain_cnt = slot_state_map_chain_cnt_est( slot_max );
197 0 : ulong sig_max = slot_max*AG_VAT_MAX*AG_SLOT_VOTE_SIG_MAX;
198 0 : ulong notar_max = slot_max*AG_VAT_MAX;
199 0 : ulong notar_chain_cnt = ag_vote_stake_map_chain_cnt_est( notar_max );
200 0 : ulong nf_max = slot_max*AG_NOTAR_FALLBACK_HASH_MAX;
201 0 : ulong nf_chain_cnt = ag_vote_stake_map_chain_cnt_est( nf_max );
202 0 : ulong s2n_chain_cnt = s2n_waiting_parent_cert_map_chain_cnt_est( s2n_max );
203 0 : ulong ready_max = slot_max + AG_SLOTS_PER_WINDOW + 1UL;
204 0 : ulong event_max = POOL_EVENT_MAX( slot_max, burst );
205 0 : ulong repair_max = REPAIR_EVENT_MAX( burst );
206 :
207 :
208 0 : FD_SCRATCH_ALLOC_INIT( l, mem );
209 0 : ag_pool_t * pool = FD_SCRATCH_ALLOC_APPEND( l, alignof(ag_pool_t), sizeof(ag_pool_t) );
210 0 : void * slot_states = FD_SCRATCH_ALLOC_APPEND( l, alignof(slot_states_t), sizeof(slot_states_t) );
211 0 : void * slot_state_pool = FD_SCRATCH_ALLOC_APPEND( l, slot_state_pool_align(), slot_state_pool_footprint( slot_max ) );
212 0 : void * slot_state_map = FD_SCRATCH_ALLOC_APPEND( l, slot_state_map_align(), slot_state_map_footprint ( slot_chain_cnt ) );
213 0 : void * bls_sig_pool = FD_SCRATCH_ALLOC_APPEND( l, fd_bls_sig_pool_align(), fd_bls_sig_pool_footprint( sig_max ) );
214 0 : void * notar_hash_stake_pool = FD_SCRATCH_ALLOC_APPEND( l, ag_vote_stake_pool_align(), ag_vote_stake_pool_footprint( notar_max ) );
215 0 : void * notar_hash_stake_map = FD_SCRATCH_ALLOC_APPEND( l, ag_vote_stake_map_align(), ag_vote_stake_map_footprint ( notar_chain_cnt ) );
216 0 : void * nf_hash_stake_pool = FD_SCRATCH_ALLOC_APPEND( l, ag_vote_stake_pool_align(), ag_vote_stake_pool_footprint( nf_max ) );
217 0 : void * nf_hash_stake_map = FD_SCRATCH_ALLOC_APPEND( l, ag_vote_stake_map_align(), ag_vote_stake_map_footprint ( nf_chain_cnt ) );
218 0 : void * parent_ready_tracker = FD_SCRATCH_ALLOC_APPEND( l, ag_parent_ready_tracker_align(), ag_parent_ready_tracker_footprint( ready_max ) );
219 0 : void * finality_tracker = FD_SCRATCH_ALLOC_APPEND( l, ag_finality_tracker_align(), ag_finality_tracker_footprint( slot_max ) );
220 0 : void * s2n_waiting_parent_cert = FD_SCRATCH_ALLOC_APPEND( l, alignof(s2n_waiting_parent_cert_t), sizeof(s2n_waiting_parent_cert_t) );
221 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 ) );
222 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 ) );
223 0 : void * pool_events = FD_SCRATCH_ALLOC_APPEND( l, pool_events_align(), pool_events_footprint( event_max ) );
224 0 : void * repair_events = FD_SCRATCH_ALLOC_APPEND( l, repair_events_align(), repair_events_footprint( repair_max ) );
225 0 : void * parent_ready_scratch = FD_SCRATCH_ALLOC_APPEND( l, alignof(ag_parent_ready_t), sizeof(ag_parent_ready_t) * slot_max );
226 0 : void * implicitly_finalized_scratch = FD_SCRATCH_ALLOC_APPEND( l, alignof(ag_block_id_t), sizeof(ag_block_id_t) * slot_max );
227 0 : void * implicitly_skipped_scratch = FD_SCRATCH_ALLOC_APPEND( l, alignof(ulong), sizeof(ulong) * slot_max );
228 0 : FD_TEST( FD_SCRATCH_ALLOC_FINI( l, ag_pool_align() ) == (ulong)mem + footprint );
229 :
230 0 : pool->slot_states = (slot_states_t *)slot_states;
231 0 : pool->slot_states->pool = slot_state_pool_join ( slot_state_pool_new ( slot_state_pool, slot_max ) );
232 0 : pool->slot_states->map = slot_state_map_join ( slot_state_map_new ( slot_state_map, slot_chain_cnt, seed ) );
233 0 : pool->slot_states->shared.bls_sig_pool = fd_bls_sig_pool_join ( fd_bls_sig_pool_new ( bls_sig_pool, sig_max ) );
234 0 : pool->slot_states->shared.notar_hash_stake_pool = ag_vote_stake_pool_join( ag_vote_stake_pool_new( notar_hash_stake_pool, notar_max ) );
235 0 : pool->slot_states->shared.notar_hash_stake_map = ag_vote_stake_map_join ( ag_vote_stake_map_new ( notar_hash_stake_map, notar_chain_cnt, seed ) );
236 0 : pool->slot_states->shared.notar_fallback_hash_stake_pool = ag_vote_stake_pool_join( ag_vote_stake_pool_new( nf_hash_stake_pool, nf_max ) );
237 0 : pool->slot_states->shared.notar_fallback_hash_stake_map = ag_vote_stake_map_join ( ag_vote_stake_map_new ( nf_hash_stake_map, nf_chain_cnt, seed ) );
238 0 : pool->slot_states->shared.prev_epoch_info = NULL;
239 0 : pool->slot_states->shared.prev_epoch_rank = USHORT_MAX;
240 0 : pool->slot_states->shared.prev_epoch_slot = ULONG_MAX;
241 0 : pool->slot_states->shared.curr_epoch_info = NULL;
242 0 : pool->slot_states->shared.curr_epoch_rank = USHORT_MAX;
243 0 : pool->slot_states->shared.curr_epoch_slot = ULONG_MAX;
244 0 : pool->slot_states->shared.next_epoch_info = NULL;
245 0 : pool->slot_states->shared.next_epoch_rank = USHORT_MAX;
246 0 : pool->slot_states->shared.next_epoch_slot = ULONG_MAX;
247 0 : pool->slot_states->shared.bls_verify_cnt = 0UL;
248 :
249 0 : pool->parent_ready_tracker = ag_parent_ready_tracker_join( ag_parent_ready_tracker_new( parent_ready_tracker, ready_max, seed ) );
250 :
251 0 : pool->finality_tracker = ag_finality_tracker_join( ag_finality_tracker_new( finality_tracker, slot_max, seed ) );
252 :
253 0 : pool->s2n_waiting_parent_cert = (s2n_waiting_parent_cert_t *)s2n_waiting_parent_cert;
254 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 ) );
255 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 ) );
256 :
257 0 : pool->pool_events = pool_events_join ( pool_events_new ( pool_events, event_max ) );
258 0 : pool->repair_events = repair_events_join( repair_events_new( repair_events, repair_max ) );
259 :
260 0 : pool->slot_max = slot_max;
261 :
262 :
263 0 : pool->scratch.parent_readys = (ag_parent_ready_t *)parent_ready_scratch;
264 0 : pool->scratch.implicitly_finalized = (ag_block_id_t *)implicitly_finalized_scratch;
265 0 : pool->scratch.implicitly_skipped = (ulong *)implicitly_skipped_scratch;
266 :
267 :
268 0 : return mem;
269 0 : }
270 :
271 : ag_pool_t *
272 0 : ag_pool_join( void * mem ) {
273 0 : ag_pool_t * pool = (ag_pool_t *)mem;
274 0 : if( FD_UNLIKELY( !pool ) ) {
275 0 : FD_LOG_WARNING(( "NULL mem" ));
276 0 : return NULL;
277 0 : }
278 0 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)pool, ag_pool_align() ) ) ) {
279 0 : FD_LOG_WARNING(( "misaligned mem" ));
280 0 : return NULL;
281 0 : }
282 0 : return pool;
283 0 : }
284 :
285 : void *
286 0 : ag_pool_leave( ag_pool_t const * pool ) {
287 0 : if( FD_UNLIKELY( !pool ) ) {
288 0 : FD_LOG_WARNING(( "NULL pool" ));
289 0 : return NULL;
290 0 : }
291 0 : return (void *)pool;
292 0 : }
293 :
294 : void *
295 0 : ag_pool_delete( void * mem ) {
296 0 : if( FD_UNLIKELY( !mem ) ) {
297 0 : FD_LOG_WARNING(( "NULL mem" ));
298 0 : return NULL;
299 0 : }
300 0 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)mem, ag_pool_align() ) ) ) {
301 0 : FD_LOG_WARNING(( "misaligned mem" ));
302 0 : return NULL;
303 0 : }
304 0 : return mem;
305 0 : }
306 :
307 : static void
308 : pool_init( ag_pool_t * self,
309 0 : ag_block_id_t const * root ) {
310 0 : ag_finality_tracker_init( self->finality_tracker, root->slot );
311 0 : self->parent_ready_tracker->root = fd_ulong_sat_sub( root->slot, AG_REWARD_SLOT_DELTA+AG_SLOTS_PER_WINDOW );
312 0 : }
313 :
314 : static void
315 : pool_push_parent_readys( ag_pool_t * self,
316 0 : ulong ready_cnt ) {
317 0 : for( ulong i=0UL; i<ready_cnt; i++ ) {
318 0 : pool_events_push( self->pool_events, (ag_pool_event_t){ .kind = AG_POOL_EVENT_PARENT_READY, .parent_ready = self->scratch.parent_readys[i] } );
319 0 : }
320 0 : }
321 :
322 : void
323 : ag_pool_from_snapshot( ag_pool_t * self,
324 0 : ag_block_id_t const * root ) {
325 0 : pool_init( self, root );
326 0 : pool_push_parent_readys( self, ag_parent_ready_tracker_mark_notar_fallback( self->parent_ready_tracker, root, self->scratch.parent_readys ) );
327 0 : }
328 :
329 : void
330 : ag_pool_from_genesis( ag_pool_t * self,
331 0 : ag_block_id_t const * genesis ) {
332 0 : pool_init( self, genesis );
333 0 : pool_push_parent_readys( self, ag_parent_ready_tracker_mark_root( self->parent_ready_tracker, genesis, self->scratch.parent_readys ) );
334 0 : }
335 :
336 : void
337 0 : ag_pool_fini( ag_pool_t * self ) {
338 0 : ag_finality_tracker_fini( self->finality_tracker );
339 0 : self->parent_ready_tracker->root = ULONG_MAX;
340 0 : }
341 :
342 : FD_FN_PURE ag_pool_metrics_t
343 0 : ag_pool_metrics( ag_pool_t const * self ) {
344 0 : return (ag_pool_metrics_t){
345 0 : .slot_state_pool_used = slot_state_pool_used( self->slot_states->pool ),
346 0 : .slot_state_pool_free = slot_state_pool_free( self->slot_states->pool ),
347 0 : .finalized_slot = ag_pool_finalized_slot( self ),
348 0 : .pool_events_cnt = pool_events_cnt( self->pool_events ),
349 0 : .bls_verify_cnt = self->slot_states->shared.bls_verify_cnt
350 0 : };
351 0 : }
352 :
353 : static ag_slot_state_t *
354 : slot_state( ag_pool_t * self,
355 0 : ulong slot ) {
356 0 : slot_state_ele_t * ele = slot_state_map_ele_query( self->slot_states->map, &slot, NULL, self->slot_states->pool );
357 0 : if( FD_LIKELY( ele ) ) return &ele->slot_state;
358 :
359 0 : ag_slot_states_t * states = &self->slot_states->shared;
360 0 : ag_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 ) );
361 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 ) );
362 :
363 0 : FD_TEST( info );
364 0 : FD_TEST( slot_state_pool_free( self->slot_states->pool ) );
365 :
366 0 : ele = slot_state_pool_ele_acquire( self->slot_states->pool );
367 0 : ele->slot = slot;
368 0 : ag_slot_state_null( &ele->slot_state, states );
369 0 : ele->slot_state.slot = slot;
370 0 : ele->slot_state.own_rank = rank;
371 0 : slot_state_map_ele_insert( self->slot_states->map, ele, self->slot_states->pool );
372 0 : return &ele->slot_state;
373 0 : }
374 :
375 : static inline ag_finalization_event_t
376 0 : finalization_event_default( ag_pool_t * self ) {
377 0 : return ag_finalization_event_default( self->scratch.implicitly_finalized, self->scratch.implicitly_skipped );
378 0 : }
379 :
380 : static void
381 : handle_finalization( ag_pool_t * self,
382 0 : ag_finalization_event_t const * event ) {
383 0 : FD_TEST( pool_events_avail( self->pool_events )>=event->implicitly_finalized_cnt+event->implicitly_skipped_cnt );
384 0 : for( ulong i=0UL; i<event->implicitly_finalized_cnt; i++ ) pool_events_push( self->pool_events, (ag_pool_event_t){ .kind = AG_POOL_EVENT_IMPLICITLY_FINALIZED, .implicitly_finalized = event->implicitly_finalized[i] } );
385 0 : for( ulong i=0UL; i<event->implicitly_skipped_cnt; i++ ) pool_events_push( self->pool_events, (ag_pool_event_t){ .kind = AG_POOL_EVENT_IMPLICITLY_SKIPPED, .implicitly_skipped = event->implicitly_skipped [i] } );
386 0 : ulong first_unpruned_slot = ag_finality_tracker_first_unpruned_slot( self->finality_tracker );
387 0 : ulong retained_slot = fd_ulong_sat_sub( first_unpruned_slot, AG_REWARD_SLOT_DELTA );
388 0 : for( ulong slot = fd_ulong_sat_sub( self->parent_ready_tracker->root, AG_REWARD_SLOT_DELTA ); slot<retained_slot; slot++ ) {
389 0 : slot_state_ele_t * ele = slot_state_map_ele_remove( self->slot_states->map, &slot, NULL, self->slot_states->pool );
390 0 : if( FD_LIKELY( ele ) ) {
391 0 : ag_slot_state_null( &ele->slot_state, &self->slot_states->shared );
392 0 : slot_state_pool_ele_release( self->slot_states->pool, ele );
393 0 : }
394 0 : s2n_waiting_parent_cert_ele_t * waiter;
395 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 );
396 0 : }
397 0 : ag_parent_ready_tracker_prune( self->parent_ready_tracker, fd_ulong_sat_sub( first_unpruned_slot, AG_REWARD_SLOT_DELTA+AG_SLOTS_PER_WINDOW ) );
398 0 : }
399 :
400 : static void
401 : add_verified_cert( ag_pool_t * self,
402 : ag_cert_t const * cert,
403 0 : fd_bls_set_t * bad ) {
404 0 : ulong slot = ag_cert_slot( cert );
405 :
406 0 : ag_slot_state_add_cert( slot_state( self, slot ), cert );
407 :
408 0 : switch( cert->kind ) {
409 0 : case AG_CERT_KIND_FINAL: {
410 0 : ag_finalization_event_t finalization_event = finalization_event_default( self );
411 0 : ag_finality_tracker_mark_finalized( self->finality_tracker, slot, &finalization_event );
412 0 : handle_finalization( self, &finalization_event );
413 0 : break;
414 0 : }
415 :
416 0 : case AG_CERT_KIND_FAST_FINAL:
417 0 : case AG_CERT_KIND_NOTAR:
418 0 : case AG_CERT_KIND_NOTAR_FALLBACK: {
419 0 : uchar const * block_hash = ag_cert_block_hash( cert );
420 0 : ag_block_id_t block_id = ag_block_id( slot, block_hash );
421 0 : if( FD_UNLIKELY( cert->kind==AG_CERT_KIND_FAST_FINAL ) ) {
422 0 : ag_finalization_event_t finalization_event = finalization_event_default( self );
423 0 : ag_finality_tracker_mark_fast_finalized( self->finality_tracker, &block_id, &finalization_event );
424 0 : handle_finalization( self, &finalization_event );
425 0 : }
426 0 : if( FD_LIKELY( cert->kind==AG_CERT_KIND_NOTAR ) ) {
427 0 : ag_finalization_event_t finalization_event = finalization_event_default( self );
428 0 : ag_finality_tracker_mark_notarized( self->finality_tracker, &block_id, &finalization_event );
429 0 : handle_finalization( self, &finalization_event );
430 0 : }
431 :
432 0 : ag_slot_states_t * states = &self->slot_states->shared;
433 0 : s2n_waiting_parent_cert_ele_t * next;
434 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 );
435 0 : child;
436 0 : child = next ) {
437 0 : next = (s2n_waiting_parent_cert_ele_t *)s2n_waiting_parent_cert_map_ele_next_const( child, NULL, self->s2n_waiting_parent_cert->pool );
438 0 : if( FD_LIKELY( !ag_block_id_eq( &child->parent, &block_id ) ) ) continue;
439 0 : ag_block_id_t child_id = child->child; /* copy before the release below */
440 0 : s2n_waiting_parent_cert_map_ele_remove_fast( self->s2n_waiting_parent_cert->map, child, self->s2n_waiting_parent_cert->pool );
441 0 : s2n_waiting_parent_cert_pool_ele_release( self->s2n_waiting_parent_cert->pool, child );
442 :
443 0 : ag_slot_state_t * child_state = slot_state( self, child_id.slot );
444 0 : fd_bls_set_t bad_child[ fd_bls_set_word_cnt ];
445 0 : int output = ag_slot_state_notify_parent_certified( child_state, states, child_id.hash, bad_child );
446 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 );
447 0 : switch( output ) {
448 0 : case -1: repair_events_push( self->repair_events, child_id ); break;
449 0 : case 0: break;
450 0 : case 1: pool_events_push( self->pool_events, (ag_pool_event_t){ .kind = AG_POOL_EVENT_SAFE_TO_NOTAR, .safe_to_notar = child_id } ); break;
451 0 : }
452 0 : }
453 :
454 0 : ulong ready_cnt = ag_parent_ready_tracker_mark_notar_fallback( self->parent_ready_tracker, &block_id, self->scratch.parent_readys );
455 0 : for( ulong i=0UL; i<ready_cnt; i++ ) {
456 0 : pool_events_push( self->pool_events, (ag_pool_event_t){ .kind = AG_POOL_EVENT_PARENT_READY, .parent_ready = self->scratch.parent_readys[i] } );
457 0 : }
458 :
459 0 : repair_events_push( self->repair_events, block_id );
460 0 : break;
461 0 : }
462 :
463 0 : case AG_CERT_KIND_SKIP: {
464 0 : ulong ready_cnt = ag_parent_ready_tracker_mark_skipped( self->parent_ready_tracker, slot, self->scratch.parent_readys );
465 0 : for( ulong i=0UL; i<ready_cnt; i++ ) {
466 0 : pool_events_push( self->pool_events, (ag_pool_event_t){ .kind = AG_POOL_EVENT_PARENT_READY, .parent_ready = self->scratch.parent_readys[i] } );
467 0 : }
468 0 : break;
469 0 : }
470 :
471 0 : default:
472 0 : FD_LOG_ERR(( "invalid cert kind %u", cert->kind ));
473 0 : }
474 :
475 0 : ag_pool_event_t event = { .kind = AG_POOL_EVENT_CERT_CREATED, .cert_created = *cert };
476 0 : pool_events_push( self->pool_events, event );
477 0 : }
478 :
479 : void
480 : ag_pool_advance_epoch( ag_pool_t * self,
481 : ag_epoch_info_t const * epoch_info,
482 : ulong epoch_rank,
483 0 : ulong epoch_slot ) {
484 0 : ag_slot_states_t * states = &self->slot_states->shared;
485 0 : if( FD_UNLIKELY( !states->curr_epoch_info ) ) {
486 0 : states->curr_epoch_info = epoch_info;
487 0 : states->curr_epoch_rank = epoch_rank;
488 0 : states->curr_epoch_slot = epoch_slot;
489 0 : } else if( FD_UNLIKELY( !states->next_epoch_info ) ) {
490 0 : states->next_epoch_info = epoch_info;
491 0 : states->next_epoch_rank = epoch_rank;
492 0 : states->next_epoch_slot = epoch_slot;
493 0 : } else {
494 0 : states->prev_epoch_info = states->curr_epoch_info;
495 0 : states->prev_epoch_slot = states->curr_epoch_slot;
496 0 : states->prev_epoch_rank = states->curr_epoch_rank;
497 0 : states->curr_epoch_info = states->next_epoch_info;
498 0 : states->curr_epoch_slot = states->next_epoch_slot;
499 0 : states->curr_epoch_rank = states->next_epoch_rank;
500 0 : states->next_epoch_info = epoch_info;
501 0 : states->next_epoch_rank = epoch_rank;
502 0 : states->next_epoch_slot = epoch_slot;
503 0 : }
504 0 : }
505 :
506 : void
507 : ag_pool_set_rank( ag_pool_t * self,
508 : ulong epoch_slot,
509 0 : ulong epoch_rank ) {
510 0 : ag_slot_states_t * states = &self->slot_states->shared;
511 0 : if ( epoch_slot==states->prev_epoch_slot ) states->prev_epoch_rank = epoch_rank;
512 0 : else if( epoch_slot==states->curr_epoch_slot ) states->curr_epoch_rank = epoch_rank;
513 0 : else if( epoch_slot==states->next_epoch_slot ) states->next_epoch_rank = epoch_rank;
514 0 : else FD_LOG_CRIT(( "no epoch starts at slot %lu", epoch_slot ));
515 :
516 0 : slot_state_map_t * map = self->slot_states->map;
517 0 : slot_state_ele_t * pool = self->slot_states->pool;
518 0 : for( slot_state_map_iter_t iter = slot_state_map_iter_init( map, pool );
519 0 : !slot_state_map_iter_done( iter, map, pool );
520 0 : iter = slot_state_map_iter_next( iter, map, pool ) ) {
521 0 : ag_slot_state_t * slot_state = &slot_state_map_iter_ele( iter, map, pool )->slot_state;
522 0 : ulong slot = slot_state->slot;
523 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 ) );
524 0 : if( FD_UNLIKELY( slot_state->own_rank!=rank ) ) ag_slot_state_set_own_rank( slot_state, rank );
525 0 : }
526 0 : }
527 :
528 : int
529 : ag_pool_add_cert( ag_pool_t * self,
530 : ag_cert_t const * cert,
531 : int verified,
532 0 : fd_bls_set_t * bad ) {
533 0 : ulong slot = ag_cert_slot( cert );
534 0 : fd_bls_set_null( bad );
535 :
536 0 : ulong slot_far_in_future = ag_finality_tracker_first_unpruned_slot( self->finality_tracker ) + self->slot_max - AG_REWARD_SLOT_DELTA;
537 0 : if( FD_UNLIKELY( slot<ag_finality_tracker_first_unpruned_slot( self->finality_tracker ) ) ) return AG_POOL_ERR_SLOT_TOO_OLD;
538 0 : if( FD_UNLIKELY( slot>=slot_far_in_future ) ) return AG_POOL_ERR_SLOT_TOO_NEW;
539 :
540 0 : ag_slot_states_t const * states = &self->slot_states->shared;
541 0 : ag_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 ) );
542 0 : if( FD_UNLIKELY( !epoch_info ) ) return fd_int_if( slot<states->curr_epoch_slot, AG_POOL_ERR_SLOT_TOO_OLD, AG_POOL_ERR_SLOT_TOO_NEW );
543 :
544 0 : ag_slot_state_t * state = slot_state( self, slot );
545 0 : int duplicate = 0;
546 0 : switch( cert->kind ) {
547 0 : case AG_CERT_KIND_FINAL: duplicate = state->certs.finalize.slot!=ULONG_MAX; break;
548 0 : case AG_CERT_KIND_FAST_FINAL: duplicate = state->certs.fast_finalize.slot!=ULONG_MAX; break;
549 0 : case AG_CERT_KIND_NOTAR: duplicate = state->certs.notar.slot!=ULONG_MAX; break;
550 0 : case AG_CERT_KIND_NOTAR_FALLBACK: duplicate = ag_slot_state_is_notar_fallback( state, ag_cert_block_hash( cert ) ); break;
551 0 : case AG_CERT_KIND_SKIP: duplicate = state->certs.skip.slot!=ULONG_MAX; break;
552 0 : default: FD_LOG_CRIT(( "unreachable" ));
553 0 : }
554 0 : if( FD_UNLIKELY( duplicate ) ) return AG_POOL_ERR_DUPLICATE;
555 :
556 0 : int err = AG_CERT_VERIFY_SUCCESS;
557 0 : if( FD_LIKELY( !verified ) ) err = ag_cert_verify( cert, epoch_info );
558 0 : self->slot_states->shared.bls_verify_cnt += (ulong)( !verified && err!=AG_CERT_VERIFY_ERR_THRESHOLD );
559 0 : if( FD_UNLIKELY( err ) ) return AG_POOL_ERR_CERT_VERIFY;
560 :
561 0 : switch( cert->kind ) { /* a skip cert excludes finalization certs, Lemmas 23 and 28 */
562 0 : case AG_CERT_KIND_FINAL:
563 0 : case AG_CERT_KIND_FAST_FINAL: FD_CHECK_CRIT( state->certs.skip.slot==ULONG_MAX, "consensus safety violation" ); break;
564 0 : case AG_CERT_KIND_NOTAR:
565 0 : case AG_CERT_KIND_NOTAR_FALLBACK: break;
566 0 : case AG_CERT_KIND_SKIP: FD_CHECK_CRIT( state->certs.finalize.slot==ULONG_MAX && state->certs.fast_finalize.slot==ULONG_MAX, "consensus safety violation" ); break;
567 0 : default: FD_LOG_CRIT(( "unreachable" ));
568 0 : }
569 :
570 0 : add_verified_cert( self, cert, bad );
571 0 : return AG_POOL_SUCCESS;
572 0 : }
573 :
574 : int
575 : ag_pool_add_vote( ag_pool_t * self,
576 : ag_vote_t const * vote,
577 : int verified,
578 : fd_bls_set_t * bad,
579 0 : uchar * quorum_reached ) {
580 0 : ulong slot = ag_vote_slot( vote );
581 0 : fd_bls_set_null( bad );
582 0 : *quorum_reached = 0;
583 :
584 0 : ulong first_unpruned_slot = ag_finality_tracker_first_unpruned_slot( self->finality_tracker );
585 0 : ulong retained_slot = fd_ulong_sat_sub( first_unpruned_slot, AG_REWARD_SLOT_DELTA );
586 0 : ulong slot_far_in_future = first_unpruned_slot + self->slot_max - AG_REWARD_SLOT_DELTA;
587 0 : if( FD_UNLIKELY( slot<retained_slot ) ) return AG_POOL_ERR_SLOT_TOO_OLD;
588 0 : if( FD_UNLIKELY( slot>=slot_far_in_future ) ) return AG_POOL_ERR_SLOT_TOO_NEW;
589 0 : ag_slot_states_t * states = &self->slot_states->shared;
590 0 : ag_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 ) );
591 0 : if( FD_UNLIKELY( !epoch_info ) ) return fd_int_if( slot<states->curr_epoch_slot, AG_POOL_ERR_SLOT_TOO_OLD, AG_POOL_ERR_SLOT_TOO_NEW );
592 :
593 0 : ulong voter = ag_vote_rank( vote );
594 0 : ag_slot_state_t * slot_state_ = slot_state( self, slot );
595 0 : ulong voter_stake = ag_epoch_info_validator( epoch_info, voter )->stake;
596 :
597 0 : if ( FD_UNLIKELY( ag_slot_state_check_slashable_offence( slot_state_, states, vote )!=AG_SLASHABLE_NONE ) ) {
598 0 : return AG_POOL_ERR_SLASHABLE;
599 0 : } else if( FD_UNLIKELY( ag_slot_state_should_ignore_vote( slot_state_, states, vote ) ) ) {
600 0 : return AG_POOL_ERR_DUPLICATE;
601 0 : }
602 0 : if( FD_UNLIKELY( !verified && !blst_p2_affine_in_g2( ag_vote_sig( vote ) ) ) ) {
603 0 : fd_bls_set_insert( bad, voter );
604 0 : return AG_POOL_SUCCESS;
605 0 : }
606 :
607 0 : ag_cert_t cert_events [ AG_SLOT_STATE_OUT_CERT_MAX ]; ulong cert_event_cnt;
608 0 : ag_block_id_t safe_to_notar[ AG_SLOT_STATE_OUT_EVENT_MAX ]; ulong safe_to_notar_cnt;
609 0 : ulong safe_to_skip [ AG_SLOT_STATE_OUT_EVENT_MAX ]; ulong safe_to_skip_cnt;
610 0 : ag_block_id_t repair_events[ AG_SLOT_STATE_OUT_REPAIR_MAX ]; ulong repair_event_cnt;
611 0 : ag_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 );
612 :
613 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 ); }
614 0 : for( ulong i=0UL; i<safe_to_notar_cnt; i++ ) { pool_events_push ( self->pool_events, (ag_pool_event_t){ .kind = AG_POOL_EVENT_SAFE_TO_NOTAR, .safe_to_notar = safe_to_notar[i] } ); *quorum_reached = fd_uchar_set_bit( *quorum_reached, AG_POOL_QUORUM_REACHED_SAFE_TO_NOTAR ); }
615 0 : for( ulong i=0UL; i<safe_to_skip_cnt; i++ ) { pool_events_push ( self->pool_events, (ag_pool_event_t){ .kind = AG_POOL_EVENT_SAFE_TO_SKIP, .safe_to_skip = safe_to_skip [i] } ); *quorum_reached = fd_uchar_set_bit( *quorum_reached, AG_POOL_QUORUM_REACHED_SAFE_TO_SKIP ); }
616 0 : for( ulong i=0UL; i<repair_event_cnt; i++ ) { repair_events_push( self->repair_events, repair_events[i] ); }
617 0 : return AG_POOL_SUCCESS;
618 0 : }
619 :
620 : ag_slot_state_t const *
621 : ag_pool_slot_state( ag_pool_t const * self,
622 0 : ulong slot ) {
623 0 : slot_state_ele_t const * ele = slot_state_map_ele_query_const( self->slot_states->map, &slot, NULL, self->slot_states->pool );
624 0 : return ele ? &ele->slot_state : NULL;
625 0 : }
626 :
627 : ag_slot_states_t const *
628 0 : ag_pool_slot_states( ag_pool_t const * self ) {
629 0 : return &self->slot_states->shared;
630 0 : }
631 :
632 : int
633 : ag_pool_add_block( ag_pool_t * self,
634 : ag_block_id_t const * block_id,
635 : ag_block_id_t const * parent_id,
636 0 : fd_bls_set_t * bad ) {
637 0 : fd_bls_set_null( bad );
638 :
639 0 : ulong slot = block_id->slot;
640 0 : uchar const * block_hash = block_id->hash;
641 0 : ulong parent_slot = parent_id->slot;
642 0 : uchar const * parent_hash = parent_id->hash;
643 :
644 0 : ulong slot_far_in_future = ag_finality_tracker_first_unpruned_slot( self->finality_tracker ) + self->slot_max - AG_REWARD_SLOT_DELTA;
645 0 : if( FD_UNLIKELY( slot<ag_finality_tracker_first_unpruned_slot( self->finality_tracker ) ) ) return AG_POOL_ERR_SLOT_TOO_OLD;
646 0 : if( FD_UNLIKELY( slot>=slot_far_in_future ) ) return AG_POOL_ERR_SLOT_TOO_NEW;
647 0 : ag_slot_states_t * states = &self->slot_states->shared;
648 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, AG_POOL_ERR_SLOT_TOO_OLD, AG_POOL_ERR_SLOT_TOO_NEW );
649 :
650 0 : ag_finalization_event_t finalization_event = finalization_event_default( self );
651 0 : ag_finality_tracker_add_parent( self->finality_tracker, block_id, parent_id, &finalization_event );
652 0 : handle_finalization( self, &finalization_event );
653 0 : if( FD_UNLIKELY( slot<fd_ulong_sat_sub( ag_finality_tracker_first_unpruned_slot( self->finality_tracker ), AG_REWARD_SLOT_DELTA ) ) ) return AG_POOL_SUCCESS; /* pruned by the parent link */
654 :
655 0 : ag_slot_state_notify_parent_known( slot_state( self, slot ), block_hash );
656 0 : slot_state_ele_t * parent_state_ = slot_state_map_ele_query( self->slot_states->map, &parent_slot, NULL, self->slot_states->pool );
657 0 : ag_slot_state_t * parent_state = parent_state_ ? &parent_state_->slot_state : NULL;
658 0 : if( FD_LIKELY( parent_state && ag_slot_state_is_notar_fallback_or_stronger( parent_state, parent_hash ) ) ) {
659 0 : int output = ag_slot_state_notify_parent_certified( slot_state( self, slot ), states, block_hash, bad );
660 0 : switch( output ) {
661 0 : case -1: repair_events_push( self->repair_events, *block_id ); return AG_POOL_SUCCESS;
662 0 : case 0: return AG_POOL_SUCCESS;
663 0 : case 1: pool_events_push( self->pool_events, (ag_pool_event_t){ .kind = AG_POOL_EVENT_SAFE_TO_NOTAR, .safe_to_notar = *block_id } ); return AG_POOL_SUCCESS;
664 0 : }
665 0 : }
666 :
667 0 : if( FD_UNLIKELY( parent_slot<fd_ulong_sat_sub( ag_finality_tracker_first_unpruned_slot( self->finality_tracker ), AG_REWARD_SLOT_DELTA ) ) ) return AG_POOL_SUCCESS; /* the parent can no longer certify */
668 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 );
669 0 : ele;
670 0 : ele = s2n_waiting_parent_cert_map_ele_next_const( ele, NULL, self->s2n_waiting_parent_cert->pool ) ) {
671 0 : if( FD_UNLIKELY( ag_block_id_eq( &ele->child, block_id ) ) ) return AG_POOL_SUCCESS;
672 0 : }
673 0 : FD_TEST( s2n_waiting_parent_cert_pool_free( self->s2n_waiting_parent_cert->pool ) );
674 0 : s2n_waiting_parent_cert_ele_t * ele = s2n_waiting_parent_cert_pool_ele_acquire( self->s2n_waiting_parent_cert->pool );
675 0 : ele->parent = *parent_id;
676 0 : ele->child = *block_id;
677 0 : s2n_waiting_parent_cert_map_ele_insert( self->s2n_waiting_parent_cert->map, ele, self->s2n_waiting_parent_cert->pool );
678 :
679 0 : return AG_POOL_SUCCESS;
680 0 : }
681 :
682 : FD_STATIC_ASSERT( AG_NOTAR_FALLBACK_CERT_MAX==4UL && AG_NOTAR_FALLBACK_VOTE_MAX==3UL, standstill_iter_idx );
683 :
684 : ag_pool_standstill_iter_t *
685 : ag_pool_standstill_iter_init( ag_pool_standstill_iter_t * iter,
686 0 : ag_pool_t const * self ) {
687 0 : iter->finalized = ag_pool_finalized_slot( self );
688 0 : iter->slot = iter->finalized;
689 0 : iter->idx = 0UL;
690 0 : return ag_pool_standstill_iter_next( iter, self );
691 0 : }
692 :
693 : ag_pool_standstill_iter_t *
694 : ag_pool_standstill_iter_next( ag_pool_standstill_iter_t * iter,
695 0 : ag_pool_t const * self ) {
696 0 : ulong slot_far_in_future = ag_finality_tracker_first_unpruned_slot( self->finality_tracker ) + self->slot_max - AG_REWARD_SLOT_DELTA;
697 0 : for( ; iter->slot<slot_far_in_future; iter->slot++, iter->idx = 0UL ) {
698 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 );
699 0 : if( FD_UNLIKELY( !slot_state_ ) ) continue;
700 :
701 0 : ag_slot_certs_t const * certs = &slot_state_->slot_state.certs;
702 0 : ag_slot_votes_t const * votes = &slot_state_->slot_state.votes;
703 0 : ulong slot = slot_state_->slot_state.slot;
704 0 : ulong own_rank = slot_state_->slot_state.own_rank;
705 0 : ushort shred_version = slot_state_->slot_state.shred_version;
706 0 : ulong idx = iter->idx;
707 0 : int finalized = slot==iter->finalized;
708 0 : int fast_final = certs->fast_finalize.slot!=ULONG_MAX;
709 0 : int final = certs->finalize.slot!=ULONG_MAX && ( !finalized || ( !fast_final && certs->notar.slot!=ULONG_MAX ) );
710 0 : int notar = certs->notar.slot !=ULONG_MAX && ( !finalized || final );
711 0 : ulong nf_cert = fd_ulong_max( idx, 3UL ) - 3UL;
712 0 : ulong nf_vote = fd_ulong_max( idx, 11UL ) - 11UL;
713 :
714 0 : iter->is_vote = 0;
715 0 : if( FD_UNLIKELY( idx<1UL && final ) ) { iter->idx = 1UL; iter->cert = (ag_cert_t){ .kind = AG_CERT_KIND_FINAL, .final = certs->finalize }; return iter; }
716 0 : if( FD_UNLIKELY( idx<2UL && fast_final ) ) { iter->idx = 2UL; iter->cert = (ag_cert_t){ .kind = AG_CERT_KIND_FAST_FINAL, .fast_final = certs->fast_finalize }; return iter; }
717 0 : if( FD_LIKELY ( idx<3UL && notar ) ) { iter->idx = 3UL; iter->cert = (ag_cert_t){ .kind = AG_CERT_KIND_NOTAR, .notar = certs->notar }; return iter; }
718 0 : if( FD_UNLIKELY( finalized ) ) continue;
719 0 : if( FD_UNLIKELY( nf_cert<certs->notar_fallback_cnt ) ) { iter->idx = 4UL+nf_cert; iter->cert = (ag_cert_t){ .kind = AG_CERT_KIND_NOTAR_FALLBACK, .notar_fallback = certs->notar_fallback[ nf_cert ] }; return iter; }
720 0 : if( FD_UNLIKELY( idx<8UL && certs->skip.slot!=ULONG_MAX ) ) { iter->idx = 8UL; iter->cert = (ag_cert_t){ .kind = AG_CERT_KIND_SKIP, .skip = certs->skip }; return iter; }
721 :
722 0 : if( FD_UNLIKELY( own_rank==USHORT_MAX ) ) continue; /* unstaked */
723 0 : iter->is_vote = 1;
724 0 : if( FD_UNLIKELY( idx<9UL && fd_bls_set_test( votes->notar_set, own_rank ) ) ) {
725 0 : iter->idx = 9UL;
726 0 : iter->vote = (ag_vote_t){ .kind = AG_VOTE_KIND_NOTAR, .notar = { .slot = slot, .sig = fd_bls_sig_pool_ele_const( self->slot_states->shared.bls_sig_pool, votes->notar_sig_idx[ own_rank ] )->sig, .rank = (ushort)own_rank, .shred_version = shred_version } };
727 0 : memcpy( iter->vote.notar.block_hash, ag_vote_stake_pool_ele_const( self->slot_states->shared.notar_hash_stake_pool, votes->notar_hash_stake_idx[ own_rank ] )->block_id.hash, sizeof(ag_block_hash_t) );
728 0 : return iter;
729 0 : }
730 0 : if( FD_LIKELY ( idx<10UL && fd_bls_set_test( votes->finalize_agg.set, own_rank ) ) ) { iter->idx = 10UL; iter->vote = (ag_vote_t){ .kind = AG_VOTE_KIND_FINAL, .final = { .slot = slot, .sig = fd_bls_sig_pool_ele_const( self->slot_states->shared.bls_sig_pool, votes->finalize_sig_idx[ own_rank ] )->sig, .rank = (ushort)own_rank, .shred_version = shred_version } }; return iter; }
731 0 : if( FD_UNLIKELY( idx<11UL && fd_bls_set_test( votes->skip_agg.set, own_rank ) ) ) { iter->idx = 11UL; iter->vote = (ag_vote_t){ .kind = AG_VOTE_KIND_SKIP, .skip = { .slot = slot, .sig = fd_bls_sig_pool_ele_const( self->slot_states->shared.bls_sig_pool, votes->skip_sig_idx [ own_rank ] )->sig, .rank = (ushort)own_rank, .shred_version = shred_version } }; return iter; }
732 0 : if( FD_UNLIKELY( nf_vote<votes->notar_fallback_sig_cnt[ own_rank ] ) ) {
733 0 : iter->idx = 12UL+nf_vote;
734 0 : iter->vote = (ag_vote_t){ .kind = AG_VOTE_KIND_NOTAR_FALLBACK, .notar_fallback = { .slot = slot, .sig = fd_bls_sig_pool_ele_const( self->slot_states->shared.bls_sig_pool, votes->notar_fallback_sig_idx[ own_rank ][ nf_vote ] )->sig, .rank = (ushort)own_rank, .shred_version = shred_version } };
735 0 : memcpy( iter->vote.notar_fallback.block_hash, ag_vote_stake_pool_ele_const( self->slot_states->shared.notar_fallback_hash_stake_pool, votes->notar_fallback_hash_stake_idx[ own_rank ][ nf_vote ] )->block_id.hash, sizeof(ag_block_hash_t) );
736 0 : return iter;
737 0 : }
738 0 : if( FD_UNLIKELY( idx<15UL && fd_bls_set_test( votes->skip_fallback_agg.set, own_rank ) ) ) { iter->idx = 15UL; iter->vote = (ag_vote_t){ .kind = AG_VOTE_KIND_SKIP_FALLBACK, .skip_fallback = { .slot = slot, .sig = fd_bls_sig_pool_ele_const( self->slot_states->shared.bls_sig_pool, votes->skip_fallback_sig_idx[ own_rank ] )->sig, .rank = (ushort)own_rank, .shred_version = shred_version } }; return iter; }
739 0 : }
740 0 : iter->slot = ULONG_MAX;
741 0 : return iter;
742 0 : }
743 :
744 : FD_FN_PURE int
745 0 : ag_pool_standstill_iter_done( ag_pool_standstill_iter_t const * iter ) {
746 0 : return iter->slot==ULONG_MAX;
747 0 : }
748 :
749 : FD_FN_PURE ulong
750 0 : ag_pool_finalized_slot( ag_pool_t const * self ) {
751 0 : return ag_finality_tracker_highest_finalized_slot( self->finality_tracker );
752 0 : }
753 :
754 : FD_FN_PURE ulong
755 0 : ag_pool_highest_parent_ready( ag_pool_t const * self ) {
756 0 : return self->parent_ready_tracker->highest_ready;
757 0 : }
758 :
759 : FD_FN_PURE uchar const *
760 0 : ag_pool_finalized_block_hash( ag_pool_t const * self ) {
761 0 : return ag_finality_tracker_highest_finalized_block_hash( self->finality_tracker );
762 0 : }
763 :
764 : ulong
765 : ag_pool_parents_ready( ag_pool_t const * self,
766 : ulong slot,
767 : ag_block_id_t * out,
768 0 : ulong out_max ) {
769 0 : return ag_parent_ready_tracker_parents_ready( self->parent_ready_tracker, slot, out, out_max );
770 0 : }
771 :
772 : ag_block_id_t
773 : ag_pool_wait_for_parent_ready( ag_pool_t * self,
774 0 : ulong slot ) {
775 0 : return ag_parent_ready_tracker_wait_for_parent_ready( self->parent_ready_tracker, slot );
776 0 : }
777 :
778 : int
779 : ag_pool_poll_pool_event( ag_pool_t * self,
780 0 : ag_pool_event_t * event ) {
781 0 : if( FD_LIKELY( pool_events_empty( self->pool_events ) ) ) return 0;
782 0 : *event = pool_events_pop( self->pool_events );
783 0 : if( FD_LIKELY( event->kind!=AG_POOL_EVENT_PARENT_READY ) ) return 1;
784 0 : ag_parent_ready_state_t * state = ag_parent_ready_state_map_ele_query( self->parent_ready_tracker->states.map, &event->parent_ready.slot, NULL, self->parent_ready_tracker->states.pool );
785 0 : if( FD_LIKELY( state ) ) state->ready = 0; /* NULL if pruned while queued */
786 0 : return 1;
787 0 : }
788 :
789 : int
790 : ag_pool_poll_repair_event( ag_pool_t * self,
791 0 : ag_block_id_t * block ) {
792 0 : if( FD_LIKELY( repair_events_empty( self->repair_events ) ) ) return 0;
793 0 : *block = repair_events_pop( self->repair_events );
794 0 : return 1;
795 0 : }
|