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