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