Line data Source code
1 : #include "fd_bank_err.h"
2 :
3 : #include "../../disco/tiles.h"
4 : #include "generated/fd_bank_tile_seccomp.h"
5 : #include "../../disco/pack/fd_pack.h"
6 : #include "../../disco/pack/fd_pack_cost.h"
7 : #include "../../ballet/blake3/fd_blake3.h"
8 : #include "../../ballet/bmtree/fd_bmtree.h"
9 : #include "../../disco/metrics/fd_metrics.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_bank.h"
13 : #include "../../disco/metrics/generated/fd_metrics_enums.h"
14 : #include "../../flamenco/runtime/fd_runtime.h"
15 : #include "../../flamenco/runtime/fd_bank.h"
16 :
17 : typedef struct {
18 : ulong kind_id;
19 :
20 : fd_blake3_t * blake3;
21 : void * bmtree;
22 :
23 : ulong _bank_idx;
24 : ulong _pack_idx;
25 : ulong _txn_idx;
26 : int _is_bundle;
27 :
28 : ulong * busy_fseq;
29 :
30 : fd_wksp_t * pack_in_mem;
31 : ulong pack_in_chunk0;
32 : ulong pack_in_wmark;
33 :
34 : fd_wksp_t * out_mem;
35 : ulong out_chunk0;
36 : ulong out_wmark;
37 : ulong out_chunk;
38 :
39 : fd_wksp_t * rebate_mem;
40 : ulong rebate_chunk0;
41 : ulong rebate_wmark;
42 : ulong rebate_chunk;
43 : ulong rebates_for_slot;
44 : fd_pack_rebate_sum_t rebater[ 1 ];
45 :
46 : fd_banks_t * banks;
47 : fd_spad_t * exec_spad;
48 :
49 : fd_exec_txn_ctx_t txn_ctx[1];
50 :
51 : struct {
52 : ulong txn_result[ FD_METRICS_ENUM_TRANSACTION_RESULT_CNT ];
53 : } metrics;
54 : } fd_bank_ctx_t;
55 :
56 : FD_FN_CONST static inline ulong
57 0 : scratch_align( void ) {
58 0 : return 128UL;
59 0 : }
60 :
61 : FD_FN_PURE static inline ulong
62 0 : scratch_footprint( fd_topo_tile_t const * tile ) {
63 0 : (void)tile;
64 0 : ulong l = FD_LAYOUT_INIT;
65 0 : l = FD_LAYOUT_APPEND( l, alignof( fd_bank_ctx_t ), sizeof( fd_bank_ctx_t ) );
66 0 : l = FD_LAYOUT_APPEND( l, FD_BLAKE3_ALIGN, FD_BLAKE3_FOOTPRINT );
67 0 : l = FD_LAYOUT_APPEND( l, FD_BMTREE_COMMIT_ALIGN, FD_BMTREE_COMMIT_FOOTPRINT(0) );
68 0 : l = FD_LAYOUT_APPEND( l, FD_SPAD_ALIGN, FD_SPAD_FOOTPRINT( FD_RUNTIME_TRANSACTION_EXECUTION_FOOTPRINT_DEFAULT ) );
69 0 : l = FD_LAYOUT_APPEND( l, fd_txncache_align(), fd_txncache_footprint( tile->bank.max_live_slots ) );
70 0 : return FD_LAYOUT_FINI( l, scratch_align() );
71 0 : }
72 :
73 : static inline void
74 0 : metrics_write( fd_bank_ctx_t * ctx ) {
75 0 : FD_MCNT_ENUM_COPY( BANKF, TRANSACTION_RESULT, ctx->metrics.txn_result );
76 0 : }
77 :
78 : static int
79 : before_frag( fd_bank_ctx_t * ctx,
80 : ulong in_idx,
81 : ulong seq,
82 0 : ulong sig ) {
83 0 : (void)in_idx;
84 0 : (void)seq;
85 :
86 : /* Pack also outputs "leader slot done" which we can ignore. */
87 0 : if( FD_UNLIKELY( fd_disco_poh_sig_pkt_type( sig )!=POH_PKT_TYPE_MICROBLOCK ) ) return 1;
88 :
89 0 : ulong target_bank_kind_id = fd_disco_poh_sig_bank_tile( sig );
90 0 : if( FD_UNLIKELY( target_bank_kind_id!=ctx->kind_id ) ) return 1;
91 :
92 0 : return 0;
93 0 : }
94 :
95 : static inline void
96 : during_frag( fd_bank_ctx_t * ctx,
97 : ulong in_idx FD_PARAM_UNUSED,
98 : ulong seq FD_PARAM_UNUSED,
99 : ulong sig FD_PARAM_UNUSED,
100 : ulong chunk,
101 : ulong sz,
102 0 : ulong ctl FD_PARAM_UNUSED ) {
103 :
104 0 : uchar * src = (uchar *)fd_chunk_to_laddr( ctx->pack_in_mem, chunk );
105 0 : uchar * dst = (uchar *)fd_chunk_to_laddr( ctx->out_mem, ctx->out_chunk );
106 :
107 0 : if( FD_UNLIKELY( chunk<ctx->pack_in_chunk0 || chunk>ctx->pack_in_wmark || sz>USHORT_MAX ) )
108 0 : FD_LOG_ERR(( "chunk %lu %lu corrupt, not in range [%lu,%lu]", chunk, sz, ctx->pack_in_chunk0, ctx->pack_in_wmark ));
109 :
110 0 : fd_memcpy( dst, src, sz-sizeof(fd_microblock_bank_trailer_t) );
111 0 : fd_microblock_bank_trailer_t * trailer = (fd_microblock_bank_trailer_t *)( src+sz-sizeof(fd_microblock_bank_trailer_t) );
112 0 : ctx->_bank_idx = trailer->bank_idx;
113 0 : ctx->_pack_idx = trailer->pack_idx;
114 0 : ctx->_txn_idx = trailer->pack_txn_idx;
115 0 : ctx->_is_bundle = trailer->is_bundle;
116 0 : }
117 :
118 : static void
119 : hash_transactions( void * mem,
120 : fd_txn_p_t * txns,
121 : ulong txn_cnt,
122 0 : uchar * mixin ) {
123 0 : fd_bmtree_commit_t * bmtree = fd_bmtree_commit_init( mem, 32UL, 1UL, 0UL );
124 0 : for( ulong i=0UL; i<txn_cnt; i++ ) {
125 0 : fd_txn_p_t * _txn = txns + i;
126 0 : if( FD_UNLIKELY( !(_txn->flags & FD_TXN_P_FLAGS_EXECUTE_SUCCESS) ) ) continue;
127 :
128 0 : fd_txn_t * txn = TXN(_txn);
129 0 : for( ulong j=0; j<txn->signature_cnt; j++ ) {
130 0 : fd_bmtree_node_t node[1];
131 0 : fd_bmtree_hash_leaf( node, _txn->payload+txn->signature_off+64UL*j, 64UL, 1UL );
132 0 : fd_bmtree_commit_append( bmtree, node, 1UL );
133 0 : }
134 0 : }
135 0 : uchar * root = fd_bmtree_commit_fini( bmtree );
136 0 : fd_memcpy( mixin, root, 32UL );
137 0 : }
138 :
139 : static inline void
140 : handle_microblock( fd_bank_ctx_t * ctx,
141 : ulong seq,
142 : ulong sig,
143 : ulong sz,
144 : ulong begin_tspub,
145 0 : fd_stem_context_t * stem ) {
146 0 : uchar * dst = (uchar *)fd_chunk_to_laddr( ctx->out_mem, ctx->out_chunk );
147 :
148 0 : ulong slot = fd_disco_poh_sig_slot( sig );
149 0 : ulong txn_cnt = (sz-sizeof(fd_microblock_bank_trailer_t))/sizeof(fd_txn_p_t);
150 :
151 0 : fd_bank_t * bank = fd_banks_bank_query( ctx->banks, ctx->_bank_idx );
152 0 : FD_TEST( bank );
153 0 : ulong bank_slot = fd_bank_slot_get( bank );
154 0 : FD_TEST( bank_slot==slot );
155 :
156 0 : fd_acct_addr_t const * writable_alt[ MAX_TXN_PER_MICROBLOCK ] = { NULL };
157 :
158 0 : for( ulong i=0UL; i<txn_cnt; i++ ) {
159 0 : fd_txn_p_t * txn = (fd_txn_p_t *)( dst + (i*sizeof(fd_txn_p_t)) );
160 0 : fd_exec_txn_ctx_t * txn_ctx = ctx->txn_ctx;
161 :
162 0 : txn->flags &= ~FD_TXN_P_FLAGS_SANITIZE_SUCCESS;
163 :
164 0 : int err = fd_runtime_prepare_and_execute_txn( ctx->banks, ctx->_bank_idx, txn_ctx, txn, ctx->exec_spad, NULL );
165 0 : if( FD_UNLIKELY( !(txn_ctx->flags & FD_TXN_P_FLAGS_SANITIZE_SUCCESS ) ) ) {
166 0 : ctx->metrics.txn_result[ fd_bank_err_from_runtime_err( err ) ]++;
167 0 : continue;
168 0 : }
169 :
170 : /* The account keys in the transaction context are laid out such
171 : that first the non-alt accounts are laid out, then the writable
172 : alt accounts, and finally the read-only alt accounts. */
173 0 : fd_txn_t * txn_descriptor = TXN( &txn_ctx->txn );
174 0 : for( ushort i=txn_descriptor->acct_addr_cnt; i<txn_descriptor->acct_addr_cnt+txn_descriptor->addr_table_adtl_writable_cnt; i++ ) {
175 0 : writable_alt[ i-txn_descriptor->acct_addr_cnt ] = fd_type_pun_const( &txn_ctx->account_keys[ i ] );
176 0 : }
177 :
178 0 : txn->flags |= FD_TXN_P_FLAGS_SANITIZE_SUCCESS;
179 :
180 0 : uint requested_exec_plus_acct_data_cus = txn->pack_cu.requested_exec_plus_acct_data_cus;
181 0 : uint non_execution_cus = txn->pack_cu.non_execution_cus;
182 :
183 : /* Assume failure, set below if success. If it doesn't land in the
184 : block, rebate the non-execution CUs too. */
185 0 : txn->bank_cu.actual_consumed_cus = 0U;
186 0 : txn->bank_cu.rebated_cus = requested_exec_plus_acct_data_cus + non_execution_cus;
187 0 : txn->flags &= ~FD_TXN_P_FLAGS_EXECUTE_SUCCESS;
188 :
189 : /* Stash the result in the flags value so that pack can inspect it. */
190 : /* TODO: Need to translate the err to a hacky Frankendancer style err
191 : that pack and GUI expect ... */
192 0 : txn->flags = (txn->flags & 0x00FFFFFFU) | ((uint)(-err)<<24);
193 :
194 0 : ctx->metrics.txn_result[ fd_bank_err_from_runtime_err( err ) ]++;
195 :
196 0 : uint actual_execution_cus = (uint)(txn_ctx->compute_budget_details.compute_unit_limit - txn_ctx->compute_budget_details.compute_meter);
197 0 : uint actual_acct_data_cus = (uint)(txn_ctx->loaded_accounts_data_size_cost);
198 :
199 0 : int is_simple_vote = 0;
200 0 : if( FD_UNLIKELY( is_simple_vote = fd_txn_is_simple_vote_transaction( TXN(txn), txn->payload ) ) ) {
201 : /* Simple votes are charged fixed amounts of compute regardless of
202 : the real cost they incur. Unclear what cost is returned by
203 : fd_execute txn, however, so we override it here. */
204 0 : actual_execution_cus = FD_PACK_VOTE_DEFAULT_COMPUTE_UNITS;
205 0 : actual_acct_data_cus = 0U;
206 0 : }
207 :
208 : /* FeesOnly transactions are transactions that failed to load
209 : before they even reach the VM stage. They have zero execution
210 : cost but do charge for the account data they are able to load.
211 : FeesOnly votes are charged the fixed voe cost. */
212 0 : txn->bank_cu.rebated_cus = requested_exec_plus_acct_data_cus - ( actual_execution_cus + actual_acct_data_cus );
213 0 : txn->bank_cu.actual_consumed_cus = non_execution_cus + actual_execution_cus + actual_acct_data_cus;
214 :
215 : /* TXN_P_FLAGS_EXECUTE_SUCCESS means that it should be included in
216 : the block. It's a bit of a misnomer now that there are fee-only
217 : transactions. */
218 0 : FD_TEST( txn_ctx->flags & FD_TXN_P_FLAGS_EXECUTE_SUCCESS );
219 0 : txn->flags |= FD_TXN_P_FLAGS_EXECUTE_SUCCESS;
220 :
221 : /* The VM will stop executing and fail an instruction immediately if
222 : it exceeds its requested CUs. A transaction which requests less
223 : account data than it actually consumes will fail in the account
224 : loading stage. */
225 0 : if( FD_UNLIKELY( actual_execution_cus+actual_acct_data_cus>requested_exec_plus_acct_data_cus ) ) {
226 0 : FD_LOG_HEXDUMP_WARNING(( "txn", txn->payload, txn->payload_sz ));
227 0 : FD_LOG_ERR(( "Actual CUs unexpectedly exceeded requested amount. actual_execution_cus (%u) actual_acct_data_cus "
228 0 : "(%u) requested_exec_plus_acct_data_cus (%u) is_simple_vote (%i) exec_failed (%i)",
229 0 : actual_execution_cus, actual_acct_data_cus, requested_exec_plus_acct_data_cus, is_simple_vote,
230 0 : err ));
231 0 : }
232 :
233 : /* Commit must succeed so no failure path. Once commit is called,
234 : the transactions MUST be mixed into the PoH otherwise we will
235 : fork and diverge, so the link from here til PoH mixin must be
236 : completely reliable with nothing dropped.
237 :
238 : fd_runtime_finalize_txn checks if the transaction fits into the
239 : block with the cost tracker. If it doesn't fit, flags is set to
240 : zero. A key invariant of the leader pipeline is that pack
241 : ensures all transactions must fit already, so it is a fatal error
242 : if that happens. We cannot reject the transaction here as there
243 : would be no way to undo the partially applied changes to the bank
244 : in finalize anyway. */
245 0 : fd_runtime_finalize_txn( ctx->txn_ctx->funk, txn_ctx->status_cache, txn_ctx->xid, txn_ctx, bank, NULL );
246 0 : FD_TEST( txn->flags );
247 0 : }
248 :
249 : /* Indicate to pack tile we are done processing the transactions so
250 : it can pack new microblocks using these accounts. */
251 0 : fd_fseq_update( ctx->busy_fseq, seq );
252 :
253 : /* Prepare the rebate */
254 0 : fd_pack_rebate_sum_add_txn( ctx->rebater, (fd_txn_p_t const *)dst, writable_alt, txn_cnt );
255 :
256 : /* Now produce the merkle hash of the transactions for inclusion
257 : (mixin) to the PoH hash. This is done on the bank tile because
258 : it shards / scales horizontally here, while PoH does not. */
259 0 : fd_microblock_trailer_t * trailer = (fd_microblock_trailer_t *)( dst + txn_cnt*sizeof(fd_txn_p_t) );
260 0 : hash_transactions( ctx->bmtree, (fd_txn_p_t*)dst, txn_cnt, trailer->hash );
261 0 : trailer->pack_txn_idx = ctx->_txn_idx;
262 0 : trailer->tips = 0UL;
263 :
264 0 : long tickcount = fd_tickcount();
265 0 : long microblock_start_ticks = fd_frag_meta_ts_decomp( begin_tspub, tickcount );
266 0 : long microblock_duration_ticks = fd_long_max(tickcount - microblock_start_ticks, 0L);
267 :
268 : // TODO: Execution timestamps
269 0 : long tx_start_ticks = 0L; //(long)out_timestamps[ 0 ];
270 0 : long tx_load_end_ticks = 0L; //(long)out_timestamps[ 1 ];
271 0 : long tx_end_ticks = 0L; //(long)out_timestamps[ 2 ];
272 0 : long tx_preload_end_ticks = 0L; //(long)out_timestamps[ 3 ];
273 :
274 0 : trailer->txn_start_pct = (uchar)(((double)(tx_start_ticks - microblock_start_ticks) * (double)UCHAR_MAX) / (double)microblock_duration_ticks);
275 0 : trailer->txn_load_end_pct = (uchar)(((double)(tx_load_end_ticks - microblock_start_ticks) * (double)UCHAR_MAX) / (double)microblock_duration_ticks);
276 0 : trailer->txn_end_pct = (uchar)(((double)(tx_end_ticks - microblock_start_ticks) * (double)UCHAR_MAX) / (double)microblock_duration_ticks);
277 0 : trailer->txn_preload_end_pct = (uchar)(((double)(tx_preload_end_ticks - microblock_start_ticks) * (double)UCHAR_MAX) / (double)microblock_duration_ticks);
278 :
279 : /* MAX_MICROBLOCK_SZ - (MAX_TXN_PER_MICROBLOCK*sizeof(fd_txn_p_t)) == 64
280 : so there's always 64 extra bytes at the end to stash the hash. */
281 0 : FD_STATIC_ASSERT( MAX_MICROBLOCK_SZ-(MAX_TXN_PER_MICROBLOCK*sizeof(fd_txn_p_t))>=sizeof(fd_microblock_trailer_t), poh_shred_mtu );
282 0 : FD_STATIC_ASSERT( MAX_MICROBLOCK_SZ-(MAX_TXN_PER_MICROBLOCK*sizeof(fd_txn_p_t))>=sizeof(fd_microblock_bank_trailer_t), poh_shred_mtu );
283 :
284 : /* We have a race window with the GUI, where if the slot is ending it
285 : will snap these metrics to draw the waterfall, but see them outdated
286 : because housekeeping hasn't run. For now just update them here, but
287 : PoH should eventually flush the pipeline before ending the slot. */
288 0 : metrics_write( ctx );
289 :
290 0 : ulong bank_sig = fd_disco_bank_sig( slot, ctx->_pack_idx );
291 :
292 : /* We always need to publish, even if there are no successfully executed
293 : transactions so the PoH tile can keep an accurate count of microblocks
294 : it has seen. */
295 0 : ulong new_sz = txn_cnt*sizeof(fd_txn_p_t) + sizeof(fd_microblock_trailer_t);
296 0 : fd_stem_publish( stem, 0UL, bank_sig, ctx->out_chunk, new_sz, 0UL, 0UL, (ulong)fd_frag_meta_ts_comp( tickcount ) );
297 0 : ctx->out_chunk = fd_dcache_compact_next( ctx->out_chunk, new_sz, ctx->out_chunk0, ctx->out_wmark );
298 0 : }
299 :
300 : static inline void
301 : handle_bundle( fd_bank_ctx_t * ctx,
302 : ulong seq,
303 : ulong sig,
304 : ulong sz,
305 : ulong begin_tspub,
306 0 : fd_stem_context_t * stem ) {
307 0 : uchar * dst = (uchar *)fd_chunk_to_laddr( ctx->out_mem, ctx->out_chunk );
308 0 : fd_txn_p_t * txns = (fd_txn_p_t *)dst;
309 :
310 0 : ulong slot = fd_disco_poh_sig_slot( sig );
311 0 : ulong txn_cnt = (sz-sizeof(fd_microblock_bank_trailer_t))/sizeof(fd_txn_p_t);
312 :
313 0 : fd_acct_addr_t const * writable_alt[ MAX_TXN_PER_MICROBLOCK ] = { NULL };
314 :
315 0 : int execution_success = 1;
316 0 : int transaction_err[ MAX_TXN_PER_MICROBLOCK ];
317 0 : for( ulong i=0UL; i<txn_cnt; i++ ) transaction_err[ i ] = 40; /* Pack interprets this as BUNDLE_PEER due to Frankendancer*/
318 :
319 0 : uint actual_execution_cus [ MAX_TXN_PER_MICROBLOCK ] = { 0U };
320 0 : uint actual_acct_data_cus [ MAX_TXN_PER_MICROBLOCK ] = { 0U };
321 0 : ulong out_timestamps [ 4*MAX_TXN_PER_MICROBLOCK ] = { 0U };
322 0 : ulong tips [ MAX_TXN_PER_MICROBLOCK ] = { 0U };
323 :
324 0 : for( ulong i=0UL; i<txn_cnt; i++ ) {
325 0 : fd_txn_p_t * txn = txns+i;
326 :
327 0 : fd_exec_txn_ctx_t txn_ctx[ 1 ]; // TODO ... bank manager ?
328 0 : txn->flags &= ~(FD_TXN_P_FLAGS_SANITIZE_SUCCESS | FD_TXN_P_FLAGS_EXECUTE_SUCCESS);
329 0 : int err = fd_runtime_prepare_and_execute_txn( NULL, ULONG_MAX, txn_ctx, txn, NULL, NULL ); /* TODO ... */
330 :
331 0 : transaction_err[ i ] = err;
332 0 : if( FD_UNLIKELY( err ) ) {
333 0 : execution_success = 0;
334 0 : break;
335 0 : }
336 :
337 : /* The account keys in the transaction context are laid out such
338 : that first the non-alt accounts are laid out, then the writable
339 : alt accounts, and finally the read-only alt accounts. */
340 0 : fd_txn_t * txn_descriptor = TXN( &txn_ctx->txn );
341 0 : for( ushort i=txn_descriptor->acct_addr_cnt; i<txn_descriptor->acct_addr_cnt+txn_descriptor->addr_table_adtl_writable_cnt; i++ ) {
342 0 : writable_alt[ i ] = fd_type_pun_const( &txn_ctx->account_keys[ i ] );
343 0 : }
344 :
345 0 : txn->flags |= FD_TXN_P_FLAGS_SANITIZE_SUCCESS;
346 0 : actual_execution_cus[ i ] = (uint)(txn_ctx->compute_budget_details.compute_unit_limit - txn_ctx->compute_budget_details.compute_meter);
347 0 : actual_acct_data_cus[ i ] = (uint)(txn_ctx->loaded_accounts_data_size);
348 0 : (void)tips; // TODO: GUI, report tips
349 0 : (void)out_timestamps; // TODO: GUI, report timestamps
350 0 : }
351 :
352 0 : for( ulong i=0UL; i<txn_cnt; i++ ) ctx->metrics.txn_result[ fd_bank_err_from_runtime_err( transaction_err[ i ] ) ]++;
353 :
354 0 : if( FD_LIKELY( execution_success ) ) {
355 0 : for( ulong i=0UL; i<txn_cnt; i++ ) {
356 0 : txns[ i ].flags |= FD_TXN_P_FLAGS_EXECUTE_SUCCESS;
357 0 : txns[ i ].flags = (txns[ i ].flags & 0x00FFFFFFU); /* Clear error bits to indicate success */
358 0 : }
359 0 : } else {
360 : /* If any transaction fails in a bundle ... they all fail */
361 0 : for( ulong i=0UL; i<txn_cnt; i++ ) {
362 0 : fd_txn_p_t * txn = txns+i;
363 :
364 0 : if( FD_UNLIKELY( !(txn->flags & FD_TXN_P_FLAGS_SANITIZE_SUCCESS) ) ) continue;
365 0 : txn->flags &= ~FD_TXN_P_FLAGS_EXECUTE_SUCCESS;
366 0 : txn->flags = (txn->flags & 0x00FFFFFFU) | ((uint)(-transaction_err[ i ])<<24);
367 0 : }
368 0 : }
369 :
370 : /* Indicate to pack tile we are done processing the transactions so
371 : it can pack new microblocks using these accounts. */
372 0 : fd_fseq_update( ctx->busy_fseq, seq );
373 :
374 0 : uint consumed_cus[ MAX_TXN_PER_MICROBLOCK ] = { 0U };
375 :
376 0 : for( ulong i=0UL; i<txn_cnt; i++ ) {
377 0 : fd_txn_p_t * txn = txns+i;
378 :
379 0 : uint requested_exec_plus_acct_data_cus = txn->pack_cu.requested_exec_plus_acct_data_cus;
380 0 : uint non_execution_cus = txn->pack_cu.non_execution_cus;
381 :
382 0 : if( FD_UNLIKELY( fd_txn_is_simple_vote_transaction( TXN(txns + i), txns[ i ].payload ) ) ) {
383 : /* Although bundles dont typically contain simple votes, we want
384 : to charge them correctly anyways. */
385 0 : consumed_cus[ i ] = FD_PACK_VOTE_DEFAULT_COMPUTE_UNITS;
386 0 : } else {
387 : /* Note that some transactions will have 0 consumed cus because
388 : they were never actually executed, due to an earlier
389 : transaction failing. */
390 0 : consumed_cus[ i ] = actual_execution_cus[ i ] + actual_acct_data_cus[ i ];
391 0 : }
392 :
393 : /* Assume failure, set below if success. If it doesn't land in the
394 : block, rebate the non-execution CUs too. */
395 0 : txn->bank_cu.rebated_cus = requested_exec_plus_acct_data_cus + non_execution_cus;
396 :
397 : /* We want to include consumed CUs for failed bundles for
398 : monitoring, even though they aren't included in the block. This
399 : is safe because the poh tile first checks if a txn is included in
400 : the block before counting its "actual_consumed_cus" towards the
401 : block tally. */
402 0 : txn->bank_cu.actual_consumed_cus = non_execution_cus + consumed_cus[ i ];
403 :
404 0 : if( FD_LIKELY( execution_success ) ) {
405 0 : if( FD_UNLIKELY( consumed_cus[ i ] > requested_exec_plus_acct_data_cus ) ) {
406 0 : FD_LOG_HEXDUMP_WARNING(( "txn", txn->payload, txn->payload_sz ));
407 0 : FD_LOG_ERR(( "transaction %lu in bundle consumed %u CUs > requested %u CUs", i, consumed_cus[ i ], requested_exec_plus_acct_data_cus ));
408 0 : }
409 :
410 0 : txn->bank_cu.actual_consumed_cus = non_execution_cus + consumed_cus[ i ];
411 0 : txn->bank_cu.rebated_cus = requested_exec_plus_acct_data_cus - consumed_cus[ i ];
412 0 : }
413 0 : }
414 :
415 0 : fd_pack_rebate_sum_add_txn( ctx->rebater, txns, writable_alt, txn_cnt );
416 :
417 : /* We need to publish each transaction separately into its own
418 : microblock, so make a temporary copy on the stack so we can move
419 : all the data around. */
420 0 : fd_txn_p_t bundle_txn_temp[ 5UL ];
421 0 : for( ulong i=0UL; i<txn_cnt; i++ ) {
422 0 : bundle_txn_temp[ i ] = txns[ i ];
423 0 : }
424 :
425 0 : for( ulong i=0UL; i<txn_cnt; i++ ) {
426 0 : uchar * dst = (uchar *)fd_chunk_to_laddr( ctx->out_mem, ctx->out_chunk );
427 0 : fd_memcpy( dst, bundle_txn_temp+i, sizeof(fd_txn_p_t) );
428 :
429 0 : fd_microblock_trailer_t * trailer = (fd_microblock_trailer_t *)( dst+sizeof(fd_txn_p_t) );
430 0 : hash_transactions( ctx->bmtree, (fd_txn_p_t*)dst, 1UL, trailer->hash );
431 0 : trailer->pack_txn_idx = ctx->_txn_idx + i;
432 0 : trailer->tips = tips[ i ];
433 :
434 0 : ulong bank_sig = fd_disco_bank_sig( slot, ctx->_pack_idx+i );
435 :
436 0 : long tickcount = fd_tickcount();
437 0 : long microblock_start_ticks = fd_frag_meta_ts_decomp( begin_tspub, tickcount );
438 0 : long microblock_duration_ticks = fd_long_max(tickcount - microblock_start_ticks, 0L);
439 :
440 0 : long tx_start_ticks = (long)out_timestamps[ 4*i + 0 ];
441 0 : long tx_load_end_ticks = (long)out_timestamps[ 4*i + 1 ];
442 0 : long tx_end_ticks = (long)out_timestamps[ 4*i + 2 ];
443 0 : long tx_preload_end_ticks = (long)out_timestamps[ 4*i + 3 ];
444 :
445 0 : trailer->txn_start_pct = (uchar)(((double)(tx_start_ticks - microblock_start_ticks) * (double)UCHAR_MAX) / (double)microblock_duration_ticks);
446 0 : trailer->txn_load_end_pct = (uchar)(((double)(tx_load_end_ticks - microblock_start_ticks) * (double)UCHAR_MAX) / (double)microblock_duration_ticks);
447 0 : trailer->txn_end_pct = (uchar)(((double)(tx_end_ticks - microblock_start_ticks) * (double)UCHAR_MAX) / (double)microblock_duration_ticks);
448 0 : trailer->txn_preload_end_pct = (uchar)(((double)(tx_preload_end_ticks - microblock_start_ticks) * (double)UCHAR_MAX) / (double)microblock_duration_ticks);
449 :
450 0 : ulong new_sz = sizeof(fd_txn_p_t) + sizeof(fd_microblock_trailer_t);
451 0 : fd_stem_publish( stem, 0UL, bank_sig, ctx->out_chunk, new_sz, 0UL, 0UL, (ulong)fd_frag_meta_ts_comp( tickcount ) );
452 0 : ctx->out_chunk = fd_dcache_compact_next( ctx->out_chunk, new_sz, ctx->out_chunk0, ctx->out_wmark );
453 0 : }
454 :
455 0 : metrics_write( ctx );
456 0 : }
457 :
458 : static inline void
459 : after_frag( fd_bank_ctx_t * ctx,
460 : ulong in_idx,
461 : ulong seq,
462 : ulong sig,
463 : ulong sz,
464 : ulong tsorig,
465 : ulong tspub,
466 0 : fd_stem_context_t * stem ) {
467 0 : (void)in_idx;
468 :
469 0 : ulong slot = fd_disco_poh_sig_slot( sig );
470 0 : if( FD_UNLIKELY( slot!=ctx->rebates_for_slot ) ) {
471 : /* If pack has already moved on to a new slot, the rebates are no
472 : longer useful. */
473 0 : fd_pack_rebate_sum_clear( ctx->rebater );
474 0 : ctx->rebates_for_slot = slot;
475 0 : }
476 :
477 0 : if( FD_UNLIKELY( ctx->_is_bundle ) ) handle_bundle( ctx, seq, sig, sz, tspub, stem );
478 0 : else handle_microblock( ctx, seq, sig, sz, tspub, stem );
479 :
480 : /* TODO: Use fancier logic to coalesce rebates e.g. and move this to
481 : after_credit */
482 0 : ulong written_sz = 0UL;
483 0 : while( 0UL!=(written_sz=fd_pack_rebate_sum_report( ctx->rebater, fd_chunk_to_laddr( ctx->rebate_mem, ctx->rebate_chunk ) )) ) {
484 0 : ulong tspub = (ulong)fd_frag_meta_ts_comp( fd_tickcount() );
485 0 : fd_stem_publish( stem, 1UL, slot, ctx->rebate_chunk, written_sz, 0UL, tsorig, tspub );
486 0 : ctx->rebate_chunk = fd_dcache_compact_next( ctx->rebate_chunk, written_sz, ctx->rebate_chunk0, ctx->rebate_wmark );
487 0 : }
488 0 : }
489 :
490 : static void
491 : unprivileged_init( fd_topo_t * topo,
492 0 : fd_topo_tile_t * tile ) {
493 0 : void * scratch = fd_topo_obj_laddr( topo, tile->tile_obj_id );
494 :
495 0 : FD_SCRATCH_ALLOC_INIT( l, scratch );
496 0 : fd_bank_ctx_t * ctx = FD_SCRATCH_ALLOC_APPEND( l, alignof( fd_bank_ctx_t ), sizeof( fd_bank_ctx_t ) );
497 0 : void * blake3 = FD_SCRATCH_ALLOC_APPEND( l, FD_BLAKE3_ALIGN, FD_BLAKE3_FOOTPRINT );
498 0 : void * bmtree = FD_SCRATCH_ALLOC_APPEND( l, FD_BMTREE_COMMIT_ALIGN, FD_BMTREE_COMMIT_FOOTPRINT(0) );
499 0 : void * exec_spad = FD_SCRATCH_ALLOC_APPEND( l, FD_SPAD_ALIGN, FD_SPAD_FOOTPRINT( FD_RUNTIME_TRANSACTION_EXECUTION_FOOTPRINT_DEFAULT ) );
500 0 : void * _txncache = FD_SCRATCH_ALLOC_APPEND( l, fd_txncache_align(), fd_txncache_footprint( tile->bank.max_live_slots ) );
501 :
502 0 : #define NONNULL( x ) (__extension__({ \
503 0 : __typeof__((x)) __x = (x); \
504 0 : if( FD_UNLIKELY( !__x ) ) FD_LOG_ERR(( #x " was unexpectedly NULL" )); \
505 0 : __x; }))
506 :
507 0 : ctx->kind_id = tile->kind_id;
508 0 : ctx->blake3 = NONNULL( fd_blake3_join( fd_blake3_new( blake3 ) ) );
509 0 : ctx->bmtree = NONNULL( bmtree );
510 0 : ctx->exec_spad = NONNULL( fd_spad_join( fd_spad_new( exec_spad, FD_RUNTIME_TRANSACTION_EXECUTION_FOOTPRINT_DEFAULT ) ) );
511 :
512 0 : NONNULL( fd_pack_rebate_sum_join( fd_pack_rebate_sum_new( ctx->rebater ) ) );
513 0 : ctx->rebates_for_slot = 0UL;
514 :
515 0 : NONNULL( fd_exec_txn_ctx_join( fd_exec_txn_ctx_new( ctx->txn_ctx ), ctx->exec_spad, fd_wksp_containing( exec_spad ) ) );
516 0 : ctx->txn_ctx->bank_hash_cmp = NULL; /* TODO - do we need this? */
517 0 : ctx->txn_ctx->spad = ctx->exec_spad;
518 0 : ctx->txn_ctx->spad_wksp = fd_wksp_containing( exec_spad );
519 0 : NONNULL( fd_funk_join( ctx->txn_ctx->funk, fd_topo_obj_laddr( topo, tile->bank.funk_obj_id ) ) );
520 :
521 0 : void * _txncache_shmem = fd_topo_obj_laddr( topo, tile->bank.txncache_obj_id );
522 0 : fd_txncache_shmem_t * txncache_shmem = fd_txncache_shmem_join( _txncache_shmem );
523 0 : FD_TEST( txncache_shmem );
524 0 : fd_txncache_t * txncache = fd_txncache_join( fd_txncache_new( _txncache, txncache_shmem ) );
525 0 : FD_TEST( txncache );
526 0 : ctx->txn_ctx->status_cache = txncache;
527 :
528 0 : ulong banks_obj_id = fd_pod_queryf_ulong( topo->props, ULONG_MAX, "banks" );
529 0 : FD_TEST( banks_obj_id!=ULONG_MAX );
530 0 : ctx->banks = NONNULL( fd_banks_join( fd_topo_obj_laddr( topo, banks_obj_id ) ) );
531 :
532 0 : ulong busy_obj_id = fd_pod_queryf_ulong( topo->props, ULONG_MAX, "bank_busy.%lu", tile->kind_id );
533 0 : FD_TEST( busy_obj_id!=ULONG_MAX );
534 0 : ctx->busy_fseq = fd_fseq_join( fd_topo_obj_laddr( topo, busy_obj_id ) );
535 0 : if( FD_UNLIKELY( !ctx->busy_fseq ) ) FD_LOG_ERR(( "banking tile %lu has no busy flag", tile->kind_id ));
536 :
537 0 : memset( &ctx->metrics, 0, sizeof( ctx->metrics ) );
538 :
539 0 : ctx->pack_in_mem = topo->workspaces[ topo->objs[ topo->links[ tile->in_link_id[ 0UL ] ].dcache_obj_id ].wksp_id ].wksp;
540 0 : ctx->pack_in_chunk0 = fd_dcache_compact_chunk0( ctx->pack_in_mem, topo->links[ tile->in_link_id[ 0UL ] ].dcache );
541 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 );
542 :
543 0 : ctx->out_mem = topo->workspaces[ topo->objs[ topo->links[ tile->out_link_id[ 0 ] ].dcache_obj_id ].wksp_id ].wksp;
544 0 : ctx->out_chunk0 = fd_dcache_compact_chunk0( ctx->out_mem, topo->links[ tile->out_link_id[ 0 ] ].dcache );
545 0 : ctx->out_wmark = fd_dcache_compact_wmark ( ctx->out_mem, topo->links[ tile->out_link_id[ 0 ] ].dcache, topo->links[ tile->out_link_id[ 0 ] ].mtu );
546 0 : ctx->out_chunk = ctx->out_chunk0;
547 :
548 :
549 0 : ctx->rebate_mem = topo->workspaces[ topo->objs[ topo->links[ tile->out_link_id[ 1 ] ].dcache_obj_id ].wksp_id ].wksp;
550 0 : ctx->rebate_chunk0 = fd_dcache_compact_chunk0( ctx->rebate_mem, topo->links[ tile->out_link_id[ 1 ] ].dcache );
551 0 : ctx->rebate_wmark = fd_dcache_compact_wmark ( ctx->rebate_mem, topo->links[ tile->out_link_id[ 1 ] ].dcache, topo->links[ tile->out_link_id[ 1 ] ].mtu );
552 0 : ctx->rebate_chunk = ctx->rebate_chunk0;
553 0 : }
554 :
555 : static ulong
556 : populate_allowed_seccomp( fd_topo_t const * topo,
557 : fd_topo_tile_t const * tile,
558 : ulong out_cnt,
559 0 : struct sock_filter * out ) {
560 0 : (void)topo;
561 0 : (void)tile;
562 :
563 0 : populate_sock_filter_policy_fd_bank_tile( out_cnt, out, (uint)fd_log_private_logfile_fd() );
564 0 : return sock_filter_policy_fd_bank_tile_instr_cnt;
565 0 : }
566 :
567 : static ulong
568 : populate_allowed_fds( fd_topo_t const * topo,
569 : fd_topo_tile_t const * tile,
570 : ulong out_fds_cnt,
571 0 : int * out_fds ) {
572 0 : (void)topo;
573 0 : (void)tile;
574 :
575 0 : if( FD_UNLIKELY( out_fds_cnt<2UL ) ) FD_LOG_ERR(( "out_fds_cnt %lu", out_fds_cnt ));
576 :
577 0 : ulong out_cnt = 0UL;
578 0 : out_fds[ out_cnt++ ] = 2; /* stderr */
579 0 : if( FD_LIKELY( -1!=fd_log_private_logfile_fd() ) )
580 0 : out_fds[ out_cnt++ ] = fd_log_private_logfile_fd(); /* logfile */
581 0 : return out_cnt;
582 0 : }
583 :
584 : /* For a bundle, one bundle might burst into at most 5 separate PoH mixins, since the
585 : microblocks cannot be conflicting. */
586 :
587 0 : #define STEM_BURST (5UL)
588 :
589 : /* See explanation in fd_pack */
590 0 : #define STEM_LAZY (128L*3000L)
591 :
592 0 : #define STEM_CALLBACK_CONTEXT_TYPE fd_bank_ctx_t
593 0 : #define STEM_CALLBACK_CONTEXT_ALIGN alignof(fd_bank_ctx_t)
594 :
595 0 : #define STEM_CALLBACK_METRICS_WRITE metrics_write
596 0 : #define STEM_CALLBACK_BEFORE_FRAG before_frag
597 0 : #define STEM_CALLBACK_DURING_FRAG during_frag
598 0 : #define STEM_CALLBACK_AFTER_FRAG after_frag
599 :
600 : #include "../../disco/stem/fd_stem.c"
601 :
602 : fd_topo_run_tile_t fd_tile_bank = {
603 : .name = "bank",
604 : .populate_allowed_seccomp = populate_allowed_seccomp,
605 : .populate_allowed_fds = populate_allowed_fds,
606 : .scratch_align = scratch_align,
607 : .scratch_footprint = scratch_footprint,
608 : .unprivileged_init = unprivileged_init,
609 : .run = stem_run,
610 : };
|