LCOV - code coverage report
Current view: top level - discof/resolv - fd_resolv_tile.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 350 0.0 %
Date: 2025-12-06 04:45:29 Functions: 0 14 0.0 %

          Line data    Source code
       1             : #include "fd_resolv_tile.h"
       2             : #include "../../disco/fd_txn_m.h"
       3             : #include "../../disco/topo/fd_topo.h"
       4             : #include "../replay/fd_replay_tile.h"
       5             : #include "generated/fd_resolv_tile_seccomp.h"
       6             : #include "../../disco/metrics/fd_metrics.h"
       7             : #include "../../flamenco/accdb/fd_accdb_sync.h"
       8             : #include "../../flamenco/accdb/fd_accdb_impl_v1.h"
       9             : #include "../../flamenco/runtime/fd_alut_interp.h"
      10             : #include "../../flamenco/runtime/fd_system_ids_pp.h"
      11             : #include "../../flamenco/runtime/fd_bank.h"
      12             : #include "../../util/pod/fd_pod_format.h"
      13             : 
      14             : #if FD_HAS_AVX
      15             : #include "../../util/simd/fd_avx.h"
      16             : #endif
      17             : 
      18           0 : #define IN_KIND_DEDUP  (0)
      19           0 : #define IN_KIND_REPLAY (1)
      20             : 
      21             : struct blockhash {
      22             :   uchar b[ 32 ];
      23             : };
      24             : 
      25             : typedef struct blockhash blockhash_t;
      26             : 
      27             : struct blockhash_map {
      28             :   blockhash_t key;
      29             :   ulong       slot;
      30             : };
      31             : 
      32             : typedef struct blockhash_map blockhash_map_t;
      33             : 
      34             : static const blockhash_t null_blockhash = { 0 };
      35             : 
      36             : /* The blockhash ring holds recent blockhashes, so we can identify when
      37             :    a transaction arrives, what slot it will expire (and can no longer be
      38             :    packed) in.  This is useful so we don't send transactions to pack
      39             :    that are no longer packable.
      40             : 
      41             :    Unfortunately, poorly written transaction senders frequently send
      42             :    transactions from millions of slots ago, so we need a large ring to
      43             :    be able to determine and evict these.  The highest practically useful
      44             :    value here is around 22, which works out to 19 days of blockhash
      45             :    history.  Beyond this, the validator is likely to be restarted, and
      46             :    lose the history anyway. */
      47             : 
      48           0 : #define BLOCKHASH_LG_RING_CNT 22UL
      49           0 : #define BLOCKHASH_RING_LEN   (1UL<<BLOCKHASH_LG_RING_CNT)
      50             : 
      51             : #define MAP_NAME              map
      52           0 : #define MAP_T                 blockhash_map_t
      53           0 : #define MAP_KEY_T             blockhash_t
      54           0 : #define MAP_LG_SLOT_CNT       (BLOCKHASH_LG_RING_CNT+1UL)
      55           0 : #define MAP_KEY_NULL          null_blockhash
      56             : #if FD_HAS_AVX
      57           0 : # define MAP_KEY_INVAL(k)     _mm256_testz_si256( wb_ldu( (k).b ), wb_ldu( (k).b ) )
      58             : #else
      59             : # define MAP_KEY_INVAL(k)     MAP_KEY_EQUAL(k, null_blockhash)
      60             : #endif
      61           0 : #define MAP_KEY_EQUAL(k0,k1)  (!memcmp((k0).b,(k1).b, 32UL))
      62             : #define MAP_MEMOIZE           0
      63             : #define MAP_KEY_EQUAL_IS_SLOW 1
      64           0 : #define MAP_KEY_HASH(key)     fd_uint_load_4( (key).b )
      65             : #define MAP_QUERY_OPT         1
      66             : 
      67             : #include "../../util/tmpl/fd_map.c"
      68             : 
      69             : typedef struct {
      70             :   union {
      71             :     ulong pool_next; /* Used when it's released */
      72             :     ulong lru_next;  /* Used when it's acquired */
      73             :   };                 /* .. so it's okay to store them in the same memory */
      74             :   ulong lru_prev;
      75             : 
      76             :   ulong map_next;
      77             :   ulong map_prev;
      78             : 
      79             :   blockhash_t * blockhash;
      80             :   uchar _[ FD_TPU_PARSED_MTU ] __attribute__((aligned(alignof(fd_txn_m_t))));
      81             : } fd_stashed_txn_m_t;
      82             : 
      83             : #define POOL_NAME      pool
      84           0 : #define POOL_T         fd_stashed_txn_m_t
      85           0 : #define POOL_NEXT      pool_next
      86             : #define POOL_IDX_T     ulong
      87             : 
      88             : #include "../../util/tmpl/fd_pool.c"
      89             : 
      90             : /* We'll push at the head, which means the tail is the oldest. */
      91             : #define DLIST_NAME  lru_list
      92             : #define DLIST_ELE_T fd_stashed_txn_m_t
      93           0 : #define DLIST_PREV  lru_prev
      94           0 : #define DLIST_NEXT  lru_next
      95             : 
      96             : #include "../../util/tmpl/fd_dlist.c"
      97             : 
      98             : #define MAP_NAME          map_chain
      99           0 : #define MAP_ELE_T         fd_stashed_txn_m_t
     100             : #define MAP_KEY_T         blockhash_t *
     101           0 : #define MAP_KEY           blockhash
     102           0 : #define MAP_IDX_T         ulong
     103           0 : #define MAP_NEXT          map_next
     104           0 : #define MAP_PREV          map_prev
     105           0 : #define MAP_KEY_HASH(k,s) ((s) ^ fd_ulong_load_8( (*(k))->b ))
     106           0 : #define MAP_KEY_EQ(k0,k1) (!memcmp((*(k0))->b, (*(k1))->b, 32UL))
     107             : #define MAP_OPTIMIZE_RANDOM_ACCESS_REMOVAL 1
     108             : #define MAP_MULTI         1
     109             : 
     110             : #include "../../util/tmpl/fd_map_chain.c"
     111             : 
     112             : typedef struct {
     113             :   int         kind;
     114             : 
     115             :   fd_wksp_t * mem;
     116             :   ulong       chunk0;
     117             :   ulong       wmark;
     118             :   ulong       mtu;
     119             : } fd_resolv_in_ctx_t;
     120             : 
     121             : typedef struct {
     122             :   fd_wksp_t * mem;
     123             :   ulong       chunk0;
     124             :   ulong       wmark;
     125             :   ulong       chunk;
     126             : } fd_resolv_out_ctx_t;
     127             : 
     128             : typedef struct {
     129             :   ulong round_robin_idx;
     130             :   ulong round_robin_cnt;
     131             : 
     132             :   int   bundle_failed;
     133             :   ulong bundle_id;
     134             : 
     135             :   blockhash_map_t * blockhash_map;
     136             : 
     137             :   ulong flushing_slot;
     138             :   ulong flush_pool_idx;
     139             : 
     140             :   /* In the full client, the resolv tile is passed only a rooted bank
     141             :      index from replay whenever the root is advanced.
     142             : 
     143             :      This is enough to query the accounts database for that bank and
     144             :      retrieve the address lookup tables.  Because of lifetime concerns
     145             :      around bank ownership, the replay tile is solely responsible for
     146             :      freeing the bank when it is no longer needed.  To facilitate this,
     147             :      the resolv tile sends a message to replay when it is done with a
     148             :      rooted bank (after exchanging it for a new rooted bank). */
     149             :   fd_banks_t * banks;
     150             :   fd_bank_t *  bank;
     151             : 
     152             :   fd_accdb_user_t accdb[1];
     153             : 
     154             :   fd_stashed_txn_m_t * pool;
     155             :   map_chain_t *        map_chain;
     156             :   lru_list_t           lru_list[1];
     157             : 
     158             :   ulong completed_slot;
     159             :   ulong blockhash_ring_idx;
     160             :   blockhash_t blockhash_ring[ BLOCKHASH_RING_LEN ];
     161             : 
     162             :   fd_replay_root_advanced_t  _rooted_slot_msg;
     163             :   fd_replay_slot_completed_t _completed_slot_msg;
     164             : 
     165             :   struct {
     166             :     ulong lut[ FD_METRICS_COUNTER_RESOLV_LUT_RESOLVED_CNT ];
     167             :     ulong blockhash_expired;
     168             :     ulong bundle_peer_failure;
     169             :     ulong stash[ FD_METRICS_COUNTER_RESOLV_STASH_OPERATION_CNT ];
     170             :     ulong db_race;
     171             :   } metrics;
     172             : 
     173             :   fd_resolv_in_ctx_t in[ 64UL ];
     174             : 
     175             :   fd_resolv_out_ctx_t out_pack[ 1UL ];
     176             :   fd_resolv_out_ctx_t out_replay[ 1UL ];
     177             : } fd_resolv_ctx_t;
     178             : 
     179             : FD_FN_CONST static inline ulong
     180           0 : scratch_align( void ) {
     181           0 :   return alignof( fd_resolv_ctx_t );
     182           0 : }
     183             : 
     184             : FD_FN_PURE static inline ulong
     185           0 : scratch_footprint( fd_topo_tile_t const * tile ) {
     186           0 :   (void)tile;
     187           0 :   ulong l = FD_LAYOUT_INIT;
     188           0 :   l = FD_LAYOUT_APPEND( l, alignof( fd_resolv_ctx_t ), sizeof( fd_resolv_ctx_t )        );
     189           0 :   l = FD_LAYOUT_APPEND( l, pool_align(),               pool_footprint     ( 1UL<<16UL ) );
     190           0 :   l = FD_LAYOUT_APPEND( l, map_chain_align(),          map_chain_footprint( 8192UL    ) );
     191           0 :   l = FD_LAYOUT_APPEND( l, map_align(),                map_footprint()                  );
     192           0 :   return FD_LAYOUT_FINI( l, scratch_align() );
     193           0 : }
     194             : 
     195             : static inline void
     196           0 : metrics_write( fd_resolv_ctx_t * ctx ) {
     197           0 :   FD_MCNT_SET(       RESOLF, BLOCKHASH_EXPIRED,               ctx->metrics.blockhash_expired );
     198           0 :   FD_MCNT_ENUM_COPY( RESOLF, LUT_RESOLVED,                    ctx->metrics.lut );
     199           0 :   FD_MCNT_ENUM_COPY( RESOLF, STASH_OPERATION,                 ctx->metrics.stash );
     200           0 :   FD_MCNT_SET(       RESOLF, TRANSACTION_BUNDLE_PEER_FAILURE, ctx->metrics.bundle_peer_failure );
     201           0 :   FD_MCNT_SET(       RESOLF, DB_RACES,                        ctx->metrics.db_race );
     202           0 : }
     203             : 
     204             : static int
     205             : before_frag( fd_resolv_ctx_t * ctx,
     206             :              ulong             in_idx,
     207             :              ulong             seq,
     208           0 :              ulong             sig ) {
     209           0 :   (void)sig;
     210             : 
     211           0 :   if( FD_UNLIKELY( ctx->in[in_idx].kind==IN_KIND_REPLAY ) ) return 0;
     212             : 
     213           0 :   return (seq % ctx->round_robin_cnt) != ctx->round_robin_idx;
     214           0 : }
     215             : 
     216             : static inline void
     217             : during_frag( fd_resolv_ctx_t * ctx,
     218             :              ulong             in_idx,
     219             :              ulong             seq FD_PARAM_UNUSED,
     220             :              ulong             sig FD_PARAM_UNUSED,
     221             :              ulong             chunk,
     222             :              ulong             sz,
     223           0 :              ulong             ctl FD_PARAM_UNUSED ) {
     224             : 
     225           0 :   if( FD_UNLIKELY( chunk<ctx->in[ in_idx ].chunk0 || chunk>ctx->in[ in_idx ].wmark || sz>ctx->in[ in_idx ].mtu ) )
     226           0 :     FD_LOG_ERR(( "chunk %lu %lu corrupt, not in range [%lu,%lu]", chunk, sz, ctx->in[ in_idx ].chunk0, ctx->in[ in_idx ].wmark ));
     227             : 
     228           0 :   switch( ctx->in[in_idx].kind ) {
     229           0 :     case IN_KIND_DEDUP: {
     230           0 :       uchar * src = (uchar *)fd_chunk_to_laddr( ctx->in[in_idx].mem, chunk );
     231           0 :       uchar * dst = (uchar *)fd_chunk_to_laddr( ctx->out_pack->mem, ctx->out_pack->chunk );
     232           0 :       fd_memcpy( dst, src, sz );
     233           0 :       break;
     234           0 :     }
     235           0 :     case IN_KIND_REPLAY: {
     236           0 :       if( FD_UNLIKELY( sig==REPLAY_SIG_ROOT_ADVANCED ) ) {
     237           0 :         ctx->_rooted_slot_msg = *(fd_replay_root_advanced_t *)fd_chunk_to_laddr_const( ctx->in[in_idx].mem, chunk );
     238           0 :       } else if( FD_UNLIKELY( sig==REPLAY_SIG_SLOT_COMPLETED ) ) {
     239           0 :         ctx->_completed_slot_msg = *(fd_replay_slot_completed_t *)fd_chunk_to_laddr_const( ctx->in[in_idx].mem, chunk );
     240           0 :       }
     241           0 :       break;
     242           0 :     }
     243           0 :     default:
     244           0 :       FD_LOG_ERR(( "unknown in kind %d", ctx->in[in_idx].kind ));
     245           0 :   }
     246           0 : }
     247             : 
     248             : /* peek_alut reads a single address lookup table from database cache. */
     249             : 
     250             : static int
     251             : peek_alut( fd_resolv_ctx_t *  ctx,
     252             :            fd_txn_m_t *       txnm,
     253             :            fd_alut_interp_t * interp,
     254           0 :            ulong              alut_idx ) {
     255           0 :   fd_funk_txn_xid_t const xid = { .ul = { fd_bank_slot_get( ctx->bank ), fd_bank_slot_get( ctx->bank ) } };
     256             : 
     257           0 :   ulong ro_indir_cnt_old = interp->ro_indir_cnt;
     258           0 :   ulong rw_indir_cnt_old = interp->rw_indir_cnt;
     259           0 :   ulong alut_idx_old     = interp->alut_idx;
     260             : 
     261           0 :   fd_txn_t const * txn         = fd_txn_m_txn_t_const  ( txnm );
     262           0 :   uchar const *    txn_payload = fd_txn_m_payload_const( txnm );
     263           0 :   fd_txn_acct_addr_lut_t const * addr_lut =
     264           0 :       &fd_txn_get_address_tables_const( txn )[ alut_idx ];
     265           0 :   fd_pubkey_t addr_lut_acc = FD_LOAD( fd_pubkey_t, txn_payload+addr_lut->addr_off );
     266             : 
     267           0 :   int err = 0;
     268           0 :   for(;;) {
     269           0 :     fd_accdb_peek_t _peek[1];
     270           0 :     fd_accdb_peek_t * peek = fd_accdb_peek(
     271           0 :         ctx->accdb, _peek, &xid, &addr_lut_acc );
     272           0 :     if( FD_UNLIKELY( !peek ) ) {
     273           0 :       err = FD_RUNTIME_TXN_ERR_ADDRESS_LOOKUP_TABLE_NOT_FOUND;
     274           0 :       break;
     275           0 :     }
     276             : 
     277           0 :     err = fd_alut_interp_next(
     278           0 :         interp,
     279           0 :         &addr_lut_acc,
     280           0 :         fd_accdb_ref_owner     ( peek->acc ),
     281           0 :         fd_accdb_ref_data_const( peek->acc ),
     282           0 :         fd_accdb_ref_data_sz   ( peek->acc ) );
     283             : 
     284           0 :     int peek_ok = fd_accdb_peek_test( peek );
     285           0 :     fd_accdb_peek_drop( peek );
     286           0 :     if( FD_LIKELY( peek_ok ) ) break;
     287             : 
     288             :     /* Restore old interp state and retry */
     289           0 :     FD_SPIN_PAUSE();
     290           0 :     ctx->metrics.db_race++;
     291           0 :     interp->ro_indir_cnt = ro_indir_cnt_old;
     292           0 :     interp->rw_indir_cnt = rw_indir_cnt_old;
     293           0 :     interp->alut_idx     = alut_idx_old;
     294           0 :   }
     295             : 
     296           0 :   return err;
     297           0 : }
     298             : 
     299             : /* peek_aluts reads address lookup tables from database cache.
     300             :    Gracefully recovers from data races and missing accounts. */
     301             : 
     302             : static int
     303             : peek_aluts( fd_resolv_ctx_t * ctx,
     304           0 :             fd_txn_m_t *      txnm ) {
     305             : 
     306             :   /* Unpack context */
     307           0 :   fd_txn_t const *          txn          = fd_txn_m_txn_t_const  ( txnm );
     308           0 :   uchar const *             txn_payload  = fd_txn_m_payload_const( txnm );
     309           0 :   ulong const               alut_cnt     = txn->addr_table_lookup_cnt;
     310           0 :   ulong const               slot         = fd_bank_slot_get( ctx->bank );
     311           0 :   fd_sysvar_cache_t const * sysvar_cache = fd_bank_sysvar_cache_query( ctx->bank ); FD_TEST( sysvar_cache );
     312           0 :   fd_slot_hash_t const *    slot_hashes  = fd_sysvar_cache_slot_hashes_join_const( sysvar_cache );
     313             : 
     314             :   /* Write indirect addrs into here */
     315           0 :   fd_acct_addr_t * indir_addrs = fd_txn_m_alut( txnm );
     316             : 
     317           0 :   int err = FD_RUNTIME_EXECUTE_SUCCESS;
     318           0 :   fd_alut_interp_t interp[1];
     319           0 :   fd_alut_interp_new( interp, indir_addrs, txn, txn_payload, slot_hashes, slot );
     320           0 :   for( ulong i=0UL; i<alut_cnt; i++ ) {
     321           0 :     err = peek_alut( ctx, txnm, interp, i );
     322           0 :     if( FD_UNLIKELY( err ) ) break;
     323           0 :   }
     324           0 :   fd_alut_interp_delete( interp );
     325           0 :   fd_sysvar_cache_slot_hashes_leave_const( sysvar_cache, slot_hashes );
     326             : 
     327           0 :   ulong ctr_idx;
     328           0 :   switch( err ) {
     329           0 :   case FD_RUNTIME_EXECUTE_SUCCESS:                            ctr_idx = FD_METRICS_ENUM_LUT_RESOLVE_RESULT_V_SUCCESS_IDX;               break;
     330           0 :   case FD_RUNTIME_TXN_ERR_ADDRESS_LOOKUP_TABLE_NOT_FOUND:     ctr_idx = FD_METRICS_ENUM_LUT_RESOLVE_RESULT_V_ACCOUNT_NOT_FOUND_IDX;     break;
     331           0 :   case FD_RUNTIME_TXN_ERR_INVALID_ADDRESS_LOOKUP_TABLE_OWNER: ctr_idx = FD_METRICS_ENUM_LUT_RESOLVE_RESULT_V_INVALID_ACCOUNT_OWNER_IDX; break;
     332           0 :   case FD_RUNTIME_TXN_ERR_INVALID_ADDRESS_LOOKUP_TABLE_DATA:  ctr_idx = FD_METRICS_ENUM_LUT_RESOLVE_RESULT_V_INVALID_ACCOUNT_DATA_IDX;  break;
     333           0 :   case FD_RUNTIME_TXN_ERR_INVALID_ADDRESS_LOOKUP_TABLE_INDEX: ctr_idx = FD_METRICS_ENUM_LUT_RESOLVE_RESULT_V_INVALID_LOOKUP_INDEX_IDX;  break;
     334           0 :   default:                                                    ctr_idx = FD_METRICS_ENUM_LUT_RESOLVE_RESULT_V_ACCOUNT_UNINITIALIZED_IDX; break;
     335           0 :   }
     336           0 :   ctx->metrics.lut[ ctr_idx ]++;
     337           0 :   return err;
     338           0 : }
     339             : 
     340             : static int
     341             : publish_txn( fd_resolv_ctx_t *          ctx,
     342             :              fd_stem_context_t *        stem,
     343           0 :              fd_stashed_txn_m_t const * stashed ) {
     344           0 :   fd_txn_m_t * txnm = fd_chunk_to_laddr( ctx->out_pack->mem, ctx->out_pack->chunk );
     345           0 :   fd_memcpy( txnm, stashed->_, fd_txn_m_realized_footprint( (fd_txn_m_t *)stashed->_, 1, 0 ) );
     346             : 
     347           0 :   fd_txn_t const * txnt = fd_txn_m_txn_t( txnm );
     348             : 
     349           0 :   txnm->reference_slot = ctx->flushing_slot;
     350             : 
     351           0 :   if( FD_UNLIKELY( txnt->addr_table_adtl_cnt ) ) {
     352           0 :     if( FD_UNLIKELY( !ctx->bank ) ) {
     353           0 :       FD_MCNT_INC( RESOLF, NO_BANK_DROP, 1 );
     354           0 :       return 0;
     355           0 :     }
     356           0 :     int err = peek_aluts( ctx, txnm );
     357           0 :     if( FD_UNLIKELY( err ) ) return 0;
     358           0 :   }
     359             : 
     360           0 :   ulong realized_sz = fd_txn_m_realized_footprint( txnm, 1, 1 );
     361           0 :   ulong tspub = fd_frag_meta_ts_comp( fd_tickcount() );
     362           0 :   fd_stem_publish( stem, 0UL, txnm->reference_slot, ctx->out_pack->chunk, realized_sz, 0UL, 0UL, tspub );
     363           0 :   ctx->out_pack->chunk = fd_dcache_compact_next( ctx->out_pack->chunk, realized_sz, ctx->out_pack->chunk0, ctx->out_pack->wmark );
     364             : 
     365           0 :   return 1;
     366           0 : }
     367             : 
     368             : static inline void
     369             : after_credit( fd_resolv_ctx_t *   ctx,
     370             :               fd_stem_context_t * stem,
     371             :               int *               opt_poll_in,
     372           0 :               int *               charge_busy ) {
     373           0 :   if( FD_LIKELY( ctx->flush_pool_idx==ULONG_MAX ) ) return;
     374             : 
     375           0 :   *charge_busy = 1;
     376           0 :   *opt_poll_in = 0;
     377             : 
     378           0 :   ulong next = map_chain_idx_next_const( ctx->flush_pool_idx, ULONG_MAX, ctx->pool );
     379           0 :   map_chain_idx_remove_fast( ctx->map_chain, ctx->flush_pool_idx, ctx->pool );
     380           0 :   if( FD_LIKELY( publish_txn( ctx, stem, pool_ele( ctx->pool, ctx->flush_pool_idx ) ) ) ) {
     381           0 :     ctx->metrics.stash[ FD_METRICS_ENUM_RESOLVE_STASH_OPERATION_V_PUBLISHED_IDX ]++;
     382           0 :   } else {
     383           0 :     ctx->metrics.stash[ FD_METRICS_ENUM_RESOLVE_STASH_OPERATION_V_REMOVED_IDX ]++;
     384           0 :   }
     385           0 :   lru_list_idx_remove( ctx->lru_list, ctx->flush_pool_idx, ctx->pool );
     386           0 :   pool_idx_release( ctx->pool, ctx->flush_pool_idx );
     387           0 :   ctx->flush_pool_idx = next;
     388           0 : }
     389             : 
     390             : /* Returns 0 if not a durable nonce transaction and 1 if it may be a
     391             :    durable nonce transaction */
     392             : 
     393             : FD_FN_PURE static inline int
     394             : fd_resolv_is_durable_nonce( fd_txn_t const * txn,
     395           0 :                             uchar    const * payload ) {
     396           0 :   if( FD_UNLIKELY( txn->instr_cnt==0 ) ) return 0;
     397             : 
     398           0 :   fd_txn_instr_t const * ix0 = &txn->instr[ 0 ];
     399           0 :   fd_acct_addr_t const * prog0 = fd_txn_get_acct_addrs( txn, payload ) + ix0->program_id;
     400             :   /* First instruction must be SystemProgram nonceAdvance instruction */
     401           0 :   fd_acct_addr_t const system_program[1] = { { { SYS_PROG_ID } } };
     402           0 :   if( FD_LIKELY( memcmp( prog0, system_program, sizeof(fd_acct_addr_t) ) ) )        return 0;
     403             : 
     404             :   /* instruction with three accounts and a four byte instruction data, a
     405             :      little-endian uint value 4 */
     406           0 :   if( FD_UNLIKELY( (ix0->data_sz!=4) | (ix0->acct_cnt!=3) ) ) return 0;
     407             : 
     408           0 :   return fd_uint_load_4( payload + ix0->data_off )==4U;
     409           0 : }
     410             : 
     411             : static inline void
     412             : after_frag( fd_resolv_ctx_t *   ctx,
     413             :             ulong               in_idx,
     414             :             ulong               seq,
     415             :             ulong               sig,
     416             :             ulong               sz,
     417             :             ulong               tsorig,
     418             :             ulong               _tspub,
     419           0 :             fd_stem_context_t * stem ) {
     420           0 :   (void)seq;
     421           0 :   (void)sz;
     422           0 :   (void)_tspub;
     423             : 
     424           0 :   if( FD_UNLIKELY( ctx->in[in_idx].kind==IN_KIND_REPLAY ) ) {
     425           0 :     switch( sig ) {
     426           0 :       case REPLAY_SIG_SLOT_COMPLETED: {
     427           0 :         fd_replay_slot_completed_t const * msg = &ctx->_completed_slot_msg;
     428             : 
     429             :         /* blockhash_ring is initalized to all zeros. blockhash=0 is an illegal map query */
     430           0 :         if( FD_UNLIKELY( memcmp( &ctx->blockhash_ring[ ctx->blockhash_ring_idx%BLOCKHASH_RING_LEN ], (uchar[ 32UL ]){ 0UL }, sizeof(blockhash_t) ) ) ) {
     431           0 :           blockhash_map_t * entry = map_query( ctx->blockhash_map, ctx->blockhash_ring[ ctx->blockhash_ring_idx%BLOCKHASH_RING_LEN ], NULL );
     432           0 :           if( FD_LIKELY( entry ) ) map_remove( ctx->blockhash_map, entry );
     433           0 :         }
     434             : 
     435           0 :         memcpy( ctx->blockhash_ring[ ctx->blockhash_ring_idx%BLOCKHASH_RING_LEN ].b, msg->block_hash.uc, 32UL );
     436           0 :         ctx->blockhash_ring_idx++;
     437             : 
     438           0 :         blockhash_map_t * blockhash = map_insert( ctx->blockhash_map, *(blockhash_t *)msg->block_hash.uc );
     439           0 :         blockhash->slot = msg->slot;
     440             : 
     441           0 :         blockhash_t * hash = (blockhash_t *)msg->block_hash.uc;
     442           0 :         ctx->flush_pool_idx  = map_chain_idx_query_const( ctx->map_chain, &hash, ULONG_MAX, ctx->pool );
     443           0 :         ctx->flushing_slot   = msg->slot;
     444             : 
     445           0 :         ctx->completed_slot = msg->slot;
     446           0 :         break;
     447           0 :       }
     448           0 :       case REPLAY_SIG_ROOT_ADVANCED: {
     449           0 :         fd_replay_root_advanced_t const * msg = &ctx->_rooted_slot_msg;
     450             : 
     451             :         /* Replace current bank with new bank */
     452           0 :         fd_bank_t * prev_bank = ctx->bank;
     453             : 
     454           0 :         ctx->bank = fd_banks_bank_query( ctx->banks, msg->bank_idx );
     455           0 :         FD_TEST( ctx->bank );
     456             : 
     457             :         /* Send slot completed message back to replay, so it can
     458             :            decrement the reference count of the previous bank. */
     459           0 :         if( FD_LIKELY( prev_bank ) ) {
     460           0 :           ulong tspub = fd_frag_meta_ts_comp( fd_tickcount() );
     461           0 :           fd_resolv_slot_exchanged_t * slot_exchanged =
     462           0 :             fd_type_pun( fd_chunk_to_laddr( ctx->out_replay->mem, ctx->out_replay->chunk ) );
     463           0 :           slot_exchanged->bank_idx = prev_bank->idx;
     464           0 :           fd_stem_publish( stem, 1UL, 0UL, ctx->out_replay->chunk, sizeof(fd_resolv_slot_exchanged_t), 0UL, tsorig, tspub );
     465           0 :           ctx->out_replay->chunk = fd_dcache_compact_next( ctx->out_replay->chunk, sizeof(fd_resolv_slot_exchanged_t), ctx->out_replay->chunk0, ctx->out_replay->wmark );
     466           0 :         }
     467             : 
     468           0 :         break;
     469           0 :       }
     470           0 :       default: break;
     471           0 :     }
     472           0 :     return;
     473           0 :   }
     474             : 
     475           0 :   fd_txn_m_t * txnm = (fd_txn_m_t *)fd_chunk_to_laddr( ctx->out_pack->mem, ctx->out_pack->chunk );
     476           0 :   FD_TEST( txnm->payload_sz<=FD_TPU_MTU );
     477           0 :   FD_TEST( txnm->txn_t_sz<=FD_TXN_MAX_SZ );
     478           0 :   fd_txn_t const * txnt = fd_txn_m_txn_t( txnm );
     479             : 
     480             :   /* If we find the recent blockhash, life is simple.  We drop
     481             :      transactions that couldn't possibly execute any more, and forward
     482             :      to pack ones that could.
     483             : 
     484             :      If we can't find the recent blockhash ... it means one of four
     485             :      things,
     486             : 
     487             :      (1) The blockhash is really old (more than 19 days) or just
     488             :          non-existent.
     489             :      (2) The blockhash is not that old, but was created before this
     490             :          validator was started.
     491             :      (3) It's really new (we haven't seen the bank yet).
     492             :      (4) It's a durable nonce transaction, or part of a bundle (just let
     493             :          it pass).
     494             : 
     495             :     For durable nonce transactions, there isn't much we can do except
     496             :     pass them along and see if they execute.
     497             : 
     498             :     For the other three cases ... we don't want to flood pack with what
     499             :     might be junk transactions, so we accumulate them into a local
     500             :     buffer.  If we later see the blockhash come to exist, we forward any
     501             :     buffered transactions to back. */
     502             : 
     503           0 :   if( FD_UNLIKELY( txnm->block_engine.bundle_id && (txnm->block_engine.bundle_id!=ctx->bundle_id) ) ) {
     504           0 :     ctx->bundle_failed = 0;
     505           0 :     ctx->bundle_id     = txnm->block_engine.bundle_id;
     506           0 :   }
     507             : 
     508           0 :   if( FD_UNLIKELY( txnm->block_engine.bundle_id && ctx->bundle_failed ) ) {
     509           0 :     ctx->metrics.bundle_peer_failure++;
     510           0 :     return;
     511           0 :   }
     512             : 
     513           0 :   txnm->reference_slot = ctx->completed_slot;
     514           0 :   blockhash_map_t const * blockhash = map_query_const( ctx->blockhash_map, *(blockhash_t*)( fd_txn_m_payload( txnm )+txnt->recent_blockhash_off ), NULL );
     515           0 :   if( FD_LIKELY( blockhash ) ) {
     516           0 :     txnm->reference_slot = blockhash->slot;
     517           0 :     if( FD_UNLIKELY( txnm->reference_slot+151UL<ctx->completed_slot ) ) {
     518           0 :       if( FD_UNLIKELY( txnm->block_engine.bundle_id ) ) ctx->bundle_failed = 1;
     519           0 :       ctx->metrics.blockhash_expired++;
     520           0 :       return;
     521           0 :     }
     522           0 :   }
     523             : 
     524           0 :   int is_bundle_member = !!txnm->block_engine.bundle_id;
     525           0 :   int is_durable_nonce = fd_resolv_is_durable_nonce( txnt, fd_txn_m_payload( txnm ) );
     526             : 
     527           0 :   if( FD_UNLIKELY( !is_bundle_member && !is_durable_nonce && !blockhash ) ) {
     528           0 :     ulong pool_idx;
     529           0 :     if( FD_UNLIKELY( !pool_free( ctx->pool ) ) ) {
     530           0 :       pool_idx = lru_list_idx_pop_tail( ctx->lru_list, ctx->pool );
     531           0 :       map_chain_idx_remove_fast( ctx->map_chain, pool_idx, ctx->pool );
     532           0 :       ctx->metrics.stash[ FD_METRICS_ENUM_RESOLVE_STASH_OPERATION_V_OVERRUN_IDX ]++;
     533           0 :     } else {
     534           0 :       pool_idx = pool_idx_acquire( ctx->pool );
     535           0 :     }
     536             : 
     537           0 :     fd_stashed_txn_m_t * stash_txn = pool_ele( ctx->pool, pool_idx );
     538             :     /* There's a compiler bug in GCC version 12 (at least 12.1, 12.3 and
     539             :        12.4) that cause it to think stash_txn is a null pointer.  It
     540             :        then complains that the memcpy is bad and refuses to compile the
     541             :        memcpy below.  It is possible for pool_ele to return NULL, but
     542             :        that can't happen because if pool_free is 0, then all the pool
     543             :        elements must be in the LRU list, so idx_pop_tail won't return
     544             :        IDX_NULL; and if pool_free returns non-zero, then
     545             :        pool_idx_acquire won't return POOL_IDX_NULL. */
     546           0 :     FD_COMPILER_FORGET( stash_txn );
     547           0 :     fd_memcpy( stash_txn->_, txnm, fd_txn_m_realized_footprint( txnm, 1, 0 ) );
     548           0 :     stash_txn->blockhash = (blockhash_t *)(fd_txn_m_payload( (fd_txn_m_t *)(stash_txn->_) ) + txnt->recent_blockhash_off);
     549           0 :     ctx->metrics.stash[ FD_METRICS_ENUM_RESOLVE_STASH_OPERATION_V_INSERTED_IDX ]++;
     550             : 
     551           0 :     map_chain_ele_insert( ctx->map_chain, stash_txn, ctx->pool );
     552           0 :     lru_list_idx_push_head( ctx->lru_list, pool_idx, ctx->pool );
     553             : 
     554           0 :     return;
     555           0 :   }
     556             : 
     557           0 :   if( FD_UNLIKELY( txnt->addr_table_adtl_cnt ) ) {
     558           0 :     if( FD_UNLIKELY( !ctx->bank ) ) {
     559           0 :       FD_MCNT_INC( RESOLF, NO_BANK_DROP, 1 );
     560           0 :       if( FD_UNLIKELY( txnm->block_engine.bundle_id ) ) ctx->bundle_failed = 1;
     561           0 :       return;
     562           0 :     }
     563             : 
     564           0 :     int result = peek_aluts( ctx, txnm );
     565           0 :     if( FD_UNLIKELY( result ) ) {
     566           0 :       if( FD_UNLIKELY( txnm->block_engine.bundle_id ) ) ctx->bundle_failed = 1;
     567           0 :       return;
     568           0 :     }
     569           0 :   }
     570             : 
     571           0 :   ulong realized_sz = fd_txn_m_realized_footprint( txnm, 1, 1 );
     572           0 :   ulong tspub = fd_frag_meta_ts_comp( fd_tickcount() );
     573           0 :   fd_stem_publish( stem, 0UL, txnm->reference_slot, ctx->out_pack->chunk, realized_sz, 0UL, tsorig, tspub );
     574           0 :   ctx->out_pack->chunk = fd_dcache_compact_next( ctx->out_pack->chunk, realized_sz, ctx->out_pack->chunk0, ctx->out_pack->wmark );
     575           0 : }
     576             : 
     577             : static void
     578             : unprivileged_init( fd_topo_t *      topo,
     579           0 :                    fd_topo_tile_t * tile ) {
     580           0 :   void * scratch = fd_topo_obj_laddr( topo, tile->tile_obj_id );
     581             : 
     582           0 :   FD_SCRATCH_ALLOC_INIT( l, scratch );
     583           0 :   fd_resolv_ctx_t * ctx = FD_SCRATCH_ALLOC_APPEND( l, alignof( fd_resolv_ctx_t ), sizeof( fd_resolv_ctx_t ) );
     584             : 
     585           0 :   ctx->round_robin_cnt = fd_topo_tile_name_cnt( topo, tile->name );
     586           0 :   ctx->round_robin_idx = tile->kind_id;
     587             : 
     588           0 :   ctx->bundle_failed = 0;
     589           0 :   ctx->bundle_id     = 0UL;
     590             : 
     591           0 :   ctx->completed_slot = 0UL;
     592           0 :   ctx->blockhash_ring_idx = 0UL;
     593             : 
     594           0 :   ctx->flush_pool_idx = ULONG_MAX;
     595             : 
     596           0 :   ctx->pool = pool_join( pool_new( FD_SCRATCH_ALLOC_APPEND( l, pool_align(), pool_footprint( 1UL<<16UL ) ), 1UL<<16UL ) );
     597           0 :   FD_TEST( ctx->pool );
     598             : 
     599           0 :   ctx->map_chain = map_chain_join( map_chain_new( FD_SCRATCH_ALLOC_APPEND( l, map_chain_align(), map_chain_footprint( 8192ULL ) ), 8192UL , 0UL ) );
     600           0 :   FD_TEST( ctx->map_chain );
     601             : 
     602           0 :   FD_TEST( ctx->lru_list==lru_list_join( lru_list_new( ctx->lru_list ) ) );
     603             : 
     604           0 :   memset( ctx->blockhash_ring, 0, sizeof( ctx->blockhash_ring ) );
     605           0 :   memset( &ctx->metrics, 0, sizeof( ctx->metrics ) );
     606             : 
     607           0 :   ctx->blockhash_map = map_join( map_new( FD_SCRATCH_ALLOC_APPEND( l, map_align(), map_footprint() ) ) );
     608           0 :   FD_TEST( ctx->blockhash_map );
     609             : 
     610           0 :   FD_TEST( tile->in_cnt<=sizeof( ctx->in )/sizeof( ctx->in[ 0 ] ) );
     611           0 :   for( ulong i=0UL; i<tile->in_cnt; i++ ) {
     612           0 :     fd_topo_link_t * link = &topo->links[ tile->in_link_id[ i ] ];
     613           0 :     fd_topo_wksp_t * link_wksp = &topo->workspaces[ topo->objs[ link->dcache_obj_id ].wksp_id ];
     614             : 
     615           0 :     if( FD_LIKELY(      !strcmp( link->name, "replay_out"   ) ) ) ctx->in[ i ].kind = IN_KIND_REPLAY;
     616           0 :     else if( FD_LIKELY( !strcmp( link->name, "dedup_resolv" ) ) ) ctx->in[ i ].kind = IN_KIND_DEDUP;
     617           0 :     else FD_LOG_ERR(( "unknown in link name '%s'", link->name ));
     618             : 
     619           0 :     ctx->in[i].mem    = link_wksp->wksp;
     620           0 :     ctx->in[i].chunk0 = fd_dcache_compact_chunk0( ctx->in[i].mem, link->dcache );
     621           0 :     ctx->in[i].wmark  = fd_dcache_compact_wmark ( ctx->in[i].mem, link->dcache, link->mtu );
     622           0 :     ctx->in[i].mtu    = link->mtu;
     623           0 :   }
     624             : 
     625           0 :   ctx->out_pack->mem    = topo->workspaces[ topo->objs[ topo->links[ tile->out_link_id[ 0 ] ].dcache_obj_id ].wksp_id ].wksp;
     626           0 :   ctx->out_pack->chunk0 = fd_dcache_compact_chunk0( ctx->out_pack->mem, topo->links[ tile->out_link_id[ 0 ] ].dcache );
     627           0 :   ctx->out_pack->wmark  = fd_dcache_compact_wmark ( ctx->out_pack->mem, topo->links[ tile->out_link_id[ 0 ] ].dcache, topo->links[ tile->out_link_id[ 0 ] ].mtu );
     628           0 :   ctx->out_pack->chunk  = ctx->out_pack->chunk0;
     629             : 
     630           0 :   ctx->out_replay->mem    = topo->workspaces[ topo->objs[ topo->links[ tile->out_link_id[ 1 ] ].dcache_obj_id ].wksp_id ].wksp;
     631           0 :   ctx->out_replay->chunk0 = fd_dcache_compact_chunk0( ctx->out_replay->mem, topo->links[ tile->out_link_id[ 1 ] ].dcache );
     632           0 :   ctx->out_replay->wmark  = fd_dcache_compact_wmark ( ctx->out_replay->mem, topo->links[ tile->out_link_id[ 1 ] ].dcache, topo->links[ tile->out_link_id[ 1 ] ].mtu );
     633           0 :   ctx->out_replay->chunk  = ctx->out_replay->chunk0;
     634             : 
     635           0 :   FD_TEST( fd_accdb_user_v1_init( ctx->accdb, fd_topo_obj_laddr( topo, tile->resolv.funk_obj_id ) ) );
     636             : 
     637           0 :   ulong banks_obj_id = fd_pod_queryf_ulong( topo->props, ULONG_MAX, "banks" );
     638           0 :   FD_TEST( banks_obj_id!=ULONG_MAX );
     639           0 :   ctx->banks = fd_banks_join( fd_topo_obj_laddr( topo, banks_obj_id ) );
     640           0 :   FD_TEST( ctx->banks );
     641           0 :   ctx->bank = NULL;
     642             : 
     643           0 :   ulong scratch_top = FD_SCRATCH_ALLOC_FINI( l, 1UL );
     644           0 :   if( FD_UNLIKELY( scratch_top > (ulong)scratch + scratch_footprint( tile ) ) )
     645           0 :     FD_LOG_ERR(( "scratch overflow %lu %lu %lu", scratch_top - (ulong)scratch - scratch_footprint( tile ), scratch_top, (ulong)scratch + scratch_footprint( tile ) ));
     646           0 : }
     647             : 
     648             : static ulong
     649             : populate_allowed_seccomp( fd_topo_t const *      topo,
     650             :                           fd_topo_tile_t const * tile,
     651             :                           ulong                  out_cnt,
     652           0 :                           struct sock_filter *   out ) {
     653           0 :   (void)topo;
     654           0 :   (void)tile;
     655             : 
     656           0 :   populate_sock_filter_policy_fd_resolv_tile( out_cnt, out, (uint)fd_log_private_logfile_fd() );
     657           0 :   return sock_filter_policy_fd_resolv_tile_instr_cnt;
     658           0 : }
     659             : 
     660             : static ulong
     661             : populate_allowed_fds( fd_topo_t const *      topo,
     662             :                       fd_topo_tile_t const * tile,
     663             :                       ulong                  out_fds_cnt,
     664           0 :                       int *                  out_fds ) {
     665           0 :   (void)topo;
     666           0 :   (void)tile;
     667             : 
     668           0 :   if( FD_UNLIKELY( out_fds_cnt<2UL ) ) FD_LOG_ERR(( "out_fds_cnt %lu", out_fds_cnt ));
     669             : 
     670           0 :   ulong out_cnt = 0UL;
     671           0 :   out_fds[ out_cnt++ ] = 2; /* stderr */
     672           0 :   if( FD_LIKELY( -1!=fd_log_private_logfile_fd() ) )
     673           0 :     out_fds[ out_cnt++ ] = fd_log_private_logfile_fd(); /* logfile */
     674           0 :   return out_cnt;
     675           0 : }
     676             : 
     677           0 : #define STEM_BURST (1UL)
     678             : 
     679           0 : #define STEM_CALLBACK_CONTEXT_TYPE  fd_resolv_ctx_t
     680           0 : #define STEM_CALLBACK_CONTEXT_ALIGN alignof(fd_resolv_ctx_t)
     681             : 
     682           0 : #define STEM_CALLBACK_METRICS_WRITE metrics_write
     683           0 : #define STEM_CALLBACK_AFTER_CREDIT  after_credit
     684           0 : #define STEM_CALLBACK_BEFORE_FRAG   before_frag
     685           0 : #define STEM_CALLBACK_DURING_FRAG   during_frag
     686           0 : #define STEM_CALLBACK_AFTER_FRAG    after_frag
     687             : 
     688             : #include "../../disco/stem/fd_stem.c"
     689             : 
     690             : fd_topo_run_tile_t fd_tile_resolv = {
     691             :   .name                     = "resolv",
     692             :   .populate_allowed_seccomp = populate_allowed_seccomp,
     693             :   .populate_allowed_fds     = populate_allowed_fds,
     694             :   .scratch_align            = scratch_align,
     695             :   .scratch_footprint        = scratch_footprint,
     696             :   .unprivileged_init        = unprivileged_init,
     697             :   .run                      = stem_run,
     698             : };

Generated by: LCOV version 1.14