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 27 : #define QUEUE_T ag_event_pool_t
8 : #include "../../util/tmpl/fd_queue_dynamic.c"
9 :
10 : #define QUEUE_NAME repair_events
11 0 : #define QUEUE_T ag_event_repair_t
12 : #include "../../util/tmpl/fd_queue_dynamic.c"
13 :
14 : struct slot_state_ele {
15 : ulong slot;
16 : ulong next;
17 : ag_slot_state_t slot_state;
18 : };
19 : typedef struct slot_state_ele slot_state_ele_t;
20 :
21 : #define POOL_NAME slot_state_pool
22 228 : #define POOL_T slot_state_ele_t
23 : #include "../../util/tmpl/fd_pool.c"
24 :
25 : #define MAP_NAME slot_state_map
26 : #define MAP_ELE_T slot_state_ele_t
27 540 : #define MAP_KEY slot
28 : #define MAP_KEY_T ulong
29 5547 : #define MAP_KEY_EQ(k0,k1) ((*(k0))==(*(k1)))
30 6579 : #define MAP_KEY_HASH(key,seed) (fd_ulong_hash( (*(key)) ^ (seed) ))
31 1218 : #define MAP_NEXT next
32 : #include "../../util/tmpl/fd_map_chain.c"
33 :
34 : struct s2n_waiting_parent_cert_ele {
35 : ag_block_id_t parent;
36 : ulong next;
37 : ag_block_id_t child;
38 : };
39 : typedef struct s2n_waiting_parent_cert_ele s2n_waiting_parent_cert_ele_t;
40 :
41 : #define POOL_NAME s2n_waiting_parent_cert_pool
42 228 : #define POOL_T s2n_waiting_parent_cert_ele_t
43 : #include "../../util/tmpl/fd_pool.c"
44 :
45 : #define MAP_NAME s2n_waiting_parent_cert_map
46 : #define MAP_ELE_T s2n_waiting_parent_cert_ele_t
47 24 : #define MAP_KEY parent
48 : #define MAP_KEY_T ag_block_id_t
49 6 : #define MAP_KEY_EQ(k0,k1) (ag_block_id_eq((k0),(k1)))
50 753 : #define MAP_KEY_HASH(key,seed) (fd_hash((seed),(key),sizeof(ag_block_id_t)))
51 24 : #define MAP_NEXT next
52 : #include "../../util/tmpl/fd_map_chain.c"
53 :
54 : struct slot_states {
55 : slot_state_ele_t * pool;
56 : slot_state_map_t * map;
57 : };
58 : typedef struct slot_states slot_states_t;
59 :
60 : struct s2n_waiting_parent_cert {
61 : s2n_waiting_parent_cert_ele_t * pool;
62 : s2n_waiting_parent_cert_map_t * map;
63 : };
64 : typedef struct s2n_waiting_parent_cert s2n_waiting_parent_cert_t;
65 :
66 : struct __attribute__((aligned(128UL))) ag_pool {
67 : ulong seq; /* sequence number for total ordering events as producer */
68 : ulong slot_max;
69 :
70 : slot_states_t * slot_states;
71 : ag_parent_ready_tracker_t * parent_ready_tracker;
72 : ag_finality_tracker_t * finality_tracker;
73 : s2n_waiting_parent_cert_t * s2n_waiting_parent_cert;
74 :
75 : ag_epoch_info_t const * prev_epoch_info;
76 : ulong prev_epoch_rank;
77 : ulong prev_epoch_slot;
78 : ag_epoch_info_t const * curr_epoch_info;
79 : ulong curr_epoch_rank;
80 : ulong curr_epoch_slot;
81 : ag_epoch_info_t const * next_epoch_info;
82 : ulong next_epoch_rank;
83 : ulong next_epoch_slot;
84 :
85 : ag_event_pool_t * pool_events;
86 : ag_event_repair_t * repair_events;
87 :
88 : struct {
89 : struct {
90 : ag_cert_t * own_certs;
91 : ag_vote_t * own_votes;
92 : } standstill;
93 : ag_parent_ready_t * parent_readys;
94 : ulong parent_ready_cnt;
95 : ag_block_id_t * implicitly_finalized;
96 : ulong * implicitly_skipped;
97 : } scratch;
98 : };
99 :
100 : ulong
101 1026 : ag_pool_align( void ) {
102 1026 : return alignof(ag_pool_t);
103 1026 : }
104 :
105 : ulong
106 228 : ag_pool_footprint( ulong slot_max ) {
107 228 : if( FD_UNLIKELY( slot_max<AG_SLOTS_PER_WINDOW+AG_REWARD_SLOT_DELTA ) ) return 0UL;
108 :
109 228 : ulong slot_chain_cnt = slot_state_map_chain_cnt_est( slot_max );
110 228 : ulong s2n_max = slot_max*AG_EQVOC_BLOCK_HASH_MAX;
111 228 : ulong own_cert_max = slot_max*( AG_NOTAR_FALLBACK_CERT_MAX + 1UL /* notar */ + 1UL /* skip */ );
112 228 : ulong own_vote_max = slot_max*( AG_NOTAR_FALLBACK_VOTE_MAX + 1UL /* notar or skip */ + 1UL /* skip_fallback */ );
113 228 : ulong s2n_chain_cnt = s2n_waiting_parent_cert_map_chain_cnt_est( s2n_max );
114 :
115 228 : return FD_LAYOUT_FINI(
116 228 : FD_LAYOUT_APPEND(
117 228 : FD_LAYOUT_APPEND(
118 228 : FD_LAYOUT_APPEND(
119 228 : FD_LAYOUT_APPEND(
120 228 : FD_LAYOUT_APPEND(
121 228 : FD_LAYOUT_APPEND(
122 228 : FD_LAYOUT_APPEND(
123 228 : FD_LAYOUT_APPEND(
124 228 : FD_LAYOUT_APPEND(
125 228 : FD_LAYOUT_APPEND(
126 228 : FD_LAYOUT_APPEND(
127 228 : FD_LAYOUT_APPEND(
128 228 : FD_LAYOUT_APPEND(
129 228 : FD_LAYOUT_APPEND(
130 228 : FD_LAYOUT_APPEND(
131 228 : FD_LAYOUT_APPEND(
132 228 : FD_LAYOUT_INIT,
133 228 : alignof(ag_pool_t), sizeof(ag_pool_t) ),
134 228 : alignof(slot_states_t), sizeof(slot_states_t) ),
135 228 : slot_state_pool_align(), slot_state_pool_footprint( slot_max ) ),
136 228 : slot_state_map_align(), slot_state_map_footprint ( slot_chain_cnt ) ),
137 228 : ag_parent_ready_tracker_align(), ag_parent_ready_tracker_footprint( slot_max ) ),
138 228 : ag_finality_tracker_align(), ag_finality_tracker_footprint( slot_max ) ),
139 228 : alignof(s2n_waiting_parent_cert_t), sizeof(s2n_waiting_parent_cert_t) ),
140 228 : s2n_waiting_parent_cert_pool_align(), s2n_waiting_parent_cert_pool_footprint( s2n_max ) ),
141 228 : s2n_waiting_parent_cert_map_align(), s2n_waiting_parent_cert_map_footprint ( s2n_chain_cnt ) ),
142 228 : pool_events_align(), pool_events_footprint( slot_max ) ),
143 228 : repair_events_align(), repair_events_footprint( slot_max ) ),
144 228 : alignof(ag_cert_t), sizeof(ag_cert_t) * own_cert_max ),
145 228 : alignof(ag_vote_t), sizeof(ag_vote_t) * own_vote_max ),
146 228 : alignof(ag_parent_ready_t), sizeof(ag_parent_ready_t) * slot_max ),
147 228 : alignof(ag_block_id_t), sizeof(ag_block_id_t) * slot_max ),
148 228 : alignof(ulong), sizeof(ulong) * slot_max ),
149 228 : ag_pool_align() );
150 228 : }
151 :
152 : void *
153 : ag_pool_new( void * mem,
154 : ulong slot_max,
155 114 : ulong seed ) {
156 114 : if( FD_UNLIKELY( !mem ) ) {
157 0 : FD_LOG_WARNING(( "NULL mem" ));
158 0 : return NULL;
159 0 : }
160 114 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)mem, ag_pool_align() ) ) ) {
161 0 : FD_LOG_WARNING(( "misaligned mem" ));
162 0 : return NULL;
163 0 : }
164 114 : ulong footprint = ag_pool_footprint( slot_max );
165 114 : if( FD_UNLIKELY( !footprint ) ) {
166 0 : FD_LOG_WARNING(( "bad slot_max (%lu)", slot_max ));
167 0 : return NULL;
168 0 : }
169 114 : fd_memset( mem, 0, footprint );
170 :
171 114 : ulong s2n_max = slot_max*AG_EQVOC_BLOCK_HASH_MAX;
172 114 : ulong own_cert_max = slot_max*( AG_NOTAR_FALLBACK_CERT_MAX + 1UL /* notar */ + 1UL /* skip */ );
173 114 : ulong own_vote_max = slot_max*( AG_NOTAR_FALLBACK_VOTE_MAX + 1UL /* notar or skip */ + 1UL /* skip_fallback */ );
174 114 : ulong slot_chain_cnt = slot_state_map_chain_cnt_est( slot_max );
175 114 : ulong s2n_chain_cnt = s2n_waiting_parent_cert_map_chain_cnt_est( s2n_max );
176 :
177 :
178 114 : FD_SCRATCH_ALLOC_INIT( l, mem );
179 114 : ag_pool_t * pool = FD_SCRATCH_ALLOC_APPEND( l, alignof(ag_pool_t), sizeof(ag_pool_t) );
180 114 : void * slot_states = FD_SCRATCH_ALLOC_APPEND( l, alignof(slot_states_t), sizeof(slot_states_t) );
181 114 : void * slot_state_pool = FD_SCRATCH_ALLOC_APPEND( l, slot_state_pool_align(), slot_state_pool_footprint( slot_max ) );
182 114 : void * slot_state_map = FD_SCRATCH_ALLOC_APPEND( l, slot_state_map_align(), slot_state_map_footprint ( slot_chain_cnt ) );
183 114 : void * parent_ready_tracker = FD_SCRATCH_ALLOC_APPEND( l, ag_parent_ready_tracker_align(), ag_parent_ready_tracker_footprint( slot_max ) );
184 114 : void * finality_tracker = FD_SCRATCH_ALLOC_APPEND( l, ag_finality_tracker_align(), ag_finality_tracker_footprint( slot_max ) );
185 114 : void * s2n_waiting_parent_cert = FD_SCRATCH_ALLOC_APPEND( l, alignof(s2n_waiting_parent_cert_t), sizeof(s2n_waiting_parent_cert_t) );
186 114 : 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 ) );
187 114 : 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 ) );
188 114 : void * pool_events = FD_SCRATCH_ALLOC_APPEND( l, pool_events_align(), pool_events_footprint( slot_max ) );
189 114 : void * repair_events = FD_SCRATCH_ALLOC_APPEND( l, repair_events_align(), repair_events_footprint( slot_max ) );
190 114 : void * own_cert_scratch = FD_SCRATCH_ALLOC_APPEND( l, alignof(ag_cert_t), sizeof(ag_cert_t) * own_cert_max );
191 114 : void * own_vote_scratch = FD_SCRATCH_ALLOC_APPEND( l, alignof(ag_vote_t), sizeof(ag_vote_t) * own_vote_max );
192 114 : void * parent_ready_scratch = FD_SCRATCH_ALLOC_APPEND( l, alignof(ag_parent_ready_t), sizeof(ag_parent_ready_t) * slot_max );
193 114 : void * implicitly_finalized_scratch = FD_SCRATCH_ALLOC_APPEND( l, alignof(ag_block_id_t), sizeof(ag_block_id_t) * slot_max );
194 114 : void * implicitly_skipped_scratch = FD_SCRATCH_ALLOC_APPEND( l, alignof(ulong), sizeof(ulong) * slot_max );
195 114 : FD_TEST( FD_SCRATCH_ALLOC_FINI( l, ag_pool_align() ) == (ulong)mem + footprint );
196 :
197 114 : pool->prev_epoch_info = NULL;
198 114 : pool->prev_epoch_rank = USHORT_MAX;
199 114 : pool->prev_epoch_slot = ULONG_MAX;
200 114 : pool->curr_epoch_info = NULL;
201 114 : pool->curr_epoch_rank = USHORT_MAX;
202 114 : pool->curr_epoch_slot = ULONG_MAX;
203 114 : pool->next_epoch_info = NULL;
204 114 : pool->next_epoch_rank = USHORT_MAX;
205 114 : pool->next_epoch_slot = ULONG_MAX;
206 :
207 114 : pool->slot_states = (slot_states_t *)slot_states;
208 114 : pool->slot_states->pool = slot_state_pool_join( slot_state_pool_new( slot_state_pool, slot_max ) );
209 114 : pool->slot_states->map = slot_state_map_join ( slot_state_map_new ( slot_state_map, slot_chain_cnt, seed ) );
210 :
211 114 : pool->parent_ready_tracker = ag_parent_ready_tracker_join( ag_parent_ready_tracker_new( parent_ready_tracker, slot_max, seed ) );
212 :
213 114 : pool->finality_tracker = ag_finality_tracker_join( ag_finality_tracker_new( finality_tracker, slot_max, seed ) );
214 :
215 114 : pool->s2n_waiting_parent_cert = (s2n_waiting_parent_cert_t *)s2n_waiting_parent_cert;
216 114 : 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 ) );
217 114 : 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 ) );
218 :
219 114 : pool->pool_events = pool_events_join ( pool_events_new ( pool_events, slot_max ) );
220 114 : pool->repair_events = repair_events_join( repair_events_new( repair_events, slot_max ) );
221 :
222 114 : pool->slot_max = slot_max;
223 :
224 114 : pool->seq = 0UL;
225 :
226 114 : pool->scratch.standstill.own_certs = (ag_cert_t *)own_cert_scratch;
227 114 : pool->scratch.standstill.own_votes = (ag_vote_t *)own_vote_scratch;
228 114 : pool->scratch.parent_readys = (ag_parent_ready_t *)parent_ready_scratch;
229 114 : pool->scratch.parent_ready_cnt = 0UL;
230 114 : pool->scratch.implicitly_finalized = (ag_block_id_t *)implicitly_finalized_scratch;
231 114 : pool->scratch.implicitly_skipped = (ulong *)implicitly_skipped_scratch;
232 :
233 :
234 114 : return mem;
235 114 : }
236 :
237 : ag_pool_t *
238 114 : ag_pool_join( void * mem ) {
239 114 : ag_pool_t * pool = (ag_pool_t *)mem;
240 114 : if( FD_UNLIKELY( !pool ) ) {
241 0 : FD_LOG_WARNING(( "NULL mem" ));
242 0 : return NULL;
243 0 : }
244 114 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)pool, ag_pool_align() ) ) ) {
245 0 : FD_LOG_WARNING(( "misaligned mem" ));
246 0 : return NULL;
247 0 : }
248 114 : return pool;
249 114 : }
250 :
251 : void *
252 114 : ag_pool_leave( ag_pool_t const * pool ) {
253 114 : if( FD_UNLIKELY( !pool ) ) {
254 0 : FD_LOG_WARNING(( "NULL pool" ));
255 0 : return NULL;
256 0 : }
257 114 : return (void *)pool;
258 114 : }
259 :
260 : void *
261 114 : ag_pool_delete( void * mem ) {
262 114 : if( FD_UNLIKELY( !mem ) ) {
263 0 : FD_LOG_WARNING(( "NULL mem" ));
264 0 : return NULL;
265 0 : }
266 114 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)mem, ag_pool_align() ) ) ) {
267 0 : FD_LOG_WARNING(( "misaligned mem" ));
268 0 : return NULL;
269 0 : }
270 114 : return mem;
271 114 : }
272 :
273 : void
274 : ag_pool_init( ag_pool_t * self,
275 114 : ulong slot ) {
276 114 : ag_finality_tracker_init( self->finality_tracker, slot );
277 114 : self->parent_ready_tracker->root = slot;
278 114 : }
279 :
280 : void
281 0 : ag_pool_fini( ag_pool_t * self ) {
282 0 : ag_finality_tracker_fini( self->finality_tracker );
283 0 : self->parent_ready_tracker->root = ULONG_MAX;
284 0 : }
285 :
286 : FD_FN_CONST char const *
287 0 : ag_pool_strerror( int err ) {
288 0 : switch( err ) {
289 0 : case AG_POOL_SUCCESS: return "success";
290 0 : case AG_POOL_ERR_SLOT_OUT_OF_BOUNDS: return "slot is either too old or too far in the future";
291 0 : case AG_POOL_ERR_DUPLICATE: return "duplicate vote or cert";
292 0 : case AG_POOL_ERR_SLASHABLE: return "vote constitutes a slashable offence";
293 0 : case AG_POOL_ERR_CERT_VERIFY: return "cert failed the signature or threshold check";
294 0 : default: return "unknown";
295 0 : }
296 0 : }
297 :
298 : static ag_slot_state_t *
299 : slot_state( ag_pool_t * self,
300 5439 : ulong slot ) {
301 5439 : slot_state_ele_t * ele = slot_state_map_ele_query( self->slot_states->map, &slot, NULL, self->slot_states->pool );
302 5439 : if( FD_LIKELY( ele ) ) return &ele->slot_state;
303 :
304 540 : ag_epoch_info_t const * info = fd_ptr_if ( slot>=self->next_epoch_slot, self->next_epoch_info, fd_ptr_if ( slot>=self->curr_epoch_slot, self->curr_epoch_info, self->prev_epoch_info ) );
305 540 : ulong rank = fd_ulong_if( slot>=self->next_epoch_slot, self->next_epoch_rank, fd_ulong_if( slot>=self->curr_epoch_slot, self->curr_epoch_rank, self->prev_epoch_rank ) );
306 :
307 540 : FD_TEST( info );
308 540 : FD_TEST( slot_state_pool_free( self->slot_states->pool ) );
309 :
310 540 : ele = slot_state_pool_ele_acquire( self->slot_states->pool );
311 540 : ele->slot = slot;
312 540 : ag_slot_state_zero( &ele->slot_state, slot, info, rank );
313 540 : slot_state_map_ele_insert( self->slot_states->map, ele, self->slot_states->pool );
314 540 : return &ele->slot_state;
315 540 : }
316 :
317 : static inline ag_finalization_event_t
318 747 : finalization_event_default( ag_pool_t * self ) {
319 747 : return ag_finalization_event_default( self->scratch.implicitly_finalized, self->scratch.implicitly_skipped );
320 747 : }
321 :
322 : static void
323 : handle_finalization( ag_pool_t * self,
324 720 : ag_finalization_event_t const * event ) {
325 720 : ag_parent_ready_t new_parents_ready = ag_parent_ready_tracker_handle_finalization( self->parent_ready_tracker, event, self->scratch.parent_readys, &self->scratch.parent_ready_cnt );
326 720 : if( FD_LIKELY( new_parents_ready.slot!=ULONG_MAX ) ) {
327 18 : ag_event_pool_t event = { .seq = self->seq++, .kind = AG_EVENT_POOL_PARENT_READY, .parent_ready = { .slot = new_parents_ready.slot, .parent = new_parents_ready.parent } };
328 18 : pool_events_push( self->pool_events, event );
329 18 : }
330 720 : ulong first_unpruned_slot = ag_finality_tracker_first_unpruned_slot( self->finality_tracker );
331 720 : ulong retained_slot = fd_ulong_sat_sub( first_unpruned_slot, AG_REWARD_SLOT_DELTA );
332 891 : for( ulong slot = fd_ulong_sat_sub( self->parent_ready_tracker->root, AG_REWARD_SLOT_DELTA ); slot<retained_slot; slot++ ) {
333 171 : slot_state_ele_t * ele = slot_state_map_ele_remove( self->slot_states->map, &slot, NULL, self->slot_states->pool );
334 171 : if( FD_LIKELY( ele ) ) slot_state_pool_ele_release( self->slot_states->pool, ele );
335 171 : }
336 720 : ag_parent_ready_tracker_prune( self->parent_ready_tracker, first_unpruned_slot );
337 720 : }
338 :
339 : static void
340 : add_valid_cert( ag_pool_t * self,
341 : ag_cert_t const * cert,
342 1113 : fd_bls_set_t * bad ) {
343 1113 : ulong slot = ag_cert_slot( cert );
344 :
345 1113 : ag_slot_state_add_cert( slot_state( self, slot ), cert );
346 :
347 1113 : switch( cert->kind ) {
348 45 : case AG_CERT_KIND_FINAL: {
349 45 : ag_finalization_event_t finalization_event = finalization_event_default( self );
350 45 : ag_finality_tracker_mark_finalized( self->finality_tracker, slot, &finalization_event );
351 45 : handle_finalization( self, &finalization_event );
352 45 : break;
353 0 : }
354 :
355 321 : case AG_CERT_KIND_FAST_FINAL: {
356 321 : ag_cert_fast_final_t const * ff_cert = &cert->fast_final;
357 321 : ag_block_id_t block_id = ag_block_id( slot, ff_cert->block_hash );
358 321 : ag_finalization_event_t finalization_event = finalization_event_default( self );
359 321 : ag_finality_tracker_mark_fast_finalized( self->finality_tracker, &block_id, &finalization_event );
360 321 : handle_finalization( self, &finalization_event );
361 321 : break;
362 0 : }
363 :
364 354 : case AG_CERT_KIND_NOTAR:
365 699 : case AG_CERT_KIND_NOTAR_FALLBACK: {
366 699 : uchar const * block_hash = ag_cert_block_hash( cert );
367 699 : ag_block_id_t block_id = ag_block_id( slot, block_hash );
368 699 : if( FD_LIKELY( cert->kind==AG_CERT_KIND_NOTAR ) ) {
369 354 : ag_finalization_event_t finalization_event = finalization_event_default( self );
370 354 : ag_finality_tracker_mark_notarized( self->finality_tracker, &block_id, &finalization_event );
371 354 : handle_finalization( self, &finalization_event );
372 354 : }
373 :
374 699 : s2n_waiting_parent_cert_ele_t * child = s2n_waiting_parent_cert_map_ele_remove( self->s2n_waiting_parent_cert->map, &block_id, NULL, self->s2n_waiting_parent_cert->pool );
375 699 : if( FD_LIKELY( child ) ) {
376 0 : ag_block_id_t child_id = child->child; /* copy before the release below */
377 0 : s2n_waiting_parent_cert_pool_ele_release( self->s2n_waiting_parent_cert->pool, child );
378 :
379 0 : ag_slot_state_t * child_state = slot_state( self, child_id.slot );
380 0 : fd_bls_set_t bad_child[ fd_bls_set_word_cnt ];
381 0 : int output = ag_slot_state_notify_parent_certified( child_state, child_id.hash, bad_child );
382 0 : if( FD_LIKELY( child_state->epoch_info==slot_state( self, slot )->epoch_info ) ) fd_bls_set_union( bad, bad, bad_child );
383 0 : switch( output ) {
384 0 : case -1: repair_events_push( self->repair_events, (ag_event_repair_t){ .seq = self->seq++, .block = child_id } ); break;
385 0 : case 0: break;
386 0 : case 1: pool_events_push( self->pool_events, (ag_event_pool_t){ .seq = self->seq++, .kind = AG_EVENT_POOL_SAFE_TO_NOTAR, .safe_to_notar = child_id } ); break;
387 0 : }
388 0 : }
389 :
390 699 : ag_parent_ready_tracker_mark_notar_fallback( self->parent_ready_tracker, &block_id, self->scratch.parent_readys, &self->scratch.parent_ready_cnt );
391 699 : ag_parent_ready_t const * readys = self->scratch.parent_readys;
392 699 : ulong ready_cnt = self->scratch.parent_ready_cnt;
393 780 : for( ulong i=0UL; i<ready_cnt; i++ ) {
394 81 : ag_parent_ready_t const * ready = &readys[i];
395 81 : FD_TEST( ag_is_start_of_window( ready->slot ) ); /* readiness is granted at window starts */
396 81 : ag_event_pool_t event = { .seq = self->seq++, .kind = AG_EVENT_POOL_PARENT_READY };
397 81 : event.parent_ready.slot = ready->slot;
398 81 : event.parent_ready.parent = ready->parent;
399 81 : pool_events_push( self->pool_events, event );
400 81 : }
401 :
402 699 : repair_events_push( self->repair_events, (ag_event_repair_t){ .seq = self->seq++, .block = block_id } );
403 699 : break;
404 699 : }
405 :
406 48 : case AG_CERT_KIND_SKIP: {
407 48 : ag_parent_ready_tracker_mark_skipped( self->parent_ready_tracker, slot, self->scratch.parent_readys, &self->scratch.parent_ready_cnt );
408 48 : ag_parent_ready_t const * readys = self->scratch.parent_readys;
409 48 : ulong ready_cnt = self->scratch.parent_ready_cnt;
410 57 : for( ulong i=0UL; i<ready_cnt; i++ ) {
411 9 : ag_parent_ready_t const * ready = &readys[i];
412 9 : FD_TEST( ag_is_start_of_window( ready->slot ) ); /* readiness is granted at window starts */
413 9 : ag_event_pool_t event = { .seq = self->seq++, .kind = AG_EVENT_POOL_PARENT_READY };
414 9 : event.parent_ready.slot = ready->slot;
415 9 : event.parent_ready.parent = ready->parent;
416 9 : pool_events_push( self->pool_events, event );
417 9 : }
418 48 : break;
419 48 : }
420 :
421 48 : default:
422 0 : FD_LOG_ERR(( "invalid cert kind %u", cert->kind ));
423 1113 : }
424 :
425 1113 : ag_event_pool_t event = { .seq = self->seq++, .kind = AG_EVENT_POOL_CERT_CREATED, .cert_created = *cert };
426 1113 : pool_events_push( self->pool_events, event );
427 1113 : }
428 :
429 : void
430 : ag_pool_advance_epoch( ag_pool_t * self,
431 : ag_epoch_info_t const * epoch_info,
432 : ulong epoch_rank,
433 132 : ulong epoch_slot ) {
434 132 : if( FD_UNLIKELY( !self->curr_epoch_info ) ) {
435 114 : self->curr_epoch_info = epoch_info;
436 114 : self->curr_epoch_rank = epoch_rank;
437 114 : self->curr_epoch_slot = epoch_slot;
438 114 : } else if( FD_UNLIKELY( !self->next_epoch_info ) ) {
439 12 : self->next_epoch_info = epoch_info;
440 12 : self->next_epoch_rank = epoch_rank;
441 12 : self->next_epoch_slot = epoch_slot;
442 12 : } else {
443 6 : self->prev_epoch_info = self->curr_epoch_info;
444 6 : self->prev_epoch_slot = self->curr_epoch_slot;
445 6 : self->prev_epoch_rank = self->curr_epoch_rank;
446 6 : self->curr_epoch_info = self->next_epoch_info;
447 6 : self->curr_epoch_slot = self->next_epoch_slot;
448 6 : self->curr_epoch_rank = self->next_epoch_rank;
449 6 : self->next_epoch_info = epoch_info;
450 6 : self->next_epoch_rank = epoch_rank;
451 6 : self->next_epoch_slot = epoch_slot;
452 6 : }
453 132 : }
454 :
455 : int
456 : ag_pool_add_cert( ag_pool_t * self,
457 : ag_cert_t const * cert,
458 147 : fd_bls_set_t * bad ) {
459 147 : ulong slot = ag_cert_slot( cert );
460 147 : fd_bls_set_null( bad );
461 :
462 147 : ulong slot_far_in_future = ag_finality_tracker_first_unpruned_slot( self->finality_tracker ) + self->slot_max - AG_REWARD_SLOT_DELTA;
463 147 : if( FD_UNLIKELY( slot<ag_finality_tracker_first_unpruned_slot( self->finality_tracker ) || slot>=slot_far_in_future ) ) return AG_POOL_ERR_SLOT_OUT_OF_BOUNDS;
464 :
465 111 : ag_epoch_info_t const * epoch_info = fd_ptr_if( slot>=self->next_epoch_slot, self->next_epoch_info, fd_ptr_if( slot>=self->curr_epoch_slot, self->curr_epoch_info, self->prev_epoch_info ) );
466 111 : if( FD_UNLIKELY( !epoch_info ) ) return AG_POOL_ERR_SLOT_OUT_OF_BOUNDS;
467 :
468 111 : ag_slot_state_t * state = slot_state( self, slot );
469 111 : int duplicate = 0;
470 111 : switch( cert->kind ) {
471 0 : case AG_CERT_KIND_FINAL: duplicate = state->certs.finalize.slot!=ULONG_MAX; break;
472 81 : case AG_CERT_KIND_FAST_FINAL: duplicate = state->certs.fast_finalize.slot!=ULONG_MAX; break;
473 21 : case AG_CERT_KIND_NOTAR: duplicate = state->certs.notar.slot!=ULONG_MAX; break;
474 0 : case AG_CERT_KIND_NOTAR_FALLBACK: duplicate = ag_slot_state_is_notar_fallback( state, ag_cert_block_hash( cert ) ); break;
475 9 : case AG_CERT_KIND_SKIP: duplicate = state->certs.skip.slot!=ULONG_MAX; break;
476 0 : default: FD_LOG_CRIT(( "unreachable" ));
477 111 : }
478 111 : if( FD_UNLIKELY( duplicate ) ) return AG_POOL_ERR_DUPLICATE;
479 :
480 105 : if( FD_UNLIKELY( !ag_cert_verify( cert, epoch_info ) ) ) return AG_POOL_ERR_CERT_VERIFY;
481 :
482 105 : switch( cert->kind ) { /* a skip cert excludes finalization certs, Lemmas 23 and 28 */
483 0 : case AG_CERT_KIND_FINAL:
484 81 : case AG_CERT_KIND_FAST_FINAL: FD_CHECK_CRIT( state->certs.skip.slot==ULONG_MAX, "consensus safety violation" ); break;
485 81 : case AG_CERT_KIND_NOTAR:
486 18 : case AG_CERT_KIND_NOTAR_FALLBACK: break;
487 6 : 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;
488 6 : default: FD_LOG_CRIT(( "unreachable" ));
489 105 : }
490 :
491 105 : add_valid_cert( self, cert, bad );
492 105 : return AG_POOL_SUCCESS;
493 105 : }
494 :
495 : int
496 : ag_pool_add_vote( ag_pool_t * self,
497 : ag_vote_t const * vote,
498 4305 : fd_bls_set_t * bad ) {
499 4305 : ulong slot = ag_vote_slot( vote );
500 4305 : fd_bls_set_null( bad );
501 :
502 4305 : ulong first_unpruned_slot = ag_finality_tracker_first_unpruned_slot( self->finality_tracker );
503 4305 : ulong retained_slot = fd_ulong_sat_sub( first_unpruned_slot, AG_REWARD_SLOT_DELTA );
504 4305 : ulong slot_far_in_future = first_unpruned_slot + self->slot_max - AG_REWARD_SLOT_DELTA;
505 4305 : if( FD_UNLIKELY( slot<retained_slot || slot>=slot_far_in_future ) ) {
506 132 : return AG_POOL_ERR_SLOT_OUT_OF_BOUNDS;
507 132 : }
508 4173 : if( FD_UNLIKELY( !fd_ptr_if( slot>=self->next_epoch_slot, self->next_epoch_info, fd_ptr_if( slot>=self->curr_epoch_slot, self->curr_epoch_info, self->prev_epoch_info ) ) ) ) {
509 0 : return AG_POOL_ERR_SLOT_OUT_OF_BOUNDS;
510 0 : }
511 :
512 4173 : ulong voter = ag_vote_rank( vote );
513 4173 : ag_slot_state_t * slot_state_ = slot_state( self, slot );
514 4173 : ulong voter_stake = ag_epoch_info_validator( slot_state_->epoch_info, voter )->stake;
515 :
516 4173 : if ( FD_UNLIKELY( ag_slot_state_check_slashable_offence( slot_state_, vote )!=AG_SLASHABLE_NONE ) ) {
517 6 : return AG_POOL_ERR_SLASHABLE;
518 4167 : } else if( FD_UNLIKELY( ag_slot_state_should_ignore_vote( slot_state_, vote ) ) ) {
519 6 : return AG_POOL_ERR_DUPLICATE;
520 6 : }
521 :
522 4161 : ag_event_cert_t cert_events [ AG_SLOT_STATE_OUT_CERT_MAX ]; ulong cert_event_cnt;
523 4161 : ag_event_pool_t pool_events [ AG_SLOT_STATE_OUT_EVENT_MAX ]; ulong pool_event_cnt;
524 4161 : ag_event_repair_t repair_events[ AG_SLOT_STATE_OUT_REPAIR_MAX ]; ulong repair_event_cnt;
525 4161 : ag_slot_state_add_vote( slot_state_, vote, voter_stake, cert_events, &cert_event_cnt, pool_events, &pool_event_cnt, repair_events, &repair_event_cnt, bad );
526 :
527 5169 : for( ulong i=0UL; i<cert_event_cnt; i++ ) add_valid_cert( self, &cert_events[i].cert, bad );
528 4170 : for( ulong i=0UL; i<pool_event_cnt; i++ ) { pool_events [i].seq = self->seq++; pool_events_push ( self->pool_events, pool_events [i] ); }
529 6198 : for( ulong i=0UL; i<repair_event_cnt; i++ ) { repair_events[i].seq = self->seq++; repair_events_push( self->repair_events, repair_events[i] ); }
530 4161 : return AG_POOL_SUCCESS;
531 4173 : }
532 :
533 : ag_slot_state_t const *
534 : ag_pool_slot_state( ag_pool_t const * self,
535 45 : ulong slot ) {
536 45 : slot_state_ele_t const * ele = slot_state_map_ele_query_const( self->slot_states->map, &slot, NULL, self->slot_states->pool );
537 45 : return ele ? &ele->slot_state : NULL;
538 45 : }
539 :
540 : int
541 : ag_pool_add_block( ag_pool_t * self,
542 : ag_block_id_t const * block_id,
543 : ag_block_id_t const * parent_id,
544 33 : fd_bls_set_t * bad ) {
545 33 : fd_bls_set_null( bad );
546 :
547 33 : ulong slot = block_id->slot;
548 33 : uchar const * block_hash = block_id->hash;
549 33 : ulong parent_slot = parent_id->slot;
550 33 : uchar const * parent_hash = parent_id->hash;
551 :
552 33 : ulong slot_far_in_future = ag_finality_tracker_first_unpruned_slot( self->finality_tracker ) + self->slot_max - AG_REWARD_SLOT_DELTA;
553 33 : if( FD_UNLIKELY( slot<ag_finality_tracker_first_unpruned_slot( self->finality_tracker ) || slot>=slot_far_in_future ) ) return AG_POOL_ERR_SLOT_OUT_OF_BOUNDS;
554 27 : if( FD_UNLIKELY( !fd_ptr_if( slot>=self->next_epoch_slot, self->next_epoch_info, fd_ptr_if( slot>=self->curr_epoch_slot, self->curr_epoch_info, self->prev_epoch_info ) ) ) ) return AG_POOL_ERR_SLOT_OUT_OF_BOUNDS;
555 :
556 27 : ag_finalization_event_t finalization_event = finalization_event_default( self );
557 27 : ag_finality_tracker_add_parent( self->finality_tracker, block_id, parent_id, &finalization_event );
558 27 : ag_parent_ready_t new_parents_ready = ag_parent_ready_tracker_handle_finalization( self->parent_ready_tracker, &finalization_event, self->scratch.parent_readys, &self->scratch.parent_ready_cnt );
559 27 : if( FD_UNLIKELY( new_parents_ready.slot!=ULONG_MAX ) ) {
560 6 : ag_event_pool_t event = { .seq = self->seq++, .kind = AG_EVENT_POOL_PARENT_READY, .parent_ready = { .slot = new_parents_ready.slot, .parent = new_parents_ready.parent } };
561 6 : pool_events_push( self->pool_events, event );
562 6 : }
563 :
564 27 : ag_slot_state_notify_parent_known( slot_state( self, slot ), block_hash );
565 27 : slot_state_ele_t * parent_state_ = slot_state_map_ele_query( self->slot_states->map, &parent_slot, NULL, self->slot_states->pool );
566 27 : ag_slot_state_t * parent_state = parent_state_ ? &parent_state_->slot_state : NULL;
567 27 : if( FD_LIKELY( parent_state && ag_slot_state_is_notar_fallback_or_stronger( parent_state, parent_hash ) ) ) {
568 15 : int output = ag_slot_state_notify_parent_certified( slot_state( self, slot ), block_hash, bad );
569 15 : switch( output ) {
570 0 : case -1: repair_events_push( self->repair_events, (ag_event_repair_t){ .seq = self->seq++, .block = *block_id } ); return AG_POOL_SUCCESS;
571 12 : case 0: break;
572 3 : case 1: pool_events_push( self->pool_events, (ag_event_pool_t){ .seq = self->seq++, .kind = AG_EVENT_POOL_SAFE_TO_NOTAR, .safe_to_notar = *block_id } ); return AG_POOL_SUCCESS;
573 15 : }
574 15 : }
575 :
576 24 : s2n_waiting_parent_cert_ele_t * ele = s2n_waiting_parent_cert_map_ele_query( self->s2n_waiting_parent_cert->map, parent_id, NULL, self->s2n_waiting_parent_cert->pool );
577 24 : if( FD_UNLIKELY( !ele ) ) {
578 24 : ele = s2n_waiting_parent_cert_pool_ele_acquire( self->s2n_waiting_parent_cert->pool );
579 24 : ele->parent = *parent_id;
580 24 : s2n_waiting_parent_cert_map_ele_insert( self->s2n_waiting_parent_cert->map, ele, self->s2n_waiting_parent_cert->pool );
581 24 : }
582 24 : ele->child = *block_id;
583 :
584 24 : return AG_POOL_SUCCESS;
585 27 : }
586 :
587 : void
588 6 : ag_pool_recover_from_standstill( ag_pool_t * self ) {
589 6 : ag_cert_t * certs = self->scratch.standstill.own_certs;
590 6 : ulong certs_cnt = 0UL;
591 6 : ag_vote_t * votes = self->scratch.standstill.own_votes;
592 6 : ulong votes_cnt = 0UL;
593 :
594 : /* 1. collect our finalized slot's cert */
595 :
596 6 : ulong finalized_slot = ag_pool_finalized_slot( self );
597 6 : slot_state_ele_t const * fast_final_or_final_ = slot_state_map_ele_query_const( self->slot_states->map, &finalized_slot, NULL, self->slot_states->pool );
598 6 : if( FD_LIKELY( fast_final_or_final_ ) ) { /* possible no cert if snapshot slot */
599 3 : ag_slot_certs_t const * fast_final_or_final = &fast_final_or_final_->slot_state.certs;
600 3 : if( FD_LIKELY( fast_final_or_final->fast_finalize.slot!=ULONG_MAX ) ) {
601 3 : certs[certs_cnt++] = (ag_cert_t){ .kind = AG_CERT_KIND_FAST_FINAL, .fast_final = fast_final_or_final->fast_finalize };
602 3 : } else {
603 0 : FD_TEST( fast_final_or_final->finalize.slot!=ULONG_MAX );
604 0 : FD_TEST( fast_final_or_final->notar.slot !=ULONG_MAX );
605 0 : certs[certs_cnt++] = (ag_cert_t){ .kind = AG_CERT_KIND_FINAL, .final = fast_final_or_final->finalize };
606 0 : certs[certs_cnt++] = (ag_cert_t){ .kind = AG_CERT_KIND_NOTAR, .notar = fast_final_or_final->notar };
607 0 : }
608 3 : }
609 :
610 : /* 2. collect every cert and own vote for slots > finalized slot */
611 :
612 6 : slot_state_map_t * map = self->slot_states->map;
613 6 : slot_state_ele_t * pool = self->slot_states->pool;
614 6 : for( slot_state_map_iter_t iter = slot_state_map_iter_init( map, pool );
615 15 : !slot_state_map_iter_done( iter, map, pool );
616 9 : iter = slot_state_map_iter_next( iter, map, pool ) ) {
617 9 : ag_slot_state_t const * slot_state = &slot_state_map_iter_ele_const( iter, map, pool )->slot_state;
618 9 : if( FD_UNLIKELY( slot_state->slot<=finalized_slot ) ) continue;
619 :
620 6 : if( FD_UNLIKELY( slot_state->certs.finalize.slot !=ULONG_MAX ) ) certs[ certs_cnt++ ] = (ag_cert_t){ .kind = AG_CERT_KIND_FINAL, .final = slot_state->certs.finalize };
621 6 : if( FD_UNLIKELY( slot_state->certs.fast_finalize.slot!=ULONG_MAX ) ) certs[ certs_cnt++ ] = (ag_cert_t){ .kind = AG_CERT_KIND_FAST_FINAL, .fast_final = slot_state->certs.fast_finalize };
622 6 : if( FD_LIKELY ( slot_state->certs.notar.slot !=ULONG_MAX ) ) certs[ certs_cnt++ ] = (ag_cert_t){ .kind = AG_CERT_KIND_NOTAR, .notar = slot_state->certs.notar };
623 6 : for( ulong i=0UL; i<slot_state->certs.notar_fallback_cnt; i++ ) {
624 0 : certs[ certs_cnt++ ] = (ag_cert_t){ .kind = AG_CERT_KIND_NOTAR_FALLBACK, .notar_fallback = slot_state->certs.notar_fallback[i] };
625 0 : }
626 6 : if( FD_UNLIKELY( slot_state->certs.skip.slot !=ULONG_MAX ) ) certs[ certs_cnt++ ] = (ag_cert_t){ .kind = AG_CERT_KIND_SKIP, .skip = slot_state->certs.skip };
627 :
628 6 : ag_slot_voted_stake_t const * voted_stakes = &slot_state->votes;
629 6 : ulong own_rank = slot_state->own_rank;
630 6 : ushort shred_version = slot_state->shred_version;
631 6 : if( FD_UNLIKELY( own_rank==USHORT_MAX ) ) continue; /* unstaked */
632 12294 : for( ulong slot_idx=0UL; slot_idx<notar_map_slot_cnt(); slot_idx++ ) {
633 12288 : if( FD_LIKELY( notar_map_key_inval( voted_stakes->notar[ slot_idx ].hash ) || !fd_bls_set_test( voted_stakes->notar[ slot_idx ].agg.set, own_rank ) ) ) continue;
634 3 : votes[ votes_cnt ] = (ag_vote_t){ .kind = AG_VOTE_KIND_NOTAR, .notar = { .slot = slot_state->slot, .sig = voted_stakes->notar_sig[ own_rank ], .rank = (ushort)own_rank, .shred_version = shred_version } };
635 3 : memcpy( votes[ votes_cnt ].notar.block_hash, voted_stakes->notar[ slot_idx ].hash.uc, sizeof(ag_block_hash_t) );
636 3 : votes_cnt++;
637 3 : }
638 6 : if( FD_LIKELY ( fd_bls_set_test( voted_stakes->finalize_agg.set, own_rank ) ) ) votes[ votes_cnt++ ] = (ag_vote_t){ .kind = AG_VOTE_KIND_FINAL, .final = { .slot = slot_state->slot, .sig = voted_stakes->finalize_sig[ own_rank ], .rank = (ushort)own_rank, .shred_version = shred_version } };
639 6 : if( FD_UNLIKELY( fd_bls_set_test( voted_stakes->skip_agg.set, own_rank ) ) ) votes[ votes_cnt++ ] = (ag_vote_t){ .kind = AG_VOTE_KIND_SKIP, .skip = { .slot = slot_state->slot, .sig = voted_stakes->skip_sig [ own_rank ], .rank = (ushort)own_rank, .shred_version = shred_version } };
640 6 : for( ulong j=0UL; j<voted_stakes->notar_fallback_sig_cnt[ own_rank ]; j++ ) {
641 0 : votes[ votes_cnt ] = (ag_vote_t){ .kind = AG_VOTE_KIND_NOTAR_FALLBACK, .notar_fallback = { .slot = slot_state->slot, .sig = voted_stakes->notar_fallback_sig[ own_rank ][ j ], .rank = (ushort)own_rank, .shred_version = shred_version } };
642 0 : memcpy( votes[ votes_cnt ].notar_fallback.block_hash, voted_stakes->notar_fallback_sig_hash[ own_rank ][ j ], sizeof(ag_block_hash_t) );
643 0 : votes_cnt++;
644 0 : }
645 6 : if( FD_UNLIKELY( fd_bls_set_test( voted_stakes->skip_fallback_agg.set, own_rank ) ) ) votes[ votes_cnt++ ] = (ag_vote_t){ .kind = AG_VOTE_KIND_SKIP_FALLBACK, .skip_fallback = { .slot = slot_state->slot, .sig = voted_stakes->skip_fallback_sig[ own_rank ], .rank = (ushort)own_rank, .shred_version = shred_version } };
646 6 : }
647 :
648 : /* 3. push out a standstill pool event containing the above */
649 :
650 6 : pool_events_push( self->pool_events, (ag_event_pool_t){ .seq = self->seq++, .kind = AG_EVENT_POOL_STANDSTILL, .standstill = { .slot = finalized_slot + 1UL, .certs = certs, .cert_cnt = certs_cnt, .votes = votes, .vote_cnt = votes_cnt } } );
651 6 : }
652 :
653 : FD_FN_PURE ulong
654 57 : ag_pool_finalized_slot( ag_pool_t const * self ) {
655 57 : return ag_finality_tracker_highest_finalized_slot( self->finality_tracker );
656 57 : }
657 :
658 : FD_FN_PURE uchar const *
659 9 : ag_pool_finalized_block_hash( ag_pool_t const * self ) {
660 9 : return ag_finality_tracker_highest_finalized_block_hash( self->finality_tracker );
661 9 : }
662 :
663 : ag_block_id_t const *
664 : ag_pool_parents_ready( ag_pool_t * self,
665 : ulong slot,
666 48 : ulong * cnt ) {
667 48 : return ag_parent_ready_tracker_parents_ready( self->parent_ready_tracker, slot, cnt );
668 48 : }
669 :
670 : ag_block_id_t
671 : ag_pool_wait_for_parent_ready( ag_pool_t * self,
672 9 : ulong slot ) {
673 9 : return ag_parent_ready_tracker_wait_for_parent_ready( self->parent_ready_tracker, slot );
674 9 : }
675 :
676 : int
677 : ag_pool_poll_pool_event( ag_pool_t * self,
678 0 : ag_event_pool_t * event ) {
679 0 : if( FD_LIKELY( pool_events_empty( self->pool_events ) ) ) return 0;
680 0 : *event = pool_events_pop( self->pool_events );
681 0 : return 1;
682 0 : }
683 :
684 : int
685 : ag_pool_poll_repair_event( ag_pool_t * self,
686 0 : ag_event_repair_t * event ) {
687 0 : if( FD_LIKELY( repair_events_empty( self->repair_events ) ) ) return 0;
688 0 : *event = repair_events_pop( self->repair_events );
689 0 : return 1;
690 0 : }
|