LCOV - code coverage report
Current view: top level - discof/genesis - fd_genesi_tile.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 458 0.0 %
Date: 2026-11-11 12:40:43 Functions: 0 18 0.0 %

          Line data    Source code
       1             : #define _GNU_SOURCE
       2             : #include "fd_genesi_tile.h"
       3             : #include "fd_genesis_client.h"
       4             : #include "../../disco/topo/fd_topo.h"
       5             : #include "../../disco/topo/fd_dns_resolve.h"
       6             : #include "../../disco/waker/fd_waker.h"
       7             : #include "../../disco/fd_clock_tile.h"
       8             : #include "../../flamenco/accdb/fd_accdb.h"
       9             : #include "../../flamenco/accdb/fd_accdb_shmem.h"
      10             : #include "../../flamenco/genesis/fd_genesis_parse.h"
      11             : #include "../../disco/events/generated/fd_event_gen.h"
      12             : #include "../../third_party/bzip2/bzlib.h"
      13             : #include "../../ballet/sha256/fd_sha256.h"
      14             : #include "../../flamenco/runtime/fd_hashes.h"
      15             : #include "../../util/archive/fd_tar.h"
      16             : 
      17             : #include <stdio.h>
      18             : #include <errno.h>
      19             : #include <fcntl.h>
      20             : #include <poll.h>
      21             : #include <sys/syscall.h>
      22             : #include <unistd.h>
      23             : #include <netinet/in.h>
      24             : #include <linux/fs.h>
      25             : 
      26             : #include <sys/socket.h>
      27             : #include <linux/futex.h>
      28             : #include "generated/fd_genesi_tile_seccomp.h"
      29             : 
      30             : static void *
      31             : bz2_malloc( void * opaque,
      32             :             int    items,
      33           0 :             int    size ) {
      34           0 :   fd_alloc_t * alloc = (fd_alloc_t *)opaque;
      35             : 
      36           0 :   if( FD_UNLIKELY( items<=0 || size<=0 ) ) {
      37           0 :     FD_LOG_WARNING(( "bz2_malloc: invalid items=%d or size=%d", items, size ));
      38           0 :     return NULL;
      39           0 :   }
      40           0 :   void * result = fd_alloc_malloc( alloc, alignof(max_align_t), (ulong)items*(ulong)size );
      41           0 :   if( FD_UNLIKELY( !result ) ) return NULL;
      42           0 :   return result;
      43           0 : }
      44             : 
      45             : static void
      46             : bz2_free( void * opaque,
      47           0 :           void * addr ) {
      48           0 :   fd_alloc_t * alloc = (fd_alloc_t *)opaque;
      49             : 
      50           0 :   if( FD_UNLIKELY( !addr ) ) return;
      51           0 :   fd_alloc_free( alloc, addr );
      52           0 : }
      53             : 
      54             : struct fd_genesi_tile {
      55             :   fd_accdb_t * accdb;
      56             :   fd_accdb_fork_id_t accdb_root;
      57             : 
      58             :   fd_hash_t genesis_hash[1];
      59             : 
      60             :   fd_genesis_client_t * client;
      61             : 
      62             :   ulong   waker_client_idx;
      63             :   ulong * waker_fseq;
      64             : 
      65             :   fd_clock_tile_t clock[1];
      66             : 
      67             :   fd_ip4_port_t entrypoints[ FD_TOPO_GOSSIP_ENTRYPOINTS_MAX ];
      68             :   ulong         entrypoints_cnt;
      69             : 
      70             :   fd_lthash_value_t lthash[1];
      71             : 
      72             :   int local_genesis;
      73             :   int bootstrap;
      74             :   int shutdown;
      75             : 
      76             :   int has_expected_genesis_hash;
      77             :   uchar expected_genesis_hash[ 32UL ];
      78             :   ushort expected_shred_version;
      79             :   int validate_genesis_hash;
      80             : 
      81             :   char genesis_path[ PATH_MAX ];
      82             : 
      83             :   int in_fd;
      84             :   int out_fd;
      85             :   int out_dir_fd;
      86             : 
      87             :   struct {
      88             :     fd_wksp_t * mem;
      89             :     ulong       chunk0;
      90             :   } out;
      91             : 
      92             :   fd_alloc_t * bz2_alloc;
      93             : 
      94             :   fd_genesis_t * genesis;
      95             :   uchar *        genesis_blob;
      96             :   ulong          genesis_blob_sz;
      97             :   ulong          max_message_size;
      98             : };
      99             : 
     100             : typedef struct fd_genesi_tile fd_genesi_tile_t;
     101             : 
     102             : FD_FN_CONST static inline ulong
     103           0 : scratch_align( void ) {
     104           0 :   ulong a = alignof( fd_genesi_tile_t );
     105           0 :   a = fd_ulong_max( a, fd_genesis_client_align() );
     106           0 :   a = fd_ulong_max( a, fd_alloc_align() );
     107           0 :   a = fd_ulong_max( a, fd_genesis_align() );
     108           0 :   return a;
     109           0 : }
     110             : 
     111             : FD_FN_PURE static inline ulong
     112           0 : scratch_footprint( fd_topo_tile_t const * tile ) {
     113           0 :   ulong l = FD_LAYOUT_INIT;
     114           0 :   l = FD_LAYOUT_APPEND( l, alignof( fd_genesi_tile_t ), sizeof( fd_genesi_tile_t )    );
     115           0 :   l = FD_LAYOUT_APPEND( l, fd_genesis_client_align(),   fd_genesis_client_footprint() );
     116           0 :   l = FD_LAYOUT_APPEND( l, fd_alloc_align(),            fd_alloc_footprint()          );
     117           0 :   if( FD_UNLIKELY( !tile->genesi.entrypoints_cnt ) ) {
     118           0 :     l = FD_LAYOUT_APPEND( l, fd_accdb_align(),          fd_accdb_footprint( tile->genesi.max_live_slots, 0 ) );
     119           0 :   }
     120           0 :   l = FD_LAYOUT_APPEND( l, fd_genesis_align(),          fd_genesis_footprint( fd_genesis_account_max( tile->genesi.max_message_size ) ) );
     121           0 :   l = FD_LAYOUT_APPEND( l, alignof(uchar),              tile->genesi.max_message_size + 4UL*FD_TAR_BLOCK_SZ );
     122           0 :   return FD_LAYOUT_FINI( l, scratch_align() );
     123           0 : }
     124             : 
     125             : FD_FN_CONST static inline ulong
     126           0 : loose_footprint( fd_topo_tile_t const * tile ) {
     127           0 :   (void)tile;
     128             :   /* Leftover space for bzip2 allocations */
     129           0 :   return 1UL<<26; /* 64 MiB */
     130           0 : }
     131             : 
     132             : static inline int
     133           0 : should_shutdown( fd_genesi_tile_t * ctx ) {
     134           0 :   return ctx->shutdown;
     135           0 : }
     136             : 
     137             : static void
     138           0 : during_housekeeping( fd_genesi_tile_t * ctx ) {
     139           0 :   if( FD_UNLIKELY( fd_clock_tile_recal_due( ctx->clock ) ) ) fd_clock_tile_recal( ctx->clock );
     140           0 : }
     141             : 
     142             : static long
     143           0 : next_deadline( fd_genesi_tile_t * ctx ) {
     144           0 :   if( FD_LIKELY( ctx->shutdown || ctx->local_genesis ) ) return LONG_MAX;
     145             : 
     146           0 :   long deadline = fd_genesis_client_deadline_nanos( ctx->client );
     147           0 :   if( FD_UNLIKELY( deadline==LONG_MAX ) ) return 0L; /* first poll starts the clock */
     148           0 :   return fd_clock_tile_wallclock_to_tickcount( ctx->clock, deadline );
     149           0 : }
     150             : 
     151             : static fd_accdb_fork_id_t
     152             : initialize_accdb( fd_accdb_t *         accdb,
     153             :                   fd_genesis_t const * genesis,
     154             :                   uchar const *        genesis_blob,
     155           0 :                   fd_lthash_value_t *  lthash ) {
     156           0 :   fd_accdb_fork_id_t fork_id = fd_accdb_attach_child( accdb, (fd_accdb_fork_id_t) {.val = USHORT_MAX } );
     157             : 
     158           0 :   for( ulong i=0UL; i<genesis->account_cnt; i++ ) {
     159           0 :     fd_genesis_account_t account[1];
     160           0 :     fd_genesis_account( genesis, genesis_blob, account, i );
     161             : 
     162           0 :     fd_acc_t acc = fd_accdb_write_one( accdb, fork_id, account->pubkey.key );
     163           0 :     fd_memcpy( acc.owner, account->owner.uc, 32UL );
     164           0 :     acc.lamports = account->lamports;
     165           0 :     acc.executable = !!account->executable;
     166           0 :     acc.data_len = account->data_len;
     167           0 :     fd_memcpy( acc.data, account->data, account->data_len );
     168             : 
     169           0 :     fd_lthash_value_t new_hash[1];
     170           0 :     fd_hashes_account_lthash_simple( account->pubkey.uc, acc.owner, acc.lamports, acc.executable, account->data, account->data_len, new_hash );
     171           0 :     fd_lthash_add( lthash, new_hash );
     172             : 
     173           0 :     acc.commit = 1;
     174           0 :     fd_accdb_unwrite_one( accdb, &acc );
     175           0 :   }
     176           0 :   return fork_id;
     177           0 : }
     178             : 
     179             : static inline void
     180             : verify_cluster_type( fd_genesis_t const * genesis,
     181             :                      fd_hash_t const *    genesis_hash,
     182           0 :                      char const *         genesis_path ) {
     183             : 
     184           0 :   fd_hash_t mainnet_hash[1];
     185           0 :   FD_TEST( fd_base58_decode_32( "5eykt4UsFv8P8NJdTREpY1vzqKqZKvdpKuc147dw2N9d", mainnet_hash->uc ) );
     186             : 
     187           0 :   fd_hash_t testnet_hash[1];
     188           0 :   FD_TEST( fd_base58_decode_32( "4uhcVJyU9pJkvQyS88uRDiswHXSCkY3zQawwpjk2NsNY", testnet_hash->uc ) );
     189             : 
     190           0 :   fd_hash_t devnet_hash[1];
     191           0 :   FD_TEST( fd_base58_decode_32( "EtWTRABZaYq6iMfeYKouRu166VU2xqa1wcaWoxPkrZBG", devnet_hash->uc ) );
     192             : 
     193           0 :   switch( genesis->cluster_type ) {
     194           0 :     case FD_GENESIS_TYPE_MAINNET: {
     195           0 :       if( FD_UNLIKELY( !fd_hash_eq( genesis_hash, mainnet_hash ) ) ) {
     196           0 :         FD_BASE58_ENCODE_32_BYTES( genesis_hash->uc, genesis_hash_b58 );
     197           0 :         FD_LOG_ERR(( "genesis file `%s` has cluster type MAINNET but unexpected genesis hash `%s`",
     198           0 :                      genesis_path, genesis_hash_b58 ));
     199           0 :       }
     200           0 :       break;
     201           0 :     }
     202           0 :     case FD_GENESIS_TYPE_TESTNET: {
     203           0 :       if( FD_UNLIKELY( !fd_hash_eq( genesis_hash, testnet_hash ) ) ) {
     204           0 :         FD_BASE58_ENCODE_32_BYTES( genesis_hash->uc, genesis_hash_b58 );
     205           0 :         FD_LOG_ERR(( "genesis file `%s` has cluster type TESTNET but unexpected genesis hash `%s`",
     206           0 :                      genesis_path, genesis_hash_b58 ));
     207           0 :       }
     208           0 :       break;
     209           0 :     }
     210           0 :     case FD_GENESIS_TYPE_DEVNET: {
     211           0 :       if( FD_UNLIKELY( !fd_hash_eq( genesis_hash, devnet_hash ) ) ) {
     212           0 :         FD_BASE58_ENCODE_32_BYTES( genesis_hash->uc, genesis_hash_b58 );
     213           0 :         FD_LOG_ERR(( "genesis file `%s` has cluster type DEVNET but unexpected genesis hash `%s`",
     214           0 :                      genesis_path, genesis_hash_b58 ));
     215           0 :       }
     216           0 :       break;
     217           0 :     }
     218           0 :     default:
     219           0 :       break;
     220           0 :   }
     221           0 : }
     222             : 
     223             : static void
     224             : after_credit( fd_genesi_tile_t *  ctx,
     225             :               fd_stem_context_t * stem,
     226             :               int *               opt_poll_in,
     227           0 :               int *               charge_busy ) {
     228           0 :   (void)opt_poll_in;
     229             : 
     230           0 :   if( FD_UNLIKELY( ctx->shutdown ) ) return;
     231             : 
     232           0 :   if( FD_LIKELY( ctx->local_genesis ) ) {
     233           0 :     FD_TEST( -1!=ctx->in_fd );
     234             : 
     235           0 :     if( FD_UNLIKELY( ctx->genesis_blob_sz>ctx->max_message_size ) ) {
     236           0 :       FD_LOG_ERR(( "Genesis file `%s` exceeds maximum size (blob_sz=%lu max=%lu)",
     237           0 :                    ctx->genesis_path, ctx->genesis_blob_sz, ctx->max_message_size ));
     238           0 :     }
     239           0 :     ulong msg_sz = sizeof(fd_genesis_meta_t) + ctx->genesis_blob_sz;
     240           0 :     if( FD_UNLIKELY( msg_sz>fd_genesi_tile_mtu( ctx->max_message_size ) ) ) {
     241           0 :       FD_LOG_ERR(( "The genesis file `%s` is too large for this Firedancer configuration (msg_sz=%lu max=%lu).\n"
     242           0 :                    "Cannot start Firedancer. Please use a different genesis config or increase `development.genesis.max_file_size_mib`.",
     243           0 :                    ctx->genesis_path, msg_sz, fd_genesi_tile_mtu( ctx->max_message_size ) ));
     244           0 :     }
     245             : 
     246           0 :     fd_genesis_meta_t * dst = fd_chunk_to_laddr( ctx->out.mem, ctx->out.chunk0 );
     247           0 :     memset( dst, 0, sizeof(fd_genesis_meta_t) );
     248           0 :     dst->creation_time_seconds = ctx->genesis->creation_time;
     249           0 :     dst->genesis_hash = *ctx->genesis_hash;
     250             : 
     251           0 :     if( FD_UNLIKELY( ctx->bootstrap ) ) {
     252           0 :       dst->accdb_root = ctx->accdb_root;
     253           0 :       dst->bootstrap  = 1;
     254           0 :       dst->has_lthash = 1;
     255           0 :       dst->lthash     = *ctx->lthash;
     256           0 :     }
     257             : 
     258           0 :     uchar * dst_blob = (uchar *)( dst+1 );
     259           0 :     dst->blob_sz = ctx->genesis_blob_sz;
     260           0 :     fd_memcpy( dst_blob, ctx->genesis_blob, ctx->genesis_blob_sz );
     261             : 
     262           0 :     fd_stem_publish( stem, 0UL, msg_sz, ctx->out.chunk0, 0UL, 0UL, 0UL, 0UL );
     263           0 :     *charge_busy = 1;
     264           0 :     FD_LOG_NOTICE(( "loaded local genesis.bin from file %s%s%s", fd_log_style_dim(), ctx->genesis_path, fd_log_style_normal() ));
     265             : 
     266           0 :     ctx->shutdown = 1;
     267           0 :   } else {
     268           0 :     int fired = fd_fseq_query( ctx->waker_fseq )==1UL;
     269           0 :     if( FD_LIKELY( fired ) ) fd_fseq_update( ctx->waker_fseq, 0UL );
     270           0 :     uchar * buffer;
     271           0 :     ulong buffer_sz;
     272           0 :     fd_ip4_port_t peer;
     273           0 :     int result = fd_genesis_client_poll( ctx->client, fd_clock_tile_now( ctx->clock ), &peer, &buffer, &buffer_sz, charge_busy );
     274           0 :     if( FD_LIKELY( fired ) ) fd_waker_client_rearm( ctx->waker_client_idx );
     275           0 :     if( FD_UNLIKELY( -1==result ) ) FD_LOG_ERR(( "failed to retrieve genesis.bin from any configured gossip entrypoints" ));
     276           0 :     if( FD_LIKELY( 1==result ) ) return;
     277             : 
     278           0 :     uchar * decompressed = ctx->genesis_blob;
     279           0 :     ulong   actual_decompressed_sz = 0UL;
     280           0 :     bz_stream bzstrm = {0};
     281           0 :     bzstrm.bzalloc = bz2_malloc;
     282           0 :     bzstrm.bzfree  = bz2_free;
     283           0 :     bzstrm.opaque  = ctx->bz2_alloc;
     284           0 :     int bzerr = BZ2_bzDecompressInit( &bzstrm, 0, 0 );
     285           0 :     if( FD_UNLIKELY( BZ_OK!=bzerr ) ) FD_LOG_ERR(( "BZ2_bzDecompressInit() failed (%d)", bzerr ));
     286             : 
     287           0 :     ulong decompressed_sz = ctx->max_message_size + 4UL*FD_TAR_BLOCK_SZ;
     288             : 
     289           0 :     bzstrm.next_in   = (char *)buffer;
     290           0 :     bzstrm.avail_in  = (uint)buffer_sz;
     291           0 :     bzstrm.next_out  = (char *)decompressed;
     292           0 :     bzstrm.avail_out = (uint)decompressed_sz;
     293           0 :     bzerr = BZ2_bzDecompress( &bzstrm );
     294             :     /* genesis.bin is the first archive entry.  Stop after filling the
     295             :        bounded prefix buffer instead of retaining the trailing RocksDB
     296             :        entries included by Agave. */
     297           0 :     if( FD_UNLIKELY( BZ_STREAM_END!=bzerr && !(BZ_OK==bzerr && !bzstrm.avail_out) ) ) {
     298           0 :       FD_LOG_ERR(( "BZ2_bzDecompress() failed or input was truncated (%d)", bzerr ));
     299           0 :     }
     300             : 
     301           0 :     actual_decompressed_sz = decompressed_sz - (ulong)bzstrm.avail_out;
     302             : 
     303           0 :     bzerr = BZ2_bzDecompressEnd( &bzstrm );
     304           0 :     if( FD_UNLIKELY( BZ_OK!=bzerr ) ) FD_LOG_ERR(( "BZ2_bzDecompressEnd() failed (%d)", bzerr ));
     305             : 
     306           0 :     FD_TEST( actual_decompressed_sz>=FD_TAR_BLOCK_SZ );
     307             : 
     308           0 :     fd_tar_meta_t const * meta = (fd_tar_meta_t const *)decompressed;
     309           0 :     FD_TEST( !strcmp( meta->name, "genesis.bin" ) );
     310           0 :     uchar const * blob    = decompressed+FD_TAR_BLOCK_SZ;
     311           0 :     ulong         blob_sz = fd_tar_meta_get_size( meta );
     312           0 :     if( FD_UNLIKELY( blob_sz==ULONG_MAX ) ) {
     313           0 :       FD_LOG_ERR(( "Genesis blob from peer at `http://" FD_IP4_ADDR_FMT ":%hu` has invalid tar header size",
     314           0 :                    FD_IP4_ADDR_FMT_ARGS( peer.addr ), fd_ushort_bswap( peer.port ) ));
     315           0 :     }
     316           0 :     if( FD_UNLIKELY( blob_sz>ctx->max_message_size ) ) {
     317           0 :       FD_LOG_ERR(( "Genesis blob from peer at `http://" FD_IP4_ADDR_FMT ":%hu` exceeds maximum size (blob_sz=%lu max=%lu)",
     318           0 :                    FD_IP4_ADDR_FMT_ARGS( peer.addr ), fd_ushort_bswap( peer.port ), blob_sz, ctx->max_message_size ));
     319           0 :     }
     320           0 :     FD_TEST( actual_decompressed_sz>=fd_ulong_sat_add( FD_TAR_BLOCK_SZ, blob_sz ) );
     321             : 
     322           0 :     fd_hash_t hash[1];
     323           0 :     fd_sha256_hash( blob, blob_sz, hash->uc );
     324             : 
     325             :     /* Can't verify expected_shred_version here because it needs to be
     326             :        mixed in with hard_forks from the snapshot.  Replay tile will
     327             :        combine them and do this verification. */
     328             : 
     329           0 :     if( FD_LIKELY( ctx->has_expected_genesis_hash && memcmp( hash, ctx->expected_genesis_hash, 32UL ) ) ) {
     330           0 :       FD_BASE58_ENCODE_32_BYTES( ctx->expected_genesis_hash, expected_genesis_hash_b58 );
     331           0 :       FD_BASE58_ENCODE_32_BYTES( hash->uc, hash_b58 );
     332           0 :       FD_LOG_ERR(( "An expected genesis hash of `%s` has been set in your configuration file at [consensus.expected_genesis_hash] "
     333           0 :                    "but the genesis hash derived from the peer at `http://" FD_IP4_ADDR_FMT ":%hu` has unexpected hash `%s`",
     334           0 :                    expected_genesis_hash_b58, FD_IP4_ADDR_FMT_ARGS( peer.addr ), fd_ushort_bswap( peer.port ), hash_b58 ));
     335           0 :     }
     336             : 
     337           0 :     FD_TEST( !ctx->bootstrap );
     338             : 
     339           0 :     fd_genesis_t * genesis = fd_genesis_parse( ctx->genesis, blob, blob_sz );
     340           0 :     if( FD_UNLIKELY( !genesis ) ) {
     341           0 :       FD_LOG_ERR(( "unable to decode downloaded solana genesis file due to violated hardcoded limits" ));
     342           0 :     }
     343             : 
     344           0 :     if( FD_LIKELY( ctx->validate_genesis_hash ) ) {
     345           0 :       verify_cluster_type( genesis, hash, ctx->genesis_path );
     346           0 :     }
     347             : 
     348           0 :     ulong msg_sz = sizeof(fd_genesis_meta_t) + blob_sz;
     349           0 :     if( FD_UNLIKELY( msg_sz>fd_genesi_tile_mtu( ctx->max_message_size ) ) ) {
     350           0 :       FD_LOG_ERR(( "The genesis blob downloaded from peer at `http://" FD_IP4_ADDR_FMT ":%hu` is too large for this Firedancer configuration (msg_sz=%lu max=%lu).\n"
     351           0 :                    "Cannot start Firedancer. Please use a different genesis config or increase `development.genesis.max_file_size_mib`.",
     352           0 :                    FD_IP4_ADDR_FMT_ARGS( peer.addr ), fd_ushort_bswap( peer.port ), msg_sz, fd_genesi_tile_mtu( ctx->max_message_size ) ));
     353           0 :     }
     354             : 
     355           0 :     fd_genesis_meta_t * dst = fd_chunk_to_laddr( ctx->out.mem, ctx->out.chunk0 );
     356           0 :     memset( dst, 0, sizeof(fd_genesis_meta_t) );
     357           0 :     dst->creation_time_seconds = genesis->creation_time;
     358           0 :     dst->genesis_hash = *hash;
     359             : 
     360           0 :     uchar * dst_blob = (uchar *)( dst+1 );
     361           0 :     dst->blob_sz = blob_sz;
     362           0 :     fd_memcpy( dst_blob, blob, blob_sz );
     363             : 
     364           0 :     fd_stem_publish( stem, 0UL, msg_sz, ctx->out.chunk0, 0UL, 0UL, 0UL, 0UL );
     365             : 
     366           0 :     ulong bytes_written = 0UL;
     367           0 :     while( bytes_written<blob_sz ) {
     368           0 :       long result = write( ctx->out_fd, blob+bytes_written, blob_sz-bytes_written );
     369           0 :       if( FD_UNLIKELY( -1==result ) ) FD_LOG_ERR(( "write() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     370           0 :       bytes_written += (ulong)result;
     371           0 :     }
     372             : 
     373           0 :     char basename[ PATH_MAX ];
     374           0 :     const char * last_slash = strrchr( ctx->genesis_path, '/' );
     375           0 :     if( FD_LIKELY( last_slash ) ) FD_TEST( fd_cstr_printf_check( basename, PATH_MAX, NULL, "%s", last_slash+1UL ) );
     376           0 :     else                          FD_TEST( fd_cstr_printf_check( basename, PATH_MAX, NULL, "%s", ctx->genesis_path ) );
     377             : 
     378           0 :     char basename_partial[ PATH_MAX ];
     379           0 :     FD_TEST( fd_cstr_printf_check( basename_partial, PATH_MAX, NULL, "%s.partial", basename ) );
     380             : 
     381           0 :     int err = (int)syscall( SYS_renameat2, ctx->out_dir_fd, basename_partial, ctx->out_dir_fd, basename, RENAME_NOREPLACE );
     382           0 :     if( FD_UNLIKELY( -1==err ) ) FD_LOG_ERR(( "renameat2() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     383             : 
     384           0 :     FD_LOG_NOTICE(( "retrieved genesis %s%s%s from peer at %shttp://" FD_IP4_ADDR_FMT ":%hu/genesis.tar.bz2%s",
     385           0 :                     fd_log_style_dim(), ctx->genesis_path, fd_log_style_normal(),
     386           0 :                     fd_log_style_dim(), FD_IP4_ADDR_FMT_ARGS( peer.addr ), fd_ushort_bswap( peer.port ), fd_log_style_normal() ));
     387             : 
     388           0 :     ctx->shutdown = 1;
     389           0 :   }
     390           0 : }
     391             : 
     392             : static void
     393             : process_local_genesis( fd_genesi_tile_t * ctx,
     394           0 :                        char const *       genesis_path ) {
     395           0 :   ctx->genesis_blob_sz = 0UL;
     396           0 :   for(;;) {
     397           0 :     if( FD_UNLIKELY( ctx->genesis_blob_sz==ctx->max_message_size ) ) {
     398           0 :       uchar extra;
     399           0 :       long result = read( ctx->in_fd, &extra, 1UL );
     400           0 :       if( FD_UNLIKELY( -1==result ) ) FD_LOG_ERR(( "read() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     401           0 :       if( FD_UNLIKELY( result ) ) {
     402           0 :         FD_LOG_ERR(( "The genesis file at `%s` exceeds `development.genesis.max_file_size_mib` (%lu MiB).",
     403           0 :                      genesis_path, ctx->max_message_size>>20 ));
     404           0 :       }
     405           0 :       break;
     406           0 :     }
     407           0 :     long result = read( ctx->in_fd, ctx->genesis_blob+ctx->genesis_blob_sz, ctx->max_message_size-ctx->genesis_blob_sz );
     408           0 :     if( FD_UNLIKELY( -1==result ) ) FD_LOG_ERR(( "read() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     409           0 :     if( FD_UNLIKELY( !result ) ) break;
     410           0 :     ctx->genesis_blob_sz += (ulong)result;
     411           0 :   }
     412             : 
     413           0 :   if( FD_UNLIKELY( -1==close( ctx->in_fd ) ) ) FD_LOG_ERR(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     414             : 
     415           0 :   fd_genesis_t * genesis = fd_genesis_parse( ctx->genesis, ctx->genesis_blob, ctx->genesis_blob_sz );
     416           0 :   if( FD_UNLIKELY( !genesis ) ) {
     417           0 :     FD_LOG_ERR(( "unable to decode solana genesis from local file due to violated hardcoded limits" ));
     418           0 :   }
     419             : 
     420           0 :   fd_sha256_hash( ctx->genesis_blob, ctx->genesis_blob_sz, ctx->genesis_hash );
     421           0 :   if( FD_LIKELY( ctx->validate_genesis_hash ) ) {
     422           0 :     verify_cluster_type( genesis, ctx->genesis_hash, genesis_path );
     423           0 :   }
     424             : 
     425           0 :   if( FD_UNLIKELY( ctx->bootstrap && ctx->expected_shred_version ) ) {
     426           0 :     ushort xor = 0;
     427           0 :     for( ulong i=0UL; i<16UL; i++ ) xor ^= ctx->genesis_hash->us[ i ];
     428             : 
     429           0 :     xor = fd_ushort_bswap( xor );
     430           0 :     xor = fd_ushort_if( xor<USHORT_MAX, (ushort)(xor + 1), USHORT_MAX );
     431             : 
     432           0 :     FD_TEST( xor );
     433             : 
     434           0 :     if( FD_UNLIKELY( xor!=ctx->expected_shred_version ) ) {
     435           0 :       FD_LOG_ERR(( "This node is bootstrapping the cluster as it has no gossip entrypoints provided, but "
     436           0 :                    "a [consensus.expected_shred_version] of %hu is provided which does not match the shred "
     437           0 :                    "version of %hu computed from the genesis.bin file at `%s`",
     438           0 :                    ctx->expected_shred_version, xor, genesis_path ));
     439           0 :     }
     440           0 :   }
     441             : 
     442           0 :   if( FD_LIKELY( ctx->has_expected_genesis_hash && memcmp( ctx->genesis_hash, ctx->expected_genesis_hash, 32UL ) ) ) {
     443           0 :     FD_BASE58_ENCODE_32_BYTES( ctx->expected_genesis_hash, expected_genesis_hash_b58 );
     444           0 :     FD_BASE58_ENCODE_32_BYTES( ctx->genesis_hash->uc,      genesis_hash_b58          );
     445           0 :     FD_LOG_ERR(( "An expected genesis hash of %s%s%s has been set in your configuration file at [consensus.expected_genesis_hash] "
     446           0 :                  "but the genesis hash derived from the genesis file at %s%s%s has unexpected hash %s%s%s",
     447           0 :                  fd_log_style_bold(), expected_genesis_hash_b58, fd_log_style_normal(),
     448           0 :                  fd_log_style_dim(), genesis_path, fd_log_style_normal(),
     449           0 :                  fd_log_style_bold(), genesis_hash_b58, fd_log_style_normal() ));
     450           0 :   }
     451           0 : }
     452             : 
     453             : static void
     454             : privileged_init( fd_topo_t const *      topo,
     455           0 :                  fd_topo_tile_t const * tile ) {
     456           0 :   void * scratch = fd_topo_obj_laddr( topo, tile->tile_obj_id );
     457             : 
     458           0 :   FD_SCRATCH_ALLOC_INIT( l, scratch );
     459           0 :   fd_genesi_tile_t * ctx        = FD_SCRATCH_ALLOC_APPEND( l, alignof( fd_genesi_tile_t ), sizeof( fd_genesi_tile_t )    );
     460           0 :   fd_genesis_client_t * _client = FD_SCRATCH_ALLOC_APPEND( l, fd_genesis_client_align(),   fd_genesis_client_footprint() );
     461             : 
     462           0 :   fd_memset( ctx, 0, sizeof( fd_genesi_tile_t ) );
     463             : 
     464           0 :   ctx->local_genesis = 1;
     465           0 :   ctx->in_fd = open( tile->genesi.genesis_path, O_RDONLY|O_CLOEXEC );
     466           0 :   if( FD_UNLIKELY( -1==ctx->in_fd ) ) {
     467           0 :     if( FD_LIKELY( errno==ENOENT  ) ) {
     468           0 :       FD_LOG_INFO(( "no local genesis.bin file found at `%s`", tile->genesi.genesis_path ));
     469             : 
     470           0 :       if( FD_UNLIKELY( !tile->genesi.entrypoints_cnt ) ) {
     471           0 :         FD_LOG_ERR(( "This node is bootstrapping the cluster as it has no gossip entrypoints provided, but "
     472           0 :                      "the genesis.bin file at `%s` does not exist.  Please provide a valid genesis.bin "
     473           0 :                      "file by running genesis, or join an existing cluster.",
     474           0 :                      tile->genesi.genesis_path ));
     475           0 :       } else {
     476           0 :         if( FD_UNLIKELY( !tile->genesi.allow_download ) ) {
     477           0 :           FD_LOG_ERR(( "There is no genesis.bin file at `%s` and automatic downloading is disabled as "
     478           0 :                        "genesis_download is false in your configuration file.  Please either provide a valid "
     479           0 :                        "genesis.bin file locally, or allow downloading from a gossip entrypoint.",
     480           0 :                        tile->genesi.genesis_path ));
     481           0 :         } else {
     482           0 :           char basename[ PATH_MAX ];
     483           0 :           fd_cstr_ncpy( basename, tile->genesi.genesis_path, PATH_MAX );
     484           0 :           char * last_slash = strrchr( basename, '/' );
     485           0 :           if( FD_LIKELY( last_slash ) ) *last_slash = '\0';
     486             : 
     487           0 :           ctx->out_dir_fd = open( basename, O_RDONLY|O_CLOEXEC|O_DIRECTORY );
     488           0 :           if( FD_UNLIKELY( -1==ctx->out_dir_fd ) ) FD_LOG_ERR(( "open() failed for genesis dir `%s` (%i-%s)", basename, errno, fd_io_strerror( errno ) ));
     489             : 
     490             :           /* Switch to non-root uid/gid for file creation.  Permissions checks
     491             :             are still done as root. */
     492           0 :           gid_t gid = getgid();
     493           0 :           uid_t uid = getuid();
     494           0 :           if( FD_LIKELY( !gid && -1==syscall( __NR_setresgid, -1, tile->genesi.target_gid, -1 ) ) ) FD_LOG_ERR(( "setresgid() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     495           0 :           if( FD_LIKELY( !uid && -1==syscall( __NR_setresuid, -1, tile->genesi.target_uid, -1 ) ) ) FD_LOG_ERR(( "setresuid() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     496             : 
     497           0 :           char const * genesis_basename = strrchr( tile->genesi.genesis_path, '/' );
     498           0 :           genesis_basename = genesis_basename ? genesis_basename+1UL : tile->genesi.genesis_path;
     499             : 
     500           0 :           char partialname[ PATH_MAX ];
     501           0 :           FD_TEST( fd_cstr_printf_check( partialname, PATH_MAX, NULL, "%s.partial", genesis_basename ) );
     502           0 :           ctx->out_fd = openat( ctx->out_dir_fd, partialname, O_CREAT|O_WRONLY|O_CLOEXEC|O_TRUNC, S_IRUSR|S_IWUSR );
     503           0 :           if( FD_UNLIKELY( -1==ctx->out_fd ) ) FD_LOG_ERR(( "openat() failed for genesis file `%s.partial` (%i-%s)", tile->genesi.genesis_path, errno, fd_io_strerror( errno ) ));
     504             : 
     505           0 :           if( FD_UNLIKELY( -1==syscall( __NR_setresuid, -1, uid, -1 ) ) ) FD_LOG_ERR(( "setresuid() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     506           0 :           if( FD_UNLIKELY( -1==syscall( __NR_setresgid, -1, gid, -1 ) ) ) FD_LOG_ERR(( "setresgid() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     507             : 
     508           0 :           ctx->local_genesis = 0;
     509           0 :           ctx->client = fd_genesis_client_join( fd_genesis_client_new( _client ) );
     510           0 :           FD_TEST( ctx->client );
     511             : 
     512           0 :           fd_dns_resolve_peers( tile->genesi.entrypoints[ 0 ], sizeof(tile->genesi.entrypoints[ 0 ]), tile->genesi.entrypoints_cnt, "gossip.entrypoints", ctx->entrypoints );
     513           0 :           ctx->entrypoints_cnt = tile->genesi.entrypoints_cnt;
     514             : 
     515           0 :           fd_genesis_client_init( ctx->client, ctx->entrypoints, ctx->entrypoints_cnt, FD_WAKER_INNER_FD( tile->waker_client_idx ) );
     516           0 :         }
     517           0 :       }
     518           0 :     } else {
     519           0 :       FD_LOG_ERR(( "could not open genesis.bin file at `%s` (%i-%s)", tile->genesi.genesis_path, errno, fd_io_strerror( errno ) ));
     520           0 :     }
     521           0 :   }
     522           0 : }
     523             : 
     524             : static void
     525             : unprivileged_init( fd_topo_t const *      topo,
     526           0 :                    fd_topo_tile_t const * tile ) {
     527           0 :   void * scratch = fd_topo_obj_laddr( topo, tile->tile_obj_id );
     528             : 
     529           0 :   FD_SCRATCH_ALLOC_INIT( l, scratch );
     530           0 :   fd_genesi_tile_t * ctx = FD_SCRATCH_ALLOC_APPEND( l, alignof( fd_genesi_tile_t ), sizeof( fd_genesi_tile_t )                        );
     531           0 :                            FD_SCRATCH_ALLOC_APPEND( l, fd_genesis_client_align(),   fd_genesis_client_footprint()                     );
     532           0 :   void * _alloc          = FD_SCRATCH_ALLOC_APPEND( l, fd_alloc_align(),            fd_alloc_footprint()                              );
     533           0 :   void * _accdb          = !tile->genesi.entrypoints_cnt ?
     534           0 :                            FD_SCRATCH_ALLOC_APPEND( l, fd_accdb_align(),            fd_accdb_footprint( tile->genesi.max_live_slots, 0 ) ) :
     535           0 :                            NULL;
     536           0 :   void * _genesis        = FD_SCRATCH_ALLOC_APPEND( l, fd_genesis_align(),          fd_genesis_footprint( fd_genesis_account_max( tile->genesi.max_message_size ) ) );
     537           0 :   void * _genesis_blob   = FD_SCRATCH_ALLOC_APPEND( l, alignof(uchar),              tile->genesi.max_message_size + 4UL*FD_TAR_BLOCK_SZ );
     538             : 
     539           0 :   fd_lthash_zero( ctx->lthash );
     540             : 
     541           0 :   ctx->waker_client_idx = tile->waker_client_idx;
     542           0 :   FD_TEST( ctx->waker_client_idx!=ULONG_MAX );
     543           0 :   ctx->waker_fseq = fd_fseq_join( fd_topo_obj_laddr( topo, tile->waker_fseq_obj_id ) );
     544           0 :   FD_TEST( ctx->waker_fseq );
     545           0 :   fd_clock_tile_init( ctx->clock );
     546             : 
     547           0 :   ctx->genesis = fd_genesis_new( _genesis, fd_genesis_account_max( tile->genesi.max_message_size ) );
     548           0 :   FD_TEST( ctx->genesis );
     549           0 :   ctx->genesis_blob = _genesis_blob;
     550           0 :   ctx->max_message_size = tile->genesi.max_message_size;
     551           0 :   ctx->shutdown = 0;
     552           0 :   ctx->bootstrap = !tile->genesi.entrypoints_cnt;
     553           0 :   ctx->expected_shred_version = tile->genesi.expected_shred_version;
     554           0 :   ctx->has_expected_genesis_hash = tile->genesi.has_expected_genesis_hash;
     555           0 :   ctx->validate_genesis_hash = tile->genesi.validate_genesis_hash;
     556           0 :   fd_memcpy( ctx->expected_genesis_hash, tile->genesi.expected_genesis_hash, 32UL );
     557             : 
     558           0 :   ctx->accdb = NULL;
     559           0 :   if( FD_UNLIKELY( ctx->bootstrap ) ) {
     560           0 :     void * _accdb_shmem = fd_topo_obj_laddr( topo, tile->genesi.accdb_obj_id );
     561           0 :     fd_accdb_shmem_t * accdb_shmem = fd_accdb_shmem_join( _accdb_shmem );
     562           0 :     FD_TEST( accdb_shmem );
     563           0 :     fd_sleep_t * accdb_sleep = topo->sleep_obj_id!=ULONG_MAX ? fd_sleep_join( fd_topo_obj_laddr( topo, topo->sleep_obj_id ) ) : NULL;
     564           0 :     ctx->accdb = fd_accdb_join( fd_accdb_new( _accdb, accdb_shmem, FD_ACCDB_FD_RW, 0UL, NULL, accdb_sleep, fd_topo_find_tile( topo, "accdb", 0UL ), 0 ) );
     565           0 :     FD_TEST( ctx->accdb );
     566           0 :   }
     567             : 
     568           0 :   if( FD_LIKELY( -1!=ctx->in_fd ) ) {
     569           0 :     process_local_genesis( ctx, tile->genesi.genesis_path );
     570           0 :     if( FD_UNLIKELY( ctx->bootstrap ) ) {
     571           0 :       ctx->accdb_root = initialize_accdb( ctx->accdb, ctx->genesis, ctx->genesis_blob, ctx->lthash );
     572           0 :       fd_accdb_flush_metrics( ctx->accdb );
     573           0 :     }
     574           0 :   }
     575             : 
     576           0 :   FD_TEST( fd_cstr_printf_check( ctx->genesis_path, PATH_MAX, NULL, "%s", tile->genesi.genesis_path ) );
     577             : 
     578           0 :   FD_TEST( tile->out_cnt==1UL );
     579           0 :   fd_topo_link_t const * out_link = &topo->links[ tile->out_link_id[ 0 ] ];
     580           0 :   FD_TEST( out_link->depth==1UL );  /* buffer holds a single message (dcache not a ring buffer) */
     581           0 :   FD_TEST( out_link->mtu>=fd_genesi_tile_mtu( tile->genesi.max_message_size ) );
     582           0 :   ctx->out.mem    = fd_wksp_containing( out_link->dcache );
     583           0 :   ctx->out.chunk0 = fd_dcache_compact_chunk0( ctx->out.mem, out_link->dcache );
     584             : 
     585           0 :   ctx->bz2_alloc = fd_alloc_join( fd_alloc_new( _alloc, 1UL ), 1UL );
     586           0 :   FD_TEST( ctx->bz2_alloc );
     587             : 
     588           0 :   ulong scratch_top = FD_SCRATCH_ALLOC_FINI( l, scratch_align() );
     589           0 :   if( FD_UNLIKELY( scratch_top > (ulong)scratch + scratch_footprint( tile ) ) )
     590           0 :     FD_LOG_ERR(( "scratch overflow %lu %lu %lu", scratch_top - (ulong)scratch - scratch_footprint( tile ), scratch_top, (ulong)scratch + scratch_footprint( tile ) ));
     591           0 : }
     592             : 
     593             : static ulong
     594             : rlimit_file_cnt( fd_topo_t const *      topo FD_PARAM_UNUSED,
     595           0 :                  fd_topo_tile_t const * tile ) {
     596           0 :   return 1UL +                         /* stderr */
     597           0 :          1UL +                         /* logfile */
     598           0 :          1UL +                         /* genesis file */
     599           0 :          1UL +                         /* genesis dir */
     600           0 :          tile->genesi.entrypoints_cnt; /* for the client */
     601           0 : }
     602             : 
     603             : static ulong
     604             : populate_allowed_seccomp( fd_topo_t const *      topo,
     605             :                           fd_topo_tile_t const * tile,
     606             :                           ulong                  out_cnt,
     607           0 :                           struct sock_filter *   out ) {
     608             : 
     609           0 :   void * scratch = fd_topo_obj_laddr( topo, tile->tile_obj_id );
     610             : 
     611           0 :   FD_SCRATCH_ALLOC_INIT( l, scratch );
     612           0 :   fd_genesi_tile_t * ctx = FD_SCRATCH_ALLOC_APPEND( l, alignof( fd_genesi_tile_t ), sizeof( fd_genesi_tile_t ) );
     613             : 
     614           0 :   uint in_fd, out_fd, out_dir_fd;
     615           0 :   if( FD_LIKELY( -1!=ctx->in_fd ) ) {
     616           0 :     in_fd      = (uint)ctx->in_fd;
     617           0 :     out_fd     = (uint)-1;
     618           0 :     out_dir_fd = (uint)-1;
     619           0 :   } else {
     620           0 :     in_fd      = (uint)-1;
     621           0 :     out_fd     = (uint)ctx->out_fd;
     622           0 :     out_dir_fd = (uint)ctx->out_dir_fd;
     623           0 :   }
     624             : 
     625           0 :   uint accounts_fd = !tile->genesi.entrypoints_cnt ? (uint)FD_ACCDB_FD_RW : (uint)-1;
     626           0 :   uint epoll_inner_fd = (uint)FD_WAKER_INNER_FD( tile->waker_client_idx );
     627           0 :   uint epoll_outer_fd = (uint)FD_WAKER_OUTER_FD;
     628           0 :   populate_sock_filter_policy_fd_genesi_tile( out_cnt, out, (uint)fd_log_private_logfile_fd(), in_fd, out_fd, out_dir_fd, accounts_fd, epoll_inner_fd, epoll_outer_fd );
     629           0 :   return sock_filter_policy_fd_genesi_tile_instr_cnt;
     630           0 : }
     631             : 
     632             : static ulong
     633             : populate_allowed_fds( fd_topo_t const *      topo,
     634             :                       fd_topo_tile_t const * tile,
     635             :                       ulong                  out_fds_cnt,
     636           0 :                       int *                  out_fds ) {
     637           0 :   void * scratch = fd_topo_obj_laddr( topo, tile->tile_obj_id );
     638             : 
     639           0 :   FD_SCRATCH_ALLOC_INIT( l, scratch );
     640           0 :   fd_genesi_tile_t * ctx = FD_SCRATCH_ALLOC_APPEND( l, alignof( fd_genesi_tile_t ), sizeof( fd_genesi_tile_t ) );
     641             : 
     642           0 :   if( FD_UNLIKELY( out_fds_cnt<tile->genesi.entrypoints_cnt+8UL ) ) FD_LOG_ERR(( "out_fds_cnt %lu", out_fds_cnt ));
     643             : 
     644           0 :   ulong out_cnt = 0UL;
     645           0 :   out_fds[ out_cnt++ ] = 2; /* stderr */
     646           0 :   if( FD_LIKELY( -1!=fd_log_private_logfile_fd() ) )
     647           0 :     out_fds[ out_cnt++ ] = fd_log_private_logfile_fd(); /* logfile */
     648           0 :   out_fds[ out_cnt++ ] = FD_WAKER_OUTER_FD;                           /* waker outer epoll fd (rearm) */
     649           0 :   out_fds[ out_cnt++ ] = FD_WAKER_INNER_FD( tile->waker_client_idx ); /* waker inner epoll fd */
     650           0 :   if( FD_UNLIKELY( !tile->genesi.entrypoints_cnt ) )
     651           0 :     out_fds[ out_cnt++ ] = FD_ACCDB_FD_RW; /* accounts db */
     652             : 
     653           0 :   if( FD_UNLIKELY( -1==ctx->in_fd ) ) {
     654           0 :     FD_TEST( -1!=ctx->out_dir_fd );
     655           0 :     FD_TEST( -1!=ctx->out_fd );
     656           0 :     out_fds[ out_cnt++ ] = ctx->out_dir_fd;
     657           0 :     out_fds[ out_cnt++ ] = ctx->out_fd;
     658             : 
     659           0 :     for( ulong i=0UL; i<tile->genesi.entrypoints_cnt; i++ ) {
     660           0 :       int fd = fd_genesis_client_get_pollfds( ctx->client )[ i ].fd;
     661           0 :       if( FD_LIKELY( -1!=fd ) ) out_fds[ out_cnt++ ] = fd;
     662           0 :     }
     663           0 :   } else {
     664           0 :     FD_TEST( -1!=ctx->in_fd );
     665           0 :     out_fds[ out_cnt++ ] = ctx->in_fd;
     666           0 :   }
     667             : 
     668           0 :   return out_cnt;
     669           0 : }
     670             : 
     671           0 : #define STEM_LAZY ((long)1e5) /* 0.1ms */
     672           0 : #define STEM_BURST (1UL)
     673             : 
     674           0 : #define STEM_CALLBACK_CONTEXT_TYPE  fd_genesi_tile_t
     675           0 : #define STEM_CALLBACK_CONTEXT_ALIGN alignof(fd_genesi_tile_t)
     676             : 
     677           0 : #define STEM_CALLBACK_DURING_HOUSEKEEPING during_housekeeping
     678           0 : #define STEM_CALLBACK_NEXT_DEADLINE       next_deadline
     679           0 : #define STEM_CALLBACK_AFTER_CREDIT        after_credit
     680           0 : #define STEM_CALLBACK_SHOULD_SHUTDOWN     should_shutdown
     681             : 
     682             : #include "../../disco/stem/fd_stem.c"
     683             : 
     684             : static ulong
     685           0 : max_event_sz( fd_topo_tile_t const * tile FD_PARAM_UNUSED ) {
     686           0 :   return sizeof(fd_event_accdb_partition_added_t);
     687           0 : }
     688             : 
     689             : fd_topo_run_tile_t fd_tile_genesi = {
     690             :   .name                     = "genesi",
     691             :   .rlimit_file_cnt_fn       = rlimit_file_cnt,
     692             :   .allow_connect            = 1,
     693             :   .allow_renameat           = 1,
     694             :   .populate_allowed_seccomp = populate_allowed_seccomp,
     695             :   .populate_allowed_fds     = populate_allowed_fds,
     696             :   .loose_footprint          = loose_footprint,
     697             :   .scratch_align            = scratch_align,
     698             :   .scratch_footprint        = scratch_footprint,
     699             :   .privileged_init          = privileged_init,
     700             :   .unprivileged_init        = unprivileged_init,
     701             :   .max_event_sz             = max_event_sz,
     702             :   .run                      = stem_run,
     703             : };

Generated by: LCOV version 1.14