LCOV - code coverage report
Current view: top level - disco/restart - fd_restart.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 360 0.0 %
Date: 2025-01-08 12:08:44 Functions: 0 13 0.0 %

          Line data    Source code
       1             : #include <sys/types.h>
       2             : #include <unistd.h>
       3             : #include "fd_restart.h"
       4             : #include "../../util/fd_util.h"
       5             : #include "../../flamenco/stakes/fd_stakes.h"
       6             : #include "../../flamenco/runtime/sysvar/fd_sysvar_epoch_schedule.h"
       7             : 
       8             : #pragma GCC diagnostic ignored "-Wformat"
       9             : #pragma GCC diagnostic ignored "-Wformat-extra-args"
      10           0 : #define BITS_PER_UCHAR ( 8*sizeof(uchar) )
      11           0 : #define BITS_PER_ULONG ( 8*sizeof(ulong) )
      12             : 
      13             : void *
      14           0 : fd_restart_new( void * mem ) {
      15           0 :   if( FD_UNLIKELY( !mem ) ) {
      16           0 :     FD_LOG_WARNING(( "NULL mem" ));
      17           0 :     return NULL;
      18           0 :   }
      19             : 
      20           0 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)mem, fd_restart_align() ) ) ) {
      21           0 :     FD_LOG_WARNING(( "misaligned mem" ));
      22           0 :     return NULL;
      23           0 :   }
      24             : 
      25           0 :   fd_memset( mem, 0, fd_restart_footprint() );
      26           0 :   return mem;
      27           0 : }
      28             : 
      29             : fd_restart_t *
      30           0 : fd_restart_join( void * restart ) {
      31           0 :   if( FD_UNLIKELY( !restart ) ) {
      32           0 :     FD_LOG_WARNING(( "NULL mem" ));
      33           0 :     return NULL;
      34           0 :   }
      35             : 
      36           0 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)restart, fd_restart_align() ) ) ) {
      37           0 :     FD_LOG_WARNING(( "misaligned mem" ));
      38           0 :     return NULL;
      39           0 :   }
      40             : 
      41           0 :   fd_restart_t * restart_ = (fd_restart_t *)restart;
      42           0 :   return restart_;
      43           0 : }
      44             : 
      45             : static int
      46           0 : fd_restart_recv_enough_stake( fd_restart_t * restart ) {
      47           0 :   ulong received[RESTART_EPOCHS_MAX] = { restart->total_stake_received[0]*100/restart->total_stake[0],
      48           0 :                                          restart->total_stake_received[1]*100/restart->total_stake[1] };
      49           0 :   ulong voted[RESTART_EPOCHS_MAX]    = { restart->total_stake_received_and_voted[0]*100/restart->total_stake[0],
      50           0 :                                          restart->total_stake_received_and_voted[1]*100/restart->total_stake[1] };
      51             : 
      52           0 :   for( ulong e=0; e<RESTART_EPOCHS_MAX; e++ ) {
      53           0 :     FD_LOG_NOTICE(( "Epoch%lu: %lu/%lu = %lu%c stake received\n",
      54           0 :                     restart->root_epoch+e, restart->total_stake_received[e], restart->total_stake[e], received[e], '%' ));
      55           0 :     FD_LOG_NOTICE(( "Epoch%lu: %lu/%lu = %lu%c stake voted\n",
      56           0 :                     restart->root_epoch+e, restart->total_stake_received_and_voted[e], restart->total_stake[e], voted[e], '%' ));
      57           0 :   }
      58             : 
      59           0 :   ulong min_active_stake = received[0];
      60           0 :   if( FD_UNLIKELY( voted[1]>=WAIT_FOR_NEXT_EPOCH_THRESHOLD_PERCENT ) ) {
      61           0 :     min_active_stake = fd_ulong_min( min_active_stake, received[1] );
      62           0 :   }
      63           0 :   return min_active_stake>=WAIT_FOR_SUPERMAJORITY_THRESHOLD_PERCENT;
      64           0 : }
      65             : 
      66             : static void
      67             : fd_restart_recv_last_voted_fork_slots( fd_restart_t * restart,
      68             :                                        fd_gossip_restart_last_voted_fork_slots_t * msg,
      69           0 :                                        ulong * out_heaviest_fork_found ) {
      70           0 :   if( FD_UNLIKELY( restart->stage!=WR_STAGE_FIND_HEAVIEST_FORK_SLOT_NUM ) ) return;
      71             : 
      72             :   /* Check that funk is not too stale for aggregating this message */
      73           0 :   ulong voted_epoch = fd_slot_to_epoch( restart->epoch_schedule, msg->last_voted_slot, NULL );
      74           0 :   if( FD_UNLIKELY( voted_epoch!=restart->root_epoch && voted_epoch!=restart->root_epoch+1 ) ) {
      75           0 :     FD_LOG_WARNING(( "Ignore last_voted_fork_slots message from validator %s for epoch%lu (because root_epoch=%lu is stale)",
      76           0 :                      FD_BASE58_ENC_32_ALLOCA( &msg->from ), voted_epoch, restart->root_epoch ));
      77           0 :     return;
      78           0 :   }
      79           0 :   if( FD_UNLIKELY( msg->last_voted_slot>=restart->funk_root+LAST_VOTED_FORK_MAX_SLOTS ) ) {
      80           0 :     FD_LOG_WARNING(( "Ignore last_voted_fork_slots message for slot=%lu (because funk_root=%lu is stale) from validator %s",
      81           0 :                      msg->last_voted_slot, restart->funk_root, FD_BASE58_ENC_32_ALLOCA( &msg->from ) ));
      82           0 :     return;
      83           0 :   }
      84             : 
      85             :   /* Find the message sender from restart->stake_weights */
      86           0 :   fd_pubkey_t * pubkey = &msg->from;
      87           0 :   ulong stake_received[ RESTART_EPOCHS_MAX ] = {0UL, 0UL};
      88             : 
      89           0 :   for( ulong e=0; e<RESTART_EPOCHS_MAX; e++ ) {
      90           0 :     for( ulong i=0; i<restart->num_vote_accts[e]; i++ ) {
      91           0 :       if( FD_UNLIKELY( memcmp( pubkey->key, restart->stake_weights[e][i].key.key, sizeof(fd_pubkey_t) )==0 ) ) {
      92           0 :         if( FD_UNLIKELY( restart->last_voted_fork_slots_received[e][i] ) ) {
      93           0 :           FD_LOG_NOTICE(( "Duplicate last_voted_fork_slots message from validator %s", FD_BASE58_ENC_32_ALLOCA( pubkey ) ));
      94           0 :           return;
      95           0 :         }
      96           0 :         stake_received[e] = restart->stake_weights[e][i].stake;
      97           0 :         restart->last_voted_fork_slots_received[e][i] = 1;
      98           0 :         break;
      99           0 :       }
     100           0 :     }
     101           0 :     restart->total_stake_received[e]             += stake_received[e];
     102           0 :     if( FD_LIKELY( restart->root_epoch+e<=voted_epoch ) ) {
     103           0 :       restart->total_stake_received_and_voted[e] += stake_received[e];
     104           0 :     }
     105           0 :   }
     106             : 
     107           0 :   if( FD_UNLIKELY( stake_received[0]==0 && stake_received[1]==0 ) ) {
     108           0 :     FD_LOG_WARNING(( "Get last_voted_fork_slots message from validator %s with 0 stake", FD_BASE58_ENC_32_ALLOCA( pubkey ) ));
     109           0 :     return;
     110           0 :   }
     111             : 
     112             :   /* Decode the bitmap in the message and aggregate validator stake into slot_to_stake */
     113             :   /* The gossip tile should have already converted the bitmap into raw format */
     114           0 :   if( FD_UNLIKELY( msg->last_voted_slot+1<msg->offsets.inner.raw_offsets.offsets.len ) ) {
     115           0 :     FD_LOG_WARNING(( "Received invalid last_voted_fork_slot message from validator %s because %lu<%lu",
     116           0 :                      FD_BASE58_ENC_32_ALLOCA( pubkey ), msg->last_voted_slot+1, msg->offsets.inner.raw_offsets.offsets.len ));
     117           0 :   }
     118           0 :   for( ulong i=0; i<msg->offsets.inner.raw_offsets.offsets.len; i++ ) {
     119           0 :     if( FD_UNLIKELY( msg->last_voted_slot<restart->funk_root+i ) ) break;
     120             : 
     121           0 :     ulong slot     = msg->last_voted_slot-i;
     122           0 :     ulong byte_off = i/BITS_PER_UCHAR;
     123           0 :     int   bit_off  = i%BITS_PER_UCHAR;
     124           0 :     int   bit      = fd_uchar_extract_bit( msg->offsets.inner.raw_offsets.offsets.bits.bits[byte_off], bit_off );
     125           0 :     if( FD_LIKELY( bit ) ) {
     126           0 :       ulong offset = slot-restart->funk_root;
     127           0 :       ulong slot_epoch = fd_slot_to_epoch( restart->epoch_schedule, slot, NULL );
     128           0 :       FD_TEST( slot_epoch==restart->root_epoch || slot_epoch==restart->root_epoch+1 );
     129           0 :       restart->slot_to_stake[offset] += stake_received[slot_epoch-restart->root_epoch];
     130           0 :     }
     131           0 :   }
     132             : 
     133           0 :   if( FD_UNLIKELY( fd_restart_recv_enough_stake( restart ) ) ) {
     134           0 :     ulong stake_threshold[ RESTART_EPOCHS_MAX ] = { restart->total_stake_received[0]
     135           0 :                                                     - restart->total_stake[0]*HEAVIEST_FORK_THRESHOLD_DELTA_PERCENT/100UL,
     136           0 :                                                     restart->total_stake_received[1]
     137           0 :                                                     - restart->total_stake[1]*HEAVIEST_FORK_THRESHOLD_DELTA_PERCENT/100UL };
     138             :     /* The subtraction is safe because restart->total_stake_received[0/1] should be at least >(80-9)%==71% at this point */
     139             : 
     140           0 :     restart->heaviest_fork_slot = restart->funk_root;
     141           0 :     for( ulong offset=0; offset<LAST_VOTED_FORK_MAX_SLOTS; offset++ ) {
     142           0 :       ulong slot       = restart->funk_root+offset;
     143           0 :       ulong slot_epoch = fd_slot_to_epoch( restart->epoch_schedule, slot, NULL );
     144           0 :       if( slot_epoch>restart->root_epoch+1 ) break;
     145           0 :       if( FD_LIKELY( restart->slot_to_stake[offset]>=stake_threshold[slot_epoch-restart->root_epoch] ) ) {
     146           0 :         restart->heaviest_fork_slot = restart->funk_root+offset;
     147           0 :       }
     148           0 :     }
     149           0 :     FD_LOG_NOTICE(( "[%s] Found heaviest fork slot=%lu", __func__, restart->heaviest_fork_slot ));
     150             : 
     151           0 :     *out_heaviest_fork_found = 1;
     152           0 :     restart->stage           = WR_STAGE_FIND_HEAVIEST_FORK_BANK_HASH;
     153           0 :   }
     154           0 : }
     155             : 
     156             : static void
     157             : fd_restart_recv_heaviest_fork( fd_restart_t * restart,
     158           0 :                                fd_gossip_restart_heaviest_fork_t * msg ) {
     159           0 :   if( FD_LIKELY( memcmp( restart->coordinator_pubkey.key,
     160           0 :                          msg->from.key, sizeof(fd_pubkey_t) )==0 ) ) {
     161           0 :     FD_LOG_WARNING(( "Received a restart_heaviest_fork message: slot=%lu, hash=%s",
     162           0 :                      msg->last_slot, FD_BASE58_ENC_32_ALLOCA( &msg->last_slot_hash ) ));
     163           0 :     fd_memcpy( &restart->coordinator_heaviest_fork_bank_hash, &msg->last_slot_hash, sizeof(fd_hash_t) );
     164           0 :     restart->coordinator_heaviest_fork_slot  = msg->last_slot;
     165           0 :     restart->coordinator_heaviest_fork_ready = 1;
     166           0 :   } else {
     167           0 :     FD_LOG_WARNING(( "Received and ignored a restart_heaviest_fork message from non-coordinator %s",
     168           0 :                      FD_BASE58_ENC_32_ALLOCA( &msg->from ) ));
     169           0 :   }
     170           0 : }
     171             : 
     172             : void
     173             : fd_restart_recv_gossip_msg( fd_restart_t * restart,
     174             :                             void * gossip_msg,
     175           0 :                             ulong * out_heaviest_fork_found ) {
     176           0 :     uchar * src = (uchar *) fd_type_pun( gossip_msg );
     177           0 :     uint discriminant = FD_LOAD( uint, src );
     178           0 :     src += sizeof(uint);
     179             : 
     180           0 :     if( discriminant==fd_crds_data_enum_restart_heaviest_fork ) {
     181             :       /* Incoming packet from gossip tile. Format:
     182             :          Wen-restart gossip message for heaviest_fork (fd_gossip_restart_heaviest_fork_t)
     183             :       */
     184           0 :       fd_gossip_restart_heaviest_fork_t * msg = (fd_gossip_restart_heaviest_fork_t * ) fd_type_pun( src );
     185           0 :       fd_restart_recv_heaviest_fork( restart, msg );
     186           0 :     } else if( discriminant==fd_crds_data_enum_restart_last_voted_fork_slots ) {
     187             :       /* Incoming packet from gossip tile. Format:
     188             :          Wen-restart gossip message for last voted fork slots (fd_gossip_restart_last_voted_fork_slots_t)
     189             :          Bitmap in raw format (uchar* - bitmap size is specified in the gossip message)
     190             :       */
     191           0 :       fd_gossip_restart_last_voted_fork_slots_t * msg = (fd_gossip_restart_last_voted_fork_slots_t * ) fd_type_pun( src );
     192           0 :       msg->offsets.inner.raw_offsets.offsets.bits.bits = src + sizeof(fd_gossip_restart_last_voted_fork_slots_t);
     193           0 :       fd_restart_recv_last_voted_fork_slots( restart, msg, out_heaviest_fork_found );
     194           0 :     }
     195           0 : }
     196             : 
     197             : void
     198             : fd_restart_find_heaviest_fork_bank_hash( fd_restart_t * restart,
     199             :                                          fd_funk_t * funk,
     200           0 :                                          ulong * out_need_repair ) {
     201           0 :   if( FD_UNLIKELY( restart->heaviest_fork_slot<restart->funk_root ) ) {
     202           0 :     FD_LOG_ERR(( "Halting wen-restart because heaviest_fork_slot(%lu) < funk_root(%lu)",
     203           0 :                  restart->heaviest_fork_slot, restart->funk_root ));
     204           0 :   } else if( FD_UNLIKELY( restart->heaviest_fork_slot==restart->funk_root ) ) {
     205           0 :     FD_LOG_NOTICE(( "Found bank hash of slot%lu in funk: %s",
     206           0 :                     restart->funk_root, FD_BASE58_ENC_32_ALLOCA( &restart->root_bank_hash ) ));
     207           0 :     fd_memcpy( &restart->heaviest_fork_bank_hash, &restart->root_bank_hash, sizeof(fd_hash_t) );
     208           0 :     restart->heaviest_fork_ready = 1;
     209             : 
     210           0 :     *out_need_repair = 0;
     211           0 :   } else {
     212             :     /* Cancel any leftover in-preparation transactions from funk */
     213           0 :     fd_funk_start_write( funk );
     214           0 :     fd_funk_txn_cancel_all( funk, 1 );
     215           0 :     fd_funk_end_write( funk );
     216             : 
     217           0 :     *out_need_repair = 1;
     218           0 :   }
     219           0 : }
     220             : 
     221             : void
     222             : fd_restart_verify_heaviest_fork( fd_restart_t * restart,
     223             :                                  uchar * out_buf,
     224           0 :                                  ulong * out_send ) {
     225           0 :   *out_send = 0;
     226           0 :   if( FD_UNLIKELY( restart->stage!=WR_STAGE_FIND_HEAVIEST_FORK_BANK_HASH ) ) return;
     227             : 
     228           0 :   if( FD_UNLIKELY(( restart->heaviest_fork_ready==1 )) ) {
     229           0 :     if( FD_UNLIKELY( memcmp( restart->my_pubkey.key,
     230           0 :                              restart->coordinator_pubkey.key,
     231           0 :                              sizeof(fd_pubkey_t) )==0 ) ) {
     232             :       /* I am the wen-restart coordinator */
     233           0 :       *out_send = 1;
     234           0 :     } else if( FD_UNLIKELY( restart->coordinator_heaviest_fork_ready==1 ) ) {
     235             :       /* I am not the wen-restart coordinator, but the coordinator message was received */
     236           0 :       if( restart->heaviest_fork_slot!=restart->coordinator_heaviest_fork_slot ) {
     237           0 :         FD_LOG_ERR(( "Heaviest fork mismatch: my slot=%lu, coordinator slot=%lu",
     238           0 :                      restart->heaviest_fork_slot, restart->coordinator_heaviest_fork_slot ));
     239           0 :       }
     240           0 :       if( memcmp( restart->heaviest_fork_bank_hash.hash,
     241           0 :                   restart->coordinator_heaviest_fork_bank_hash.hash,
     242           0 :                   sizeof(fd_hash_t) )!=0 ) {
     243           0 :         FD_LOG_ERR(( "Heaviest fork mismatch for slot%lu: my hash=%s, coordinator hash=%s",
     244           0 :                      restart->heaviest_fork_slot,
     245           0 :                      FD_BASE58_ENC_32_ALLOCA( &restart->heaviest_fork_bank_hash ),
     246           0 :                      FD_BASE58_ENC_32_ALLOCA( &restart->coordinator_heaviest_fork_bank_hash ) ));
     247           0 :       }
     248           0 :       *out_send = 1;
     249           0 :     }
     250             : 
     251           0 :     if( FD_UNLIKELY( *out_send ) ) {
     252           0 :       fd_gossip_restart_heaviest_fork_t * msg = (fd_gossip_restart_heaviest_fork_t *) fd_type_pun( out_buf );
     253           0 :       msg->observed_stake                     = 0;
     254           0 :       msg->last_slot                          = restart->heaviest_fork_slot;
     255           0 :       fd_memcpy( msg->last_slot_hash.hash, restart->heaviest_fork_bank_hash.hash, sizeof(fd_hash_t) );
     256             : 
     257           0 :       restart->stage = WR_STAGE_GENERATE_SNAPSHOT;
     258           0 :       FD_LOG_WARNING(( "Wen-restart succeeds with slot=%lu, bank hash=%s",
     259           0 :                        restart->heaviest_fork_slot, FD_BASE58_ENC_32_ALLOCA( &restart->heaviest_fork_bank_hash ) ));
     260             :       /* TODO: insert a hard fork and generate an incremental snapshot */
     261             :       /* The incremental snapshot should contain everything from restart->funk_root to restart->heaviest_fork_slot. */
     262             : 
     263           0 :       restart->stage = WR_STAGE_DONE;
     264           0 :     }
     265           0 :   }
     266           0 : }
     267             : 
     268             : void
     269             : fd_restart_convert_runlength_to_raw_bitmap( fd_gossip_restart_last_voted_fork_slots_t * msg,
     270             :                                             uchar * out_bitmap,
     271           0 :                                             ulong * out_bitmap_len ) {
     272           0 :   ulong bit_cnt   = 0;
     273           0 :   *out_bitmap_len = 0;
     274           0 :   fd_memset( out_bitmap, 0, FD_RESTART_RAW_BITMAP_BYTES_MAX );
     275             : 
     276           0 :   for ( ulong i=0, bit=1; i<msg->offsets.inner.run_length_encoding.offsets_len; i++ ) {
     277           0 :     ushort cnt = msg->offsets.inner.run_length_encoding.offsets[i].bits;
     278           0 :     if( bit ) {
     279           0 :       for ( ulong pos=bit_cnt; pos<bit_cnt+cnt; pos++ ) {
     280           0 :         if( FD_UNLIKELY( pos/BITS_PER_UCHAR>=FD_RESTART_RAW_BITMAP_BYTES_MAX ) ) {
     281             :           /* Invalid message triggering a buffer overflow */
     282           0 :           *out_bitmap_len = FD_RESTART_RAW_BITMAP_BYTES_MAX+1;
     283           0 :           return;
     284           0 :         }
     285           0 :         out_bitmap[pos/BITS_PER_UCHAR] = fd_uchar_set_bit( out_bitmap[pos/BITS_PER_UCHAR], pos%BITS_PER_UCHAR );
     286           0 :       }
     287           0 :     }
     288           0 :     bit            ^= 1;
     289           0 :     bit_cnt        += cnt;
     290           0 :     *out_bitmap_len = (bit_cnt-1)/BITS_PER_UCHAR+1;
     291           0 :   }
     292           0 :   msg->offsets.discriminant                            = fd_restart_slots_offsets_enum_raw_offsets;
     293           0 :   msg->offsets.inner.raw_offsets.offsets.has_bits      = 1;
     294           0 :   msg->offsets.inner.raw_offsets.offsets.len           = bit_cnt;
     295           0 :   msg->offsets.inner.raw_offsets.offsets.bits.bits_len = *out_bitmap_len;
     296           0 : }
     297             : 
     298             : void
     299             : fd_restart_convert_raw_bitmap_to_runlength( fd_gossip_restart_last_voted_fork_slots_t * msg,
     300           0 :                                             fd_restart_run_length_encoding_inner_t * out_encoding ) {
     301           0 :   ushort cnt         = 0;
     302           0 :   int    last_bit    = 1;
     303           0 :   ulong  offsets_len = 0;
     304           0 :   for( ulong raw_bitmap_iter=0;
     305           0 :        raw_bitmap_iter<msg->offsets.inner.raw_offsets.offsets.len &&
     306           0 :        offsets_len<FD_RESTART_PACKET_BITMAP_BYTES_MAX/sizeof(ushort);
     307           0 :        raw_bitmap_iter++ ) {
     308           0 :     ulong idx = raw_bitmap_iter/BITS_PER_UCHAR;
     309           0 :     int   off = raw_bitmap_iter%BITS_PER_UCHAR;
     310           0 :     int   bit = fd_uchar_extract_bit( msg->offsets.inner.raw_offsets.offsets.bits.bits[idx], off );
     311           0 :     if( FD_LIKELY( bit==last_bit ) ) {
     312           0 :       cnt++;
     313           0 :     } else {
     314           0 :       out_encoding[offsets_len++].bits = cnt;
     315           0 :       cnt                              = 1;
     316           0 :       last_bit                         = bit;
     317           0 :     }
     318           0 :   }
     319           0 :   out_encoding[offsets_len++].bits = cnt;
     320             : 
     321           0 :   msg->offsets.discriminant                          = fd_restart_slots_offsets_enum_run_length_encoding;
     322           0 :   msg->offsets.inner.run_length_encoding.offsets_len = offsets_len;
     323           0 :   msg->offsets.inner.run_length_encoding.offsets     = out_encoding;
     324           0 : }
     325             : 
     326             : void
     327             : fd_restart_init( fd_restart_t * restart,
     328             :                  ulong funk_root,
     329             :                  fd_hash_t * root_bank_hash,
     330             :                  fd_vote_accounts_t const ** epoch_stakes,
     331             :                  fd_epoch_schedule_t * epoch_schedule,
     332             :                  int tower_checkpt_fileno,
     333             :                  fd_slot_history_t const * slot_history,
     334             :                  fd_pubkey_t * my_pubkey,
     335             :                  fd_pubkey_t * coordinator_pubkey,
     336             :                  uchar * out_buf,
     337           0 :                  ulong * out_buf_len ) {
     338           0 :   restart->funk_root                       = funk_root;
     339           0 :   restart->epoch_schedule                  = epoch_schedule;
     340           0 :   restart->root_epoch                      = fd_slot_to_epoch( epoch_schedule, restart->funk_root, NULL ),
     341           0 :   restart->stage                           = WR_STAGE_FIND_HEAVIEST_FORK_SLOT_NUM;
     342           0 :   restart->heaviest_fork_ready             = 0;
     343           0 :   restart->coordinator_heaviest_fork_ready = 0;
     344           0 :   fd_memcpy( restart->root_bank_hash.hash, root_bank_hash, sizeof(fd_pubkey_t) );
     345           0 :   fd_memcpy( restart->my_pubkey.key, my_pubkey, sizeof(fd_pubkey_t) );
     346           0 :   fd_memcpy( restart->coordinator_pubkey.key, coordinator_pubkey, sizeof(fd_pubkey_t) );
     347           0 :   fd_memset( restart->slot_to_stake, 0, sizeof(restart->slot_to_stake) );
     348           0 :   fd_memset( restart->last_voted_fork_slots_received, 0, sizeof(restart->last_voted_fork_slots_received) );
     349           0 :   FD_LOG_WARNING(( "[%s]\nfunk root=%lu\nroot epoch=%lu\nroot_bank_hash=%s\ncoordinator pubkey: %s\nMy pubkey: %s",
     350           0 :                     __func__,
     351           0 :                     restart->funk_root,
     352           0 :                     restart->root_epoch,
     353           0 :                     FD_BASE58_ENC_32_ALLOCA( &restart->root_bank_hash ),
     354           0 :                     FD_BASE58_ENC_32_ALLOCA( &restart->coordinator_pubkey ),
     355           0 :                     FD_BASE58_ENC_32_ALLOCA( &restart->my_pubkey ) ));
     356             : 
     357             :   /* Save the vote accounts stake information for the MAX_EPOCH epochs */
     358           0 :   FD_TEST( RESTART_EPOCHS_MAX==2 );
     359           0 :   for( ulong e=0; e<RESTART_EPOCHS_MAX; e++ ) {
     360           0 :     if( epoch_stakes[e]->vote_accounts_root==NULL ) FD_LOG_ERR(( "vote account information is missing for epoch#%lu", restart->root_epoch+e ));
     361           0 :     restart->num_vote_accts[e]                 = fd_stake_weights_by_node( epoch_stakes[e], restart->stake_weights[e] );
     362           0 :     restart->total_stake[e]                    = 0;
     363           0 :     restart->total_stake_received[e]           = 0;
     364           0 :     restart->total_stake_received_and_voted[e] = 0;
     365           0 :     FD_TEST( restart->num_vote_accts[e]<=FD_RESTART_MAX_PEERS );
     366             : 
     367           0 :     for( ulong i=0; i<restart->num_vote_accts[e]; i++ ) {
     368           0 :       FD_LOG_DEBUG(( "Epoch#%lu voter %s holds stake amount=%lu",
     369           0 :                       restart->root_epoch+e,
     370           0 :                       FD_BASE58_ENC_32_ALLOCA( &restart->stake_weights[e][i].key ),
     371           0 :                       restart->stake_weights[e][i].stake ));
     372           0 :       restart->total_stake[e] += restart->stake_weights[e][i].stake;
     373           0 :     }
     374           0 :     FD_LOG_NOTICE(( "[%s] There are %lu staked voters in epoch#%lu with total stake %lu",
     375           0 :                     __func__, restart->num_vote_accts[e], restart->root_epoch+e, restart->total_stake[e] ));
     376           0 :   }
     377             : 
     378             :   /* Get the last_voted_slot and its bank hash from the tower checkpoint file */
     379           0 :   fd_hash_t tower_bank_hash;
     380           0 :   ulong tower_height, tower_slots[ FD_TOWER_VOTE_MAX+1 ];
     381           0 :   fd_restart_tower_restore( &tower_bank_hash, tower_slots, &tower_height, tower_checkpt_fileno );
     382             : 
     383           0 :   fd_gossip_restart_last_voted_fork_slots_t * msg = (fd_gossip_restart_last_voted_fork_slots_t *) fd_type_pun( out_buf );
     384           0 :   msg->last_voted_slot = tower_slots[tower_height-1];
     385           0 :   fd_memcpy( msg->last_voted_hash.hash, tower_bank_hash.hash, sizeof(fd_hash_t) );
     386             : 
     387             :   /* Given last_voted_slot, get the bitmap for the last_voted_fork_slots gossip message */
     388           0 :   ulong end_slot   = msg->last_voted_slot;
     389           0 :   uchar * bitmap   = out_buf+sizeof(fd_gossip_restart_last_voted_fork_slots_t);
     390           0 :   ulong start_slot = ( end_slot>LAST_VOTED_FORK_MAX_SLOTS? end_slot-LAST_VOTED_FORK_MAX_SLOTS : 0 );
     391           0 :   ulong num_slots  = end_slot-start_slot+1;
     392           0 :   msg->offsets.discriminant                            = fd_restart_slots_offsets_enum_raw_offsets;
     393           0 :   msg->offsets.inner.raw_offsets.offsets.has_bits      = 1;
     394           0 :   msg->offsets.inner.raw_offsets.offsets.len           = num_slots;
     395           0 :   msg->offsets.inner.raw_offsets.offsets.bits.bits     = bitmap;
     396           0 :   msg->offsets.inner.raw_offsets.offsets.bits.bits_len = ( num_slots-1 )/BITS_PER_UCHAR+1;
     397           0 :   *out_buf_len = sizeof(fd_gossip_restart_last_voted_fork_slots_t)+( num_slots-1 )/BITS_PER_UCHAR+1;
     398           0 :   FD_LOG_NOTICE(( "[%s] last_voted_slot=%lu, bank_hash=%s, encoding %lu bits in bitmap",
     399           0 :                   __func__, msg->last_voted_slot, FD_BASE58_ENC_32_ALLOCA( &tower_bank_hash ), num_slots ));
     400             : 
     401             :   /* Encode slots from the tower checkpoint into the bitmap */
     402           0 :   for( ulong i=0; i<tower_height; i++ ) {
     403           0 :     ulong offset_from_end = end_slot-tower_slots[i];
     404           0 :     ulong out_idx         = offset_from_end/BITS_PER_UCHAR;
     405           0 :     int   out_bit_off     = offset_from_end%BITS_PER_UCHAR;
     406           0 :     bitmap[out_idx]       = fd_uchar_set_bit( bitmap[out_idx], out_bit_off );
     407           0 :   }
     408             : 
     409             :   /* Encode slots from the slot_history system program into the bitmap */
     410           0 :   if( FD_UNLIKELY( tower_slots[0]!=slot_history->next_slot ) ) {
     411           0 :     FD_LOG_WARNING(( "You may be loading a wrong snapshot in funk.\n \
     412           0 :                       We expect tower root(%lu) to be the same as slot_history->next_slot(%lu)", tower_slots[0], slot_history->next_slot ));
     413           0 :   }
     414             : 
     415           0 :   for( ulong i=start_slot; i<slot_history->next_slot; i++ ) {
     416           0 :     ulong in_idx          = ( i/BITS_PER_ULONG )%( slot_history->bits.bits->blocks_len );
     417           0 :     int   in_bit_off      = i%BITS_PER_ULONG;
     418             : 
     419           0 :     ulong offset_from_end = end_slot-i;
     420           0 :     ulong out_idx         = offset_from_end/BITS_PER_UCHAR;
     421           0 :     int   out_bit_off     = offset_from_end%BITS_PER_UCHAR;
     422             : 
     423           0 :     if( FD_LIKELY( fd_ulong_extract_bit( slot_history->bits.bits->blocks[in_idx], in_bit_off ) ) ) {
     424             :       /* bit#i in slot_history is 1 */
     425           0 :       bitmap[out_idx] = fd_uchar_set_bit( bitmap[out_idx], out_bit_off );
     426           0 :     } else {
     427             :       /* bit#i in slot_history is 0 */
     428           0 :       bitmap[out_idx] = fd_uchar_clear_bit( bitmap[out_idx], out_bit_off );
     429           0 :     }
     430           0 :   }
     431             : 
     432           0 :   ulong found;
     433           0 :   fd_memcpy( msg->from.key, my_pubkey->key, sizeof(fd_pubkey_t) );
     434           0 :   fd_restart_recv_last_voted_fork_slots( restart, msg, &found );
     435           0 :   if( FD_UNLIKELY( found ) ) FD_LOG_WARNING(( "[%s] It seems that this single validator alone has >80% stake", __func__ ));
     436           0 : }
     437             : 
     438             : void
     439             : fd_restart_tower_checkpt( fd_hash_t const * vote_bank_hash,
     440             :                           fd_tower_t * tower,
     441           0 :                           int tower_checkpt_fileno ) {
     442           0 :     lseek( tower_checkpt_fileno, 0, SEEK_SET );
     443           0 :     ulong wsz, total_wsz = 0;
     444           0 :     ulong slots_cnt = fd_tower_votes_cnt( tower->votes )+1;
     445             : 
     446           0 :     fd_io_write( tower_checkpt_fileno, vote_bank_hash, sizeof(fd_hash_t), sizeof(fd_hash_t), &wsz );
     447           0 :     if( FD_UNLIKELY( wsz!=sizeof(fd_hash_t) ) ) goto checkpt_finish;
     448           0 :     total_wsz += wsz;
     449           0 :     fd_io_write( tower_checkpt_fileno, &slots_cnt, sizeof(ulong), sizeof(ulong), &wsz );
     450           0 :     if( FD_UNLIKELY( wsz!=sizeof(ulong) ) ) goto checkpt_finish;
     451           0 :     total_wsz += wsz;
     452           0 :     fd_io_write( tower_checkpt_fileno, &tower->root, sizeof(ulong), sizeof(ulong), &wsz );
     453           0 :     if( FD_UNLIKELY( wsz!=sizeof(ulong) ) ) goto checkpt_finish;
     454           0 :     total_wsz += wsz;
     455             : 
     456           0 :     for( fd_tower_votes_iter_t tower_iter = fd_tower_votes_iter_init( tower->votes );
     457           0 :          !fd_tower_votes_iter_done( tower->votes, tower_iter );
     458           0 :          tower_iter = fd_tower_votes_iter_next( tower->votes, tower_iter ) ) {
     459           0 :       ulong slot = fd_tower_votes_iter_ele( tower->votes, tower_iter )->slot;
     460           0 :       fd_io_write( tower_checkpt_fileno, &slot, sizeof(ulong), sizeof(ulong), &wsz );
     461           0 :       if( FD_UNLIKELY( wsz!=sizeof(ulong) ) ) goto checkpt_finish;
     462           0 :       total_wsz += wsz;
     463           0 :     }
     464             : 
     465           0 :     fsync( tower_checkpt_fileno );
     466           0 :     checkpt_finish:
     467           0 :     if( FD_UNLIKELY( total_wsz!=sizeof(fd_hash_t)+sizeof(ulong)*( slots_cnt+1 ) ) ) FD_LOG_WARNING(( "Failed at checkpointing tower" ));
     468           0 : }
     469             : 
     470             : void
     471             : fd_restart_tower_restore( fd_hash_t * vote_bank_hash,
     472             :                           ulong * tower_slots,
     473             :                           ulong * tower_height,
     474           0 :                           int tower_checkpt_fileno ) {
     475           0 :   ulong rsz;
     476           0 :   FD_TEST( 0==fd_io_read( tower_checkpt_fileno, vote_bank_hash, sizeof(fd_hash_t), sizeof(fd_hash_t), &rsz ) );
     477           0 :   FD_TEST( rsz==sizeof(fd_hash_t) );
     478           0 :   FD_TEST( 0==fd_io_read( tower_checkpt_fileno, tower_height, sizeof(ulong), sizeof(ulong), &rsz ) );
     479           0 :   FD_TEST( rsz==sizeof(ulong) );
     480           0 :   FD_TEST( *tower_height<=FD_TOWER_VOTE_MAX+1 );
     481           0 :   for( ulong i=0; i<*tower_height; i++ ) {
     482           0 :     FD_TEST( 0==fd_io_read( tower_checkpt_fileno, tower_slots+i, sizeof(ulong), sizeof(ulong), &rsz ) );
     483           0 :     FD_TEST( rsz==sizeof(ulong) );
     484           0 :   }
     485           0 : }

Generated by: LCOV version 1.14