LCOV - code coverage report
Current view: top level - flamenco/stakes - fd_stakes_from_snapshot.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 108 0.0 %
Date: 2025-03-20 12:08:36 Functions: 0 7 0.0 %

          Line data    Source code
       1             : #define FD_SCRATCH_USE_HANDHOLDING 1
       2             : #include "../fd_flamenco.h"
       3             : #include "../../ballet/base58/fd_base58.h"
       4             : #include "../types/fd_types.h"
       5             : #include "../leaders/fd_leaders.h"
       6             : #include "../runtime/sysvar/fd_sysvar_epoch_schedule.h"
       7             : #include "fd_stakes.h"
       8             : 
       9             : #include <errno.h>
      10             : #include <stdio.h>
      11             : #include <stdlib.h>
      12             : #include <sys/stat.h>
      13             : 
      14             : static int
      15           0 : usage( void ) {
      16           0 :   fprintf( stderr,
      17           0 :     "usage: fd_stakes_from_snapshot {nodes/leaders} {FILE}\n"
      18           0 :     "\n"
      19           0 :     "Derive epoch stake information from snapshot.\n"
      20           0 :     "\n"
      21           0 :     "Mode:\n"
      22           0 :     "  epochs     Print available epochs\n"
      23           0 :     "  nodes      Dump active stake per node identity\n"
      24           0 :     "             CSV format: {pubkey},{stake}\n"
      25           0 :     "  leaders    Dump leader schedule\n"
      26           0 :     "             CSV format: {slot},{pubkey}\n"
      27           0 :     "\n"
      28           0 :     "FILE is the file path to a .tar.zst snapshot or a raw\n"
      29           0 :     "  bincode snapshot manifest\n"
      30           0 :     "\n"
      31           0 :     "Options:\n"
      32           0 :     "\n"
      33           0 :     "  --page-sz      {gigantic|huge|normal}    Page size\n"
      34           0 :     "  --page-cnt     2                         Page count\n"
      35           0 :     "  --scratch-mb   1024                      Scratch mem MiB\n"
      36           0 :     "  --epoch        <ulong>                   Epoch number\n" );
      37           0 :   return 0;
      38           0 : }
      39             : 
      40             : #define ACTION_NODES   (0)
      41             : #define ACTION_LEADERS (1)
      42             : #define ACTION_EPOCHS  (2)
      43             : 
      44             : /* _find_epoch looks for epoch stakes for the requested epoch.
      45             :    On failure, logs error and aborts application. */
      46             : 
      47             : static fd_epoch_stakes_t const *
      48             : _find_epoch( fd_solana_manifest_t const * manifest,
      49           0 :              ulong                        epoch ) {
      50             : 
      51             : 
      52           0 :   if( FD_UNLIKELY( epoch==ULONG_MAX ) ) {
      53           0 :     fprintf( stderr, "error: missing --epoch\n" );
      54           0 :     usage();
      55           0 :     exit(1);
      56           0 :   }
      57             : 
      58           0 :   fd_epoch_stakes_t const * stakes = NULL;
      59           0 :   fd_epoch_epoch_stakes_pair_t const * epochs = manifest->bank.epoch_stakes;
      60           0 :   for( ulong i=0; i < manifest->bank.epoch_stakes_len; i++ ) {
      61           0 :     if( epochs[ i ].key==epoch ) {
      62           0 :       stakes = &epochs[i].value;
      63           0 :       break;
      64           0 :     }
      65           0 :   }
      66           0 :   if( FD_UNLIKELY( !stakes ) )
      67           0 :     FD_LOG_ERR(( "Snapshot missing EpochStakes for epoch %lu", epoch ));
      68             : 
      69           0 :   return stakes;
      70           0 : }
      71             : 
      72             : static fd_stake_weight_t *
      73             : _get_stake_weights( fd_solana_manifest_t const * manifest,
      74             :                     ulong                        epoch,
      75           0 :                     ulong *                      out_cnt ) {
      76             : 
      77           0 :   fd_epoch_stakes_t  const * stakes = _find_epoch( manifest, epoch );
      78           0 :   fd_vote_accounts_t const * vaccs = &stakes->stakes.vote_accounts;
      79             : 
      80           0 :   ulong vote_acc_cnt = fd_vote_accounts_pair_t_map_size( vaccs->vote_accounts_pool, vaccs->vote_accounts_root );
      81           0 :   FD_LOG_NOTICE(( "vote_acc_cnt=%lu", vote_acc_cnt ));
      82           0 :   fd_stake_weight_t * weights = fd_scratch_alloc( alignof(fd_stake_weight_t), vote_acc_cnt * sizeof(fd_stake_weight_t) );
      83           0 :   if( FD_UNLIKELY( !weights ) ) FD_LOG_ERR(( "fd_scratch_alloc() failed" ));
      84             : 
      85             :   /* FIXME: This will crash because the spad is not set up. */
      86           0 :   ulong weight_cnt = fd_stake_weights_by_node( vaccs, weights, NULL );
      87           0 :   if( FD_UNLIKELY( weight_cnt==ULONG_MAX ) ) FD_LOG_ERR(( "fd_stake_weights_by_node() failed" ));
      88             : 
      89           0 :   *out_cnt = weight_cnt;
      90           0 :   return weights;
      91           0 : }
      92             : 
      93             : static int
      94           0 : action_epochs( fd_solana_manifest_t const * manifest ) {
      95           0 :   fd_epoch_epoch_stakes_pair_t const * epochs = manifest->bank.epoch_stakes;
      96           0 :   for( ulong i=0; i < manifest->bank.epoch_stakes_len; i++ )
      97           0 :     printf( "%lu\n", epochs[ i ].key );
      98           0 :   return 0;
      99           0 : }
     100             : 
     101             : static int
     102             : action_nodes( fd_solana_manifest_t const * manifest,
     103           0 :               ulong                        epoch ) {
     104             : 
     105           0 :   ulong               weight_cnt;
     106           0 :   fd_stake_weight_t * weights = _get_stake_weights( manifest, epoch, &weight_cnt );
     107             : 
     108           0 :   for( ulong i=0UL; i<weight_cnt; i++ ) {
     109           0 :     fd_stake_weight_t const * w = weights + i;
     110           0 :     char keyB58[ FD_BASE58_ENCODED_32_SZ ];
     111           0 :     fd_base58_encode_32( w->key.key, NULL,  keyB58 );
     112           0 :     printf( "%s,%lu\n", keyB58, w->stake );
     113           0 :   }
     114             : 
     115           0 :   return 0;
     116           0 : }
     117             : 
     118             : static int
     119             : action_leaders( fd_solana_manifest_t const * manifest,
     120           0 :                 ulong                        epoch ) {
     121             : 
     122           0 :   ulong               weight_cnt;
     123           0 :   fd_stake_weight_t * weights = _get_stake_weights( manifest, epoch, &weight_cnt );
     124             : 
     125           0 :   fd_epoch_schedule_t const * sched = &manifest->bank.epoch_schedule;
     126           0 :   ulong slot0     = fd_epoch_slot0   ( sched, epoch );
     127           0 :   ulong slot_cnt  = fd_epoch_slot_cnt( sched, epoch );
     128           0 :   ulong sched_cnt = slot_cnt/FD_EPOCH_SLOTS_PER_ROTATION;
     129             : 
     130           0 :   void * leaders_mem = fd_scratch_alloc( fd_epoch_leaders_align(), fd_epoch_leaders_footprint( weight_cnt, sched_cnt ) );
     131           0 :          leaders_mem = fd_epoch_leaders_new( leaders_mem, epoch, slot0, slot_cnt, weight_cnt, weights, 0UL );
     132           0 :   fd_epoch_leaders_t * leaders = fd_epoch_leaders_join( leaders_mem );
     133           0 :   FD_TEST( leaders );
     134             : 
     135           0 :   ulong slot = slot0;
     136           0 :   for( ulong i=0; i<sched_cnt; i++ ) {
     137           0 :     fd_pubkey_t const * leader = fd_epoch_leaders_get( leaders, slot );
     138           0 :     char keyB58[ FD_BASE58_ENCODED_32_SZ ];
     139           0 :     fd_base58_encode_32( leader->key, NULL, keyB58 );
     140           0 :     for( ulong j=0; j<FD_EPOCH_SLOTS_PER_ROTATION; j++ ) {
     141           0 :       printf( "%lu,%s\n", slot, keyB58 );
     142           0 :       slot++;
     143           0 :     }
     144           0 :   }
     145             : 
     146           0 :   return 0;
     147           0 : }
     148             : 
     149             : /* _is_zstd returns 1 if given file handle points to the beginning of a
     150             :     zstd stream, otherwise zero. */
     151             : 
     152             : static int
     153           0 : _is_zstd( FILE * file ) {
     154           0 :   uint magic;
     155           0 :   ulong n = fread( &magic, 1UL, 4UL, file );
     156           0 :   if( FD_UNLIKELY( feof( file ) ) ) {
     157           0 :     clearerr( file );
     158           0 :     fseek( file, -(long)n, SEEK_CUR );
     159           0 :     return 0;
     160           0 :   }
     161           0 :   int err = ferror( file );
     162           0 :   if( FD_UNLIKELY( err ) )
     163           0 :     FD_LOG_ERR(( "fread() failed (%d-%s)", err, strerror( err ) ));
     164           0 :   fseek( file, -4L, SEEK_CUR );
     165           0 :   return ( magic==0xFD2FB528UL );
     166           0 : }
     167             : 
     168             : /* TODO older Solana snapshots are gzip, add support */
     169             : 
     170             : int
     171             : main( int     argc,
     172             :       char ** argv ) {
     173             :   fd_boot( &argc, &argv );
     174             :   fd_flamenco_boot( &argc, &argv );
     175             : 
     176             :   for( int i=1; i<argc; i++ )
     177             :     if( 0==strcmp( argv[i], "--help" ) ) return usage();
     178             : 
     179             :   char const * _page_sz   = fd_env_strip_cmdline_cstr ( &argc, &argv, "--page-sz",    NULL, "gigantic" );
     180             :   ulong        page_cnt   = fd_env_strip_cmdline_ulong( &argc, &argv, "--page-cnt",   NULL, 2UL        );
     181             :   ulong        scratch_mb = fd_env_strip_cmdline_ulong( &argc, &argv, "--scratch-mb", NULL, 1024UL     );
     182             :   ulong        epoch      = fd_env_strip_cmdline_ulong( &argc, &argv, "--epoch",      NULL, ULONG_MAX  );
     183             : 
     184             :   ulong page_sz = fd_cstr_to_shmem_page_sz( _page_sz );
     185             :   if( FD_UNLIKELY( !page_sz ) ) FD_LOG_ERR(( "unsupported --page-sz" ));
     186             : 
     187             :   if( FD_UNLIKELY( argc!=3 ) ) {
     188             :     fprintf( stderr, "error: missing arguments\n" );
     189             :     return usage();
     190             :   }
     191             : 
     192             :   char const * mode     = argv[1];
     193             :   char const * filepath = argv[2];
     194             : 
     195             :   int action;
     196             :   /**/ if( 0==strcmp( mode, "epochs"  ) ) action = ACTION_EPOCHS;
     197             :   else if( 0==strcmp( mode, "nodes"   ) ) action = ACTION_NODES;
     198             :   else if( 0==strcmp( mode, "leaders" ) ) action = ACTION_LEADERS;
     199             :   else {
     200             :     fprintf( stderr, "error: invalid mode \"%s\"\n", mode );
     201             :     return usage();
     202             :   }
     203             : 
     204             :   /* Create workspace and scratch allocator */
     205             : 
     206             :   fd_wksp_t * wksp = fd_wksp_new_anonymous( page_sz, page_cnt, fd_log_cpu_id(), "wksp", 0UL );
     207             :   if( FD_UNLIKELY( !wksp ) ) FD_LOG_ERR(( "fd_wksp_new_anonymous() failed" ));
     208             : 
     209             :   ulong  smax = scratch_mb << 20;
     210             :   void * smem = fd_wksp_alloc_laddr( wksp, fd_scratch_smem_align(), smax, 1UL );
     211             :   if( FD_UNLIKELY( !smem ) ) FD_LOG_ERR(( "Failed to alloc scratch mem" ));
     212             :   ulong  scratch_depth = 4UL;
     213             :   void * fmem = fd_wksp_alloc_laddr( wksp, fd_scratch_fmem_align(), fd_scratch_fmem_footprint( scratch_depth ), 2UL );
     214             :   if( FD_UNLIKELY( !fmem ) ) FD_LOG_ERR(( "Failed to alloc scratch frames" ));
     215             : 
     216             :   fd_scratch_attach( smem, fmem, smax, scratch_depth );
     217             :   fd_scratch_push();
     218             : 
     219             :   /* Open file */
     220             : 
     221             :   FD_LOG_INFO(( "Reading snapshot %s", filepath ));
     222             :   FILE * file = fopen( filepath, "rb" );
     223             :   if( FD_UNLIKELY( !file ) ) FD_LOG_ERR(( "fopen(%s) failed (%d-%s)", filepath, errno, strerror( errno ) ));
     224             : 
     225             :   /* Read manifest bytes */
     226             : 
     227             :   void * manifest_bin;
     228             :   ulong  manifest_binsz;
     229             :   if( _is_zstd( file ) ) {
     230             :     FD_LOG_NOTICE(( "Detected .tar.zst stream" ));
     231             :     FD_LOG_ERR(( "TODO" ));
     232             :   } else {
     233             :     FD_LOG_NOTICE(( "Assuming raw bincode file" ));
     234             : 
     235             :     /* Allocate buffer suitable for storing file */
     236             : 
     237             :     struct stat stat;
     238             :     int err = fstat( fileno( file ), &stat );
     239             :     if( FD_UNLIKELY( err ) ) FD_LOG_ERR(( "fstat() failed (%d-%s)", errno, strerror( errno ) ));
     240             :     manifest_binsz = (ulong)stat.st_size;
     241             : 
     242             :     manifest_bin = fd_wksp_alloc_laddr( wksp, 1UL, manifest_binsz, 3UL );
     243             :     if( FD_UNLIKELY( !manifest_bin ) ) FD_LOG_ERR(( "failed to allocate metadata buffer" ));
     244             : 
     245             :     /* Read file into buffer */
     246             : 
     247             :     ulong n = fread( manifest_bin, manifest_binsz, 1UL, file );
     248             :     if( FD_UNLIKELY( n!=1UL ) ) FD_LOG_ERR(( "fread() failed (eof=%d)", feof( file ) ));
     249             :     FD_LOG_NOTICE(( "Read manifest (%.3f MiB)", (double)manifest_binsz/(1UL<<20) ));
     250             :   }
     251             : 
     252             :   /* Deserialize manifest */
     253             : 
     254             :   long dt = -fd_log_wallclock();
     255             : 
     256             :   fd_bincode_decode_ctx_t decode = {
     257             :     .data    = manifest_bin,
     258             :     .dataend = (void *)( (ulong)manifest_bin + manifest_binsz ),
     259             :   };
     260             : 
     261             :   ulong total_sz   = 0UL;
     262             :   int   decode_err = fd_solana_manifest_decode_footprint( &decode, &total_sz );
     263             :   if( FD_UNLIKELY( decode_err ) ) {
     264             :     FD_LOG_ERR(( "Failed to decode manifest" ));
     265             :   }
     266             : 
     267             :   uchar * mem = fd_scratch_alloc( alignof(fd_solana_manifest_t), total_sz );
     268             :   if( FD_UNLIKELY( !mem ) ) {
     269             :     FD_LOG_ERR(( "Unable to allocate memory for manifest" ));
     270             :   }
     271             : 
     272             :   fd_solana_manifest_t * manifest = fd_solana_manifest_decode( mem, &decode );
     273             : 
     274             :   fd_wksp_free_laddr( manifest_bin ); manifest_bin = NULL;
     275             :   dt += fd_log_wallclock();
     276             :   FD_LOG_NOTICE(( "Deserialized manifest (took %.3fs)", (double)dt/1e9 ));
     277             : 
     278             :   /* Action */
     279             : 
     280             :   int res;
     281             :   fd_scratch_push();
     282             :   switch( action ) {
     283             :   case ACTION_LEADERS:
     284             :     res = action_leaders( manifest, epoch );
     285             :     break;
     286             :   case ACTION_NODES:
     287             :     res = action_nodes( manifest, epoch );
     288             :     break;
     289             :   case ACTION_EPOCHS:
     290             :     res = action_epochs( manifest );
     291             :     break;
     292             :   default:
     293             :     __builtin_unreachable();
     294             :   }
     295             :   fd_scratch_pop();
     296             : 
     297             :   /* Cleanup */
     298             : 
     299             :   fd_scratch_pop();
     300             :   FD_TEST( fd_scratch_frame_used()==0UL );
     301             :   fd_wksp_free_laddr( fd_scratch_detach( NULL ) );
     302             :   fd_wksp_free_laddr( fmem                      );
     303             :   fclose( file );
     304             :   fd_wksp_detach( wksp );
     305             :   fd_flamenco_halt();
     306             :   fd_halt();
     307             :   return res;
     308             : }

Generated by: LCOV version 1.14