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