Line data Source code
1 : #include "fd_tower.h"
2 : #include "../../flamenco/txn/fd_txn_generate.h"
3 : #include "../../flamenco/runtime/fd_system_ids.h"
4 :
5 0 : #define THRESHOLD_DEPTH (8)
6 0 : #define THRESHOLD_PCT (2.0 / 3.0)
7 : #define SHALLOW_THRESHOLD_DEPTH (4)
8 : #define SHALLOW_THRESHOLD_PCT (0.38)
9 0 : #define SWITCH_PCT (0.38)
10 :
11 : void *
12 3 : fd_tower_new( void * shmem ) {
13 3 : if( FD_UNLIKELY( !shmem ) ) {
14 0 : FD_LOG_WARNING(( "NULL mem" ));
15 0 : return NULL;
16 0 : }
17 :
18 3 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shmem, fd_tower_align() ) ) ) {
19 0 : FD_LOG_WARNING(( "misaligned mem" ));
20 0 : return NULL;
21 0 : }
22 :
23 3 : return fd_tower_votes_new( shmem );
24 3 : }
25 :
26 : fd_tower_t *
27 3 : fd_tower_join( void * shtower ) {
28 :
29 3 : if( FD_UNLIKELY( !shtower ) ) {
30 0 : FD_LOG_WARNING(( "NULL tower" ));
31 0 : return NULL;
32 0 : }
33 :
34 3 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shtower, fd_tower_align() ) ) ) {
35 0 : FD_LOG_WARNING(( "misaligned tower" ));
36 0 : return NULL;
37 0 : }
38 :
39 3 : return fd_tower_votes_join( shtower );
40 3 : }
41 :
42 : void *
43 3 : fd_tower_leave( fd_tower_t * tower ) {
44 :
45 3 : if( FD_UNLIKELY( !tower ) ) {
46 0 : FD_LOG_WARNING(( "NULL tower" ));
47 0 : return NULL;
48 0 : }
49 :
50 3 : return fd_tower_votes_leave( tower );
51 3 : }
52 :
53 : void *
54 3 : fd_tower_delete( void * tower ) {
55 :
56 3 : if( FD_UNLIKELY( !tower ) ) {
57 0 : FD_LOG_WARNING(( "NULL tower" ));
58 0 : return NULL;
59 0 : }
60 :
61 3 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)tower, fd_tower_align() ) ) ) {
62 0 : FD_LOG_WARNING(( "misaligned tower" ));
63 0 : return NULL;
64 0 : }
65 :
66 3 : return fd_tower_votes_delete( tower );
67 3 : }
68 :
69 : static inline ulong
70 105 : expiration( fd_tower_vote_t const * vote ) {
71 105 : ulong lockout = 1UL << vote->conf;
72 105 : return vote->slot + lockout;
73 105 : }
74 :
75 : static inline ulong
76 102 : simulate_vote( fd_tower_t const * tower, ulong slot ) {
77 102 : ulong cnt = fd_tower_votes_cnt( tower );
78 108 : while( cnt ) {
79 :
80 : /* Return early if we can't pop the top tower vote, even if votes
81 : below it are expired. */
82 :
83 105 : if( FD_LIKELY( expiration( fd_tower_votes_peek_index_const( tower, cnt - 1 ) ) >= slot ) ) {
84 99 : break;
85 99 : }
86 6 : cnt--;
87 6 : }
88 102 : return cnt;
89 102 : }
90 :
91 : int
92 : fd_tower_lockout_check( fd_tower_t const * tower,
93 : fd_ghost_t const * ghost,
94 : ulong switch_slot,
95 0 : fd_hash_t const * hash_id ) {
96 0 : #if FD_TOWER_USE_HANDHOLDING
97 0 : FD_TEST( !fd_tower_votes_empty( tower ) ); /* caller error */
98 0 : #endif
99 :
100 : /* Simulate a vote to pop off all the votes that have been expired at
101 : the top of the tower. */
102 :
103 0 : ulong cnt = simulate_vote( tower, switch_slot );
104 :
105 : /* By definition, all votes in the tower must be for the same fork, so
106 : check if the previous vote (ie. the last vote in the tower) is on
107 : the same fork as the fork we want to vote for. We do this using
108 : ghost by checking if the previous vote slot is an ancestor of the
109 : `slot`. If the previous vote slot is too old (ie. older than
110 : ghost->root), then we don't have ancestry information anymore and
111 : we just assume it is on the same fork.
112 :
113 : FIXME discuss if it is safe to assume that? */
114 :
115 0 : fd_tower_vote_t const * vote = fd_tower_votes_peek_index_const( tower, cnt - 1 );
116 0 : fd_ghost_ele_t const * root = fd_ghost_root_const( ghost );
117 :
118 0 : int lockout_check = vote->slot < root->slot ||
119 0 : fd_ghost_is_ancestor( ghost, fd_ghost_hash( ghost, vote->slot ), hash_id );
120 0 : FD_LOG_NOTICE(( "[fd_tower_lockout_check] ok? %d. top: (slot: %lu, conf: %lu). switch: %lu.", lockout_check, vote->slot, vote->conf, switch_slot ));
121 0 : return lockout_check;
122 0 : }
123 :
124 : int
125 : fd_tower_switch_check( fd_tower_t const * tower,
126 : fd_epoch_t const * epoch,
127 : fd_ghost_t const * ghost,
128 : ulong switch_slot,
129 0 : fd_hash_t const * switch_hash_id ) {
130 0 : #if FD_TOWER_USE_HANDHOLDING
131 0 : FD_TEST( !fd_tower_votes_empty( tower ) ); /* caller error */
132 0 : #endif
133 :
134 0 : fd_tower_vote_t const * vote = fd_tower_votes_peek_tail_const( tower );
135 0 : fd_ghost_ele_t const * root = fd_ghost_root_const( ghost );
136 :
137 0 : if( FD_UNLIKELY( vote->slot < root->slot ) ) {
138 :
139 : /* It is possible our last vote slot precedes our ghost root. This
140 : can happen, for example, when we restart from a snapshot and set
141 : the ghost root to the snapshot slot (we won't have an ancestry
142 : before the snapshot slot.)
143 :
144 : If this is the case, we assume it's ok to switch. */
145 :
146 0 : return 1;
147 0 : }
148 :
149 : /* fd_tower_switch_check is only called if latest_vote->slot and
150 : fork->slot are on different forks (determined by is_descendant), so
151 : they must not fall on the same ancestry path back to the gca.
152 :
153 : INVALID:
154 :
155 : 0
156 : \
157 : 1 <- a
158 : \
159 : 2 <- b
160 :
161 : VALID:
162 :
163 : 0
164 : / \
165 : 1 2
166 : ^ ^
167 : a b
168 :
169 : */
170 :
171 0 : #if FD_TOWER_USE_HANDHOLDING
172 0 : FD_TEST( !fd_ghost_is_ancestor( ghost, fd_ghost_hash( ghost, vote->slot ), switch_hash_id ) );
173 0 : #endif
174 0 : fd_hash_t const * vote_block_id = fd_ghost_hash( ghost, vote->slot );
175 0 : fd_ghost_hash_map_t const * maph = fd_ghost_hash_map_const( ghost );
176 0 : fd_ghost_ele_t const * pool = fd_ghost_pool_const( ghost );
177 0 : fd_ghost_ele_t const * gca = fd_ghost_gca( ghost, vote_block_id, switch_hash_id );
178 0 : ulong gca_idx = fd_ghost_hash_map_idx_query_const( maph, &gca->key, ULONG_MAX, pool );
179 :
180 : /* gca_child is our latest_vote slot's ancestor that is also a direct
181 : child of GCA. So we do not count it towards the stake of the
182 : different forks. */
183 :
184 0 : fd_ghost_ele_t const * gca_child = fd_ghost_query_const( ghost, vote_block_id );
185 0 : while( FD_LIKELY( gca_child->parent != gca_idx ) ) {
186 0 : gca_child = fd_ghost_pool_ele_const( pool, gca_child->parent );
187 0 : }
188 :
189 0 : ulong switch_stake = 0;
190 0 : fd_ghost_ele_t const * child = fd_ghost_child_const( ghost, gca );
191 0 : while( FD_LIKELY( child ) ) {
192 0 : if( FD_LIKELY( child != gca_child ) ) {
193 0 : switch_stake += child->weight;
194 0 : }
195 0 : child = fd_ghost_pool_ele_const( pool, child->sibling );
196 0 : }
197 :
198 0 : double switch_pct = (double)switch_stake / (double)epoch->total_stake;
199 0 : FD_LOG_DEBUG(( "[%s] ok? %d. top: %lu. switch: %lu. switch stake: %.0lf%%.", __func__, switch_pct > SWITCH_PCT, fd_tower_votes_peek_tail_const( tower )->slot, switch_slot, switch_pct * 100.0 ));
200 0 : return switch_pct > SWITCH_PCT;
201 0 : }
202 :
203 : int
204 : fd_tower_threshold_check( fd_tower_t const * tower,
205 : fd_epoch_t const * epoch,
206 : fd_funk_t * funk,
207 : fd_funk_txn_t const * txn,
208 : ulong slot,
209 0 : fd_tower_t * scratch ) {
210 :
211 : /* First, simulate a vote, popping off everything that would be
212 : expired by voting for the current slot. */
213 :
214 0 : ulong cnt = simulate_vote( tower, slot );
215 :
216 : /* Return early if our tower is not at least THRESHOLD_DEPTH deep
217 : after simulating. */
218 :
219 0 : if( FD_UNLIKELY( cnt < THRESHOLD_DEPTH ) ) return 1;
220 :
221 : /* Get the vote slot from THRESHOLD_DEPTH back. Note THRESHOLD_DEPTH
222 : is the 8th index back _including_ the simulated vote at index 0,
223 : which is not accounted for by `cnt`, so subtracting THRESHOLD_DEPTH
224 : will conveniently index the threshold vote. */
225 :
226 0 : ulong threshold_slot = fd_tower_votes_peek_index_const( tower, cnt - THRESHOLD_DEPTH )->slot;
227 :
228 : /* Track the amount of stake that has vote slot >= threshold_slot. */
229 :
230 0 : ulong threshold_stake = 0;
231 :
232 : /* Iterate all the vote accounts. */
233 :
234 0 : fd_voter_t const * epoch_voters = fd_epoch_voters_const( epoch );
235 0 : for (ulong i = 0; i < fd_epoch_voters_slot_cnt( epoch_voters ); i++ ) {
236 0 : if( FD_LIKELY( fd_epoch_voters_key_inval( epoch_voters[i].key ) ) ) continue /* most slots are empty */;
237 :
238 0 : fd_voter_t const * voter = &epoch_voters[i];
239 :
240 : /* Convert the landed_votes into tower's vote_slots interface. */
241 :
242 0 : fd_tower_votes_remove_all( scratch );
243 0 : int err = fd_tower_from_vote_acc( scratch, funk, txn, &voter->rec );
244 0 : if( FD_UNLIKELY( err ) ) {
245 0 : FD_LOG_WARNING(( "[%s] failed to read vote account %s", __func__, FD_BASE58_ENC_32_ALLOCA(&voter->key) ));
246 0 : continue;
247 0 : }
248 :
249 : /* If this voter has not voted, continue. */
250 :
251 0 : if( FD_UNLIKELY( fd_tower_votes_empty( scratch ) ) ) continue;
252 :
253 0 : ulong cnt = simulate_vote( scratch, slot );
254 :
255 : /* Continue if their tower is empty after simulating. */
256 :
257 0 : if( FD_UNLIKELY( !cnt ) ) continue;
258 :
259 : /* Get their latest vote. */
260 :
261 0 : fd_tower_vote_t const * vote = fd_tower_votes_peek_index( scratch, cnt - 1 );
262 :
263 : /* Count their stake towards the threshold check if their latest
264 : vote slot >= our threshold slot.
265 :
266 : Because we are iterating vote accounts on the same fork that we
267 : we want to vote for, we know these slots must all occur along
268 : the same fork ancestry.
269 :
270 : Therefore, if their latest vote slot >= our threshold slot, we
271 : know that vote must be for the threshold slot itself or one of
272 : threshold slot's descendants. */
273 :
274 0 : if( FD_LIKELY( vote->slot >= threshold_slot ) ) {
275 0 : threshold_stake += voter->stake;
276 0 : }
277 0 : }
278 :
279 0 : double threshold_pct = (double)threshold_stake / (double)epoch->total_stake;
280 0 : FD_LOG_NOTICE(( "[%s] ok? %d. top: %lu. threshold: %lu. stake: %.0lf%%.", __func__, threshold_pct > THRESHOLD_PCT, fd_tower_votes_peek_tail_const( tower )->slot, threshold_slot, threshold_pct * 100.0 ));
281 0 : return threshold_pct > THRESHOLD_PCT;
282 0 : }
283 :
284 : ulong
285 : fd_tower_reset_slot( fd_tower_t const * tower,
286 0 : fd_ghost_t const * ghost ) {
287 :
288 0 : fd_tower_vote_t const * vote = fd_tower_votes_peek_tail_const( tower );
289 0 : fd_ghost_ele_t const * root = fd_ghost_root_const( ghost );
290 0 : fd_ghost_ele_t const * head = fd_ghost_head( ghost, root );
291 0 : fd_hash_t const * vote_block_id = fd_ghost_hash( ghost, vote->slot );
292 :
293 : /* Reset to the ghost head if any of the following is true:
294 : 1. haven't voted
295 : 2. last vote < ghost root
296 : 3. ghost root is not an ancestory of last vote */
297 :
298 0 : if( FD_UNLIKELY( !vote || vote->slot < root->slot ||
299 0 : !fd_ghost_is_ancestor( ghost, &root->key, vote_block_id ) ) ) {
300 0 : return head->slot;
301 0 : }
302 :
303 : /* Find the ghost node keyed by our last vote slot. It is invariant
304 : that this node must always be found after doing the above check.
305 : Otherwise ghost and tower contain implementation bugs and/or are
306 : corrupt. */
307 :
308 0 : fd_ghost_ele_t const * vote_node = fd_ghost_query_const( ghost, vote_block_id );
309 0 : #if FD_TOWER_USE_HANDHOLDING
310 0 : if( FD_UNLIKELY( !vote_node ) ) {
311 0 : fd_ghost_print( ghost, 0, root );
312 0 : FD_LOG_ERR(( "[%s] invariant violation: unable to find last tower vote slot %lu in ghost.", __func__, vote->slot ));
313 0 : }
314 0 : #endif
315 :
316 : /* Starting from the node keyed by the last vote slot, greedy traverse
317 : for the head. */
318 :
319 0 : return fd_ghost_head( ghost, vote_node )->slot;
320 0 : }
321 :
322 : ulong
323 : fd_tower_vote_slot( fd_tower_t * tower,
324 : fd_epoch_t const * epoch,
325 : fd_funk_t * funk,
326 : fd_funk_txn_t const * txn,
327 : fd_ghost_t const * ghost,
328 0 : fd_tower_t * scratch ) {
329 :
330 0 : fd_tower_vote_t const * vote = fd_tower_votes_peek_tail_const( tower );
331 0 : fd_ghost_ele_t const * root = fd_ghost_root_const( ghost );
332 0 : fd_ghost_ele_t const * head = fd_ghost_head( ghost, root );
333 :
334 : /* Vote for the ghost head if any of the following is true:
335 :
336 : 1. haven't voted
337 : 2. last vote < ghost root
338 : 3. ghost root is not an ancestory of last vote
339 :
340 : FIXME need to ensure lockout safety for case 2 and 3 */
341 :
342 0 : if( FD_UNLIKELY( !vote || vote->slot < root->slot ) ) {
343 0 : return head->slot;
344 0 : }
345 0 : fd_hash_t const * vote_block_id = fd_ghost_hash( ghost, vote->slot );
346 0 : if( FD_UNLIKELY( !fd_ghost_is_ancestor( ghost, &root->key, vote_block_id ) ) ) {
347 0 : return head->slot;
348 0 : }
349 :
350 : /* Optimize for when there is just one fork or that we already
351 : previously voted for the best fork. */
352 :
353 0 : if( FD_LIKELY( fd_ghost_is_ancestor( ghost, vote_block_id, &head->key ) ) ) {
354 :
355 : /* The ghost head is on the same fork as our last vote slot, so we
356 : can vote fork it as long as we pass the threshold check. */
357 :
358 0 : if( FD_LIKELY( fd_tower_threshold_check( tower, epoch, funk, txn, head->slot, scratch ) ) ) {
359 0 : FD_LOG_DEBUG(( "[%s] success (threshold). best: %lu. vote: (slot: %lu conf: %lu)", __func__, head->slot, vote->slot, vote->conf ));
360 0 : return head->slot;
361 0 : }
362 0 : FD_LOG_DEBUG(( "[%s] failure (threshold). best: %lu. vote: (slot: %lu conf: %lu)", __func__, head->slot, vote->slot, vote->conf ));
363 0 : return FD_SLOT_NULL; /* can't vote. need to wait for threshold check. */
364 0 : }
365 :
366 : /* The ghost head is on a different fork from our last vote slot, so
367 : try to switch if we pass lockout and switch threshold. */
368 :
369 0 : if( FD_UNLIKELY( fd_tower_lockout_check( tower, ghost, head->slot, &head->key ) &&
370 0 : fd_tower_switch_check( tower, epoch, ghost, head->slot, &head->key ) ) ) {
371 0 : FD_LOG_DEBUG(( "[%s] success (lockout switch). best: %lu. vote: (slot: %lu conf: %lu)", __func__, head->slot, vote->slot, vote->conf ));
372 0 : return head->slot;
373 0 : }
374 0 : FD_LOG_DEBUG(( "[%s] failure (lockout switch). best: %lu. vote: (slot: %lu conf: %lu)", __func__, head->slot, vote->slot, vote->conf ));
375 0 : return FD_SLOT_NULL;
376 0 : }
377 :
378 : ulong
379 99 : fd_tower_vote( fd_tower_t * tower, ulong slot ) {
380 99 : FD_LOG_DEBUG(( "[%s] voting for slot %lu", __func__, slot ));
381 :
382 99 : #if FD_TOWER_USE_HANDHOLDING
383 99 : fd_tower_vote_t const * vote = fd_tower_votes_peek_tail_const( tower );
384 99 : if( FD_UNLIKELY( vote && slot < vote->slot ) ) FD_LOG_ERR(( "[%s] slot %lu < vote->slot %lu", __func__, slot, vote->slot )); /* caller error*/
385 99 : #endif
386 :
387 : /* Use simulate_vote to determine how many expired votes to pop. */
388 :
389 99 : ulong cnt = simulate_vote( tower, slot );
390 :
391 : /* Pop everything that got expired. */
392 :
393 102 : while( fd_tower_votes_cnt( tower ) > cnt ) {
394 3 : fd_tower_votes_pop_tail( tower );
395 3 : }
396 :
397 : /* If the tower is still full after expiring, then pop and return the
398 : bottom vote slot as the new root because this vote has incremented
399 : it to max lockout. Otherwise this is a no-op and there is no new
400 : root (FD_SLOT_NULL). */
401 :
402 99 : ulong root = FD_SLOT_NULL;
403 99 : if( FD_LIKELY( fd_tower_votes_full( tower ) ) ) { /* optimize for full tower */
404 3 : root = fd_tower_votes_pop_head( tower ).slot;
405 3 : }
406 :
407 : /* Increment confirmations (double lockouts) for consecutive
408 : confirmations in prior votes. */
409 :
410 99 : ulong prev_conf = 0;
411 99 : for( fd_tower_votes_iter_t iter = fd_tower_votes_iter_init_rev( tower );
412 1584 : !fd_tower_votes_iter_done_rev( tower, iter );
413 1488 : iter = fd_tower_votes_iter_prev( tower, iter ) ) {
414 1488 : fd_tower_vote_t * vote = fd_tower_votes_iter_ele( tower, iter );
415 1488 : if( FD_UNLIKELY( vote->conf != ++prev_conf ) ) {
416 3 : break;
417 3 : }
418 1485 : vote->conf++;
419 1485 : }
420 :
421 : /* Add the new vote to the tower. */
422 :
423 99 : fd_tower_votes_push_tail( tower, (fd_tower_vote_t){ .slot = slot, .conf = 1 } );
424 :
425 : /* Return the new root (FD_SLOT_NULL if there is none). */
426 :
427 99 : return root;
428 99 : }
429 :
430 : ulong
431 3 : fd_tower_simulate_vote( fd_tower_t const * tower, ulong slot ) {
432 3 : #if FD_TOWER_USE_HANDHOLDING
433 3 : FD_TEST( !fd_tower_votes_empty( tower ) ); /* caller error */
434 3 : #endif
435 :
436 3 : return simulate_vote( tower, slot );
437 3 : }
438 :
439 : int
440 : fd_tower_from_vote_acc( fd_tower_t * tower,
441 : fd_funk_t * funk,
442 : fd_funk_txn_t const * txn,
443 0 : fd_funk_rec_key_t const * vote_acc ) {
444 0 : # if FD_TOWER_USE_HANDHOLDING
445 0 : FD_TEST( fd_tower_votes_empty( tower ) );
446 0 : # endif
447 :
448 0 : for(;;) {
449 :
450 : /* Speculatively query the record and parse the voter state. If the
451 : record is missing or the voter state fails to parse, then return
452 : early (tower will be empty). */
453 :
454 0 : fd_funk_rec_query_t query;
455 0 : fd_funk_rec_t const * rec = fd_funk_rec_query_try_global( funk, txn, vote_acc, NULL, &query );
456 0 : if( FD_UNLIKELY( !rec ) ) return -1; /* record not found */
457 0 : fd_voter_state_t const * state = fd_voter_state( funk, rec );
458 0 : if( FD_UNLIKELY( !state ) ) return -1; /* unable to parse voter state */
459 :
460 : /* Speculatively query the cnt. */
461 :
462 0 : ulong cnt = fd_voter_state_cnt( state ); /* TODO remove once Funk reads are safe */
463 0 : if( FD_UNLIKELY( fd_funk_rec_query_test( &query ) != FD_FUNK_SUCCESS ) ) continue;
464 0 : if( FD_UNLIKELY( cnt > 31UL ) ) FD_LOG_ERR(( "[%s] funk vote account corruption. cnt %lu > 31", __func__, cnt ));
465 :
466 : /* Speculatively read the votes out of the state and push them onto
467 : the tower. If there is a conflicting operation during this read,
468 : rollback the tower. */
469 :
470 0 : fd_tower_vote_t vote = { 0 };
471 0 : ulong sz = sizeof(fd_voter_vote_old_t);
472 0 : for( ulong i = 0; i < cnt; i++ ) {
473 0 : if( FD_UNLIKELY( state->discriminant == fd_vote_state_versioned_enum_v0_23_5 ) ) {
474 0 : memcpy( (uchar *)&vote, (uchar *)(state->v0_23_5.votes + i), sz );
475 0 : } else if ( FD_UNLIKELY( state->discriminant == fd_vote_state_versioned_enum_v1_14_11 ) ) {
476 0 : memcpy( (uchar *)&vote, (uchar *)(state->v1_14_11.votes + i), sz );
477 0 : } else if ( FD_UNLIKELY( state->discriminant == fd_vote_state_versioned_enum_current ) ) {
478 0 : memcpy( (uchar *)&vote, (uchar *)(state->votes + i) + sizeof(uchar) /* latency */, sz );
479 0 : } else {
480 0 : FD_LOG_ERR(( "[%s] unknown state->discriminant %u", __func__, state->discriminant ));
481 0 : }
482 0 : fd_tower_votes_push_tail( tower, vote );
483 0 : }
484 :
485 0 : if( FD_LIKELY( fd_funk_rec_query_test( &query ) == FD_FUNK_SUCCESS ) ) return 0;
486 0 : else fd_tower_votes_remove_all( tower ); /* reset the tower and try again */
487 0 : }
488 0 : }
489 :
490 : void
491 : fd_tower_to_vote_txn( fd_tower_t const * tower,
492 : ulong root,
493 : fd_lockout_offset_t * lockouts_scratch,
494 : fd_hash_t const * bank_hash,
495 : fd_hash_t const * recent_blockhash,
496 : fd_pubkey_t const * validator_identity,
497 : fd_pubkey_t const * vote_authority,
498 : fd_pubkey_t const * vote_acc,
499 0 : fd_txn_p_t * vote_txn ) {
500 :
501 0 : fd_compact_vote_state_update_t tower_sync;
502 0 : tower_sync.root = root;
503 0 : tower_sync.lockouts_len = (ushort)fd_tower_votes_cnt( tower );
504 0 : tower_sync.lockouts = lockouts_scratch;
505 0 : tower_sync.timestamp = fd_log_wallclock();
506 0 : tower_sync.has_timestamp = 1;
507 :
508 0 : ulong prev = tower_sync.root;
509 0 : ulong i = 0UL;
510 0 : for( fd_tower_votes_iter_t iter = fd_tower_votes_iter_init( tower );
511 0 : !fd_tower_votes_iter_done( tower, iter );
512 0 : iter = fd_tower_votes_iter_next( tower, iter ) ) {
513 0 : fd_tower_vote_t const * vote = fd_tower_votes_iter_ele_const( tower, iter );
514 0 : tower_sync.lockouts[i].offset = vote->slot - prev;
515 0 : tower_sync.lockouts[i].confirmation_count = (uchar)vote->conf;
516 0 : prev = vote->slot;
517 0 : i++;
518 0 : }
519 0 : memcpy( tower_sync.hash.uc, bank_hash, sizeof(fd_hash_t) );
520 :
521 0 : uchar * txn_out = vote_txn->payload;
522 0 : uchar * txn_meta_out = vote_txn->_;
523 :
524 0 : int same_addr = !memcmp( validator_identity, vote_authority, sizeof(fd_pubkey_t) );
525 0 : if( FD_LIKELY( same_addr ) ) {
526 :
527 : /* 0: validator identity
528 : 1: vote account address
529 : 2: vote program */
530 :
531 0 : fd_txn_accounts_t accts;
532 0 : accts.signature_cnt = 1;
533 0 : accts.readonly_signed_cnt = 0;
534 0 : accts.readonly_unsigned_cnt = 1;
535 0 : accts.acct_cnt = 3;
536 0 : accts.signers_w = validator_identity;
537 0 : accts.signers_r = NULL;
538 0 : accts.non_signers_w = vote_acc;
539 0 : accts.non_signers_r = &fd_solana_vote_program_id;
540 0 : FD_TEST( fd_txn_base_generate( txn_meta_out, txn_out, accts.signature_cnt, &accts, recent_blockhash->uc ) );
541 0 : } else {
542 :
543 : /* 0: validator identity
544 : 1: vote authority
545 : 2: vote account address
546 : 3: vote program */
547 :
548 0 : fd_txn_accounts_t accts;
549 0 : accts.signature_cnt = 2;
550 0 : accts.readonly_signed_cnt = 1;
551 0 : accts.readonly_unsigned_cnt = 1;
552 0 : accts.acct_cnt = 4;
553 0 : accts.signers_w = validator_identity;
554 0 : accts.signers_r = vote_authority;
555 0 : accts.non_signers_w = vote_acc;
556 0 : accts.non_signers_r = &fd_solana_vote_program_id;
557 0 : FD_TEST( fd_txn_base_generate( txn_meta_out, txn_out, accts.signature_cnt, &accts, recent_blockhash->uc ) );
558 0 : }
559 :
560 : /* Add the vote instruction to the transaction. */
561 :
562 0 : fd_vote_instruction_t vote_ix;
563 0 : uchar vote_ix_buf[FD_TXN_MTU];
564 0 : vote_ix.discriminant = fd_vote_instruction_enum_compact_update_vote_state;
565 0 : vote_ix.inner.compact_update_vote_state = tower_sync;
566 0 : fd_bincode_encode_ctx_t encode = { .data = vote_ix_buf, .dataend = ( vote_ix_buf + FD_TXN_MTU ) };
567 0 : fd_vote_instruction_encode( &vote_ix, &encode );
568 0 : uchar program_id;
569 0 : uchar ix_accs[2];
570 0 : if( FD_LIKELY( same_addr ) ) {
571 0 : ix_accs[0] = 1; /* vote account address */
572 0 : ix_accs[1] = 0; /* vote authority */
573 0 : program_id = 2; /* vote program */
574 0 : } else {
575 0 : ix_accs[0] = 2; /* vote account address */
576 0 : ix_accs[1] = 1; /* vote authority */
577 0 : program_id = 3; /* vote program */
578 0 : }
579 0 : ushort vote_ix_sz = (ushort)fd_vote_instruction_size( &vote_ix );
580 0 : vote_txn->payload_sz = fd_txn_add_instr( txn_meta_out, txn_out, program_id, ix_accs, 2, vote_ix_buf, vote_ix_sz );
581 0 : }
582 :
583 : int
584 0 : fd_tower_verify( fd_tower_t const * tower ) {
585 0 : fd_tower_vote_t const * prev = NULL;
586 0 : for( fd_tower_votes_iter_t iter = fd_tower_votes_iter_init( tower );
587 0 : !fd_tower_votes_iter_done( tower, iter );
588 0 : iter = fd_tower_votes_iter_next( tower, iter ) ) {
589 0 : fd_tower_vote_t const * vote = fd_tower_votes_iter_ele_const( tower, iter );
590 0 : if( FD_LIKELY( prev && !( vote->slot < prev->slot && vote->conf < prev->conf ) ) ) {
591 0 : FD_LOG_WARNING(( "[%s] invariant violation: vote %lu %lu. prev %lu %lu", __func__, vote->slot, vote->conf, prev->slot, prev->conf ));
592 0 : return -1;
593 0 : }
594 0 : prev = vote;
595 0 : }
596 0 : return 0;
597 0 : }
598 :
599 : #include <stdio.h>
600 :
601 : void
602 0 : fd_tower_print( fd_tower_t const * tower, ulong root ) {
603 0 : FD_LOG_NOTICE( ( "\n\n[Tower]" ) );
604 0 : ulong max_slot = 0;
605 :
606 : /* Determine spacing. */
607 :
608 0 : for( fd_tower_votes_iter_t iter = fd_tower_votes_iter_init_rev( tower );
609 0 : !fd_tower_votes_iter_done_rev( tower, iter );
610 0 : iter = fd_tower_votes_iter_prev( tower, iter ) ) {
611 :
612 0 : max_slot = fd_ulong_max( max_slot, fd_tower_votes_iter_ele_const( tower, iter )->slot );
613 0 : }
614 :
615 : /* Calculate the number of digits in the maximum slot value. */
616 :
617 0 : int digit_cnt = 0;
618 0 : unsigned long rem = max_slot;
619 0 : do {
620 0 : rem /= 10;
621 0 : ++digit_cnt;
622 0 : } while( rem > 0 );
623 :
624 : /* Print the table header */
625 :
626 0 : printf( "slot%*s | %s\n", digit_cnt - (int)strlen("slot"), "", "confirmation count" );
627 :
628 : /* Print the divider line */
629 :
630 0 : for( int i = 0; i < digit_cnt; i++ ) {
631 0 : printf( "-" );
632 0 : }
633 0 : printf( " | " );
634 0 : for( ulong i = 0; i < strlen( "confirmation count" ); i++ ) {
635 0 : printf( "-" );
636 0 : }
637 0 : printf( "\n" );
638 :
639 : /* Print each record in the table */
640 :
641 0 : for( fd_tower_votes_iter_t iter = fd_tower_votes_iter_init_rev( tower );
642 0 : !fd_tower_votes_iter_done_rev( tower, iter );
643 0 : iter = fd_tower_votes_iter_prev( tower, iter ) ) {
644 :
645 0 : fd_tower_vote_t const * vote = fd_tower_votes_iter_ele_const( tower, iter );
646 0 : printf( "%*lu | %lu\n", digit_cnt, vote->slot, vote->conf );
647 0 : max_slot = fd_ulong_max( max_slot, fd_tower_votes_iter_ele_const( tower, iter )->slot );
648 0 : }
649 0 : printf( "%*lu | root\n", digit_cnt, root );
650 : printf( "\n" );
651 0 : }
|