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 109 0.0 %
Date: 2025-08-05 05:04: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_vote_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_vote_stake_weight_t * weights = fd_scratch_alloc( alignof(fd_vote_stake_weight_t), vote_acc_cnt * sizeof(fd_vote_stake_weight_t) );
      83           0 :   if( FD_UNLIKELY( !weights ) ) FD_LOG_ERR(( "fd_scratch_alloc() failed" ));
      84             : 
      85           0 :   ulong weight_cnt = fd_stake_weights_by_node( NULL, weights );
      86           0 :   if( FD_UNLIKELY( weight_cnt==ULONG_MAX ) ) FD_LOG_ERR(( "fd_stake_weights_by_node() failed" ));
      87             : 
      88           0 :   *out_cnt = weight_cnt;
      89           0 :   return weights;
      90           0 : }
      91             : 
      92             : static int
      93           0 : action_epochs( fd_solana_manifest_t const * manifest ) {
      94           0 :   fd_epoch_epoch_stakes_pair_t const * epochs = manifest->bank.epoch_stakes;
      95           0 :   for( ulong i=0; i < manifest->bank.epoch_stakes_len; i++ )
      96           0 :     printf( "%lu\n", epochs[ i ].key );
      97           0 :   return 0;
      98           0 : }
      99             : 
     100             : static int
     101             : action_nodes( fd_solana_manifest_t const * manifest,
     102           0 :               ulong                        epoch ) {
     103             : 
     104           0 :   ulong               weight_cnt;
     105           0 :   fd_vote_stake_weight_t * weights = _get_stake_weights( manifest, epoch, &weight_cnt );
     106             : 
     107           0 :   for( ulong i=0UL; i<weight_cnt; i++ ) {
     108           0 :     fd_vote_stake_weight_t const * w = weights + i;
     109           0 :     char keyB58[ FD_BASE58_ENCODED_32_SZ ];
     110           0 :     fd_base58_encode_32( w->id_key.key, NULL,  keyB58 );
     111           0 :     printf( "%s,%lu\n", keyB58, w->stake );
     112           0 :   }
     113             : 
     114           0 :   return 0;
     115           0 : }
     116             : 
     117             : static int
     118             : action_leaders( fd_solana_manifest_t const * manifest,
     119           0 :                 ulong                        epoch ) {
     120             : 
     121           0 :   ulong               weight_cnt;
     122           0 :   fd_vote_stake_weight_t * weights = _get_stake_weights( manifest, epoch, &weight_cnt );
     123             : 
     124           0 :   fd_epoch_schedule_t const * sched = &manifest->bank.epoch_schedule;
     125           0 :   ulong slot0     = fd_epoch_slot0   ( sched, epoch );
     126           0 :   ulong slot_cnt  = fd_epoch_slot_cnt( sched, epoch );
     127           0 :   ulong sched_cnt = slot_cnt/FD_EPOCH_SLOTS_PER_ROTATION;
     128             : 
     129           0 :   ulong vote_keyed_lsched = 0; /* Set to 1 after SIMD-0180 gets activated */
     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, vote_keyed_lsched );
     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             : 
     175             :   for( int i=1; i<argc; i++ )
     176             :     if( 0==strcmp( argv[i], "--help" ) ) return usage();
     177             : 
     178             :   char const * _page_sz   = fd_env_strip_cmdline_cstr ( &argc, &argv, "--page-sz",    NULL, "gigantic" );
     179             :   ulong        page_cnt   = fd_env_strip_cmdline_ulong( &argc, &argv, "--page-cnt",   NULL, 2UL        );
     180             :   ulong        scratch_mb = fd_env_strip_cmdline_ulong( &argc, &argv, "--scratch-mb", NULL, 1024UL     );
     181             :   ulong        epoch      = fd_env_strip_cmdline_ulong( &argc, &argv, "--epoch",      NULL, ULONG_MAX  );
     182             : 
     183             :   ulong page_sz = fd_cstr_to_shmem_page_sz( _page_sz );
     184             :   if( FD_UNLIKELY( !page_sz ) ) FD_LOG_ERR(( "unsupported --page-sz" ));
     185             : 
     186             :   if( FD_UNLIKELY( argc!=3 ) ) {
     187             :     fprintf( stderr, "error: missing arguments\n" );
     188             :     return usage();
     189             :   }
     190             : 
     191             :   char const * mode     = argv[1];
     192             :   char const * filepath = argv[2];
     193             : 
     194             :   int action;
     195             :   /**/ if( 0==strcmp( mode, "epochs"  ) ) action = ACTION_EPOCHS;
     196             :   else if( 0==strcmp( mode, "nodes"   ) ) action = ACTION_NODES;
     197             :   else if( 0==strcmp( mode, "leaders" ) ) action = ACTION_LEADERS;
     198             :   else {
     199             :     fprintf( stderr, "error: invalid mode \"%s\"\n", mode );
     200             :     return usage();
     201             :   }
     202             : 
     203             :   /* Create workspace and scratch allocator */
     204             : 
     205             :   fd_wksp_t * wksp = fd_wksp_new_anonymous( page_sz, page_cnt, fd_log_cpu_id(), "wksp", 0UL );
     206             :   if( FD_UNLIKELY( !wksp ) ) FD_LOG_ERR(( "fd_wksp_new_anonymous() failed" ));
     207             : 
     208             :   ulong  smax = scratch_mb << 20;
     209             :   void * smem = fd_wksp_alloc_laddr( wksp, fd_scratch_smem_align(), smax, 1UL );
     210             :   if( FD_UNLIKELY( !smem ) ) FD_LOG_ERR(( "Failed to alloc scratch mem" ));
     211             :   ulong  scratch_depth = 4UL;
     212             :   void * fmem = fd_wksp_alloc_laddr( wksp, fd_scratch_fmem_align(), fd_scratch_fmem_footprint( scratch_depth ), 2UL );
     213             :   if( FD_UNLIKELY( !fmem ) ) FD_LOG_ERR(( "Failed to alloc scratch frames" ));
     214             : 
     215             :   fd_scratch_attach( smem, fmem, smax, scratch_depth );
     216             :   fd_scratch_push();
     217             : 
     218             :   /* Open file */
     219             : 
     220             :   FD_LOG_INFO(( "Reading snapshot %s", filepath ));
     221             :   FILE * file = fopen( filepath, "rb" );
     222             :   if( FD_UNLIKELY( !file ) ) FD_LOG_ERR(( "fopen(%s) failed (%d-%s)", filepath, errno, strerror( errno ) ));
     223             : 
     224             :   /* Read manifest bytes */
     225             : 
     226             :   void * manifest_bin;
     227             :   ulong  manifest_binsz;
     228             :   if( _is_zstd( file ) ) {
     229             :     FD_LOG_NOTICE(( "Detected .tar.zst stream" ));
     230             :     FD_LOG_ERR(( "TODO" ));
     231             :   } else {
     232             :     FD_LOG_NOTICE(( "Assuming raw bincode file" ));
     233             : 
     234             :     /* Allocate buffer suitable for storing file */
     235             : 
     236             :     struct stat stat;
     237             :     int err = fstat( fileno( file ), &stat );
     238             :     if( FD_UNLIKELY( err ) ) FD_LOG_ERR(( "fstat() failed (%d-%s)", errno, strerror( errno ) ));
     239             :     manifest_binsz = (ulong)stat.st_size;
     240             : 
     241             :     manifest_bin = fd_wksp_alloc_laddr( wksp, 1UL, manifest_binsz, 3UL );
     242             :     if( FD_UNLIKELY( !manifest_bin ) ) FD_LOG_ERR(( "failed to allocate metadata buffer" ));
     243             : 
     244             :     /* Read file into buffer */
     245             : 
     246             :     ulong n = fread( manifest_bin, manifest_binsz, 1UL, file );
     247             :     if( FD_UNLIKELY( n!=1UL ) ) FD_LOG_ERR(( "fread() failed (eof=%d)", feof( file ) ));
     248             :     FD_LOG_NOTICE(( "Read manifest (%.3f MiB)", (double)manifest_binsz/(1UL<<20) ));
     249             :   }
     250             : 
     251             :   /* Deserialize manifest */
     252             : 
     253             :   long dt = -fd_log_wallclock();
     254             : 
     255             :   int decode_err;
     256             :   fd_solana_manifest_t * manifest = fd_bincode_decode_scratch(
     257             :       solana_manifest, manifest_bin, manifest_binsz, &decode_err );
     258             :   if( FD_UNLIKELY( decode_err ) ) {
     259             :     FD_LOG_ERR(( "Failed to decode manifest" ));
     260             :   }
     261             : 
     262             :   fd_wksp_free_laddr( manifest_bin ); manifest_bin = NULL;
     263             :   dt += fd_log_wallclock();
     264             :   FD_LOG_NOTICE(( "Deserialized manifest (took %.3fs)", (double)dt/1e9 ));
     265             : 
     266             :   /* Action */
     267             : 
     268             :   int res;
     269             :   fd_scratch_push();
     270             :   switch( action ) {
     271             :   case ACTION_LEADERS:
     272             :     res = action_leaders( manifest, epoch );
     273             :     break;
     274             :   case ACTION_NODES:
     275             :     res = action_nodes( manifest, epoch );
     276             :     break;
     277             :   case ACTION_EPOCHS:
     278             :     res = action_epochs( manifest );
     279             :     break;
     280             :   default:
     281             :     __builtin_unreachable();
     282             :   }
     283             :   fd_scratch_pop();
     284             : 
     285             :   /* Cleanup */
     286             : 
     287             :   fd_scratch_pop();
     288             :   FD_TEST( fd_scratch_frame_used()==0UL );
     289             :   fd_wksp_free_laddr( fd_scratch_detach( NULL ) );
     290             :   fd_wksp_free_laddr( fmem                      );
     291             :   fclose( file );
     292             :   fd_wksp_detach( wksp );
     293             :   fd_halt();
     294             :   return res;
     295             : }

Generated by: LCOV version 1.14