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