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