Line data Source code
1 : #include "../../../../disco/tiles.h"
2 :
3 : #include "../../../../disco/pack/fd_pack.h"
4 : #include "../../../../disco/pack/fd_pack_cost.h"
5 : #include "../../../../ballet/blake3/fd_blake3.h"
6 : #include "../../../../ballet/bmtree/fd_bmtree.h"
7 : #include "../../../../disco/metrics/fd_metrics.h"
8 : #include "../../../../disco/topo/fd_pod_format.h"
9 : #include "../../../../disco/bank/fd_bank_abi.h"
10 : #include "../../../../disco/metrics/generated/fd_metrics_bank.h"
11 :
12 0 : #define FD_BANK_TRANSACTION_LANDED 1
13 : #define FD_BANK_TRANSACTION_EXECUTED 2
14 :
15 : typedef struct {
16 : ulong kind_id;
17 :
18 : fd_blake3_t * blake3;
19 : void * bmtree;
20 :
21 : uchar * txn_abi_mem;
22 : uchar * txn_sidecar_mem;
23 :
24 : void const * _bank;
25 : ulong _microblock_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 : struct {
40 : ulong slot_acquire[ 3 ];
41 :
42 : ulong txn_load_address_lookup_tables[ 6 ];
43 : ulong transaction_result[ 40 ];
44 : ulong processing_failed;
45 : ulong fee_only;
46 : ulong exec_failed;
47 : ulong success;
48 : } metrics;
49 : } fd_bank_ctx_t;
50 :
51 : FD_FN_CONST static inline ulong
52 12 : scratch_align( void ) {
53 12 : return 128UL;
54 12 : }
55 :
56 : FD_FN_PURE static inline ulong
57 12 : scratch_footprint( fd_topo_tile_t const * tile ) {
58 12 : (void)tile;
59 12 : ulong l = FD_LAYOUT_INIT;
60 12 : l = FD_LAYOUT_APPEND( l, alignof( fd_bank_ctx_t ), sizeof( fd_bank_ctx_t ) );
61 12 : l = FD_LAYOUT_APPEND( l, FD_BLAKE3_ALIGN, FD_BLAKE3_FOOTPRINT );
62 12 : l = FD_LAYOUT_APPEND( l, FD_BMTREE_COMMIT_ALIGN, FD_BMTREE_COMMIT_FOOTPRINT(0) );
63 12 : l = FD_LAYOUT_APPEND( l, FD_BANK_ABI_TXN_ALIGN, MAX_TXN_PER_MICROBLOCK*FD_BANK_ABI_TXN_FOOTPRINT );
64 12 : l = FD_LAYOUT_APPEND( l, FD_BANK_ABI_TXN_ALIGN, FD_BANK_ABI_TXN_FOOTPRINT_SIDECAR_MAX );
65 12 : return FD_LAYOUT_FINI( l, scratch_align() );
66 12 : }
67 :
68 : static inline void
69 0 : metrics_write( fd_bank_ctx_t * ctx ) {
70 0 : FD_MCNT_ENUM_COPY( BANK, SLOT_ACQUIRE, ctx->metrics.slot_acquire );
71 :
72 0 : FD_MCNT_ENUM_COPY( BANK, TRANSACTION_LOAD_ADDRESS_TABLES, ctx->metrics.txn_load_address_lookup_tables );
73 0 : FD_MCNT_ENUM_COPY( BANK, TRANSACTION_RESULT, ctx->metrics.transaction_result );
74 :
75 0 : FD_MCNT_SET( BANK, PROCESSING_FAILED, ctx->metrics.processing_failed );
76 0 : FD_MCNT_SET( BANK, FEE_ONLY_TRANSACTIONS, ctx->metrics.fee_only );
77 0 : FD_MCNT_SET( BANK, EXECUTED_FAILED_TRANSACTIONS, ctx->metrics.exec_failed );
78 0 : FD_MCNT_SET( BANK, SUCCESSFUL_TRANSACTIONS, ctx->metrics.success );
79 0 : }
80 :
81 : static int
82 : before_frag( fd_bank_ctx_t * ctx,
83 : ulong in_idx,
84 : ulong seq,
85 0 : ulong sig ) {
86 0 : (void)in_idx;
87 0 : (void)seq;
88 :
89 : /* Pack also outputs "leader slot done" which we can ignore. */
90 0 : if( FD_UNLIKELY( fd_disco_poh_sig_pkt_type( sig )!=POH_PKT_TYPE_MICROBLOCK ) ) return 1;
91 :
92 0 : ulong target_bank_idx = fd_disco_poh_sig_bank_tile( sig );
93 0 : if( FD_UNLIKELY( target_bank_idx!=ctx->kind_id ) ) return 1;
94 :
95 0 : return 0;
96 0 : }
97 :
98 : extern void * fd_ext_bank_pre_balance_info( void const * bank, void * txns, ulong txn_cnt );
99 : extern int fd_ext_bank_execute_and_commit_bundle( void const * bank, void * txns, ulong txn_cnt, int * out_transaction_err, uint * actual_execution_cus, uint * actual_acct_data_cus );
100 : extern void * fd_ext_bank_load_and_execute_txns( void const * bank, void * txns, ulong txn_cnt, int * out_processing_results, int * out_transaction_err, uint * out_consumed_exec_cus, uint * out_consumed_acct_data_cus );
101 : extern void fd_ext_bank_commit_txns( void const * bank, void const * txns, ulong txn_cnt , void * load_and_execute_output, void * pre_balance_info );
102 : extern void fd_ext_bank_release_thunks( void * load_and_execute_output );
103 : extern void fd_ext_bank_release_pre_balance_info( void * pre_balance_info );
104 : extern int fd_ext_bank_verify_precompiles( void const * bank, void const * txn );
105 :
106 : static inline void
107 : during_frag( fd_bank_ctx_t * ctx,
108 : ulong in_idx FD_PARAM_UNUSED,
109 : ulong seq FD_PARAM_UNUSED,
110 : ulong sig FD_PARAM_UNUSED,
111 : ulong chunk,
112 : ulong sz,
113 0 : ulong ctl FD_PARAM_UNUSED ) {
114 :
115 0 : uchar * src = (uchar *)fd_chunk_to_laddr( ctx->pack_in_mem, chunk );
116 0 : uchar * dst = (uchar *)fd_chunk_to_laddr( ctx->out_mem, ctx->out_chunk );
117 :
118 0 : if( FD_UNLIKELY( chunk<ctx->pack_in_chunk0 || chunk>ctx->pack_in_wmark || sz>USHORT_MAX ) )
119 0 : FD_LOG_ERR(( "chunk %lu %lu corrupt, not in range [%lu,%lu]", chunk, sz, ctx->pack_in_chunk0, ctx->pack_in_wmark ));
120 :
121 0 : fd_memcpy( dst, src, sz-sizeof(fd_microblock_bank_trailer_t) );
122 0 : fd_microblock_bank_trailer_t * trailer = (fd_microblock_bank_trailer_t *)( src+sz-sizeof(fd_microblock_bank_trailer_t) );
123 0 : ctx->_bank = trailer->bank;
124 0 : ctx->_microblock_idx = trailer->microblock_idx;
125 0 : ctx->_is_bundle = trailer->is_bundle;
126 0 : }
127 :
128 : static void
129 : hash_transactions( void * mem,
130 : fd_txn_p_t * txns,
131 : ulong txn_cnt,
132 0 : uchar * mixin ) {
133 0 : fd_bmtree_commit_t * bmtree = fd_bmtree_commit_init( mem, 32UL, 1UL, 0UL );
134 0 : for( ulong i=0UL; i<txn_cnt; i++ ) {
135 0 : fd_txn_p_t * _txn = txns + i;
136 0 : if( FD_UNLIKELY( !(_txn->flags & FD_TXN_P_FLAGS_EXECUTE_SUCCESS) ) ) continue;
137 :
138 0 : fd_txn_t * txn = TXN(_txn);
139 0 : for( ulong j=0; j<txn->signature_cnt; j++ ) {
140 0 : fd_bmtree_node_t node[1];
141 0 : fd_bmtree_hash_leaf( node, _txn->payload+txn->signature_off+64UL*j, 64UL, 1UL );
142 0 : fd_bmtree_commit_append( bmtree, node, 1UL );
143 0 : }
144 0 : }
145 0 : uchar * root = fd_bmtree_commit_fini( bmtree );
146 0 : fd_memcpy( mixin, root, 32UL );
147 0 : }
148 :
149 : static inline void
150 : handle_microblock( fd_bank_ctx_t * ctx,
151 : ulong seq,
152 : ulong sig,
153 : ulong sz,
154 0 : fd_stem_context_t * stem ) {
155 0 : uchar * dst = (uchar *)fd_chunk_to_laddr( ctx->out_mem, ctx->out_chunk );
156 :
157 0 : ulong slot = fd_disco_poh_sig_slot( sig );
158 0 : ulong txn_cnt = (sz-sizeof(fd_microblock_bank_trailer_t))/sizeof(fd_txn_p_t);
159 :
160 0 : ulong sanitized_txn_cnt = 0UL;
161 0 : ulong sidecar_footprint_bytes = 0UL;
162 0 : for( ulong i=0UL; i<txn_cnt; i++ ) {
163 0 : fd_txn_p_t * txn = (fd_txn_p_t *)( dst + (i*sizeof(fd_txn_p_t)) );
164 :
165 0 : void * abi_txn = ctx->txn_abi_mem + (sanitized_txn_cnt*FD_BANK_ABI_TXN_FOOTPRINT);
166 0 : void * abi_txn_sidecar = ctx->txn_sidecar_mem + sidecar_footprint_bytes;
167 0 : txn->flags &= ~FD_TXN_P_FLAGS_SANITIZE_SUCCESS;
168 :
169 0 : int result = fd_bank_abi_txn_init( abi_txn, abi_txn_sidecar, ctx->_bank, slot, ctx->blake3, txn->payload, txn->payload_sz, TXN(txn), !!(txn->flags & FD_TXN_P_FLAGS_IS_SIMPLE_VOTE) );
170 0 : ctx->metrics.txn_load_address_lookup_tables[ result ]++;
171 0 : if( FD_UNLIKELY( result!=FD_BANK_ABI_TXN_INIT_SUCCESS ) ) continue;
172 :
173 0 : int precompile_result = fd_ext_bank_verify_precompiles( ctx->_bank, abi_txn );
174 0 : if( FD_UNLIKELY( precompile_result ) ) {
175 0 : FD_MCNT_INC( BANK, PRECOMPILE_VERIFY_FAILURE, 1 );
176 0 : continue;
177 0 : }
178 :
179 0 : txn->flags |= FD_TXN_P_FLAGS_SANITIZE_SUCCESS;
180 :
181 0 : fd_txn_t * txn1 = TXN(txn);
182 0 : sidecar_footprint_bytes += FD_BANK_ABI_TXN_FOOTPRINT_SIDECAR( txn1->acct_addr_cnt, txn1->addr_table_adtl_cnt, txn1->instr_cnt, txn1->addr_table_lookup_cnt );
183 0 : sanitized_txn_cnt++;
184 0 : }
185 :
186 : /* Just because a transaction was executed doesn't mean it succeeded,
187 : but all executed transactions get committed. */
188 0 : int processing_results [ MAX_TXN_PER_MICROBLOCK ] = { 0 };
189 0 : int transaction_err [ MAX_TXN_PER_MICROBLOCK ] = { 0 };
190 0 : uint consumed_exec_cus [ MAX_TXN_PER_MICROBLOCK ] = { 0U };
191 0 : uint consumed_acct_data_cus[ MAX_TXN_PER_MICROBLOCK ] = { 0U };
192 :
193 0 : void * pre_balance_info = fd_ext_bank_pre_balance_info( ctx->_bank, ctx->txn_abi_mem, sanitized_txn_cnt );
194 :
195 0 : void * load_and_execute_output = fd_ext_bank_load_and_execute_txns( ctx->_bank,
196 0 : ctx->txn_abi_mem,
197 0 : sanitized_txn_cnt,
198 0 : processing_results,
199 0 : transaction_err,
200 0 : consumed_exec_cus,
201 0 : consumed_acct_data_cus );
202 :
203 0 : ulong sanitized_idx = 0UL;
204 0 : for( ulong i=0UL; i<txn_cnt; i++ ) {
205 0 : fd_txn_p_t * txn = (fd_txn_p_t *)( dst + (i*sizeof(fd_txn_p_t)) );
206 :
207 0 : uint requested_exec_plus_acct_data_cus = txn->pack_cu.requested_exec_plus_acct_data_cus;
208 0 : uint non_execution_cus = txn->pack_cu.non_execution_cus;
209 :
210 : /* Assume failure, set below if success. If it doesn't land cin the
211 : block, rebate the non-execution CUs too. */
212 0 : txn->bank_cu.rebated_cus = requested_exec_plus_acct_data_cus + non_execution_cus;
213 0 : txn->flags &= ~FD_TXN_P_FLAGS_EXECUTE_SUCCESS;
214 0 : if( FD_UNLIKELY( !(txn->flags & FD_TXN_P_FLAGS_SANITIZE_SUCCESS) ) ) continue;
215 :
216 0 : sanitized_idx++;
217 :
218 0 : int is_simple_vote = 0;
219 0 : if( FD_UNLIKELY( is_simple_vote = fd_txn_is_simple_vote_transaction( TXN(txn), txn->payload ) ) ) {
220 : /* Simple votes are charged fixed amounts of compute regardless of
221 : the real cost they incur. fd_ext_bank_load_and_execute_txns
222 : returns the real cost, however, so we override it here. */
223 0 : consumed_exec_cus[ sanitized_idx-1UL ] = FD_PACK_VOTE_DEFAULT_COMPUTE_UNITS;
224 0 : consumed_acct_data_cus[ sanitized_idx-1UL ] = 0;
225 0 : }
226 :
227 : /* Stash the result in the flags value so that pack can inspect it.
228 : */
229 0 : txn->flags = (txn->flags & 0x00FFFFFFU) | ((uint)transaction_err[ sanitized_idx-1UL ]<<24);
230 :
231 0 : ctx->metrics.transaction_result[ transaction_err [ sanitized_idx-1UL ] ]++;
232 :
233 0 : ctx->metrics.processing_failed += (ulong)(processing_results[ sanitized_idx-1UL ]==0 );
234 0 : ctx->metrics.fee_only += (ulong)(processing_results[ sanitized_idx-1UL ]==FD_BANK_TRANSACTION_LANDED);
235 :
236 0 : if( FD_UNLIKELY( !(processing_results[ sanitized_idx-1UL ] & FD_BANK_TRANSACTION_LANDED) ) ) continue;
237 :
238 0 : uint actual_execution_cus = consumed_exec_cus[ sanitized_idx-1UL ];
239 0 : uint actual_acct_data_cus = consumed_acct_data_cus[ sanitized_idx-1UL ];
240 :
241 : /* FeesOnly transactions are transactions that failed to load
242 : before they even reach the VM stage. They have zero execution
243 : cost but do charge for the account data they are able to load. */
244 0 : txn->bank_cu.actual_consumed_cus = non_execution_cus + actual_execution_cus + actual_acct_data_cus;
245 0 : txn->bank_cu.rebated_cus = requested_exec_plus_acct_data_cus - ( actual_execution_cus + actual_acct_data_cus );
246 :
247 : /* TXN_P_FLAGS_EXECUTE_SUCCESS means that it should be included in
248 : the block. It's a bit of a misnomer now that there are fee-only
249 : transactions. */
250 0 : txn->flags |= FD_TXN_P_FLAGS_EXECUTE_SUCCESS;
251 :
252 0 : if( FD_UNLIKELY( !(processing_results[ sanitized_idx-1UL ] & FD_BANK_TRANSACTION_EXECUTED) ) ) continue;
253 :
254 0 : if( transaction_err[ sanitized_idx-1UL ] ) ctx->metrics.exec_failed++;
255 0 : else ctx->metrics.success++;
256 :
257 : /* The VM will stop executing and fail an instruction immediately if
258 : it exceeds its requested CUs. A transaction which requests less
259 : account data than it actually consumes will fail in the account
260 : loading stage. ... todo ... enforce this check after SIMD-170
261 : is released. */
262 0 : if( FD_UNLIKELY( actual_execution_cus+actual_acct_data_cus > requested_exec_plus_acct_data_cus ) ) {
263 0 : FD_LOG_DEBUG(( "Actual CUs unexpectedly exceeded requested amount. actual_execution_cus (%u) actual_acct_data_cus "
264 0 : "(%u) requested_exec_plus_acct_data_cus (%u) is_simple_vote (%i) exec_failed (%i)",
265 0 : actual_execution_cus, actual_acct_data_cus, requested_exec_plus_acct_data_cus, is_simple_vote,
266 0 : transaction_err[ sanitized_idx-1UL ] ));
267 0 : FD_LOG_HEXDUMP_DEBUG(( "txn", txn->payload, txn->payload_sz ));
268 :
269 0 : actual_execution_cus = requested_exec_plus_acct_data_cus;
270 0 : actual_acct_data_cus = 0UL;
271 0 : }
272 :
273 0 : txn->bank_cu.actual_consumed_cus = non_execution_cus + actual_execution_cus + actual_acct_data_cus;
274 0 : txn->bank_cu.rebated_cus = requested_exec_plus_acct_data_cus - ( actual_execution_cus + actual_acct_data_cus );
275 0 : }
276 :
277 : /* Commit must succeed so no failure path. This function takes
278 : ownership of the load_and_execute_output and pre_balance_info heap
279 : allocations and will free them before it returns. They should not
280 : be reused. Once commit is called, the transactions MUST be mixed
281 : into the PoH otherwise we will fork and diverge, so the link from
282 : here til PoH mixin must be completely reliable with nothing dropped. */
283 0 : fd_ext_bank_commit_txns( ctx->_bank, ctx->txn_abi_mem, sanitized_txn_cnt, load_and_execute_output, pre_balance_info );
284 0 : pre_balance_info = NULL;
285 0 : load_and_execute_output = NULL;
286 :
287 : /* Indicate to pack tile we are done processing the transactions so
288 : it can pack new microblocks using these accounts. */
289 0 : fd_fseq_update( ctx->busy_fseq, seq );
290 :
291 : /* Now produce the merkle hash of the transactions for inclusion
292 : (mixin) to the PoH hash. This is done on the bank tile because
293 : it shards / scales horizontally here, while PoH does not. */
294 0 : fd_microblock_trailer_t * trailer = (fd_microblock_trailer_t *)( dst + txn_cnt*sizeof(fd_txn_p_t) );
295 0 : hash_transactions( ctx->bmtree, (fd_txn_p_t*)dst, txn_cnt, trailer->hash );
296 :
297 : /* MAX_MICROBLOCK_SZ - (MAX_TXN_PER_MICROBLOCK*sizeof(fd_txn_p_t)) == 64
298 : so there's always 64 extra bytes at the end to stash the hash. */
299 0 : FD_STATIC_ASSERT( MAX_MICROBLOCK_SZ-(MAX_TXN_PER_MICROBLOCK*sizeof(fd_txn_p_t))>=sizeof(fd_microblock_trailer_t), poh_shred_mtu );
300 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 );
301 :
302 : /* We have a race window with the GUI, where if the slot is ending it
303 : will snap these metrics to draw the waterfall, but see them outdated
304 : because housekeeping hasn't run. For now just update them here, but
305 : PoH should eventually flush the pipeline before ending the slot. */
306 0 : metrics_write( ctx );
307 :
308 0 : ulong bank_sig = fd_disco_bank_sig( slot, ctx->_microblock_idx );
309 :
310 : /* We always need to publish, even if there are no successfully executed
311 : transactions so the PoH tile can keep an accurate count of microblocks
312 : it has seen. */
313 0 : ulong tspub = (ulong)fd_frag_meta_ts_comp( fd_tickcount() );
314 0 : ulong new_sz = txn_cnt*sizeof(fd_txn_p_t) + sizeof(fd_microblock_trailer_t);
315 0 : fd_stem_publish( stem, 0UL, bank_sig, ctx->out_chunk, new_sz, 0UL, 0UL, tspub );
316 0 : ctx->out_chunk = fd_dcache_compact_next( ctx->out_chunk, new_sz, ctx->out_chunk0, ctx->out_wmark );
317 0 : }
318 :
319 : static inline void
320 : handle_bundle( fd_bank_ctx_t * ctx,
321 : ulong seq,
322 : ulong sig,
323 : ulong sz,
324 0 : fd_stem_context_t * stem ) {
325 0 : uchar * dst = (uchar *)fd_chunk_to_laddr( ctx->out_mem, ctx->out_chunk );
326 0 : fd_txn_p_t * txns = (fd_txn_p_t *)dst;
327 :
328 0 : ulong slot = fd_disco_poh_sig_slot( sig );
329 0 : ulong txn_cnt = (sz-sizeof(fd_microblock_bank_trailer_t))/sizeof(fd_txn_p_t);
330 :
331 0 : int execution_success = 1;
332 :
333 0 : ulong sidecar_footprint_bytes = 0UL;
334 0 : for( ulong i=0UL; i<txn_cnt; i++ ) {
335 0 : fd_txn_p_t * txn = txns+i;
336 :
337 0 : void * abi_txn = ctx->txn_abi_mem + (i*FD_BANK_ABI_TXN_FOOTPRINT);
338 0 : void * abi_txn_sidecar = ctx->txn_sidecar_mem + sidecar_footprint_bytes;
339 0 : txn->flags &= ~(FD_TXN_P_FLAGS_SANITIZE_SUCCESS | FD_TXN_P_FLAGS_EXECUTE_SUCCESS);
340 :
341 0 : int result = fd_bank_abi_txn_init( abi_txn, abi_txn_sidecar, ctx->_bank, slot, ctx->blake3, txn->payload, txn->payload_sz, TXN(txn), !!(txn->flags & FD_TXN_P_FLAGS_IS_SIMPLE_VOTE) );
342 0 : ctx->metrics.txn_load_address_lookup_tables[ result ]++;
343 0 : if( FD_UNLIKELY( result!=FD_BANK_ABI_TXN_INIT_SUCCESS ) ) {
344 0 : execution_success = 0;
345 0 : continue;
346 0 : }
347 :
348 0 : int precompile_result = fd_ext_bank_verify_precompiles( ctx->_bank, abi_txn );
349 0 : if( FD_UNLIKELY( precompile_result ) ) {
350 0 : execution_success = 0;
351 0 : FD_MCNT_INC( BANK, PRECOMPILE_VERIFY_FAILURE, 1 );
352 0 : continue;
353 0 : }
354 :
355 0 : txn->flags |= FD_TXN_P_FLAGS_SANITIZE_SUCCESS;
356 :
357 0 : fd_txn_t * txn1 = TXN(txn);
358 0 : sidecar_footprint_bytes += FD_BANK_ABI_TXN_FOOTPRINT_SIDECAR( txn1->acct_addr_cnt, txn1->addr_table_adtl_cnt, txn1->instr_cnt, txn1->addr_table_lookup_cnt );
359 0 : }
360 :
361 0 : int transaction_err [ MAX_TXN_PER_MICROBLOCK ];
362 0 : for( ulong i=0UL; i<txn_cnt; i++ ) transaction_err[ i ] = FD_METRICS_ENUM_TRANSACTION_ERROR_V_BUNDLE_PEER_IDX;
363 :
364 0 : uint actual_execution_cus[ MAX_TXN_PER_MICROBLOCK ] = { 0U };
365 0 : uint actual_acct_data_cus[ MAX_TXN_PER_MICROBLOCK ] = { 0U };
366 0 : uint consumed_cus [ MAX_TXN_PER_MICROBLOCK ] = { 0U };
367 0 : if( FD_LIKELY( execution_success ) ) {
368 0 : execution_success = fd_ext_bank_execute_and_commit_bundle( ctx->_bank, ctx->txn_abi_mem, txn_cnt, transaction_err, actual_execution_cus, actual_acct_data_cus );
369 0 : }
370 :
371 0 : if( FD_LIKELY( execution_success ) ) {
372 0 : ctx->metrics.success += txn_cnt;
373 0 : ctx->metrics.transaction_result[ FD_METRICS_ENUM_TRANSACTION_ERROR_V_SUCCESS_IDX ] += txn_cnt;
374 0 : for( ulong i=0UL; i<txn_cnt; i++ ) {
375 0 : txns[ i ].flags |= FD_TXN_P_FLAGS_EXECUTE_SUCCESS;
376 0 : txns[ i ].flags = (txns[ i ].flags & 0x00FFFFFFU); /* Clear error bits to indicate success */
377 0 : if( FD_UNLIKELY( fd_txn_is_simple_vote_transaction( TXN(txns + i), txns[ i ].payload ) ) ) {
378 : /* Although bundles dont typically contain simple votes, we want
379 : to charge them correctly anyways. */
380 0 : consumed_cus[ i ] = FD_PACK_VOTE_DEFAULT_COMPUTE_UNITS;
381 0 : } else {
382 0 : consumed_cus[ i ] = actual_execution_cus[ i ] + actual_acct_data_cus[ i ];
383 0 : }
384 0 : }
385 0 : } else {
386 : /* If any transaction fails in a bundle ... they all fail */
387 0 : for( ulong i=0UL; i<txn_cnt; i++ ) {
388 0 : fd_txn_p_t * txn = txns+i;
389 :
390 : /* If the budle failed, we want to set actual cus = requested
391 : so that the entire bundle is rebated. */
392 0 : consumed_cus[ i ] = txn->pack_cu.requested_exec_plus_acct_data_cus;
393 :
394 : /* Don't double count metrics for transactions that failed to
395 : sanitize. */
396 0 : if( FD_UNLIKELY( !(txn->flags & FD_TXN_P_FLAGS_SANITIZE_SUCCESS) ) ) continue;
397 :
398 0 : txn->flags &= ~FD_TXN_P_FLAGS_EXECUTE_SUCCESS;
399 0 : txn->flags = (txn->flags & 0x00FFFFFFU) | ((uint)transaction_err[ i ]<<24);
400 0 : ctx->metrics.processing_failed++;
401 0 : ctx->metrics.transaction_result[ transaction_err[ i ] ]++;
402 0 : }
403 0 : }
404 :
405 : /* Indicate to pack tile we are done processing the transactions so
406 : it can pack new microblocks using these accounts. */
407 0 : fd_fseq_update( ctx->busy_fseq, seq );
408 :
409 0 : for( ulong i=0UL; i<txn_cnt; i++ ) {
410 0 : fd_txn_p_t * txn = txns+i;
411 :
412 0 : uint requested_exec_plus_acct_data_cus = txn->pack_cu.requested_exec_plus_acct_data_cus;
413 0 : uint non_execution_cus = txn->pack_cu.non_execution_cus;
414 : /* Assume failure, set below if success. If it doesn't land in the
415 : block, rebate the non-execution CUs too. */
416 0 : txn->bank_cu.rebated_cus = requested_exec_plus_acct_data_cus + non_execution_cus;
417 :
418 0 : if( FD_LIKELY( (txn->flags & (FD_TXN_P_FLAGS_SANITIZE_SUCCESS|FD_TXN_P_FLAGS_EXECUTE_SUCCESS))==(FD_TXN_P_FLAGS_SANITIZE_SUCCESS|FD_TXN_P_FLAGS_EXECUTE_SUCCESS) ) ) {
419 0 : txn->bank_cu.actual_consumed_cus = non_execution_cus + consumed_cus[ i ];
420 : /* FD_TEST( consumed_cus[ i ] <= requested_exec_plus_acct_data_cus ); */
421 0 : if( FD_UNLIKELY( consumed_cus[ i ] > requested_exec_plus_acct_data_cus ) ) {
422 0 : FD_LOG_DEBUG(( "transaction %lu in bundle consumed %u CUs > requested %u CUs", i, consumed_cus[ i ], requested_exec_plus_acct_data_cus ));
423 0 : FD_LOG_HEXDUMP_DEBUG(( "txn", txn->payload, txn->payload_sz ));
424 0 : consumed_cus[ i ] = requested_exec_plus_acct_data_cus;
425 0 : }
426 0 : txn->bank_cu.rebated_cus = requested_exec_plus_acct_data_cus - consumed_cus[ i ];
427 0 : }
428 0 : }
429 :
430 : /* We need to publish each transaction separately into its own
431 : microblock, so make a temporary copy on the stack so we can move
432 : all the data around. */
433 0 : fd_txn_p_t bundle_txn_temp[ 5UL ];
434 0 : for( ulong i=0UL; i<txn_cnt; i++ ) {
435 0 : fd_memcpy( bundle_txn_temp+i, txns+i, sizeof(fd_txn_p_t) );
436 0 : }
437 :
438 0 : for( ulong i=0UL; i<txn_cnt; i++ ) {
439 0 : uchar * dst = (uchar *)fd_chunk_to_laddr( ctx->out_mem, ctx->out_chunk );
440 0 : fd_memcpy( dst, bundle_txn_temp+i, sizeof(fd_txn_p_t) );
441 :
442 0 : fd_microblock_trailer_t * trailer = (fd_microblock_trailer_t *)( dst+sizeof(fd_txn_p_t) );
443 0 : hash_transactions( ctx->bmtree, (fd_txn_p_t*)dst, 1UL, trailer->hash );
444 :
445 0 : ulong bank_sig = fd_disco_bank_sig( slot, ctx->_microblock_idx+i );
446 :
447 0 : ulong tspub = (ulong)fd_frag_meta_ts_comp( fd_tickcount() );
448 0 : ulong new_sz = sizeof(fd_txn_p_t) + sizeof(fd_microblock_trailer_t);
449 0 : fd_stem_publish( stem, 0UL, bank_sig, ctx->out_chunk, new_sz, 0UL, 0UL, tspub );
450 0 : ctx->out_chunk = fd_dcache_compact_next( ctx->out_chunk, new_sz, ctx->out_chunk0, ctx->out_wmark );
451 0 : }
452 :
453 0 : metrics_write( ctx );
454 0 : }
455 :
456 : static inline void
457 : after_frag( fd_bank_ctx_t * ctx,
458 : ulong in_idx,
459 : ulong seq,
460 : ulong sig,
461 : ulong sz,
462 : ulong tsorig,
463 0 : fd_stem_context_t * stem ) {
464 0 : (void)in_idx;
465 0 : (void)tsorig;
466 :
467 0 : if( FD_UNLIKELY( ctx->_is_bundle ) ) handle_bundle( ctx, seq, sig, sz, stem );
468 0 : else handle_microblock( ctx, seq, sig, sz, stem );
469 0 : }
470 :
471 : static void
472 : unprivileged_init( fd_topo_t * topo,
473 0 : fd_topo_tile_t * tile ) {
474 0 : void * scratch = fd_topo_obj_laddr( topo, tile->tile_obj_id );
475 :
476 0 : FD_SCRATCH_ALLOC_INIT( l, scratch );
477 0 : fd_bank_ctx_t * ctx = FD_SCRATCH_ALLOC_APPEND( l, alignof( fd_bank_ctx_t ), sizeof( fd_bank_ctx_t ) );
478 0 : void * blake3 = FD_SCRATCH_ALLOC_APPEND( l, FD_BLAKE3_ALIGN, FD_BLAKE3_FOOTPRINT );
479 0 : void * bmtree = FD_SCRATCH_ALLOC_APPEND( l, FD_BMTREE_COMMIT_ALIGN, FD_BMTREE_COMMIT_FOOTPRINT(0) );
480 0 : ctx->txn_abi_mem = FD_SCRATCH_ALLOC_APPEND( l, FD_BANK_ABI_TXN_ALIGN, MAX_TXN_PER_MICROBLOCK*FD_BANK_ABI_TXN_FOOTPRINT );
481 0 : ctx->txn_sidecar_mem = FD_SCRATCH_ALLOC_APPEND( l, FD_BANK_ABI_TXN_ALIGN, FD_BANK_ABI_TXN_FOOTPRINT_SIDECAR_MAX );
482 :
483 0 : #define NONNULL( x ) (__extension__({ \
484 0 : __typeof__((x)) __x = (x); \
485 0 : if( FD_UNLIKELY( !__x ) ) FD_LOG_ERR(( #x " was unexpectedly NULL" )); \
486 0 : __x; }))
487 :
488 0 : ctx->kind_id = tile->kind_id;
489 0 : ctx->blake3 = NONNULL( fd_blake3_join( fd_blake3_new( blake3 ) ) );
490 0 : ctx->bmtree = NONNULL( bmtree );
491 :
492 0 : ulong busy_obj_id = fd_pod_queryf_ulong( topo->props, ULONG_MAX, "bank_busy.%lu", tile->kind_id );
493 0 : FD_TEST( busy_obj_id!=ULONG_MAX );
494 0 : ctx->busy_fseq = fd_fseq_join( fd_topo_obj_laddr( topo, busy_obj_id ) );
495 0 : if( FD_UNLIKELY( !ctx->busy_fseq ) ) FD_LOG_ERR(( "banking tile %lu has no busy flag", tile->kind_id ));
496 :
497 0 : memset( &ctx->metrics, 0, sizeof( ctx->metrics ) );
498 :
499 0 : ctx->pack_in_mem = topo->workspaces[ topo->objs[ topo->links[ tile->in_link_id[ 0UL ] ].dcache_obj_id ].wksp_id ].wksp;
500 0 : ctx->pack_in_chunk0 = fd_dcache_compact_chunk0( ctx->pack_in_mem, topo->links[ tile->in_link_id[ 0UL ] ].dcache );
501 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 );
502 :
503 0 : ctx->out_mem = topo->workspaces[ topo->objs[ topo->links[ tile->out_link_id[ 0 ] ].dcache_obj_id ].wksp_id ].wksp;
504 0 : ctx->out_chunk0 = fd_dcache_compact_chunk0( ctx->out_mem, topo->links[ tile->out_link_id[ 0 ] ].dcache );
505 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 );
506 0 : ctx->out_chunk = ctx->out_chunk0;
507 0 : }
508 :
509 : /* For a bundle, one bundle might burst into at most 5 separate PoH mixins, since the
510 : microblocks cannot be conflicting. */
511 :
512 0 : #define STEM_BURST (5UL)
513 :
514 : /* See explanation in fd_pack */
515 0 : #define STEM_LAZY (128L*3000L)
516 :
517 0 : #define STEM_CALLBACK_CONTEXT_TYPE fd_bank_ctx_t
518 0 : #define STEM_CALLBACK_CONTEXT_ALIGN alignof(fd_bank_ctx_t)
519 :
520 0 : #define STEM_CALLBACK_METRICS_WRITE metrics_write
521 0 : #define STEM_CALLBACK_BEFORE_FRAG before_frag
522 0 : #define STEM_CALLBACK_DURING_FRAG during_frag
523 0 : #define STEM_CALLBACK_AFTER_FRAG after_frag
524 :
525 : #include "../../../../disco/stem/fd_stem.c"
526 :
527 : fd_topo_run_tile_t fd_tile_bank = {
528 : .name = "bank",
529 : .scratch_align = scratch_align,
530 : .scratch_footprint = scratch_footprint,
531 : .unprivileged_init = unprivileged_init,
532 : .run = stem_run,
533 : };
|