LCOV - code coverage report
Current view: top level - discof/resolv - fd_resolv_tile.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 360 0.0 %
Date: 2026-10-22 18:10:43 Functions: 0 28 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 "../../discof/fd_startup.h"
       6             : #include "../../disco/metrics/fd_metrics.h"
       7             : #include "../../flamenco/accdb/fd_accdb.h"
       8             : #include "../../flamenco/accdb/fd_accdb_shmem.h"
       9             : #include "../../flamenco/runtime/fd_alut.h"
      10             : #include "../../flamenco/runtime/fd_runtime_const.h"
      11             : #include "../../flamenco/runtime/fd_system_ids_pp.h"
      12             : #include "../../flamenco/runtime/fd_bank.h"
      13             : #include "../../tango/fseq/fd_fseq.h"
      14             : #include "../../util/pod/fd_pod_format.h"
      15             : #include "../../util/fd_hash32.h"
      16             : 
      17             : #include <time.h>
      18             : #include <linux/futex.h>
      19             : #include "generated/fd_resolv_tile_seccomp.h"
      20             : 
      21             : #if FD_HAS_AVX
      22             : #include "../../util/simd/fd_avx.h"
      23             : #endif
      24             : 
      25           0 : #define IN_KIND_DEDUP  (0)
      26           0 : #define IN_KIND_REPLAY (1)
      27             : 
      28             : struct blockhash {
      29             :   uchar b[ 32 ];
      30             : };
      31             : 
      32             : typedef struct blockhash blockhash_t;
      33             : 
      34             : struct blockhash_map {
      35             :   blockhash_t key;
      36             :   ulong       slot;
      37             :   ulong       block_height;
      38             :   /* The map_chain variables: */
      39             :   uint        next;
      40             :   /* It's probably not worth 4 bytes of cache per element to store
      41             :      chain_prev, since the query/delete ratio is probably about 1000:1,
      42             :      but alignof is 8, so we'd just store padding here instead */
      43             :   uint        prev;
      44             : };
      45             : 
      46             : typedef struct blockhash_map blockhash_map_t;
      47             : 
      48             : /* The blockhash ring holds recent blockhashes, so we can identify when
      49             :    a transaction arrives, what slot it will expire (and can no longer be
      50             :    packed) in.  This is useful so we don't send transactions to pack
      51             :    that are no longer packable.
      52             : 
      53             :    Similarly, we also store the nonce version of the blockhash, which is
      54             :    stored in nonce accounts when a transaction executes in the following
      55             :    slot.  Since nonce transactions stay valid for arbitrarily long, this
      56             :    just helps us order nonce transactions that try to advance the same
      57             :    account.
      58             : 
      59             :    Unfortunately, poorly written transaction senders frequently send
      60             :    transactions from millions of slots ago, so we need a large ring to
      61             :    be able to determine and evict these.  The highest practically useful
      62             :    value here is around 22, which works out to 19 days of blockhash
      63             :    history.  Beyond this, the validator is likely to be restarted, and
      64             :    lose the history anyway. */
      65             : 
      66           0 : #define BLOCKHASH_LG_RING_CNT 22UL
      67           0 : #define BLOCKHASH_RING_LEN   (1UL<<BLOCKHASH_LG_RING_CNT)
      68             : 
      69             : #define MAP_NAME               map
      70           0 : #define MAP_ELE_T              blockhash_map_t
      71             : #define MAP_KEY_T              blockhash_t
      72           0 : #define MAP_IDX_T              uint
      73             : #define MAP_OPTIMIZE_RANDOM_ACCESS_REMOVAL 1
      74           0 : #define MAP_KEY_EQ(k0,k1)      (!memcmp((k0)->b,(k1)->b, 32UL))
      75           0 : #define MAP_KEY_HASH(key,seed) ((uint)fd_hash32( (key)->b, (seed) ))
      76             : 
      77             : #include "../../util/tmpl/fd_map_chain.c"
      78             : 
      79             : typedef struct {
      80             :   union {
      81             :     ulong pool_next; /* Used when it's released */
      82             :     ulong lru_next;  /* Used when it's acquired */
      83             :   };                 /* .. so it's okay to store them in the same memory */
      84             :   ulong lru_prev;
      85             : 
      86             :   ulong map_next;
      87             :   ulong map_prev;
      88             : 
      89             :   blockhash_t * blockhash;
      90             :   uchar _[ FD_TPU_PARSED_MTU ] __attribute__((aligned(alignof(fd_txn_m_t))));
      91             : } fd_stashed_txn_m_t;
      92             : 
      93             : #define POOL_NAME      pool
      94           0 : #define POOL_T         fd_stashed_txn_m_t
      95           0 : #define POOL_NEXT      pool_next
      96             : #define POOL_IDX_T     ulong
      97             : 
      98             : #include "../../util/tmpl/fd_pool.c"
      99             : 
     100             : /* We'll push at the head, which means the tail is the oldest. */
     101             : #define DLIST_NAME  lru_list
     102             : #define DLIST_ELE_T fd_stashed_txn_m_t
     103           0 : #define DLIST_PREV  lru_prev
     104           0 : #define DLIST_NEXT  lru_next
     105             : 
     106             : #include "../../util/tmpl/fd_dlist.c"
     107             : 
     108             : #define MAP_NAME          map_chain
     109           0 : #define MAP_ELE_T         fd_stashed_txn_m_t
     110             : #define MAP_KEY_T         blockhash_t *
     111           0 : #define MAP_KEY           blockhash
     112           0 : #define MAP_IDX_T         ulong
     113           0 : #define MAP_NEXT          map_next
     114           0 : #define MAP_PREV          map_prev
     115           0 : #define MAP_KEY_HASH(k,s) fd_hash32( (*(k))->b, (s) )
     116           0 : #define MAP_KEY_EQ(k0,k1) (!memcmp((*(k0))->b, (*(k1))->b, 32UL))
     117             : #define MAP_OPTIMIZE_RANDOM_ACCESS_REMOVAL 1
     118             : #define MAP_MULTI         1
     119             : 
     120             : #include "../../util/tmpl/fd_map_chain.c"
     121             : 
     122             : typedef struct {
     123             :   int         kind;
     124             : 
     125             :   fd_wksp_t * mem;
     126             :   ulong       chunk0;
     127             :   ulong       wmark;
     128             :   ulong       mtu;
     129             : } fd_resolv_in_ctx_t;
     130             : 
     131             : typedef struct {
     132             :   fd_wksp_t * mem;
     133             :   ulong       chunk0;
     134             :   ulong       wmark;
     135             :   ulong       chunk;
     136             : } fd_resolv_out_ctx_t;
     137             : 
     138             : typedef struct {
     139             :   ulong round_robin_idx;
     140             :   ulong round_robin_cnt;
     141             :   ulong map_seed;
     142             : 
     143             :   int   bundle_failed;
     144             :   ulong bundle_id;
     145             : 
     146             :   map_t * blockhash_map;
     147             :   map_t * nonce_blockhash_map;
     148             : 
     149             :   ulong flushing_block_height;
     150             :   ulong flush_pool_idx;
     151             : 
     152             :   /* In the full client, the resolv tile is passed only a rooted bank
     153             :      index from replay whenever the root is advanced.
     154             : 
     155             :      This is enough to query the accounts database for that bank and
     156             :      retrieve the address lookup tables.  Because of lifetime concerns
     157             :      around bank ownership, the replay tile is solely responsible for
     158             :      freeing the bank when it is no longer needed.  To facilitate this,
     159             :      the resolv tile sends a message to replay when it is done with a
     160             :      rooted bank (after exchanging it for a new rooted bank). */
     161             :   fd_banks_t * banks;
     162             :   fd_bank_t * bank;
     163             :   fd_accdb_t * accdb;
     164             : 
     165             :   fd_stashed_txn_m_t * pool;
     166             :   map_chain_t *        map_chain;
     167             :   lru_list_t           lru_list[1];
     168             : 
     169             :   fd_startup_gate_t startup_gate[1];
     170             : 
     171             :   ulong completed_slot;
     172             :   ulong completed_block_height;
     173             :   /* The total number of blockhashes that have been inserted in
     174             :      blockhash_ring (and since nonce_blockhash_ring is parallel, also in
     175             :      nonce_blockhash_ring. */
     176             :   ulong blockhash_ring_idx;
     177             : 
     178             :   /* These are the pools used the blockhash_map */
     179             :   blockhash_map_t blockhash_ring      [ BLOCKHASH_RING_LEN ];
     180             :   blockhash_map_t nonce_blockhash_ring[ BLOCKHASH_RING_LEN ];
     181             : 
     182             :   fd_replay_root_advanced_t  _rooted_slot_msg;
     183             :   fd_replay_slot_completed_t _completed_slot_msg;
     184             : 
     185             :   struct {
     186             :     ulong lut[ FD_METRICS_COUNTER_RESOLV_TXN_LUT_RESOLVE_ATTEMPTED_CNT ];
     187             :     ulong txn_dropped[ FD_METRICS_ENUM_RESOLV_TXN_DROP_REASON_CNT ];
     188             :     ulong stash_inserted;
     189             :     ulong stash_removed[ FD_METRICS_COUNTER_RESOLV_STASH_TXN_REMOVED_CNT ];
     190             :   } metrics;
     191             : 
     192             :   fd_resolv_in_ctx_t in[ 64UL ];
     193             : 
     194             :   fd_resolv_out_ctx_t out_pack[ 1UL ];
     195             :   fd_resolv_out_ctx_t out_replay[ 1UL ];
     196             : 
     197             :   /* Scratch buffers for fd_accdb_read_one_nocache.  RO accdb joiners
     198             :      must use the nocache API (see fd_accdb.h), which writes the account
     199             :      data into caller-provided buffers rather than returning a pointer
     200             :      into the cache.  Reused across alut reads; peek_alut consumes the
     201             :      bytes synchronously inside fd_alut_interp_next. */
     202             :   uchar alut_owner[ 32UL ];
     203             :   uchar alut_data[ FD_RUNTIME_ACC_SZ_MAX ];
     204             : } fd_resolv_ctx_t;
     205             : 
     206             : FD_FN_CONST static inline ulong
     207           0 : scratch_align( void ) {
     208           0 :   return fd_ulong_max( fd_ulong_max( alignof( fd_resolv_ctx_t ), pool_align() ), fd_ulong_max( map_chain_align(), map_align() ) );
     209           0 : }
     210             : 
     211             : FD_FN_PURE static inline ulong
     212           0 : scratch_footprint( fd_topo_tile_t const * tile ) {
     213             :   /* The map is normally full, so make the chain cnt a bit bigger */
     214           0 :   ulong map_chain_cnt = 2UL*map_chain_cnt_est( BLOCKHASH_RING_LEN );
     215           0 :   ulong l = FD_LAYOUT_INIT;
     216           0 :   l = FD_LAYOUT_APPEND( l, alignof( fd_resolv_ctx_t ), sizeof( fd_resolv_ctx_t )                            );
     217           0 :   l = FD_LAYOUT_APPEND( l, pool_align(),               pool_footprint     ( 1UL<<16UL )                     );
     218           0 :   l = FD_LAYOUT_APPEND( l, map_chain_align(),          map_chain_footprint( 8192UL    )                     );
     219           0 :   l = FD_LAYOUT_APPEND( l, map_align(),                map_footprint( map_chain_cnt   )                     );
     220           0 :   l = FD_LAYOUT_APPEND( l, map_align(),                map_footprint( map_chain_cnt   )                     );
     221           0 :   l = FD_LAYOUT_APPEND( l, fd_accdb_align(),           fd_accdb_footprint( tile->resolv.max_live_slots, 0 ) );
     222           0 :   return FD_LAYOUT_FINI( l, scratch_align() );
     223           0 : }
     224             : 
     225             : static inline void
     226           0 : metrics_write( fd_resolv_ctx_t * ctx ) {
     227           0 :   FD_MCNT_ENUM_COPY( RESOLV, TXN_DROPPED,                   ctx->metrics.txn_dropped         );
     228           0 :   FD_MCNT_ENUM_COPY( RESOLV, TXN_LUT_RESOLVE_ATTEMPTED,     ctx->metrics.lut                 );
     229           0 :   FD_MCNT_SET(       RESOLV, STASH_TXN_INSERTED,            ctx->metrics.stash_inserted      );
     230           0 :   FD_MCNT_ENUM_COPY( RESOLV, STASH_TXN_REMOVED,             ctx->metrics.stash_removed       );
     231             : 
     232           0 :   FD_ACCDB_METRICS_WRITE_RO( RESOLV, fd_accdb_metrics( ctx->accdb ) );
     233           0 : }
     234             : 
     235             : static int
     236             : before_frag( fd_resolv_ctx_t * ctx,
     237             :              ulong             in_idx,
     238             :              ulong             seq,
     239           0 :              ulong             sig ) {
     240           0 :   fd_startup_gate_busy( ctx->startup_gate );
     241             : 
     242           0 :   if( FD_UNLIKELY( ctx->in[in_idx].kind==IN_KIND_REPLAY ) )
     243           0 :     return sig!=REPLAY_SIG_SLOT_COMPLETED && sig!=REPLAY_SIG_ROOT_ADVANCED;
     244             : 
     245             :   /* Bundle transactions (sig==1) must arrive at pack in order.  Route
     246             :      all bundle traffic to resolv:0. */
     247           0 :   if( FD_UNLIKELY( sig ) ) return ctx->round_robin_idx!=0UL;
     248             : 
     249           0 :   return (seq % ctx->round_robin_cnt) != ctx->round_robin_idx;
     250           0 : }
     251             : 
     252             : static inline void
     253             : during_frag( fd_resolv_ctx_t * ctx,
     254             :              ulong             in_idx,
     255             :              ulong             seq FD_PARAM_UNUSED,
     256             :              ulong             sig FD_PARAM_UNUSED,
     257             :              ulong             chunk,
     258             :              ulong             sz,
     259           0 :              ulong             ctl FD_PARAM_UNUSED ) {
     260             : 
     261           0 :   if( FD_UNLIKELY( chunk<ctx->in[ in_idx ].chunk0 || chunk>ctx->in[ in_idx ].wmark || sz>ctx->in[ in_idx ].mtu ) )
     262           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 ));
     263             : 
     264           0 :   switch( ctx->in[in_idx].kind ) {
     265           0 :     case IN_KIND_DEDUP: {
     266           0 :       uchar * src = (uchar *)fd_chunk_to_laddr( ctx->in[in_idx].mem, chunk );
     267           0 :       uchar * dst = (uchar *)fd_chunk_to_laddr( ctx->out_pack->mem, ctx->out_pack->chunk );
     268           0 :       fd_memcpy( dst, src, sz );
     269           0 :       break;
     270           0 :     }
     271           0 :     case IN_KIND_REPLAY: {
     272           0 :       if( FD_UNLIKELY( sig==REPLAY_SIG_ROOT_ADVANCED ) ) {
     273           0 :         ctx->_rooted_slot_msg = *(fd_replay_root_advanced_t *)fd_chunk_to_laddr_const( ctx->in[in_idx].mem, chunk );
     274           0 :       } else if( FD_UNLIKELY( sig==REPLAY_SIG_SLOT_COMPLETED ) ) {
     275           0 :         ctx->_completed_slot_msg = *(fd_replay_slot_completed_t *)fd_chunk_to_laddr_const( ctx->in[in_idx].mem, chunk );
     276           0 :       }
     277           0 :       break;
     278           0 :     }
     279           0 :     default:
     280           0 :       FD_LOG_ERR(( "unknown in kind %d", ctx->in[in_idx].kind ));
     281           0 :   }
     282           0 : }
     283             : 
     284             : /* peek_alut reads a single address lookup table from database cache. */
     285             : 
     286             : static int
     287             : peek_alut( fd_resolv_ctx_t *  ctx,
     288             :            fd_txn_m_t *       txnm,
     289             :            fd_alut_interp_t * interp,
     290           0 :            ulong              alut_idx ) {
     291           0 :   fd_txn_t const * txn         = fd_txn_m_txn_t_const  ( txnm );
     292           0 :   uchar const *    txn_payload = fd_txn_m_payload_const( txnm );
     293           0 :   fd_txn_acct_addr_lut_t const * addr_lut = &fd_txn_get_address_tables_const( txn )[ alut_idx ];
     294           0 :   fd_pubkey_t addr_lut_acc = FD_LOAD( fd_pubkey_t, txn_payload+addr_lut->addr_off );
     295             : 
     296             :   /* https://github.com/anza-xyz/agave/blob/368ea563c423b0a85cc317891187e15c9a321521/accounts-db/src/accounts.rs#L90-L94
     297             : 
     298             :      The resolv tile maps accdb read-only and so must use the nocache
     299             :      read API; fd_accdb_read_one would mutate writer-only shmem. */
     300           0 :   ulong lamports;
     301           0 :   int   executable;
     302           0 :   ulong data_len;
     303           0 :   fd_accdb_read_one_nocache( ctx->accdb, ctx->bank->accdb_fork_id, addr_lut_acc.uc,
     304           0 :                              &lamports, &executable, ctx->alut_owner, ctx->alut_data, &data_len );
     305           0 :   if( FD_UNLIKELY( !lamports ) ) return FD_RUNTIME_TXN_ERR_ADDRESS_LOOKUP_TABLE_NOT_FOUND;
     306             : 
     307           0 :   return fd_alut_interp_next( interp, &addr_lut_acc, ctx->alut_owner, ctx->alut_data, data_len );
     308           0 : }
     309             : 
     310             : /* peek_aluts reads address lookup tables from database cache.
     311             :    Gracefully recovers from data races and missing accounts. */
     312             : 
     313             : static int
     314             : peek_aluts( fd_resolv_ctx_t * ctx,
     315           0 :             fd_txn_m_t *      txnm ) {
     316             :   /* Unpack context */
     317           0 :   fd_txn_t const *          txn          = fd_txn_m_txn_t_const  ( txnm );
     318           0 :   uchar const *             txn_payload  = fd_txn_m_payload_const( txnm );
     319           0 :   ulong const               alut_cnt     = txn->addr_table_lookup_cnt;
     320           0 :   ulong const               slot         = ctx->bank->f.slot;
     321           0 :   fd_sysvar_cache_t const * sysvar_cache = &ctx->bank->f.sysvar_cache;
     322           0 :   fd_slot_hashes_t slot_hashes_view[1];
     323           0 :   if( FD_UNLIKELY( !fd_sysvar_cache_slot_hashes_view( sysvar_cache, slot_hashes_view ) ) ) {
     324           0 :     ctx->metrics.lut[ FD_METRICS_ENUM_LUT_RESOLVE_RESULT_V_ACCOUNT_NOT_FOUND_IDX ]++;
     325           0 :     return FD_RUNTIME_TXN_ERR_ACCOUNT_NOT_FOUND;
     326           0 :   }
     327             : 
     328             :   /* Write indirect addrs into here */
     329           0 :   fd_acct_addr_t * indir_addrs = fd_txn_m_alut( txnm );
     330             : 
     331           0 :   int err = FD_RUNTIME_EXECUTE_SUCCESS;
     332           0 :   fd_alut_interp_t interp[1];
     333           0 :   fd_alut_interp_new( interp, indir_addrs, txn, txn_payload, slot_hashes_view, slot );
     334           0 :   for( ulong i=0UL; i<alut_cnt; i++ ) {
     335           0 :     err = peek_alut( ctx, txnm, interp, i );
     336           0 :     if( FD_UNLIKELY( err ) ) break;
     337           0 :   }
     338             : 
     339           0 :   ulong ctr_idx;
     340           0 :   switch( err ) {
     341           0 :   case FD_RUNTIME_EXECUTE_SUCCESS:                            ctr_idx = FD_METRICS_ENUM_LUT_RESOLVE_RESULT_V_SUCCESS_IDX;               break;
     342           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;
     343           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;
     344           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;
     345           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;
     346           0 :   default:                                                    ctr_idx = FD_METRICS_ENUM_LUT_RESOLVE_RESULT_V_ACCOUNT_UNINITIALIZED_IDX; break;
     347           0 :   }
     348           0 :   ctx->metrics.lut[ ctr_idx ]++;
     349           0 :   return err;
     350           0 : }
     351             : 
     352             : static int
     353             : publish_txn( fd_resolv_ctx_t *          ctx,
     354             :              fd_stem_context_t *        stem,
     355           0 :              fd_stashed_txn_m_t const * stashed ) {
     356           0 :   fd_txn_m_t * txnm = fd_chunk_to_laddr( ctx->out_pack->mem, ctx->out_pack->chunk );
     357           0 :   fd_memcpy( txnm, stashed->_, fd_txn_m_realized_footprint( (fd_txn_m_t *)stashed->_, 1, 0 ) );
     358             : 
     359           0 :   fd_txn_t const * txnt = fd_txn_m_txn_t( txnm );
     360             : 
     361           0 :   txnm->reference_block_height = ctx->flushing_block_height;
     362             : 
     363           0 :   if( FD_UNLIKELY( txnt->addr_table_adtl_cnt ) ) {
     364           0 :     if( FD_UNLIKELY( !ctx->bank ) ) {
     365           0 :       ctx->metrics.txn_dropped[ FD_METRICS_ENUM_RESOLV_TXN_DROP_REASON_V_NO_BANK_IDX ]++;
     366           0 :       return 0;
     367           0 :     }
     368           0 :     int err = peek_aluts( ctx, txnm );
     369           0 :     if( FD_UNLIKELY( err ) ) return 0;
     370           0 :   }
     371             : 
     372           0 :   ulong realized_sz = fd_txn_m_realized_footprint( txnm, 1, 1 );
     373           0 :   ulong tspub = fd_frag_meta_ts_comp( fd_tickcount() );
     374           0 :   fd_stem_publish( stem, 0UL, txnm->reference_block_height, ctx->out_pack->chunk, realized_sz, 0UL, 0UL, tspub );
     375           0 :   ctx->out_pack->chunk = fd_dcache_compact_next( ctx->out_pack->chunk, realized_sz, ctx->out_pack->chunk0, ctx->out_pack->wmark );
     376             : 
     377           0 :   return 1;
     378           0 : }
     379             : 
     380             : static inline void
     381             : after_credit( fd_resolv_ctx_t *   ctx,
     382             :               fd_stem_context_t * stem,
     383             :               int *               opt_poll_in,
     384           0 :               int *               charge_busy ) {
     385           0 :   if( FD_UNLIKELY( !fd_startup_gate_idle( ctx->startup_gate ) ) ) return;
     386             : 
     387           0 :   if( FD_LIKELY( ctx->flush_pool_idx==ULONG_MAX ) ) return;
     388             : 
     389           0 :   *charge_busy = 1;
     390           0 :   *opt_poll_in = 0;
     391             : 
     392           0 :   ulong next = map_chain_idx_next_const( ctx->flush_pool_idx, ULONG_MAX, ctx->pool );
     393           0 :   map_chain_idx_remove_fast( ctx->map_chain, ctx->flush_pool_idx, ctx->pool );
     394           0 :   if( FD_LIKELY( publish_txn( ctx, stem, pool_ele( ctx->pool, ctx->flush_pool_idx ) ) ) ) {
     395           0 :     ctx->metrics.stash_removed[ FD_METRICS_ENUM_STASH_REMOVE_REASON_V_PUBLISHED_IDX ]++;
     396           0 :   } else {
     397           0 :     ctx->metrics.stash_removed[ FD_METRICS_ENUM_STASH_REMOVE_REASON_V_FAILED_IDX    ]++;
     398           0 :   }
     399           0 :   lru_list_idx_remove( ctx->lru_list, ctx->flush_pool_idx, ctx->pool );
     400           0 :   pool_idx_release( ctx->pool, ctx->flush_pool_idx );
     401           0 :   ctx->flush_pool_idx = next;
     402           0 : }
     403             : 
     404             : static inline void
     405             : after_frag( fd_resolv_ctx_t *   ctx,
     406             :             ulong               in_idx,
     407             :             ulong               seq,
     408             :             ulong               sig,
     409             :             ulong               sz,
     410             :             ulong               tsorig,
     411             :             ulong               _tspub,
     412           0 :             fd_stem_context_t * stem ) {
     413           0 :   (void)seq;
     414           0 :   (void)sz;
     415           0 :   (void)_tspub;
     416             : 
     417           0 :   if( FD_UNLIKELY( ctx->in[in_idx].kind==IN_KIND_REPLAY ) ) {
     418           0 :     switch( sig ) {
     419           0 :       case REPLAY_SIG_SLOT_COMPLETED: {
     420           0 :         fd_replay_slot_completed_t const * msg = &ctx->_completed_slot_msg;
     421             : 
     422             :         /* Equivocating slot with same blockhash, ignore.  See fd_txncache.h on how this is possible.
     423             :            TODO make sure matches how agave handles it */
     424           0 :         if( FD_UNLIKELY( map_ele_query( ctx->blockhash_map, (blockhash_t *)msg->block_hash.uc, NULL, ctx->blockhash_ring ) ) ) {
     425           0 :           FD_LOG_WARNING(( "slot with same blockhash, ignoring: %lu", msg->slot ));
     426           0 :           return;
     427           0 :         }
     428             : 
     429             :         /* if we've already inserted BLOCKHASH_RING_LEN, then we need to
     430             :            delete the one we're overwriting */
     431           0 :         if( FD_UNLIKELY( ctx->blockhash_ring_idx>=BLOCKHASH_RING_LEN ) ) {
     432           0 :           map_idx_remove_fast( ctx->blockhash_map,       ctx->blockhash_ring_idx%BLOCKHASH_RING_LEN, ctx->blockhash_ring       );
     433           0 :           map_idx_remove_fast( ctx->nonce_blockhash_map, ctx->blockhash_ring_idx%BLOCKHASH_RING_LEN, ctx->nonce_blockhash_ring );
     434           0 :         }
     435           0 :         blockhash_map_t * entry       = ctx->blockhash_ring      +(ctx->blockhash_ring_idx%BLOCKHASH_RING_LEN);
     436           0 :         blockhash_map_t * nonce_entry = ctx->nonce_blockhash_ring+(ctx->blockhash_ring_idx%BLOCKHASH_RING_LEN);
     437             : 
     438             :         /* See fd_durable_nonce_from_blockhash */
     439           0 :         struct {
     440           0 :           char      tag[13];
     441           0 :           fd_hash_t bh[1];
     442           0 :         } hash_buf[1] = {{ .tag = "DURABLE_NONCE", .bh = { msg->block_hash } }};
     443             : 
     444           0 :         memcpy( entry->key.b, msg->block_hash.uc, 32UL );  fd_sha256_hash( hash_buf, sizeof(hash_buf), nonce_entry->key.b );
     445           0 :         entry->slot         = msg->slot;                   nonce_entry->slot         = msg->slot;
     446           0 :         entry->block_height = msg->block_height;           nonce_entry->block_height = msg->block_height;
     447             : 
     448           0 :         map_ele_insert( ctx->blockhash_map,       entry,       ctx->blockhash_ring       );
     449           0 :         map_ele_insert( ctx->nonce_blockhash_map, nonce_entry, ctx->nonce_blockhash_ring );
     450           0 :         ctx->blockhash_ring_idx++;
     451             : 
     452           0 :         blockhash_t * hash = (blockhash_t *)msg->block_hash.uc;
     453           0 :         ctx->flush_pool_idx  = map_chain_idx_query_const( ctx->map_chain, &hash, ULONG_MAX, ctx->pool );
     454           0 :         ctx->flushing_block_height  = msg->block_height;
     455             : 
     456           0 :         ctx->completed_slot         = msg->slot;
     457           0 :         ctx->completed_block_height = msg->block_height;
     458           0 :         break;
     459           0 :       }
     460           0 :       case REPLAY_SIG_ROOT_ADVANCED: {
     461           0 :         fd_replay_root_advanced_t const * msg = &ctx->_rooted_slot_msg;
     462             : 
     463             :         /* Replace current bank with new bank */
     464           0 :         fd_bank_t * prev_bank = ctx->bank;
     465             : 
     466           0 :         ctx->bank = fd_banks_bank_query( ctx->banks, msg->bank_idx );
     467           0 :         FD_TEST( ctx->bank );
     468             : 
     469             :         /* Send slot completed message back to replay, so it can
     470             :            decrement the reference count of the previous bank. */
     471           0 :         if( FD_LIKELY( prev_bank ) ) {
     472           0 :           ulong tspub = fd_frag_meta_ts_comp( fd_tickcount() );
     473           0 :           fd_resolv_slot_exchanged_t * slot_exchanged =
     474           0 :             fd_type_pun( fd_chunk_to_laddr( ctx->out_replay->mem, ctx->out_replay->chunk ) );
     475           0 :           slot_exchanged->bank_idx = prev_bank->idx;
     476           0 :           fd_stem_publish( stem, 1UL, 0UL, ctx->out_replay->chunk, sizeof(fd_resolv_slot_exchanged_t), 0UL, tsorig, tspub );
     477           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 );
     478           0 :         }
     479             : 
     480           0 :         break;
     481           0 :       }
     482           0 :       default: break;
     483           0 :     }
     484           0 :     return;
     485           0 :   }
     486             : 
     487           0 :   fd_txn_m_t * txnm = (fd_txn_m_t *)fd_chunk_to_laddr( ctx->out_pack->mem, ctx->out_pack->chunk );
     488           0 :   FD_TEST( txnm->payload_sz<=FD_TPU_MTU );
     489           0 :   FD_TEST( txnm->txn_t_sz<=FD_TXN_MAX_SZ );
     490           0 :   fd_txn_t const * txnt = fd_txn_m_txn_t( txnm );
     491             : 
     492             :   /* If the transaction doesn't look like a nonce transaction and we
     493             :      find the recent blockhash, life is simple.  We drop transactions
     494             :      that couldn't possibly execute any more, and forward to pack ones
     495             :      that could.
     496             : 
     497             :      If we can't find the recent blockhash ... it means one of three
     498             :      things,
     499             : 
     500             :      (1) The blockhash is really old (more than 19 days) or just
     501             :          non-existent.
     502             :      (2) The blockhash is not that old, but was created before this
     503             :          validator was started.
     504             :      (3) It's really new (we haven't seen the bank yet).
     505             : 
     506             :     For durable nonce transactions, we map the value in the recent
     507             :     blockhash field to a slot and its block height.  This doesn't
     508             :     immediately let us discard any transactions, but it lets pack order
     509             :     nonce transactions, which allows it to throw out old ones.
     510             : 
     511             :     For the other three cases ... we don't want to flood pack with what
     512             :     might be junk transactions, so we accumulate them into a local
     513             :     buffer.  If we later see the blockhash come to exist, we forward any
     514             :     buffered transactions to back. */
     515             : 
     516           0 :   if( FD_UNLIKELY( txnm->block_engine.bundle_id && (txnm->block_engine.bundle_id!=ctx->bundle_id) ) ) {
     517           0 :     ctx->bundle_failed = 0;
     518           0 :     ctx->bundle_id     = txnm->block_engine.bundle_id;
     519           0 :   }
     520             : 
     521           0 :   if( FD_UNLIKELY( txnm->block_engine.bundle_id && ctx->bundle_failed ) ) {
     522           0 :     ctx->metrics.txn_dropped[ FD_METRICS_ENUM_RESOLV_TXN_DROP_REASON_V_BUNDLE_PEER_FAILED_IDX ]++;
     523           0 :     return;
     524           0 :   }
     525             : 
     526           0 :   txnm->reference_block_height = ctx->completed_block_height;
     527             : 
     528           0 :   int is_durable_nonce = fd_disco_tpu_is_durable_nonce( txnt, fd_txn_m_payload( txnm ) );
     529           0 :   map_t const *           map  = fd_ptr_if( is_durable_nonce, ctx->nonce_blockhash_map, ctx->blockhash_map );
     530           0 :   blockhash_map_t const * pool = is_durable_nonce ? ctx->nonce_blockhash_ring : ctx->blockhash_ring;
     531             : 
     532           0 :   blockhash_t const * recent_blockhash = (blockhash_t const *)( fd_txn_m_payload( txnm )+txnt->recent_blockhash_off );
     533             : 
     534           0 :   blockhash_map_t const * blockhash = map_ele_query_const( map, recent_blockhash, NULL, pool );
     535           0 :   if( FD_LIKELY( blockhash ) ) {
     536           0 :     txnm->reference_block_height = blockhash->block_height;
     537           0 :     if( FD_UNLIKELY( (!is_durable_nonce) & (txnm->reference_block_height+151UL<ctx->completed_block_height) ) ) {
     538           0 :       if( FD_UNLIKELY( txnm->block_engine.bundle_id ) ) ctx->bundle_failed = 1;
     539           0 :       ctx->metrics.txn_dropped[ FD_METRICS_ENUM_RESOLV_TXN_DROP_REASON_V_BLOCKHASH_EXPIRED_IDX ]++;
     540           0 :       return;
     541           0 :     }
     542           0 :   }
     543             : 
     544           0 :   int is_bundle_member = !!txnm->block_engine.bundle_id;
     545             : 
     546           0 :   if( FD_UNLIKELY( !is_bundle_member && !is_durable_nonce && !blockhash ) ) {
     547           0 :     ulong pool_idx;
     548           0 :     if( FD_UNLIKELY( !pool_free( ctx->pool ) ) ) {
     549           0 :       pool_idx = lru_list_idx_pop_tail( ctx->lru_list, ctx->pool );
     550           0 :       map_chain_idx_remove_fast( ctx->map_chain, pool_idx, ctx->pool );
     551           0 :       ctx->metrics.stash_removed[ FD_METRICS_ENUM_STASH_REMOVE_REASON_V_EVICTED_IDX ]++;
     552           0 :     } else {
     553           0 :       pool_idx = pool_idx_acquire( ctx->pool );
     554           0 :     }
     555             : 
     556           0 :     fd_stashed_txn_m_t * stash_txn = pool_ele( ctx->pool, pool_idx );
     557             :     /* There's a compiler bug in GCC version 12 (at least 12.1, 12.3 and
     558             :        12.4) that cause it to think stash_txn is a null pointer.  It
     559             :        then complains that the memcpy is bad and refuses to compile the
     560             :        memcpy below.  It is possible for pool_ele to return NULL, but
     561             :        that can't happen because if pool_free is 0, then all the pool
     562             :        elements must be in the LRU list, so idx_pop_tail won't return
     563             :        IDX_NULL; and if pool_free returns non-zero, then
     564             :        pool_idx_acquire won't return POOL_IDX_NULL. */
     565           0 :     FD_COMPILER_FORGET( stash_txn );
     566           0 :     fd_memcpy( stash_txn->_, txnm, fd_txn_m_realized_footprint( txnm, 1, 0 ) );
     567           0 :     stash_txn->blockhash = (blockhash_t *)(fd_txn_m_payload( (fd_txn_m_t *)(stash_txn->_) ) + txnt->recent_blockhash_off);
     568           0 :     ctx->metrics.stash_inserted++;
     569             : 
     570           0 :     map_chain_ele_insert( ctx->map_chain, stash_txn, ctx->pool );
     571           0 :     lru_list_idx_push_head( ctx->lru_list, pool_idx, ctx->pool );
     572             : 
     573           0 :     return;
     574           0 :   }
     575             : 
     576           0 :   if( FD_UNLIKELY( txnt->addr_table_adtl_cnt ) ) {
     577           0 :     if( FD_UNLIKELY( !ctx->bank ) ) {
     578           0 :       ctx->metrics.txn_dropped[ FD_METRICS_ENUM_RESOLV_TXN_DROP_REASON_V_NO_BANK_IDX ]++;
     579           0 :       if( FD_UNLIKELY( txnm->block_engine.bundle_id ) ) ctx->bundle_failed = 1;
     580           0 :       return;
     581           0 :     }
     582             : 
     583           0 :     int result = peek_aluts( ctx, txnm );
     584           0 :     if( FD_UNLIKELY( result ) ) {
     585           0 :       if( FD_UNLIKELY( txnm->block_engine.bundle_id ) ) ctx->bundle_failed = 1;
     586           0 :       return;
     587           0 :     }
     588           0 :   }
     589             : 
     590           0 :   ulong realized_sz = fd_txn_m_realized_footprint( txnm, 1, 1 );
     591           0 :   ulong tspub = fd_frag_meta_ts_comp( fd_tickcount() );
     592           0 :   fd_stem_publish( stem, 0UL, txnm->reference_block_height, ctx->out_pack->chunk, realized_sz, 0UL, tsorig, tspub );
     593           0 :   ctx->out_pack->chunk = fd_dcache_compact_next( ctx->out_pack->chunk, realized_sz, ctx->out_pack->chunk0, ctx->out_pack->wmark );
     594           0 : }
     595             : 
     596             : static void
     597             : privileged_init( fd_topo_t const *      topo,
     598           0 :                  fd_topo_tile_t const * tile ) {
     599           0 :   void * scratch = fd_topo_obj_laddr( topo, tile->tile_obj_id );
     600             : 
     601           0 :   FD_SCRATCH_ALLOC_INIT( l, scratch );
     602           0 :   fd_resolv_ctx_t * ctx = FD_SCRATCH_ALLOC_APPEND( l, alignof( fd_resolv_ctx_t ), sizeof( fd_resolv_ctx_t ) );
     603           0 :   FD_TEST( fd_rng_secure( &ctx->map_seed, sizeof(ctx->map_seed) ) );
     604           0 : }
     605             : 
     606             : static void
     607             : unprivileged_init( fd_topo_t const *      topo,
     608           0 :                    fd_topo_tile_t const * tile ) {
     609           0 :   void * scratch = fd_topo_obj_laddr( topo, tile->tile_obj_id );
     610             : 
     611           0 :   FD_SCRATCH_ALLOC_INIT( l, scratch );
     612           0 :   fd_resolv_ctx_t * ctx = FD_SCRATCH_ALLOC_APPEND( l, alignof( fd_resolv_ctx_t ), sizeof( fd_resolv_ctx_t ) );
     613             : 
     614           0 :   ctx->round_robin_cnt = fd_topo_tile_name_cnt( topo, tile->name );
     615           0 :   ctx->round_robin_idx = tile->kind_id;
     616             : 
     617           0 :   ctx->bundle_failed = 0;
     618           0 :   ctx->bundle_id     = 0UL;
     619             : 
     620           0 :   ctx->completed_slot         = 0UL;
     621           0 :   ctx->completed_block_height = 0UL;
     622           0 :   ctx->blockhash_ring_idx     = 0UL;
     623             : 
     624           0 :   ctx->flush_pool_idx = ULONG_MAX;
     625             : 
     626           0 :   ctx->pool = pool_join( pool_new( FD_SCRATCH_ALLOC_APPEND( l, pool_align(), pool_footprint( 1UL<<16UL ) ), 1UL<<16UL ) );
     627           0 :   FD_TEST( ctx->pool );
     628             : 
     629           0 :   ctx->map_chain = map_chain_join( map_chain_new( FD_SCRATCH_ALLOC_APPEND( l, map_chain_align(), map_chain_footprint( 8192ULL ) ), 8192UL, ctx->map_seed ) );
     630           0 :   FD_TEST( ctx->map_chain );
     631             : 
     632           0 :   FD_TEST( ctx->lru_list==lru_list_join( lru_list_new( ctx->lru_list ) ) );
     633             : 
     634           0 :   memset( ctx->blockhash_ring,       0, sizeof( ctx->blockhash_ring      ) );
     635           0 :   memset( ctx->nonce_blockhash_ring, 0, sizeof( ctx->nonce_blockhash_ring ) );
     636           0 :   memset( &ctx->metrics, 0, sizeof( ctx->metrics ) );
     637             : 
     638           0 :   ulong map_chain_cnt = 2UL*map_chain_cnt_est( BLOCKHASH_RING_LEN );
     639           0 :   ulong footprint     = map_footprint( map_chain_cnt );
     640           0 :   ctx->blockhash_map       = map_join( map_new( FD_SCRATCH_ALLOC_APPEND( l, map_align(), footprint ), map_chain_cnt, ctx->map_seed ) );
     641           0 :   ctx->nonce_blockhash_map = map_join( map_new( FD_SCRATCH_ALLOC_APPEND( l, map_align(), footprint ), map_chain_cnt, ctx->map_seed ) );
     642           0 :   FD_TEST( ctx->blockhash_map       );
     643           0 :   FD_TEST( ctx->nonce_blockhash_map );
     644             : 
     645           0 :   FD_TEST( tile->in_cnt<=sizeof( ctx->in )/sizeof( ctx->in[ 0 ] ) );
     646           0 :   for( ulong i=0UL; i<tile->in_cnt; i++ ) {
     647           0 :     fd_topo_link_t const * link = &topo->links[ tile->in_link_id[ i ] ];
     648           0 :     fd_topo_wksp_t const * link_wksp = &topo->workspaces[ topo->objs[ link->dcache_obj_id ].wksp_id ];
     649             : 
     650           0 :     if( FD_LIKELY(      !strcmp( link->name, "replay_slot"  ) ) ) ctx->in[ i ].kind = IN_KIND_REPLAY;
     651           0 :     else if( FD_LIKELY( !strcmp( link->name, "dedup_resolv" ) ) ) ctx->in[ i ].kind = IN_KIND_DEDUP;
     652           0 :     else FD_LOG_ERR(( "unknown in link name '%s'", link->name ));
     653             : 
     654           0 :     ctx->in[i].mem    = link_wksp->wksp;
     655           0 :     ctx->in[i].chunk0 = fd_dcache_compact_chunk0( ctx->in[i].mem, link->dcache );
     656           0 :     ctx->in[i].wmark  = fd_dcache_compact_wmark ( ctx->in[i].mem, link->dcache, link->mtu );
     657           0 :     ctx->in[i].mtu    = link->mtu;
     658           0 :   }
     659             : 
     660           0 :   ctx->out_pack->mem    = topo->workspaces[ topo->objs[ topo->links[ tile->out_link_id[ 0 ] ].dcache_obj_id ].wksp_id ].wksp;
     661           0 :   ctx->out_pack->chunk0 = fd_dcache_compact_chunk0( ctx->out_pack->mem, topo->links[ tile->out_link_id[ 0 ] ].dcache );
     662           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 );
     663           0 :   ctx->out_pack->chunk  = ctx->out_pack->chunk0;
     664             : 
     665           0 :   ctx->out_replay->mem    = topo->workspaces[ topo->objs[ topo->links[ tile->out_link_id[ 1 ] ].dcache_obj_id ].wksp_id ].wksp;
     666           0 :   ctx->out_replay->chunk0 = fd_dcache_compact_chunk0( ctx->out_replay->mem, topo->links[ tile->out_link_id[ 1 ] ].dcache );
     667           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 );
     668           0 :   ctx->out_replay->chunk  = ctx->out_replay->chunk0;
     669             : 
     670           0 :   ulong banks_obj_id = fd_pod_queryf_ulong( topo->props, ULONG_MAX, "banks" );
     671           0 :   FD_TEST( banks_obj_id!=ULONG_MAX );
     672           0 :   ctx->banks = fd_banks_join( fd_topo_obj_laddr( topo, banks_obj_id ) );
     673           0 :   FD_TEST( ctx->banks );
     674           0 :   ctx->bank = NULL;
     675             : 
     676             :   /* Read-only join to accdb.  The accdb workspace is mapped PROT_READ
     677             :      in this tile (see topology); the only writable external mapping
     678             :      is our private epoch fseq.  FD_ACCDB_FD_RO is the O_RDONLY dup
     679             :      of the accdb data file. */
     680           0 :   void * _accdb_join = FD_SCRATCH_ALLOC_APPEND( l, fd_accdb_align(), fd_accdb_footprint( tile->resolv.max_live_slots, 0 ) );
     681           0 :   void * _accdb_shmem = fd_topo_obj_laddr( topo, tile->resolv.accdb_obj_id );
     682           0 :   fd_accdb_shmem_t * accdb_shmem_ro = fd_accdb_shmem_join( _accdb_shmem );
     683           0 :   FD_TEST( accdb_shmem_ro );
     684           0 :   ulong * epoch_fseq = fd_fseq_join( fd_topo_obj_laddr( topo, tile->resolv.accdb_epoch_fseq_obj_id ) );
     685           0 :   FD_TEST( epoch_fseq );
     686           0 :   ctx->accdb = fd_accdb_join_readonly( _accdb_join, accdb_shmem_ro, epoch_fseq, FD_ACCDB_FD_RO );
     687           0 :   FD_TEST( ctx->accdb );
     688             : 
     689           0 :   ulong scratch_top = FD_SCRATCH_ALLOC_FINI( l, scratch_align() );
     690           0 :   if( FD_UNLIKELY( scratch_top > (ulong)scratch + scratch_footprint( tile ) ) )
     691           0 :     FD_LOG_ERR(( "scratch overflow %lu %lu %lu", scratch_top - (ulong)scratch - scratch_footprint( tile ), scratch_top, (ulong)scratch + scratch_footprint( tile ) ));
     692             : 
     693           0 :   fd_startup_gate_init( ctx->startup_gate, topo, tile->in_cnt );
     694           0 : }
     695             : 
     696             : static ulong
     697             : populate_allowed_seccomp( fd_topo_t const *      topo,
     698             :                           fd_topo_tile_t const * tile,
     699             :                           ulong                  out_cnt,
     700           0 :                           struct sock_filter *   out ) {
     701           0 :   (void)topo;
     702           0 :   (void)tile;
     703             : 
     704           0 :   populate_sock_filter_policy_fd_resolv_tile( out_cnt, out, (uint)fd_log_private_logfile_fd(), (uint)FD_ACCDB_FD_RO );
     705           0 :   return sock_filter_policy_fd_resolv_tile_instr_cnt;
     706           0 : }
     707             : 
     708             : static ulong
     709             : populate_allowed_fds( fd_topo_t const *      topo,
     710             :                       fd_topo_tile_t const * tile,
     711             :                       ulong                  out_fds_cnt,
     712           0 :                       int *                  out_fds ) {
     713           0 :   (void)topo;
     714           0 :   (void)tile;
     715             : 
     716           0 :   if( FD_UNLIKELY( out_fds_cnt<3UL ) ) FD_LOG_ERR(( "out_fds_cnt %lu", out_fds_cnt ));
     717             : 
     718           0 :   ulong out_cnt = 0UL;
     719           0 :   out_fds[ out_cnt++ ] = 2; /* stderr */
     720           0 :   if( FD_LIKELY( -1!=fd_log_private_logfile_fd() ) )
     721           0 :     out_fds[ out_cnt++ ] = fd_log_private_logfile_fd(); /* logfile */
     722           0 :   out_fds[ out_cnt++ ] = FD_ACCDB_FD_RO; /* accounts db readonly fd */
     723           0 :   return out_cnt;
     724           0 : }
     725             : 
     726           0 : #define STEM_BURST (1UL)
     727             : 
     728             : /* The default STEM_LAZY is derived from cr_max, which is the minimum
     729             :    depth among all reliably-consumed output links.  The resolv_replay
     730             :    link (depth 4096) dominates this, even though it only carries ~2-3
     731             :    msgs/s.  This makes housekeeping fire ~16x more often than necessary.
     732             :    We override with roughly what the default would be without accounting
     733             :    for it. */
     734           0 : #define STEM_LAZY (128000L) /* 128 us */
     735             : 
     736           0 : #define STEM_CALLBACK_CONTEXT_TYPE  fd_resolv_ctx_t
     737           0 : #define STEM_CALLBACK_CONTEXT_ALIGN alignof(fd_resolv_ctx_t)
     738             : 
     739           0 : #define STEM_CALLBACK_METRICS_WRITE metrics_write
     740           0 : #define STEM_CALLBACK_AFTER_CREDIT  after_credit
     741           0 : #define STEM_CALLBACK_BEFORE_FRAG   before_frag
     742           0 : #define STEM_CALLBACK_DURING_FRAG   during_frag
     743           0 : #define STEM_CALLBACK_AFTER_FRAG    after_frag
     744             : 
     745             : #include "../../disco/stem/fd_stem.c"
     746             : 
     747             : fd_topo_run_tile_t fd_tile_resolv = {
     748             :   .name                     = "resolv",
     749             :   .populate_allowed_seccomp = populate_allowed_seccomp,
     750             :   .populate_allowed_fds     = populate_allowed_fds,
     751             :   .scratch_align            = scratch_align,
     752             :   .scratch_footprint        = scratch_footprint,
     753             :   .privileged_init          = privileged_init,
     754             :   .unprivileged_init        = unprivileged_init,
     755             :   .run                      = stem_run,
     756             : };

Generated by: LCOV version 1.14