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