Line data Source code
1 : #include "fd_cost_tracker.h"
2 : #include "fd_system_ids.h"
3 : #include "fd_bank.h"
4 : #include "fd_runtime.h"
5 : #include "../features/fd_features.h"
6 :
7 : struct account_cost {
8 : fd_pubkey_t account;
9 : ulong cost;
10 :
11 : struct {
12 : ulong next;
13 : } map;
14 : };
15 :
16 : typedef struct account_cost account_cost_t;
17 :
18 : #define MAP_NAME account_cost_map
19 : #define MAP_KEY_T fd_pubkey_t
20 : #define MAP_ELE_T account_cost_t
21 0 : #define MAP_KEY account
22 0 : #define MAP_KEY_EQ(k0,k1) (fd_pubkey_eq( k0, k1 ))
23 0 : #define MAP_KEY_HASH(key,seed) (fd_hash( seed, key, sizeof(fd_pubkey_t) ))
24 0 : #define MAP_NEXT map.next
25 : #include "../../util/tmpl/fd_map_chain.c"
26 :
27 : struct cost_tracker_outer {
28 : fd_cost_tracker_t cost_tracker[1];
29 : ulong pool_offset;
30 : ulong accounts_used;
31 : ulong magic;
32 : };
33 :
34 : typedef struct cost_tracker_outer cost_tracker_outer_t;
35 :
36 : FD_FN_CONST ulong
37 93 : fd_cost_tracker_align( void ) {
38 93 : return FD_COST_TRACKER_ALIGN;
39 93 : }
40 :
41 : FD_FN_CONST ulong
42 9 : fd_cost_tracker_footprint( void ) {
43 9 : ulong map_chain_cnt = account_cost_map_chain_cnt_est( FD_RUNTIME_MAX_WRITABLE_ACCOUNTS_PER_SLOT );
44 :
45 9 : ulong l = FD_LAYOUT_INIT;
46 9 : l = FD_LAYOUT_APPEND( l, fd_cost_tracker_align(), sizeof(cost_tracker_outer_t) );
47 9 : l = FD_LAYOUT_APPEND( l, account_cost_map_align(), account_cost_map_footprint( map_chain_cnt ) );
48 9 : l = FD_LAYOUT_APPEND( l, alignof(account_cost_t), FD_RUNTIME_MAX_WRITABLE_ACCOUNTS_PER_SLOT*sizeof(account_cost_t) );
49 9 : return FD_LAYOUT_FINI( l, fd_cost_tracker_align() );
50 9 : }
51 :
52 : void *
53 : fd_cost_tracker_new( void * shmem,
54 : int larger_max_cost_per_block,
55 18 : ulong seed ) {
56 18 : if( FD_UNLIKELY( !shmem ) ) {
57 3 : FD_LOG_WARNING(( "NULL shmem" ));
58 3 : return NULL;
59 3 : }
60 :
61 15 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shmem, fd_cost_tracker_align() ) ) ) {
62 0 : FD_LOG_WARNING(( "misaligned shmem" ));
63 0 : return NULL;
64 0 : }
65 :
66 15 : ulong map_chain_cnt = account_cost_map_chain_cnt_est( FD_RUNTIME_MAX_WRITABLE_ACCOUNTS_PER_SLOT );
67 :
68 15 : FD_SCRATCH_ALLOC_INIT( l, shmem );
69 15 : cost_tracker_outer_t * cost_tracker = FD_SCRATCH_ALLOC_APPEND( l, fd_cost_tracker_align(), sizeof(cost_tracker_outer_t) );
70 15 : void * _map = FD_SCRATCH_ALLOC_APPEND( l, account_cost_map_align(), account_cost_map_footprint( map_chain_cnt ) );
71 15 : void * _accounts = FD_SCRATCH_ALLOC_APPEND( l, alignof(account_cost_t), FD_RUNTIME_MAX_WRITABLE_ACCOUNTS_PER_SLOT*sizeof(account_cost_t) );
72 :
73 0 : account_cost_map_t * map = account_cost_map_join( account_cost_map_new( _map, map_chain_cnt, seed ) );
74 15 : FD_TEST( map );
75 :
76 15 : cost_tracker->pool_offset = (ulong)_accounts-(ulong)cost_tracker;
77 :
78 15 : cost_tracker->cost_tracker->larger_max_cost_per_block = larger_max_cost_per_block;
79 :
80 15 : (void)_accounts;
81 :
82 15 : FD_COMPILER_MFENCE();
83 15 : FD_VOLATILE( cost_tracker->magic ) = FD_COST_TRACKER_MAGIC;
84 15 : FD_COMPILER_MFENCE();
85 :
86 15 : return shmem;
87 15 : }
88 :
89 : fd_cost_tracker_t *
90 21 : fd_cost_tracker_join( void * shct ) {
91 21 : if( FD_UNLIKELY( !shct ) ) {
92 3 : FD_LOG_WARNING(( "NULL mem" ));
93 3 : return NULL;
94 3 : }
95 :
96 18 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shct, fd_cost_tracker_align() ) ) ) {
97 0 : FD_LOG_WARNING(( "misaligned mem" ));
98 0 : return NULL;
99 0 : }
100 :
101 18 : cost_tracker_outer_t * cost_tracker = (cost_tracker_outer_t *)shct;
102 :
103 18 : if( FD_UNLIKELY( cost_tracker->magic!=FD_COST_TRACKER_MAGIC ) ) {
104 3 : FD_LOG_WARNING(( "Invalid cost tracker magic" ));
105 3 : return NULL;
106 3 : }
107 :
108 15 : return cost_tracker->cost_tracker;
109 18 : }
110 :
111 : void
112 : fd_cost_tracker_init( fd_cost_tracker_t * cost_tracker,
113 : fd_features_t const * features,
114 0 : ulong slot ) {
115 0 : if( FD_FEATURE_ACTIVE( slot, features, raise_block_limits_to_100m ) ) {
116 0 : cost_tracker->block_cost_limit = FD_MAX_BLOCK_UNITS_SIMD_0286;
117 0 : cost_tracker->vote_cost_limit = FD_MAX_VOTE_UNITS;
118 0 : cost_tracker->account_cost_limit = FD_MAX_WRITABLE_ACCOUNT_UNITS;
119 0 : } else if( FD_FEATURE_ACTIVE( slot, features, raise_block_limits_to_60m ) ) {
120 0 : cost_tracker->block_cost_limit = FD_MAX_BLOCK_UNITS_SIMD_0256;
121 0 : cost_tracker->vote_cost_limit = FD_MAX_VOTE_UNITS;
122 0 : cost_tracker->account_cost_limit = FD_MAX_WRITABLE_ACCOUNT_UNITS;
123 0 : } else {
124 0 : cost_tracker->block_cost_limit = FD_MAX_BLOCK_UNITS_SIMD_0207;
125 0 : cost_tracker->vote_cost_limit = FD_MAX_VOTE_UNITS;
126 0 : cost_tracker->account_cost_limit = FD_MAX_WRITABLE_ACCOUNT_UNITS;
127 0 : }
128 :
129 0 : if( FD_UNLIKELY( cost_tracker->larger_max_cost_per_block ) ) cost_tracker->block_cost_limit = LARGER_MAX_COST_PER_BLOCK;
130 :
131 : /* https://github.com/anza-xyz/agave/blob/v3.0.1/runtime/src/bank.rs#L4059-L4066 */
132 0 : if( FD_FEATURE_ACTIVE( slot, features, raise_account_cu_limit ) ) {
133 0 : cost_tracker->account_cost_limit = fd_ulong_sat_mul( cost_tracker->block_cost_limit, 40UL ) / 100UL;
134 0 : }
135 :
136 0 : cost_tracker->block_cost = 0UL;
137 0 : cost_tracker->vote_cost = 0UL;
138 0 : cost_tracker->allocated_accounts_data_size = 0UL;
139 :
140 0 : cost_tracker_outer_t * outer = fd_type_pun( cost_tracker );
141 0 : outer->accounts_used = 0UL;
142 0 : account_cost_map_reset( fd_type_pun( outer+1UL ) );
143 0 : }
144 :
145 : /* https://github.com/anza-xyz/agave/blob/v2.2.0/cost-model/src/cost_model.rs#L313-L321 */
146 : FD_FN_PURE static inline ulong
147 0 : get_instructions_data_cost( fd_txn_in_t const * txn_in ) {
148 0 : ulong total_instr_data_sz = 0UL;
149 0 : for( ushort i=0; i<TXN( txn_in->txn )->instr_cnt; i++ ) {
150 0 : total_instr_data_sz += TXN( txn_in->txn )->instr[ i ].data_sz;
151 0 : }
152 0 : return total_instr_data_sz / FD_PACK_INV_COST_PER_INSTR_DATA_BYTE;
153 0 : }
154 :
155 : /* https://github.com/anza-xyz/agave/blob/v2.2.0/cost-model/src/cost_model.rs#L152-L187 */
156 : FD_FN_PURE static inline ulong
157 0 : get_signature_cost( fd_txn_in_t const * txn_in, fd_bank_t * bank ) {
158 0 : fd_txn_t const * txn = TXN( txn_in->txn );
159 0 : void const * payload = txn_in->txn->payload;
160 0 : fd_acct_addr_t const * accounts = fd_txn_get_acct_addrs( txn, payload );
161 :
162 : /* Compute signature counts (both normal + precompile)
163 : TODO: Factor this logic out into a shared function that can be used
164 : both here and in fd_pack_cost.h */
165 0 : ulong signature_cost = fd_ulong_sat_mul( txn->signature_cnt, FD_PACK_COST_PER_SIGNATURE );
166 0 : ulong num_secp256k1_instruction_signatures = 0UL;
167 0 : ulong num_ed25519_instruction_signatures = 0UL;
168 0 : ulong num_secp256r1_instruction_signatures = 0UL;
169 :
170 0 : for( ushort i=0; i<txn->instr_cnt; i++ ) {
171 0 : fd_txn_instr_t const * instr = &txn->instr[ i ];
172 0 : if( instr->data_sz==0UL ) continue;
173 :
174 0 : fd_acct_addr_t const * prog_id = accounts + instr->program_id;
175 0 : uchar const * instr_data = fd_txn_get_instr_data( instr, payload );
176 :
177 0 : if( fd_memeq( prog_id, fd_solana_ed25519_sig_verify_program_id.key, sizeof(fd_pubkey_t) ) ) {
178 0 : num_ed25519_instruction_signatures += (ulong)instr_data[ 0 ];
179 0 : } else if( fd_memeq( prog_id, fd_solana_keccak_secp_256k_program_id.key, sizeof(fd_pubkey_t) ) ) {
180 0 : num_secp256k1_instruction_signatures += (ulong)instr_data[ 0 ];
181 0 : } else if( fd_memeq( prog_id, fd_solana_secp256r1_program_id.key, sizeof(fd_pubkey_t) ) ) {
182 0 : num_secp256r1_instruction_signatures += (ulong)instr_data[ 0 ];
183 0 : }
184 0 : }
185 :
186 : /* No direct permalink, just factored out for readability */
187 0 : ulong secp256k1_verify_cost = fd_ulong_sat_mul( FD_PACK_COST_PER_SECP256K1_SIGNATURE, num_secp256k1_instruction_signatures );
188 :
189 : /* https://github.com/anza-xyz/agave/blob/v2.2.0/cost-model/src/cost_model.rs#L155-L160 */
190 0 : ulong ed25519_verify_cost;
191 0 : if( FD_FEATURE_ACTIVE_BANK( bank, ed25519_precompile_verify_strict ) ) {
192 0 : ed25519_verify_cost = fd_ulong_sat_mul( FD_PACK_COST_PER_ED25519_SIGNATURE, num_ed25519_instruction_signatures );
193 0 : } else {
194 0 : ed25519_verify_cost = fd_ulong_sat_mul( FD_PACK_COST_PER_NON_STRICT_ED25519_SIGNATURE, num_ed25519_instruction_signatures );
195 0 : }
196 :
197 : /* https://github.com/anza-xyz/agave/blob/v2.2.0/cost-model/src/cost_model.rs#L162-L167 */
198 0 : ulong secp256r1_verify_cost = 0UL;
199 0 : if( FD_FEATURE_ACTIVE_BANK( bank, enable_secp256r1_precompile ) ) {
200 0 : secp256r1_verify_cost = fd_ulong_sat_mul( FD_PACK_COST_PER_SECP256R1_SIGNATURE, num_secp256r1_instruction_signatures );
201 0 : }
202 :
203 : /* https://github.com/anza-xyz/agave/blob/v2.2.0/cost-model/src/cost_model.rs#L169-L186 */
204 0 : return fd_ulong_sat_add( signature_cost,
205 0 : fd_ulong_sat_add( secp256k1_verify_cost,
206 0 : fd_ulong_sat_add( ed25519_verify_cost,
207 0 : secp256r1_verify_cost ) ) );
208 0 : }
209 :
210 : /* https://github.com/anza-xyz/agave/blob/v2.2.0/cost-model/src/cost_model.rs#L190-L192 */
211 : FD_FN_PURE static inline ulong
212 0 : get_write_lock_cost( ulong num_write_locks ) {
213 0 : return fd_ulong_sat_mul( num_write_locks, FD_WRITE_LOCK_UNITS );
214 0 : }
215 :
216 : /* Loop through all instructions here and deserialize the instruction data to try to determine any
217 : system program allocations done.
218 :
219 : https://github.com/anza-xyz/agave/blob/v2.2.0/cost-model/src/cost_model.rs#L367-L386 */
220 : static inline ulong
221 0 : calculate_allocated_accounts_data_size( fd_txn_in_t const * txn_in ) {
222 0 : fd_txn_t const * txn = TXN( txn_in->txn );
223 0 : void const * payload = txn_in->txn->payload;
224 :
225 0 : ulong allocated_accounts_data_size = 0UL;
226 0 : for( ushort i=0; i<txn->instr_cnt; i++ ) {
227 0 : fd_txn_instr_t const * instr = &txn->instr[ i ];
228 0 : fd_acct_addr_t const * accounts = fd_txn_get_acct_addrs( txn, payload );
229 0 : fd_acct_addr_t const * prog_id = accounts + instr->program_id;
230 0 : uchar const * instr_data = fd_txn_get_instr_data( instr, payload );
231 :
232 0 : if( instr->data_sz==0UL || !fd_memeq( prog_id, &fd_solana_system_program_id, sizeof(fd_pubkey_t) ) ) continue;
233 :
234 0 : fd_bincode_decode_ctx_t ctx = {
235 0 : .data = instr_data,
236 0 : .dataend = instr_data + instr->data_sz,
237 0 : };
238 :
239 0 : ulong total_sz = 0UL;
240 0 : int err = fd_system_program_instruction_decode_footprint( &ctx, &total_sz );
241 0 : if( FD_UNLIKELY( err ) ) continue;
242 :
243 0 : uchar buf[total_sz];
244 0 : fd_system_program_instruction_t * instruction = fd_system_program_instruction_decode( buf, &ctx );
245 0 : if( FD_UNLIKELY( !instruction ) ) continue;
246 :
247 : /* https://github.com/anza-xyz/agave/blob/v2.2.0/cost-model/src/cost_model.rs#L330-L346 */
248 0 : ulong space = 0UL;
249 :
250 0 : switch( instruction->discriminant ) {
251 0 : case fd_system_program_instruction_enum_create_account: {
252 0 : space = instruction->inner.create_account.space;
253 0 : break;
254 0 : }
255 0 : case fd_system_program_instruction_enum_create_account_with_seed: {
256 0 : space = instruction->inner.create_account_with_seed.space;
257 0 : break;
258 0 : }
259 0 : case fd_system_program_instruction_enum_allocate: {
260 0 : space = instruction->inner.allocate;
261 0 : break;
262 0 : }
263 0 : case fd_system_program_instruction_enum_allocate_with_seed: {
264 0 : space = instruction->inner.allocate_with_seed.space;
265 0 : break;
266 0 : }
267 0 : }
268 :
269 : /* https://github.com/anza-xyz/agave/blob/v2.2.0/cost-model/src/cost_model.rs#L373-L380 */
270 0 : if( FD_UNLIKELY( space>FD_RUNTIME_ACC_SZ_MAX ) ) return 0UL;
271 :
272 0 : allocated_accounts_data_size = fd_ulong_sat_add( allocated_accounts_data_size, space );
273 0 : }
274 :
275 : /* https://github.com/anza-xyz/agave/blob/v2.2.0/cost-model/src/cost_model.rs#L396-L397 */
276 0 : return fd_ulong_min( 2UL*FD_RUNTIME_ACC_SZ_MAX, allocated_accounts_data_size );
277 0 : }
278 :
279 : /* https://github.com/anza-xyz/agave/blob/v2.2.0/cost-model/src/cost_model.rs#L123-L149 */
280 : static inline fd_transaction_cost_t
281 : calculate_non_vote_transaction_cost( fd_bank_t * bank,
282 : fd_txn_in_t const * txn_in,
283 : fd_txn_out_t const * txn_out,
284 : ulong loaded_accounts_data_size_cost,
285 0 : ulong data_bytes_cost ) {
286 :
287 : /* https://github.com/anza-xyz/agave/blob/v2.2.0/cost-model/src/cost_model.rs#L132 */
288 0 : ulong signature_cost = get_signature_cost( txn_in, bank );
289 :
290 : /* https://github.com/anza-xyz/agave/blob/v2.2.0/cost-model/src/cost_model.rs#L133 */
291 0 : ulong write_lock_cost = get_write_lock_cost( fd_txn_account_cnt( TXN( txn_in->txn ), FD_TXN_ACCT_CAT_WRITABLE ) );
292 :
293 : /* https://github.com/anza-xyz/agave/blob/v2.2.0/cost-model/src/cost_model.rs#L135-L136 */
294 0 : ulong allocated_accounts_data_size = calculate_allocated_accounts_data_size( txn_in );
295 :
296 0 : return (fd_transaction_cost_t){ .discriminant = fd_transaction_cost_enum_transaction,
297 0 : .inner = {
298 0 : .transaction = {
299 0 : .signature_cost = signature_cost,
300 0 : .write_lock_cost = write_lock_cost,
301 0 : .data_bytes_cost = data_bytes_cost,
302 0 : .programs_execution_cost = fd_ulong_sat_sub( txn_out->details.compute_budget.compute_unit_limit,
303 0 : txn_out->details.compute_budget.compute_meter ),
304 0 : .loaded_accounts_data_size_cost = loaded_accounts_data_size_cost,
305 0 : .allocated_accounts_data_size = allocated_accounts_data_size,
306 0 : }
307 0 : }
308 0 : };
309 0 : }
310 :
311 : /* https://github.com/anza-xyz/agave/blob/v2.2.0/cost-model/src/transaction_cost.rs#L26-L42 */
312 : FD_FN_PURE static inline ulong
313 0 : transaction_cost_sum( fd_transaction_cost_t const * txn_cost ) {
314 0 : switch( txn_cost->discriminant ) {
315 0 : case fd_transaction_cost_enum_simple_vote: {
316 : /* https://github.com/anza-xyz/agave/blob/v2.2.0/cost-model/src/transaction_cost.rs#L38 */
317 0 : return FD_PACK_SIMPLE_VOTE_COST;
318 0 : }
319 0 : case fd_transaction_cost_enum_transaction: {
320 : /* https://github.com/anza-xyz/agave/blob/v2.2.0/cost-model/src/transaction_cost.rs#L164-L171 */
321 0 : fd_usage_cost_details_t const * usage_cost = &txn_cost->inner.transaction;
322 0 : ulong cost = 0UL;
323 :
324 0 : cost = fd_ulong_sat_add( cost, usage_cost->signature_cost );
325 0 : cost = fd_ulong_sat_add( cost, usage_cost->write_lock_cost );
326 0 : cost = fd_ulong_sat_add( cost, usage_cost->data_bytes_cost );
327 0 : cost = fd_ulong_sat_add( cost, usage_cost->programs_execution_cost );
328 0 : cost = fd_ulong_sat_add( cost, usage_cost->loaded_accounts_data_size_cost );
329 :
330 0 : return cost;
331 0 : }
332 0 : default: {
333 0 : __builtin_unreachable();
334 0 : }
335 0 : }
336 0 : }
337 :
338 : FD_FN_PURE static inline ulong
339 0 : get_allocated_accounts_data_size( fd_transaction_cost_t const * txn_cost ) {
340 0 : switch( txn_cost->discriminant ) {
341 0 : case fd_transaction_cost_enum_simple_vote:
342 0 : return 0UL;
343 0 : case fd_transaction_cost_enum_transaction:
344 0 : return txn_cost->inner.transaction.allocated_accounts_data_size;
345 0 : default:
346 0 : __builtin_unreachable();
347 0 : }
348 0 : }
349 :
350 : /* https://github.com/anza-xyz/agave/blob/v2.2.0/cost-model/src/cost_tracker.rs#L277-L322 */
351 : static inline int
352 : would_fit( fd_cost_tracker_t const * cost_tracker,
353 : fd_bank_t * bank,
354 : fd_txn_in_t const * txn_in,
355 : fd_txn_out_t * txn_out,
356 0 : fd_transaction_cost_t const * tx_cost ) {
357 :
358 : /* https://github.com/anza-xyz/agave/blob/v2.2.0/cost-model/src/cost_tracker.rs#L281 */
359 0 : ulong cost = transaction_cost_sum( tx_cost );
360 :
361 : /* https://github.com/anza-xyz/agave/blob/v2.2.0/cost-model/src/cost_tracker.rs#L283-L288 */
362 0 : if( fd_transaction_cost_is_simple_vote( tx_cost ) ) {
363 0 : if( FD_UNLIKELY( fd_ulong_sat_add( cost_tracker->vote_cost, cost )>cost_tracker->vote_cost_limit ) ) {
364 0 : return FD_COST_TRACKER_ERROR_WOULD_EXCEED_VOTE_MAX_LIMIT;
365 0 : }
366 0 : }
367 :
368 : /* https://github.com/anza-xyz/agave/blob/v2.2.0/cost-model/src/cost_tracker.rs#L290-L293 */
369 0 : if( FD_UNLIKELY( fd_ulong_sat_add( cost_tracker->block_cost, cost )>cost_tracker->block_cost_limit ) ) {
370 0 : return FD_COST_TRACKER_ERROR_WOULD_EXCEED_BLOCK_MAX_LIMIT;
371 0 : }
372 :
373 : /* https://github.com/anza-xyz/agave/blob/v2.2.0/cost-model/src/cost_tracker.rs#L295-L298 */
374 0 : if( FD_UNLIKELY( cost>cost_tracker->account_cost_limit ) ) {
375 0 : return FD_COST_TRACKER_ERROR_WOULD_EXCEED_ACCOUNT_MAX_LIMIT;
376 0 : }
377 :
378 : /* https://github.com/anza-xyz/agave/blob/v2.2.0/cost-model/src/cost_tracker.rs#L300-L301 */
379 0 : ulong allocated_accounts_data_size = fd_ulong_sat_add( cost_tracker->allocated_accounts_data_size,
380 0 : get_allocated_accounts_data_size( tx_cost ) );
381 :
382 : /* https://github.com/anza-xyz/agave/blob/v2.2.0/cost-model/src/cost_tracker.rs#L303-L304 */
383 0 : if( FD_UNLIKELY( allocated_accounts_data_size>FD_MAX_BLOCK_ACCOUNTS_DATA_SIZE_DELTA ) ) {
384 0 : return FD_COST_TRACKER_ERROR_WOULD_EXCEED_ACCOUNT_DATA_BLOCK_LIMIT;
385 0 : }
386 :
387 : /* https://github.com/anza-xyz/agave/blob/v2.2.0/cost-model/src/cost_tracker.rs#L308-L319 */
388 :
389 0 : account_cost_map_t const * map = fd_type_pun_const(((cost_tracker_outer_t const *)cost_tracker)+1UL);
390 0 : account_cost_t const * pool = fd_type_pun_const( (void*)((ulong)cost_tracker + ((cost_tracker_outer_t const *)cost_tracker)->pool_offset) );
391 :
392 0 : for( ulong i=0UL; i<txn_out->accounts.accounts_cnt; i++ ) {
393 0 : if( !fd_runtime_account_is_writable_idx( txn_in, txn_out, bank, (ushort)i ) ) continue;
394 :
395 0 : fd_pubkey_t const * writable_acc = &txn_out->accounts.account_keys[i];
396 :
397 0 : account_cost_t const * chained_cost = account_cost_map_ele_query_const( map, writable_acc, NULL, pool );
398 0 : if( FD_UNLIKELY( chained_cost && fd_ulong_sat_add( chained_cost->cost, cost )>cost_tracker->account_cost_limit ) ) {
399 0 : return FD_COST_TRACKER_ERROR_WOULD_EXCEED_ACCOUNT_MAX_LIMIT;
400 0 : }
401 0 : }
402 :
403 0 : return FD_COST_TRACKER_SUCCESS;
404 0 : }
405 :
406 : /* https://github.com/anza-xyz/agave/blob/v2.2.0/cost-model/src/cost_tracker.rs#L352-L372 */
407 : static inline void
408 : add_transaction_execution_cost( fd_cost_tracker_t * _cost_tracker,
409 : fd_bank_t * bank,
410 : fd_txn_in_t const * txn_in,
411 : fd_txn_out_t * txn_out,
412 : fd_transaction_cost_t const * tx_cost,
413 0 : ulong adjustment ) {
414 0 : cost_tracker_outer_t * cost_tracker = fd_type_pun( _cost_tracker );
415 0 : account_cost_map_t * map = fd_type_pun( cost_tracker+1UL );
416 0 : account_cost_t * pool = fd_type_pun( (void*)((ulong)cost_tracker+cost_tracker->pool_offset) );
417 :
418 0 : for( ulong i=0UL; i<txn_out->accounts.accounts_cnt; i++ ) {
419 0 : if( FD_LIKELY( !fd_runtime_account_is_writable_idx( txn_in, txn_out, bank, (ushort)i ) ) ) continue;
420 :
421 0 : fd_pubkey_t const * writable_acc = &txn_out->accounts.account_keys[i];
422 :
423 0 : account_cost_t * account_cost = account_cost_map_ele_query( map, writable_acc, NULL, pool );
424 0 : if( FD_UNLIKELY( !account_cost ) ) {
425 0 : FD_TEST( cost_tracker->accounts_used<FD_RUNTIME_MAX_WRITABLE_ACCOUNTS_PER_SLOT );
426 :
427 0 : account_cost = pool+cost_tracker->accounts_used;
428 0 : cost_tracker->accounts_used++;
429 :
430 0 : account_cost->account = *writable_acc;
431 0 : account_cost->cost = adjustment;
432 :
433 0 : account_cost_map_ele_insert( map, account_cost, pool );
434 0 : } else {
435 0 : account_cost->cost = fd_ulong_sat_add( account_cost->cost, adjustment );
436 0 : }
437 0 : }
438 :
439 0 : cost_tracker->cost_tracker->block_cost = fd_ulong_sat_add( cost_tracker->cost_tracker->block_cost, adjustment );
440 0 : if( FD_UNLIKELY( fd_transaction_cost_is_simple_vote( tx_cost ) ) ) {
441 0 : cost_tracker->cost_tracker->vote_cost = fd_ulong_sat_add( cost_tracker->cost_tracker->vote_cost, adjustment );
442 0 : }
443 0 : }
444 :
445 : /* https://github.com/anza-xyz/agave/blob/v2.2.0/cost-model/src/cost_tracker.rs#L325-L335 */
446 : static inline void
447 : add_transaction_cost( fd_cost_tracker_t * cost_tracker,
448 : fd_bank_t * bank,
449 : fd_txn_in_t const * txn_in,
450 : fd_txn_out_t * txn_out,
451 0 : fd_transaction_cost_t const * tx_cost ) {
452 : /* Note: We purposely omit signature counts updates since they're not relevant to cost calculations right now. */
453 0 : cost_tracker->allocated_accounts_data_size += get_allocated_accounts_data_size( tx_cost );
454 0 : add_transaction_execution_cost( cost_tracker, bank, txn_in, txn_out, tx_cost, transaction_cost_sum( tx_cost ) );
455 0 : }
456 :
457 : /* https://github.com/anza-xyz/agave/blob/v2.2.0/cost-model/src/cost_model.rs#L323-L328 */
458 : FD_FN_PURE ulong
459 0 : fd_cost_tracker_calculate_loaded_accounts_data_size_cost( fd_txn_out_t const * txn_out ) {
460 0 : ulong cost = fd_ulong_sat_sub( fd_ulong_sat_add( txn_out->details.loaded_accounts_data_size,
461 0 : FD_ACCOUNT_DATA_COST_PAGE_SIZE ),
462 0 : 1UL );
463 0 : cost /= FD_ACCOUNT_DATA_COST_PAGE_SIZE;
464 0 : return fd_ulong_sat_mul( cost, FD_VM_HEAP_COST );
465 0 : }
466 :
467 : int
468 : fd_cost_tracker_calculate_cost_and_add( fd_cost_tracker_t * cost_tracker,
469 : fd_bank_t * bank,
470 : fd_txn_in_t const * txn_in,
471 0 : fd_txn_out_t * txn_out ) {
472 :
473 : /* https://github.com/anza-xyz/agave/blob/v2.1.0/cost-model/src/cost_model.rs#L83-L85 */
474 0 : fd_transaction_cost_t txn_cost;
475 0 : if( fd_txn_is_simple_vote_transaction( TXN( txn_in->txn ), txn_in->txn->payload ) ) {
476 0 : txn_cost = (fd_transaction_cost_t){ .discriminant = fd_transaction_cost_enum_simple_vote };
477 0 : } else {
478 : /* https://github.com/anza-xyz/agave/blob/v2.2.0/cost-model/src/cost_model.rs#L78-L81 */
479 0 : ulong loaded_accounts_data_size_cost = fd_cost_tracker_calculate_loaded_accounts_data_size_cost( txn_out );
480 :
481 : /* https://github.com/anza-xyz/agave/blob/v2.2.0/cost-model/src/cost_model.rs#L82-L83 */
482 0 : ulong instructions_data_cost = get_instructions_data_cost( txn_in );
483 :
484 : /* https://github.com/anza-xyz/agave/blob/v2.2.0/cost-model/src/cost_model.rs#L85-L93 */
485 0 : txn_cost = calculate_non_vote_transaction_cost( bank, txn_in, txn_out, loaded_accounts_data_size_cost, instructions_data_cost );
486 0 : }
487 :
488 : /* https://github.com/anza-xyz/agave/blob/v2.2.0/cost-model/src/cost_tracker.rs#L167 */
489 0 : int err = would_fit( cost_tracker, bank, txn_in, txn_out, &txn_cost );
490 0 : if( FD_UNLIKELY( err ) ) return err;
491 :
492 : /* We don't need `updated_costliest_account_cost` since it seems to be
493 : for a different use case other than validating block cost limits.
494 : https://github.com/anza-xyz/agave/blob/v2.2.0/cost-model/src/cost_tracker.rs#L168 */
495 0 : add_transaction_cost( cost_tracker, bank, txn_in, txn_out, &txn_cost );
496 0 : return FD_COST_TRACKER_SUCCESS;
497 0 : }
|