Line data Source code
1 : #ifndef HEADER_fd_src_disco_pack_fd_pack_cost_h
2 : #define HEADER_fd_src_disco_pack_fd_pack_cost_h
3 :
4 : #include "../../ballet/fd_ballet_base.h"
5 : #include "fd_pack.h"
6 : #include "fd_compute_budget_program.h"
7 : #include "../../flamenco/runtime/fd_system_ids_pp.h"
8 : #include "../../ballet/txn/fd_txn.h"
9 : #include "../../ballet/txn/fd_txn_v1.h"
10 :
11 : /* Transaction cost estimates sum signature, write-lock, instruction
12 : data, execution and loaded-account data costs. Execution and loaded
13 : data costs use compute-budget limits before execution.
14 :
15 : The perfect hash below identifies builtins for the default compute
16 : budget when no explicit execution limit is provided. */
17 :
18 : struct __attribute__((aligned(32))) fd_pack_builtin_prog_cost {
19 : uchar program_id[32];
20 : ulong cost_per_instr;
21 : };
22 : typedef struct fd_pack_builtin_prog_cost fd_pack_builtin_prog_cost_t;
23 :
24 : #define MAP_PERFECT_NAME fd_pack_builtin
25 : #define MAP_PERFECT_LG_TBL_SZ 4
26 : #define MAP_PERFECT_T fd_pack_builtin_prog_cost_t
27 : #define MAP_PERFECT_HASH_C 478U
28 : #define MAP_PERFECT_KEY program_id
29 : #define MAP_PERFECT_KEY_T fd_acct_addr_t const *
30 : #define MAP_PERFECT_ZERO_KEY (0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0)
31 : #define MAP_PERFECT_COMPLEX_KEY 1
32 0 : #define MAP_PERFECT_KEYS_EQUAL(k1,k2) (!memcmp( (k1), (k2), 32UL ))
33 :
34 0 : #define PERFECT_HASH( u ) (((478U*(u))>>28)&0xFU)
35 :
36 : #define MAP_PERFECT_HASH_PP( a00,a01,a02,a03,a04,a05,a06,a07,a08,a09,a10,a11,a12,a13,a14,a15, \
37 : a16,a17,a18,a19,a20,a21,a22,a23,a24,a25,a26,a27,a28,a29,a30,a31) \
38 : PERFECT_HASH( (a08 | (a09<<8) | (a10<<16) | (a11<<24)) )
39 0 : #define MAP_PERFECT_HASH_R( ptr ) PERFECT_HASH( fd_uint_load_4( (uchar const *)ptr->b + 8UL ) )
40 :
41 :
42 : /* The cost model estimates 200,000 CUs for builtin programs that were migrated to BPF */
43 : #define MAP_PERFECT_0 ( SYS_PROG_ID ), .cost_per_instr= FD_COMPUTE_BUDGET_MAX_BUILTIN_CU_LIMIT
44 : #define MAP_PERFECT_1 ( COMPUTE_BUDGET_PROG_ID ), .cost_per_instr= FD_COMPUTE_BUDGET_MAX_BUILTIN_CU_LIMIT
45 : #define MAP_PERFECT_2 ( BPF_UPGRADEABLE_PROG_ID ), .cost_per_instr= FD_COMPUTE_BUDGET_MAX_BUILTIN_CU_LIMIT
46 : #define MAP_PERFECT_3 ( BPF_LOADER_1_PROG_ID ), .cost_per_instr= FD_COMPUTE_BUDGET_MAX_BUILTIN_CU_LIMIT
47 : #define MAP_PERFECT_4 ( BPF_LOADER_2_PROG_ID ), .cost_per_instr= FD_COMPUTE_BUDGET_MAX_BUILTIN_CU_LIMIT
48 : #define MAP_PERFECT_5 ( LOADER_V4_PROG_ID ), .cost_per_instr= FD_COMPUTE_BUDGET_MAX_BUILTIN_CU_LIMIT
49 : #define MAP_PERFECT_6 ( KECCAK_SECP_PROG_ID ), .cost_per_instr= FD_COMPUTE_BUDGET_MAX_BUILTIN_CU_LIMIT
50 : #define MAP_PERFECT_7 ( ED25519_SV_PROG_ID ), .cost_per_instr= FD_COMPUTE_BUDGET_MAX_BUILTIN_CU_LIMIT
51 : #define MAP_PERFECT_8 ( SECP256R1_PROG_ID ), .cost_per_instr= 0UL /* Not officially a builtin */
52 :
53 : #include "../../util/tmpl/fd_map_perfect.c"
54 :
55 : /* Redefine it so we can use it below */
56 : #define MAP_PERFECT_HASH_PP( a00,a01,a02,a03,a04,a05,a06,a07,a08,a09,a10,a11,a12,a13,a14,a15, \
57 : a16,a17,a18,a19,a20,a21,a22,a23,a24,a25,a26,a27,a28,a29,a30,a31) \
58 0 : PERFECT_HASH( ((uint)a08 | ((uint)a09<<8) | ((uint)a10<<16) | ((uint)a11<<24)) )
59 :
60 7131 : #define FD_PACK_COST_PER_SIGNATURE ( 720U)
61 7131 : #define FD_PACK_COST_PER_ED25519_SIGNATURE ( 2400U)
62 7131 : #define FD_PACK_COST_PER_SECP256K1_SIGNATURE ( 6690U)
63 7131 : #define FD_PACK_COST_PER_SECP256R1_SIGNATURE ( 4800U)
64 0 : #define FD_PACK_COST_PER_WRITABLE_ACCT ( 300U)
65 7137 : #define FD_PACK_INV_COST_PER_INSTR_DATA_BYTE ( 4U)
66 :
67 : /* The computation here is similar to the computation for the max
68 : fd_txn_t size. There are various things a transaction can include
69 : that consume CUs, and they also consume some bytes of payload. It
70 : then becomes an integer linear programming problem. First, the best
71 : use of bytes is to request 1.4M CUs, which for a V1 transaction is
72 : done using the config value, consuming 4 bytes of payload. This is
73 : 350,000 CUs per byte, which is obviously the best move.
74 :
75 : Precompile signatures are not counted against CU limit, so the best
76 : use of bytes is precompiles, which consume <609 CUs per byte in the
77 : worst case: each secp256k1 signature consumes 6690 CUs and 11 bytes.
78 : Firedancer limits the number of precompile signatures to 16.
79 :
80 : With the 16 precompile signatures used, we can only continue to
81 : increase the cost by adding writable accounts or writable signer
82 : accounts. Writable signers consume 96 bytes use 1020 CUs. Writable
83 : non-signers consume 32 bytes and use 300 CUs. That's 10.6 CUs/byte
84 : and 9.4 CUs/byte, respectively, so in general, writable signers are
85 : more efficient and we want to add as many as we can, up to the
86 : limits of 12 signatures and 64 accounts.
87 :
88 : Finally, with any bytes that remain, we can add them to one of the
89 : instruction datas for 0.25 CUs/byte.
90 :
91 : Note that by default, 64MiB of data are assumed for the loaded
92 : accounts data size cost. This corresponds (currently) to 16384 CUs.
93 :
94 : This gives a transaction that looks like
95 : Field bytes consumed CUs used
96 : version byte 1 0
97 : sig cnt 1 0
98 : ro signed + ro unsigned 2 0
99 : config mask 4 0
100 : Recent blockhash 32 0
101 : instr cnt + acct addr cnt 2 0
102 : 12 signatures 768 8,640
103 : 64 writable pubkeys 2,048 19,200
104 : cu limit config value 4 1,400,000
105 : loaded data config value 4 16,384
106 : secp256k1 precompile header 4 0
107 : 16 secp256k1 sigs 177 107,040
108 : remaining bytes 1,049 0
109 : instr data cost 0 306
110 : + ---------------------------------------------------------------
111 : 4,096 1,551,570
112 :
113 : One of the main take-aways from this is that the cost of a
114 : transaction easily fits in a uint. */
115 : #define FD_PACK_MAX_TXN_COST (1551570UL)
116 : FD_STATIC_ASSERT( FD_PACK_MAX_TXN_COST < (ulong)UINT_MAX, fd_pack_max_cost );
117 :
118 : /* Every transaction has at least a fee payer, a writable signer. */
119 0 : #define FD_PACK_MIN_TXN_COST (FD_PACK_COST_PER_SIGNATURE+FD_PACK_COST_PER_WRITABLE_ACCT)
120 :
121 : /* The Solana protocol tries to impose a limit on the total amount of
122 : account data that can be allocated in a block (via the creation of
123 : new accounts and the growth of existing accounts). Unfortunately,
124 : the way this limit is specified by the protocol is buggy and means
125 : that it is neither an upper bound nor a lower bound for what it is
126 : supposed to measure. If USE_TRUE_ALLOC_BOUND is set to 1, this code
127 : will compute a true upper bound for the amount the transaction can
128 : allocate. If it is set to 0, it will compute the value in an
129 : Agave-compatible way, that is, a value that is normally identical to
130 : what Agave produces, but slightly higher in certain cases. */
131 : #define FD_PACK_COST_USE_TRUE_ALLOC_BOUND 0
132 :
133 : /* NOTE: THE FOLLOWING CONSTANTS ARE CONSENSUS CRITICAL AND CANNOT BE
134 : CHANGED WITHOUT COORDINATING WITH ANZA. */
135 :
136 : /* These are bounds on known limits. Upper bound values are used to
137 : calculate memory footprints while lower bounds are used for
138 : initializing consensus-dependent logic and invariant checking. As a
139 : leader, it is OK to produce blocks using limits smaller than the
140 : active on-chain limits. Replay should always use the correct
141 : chain-derived limits.
142 :
143 : The actual limits used by pack may be updated dynamically to some
144 : in-bounds value. If there is an anticipated feature activation that
145 : changes these limits, the upper bound should be the largest
146 : anticipated value while the lower bound should be the current active
147 : limit. */
148 0 : #define FD_PACK_MAX_COST_PER_BLOCK_LOWER_BOUND (30000000UL)
149 0 : #define FD_PACK_MAX_WRITE_COST_PER_ACCT_LOWER_BOUND (12000000UL)
150 :
151 : /* The 200ms slot params with raise_block_limits_to_100m (simd 0286,
152 : simd 0525). Clusters with a larger block cost limit are not
153 : supported. */
154 0 : #define FD_PACK_MAX_COST_PER_BLOCK_UPPER_BOUND ( 50000000UL)
155 0 : #define FD_PACK_MAX_WRITE_COST_PER_ACCT_UPPER_BOUND ( FD_PACK_MAX_COST_PER_BLOCK_UPPER_BOUND * 4UL / 10UL ) /* simd 0306 */
156 :
157 : /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.1/cost-model/src/block_cost_limits.rs#L39-L41 */
158 0 : #define FD_PACK_MAX_ALLOCATED_DATA_PER_BLOCK ( 100UL*1000UL*1000UL )
159 :
160 : FD_STATIC_ASSERT( FD_MAX_TXN_PER_SLOT_CU==(FD_PACK_MAX_COST_PER_BLOCK_UPPER_BOUND/FD_PACK_MIN_TXN_COST), max_txn_per_slot_cu );
161 :
162 : /* The txncache should at most store one entry for each transaction, so
163 : you would expect this value to be just FD_MAX_TXN_PER_SLOT. But
164 : there's a minor complication ... Agave inserts each transaction into
165 : the status cache twice, once with the signature as the key, and
166 : once with the message hash as the key. This is to support querying
167 : transactions by either signature or message hash. We load snapshots
168 : from Agave nodes which serve both entries, and there is no way to
169 : filter out the by signature entries which are useless to us, so
170 : initially the status cache needs twice as much space. */
171 0 : #define FD_PACK_MAX_TXNCACHE_TXN_PER_SLOT (2UL*FD_MAX_TXN_PER_SLOT)
172 :
173 : /* Computes the total cost and a few related properties for the
174 : specified transaction. On success, returns the cost, which is in
175 : [1020, FD_PACK_MAX_TXN_COST].
176 :
177 : Additionally:
178 : If opt_execution_cost is non-null, on success it will contain the
179 : execution cost (BPF execution cost + built-in execution cost). This
180 : value is in [0, the returned value).
181 : If opt_fee is non-null, on success it will contain the priority fee,
182 : measured in lamports (i.e. the part of the fee that excludes the
183 : per-signature fee). This value is in [0, ULONG_MAX].
184 : If opt_precompile_sig_cnt is non-null, on success it will contain the
185 : total number of signatures in secp256k1, Ed25519, and secp256r1
186 : precompile instructions. This value is in [0, 16]. Note that this
187 : does not do full parsing of the precompile instruction, and it may
188 : be malformed.
189 : If opt_loaded_accounts_data_cost is non-null, on success it will
190 : contain the total requested cost due to loaded accounts data. This
191 : value is in [0, the returned value).
192 : If opt_allocated_data is non-null, on success it will contain a value
193 : that depends on FD_PACK_COST_USE_TRUE_ALLOC_BOUND (see above),
194 : relating to the amount of data (in bytes) this transaction can
195 : allocate. This value is in [0, 20MB].
196 :
197 : On failure, returns 0 and does not modify the value pointed to by
198 : opt_execution_cost, opt_fee, or opt_precompile_sig_cnt. */
199 : static inline ulong
200 : fd_pack_compute_cost( fd_txn_t const * txn,
201 : uchar const * payload,
202 : ulong * opt_execution_cost,
203 : ulong * opt_fee,
204 : ulong * opt_precompile_sig_cnt,
205 : ulong * opt_loaded_accounts_data_cost,
206 0 : ulong * opt_allocated_data ) {
207 :
208 0 : #define ROW(x) fd_pack_builtin_tbl + MAP_PERFECT_HASH_PP( x )
209 0 : fd_pack_builtin_prog_cost_t const * compute_budget_row = ROW( COMPUTE_BUDGET_PROG_ID );
210 0 : fd_pack_builtin_prog_cost_t const * ed25519_precompile_row = ROW( ED25519_SV_PROG_ID );
211 0 : fd_pack_builtin_prog_cost_t const * secp256k1_precomp_row = ROW( KECCAK_SECP_PROG_ID );
212 0 : fd_pack_builtin_prog_cost_t const * secp256r1_precomp_row = ROW( SECP256R1_PROG_ID );
213 : /* The three programs that are able to allocate more than 10 kB per
214 : account */
215 0 : fd_pack_builtin_prog_cost_t const * system_program_row = ROW( SYS_PROG_ID );
216 : #if FD_PACK_COST_USE_TRUE_ALLOC_BOUND
217 : fd_pack_builtin_prog_cost_t const * upgradeable_loader_row = ROW( BPF_UPGRADEABLE_PROG_ID );
218 : fd_pack_builtin_prog_cost_t const * loader_v4_row = ROW( LOADER_V4_PROG_ID );
219 : #endif /* FD_PACK_COST_USE_TRUE_ALLOC_BOUND */
220 0 : #undef ROW
221 :
222 : /* We need to be mindful of overflow here, but it's not terrible.
223 : signature_cost < FD_TXN_ACCT_ADDR_MAX*720 + FD_TXN_INSTR_MAX * UCHAR_MAX * 6690,
224 : writable_cost <= FD_TXN_ACCT_ADDR_MAX*300 */
225 0 : ulong signature_cnt = fd_txn_account_cnt( txn, FD_TXN_ACCT_CAT_SIGNER );
226 0 : ulong signature_cost = FD_PACK_COST_PER_SIGNATURE * signature_cnt;
227 0 : ulong writable_cost = FD_PACK_COST_PER_WRITABLE_ACCT * fd_txn_account_cnt( txn, FD_TXN_ACCT_CAT_WRITABLE );
228 :
229 0 : ulong instr_data_sz = 0UL; /* < FD_TPU_MTU */
230 0 : ulong non_builtin_cnt = 0UL; /* <= FD_TXN_INSTR_MAX */
231 0 : ulong precompile_sig_cnt = 0UL; /* <= FD_TXN_INSTR_MAX * UCHAR_MAX */
232 0 : ulong allocated_data = 0UL; /* in bytes */
233 0 : fd_acct_addr_t const * addr_base = fd_txn_get_acct_addrs( txn, payload );
234 :
235 0 : fd_compute_budget_program_state_t cbp[1];
236 0 : fd_compute_budget_program_init( cbp );
237 :
238 0 : for( ulong i=0UL; i<txn->instr_cnt; i++ ) {
239 0 : ulong data_sz = txn->instr[i].data_sz;
240 0 : instr_data_sz += data_sz;
241 :
242 0 : ulong prog_id_idx = (ulong)txn->instr[i].program_id;
243 0 : fd_acct_addr_t const * prog_id = addr_base + prog_id_idx;
244 :
245 : /* Lookup prog_id in hash table */
246 :
247 0 : fd_pack_builtin_prog_cost_t null_row[1] = {{{ 0 }, 0UL }};
248 0 : fd_pack_builtin_prog_cost_t const * in_tbl = fd_pack_builtin_query( prog_id, null_row );
249 0 : non_builtin_cnt += !in_tbl->cost_per_instr; /* null row has 0 cost */
250 :
251 : /* If the transaction is not a V1 transaction, parse the compute budget instruction.
252 : Compute budget instructions are silently ignored for V1 transactions. */
253 0 : if( FD_UNLIKELY( txn->transaction_version!=FD_TXN_V1 && in_tbl==compute_budget_row ) ) {
254 0 : if( FD_UNLIKELY( 0==fd_compute_budget_program_parse( payload+txn->instr[i].data_off, data_sz, cbp ) ) )
255 0 : return 0UL;
256 0 : } else if( FD_UNLIKELY( (in_tbl==ed25519_precompile_row) ) ) {
257 : /* First byte is # of signatures. Branchless tail reading here is
258 : probably okay, but this seems safer. */
259 0 : ulong ed25519_signature_cnt = (data_sz>0) ? (ulong)payload[ txn->instr[i].data_off ] : 0UL;
260 0 : if( FD_UNLIKELY( data_sz<2UL+14UL*ed25519_signature_cnt ) ) return 0UL;
261 0 : precompile_sig_cnt += ed25519_signature_cnt;
262 0 : signature_cost += ed25519_signature_cnt * FD_PACK_COST_PER_ED25519_SIGNATURE;
263 0 : } else if( FD_UNLIKELY( (in_tbl==secp256k1_precomp_row) ) ) {
264 0 : ulong secp256k1_signature_cnt = (data_sz>0) ? (ulong)payload[ txn->instr[i].data_off ] : 0UL;
265 0 : if( FD_UNLIKELY( data_sz<1UL+11UL*secp256k1_signature_cnt ) ) return 0UL;
266 0 : precompile_sig_cnt += secp256k1_signature_cnt;
267 0 : signature_cost += secp256k1_signature_cnt * FD_PACK_COST_PER_SECP256K1_SIGNATURE;
268 0 : } else if( FD_UNLIKELY( (in_tbl==secp256r1_precomp_row) ) ) {
269 0 : ulong secp256r1_signature_cnt = (data_sz>0) ? (ulong)payload[ txn->instr[i].data_off ] : 0UL;
270 0 : if( FD_UNLIKELY( data_sz<2UL+14UL*secp256r1_signature_cnt ) ) return 0UL;
271 0 : precompile_sig_cnt += secp256r1_signature_cnt;
272 0 : signature_cost += secp256r1_signature_cnt * FD_PACK_COST_PER_SECP256R1_SIGNATURE;
273 0 : }
274 : /* BPF programs can allocate 10kB per account per instruction.
275 : The vote program (see below) and the address lookup table program
276 : (up to 56+32*256 bytes) do allocate, and several native programs
277 : can reduce the size of an account, but the only ones that can
278 : possibly allocate more than 10kB per account are below.
279 : Additionally, none of the above programs allocate at all.
280 :
281 : The vote program may allocate FD_VOTE_STATE_V3_SZ or V4, which
282 : are both 3762 bytes. It is caught by the general case and
283 : overestimated at 10kB per account, which is fine.
284 :
285 : Note: This complexity here is pretty gross, but we need a better
286 : bound for allocated_data than 20MB, and this seems to be the best
287 : way. */
288 0 : #define MAX_ALLOC (20UL*1024UL*1024UL) /* mostly to prevent attacker-induced overflow */
289 0 : #define DEFAULT_ALLOC (10UL*1024UL) /* == MAX_PERMITTED_DATA_INCREASE */
290 0 : else if( FD_UNLIKELY( in_tbl==system_program_row ) ) {
291 0 : ulong discriminant = ULONG_MAX;
292 0 : uchar const * base = payload + txn->instr[i].data_off;
293 0 : if( FD_UNLIKELY( data_sz<12UL ) ) continue;
294 0 : discriminant = FD_LOAD( uint, base );
295 0 : base += sizeof(uint); data_sz -= sizeof(uint);
296 0 : ulong seed_len;
297 0 : switch( discriminant ) {
298 0 : case 0UL: /* FD_SYSTEM_PROGRAM_INSTR_CREATE_ACCOUNT */
299 0 : if( FD_UNLIKELY( data_sz<8UL+8UL ) ) break;
300 : /* Note: here (and below), Agave sets alloc to 0 if any of
301 : these instructions request more than 10 MB. We don't
302 : bother with that. If a transaction has an instruction that
303 : requests more than 10 MB and pays a sufficient fee for it,
304 : and then fails, that's not a big problem. We're always
305 : computing a conservative estimate. */
306 0 : allocated_data += fd_ulong_min( MAX_ALLOC, FD_LOAD( ulong, base+8UL ) );
307 0 : break;
308 0 : case 3UL: /* FD_SYSTEM_PROGRAM_INSTR_CREATE_ACCOUNT_WITH_SEED */
309 0 : if( FD_UNLIKELY( data_sz<32UL+8UL ) ) break;
310 0 : seed_len = FD_LOAD( ulong, base+32UL );
311 0 : base += 32UL+8UL; data_sz -= 32UL+8UL;
312 0 : if( FD_UNLIKELY( data_sz<seed_len ) ) break;
313 0 : base += seed_len; data_sz -= seed_len;
314 0 : if( FD_UNLIKELY( data_sz<(8UL+8UL) ) ) break;
315 0 : allocated_data += fd_ulong_min( MAX_ALLOC, FD_LOAD( ulong, base+8UL ) );
316 0 : break;
317 0 : case 8UL: /* FD_SYSTEM_PROGRAM_INSTR_ALLOCATE */
318 0 : if( FD_UNLIKELY( data_sz<8UL ) ) break;
319 0 : allocated_data += fd_ulong_min( MAX_ALLOC, FD_LOAD( ulong, base ) );
320 0 : break;
321 0 : case 9UL: /* FD_SYSTEM_PROGRAM_INSTR_ALLOCATE_WITH_SEED */
322 0 : if( FD_UNLIKELY( data_sz<32UL+8UL ) ) break;
323 0 : seed_len = FD_LOAD( ulong, base+32UL );
324 0 : base += 32UL+8UL; data_sz -= 32UL+8UL;
325 0 : if( FD_UNLIKELY( data_sz<seed_len ) ) break;
326 0 : base += seed_len; data_sz -= seed_len;
327 0 : if( FD_UNLIKELY( data_sz<8UL ) ) break;
328 0 : allocated_data += fd_ulong_min( MAX_ALLOC, FD_LOAD( ulong, base ) );
329 0 : break;
330 0 : case 13UL: /* create_account_allow_prefund */
331 : /* Agave returns 0 here until the feature gate has been
332 : activated. Rather than feature gate this, we just act
333 : conservatively. */
334 0 : if( FD_UNLIKELY( data_sz<8UL+8UL ) ) break;
335 0 : allocated_data += fd_ulong_min( MAX_ALLOC, FD_LOAD( ulong, base+8UL ) );
336 0 : break;
337 0 : default:
338 0 : break;
339 0 : }
340 : #if FD_PACK_COST_USE_TRUE_ALLOC_BOUND
341 : } else if( FD_UNLIKELY( in_tbl==upgradeable_loader_row ) ) {
342 : ulong discriminant = ULONG_MAX;
343 : uchar const * base = payload + txn->instr[i].data_off;
344 : if( FD_UNLIKELY( data_sz<8UL ) ) continue;
345 : discriminant = FD_LOAD( uint, base );
346 : base += sizeof(uint); data_sz -= sizeof(uint);
347 : switch( discriminant ) {
348 : case 6UL: /* fd_bpf_upgradeable_loader_program_instruction_enum_extend_program */
349 : case 9UL: /* fd_bpf_upgradeable_loader_program_instruction_enum_extend_program_checked */
350 : allocated_data += fd_ulong_min( MAX_ALLOC, FD_LOAD( uint, base ) );
351 : break;
352 : default:
353 : allocated_data += DEFAULT_ALLOC; /* Some other instructions may alloc a tiny bit */
354 : break;
355 : }
356 : } else if( FD_UNLIKELY( in_tbl==loader_v4_row ) ) {
357 : ulong discriminant = ULONG_MAX;
358 : uchar const * base = payload + txn->instr[i].data_off;
359 : if( FD_UNLIKELY( data_sz<8UL ) ) continue;
360 : discriminant = FD_LOAD( uint, base );
361 : base += sizeof(uint); data_sz -= sizeof(uint);
362 : switch( discriminant ) {
363 : case 2UL: /* fd_loader_v4_program_instruction_enum_set_program_length */
364 : allocated_data += fd_ulong_min( MAX_ALLOC, FD_LOAD( uint, base ) );
365 : break;
366 : default:
367 : break;
368 : }
369 : } else {
370 : /* This could be the writable accounts only, but this bound is
371 : much cheaper, and good enough. */
372 : allocated_data += DEFAULT_ALLOC * txn->instr[i].acct_cnt;
373 : #endif /* FD_PACK_COST_USE_TRUE_ALLOC_BOUND */
374 0 : }
375 0 : }
376 :
377 : /* Firedancer's block packer limits the total amount of precompile
378 : signatures in a transaction to 16. Anything beyond this is likely
379 : spam. */
380 0 : if( FD_UNLIKELY( precompile_sig_cnt>16UL ) ) return 0UL;
381 :
382 : /* E.g. a transaction can alloc a 10MB account, close it, and repeat
383 : many times. According to the spec, this would count as a 20MB
384 : allocation. See
385 : https://github.com/anza-xyz/agave/blob/2a61a3ecd417b0515c0b2f322d0128394f20626b/cost-model/src/cost_model.rs#L317-L318
386 : */
387 0 : allocated_data = fd_ulong_min( allocated_data, 20UL*1024UL*1024UL ); /* MAX_PERMITTED_ACCOUNTS_DATA_ALLOCATIONS_PER_TRANSACTION */
388 0 : #undef MAX_ALLOC
389 0 : #undef DEFAULT_ALLOC
390 :
391 0 : ulong instr_data_cost = instr_data_sz / FD_PACK_INV_COST_PER_INSTR_DATA_BYTE; /* < FD_TPU_MTU/FD_PACK_INV_COST_PER_INSTR_DATA_BYTE == 1024 */
392 :
393 0 : ulong fee[1];
394 0 : uint execution_cost[1];
395 0 : ulong loaded_account_data_cost[1];
396 :
397 : /* V1 transactions use the config mask, and silently ignore compute
398 : budget program instructions. */
399 0 : if( txn->transaction_version==FD_TXN_V1 ) {
400 0 : ulong v1_priority_fee, v1_cu_limit, v1_loaded, v1_heap;
401 0 : uint v1_config_mask = fd_uint_load_4( payload+4 );
402 0 : fd_txn_parse_v1_config( v1_config_mask, payload+txn->v1_txn_config_values_off,
403 0 : &v1_priority_fee, &v1_cu_limit, &v1_loaded, &v1_heap );
404 0 : *fee = v1_priority_fee;
405 0 : *execution_cost = (uint)fd_ulong_min( v1_cu_limit, FD_COMPUTE_BUDGET_MAX_CU_LIMIT );
406 0 : v1_loaded = fd_ulong_min( v1_loaded, FD_COMPUTE_BUDGET_MAX_LOADED_DATA_SZ );
407 :
408 : /* V1 transactions which request a loaded accounts data size limit
409 : of 0, or do not explicitly set this limit, are not thrown out.
410 :
411 : As of define_ltds_fee_only_semantics, which is activated on
412 : all networks, the loaded accounts data size for fee-only
413 : transactions that fail due to exceeding the requested loaded
414 : accounts data size limit is the requested loaded accounts data
415 : size limit.
416 : This means that advance nonce transactions that request a loaded
417 : accounts data size limit of 0 are still packed and the nonce
418 : still advances.
419 : https://github.com/anza-xyz/agave/blob/v4.2.0-beta.1/core/src/banking_stage/qos_service.rs#L68-L72
420 :
421 : V1 transactions which request a loaded account data
422 : size limit which is not a multiple of 32 KiB have the loaded
423 : account data cost rounded up to the nearest 32 KiB. This matches
424 : Agave's cost tracker behaviour.
425 : https://github.com/anza-xyz/agave/blob/v4.2.0-beta.1/cost-model/src/cost_model.rs#L188-L203 */
426 0 : *loaded_account_data_cost = FD_COMPUTE_BUDGET_HEAP_COST *
427 0 : ( ( v1_loaded + FD_COMPUTE_BUDGET_ACCOUNT_DATA_COST_PAGE_SIZE - 1UL )
428 0 : / FD_COMPUTE_BUDGET_ACCOUNT_DATA_COST_PAGE_SIZE );
429 0 : } else {
430 0 : fd_compute_budget_program_finalize( cbp, txn->instr_cnt, txn->instr_cnt-non_builtin_cnt, fee, execution_cost, loaded_account_data_cost );
431 0 : }
432 :
433 0 : fd_ulong_store_if( !!opt_execution_cost, opt_execution_cost, (ulong)(*execution_cost) );
434 0 : fd_ulong_store_if( !!opt_fee, opt_fee, *fee );
435 0 : fd_ulong_store_if( !!opt_precompile_sig_cnt, opt_precompile_sig_cnt, precompile_sig_cnt );
436 0 : fd_ulong_store_if( !!opt_loaded_accounts_data_cost, opt_loaded_accounts_data_cost, *loaded_account_data_cost );
437 0 : fd_ulong_store_if( !!opt_allocated_data, opt_allocated_data, allocated_data );
438 :
439 : #if DETAILED_LOGGING
440 : FD_BASE58_ENCODE_64_BYTES( (const uchar *)fd_txn_get_signatures(txn, payload), signature_cstr );
441 : FD_LOG_NOTICE(( "TXN signature[%s] signature_cost[%lu] writable_cost[%lu] instr_data_cost[%lu] non_builtin_cnt[%lu] loaded_account_data_cost[%lu] precompile_sig_cnt[%lu] fee[%lu]",
442 : signature_cstr, signature_cost, writable_cost, instr_data_cost, non_builtin_cnt, *loaded_account_data_cost, precompile_sig_cnt, *fee));
443 : #endif
444 :
445 : /* <= FD_PACK_MAX_COST, so no overflow concerns */
446 0 : ulong total_cost = signature_cost + writable_cost + (ulong)(*execution_cost) + instr_data_cost + *loaded_account_data_cost;
447 :
448 0 : return total_cost;
449 0 : }
450 : #undef MAP_PERFECT_HASH_PP
451 : #undef PERFECT_HASH
452 :
453 : #endif /* HEADER_fd_src_disco_pack_fd_pack_cost_h */
|