LCOV - code coverage report
Current view: top level - discof/restart - fd_restart.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 407 0.0 %
Date: 2025-03-20 12:08:36 Functions: 0 12 0.0 %

          Line data    Source code
       1             : #include "fd_restart.h"
       2             : 
       3             : #include "../../flamenco/stakes/fd_stakes.h"
       4             : #include "../../flamenco/snapshot/fd_snapshot_create.h"
       5             : #include "../../flamenco/runtime/sysvar/fd_sysvar_epoch_schedule.h"
       6             : 
       7             : #include <sys/types.h>
       8             : #include <unistd.h>
       9             : 
      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[FD_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[FD_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<FD_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]>=FD_RESTART_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>=FD_RESTART_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!=FD_RESTART_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+FD_RESTART_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[ FD_RESTART_EPOCHS_MAX ] = {0UL, 0UL};
      88             : 
      89           0 :   for( ulong e=0; e<FD_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[ FD_RESTART_EPOCHS_MAX ] = { restart->total_stake_received[0]
     135           0 :                                                        - restart->total_stake[0]*FD_RESTART_HEAVIEST_FORK_THRESHOLD_DELTA_PERCENT/100UL,
     136           0 :                                                        restart->total_stake_received[1]
     137           0 :                                                        - restart->total_stake[1]*FD_RESTART_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<FD_RESTART_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           = FD_RESTART_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             :                                  ulong *          is_constipated,
     224             :                                  fd_slot_pair_t * hard_forks,
     225             :                                  ulong            hard_forks_len,
     226             :                                  fd_hash_t *      genesis_hash,
     227             :                                  uchar *          out_buf,
     228           0 :                                  ulong *          out_send ) {
     229           0 :   *out_send = 0;
     230           0 :   if( FD_UNLIKELY( restart->stage!=FD_RESTART_STAGE_FIND_HEAVIEST_FORK_BANK_HASH ) ) return;
     231             : 
     232           0 :   if( FD_UNLIKELY(( restart->heaviest_fork_ready==1 )) ) {
     233           0 :     if( FD_UNLIKELY( memcmp( restart->my_pubkey.key,
     234           0 :                              restart->coordinator_pubkey.key,
     235           0 :                              sizeof(fd_pubkey_t) )==0 ) ) {
     236             :       /* I am the wen-restart coordinator */
     237           0 :       *out_send = 1;
     238           0 :     } else if( FD_UNLIKELY( restart->coordinator_heaviest_fork_ready==1 ) ) {
     239             :       /* I am not the wen-restart coordinator, but the coordinator message was received */
     240           0 :       if( restart->heaviest_fork_slot!=restart->coordinator_heaviest_fork_slot ) {
     241           0 :         FD_LOG_ERR(( "Heaviest fork mismatch: my slot=%lu, coordinator slot=%lu",
     242           0 :                      restart->heaviest_fork_slot, restart->coordinator_heaviest_fork_slot ));
     243           0 :       }
     244           0 :       if( memcmp( restart->heaviest_fork_bank_hash.hash,
     245           0 :                   restart->coordinator_heaviest_fork_bank_hash.hash,
     246           0 :                   sizeof(fd_hash_t) )!=0 ) {
     247           0 :         FD_LOG_ERR(( "Heaviest fork mismatch for slot%lu: my hash=%s, coordinator hash=%s",
     248           0 :                      restart->heaviest_fork_slot,
     249           0 :                      FD_BASE58_ENC_32_ALLOCA( &restart->heaviest_fork_bank_hash ),
     250           0 :                      FD_BASE58_ENC_32_ALLOCA( &restart->coordinator_heaviest_fork_bank_hash ) ));
     251           0 :       }
     252           0 :       *out_send = 1;
     253           0 :     }
     254             : 
     255           0 :     if( FD_UNLIKELY( *out_send ) ) {
     256           0 :       fd_gossip_restart_heaviest_fork_t * msg = (fd_gossip_restart_heaviest_fork_t *) fd_type_pun( out_buf );
     257           0 :       msg->observed_stake                     = 0;
     258           0 :       msg->last_slot                          = restart->heaviest_fork_slot;
     259           0 :       fd_memcpy( msg->last_slot_hash.hash, restart->heaviest_fork_bank_hash.hash, sizeof(fd_hash_t) );
     260             : 
     261           0 :       restart->stage = FD_RESTART_STAGE_GENERATE_SNAPSHOT;
     262             : 
     263             :       /* Generate a full snapshot since we started wen-restart with a funk file instead of a snapshot file */
     264           0 :       ulong updated_fseq = fd_batch_fseq_pack( 1, 0, restart->heaviest_fork_slot );
     265           0 :       fd_fseq_update( is_constipated, updated_fseq );
     266             : 
     267             :       /* Calculate the new shred version after inserting a hard fork */
     268           0 :       fd_sha256_t _sha[ 1 ];  fd_sha256_t * sha = fd_sha256_join( fd_sha256_new( _sha ) );
     269           0 :       fd_sha256_init( sha );
     270           0 :       fd_sha256_append( sha, genesis_hash->hash, sizeof(fd_pubkey_t) );
     271           0 :       for( ulong i=0; i<hard_forks_len; i++ )
     272           0 :         fd_sha256_append( sha, hard_forks+i, sizeof(fd_slot_pair_t) );
     273             : 
     274           0 :       union {
     275           0 :         uchar  c[ 32 ];
     276           0 :         ushort s[ 16 ];
     277           0 :       } hash;
     278           0 :       fd_sha256_fini( sha, hash.c );
     279           0 :       fd_sha256_delete( fd_sha256_leave( sha ) );
     280             : 
     281           0 :       ushort xor = 0;
     282           0 :       for( ulong i=0UL; i<16UL; i++ ) xor ^= hash.s[ i ];
     283           0 :       xor = fd_ushort_bswap( xor );
     284           0 :       ushort new_shred_version = fd_ushort_if( xor<USHORT_MAX, (ushort)(xor + 1), USHORT_MAX );
     285             : 
     286           0 :       FD_LOG_WARNING(( "Wen-restart succeeds with slot=%lu, bank hash=%s, shred version=%u",
     287           0 :                        restart->heaviest_fork_slot, FD_BASE58_ENC_32_ALLOCA( &restart->heaviest_fork_bank_hash ), new_shred_version ));
     288             : 
     289           0 :       restart->stage = FD_RESTART_STAGE_DONE;
     290           0 :     }
     291           0 :   }
     292           0 : }
     293             : 
     294             : void
     295             : fd_restart_convert_runlength_to_raw_bitmap( fd_gossip_restart_last_voted_fork_slots_t * msg,
     296             :                                             uchar * out_bitmap,
     297           0 :                                             ulong * out_bitmap_len ) {
     298           0 :   ulong bit_cnt   = 0;
     299           0 :   *out_bitmap_len = 0;
     300           0 :   fd_memset( out_bitmap, 0, FD_RESTART_RAW_BITMAP_BYTES_MAX );
     301             : 
     302           0 :   for ( ulong i=0, bit=1; i<msg->offsets.inner.run_length_encoding.offsets_len; i++ ) {
     303           0 :     ushort cnt = msg->offsets.inner.run_length_encoding.offsets[i].bits;
     304           0 :     if( bit ) {
     305           0 :       for ( ulong pos=bit_cnt; pos<bit_cnt+cnt; pos++ ) {
     306           0 :         if( FD_UNLIKELY( pos/BITS_PER_UCHAR>=FD_RESTART_RAW_BITMAP_BYTES_MAX ) ) {
     307             :           /* Invalid message triggering a buffer overflow */
     308           0 :           *out_bitmap_len = FD_RESTART_RAW_BITMAP_BYTES_MAX+1;
     309           0 :           return;
     310           0 :         }
     311           0 :         out_bitmap[pos/BITS_PER_UCHAR] = fd_uchar_set_bit( out_bitmap[pos/BITS_PER_UCHAR], pos%BITS_PER_UCHAR );
     312           0 :       }
     313           0 :     }
     314           0 :     bit            ^= 1;
     315           0 :     bit_cnt        += cnt;
     316           0 :     *out_bitmap_len = (bit_cnt-1)/BITS_PER_UCHAR+1;
     317           0 :   }
     318           0 :   msg->offsets.discriminant                            = fd_restart_slots_offsets_enum_raw_offsets;
     319           0 :   msg->offsets.inner.raw_offsets.offsets.has_bits      = 1;
     320           0 :   msg->offsets.inner.raw_offsets.offsets.len           = bit_cnt;
     321           0 :   msg->offsets.inner.raw_offsets.offsets.bits.bits_len = *out_bitmap_len;
     322           0 : }
     323             : 
     324             : void
     325             : fd_restart_convert_raw_bitmap_to_runlength( fd_gossip_restart_last_voted_fork_slots_t * msg,
     326           0 :                                             fd_restart_run_length_encoding_inner_t * out_encoding ) {
     327           0 :   ushort cnt         = 0;
     328           0 :   int    last_bit    = 1;
     329           0 :   ulong  offsets_len = 0;
     330           0 :   for( ulong raw_bitmap_iter=0;
     331           0 :        raw_bitmap_iter<msg->offsets.inner.raw_offsets.offsets.len &&
     332           0 :        offsets_len<FD_RESTART_PACKET_BITMAP_BYTES_MAX/sizeof(ushort);
     333           0 :        raw_bitmap_iter++ ) {
     334           0 :     ulong idx = raw_bitmap_iter/BITS_PER_UCHAR;
     335           0 :     int   off = raw_bitmap_iter%BITS_PER_UCHAR;
     336           0 :     int   bit = fd_uchar_extract_bit( msg->offsets.inner.raw_offsets.offsets.bits.bits[idx], off );
     337           0 :     if( FD_LIKELY( bit==last_bit ) ) {
     338           0 :       cnt++;
     339           0 :     } else {
     340           0 :       out_encoding[offsets_len++].bits = cnt;
     341           0 :       cnt                              = 1;
     342           0 :       last_bit                         = bit;
     343           0 :     }
     344           0 :   }
     345           0 :   out_encoding[offsets_len++].bits = cnt;
     346             : 
     347           0 :   msg->offsets.discriminant                          = fd_restart_slots_offsets_enum_run_length_encoding;
     348           0 :   msg->offsets.inner.run_length_encoding.offsets_len = offsets_len;
     349           0 :   msg->offsets.inner.run_length_encoding.offsets     = out_encoding;
     350           0 : }
     351             : 
     352             : void
     353             : fd_restart_init( fd_restart_t *              restart,
     354             :                  ulong                       funk_root,
     355             :                  fd_hash_t *                 root_bank_hash,
     356             :                  fd_vote_accounts_t const ** epoch_stakes,
     357             :                  fd_epoch_schedule_t *       epoch_schedule,
     358             :                  int                         tower_checkpt_fileno,
     359             :                  fd_slot_history_t const *   slot_history,
     360             :                  fd_pubkey_t *               my_pubkey,
     361             :                  fd_pubkey_t *               coordinator_pubkey,
     362             :                  uchar *                     out_buf,
     363             :                  ulong *                     out_buf_len,
     364           0 :                  fd_spad_t *                 runtime_spad ) {
     365           0 :   restart->funk_root                       = funk_root;
     366           0 :   restart->epoch_schedule                  = epoch_schedule;
     367           0 :   restart->root_epoch                      = fd_slot_to_epoch( epoch_schedule, restart->funk_root, NULL ),
     368           0 :   restart->stage                           = FD_RESTART_STAGE_FIND_HEAVIEST_FORK_SLOT_NUM;
     369           0 :   restart->heaviest_fork_ready             = 0;
     370           0 :   restart->coordinator_heaviest_fork_ready = 0;
     371           0 :   fd_memcpy( restart->root_bank_hash.hash, root_bank_hash, sizeof(fd_pubkey_t) );
     372           0 :   fd_memcpy( restart->my_pubkey.key, my_pubkey, sizeof(fd_pubkey_t) );
     373           0 :   fd_memcpy( restart->coordinator_pubkey.key, coordinator_pubkey, sizeof(fd_pubkey_t) );
     374           0 :   fd_memset( restart->slot_to_stake, 0, sizeof(restart->slot_to_stake) );
     375           0 :   fd_memset( restart->last_voted_fork_slots_received, 0, sizeof(restart->last_voted_fork_slots_received) );
     376           0 :   FD_LOG_WARNING(( "[%s]\nfunk root=%lu\nroot epoch=%lu\nroot_bank_hash=%s\ncoordinator pubkey: %s\nMy pubkey: %s",
     377           0 :                     __func__,
     378           0 :                     restart->funk_root,
     379           0 :                     restart->root_epoch,
     380           0 :                     FD_BASE58_ENC_32_ALLOCA( &restart->root_bank_hash ),
     381           0 :                     FD_BASE58_ENC_32_ALLOCA( &restart->coordinator_pubkey ),
     382           0 :                     FD_BASE58_ENC_32_ALLOCA( &restart->my_pubkey ) ));
     383             : 
     384             :   /* Save the vote accounts stake information for the MAX_EPOCH epochs */
     385           0 :   FD_TEST( FD_RESTART_EPOCHS_MAX==2 );
     386           0 :   for( ulong e=0; e<FD_RESTART_EPOCHS_MAX; e++ ) {
     387           0 :     if( epoch_stakes[e]->vote_accounts_root==NULL ) FD_LOG_ERR(( "vote account information is missing for epoch#%lu", restart->root_epoch+e ));
     388           0 :     restart->num_vote_accts[e]                 = fd_stake_weights_by_node( epoch_stakes[e], restart->stake_weights[e], runtime_spad );
     389           0 :     restart->total_stake[e]                    = 0;
     390           0 :     restart->total_stake_received[e]           = 0;
     391           0 :     restart->total_stake_received_and_voted[e] = 0;
     392           0 :     FD_TEST( restart->num_vote_accts[e]<=FD_RESTART_MAX_PEERS );
     393             : 
     394           0 :     for( ulong i=0; i<restart->num_vote_accts[e]; i++ ) {
     395           0 :       FD_LOG_DEBUG(( "Epoch#%lu voter %s holds stake amount=%lu",
     396           0 :                       restart->root_epoch+e,
     397           0 :                       FD_BASE58_ENC_32_ALLOCA( &restart->stake_weights[e][i].key ),
     398           0 :                       restart->stake_weights[e][i].stake ));
     399           0 :       restart->total_stake[e] += restart->stake_weights[e][i].stake;
     400           0 :     }
     401           0 :     FD_LOG_NOTICE(( "[%s] There are %lu staked voters in epoch#%lu with total stake %lu",
     402           0 :                     __func__, restart->num_vote_accts[e], restart->root_epoch+e, restart->total_stake[e] ));
     403           0 :   }
     404             : 
     405             :   /* Get the last_voted_slot and its bank hash from the tower checkpoint file */
     406           0 :   fd_hash_t tower_bank_hash;
     407           0 :   ulong rsz, tower_height, tower_root, tower_slots[ FD_TOWER_VOTE_MAX+1 ];
     408             : 
     409           0 :   FD_TEST( 0==fd_io_read( tower_checkpt_fileno, &tower_bank_hash, sizeof(fd_hash_t), sizeof(fd_hash_t), &rsz ) );
     410           0 :   FD_TEST( rsz==sizeof(fd_hash_t) );
     411           0 :   FD_TEST( 0==fd_io_read( tower_checkpt_fileno, &tower_height, sizeof(ulong), sizeof(ulong), &rsz ) );
     412           0 :   FD_TEST( rsz==sizeof(ulong) );
     413           0 :   FD_TEST( tower_height<=FD_TOWER_VOTE_MAX );
     414           0 :   FD_TEST( 0==fd_io_read( tower_checkpt_fileno, &tower_root, sizeof(ulong), sizeof(ulong), &rsz ) );
     415           0 :   FD_TEST( rsz==sizeof(ulong) );
     416           0 :   ulong tower_slots_sz = sizeof(ulong)*tower_height;
     417           0 :   FD_TEST( 0==fd_io_read( tower_checkpt_fileno, tower_slots, tower_slots_sz, tower_slots_sz, &rsz ) );
     418           0 :   FD_TEST( rsz==tower_slots_sz );
     419             : 
     420           0 :   fd_gossip_restart_last_voted_fork_slots_t * msg = (fd_gossip_restart_last_voted_fork_slots_t *) fd_type_pun( out_buf );
     421           0 :   msg->last_voted_slot = tower_slots[tower_height-1];
     422           0 :   fd_memcpy( msg->last_voted_hash.hash, tower_bank_hash.hash, sizeof(fd_hash_t) );
     423             : 
     424             :   /* Given last_voted_slot, get the bitmap for the last_voted_fork_slots gossip message */
     425           0 :   ulong end_slot   = msg->last_voted_slot;
     426           0 :   ulong start_slot = ( end_slot>FD_RESTART_LAST_VOTED_FORK_MAX_SLOTS? end_slot-FD_RESTART_LAST_VOTED_FORK_MAX_SLOTS : 0 );
     427           0 :   ulong num_slots  = end_slot-start_slot+1;
     428           0 :   uchar * bitmap   = out_buf+sizeof(fd_gossip_restart_last_voted_fork_slots_t);
     429           0 :   fd_memset( bitmap, 0,  num_slots/BITS_PER_UCHAR+1 );
     430           0 :   msg->offsets.discriminant                            = fd_restart_slots_offsets_enum_raw_offsets;
     431           0 :   msg->offsets.inner.raw_offsets.offsets.has_bits      = 1;
     432           0 :   msg->offsets.inner.raw_offsets.offsets.len           = num_slots;
     433           0 :   msg->offsets.inner.raw_offsets.offsets.bits.bits     = bitmap;
     434           0 :   msg->offsets.inner.raw_offsets.offsets.bits.bits_len = ( num_slots-1 )/BITS_PER_UCHAR+1;
     435           0 :   *out_buf_len = sizeof(fd_gossip_restart_last_voted_fork_slots_t)+( num_slots-1 )/BITS_PER_UCHAR+1;
     436           0 :   FD_LOG_NOTICE(( "[%s] last_voted_slot=%lu, bank_hash=%s, encoding %lu bits in bitmap",
     437           0 :                   __func__, msg->last_voted_slot, FD_BASE58_ENC_32_ALLOCA( &tower_bank_hash ), num_slots ));
     438             : 
     439             :   /* Encode slots from the tower checkpoint into the bitmap */
     440           0 :   ulong checkpt_ghost_root=ULONG_MAX;
     441           0 :   while(1) {
     442           0 :     ulong slot;
     443           0 :     FD_TEST( 0==fd_io_read( tower_checkpt_fileno, &slot, sizeof(ulong), sizeof(ulong), &rsz ) );
     444           0 :     if( FD_UNLIKELY( slot==ULONG_MAX ) ) break;
     445           0 :     checkpt_ghost_root = slot;
     446             : 
     447           0 :     ulong offset_from_end = end_slot-slot;
     448           0 :     ulong out_idx         = offset_from_end/BITS_PER_UCHAR;
     449           0 :     int   out_bit_off     = offset_from_end%BITS_PER_UCHAR;
     450           0 :     bitmap[out_idx]       = fd_uchar_set_bit( bitmap[out_idx], out_bit_off );
     451           0 :   }
     452             : 
     453             :   /* Encode slots from the slot_history system program into the bitmap */
     454           0 :   if( FD_UNLIKELY( checkpt_ghost_root>slot_history->next_slot ) ) {
     455           0 :     FD_LOG_WARNING(( "You may be loading a wrong snapshot in funk.\n \
     456           0 :                       We expect checkpointed ghost root(%lu) <= slot_history->next_slot(%lu)", checkpt_ghost_root, slot_history->next_slot ));
     457           0 :   }
     458             : 
     459           0 :   for( ulong i=start_slot; i<slot_history->next_slot; i++ ) {
     460           0 :     ulong in_idx          = ( i/BITS_PER_ULONG )%( slot_history->bits.bits->blocks_len );
     461           0 :     int   in_bit_off      = i%BITS_PER_ULONG;
     462             : 
     463           0 :     ulong offset_from_end = end_slot-i;
     464           0 :     ulong out_idx         = offset_from_end/BITS_PER_UCHAR;
     465           0 :     int   out_bit_off     = offset_from_end%BITS_PER_UCHAR;
     466             : 
     467           0 :     if( FD_LIKELY( fd_ulong_extract_bit( slot_history->bits.bits->blocks[in_idx], in_bit_off ) ) ) {
     468             :       /* bit#i in slot_history is 1 */
     469           0 :       bitmap[out_idx] = fd_uchar_set_bit( bitmap[out_idx], out_bit_off );
     470           0 :     }
     471           0 :   }
     472             : 
     473           0 :   ulong found = 0;
     474           0 :   fd_memcpy( msg->from.key, my_pubkey->key, sizeof(fd_pubkey_t) );
     475           0 :   fd_restart_recv_last_voted_fork_slots( restart, msg, &found );
     476           0 :   if( FD_UNLIKELY( found ) ) FD_LOG_WARNING(( "[%s] It seems that this single validator alone has >80%% stake", __func__ ));
     477           0 : }
     478             : 
     479             : void
     480             : fd_restart_tower_checkpt( fd_hash_t const * vote_bank_hash,
     481             :                           fd_tower_t * tower,
     482             :                           fd_ghost_t * ghost,
     483             :                           ulong root,
     484           0 :                           int tower_checkpt_fileno ) {
     485           0 :   lseek( tower_checkpt_fileno, 0, SEEK_SET );
     486           0 :   ulong wsz;
     487           0 :   ulong total_wsz           = 0;
     488           0 :   ulong checkpt_history_len = 0;
     489           0 :   ulong tower_height        = fd_tower_votes_cnt( tower );
     490           0 :   FD_TEST( tower_height>0 );
     491             : 
     492             :   /* Checkpoint the tower */
     493           0 :   fd_io_write( tower_checkpt_fileno, vote_bank_hash, sizeof(fd_hash_t), sizeof(fd_hash_t), &wsz );
     494           0 :   if( FD_UNLIKELY( wsz!=sizeof(fd_hash_t) ) ) goto checkpt_finish;
     495           0 :   total_wsz += wsz;
     496           0 :   fd_io_write( tower_checkpt_fileno, &tower_height, sizeof(ulong), sizeof(ulong), &wsz );
     497           0 :   if( FD_UNLIKELY( wsz!=sizeof(ulong) ) ) goto checkpt_finish;
     498           0 :   total_wsz += wsz;
     499           0 :   fd_io_write( tower_checkpt_fileno, &root, sizeof(ulong), sizeof(ulong), &wsz );
     500           0 :   if( FD_UNLIKELY( wsz!=sizeof(ulong) ) ) goto checkpt_finish;
     501           0 :   total_wsz += wsz;
     502             : 
     503           0 :   ulong last_voted_slot = ULONG_MAX;
     504           0 :   for( fd_tower_votes_iter_t tower_iter = fd_tower_votes_iter_init( tower );
     505           0 :        !fd_tower_votes_iter_done( tower, tower_iter );
     506           0 :        tower_iter = fd_tower_votes_iter_next( tower, tower_iter ) ) {
     507           0 :     ulong slot = fd_tower_votes_iter_ele( tower, tower_iter )->slot;
     508           0 :     fd_io_write( tower_checkpt_fileno, &slot, sizeof(ulong), sizeof(ulong), &wsz );
     509           0 :     if( FD_UNLIKELY( wsz!=sizeof(ulong) ) ) goto checkpt_finish;
     510           0 :     total_wsz += wsz;
     511             : 
     512           0 :     last_voted_slot = slot;
     513           0 :   }
     514             : 
     515             :   /* Checkpoint the vote slot history */
     516           0 :   fd_ghost_node_map_t * node_map   = fd_ghost_node_map( ghost );
     517           0 :   fd_ghost_node_t *     node_pool  = fd_ghost_node_pool( ghost );
     518           0 :   fd_ghost_node_t *     node       = fd_ghost_node_map_ele_query( node_map, &last_voted_slot, NULL, node_pool );
     519           0 :   ulong                 ghost_root = fd_ghost_node_pool_ele_const( node_pool, ghost->root_idx )->slot;
     520             : 
     521           0 :   FD_TEST( node!=NULL );
     522           0 :   while( node->slot != ghost_root ) {
     523           0 :     fd_io_write( tower_checkpt_fileno, &node->slot, sizeof(ulong), sizeof(ulong), &wsz );
     524           0 :     if( FD_UNLIKELY( wsz!=sizeof(ulong) ) ) goto checkpt_finish;
     525           0 :     total_wsz += wsz;
     526           0 :     checkpt_history_len++;
     527             : 
     528           0 :     node = fd_ghost_node_pool_ele( node_pool, node->parent_idx );
     529           0 :   }
     530             : 
     531             :   /* Checkpoint the ghost root */
     532           0 :   fd_io_write( tower_checkpt_fileno, &node->slot, sizeof(ulong), sizeof(ulong), &wsz );
     533           0 :   if( FD_UNLIKELY( wsz!=sizeof(ulong) ) ) goto checkpt_finish;
     534           0 :   total_wsz += wsz;
     535           0 :   checkpt_history_len++;
     536             : 
     537             :   /* Mark the end of slot history */
     538           0 :   ulong end = ULONG_MAX;
     539           0 :   fd_io_write( tower_checkpt_fileno, &end, sizeof(ulong), sizeof(ulong), &wsz );
     540           0 :   if( FD_UNLIKELY( wsz!=sizeof(ulong) ) ) goto checkpt_finish;
     541           0 :   total_wsz += wsz;
     542           0 :   checkpt_history_len++;
     543             : 
     544             :   /* Truncate and flush the checkpoint file */
     545           0 :   fd_io_truncate( tower_checkpt_fileno, total_wsz );
     546           0 :   fsync( tower_checkpt_fileno );
     547           0 : checkpt_finish:
     548           0 :   if( FD_UNLIKELY( total_wsz!=sizeof(fd_hash_t)+sizeof(ulong)*( 2+tower_height+checkpt_history_len ) ) ) {
     549           0 :     FD_LOG_WARNING(( "Failed at checkpointing tower" ));
     550           0 :   }
     551           0 : }

Generated by: LCOV version 1.14