LCOV - code coverage report
Current view: top level - discof/poh - fd_pohi_tile.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 217 0.0 %
Date: 2025-03-20 12:08:36 Functions: 0 17 0.0 %

          Line data    Source code
       1             : #include "fd_poh.h"
       2             : 
       3             : #include "../../disco/pack/fd_pack.h"
       4             : #include "../../ballet/sha256/fd_sha256.h"
       5             : #include "../../disco/topo/fd_pod_format.h"
       6             : #include "../../disco/shred/fd_shredder.h"
       7             : #include "../../disco/shred/fd_stake_ci.h"
       8             : #include "../../disco/keyguard/fd_keyload.h"
       9             : #include "../../disco/metrics/generated/fd_metrics_poh.h"
      10             : #include "../../flamenco/leaders/fd_leaders.h"
      11             : #include "../../flamenco/fd_flamenco.h"
      12             : 
      13             : /* TODO: TILE NEEDS SECCOMP POLICY */
      14             : 
      15             : /* The PoH recorder is implemented in Firedancer but for now needs to
      16             :    work with Agave, so we have a locking scheme for them to
      17             :    co-operate.
      18             : 
      19             :    This is because the PoH tile lives in the Agave memory address
      20             :    space and their version of concurrency is locking the PoH recorder
      21             :    and reading arbitrary fields.
      22             : 
      23             :    So we allow them to lock the PoH tile, although with a very bad (for
      24             :    them) locking scheme.  By default, the tile has full and exclusive
      25             :    access to the data.  If part of Agave wishes to read/write they
      26             :    can either,
      27             : 
      28             :      1. Rewrite their concurrency to message passing based on mcache
      29             :         (preferred, but not feasible).
      30             :      2. Signal to the tile they wish to acquire the lock, by setting
      31             :         fd_poh_waiting_lock to 1.
      32             : 
      33             :    During housekeeping, the tile will check if there is the waiting lock
      34             :    is set to 1, and if so, set the returned lock to 1, indicating to the
      35             :    waiter that they may now proceed.
      36             : 
      37             :    When the waiter is done reading and writing, they restore the
      38             :    returned lock value back to zero, and the POH tile continues with its
      39             :    day. */
      40             : 
      41             : typedef struct {
      42             :   fd_poh_tile_ctx_t * poh_tile_ctx;
      43             : 
      44             :   int filter_frag;
      45             : 
      46             :   ulong bank_cnt;
      47             :   ulong * bank_busy[ 64UL ];
      48             : 
      49             :   ulong stake_in_idx;
      50             : 
      51             :   ulong pack_in_idx;
      52             : 
      53             :   /* These are temporarily set in during_frag so they can be used in
      54             :      after_frag once the frag has been validated as not overrun. */
      55             :   uchar _txns[ 1024* USHORT_MAX ];
      56             :   fd_microblock_trailer_t _microblock_trailer[1];
      57             : 
      58             :   int is_initialized;
      59             :   int recently_reset;
      60             : 
      61             :   ulong * current_slot;
      62             : 
      63             :   fd_poh_tile_in_ctx_t bank_in[ 32 ];
      64             :   fd_poh_tile_in_ctx_t stake_in;
      65             :   fd_poh_tile_in_ctx_t pack_in;
      66             : 
      67             :   fd_wksp_t * shred_out_mem;
      68             :   ulong       shred_out_chunk0;
      69             :   ulong       shred_out_wmark;
      70             :   ulong       shred_out_chunk;
      71             : 
      72             :   fd_frag_meta_t * pack_out_mcache;
      73             :   ulong            pack_out_depth;
      74             :   ulong            pack_out_seq;
      75             :   ulong *          pack_out_sync;
      76             : 
      77             :   fd_wksp_t *      pack_out_mem;
      78             :   ulong            pack_out_chunk0;
      79             :   ulong            pack_out_wmark;
      80             :   ulong            pack_out_chunk;
      81             : 
      82             :   fd_stem_context_t * stem;
      83             : 
      84             : } fd_poh_ctx_t;
      85             : 
      86             : static void
      87           0 : signal_leader_change( void * _arg FD_PARAM_UNUSED ) {
      88             :   // TODO publishing etc
      89           0 : }
      90             : 
      91             : static void *
      92           0 : get_mircoblock_buffer( void * _arg ) {
      93           0 :   fd_poh_ctx_t * ctx = (fd_poh_ctx_t *)_arg;
      94           0 :   uchar * dst = (uchar *)fd_chunk_to_laddr( ctx->shred_out_mem, ctx->shred_out_chunk );
      95           0 :   return dst;
      96           0 : }
      97             : 
      98             : static void
      99           0 : publish_microblock( void * _arg, ulong tspub, ulong sig, ulong sz ) {
     100           0 :   fd_poh_ctx_t * ctx = (fd_poh_ctx_t *)_arg;
     101           0 :   fd_stem_publish( ctx->stem, 0UL, sig, ctx->shred_out_chunk, sz, 0UL, 0UL, tspub );
     102           0 :   ctx->shred_out_chunk = fd_dcache_compact_next( ctx->shred_out_chunk, sz, ctx->shred_out_chunk0, ctx->shred_out_wmark );
     103           0 : }
     104             : 
     105             : static void *
     106           0 : get_pack_buffer( void * _arg ) {
     107           0 :   fd_poh_ctx_t * ctx = (fd_poh_ctx_t *)_arg;
     108           0 :   uchar * dst = (uchar *)fd_chunk_to_laddr( ctx->pack_out_mem, ctx->pack_out_chunk );
     109           0 :   return dst;
     110           0 : }
     111             : 
     112             : static void
     113           0 : publish_pack( void * _arg, ulong tspub, ulong sig, ulong sz ) {
     114           0 :   fd_poh_ctx_t * ctx = (fd_poh_ctx_t *)_arg;
     115           0 :   fd_mcache_publish( ctx->pack_out_mcache, ctx->pack_out_depth, ctx->pack_out_seq, sig, ctx->pack_out_chunk, sizeof(fd_became_leader_t), 0UL, 0UL, tspub );
     116           0 :   ctx->pack_out_chunk = fd_dcache_compact_next( ctx->pack_out_chunk, sz, ctx->pack_out_chunk0, ctx->pack_out_wmark );
     117           0 :   ctx->pack_out_seq = fd_seq_inc( ctx->pack_out_seq, 1UL );
     118           0 : }
     119             : 
     120             : static void
     121             : register_tick( void * _arg                FD_PARAM_UNUSED,
     122             :                ulong  current_leader_slot FD_PARAM_UNUSED,
     123           0 :                uchar  hash[ static 32 ]   FD_PARAM_UNUSED ) { }
     124             : 
     125             : /* The PoH tile needs to interact with the Agave address space to
     126             :    do certain operations that Firedancer hasn't reimplemented yet, a.k.a
     127             :    transaction execution.  We have Agave export some wrapper
     128             :    functions that we call into during regular tile execution.  These do
     129             :    not need any locking, since they are called serially from the single
     130             :    PoH tile. */
     131             : 
     132             : extern void fd_ext_bank_acquire( void const * bank );
     133             : extern void fd_ext_bank_release( void const * bank );
     134             : 
     135           0 : void fd_poh_signal_leader_change( fd_poh_ctx_t * ctx FD_PARAM_UNUSED ) { }
     136             : 
     137             : void fd_poh_register_tick( fd_poh_ctx_t * ctx         FD_PARAM_UNUSED,
     138             :                            ulong          reset_slot  FD_PARAM_UNUSED,
     139           0 :                            uchar const *  hash        FD_PARAM_UNUSED ) { }
     140             : 
     141             : /* fd_ext_poh_initialize is called by Agave on startup to
     142             :    initialize the PoH tile with some static configuration, and the
     143             :    initial reset slot and hash which it retrieves from a snapshot.
     144             : 
     145             :    This function is called by some random Agave thread, but
     146             :    it blocks booting of the PoH tile.  The tile will spin until it
     147             :    determines that this initialization has happened.
     148             : 
     149             :    signal_leader_change is an opaque Rust object that is used to
     150             :    tell the replay stage that the leader has changed.  It is a
     151             :    Box::into_raw(Arc::increment_strong(crossbeam::Sender)), so it
     152             :    has infinite lifetime unless this C code releases the refcnt.
     153             : 
     154             :    It can be used with `fd_ext_poh_signal_leader_change` which
     155             :    will just issue a nonblocking send on the channel. */
     156             : 
     157             : void
     158             : fd_poh_initialize( fd_poh_ctx_t * ctx,
     159             :                    ulong          tick_duration_ns, /* See clock comments above, will be 500ns for mainnet-beta. */
     160             :                    ulong          hashcnt_per_tick,    /* See clock comments above, will be 12,500 for mainnet-beta. */
     161             :                    ulong          ticks_per_slot,      /* See clock comments above, will almost always be 64. */
     162             :                    ulong          tick_height,         /* The counter (height) of the tick to start hashing on top of. */
     163           0 :                    uchar const *  last_entry_hash      /* Points to start of a 32 byte region of memory, the hash itself at the tick height. */ ) {
     164           0 :   fd_poh_tile_initialize( ctx->poh_tile_ctx, tick_duration_ns, hashcnt_per_tick, ticks_per_slot, tick_height,
     165           0 :       last_entry_hash );
     166           0 :   ctx->recently_reset = 1;
     167           0 :   ctx->is_initialized = 1;
     168           0 : }
     169             : 
     170             : /* fd_ext_poh_reset is called by the Agave client when a slot on
     171             :    the active fork has finished a block and we need to reset our PoH to
     172             :    be ticking on top of the block it produced. */
     173             : 
     174             : FD_FN_CONST static inline ulong
     175           0 : scratch_align( void ) {
     176           0 :   return 128UL;
     177           0 : }
     178             : 
     179             : FD_FN_PURE static inline ulong
     180           0 : scratch_footprint( fd_topo_tile_t const * tile ) {
     181           0 :   (void)tile;
     182           0 :   ulong l = FD_LAYOUT_INIT;
     183           0 :   l = FD_LAYOUT_APPEND( l, alignof( fd_poh_ctx_t ), sizeof( fd_poh_ctx_t ) );
     184           0 :   l = FD_LAYOUT_APPEND( l, fd_poh_tile_align(), fd_poh_tile_footprint() );
     185           0 :   return FD_LAYOUT_FINI( l, scratch_align() );
     186           0 : }
     187             : 
     188             : static inline void
     189             : after_credit( fd_poh_ctx_t *      ctx,
     190             :               fd_stem_context_t * stem,
     191             :               int *               opt_poll_in,
     192           0 :               int *               charge_busy ) {
     193           0 :   ctx->stem = stem;
     194             : 
     195           0 :   if( FD_UNLIKELY( !ctx->is_initialized ) ) return;
     196             : 
     197           0 :   if( FD_LIKELY( ctx->poh_tile_ctx->current_leader_slot==FD_SLOT_NULL && ctx->recently_reset ) ) {
     198             :     /* We are not leader, but we should check if we have reached a leader slot! */
     199           0 :     ulong leader_slot = FD_SLOT_NULL;
     200           0 :     ulong reset_slot = FD_SLOT_NULL;
     201           0 :     int has_reached_leader_slot = fd_poh_tile_reached_leader_slot( ctx->poh_tile_ctx, &leader_slot, &reset_slot );
     202           0 :     if( has_reached_leader_slot ) {
     203           0 :       fd_poh_tile_begin_leader( ctx->poh_tile_ctx, leader_slot, ctx->poh_tile_ctx->hashcnt_per_tick );
     204           0 :       ctx->recently_reset = 0;
     205           0 :     }
     206           0 :     *charge_busy = 1;
     207           0 :   }
     208             : 
     209           0 :   if( FD_LIKELY( fd_poh_tile_after_credit( ctx->poh_tile_ctx, opt_poll_in ) ) ) {
     210           0 :     *charge_busy = 1;
     211           0 :   }
     212             : 
     213           0 :   fd_fseq_update( ctx->current_slot, ctx->poh_tile_ctx->slot );
     214           0 : }
     215             : 
     216             : static inline void
     217           0 : during_housekeeping( fd_poh_ctx_t * ctx ) {
     218           0 :   fd_poh_tile_during_housekeeping( ctx->poh_tile_ctx );
     219           0 : }
     220             : 
     221             : static inline void
     222             : during_frag( fd_poh_ctx_t * ctx,
     223             :              ulong         in_idx,
     224             :              ulong         seq FD_PARAM_UNUSED,
     225             :              ulong         sig,
     226             :              ulong         chunk,
     227             :              ulong         sz,
     228           0 :              ulong         ctl FD_PARAM_UNUSED ) {
     229             : 
     230           0 :   ctx->filter_frag = 0;
     231             : 
     232           0 :   if( FD_UNLIKELY( in_idx==ctx->stake_in_idx ) ) {
     233           0 :     if( FD_UNLIKELY( chunk<ctx->stake_in.chunk0 || chunk>ctx->stake_in.wmark ) )
     234           0 :       FD_LOG_ERR(( "chunk %lu %lu corrupt, not in range [%lu,%lu]", chunk, sz,
     235           0 :             ctx->stake_in.chunk0, ctx->stake_in.wmark ));
     236             : 
     237           0 :     uchar const * dcache_entry = fd_chunk_to_laddr_const( ctx->stake_in.mem, chunk );
     238           0 :     fd_poh_tile_init_stakes( ctx->poh_tile_ctx, dcache_entry );
     239           0 :     return;
     240           0 :   }
     241             : 
     242           0 :   ulong pkt_type;
     243           0 :   ulong slot;
     244           0 :   if( FD_UNLIKELY( in_idx==ctx->pack_in_idx ) ) {
     245           0 :     pkt_type = fd_disco_poh_sig_pkt_type( sig );
     246           0 :     slot = fd_disco_poh_sig_slot( sig );
     247           0 :   } else {
     248           0 :     pkt_type = POH_PKT_TYPE_MICROBLOCK;
     249           0 :     slot = fd_disco_bank_sig_slot( sig );
     250           0 :   }
     251             : 
     252           0 :   int is_frag_for_prior_leader_slot = 0;
     253           0 :   if( FD_LIKELY( pkt_type==POH_PKT_TYPE_DONE_PACKING || pkt_type==POH_PKT_TYPE_MICROBLOCK ) ) {
     254             :     /* The following sequence is possible...
     255             : 
     256             :         1. We become leader in slot 10
     257             :         2. While leader, we switch to a fork that is on slot 8, where
     258             :             we are leader
     259             :         3. We get the in-flight microblocks for slot 10
     260             : 
     261             :       These in-flight microblocks need to be dropped, so we check
     262             :       against the high water mark (highwater_leader_slot) rather than
     263             :       the current hashcnt here when determining what to drop.
     264             : 
     265             :       We know if the slot is lower than the high water mark it's from a stale
     266             :       leader slot, because we will not become leader for the same slot twice
     267             :       even if we are reset back in time (to prevent duplicate blocks). */
     268           0 :     is_frag_for_prior_leader_slot = slot<ctx->poh_tile_ctx->highwater_leader_slot;
     269           0 :   }
     270             : 
     271           0 :   if( FD_UNLIKELY( in_idx==ctx->pack_in_idx ) ) {
     272             :     /* We now know the real amount of microblocks published, so set an
     273             :        exact bound for once we receive them. */
     274           0 :     ctx->filter_frag = 1;
     275           0 :     if( fd_disco_poh_sig_pkt_type( sig )==POH_PKT_TYPE_DONE_PACKING ) {
     276           0 :       if( FD_UNLIKELY( is_frag_for_prior_leader_slot ) ) return;
     277             : 
     278           0 :       fd_done_packing_t const * done_packing = fd_chunk_to_laddr( ctx->pack_in.mem, chunk );
     279           0 :       fd_poh_tile_done_packing( ctx->poh_tile_ctx, done_packing->microblocks_in_slot );
     280           0 :     }
     281           0 :     return;
     282           0 :   } else {
     283           0 :     if( FD_UNLIKELY( chunk<ctx->bank_in[ in_idx ].chunk0 || chunk>ctx->bank_in[ in_idx ].wmark || sz>USHORT_MAX ) )
     284           0 :       FD_LOG_ERR(( "chunk %lu %lu corrupt, not in range [%lu,%lu]", chunk, sz, ctx->bank_in[ in_idx ].chunk0, ctx->bank_in[ in_idx ].wmark ));
     285             : 
     286           0 :     if( !ctx->is_initialized && fd_disco_replay_old_sig_flags( sig )==REPLAY_FLAG_INIT ) {
     287           0 :       FD_LOG_INFO(( "init msg rx" ));
     288           0 :       fd_poh_init_msg_t * init_msg = (fd_poh_init_msg_t *)fd_chunk_to_laddr( ctx->bank_in[ in_idx ].mem, chunk );
     289           0 :       fd_poh_initialize( ctx, init_msg->tick_duration_ns, init_msg->hashcnt_per_tick, init_msg->ticks_per_slot, init_msg->tick_height, init_msg->last_entry_hash );
     290           0 :       ctx->filter_frag = 1;
     291           0 :       return;
     292           0 :     }
     293           0 :     uchar * src = (uchar *)fd_chunk_to_laddr( ctx->bank_in[ in_idx ].mem, chunk );
     294             : 
     295           0 :     ulong raw_sz = (sz * sizeof(fd_txn_p_t))+sizeof(fd_microblock_trailer_t);
     296           0 :     FD_TEST( raw_sz<=1024*USHORT_MAX );
     297           0 :     fd_memcpy( ctx->_txns, src, raw_sz-sizeof(fd_microblock_trailer_t) );
     298           0 :     fd_memcpy( ctx->_microblock_trailer, src+(sz * sizeof(fd_txn_p_t)), sizeof(fd_microblock_trailer_t) );
     299             : 
     300           0 :     ctx->filter_frag = is_frag_for_prior_leader_slot;
     301           0 :   }
     302           0 : }
     303             : 
     304             : static inline void
     305             : after_frag( fd_poh_ctx_t *      ctx,
     306             :             ulong               in_idx,
     307             :             ulong               seq,
     308             :             ulong               sig,
     309             :             ulong               sz,
     310             :             ulong               tsorig,
     311             :             ulong               tspub,
     312           0 :             fd_stem_context_t * stem ) {
     313           0 :   (void)seq;
     314           0 :   (void)tsorig;
     315           0 :   (void)tspub;
     316           0 :   (void)stem;
     317             : 
     318           0 :   if( FD_UNLIKELY( ctx->filter_frag ) ) return;
     319             : 
     320           0 :   if( FD_UNLIKELY( in_idx==ctx->stake_in_idx ) ) {
     321             :     /* Nothing to do if we transition into being leader, since it
     322             :        will just get picked up by the regular tick loop. */
     323           0 :     fd_poh_tile_fini_stakes( ctx->poh_tile_ctx );
     324           0 :     return;
     325           0 :   }
     326             : 
     327           0 :   if( FD_UNLIKELY( !ctx->poh_tile_ctx->microblocks_lower_bound ) ) {
     328           0 :     double tick_per_ns = fd_tempo_tick_per_ns( NULL );
     329           0 :     fd_histf_sample( ctx->poh_tile_ctx->first_microblock_delay, (ulong)((double)(fd_log_wallclock()-ctx->poh_tile_ctx->reset_slot_start_ns)/tick_per_ns) );
     330           0 :   }
     331             : 
     332           0 :   ulong target_flags = fd_disco_replay_old_sig_flags( sig );
     333           0 :   ulong target_slot = fd_disco_replay_old_sig_slot( sig );
     334             : 
     335           0 :   ulong is_packed_microblock = target_flags & REPLAY_FLAG_PACKED_MICROBLOCK;
     336           0 :   ulong is_finalized_block = target_flags & REPLAY_FLAG_FINISHED_BLOCK;
     337             :   // ulong is_catching_up = target_flags & REPLAY_FLAG_CATCHING_UP;
     338             : 
     339           0 :   if( is_packed_microblock ) {
     340           0 :     ulong txn_cnt = sz;
     341           0 :     fd_txn_p_t * txns = (fd_txn_p_t *)(ctx->_txns);
     342           0 :     ulong sig = fd_disco_poh_sig( target_slot, POH_PKT_TYPE_MICROBLOCK, in_idx );
     343           0 :     fd_poh_tile_process_packed_microblock( ctx->poh_tile_ctx, target_slot, sig, txns, txn_cnt, ctx->_microblock_trailer->hash );
     344           0 :   } else {
     345           0 :      if( is_finalized_block && ctx->is_initialized ) {
     346           0 :       fd_poh_tile_reset( ctx->poh_tile_ctx, target_slot, ctx->_microblock_trailer->hash, ctx->poh_tile_ctx->hashcnt_per_tick );
     347           0 :       ctx->recently_reset = 1;
     348           0 :     }
     349           0 :   }
     350           0 : }
     351             : 
     352             : static void
     353             : privileged_init( fd_topo_t *      topo,
     354           0 :                  fd_topo_tile_t * tile ) {
     355           0 :   void * scratch = fd_topo_obj_laddr( topo, tile->tile_obj_id );
     356             : 
     357           0 :   FD_SCRATCH_ALLOC_INIT( l, scratch );
     358           0 :   fd_poh_ctx_t * ctx = FD_SCRATCH_ALLOC_APPEND( l, alignof( fd_poh_ctx_t ), sizeof( fd_poh_ctx_t ) );
     359           0 :   ctx->poh_tile_ctx = FD_SCRATCH_ALLOC_APPEND( l, fd_poh_tile_align(), fd_poh_tile_footprint()  );
     360             : 
     361           0 :   if( FD_UNLIKELY( !strcmp( tile->poh.identity_key_path, "" ) ) )
     362           0 :     FD_LOG_ERR(( "identity_key_path not set" ));
     363             : 
     364           0 :   const uchar * identity_key = fd_keyload_load( tile->poh.identity_key_path, /* pubkey only: */ 1 );
     365           0 :   fd_memcpy( ctx->poh_tile_ctx->identity_key.uc, identity_key, 32UL );
     366           0 : }
     367             : 
     368             : static void
     369             : unprivileged_init( fd_topo_t *      topo,
     370           0 :                    fd_topo_tile_t * tile ) {
     371           0 :   void * scratch = fd_topo_obj_laddr( topo, tile->tile_obj_id );
     372             : 
     373           0 :   FD_SCRATCH_ALLOC_INIT( l, scratch );
     374           0 :   fd_poh_ctx_t * ctx = FD_SCRATCH_ALLOC_APPEND( l, alignof( fd_poh_ctx_t ), sizeof( fd_poh_ctx_t ) );
     375           0 :   ctx->poh_tile_ctx = FD_SCRATCH_ALLOC_APPEND( l, fd_poh_tile_align(), fd_poh_tile_footprint()  );
     376           0 : #define NONNULL( x ) (__extension__({                                        \
     377           0 :       __typeof__((x)) __x = (x);                                             \
     378           0 :       if( FD_UNLIKELY( !__x ) ) FD_LOG_ERR(( #x " was unexpectedly NULL" )); \
     379           0 :       __x; }))
     380             : 
     381             :   // TODO: scratch alloc needs fixing!
     382           0 :   fd_poh_tile_new( ctx->poh_tile_ctx, ctx, get_mircoblock_buffer, publish_microblock, get_pack_buffer, publish_pack, register_tick, signal_leader_change );
     383             : 
     384           0 :   ulong poh_slot_obj_id = fd_pod_query_ulong( topo->props, "poh_slot", ULONG_MAX );
     385           0 :   FD_TEST( poh_slot_obj_id!=ULONG_MAX );
     386           0 :   ctx->current_slot = fd_fseq_join( fd_topo_obj_laddr( topo, poh_slot_obj_id ) );
     387             : 
     388           0 :   ctx->is_initialized = 0;
     389           0 :   ctx->recently_reset = 0;
     390           0 :   ctx->bank_cnt = tile->in_cnt-2UL;
     391           0 :   ctx->stake_in_idx = tile->in_cnt-2UL;
     392           0 :   ctx->pack_in_idx = tile->in_cnt-1UL;
     393             : 
     394           0 :   FD_TEST( ctx->bank_cnt<=sizeof(ctx->bank_busy)/sizeof(ctx->bank_busy[0]) );
     395           0 :   for( ulong i=0UL; i<ctx->bank_cnt; i++ ) {
     396           0 :     ulong busy_obj_id = fd_pod_queryf_ulong( topo->props, ULONG_MAX, "bank_busy.%lu", i );
     397           0 :     FD_TEST( busy_obj_id!=ULONG_MAX );
     398           0 :     ctx->bank_busy[ i ] = fd_fseq_join( fd_topo_obj_laddr( topo, busy_obj_id ) );
     399           0 :     if( FD_UNLIKELY( !ctx->bank_busy[ i ] ) ) FD_LOG_ERR(( "banking tile %lu has no busy flag", i ));
     400           0 :   }
     401             : 
     402           0 :   for( ulong i=0UL; i<tile->in_cnt-2UL; i++ ) {
     403           0 :     fd_topo_link_t * link = &topo->links[ tile->in_link_id[ i ] ];
     404           0 :     fd_topo_wksp_t * link_wksp = &topo->workspaces[ topo->objs[ link->dcache_obj_id ].wksp_id ];
     405           0 :     FD_TEST( strcmp( link->name, "replay_poh" )==0 );
     406             : 
     407           0 :     ctx->bank_in[ i ].mem    = link_wksp->wksp;
     408           0 :     ctx->bank_in[ i ].chunk0 = fd_dcache_compact_chunk0( ctx->bank_in[i].mem, link->dcache );
     409           0 :     ctx->bank_in[ i ].wmark  = fd_dcache_compact_wmark ( ctx->bank_in[i].mem, link->dcache, link->mtu );
     410           0 :   }
     411             : 
     412           0 :   FD_TEST( strcmp( topo->links[ tile->in_link_id[ ctx->stake_in_idx ] ].name, "stake_out" )==0 );
     413           0 :   ctx->stake_in.mem    = topo->workspaces[ topo->objs[ topo->links[ tile->in_link_id[ ctx->stake_in_idx ] ].dcache_obj_id ].wksp_id ].wksp;
     414           0 :   ctx->stake_in.chunk0 = fd_dcache_compact_chunk0( ctx->stake_in.mem, topo->links[ tile->in_link_id[ ctx->stake_in_idx ] ].dcache );
     415           0 :   ctx->stake_in.wmark  = fd_dcache_compact_wmark ( ctx->stake_in.mem, topo->links[ tile->in_link_id[ ctx->stake_in_idx ] ].dcache, topo->links[ tile->in_link_id[ ctx->stake_in_idx ] ].mtu );
     416             : 
     417           0 :   FD_TEST( strcmp( topo->links[ tile->in_link_id[ ctx->pack_in_idx ] ].name, "pack_replay" )==0 );
     418           0 :   ctx->pack_in.mem    = topo->workspaces[ topo->objs[ topo->links[ tile->in_link_id[ ctx->pack_in_idx ] ].dcache_obj_id ].wksp_id ].wksp;
     419           0 :   ctx->pack_in.chunk0 = fd_dcache_compact_chunk0( ctx->stake_in.mem, topo->links[ tile->in_link_id[ ctx->pack_in_idx ] ].dcache );
     420           0 :   ctx->pack_in.wmark  = fd_dcache_compact_wmark ( ctx->stake_in.mem, topo->links[ tile->in_link_id[ ctx->pack_in_idx ] ].dcache, topo->links[ tile->in_link_id[ ctx->pack_in_idx ] ].mtu );
     421             : 
     422           0 :   ctx->shred_out_mem    = topo->workspaces[ topo->objs[ topo->links[ tile->out_link_id[ 0 ] ].dcache_obj_id ].wksp_id ].wksp;
     423           0 :   ctx->shred_out_chunk0 = fd_dcache_compact_chunk0( ctx->shred_out_mem, topo->links[ tile->out_link_id[ 0 ] ].dcache );
     424           0 :   ctx->shred_out_wmark  = fd_dcache_compact_wmark ( ctx->shred_out_mem, topo->links[ tile->out_link_id[ 0 ] ].dcache, topo->links[ tile->out_link_id[ 0 ] ].mtu );
     425           0 :   ctx->shred_out_chunk  = ctx->shred_out_chunk0;
     426             : 
     427           0 :   ctx->pack_out_mcache = topo->links[ tile->out_link_id[ 1 ] ].mcache;
     428           0 :   ctx->pack_out_sync   = fd_mcache_seq_laddr( ctx->pack_out_mcache );
     429           0 :   ctx->pack_out_depth  = fd_mcache_depth( ctx->pack_out_mcache );
     430           0 :   ctx->pack_out_seq    = fd_mcache_seq_query( ctx->pack_out_sync );
     431             : 
     432           0 :   ctx->pack_out_mem    = topo->workspaces[ topo->objs[ topo->links[ tile->out_link_id[ 1 ] ].dcache_obj_id ].wksp_id ].wksp;
     433           0 :   ctx->pack_out_chunk0 = fd_dcache_compact_chunk0( ctx->pack_out_mem, topo->links[ tile->out_link_id[ 1 ] ].dcache );
     434           0 :   ctx->pack_out_wmark  = fd_dcache_compact_wmark ( ctx->pack_out_mem, topo->links[ tile->out_link_id[ 1 ] ].dcache, topo->links[ tile->out_link_id[ 1 ] ].mtu );
     435           0 :   ctx->pack_out_chunk  = ctx->pack_out_chunk0;
     436             : 
     437           0 :   ulong scratch_top = FD_SCRATCH_ALLOC_FINI( l, 1UL );
     438           0 :   if( FD_UNLIKELY( scratch_top > (ulong)scratch + scratch_footprint( tile ) ) )
     439           0 :     FD_LOG_ERR(( "scratch overflow %lu %lu %lu", scratch_top - (ulong)scratch - scratch_footprint( tile ), scratch_top, (ulong)scratch + scratch_footprint( tile ) ));
     440           0 : }
     441             : 
     442             : /* One tick, one microblock, and one leader update. */
     443           0 : #define STEM_BURST (3UL)
     444             : 
     445             : /* See explanation in fd_pack */
     446           0 : #define STEM_LAZY  (128L*3000L)
     447             : 
     448           0 : #define STEM_CALLBACK_CONTEXT_TYPE  fd_poh_ctx_t
     449           0 : #define STEM_CALLBACK_CONTEXT_ALIGN alignof(fd_poh_ctx_t)
     450             : 
     451           0 : #define STEM_CALLBACK_DURING_HOUSEKEEPING during_housekeeping
     452           0 : #define STEM_CALLBACK_AFTER_CREDIT        after_credit
     453           0 : #define STEM_CALLBACK_DURING_FRAG         during_frag
     454           0 : #define STEM_CALLBACK_AFTER_FRAG          after_frag
     455             : 
     456             : #include "../../disco/stem/fd_stem.c"
     457             : 
     458             : fd_topo_run_tile_t fd_tile_poh_int = {
     459             :   .name                     = "pohi",
     460             :   .populate_allowed_seccomp = NULL,
     461             :   .populate_allowed_fds     = NULL,
     462             :   .scratch_align            = scratch_align,
     463             :   .scratch_footprint        = scratch_footprint,
     464             :   .privileged_init          = privileged_init,
     465             :   .unprivileged_init        = unprivileged_init,
     466             :   .run                      = stem_run,
     467             : };

Generated by: LCOV version 1.14