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

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

Generated by: LCOV version 1.14