Line data Source code
1 : #include "fd_execle_err.h"
2 :
3 : #include "../../disco/tiles.h"
4 : #include "../../disco/pack/fd_pack.h"
5 : #include "../../disco/pack/fd_pack_cost.h"
6 : #include "../../ballet/blake3/fd_blake3.h"
7 : #include "../../ballet/bmtree/fd_bmtree.h"
8 : #include "../../disco/metrics/fd_metrics.h"
9 : #include "../../disco/events/generated/fd_event_gen.h"
10 : #include "../../util/pod/fd_pod_format.h"
11 : #include "../../disco/pack/fd_pack_rebate_sum.h"
12 : #include "../../disco/metrics/generated/fd_metrics_enums.h"
13 : #include "../../discof/fd_startup.h"
14 : #include "../../flamenco/runtime/fd_runtime.h"
15 : #include "../../flamenco/runtime/fd_bank.h"
16 : #include "../../flamenco/progcache/fd_progcache_user.h"
17 : #include "../../flamenco/log_collector/fd_log_collector_base.h"
18 : #include <time.h>
19 : #include <linux/futex.h>
20 : #include "generated/fd_execle_tile_seccomp.h"
21 :
22 0 : #define REBATE_BATCH_IDLE_LOOPS (128UL)
23 0 : #define REBATE_BATCH_MAX_MICROBLOCKS (16UL)
24 :
25 : FD_STATIC_ASSERT( REBATE_BATCH_MAX_MICROBLOCKS*FD_PACK_REBATE_MAX_ENTRIES<=FD_PACK_REBATE_SUM_CAPACITY,
26 : rebate_batch_fits_rebater );
27 :
28 : struct fd_execle_out {
29 : ulong idx;
30 : fd_wksp_t * mem;
31 : ulong chunk0;
32 : ulong wmark;
33 : ulong chunk;
34 : };
35 :
36 : typedef struct fd_execle_out fd_execle_out_t;
37 :
38 : struct fd_execle_tile {
39 : ulong kind_id;
40 :
41 : fd_startup_gate_t startup_gate[1];
42 :
43 : fd_blake3_t * blake3;
44 : void * bmtree;
45 :
46 : ulong _bank_idx;
47 : ulong _pack_idx;
48 : ulong _txn_idx;
49 : int _is_bundle;
50 : fd_acct_addr_t _alt_accts[MAX_TXN_PER_MICROBLOCK][FD_TXN_ACCT_ADDR_MAX];
51 :
52 : ulong * busy_fseq;
53 :
54 : fd_wksp_t * pack_in_mem;
55 : ulong pack_in_chunk0;
56 : ulong pack_in_wmark;
57 :
58 : fd_execle_out_t out_poh[1];
59 : fd_execle_out_t out_pack[1];
60 :
61 : ulong rebates_for_slot;
62 : ulong rebate_tsorig;
63 : ulong rebate_microblock_cnt;
64 : ulong rebate_idle_loop_cnt;
65 : int rebate_draining;
66 : int enable_rebates;
67 : ulong rebate_seed;
68 : fd_pack_rebate_sum_t rebater[ 1 ];
69 :
70 : fd_banks_t * banks;
71 : fd_accdb_t * accdb;
72 :
73 : fd_progcache_t progcache[1];
74 :
75 : fd_runtime_t runtime[1];
76 :
77 : /* For bundle execution, we need to execute each transaction against
78 : a separate transaction context and a set of accounts, but the exec
79 : stack can be reused. We will also use these same memory regions
80 : for non-bundle execution. */
81 : fd_txn_in_t txn_in[ FD_PACK_MAX_TXN_PER_BUNDLE ];
82 : fd_txn_out_t txn_out[ FD_PACK_MAX_TXN_PER_BUNDLE ];
83 :
84 : fd_log_collector_t log_collector[ 1 ];
85 :
86 : float ns_per_tick;
87 :
88 : struct {
89 : /* Ticks spent loading txn accounts */
90 : ulong txn_load_cum_ticks;
91 :
92 : /* Ticks spent validating txn invariants (e.g. status cache, fee payer) */
93 : ulong txn_check_cum_ticks;
94 :
95 : /* Ticks spent executing a txn (includes any VM time) */
96 : ulong txn_exec_cum_ticks;
97 :
98 : /* Ticks spent committing a txn (database writes) */
99 : ulong txn_commit_cum_ticks;
100 : } metrics;
101 :
102 : };
103 :
104 : typedef struct fd_execle_tile fd_execle_tile_t;
105 :
106 : FD_FN_CONST static inline ulong
107 0 : scratch_align( void ) {
108 0 : return 128UL;
109 0 : }
110 :
111 : FD_FN_PURE static inline ulong
112 0 : scratch_footprint( fd_topo_tile_t const * tile ) {
113 0 : ulong l = FD_LAYOUT_INIT;
114 0 : l = FD_LAYOUT_APPEND( l, alignof( fd_execle_tile_t ), sizeof( fd_execle_tile_t ) );
115 0 : l = FD_LAYOUT_APPEND( l, FD_BLAKE3_ALIGN, FD_BLAKE3_FOOTPRINT );
116 0 : l = FD_LAYOUT_APPEND( l, FD_BMTREE_COMMIT_ALIGN, FD_BMTREE_COMMIT_FOOTPRINT(0) );
117 0 : l = FD_LAYOUT_APPEND( l, fd_txncache_align(), fd_txncache_footprint( tile->execle.max_live_slots ) );
118 0 : l = FD_LAYOUT_APPEND( l, fd_accdb_align(), fd_accdb_footprint( tile->execle.max_live_slots, 0 ) );
119 0 : l = FD_LAYOUT_APPEND( l, FD_PROGCACHE_SCRATCH_ALIGN, FD_PROGCACHE_SCRATCH_FOOTPRINT );
120 0 : return FD_LAYOUT_FINI( l, scratch_align() );
121 0 : }
122 :
123 : static inline void
124 0 : metrics_write( fd_execle_tile_t * ctx ) {
125 0 : fd_accdb_flush_metrics( ctx->accdb );
126 :
127 0 : FD_MCNT_SET( EXECLE, INSTRUCTION_EXECUTED, ctx->runtime->metrics.instr_cum );
128 0 : FD_MCNT_SET( EXECLE, CPI_EXECUTED, ctx->runtime->metrics.cpi_cum );
129 0 : FD_MCNT_SET( EXECLE, ACCOUNT_LTHASH_SKIPPED, ctx->runtime->metrics.lthash_unchanged_cnt );
130 :
131 0 : FD_MCNT_SET( EXECLE, CU_CONSUMED, ctx->runtime->metrics.cu_cum );
132 :
133 0 : FD_MCNT_SET( EXECLE, TXN_REGIME_DURATION_NANOS_LOAD, ctx->metrics.txn_check_cum_ticks+ctx->metrics.txn_load_cum_ticks );
134 0 : FD_MCNT_SET( EXECLE, TXN_REGIME_DURATION_NANOS_EXECUTE, ctx->metrics.txn_exec_cum_ticks );
135 0 : FD_MCNT_SET( EXECLE, TXN_REGIME_DURATION_NANOS_COMMIT, ctx->metrics.txn_commit_cum_ticks );
136 :
137 0 : fd_runtime_t const * runtime = ctx->runtime;
138 0 : ulong cpi_ticks = runtime->metrics.cpi_setup_cum_ticks + runtime->metrics.cpi_commit_cum_ticks;
139 0 : ulong exec_ticks = fd_ulong_sat_sub( runtime->metrics.vm_exec_cum_ticks, cpi_ticks );
140 0 : FD_MCNT_SET( EXECLE, VM_REGIME_DURATION_NANOS_SETUP, runtime->metrics.vm_setup_cum_ticks );
141 0 : FD_MCNT_SET( EXECLE, VM_REGIME_DURATION_NANOS_COMMIT, runtime->metrics.vm_commit_cum_ticks );
142 0 : FD_MCNT_SET( EXECLE, VM_REGIME_DURATION_NANOS_SETUP_CPI, runtime->metrics.cpi_setup_cum_ticks );
143 0 : FD_MCNT_SET( EXECLE, VM_REGIME_DURATION_NANOS_COMMIT_CPI, runtime->metrics.cpi_commit_cum_ticks );
144 0 : FD_MCNT_SET( EXECLE, VM_REGIME_DURATION_NANOS_INTERPRETER, exec_ticks );
145 :
146 0 : FD_PROGCACHE_METRICS_WRITE( EXECLE, ctx->progcache->metrics );
147 :
148 0 : FD_ACCDB_METRICS_WRITE( EXECLE, fd_accdb_metrics( ctx->accdb ) );
149 0 : }
150 :
151 : static inline int
152 0 : prevent_park( fd_execle_tile_t * ctx ) {
153 : /* A deferred rebate batch counts loops, not time, never park on it */
154 0 : return ctx->enable_rebates && !!ctx->rebate_microblock_cnt;
155 0 : }
156 :
157 : static inline void
158 : after_credit( fd_execle_tile_t * ctx,
159 : fd_stem_context_t * stem,
160 : int * opt_poll_in,
161 0 : int * charge_busy ) {
162 0 : fd_startup_gate_idle( ctx->startup_gate );
163 :
164 0 : if( FD_UNLIKELY( !ctx->enable_rebates || !ctx->rebate_microblock_cnt ) ) return;
165 :
166 0 : if( FD_LIKELY( !ctx->rebate_draining ) ) {
167 0 : if( FD_LIKELY( ctx->rebate_microblock_cnt<REBATE_BATCH_MAX_MICROBLOCKS &&
168 0 : ctx->rebate_idle_loop_cnt<REBATE_BATCH_IDLE_LOOPS &&
169 0 : !ctx->rebater->ib_result ) ) {
170 0 : ctx->rebate_idle_loop_cnt++;
171 0 : return;
172 0 : }
173 0 : ctx->rebate_draining = 1;
174 0 : }
175 :
176 0 : ulong written_sz = fd_pack_rebate_sum_report( ctx->rebater, fd_chunk_to_laddr( ctx->out_pack->mem, ctx->out_pack->chunk ) );
177 0 : if( FD_UNLIKELY( written_sz ) ) {
178 0 : ulong tspub = (ulong)fd_frag_meta_ts_comp( fd_tickcount() );
179 0 : fd_stem_publish( stem, ctx->out_pack->idx, ctx->rebates_for_slot, ctx->out_pack->chunk, written_sz, 0UL, ctx->rebate_tsorig, tspub );
180 0 : ctx->out_pack->chunk = fd_dcache_compact_next( ctx->out_pack->chunk, written_sz, ctx->out_pack->chunk0, ctx->out_pack->wmark );
181 0 : *opt_poll_in = 0;
182 0 : *charge_busy = 1;
183 0 : } else {
184 0 : ctx->rebate_microblock_cnt = 0UL;
185 0 : ctx->rebate_idle_loop_cnt = 0UL;
186 0 : ctx->rebate_draining = 0;
187 0 : }
188 0 : }
189 :
190 : static int
191 : before_frag( fd_execle_tile_t * ctx,
192 : ulong in_idx,
193 : ulong seq,
194 0 : ulong sig ) {
195 0 : (void)in_idx; (void)seq;
196 :
197 0 : fd_startup_gate_busy( ctx->startup_gate );
198 :
199 : /* Pack also outputs "leader slot done" which we can ignore. */
200 0 : if( FD_UNLIKELY( fd_disco_poh_sig_pkt_type( sig )!=POH_PKT_TYPE_MICROBLOCK ) ) return 1;
201 :
202 0 : ulong target_execle_kind_id = fd_disco_poh_sig_execle_tile( sig );
203 0 : if( FD_UNLIKELY( target_execle_kind_id!=ctx->kind_id ) ) return 1;
204 :
205 0 : return 0;
206 0 : }
207 :
208 : static inline void
209 : during_frag( fd_execle_tile_t * ctx,
210 : ulong in_idx,
211 : ulong seq,
212 : ulong sig,
213 : ulong chunk,
214 : ulong sz,
215 0 : ulong ctl ) {
216 0 : (void)in_idx; (void)seq; (void)sig; (void)ctl;
217 :
218 0 : uchar * src = (uchar *)fd_chunk_to_laddr( ctx->pack_in_mem, chunk );
219 0 : uchar * dst = (uchar *)fd_chunk_to_laddr( ctx->out_poh->mem, ctx->out_poh->chunk );
220 :
221 0 : if( FD_UNLIKELY( chunk<ctx->pack_in_chunk0 || chunk>ctx->pack_in_wmark || sz>USHORT_MAX ) )
222 0 : FD_LOG_ERR(( "chunk %lu %lu corrupt, not in range [%lu,%lu]", chunk, sz, ctx->pack_in_chunk0, ctx->pack_in_wmark ));
223 :
224 : /* Pack sends fd_txn_e_t (with ALT accounts), but PoH expects fd_txn_p_t.
225 : We copy the fd_txn_p_t portion to the PoH output buffer, and copy the
226 : ALT accounts to the tile context for rebates. */
227 0 : ulong txn_cnt = (sz-sizeof(fd_microblock_execle_trailer_t))/sizeof(fd_txn_e_t);
228 0 : fd_txn_e_t const * src_txn_e = (fd_txn_e_t const *)src;
229 0 : fd_txn_p_t * dst_txn_p = (fd_txn_p_t *)dst;
230 0 : for( ulong i=0UL; i<txn_cnt; i++ ) {
231 0 : fd_txn_p_t const * s = src_txn_e[i].txnp;
232 0 : fd_txn_p_t * d = dst_txn_p + i;
233 0 : fd_txn_t const * t = TXN( s );
234 0 : ulong payload_sz = fd_ulong_min( s->payload_sz, FD_TPU_MTU );
235 0 : ulong txn_sz = fd_ulong_min( fd_txn_footprint( t->instr_cnt, t->addr_table_lookup_cnt ), FD_TXN_MAX_SZ );
236 0 : fd_memcpy( d->payload, s->payload, payload_sz );
237 0 : fd_memcpy( &d->payload_sz, &s->payload_sz, offsetof(fd_txn_p_t, _)-offsetof(fd_txn_p_t, payload_sz) );
238 0 : fd_memcpy( d->_, s->_, txn_sz );
239 0 : ulong alt_cnt = fd_ulong_min( (ulong)t->addr_table_adtl_cnt, FD_TXN_ACCT_ADDR_MAX );
240 0 : fd_memcpy( ctx->_alt_accts[i], src_txn_e[i].alt_accts, alt_cnt * sizeof(fd_acct_addr_t) );
241 0 : }
242 :
243 0 : fd_microblock_execle_trailer_t * trailer = (fd_microblock_execle_trailer_t *)( src+sz-sizeof(fd_microblock_execle_trailer_t) );
244 0 : ctx->_bank_idx = trailer->bank_idx;
245 0 : ctx->_pack_idx = trailer->pack_idx;
246 0 : ctx->_txn_idx = trailer->pack_txn_idx;
247 0 : ctx->_is_bundle = trailer->is_bundle;
248 0 : }
249 :
250 : static void
251 : hash_transactions( void * mem,
252 : fd_txn_p_t * txns,
253 : ulong txn_cnt,
254 0 : uchar * mixin ) {
255 0 : fd_bmtree_commit_t * bmtree = fd_bmtree_commit_init( mem, 32UL, 1UL, 0UL );
256 0 : for( ulong i=0UL; i<txn_cnt; i++ ) {
257 0 : fd_txn_p_t * _txn = txns + i;
258 0 : if( FD_UNLIKELY( !(_txn->flags & FD_TXN_P_FLAGS_EXECUTE_SUCCESS) ) ) continue;
259 :
260 0 : fd_txn_t * txn = TXN(_txn);
261 0 : for( ulong j=0; j<txn->signature_cnt; j++ ) {
262 0 : fd_bmtree_node_t node[1];
263 0 : fd_bmtree_hash_leaf( node, _txn->payload+txn->signature_off+64UL*j, 64UL, 1UL );
264 0 : fd_bmtree_commit_append( bmtree, node, 1UL );
265 0 : }
266 0 : }
267 0 : if( FD_LIKELY( fd_bmtree_commit_leaf_cnt( bmtree ) ) ) {
268 0 : uchar * root = fd_bmtree_commit_fini( bmtree );
269 0 : fd_memcpy( mixin, root, 32UL );
270 0 : } else {
271 0 : fd_memset( mixin, 0, 32UL );
272 : /* If FD_HAS_MSAN, poison so we can detect if this is ever accessed upstream, even though it should never be. In production, this is a no-op. */
273 0 : fd_msan_poison( mixin, 32UL );
274 0 : }
275 0 : }
276 :
277 : static inline void
278 : handle_microblock( fd_execle_tile_t * ctx,
279 : ulong seq,
280 : ulong sig,
281 : ulong sz,
282 : ulong begin_tspub,
283 0 : fd_stem_context_t * stem ) {
284 0 : uchar * dst = (uchar *)fd_chunk_to_laddr( ctx->out_poh->mem, ctx->out_poh->chunk );
285 :
286 0 : ulong slot = fd_disco_poh_sig_slot( sig );
287 0 : ulong txn_cnt = (sz-sizeof(fd_microblock_execle_trailer_t))/sizeof(fd_txn_e_t);
288 :
289 0 : fd_bank_t * bank = fd_banks_bank_query( ctx->banks, ctx->_bank_idx );
290 0 : FD_TEST( bank );
291 0 : ulong bank_slot = bank->f.slot;
292 0 : FD_TEST( bank_slot==slot );
293 :
294 0 : fd_microblock_trailer_t * trailer = (fd_microblock_trailer_t *)( dst + txn_cnt*sizeof(fd_txn_p_t) );
295 0 : trailer->txn_ns_dt = (fd_txn_ns_dt_t){0};
296 0 : trailer->bank_seq = bank->bank_seq;
297 0 : trailer->exec_end_ticks = LONG_MAX;
298 :
299 : /* exec_start_ticks is the first transaction's load_start_ticks (an
300 : empty microblock reads the clock itself); microblock_start_ticks
301 : stays pack's publish time, decompressed against it. */
302 0 : long exec_start_ticks = txn_cnt ? 0L : fd_tickcount();
303 0 : long microblock_start_ticks = txn_cnt ? 0L : fd_frag_meta_ts_decomp( begin_tspub, exec_start_ticks );
304 0 : long last_ticks = exec_start_ticks;
305 :
306 0 : for( ulong i=0UL; i<txn_cnt; i++ ) {
307 0 : fd_txn_p_t * txn = (fd_txn_p_t *)( dst + (i*sizeof(fd_txn_p_t)) );
308 0 : fd_txn_in_t * txn_in = &ctx->txn_in[ 0 ];
309 0 : fd_txn_out_t * txn_out = &ctx->txn_out[ 0 ];
310 :
311 0 : uint const requested_exec_plus_acct_data_cus = txn->pack_cu.requested_exec_plus_acct_data_cus;
312 0 : uint const non_execution_cus = txn->pack_cu.non_execution_cus;
313 :
314 : /* Assume failure, set below if success. If it doesn't land in the
315 : block, rebate the non-execution CUs too. */
316 0 : txn->execle_cu.actual_consumed_cus = 0U;
317 0 : txn->execle_cu.rebated_cus = requested_exec_plus_acct_data_cus + non_execution_cus;
318 0 : txn->flags &= ~FD_TXN_P_FLAGS_SANITIZE_SUCCESS;
319 0 : txn->flags &= ~FD_TXN_P_FLAGS_EXECUTE_SUCCESS;
320 :
321 0 : txn_in->bundle.is_bundle = 0;
322 0 : txn_in->txn = txn;
323 0 : txn_in->index_in_slot = ctx->_txn_idx + i;
324 :
325 0 : fd_runtime_prepare_and_execute_txn( ctx->runtime, bank, txn_in, txn_out );
326 :
327 0 : if( FD_UNLIKELY( !i ) ) {
328 0 : exec_start_ticks = txn_out->details.load_start_ticks;
329 0 : microblock_start_ticks = fd_frag_meta_ts_decomp( begin_tspub, exec_start_ticks );
330 0 : last_ticks = exec_start_ticks;
331 0 : }
332 :
333 : /* Stash the result in the flags value so that pack can inspect it. */
334 0 : txn->flags = (txn->flags & 0x00FFFFFFU) | ((uint)(-txn_out->err.txn_err)<<24);
335 :
336 : /* Drop no-op transactions, we don't want to include these as they
337 : charge no fees. */
338 0 : if( FD_UNLIKELY( txn_out->err.is_noop ) ) {
339 0 : txn_out->err.is_committable = 0;
340 0 : }
341 :
342 0 : fd_metrics_tl[ MIDX( COUNTER, EXECLE, TXN_PROCESSED_BY_VERSION )+fd_execle_version_from_txn( TXN( txn ) ) ]++;
343 :
344 0 : if( FD_UNLIKELY( !txn_out->err.is_committable ) ) {
345 0 : FD_TEST( !txn_out->err.is_fees_only );
346 0 : fd_runtime_cancel_txn( ctx->runtime, bank, txn_in, txn_out );
347 : /* Use pre-resolved ALT accounts for rebates even for unlanded transactions */
348 0 : fd_acct_addr_t const * writable_alt = ctx->_alt_accts[i];
349 0 : if( FD_LIKELY( ctx->enable_rebates ) ) fd_pack_rebate_sum_add_txn( ctx->rebater, txn, &writable_alt, 1UL );
350 0 : fd_metrics_tl[ MIDX( COUNTER, EXECLE, TXN_PROCESSED_BY_LANDING )+FD_METRICS_ENUM_TXN_LANDING_RESULT_V_DROPPED_IDX ]++;
351 0 : fd_metrics_tl[ MIDX( COUNTER, EXECLE, TXN_PROCESSED )+(ulong)fd_execle_err_from_runtime_err( txn_out->err.txn_err ) ]++;
352 0 : last_ticks = fd_tickcount();
353 0 : continue;
354 0 : }
355 :
356 : /* Check for FeesOnly transactions where actual CUs exceed
357 : requested. This can happen for durable nonce transactions
358 : with oversized nonce accounts. Skip the commit to prevent
359 : underflow in the rebate computation. The transaction keeps its
360 : default full rebate set at the top of the loop. */
361 0 : if( FD_UNLIKELY( txn_out->err.is_fees_only ) ) {
362 0 : uint fee_only_actual_exec_cus = (uint)(txn_out->details.compute_budget.compute_unit_limit - txn_out->details.compute_budget.compute_meter);
363 0 : uint fee_only_actual_data_cus = (uint)(txn_out->details.txn_cost.transaction.loaded_accounts_data_size_cost);
364 0 : if( FD_UNLIKELY( fee_only_actual_exec_cus + fee_only_actual_data_cus > requested_exec_plus_acct_data_cus ) ) {
365 0 : FD_LOG_WARNING(( "FeesOnly txn actual CUs (%u+%u) exceed requested (%u), dropping",
366 0 : fee_only_actual_exec_cus, fee_only_actual_data_cus, requested_exec_plus_acct_data_cus ));
367 0 : txn_out->err.is_committable = 0;
368 0 : fd_runtime_cancel_txn( ctx->runtime, bank, txn_in, txn_out );
369 : /* txn->execle_cu already initialized to full rebate at top of loop */
370 0 : fd_acct_addr_t const * writable_alt = ctx->_alt_accts[i];
371 0 : if( FD_LIKELY( ctx->enable_rebates ) ) fd_pack_rebate_sum_add_txn( ctx->rebater, txn, &writable_alt, 1UL );
372 0 : fd_metrics_tl[ MIDX( COUNTER, EXECLE, TXN_PROCESSED_BY_LANDING )+FD_METRICS_ENUM_TXN_LANDING_RESULT_V_DROPPED_IDX ]++;
373 0 : fd_metrics_tl[ MIDX( COUNTER, EXECLE, TXN_PROCESSED )+(ulong)fd_execle_err_from_runtime_err( txn_out->err.txn_err ) ]++;
374 : /* FD_TXN_P_FLAGS_EXECUTE_SUCCESS = 0 ensures txn won't be
375 : mixed-in by POH */
376 0 : last_ticks = fd_tickcount();
377 0 : continue;
378 0 : }
379 0 : }
380 :
381 0 : if( FD_UNLIKELY( txn_out->err.is_fees_only ) ) fd_metrics_tl[ MIDX( COUNTER, EXECLE, TXN_PROCESSED_BY_LANDING )+FD_METRICS_ENUM_TXN_LANDING_RESULT_V_FEES_ONLY_IDX ]++;
382 0 : else if( FD_UNLIKELY( txn_out->err.txn_err ) ) fd_metrics_tl[ MIDX( COUNTER, EXECLE, TXN_PROCESSED_BY_LANDING )+FD_METRICS_ENUM_TXN_LANDING_RESULT_V_FAILED_IDX ]++;
383 0 : else fd_metrics_tl[ MIDX( COUNTER, EXECLE, TXN_PROCESSED_BY_LANDING )+FD_METRICS_ENUM_TXN_LANDING_RESULT_V_SUCCESS_IDX ]++;
384 :
385 : /* TXN_P_FLAGS_EXECUTE_SUCCESS means that it should be included in
386 : the block. It's a bit of a misnomer now that there are fee-only
387 : transactions. */
388 0 : txn->flags |= FD_TXN_P_FLAGS_EXECUTE_SUCCESS | FD_TXN_P_FLAGS_SANITIZE_SUCCESS;
389 0 : fd_metrics_tl[ MIDX( COUNTER, EXECLE, TXN_PROCESSED )+(ulong)fd_execle_err_from_runtime_err( txn_out->err.txn_err ) ]++;
390 :
391 : /* Commit must succeed so no failure path. Once commit is called,
392 : the transactions MUST be mixed into the PoH otherwise we will
393 : fork and diverge, so the link from here til PoH mixin must be
394 : completely reliable with nothing dropped.
395 :
396 : fd_runtime_commit_txn checks if the transaction fits into the
397 : block with the cost tracker. If it doesn't fit, flags is set to
398 : zero. A key invariant of the leader pipeline is that pack
399 : ensures all transactions must fit already, so it is a fatal error
400 : if that happens. We cannot reject the transaction here as there
401 : would be no way to undo the partially applied changes to the bank
402 : in finalize anyway. */
403 0 : fd_runtime_commit_txn( ctx->runtime, bank, txn_in, txn_out );
404 :
405 0 : long const txn_end_ticks = fd_tickcount();
406 0 : last_ticks = txn_end_ticks;
407 :
408 0 : ulong const load_ticks_dt = fd_ulong_if( txn_out->details.check_start_ticks==LONG_MAX || txn_out->details.load_start_ticks==LONG_MAX, 0UL, (ulong)( txn_out->details.check_start_ticks - txn_out->details.load_start_ticks ) );
409 0 : ulong const check_ticks_dt = fd_ulong_if( txn_out->details.exec_start_ticks==LONG_MAX || txn_out->details.check_start_ticks==LONG_MAX, 0UL, (ulong)( txn_out->details.exec_start_ticks - txn_out->details.check_start_ticks ) );
410 0 : ulong const exec_ticks_dt = fd_ulong_if( txn_out->details.commit_start_ticks==LONG_MAX || txn_out->details.exec_start_ticks==LONG_MAX, 0UL, (ulong)( txn_out->details.commit_start_ticks - txn_out->details.exec_start_ticks ) );
411 0 : ulong const commit_ticks_dt = fd_ulong_if( txn_end_ticks==LONG_MAX || txn_out->details.commit_start_ticks==LONG_MAX, 0UL, (ulong)( txn_end_ticks - txn_out->details.commit_start_ticks ) );
412 :
413 0 : ctx->metrics.txn_load_cum_ticks += load_ticks_dt;
414 0 : ctx->metrics.txn_check_cum_ticks += check_ticks_dt;
415 0 : ctx->metrics.txn_exec_cum_ticks += exec_ticks_dt;
416 0 : ctx->metrics.txn_commit_cum_ticks += commit_ticks_dt;
417 :
418 0 : trailer->txn_ns_dt.load_start = fd_float_if( txn_out->details.load_start_ticks==LONG_MAX, 0., (float)fd_long_max( 0L, txn_out->details.load_start_ticks - microblock_start_ticks ) * ctx->ns_per_tick );
419 0 : trailer->txn_ns_dt.check_start = fd_float_if( txn_out->details.check_start_ticks==LONG_MAX, trailer->txn_ns_dt.load_start, (float)fd_long_max( 0L, txn_out->details.check_start_ticks - microblock_start_ticks ) * ctx->ns_per_tick );
420 0 : trailer->txn_ns_dt.exec_start = fd_float_if( txn_out->details.exec_start_ticks==LONG_MAX, trailer->txn_ns_dt.check_start, (float)fd_long_max( 0L, txn_out->details.exec_start_ticks - microblock_start_ticks ) * ctx->ns_per_tick );
421 0 : trailer->txn_ns_dt.commit_start = fd_float_if( txn_out->details.commit_start_ticks==LONG_MAX, trailer->txn_ns_dt.exec_start, (float)fd_long_max( 0L, txn_out->details.commit_start_ticks - microblock_start_ticks ) * ctx->ns_per_tick );
422 0 : trailer->txn_ns_dt.commit_end = fd_float_if( txn_end_ticks==LONG_MAX, trailer->txn_ns_dt.commit_start, (float)fd_long_max( 0L, txn_end_ticks - microblock_start_ticks ) * ctx->ns_per_tick );
423 0 : trailer->exec_end_ticks = txn_end_ticks;
424 :
425 0 : if( FD_UNLIKELY( !txn_out->err.is_committable ) ) {
426 : /* If the transaction failed to fit into the block, we need to
427 : updated the transaction flag with the error code. */
428 0 : txn->flags = (txn->flags & 0x00FFFFFFU) | ((uint)(-txn_out->err.txn_err)<<24);
429 0 : fd_cost_tracker_t * cost_tracker = fd_cost_tracker_store_peek( fd_bank_cost_tracker( bank ), bank->cost_tracker_fork_id );
430 0 : uchar * signature = (uchar *)txn_in->txn->payload + TXN( txn_in->txn )->signature_off;
431 0 : int err = fd_cost_tracker_try_add_cost( cost_tracker, txn_out );
432 0 : FD_LOG_HEXDUMP_WARNING(( "txn", txn->payload, txn->payload_sz ));
433 0 : FD_BASE58_ENCODE_64_BYTES( signature, signature_b58 );
434 0 : FD_LOG_CRIT(( "transaction %s failed to fit into block despite pack guaranteeing it would "
435 0 : "(res=%d) [block_cost=%lu, allocated_accounts_data_size=%lu, "
436 0 : "block_cost_limit=%lu, account_cost_limit=%lu]",
437 0 : signature_b58, err, cost_tracker->block_cost,
438 0 : cost_tracker->allocated_accounts_data_size,
439 0 : cost_tracker->block_cost_limit,
440 0 : cost_tracker->account_cost_limit ));
441 0 : }
442 :
443 0 : uint actual_execution_cus = (uint)(txn_out->details.compute_budget.compute_unit_limit - txn_out->details.compute_budget.compute_meter);
444 0 : uint actual_acct_data_cus = (uint)(txn_out->details.txn_cost.transaction.loaded_accounts_data_size_cost);
445 :
446 : /* The VM will stop executing and fail an instruction immediately if
447 : it exceeds its requested CUs. A transaction which requests less
448 : account data than it actually consumes will fail in the account
449 : loading stage. */
450 0 : if( FD_UNLIKELY( actual_execution_cus + actual_acct_data_cus > requested_exec_plus_acct_data_cus ) ) {
451 0 : uchar * _signature = (uchar *)txn->payload + TXN( txn )->signature_off;
452 0 : FD_BASE58_ENCODE_64_BYTES( _signature, _signature_b58 );
453 0 : fd_cost_tracker_t const * _ct = fd_cost_tracker_store_peek( fd_bank_cost_tracker( bank ), bank->cost_tracker_fork_id );
454 0 : FD_LOG_HEXDUMP_WARNING(( "txn", txn->payload, txn->payload_sz ));
455 0 : FD_LOG_ERR(( "transaction %s actual CUs (%u+%u) exceeded requested (%u) despite pack guaranteeing it would fit "
456 0 : "[is_simple_vote=%i, is_fees_only=%i, block_cost=%lu, block_cost_limit=%lu, account_cost_limit=%lu]",
457 0 : _signature_b58, actual_execution_cus, actual_acct_data_cus, requested_exec_plus_acct_data_cus,
458 0 : fd_txn_is_simple_vote_transaction( TXN(txn), txn->payload ),
459 0 : txn_out->err.is_fees_only, _ct->block_cost, _ct->block_cost_limit, _ct->account_cost_limit ));
460 0 : }
461 :
462 0 : txn->execle_cu.rebated_cus = requested_exec_plus_acct_data_cus - (actual_execution_cus + actual_acct_data_cus);
463 0 : txn->execle_cu.actual_consumed_cus = non_execution_cus + actual_execution_cus + actual_acct_data_cus;
464 :
465 : /* Use ALT accounts copied in during_frag for rebates.
466 : These were resolved by resolv_tile and are needed because the LUT
467 : may be deactivated by the time we get here. */
468 0 : fd_acct_addr_t const * writable_alt = ctx->_alt_accts[i];
469 0 : if( FD_LIKELY( ctx->enable_rebates ) ) fd_pack_rebate_sum_add_txn( ctx->rebater, txn, &writable_alt, 1UL );
470 0 : }
471 :
472 : /* Indicate to pack tile we are done processing the transactions so
473 : it can pack new microblocks using these accounts. */
474 0 : fd_fseq_update( ctx->busy_fseq, seq );
475 :
476 : /* Now produce the merkle hash of the transactions for inclusion
477 : (mixin) to the PoH hash. This is done on the execle tile because
478 : it shards / scales horizontally here, while PoH does not. */
479 0 : hash_transactions( ctx->bmtree, (fd_txn_p_t*)dst, txn_cnt, trailer->hash );
480 0 : trailer->pack_txn_idx = ctx->_txn_idx;
481 0 : trailer->tips = ctx->txn_out[ 0 ].details.tips;
482 0 : trailer->exec_start_ticks = exec_start_ticks;
483 :
484 : /* When sending MAX_TXN_PER_MICROBLOCK transactions as fd_txn_p_t to PoH,
485 : there's always extra bytes at the end to stash the trailer. */
486 0 : FD_STATIC_ASSERT( MAX_MICROBLOCK_SZ-(MAX_TXN_PER_MICROBLOCK*sizeof(fd_txn_p_t))>=sizeof(fd_microblock_trailer_t), poh_shred_mtu );
487 0 : FD_STATIC_ASSERT( MAX_MICROBLOCK_SZ-(MAX_TXN_PER_MICROBLOCK*sizeof(fd_txn_p_t))>=sizeof(fd_microblock_execle_trailer_t), poh_shred_mtu );
488 :
489 0 : ulong execle_sig = fd_disco_execle_sig( slot, ctx->_pack_idx );
490 :
491 : /* We always need to publish, even if there are no successfully executed
492 : transactions so the PoH tile can keep an accurate count of microblocks
493 : it has seen. PoH ignores tspub and the GUI takes it as the end of
494 : execution, which every path through the loop leaves in last_ticks. */
495 0 : ulong new_sz = txn_cnt*sizeof(fd_txn_p_t) + sizeof(fd_microblock_trailer_t);
496 0 : fd_stem_publish( stem, ctx->out_poh->idx, execle_sig, ctx->out_poh->chunk, new_sz, 0UL, (ulong)fd_frag_meta_ts_comp( microblock_start_ticks ), (ulong)fd_frag_meta_ts_comp( last_ticks ) );
497 0 : ctx->out_poh->chunk = fd_dcache_compact_next( ctx->out_poh->chunk, new_sz, ctx->out_poh->chunk0, ctx->out_poh->wmark );
498 0 : }
499 :
500 : static inline void
501 : handle_bundle( fd_execle_tile_t * ctx,
502 : ulong seq,
503 : ulong sig,
504 : ulong sz,
505 : ulong begin_tspub,
506 0 : fd_stem_context_t * stem ) {
507 0 : long const bundle_start_ticks = fd_tickcount();
508 0 : long const microblock_start_ticks = fd_frag_meta_ts_decomp( begin_tspub, bundle_start_ticks );
509 :
510 0 : fd_txn_p_t * txns = (fd_txn_p_t *)fd_chunk_to_laddr( ctx->out_poh->mem, ctx->out_poh->chunk );
511 :
512 0 : ulong slot = fd_disco_poh_sig_slot( sig );
513 0 : ulong txn_cnt = (sz-sizeof(fd_microblock_execle_trailer_t))/sizeof(fd_txn_e_t);
514 :
515 0 : fd_bank_t * bank = fd_banks_bank_query( ctx->banks, ctx->_bank_idx );
516 0 : FD_TEST( bank );
517 0 : ulong bank_slot = bank->f.slot;
518 0 : FD_TEST( bank_slot==slot );
519 :
520 0 : fd_acct_addr_t const * writable_alt [ MAX_TXN_PER_MICROBLOCK ];
521 0 : ulong tips [ MAX_TXN_PER_MICROBLOCK ] = { 0U };
522 0 : long txn_end_ticks[ MAX_TXN_PER_MICROBLOCK ];
523 0 : for( ulong i=0UL; i<MAX_TXN_PER_MICROBLOCK; i++ ) txn_end_ticks[ i ] = LONG_MAX;
524 :
525 : /* Pre-populate ALT accounts for all transactions for rebates.
526 : These were copied in during_frag from the source fd_txn_e_t,
527 : resolved by fd_resolv_tile. */
528 0 : for( ulong i=0UL; i<txn_cnt; i++ ) {
529 0 : writable_alt[i] = ctx->_alt_accts[i];
530 0 : ctx->txn_in[ i ].txn = &txns[ i ];
531 0 : ctx->txn_in[ i ].bundle.is_bundle = 1;
532 0 : ctx->txn_in[ i ].index_in_slot = ctx->_txn_idx + i;
533 0 : ctx->txn_out[ i ].err.txn_err = FD_RUNTIME_EXECUTE_SUCCESS;
534 0 : }
535 :
536 : /* Acquire all accdb resources in order to execute the bundle. */
537 0 : int const setup_bundle = fd_runtime_prepare_bundle_accounts( ctx->runtime, bank, ctx->txn_in, ctx->txn_out, txn_cnt )==FD_RUNTIME_EXECUTE_SUCCESS;
538 0 : ulong failed_idx = ULONG_MAX;
539 0 : if( FD_UNLIKELY( !setup_bundle ) ) {
540 0 : for( ulong i=0UL; i<txn_cnt; i++ ) {
541 0 : if( ctx->txn_out[ i ].err.txn_err==FD_RUNTIME_EXECUTE_SUCCESS ) continue;
542 0 : failed_idx = i;
543 0 : break;
544 0 : }
545 0 : FD_TEST( failed_idx!=ULONG_MAX );
546 0 : }
547 :
548 : /* Every transaction in the bundle should be executed in order against
549 : different transaction contexts. */
550 0 : for( ulong i=0UL; i<txn_cnt; i++ ) {
551 :
552 0 : fd_txn_p_t * txn = &txns[ i ];
553 0 : fd_txn_in_t * txn_in = &ctx->txn_in[ i ];
554 0 : fd_txn_out_t * txn_out = &ctx->txn_out[ i ];
555 :
556 0 : txn->flags &= ~(FD_TXN_P_FLAGS_SANITIZE_SUCCESS | FD_TXN_P_FLAGS_EXECUTE_SUCCESS);
557 :
558 0 : if( failed_idx!=ULONG_MAX ) continue;
559 :
560 0 : fd_runtime_prepare_and_execute_txn( ctx->runtime, bank, txn_in, txn_out );
561 :
562 0 : txn->flags = (txn->flags & 0x00FFFFFFU) | ((uint)(-txn_out->err.txn_err)<<24);
563 0 : if( FD_UNLIKELY( !txn_out->err.is_committable || txn_out->err.txn_err!=FD_RUNTIME_EXECUTE_SUCCESS ) ) failed_idx = i;
564 0 : }
565 :
566 : /* If all of the transactions in the bundle executed successfully, we
567 : can commit the transactions in order. At this point, we can also
568 : accumulate unused CUs to the rebate. Otherwise, if any transaction
569 : fails, we need to exclude all the bundle transactions and rebate
570 : all of the CUs. */
571 0 : if( FD_LIKELY( failed_idx==ULONG_MAX ) ) {
572 0 : for( ulong i=0UL; i<txn_cnt; i++ ) {
573 :
574 0 : fd_txn_in_t * txn_in = &ctx->txn_in[ i ];
575 0 : fd_txn_out_t * txn_out = &ctx->txn_out[ i ];
576 0 : uchar * signature = (uchar *)txn_in->txn->payload + TXN( txn_in->txn )->signature_off;
577 :
578 0 : fd_runtime_commit_txn( ctx->runtime, bank, txn_in, txn_out );
579 :
580 0 : txn_end_ticks[ i ] = fd_tickcount();
581 :
582 0 : if( FD_UNLIKELY( !txn_out->err.is_committable ) ) {
583 0 : txns[ i ].flags = (txns[ i ].flags & 0x00FFFFFFU) | ((uint)(-txn_out->err.txn_err)<<24);
584 0 : fd_cost_tracker_t * cost_tracker = fd_cost_tracker_store_peek( fd_bank_cost_tracker( bank ), bank->cost_tracker_fork_id );
585 0 : int err = fd_cost_tracker_try_add_cost( cost_tracker, txn_out );
586 0 : FD_LOG_HEXDUMP_WARNING(( "txn", txns[ i ].payload, txns[ i ].payload_sz ));
587 0 : FD_BASE58_ENCODE_64_BYTES( signature, signature_b58 );
588 0 : FD_LOG_CRIT(( "transaction %s failed to fit into block despite pack guaranteeing it would "
589 0 : "(res=%d) [block_cost=%lu, allocated_accounts_data_size=%lu, "
590 0 : "block_cost_limit=%lu, account_cost_limit=%lu]",
591 0 : signature_b58, err, cost_tracker->block_cost,
592 0 : cost_tracker->allocated_accounts_data_size,
593 0 : cost_tracker->block_cost_limit, cost_tracker->account_cost_limit ));
594 0 : }
595 :
596 0 : uint actual_execution_cus = (uint)(txn_out->details.compute_budget.compute_unit_limit - txn_out->details.compute_budget.compute_meter);
597 0 : uint actual_acct_data_cus = (uint)(txn_out->details.txn_cost.transaction.loaded_accounts_data_size_cost);
598 0 : uint non_execution_cus = txns[ i ].pack_cu.non_execution_cus;
599 0 : uint requested_exec_plus_acct_data_cus = txns[ i ].pack_cu.requested_exec_plus_acct_data_cus;
600 :
601 0 : if( FD_UNLIKELY( actual_execution_cus + actual_acct_data_cus > requested_exec_plus_acct_data_cus ) ) {
602 0 : FD_BASE58_ENCODE_64_BYTES( signature, signature_b58 );
603 0 : fd_cost_tracker_t const * _ct = fd_cost_tracker_store_peek( fd_bank_cost_tracker( bank ), bank->cost_tracker_fork_id );
604 0 : FD_LOG_HEXDUMP_WARNING(( "txn", txns[ i ].payload, txns[ i ].payload_sz ));
605 0 : FD_LOG_ERR(( "transaction %s actual CUs (%u+%u) exceeded requested (%u) despite pack guaranteeing it would "
606 0 : "fit [block_cost=%lu, block_cost_limit=%lu, account_cost_limit=%lu]",
607 0 : signature_b58, actual_execution_cus, actual_acct_data_cus, requested_exec_plus_acct_data_cus,
608 0 : _ct->block_cost, _ct->block_cost_limit, _ct->account_cost_limit ));
609 0 : }
610 0 : txns[ i ].execle_cu.rebated_cus = requested_exec_plus_acct_data_cus - (actual_execution_cus + actual_acct_data_cus);
611 0 : txns[ i ].execle_cu.actual_consumed_cus = non_execution_cus + actual_execution_cus + actual_acct_data_cus;
612 :
613 0 : txns[ i ].flags |= FD_TXN_P_FLAGS_EXECUTE_SUCCESS | FD_TXN_P_FLAGS_SANITIZE_SUCCESS;
614 0 : tips[ i ] = txn_out->details.tips;
615 :
616 0 : fd_metrics_tl[ MIDX( COUNTER, EXECLE, TXN_PROCESSED_BY_LANDING )+FD_METRICS_ENUM_TXN_LANDING_RESULT_V_SUCCESS_IDX ]++;
617 0 : fd_metrics_tl[ MIDX( COUNTER, EXECLE, TXN_PROCESSED )+FD_METRICS_ENUM_TXN_RESULT_V_SUCCESS_IDX ]++;
618 0 : fd_metrics_tl[ MIDX( COUNTER, EXECLE, TXN_PROCESSED_BY_VERSION )+fd_execle_version_from_txn( TXN( &txns[ i ] ) ) ]++;
619 0 : }
620 0 : } else {
621 : /* A failed bundle is dropped in its entirety: every transaction is
622 : marked non-committable and none of them land in the block. We
623 : intentionally do NOT emit runtime_txn events here */
624 0 : for( ulong i=0UL; i<txn_cnt; i++ ) {
625 :
626 0 : ctx->txn_out[ i ].err.is_committable = 0;
627 :
628 0 : if( !setup_bundle || i>failed_idx ) {
629 0 : ctx->txn_out[ i ].details.load_start_ticks = LONG_MAX;
630 0 : ctx->txn_out[ i ].details.check_start_ticks = LONG_MAX;
631 0 : ctx->txn_out[ i ].details.exec_start_ticks = LONG_MAX;
632 0 : ctx->txn_out[ i ].details.commit_start_ticks = LONG_MAX;
633 0 : }
634 :
635 0 : uint requested_exec_plus_acct_data_cus = txns[ i ].pack_cu.requested_exec_plus_acct_data_cus;
636 0 : uint non_execution_cus = txns[ i ].pack_cu.non_execution_cus;
637 0 : txns[ i ].execle_cu.actual_consumed_cus = 0U;
638 0 : txns[ i ].execle_cu.rebated_cus = requested_exec_plus_acct_data_cus + non_execution_cus;
639 0 : tips[ i ] = 0UL;
640 0 : int txn_err = ctx->txn_out[ i ].err.txn_err;
641 0 : if( i!=failed_idx || txn_err==FD_RUNTIME_EXECUTE_SUCCESS ) txn_err = FD_RUNTIME_TXN_ERR_BUNDLE_PEER;
642 0 : txns[ i ].flags = (txns[ i ].flags & 0x00FFFFFFU) | ((uint)(-txn_err)<<24);
643 :
644 0 : fd_metrics_tl[ MIDX( COUNTER, EXECLE, TXN_PROCESSED_BY_LANDING )+FD_METRICS_ENUM_TXN_LANDING_RESULT_V_DROPPED_IDX ]++;
645 0 : if( i==failed_idx ) fd_metrics_tl[ MIDX( COUNTER, EXECLE, TXN_PROCESSED )+(ulong)fd_execle_err_from_runtime_err( ctx->txn_out[ i ].err.txn_err ) ]++;
646 0 : else fd_metrics_tl[ MIDX( COUNTER, EXECLE, TXN_PROCESSED )+FD_METRICS_ENUM_TXN_RESULT_V_BUNDLE_PEER_IDX ]++;
647 0 : fd_metrics_tl[ MIDX( COUNTER, EXECLE, TXN_PROCESSED_BY_VERSION )+fd_execle_version_from_txn( TXN( &txns[ i ] ) ) ]++;
648 0 : }
649 0 : }
650 :
651 0 : if( FD_LIKELY( setup_bundle ) ) fd_runtime_fini_bundle( ctx->runtime );
652 :
653 0 : if( FD_LIKELY( ctx->enable_rebates ) ) fd_pack_rebate_sum_add_txn( ctx->rebater, txns, writable_alt, txn_cnt );
654 :
655 0 : for( ulong i=0UL; i<txn_cnt; i++ ) {
656 0 : fd_txn_out_t const * txn_out = &ctx->txn_out[ i ];
657 :
658 0 : ctx->metrics.txn_load_cum_ticks += fd_ulong_if( txn_out->details.check_start_ticks==LONG_MAX || txn_out->details.load_start_ticks==LONG_MAX, 0UL, (ulong)( txn_out->details.check_start_ticks - txn_out->details.load_start_ticks ) );
659 0 : ctx->metrics.txn_check_cum_ticks += fd_ulong_if( txn_out->details.exec_start_ticks==LONG_MAX || txn_out->details.check_start_ticks==LONG_MAX, 0UL, (ulong)( txn_out->details.exec_start_ticks - txn_out->details.check_start_ticks ) );
660 0 : ctx->metrics.txn_exec_cum_ticks += fd_ulong_if( txn_out->details.commit_start_ticks==LONG_MAX || txn_out->details.exec_start_ticks==LONG_MAX, 0UL, (ulong)( txn_out->details.commit_start_ticks - txn_out->details.exec_start_ticks ) );
661 0 : ctx->metrics.txn_commit_cum_ticks += fd_ulong_if( txn_end_ticks[ i ]==LONG_MAX || txn_out->details.commit_start_ticks==LONG_MAX, 0UL, (ulong)( txn_end_ticks[ i ] - txn_out->details.commit_start_ticks ) );
662 0 : }
663 :
664 : /* Indicate to pack tile we are done processing the transactions so
665 : it can pack new microblocks using these accounts. */
666 0 : fd_fseq_update( ctx->busy_fseq, seq );
667 :
668 : /* We need to publish each transaction separately into its own
669 : microblock, so make a temporary copy on the stack so we can move
670 : all the data around. */
671 0 : fd_txn_p_t bundle_txn_temp[ 5UL ];
672 0 : for( ulong i=0UL; i<txn_cnt; i++ ) {
673 0 : bundle_txn_temp[ i ] = txns[ i ];
674 0 : }
675 :
676 0 : for( ulong i=0UL; i<txn_cnt; i++ ) {
677 0 : fd_txn_out_t * txn_out = &ctx->txn_out[ i ];
678 :
679 0 : uchar * dst = (uchar *)fd_chunk_to_laddr( ctx->out_poh->mem, ctx->out_poh->chunk );
680 0 : fd_memcpy( dst, bundle_txn_temp+i, sizeof(fd_txn_p_t) );
681 :
682 0 : fd_microblock_trailer_t * trailer = (fd_microblock_trailer_t *)( dst+sizeof(fd_txn_p_t) );
683 0 : hash_transactions( ctx->bmtree, (fd_txn_p_t*)dst, 1UL, trailer->hash );
684 0 : trailer->pack_txn_idx = ctx->_txn_idx + i;
685 0 : trailer->tips = tips[ i ];
686 0 : trailer->bank_seq = bank->bank_seq;
687 0 : trailer->exec_start_ticks = bundle_start_ticks;
688 0 : trailer->exec_end_ticks = txn_end_ticks[ i ];
689 :
690 0 : ulong execle_sig = fd_disco_execle_sig( slot, ctx->_pack_idx+i );
691 :
692 0 : trailer->txn_ns_dt.load_start = fd_float_if( txn_out->details.load_start_ticks==LONG_MAX, 0., (float)fd_long_max( 0L, txn_out->details.load_start_ticks - microblock_start_ticks ) * ctx->ns_per_tick );
693 0 : trailer->txn_ns_dt.check_start = fd_float_if( txn_out->details.check_start_ticks==LONG_MAX, trailer->txn_ns_dt.load_start, (float)fd_long_max( 0L, txn_out->details.check_start_ticks - microblock_start_ticks ) * ctx->ns_per_tick );
694 0 : trailer->txn_ns_dt.exec_start = fd_float_if( txn_out->details.exec_start_ticks==LONG_MAX, trailer->txn_ns_dt.check_start, (float)fd_long_max( 0L, txn_out->details.exec_start_ticks - microblock_start_ticks ) * ctx->ns_per_tick );
695 0 : trailer->txn_ns_dt.commit_start = fd_float_if( txn_out->details.commit_start_ticks==LONG_MAX, trailer->txn_ns_dt.exec_start, (float)fd_long_max( 0L, txn_out->details.commit_start_ticks - microblock_start_ticks ) * ctx->ns_per_tick );
696 0 : trailer->txn_ns_dt.commit_end = fd_float_if( txn_end_ticks[ i ]==LONG_MAX, trailer->txn_ns_dt.commit_start, (float)fd_long_max( 0L, txn_end_ticks[ i ] - microblock_start_ticks ) * ctx->ns_per_tick );
697 :
698 0 : ulong new_sz = sizeof(fd_txn_p_t) + sizeof(fd_microblock_trailer_t);
699 0 : fd_stem_publish( stem, ctx->out_poh->idx, execle_sig, ctx->out_poh->chunk, new_sz, 0UL, (ulong)fd_frag_meta_ts_comp( microblock_start_ticks ), (ulong)fd_frag_meta_ts_comp( fd_tickcount() ) );
700 0 : ctx->out_poh->chunk = fd_dcache_compact_next( ctx->out_poh->chunk, new_sz, ctx->out_poh->chunk0, ctx->out_poh->wmark );
701 0 : }
702 0 : }
703 :
704 : static inline void
705 : after_frag( fd_execle_tile_t * ctx,
706 : ulong in_idx,
707 : ulong seq,
708 : ulong sig,
709 : ulong sz,
710 : ulong tsorig,
711 : ulong tspub,
712 0 : fd_stem_context_t * stem ) {
713 0 : ulong slot = fd_disco_poh_sig_slot( sig );
714 0 : if( FD_LIKELY( ctx->enable_rebates ) ) {
715 0 : if( FD_UNLIKELY( slot!=ctx->rebates_for_slot ) ) {
716 : /* If pack has already moved on to a new slot, the rebates are no
717 : longer useful. */
718 0 : fd_pack_rebate_sum_clear( ctx->rebater );
719 0 : ctx->rebate_microblock_cnt = 0UL;
720 0 : ctx->rebate_idle_loop_cnt = 0UL;
721 0 : ctx->rebate_draining = 0;
722 0 : ctx->rebates_for_slot = slot;
723 0 : }
724 0 : if( FD_UNLIKELY( !ctx->rebate_microblock_cnt ) ) {
725 0 : ctx->rebate_tsorig = tsorig;
726 0 : }
727 0 : ctx->rebate_idle_loop_cnt = 0UL;
728 0 : }
729 :
730 0 : if( FD_UNLIKELY( ctx->_is_bundle ) ) handle_bundle( ctx, seq, sig, sz, tspub, stem );
731 0 : else handle_microblock( ctx, seq, sig, sz, tspub, stem );
732 0 : if( FD_LIKELY( ctx->enable_rebates ) ) {
733 0 : ulong txn_cnt = (sz-sizeof(fd_microblock_execle_trailer_t))/sizeof(fd_txn_e_t);
734 0 : ctx->rebate_microblock_cnt += fd_ulong_if( ctx->_is_bundle, txn_cnt, 1UL );
735 0 : }
736 :
737 : /* Return the pack_execle credit now rather than at housekeeping, so
738 : the link can be shallow enough for pack to keep it in cache. */
739 0 : fd_stem_credit_return( stem, in_idx, seq+1UL );
740 0 : }
741 :
742 : static inline fd_execle_out_t
743 : out1( fd_topo_t const * topo,
744 : fd_topo_tile_t const * tile,
745 0 : char const * name ) {
746 0 : ulong idx = ULONG_MAX;
747 :
748 0 : for( ulong i=0UL; i<tile->out_cnt; i++ ) {
749 0 : fd_topo_link_t const * link = &topo->links[ tile->out_link_id[ i ] ];
750 0 : if( !strcmp( link->name, name ) ) {
751 0 : if( FD_UNLIKELY( idx!=ULONG_MAX ) ) FD_LOG_ERR(( "tile %s:%lu had multiple output links named %s but expected one", tile->name, tile->kind_id, name ));
752 0 : idx = i;
753 0 : }
754 0 : }
755 :
756 0 : if( FD_UNLIKELY( idx==ULONG_MAX ) ) return (fd_execle_out_t){ .idx = ULONG_MAX, .mem = NULL, .chunk0 = 0, .wmark = 0, .chunk = 0 };
757 :
758 0 : void * mem = topo->workspaces[ topo->objs[ topo->links[ tile->out_link_id[ idx ] ].dcache_obj_id ].wksp_id ].wksp;
759 0 : ulong chunk0 = fd_dcache_compact_chunk0( mem, topo->links[ tile->out_link_id[ idx ] ].dcache );
760 0 : ulong wmark = fd_dcache_compact_wmark ( mem, topo->links[ tile->out_link_id[ idx ] ].dcache, topo->links[ tile->out_link_id[ idx ] ].mtu );
761 :
762 0 : return (fd_execle_out_t){ .idx = idx, .mem = mem, .chunk0 = chunk0, .wmark = wmark, .chunk = chunk0 };
763 0 : }
764 :
765 : static void
766 : privileged_init( fd_topo_t const * topo,
767 0 : fd_topo_tile_t const * tile ) {
768 0 : void * scratch = fd_topo_obj_laddr( topo, tile->tile_obj_id );
769 :
770 0 : FD_SCRATCH_ALLOC_INIT( l, scratch );
771 0 : fd_execle_tile_t * ctx = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_execle_tile_t), sizeof(fd_execle_tile_t) );
772 0 : FD_TEST( fd_rng_secure( &ctx->rebate_seed, sizeof(ctx->rebate_seed) ) );
773 0 : }
774 :
775 : static void
776 : unprivileged_init( fd_topo_t const * topo,
777 0 : fd_topo_tile_t const * tile ) {
778 0 : void * scratch = fd_topo_obj_laddr( topo, tile->tile_obj_id );
779 :
780 0 : FD_SCRATCH_ALLOC_INIT( l, scratch );
781 0 : fd_execle_tile_t * ctx = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_execle_tile_t), sizeof(fd_execle_tile_t) );
782 0 : void * blake3 = FD_SCRATCH_ALLOC_APPEND( l, FD_BLAKE3_ALIGN, FD_BLAKE3_FOOTPRINT );
783 0 : void * bmtree = FD_SCRATCH_ALLOC_APPEND( l, FD_BMTREE_COMMIT_ALIGN, FD_BMTREE_COMMIT_FOOTPRINT(0) );
784 0 : void * _txncache = FD_SCRATCH_ALLOC_APPEND( l, fd_txncache_align(), fd_txncache_footprint( tile->execle.max_live_slots ) );
785 0 : void * _accdb = FD_SCRATCH_ALLOC_APPEND( l, fd_accdb_align(), fd_accdb_footprint( tile->execle.max_live_slots, 0 ) );
786 0 : void * pc_scratch = FD_SCRATCH_ALLOC_APPEND( l, FD_PROGCACHE_SCRATCH_ALIGN, FD_PROGCACHE_SCRATCH_FOOTPRINT );
787 :
788 0 : #define NONNULL( x ) (__extension__({ \
789 0 : __typeof__((x)) __x = (x); \
790 0 : if( FD_UNLIKELY( !__x ) ) FD_LOG_ERR(( #x " was unexpectedly NULL" )); \
791 0 : __x; }))
792 :
793 0 : ctx->kind_id = tile->kind_id;
794 0 : ctx->blake3 = NONNULL( fd_blake3_join( fd_blake3_new( blake3 ) ) );
795 0 : ctx->bmtree = NONNULL( bmtree );
796 :
797 0 : NONNULL( fd_pack_rebate_sum_join( fd_pack_rebate_sum_new( ctx->rebater, ctx->rebate_seed ) ) );
798 0 : ctx->rebates_for_slot = 0UL;
799 0 : ctx->rebate_microblock_cnt = 0UL;
800 0 : ctx->rebate_idle_loop_cnt = 0UL;
801 0 : ctx->rebate_draining = 0;
802 :
803 0 : FD_TEST( fd_progcache_join( ctx->progcache,
804 0 : fd_topo_obj_laddr( topo, tile->execle.progcache_obj_id ),
805 0 : pc_scratch, FD_PROGCACHE_SCRATCH_FOOTPRINT ) );
806 :
807 0 : void * _txncache_shmem = fd_topo_obj_laddr( topo, tile->execle.txncache_obj_id );
808 0 : fd_txncache_shmem_t * txncache_shmem = fd_txncache_shmem_join( _txncache_shmem );
809 0 : FD_TEST( txncache_shmem );
810 0 : fd_txncache_t * txncache = fd_txncache_join( fd_txncache_new( _txncache, txncache_shmem ) );
811 0 : FD_TEST( txncache );
812 :
813 0 : void * _accdb_shmem = fd_topo_obj_laddr( topo, tile->execle.accdb_obj_id );
814 0 : fd_accdb_shmem_t * accdb_shmem = fd_accdb_shmem_join( _accdb_shmem );
815 0 : FD_TEST( accdb_shmem );
816 0 : fd_sleep_t * accdb_sleep = topo->sleep_obj_id!=ULONG_MAX ? fd_sleep_join( fd_topo_obj_laddr( topo, topo->sleep_obj_id ) ) : NULL;
817 0 : ctx->accdb = fd_accdb_join( fd_accdb_new( _accdb, accdb_shmem, FD_ACCDB_FD_RW, 0UL, NULL, accdb_sleep, fd_topo_find_tile( topo, "accdb", 0UL ), 0 ) );
818 0 : FD_TEST( ctx->accdb );
819 :
820 0 : for( ulong i=0UL; i<FD_PACK_MAX_TXN_PER_BUNDLE; i++ ) {
821 0 : ctx->txn_in[ i ].bundle.prev_txn_cnt = i;
822 0 : for( ulong j=0UL; j<i; j++ ) ctx->txn_in[ i ].bundle.prev_txn_outs[ j ] = &ctx->txn_out[ j ];
823 0 : }
824 :
825 0 : ctx->runtime->accdb = ctx->accdb;
826 0 : ctx->runtime->progcache = ctx->progcache;
827 0 : ctx->runtime->status_cache = txncache;
828 0 : memset( &ctx->runtime->log, 0, sizeof(ctx->runtime->log) );
829 0 : ctx->runtime->log.log_collector = ctx->log_collector;
830 0 : ctx->runtime->fuzz.enabled = 0;
831 0 : ctx->runtime->fuzz.reclaim_accounts = 0;
832 0 : ctx->runtime->accounts.executable_cnt = 0UL;
833 0 : ctx->runtime->accounts.account_cnt = 0UL;
834 :
835 0 : ulong banks_obj_id = fd_pod_queryf_ulong( topo->props, ULONG_MAX, "banks" );
836 0 : FD_TEST( banks_obj_id!=ULONG_MAX );
837 0 : ctx->banks = fd_banks_join( fd_topo_obj_laddr( topo, banks_obj_id ) );
838 0 : NONNULL( ctx->banks );
839 :
840 0 : ulong busy_obj_id = fd_pod_queryf_ulong( topo->props, ULONG_MAX, "execle_busy.%lu", tile->kind_id );
841 0 : FD_TEST( busy_obj_id!=ULONG_MAX );
842 0 : ctx->busy_fseq = fd_fseq_join( fd_topo_obj_laddr( topo, busy_obj_id ) );
843 0 : if( FD_UNLIKELY( !ctx->busy_fseq ) ) FD_LOG_ERR(( "execle tile %lu has no busy flag", tile->kind_id ));
844 :
845 0 : memset( &ctx->metrics, 0, sizeof( ctx->metrics ) );
846 0 : memset( &ctx->runtime->metrics, 0, sizeof( ctx->runtime->metrics ) );
847 :
848 0 : ctx->pack_in_mem = topo->workspaces[ topo->objs[ topo->links[ tile->in_link_id[ 0UL ] ].dcache_obj_id ].wksp_id ].wksp;
849 0 : ctx->pack_in_chunk0 = fd_dcache_compact_chunk0( ctx->pack_in_mem, topo->links[ tile->in_link_id[ 0UL ] ].dcache );
850 0 : ctx->pack_in_wmark = fd_dcache_compact_wmark ( ctx->pack_in_mem, topo->links[ tile->in_link_id[ 0UL ] ].dcache, topo->links[ tile->in_link_id[ 0UL ] ].mtu );
851 :
852 0 : *ctx->out_poh = out1( topo, tile, "execle_poh" ); FD_TEST( ctx->out_poh->idx!=ULONG_MAX );
853 0 : *ctx->out_pack = out1( topo, tile, "execle_pack" );
854 :
855 0 : ctx->enable_rebates = ctx->out_pack->idx!=ULONG_MAX;
856 :
857 0 : ctx->ns_per_tick = 1.f / (float)fd_tempo_tick_per_ns( NULL );
858 :
859 0 : fd_startup_gate_init( ctx->startup_gate, topo, tile->in_cnt );
860 :
861 0 : ulong scratch_top = FD_SCRATCH_ALLOC_FINI( l, scratch_align() );
862 0 : if( FD_UNLIKELY( scratch_top > (ulong)scratch + scratch_footprint( tile ) ) )
863 0 : FD_LOG_ERR(( "scratch overflow %lu %lu %lu", scratch_top - (ulong)scratch - scratch_footprint( tile ), scratch_top, (ulong)scratch + scratch_footprint( tile ) ));
864 0 : }
865 :
866 : static ulong
867 : populate_allowed_seccomp( fd_topo_t const * topo,
868 : fd_topo_tile_t const * tile,
869 : ulong out_cnt,
870 0 : struct sock_filter * out ) {
871 0 : (void)topo;
872 0 : (void)tile;
873 :
874 0 : populate_sock_filter_policy_fd_execle_tile( out_cnt, out, (uint)fd_log_private_logfile_fd(), FD_ACCDB_FD_RW, FD_STAKE_DELEGATIONS_FD, FD_VOTE_STAKES_FD );
875 0 : return sock_filter_policy_fd_execle_tile_instr_cnt;
876 0 : }
877 :
878 : static ulong
879 : populate_allowed_fds( fd_topo_t const * topo,
880 : fd_topo_tile_t const * tile,
881 : ulong out_fds_cnt,
882 0 : int * out_fds ) {
883 0 : (void)topo;
884 0 : (void)tile;
885 :
886 0 : if( FD_UNLIKELY( out_fds_cnt<5UL ) ) FD_LOG_ERR(( "out_fds_cnt %lu", out_fds_cnt ));
887 :
888 0 : ulong out_cnt = 0UL;
889 0 : out_fds[ out_cnt++ ] = 2; /* stderr */
890 0 : if( FD_LIKELY( -1!=fd_log_private_logfile_fd() ) )
891 0 : out_fds[ out_cnt++ ] = fd_log_private_logfile_fd(); /* logfile */
892 0 : out_fds[ out_cnt++ ] = FD_ACCDB_FD_RW; /* accounts db */
893 0 : out_fds[ out_cnt++ ] = FD_STAKE_DELEGATIONS_FD; /* stake delegation disk spill */
894 0 : out_fds[ out_cnt++ ] = FD_VOTE_STAKES_FD; /* vote stakes disk spill */
895 :
896 0 : return out_cnt;
897 0 : }
898 :
899 : /* For a bundle, one bundle might burst into at most 5 separate PoH mixins, since the
900 : microblocks cannot be conflicting. */
901 :
902 0 : #define STEM_BURST (5UL)
903 :
904 : /* See explanation in fd_pack */
905 0 : #define STEM_LAZY (128L*3000L)
906 :
907 0 : #define STEM_CALLBACK_CONTEXT_TYPE fd_execle_tile_t
908 0 : #define STEM_CALLBACK_CONTEXT_ALIGN alignof(fd_execle_tile_t)
909 :
910 0 : #define STEM_CALLBACK_METRICS_WRITE metrics_write
911 0 : #define STEM_CALLBACK_AFTER_CREDIT after_credit
912 : #define STEM_CALLBACK_PREVENT_PARK prevent_park
913 0 : #define STEM_CALLBACK_BEFORE_FRAG before_frag
914 0 : #define STEM_CALLBACK_DURING_FRAG during_frag
915 0 : #define STEM_CALLBACK_AFTER_FRAG after_frag
916 :
917 : #include "../../disco/stem/fd_stem.c"
918 :
919 : static ulong
920 0 : max_event_sz( fd_topo_tile_t const * tile ) {
921 : /* execle emits accdb_partition_added, plus runtime_txn when diffs are on. */
922 0 : ulong sz = sizeof(fd_event_accdb_partition_added_t);
923 0 : if( tile->execle.report_runtime_diffs && sizeof(fd_event_runtime_txn_t)>sz ) sz = sizeof(fd_event_runtime_txn_t);
924 0 : return sz;
925 0 : }
926 :
927 : fd_topo_run_tile_t fd_tile_execle = {
928 : .name = "execle",
929 : .max_event_sz = max_event_sz,
930 : .populate_allowed_seccomp = populate_allowed_seccomp,
931 : .populate_allowed_fds = populate_allowed_fds,
932 : .scratch_align = scratch_align,
933 : .scratch_footprint = scratch_footprint,
934 : .privileged_init = privileged_init,
935 : .unprivileged_init = unprivileged_init,
936 : .run = stem_run,
937 : };
|