LCOV - code coverage report
Current view: top level - choreo/tower - fd_tower.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 567 0.0 %
Date: 2024-11-13 11:58:15 Functions: 0 24 0.0 %

          Line data    Source code
       1             : #include "fd_tower.h"
       2             : #include "../../flamenco/runtime/program/fd_vote_program.h"
       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             : /* Private implementation functions */
      11             : 
      12             : static inline ulong
      13           0 : lockout_expiration_slot( fd_tower_vote_t const * vote ) {
      14           0 :   ulong lockout = 1UL << vote->conf;
      15           0 :   return vote->slot + lockout;
      16           0 : }
      17             : 
      18             : void
      19           0 : print( fd_tower_vote_t * tower_votes, ulong root ) {
      20           0 :   ulong max_slot = 0;
      21             : 
      22             :   /* Determine spacing. */
      23             : 
      24           0 :   for( fd_tower_votes_iter_t iter = fd_tower_votes_iter_init_rev( tower_votes );
      25           0 :        !fd_tower_votes_iter_done_rev( tower_votes, iter );
      26           0 :        iter = fd_tower_votes_iter_prev( tower_votes, iter ) ) {
      27             : 
      28           0 :     max_slot = fd_ulong_max( max_slot, fd_tower_votes_iter_ele_const( tower_votes, iter )->slot );
      29           0 :   }
      30             : 
      31             :   /* Calculate the number of digits in the maximum slot value. */
      32             : 
      33           0 :   int           digit_cnt = 0;
      34           0 :   unsigned long rem       = max_slot;
      35           0 :   do {
      36           0 :     rem /= 10;
      37           0 :     ++digit_cnt;
      38           0 :   } while( rem > 0 );
      39             : 
      40             :   /* Print the table header */
      41             : 
      42           0 :   printf( "%*s | %s\n", digit_cnt, "slot", "confirmation count" );
      43             : 
      44             :   /* Print the divider line */
      45             : 
      46           0 :   for( int i = 0; i < digit_cnt; i++ ) {
      47           0 :     printf( "-" );
      48           0 :   }
      49           0 :   printf( " | " );
      50           0 :   for( ulong i = 0; i < strlen( "confirmation_count" ); i++ ) {
      51           0 :     printf( "-" );
      52           0 :   }
      53           0 :   printf( "\n" );
      54             : 
      55             :   /* Print each record in the table */
      56             : 
      57           0 :   for( fd_tower_votes_iter_t iter = fd_tower_votes_iter_init_rev( tower_votes );
      58           0 :        !fd_tower_votes_iter_done_rev( tower_votes, iter );
      59           0 :        iter = fd_tower_votes_iter_prev( tower_votes, iter ) ) {
      60             : 
      61           0 :     fd_tower_vote_t const * vote = fd_tower_votes_iter_ele_const( tower_votes, iter );
      62           0 :     printf( "%*lu | %lu\n", digit_cnt, vote->slot, vote->conf );
      63           0 :     max_slot = fd_ulong_max( max_slot, fd_tower_votes_iter_ele_const( tower_votes, iter )->slot );
      64           0 :   }
      65           0 :   printf( "%*lu | root\n", digit_cnt, root );
      66           0 :   printf( "\n" );
      67           0 : }
      68             : 
      69             : static inline ulong
      70           0 : simulate_vote( fd_tower_vote_t const * votes, ulong slot ) {
      71           0 :   ulong cnt = fd_tower_votes_cnt( votes );
      72           0 :   while( cnt ) {
      73             : 
      74             :     /* Return early if we can't pop the top tower vote, even if votes
      75             :        below it are expired. */
      76             : 
      77           0 :     if( FD_LIKELY( lockout_expiration_slot( fd_tower_votes_peek_index_const( votes, cnt - 1 ) ) >=
      78           0 :                    slot ) ) {
      79           0 :       break;
      80           0 :     }
      81           0 :     cnt--;
      82           0 :   }
      83           0 :   return cnt;
      84           0 : }
      85             : 
      86             : static void
      87             : tower_votes_from_landed_votes( fd_tower_vote_t *        tower_votes,
      88           0 :                                fd_landed_vote_t const * landed_votes ) {
      89           0 :   for( deq_fd_landed_vote_t_iter_t iter = deq_fd_landed_vote_t_iter_init( landed_votes );
      90           0 :        !deq_fd_landed_vote_t_iter_done( landed_votes, iter );
      91           0 :        iter = deq_fd_landed_vote_t_iter_next( landed_votes, iter ) ) {
      92           0 :     fd_landed_vote_t const * landed_vote = deq_fd_landed_vote_t_iter_ele_const( landed_votes,
      93           0 :                                                                                 iter );
      94           0 :     fd_tower_votes_push_tail( tower_votes,
      95           0 :                               ( fd_tower_vote_t ){
      96           0 :                                   .slot = landed_vote->lockout.slot,
      97           0 :                                   .conf = landed_vote->lockout.confirmation_count } );
      98           0 :   }
      99           0 : }
     100             : 
     101             : /* Public API */
     102             : 
     103             : /* clang-format off */
     104             : void *
     105           0 : fd_tower_new( void * shmem ) {
     106           0 :   if( FD_UNLIKELY( !shmem ) ) {
     107           0 :     FD_LOG_WARNING(( "NULL mem" ));
     108           0 :     return NULL;
     109           0 :   }
     110             : 
     111           0 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shmem, fd_tower_align() ) ) ) {
     112           0 :     FD_LOG_WARNING(( "misaligned mem" ));
     113           0 :     return NULL;
     114           0 :   }
     115             : 
     116           0 :   ulong footprint = fd_tower_footprint();
     117           0 :   if( FD_UNLIKELY( !footprint ) ) {
     118           0 :     FD_LOG_WARNING(( "bad mem" ));
     119           0 :     return NULL;
     120           0 :   }
     121             : 
     122           0 :   fd_memset( shmem, 0, footprint );
     123           0 :   ulong        laddr = (ulong)shmem;
     124           0 :   fd_tower_t * tower = (void *)laddr;
     125           0 :   laddr             += sizeof( fd_tower_t );
     126             : 
     127           0 :   laddr        = fd_ulong_align_up( laddr, fd_tower_votes_align() );
     128           0 :   tower->votes = fd_tower_votes_new( (void *)laddr );
     129           0 :   laddr       += fd_tower_votes_footprint();
     130             : 
     131           0 :   laddr            = fd_ulong_align_up( laddr, fd_tower_vote_accs_align() );
     132           0 :   tower->vote_accs = fd_tower_vote_accs_new( (void *)laddr );
     133           0 :   laddr           += fd_tower_vote_accs_footprint();
     134             : 
     135           0 :   return shmem;
     136           0 : }
     137             : /* clang-format on */
     138             : 
     139             : /* clang-format off */
     140             : fd_tower_t *
     141           0 : fd_tower_join( void * shtower ) {
     142             : 
     143           0 :   if( FD_UNLIKELY( !shtower ) ) {
     144           0 :     FD_LOG_WARNING(( "NULL tower" ));
     145           0 :     return NULL;
     146           0 :   }
     147             : 
     148           0 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shtower, fd_tower_align() ) ) ) {
     149           0 :     FD_LOG_WARNING(( "misaligned tower" ));
     150           0 :     return NULL;
     151           0 :   }
     152             : 
     153           0 :   ulong        laddr = (ulong)shtower; /* offset from a memory region */
     154           0 :   fd_tower_t * tower = (void *)shtower;
     155           0 :   laddr             += sizeof(fd_tower_t);
     156             : 
     157           0 :   laddr        = fd_ulong_align_up( laddr, fd_tower_votes_align() );
     158           0 :   tower->votes = fd_tower_votes_new( (void *)laddr );
     159           0 :   laddr       += fd_tower_votes_footprint();
     160             : 
     161           0 :   laddr            = fd_ulong_align_up( laddr, fd_tower_vote_accs_align() );
     162           0 :   tower->vote_accs = fd_tower_vote_accs_new( (void *)laddr );
     163           0 :   laddr           += fd_tower_vote_accs_footprint();
     164             : 
     165           0 :   return (fd_tower_t *)shtower;
     166           0 : }
     167             : /* clang-format on */
     168             : 
     169             : void *
     170           0 : fd_tower_leave( fd_tower_t const * tower ) {
     171             : 
     172           0 :   if( FD_UNLIKELY( !tower ) ) {
     173           0 :     FD_LOG_WARNING(( "NULL tower" ));
     174           0 :     return NULL;
     175           0 :   }
     176             : 
     177           0 :   return (void *)tower;
     178           0 : }
     179             : 
     180             : void *
     181           0 : fd_tower_delete( void * tower ) {
     182             : 
     183           0 :   if( FD_UNLIKELY( !tower ) ) {
     184           0 :     FD_LOG_WARNING(( "NULL tower" ));
     185           0 :     return NULL;
     186           0 :   }
     187             : 
     188           0 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)tower, fd_tower_align() ) ) ) {
     189           0 :     FD_LOG_WARNING(( "misaligned tower" ));
     190           0 :     return NULL;
     191           0 :   }
     192             : 
     193           0 :   return tower;
     194           0 : }
     195             : 
     196             : void
     197             : fd_tower_init( fd_tower_t *                tower,
     198             :                fd_pubkey_t const *         vote_acc_addr,
     199             :                fd_acc_mgr_t *              acc_mgr,
     200             :                fd_exec_epoch_ctx_t const * epoch_ctx,
     201             :                fd_fork_t const *           fork,
     202           0 :                ulong *                     smr ) {
     203             : 
     204           0 :   if( FD_UNLIKELY( !tower ) ) {
     205           0 :     FD_LOG_WARNING(( "NULL tower" ));
     206           0 :     return;
     207           0 :   }
     208             : 
     209             :   /* Restore our tower using the vote account state. */
     210             : 
     211           0 :   FD_SCRATCH_SCOPE_BEGIN {
     212           0 :     fd_vote_state_versioned_t vote_state_versioned = { 0 };
     213             : 
     214           0 :     fd_vote_state_t * vote_state = fd_tower_vote_state_query( tower,
     215           0 :                                                               vote_acc_addr,
     216           0 :                                                               acc_mgr,
     217           0 :                                                               fork,
     218           0 :                                                               fd_scratch_virtual(),
     219           0 :                                                               &vote_state_versioned );
     220           0 :     if( FD_LIKELY( vote_state ) ) {
     221           0 :       fd_tower_from_vote_state( tower, vote_state );
     222           0 :       FD_LOG_NOTICE(( "[%s] loading vote state for vote acc: %s", __func__, FD_BASE58_ENC_32_ALLOCA( vote_acc_addr ) ));
     223           0 :     } else {
     224           0 :       FD_LOG_WARNING(( "[%s] didn't find existing vote state for vote acc: %s",
     225           0 :                        __func__,
     226           0 :                        FD_BASE58_ENC_32_ALLOCA( vote_acc_addr ) ));
     227           0 :     }
     228           0 :   }
     229           0 :   FD_SCRATCH_SCOPE_END;
     230             : 
     231             :   /* Init vote_accs and total_stake. */
     232             : 
     233           0 :   fd_tower_epoch_update( tower, epoch_ctx );
     234             : 
     235             :   /* Init the smr. */
     236             : 
     237           0 :   tower->smr = smr;
     238           0 : }
     239             : 
     240             : int
     241             : fd_tower_lockout_check( fd_tower_t const * tower,
     242             :                         fd_fork_t const *  fork,
     243           0 :                         fd_ghost_t const * ghost ) {
     244             : 
     245             :   /* Simulate a vote to pop off all the votes that have been expired at
     246             :      the top of the tower. */
     247             : 
     248           0 :   ulong cnt = simulate_vote( tower->votes, fork->slot );
     249             : 
     250             :   /* By definition, all votes in the tower must be for the same fork, so
     251             :      check if the top vote of the tower after simulating is on the same
     252             :      fork as the fork we want to vote for (ie. fork->slot is a
     253             :      descendant of top vote slot).  If the top vote slot is too old (ie.
     254             :      older than ghost->root), we just assume it is on the same fork. */
     255             : 
     256           0 :   fd_tower_vote_t const * top_vote = fd_tower_votes_peek_index_const( tower->votes, cnt - 1 );
     257             : 
     258           0 :   int lockout_check = top_vote->slot < ghost->root->slot ||
     259           0 :                       fd_ghost_is_descendant( ghost, fork->slot, top_vote->slot );
     260           0 :   FD_LOG_NOTICE(( "[fd_tower_lockout_check] ok? %d. top: (slot: %lu, conf: %lu). switch: %lu.",
     261           0 :                   lockout_check,
     262           0 :                   top_vote->slot,
     263           0 :                   top_vote->conf,
     264           0 :                   fork->slot ));
     265           0 :   return lockout_check;
     266           0 : }
     267             : 
     268             : int
     269             : fd_tower_switch_check( fd_tower_t const * tower,
     270             :                        fd_fork_t const *  fork,
     271           0 :                        fd_ghost_t const * ghost ) {
     272             : 
     273           0 :   fd_tower_vote_t const * latest_vote = fd_tower_votes_peek_tail_const( tower->votes );
     274             : 
     275           0 :   if( FD_UNLIKELY( latest_vote->slot < ghost->root->slot ) ) {
     276             : 
     277             :     /* It is possible our latest vote slot precedes our ghost root. This
     278             :        can happen, for example, when we restart from a snapshot and set
     279             :        the ghost root to the snapshot slot (we won't have an ancestry
     280             :        before the snapshot slot.)
     281             : 
     282             :        If this is the case, we assume it's ok to switch. */
     283             : 
     284           0 :     return 1;
     285           0 :   }
     286             : 
     287             :   /* Assumption: fd_tower_switch_check is only called if
     288             :      latest_vote->slot and fork->slot are on different forks (determined
     289             :      by is_descendant), so they must not fall on the same ancestry path
     290             :      back to the gca.
     291             : 
     292             :      INVALID:
     293             : 
     294             :        0
     295             :         \
     296             :          1    <- a
     297             :           \
     298             :            2  <- b
     299             : 
     300             :      VALID:
     301             : 
     302             :        0
     303             :       / \
     304             :      1   2
     305             :      ^   ^
     306             :      a   b
     307             : 
     308             :   */
     309             : 
     310           0 :   fd_ghost_node_t const * gca = fd_ghost_gca( ghost, latest_vote->slot, fork->slot );
     311             : 
     312             :   /* gca_child is our latest_vote slot's ancestor that is also a direct
     313             :      child of GCA.  So we do not count it towards the stake of the
     314             :      different forks. */
     315             : 
     316           0 :   fd_ghost_node_t const * gca_child = fd_ghost_query( ghost, latest_vote->slot );
     317           0 :   while( gca_child->parent != gca ) {
     318           0 :     gca_child = gca_child->parent;
     319           0 :   }
     320             : 
     321           0 :   ulong switch_stake = 0;
     322           0 :   fd_ghost_node_t const * child = gca->child;
     323           0 :   while ( FD_LIKELY( child ) ) {
     324           0 :     if ( FD_LIKELY ( child != gca_child ) ) {
     325           0 :       switch_stake += child->weight;
     326           0 :     }
     327           0 :     child = child->sibling;
     328           0 :   }
     329             : 
     330           0 :   double switch_pct = (double)switch_stake / (double)tower->total_stake;
     331           0 :   FD_LOG_NOTICE(( "[fd_tower_switch_check] ok? %d. top: %lu. switch: %lu. switch stake: %.0lf%%.",
     332           0 :                   switch_pct > SWITCH_PCT,
     333           0 :                   fd_tower_votes_peek_tail_const( tower->votes )->slot,
     334           0 :                   fork->slot,
     335           0 :                   switch_pct * 100.0 ));
     336           0 :   return switch_pct > SWITCH_PCT;
     337           0 : }
     338             : 
     339             : int
     340             : fd_tower_threshold_check( fd_tower_t const * tower,
     341             :                           fd_fork_t const *  fork,
     342           0 :                           fd_acc_mgr_t *     acc_mgr ) {
     343             : 
     344             :   /* First, simulate a vote, popping off everything that would be
     345             :      expired by voting for the current slot. */
     346             : 
     347           0 :   ulong cnt = simulate_vote( tower->votes, fork->slot );
     348             : 
     349             :   /* Return early if our tower is not at least THRESHOLD_DEPTH deep
     350             :      after simulating. */
     351             : 
     352           0 :   if( FD_UNLIKELY( cnt < THRESHOLD_DEPTH ) ) return 1;
     353             : 
     354             :   /* Get the vote slot from THRESHOLD_DEPTH back. Note THRESHOLD_DEPTH
     355             :      is the 8th index back _including_ the simulated vote at index 0,
     356             :      which is not accounted for by `cnt`, so subtracting THRESHOLD_DEPTH
     357             :      will conveniently index the threshold vote. */
     358             : 
     359           0 :   ulong threshold_slot = fd_tower_votes_peek_index( tower->votes, cnt - THRESHOLD_DEPTH )->slot;
     360             : 
     361             :   /* Track the amount of stake that has vote slot >= threshold_slot. */
     362             : 
     363           0 :   ulong threshold_stake = 0;
     364             : 
     365             :   /* Iterate all the vote accounts. */
     366             : 
     367           0 :   for( fd_tower_vote_accs_iter_t iter = fd_tower_vote_accs_iter_init( tower->vote_accs );
     368           0 :        !fd_tower_vote_accs_iter_done( tower->vote_accs, iter );
     369           0 :        iter = fd_tower_vote_accs_iter_next( tower->vote_accs, iter ) ) {
     370             : 
     371           0 :     fd_tower_vote_acc_t * vote_acc = fd_tower_vote_accs_iter_ele( tower->vote_accs, iter );
     372             : 
     373           0 :     FD_SCRATCH_SCOPE_BEGIN {
     374             : 
     375             :       /* FIXME */
     376           0 :       fd_vote_state_versioned_t vote_state_versioned = { 0 };
     377             : 
     378           0 :       fd_vote_state_t * vote_state = fd_tower_vote_state_query( tower,
     379           0 :                                                                 vote_acc->addr,
     380           0 :                                                                 acc_mgr,
     381           0 :                                                                 fork,
     382           0 :                                                                 fd_scratch_virtual(),
     383           0 :                                                                 &vote_state_versioned );
     384           0 :       if( FD_UNLIKELY( !vote_state ) ) {
     385           0 :         FD_LOG_WARNING(( "[%s] failed to load vote acc addr %s. skipping.",
     386           0 :                          __func__,
     387           0 :                          FD_BASE58_ENC_32_ALLOCA( vote_acc->addr ) ));
     388           0 :         continue;
     389           0 :       }
     390             : 
     391           0 :       fd_landed_vote_t * landed_votes = vote_state->votes;
     392             : 
     393             :       /* If the vote account has an empty tower, continue. */
     394             : 
     395           0 :       if( FD_UNLIKELY( deq_fd_landed_vote_t_empty( landed_votes ) ) ) continue;
     396             : 
     397             :       /* Convert the landed_votes into tower's vote_slots interface. */
     398             : 
     399           0 :       void * mem = fd_scratch_alloc( fd_tower_votes_align(), fd_tower_votes_footprint() );
     400           0 :       fd_tower_vote_t * their_tower_votes = fd_tower_votes_join( fd_tower_votes_new( mem ) );
     401           0 :       tower_votes_from_landed_votes( their_tower_votes, landed_votes );
     402             : 
     403           0 :       ulong cnt = simulate_vote( their_tower_votes, fork->slot );
     404             : 
     405             :       /* Continue if their tower is empty after simulating. */
     406             : 
     407           0 :       if( FD_UNLIKELY( !cnt ) ) continue;
     408             : 
     409             :       /* Get their latest vote slot.*/
     410             : 
     411           0 :       fd_tower_vote_t const * vote_slot = fd_tower_votes_peek_index( their_tower_votes, cnt - 1 );
     412             : 
     413             :       /* Count their stake towards the threshold check if their latest
     414             :          vote slot >= our threshold slot.
     415             : 
     416             :          Because we are iterating vote accounts on the same fork that we
     417             :          we want to vote for, we know these slots must all occur along
     418             :          the same fork ancestry.
     419             : 
     420             :          Therefore, if their latest vote slot >= our threshold slot, we
     421             :          know that vote must be for the threshold slot itself or one of
     422             :          threshold slot's descendants. */
     423             : 
     424           0 :       if( FD_LIKELY( vote_slot->slot >= threshold_slot ) ) {
     425           0 :         threshold_stake += vote_acc->stake;
     426           0 :       }
     427           0 :     }
     428           0 :     FD_SCRATCH_SCOPE_END;
     429           0 :   }
     430             : 
     431           0 :   double threshold_pct = (double)threshold_stake / (double)tower->total_stake;
     432           0 :   FD_LOG_NOTICE(( "[fd_tower_threshold_check] ok? %d. top: %lu. threshold: %lu. stake: %.0lf%%.",
     433           0 :                   threshold_pct > THRESHOLD_PCT,
     434           0 :                   fd_tower_votes_peek_tail_const( tower->votes )->slot,
     435           0 :                   threshold_slot,
     436           0 :                   threshold_pct * 100.0 ));
     437           0 :   return threshold_pct > THRESHOLD_PCT;
     438           0 : }
     439             : 
     440             : fd_fork_t const *
     441             : fd_tower_best_fork( FD_PARAM_UNUSED fd_tower_t const * tower,
     442             :                     fd_forks_t const *                 forks,
     443           0 :                     fd_ghost_t const *                 ghost ) {
     444           0 :   fd_ghost_node_t const * head = fd_ghost_head( ghost );
     445             : 
     446             :   /* Search for the fork head in the frontier. */
     447             : 
     448           0 :   fd_fork_t const * best = fd_forks_query_const( forks, head->slot );
     449             : 
     450           0 : #if FD_TOWER_USE_HANDHOLDING
     451           0 :   if( FD_UNLIKELY( !best ) ) {
     452             : 
     453             :     /* If the best fork is not in the frontier, then we must have pruned
     454             :        it or improperly re-used its fork and we're now in a bad state. */
     455             : 
     456             :     /* TODO eqvoc */
     457             : 
     458           0 :     FD_LOG_ERR(( "missing ghost head %lu in frontier", head->slot ));
     459           0 :   }
     460           0 : #endif
     461             : 
     462           0 :   return best;
     463           0 : }
     464             : 
     465             : fd_fork_t const *
     466             : fd_tower_reset_fork( fd_tower_t const * tower,
     467             :                      fd_forks_t const * forks,
     468           0 :                      fd_ghost_t const * ghost ) {
     469             : 
     470             :   /* If the tower is empty (we haven't voted or every vote was expired),
     471             :      we simply reset to the best fork. */
     472             : 
     473           0 :   if( FD_UNLIKELY( fd_tower_votes_empty( tower->votes ) ) ) {
     474           0 :     return fd_tower_best_fork( tower, forks, ghost );
     475           0 :   }
     476             : 
     477           0 :   fd_tower_vote_t const * latest_vote = fd_tower_votes_peek_tail_const( tower->votes );
     478             : 
     479             :   /* Consider the 2 cases when our latest vote slot does not descend
     480             :      from ghost root / SMR (see also top-level documentation in
     481             :      fd_tower.h):
     482             : 
     483             :      1. If we are stuck on a minority fork, we know that we cannot
     484             :         sensibly build a block based on our last vote because we know a
     485             :         supermajority of the cluster has rooted a different fork.  So we
     486             :         simply build off the best fork.
     487             : 
     488             :      2. If our latest vote slot is older than SMR, we know we don't have
     489             :         ancestry information about our latest vote slot anymore, so we
     490             :         similarly build off the best fork. */
     491             : 
     492           0 :   if( FD_UNLIKELY( !fd_ghost_is_descendant( ghost, latest_vote->slot, ghost->root->slot ) ) ) {
     493           0 :     return fd_tower_best_fork( tower, forks, ghost );
     494           0 :   }
     495             : 
     496           0 :   fd_fork_frontier_t const * frontier = forks->frontier;
     497           0 :   fd_fork_t const *          pool     = forks->pool;
     498             : 
     499           0 :   for( fd_fork_frontier_iter_t iter = fd_fork_frontier_iter_init( frontier, pool );
     500           0 :        !fd_fork_frontier_iter_done( iter, frontier, pool );
     501           0 :        iter = fd_fork_frontier_iter_next( iter, frontier, pool ) ) {
     502           0 :     fd_fork_t const * fork = fd_fork_frontier_iter_ele_const( iter, frontier, pool );
     503           0 :     ulong slot = fd_ulong_if( fork->lock, fork->slot_ctx.slot_bank.prev_slot, fork->slot );
     504           0 :     if( FD_LIKELY( fd_ghost_is_descendant( ghost, slot, latest_vote->slot ) ) ) return fork;
     505           0 :   }
     506             : 
     507             :   /* If we've reached here, we're in a bad state. Log some diagnostics. */
     508             : 
     509           0 :   for( fd_fork_frontier_iter_t iter = fd_fork_frontier_iter_init( frontier, pool );
     510           0 :        !fd_fork_frontier_iter_done( iter, frontier, pool );
     511           0 :        iter = fd_fork_frontier_iter_next( iter, frontier, pool ) ) {
     512           0 :     fd_fork_t const * fork = fd_fork_frontier_iter_ele_const( iter, frontier, pool );
     513           0 :     ulong slot = fd_ulong_if( fork->lock, fork->slot_ctx.slot_bank.prev_slot, fork->slot );
     514             : 
     515           0 :     FD_LOG_NOTICE(( "\n\nfork lock? %d\nfork slot %lu\nfork prev slot %lu\nlatest vote slot %lu\n"
     516           0 :                     "descendant slot %lu\ndescends? %d",
     517           0 :                     fork->lock,
     518           0 :                     fork->slot,
     519           0 :                     fork->slot_ctx.slot_bank.prev_slot,
     520           0 :                     latest_vote->slot,
     521           0 :                     slot,
     522           0 :                     fd_ghost_is_descendant( ghost, slot, latest_vote->slot ) ));
     523           0 :   }
     524             : 
     525           0 :   FD_LOG_ERR(( "invariant violation: could not find our latest vote slot in frontier even though there is a valid ancestry in ghost." ));
     526           0 : }
     527             : 
     528             : fd_fork_t const *
     529             : fd_tower_vote_fork( fd_tower_t *       tower,
     530             :                     fd_forks_t const * forks,
     531             :                     fd_acc_mgr_t *     acc_mgr,
     532           0 :                     fd_ghost_t const * ghost ) {
     533             : 
     534           0 :   fd_fork_t const * vote_fork = NULL;
     535             : 
     536           0 :   fd_fork_t const * best = fd_tower_best_fork( tower, forks, ghost );
     537             : 
     538             :   /* If the tower is empty (we haven't voted or every vote was expired),
     539             :      we simply vote for the best fork. */
     540             : 
     541           0 :   if( FD_UNLIKELY( fd_tower_votes_empty( tower->votes ) ) ) {
     542           0 :     return best;
     543           0 :   }
     544             : 
     545           0 :   fd_tower_vote_t const * latest_vote = fd_tower_votes_peek_tail_const( tower->votes );
     546             : 
     547             :   /* Consider the 2 cases when our latest vote slot does not descend
     548             :      from ghost root / SMR (see also top-level documentation in
     549             :      fd_tower.h):
     550             : 
     551             :      1. If we are stuck on a minority fork, we know the cluster has
     552             :         rooted a fork that isn't our current vote fork, and we don't
     553             :         have ancestry information to determine lockout or switch
     554             :         percentage, so we switch and vote for the current best.
     555             : 
     556             :      2. If our latest vote slot is older than SMR, we know we don't have
     557             :         ancestry information to determine whether we're locked out or
     558             :         can switch, so we similarly build off the best fork. */
     559             : 
     560           0 :   if( FD_UNLIKELY( !fd_ghost_is_descendant( ghost, latest_vote->slot, ghost->root->slot ) ) ) {
     561           0 :     return fd_tower_best_fork( tower, forks, ghost );
     562           0 :   }
     563             : 
     564             :   /* Optimize for when there is just one fork (most of the time), which means best fork. */
     565             : 
     566           0 :   if( FD_LIKELY( fd_ghost_is_descendant( ghost, best->slot, latest_vote->slot ) ) ) {
     567             : 
     568             :     /* The best fork is on the same fork and we can vote for
     569             :        best_fork->slot if we pass the threshold check. */
     570             : 
     571           0 :     if( FD_LIKELY( fd_tower_threshold_check( tower, best, acc_mgr ) ) ) {
     572           0 :       FD_LOG_NOTICE(( "[fd_tower_vote_fork_select] success (threshold). best: %lu. vote: "
     573           0 :                       "(slot: %lu conf: %lu)",
     574           0 :                       best->slot,
     575           0 :                       latest_vote->slot,
     576           0 :                       latest_vote->conf ));
     577           0 :       vote_fork = best;
     578           0 :     } else {
     579           0 :       FD_LOG_NOTICE(( "[fd_tower_vote_fork_select] failure (threshold). best: %lu. vote: "
     580           0 :                       "(slot: %lu conf: %lu)",
     581           0 :                       best->slot,
     582           0 :                       latest_vote->slot,
     583           0 :                       latest_vote->conf ));
     584           0 :     }
     585             : 
     586           0 :   } else {
     587             : 
     588             :     /* The best fork is on a different fork, so try to switch if we pass
     589             :        lockout and switch threshold. */
     590             : 
     591           0 :     if( FD_UNLIKELY( fd_tower_lockout_check( tower, best, ghost ) &&
     592           0 :                      fd_tower_switch_check( tower, best, ghost ) ) ) {
     593           0 :       FD_LOG_NOTICE(( "[fd_tower_vote_fork_select] success (lockout switch). best: %lu. vote: "
     594           0 :                       "(slot: %lu conf: %lu)",
     595           0 :                       best->slot,
     596           0 :                       latest_vote->slot,
     597           0 :                       latest_vote->conf ));
     598           0 :       vote_fork = best;
     599           0 :     } else {
     600           0 :       FD_LOG_NOTICE(( "[fd_tower_vote_fork_select] failure (lockout switch). best: %lu. vote: "
     601           0 :                       "(slot: %lu conf: %lu)",
     602           0 :                       best->slot,
     603           0 :                       latest_vote->slot,
     604           0 :                       latest_vote->conf ));
     605           0 :     }
     606           0 :   }
     607             : 
     608           0 :   return vote_fork; /* NULL if we cannot vote. */
     609             : 
     610             :   /* Only process vote slots higher than our SMR. */
     611             : 
     612             :   // if( FD_LIKELY( latest_vote->root > tower->smr ) ) {
     613             : 
     614             :   //   /* Find the previous root vote by node pubkey. */
     615             : 
     616             :   //   fd_root_vote_t * prev_root_vote =
     617             :   //       fd_root_vote_map_query( root_votes, latest_vote->node_pubkey, NULL );
     618             : 
     619             :   //   if( FD_UNLIKELY( !prev_root_vote ) ) {
     620             : 
     621             :   //     /* This node pubkey has not yet voted. */
     622             : 
     623             :   //     prev_root_vote = fd_root_vote_map_insert( root_votes, latest_vote->node_pubkey );
     624             :   //   } else {
     625             : 
     626             :   //     fd_root_stake_t * root_stake =
     627             :   //         fd_root_stake_map_query( tower->root_stakes, prev_root_vote->root, NULL );
     628             :   //     root_stake->stake -= stake;
     629             :   //   }
     630             : 
     631             :   //   /* Update our bookkeeping of this node pubkey's root. */
     632             : 
     633             :   //   prev_root_vote->root = latest_vote->root;
     634             : 
     635             :   //   /* Add this node pubkey's stake to all slots in the ancestry back to the SMR. */
     636             : 
     637             :   //   fd_root_stake_t * root_stake =
     638             :   //       fd_root_stake_map_query( tower->root_stakes, latest_vote->root, NULL );
     639             :   //   if( FD_UNLIKELY( !root_stake ) ) {
     640             :   //     root_stake = fd_root_stake_map_insert( tower->root_stakes, latest_vote->root );
     641             :   //   }
     642             :   //   root_stake->stake += stake;
     643             :   // }
     644             :   // }
     645             : 
     646             :   // if( FD_LIKELY( smr > tower->smr ) ) tower->smr = smr;
     647           0 : }
     648             : 
     649             : void
     650           0 : fd_tower_epoch_update( fd_tower_t * tower, fd_exec_epoch_ctx_t const * epoch_ctx ) {
     651           0 :   fd_epoch_bank_t const * epoch_bank = fd_exec_epoch_ctx_epoch_bank_const( epoch_ctx );
     652             : 
     653           0 :   ulong total_stake = 0;
     654             : 
     655           0 :   for( fd_vote_accounts_pair_t_mapnode_t * curr = fd_vote_accounts_pair_t_map_minimum(
     656           0 :            epoch_bank->stakes.vote_accounts.vote_accounts_pool,
     657           0 :            epoch_bank->stakes.vote_accounts.vote_accounts_root );
     658           0 :        curr;
     659           0 :        curr = fd_vote_accounts_pair_t_map_successor( epoch_bank->stakes.vote_accounts
     660           0 :                                                          .vote_accounts_pool,
     661           0 :                                                      curr ) ) {
     662             : 
     663           0 : #if FD_TOWER_USE_HANDHOLDING
     664           0 :     if( FD_UNLIKELY( fd_tower_vote_accs_cnt( tower->vote_accs ) ==
     665           0 :                      fd_tower_vote_accs_max( tower->vote_accs ) ) )
     666           0 :       FD_LOG_ERR(( "fd_tower_vote_accs overflow." ));
     667           0 : #endif
     668             : 
     669           0 :     if( FD_UNLIKELY( curr->elem.stake > 0UL ) ) {
     670           0 :       fd_tower_vote_acc_t vote_acc = { .addr = &curr->elem.key, .stake = curr->elem.stake };
     671           0 :       fd_tower_vote_accs_push_tail( tower->vote_accs, vote_acc );
     672           0 :     }
     673           0 :     total_stake += curr->elem.stake;
     674           0 :   }
     675           0 :   tower->total_stake = total_stake;
     676           0 : }
     677             : 
     678             : void
     679             : fd_tower_fork_update( fd_tower_t const * tower,
     680             :                       fd_fork_t const *  fork,
     681             :                       fd_acc_mgr_t *     acc_mgr,
     682             :                       fd_blockstore_t *  blockstore,
     683           0 :                       fd_ghost_t *       ghost ) {
     684             : 
     685             :   /* Get the parent key. Every slot except the root must have a parent. */
     686             : 
     687           0 :   fd_blockstore_start_read( blockstore );
     688           0 :   ulong parent_slot = fd_blockstore_parent_slot_query( blockstore, fork->slot );
     689           0 : #if FD_TOWER_USE_HANDHOLDING
     690             :   /* we must have a parent slot and bank hash, given we just executed
     691             :      its child. if not, likely a bug in blockstore pruning. */
     692           0 :   if( FD_UNLIKELY( parent_slot == FD_SLOT_NULL ) ) {
     693           0 :     FD_LOG_ERR(( "missing parent slot for curr slot %lu", fork->slot ));
     694           0 :   };
     695           0 : #endif
     696           0 :   fd_blockstore_end_read( blockstore );
     697             : 
     698             :   /* Insert the new fork head into ghost. */
     699             : 
     700           0 :   fd_ghost_node_t * ghost_node = fd_ghost_insert( ghost, fork->slot, parent_slot );
     701             : 
     702           0 : #if FD_TOWER_USE_HANDHOLDING
     703           0 :   if( FD_UNLIKELY( !ghost_node ) ) {
     704           0 :     FD_LOG_ERR(( "failed to insert ghost node %lu", fork->slot ));
     705           0 :   }
     706           0 : #endif
     707           0 :   for( fd_tower_vote_accs_iter_t iter = fd_tower_vote_accs_iter_init_rev( tower->vote_accs );
     708           0 :        !fd_tower_vote_accs_iter_done_rev( tower->vote_accs, iter );
     709           0 :        iter = fd_tower_vote_accs_iter_prev( tower->vote_accs, iter ) ) {
     710             : 
     711           0 :     fd_tower_vote_acc_t * vote_acc = fd_tower_vote_accs_iter_ele( tower->vote_accs, iter );
     712             : 
     713           0 :     FD_SCRATCH_SCOPE_BEGIN {
     714             : 
     715             :       /* FIXME */
     716           0 :       fd_vote_state_versioned_t vote_state_versioned = { 0 };
     717             : 
     718           0 :       fd_vote_state_t * vote_state = fd_tower_vote_state_query( tower,
     719           0 :                                                                 vote_acc->addr,
     720           0 :                                                                 acc_mgr,
     721           0 :                                                                 fork,
     722           0 :                                                                 fd_scratch_virtual(),
     723           0 :                                                                 &vote_state_versioned );
     724           0 :       if( FD_UNLIKELY( !vote_state ) ) {
     725           0 :         FD_LOG_WARNING(( "[%s] failed to load vote acc addr %s. skipping.",
     726           0 :                          __func__,
     727           0 :                          FD_BASE58_ENC_32_ALLOCA( vote_acc->addr ) ));
     728           0 :         continue;
     729           0 :       }
     730             : 
     731           0 :       fd_landed_vote_t * landed_votes = vote_state->votes;
     732             : 
     733             :       /* If the vote account has an empty tower, continue. */
     734             : 
     735           0 :       if( FD_UNLIKELY( deq_fd_landed_vote_t_empty( landed_votes ) ) ) continue;
     736             : 
     737             :       /* Get the vote account's latest vote. */
     738             : 
     739           0 :       ulong vote_slot = deq_fd_landed_vote_t_peek_tail_const( landed_votes )->lockout.slot;
     740             : 
     741           0 :       if( FD_UNLIKELY( vote_slot < ghost->root->slot ) ) continue;
     742             : 
     743             :       /* Only process votes for slots >= root. Ghost requires vote slot
     744             :          to already exist in the ghost tree. */
     745             : 
     746           0 :       if( FD_UNLIKELY( vote_slot >= ghost->root->slot ) ) {
     747           0 :         fd_ghost_node_t const * node = fd_ghost_replay_vote( ghost, vote_slot, &vote_state->node_pubkey, vote_acc->stake );
     748             : 
     749             :         /* Check if it has crossed the equivocation safety and optimistic confirmation thresholds. */
     750             : 
     751           0 :         fd_blockstore_start_write( blockstore );
     752           0 :         fd_block_map_t * block_map_entry = fd_blockstore_block_map_query( blockstore, vote_slot );
     753             : 
     754           0 :         int eqvocsafe = fd_uchar_extract_bit( block_map_entry->flags, FD_BLOCK_FLAG_EQVOCSAFE );
     755           0 :         if( FD_UNLIKELY( !eqvocsafe ) ) {
     756           0 :           double pct = (double)node->stake / (double)ghost->total_stake;
     757           0 :           if( FD_UNLIKELY( pct > FD_EQVOCSAFE_PCT ) ) {
     758           0 :             FD_LOG_NOTICE( ( "equivocation safe %lu", block_map_entry->slot ) );
     759           0 :             block_map_entry->flags = fd_uchar_set_bit( block_map_entry->flags, FD_BLOCK_FLAG_EQVOCSAFE );
     760           0 :             blockstore->hcs = fd_ulong_max( blockstore->hcs, block_map_entry->slot );
     761           0 :           }
     762           0 :         }
     763             : 
     764           0 :         int confirmed = fd_uchar_extract_bit( block_map_entry->flags, FD_BLOCK_FLAG_CONFIRMED );
     765           0 :         if( FD_UNLIKELY( !confirmed ) ) {
     766           0 :           double pct = (double)node->stake / (double)ghost->total_stake;
     767           0 :           if( FD_UNLIKELY( pct > FD_CONFIRMED_PCT ) ) {
     768           0 :             FD_LOG_NOTICE( ( "confirming %lu", block_map_entry->slot ) );
     769           0 :             block_map_entry->flags = fd_uchar_set_bit( block_map_entry->flags, FD_BLOCK_FLAG_CONFIRMED );
     770           0 :             blockstore->hcs = fd_ulong_max( blockstore->hcs, block_map_entry->slot );
     771           0 :           }
     772           0 :         }
     773             : 
     774           0 :         fd_blockstore_end_write( blockstore );
     775           0 :       }
     776             : 
     777             :       /* Check if this voter's root >= ghost root. We can't process
     778             :          other voters' roots that precede the ghost root. */
     779             : 
     780           0 :       if( FD_UNLIKELY( vote_state->root_slot >= ghost->root->slot ) ) {
     781           0 :         fd_ghost_node_t const * node = fd_ghost_rooted_vote( ghost, vote_state->root_slot, &vote_state->node_pubkey, vote_acc->stake );
     782             : 
     783             :         /* Check if it has crossed finalized threshold. */
     784             : 
     785           0 :         fd_blockstore_start_write( blockstore );
     786           0 :         fd_block_map_t * block_map_entry = fd_blockstore_block_map_query( blockstore, vote_state->root_slot );
     787           0 :         int finalized = fd_uchar_extract_bit( block_map_entry->flags, FD_BLOCK_FLAG_FINALIZED );
     788           0 :         if( FD_UNLIKELY( !finalized ) ) {
     789           0 :           double pct = (double)node->rooted_stake / (double)ghost->total_stake;
     790           0 :           if( FD_UNLIKELY( pct > FD_FINALIZED_PCT ) ) {
     791           0 :             ulong smr = block_map_entry->slot;
     792           0 :             FD_LOG_NOTICE(( "finalizing %lu", block_map_entry->slot ));
     793           0 :             fd_block_map_t * ancestor = block_map_entry;
     794           0 :             while( ancestor ) {
     795           0 :               ancestor->flags = fd_uchar_set_bit( ancestor->flags, FD_BLOCK_FLAG_FINALIZED );
     796           0 :               ancestor        = fd_blockstore_block_map_query( blockstore, ancestor->parent_slot );
     797           0 :             }
     798           0 : #if FD_TOWER_USE_HANDHOLDING
     799           0 :             if( FD_UNLIKELY( smr <= fd_fseq_query( tower->smr ) ) ) {
     800           0 :               FD_LOG_ERR(( "invariant violation. newly observed SMR %lu <= existing fseq SMR %lu.",
     801           0 :                            smr,
     802           0 :                            fd_fseq_query( tower->smr ) ));
     803           0 :             }
     804           0 : #endif
     805           0 :             fd_fseq_update( tower->smr, smr );
     806           0 :           }
     807           0 :         }
     808           0 :         fd_blockstore_end_write( blockstore );
     809           0 :       }
     810           0 :     }
     811           0 :     FD_SCRATCH_SCOPE_END;
     812           0 :   }
     813           0 : }
     814             : 
     815             : ulong
     816           0 : fd_tower_simulate_vote( fd_tower_t const * tower, ulong slot ) {
     817           0 :   return simulate_vote( tower->votes, slot );
     818           0 : }
     819             : 
     820             : void
     821           0 : fd_tower_vote( fd_tower_t const * tower, ulong slot ) {
     822           0 :   FD_LOG_NOTICE(( "[fd_tower_vote] voting for slot %lu", slot ));
     823             : 
     824             :   /* Check we're not voting for the exact same slot as our latest tower
     825             :      vote. This can happen when there are forks. */
     826             : 
     827           0 :   fd_tower_vote_t * latest_vote = fd_tower_votes_peek_tail( tower->votes );
     828           0 :   if( FD_UNLIKELY( latest_vote && latest_vote->slot == slot ) ) {
     829           0 :     FD_LOG_NOTICE(( "[fd_tower_vote] already voted for slot %lu", slot ));
     830           0 :     return;
     831           0 :   }
     832             : 
     833           0 : #if FD_TOWER_USE_HANDHOLDING
     834             : 
     835             :   /* Check we aren't voting for a slot earlier than the latest tower
     836             :      vote. This should not happen and indicates a bug, because on the
     837             :      same vote fork the slot should be monotonically non-decreasing. */
     838             : 
     839           0 :   for( fd_tower_votes_iter_t iter = fd_tower_votes_iter_init_rev( tower->votes );
     840           0 :        !fd_tower_votes_iter_done_rev( tower->votes, iter );
     841           0 :        iter = fd_tower_votes_iter_prev( tower->votes, iter ) ) {
     842           0 :     fd_tower_vote_t const * vote = fd_tower_votes_iter_ele_const( tower->votes, iter );
     843           0 :     if( FD_UNLIKELY( slot == vote->slot ) ) {
     844           0 :       fd_tower_print( tower );
     845           0 :       FD_LOG_ERR(( "[fd_tower_vote] double-voting for old slot %lu (new vote: %lu)",
     846           0 :                    slot,
     847           0 :                    vote->slot ));
     848           0 :     }
     849           0 :   }
     850             : 
     851           0 : #endif
     852             : 
     853             :   /* First, simulate a vote for slot. We do this purely for
     854             :      implementation convenience and code reuse.
     855             : 
     856             :      As the name of this function indicates, we are not just
     857             :      simulating and in fact voting for this fork by pushing this a new
     858             :      vote onto the tower. */
     859             : 
     860           0 :   ulong cnt = simulate_vote( tower->votes, slot );
     861             : 
     862             :   /* Pop everything that got expired. */
     863             : 
     864           0 :   while( fd_tower_votes_cnt( tower->votes ) > cnt ) {
     865           0 :     fd_tower_votes_pop_tail( tower->votes );
     866           0 :   }
     867             : 
     868             :   /* Increase confirmations (double lockouts) in consecutive votes. */
     869             : 
     870           0 :   ulong prev_conf = 0;
     871           0 :   for( fd_tower_votes_iter_t iter = fd_tower_votes_iter_init_rev( tower->votes );
     872           0 :        !fd_tower_votes_iter_done_rev( tower->votes, iter );
     873           0 :        iter = fd_tower_votes_iter_prev( tower->votes, iter ) ) {
     874           0 :     fd_tower_vote_t * vote = fd_tower_votes_iter_ele( tower->votes, iter );
     875           0 :     if( FD_UNLIKELY( vote->conf != ++prev_conf ) ) {
     876           0 :       break;
     877           0 :     }
     878           0 :     vote->conf++;
     879           0 :   }
     880             : 
     881             :   /* Add the new vote to the tower. */
     882             : 
     883           0 :   fd_tower_votes_push_tail( tower->votes, ( fd_tower_vote_t ){ .slot = slot, .conf = 1 } );
     884           0 : }
     885             : 
     886             : void
     887           0 : fd_tower_print( fd_tower_t const * tower ) {
     888           0 :   print( tower->votes, tower->root );
     889           0 : }
     890             : 
     891             : int
     892           0 : fd_tower_vote_state_cmp( fd_tower_t const * tower, fd_vote_state_t * vote_state ) {
     893           0 : #if FD_TOWER_USE_HANDHOLDING
     894           0 :   if( FD_UNLIKELY( !tower->root ) ) {
     895           0 :     FD_LOG_ERR(( "[%s] tower is missing root.", __func__ ));
     896           0 :   }
     897             : 
     898           0 :   if( FD_UNLIKELY( fd_tower_votes_empty( tower->votes ) ) ) {
     899           0 :     FD_LOG_ERR(( "[%s] tower is empty.", __func__ ));
     900           0 :   }
     901             : 
     902           0 :   if( FD_UNLIKELY( !vote_state->has_root_slot ) ) {
     903           0 :     FD_LOG_ERR(( "[%s] vote_state is missing root.", __func__ ));
     904           0 :   }
     905             : 
     906           0 :   if( FD_UNLIKELY( deq_fd_landed_vote_t_empty( vote_state->votes ) ) ) {
     907           0 :     FD_LOG_ERR(( "[%s] vote_state is empty.", __func__ ));
     908           0 :   }
     909           0 : #endif
     910             : 
     911           0 :   ulong local   = fd_tower_votes_peek_tail_const( tower->votes )->slot;
     912           0 :   ulong cluster = deq_fd_landed_vote_t_peek_tail_const( vote_state->votes )->lockout.slot;
     913           0 :   return fd_int_if( local == cluster, 0, fd_int_if( local > cluster, 1, -1 ) );
     914           0 : }
     915             : 
     916             : fd_vote_state_t *
     917             : fd_tower_vote_state_query( FD_PARAM_UNUSED fd_tower_t const * tower,
     918             :                            fd_pubkey_t const *                vote_acc_addr,
     919             :                            fd_acc_mgr_t *                     acc_mgr,
     920             :                            fd_fork_t const *                  fork,
     921             :                            fd_valloc_t                        valloc,
     922           0 :                            fd_vote_state_versioned_t *        versioned ) {
     923           0 :   int rc;
     924             : 
     925           0 :   FD_BORROWED_ACCOUNT_DECL( vote_acc );
     926           0 :   rc = fd_acc_mgr_view( acc_mgr, fork->slot_ctx.funk_txn, vote_acc_addr, vote_acc );
     927           0 :   if( FD_UNLIKELY( rc == FD_ACC_MGR_ERR_UNKNOWN_ACCOUNT ) ) {
     928           0 :     FD_LOG_WARNING(( "[%s] fd_acc_mgr_view could not find vote account %s. error: %d",
     929           0 :                      __func__,
     930           0 :                      FD_BASE58_ENC_32_ALLOCA( vote_acc_addr ),
     931           0 :                      rc ));
     932           0 :     return NULL;
     933           0 :   } else if( FD_UNLIKELY( rc != FD_ACC_MGR_SUCCESS ) ) {
     934           0 :     FD_LOG_ERR(( "[%s] fd_acc_mgr_view failed on vote account %s. error: %d",
     935           0 :                  __func__,
     936           0 :                  FD_BASE58_ENC_32_ALLOCA( vote_acc_addr ),
     937           0 :                  rc ));
     938           0 :   }
     939             : 
     940           0 :   rc = fd_vote_get_state( vote_acc, valloc, versioned );
     941           0 :   if( FD_UNLIKELY( rc != FD_ACC_MGR_SUCCESS ) ) {
     942           0 :     FD_LOG_ERR(( "[%s] fd_vote_get_state failed on vote account %s. error: %d",
     943           0 :                  __func__,
     944           0 :                  FD_BASE58_ENC_32_ALLOCA( vote_acc_addr ),
     945           0 :                  rc ));
     946           0 :   }
     947             : 
     948           0 :   fd_vote_convert_to_current( versioned, valloc );
     949             : 
     950           0 :   return &versioned->inner.current;
     951           0 : }
     952             : 
     953             : void
     954           0 : fd_tower_from_vote_state( fd_tower_t * tower, fd_vote_state_t * vote_state ) {
     955           0 : #if FD_TOWER_USE_HANDHOLDING
     956           0 :   if( FD_UNLIKELY( tower->root ) ) {
     957           0 :     FD_LOG_WARNING(( "[%s] overwriting existing tower root %lu.", __func__, tower->root ));
     958           0 :   }
     959             : 
     960           0 :   if( FD_UNLIKELY( !fd_tower_votes_empty( tower->votes ) ) ) {
     961           0 :     FD_LOG_WARNING(( "[%s] overwriting existing tower votes.", __func__ ));
     962           0 :   }
     963             : 
     964           0 :   if( FD_UNLIKELY( !vote_state->has_root_slot ) ) {
     965           0 :     FD_LOG_WARNING(( "[%s] vote_state is missing root.", __func__ ));
     966           0 :   }
     967             : 
     968           0 :   if( FD_UNLIKELY( deq_fd_landed_vote_t_empty( vote_state->votes ) ) ) {
     969           0 :     FD_LOG_WARNING(( "[%s] vote_state is empty.", __func__ ));
     970           0 :   }
     971             : 
     972           0 : #endif
     973             : 
     974           0 :   tower_votes_from_landed_votes( tower->votes, vote_state->votes );
     975           0 :   tower->root = vote_state->root_slot;
     976           0 : }
     977             : 
     978             : // ulong i = fd_tower_votes_cnt( tower->votes );
     979             : // while ( fd_tower_vote(const fd_tower_t *tower, ulong slot) )
     980             : 
     981             : // /* If the local view of our tower is a strict subset or divergent from
     982             : //    the cluster view, then we need to sync with the cluster ie. replace
     983             : //    our local state with the cluster state. */
     984             : 
     985             : // int   sync = 0;
     986             : // ulong i    = 0;
     987             : // for( deq_fd_landed_vote_t_iter_t iter = deq_fd_landed_vote_t_iter_init( vote_state->votes );
     988             : //      !deq_fd_landed_vote_t_iter_done( vote_state->votes, iter );
     989             : //      iter = deq_fd_landed_vote_t_iter_next( vote_state->votes, iter ) ) {
     990             : //   fd_landed_vote_t const * landed_vote =
     991             : //       deq_fd_landed_vote_t_iter_ele_const( vote_state->votes, iter );
     992             : //   fd_tower_vote_t const * tower_vote = fd_tower_votes_peek_index_const( tower->votes, i++ );
     993             : 
     994             : //   if( FD_UNLIKELY( !tower_vote ) ) {
     995             : 
     996             : //     /* Local tower is shorter, need sync*/
     997             : 
     998             : //     sync = 1;
     999             : //   }
    1000             : 
    1001             : //   if( FD_UNLIKELY( tower_vote->slot != landed_vote->lockout.slot ||
    1002             : //                    tower_vote->conf != landed_vote->lockout.confirmation_count ) ) {
    1003             : 
    1004             : //     /* Local tower diverges, need sync */
    1005             : 
    1006             : //     sync = 1;
    1007             : //   }
    1008             : // }
    1009             : 
    1010             : // /* Note we don't sync if the local view of the tower is taller.
    1011             : //    Syncing can lead to a problem where we can otherwise get "stuck" in
    1012             : //    a cluster-sync loop when our votes don't land.  For example, if our
    1013             : //    tower is currently [1 3 5], and we vote [1 3 5 7].  That vote
    1014             : //    doesn't land, so we re-sync and as a result vote [1 3 5 9].
    1015             : //    Instead we would prefer [1 3 5 7 9] for the second vote. */
    1016             : 
    1017             : // if( FD_UNLIKELY( sync ) ) {
    1018             : 
    1019             : //   FD_LOG_WARNING(("syncing with cluster"));
    1020             : 
    1021             : //   /* Sync local with cluster.  */
    1022             : 
    1023             : //   fd_tower_votes_remove_all( tower->votes );
    1024             : //   tower_votes_from_landed_votes( tower->votes, vote_state->votes );
    1025             : //   tower->root = vote_state->root_slot;
    1026             : // }
    1027             : 
    1028             : void
    1029             : fd_tower_to_tower_sync( fd_tower_t const *               tower,
    1030             :                         fd_hash_t const *                bank_hash,
    1031           0 :                         fd_compact_vote_state_update_t * tower_sync ) {
    1032           0 :   tower_sync->root          = tower->root;
    1033           0 :   long ts                   = fd_log_wallclock();
    1034           0 :   tower_sync->has_timestamp = 1;
    1035           0 :   tower_sync->timestamp     = ts;
    1036           0 :   tower_sync->lockouts_len  = (ushort)fd_tower_votes_cnt( tower->votes );
    1037           0 :   tower_sync->lockouts      = (fd_lockout_offset_t *)
    1038           0 :       fd_scratch_alloc( alignof( fd_lockout_offset_t ),
    1039           0 :                         tower_sync->lockouts_len * sizeof( fd_lockout_offset_t ) );
    1040             : 
    1041           0 :   ulong i         = 0UL;
    1042           0 :   ulong curr_slot = tower_sync->root;
    1043             : 
    1044           0 :   for( fd_tower_votes_iter_t iter = fd_tower_votes_iter_init( tower->votes );
    1045           0 :        !fd_tower_votes_iter_done( tower->votes, iter );
    1046           0 :        iter = fd_tower_votes_iter_next( tower->votes, iter ) ) {
    1047           0 :     fd_tower_vote_t const * vote = fd_tower_votes_iter_ele_const( tower->votes, iter );
    1048           0 :     FD_TEST( vote->slot >= tower_sync->root );
    1049           0 :     ulong offset                               = vote->slot - curr_slot;
    1050           0 :     curr_slot                                  = vote->slot;
    1051           0 :     uchar conf                                 = (uchar)vote->conf;
    1052           0 :     tower_sync->lockouts[i].offset             = offset;
    1053           0 :     tower_sync->lockouts[i].confirmation_count = conf;
    1054           0 :     memcpy( tower_sync->hash.uc, bank_hash, sizeof( fd_hash_t ) );
    1055           0 :     i++;
    1056           0 :   }
    1057           0 : }

Generated by: LCOV version 1.14