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

Generated by: LCOV version 1.14