LCOV - code coverage report
Current view: top level - choreo/tower - fd_tower.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 354 0.0 %
Date: 2025-01-08 12:08:44 Functions: 0 17 0.0 %

          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 : }

Generated by: LCOV version 1.14