LCOV - code coverage report
Current view: top level - discof/genesis - fd_genesi_tile.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 365 0.0 %
Date: 2025-10-27 04:40:00 Functions: 0 15 0.0 %

          Line data    Source code
       1             : #include <linux/limits.h>
       2             : #define _GNU_SOURCE
       3             : #include "fd_genesi_tile.h"
       4             : #include "fd_genesis_client.h"
       5             : #include "../../disco/topo/fd_topo.h"
       6             : #include "../../ballet/sha256/fd_sha256.h"
       7             : #include "../../flamenco/runtime/fd_txn_account.h"
       8             : #include "../../flamenco/accdb/fd_accdb_admin.h"
       9             : #include "../../flamenco/accdb/fd_accdb_user.h"
      10             : #include "../../flamenco/runtime/fd_hashes.h"
      11             : #include "../../util/archive/fd_tar.h"
      12             : 
      13             : #include <stdio.h>
      14             : #include <errno.h>
      15             : #include <fcntl.h>
      16             : #include <sys/poll.h>
      17             : #include <sys/socket.h>
      18             : #include <sys/stat.h>
      19             : #include <unistd.h>
      20             : #include <netinet/in.h>
      21             : #include <linux/fs.h>
      22             : #include <bzlib.h>
      23             : 
      24             : #include "generated/fd_genesi_tile_seccomp.h"
      25             : 
      26             : 
      27           0 : #define GENESIS_MAX_SZ (10UL*1024UL*1024UL) /* 10 MiB */
      28             : 
      29             : static void *
      30             : bz2_malloc( void * opaque,
      31             :             int    items,
      32           0 :             int    size ) {
      33           0 :   fd_alloc_t * alloc = (fd_alloc_t *)opaque;
      34             : 
      35           0 :   void * result = fd_alloc_malloc( alloc, alignof(max_align_t), (ulong)(items*size) );
      36           0 :   if( FD_UNLIKELY( !result ) ) return NULL;
      37           0 :   return result;
      38           0 : }
      39             : 
      40             : static void
      41             : bz2_free( void * opaque,
      42           0 :           void * addr ) {
      43           0 :   fd_alloc_t * alloc = (fd_alloc_t *)opaque;
      44             : 
      45           0 :   if( FD_UNLIKELY( !addr ) ) return;
      46           0 :   fd_alloc_free( alloc, addr );
      47           0 : }
      48             : 
      49             : struct fd_genesi_tile {
      50             :   fd_accdb_admin_t accdb_admin[1];
      51             :   fd_accdb_user_t  accdb[1];
      52             : 
      53             :   uchar genesis_hash[ 32UL ];
      54             : 
      55             :   fd_genesis_client_t * client;
      56             : 
      57             :   fd_lthash_value_t lthash[1];
      58             : 
      59             :   int local_genesis;
      60             :   int bootstrap;
      61             :   int shutdown;
      62             : 
      63             :   int has_expected_genesis_hash;
      64             :   uchar expected_genesis_hash[ 32UL ];
      65             :   ushort expected_shred_version;
      66             : 
      67             :   ulong genesis_sz;
      68             :   uchar genesis[ GENESIS_MAX_SZ ] __attribute__((aligned(alignof(fd_genesis_solana_global_t)))); /* 10 MiB buffer for decoded genesis */
      69             :   uchar buffer[ GENESIS_MAX_SZ ]; /* 10 MiB buffer for reading genesis file */
      70             : 
      71             :   char genesis_path[ PATH_MAX ];
      72             : 
      73             :   int in_fd;
      74             :   int out_fd;
      75             :   int out_dir_fd;
      76             : 
      77             :   fd_wksp_t * out_mem;
      78             :   ulong       out_chunk0;
      79             :   ulong       out_wmark;
      80             :   ulong       out_chunk;
      81             : 
      82             :   fd_alloc_t * bz2_alloc;
      83             : };
      84             : 
      85             : typedef struct fd_genesi_tile fd_genesi_tile_t;
      86             : 
      87             : FD_FN_CONST static inline ulong
      88           0 : scratch_align( void ) {
      89           0 :   return alignof( fd_genesi_tile_t );
      90           0 : }
      91             : 
      92             : FD_FN_PURE static inline ulong
      93           0 : scratch_footprint( fd_topo_tile_t const * tile ) {
      94           0 :   (void)tile;
      95             : 
      96           0 :   ulong l = FD_LAYOUT_INIT;
      97           0 :   l = FD_LAYOUT_APPEND( l, alignof( fd_genesi_tile_t ), sizeof( fd_genesi_tile_t )    );
      98           0 :   l = FD_LAYOUT_APPEND( l, fd_genesis_client_align(),   fd_genesis_client_footprint() );
      99           0 :   l = FD_LAYOUT_APPEND( l, fd_alloc_align(),            fd_alloc_footprint()          );
     100           0 :   return FD_LAYOUT_FINI( l, scratch_align() );
     101           0 : }
     102             : 
     103             : FD_FN_CONST static inline ulong
     104           0 : loose_footprint( fd_topo_tile_t const * tile ) {
     105           0 :   (void)tile;
     106             :   /* Leftover space for bzip2 allocations */
     107           0 :   return 1UL<<26; /* 64 MiB */
     108           0 : }
     109             : 
     110             : static inline int
     111           0 : should_shutdown( fd_genesi_tile_t * ctx ) {
     112           0 :   return ctx->shutdown;
     113           0 : }
     114             : 
     115             : static void
     116           0 : initialize_accdb( fd_genesi_tile_t * ctx ) {
     117             :   /* Insert accounts at root */
     118           0 :   fd_funk_txn_xid_t root_xid; fd_funk_txn_xid_set_root( &root_xid );
     119             : 
     120           0 :   fd_genesis_solana_global_t * genesis = fd_type_pun( ctx->genesis );
     121             : 
     122           0 :   fd_pubkey_account_pair_global_t const * accounts = fd_genesis_solana_accounts_join( genesis );
     123             : 
     124           0 :   for( ulong i=0UL; i<genesis->accounts_len; i++ ) {
     125           0 :     fd_pubkey_account_pair_global_t const * account = &accounts[ i ];
     126             : 
     127             :     /* FIXME use accdb API */
     128           0 :     fd_funk_rec_prepare_t prepare[1];
     129           0 :     fd_funk_rec_key_t key[1]; memcpy( key->uc, account->key.uc, sizeof(fd_pubkey_t) );
     130           0 :     fd_funk_rec_t * rec = fd_funk_rec_prepare( ctx->accdb->funk, &root_xid, key, prepare, NULL );
     131           0 :     FD_TEST( rec );
     132           0 :     fd_account_meta_t * meta = fd_funk_val_truncate( rec, ctx->accdb->funk->alloc, ctx->accdb->funk->wksp, 16UL, sizeof(fd_account_meta_t)+account->account.data_len, NULL );
     133           0 :     FD_TEST( meta );
     134           0 :     void * data = (void *)( meta+1 );
     135           0 :     fd_memcpy( meta->owner, account->account.owner.uc, sizeof(fd_pubkey_t) );
     136           0 :     meta->lamports = account->account.lamports;
     137           0 :     meta->slot = 0UL;
     138           0 :     meta->executable = !!account->account.executable;
     139           0 :     meta->dlen = (uint)account->account.data_len;
     140           0 :     fd_memcpy( data, fd_solana_account_data_join( &account->account ), account->account.data_len );
     141           0 :     fd_funk_rec_publish( ctx->accdb->funk, prepare );
     142             : 
     143           0 :     fd_lthash_value_t new_hash[1];
     144           0 :     fd_hashes_account_lthash( &account->key, meta, data, new_hash );
     145           0 :     fd_lthash_add( ctx->lthash, new_hash );
     146           0 :   }
     147           0 : }
     148             : 
     149             : static inline void
     150             : verify_cluster_type( fd_genesis_solana_global_t const * genesis,
     151             :                      uchar const *                      genesis_hash,
     152           0 :                      char const *                       genesis_path ) {
     153           0 : #define TESTNET     (0)
     154           0 : #define MAINNET     (1)
     155           0 : #define DEVNET      (2)
     156           0 : #define DEVELOPMENT (3)
     157             : 
     158           0 :   uchar mainnet_hash[ 32 ];
     159           0 :   FD_TEST( fd_base58_decode_32( "5eykt4UsFv8P8NJdTREpY1vzqKqZKvdpKuc147dw2N9d", mainnet_hash ) );
     160             : 
     161           0 :   uchar testnet_hash[ 32 ];
     162           0 :   FD_TEST( fd_base58_decode_32( "4uhcVJyU9pJkvQyS88uRDiswHXSCkY3zQawwpjk2NsNY", testnet_hash ) );
     163             : 
     164           0 :   uchar devnet_hash[ 32 ];
     165           0 :   FD_TEST( fd_base58_decode_32( "EtWTRABZaYq6iMfeYKouRu166VU2xqa1wcaWoxPkrZBG", devnet_hash ) );
     166             : 
     167           0 :   switch( genesis->cluster_type ) {
     168           0 :     case MAINNET: {
     169           0 :       if( FD_UNLIKELY( memcmp( genesis_hash, mainnet_hash, 32UL ) ) ) {
     170           0 :         FD_LOG_ERR(( "genesis file `%s` has cluster type MAINNET but unexpected genesis hash `%s`",
     171           0 :                      genesis_path, FD_BASE58_ENC_32_ALLOCA( genesis_hash ) ));
     172           0 :       }
     173           0 :       break;
     174           0 :     }
     175           0 :     case TESTNET: {
     176           0 :       if( FD_UNLIKELY( memcmp( genesis_hash, testnet_hash, 32UL ) ) ) {
     177           0 :         FD_LOG_ERR(( "genesis file `%s` has cluster type TESTNET but unexpected genesis hash `%s`",
     178           0 :                      genesis_path, FD_BASE58_ENC_32_ALLOCA( genesis_hash ) ));
     179           0 :       }
     180           0 :       break;
     181           0 :     }
     182           0 :     case DEVNET: {
     183           0 :       if( FD_UNLIKELY( memcmp( genesis_hash, devnet_hash, 32UL ) ) ) {
     184           0 :         FD_LOG_ERR(( "genesis file `%s` has cluster type DEVNET but unexpected genesis hash `%s`",
     185           0 :                      genesis_path, FD_BASE58_ENC_32_ALLOCA( genesis_hash ) ));
     186           0 :       }
     187           0 :       break;
     188           0 :     }
     189           0 :     default:
     190           0 :       break;
     191           0 :   }
     192             : 
     193           0 : #undef TESTNET
     194           0 : #undef MAINNET
     195           0 : #undef DEVNET
     196           0 : #undef DEVELOPMENT
     197           0 : }
     198             : 
     199             : static void
     200             : after_credit( fd_genesi_tile_t *  ctx,
     201             :               fd_stem_context_t * stem,
     202             :               int *               opt_poll_in,
     203           0 :               int *               charge_busy ) {
     204           0 :   (void)opt_poll_in;
     205             : 
     206           0 :   if( FD_UNLIKELY( ctx->shutdown ) ) return;
     207             : 
     208           0 :   if( FD_LIKELY( ctx->local_genesis ) ) {
     209           0 :     FD_TEST( -1!=ctx->in_fd );
     210             : 
     211           0 :     uchar * dst = fd_chunk_to_laddr( ctx->out_mem, ctx->out_chunk );
     212           0 :     if( FD_UNLIKELY( ctx->bootstrap ) ) {
     213           0 :       fd_memcpy( dst, &ctx->lthash->bytes, sizeof(fd_lthash_value_t) );
     214           0 :       fd_memcpy( dst+sizeof(fd_lthash_value_t), &ctx->genesis_hash, sizeof(fd_hash_t) );
     215           0 :       fd_memcpy( dst+sizeof(fd_lthash_value_t)+sizeof(fd_hash_t), ctx->genesis, ctx->genesis_sz );
     216             : 
     217           0 :       fd_stem_publish( stem, 0UL, GENESI_SIG_BOOTSTRAP_COMPLETED, ctx->out_chunk, 0UL, 0UL, 0UL, 0UL );
     218           0 :       ctx->out_chunk = fd_dcache_compact_next( ctx->out_chunk, ctx->genesis_sz+sizeof(fd_hash_t)+sizeof(fd_lthash_value_t), ctx->out_chunk0, ctx->out_wmark );
     219           0 :     } else {
     220           0 :       fd_memcpy( dst, ctx->genesis_hash, sizeof(fd_hash_t) );
     221           0 :       fd_stem_publish( stem, 0UL, GENESI_SIG_GENESIS_HASH, ctx->out_chunk, sizeof(fd_hash_t), 0UL, 0UL, 0UL );
     222           0 :       ctx->out_chunk = fd_dcache_compact_next( ctx->out_chunk, sizeof(fd_hash_t), ctx->out_chunk0, ctx->out_wmark );
     223           0 :     }
     224             : 
     225           0 :     *charge_busy = 1;
     226           0 :     FD_LOG_NOTICE(( "loaded local genesis.bin from file `%s`", ctx->genesis_path ));
     227             : 
     228           0 :     ctx->shutdown = 1;
     229           0 :   } else {
     230           0 :     uchar * buffer;
     231           0 :     ulong buffer_sz;
     232           0 :     fd_ip4_port_t peer;
     233           0 :     int result = fd_genesis_client_poll( ctx->client, &peer, &buffer, &buffer_sz, charge_busy );
     234           0 :     if( FD_UNLIKELY( -1==result ) ) FD_LOG_ERR(( "failed to retrieve genesis.bin from any configured gossip entrypoints" ));
     235           0 :     if( FD_LIKELY( 1==result ) ) return;
     236             : 
     237           0 :     bz_stream bzstrm = {0};
     238           0 :     bzstrm.bzalloc = bz2_malloc;
     239           0 :     bzstrm.bzfree  = bz2_free;
     240           0 :     bzstrm.opaque  = ctx->bz2_alloc;
     241           0 :     int bzerr = BZ2_bzDecompressInit( &bzstrm, 0, 0 );
     242           0 :     if( FD_UNLIKELY( BZ_OK!=bzerr ) ) FD_LOG_ERR(( "BZ2_bzDecompressInit() failed (%d)", bzerr ));
     243             : 
     244           0 :     uchar * decompressed = ctx->buffer;
     245           0 :     ulong   decompressed_sz = GENESIS_MAX_SZ;
     246             : 
     247           0 :     bzstrm.next_in   = (char *)buffer;
     248           0 :     bzstrm.avail_in  = (uint)buffer_sz;
     249           0 :     bzstrm.next_out  = (char *)decompressed;
     250           0 :     bzstrm.avail_out = (uint)decompressed_sz;
     251           0 :     bzerr = BZ2_bzDecompress( &bzstrm );
     252           0 :     if( FD_UNLIKELY( BZ_STREAM_END!=bzerr ) ) FD_LOG_ERR(( "BZ2_bzDecompress() failed (%d)", bzerr ));
     253             : 
     254           0 :     ulong actual_decompressed_sz = decompressed_sz - (ulong)bzstrm.avail_out;
     255             : 
     256           0 :     FD_TEST( actual_decompressed_sz>=512UL );
     257             : 
     258           0 :     fd_tar_meta_t const * meta = (fd_tar_meta_t const *)decompressed;
     259           0 :     FD_TEST( !strcmp( meta->name, "genesis.bin" ) );
     260           0 :     FD_TEST( actual_decompressed_sz>=512UL+fd_tar_meta_get_size( meta ) );
     261             : 
     262           0 :     uchar hash[ 32UL ];
     263           0 :     fd_sha256_hash( decompressed+512UL, fd_tar_meta_get_size( meta ), hash );
     264             : 
     265             :     /* Can't verify expected_shred_version here because it needs to be
     266             :        mixed in with hard_forks from the snapshot.  Replay tile will
     267             :        combine them and do this verification. */
     268             : 
     269           0 :     if( FD_LIKELY( ctx->has_expected_genesis_hash && memcmp( hash, ctx->expected_genesis_hash, 32UL ) ) ) {
     270           0 :       FD_LOG_ERR(( "An expected genesis hash of `%s` has been set in your configuration file at [consensus.expected_genesis_hash] "
     271           0 :                    "but the genesis hash derived from the peer at `http://" FD_IP4_ADDR_FMT ":%hu` has unexpected hash `%s`",
     272           0 :                    FD_BASE58_ENC_32_ALLOCA( ctx->expected_genesis_hash ), FD_IP4_ADDR_FMT_ARGS( peer.addr ), fd_ushort_bswap( peer.port ), FD_BASE58_ENC_32_ALLOCA( hash ) ));
     273           0 :     }
     274             : 
     275           0 :     FD_TEST( !ctx->bootstrap );
     276             : 
     277           0 :     fd_bincode_decode_ctx_t decode_ctx = {
     278           0 :       .data    = decompressed+512UL,
     279           0 :       .dataend = decompressed+512UL+fd_tar_meta_get_size( meta ),
     280           0 :     };
     281             : 
     282           0 :     ctx->genesis_sz = 0UL;
     283           0 :     int err = fd_genesis_solana_decode_footprint( &decode_ctx, &ctx->genesis_sz );
     284           0 :     if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) FD_LOG_ERR(( "malformed genesis file from peer at http://" FD_IP4_ADDR_FMT ":%hu", FD_IP4_ADDR_FMT_ARGS( peer.addr ), peer.port ));
     285           0 :     if( FD_UNLIKELY( ctx->genesis_sz>sizeof(ctx->genesis) ) ) FD_LOG_ERR(( "genesis file at `%s` decode footprint too large (%lu bytes, max %lu)", ctx->genesis_path, ctx->genesis_sz, sizeof(ctx->genesis) ));
     286             : 
     287           0 :     fd_genesis_solana_global_t * genesis = fd_genesis_solana_decode_global( ctx->genesis, &decode_ctx );
     288           0 :     FD_TEST( genesis );
     289             : 
     290           0 :     verify_cluster_type( genesis, hash, ctx->genesis_path );
     291             : 
     292           0 :     uchar * dst = fd_chunk_to_laddr( ctx->out_mem, ctx->out_chunk );
     293           0 :     fd_memcpy( dst, hash, sizeof(fd_hash_t) );
     294           0 :     FD_LOG_WARNING(( "Genesis hash from peer: %s", FD_BASE58_ENC_32_ALLOCA( dst ) ));
     295           0 :     fd_stem_publish( stem, 0UL, GENESI_SIG_GENESIS_HASH, ctx->out_chunk, 32UL, 0UL, 0UL, 0UL );
     296           0 :     ctx->out_chunk = fd_dcache_compact_next( ctx->out_chunk, sizeof(fd_hash_t), ctx->out_chunk0, ctx->out_wmark );
     297             : 
     298           0 :     ulong bytes_written = 0UL;
     299           0 :     while( bytes_written<fd_tar_meta_get_size( meta ) ) {
     300           0 :       long result = write( ctx->out_fd, decompressed+512UL+bytes_written, fd_tar_meta_get_size( meta )-bytes_written );
     301           0 :       if( FD_UNLIKELY( -1==result ) ) FD_LOG_ERR(( "write() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     302           0 :       bytes_written += (ulong)result;
     303           0 :     }
     304             : 
     305           0 :     char basename[ PATH_MAX ];
     306           0 :     const char * last_slash = strrchr( ctx->genesis_path, '/' );
     307           0 :     if( FD_LIKELY( last_slash ) ) FD_TEST( fd_cstr_printf_check( basename, PATH_MAX, NULL, "%s", last_slash+1UL ) );
     308           0 :     else                          FD_TEST( fd_cstr_printf_check( basename, PATH_MAX, NULL, "%s", ctx->genesis_path ) );
     309             : 
     310           0 :     char basename_partial[ PATH_MAX ];
     311           0 :     FD_TEST( fd_cstr_printf_check( basename_partial, PATH_MAX, NULL, "%s.partial", basename ) );
     312             : 
     313           0 :     err = renameat2( ctx->out_dir_fd, basename_partial, ctx->out_dir_fd, basename, RENAME_NOREPLACE );
     314           0 :     if( FD_UNLIKELY( -1==err ) ) FD_LOG_ERR(( "renameat2() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     315             : 
     316           0 :     FD_LOG_NOTICE(( "retrieved genesis `%s` from peer at http://" FD_IP4_ADDR_FMT ":%hu/genesis.tar.bz2",
     317           0 :                     ctx->genesis_path, FD_IP4_ADDR_FMT_ARGS( peer.addr ), peer.port ));
     318             : 
     319           0 :     ctx->shutdown = 1;
     320           0 :   }
     321           0 : }
     322             : 
     323             : static void
     324             : process_local_genesis( fd_genesi_tile_t * ctx,
     325           0 :                        char const *       genesis_path ) {
     326           0 :   struct stat st;
     327           0 :   int err = fstat( ctx->in_fd, &st );
     328           0 :   if( FD_UNLIKELY( -1==err ) ) FD_LOG_ERR(( "stat() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     329             : 
     330           0 :   ulong size = (ulong)st.st_size;
     331             : 
     332           0 :   if( FD_UNLIKELY( size>sizeof(ctx->buffer) ) ) FD_LOG_ERR(( "genesis file `%s` too large (%lu bytes, max %lu)", genesis_path, size, (ulong)sizeof(ctx->buffer) ));
     333             : 
     334           0 :   ulong bytes_read = 0UL;
     335           0 :   while( bytes_read<size ) {
     336           0 :     long result = read( ctx->in_fd, ctx->buffer+bytes_read, size-bytes_read );
     337           0 :     if( FD_UNLIKELY( -1==result ) ) FD_LOG_ERR(( "read() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     338           0 :     if( FD_UNLIKELY( !result ) )  FD_LOG_ERR(( "read() returned 0 before reading full file" ));
     339           0 :     bytes_read += (ulong)result;
     340           0 :   }
     341             : 
     342           0 :   FD_TEST( bytes_read==size );
     343             : 
     344           0 :   if( FD_UNLIKELY( -1==close( ctx->in_fd ) ) ) FD_LOG_ERR(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     345             : 
     346           0 :   fd_bincode_decode_ctx_t decode_ctx = {
     347           0 :     .data    = ctx->buffer,
     348           0 :     .dataend = ctx->buffer+size,
     349           0 :   };
     350             : 
     351           0 :   ctx->genesis_sz = 0UL;
     352           0 :   err = fd_genesis_solana_decode_footprint( &decode_ctx, &ctx->genesis_sz );
     353           0 :   if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) FD_LOG_ERR(( "malformed genesis file at `%s`", genesis_path ));
     354           0 :   if( FD_UNLIKELY( ctx->genesis_sz>sizeof(ctx->genesis) ) ) FD_LOG_ERR(( "genesis file at `%s` decode footprint too large (%lu bytes, max %lu)", genesis_path, ctx->genesis_sz, sizeof(ctx->genesis) ));
     355             : 
     356           0 :   fd_genesis_solana_global_t * genesis = fd_genesis_solana_decode_global( ctx->genesis, &decode_ctx );
     357           0 :   FD_TEST( genesis );
     358             : 
     359           0 :   union {
     360           0 :     uchar  c[ 32 ];
     361           0 :     ushort s[ 16 ];
     362           0 :   } hash;
     363           0 :   fd_sha256_hash( ctx->buffer, size, hash.c );
     364             : 
     365           0 :   verify_cluster_type( genesis, hash.c, genesis_path );
     366             : 
     367           0 :   fd_memcpy( ctx->genesis_hash, hash.c, 32UL );
     368             : 
     369           0 :   if( FD_UNLIKELY( ctx->bootstrap && ctx->expected_shred_version ) ) {
     370           0 :     ushort xor = 0;
     371           0 :     for( ulong i=0UL; i<16UL; i++ ) xor ^= hash.s[ i ];
     372             : 
     373           0 :     xor = fd_ushort_bswap( xor );
     374           0 :     xor = fd_ushort_if( xor<USHORT_MAX, (ushort)(xor + 1), USHORT_MAX );
     375             : 
     376           0 :     FD_TEST( xor );
     377             : 
     378           0 :     if( FD_UNLIKELY( xor!=ctx->expected_shred_version ) ) {
     379           0 :       FD_LOG_ERR(( "This node is bootstrapping the cluster as it has no gossip entrypoints provided, but "
     380           0 :                    "a [consensus.expected_shred_version] of %hu is provided which does not match the shred "
     381           0 :                    "version of %hu computed from the genesis.bin file at `%s`",
     382           0 :                    ctx->expected_shred_version, xor, genesis_path ));
     383           0 :     }
     384           0 :   }
     385             : 
     386           0 :   if( FD_LIKELY( ctx->has_expected_genesis_hash && memcmp( ctx->genesis_hash, ctx->expected_genesis_hash, 32UL ) ) ) {
     387           0 :     FD_LOG_ERR(( "An expected genesis hash of `%s` has been set in your configuration file at [consensus.expected_genesis_hash] "
     388           0 :                  "but the genesis hash derived from the genesis file at `%s` has unexpected hash (expected `%s`)", FD_BASE58_ENC_32_ALLOCA( ctx->expected_genesis_hash ), genesis_path, FD_BASE58_ENC_32_ALLOCA( ctx->genesis_hash ) ));
     389           0 :   }
     390           0 : }
     391             : 
     392             : static void
     393             : privileged_init( fd_topo_t *      topo,
     394           0 :                  fd_topo_tile_t * tile ) {
     395           0 :   void * scratch = fd_topo_obj_laddr( topo, tile->tile_obj_id );
     396             : 
     397           0 :   FD_SCRATCH_ALLOC_INIT( l, scratch );
     398           0 :   fd_genesi_tile_t * ctx        = FD_SCRATCH_ALLOC_APPEND( l, alignof( fd_genesi_tile_t ), sizeof( fd_genesi_tile_t )    );
     399           0 :   fd_genesis_client_t * _client = FD_SCRATCH_ALLOC_APPEND( l, fd_genesis_client_align(),   fd_genesis_client_footprint() );
     400             : 
     401           0 :   ctx->local_genesis = 1;
     402           0 :   ctx->in_fd = open( tile->genesi.genesis_path, O_RDONLY|O_CLOEXEC );
     403           0 :   if( FD_UNLIKELY( -1==ctx->in_fd ) ) {
     404           0 :     if( FD_LIKELY( errno==ENOENT  ) ) {
     405           0 :       FD_LOG_INFO(( "no local genesis.bin file found at `%s`", tile->genesi.genesis_path ));
     406             : 
     407           0 :       if( FD_UNLIKELY( !tile->genesi.entrypoints_cnt ) ) {
     408           0 :         FD_LOG_ERR(( "This node is bootstrapping the cluster as it has no gossip entrypoints provided, but "
     409           0 :                      "the genesis.bin file at `%s` does not exist.  Please provide a valid genesis.bin "
     410           0 :                      "file by running genesis, or join an existing cluster.",
     411           0 :                      tile->genesi.genesis_path ));
     412           0 :       } else {
     413           0 :         if( FD_UNLIKELY( !tile->genesi.allow_download ) ) {
     414           0 :           FD_LOG_ERR(( "There is no genesis.bin file at `%s` and automatic downloading is disabled as "
     415           0 :                        "genesis_download is false in your configuration file.  Please either provide a valid "
     416           0 :                        "genesis.bin file locally, or allow donwloading from a gossip entrypoint.",
     417           0 :                        tile->genesi.genesis_path ));
     418           0 :         } else {
     419           0 :           char basename[ PATH_MAX ];
     420           0 :           strncpy( basename, tile->genesi.genesis_path, PATH_MAX );
     421           0 :           char * last_slash = strrchr( basename, '/' );
     422           0 :           if( FD_LIKELY( last_slash ) ) *last_slash = '\0';
     423             : 
     424           0 :           ctx->out_dir_fd = open( basename, O_RDONLY|O_CLOEXEC|O_DIRECTORY );
     425           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 ) ));
     426             : 
     427             :           /* Switch to non-root uid/gid for file creation.  Permissions checks
     428             :             are still done as root. */
     429           0 :           gid_t gid = getgid();
     430           0 :           uid_t uid = getuid();
     431           0 :           if( FD_LIKELY( !gid && setegid( tile->genesi.target_gid ) ) ) FD_LOG_ERR(( "setegid() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     432           0 :           if( FD_LIKELY( !uid && seteuid( tile->genesi.target_uid ) ) ) FD_LOG_ERR(( "seteuid() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     433             : 
     434           0 :           char partialname[ PATH_MAX ];
     435           0 :           FD_TEST( fd_cstr_printf_check( partialname, PATH_MAX, NULL, "%s.partial", tile->genesi.genesis_path ) );
     436           0 :           ctx->out_fd = openat( ctx->out_dir_fd, "genesis.bin.partial", O_CREAT|O_WRONLY|O_CLOEXEC|O_TRUNC, S_IRUSR|S_IWUSR );
     437           0 :           if( FD_UNLIKELY( -1==ctx->out_fd ) ) FD_LOG_ERR(( "openat() failed for genesis file `%s` (%i-%s)", partialname, errno, fd_io_strerror( errno ) ));
     438             : 
     439           0 :           if( FD_UNLIKELY( seteuid( uid ) ) ) FD_LOG_ERR(( "seteuid() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     440           0 :           if( FD_UNLIKELY( setegid( gid ) ) ) FD_LOG_ERR(( "setegid() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
     441             : 
     442           0 :           ctx->local_genesis = 0;
     443           0 :           ctx->client = fd_genesis_client_join( fd_genesis_client_new( _client ) );
     444           0 :           FD_TEST( ctx->client );
     445           0 :           fd_genesis_client_init( ctx->client, tile->genesi.entrypoints, tile->genesi.entrypoints_cnt );
     446           0 :         }
     447           0 :       }
     448           0 :     } else {
     449           0 :       FD_LOG_ERR(( "could not open genesis.bin file at `%s` (%i-%s)", tile->genesi.genesis_path, errno, fd_io_strerror( errno ) ));
     450           0 :     }
     451           0 :   }
     452           0 : }
     453             : 
     454             : static void
     455             : unprivileged_init( fd_topo_t *      topo,
     456           0 :                    fd_topo_tile_t * tile ) {
     457           0 :   void * scratch = fd_topo_obj_laddr( topo, tile->tile_obj_id );
     458             : 
     459           0 :   FD_SCRATCH_ALLOC_INIT( l, scratch );
     460           0 :   fd_genesi_tile_t * ctx = FD_SCRATCH_ALLOC_APPEND( l, alignof( fd_genesi_tile_t ), sizeof( fd_genesi_tile_t )    );
     461           0 :                            FD_SCRATCH_ALLOC_APPEND( l, fd_genesis_client_align(),   fd_genesis_client_footprint() );
     462           0 :   void * _alloc          = FD_SCRATCH_ALLOC_APPEND( l, fd_alloc_align(),            fd_alloc_footprint()          );
     463             : 
     464           0 :   FD_TEST( fd_accdb_admin_join( ctx->accdb_admin, fd_topo_obj_laddr( topo, tile->genesi.funk_obj_id ) ) );
     465           0 :   FD_TEST( fd_accdb_user_join ( ctx->accdb,       fd_topo_obj_laddr( topo, tile->genesi.funk_obj_id ) ) );
     466             : 
     467           0 :   fd_lthash_zero( ctx->lthash );
     468             : 
     469           0 :   ctx->shutdown = 0;
     470           0 :   ctx->bootstrap = !tile->genesi.entrypoints_cnt;
     471           0 :   ctx->expected_shred_version = tile->genesi.expected_shred_version;
     472           0 :   ctx->has_expected_genesis_hash = tile->genesi.has_expected_genesis_hash;
     473           0 :   fd_memcpy( ctx->expected_genesis_hash, tile->genesi.expected_genesis_hash, 32UL );
     474           0 :   if( FD_LIKELY( -1!=ctx->in_fd ) ) {
     475           0 :     process_local_genesis( ctx, tile->genesi.genesis_path );
     476           0 :     if( FD_UNLIKELY( ctx->bootstrap ) ) initialize_accdb( ctx );
     477           0 :   }
     478             : 
     479           0 :   FD_TEST( fd_cstr_printf_check( ctx->genesis_path, PATH_MAX, NULL, "%s", tile->genesi.genesis_path ) );
     480             : 
     481           0 :   ctx->out_mem    = topo->workspaces[ topo->objs[ topo->links[ tile->out_link_id[ 0 ] ].dcache_obj_id ].wksp_id ].wksp;
     482           0 :   ctx->out_chunk0 = fd_dcache_compact_chunk0( ctx->out_mem, topo->links[ tile->out_link_id[ 0 ] ].dcache );
     483           0 :   ctx->out_wmark  = fd_dcache_compact_wmark ( ctx->out_mem, topo->links[ tile->out_link_id[ 0 ] ].dcache, topo->links[ tile->out_link_id[ 0 ] ].mtu );
     484           0 :   ctx->out_chunk  = ctx->out_chunk0;
     485             : 
     486           0 :   ctx->bz2_alloc = fd_alloc_join( fd_alloc_new( _alloc, 1UL ), 1UL );
     487           0 :   FD_TEST( ctx->bz2_alloc );
     488             : 
     489           0 :   ulong scratch_top = FD_SCRATCH_ALLOC_FINI( l, 1UL );
     490           0 :   if( FD_UNLIKELY( scratch_top > (ulong)scratch + scratch_footprint( tile ) ) )
     491           0 :     FD_LOG_ERR(( "scratch overflow %lu %lu %lu", scratch_top - (ulong)scratch - scratch_footprint( tile ), scratch_top, (ulong)scratch + scratch_footprint( tile ) ));
     492           0 : }
     493             : 
     494             : static ulong
     495             : rlimit_file_cnt( fd_topo_t const *      topo FD_PARAM_UNUSED,
     496           0 :                  fd_topo_tile_t const * tile ) {
     497           0 :   return 1UL +                         /* stderr */
     498           0 :          1UL +                         /* logfile */
     499           0 :          1UL +                         /* genesis file */
     500           0 :          1UL +                         /* genesis dir */
     501           0 :          tile->genesi.entrypoints_cnt; /* for the client */
     502           0 : }
     503             : 
     504             : static ulong
     505             : populate_allowed_seccomp( fd_topo_t const *      topo,
     506             :                           fd_topo_tile_t const * tile,
     507             :                           ulong                  out_cnt,
     508           0 :                           struct sock_filter *   out ) {
     509             : 
     510           0 :   void * scratch = fd_topo_obj_laddr( topo, tile->tile_obj_id );
     511             : 
     512           0 :   FD_SCRATCH_ALLOC_INIT( l, scratch );
     513           0 :   fd_genesi_tile_t * ctx = FD_SCRATCH_ALLOC_APPEND( l, alignof( fd_genesi_tile_t ), sizeof( fd_genesi_tile_t ) );
     514             : 
     515           0 :   uint in_fd, out_fd, out_dir_fd;
     516           0 :   if( FD_LIKELY( -1!=ctx->in_fd ) ) {
     517           0 :     in_fd      = (uint)ctx->in_fd;
     518           0 :     out_fd     = (uint)-1;
     519           0 :     out_dir_fd = (uint)-1;
     520           0 :   } else {
     521           0 :     in_fd      = (uint)-1;
     522           0 :     out_fd     = (uint)ctx->out_fd;
     523           0 :     out_dir_fd = (uint)ctx->out_dir_fd;
     524           0 :   }
     525             : 
     526           0 :   populate_sock_filter_policy_fd_genesi_tile( out_cnt, out, (uint)fd_log_private_logfile_fd(), in_fd, out_fd, out_dir_fd );
     527           0 :   return sock_filter_policy_fd_genesi_tile_instr_cnt;
     528           0 : }
     529             : 
     530             : static ulong
     531             : populate_allowed_fds( fd_topo_t const *      topo,
     532             :                       fd_topo_tile_t const * tile,
     533             :                       ulong                  out_fds_cnt,
     534           0 :                       int *                  out_fds ) {
     535           0 :   void * scratch = fd_topo_obj_laddr( topo, tile->tile_obj_id );
     536             : 
     537           0 :   FD_SCRATCH_ALLOC_INIT( l, scratch );
     538           0 :   fd_genesi_tile_t * ctx = FD_SCRATCH_ALLOC_APPEND( l, alignof( fd_genesi_tile_t ), sizeof( fd_genesi_tile_t ) );
     539             : 
     540           0 :   if( FD_UNLIKELY( out_fds_cnt<tile->genesi.entrypoints_cnt+5UL ) ) FD_LOG_ERR(( "out_fds_cnt %lu", out_fds_cnt ));
     541             : 
     542           0 :   ulong out_cnt = 0UL;
     543           0 :   out_fds[ out_cnt++ ] = 2; /* stderr */
     544           0 :   if( FD_LIKELY( -1!=fd_log_private_logfile_fd() ) )
     545           0 :     out_fds[ out_cnt++ ] = fd_log_private_logfile_fd(); /* logfile */
     546             : 
     547           0 :   if( FD_UNLIKELY( -1==ctx->in_fd ) ) {
     548           0 :     FD_TEST( -1!=ctx->out_dir_fd );
     549           0 :     FD_TEST( -1!=ctx->out_fd );
     550           0 :     out_fds[ out_cnt++ ] = ctx->out_dir_fd;
     551           0 :     out_fds[ out_cnt++ ] = ctx->out_fd;
     552             : 
     553           0 :     for( ulong i=0UL; i<tile->genesi.entrypoints_cnt; i++ ) {
     554           0 :       int fd = fd_genesis_client_get_pollfds( ctx->client )[ i ].fd;
     555           0 :       if( FD_LIKELY( -1!=fd ) ) out_fds[ out_cnt++ ] = fd;
     556           0 :     }
     557           0 :   } else {
     558           0 :     FD_TEST( -1!=ctx->in_fd );
     559           0 :     out_fds[ out_cnt++ ] = ctx->in_fd;
     560           0 :   }
     561             : 
     562           0 :   return out_cnt;
     563           0 : }
     564             : 
     565           0 : #define STEM_BURST (1UL)
     566             : 
     567           0 : #define STEM_CALLBACK_CONTEXT_TYPE  fd_genesi_tile_t
     568           0 : #define STEM_CALLBACK_CONTEXT_ALIGN alignof(fd_genesi_tile_t)
     569             : 
     570           0 : #define STEM_CALLBACK_AFTER_CREDIT    after_credit
     571             : #define STEM_CALLBACK_SHOULD_SHUTDOWN should_shutdown
     572             : 
     573             : #include "../../disco/stem/fd_stem.c"
     574             : 
     575             : fd_topo_run_tile_t fd_tile_genesi = {
     576             :   .name                     = "genesi",
     577             :   .rlimit_file_cnt_fn       = rlimit_file_cnt,
     578             :   .allow_connect            = 1,
     579             :   .allow_renameat           = 1,
     580             :   .populate_allowed_seccomp = populate_allowed_seccomp,
     581             :   .populate_allowed_fds     = populate_allowed_fds,
     582             :   .loose_footprint          = loose_footprint,
     583             :   .scratch_align            = scratch_align,
     584             :   .scratch_footprint        = scratch_footprint,
     585             :   .privileged_init          = privileged_init,
     586             :   .unprivileged_init        = unprivileged_init,
     587             :   .run                      = stem_run,
     588             : };

Generated by: LCOV version 1.14