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-07-01 05:00:49 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 and no accs
      86             :      are passed in. */
      87             : 
      88           0 :   ulong weight_cnt = fd_stake_weights_by_node( NULL, weights, NULL );
      89           0 :   if( FD_UNLIKELY( weight_cnt==ULONG_MAX ) ) FD_LOG_ERR(( "fd_stake_weights_by_node() failed" ));
      90             : 
      91           0 :   *out_cnt = weight_cnt;
      92           0 :   return weights;
      93           0 : }
      94             : 
      95             : static int
      96           0 : action_epochs( fd_solana_manifest_t const * manifest ) {
      97           0 :   fd_epoch_epoch_stakes_pair_t const * epochs = manifest->bank.epoch_stakes;
      98           0 :   for( ulong i=0; i < manifest->bank.epoch_stakes_len; i++ )
      99           0 :     printf( "%lu\n", epochs[ i ].key );
     100           0 :   return 0;
     101           0 : }
     102             : 
     103             : static int
     104             : action_nodes( fd_solana_manifest_t const * manifest,
     105           0 :               ulong                        epoch ) {
     106             : 
     107           0 :   ulong               weight_cnt;
     108           0 :   fd_stake_weight_t * weights = _get_stake_weights( manifest, epoch, &weight_cnt );
     109             : 
     110           0 :   for( ulong i=0UL; i<weight_cnt; i++ ) {
     111           0 :     fd_stake_weight_t const * w = weights + i;
     112           0 :     char keyB58[ FD_BASE58_ENCODED_32_SZ ];
     113           0 :     fd_base58_encode_32( w->key.key, NULL,  keyB58 );
     114           0 :     printf( "%s,%lu\n", keyB58, w->stake );
     115           0 :   }
     116             : 
     117           0 :   return 0;
     118           0 : }
     119             : 
     120             : static int
     121             : action_leaders( fd_solana_manifest_t const * manifest,
     122           0 :                 ulong                        epoch ) {
     123             : 
     124           0 :   ulong               weight_cnt;
     125           0 :   fd_stake_weight_t * weights = _get_stake_weights( manifest, epoch, &weight_cnt );
     126             : 
     127           0 :   fd_epoch_schedule_t const * sched = &manifest->bank.epoch_schedule;
     128           0 :   ulong slot0     = fd_epoch_slot0   ( sched, epoch );
     129           0 :   ulong slot_cnt  = fd_epoch_slot_cnt( sched, epoch );
     130           0 :   ulong sched_cnt = slot_cnt/FD_EPOCH_SLOTS_PER_ROTATION;
     131             : 
     132           0 :   void * leaders_mem = fd_scratch_alloc( fd_epoch_leaders_align(), fd_epoch_leaders_footprint( weight_cnt, sched_cnt ) );
     133           0 :          leaders_mem = fd_epoch_leaders_new( leaders_mem, epoch, slot0, slot_cnt, weight_cnt, weights, 0UL );
     134           0 :   fd_epoch_leaders_t * leaders = fd_epoch_leaders_join( leaders_mem );
     135           0 :   FD_TEST( leaders );
     136             : 
     137           0 :   ulong slot = slot0;
     138           0 :   for( ulong i=0; i<sched_cnt; i++ ) {
     139           0 :     fd_pubkey_t const * leader = fd_epoch_leaders_get( leaders, slot );
     140           0 :     char keyB58[ FD_BASE58_ENCODED_32_SZ ];
     141           0 :     fd_base58_encode_32( leader->key, NULL, keyB58 );
     142           0 :     for( ulong j=0; j<FD_EPOCH_SLOTS_PER_ROTATION; j++ ) {
     143           0 :       printf( "%lu,%s\n", slot, keyB58 );
     144           0 :       slot++;
     145           0 :     }
     146           0 :   }
     147             : 
     148           0 :   return 0;
     149           0 : }
     150             : 
     151             : /* _is_zstd returns 1 if given file handle points to the beginning of a
     152             :     zstd stream, otherwise zero. */
     153             : 
     154             : static int
     155           0 : _is_zstd( FILE * file ) {
     156           0 :   uint magic;
     157           0 :   ulong n = fread( &magic, 1UL, 4UL, file );
     158           0 :   if( FD_UNLIKELY( feof( file ) ) ) {
     159           0 :     clearerr( file );
     160           0 :     fseek( file, -(long)n, SEEK_CUR );
     161           0 :     return 0;
     162           0 :   }
     163           0 :   int err = ferror( file );
     164           0 :   if( FD_UNLIKELY( err ) )
     165           0 :     FD_LOG_ERR(( "fread() failed (%d-%s)", err, strerror( err ) ));
     166           0 :   fseek( file, -4L, SEEK_CUR );
     167           0 :   return ( magic==0xFD2FB528UL );
     168           0 : }
     169             : 
     170             : /* TODO older Solana snapshots are gzip, add support */
     171             : 
     172             : int
     173             : main( int     argc,
     174             :       char ** argv ) {
     175             :   fd_boot( &argc, &argv );
     176             :   fd_flamenco_boot( &argc, &argv );
     177             : 
     178             :   for( int i=1; i<argc; i++ )
     179             :     if( 0==strcmp( argv[i], "--help" ) ) return usage();
     180             : 
     181             :   char const * _page_sz   = fd_env_strip_cmdline_cstr ( &argc, &argv, "--page-sz",    NULL, "gigantic" );
     182             :   ulong        page_cnt   = fd_env_strip_cmdline_ulong( &argc, &argv, "--page-cnt",   NULL, 2UL        );
     183             :   ulong        scratch_mb = fd_env_strip_cmdline_ulong( &argc, &argv, "--scratch-mb", NULL, 1024UL     );
     184             :   ulong        epoch      = fd_env_strip_cmdline_ulong( &argc, &argv, "--epoch",      NULL, ULONG_MAX  );
     185             : 
     186             :   ulong page_sz = fd_cstr_to_shmem_page_sz( _page_sz );
     187             :   if( FD_UNLIKELY( !page_sz ) ) FD_LOG_ERR(( "unsupported --page-sz" ));
     188             : 
     189             :   if( FD_UNLIKELY( argc!=3 ) ) {
     190             :     fprintf( stderr, "error: missing arguments\n" );
     191             :     return usage();
     192             :   }
     193             : 
     194             :   char const * mode     = argv[1];
     195             :   char const * filepath = argv[2];
     196             : 
     197             :   int action;
     198             :   /**/ if( 0==strcmp( mode, "epochs"  ) ) action = ACTION_EPOCHS;
     199             :   else if( 0==strcmp( mode, "nodes"   ) ) action = ACTION_NODES;
     200             :   else if( 0==strcmp( mode, "leaders" ) ) action = ACTION_LEADERS;
     201             :   else {
     202             :     fprintf( stderr, "error: invalid mode \"%s\"\n", mode );
     203             :     return usage();
     204             :   }
     205             : 
     206             :   /* Create workspace and scratch allocator */
     207             : 
     208             :   fd_wksp_t * wksp = fd_wksp_new_anonymous( page_sz, page_cnt, fd_log_cpu_id(), "wksp", 0UL );
     209             :   if( FD_UNLIKELY( !wksp ) ) FD_LOG_ERR(( "fd_wksp_new_anonymous() failed" ));
     210             : 
     211             :   ulong  smax = scratch_mb << 20;
     212             :   void * smem = fd_wksp_alloc_laddr( wksp, fd_scratch_smem_align(), smax, 1UL );
     213             :   if( FD_UNLIKELY( !smem ) ) FD_LOG_ERR(( "Failed to alloc scratch mem" ));
     214             :   ulong  scratch_depth = 4UL;
     215             :   void * fmem = fd_wksp_alloc_laddr( wksp, fd_scratch_fmem_align(), fd_scratch_fmem_footprint( scratch_depth ), 2UL );
     216             :   if( FD_UNLIKELY( !fmem ) ) FD_LOG_ERR(( "Failed to alloc scratch frames" ));
     217             : 
     218             :   fd_scratch_attach( smem, fmem, smax, scratch_depth );
     219             :   fd_scratch_push();
     220             : 
     221             :   /* Open file */
     222             : 
     223             :   FD_LOG_INFO(( "Reading snapshot %s", filepath ));
     224             :   FILE * file = fopen( filepath, "rb" );
     225             :   if( FD_UNLIKELY( !file ) ) FD_LOG_ERR(( "fopen(%s) failed (%d-%s)", filepath, errno, strerror( errno ) ));
     226             : 
     227             :   /* Read manifest bytes */
     228             : 
     229             :   void * manifest_bin;
     230             :   ulong  manifest_binsz;
     231             :   if( _is_zstd( file ) ) {
     232             :     FD_LOG_NOTICE(( "Detected .tar.zst stream" ));
     233             :     FD_LOG_ERR(( "TODO" ));
     234             :   } else {
     235             :     FD_LOG_NOTICE(( "Assuming raw bincode file" ));
     236             : 
     237             :     /* Allocate buffer suitable for storing file */
     238             : 
     239             :     struct stat stat;
     240             :     int err = fstat( fileno( file ), &stat );
     241             :     if( FD_UNLIKELY( err ) ) FD_LOG_ERR(( "fstat() failed (%d-%s)", errno, strerror( errno ) ));
     242             :     manifest_binsz = (ulong)stat.st_size;
     243             : 
     244             :     manifest_bin = fd_wksp_alloc_laddr( wksp, 1UL, manifest_binsz, 3UL );
     245             :     if( FD_UNLIKELY( !manifest_bin ) ) FD_LOG_ERR(( "failed to allocate metadata buffer" ));
     246             : 
     247             :     /* Read file into buffer */
     248             : 
     249             :     ulong n = fread( manifest_bin, manifest_binsz, 1UL, file );
     250             :     if( FD_UNLIKELY( n!=1UL ) ) FD_LOG_ERR(( "fread() failed (eof=%d)", feof( file ) ));
     251             :     FD_LOG_NOTICE(( "Read manifest (%.3f MiB)", (double)manifest_binsz/(1UL<<20) ));
     252             :   }
     253             : 
     254             :   /* Deserialize manifest */
     255             : 
     256             :   long dt = -fd_log_wallclock();
     257             : 
     258             :   int decode_err;
     259             :   fd_solana_manifest_t * manifest = fd_bincode_decode_scratch(
     260             :       solana_manifest, manifest_bin, manifest_binsz, &decode_err );
     261             :   if( FD_UNLIKELY( decode_err ) ) {
     262             :     FD_LOG_ERR(( "Failed to decode manifest" ));
     263             :   }
     264             : 
     265             :   fd_wksp_free_laddr( manifest_bin ); manifest_bin = NULL;
     266             :   dt += fd_log_wallclock();
     267             :   FD_LOG_NOTICE(( "Deserialized manifest (took %.3fs)", (double)dt/1e9 ));
     268             : 
     269             :   /* Action */
     270             : 
     271             :   int res;
     272             :   fd_scratch_push();
     273             :   switch( action ) {
     274             :   case ACTION_LEADERS:
     275             :     res = action_leaders( manifest, epoch );
     276             :     break;
     277             :   case ACTION_NODES:
     278             :     res = action_nodes( manifest, epoch );
     279             :     break;
     280             :   case ACTION_EPOCHS:
     281             :     res = action_epochs( manifest );
     282             :     break;
     283             :   default:
     284             :     __builtin_unreachable();
     285             :   }
     286             :   fd_scratch_pop();
     287             : 
     288             :   /* Cleanup */
     289             : 
     290             :   fd_scratch_pop();
     291             :   FD_TEST( fd_scratch_frame_used()==0UL );
     292             :   fd_wksp_free_laddr( fd_scratch_detach( NULL ) );
     293             :   fd_wksp_free_laddr( fmem                      );
     294             :   fclose( file );
     295             :   fd_wksp_detach( wksp );
     296             :   fd_flamenco_halt();
     297             :   fd_halt();
     298             :   return res;
     299             : }

Generated by: LCOV version 1.14