LCOV - code coverage report
Current view: top level - app/fddev/commands/configure - blockstore.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 128 0.0 %
Date: 2025-09-18 04:41:32 Functions: 0 4 0.0 %

          Line data    Source code
       1             : #define _GNU_SOURCE
       2             : #include "../../../shared/commands/configure/configure.h"
       3             : 
       4             : #include "../../../platform/fd_sys_util.h"
       5             : #include "../../../platform/fd_file_util.h"
       6             : 
       7             : #include "../../../../ballet/shred/fd_shred.h"
       8             : #include "../../../../ballet/poh/fd_poh.h"
       9             : #include "../../../../disco/shred/fd_shredder.h"
      10             : #include "../../../../disco/tiles.h"
      11             : #include "../../../../discof/genesis/genesis_hash.h"
      12             : 
      13             : #include <unistd.h>
      14             : #include <dirent.h>
      15             : #include <sys/stat.h>
      16             : #include <sys/wait.h>
      17             : 
      18             : #define NAME "blockstore"
      19             : 
      20             : extern void
      21             : fd_ext_blockstore_create_block0( char const *  ledger_path,
      22             :                                  ulong         shred_cnt,
      23             :                                  uchar const * shred_bytes,
      24             :                                  ulong         shred_sz,
      25             :                                  ulong         stride );
      26             : 
      27           0 : static inline void zero_signer( void * _1, uchar * sig, uchar const * _2 ) { (void)_1; (void)_2; memset( sig, '\0', 64UL ); }
      28             : 
      29             : static void
      30           0 : init( config_t const * config ) {
      31             :   /* The Agave validator cannot boot without a block 0 existing in the
      32             :      blockstore in the ledger directory, so we have to create one.  This
      33             :      creates a directory "rocksdb" under which the blockstore with
      34             :      block0 is created.  The entire directory should be included in the
      35             :      genesis archive "genesis.tar.bz2". */
      36             : 
      37             :   /* TODO: Parse genesis.bin and use those values instead. */
      38           0 :   ulong ticks_per_slot  = config->development.genesis.ticks_per_slot;
      39           0 :   ulong hashes_per_tick = config->development.genesis.hashes_per_tick;
      40             : 
      41           0 :   char genesis_path[ PATH_MAX ];
      42           0 :   FD_TEST( fd_cstr_printf_check( genesis_path, PATH_MAX, NULL, "%s/genesis.bin", config->paths.ledger ) );
      43           0 :   uchar genesis_hash[ 32 ] = { 0 };
      44           0 :   ushort shred_version = 0;
      45           0 :   int result = compute_shred_version( genesis_path, &shred_version, genesis_hash );
      46           0 :   if( FD_UNLIKELY( -1==result && errno!=ENOENT ) ) FD_LOG_ERR(( "could not compute shred version from genesis file `%s` (%i-%s)", genesis_path, errno, fd_io_strerror( errno ) ));
      47             : 
      48             : 
      49             :   /* This is not a fundamental limit.  It could be set as high as 663
      50             :      with no modifications to the rest of the code.  It's set to 128
      51             :      because that seems more than enough and we don't need to consume
      52             :      that much stack space.  It could be set even higher if you add
      53             :      multiple FEC sets. */
      54           0 : #define GENESIS_MAX_TICKS_PER_SLOT 128UL
      55           0 :   FD_TEST( ticks_per_slot<GENESIS_MAX_TICKS_PER_SLOT );
      56           0 :   struct {
      57           0 :     ulong                   ticks_in_batch;
      58           0 :     fd_entry_batch_header_t ticks[ GENESIS_MAX_TICKS_PER_SLOT ];
      59           0 :   } batch;
      60             : 
      61           0 :   batch.ticks_in_batch = ticks_per_slot;
      62           0 :   uchar poh_hash[ 32 ] = {0};
      63           0 :   memcpy( poh_hash, genesis_hash, 32UL );
      64             : 
      65           0 :   for( ulong i=0UL; i<ticks_per_slot; i++ ) {
      66           0 :     fd_poh_append( poh_hash, hashes_per_tick );
      67             : 
      68           0 :     batch.ticks[ i ].hashcnt_delta = hashes_per_tick;
      69           0 :     batch.ticks[ i ].txn_cnt       = 0UL;
      70           0 :     memcpy( batch.ticks[ i ].hash, poh_hash, 32UL );
      71           0 :   }
      72             : 
      73           0 :   ulong batch_sz = sizeof(ulong)+ticks_per_slot*sizeof(fd_entry_batch_header_t);
      74             : 
      75           0 :   FD_TEST( fd_shredder_count_data_shreds  ( batch_sz, FD_SHRED_TYPE_MERKLE_DATA )<=34UL );
      76           0 :   FD_TEST( fd_shredder_count_parity_shreds( batch_sz, FD_SHRED_TYPE_MERKLE_CODE )<=34UL );
      77             : 
      78           0 :   fd_shred34_t data, parity;
      79           0 :   fd_fec_set_t fec;
      80           0 :   for( ulong i=0UL; i<34UL; i++ ) {
      81           0 :     fec.data_shreds  [ i ] = data.pkts  [ i ].buffer;
      82           0 :     fec.parity_shreds[ i ] = parity.pkts[ i ].buffer;
      83           0 :   }
      84           0 :   for( ulong i=34UL; i<FD_REEDSOL_DATA_SHREDS_MAX;   i++ ) fec.data_shreds  [ i ] = NULL;
      85           0 :   for( ulong i=34UL; i<FD_REEDSOL_PARITY_SHREDS_MAX; i++ ) fec.parity_shreds[ i ] = NULL;
      86             : 
      87           0 :   fd_entry_batch_meta_t meta[ 1 ] = {{
      88           0 :     .parent_offset  = 0UL,
      89           0 :     .reference_tick = ticks_per_slot,
      90           0 :     .block_complete = 1
      91           0 :   }};
      92             : 
      93           0 :   fd_shredder_t _shredder[ 1 ];
      94           0 :   fd_shredder_t * shredder = fd_shredder_join( fd_shredder_new( _shredder, zero_signer, NULL ) );
      95           0 :   fd_shredder_set_shred_version( shredder, shred_version );
      96             : 
      97           0 :   fd_shredder_init_batch( shredder, &batch, batch_sz, 0UL, meta );
      98           0 :   fd_shredder_next_fec_set( shredder, &fec, /* chained */ NULL, NULL );
      99             : 
     100             :   /* Fork off a new process for inserting the shreds to the blockstore.
     101             :      RocksDB creates a dozen background workers, and doesn't close them
     102             :      by default.  This would cause problems for sandboxing because you
     103             :      can't unshare a user namespace once multi-threaded, so just do it
     104             :      in another process that we can then terminate. */
     105             : 
     106           0 :   pid_t pid = fork();
     107           0 :   if( FD_UNLIKELY( pid == -1 ) ) FD_LOG_ERR(( "fork() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     108             : 
     109           0 :   if( FD_LIKELY( !pid ) ) {
     110             :     /* Switch to target user in the configuration when creating the
     111             :        genesis.bin file so it is permissioned correctly. */
     112           0 :     gid_t gid = getgid();
     113           0 :     uid_t uid = getuid();
     114           0 :     if( FD_LIKELY( gid == 0 && setegid( config->gid ) ) )
     115           0 :       FD_LOG_ERR(( "setegid() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     116           0 :     if( FD_LIKELY( uid == 0 && seteuid( config->uid ) ) )
     117           0 :       FD_LOG_ERR(( "seteuid() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     118             : 
     119           0 :     umask( S_IRWXO | S_IRWXG );
     120             : 
     121           0 :     fd_ext_blockstore_create_block0( config->paths.ledger, fec.data_shred_cnt, (uchar const *)data.pkts, FD_SHRED_MIN_SZ, FD_SHRED_MAX_SZ );
     122             : 
     123           0 :     fd_sys_util_exit_group( 0 );
     124           0 :   } else {
     125           0 :     int wstatus;
     126           0 :     if( FD_UNLIKELY( waitpid( pid, &wstatus, 0 )==-1 ) ) FD_LOG_ERR(( "waitpid() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     127           0 :     if( FD_UNLIKELY( WIFSIGNALED( wstatus ) ) )
     128           0 :       FD_LOG_ERR(( "genesis blockstore creation process terminated by signal %i-%s", WTERMSIG( wstatus ), fd_io_strsignal( WTERMSIG( wstatus ) ) ));
     129           0 :     if( FD_UNLIKELY( WEXITSTATUS( wstatus ) ) )
     130           0 :       FD_LOG_ERR(( "genesis blockstore creation process exited with status %i", WEXITSTATUS( wstatus ) ));
     131           0 :   }
     132             : 
     133           0 : }
     134             : 
     135             : static int
     136             : fini( config_t const * config,
     137           0 :       int              pre_init FD_PARAM_UNUSED ) {
     138           0 :   DIR * dir = opendir( config->paths.ledger );
     139           0 :   if( FD_UNLIKELY( !dir ) ) {
     140           0 :     if( errno == ENOENT ) return 0;
     141           0 :     FD_LOG_ERR(( "opendir `%s` failed (%i-%s)", config->paths.ledger, errno, fd_io_strerror( errno ) ));
     142           0 :   }
     143             : 
     144           0 :   struct dirent * entry;
     145           0 :   errno = 0;
     146           0 :   while(( entry = readdir( dir ) )) {
     147           0 :     if( FD_LIKELY( !strcmp( entry->d_name, "." ) || !strcmp( entry->d_name, ".." ) ) ) continue;
     148             : 
     149             :     /* genesis.bin managed by genesis stage*/
     150           0 :     if( FD_LIKELY( !strcmp( entry->d_name, "genesis.bin" ) ) ) continue;
     151             : 
     152           0 :     char path1[ PATH_MAX ];
     153           0 :     FD_TEST( fd_cstr_printf_check( path1, PATH_MAX, NULL, "%s/%s", config->paths.ledger, entry->d_name ) );
     154             : 
     155           0 :     struct stat st;
     156           0 :     if( FD_UNLIKELY( lstat( path1, &st ) ) ) {
     157           0 :       if( FD_LIKELY( errno == ENOENT ) ) continue;
     158           0 :       FD_LOG_ERR(( "stat `%s` failed (%i-%s)", path1, errno, fd_io_strerror( errno ) ));
     159           0 :     }
     160             : 
     161           0 :     if( FD_UNLIKELY( S_ISDIR( st.st_mode ) ) ) {
     162           0 :       if( FD_UNLIKELY( -1==fd_file_util_rmtree( path1, 1 ) ) ) FD_LOG_ERR(( "rmtree `%s` failed (%i-%s)", path1, errno, fd_io_strerror( errno ) ));
     163           0 :     } else {
     164           0 :       if( FD_UNLIKELY( unlink( path1 ) && errno != ENOENT ) )
     165           0 :         FD_LOG_ERR(( "unlink `%s` failed (%i-%s)", path1, errno, fd_io_strerror( errno ) ));
     166           0 :     }
     167           0 :   }
     168             : 
     169           0 :   if( FD_UNLIKELY( errno && errno!=ENOENT ) ) FD_LOG_ERR(( "readdir `%s` failed (%i-%s)", config->paths.ledger, errno, fd_io_strerror( errno ) ));
     170           0 :   if( FD_UNLIKELY( closedir( dir ) ) ) FD_LOG_ERR(( "closedir `%s` failed (%i-%s)", config->paths.ledger, errno, fd_io_strerror( errno ) ));
     171             : 
     172           0 :   return 1;
     173           0 : }
     174             : 
     175             : static configure_result_t
     176           0 : check( config_t const * config ) {
     177           0 :   int has_non_genesis = 0;
     178             : 
     179           0 :   DIR * dir = opendir( config->paths.ledger );
     180           0 :   if( FD_UNLIKELY( !dir ) ) {
     181           0 :     if( FD_UNLIKELY( errno==ENOENT ) ) NOT_CONFIGURED( "ledger directory does not exist at `%s`", config->paths.ledger );
     182           0 :     FD_LOG_ERR(( "opendir `%s` failed (%i-%s)", config->paths.ledger, errno, fd_io_strerror( errno ) ));
     183           0 :   }
     184             : 
     185           0 :   struct dirent * entry;
     186           0 :   errno = 0;
     187           0 :   while(( entry = readdir( dir ) )) {
     188           0 :     if( FD_LIKELY( !strcmp( entry->d_name, "." ) || !strcmp( entry->d_name, ".." ) ) ) continue;
     189             : 
     190             :     /* genesis.bin managed by genesis stage*/
     191           0 :     if( FD_LIKELY( !strcmp( entry->d_name, "genesis.bin" ) ) ) continue;
     192           0 :     if( FD_LIKELY( !strcmp( entry->d_name, "rocksdb" ) ) ) {
     193           0 :       char rocksdb_path[ PATH_MAX ];
     194           0 :       fd_cstr_printf_check( rocksdb_path, PATH_MAX, NULL, "%s/rocksdb", config->paths.ledger );
     195             : 
     196           0 :       configure_result_t result = check_dir( rocksdb_path, config->uid, config->gid, S_IFDIR | S_IRUSR | S_IWUSR | S_IXUSR );
     197           0 :       if( FD_UNLIKELY( result.result != CONFIGURE_OK ) ) {
     198           0 :         if( FD_UNLIKELY( closedir( dir ) ) ) FD_LOG_ERR(( "closedir `%s` failed (%i-%s)", config->paths.ledger, errno, fd_io_strerror( errno ) ));
     199           0 :         return result;
     200           0 :       }
     201           0 :     }
     202             : 
     203           0 :     has_non_genesis = 1;
     204           0 :     break;
     205           0 :   }
     206             : 
     207           0 :   if( FD_UNLIKELY( errno && errno!=ENOENT ) ) FD_LOG_ERR(( "readdir `%s` failed (%i-%s)", config->paths.ledger, errno, fd_io_strerror( errno ) ));
     208           0 :   if( FD_UNLIKELY( closedir( dir ) ) ) FD_LOG_ERR(( "closedir `%s` failed (%i-%s)", config->paths.ledger, errno, fd_io_strerror( errno ) ));
     209             : 
     210           0 :   if( FD_LIKELY( has_non_genesis ) ) {
     211           0 :     PARTIALLY_CONFIGURED( "rocksdb directory exists at `%s`", config->paths.ledger );
     212           0 :   } else {
     213           0 :     NOT_CONFIGURED( "rocksdb directory does not exist at `%s`", config->paths.ledger );
     214           0 :   }
     215           0 : }
     216             : 
     217             : configure_stage_t fd_cfg_stage_blockstore = {
     218             :   .name            = NAME,
     219             :   .always_recreate = 1,
     220             :   .init            = init,
     221             :   .fini            = fini,
     222             :   .check           = check,
     223             : };
     224             : 
     225             : #undef NAME

Generated by: LCOV version 1.14