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