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

          Line data    Source code
       1             : #include "fd_tower_tile.h"
       2             : #include "generated/fd_tower_tile_seccomp.h"
       3             : 
       4             : #include "../genesis/fd_genesi_tile.h"
       5             : #include "../replay/fd_replay_tile.h"
       6             : #include "../../choreo/ghost/fd_ghost.h"
       7             : #include "../../choreo/tower/fd_tower.h"
       8             : #include "../../choreo/voter/fd_voter.h"
       9             : #include "../../disco/keyguard/fd_keyload.h"
      10             : #include "../../disco/topo/fd_topo.h"
      11             : #include "../../discof/restore/utils/fd_ssmsg.h"
      12             : #include "../../ballet/lthash/fd_lthash.h"
      13             : #include "../../flamenco/fd_flamenco_base.h"
      14             : #include "../../flamenco/runtime/fd_system_ids.h"
      15             : 
      16             : #include <errno.h>
      17             : #include <fcntl.h>
      18             : 
      19             : #define LOGGING 0
      20             : 
      21           0 : #define IN_KIND_GENESIS (0)
      22           0 : #define IN_KIND_SNAP    (1)
      23           0 : #define IN_KIND_REPLAY  (2)
      24             : 
      25             : struct fd_tower_tile_in {
      26             :   fd_wksp_t * mem;
      27             :   ulong       chunk0;
      28             :   ulong       wmark;
      29             :   ulong       mtu;
      30             : };
      31             : 
      32             : typedef struct fd_tower_tile_in fd_tower_tile_in_t;
      33             : 
      34             : struct fd_tower_tile {
      35             :   fd_pubkey_t identity_key[1];
      36             :   fd_pubkey_t vote_acc[1];
      37             : 
      38             :   int initialized;
      39             : 
      40             :   int  checkpt_fd;
      41             :   int  restore_fd;
      42             : 
      43             :   fd_epoch_t * epoch;
      44             :   fd_ghost_t * ghost;
      45             :   fd_tower_t * scratch;
      46             :   fd_tower_t * tower;
      47             :   uchar *      voters;
      48             : 
      49             :   long  ts;   /* tower timestamp */
      50             : 
      51             :   fd_snapshot_manifest_t manifest;
      52             :   fd_replay_slot_completed_t replay_slot_info;
      53             : 
      54             :   ulong             replay_towers_cnt;
      55             :   fd_replay_tower_t replay_towers[ FD_REPLAY_TOWER_VOTE_ACC_MAX ];
      56             : 
      57             :   fd_tower_t * vote_towers[ FD_REPLAY_TOWER_VOTE_ACC_MAX ];
      58             :   fd_pubkey_t  vote_keys[ FD_REPLAY_TOWER_VOTE_ACC_MAX ];
      59             : 
      60             :   uchar vote_state[ FD_REPLAY_TOWER_VOTE_ACC_MAX ]; /* our vote state */
      61             : 
      62             :   int in_kind[ 64UL ];
      63             :   fd_tower_tile_in_t in[ 64UL ];
      64             : 
      65             :   fd_wksp_t * out_mem;
      66             :   ulong       out_chunk0;
      67             :   ulong       out_wmark;
      68             :   ulong       out_chunk;
      69             : };
      70             : 
      71             : typedef struct fd_tower_tile fd_tower_tile_t;
      72             : 
      73             : FD_FN_CONST static inline ulong
      74           0 : scratch_align( void ) {
      75           0 :   return 128UL;
      76           0 : }
      77             : 
      78             : FD_FN_PURE static inline ulong
      79           0 : scratch_footprint( fd_topo_tile_t const * tile ) {
      80           0 :   (void)tile;
      81             : 
      82           0 :   ulong l = FD_LAYOUT_INIT;
      83           0 :   l = FD_LAYOUT_APPEND( l, alignof(fd_tower_tile_t), sizeof(fd_tower_tile_t)                            );
      84           0 :   l = FD_LAYOUT_APPEND( l, fd_epoch_align(),         fd_epoch_footprint( FD_REPLAY_TOWER_VOTE_ACC_MAX ) );
      85           0 :   l = FD_LAYOUT_APPEND( l, fd_ghost_align(),         fd_ghost_footprint( FD_BLOCK_MAX )                 );
      86           0 :   l = FD_LAYOUT_APPEND( l, fd_tower_align(),         fd_tower_footprint()                               );
      87           0 :   l = FD_LAYOUT_APPEND( l, fd_tower_align(),         fd_tower_footprint()*FD_REPLAY_TOWER_VOTE_ACC_MAX  );
      88           0 :   return FD_LAYOUT_FINI( l, scratch_align() );
      89           0 : }
      90             : 
      91             : static void
      92           0 : update_ghost( fd_tower_tile_t * ctx ) {
      93           0 :   fd_voter_t * epoch_voters = fd_epoch_voters( ctx->epoch );
      94           0 :   for( ulong i=0UL; i<ctx->replay_towers_cnt; i++ ) {
      95           0 :     fd_replay_tower_t const * replay_tower = &ctx->replay_towers[ i ];
      96           0 :     fd_pubkey_t const *       pubkey       = &replay_tower->key;
      97           0 :     fd_voter_state_t const *  voter_state  = (fd_voter_state_t const *)fd_type_pun_const( replay_tower->acc );
      98           0 :     fd_tower_t *              tower        = ctx->vote_towers[ i ];
      99             : 
     100             :     /* Look up the voter for this vote account */
     101           0 :     fd_voter_t * voter = fd_epoch_voters_query( epoch_voters, *pubkey, NULL );
     102           0 :     if( FD_UNLIKELY( !voter ) ) {
     103             :       /* This means that the cached list of epoch voters is not in sync
     104             :          with the list passed through from replay. This likely means
     105             :          that we have crossed an epoch boundary and the epoch_voter list
     106             :          has not been updated.
     107             : 
     108             :          TODO: update the set of account in epoch_voter's to match the
     109             :                list received from replay, so that epoch_voters is
     110             :                correct across epoch boundaries. */
     111           0 :       FD_LOG_CRIT(( "voter %s was not in epoch voters", FD_BASE58_ENC_32_ALLOCA( pubkey ) ));
     112           0 :     }
     113             : 
     114           0 :     voter->stake = replay_tower->stake; /* update the voters stake */
     115             : 
     116           0 :     if( FD_UNLIKELY( !fd_voter_state_cnt( voter_state ) ) ) continue; /* skip voters with no votes */
     117             : 
     118           0 :     ulong vote = fd_tower_votes_peek_tail( tower )->slot; /* peek last vote from the tower */
     119           0 :     ulong root = fd_voter_root_slot( voter_state );
     120             : 
     121             :     /* Only process votes for slots >= root. */
     122           0 :     if( FD_LIKELY( vote != FD_SLOT_NULL && vote >= fd_ghost_root( ctx->ghost )->slot ) ) {
     123           0 :       fd_ghost_ele_t const * ele = fd_ghost_query_const( ctx->ghost, fd_ghost_hash( ctx->ghost, vote ) );
     124             : 
     125             :       /* It is an invariant violation if the vote slot is not in ghost.
     126             :          These votes come from replay ie. on-chain towers stored in vote
     127             :          accounts which implies every vote slot must have been processed
     128             :          by the vote program (ie. replayed) and therefore in ghost. */
     129             : 
     130           0 :       if( FD_UNLIKELY( !ele ) ) FD_LOG_CRIT(( "voter %s's vote slot %lu was not in ghost", FD_BASE58_ENC_32_ALLOCA( &voter->key ), vote ));
     131           0 :       fd_ghost_replay_vote( ctx->ghost, voter, &ele->key );
     132           0 :     }
     133             : 
     134             :     /* Check if this voter's root >= ghost root. We can't process roots
     135             :        before our own root because it was already pruned. */
     136             : 
     137           0 :     if( FD_LIKELY( root!=FD_SLOT_NULL && root>=fd_ghost_root( ctx->ghost )->slot ) ) {
     138           0 :       fd_ghost_ele_t const * ele = fd_ghost_query( ctx->ghost, fd_ghost_hash( ctx->ghost, root ) );
     139             : 
     140             :       /* Error if the node's root slot is not in ghost. This is an
     141             :          invariant violation, because we know their tower must be on the
     142             :          same fork as this current one that we're processing, and so by
     143             :          definition their root slot must be in our ghost (ie. we can't
     144             :          have rooted past it or be on a different fork). */
     145             : 
     146           0 :       if( FD_UNLIKELY( !ele ) ) FD_LOG_CRIT(( "voter %s's root slot %lu was not in ghost", FD_BASE58_ENC_32_ALLOCA( &voter->key ), root ));
     147             : 
     148           0 :       fd_ghost_rooted_vote( ctx->ghost, voter, root );
     149           0 :     }
     150           0 :   }
     151           0 : }
     152             : 
     153             : static void
     154             : replay_slot_completed( fd_tower_tile_t *            ctx,
     155             :                        fd_replay_slot_completed_t * slot_info,
     156             :                        ulong                        tsorig,
     157           0 :                        fd_stem_context_t *          stem ) {
     158             :   /* If we have not received any votes, something is wrong. */
     159           0 :   if( FD_UNLIKELY( !ctx->replay_towers_cnt ) ) {
     160             :     /* TODO: This is not correct. It is fine and valid to receive a
     161             :        block with no votes, we don't want to return here as we still
     162             :        want to vote on the block. */
     163           0 :     FD_LOG_WARNING(( "No vote states received from replay. No votes will be sent"));
     164           0 :     return;
     165           0 :   }
     166             : 
     167             :   /* Parse the replay vote towers */
     168           0 :   for( ulong i=0UL; i<ctx->replay_towers_cnt; i++ ) {
     169           0 :     fd_tower_votes_remove_all( ctx->vote_towers[ i ] );
     170           0 :     fd_tower_from_vote_acc_data( ctx->replay_towers[ i ].acc, ctx->vote_towers[ i ] );
     171           0 :     ctx->vote_keys[ i ] = ctx->replay_towers[ i ].key;
     172             : 
     173           0 :     if( FD_UNLIKELY( 0==memcmp( &ctx->vote_keys[ i ], ctx->vote_acc, sizeof(fd_pubkey_t) ) ) ) {
     174             : 
     175             :       /* If this is our vote account, and our tower has not been
     176             :          initialized, initialize it with our vote state */
     177             : 
     178           0 :       if( FD_UNLIKELY( fd_tower_votes_empty( ctx->tower ) ) ) {
     179           0 :         fd_tower_from_vote_acc_data( ctx->replay_towers[i].acc, ctx->tower );
     180           0 :       }
     181             : 
     182             :       /* Copy in our voter state */
     183           0 :       memcpy( &ctx->vote_state, ctx->replay_towers[ i ].acc, ctx->replay_towers[ i ].acc_sz );
     184           0 :     }
     185           0 :   }
     186             : 
     187             :   /* Update ghost with the vote account states received from replay. */
     188             : 
     189           0 :   fd_ghost_ele_t const * ghost_ele  = fd_ghost_insert( ctx->ghost, &slot_info->parent_block_id, slot_info->slot, &slot_info->block_id, ctx->epoch->total_stake );
     190           0 :   FD_TEST( ghost_ele );
     191           0 :   update_ghost( ctx );
     192             : 
     193             :   /* Populate the out frag. */
     194             : 
     195           0 :   fd_tower_slot_done_t * msg = fd_chunk_to_laddr( ctx->out_mem, ctx->out_chunk );
     196             : 
     197             :   /* 1. Determine next slot to vote for, if one exists. */
     198             : 
     199           0 :   msg->vote_slot = fd_tower_vote_slot( ctx->tower, ctx->epoch, ctx->vote_keys, ctx->vote_towers, ctx->replay_towers_cnt, ctx->ghost );
     200           0 :   msg->new_root  = 0UL;
     201             : 
     202             :   /* 2. Determine new root, if there is one.  A new vote slot can result
     203             :         in a new root but not always. */
     204             : 
     205           0 :   if( FD_LIKELY( msg->vote_slot!=FD_SLOT_NULL ) ) {
     206           0 :     msg->root_slot                  = fd_tower_vote( ctx->tower, msg->vote_slot );
     207           0 :     fd_hash_t const * root_block_id = fd_ghost_hash( ctx->ghost, msg->root_slot );
     208           0 :     if( FD_LIKELY( root_block_id ) ) {
     209           0 :       msg->root_block_id = *root_block_id;
     210           0 :       msg->new_root      = 1;
     211           0 :       fd_ghost_publish( ctx->ghost, &msg->root_block_id );
     212           0 :     }
     213           0 :   }
     214             : 
     215             :   /* 3. Populate vote_txn with the current tower (regardless of whether
     216             :         there was a new vote slot or not). */
     217             : 
     218           0 :   fd_lockout_offset_t lockouts[ FD_TOWER_VOTE_MAX ];
     219           0 :   fd_txn_p_t txn[1];
     220           0 :   fd_tower_to_vote_txn( ctx->tower, msg->root_slot, lockouts, &slot_info->bank_hash, &slot_info->block_hash, ctx->identity_key, ctx->identity_key, ctx->vote_acc, txn );
     221           0 :   FD_TEST( !fd_tower_votes_empty( ctx->tower ) );
     222           0 :   FD_TEST( txn->payload_sz && txn->payload_sz<=FD_TPU_MTU );
     223           0 :   fd_memcpy( msg->vote_txn, txn->payload, txn->payload_sz );
     224           0 :   msg->vote_txn_sz = txn->payload_sz;
     225             : 
     226             :   /* 4. Determine next slot to reset leader pipeline to. */
     227             : 
     228           0 :   msg->reset_slot     = fd_tower_reset_slot( ctx->tower, ctx->epoch, ctx->ghost );
     229           0 :   msg->reset_block_id = *fd_ghost_hash( ctx->ghost, msg->reset_slot ); /* FIXME fd_ghost_hash is a naive lookup but reset_slot only ever refers to the confirmed duplicate */
     230             : 
     231             :   /* Publish the frag */
     232             : 
     233           0 :   fd_stem_publish( stem, 0UL, 0UL, ctx->out_chunk, sizeof(fd_tower_slot_done_t), 0UL, tsorig, fd_frag_meta_ts_comp( fd_tickcount() ) );
     234           0 :   ctx->out_chunk = fd_dcache_compact_next( ctx->out_chunk, sizeof(fd_tower_slot_done_t), ctx->out_chunk0, ctx->out_wmark );
     235             : 
     236             : # if LOGGING
     237             :   fd_ghost_print( ctx->ghost, ctx->epoch->total_stake, fd_ghost_root( ctx->ghost ) );
     238             :   fd_tower_print( ctx->tower, msg->root_slot );
     239             : # endif
     240           0 : }
     241             : 
     242             : static void
     243             : init_genesis( fd_tower_tile_t *                  ctx,
     244           0 :               fd_genesis_solana_global_t const * genesis ) {
     245           0 :   fd_hash_t manifest_block_id = { .ul = { 0xf17eda2ce7b1d } }; /* FIXME manifest_block_id */
     246           0 :   fd_ghost_init( ctx->ghost, 0UL, &manifest_block_id );
     247             : 
     248           0 :   fd_voter_t * epoch_voters = fd_epoch_voters( ctx->epoch );
     249             : 
     250           0 :   fd_pubkey_account_pair_global_t const * accounts = fd_genesis_solana_accounts_join( genesis );
     251           0 :   for( ulong i=0UL; i<genesis->accounts_len; i++ ) {
     252           0 :     fd_solana_account_global_t const * account = &accounts[ i ].account;
     253           0 :     if( FD_LIKELY( memcmp( account->owner.key, fd_solana_stake_program_id.key, sizeof(fd_pubkey_t) ) ) ) continue;
     254             : 
     255           0 :     uchar const * acc_data = fd_solana_account_data_join( account );
     256             : 
     257           0 :     fd_stake_state_v2_t stake_state;
     258           0 :     if( FD_UNLIKELY( !fd_bincode_decode_static( stake_state_v2, &stake_state, acc_data, account->data_len, NULL ) ) ) {
     259           0 :       FD_LOG_ERR(( "Failed to deserialize genesis stake account %s", FD_BASE58_ENC_32_ALLOCA( accounts[ i ].key.uc ) ));
     260           0 :     }
     261             : 
     262           0 :     if( FD_UNLIKELY( !fd_stake_state_v2_is_stake( &stake_state )     ) ) continue;
     263           0 :     if( FD_UNLIKELY( !stake_state.inner.stake.stake.delegation.stake ) ) continue;
     264             : 
     265           0 :     fd_pubkey_t const * pubkey = &stake_state.inner.stake.stake.delegation.voter_pubkey;
     266             : 
     267           0 :     fd_voter_t * voter = fd_epoch_voters_insert( epoch_voters, *pubkey );
     268             : 
     269           0 :     voter->stake             = stake_state.inner.stake.stake.delegation.stake;
     270           0 :     ctx->epoch->total_stake += voter->stake;
     271           0 :   }
     272           0 : }
     273             : 
     274             : static void
     275             : snapshot_done( fd_tower_tile_t *              ctx,
     276           0 :                fd_snapshot_manifest_t const * manifest ) {
     277           0 :   fd_hash_t manifest_block_id = { .ul = { 0xf17eda2ce7b1d } }; /* FIXME manifest_block_id */
     278           0 :   fd_ghost_init( ctx->ghost, manifest->slot, &manifest_block_id );
     279             : 
     280             :   /* As per the documentation in fd_ssmsg.h, we use
     281             :      manifest->epoch_stakes[1] to initialize the epoch voters. These
     282             :      correspond to the amount staked to each vote account at the
     283             :      beginning of the current epoch:
     284             : 
     285             :      manifest->epoch_stakes[0] represents the stakes used to generate
     286             :      the leader schedule at the end of the current epoch. These are the
     287             :      stakes as of the end of two epochs ago.
     288             : 
     289             :      manifest->epoch_stakes[1] represents the stakes used to generate
     290             :      the leader schedule at the end of the next epoch. These are the
     291             :      stakes as of the end of the previous epoch, so these will be the
     292             :      stakes throughout the current epoch. */
     293           0 :   fd_voter_t * epoch_voters = fd_epoch_voters( ctx->epoch );
     294           0 :   fd_snapshot_manifest_epoch_stakes_t const * epoch_stakes = &manifest->epoch_stakes[ 1 ];
     295           0 :   for( ulong i=0UL; i<epoch_stakes->vote_stakes_len; i++ ) {
     296           0 :     if( FD_UNLIKELY( !epoch_stakes->vote_stakes[ i ].stake ) ) continue;
     297             : 
     298           0 :     fd_pubkey_t const * pubkey = (fd_pubkey_t const *)epoch_stakes->vote_stakes[ i ].vote;
     299             : 
     300           0 : #if FD_EPOCH_USE_HANDHOLDING
     301           0 :     FD_TEST( !fd_epoch_voters_query( epoch_voters, *pubkey, NULL ) );
     302           0 :     FD_TEST( fd_epoch_voters_key_cnt( epoch_voters ) < fd_epoch_voters_key_max( epoch_voters ) );
     303           0 : #endif
     304             : 
     305           0 :     fd_voter_t * voter = fd_epoch_voters_insert( epoch_voters, *pubkey );
     306             : 
     307           0 : #if FD_EPOCH_USE_HANDHOLDING
     308           0 :     FD_TEST( 0==memcmp( voter->key.uc, pubkey->uc, sizeof(fd_pubkey_t) ) );
     309           0 :     FD_TEST( fd_epoch_voters_query( epoch_voters, voter->key, NULL ) );
     310           0 : #endif
     311             : 
     312           0 :     voter->stake             = epoch_stakes->vote_stakes[ i ].stake;
     313           0 :     voter->replay_vote.slot  = FD_SLOT_NULL;
     314           0 :     voter->gossip_vote.slot  = FD_SLOT_NULL;
     315           0 :     voter->rooted_vote.slot  = FD_SLOT_NULL;
     316           0 :     ctx->epoch->total_stake += voter->stake;
     317           0 :   }
     318           0 : }
     319             : 
     320             : static inline int
     321             : returnable_frag( fd_tower_tile_t *   ctx,
     322             :                  ulong               in_idx,
     323             :                  ulong               seq,
     324             :                  ulong               sig,
     325             :                  ulong               chunk,
     326             :                  ulong               sz,
     327             :                  ulong               ctl,
     328             :                  ulong               tsorig,
     329             :                  ulong               tspub,
     330           0 :                  fd_stem_context_t * stem ) {
     331           0 :   (void)seq;
     332           0 :   (void)tspub;
     333             : 
     334           0 :   if( FD_LIKELY( ctx->in_kind[ in_idx ]==IN_KIND_GENESIS ) ) {
     335           0 :     if( FD_LIKELY( sig==GENESI_SIG_BOOTSTRAP_COMPLETED ) ) {
     336           0 :       init_genesis( ctx, fd_type_pun( (uchar*)fd_chunk_to_laddr( ctx->in[ in_idx ].mem, chunk )+sizeof(fd_lthash_value_t)+sizeof(fd_hash_t) ) );
     337           0 :       ctx->initialized = 1;
     338           0 :     }
     339           0 :   } else if( FD_LIKELY( ctx->in_kind[ in_idx ]==IN_KIND_SNAP ) ) {
     340           0 :     if( FD_UNLIKELY( fd_ssmsg_sig_message( sig )==FD_SSMSG_DONE ) ) {
     341           0 :       snapshot_done( ctx, &ctx->manifest );
     342           0 :       ctx->initialized = 1;
     343           0 :     } else {
     344           0 :       if( FD_UNLIKELY( chunk<ctx->in[ in_idx ].chunk0 || chunk>ctx->in[ in_idx ].wmark || sz>ctx->in[ in_idx ].mtu ) )
     345           0 :         FD_LOG_ERR(( "chunk %lu %lu from in %d corrupt, not in range [%lu,%lu]", chunk, sz, ctx->in_kind[ in_idx ], ctx->in[ in_idx ].chunk0, ctx->in[ in_idx ].wmark ));
     346             : 
     347           0 :       fd_memcpy( &ctx->manifest, fd_chunk_to_laddr( ctx->in[ in_idx ].mem, chunk ), sizeof(fd_snapshot_manifest_t) );
     348           0 :     }
     349           0 :   } else if( FD_LIKELY( ctx->in_kind[ in_idx ]==IN_KIND_REPLAY ) ) {
     350           0 :     if( FD_UNLIKELY( chunk<ctx->in[ in_idx ].chunk0 || chunk>ctx->in[ in_idx ].wmark || sz>ctx->in[ in_idx ].mtu ) )
     351           0 :       FD_LOG_ERR(( "chunk %lu %lu from in %d corrupt, not in range [%lu,%lu]", chunk, sz, ctx->in_kind[ in_idx ], ctx->in[ in_idx ].chunk0, ctx->in[ in_idx ].wmark ));
     352             : 
     353             :     /* If we haven't initialized from either genesis or a snapshot yet,
     354             :        we cannot process any frags from replay as it's a race condition,
     355             :        just wait until we initialize and then process. */
     356           0 :     if( FD_UNLIKELY( !ctx->initialized ) ) return 1;
     357             : 
     358           0 :     if( FD_LIKELY( sig==REPLAY_SIG_SLOT_COMPLETED ) ) {
     359           0 :       fd_memcpy( &ctx->replay_slot_info, fd_chunk_to_laddr( ctx->in[ in_idx ].mem, chunk ), sizeof(fd_replay_slot_completed_t) );
     360           0 :     } else if( FD_LIKELY( sig==REPLAY_SIG_VOTE_STATE ) ) {
     361           0 :       if( FD_UNLIKELY( fd_frag_meta_ctl_som( ctl ) ) ) ctx->replay_towers_cnt = 0;
     362             : 
     363           0 :       if( FD_UNLIKELY( ctx->replay_towers_cnt>=FD_REPLAY_TOWER_VOTE_ACC_MAX ) ) FD_LOG_ERR(( "tower received more vote states than expected" ));
     364           0 :       memcpy( &ctx->replay_towers[ ctx->replay_towers_cnt ], fd_chunk_to_laddr( ctx->in[ in_idx ].mem, chunk ), sizeof(fd_replay_tower_t) );
     365           0 :       ctx->replay_towers_cnt++;
     366             : 
     367           0 :       if( FD_UNLIKELY( fd_frag_meta_ctl_eom( ctl ) ) ) replay_slot_completed( ctx, &ctx->replay_slot_info, tsorig, stem );
     368           0 :     }
     369           0 :   } else {
     370           0 :     FD_LOG_ERR(( "unexpected input kind %d", ctx->in_kind[ in_idx ] ));
     371           0 :   }
     372             : 
     373           0 :   return 0;
     374           0 : }
     375             : 
     376             : static void
     377             : privileged_init( fd_topo_t *      topo,
     378           0 :                  fd_topo_tile_t * tile ) {
     379           0 :   void * scratch = fd_topo_obj_laddr( topo, tile->tile_obj_id );
     380           0 :   FD_SCRATCH_ALLOC_INIT( l, scratch );
     381           0 :   fd_tower_tile_t * ctx = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_tower_tile_t), sizeof(fd_tower_tile_t) );
     382             : 
     383           0 :   if( FD_UNLIKELY( !strcmp( tile->tower.identity_key_path, "" ) ) )
     384           0 :     FD_LOG_ERR(( "identity_key_path not set" ));
     385             : 
     386           0 :   ctx->identity_key[ 0 ] = *(fd_pubkey_t const *)fd_type_pun_const( fd_keyload_load( tile->tower.identity_key_path, /* pubkey only: */ 1 ) );
     387             : 
     388             :   /* The vote key can be specified either directly as a base58 encoded
     389             :      pubkey, or as a file path.  We first try to decode as a pubkey.
     390             : 
     391             :      TODO: The "vote_acc_path" should be renamed, as it might not be
     392             :      a path. */
     393           0 :   uchar * vote_key = fd_base58_decode_32( tile->tower.vote_acc_path, ctx->vote_acc->uc );
     394           0 :   if( FD_UNLIKELY( !vote_key ) ) {
     395           0 :     ctx->vote_acc[ 0 ] = *(fd_pubkey_t const *)fd_type_pun_const( fd_keyload_load( tile->tower.vote_acc_path, /* pubkey only: */ 1 ) );
     396           0 :   }
     397             : 
     398           0 :   char path[ PATH_MAX ];
     399           0 :   FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "%s/tower-1_9-%s.bin.new", tile->tower.ledger_path, FD_BASE58_ENC_32_ALLOCA( ctx->identity_key->uc ) ) );
     400           0 :   ctx->checkpt_fd = open( path, O_WRONLY|O_CREAT|O_TRUNC, 0600 );
     401           0 :   if( FD_UNLIKELY( -1==ctx->checkpt_fd ) ) FD_LOG_ERR(( "open(`%s`) failed (%i-%s)", path, errno, fd_io_strerror( errno ) ));
     402             : 
     403           0 :   FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "%s/tower-1_9-%s.bin", tile->tower.ledger_path, FD_BASE58_ENC_32_ALLOCA( ctx->identity_key->uc ) ) );
     404           0 :   ctx->restore_fd = open( path, O_RDONLY );
     405           0 :   if( FD_UNLIKELY( -1==ctx->restore_fd && errno!=ENOENT ) ) FD_LOG_WARNING(( "open(`%s`) failed (%i-%s)", path, errno, fd_io_strerror( errno ) ));
     406           0 : }
     407             : 
     408             : static void
     409             : unprivileged_init( fd_topo_t *      topo,
     410           0 :                    fd_topo_tile_t * tile ) {
     411           0 :   void * scratch = fd_topo_obj_laddr( topo, tile->tile_obj_id );
     412             : 
     413           0 :   FD_SCRATCH_ALLOC_INIT( l, scratch );
     414           0 :   fd_tower_tile_t * ctx  = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_tower_tile_t), sizeof(fd_tower_tile_t)                            );
     415           0 :   void * _epoch          = FD_SCRATCH_ALLOC_APPEND( l, fd_epoch_align(),         fd_epoch_footprint( FD_REPLAY_TOWER_VOTE_ACC_MAX ) );
     416           0 :   void * _ghost          = FD_SCRATCH_ALLOC_APPEND( l, fd_ghost_align(),         fd_ghost_footprint( FD_BLOCK_MAX )                 );
     417           0 :   void * _tower          = FD_SCRATCH_ALLOC_APPEND( l, fd_tower_align(),         fd_tower_footprint()                               );
     418           0 :   void * _vote_towers    = FD_SCRATCH_ALLOC_APPEND( l, fd_tower_align(),         fd_tower_footprint()*FD_REPLAY_TOWER_VOTE_ACC_MAX  );
     419             : 
     420           0 :   ctx->epoch = fd_epoch_join( fd_epoch_new( _epoch, FD_REPLAY_TOWER_VOTE_ACC_MAX ) );
     421           0 :   FD_TEST( ctx->epoch );
     422             : 
     423           0 :   ctx->ghost = fd_ghost_join( fd_ghost_new( _ghost, FD_BLOCK_MAX, 42UL ) );
     424           0 :   FD_TEST( ctx->ghost );
     425             : 
     426           0 :   ctx->tower = fd_tower_join( fd_tower_new( _tower ) );
     427           0 :   FD_TEST( ctx->tower );
     428             : 
     429           0 :   for( ulong i=0UL; i<FD_REPLAY_TOWER_VOTE_ACC_MAX; i++ ) {
     430           0 :     ctx->vote_towers[ i ] = fd_tower_join( fd_tower_new( (uchar*)_vote_towers+(i*fd_tower_footprint() ) ) );
     431           0 :     FD_TEST( ctx->vote_towers[ i ] );
     432           0 :   }
     433             : 
     434           0 :   ctx->initialized = 0;
     435             : 
     436           0 :   ctx->replay_towers_cnt = 0UL;
     437             : 
     438           0 :   FD_TEST( tile->in_cnt<sizeof(ctx->in_kind)/sizeof(ctx->in_kind[0]) );
     439           0 :   for( ulong i=0UL; i<tile->in_cnt; i++ ) {
     440           0 :     fd_topo_link_t * link = &topo->links[ tile->in_link_id[ i ] ];
     441           0 :     fd_topo_wksp_t * link_wksp = &topo->workspaces[ topo->objs[ link->dcache_obj_id ].wksp_id ];
     442             : 
     443           0 :     if( FD_LIKELY( !strcmp( link->name, "genesi_out"        ) ) ) ctx->in_kind[ i ] = IN_KIND_GENESIS;
     444           0 :     else if( FD_LIKELY( !strcmp( link->name, "snapin_manif" ) ) ) ctx->in_kind[ i ] = IN_KIND_SNAP;
     445           0 :     else if( FD_LIKELY( !strcmp( link->name, "replay_out"   ) ) ) ctx->in_kind[ i ] = IN_KIND_REPLAY;
     446           0 :     else FD_LOG_ERR(( "tower tile has unexpected input link %lu %s", i, link->name ));
     447             : 
     448           0 :     ctx->in[ i ].mem    = link_wksp->wksp;
     449           0 :     ctx->in[ i ].mtu    = link->mtu;
     450           0 :     ctx->in[ i ].chunk0 = fd_dcache_compact_chunk0( ctx->in[ i ].mem, link->dcache );
     451           0 :     ctx->in[ i ].wmark  = fd_dcache_compact_wmark ( ctx->in[ i ].mem, link->dcache, link->mtu );
     452           0 :   }
     453             : 
     454           0 :   ctx->out_mem    = topo->workspaces[ topo->objs[ topo->links[ tile->out_link_id[ 0 ] ].dcache_obj_id ].wksp_id ].wksp;
     455           0 :   ctx->out_chunk0 = fd_dcache_compact_chunk0( ctx->out_mem, topo->links[ tile->out_link_id[ 0 ] ].dcache );
     456           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 );
     457           0 :   ctx->out_chunk  = ctx->out_chunk0;
     458           0 : }
     459             : 
     460             : static ulong
     461             : populate_allowed_seccomp( fd_topo_t const *      topo,
     462             :                           fd_topo_tile_t const * tile,
     463             :                           ulong                  out_cnt,
     464           0 :                           struct sock_filter *   out ) {
     465           0 :   void * scratch = fd_topo_obj_laddr( topo, tile->tile_obj_id );
     466           0 :   FD_SCRATCH_ALLOC_INIT( l, scratch );
     467           0 :   fd_tower_tile_t * ctx = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_tower_tile_t), sizeof(fd_tower_tile_t) );
     468             : 
     469           0 :   populate_sock_filter_policy_fd_tower_tile( out_cnt, out, (uint)fd_log_private_logfile_fd(), (uint)ctx->checkpt_fd, (uint)ctx->restore_fd );
     470           0 :   return sock_filter_policy_fd_tower_tile_instr_cnt;
     471           0 : }
     472             : 
     473             : static ulong
     474             : populate_allowed_fds( fd_topo_t const *      topo,
     475             :                       fd_topo_tile_t const * tile,
     476             :                       ulong                  out_fds_cnt,
     477           0 :                       int *                  out_fds ) {
     478           0 :   void * scratch = fd_topo_obj_laddr( topo, tile->tile_obj_id );
     479           0 :   FD_SCRATCH_ALLOC_INIT( l, scratch );
     480           0 :   fd_tower_tile_t * ctx = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_tower_tile_t), sizeof(fd_tower_tile_t) );
     481             : 
     482           0 :   if( FD_UNLIKELY( out_fds_cnt<4UL ) ) FD_LOG_ERR(( "out_fds_cnt %lu", out_fds_cnt ));
     483             : 
     484           0 :   ulong out_cnt = 0UL;
     485           0 :   out_fds[ out_cnt++ ] = 2; /* stderr */
     486           0 :   if( FD_LIKELY( -1!=fd_log_private_logfile_fd() ) )
     487           0 :     out_fds[ out_cnt++ ] = fd_log_private_logfile_fd(); /* logfile */
     488           0 :   if( FD_LIKELY( ctx->checkpt_fd!=-1 ) ) out_fds[ out_cnt++ ] = ctx->checkpt_fd;
     489           0 :   if( FD_LIKELY( ctx->restore_fd!=-1 ) ) out_fds[ out_cnt++ ] = ctx->restore_fd;
     490           0 :   return out_cnt;
     491           0 : }
     492             : 
     493           0 : #define STEM_BURST (1UL)
     494             : 
     495           0 : #define STEM_CALLBACK_CONTEXT_TYPE    fd_tower_tile_t
     496           0 : #define STEM_CALLBACK_CONTEXT_ALIGN   alignof(fd_tower_tile_t)
     497           0 : #define STEM_CALLBACK_RETURNABLE_FRAG returnable_frag
     498             : 
     499             : #include "../../disco/stem/fd_stem.c"
     500             : 
     501             : fd_topo_run_tile_t fd_tile_tower = {
     502             :   .name                     = "tower",
     503             :   .populate_allowed_seccomp = populate_allowed_seccomp,
     504             :   .populate_allowed_fds     = populate_allowed_fds,
     505             :   .scratch_align            = scratch_align,
     506             :   .scratch_footprint        = scratch_footprint,
     507             :   .unprivileged_init        = unprivileged_init,
     508             :   .privileged_init          = privileged_init,
     509             :   .run                      = stem_run,
     510             : };

Generated by: LCOV version 1.14