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