LCOV - code coverage report
Current view: top level - app/ledger - main.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 153 0.0 %
Date: 2026-06-29 05:51:35 Functions: 0 6 0.0 %

          Line data    Source code
       1             : #if !FD_HAS_ROCKSDB
       2             : #error "fd_ledger requires RocksDB"
       3             : #endif
       4             : 
       5             : #include "../../util/fd_util.h"
       6             : #include <rocksdb/c.h>
       7             : #include <sys/stat.h>
       8             : 
       9             : static char const * const fd_rocksdb_cf_names[] = {
      10             :   "default",
      11             :   "meta",
      12             :   "dead_slots",
      13             :   "duplicate_slots",
      14             :   "erasure_meta",
      15             :   "orphans",
      16             :   "bank_hashes",
      17             :   "root",
      18             :   "index",
      19             :   "data_shred",
      20             :   "code_shred",
      21             :   "transaction_status",
      22             :   "address_signatures",
      23             :   "transaction_memos",
      24             :   "rewards",
      25             :   "blocktime",
      26             :   "perf_samples",
      27             :   "block_height",
      28             :   "optimistic_slots",
      29             :   "merkle_root_meta",
      30             : };
      31             : 
      32           0 : #define FD_ROCKSDB_CF_CNT (sizeof(fd_rocksdb_cf_names)/sizeof(fd_rocksdb_cf_names[0]))
      33             : 
      34           0 : #define FD_ROCKSDB_CFIDX_ROOT                     (7UL)
      35           0 : #define FD_ROCKSDB_CFIDX_TRANSACTION_STATUS       (11UL)
      36           0 : #define FD_ROCKSDB_CFIDX_ADDRESS_SIGNATURES       (12UL)
      37           0 : #define FD_ROCKSDB_CFIDX_TRANSACTION_MEMOS        (13UL)
      38             : 
      39             : struct fd_rocksdb {
      40             :   rocksdb_t *                      db;
      41             :   rocksdb_column_family_handle_t * cf_handles[ FD_ROCKSDB_CF_CNT ];
      42             :   rocksdb_options_t *              opts;
      43             :   rocksdb_readoptions_t *          ro;
      44             :   rocksdb_writeoptions_t *         wo;
      45             : };
      46             : typedef struct fd_rocksdb fd_rocksdb_t;
      47             : 
      48             : static char *
      49             : fd_rocksdb_init( fd_rocksdb_t * db,
      50           0 :                  char const *   db_name ) {
      51           0 :   fd_memset( db, 0, sizeof(fd_rocksdb_t) );
      52             : 
      53           0 :   db->opts = rocksdb_options_create();
      54             : 
      55           0 :   rocksdb_options_t const * cf_options[ FD_ROCKSDB_CF_CNT ];
      56           0 :   for( ulong i=0UL; i<FD_ROCKSDB_CF_CNT; i++ )
      57           0 :     cf_options[ i ] = db->opts;
      58             : 
      59           0 :   char * err = NULL;
      60             : 
      61           0 :   db->db = rocksdb_open_for_read_only_column_families(
      62           0 :       db->opts,
      63           0 :       db_name,
      64           0 :       FD_ROCKSDB_CF_CNT,
      65           0 :       fd_rocksdb_cf_names,
      66           0 :       (rocksdb_options_t const * const *)cf_options,
      67           0 :       db->cf_handles,
      68           0 :       0,
      69           0 :       &err );
      70             : 
      71           0 :   if( FD_UNLIKELY( err ) ) return err;
      72             : 
      73           0 :   db->ro = rocksdb_readoptions_create();
      74             : 
      75           0 :   return NULL;
      76           0 : }
      77             : 
      78             : static void
      79             : fd_rocksdb_new( fd_rocksdb_t * db,
      80           0 :                 char const *   db_name ) {
      81           0 :   fd_memset( db, 0, sizeof(fd_rocksdb_t) );
      82             : 
      83           0 :   db->opts = rocksdb_options_create();
      84           0 :   rocksdb_options_set_create_if_missing( db->opts, 1 );
      85           0 :   rocksdb_options_set_compression( db->opts, rocksdb_lz4_compression );
      86             : 
      87           0 :   char * err = NULL;
      88           0 :   db->db = rocksdb_open( db->opts, db_name, &err );
      89           0 :   if( FD_UNLIKELY( err ) ) {
      90           0 :     FD_LOG_ERR(( "rocksdb creation failed: %s", err ));
      91           0 :   }
      92             : 
      93           0 :   db->wo = rocksdb_writeoptions_create();
      94             : 
      95           0 :   for( ulong i=1UL; i<FD_ROCKSDB_CF_CNT; i++ ) {
      96           0 :     db->cf_handles[i] = rocksdb_create_column_family( db->db, db->opts, fd_rocksdb_cf_names[i], &err );
      97           0 :   }
      98           0 : }
      99             : 
     100             : static void
     101           0 : fd_rocksdb_destroy( fd_rocksdb_t * db ) {
     102             : 
     103           0 :   for( ulong i=0UL; i<FD_ROCKSDB_CF_CNT; i++ ) {
     104           0 :     if( db->cf_handles[i] ) {
     105           0 :       rocksdb_column_family_handle_destroy( db->cf_handles[i] );
     106           0 :       db->cf_handles[i] = NULL;
     107           0 :     }
     108           0 :   }
     109             : 
     110           0 :   if( db->ro ) {
     111           0 :     rocksdb_readoptions_destroy( db->ro );
     112           0 :     db->ro = NULL;
     113           0 :   }
     114             : 
     115           0 :   if( db->opts ) {
     116           0 :     rocksdb_options_destroy( db->opts );
     117           0 :     db->opts = NULL;
     118           0 :   }
     119             : 
     120           0 :   if( db->db ) {
     121           0 :     rocksdb_close( db->db );
     122           0 :     db->db = NULL;
     123           0 :   }
     124             : 
     125           0 :   if( db->wo ) {
     126           0 :     rocksdb_writeoptions_destroy( db->wo );
     127           0 :   }
     128           0 : }
     129             : 
     130             : static ulong
     131             : fd_rocksdb_root_slot( fd_rocksdb_t * db,
     132             :                       int            last,
     133           0 :                       char **        err ) {
     134           0 :   rocksdb_iterator_t * iter = rocksdb_create_iterator_cf( db->db, db->ro, db->cf_handles[FD_ROCKSDB_CFIDX_ROOT] );
     135           0 :   if( last ) rocksdb_iter_seek_to_last ( iter );
     136           0 :   else       rocksdb_iter_seek_to_first( iter );
     137             : 
     138           0 :   if( FD_UNLIKELY( !rocksdb_iter_valid( iter ) ) ) {
     139           0 :     rocksdb_iter_destroy( iter );
     140           0 :     *err = "db column for root is empty";
     141           0 :     return 0;
     142           0 :   }
     143             : 
     144           0 :   ulong klen = 0;
     145           0 :   char const * key = rocksdb_iter_key( iter, &klen );
     146           0 :   ulong slot = fd_ulong_bswap( FD_LOAD( ulong, key ) );
     147           0 :   rocksdb_iter_destroy( iter );
     148           0 :   return slot;
     149           0 : }
     150             : 
     151             : static void
     152             : fd_rocksdb_copy_over_slot_indexed_range( fd_rocksdb_t * src,
     153             :                                          fd_rocksdb_t * dst,
     154             :                                          ulong          cf_idx,
     155             :                                          ulong          start_slot,
     156           0 :                                          ulong          end_slot ) {
     157           0 :   FD_LOG_NOTICE(( "fd_rocksdb_copy_over_slot_indexed_range: %lu", cf_idx ));
     158             : 
     159           0 :   if( cf_idx==FD_ROCKSDB_CFIDX_TRANSACTION_MEMOS  ||
     160           0 :       cf_idx==FD_ROCKSDB_CFIDX_TRANSACTION_STATUS ||
     161           0 :       cf_idx==FD_ROCKSDB_CFIDX_ADDRESS_SIGNATURES ) {
     162           0 :     FD_LOG_NOTICE(( "fd_rocksdb_copy_over_range: skipping cf_idx=%lu because not slot indexed", cf_idx ));
     163           0 :     return;
     164           0 :   }
     165             : 
     166           0 :   rocksdb_iterator_t * iter = rocksdb_create_iterator_cf( src->db, src->ro, src->cf_handles[cf_idx] );
     167           0 :   if( FD_UNLIKELY( !iter ) ) {
     168           0 :     FD_LOG_ERR(( "rocksdb_create_iterator_cf failed for cf_idx=%lu", cf_idx ));
     169           0 :   }
     170             : 
     171           0 :   ulong start_key = fd_ulong_bswap( start_slot );
     172           0 :   for( rocksdb_iter_seek( iter, (char const *)&start_key, sizeof(start_key) ); rocksdb_iter_valid( iter ); rocksdb_iter_next( iter ) ) {
     173           0 :     ulong klen = 0;
     174           0 :     char const * key = rocksdb_iter_key( iter, &klen );
     175             : 
     176           0 :     ulong slot = fd_ulong_bswap( FD_LOAD( ulong, key ) );
     177           0 :     if( slot<start_slot ) {
     178           0 :       continue;
     179           0 :     } else if( slot>end_slot ) {
     180           0 :       break;
     181           0 :     }
     182             : 
     183           0 :     ulong vlen = 0;
     184           0 :     char const * value = rocksdb_iter_value( iter, &vlen );
     185             : 
     186           0 :     char * err = NULL;
     187           0 :     rocksdb_put_cf( dst->db, dst->wo, dst->cf_handles[cf_idx], key, klen, value, vlen, &err );
     188           0 :     if( FD_UNLIKELY( err ) ) {
     189           0 :       FD_LOG_WARNING(( "rocksdb_put_cf failed with error %s", err ));
     190           0 :       rocksdb_free( err );
     191           0 :     }
     192           0 :   }
     193           0 :   rocksdb_iter_destroy( iter );
     194           0 : }
     195             : 
     196             : /********************* Main Command Functions and Setup ***********************/
     197             : static void
     198             : minify( char const * rocksdb_path,
     199             :         char const * mini_db_dir,
     200             :         ulong        start_slot,
     201           0 :         ulong        end_slot ) {
     202             :   /* Example command:
     203             :      fd_ledger --cmd minify --rocksdb <LARGE_ROCKSDB> --minified-rocksdb <MINI_ROCKSDB>
     204             :                --start-slot <START_SLOT> --end-slot <END_SLOT> */
     205           0 :   if( FD_UNLIKELY( !rocksdb_path ) ) {
     206           0 :     FD_LOG_ERR(( "rocksdb path is NULL" ));
     207           0 :   }
     208           0 :   if( FD_UNLIKELY( !mini_db_dir ) ) {
     209           0 :     FD_LOG_ERR(( "minified rocksdb path is NULL" ));
     210           0 :   }
     211             : 
     212           0 :   fd_rocksdb_t big_rocksdb;
     213           0 :   char * err = fd_rocksdb_init( &big_rocksdb, rocksdb_path );
     214           0 :   if( FD_UNLIKELY( err ) ) {
     215           0 :     FD_LOG_ERR(( "fd_rocksdb_init at path=%s returned error=%s", rocksdb_path, err ));
     216           0 :   }
     217             : 
     218             :   /* If the directory for the minified rocksdb already exists, error out */
     219           0 :   struct stat statbuf;
     220           0 :   if( stat( mini_db_dir, &statbuf ) == 0 ) {
     221           0 :     FD_LOG_ERR(( "path for mini_db_dir=%s already exists", mini_db_dir ));
     222           0 :   }
     223             : 
     224             :   /* Create a new smaller rocksdb */
     225           0 :   fd_rocksdb_t mini_rocksdb;
     226           0 :   fd_rocksdb_new( &mini_rocksdb, mini_db_dir );
     227             : 
     228             :   /* Correctly bound off start and end slot */
     229           0 :   ulong first_slot = fd_rocksdb_root_slot( &big_rocksdb, 0, &err );
     230           0 :   ulong last_slot  = fd_rocksdb_root_slot( &big_rocksdb, 1, &err );
     231           0 :   if( start_slot < first_slot ) start_slot = first_slot;
     232           0 :   if( end_slot   > last_slot  ) end_slot   = last_slot;
     233             : 
     234           0 :   FD_LOG_NOTICE(( "copying over rocks db for range [%lu, %lu]", start_slot, end_slot ));
     235             : 
     236             :   /* Copy over all slot indexed columns */
     237           0 :   for( ulong cf_idx=1UL; cf_idx<FD_ROCKSDB_CF_CNT; cf_idx++ ) {
     238           0 :     fd_rocksdb_copy_over_slot_indexed_range( &big_rocksdb, &mini_rocksdb, cf_idx,
     239           0 :                                              start_slot, end_slot );
     240           0 :   }
     241           0 :   FD_LOG_NOTICE(( "copied over all slot indexed columns" ));
     242             : 
     243             :   /* TODO: Currently, the address signatures column family isn't copied as it
     244             :            is indexed on the pubkey. */
     245             : 
     246           0 :   rocksdb_flushoptions_t * flush_options = rocksdb_flushoptions_create();
     247           0 :   rocksdb_flushoptions_set_wait( flush_options, 1 );
     248           0 :   char * flush_err = NULL;
     249           0 :   rocksdb_flush_cfs( mini_rocksdb.db, flush_options,
     250           0 :                      &mini_rocksdb.cf_handles[ 1 ],
     251           0 :                      FD_ROCKSDB_CF_CNT - 1, &flush_err );
     252           0 :   if( FD_UNLIKELY( flush_err ) ) {
     253           0 :     FD_LOG_WARNING(( "minify: flushing minified rocksdb failed: %s", flush_err ));
     254           0 :     rocksdb_free( flush_err );
     255           0 :   }
     256           0 :   rocksdb_flushoptions_destroy( flush_options );
     257             : 
     258           0 :   fd_rocksdb_destroy( &big_rocksdb );
     259           0 :   fd_rocksdb_destroy( &mini_rocksdb );
     260           0 : }
     261             : 
     262             : int
     263             : main( int     argc,
     264             :       char ** argv ) {
     265             :   if( FD_UNLIKELY( argc==1 ) ) {
     266             :     FD_LOG_ERR(( "no command specified" ));
     267             :   }
     268             : 
     269             :   fd_boot( &argc, &argv );
     270             : 
     271             :   char const * cmd                   = fd_env_strip_cmdline_cstr  ( &argc, &argv, "--cmd",                   NULL, NULL                                               );
     272             :   ulong        start_slot            = fd_env_strip_cmdline_ulong ( &argc, &argv, "--start-slot",            NULL, 0UL                                                );
     273             :   ulong        end_slot              = fd_env_strip_cmdline_ulong ( &argc, &argv, "--end-slot",              NULL, ULONG_MAX                                          );
     274             :   char const * mini_db_dir           = fd_env_strip_cmdline_cstr  ( &argc, &argv, "--minified-rocksdb",      NULL, NULL                                               );
     275             :   char const * rocksdb_path          = fd_env_strip_cmdline_cstr  ( &argc, &argv, "--rocksdb",               NULL, NULL                                               );
     276             : 
     277             :   if( rocksdb_path ) {
     278             :     FD_LOG_NOTICE(( "rocksdb=%s", rocksdb_path ));
     279             :   }
     280             : 
     281             :   if( FD_UNLIKELY( !cmd ) ) {
     282             :     FD_LOG_ERR(( "no command specified" ));
     283             :   } else if( strcmp( cmd, "minify" ) == 0 ) {
     284             :     minify( rocksdb_path, mini_db_dir, start_slot, end_slot );
     285             :   } else {
     286             :     FD_LOG_ERR(( "unknown command=%s", cmd ));
     287             :   }
     288             : 
     289             :   return 0;
     290             : }

Generated by: LCOV version 1.14