Line data Source code
1 : #include "../bank/fd_bank_abi.h"
2 :
3 : #include "../../disco/tiles.h"
4 : #include "../../disco/fd_txn_m.h"
5 : #include "../../disco/metrics/fd_metrics.h"
6 : #include "../../flamenco/runtime/fd_system_ids_pp.h"
7 :
8 : #if FD_HAS_AVX
9 : #include "../../util/simd/fd_avx.h"
10 : #endif
11 :
12 0 : #define FD_RESOLH_IN_KIND_FRAGMENT (0)
13 0 : #define FD_RESOLH_IN_KIND_BANK (1)
14 :
15 : struct blockhash {
16 : uchar b[ 32 ];
17 : };
18 :
19 : typedef struct blockhash blockhash_t;
20 :
21 : struct blockhash_map {
22 : blockhash_t key;
23 : ulong slot;
24 : };
25 :
26 : typedef struct blockhash_map blockhash_map_t;
27 :
28 : static const blockhash_t null_blockhash = { 0 };
29 :
30 : /* The blockhash ring holds recent blockhashes, so we can identify when
31 : a transaction arrives, what slot it will expire (and can no longer be
32 : packed) in. This is useful so we don't send transactions to pack
33 : that are no longer packable.
34 :
35 : Unfortunately, poorly written transaction senders frequently send
36 : transactions from millions of slots ago, so we need a large ring to
37 : be able to determine and evict these. The highest practically useful
38 : value here is around 22, which works out to 19 days of blockhash
39 : history. Beyond this, the validator is likely to be restarted, and
40 : lose the history anyway. */
41 :
42 0 : #define BLOCKHASH_LG_RING_CNT 22UL
43 0 : #define BLOCKHASH_RING_LEN (1UL<<BLOCKHASH_LG_RING_CNT)
44 :
45 : #define MAP_NAME map
46 0 : #define MAP_T blockhash_map_t
47 0 : #define MAP_KEY_T blockhash_t
48 0 : #define MAP_LG_SLOT_CNT (BLOCKHASH_LG_RING_CNT+1UL)
49 0 : #define MAP_KEY_NULL null_blockhash
50 : #if FD_HAS_AVX
51 0 : # define MAP_KEY_INVAL(k) _mm256_testz_si256( wb_ldu( (k).b ), wb_ldu( (k).b ) )
52 : #else
53 : # define MAP_KEY_INVAL(k) MAP_KEY_EQUAL(k, null_blockhash)
54 : #endif
55 0 : #define MAP_KEY_EQUAL(k0,k1) (!memcmp((k0).b,(k1).b, 32UL))
56 : #define MAP_MEMOIZE 0
57 : #define MAP_KEY_EQUAL_IS_SLOW 1
58 0 : #define MAP_KEY_HASH(key) fd_uint_load_4( (key).b )
59 : #define MAP_QUERY_OPT 1
60 :
61 : #include "../../util/tmpl/fd_map.c"
62 :
63 : typedef struct {
64 : union {
65 : ulong pool_next; /* Used when it's released */
66 : ulong lru_next; /* Used when it's acquired */
67 : }; /* .. so it's okay to store them in the same memory */
68 : ulong lru_prev;
69 :
70 : ulong map_next;
71 : ulong map_prev;
72 :
73 : blockhash_t * blockhash;
74 : uchar _[ FD_TPU_PARSED_MTU ] __attribute__((aligned(alignof(fd_txn_m_t))));
75 : } fd_stashed_txn_m_t;
76 :
77 : #define POOL_NAME pool
78 0 : #define POOL_T fd_stashed_txn_m_t
79 0 : #define POOL_NEXT pool_next
80 : #define POOL_IDX_T ulong
81 :
82 : #include "../../util/tmpl/fd_pool.c"
83 :
84 : /* We'll push at the head, which means the tail is the oldest. */
85 : #define DLIST_NAME lru_list
86 : #define DLIST_ELE_T fd_stashed_txn_m_t
87 0 : #define DLIST_PREV lru_prev
88 0 : #define DLIST_NEXT lru_next
89 :
90 : #include "../../util/tmpl/fd_dlist.c"
91 :
92 : #define MAP_NAME map_chain
93 0 : #define MAP_ELE_T fd_stashed_txn_m_t
94 : #define MAP_KEY_T blockhash_t *
95 0 : #define MAP_KEY blockhash
96 0 : #define MAP_IDX_T ulong
97 0 : #define MAP_NEXT map_next
98 0 : #define MAP_PREV map_prev
99 0 : #define MAP_KEY_HASH(k,s) ((s) ^ fd_ulong_load_8( (*(k))->b ))
100 0 : #define MAP_KEY_EQ(k0,k1) (!memcmp((*(k0))->b, (*(k1))->b, 32UL))
101 : #define MAP_OPTIMIZE_RANDOM_ACCESS_REMOVAL 1
102 : #define MAP_MULTI 1
103 :
104 : #include "../../util/tmpl/fd_map_chain.c"
105 :
106 : struct fd_resolh_in {
107 : int kind;
108 :
109 : fd_wksp_t * mem;
110 : ulong chunk0;
111 : ulong wmark;
112 : ulong mtu;
113 : };
114 :
115 : typedef struct fd_resolh_in fd_resolh_in_t;
116 :
117 : struct fd_resolh_tile {
118 : ulong round_robin_idx;
119 : ulong round_robin_cnt;
120 :
121 : int bundle_failed;
122 : ulong bundle_id;
123 :
124 : void * root_bank;
125 : ulong root_slot;
126 :
127 : blockhash_map_t * blockhash_map;
128 :
129 : ulong flushing_slot;
130 : ulong flush_pool_idx;
131 :
132 : fd_stashed_txn_m_t * pool;
133 : map_chain_t * map_chain;
134 : lru_list_t lru_list[1];
135 :
136 : ulong completed_slot;
137 : ulong blockhash_ring_idx;
138 : blockhash_t blockhash_ring[ BLOCKHASH_RING_LEN ];
139 :
140 : uchar _bank_msg[ sizeof(fd_completed_bank_t) ];
141 :
142 : struct {
143 : ulong lut[ FD_METRICS_COUNTER_RESOLH_LUT_RESOLVED_CNT ];
144 : ulong blockhash_expired;
145 : ulong blockhash_unknown;
146 : ulong bundle_peer_failure_cnt;
147 : ulong stash[ FD_METRICS_COUNTER_RESOLH_STASH_OPERATION_CNT ];
148 : } metrics;
149 :
150 : fd_resolh_in_t in[ 64UL ];
151 :
152 : fd_wksp_t * out_mem;
153 : ulong out_chunk0;
154 : ulong out_wmark;
155 : ulong out_chunk;
156 : };
157 :
158 : typedef struct fd_resolh_tile fd_resolh_tile_t;
159 :
160 : FD_FN_CONST static inline ulong
161 0 : scratch_align( void ) {
162 0 : return alignof( fd_resolh_tile_t );
163 0 : }
164 :
165 : FD_FN_PURE static inline ulong
166 0 : scratch_footprint( fd_topo_tile_t const * tile ) {
167 0 : (void)tile;
168 0 : ulong l = FD_LAYOUT_INIT;
169 0 : l = FD_LAYOUT_APPEND( l, alignof( fd_resolh_tile_t ), sizeof( fd_resolh_tile_t ) );
170 0 : l = FD_LAYOUT_APPEND( l, pool_align(), pool_footprint( 1UL<<16UL ) );
171 0 : l = FD_LAYOUT_APPEND( l, map_chain_align(), map_chain_footprint( 8192UL ) );
172 0 : l = FD_LAYOUT_APPEND( l, map_align(), map_footprint() );
173 0 : return FD_LAYOUT_FINI( l, scratch_align() );
174 0 : }
175 :
176 : extern void fd_ext_bank_release( void const * bank );
177 :
178 : static ulong _fd_ext_resolh_tile_cnt;
179 :
180 : ulong
181 0 : fd_ext_resolv_tile_cnt( void ) {
182 0 : while( !FD_VOLATILE( _fd_ext_resolh_tile_cnt ) ) {}
183 0 : return _fd_ext_resolh_tile_cnt;
184 0 : }
185 :
186 : static inline void
187 0 : metrics_write( fd_resolh_tile_t * ctx ) {
188 0 : FD_MCNT_SET( RESOLH, BLOCKHASH_EXPIRED, ctx->metrics.blockhash_expired );
189 0 : FD_MCNT_ENUM_COPY( RESOLH, LUT_RESOLVED, ctx->metrics.lut );
190 0 : FD_MCNT_ENUM_COPY( RESOLH, STASH_OPERATION, ctx->metrics.stash );
191 0 : FD_MCNT_SET( RESOLH, TRANSACTION_BUNDLE_PEER_FAILURE, ctx->metrics.bundle_peer_failure_cnt );
192 0 : }
193 :
194 : static int
195 : before_frag( fd_resolh_tile_t * ctx,
196 : ulong in_idx,
197 : ulong seq,
198 0 : ulong sig ) {
199 0 : (void)sig;
200 :
201 0 : if( FD_UNLIKELY( ctx->in[in_idx].kind==FD_RESOLH_IN_KIND_BANK ) ) return 0;
202 :
203 0 : return (seq % ctx->round_robin_cnt) != ctx->round_robin_idx;
204 0 : }
205 :
206 : static inline void
207 : during_frag( fd_resolh_tile_t * ctx,
208 : ulong in_idx,
209 : ulong seq FD_PARAM_UNUSED,
210 : ulong sig FD_PARAM_UNUSED,
211 : ulong chunk,
212 : ulong sz,
213 0 : ulong ctl FD_PARAM_UNUSED ) {
214 :
215 0 : if( FD_UNLIKELY( chunk<ctx->in[ in_idx ].chunk0 || chunk>ctx->in[ in_idx ].wmark || sz>ctx->in[ in_idx ].mtu ) )
216 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 ));
217 :
218 0 : switch( ctx->in[in_idx].kind ) {
219 0 : case FD_RESOLH_IN_KIND_BANK:
220 0 : fd_memcpy( ctx->_bank_msg, fd_chunk_to_laddr_const( ctx->in[in_idx].mem, chunk ), sz );
221 0 : break;
222 0 : case FD_RESOLH_IN_KIND_FRAGMENT: {
223 0 : uchar * src = (uchar *)fd_chunk_to_laddr( ctx->in[in_idx].mem, chunk );
224 0 : uchar * dst = (uchar *)fd_chunk_to_laddr( ctx->out_mem, ctx->out_chunk );
225 0 : fd_memcpy( dst, src, sz );
226 0 : break;
227 0 : }
228 0 : default:
229 0 : FD_LOG_ERR(( "unknown in kind %d", ctx->in[in_idx].kind ));
230 0 : }
231 0 : }
232 :
233 : static inline int
234 : publish_txn( fd_resolh_tile_t * ctx,
235 : fd_stem_context_t * stem,
236 0 : fd_stashed_txn_m_t const * stashed ) {
237 0 : fd_txn_m_t * txnm = (fd_txn_m_t *)fd_chunk_to_laddr( ctx->out_mem, ctx->out_chunk );
238 0 : fd_memcpy( txnm, stashed->_, fd_txn_m_realized_footprint( (fd_txn_m_t *)stashed->_, 1, 0 ) );
239 :
240 0 : fd_txn_t const * txnt = fd_txn_m_txn_t( txnm );
241 :
242 0 : txnm->reference_slot = ctx->flushing_slot;
243 :
244 0 : if( FD_UNLIKELY( txnt->addr_table_adtl_cnt ) ) {
245 0 : if( FD_UNLIKELY( !ctx->root_bank ) ) {
246 0 : FD_MCNT_INC( RESOLH, NO_BANK_DROP, 1 );
247 0 : return 0;
248 0 : } else {
249 0 : int result = fd_bank_abi_resolve_address_lookup_tables( ctx->root_bank, 0, ctx->root_slot, txnt, fd_txn_m_payload( txnm ), fd_txn_m_alut( txnm ) );
250 : /* result is in [-5, 0]. We want to map -5 to 0, -4 to 1, etc. */
251 0 : ctx->metrics.lut[ (ulong)((long)FD_METRICS_COUNTER_RESOLH_LUT_RESOLVED_CNT+result-1L) ]++;
252 :
253 0 : if( FD_UNLIKELY( result!=FD_BANK_ABI_TXN_INIT_SUCCESS ) ) return 0;
254 0 : }
255 0 : }
256 :
257 0 : ulong realized_sz = fd_txn_m_realized_footprint( txnm, 1, 1 );
258 0 : ulong tspub = fd_frag_meta_ts_comp( fd_tickcount() );
259 0 : fd_stem_publish( stem, 0UL, txnm->reference_slot, ctx->out_chunk, realized_sz, 0UL, 0UL, tspub );
260 0 : ctx->out_chunk = fd_dcache_compact_next( ctx->out_chunk, realized_sz, ctx->out_chunk0, ctx->out_wmark );
261 :
262 0 : return 1;
263 0 : }
264 :
265 : static inline void
266 : after_credit( fd_resolh_tile_t * ctx,
267 : fd_stem_context_t * stem,
268 : int * opt_poll_in,
269 0 : int * charge_busy ) {
270 0 : if( FD_LIKELY( ctx->flush_pool_idx==ULONG_MAX ) ) return;
271 :
272 0 : *charge_busy = 1;
273 0 : *opt_poll_in = 0;
274 :
275 0 : ulong next = map_chain_idx_next_const( ctx->flush_pool_idx, ULONG_MAX, ctx->pool );
276 0 : map_chain_idx_remove_fast( ctx->map_chain, ctx->flush_pool_idx, ctx->pool );
277 0 : if( FD_LIKELY( publish_txn( ctx, stem, pool_ele( ctx->pool, ctx->flush_pool_idx ) ) ) ) {
278 0 : ctx->metrics.stash[ FD_METRICS_ENUM_RESOLVE_STASH_OPERATION_V_PUBLISHED_IDX ]++;
279 0 : } else {
280 0 : ctx->metrics.stash[ FD_METRICS_ENUM_RESOLVE_STASH_OPERATION_V_REMOVED_IDX ]++;
281 0 : }
282 0 : lru_list_idx_remove( ctx->lru_list, ctx->flush_pool_idx, ctx->pool );
283 0 : pool_idx_release( ctx->pool, ctx->flush_pool_idx );
284 0 : ctx->flush_pool_idx = next;
285 0 : }
286 :
287 : /* Returns 0 if not a durable nonce transaction and 1 if it may be a
288 : durable nonce transaction */
289 :
290 : FD_FN_PURE static inline int
291 : fd_resolh_is_durable_nonce( fd_txn_t const * txn,
292 0 : uchar const * payload ) {
293 0 : if( FD_UNLIKELY( txn->instr_cnt==0 ) ) return 0;
294 :
295 0 : fd_txn_instr_t const * ix0 = &txn->instr[ 0 ];
296 0 : fd_acct_addr_t const * prog0 = fd_txn_get_acct_addrs( txn, payload ) + ix0->program_id;
297 : /* First instruction must be SystemProgram nonceAdvance instruction */
298 0 : fd_acct_addr_t const system_program[1] = { { { SYS_PROG_ID } } };
299 0 : if( FD_LIKELY( memcmp( prog0, system_program, sizeof(fd_acct_addr_t) ) ) ) return 0;
300 :
301 : /* instruction with three accounts and a four byte instruction data, a
302 : little-endian uint value 4 */
303 0 : if( FD_UNLIKELY( (ix0->data_sz!=4) | (ix0->acct_cnt!=3) ) ) return 0;
304 :
305 0 : return fd_uint_load_4( payload + ix0->data_off )==4U;
306 0 : }
307 :
308 : static inline void
309 : after_frag( fd_resolh_tile_t * ctx,
310 : ulong in_idx,
311 : ulong seq,
312 : ulong sig,
313 : ulong sz,
314 : ulong tsorig,
315 : ulong _tspub,
316 0 : fd_stem_context_t * stem ) {
317 0 : (void)seq;
318 0 : (void)sz;
319 0 : (void)_tspub;
320 :
321 0 : if( FD_UNLIKELY( ctx->in[in_idx].kind==FD_RESOLH_IN_KIND_BANK ) ) {
322 0 : switch( sig ) {
323 0 : case 0: {
324 0 : fd_rooted_bank_t * frag = (fd_rooted_bank_t *)ctx->_bank_msg;
325 0 : if( FD_LIKELY( ctx->root_bank ) ) fd_ext_bank_release( ctx->root_bank );
326 :
327 0 : ctx->root_bank = frag->bank;
328 0 : ctx->root_slot = frag->slot;
329 0 : break;
330 0 : }
331 0 : case 1: {
332 0 : fd_completed_bank_t * frag = (fd_completed_bank_t *)ctx->_bank_msg;
333 :
334 : /* blockhash_ring is initalized to all zeros. blockhash=0 is an illegal map query */
335 0 : if( FD_UNLIKELY( memcmp( &ctx->blockhash_ring[ ctx->blockhash_ring_idx%BLOCKHASH_RING_LEN ], (uchar[ 32UL ]){ 0UL }, sizeof(blockhash_t) ) ) ) {
336 0 : blockhash_map_t * entry = map_query( ctx->blockhash_map, ctx->blockhash_ring[ ctx->blockhash_ring_idx%BLOCKHASH_RING_LEN ], NULL );
337 0 : if( FD_LIKELY( entry ) ) map_remove( ctx->blockhash_map, entry );
338 0 : }
339 :
340 0 : memcpy( ctx->blockhash_ring[ ctx->blockhash_ring_idx%BLOCKHASH_RING_LEN ].b, frag->hash, 32UL );
341 0 : ctx->blockhash_ring_idx++;
342 :
343 0 : blockhash_map_t * blockhash = map_insert( ctx->blockhash_map, *(blockhash_t *)frag->hash );
344 0 : blockhash->slot = frag->slot;
345 :
346 0 : blockhash_t * hash = (blockhash_t *)frag->hash;
347 0 : ctx->flush_pool_idx = map_chain_idx_query_const( ctx->map_chain, &hash, ULONG_MAX, ctx->pool );
348 0 : ctx->flushing_slot = frag->slot;
349 :
350 0 : ctx->completed_slot = frag->slot;
351 0 : break;
352 0 : }
353 0 : default:
354 0 : FD_LOG_ERR(( "unknown sig %lu", sig ));
355 0 : }
356 0 : return;
357 0 : }
358 :
359 0 : fd_txn_m_t * txnm = (fd_txn_m_t *)fd_chunk_to_laddr( ctx->out_mem, ctx->out_chunk );
360 0 : FD_TEST( txnm->payload_sz<=FD_TPU_MTU );
361 0 : FD_TEST( txnm->txn_t_sz<=FD_TXN_MAX_SZ );
362 0 : fd_txn_t const * txnt = fd_txn_m_txn_t( txnm );
363 :
364 : /* If we find the recent blockhash, life is simple. We drop
365 : transactions that couldn't possibly execute any more, and forward
366 : to pack ones that could.
367 :
368 : If we can't find the recent blockhash ... it means one of four
369 : things,
370 :
371 : (1) The blockhash is really old (more than 19 days) or just
372 : non-existent.
373 : (2) The blockhash is not that old, but was created before this
374 : validator was started.
375 : (3) It's really new (we haven't seen the bank yet).
376 : (4) It's a durable nonce transaction, or part of a bundle (just let
377 : it pass).
378 :
379 : For durable nonce transactions, there isn't much we can do except
380 : pass them along and see if they execute.
381 :
382 : For the other three cases ... we don't want to flood pack with what
383 : might be junk transactions, so we accumulate them into a local
384 : buffer. If we later see the blockhash come to exist, we forward any
385 : buffered transactions to back. */
386 :
387 0 : if( FD_UNLIKELY( txnm->block_engine.bundle_id && (txnm->block_engine.bundle_id!=ctx->bundle_id) ) ) {
388 0 : ctx->bundle_failed = 0;
389 0 : ctx->bundle_id = txnm->block_engine.bundle_id;
390 0 : }
391 :
392 0 : if( FD_UNLIKELY( txnm->block_engine.bundle_id && ctx->bundle_failed ) ) {
393 0 : ctx->metrics.bundle_peer_failure_cnt++;
394 0 : return;
395 0 : }
396 :
397 0 : txnm->reference_slot = ctx->completed_slot;
398 0 : blockhash_map_t const * blockhash = map_query_const( ctx->blockhash_map, *(blockhash_t*)( fd_txn_m_payload( txnm )+txnt->recent_blockhash_off ), NULL );
399 0 : if( FD_LIKELY( blockhash ) ) {
400 0 : txnm->reference_slot = blockhash->slot;
401 0 : if( FD_UNLIKELY( txnm->reference_slot+151UL<ctx->completed_slot ) ) {
402 0 : if( FD_UNLIKELY( txnm->block_engine.bundle_id ) ) ctx->bundle_failed = 1;
403 0 : ctx->metrics.blockhash_expired++;
404 0 : return;
405 0 : }
406 0 : }
407 :
408 0 : int is_bundle_member = !!txnm->block_engine.bundle_id;
409 0 : int is_durable_nonce = fd_resolh_is_durable_nonce( txnt, fd_txn_m_payload( txnm ) );
410 :
411 0 : if( FD_UNLIKELY( !is_bundle_member && !is_durable_nonce && !blockhash ) ) {
412 0 : ulong pool_idx;
413 0 : if( FD_UNLIKELY( !pool_free( ctx->pool ) ) ) {
414 0 : pool_idx = lru_list_idx_pop_tail( ctx->lru_list, ctx->pool );
415 0 : map_chain_idx_remove_fast( ctx->map_chain, pool_idx, ctx->pool );
416 0 : ctx->metrics.stash[ FD_METRICS_ENUM_RESOLVE_STASH_OPERATION_V_OVERRUN_IDX ]++;
417 0 : } else {
418 0 : pool_idx = pool_idx_acquire( ctx->pool );
419 0 : }
420 :
421 0 : fd_stashed_txn_m_t * stash_txn = pool_ele( ctx->pool, pool_idx );
422 : /* There's a compiler bug in GCC version 12 (at least 12.1, 12.3 and
423 : 12.4) that cause it to think stash_txn is a null pointer. It
424 : then complains that the memcpy is bad and refuses to compile the
425 : memcpy below. It is possible for pool_ele to return NULL, but
426 : that can't happen because if pool_free is 0, then all the pool
427 : elements must be in the LRU list, so idx_pop_tail won't return
428 : IDX_NULL; and if pool_free returns non-zero, then
429 : pool_idx_acquire won't return POOL_IDX_NULL. */
430 0 : FD_COMPILER_FORGET( stash_txn );
431 0 : fd_memcpy( stash_txn->_, txnm, fd_txn_m_realized_footprint( txnm, 1, 0 ) );
432 0 : stash_txn->blockhash = (blockhash_t *)(fd_txn_m_payload( (fd_txn_m_t *)(stash_txn->_) ) + txnt->recent_blockhash_off);
433 0 : ctx->metrics.stash[ FD_METRICS_ENUM_RESOLVE_STASH_OPERATION_V_INSERTED_IDX ]++;
434 :
435 0 : map_chain_ele_insert( ctx->map_chain, stash_txn, ctx->pool );
436 0 : lru_list_idx_push_head( ctx->lru_list, pool_idx, ctx->pool );
437 :
438 0 : return;
439 0 : }
440 :
441 0 : if( FD_UNLIKELY( txnt->addr_table_adtl_cnt ) ) {
442 0 : if( FD_UNLIKELY( !ctx->root_bank ) ) {
443 0 : FD_MCNT_INC( RESOLH, NO_BANK_DROP, 1 );
444 0 : if( FD_UNLIKELY( txnm->block_engine.bundle_id ) ) ctx->bundle_failed = 1;
445 0 : return;
446 0 : }
447 :
448 0 : int result = fd_bank_abi_resolve_address_lookup_tables( ctx->root_bank, 0, ctx->root_slot, txnt, fd_txn_m_payload( txnm ), fd_txn_m_alut( txnm ) );
449 : /* result is in [-5, 0]. We want to map -5 to 0, -4 to 1, etc. */
450 0 : ctx->metrics.lut[ (ulong)((long)FD_METRICS_COUNTER_RESOLH_LUT_RESOLVED_CNT+result-1L) ]++;
451 :
452 0 : if( FD_UNLIKELY( result!=FD_BANK_ABI_TXN_INIT_SUCCESS ) ) {
453 0 : if( FD_UNLIKELY( txnm->block_engine.bundle_id ) ) ctx->bundle_failed = 1;
454 0 : return;
455 0 : }
456 0 : }
457 :
458 0 : ulong realized_sz = fd_txn_m_realized_footprint( txnm, 1, 1 );
459 0 : ulong tspub = fd_frag_meta_ts_comp( fd_tickcount() );
460 0 : fd_stem_publish( stem, 0UL, txnm->reference_slot, ctx->out_chunk, realized_sz, 0UL, tsorig, tspub );
461 0 : ctx->out_chunk = fd_dcache_compact_next( ctx->out_chunk, realized_sz, ctx->out_chunk0, ctx->out_wmark );
462 0 : }
463 :
464 : static void
465 : unprivileged_init( fd_topo_t * topo,
466 0 : fd_topo_tile_t * tile ) {
467 0 : void * scratch = fd_topo_obj_laddr( topo, tile->tile_obj_id );
468 :
469 0 : FD_SCRATCH_ALLOC_INIT( l, scratch );
470 0 : fd_resolh_tile_t * ctx = FD_SCRATCH_ALLOC_APPEND( l, alignof( fd_resolh_tile_t ), sizeof( fd_resolh_tile_t ) );
471 :
472 0 : ctx->round_robin_cnt = fd_topo_tile_name_cnt( topo, tile->name );
473 0 : ctx->round_robin_idx = tile->kind_id;
474 :
475 0 : ctx->bundle_failed = 0;
476 0 : ctx->bundle_id = 0UL;
477 :
478 0 : ctx->completed_slot = 0UL;
479 0 : ctx->blockhash_ring_idx = 0UL;
480 :
481 0 : ctx->flush_pool_idx = ULONG_MAX;
482 :
483 0 : ctx->pool = pool_join( pool_new( FD_SCRATCH_ALLOC_APPEND( l, pool_align(), pool_footprint( 1UL<<16UL ) ), 1UL<<16UL ) );
484 0 : FD_TEST( ctx->pool );
485 :
486 0 : ctx->map_chain = map_chain_join( map_chain_new( FD_SCRATCH_ALLOC_APPEND( l, map_chain_align(), map_chain_footprint( 8192ULL ) ), 8192UL , 0UL ) );
487 0 : FD_TEST( ctx->map_chain );
488 :
489 0 : FD_TEST( ctx->lru_list==lru_list_join( lru_list_new( ctx->lru_list ) ) );
490 :
491 0 : if( FD_LIKELY( !tile->kind_id ) ) _fd_ext_resolh_tile_cnt = ctx->round_robin_cnt;
492 :
493 0 : ctx->root_bank = NULL;
494 :
495 0 : memset( ctx->blockhash_ring, 0, sizeof( ctx->blockhash_ring ) );
496 0 : memset( &ctx->metrics, 0, sizeof( ctx->metrics ) );
497 :
498 0 : ctx->blockhash_map = map_join( map_new( FD_SCRATCH_ALLOC_APPEND( l, map_align(), map_footprint() ) ) );
499 0 : FD_TEST( ctx->blockhash_map );
500 :
501 0 : FD_TEST( tile->in_cnt<=sizeof( ctx->in )/sizeof( ctx->in[ 0 ] ) );
502 0 : for( ulong i=0UL; i<tile->in_cnt; i++ ) {
503 0 : fd_topo_link_t * link = &topo->links[ tile->in_link_id[ i ] ];
504 0 : fd_topo_wksp_t * link_wksp = &topo->workspaces[ topo->objs[ link->dcache_obj_id ].wksp_id ];
505 :
506 0 : if( FD_LIKELY( !strcmp( link->name, "replay_resol" ) ) ) ctx->in[i].kind = FD_RESOLH_IN_KIND_BANK;
507 0 : else ctx->in[i].kind = FD_RESOLH_IN_KIND_FRAGMENT;
508 :
509 0 : ctx->in[i].mem = link_wksp->wksp;
510 0 : ctx->in[i].chunk0 = fd_dcache_compact_chunk0( ctx->in[i].mem, link->dcache );
511 0 : ctx->in[i].wmark = fd_dcache_compact_wmark ( ctx->in[i].mem, link->dcache, link->mtu );
512 0 : ctx->in[i].mtu = link->mtu;
513 0 : }
514 :
515 0 : ctx->out_mem = topo->workspaces[ topo->objs[ topo->links[ tile->out_link_id[ 0 ] ].dcache_obj_id ].wksp_id ].wksp;
516 0 : ctx->out_chunk0 = fd_dcache_compact_chunk0( ctx->out_mem, topo->links[ tile->out_link_id[ 0 ] ].dcache );
517 0 : ctx->out_wmark = fd_dcache_compact_wmark ( ctx->out_mem, topo->links[ tile->out_link_id[ 0 ] ].dcache, topo->links[ tile->out_link_id[ 0 ] ].mtu );
518 0 : ctx->out_chunk = ctx->out_chunk0;
519 :
520 0 : ulong scratch_top = FD_SCRATCH_ALLOC_FINI( l, 1UL );
521 0 : if( FD_UNLIKELY( scratch_top > (ulong)scratch + scratch_footprint( tile ) ) )
522 0 : FD_LOG_ERR(( "scratch overflow %lu %lu %lu", scratch_top - (ulong)scratch - scratch_footprint( tile ), scratch_top, (ulong)scratch + scratch_footprint( tile ) ));
523 0 : }
524 :
525 0 : #define STEM_BURST (1UL)
526 :
527 0 : #define STEM_CALLBACK_CONTEXT_TYPE fd_resolh_tile_t
528 0 : #define STEM_CALLBACK_CONTEXT_ALIGN alignof(fd_resolh_tile_t)
529 :
530 0 : #define STEM_CALLBACK_METRICS_WRITE metrics_write
531 0 : #define STEM_CALLBACK_AFTER_CREDIT after_credit
532 0 : #define STEM_CALLBACK_BEFORE_FRAG before_frag
533 0 : #define STEM_CALLBACK_DURING_FRAG during_frag
534 0 : #define STEM_CALLBACK_AFTER_FRAG after_frag
535 :
536 : #include "../../disco/stem/fd_stem.c"
537 :
538 : fd_topo_run_tile_t fd_tile_resolh = {
539 : .name = "resolh",
540 : .populate_allowed_seccomp = NULL,
541 : .populate_allowed_fds = NULL,
542 : .scratch_align = scratch_align,
543 : .scratch_footprint = scratch_footprint,
544 : .unprivileged_init = unprivileged_init,
545 : .run = stem_run,
546 : };
|