Line data Source code
1 : #include "fd_cost_tracker.h"
2 : #include "fd_slot_params.h"
3 : #include "fd_system_ids.h"
4 : #include "fd_bank.h"
5 : #include "fd_runtime.h"
6 : #include "fd_executor.h"
7 : #include "../features/fd_features.h"
8 : #include "../vm/fd_vm_base.h"
9 : #include "program/fd_system_program.h"
10 : #include "../../disco/pack/fd_pack_cost.h"
11 : #include "../../util/fd_hash32.h"
12 :
13 : FD_STATIC_ASSERT( FD_WRITE_LOCK_UNITS*FD_RUNTIME_MAX_TXN_ACC_WRITES_PER_SLOT +
14 : FD_PACK_COST_PER_SIGNATURE*((FD_RUNTIME_MAX_TXN_ACC_WRITES_PER_SLOT+63UL)/64UL)<=FD_SLOT_PARAMS_200MS_MAX_BLOCK_UNITS*100UL/60UL,
15 : max_writable_accounts_per_slot_fits );
16 : FD_STATIC_ASSERT( FD_WRITE_LOCK_UNITS*(FD_RUNTIME_MAX_TXN_ACC_WRITES_PER_SLOT+1UL) +
17 : FD_PACK_COST_PER_SIGNATURE*((FD_RUNTIME_MAX_TXN_ACC_WRITES_PER_SLOT+64UL)/64UL)>FD_SLOT_PARAMS_200MS_MAX_BLOCK_UNITS*100UL/60UL,
18 : max_writable_accounts_per_slot_is_tight );
19 :
20 : struct account_cost {
21 : fd_pubkey_t account;
22 : uint cost;
23 :
24 : struct {
25 : uint next;
26 : } map;
27 : };
28 :
29 : typedef struct account_cost account_cost_t;
30 :
31 : #define MAP_NAME account_cost_map
32 : #define MAP_KEY_T fd_pubkey_t
33 : #define MAP_ELE_T account_cost_t
34 375 : #define MAP_KEY account
35 501 : #define MAP_KEY_EQ(k0,k1) (fd_pubkey_eq( k0, k1 ))
36 1626 : #define MAP_KEY_HASH(key,seed) (fd_hash32( key->uc, seed ))
37 375 : #define MAP_NEXT map.next
38 16758 : #define MAP_IDX_T uint
39 : #include "../../util/tmpl/fd_map_chain.c"
40 :
41 : struct cost_tracker_outer {
42 : fd_cost_tracker_t cost_tracker[1];
43 : ulong pool_offset;
44 : ulong accounts_used;
45 : ulong magic;
46 : fd_rwlock_t lock;
47 : };
48 :
49 : typedef struct cost_tracker_outer cost_tracker_outer_t;
50 :
51 : FD_FN_CONST ulong
52 22116 : fd_cost_tracker_align( void ) {
53 22116 : return FD_COST_TRACKER_ALIGN;
54 22116 : }
55 :
56 : FD_FN_CONST ulong
57 9 : fd_cost_tracker_footprint( void ) {
58 9 : ulong map_chain_cnt = FD_COST_TRACKER_CHAIN_CNT_EST;
59 :
60 9 : ulong l = FD_LAYOUT_INIT;
61 9 : l = FD_LAYOUT_APPEND( l, fd_cost_tracker_align(), sizeof(cost_tracker_outer_t) );
62 9 : l = FD_LAYOUT_APPEND( l, account_cost_map_align(), account_cost_map_footprint( map_chain_cnt ) );
63 9 : l = FD_LAYOUT_APPEND( l, alignof(account_cost_t), FD_RUNTIME_MAX_TXN_ACC_WRITES_PER_SLOT*sizeof(account_cost_t) );
64 9 : return FD_LAYOUT_FINI( l, fd_cost_tracker_align() );
65 9 : }
66 :
67 : void *
68 : fd_cost_tracker_new( void * shmem,
69 : ulong bench_max_cost_per_block,
70 7359 : ulong seed ) {
71 7359 : if( FD_UNLIKELY( !shmem ) ) {
72 3 : FD_LOG_WARNING(( "NULL shmem" ));
73 3 : return NULL;
74 3 : }
75 :
76 7356 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shmem, fd_cost_tracker_align() ) ) ) {
77 0 : FD_LOG_WARNING(( "misaligned shmem" ));
78 0 : return NULL;
79 0 : }
80 :
81 7356 : ulong map_chain_cnt = FD_COST_TRACKER_CHAIN_CNT_EST;
82 :
83 7356 : FD_SCRATCH_ALLOC_INIT( l, shmem );
84 7356 : cost_tracker_outer_t * cost_tracker = FD_SCRATCH_ALLOC_APPEND( l, fd_cost_tracker_align(), sizeof(cost_tracker_outer_t) );
85 7356 : void * _map = FD_SCRATCH_ALLOC_APPEND( l, account_cost_map_align(), account_cost_map_footprint( map_chain_cnt ) );
86 7356 : void * _accounts = FD_SCRATCH_ALLOC_APPEND( l, alignof(account_cost_t), FD_RUNTIME_MAX_TXN_ACC_WRITES_PER_SLOT*sizeof(account_cost_t) );
87 :
88 7356 : account_cost_map_t * map = account_cost_map_join( account_cost_map_new( _map, map_chain_cnt, seed ) );
89 7356 : FD_TEST( map );
90 :
91 7356 : cost_tracker->pool_offset = (ulong)_accounts-(ulong)cost_tracker;
92 :
93 7356 : cost_tracker->cost_tracker->bench_max_cost_per_block = bench_max_cost_per_block;
94 :
95 7356 : fd_rwlock_new( &cost_tracker->lock );
96 :
97 7356 : (void)_accounts;
98 :
99 7356 : FD_COMPILER_MFENCE();
100 7356 : FD_VOLATILE( cost_tracker->magic ) = FD_COST_TRACKER_MAGIC;
101 7356 : FD_COMPILER_MFENCE();
102 :
103 7356 : return shmem;
104 7356 : }
105 :
106 : fd_cost_tracker_t *
107 7362 : fd_cost_tracker_join( void * shct ) {
108 7362 : if( FD_UNLIKELY( !shct ) ) {
109 3 : FD_LOG_WARNING(( "NULL mem" ));
110 3 : return NULL;
111 3 : }
112 :
113 7359 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shct, fd_cost_tracker_align() ) ) ) {
114 0 : FD_LOG_WARNING(( "misaligned mem" ));
115 0 : return NULL;
116 0 : }
117 :
118 7359 : cost_tracker_outer_t * cost_tracker = (cost_tracker_outer_t *)shct;
119 :
120 7359 : if( FD_UNLIKELY( cost_tracker->magic!=FD_COST_TRACKER_MAGIC ) ) {
121 3 : FD_LOG_WARNING(( "Invalid cost tracker magic" ));
122 3 : return NULL;
123 3 : }
124 :
125 7356 : return cost_tracker->cost_tracker;
126 7359 : }
127 :
128 : void
129 : fd_cost_tracker_init( fd_cost_tracker_t * cost_tracker,
130 : fd_features_t const * features,
131 : fd_slot_params_t const * slot_params,
132 7152 : ulong slot ) {
133 : /* https://github.com/anza-xyz/agave/blob/v4.2/runtime/src/bank.rs#L4809-L4816 */
134 7152 : cost_tracker->block_cost_limit = slot_params->max_block_units;
135 7152 : cost_tracker->account_cost_limit = slot_params->max_writable_account_units;
136 7152 : cost_tracker->data_size_limit = slot_params->max_block_accounts_data_size_delta;
137 :
138 : /* Only the block and account cost limits scale with the block limit
139 : https://github.com/anza-xyz/agave/blob/v4.2/runtime/src/slot_params.rs#L94-L119 */
140 7152 : if( FD_FEATURE_ACTIVE( slot, features, raise_block_limits_to_100m ) ) {
141 24 : cost_tracker->block_cost_limit = fd_ulong_sat_mul( cost_tracker->block_cost_limit, 100UL ) / 60UL;
142 24 : cost_tracker->account_cost_limit = fd_ulong_sat_mul( cost_tracker->account_cost_limit, 100UL ) / 60UL;
143 24 : }
144 :
145 7152 : cost_tracker->block_cost_limit = fd_ulong_max( cost_tracker->block_cost_limit, cost_tracker->bench_max_cost_per_block );
146 :
147 7152 : cost_tracker->block_cost = 0UL;
148 7152 : cost_tracker->allocated_accounts_data_size = 0UL;
149 :
150 7152 : cost_tracker_outer_t * outer = fd_type_pun( cost_tracker );
151 7152 : outer->accounts_used = 0UL;
152 7152 : account_cost_map_reset( fd_type_pun( outer+1UL ) );
153 7152 : }
154 :
155 : /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.7/cost-model/src/cost_model.rs#L209-L212 */
156 : FD_FN_PURE static inline uint
157 504 : get_instructions_data_cost( fd_txn_in_t const * txn_in ) {
158 504 : uint total_instr_data_sz = 0U;
159 957 : for( ushort i=0; i<TXN( txn_in->txn )->instr_cnt; i++ ) {
160 453 : total_instr_data_sz += TXN( txn_in->txn )->instr[ i ].data_sz;
161 453 : }
162 504 : return total_instr_data_sz / FD_PACK_INV_COST_PER_INSTR_DATA_BYTE;
163 504 : }
164 :
165 : /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.7/cost-model/src/cost_model.rs#L147-L178 */
166 : FD_FN_PURE static inline uint
167 504 : get_signature_cost( fd_txn_in_t const * txn_in ) {
168 504 : fd_txn_t const * txn = TXN( txn_in->txn );
169 504 : void const * payload = txn_in->txn->payload;
170 504 : fd_acct_addr_t const * accounts = fd_txn_get_acct_addrs( txn, payload );
171 :
172 : /* Compute signature counts (both normal + precompile)
173 : TODO: Factor this logic out into a shared function that can be used
174 : both here and in fd_pack_cost.h */
175 504 : uint num_secp256k1_instruction_signatures = 0U;
176 504 : uint num_ed25519_instruction_signatures = 0U;
177 504 : uint num_secp256r1_instruction_signatures = 0U;
178 :
179 957 : for( ushort i=0; i<txn->instr_cnt; i++ ) {
180 453 : fd_txn_instr_t const * instr = &txn->instr[ i ];
181 453 : if( instr->data_sz==0UL ) continue;
182 :
183 360 : fd_acct_addr_t const * prog_id = accounts + instr->program_id;
184 360 : uchar const * instr_data = fd_txn_get_instr_data( instr, payload );
185 :
186 360 : if( fd_memeq( prog_id, fd_solana_ed25519_sig_verify_program_id.key, sizeof(fd_pubkey_t) ) ) {
187 0 : num_ed25519_instruction_signatures += (uint)instr_data[ 0 ];
188 360 : } else if( fd_memeq( prog_id, fd_solana_keccak_secp_256k_program_id.key, sizeof(fd_pubkey_t) ) ) {
189 0 : num_secp256k1_instruction_signatures += (uint)instr_data[ 0 ];
190 360 : } else if( fd_memeq( prog_id, fd_solana_secp256r1_program_id.key, sizeof(fd_pubkey_t) ) ) {
191 0 : num_secp256r1_instruction_signatures += (uint)instr_data[ 0 ];
192 0 : }
193 360 : }
194 :
195 : /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.7/cost-model/src/cost_model.rs#L160-L177 */
196 504 : uint signature_cost = fd_uint_sat_mul( FD_PACK_COST_PER_SIGNATURE, (uint)txn->signature_cnt );
197 504 : uint secp256k1_verify_cost = fd_uint_sat_mul( FD_PACK_COST_PER_SECP256K1_SIGNATURE, num_secp256k1_instruction_signatures );
198 504 : uint ed25519_verify_cost = fd_uint_sat_mul( FD_PACK_COST_PER_ED25519_SIGNATURE, num_ed25519_instruction_signatures );
199 504 : uint secp256r1_verify_cost = fd_uint_sat_mul( FD_PACK_COST_PER_SECP256R1_SIGNATURE, num_secp256r1_instruction_signatures );
200 504 : return fd_uint_sat_add( fd_uint_sat_add( fd_uint_sat_add(
201 504 : signature_cost, secp256k1_verify_cost), ed25519_verify_cost), secp256r1_verify_cost );
202 504 : }
203 :
204 : /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.7/cost-model/src/cost_model.rs#L180-L183 */
205 : FD_FN_CONST static inline uint
206 504 : get_write_lock_cost( uint num_write_locks ) {
207 504 : return fd_uint_sat_mul( num_write_locks, FD_WRITE_LOCK_UNITS );
208 504 : }
209 :
210 : /* Loop through all instructions here and deserialize the instruction data to try to determine any
211 : system program allocations done.
212 :
213 : https://github.com/anza-xyz/agave/blob/v4.0.0-beta.7/cost-model/src/cost_model.rs#L281-L319 */
214 : static inline ulong
215 504 : calculate_allocated_accounts_data_size( fd_bank_t * bank, fd_txn_in_t const * txn_in ) {
216 504 : fd_txn_t const * txn = TXN( txn_in->txn );
217 504 : void const * payload = txn_in->txn->payload;
218 :
219 504 : ulong allocated_accounts_data_size = 0UL;
220 945 : for( ushort i=0; i<txn->instr_cnt; i++ ) {
221 453 : fd_txn_instr_t const * instr = &txn->instr[ i ];
222 453 : fd_acct_addr_t const * accounts = fd_txn_get_acct_addrs( txn, payload );
223 453 : fd_acct_addr_t const * prog_id = accounts + instr->program_id;
224 453 : uchar const * instr_data = fd_txn_get_instr_data( instr, payload );
225 :
226 453 : if( !fd_memeq( prog_id, &fd_solana_system_program_id, sizeof(fd_pubkey_t) ) ) continue;
227 :
228 192 : fd_system_program_instruction_t instruction = {0};
229 192 : if( FD_UNLIKELY( fd_system_program_instruction_decode( &instruction, instr_data, instr->data_sz ) ) ) return 0UL;
230 :
231 : /* https://github.com/anza-xyz/agave/blob/v2.2.0/cost-model/src/cost_model.rs#L330-L346 */
232 189 : ulong space = 0UL;
233 189 : switch( instruction.discriminant ) {
234 : /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.7/cost-model/src/cost_model.rs#L234 */
235 15 : case FD_SYSTEM_PROGRAM_INSTR_CREATE_ACCOUNT: {
236 15 : space = instruction.inner.create_account.space;
237 15 : break;
238 0 : }
239 : /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.7/cost-model/src/cost_model.rs#L235 */
240 0 : case FD_SYSTEM_PROGRAM_INSTR_CREATE_ACCOUNT_WITH_SEED: {
241 0 : space = instruction.inner.create_account_with_seed.space;
242 0 : break;
243 0 : }
244 : /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.7/cost-model/src/cost_model.rs#L236 */
245 39 : case FD_SYSTEM_PROGRAM_INSTR_ALLOCATE: {
246 39 : space = instruction.inner.allocate;
247 39 : break;
248 0 : }
249 : /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.7/cost-model/src/cost_model.rs#L237 */
250 0 : case FD_SYSTEM_PROGRAM_INSTR_ALLOCATE_WITH_SEED: {
251 0 : space = instruction.inner.allocate_with_seed.space;
252 0 : break;
253 0 : }
254 : /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.7/cost-model/src/cost_model.rs#L238-L243 */
255 54 : case FD_SYSTEM_PROGRAM_INSTR_CREATE_ACCOUNT_ALLOW_PREFUND: {
256 54 : if( !FD_FEATURE_ACTIVE_BANK( bank, create_account_allow_prefund ) ) {
257 3 : return 0UL;
258 3 : }
259 51 : space = instruction.inner.create_account_allow_prefund.space;
260 51 : break;
261 54 : }
262 189 : }
263 :
264 : /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.7/cost-model/src/cost_model.rs#L225-L231 */
265 186 : if( FD_UNLIKELY( space>FD_RUNTIME_ACC_SZ_MAX ) ) return 0UL;
266 :
267 180 : allocated_accounts_data_size = fd_ulong_sat_add( allocated_accounts_data_size, space );
268 180 : }
269 :
270 : /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.7/cost-model/src/cost_model.rs#L309-L318 */
271 492 : return fd_ulong_min( 2UL*FD_RUNTIME_ACC_SZ_MAX, allocated_accounts_data_size );
272 504 : }
273 :
274 : /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.7/cost-model/src/cost_model.rs#L119-L145 */
275 : static inline fd_transaction_cost_t
276 : calculate_transaction_cost( fd_bank_t * bank,
277 : fd_txn_in_t const * txn_in,
278 : fd_txn_out_t const * txn_out,
279 : uint loaded_accounts_data_size_cost,
280 504 : uint data_bytes_cost ) {
281 :
282 : /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.7/cost-model/src/cost_model.rs#L128 */
283 504 : uint signature_cost = get_signature_cost( txn_in );
284 :
285 : /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.7/cost-model/src/cost_model.rs#L129 */
286 504 : uint write_lock_cost = get_write_lock_cost( (uint)fd_txn_account_cnt( TXN( txn_in->txn ), FD_TXN_ACCT_CAT_WRITABLE ) );
287 :
288 : /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.7/cost-model/src/cost_model.rs#L131-L132 */
289 504 : ulong allocated_accounts_data_size = calculate_allocated_accounts_data_size( bank, txn_in );
290 :
291 : /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.7/cost-model/src/cost_model.rs#L134-L144 */
292 504 : return (fd_transaction_cost_t) {
293 504 : .transaction = {
294 504 : .signature_cost = signature_cost,
295 504 : .write_lock_cost = write_lock_cost,
296 504 : .data_bytes_cost = data_bytes_cost,
297 504 : .allocated_accounts_data_size = allocated_accounts_data_size,
298 : /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.7/cost-model/src/cost_model.rs#L185-L207 */
299 504 : .programs_execution_cost = fd_uint_sat_sub(
300 504 : (uint)txn_out->details.compute_budget.compute_unit_limit,
301 504 : (uint)txn_out->details.compute_budget.compute_meter ),
302 504 : .loaded_accounts_data_size_cost = loaded_accounts_data_size_cost,
303 504 : }
304 504 : };
305 504 : }
306 :
307 : /* https://github.com/anza-xyz/agave/blob/v2.2.0/cost-model/src/transaction_cost.rs#L26-L42 */
308 : static inline uint
309 663 : transaction_cost_sum( fd_transaction_cost_t const * txn_cost ) {
310 : /* https://github.com/anza-xyz/agave/blob/v2.2.0/cost-model/src/transaction_cost.rs#L164-L171 */
311 663 : fd_usage_cost_details_t const * usage_cost = &txn_cost->transaction;
312 663 : uint cost = 0U;
313 :
314 663 : cost = fd_uint_sat_add( cost, usage_cost->signature_cost );
315 663 : cost = fd_uint_sat_add( cost, usage_cost->write_lock_cost );
316 663 : cost = fd_uint_sat_add( cost, usage_cost->data_bytes_cost );
317 663 : cost = fd_uint_sat_add( cost, usage_cost->programs_execution_cost );
318 663 : cost = fd_uint_sat_add( cost, usage_cost->loaded_accounts_data_size_cost );
319 :
320 663 : return cost;
321 663 : }
322 :
323 : static inline ulong
324 648 : get_allocated_accounts_data_size( fd_transaction_cost_t const * txn_cost ) {
325 648 : return txn_cost->transaction.allocated_accounts_data_size;
326 648 : }
327 :
328 : /* https://github.com/anza-xyz/agave/blob/v2.2.0/cost-model/src/cost_tracker.rs#L277-L322 */
329 : static inline int
330 : would_fit( fd_cost_tracker_t const * cost_tracker,
331 : fd_txn_out_t * txn_out,
332 342 : fd_transaction_cost_t const * tx_cost ) {
333 :
334 : /* https://github.com/anza-xyz/agave/blob/v2.2.0/cost-model/src/cost_tracker.rs#L281 */
335 342 : uint cost = transaction_cost_sum( tx_cost );
336 :
337 : /* https://github.com/anza-xyz/agave/blob/v2.2.0/cost-model/src/cost_tracker.rs#L290-L293 */
338 342 : if( FD_UNLIKELY( fd_ulong_sat_add( cost_tracker->block_cost, cost )>cost_tracker->block_cost_limit ) ) {
339 9 : return FD_COST_TRACKER_ERROR_WOULD_EXCEED_BLOCK_MAX_LIMIT;
340 9 : }
341 :
342 : /* https://github.com/anza-xyz/agave/blob/v2.2.0/cost-model/src/cost_tracker.rs#L295-L298 */
343 333 : if( FD_UNLIKELY( cost>cost_tracker->account_cost_limit ) ) {
344 6 : return FD_COST_TRACKER_ERROR_WOULD_EXCEED_ACCOUNT_MAX_LIMIT;
345 6 : }
346 :
347 : /* https://github.com/anza-xyz/agave/blob/v2.2.0/cost-model/src/cost_tracker.rs#L300-L301 */
348 327 : ulong allocated_accounts_data_size = fd_ulong_sat_add( cost_tracker->allocated_accounts_data_size,
349 327 : get_allocated_accounts_data_size( tx_cost ) );
350 :
351 : /* https://github.com/anza-xyz/agave/blob/v2.2.0/cost-model/src/cost_tracker.rs#L303-L304 */
352 327 : if( FD_UNLIKELY( allocated_accounts_data_size>cost_tracker->data_size_limit ) ) {
353 3 : return FD_COST_TRACKER_ERROR_WOULD_EXCEED_ACCOUNT_DATA_BLOCK_LIMIT;
354 3 : }
355 :
356 : /* https://github.com/anza-xyz/agave/blob/v2.2.0/cost-model/src/cost_tracker.rs#L308-L319 */
357 :
358 324 : account_cost_map_t const * map = fd_type_pun_const(((cost_tracker_outer_t const *)cost_tracker)+1UL);
359 324 : account_cost_t const * pool = fd_type_pun_const( (void*)((ulong)cost_tracker + ((cost_tracker_outer_t const *)cost_tracker)->pool_offset) );
360 :
361 1740 : for( ulong i=0UL; i<txn_out->accounts.cnt; i++ ) {
362 1419 : if( FD_UNLIKELY( !txn_out->accounts.is_writable[ i ] ) ) continue;
363 :
364 627 : fd_pubkey_t const * writable_acc = &txn_out->accounts.keys[i];
365 :
366 627 : account_cost_t const * chained_cost = account_cost_map_ele_query_const( map, writable_acc, NULL, pool );
367 627 : if( FD_UNLIKELY( chained_cost && fd_ulong_sat_add( chained_cost->cost, cost )>cost_tracker->account_cost_limit ) ) {
368 3 : return FD_COST_TRACKER_ERROR_WOULD_EXCEED_ACCOUNT_MAX_LIMIT;
369 3 : }
370 627 : }
371 :
372 321 : return FD_COST_TRACKER_SUCCESS;
373 324 : }
374 :
375 : /* https://github.com/anza-xyz/agave/blob/v2.2.0/cost-model/src/cost_tracker.rs#L352-L372 */
376 : static inline void
377 : add_transaction_execution_cost( fd_cost_tracker_t * _cost_tracker,
378 : fd_txn_out_t * txn_out,
379 321 : uint adjustment ) {
380 321 : cost_tracker_outer_t * cost_tracker = fd_type_pun( _cost_tracker );
381 321 : account_cost_map_t * map = fd_type_pun( cost_tracker+1UL );
382 321 : account_cost_t * pool = fd_type_pun( (void*)((ulong)cost_tracker+cost_tracker->pool_offset) );
383 :
384 1737 : for( ulong i=0UL; i<txn_out->accounts.cnt; i++ ) {
385 1416 : if( FD_UNLIKELY( !txn_out->accounts.is_writable[ i ] ) ) continue;
386 :
387 624 : fd_pubkey_t const * writable_acc = &txn_out->accounts.keys[i];
388 :
389 624 : account_cost_t * account_cost = account_cost_map_ele_query( map, writable_acc, NULL, pool );
390 624 : if( FD_UNLIKELY( !account_cost ) ) {
391 375 : if( FD_UNLIKELY( cost_tracker->accounts_used>=FD_RUNTIME_MAX_TXN_ACC_WRITES_PER_SLOT ) ) {
392 0 : FD_LOG_CRIT(( "block writes more than %lu distinct accounts (block_cost_limit %lu); only block cost limits up to the 200ms slot params are supported",
393 0 : FD_RUNTIME_MAX_TXN_ACC_WRITES_PER_SLOT, _cost_tracker->block_cost_limit ));
394 0 : }
395 :
396 375 : account_cost = pool+cost_tracker->accounts_used;
397 375 : cost_tracker->accounts_used++;
398 :
399 375 : account_cost->account = *writable_acc;
400 375 : account_cost->cost = adjustment;
401 :
402 375 : account_cost_map_ele_insert( map, account_cost, pool );
403 375 : } else {
404 249 : account_cost->cost = fd_uint_sat_add( account_cost->cost, adjustment );
405 249 : }
406 624 : }
407 :
408 321 : cost_tracker->cost_tracker->block_cost = fd_ulong_sat_add( cost_tracker->cost_tracker->block_cost, adjustment );
409 321 : }
410 :
411 :
412 :
413 : /* https://github.com/anza-xyz/agave/blob/v2.2.0/cost-model/src/cost_model.rs#L323-L328 */
414 : FD_FN_PURE uint
415 504 : fd_cost_tracker_calculate_loaded_accounts_data_size_cost( fd_txn_out_t const * txn_out ) {
416 504 : uint cost = fd_uint_sat_sub( fd_uint_sat_add( (uint)txn_out->details.loaded_accounts_data_size,
417 504 : (uint)FD_ACCOUNT_DATA_COST_PAGE_SIZE ),
418 504 : 1U );
419 504 : cost /= (uint)FD_ACCOUNT_DATA_COST_PAGE_SIZE;
420 504 : return fd_uint_sat_mul( cost, (uint)FD_VM_HEAP_COST );
421 504 : }
422 :
423 : void
424 : fd_cost_tracker_calculate_cost( fd_bank_t * bank,
425 : fd_txn_in_t const * txn_in,
426 504 : fd_txn_out_t * txn_out ) {
427 504 : fd_transaction_cost_t * txn_cost = &txn_out->details.txn_cost;
428 : /* https://github.com/anza-xyz/agave/blob/v2.2.0/cost-model/src/cost_model.rs#L78-L81 */
429 504 : uint loaded_accounts_data_size_cost = fd_cost_tracker_calculate_loaded_accounts_data_size_cost( txn_out );
430 :
431 : /* https://github.com/anza-xyz/agave/blob/v2.2.0/cost-model/src/cost_model.rs#L82-L83 */
432 504 : uint instructions_data_cost = get_instructions_data_cost( txn_in );
433 :
434 : /* https://github.com/anza-xyz/agave/blob/v2.2.0/cost-model/src/cost_model.rs#L85-L93 */
435 504 : *txn_cost = calculate_transaction_cost( bank, txn_in, txn_out, loaded_accounts_data_size_cost, instructions_data_cost );
436 504 : }
437 :
438 : int
439 : fd_cost_tracker_try_add_cost( fd_cost_tracker_t * cost_tracker,
440 342 : fd_txn_out_t * txn_out ) {
441 :
442 342 : cost_tracker_outer_t * cost_tracker_outer = fd_type_pun( cost_tracker );
443 342 : fd_rwlock_write( &cost_tracker_outer->lock );
444 :
445 : /* https://github.com/anza-xyz/agave/blob/v2.2.0/cost-model/src/cost_tracker.rs#L167 */
446 342 : int err = would_fit( cost_tracker, txn_out, &txn_out->details.txn_cost );
447 342 : if( FD_UNLIKELY( err!=FD_COST_TRACKER_SUCCESS ) ) {
448 21 : fd_rwlock_unwrite( &cost_tracker_outer->lock );
449 21 : return err;
450 21 : }
451 :
452 : /* https://github.com/anza-xyz/agave/blob/v2.2.0/cost-model/src/cost_tracker.rs#L325-L335 */
453 :
454 : /* We don't need `updated_costliest_account_cost` since it seems to be
455 : for a different use case other than validating block cost limits.
456 : https://github.com/anza-xyz/agave/blob/v2.2.0/cost-model/src/cost_tracker.rs#L168 */
457 :
458 : /* Note: We purposely omit signature counts updates since they're not relevant to cost calculations right now. */
459 321 : cost_tracker->allocated_accounts_data_size += get_allocated_accounts_data_size( &txn_out->details.txn_cost );
460 321 : add_transaction_execution_cost( cost_tracker, txn_out, transaction_cost_sum( &txn_out->details.txn_cost ) );
461 :
462 321 : fd_rwlock_unwrite( &cost_tracker_outer->lock );
463 321 : return FD_COST_TRACKER_SUCCESS;
464 342 : }
|