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 : /* The functions in this header implement the transaction cost model
12 : that is soon to be part of consensus.
13 : The cost model consists of several components:
14 : * per-signature cost: The costs associated with transaction
15 : signatures + signatures from precompiles (ED25519 + SECP256*)
16 : * per-write-lock cost: cost associated with acquiring write locks
17 : for writable accounts listed in the transaction.
18 : * instruction data length cost: The fixed cost for each instruction
19 : data byte in the transaction payload.
20 : * built-in execution cost: The fixed cost associated with "builtin"
21 : instructions. "What are builtins" is defined here:
22 : https://github.com/anza-xyz/agave/blob/1baa4033e0d2d4175373f07b73ddda2f3cc0a8d6/builtins-default-costs/src/lib.rs#L120-L200
23 : After SIMD 170, all builtins have a fixed cost of 3000 cus.
24 : * BPF execution cost: The costs associated with any instruction
25 : that is not a builtin. This value comes from the VM after
26 : transaction execution.
27 : * loaded accounts data cost: Costs associated with all the account
28 : data loaded from the chain. This value is known in the
29 : transaction loading stage, after accounts data is loaded.
30 : These are all summed to determine the total transaction cost. */
31 :
32 : /* Simple votes use the full transaction cost model. Pack recognizes
33 : them so it can use a tight execution-cost upper bound and maintain a
34 : separate vote reservation for scheduling. The reservation is not a
35 : runtime consensus limit. */
36 :
37 : /* To compute the built-in cost, we need to check a table. The table
38 : is known ahead of time though, so we can build a perfect hash
39 : table for performance.
40 : The values of the table are based on https://github.com/
41 : solana-labs/solana/blob/9fb105c801e2999a24f0773443d6164e30c9ff0c/
42 : runtime/src/block_cost_limits.rs#L34-L47 . */
43 :
44 :
45 :
46 : struct __attribute__((aligned(32))) fd_pack_builtin_prog_cost {
47 : uchar program_id[32];
48 : ulong cost_per_instr;
49 : };
50 : typedef struct fd_pack_builtin_prog_cost fd_pack_builtin_prog_cost_t;
51 :
52 : #define MAP_PERFECT_NAME fd_pack_builtin
53 : #define MAP_PERFECT_LG_TBL_SZ 4
54 : #define MAP_PERFECT_T fd_pack_builtin_prog_cost_t
55 : #define MAP_PERFECT_HASH_C 478U
56 : #define MAP_PERFECT_KEY program_id
57 : #define MAP_PERFECT_KEY_T fd_acct_addr_t const *
58 : #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)
59 : #define MAP_PERFECT_COMPLEX_KEY 1
60 501627 : #define MAP_PERFECT_KEYS_EQUAL(k1,k2) (!memcmp( (k1), (k2), 32UL ))
61 :
62 836952 : #define PERFECT_HASH( u ) (((478U*(u))>>28)&0xFU)
63 :
64 : #define MAP_PERFECT_HASH_PP( a00,a01,a02,a03,a04,a05,a06,a07,a08,a09,a10,a11,a12,a13,a14,a15, \
65 : a16,a17,a18,a19,a20,a21,a22,a23,a24,a25,a26,a27,a28,a29,a30,a31) \
66 : PERFECT_HASH( (a08 | (a09<<8) | (a10<<16) | (a11<<24)) )
67 501627 : #define MAP_PERFECT_HASH_R( ptr ) PERFECT_HASH( fd_uint_load_4( (uchar const *)ptr->b + 8UL ) )
68 :
69 :
70 : /* The cost model estimates 200,000 CUs for builtin programs that were migrated to BPF */
71 : #define MAP_PERFECT_0 ( SYS_PROG_ID ), .cost_per_instr= FD_COMPUTE_BUDGET_MAX_BUILTIN_CU_LIMIT
72 : #define MAP_PERFECT_1 ( COMPUTE_BUDGET_PROG_ID ), .cost_per_instr= FD_COMPUTE_BUDGET_MAX_BUILTIN_CU_LIMIT
73 : #define MAP_PERFECT_2 ( BPF_UPGRADEABLE_PROG_ID ), .cost_per_instr= FD_COMPUTE_BUDGET_MAX_BUILTIN_CU_LIMIT
74 : #define MAP_PERFECT_3 ( BPF_LOADER_1_PROG_ID ), .cost_per_instr= FD_COMPUTE_BUDGET_MAX_BUILTIN_CU_LIMIT
75 : #define MAP_PERFECT_4 ( BPF_LOADER_2_PROG_ID ), .cost_per_instr= FD_COMPUTE_BUDGET_MAX_BUILTIN_CU_LIMIT
76 : #define MAP_PERFECT_5 ( LOADER_V4_PROG_ID ), .cost_per_instr= FD_COMPUTE_BUDGET_MAX_BUILTIN_CU_LIMIT
77 : #define MAP_PERFECT_6 ( KECCAK_SECP_PROG_ID ), .cost_per_instr= FD_COMPUTE_BUDGET_MAX_BUILTIN_CU_LIMIT
78 : #define MAP_PERFECT_7 ( ED25519_SV_PROG_ID ), .cost_per_instr= FD_COMPUTE_BUDGET_MAX_BUILTIN_CU_LIMIT
79 : #define MAP_PERFECT_8 ( SECP256R1_PROG_ID ), .cost_per_instr= 0UL /* Not officially a builtin */
80 :
81 : #include "../../util/tmpl/fd_map_perfect.c"
82 :
83 : /* Redefine it so we can use it below */
84 : #define MAP_PERFECT_HASH_PP( a00,a01,a02,a03,a04,a05,a06,a07,a08,a09,a10,a11,a12,a13,a14,a15, \
85 : a16,a17,a18,a19,a20,a21,a22,a23,a24,a25,a26,a27,a28,a29,a30,a31) \
86 335325 : PERFECT_HASH( ((uint)a08 | ((uint)a09<<8) | ((uint)a10<<16) | ((uint)a11<<24)) )
87 :
88 67479 : #define FD_PACK_COST_PER_SIGNATURE ( 720U)
89 414 : #define FD_PACK_COST_PER_ED25519_SIGNATURE ( 2400U)
90 414 : #define FD_PACK_COST_PER_SECP256K1_SIGNATURE ( 6690U)
91 414 : #define FD_PACK_COST_PER_SECP256R1_SIGNATURE ( 4800U)
92 67944 : #define FD_PACK_COST_PER_WRITABLE_ACCT ( 300U)
93 67476 : #define FD_PACK_INV_COST_PER_INSTR_DATA_BYTE ( 4U)
94 :
95 : /* The computation here is similar to the computation for the max
96 : fd_txn_t size. There are various things a transaction can include
97 : that consume CUs, and they also consume some bytes of payload. It
98 : then becomes an integer linear programming problem. First, the best
99 : use of bytes is to request 1.4M CUs, which for a V1 transaction is
100 : done using the config value, consuming 4 bytes of payload. This is
101 : 350,000 CUs per byte, which is obviously the best move.
102 :
103 : Precompile signatures are not counted against CU limit, so the best
104 : use of bytes is precompiles, which consume <609 CUs per byte in the
105 : worst case: each secp256k1 signature consumes 6690 CUs and 11 bytes.
106 : Firedancer limits the number of precompile signatures to 16.
107 :
108 : With the 16 precompile signatures used, we can only continue to
109 : increase the cost by adding writable accounts or writable signer
110 : accounts. Writable signers consume 96 bytes use 1020 CUs. Writable
111 : non-signers consume 32 bytes and use 300 CUs. That's 10.6 CUs/byte
112 : and 9.4 CUs/byte, respectively, so in general, writable signers are
113 : more efficient and we want to add as many as we can, up to the
114 : limits of 12 signatures and 64 accounts.
115 :
116 : Finally, with any bytes that remain, we can add them to one of the
117 : instruction datas for 0.25 CUs/byte.
118 :
119 : Note that by default, 64MiB of data are assumed for the loaded
120 : accounts data size cost. This corresponds (currently) to 16384 CUs.
121 :
122 : This gives a transaction that looks like
123 : Field bytes consumed CUs used
124 : version byte 1 0
125 : sig cnt 1 0
126 : ro signed + ro unsigned 2 0
127 : config mask 4 0
128 : Recent blockhash 32 0
129 : instr cnt + acct addr cnt 2 0
130 : 12 signatures 768 8,640
131 : 64 writable pubkeys 2,048 19,200
132 : cu limit config value 4 1,400,000
133 : loaded data config value 4 16,384
134 : secp256k1 precompile header 4 0
135 : 16 secp256k1 sigs 177 107,040
136 : remaining bytes 1,049 0
137 : instr data cost 0 306
138 : + ---------------------------------------------------------------
139 : 4,096 1,551,570
140 :
141 : One of the main take-aways from this is that the cost of a
142 : transaction easily fits in a uint. */
143 : #define FD_PACK_MAX_TXN_COST (1551570UL)
144 : FD_STATIC_ASSERT( FD_PACK_MAX_TXN_COST < (ulong)UINT_MAX, fd_pack_max_cost );
145 :
146 : /* Every transaction has at least a fee payer, a writable signer. */
147 : #define FD_PACK_MIN_TXN_COST (FD_PACK_COST_PER_SIGNATURE+FD_PACK_COST_PER_WRITABLE_ACCT)
148 :
149 : /* The Solana protocol tries to impose a limit on the total amount of
150 : account data that can be allocated in a block (via the creation of
151 : new accounts and the growth of existing accounts). Unfortunately,
152 : the way this limit is specified by the protocol is buggy and means
153 : that it is neither an upper bound nor a lower bound for what it is
154 : supposed to measure. If USE_TRUE_ALLOC_BOUND is set to 1, this code
155 : will compute a true upper bound for the amount the transaction can
156 : allocate. If it is set to 0, it will compute the value in an
157 : Agave-compatible way, that is, a value that is normally identical to
158 : what Agave produces, but slightly higher in certain cases. */
159 : #define FD_PACK_COST_USE_TRUE_ALLOC_BOUND 0
160 :
161 : /* NOTE: THE FOLLOWING CONSTANTS ARE CONSENSUS CRITICAL AND CANNOT BE
162 : CHANGED WITHOUT COORDINATING WITH ANZA. */
163 :
164 : /* These are bounds on known limits. Upper bound values are used to
165 : calculate memory footprints while lower bounds are used for
166 : initializing consensus-dependent logic and invariant checking. As a
167 : leader, it is OK to produce blocks using limits smaller than the
168 : active on-chain limits. Replay should always use the correct
169 : chain-derived limits.
170 :
171 : The actual limits used by pack may be updated dynamically to some
172 : in-bounds value. If there is an anticipated feature activation that
173 : changes these limits, the upper bound should be the largest
174 : anticipated value while the lower bound should be the current active
175 : limit. For Frankendancer, the actual value used for consensus will be
176 : retrieved from Agave at runtime. */
177 0 : #define FD_PACK_MAX_COST_PER_BLOCK_LOWER_BOUND (30000000UL)
178 0 : #define FD_PACK_MAX_VOTE_COST_PER_BLOCK_LOWER_BOUND (36000000UL)
179 0 : #define FD_PACK_MAX_WRITE_COST_PER_ACCT_LOWER_BOUND (12000000UL)
180 :
181 0 : #define FD_PACK_MAX_COST_PER_BLOCK_UPPER_BOUND (100000000UL) /* simd 0286 */
182 0 : #define FD_PACK_MAX_VOTE_COST_PER_BLOCK_UPPER_BOUND ( 36000000UL)
183 0 : #define FD_PACK_MAX_WRITE_COST_PER_ACCT_UPPER_BOUND ( FD_PACK_MAX_COST_PER_BLOCK_UPPER_BOUND * 4UL / 10UL ) /* simd 0306 */
184 :
185 : /* https://github.com/anza-xyz/agave/blob/v4.0.0-beta.1/cost-model/src/block_cost_limits.rs#L39-L41 */
186 288 : #define FD_PACK_MAX_ALLOCATED_DATA_PER_BLOCK ( 100UL*1000UL*1000UL )
187 :
188 : 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 );
189 :
190 : /* The txncache should at most store one entry for each transaction, so
191 : you would expect this value to be just FD_MAX_TXN_PER_SLOT. But
192 : there's a minor complication ... Agave inserts each transaction into
193 : the status cache twice, once with the signature as the key, and
194 : once with the message hash as the key. This is to support querying
195 : transactions by either signature or message hash. We load snapshots
196 : from Agave nodes which serve both entries, and there is no way to
197 : filter out the by signature entries which are useless to us, so
198 : initially the status cache needs twice as much space. */
199 6 : #define FD_PACK_MAX_TXNCACHE_TXN_PER_SLOT (2UL*FD_MAX_TXN_PER_SLOT)
200 :
201 :
202 : /* https://github.com/anza-xyz/agave/blob/v2.0.1/programs/vote/src/vote_processor.rs#L55 */
203 :
204 6879 : #define FD_PACK_VOTE_DEFAULT_COMPUTE_UNITS (2100UL)
205 :
206 : /* SIMD-0387: BLS proof-of-possession verification cost charged by the
207 : vote program for authorize instructions.
208 :
209 : https://github.com/anza-xyz/agave/blob/v4.0.0-alpha.0/programs/vote/src/vote_processor.rs#L83 */
210 6879 : #define FD_PACK_BLS_PROOF_OF_POSSESSION_VERIFICATION_COMPUTE_UNITS (34500UL)
211 :
212 : /* Upper bound on execution CUs used by vote instructions.
213 :
214 : The only vote instructions which use more than the default cost are
215 : the authorize instruction, which also charge the BLS
216 : proof-of-possession verification cost.
217 :
218 : IMPORTANT: if the vote program starts charging more CUs for any
219 : instructions, this constant will need to be updated.
220 : */
221 6879 : #define FD_PACK_VOTE_MAX_COMPUTE_UNITS (FD_PACK_VOTE_DEFAULT_COMPUTE_UNITS + FD_PACK_BLS_PROOF_OF_POSSESSION_VERIFICATION_COMPUTE_UNITS)
222 :
223 : /* Max loaded account data size: FD_COMPUTE_BUDGET_MAX_LOADED_DATA_SZ
224 :
225 : Cost: ceil( FD_COMPUTE_BUDGET_MAX_LOADED_DATA_SZ / 32,768 ) * 8
226 : = ceil( 67,108,864 / 32,768 ) * 8
227 : = 2,048 * 8
228 : = 16,384 CUs */
229 : #define FD_PACK_VOTE_DEFAULT_LOADED_ACCOUNTS_DATA_COST (16384UL)
230 :
231 : /* Maximum instruction data cost for a simple vote transaction:
232 : - 1 signature
233 : - 2 transaction accounts
234 : - 0 instruction accounts
235 :
236 : Which results in a fixed overhead of:
237 : - Signature count: 1
238 : - 1 signature: 64
239 : - Message header: 3
240 : - Account count: 1
241 : - 2 account entries: 64
242 : - Recent blockhash: 32
243 : - Instruction count: 1
244 : - program_id: 1
245 : - Instruction account count: 1
246 : - Instruction data length: 1
247 : Total: 169 bytes
248 :
249 : Leaving (FD_TXN_MTU - 169) = 1063 bytes for the instruction data.
250 : This results in a cost of 1063 / 4 = 265 CUs.
251 : */
252 : #define FD_PACK_SIMPLE_VOTE_MAX_INSTR_DATA_COST (265UL)
253 :
254 : /* A simple vote transaction is any transaction that satisfies the
255 : following conditions:
256 : - Has 1 or 2 signatures
257 : - Is a legacy transaction
258 : - Has exactly one instruction
259 : - Must invoke the vote program
260 :
261 : Therefore the worst-case most expensive simple vote transaction has:
262 : - 2 signatures
263 : - 35 writable accounts (see FD_TXN_ACCT_ADDR_MAX)
264 : - The maximum amount of compute units for a vote program instruction
265 : - 1 instruction of the maximum instruction data size
266 : - The maximum amount of loaded accounts data
267 : = 65,189 CUs
268 : */
269 : static const ulong FD_PACK_MAX_SIMPLE_VOTE_COST = ( 2UL*FD_PACK_COST_PER_SIGNATURE + /* 1,440 */
270 : 35UL*FD_PACK_COST_PER_WRITABLE_ACCT + /* 10,500 */
271 : FD_PACK_VOTE_MAX_COMPUTE_UNITS + /* 36,600 */
272 : FD_PACK_VOTE_DEFAULT_LOADED_ACCOUNTS_DATA_COST + /* 16,384 */
273 : FD_PACK_SIMPLE_VOTE_MAX_INSTR_DATA_COST ); /* 265 */
274 :
275 : #undef FD_PACK_SIMPLE_VOTE_MAX_INSTR_DATA_COST
276 :
277 : /* Computes the total cost and a few related properties for the
278 : specified transaction. On success, returns the cost, which is in
279 : [1020, FD_PACK_MAX_TXN_COST] and sets or clears the
280 : FD_TXN_P_FLAG_IS_SIMPLE_VOTE bit of the value pointed to by flags to
281 : indicate whether the transaction is a simple vote or not.
282 :
283 : Additionally:
284 : If opt_execution_cost is non-null, on success it will contain the
285 : execution cost (BPF execution cost + built-in execution cost). This
286 : value is in [0, the returned value).
287 : If opt_fee is non-null, on success it will contain the priority fee,
288 : measured in lamports (i.e. the part of the fee that excludes the
289 : per-signature fee). This value is in [0, ULONG_MAX].
290 : If opt_precompile_sig_cnt is non-null, on success it will contain the
291 : total number of signatures in precompile instructions, namely Keccak
292 : and Ed25519 signature verification programs. This value is in [0,
293 : 256*64]. Note that this does not do full parsing of the precompile
294 : instruction, and it may be malformed.
295 : If opt_loaded_accounts_data_cost is non-null, on success it will
296 : contain the total requested cost due to loaded accounts data. This
297 : value is in [0, the returned value).
298 : If opt_allocated_data is non-null, on success it will contain a value
299 : that depends on FD_PACK_COST_USE_TRUE_ALLOC_BOUND (see above),
300 : relating to the amount of data (in bytes) this transaction can
301 : allocate. This value is in [0, 20MB].
302 :
303 : On failure, returns 0 and does not modify the value pointed to by
304 : flags, opt_execution_cost, opt_fee, or opt_precompile_sig_cnt. */
305 : static inline ulong
306 : fd_pack_compute_cost( fd_txn_t const * txn,
307 : uchar const * payload,
308 : uint * flags,
309 : ulong * opt_execution_cost,
310 : ulong * opt_fee,
311 : ulong * opt_precompile_sig_cnt,
312 : ulong * opt_loaded_accounts_data_cost,
313 67065 : ulong * opt_allocated_data ) {
314 :
315 335325 : #define ROW(x) fd_pack_builtin_tbl + MAP_PERFECT_HASH_PP( x )
316 67065 : fd_pack_builtin_prog_cost_t const * compute_budget_row = ROW( COMPUTE_BUDGET_PROG_ID );
317 67065 : fd_pack_builtin_prog_cost_t const * ed25519_precompile_row = ROW( ED25519_SV_PROG_ID );
318 67065 : fd_pack_builtin_prog_cost_t const * secp256k1_precomp_row = ROW( KECCAK_SECP_PROG_ID );
319 67065 : fd_pack_builtin_prog_cost_t const * secp256r1_precomp_row = ROW( SECP256R1_PROG_ID );
320 : /* The three programs that are able to allocate more than 10 kB per
321 : account */
322 67065 : fd_pack_builtin_prog_cost_t const * system_program_row = ROW( SYS_PROG_ID );
323 : #if FD_PACK_COST_USE_TRUE_ALLOC_BOUND
324 : fd_pack_builtin_prog_cost_t const * upgradeable_loader_row = ROW( BPF_UPGRADEABLE_PROG_ID );
325 : fd_pack_builtin_prog_cost_t const * loader_v4_row = ROW( LOADER_V4_PROG_ID );
326 : #endif /* FD_PACK_COST_USE_TRUE_ALLOC_BOUND */
327 67065 : #undef ROW
328 :
329 67065 : int is_simple_vote = fd_txn_is_simple_vote_transaction( txn, payload );
330 67065 : if( FD_UNLIKELY( is_simple_vote ) ) *flags |= FD_TXN_P_FLAGS_IS_SIMPLE_VOTE;
331 60186 : else *flags &= ~FD_TXN_P_FLAGS_IS_SIMPLE_VOTE;
332 :
333 : /* We need to be mindful of overflow here, but it's not terrible.
334 : signature_cost < FD_TXN_ACCT_ADDR_MAX*720 + FD_TXN_INSTR_MAX * UCHAR_MAX * 6690,
335 : writable_cost <= FD_TXN_ACCT_ADDR_MAX*300 */
336 67065 : ulong signature_cnt = fd_txn_account_cnt( txn, FD_TXN_ACCT_CAT_SIGNER );
337 67065 : ulong signature_cost = FD_PACK_COST_PER_SIGNATURE * signature_cnt;
338 67065 : ulong writable_cost = FD_PACK_COST_PER_WRITABLE_ACCT * fd_txn_account_cnt( txn, FD_TXN_ACCT_CAT_WRITABLE );
339 :
340 67065 : ulong instr_data_sz = 0UL; /* < FD_TPU_MTU */
341 67065 : ulong non_builtin_cnt = 0UL; /* <= FD_TXN_INSTR_MAX */
342 67065 : ulong precompile_sig_cnt = 0UL; /* <= FD_TXN_INSTR_MAX * UCHAR_MAX */
343 67065 : ulong allocated_data = 0UL; /* in bytes */
344 67065 : fd_acct_addr_t const * addr_base = fd_txn_get_acct_addrs( txn, payload );
345 :
346 67065 : fd_compute_budget_program_state_t cbp[1];
347 67065 : fd_compute_budget_program_init( cbp );
348 :
349 568689 : for( ulong i=0UL; i<txn->instr_cnt; i++ ) {
350 501627 : ulong data_sz = txn->instr[i].data_sz;
351 501627 : instr_data_sz += data_sz;
352 :
353 501627 : ulong prog_id_idx = (ulong)txn->instr[i].program_id;
354 501627 : fd_acct_addr_t const * prog_id = addr_base + prog_id_idx;
355 :
356 : /* Lookup prog_id in hash table */
357 :
358 501627 : fd_pack_builtin_prog_cost_t null_row[1] = {{{ 0 }, 0UL }};
359 501627 : fd_pack_builtin_prog_cost_t const * in_tbl = fd_pack_builtin_query( prog_id, null_row );
360 501627 : non_builtin_cnt += !in_tbl->cost_per_instr; /* null row has 0 cost */
361 :
362 : /* If the transaction is not a V1 transaction, parse the compute budget instruction.
363 : Compute budget instructions are silently ignored for V1 transactions. */
364 501627 : if( FD_UNLIKELY( txn->transaction_version!=FD_TXN_V1 && in_tbl==compute_budget_row ) ) {
365 180273 : if( FD_UNLIKELY( 0==fd_compute_budget_program_parse( payload+txn->instr[i].data_off, data_sz, cbp ) ) )
366 3 : return 0UL;
367 321354 : } else if( FD_UNLIKELY( (in_tbl==ed25519_precompile_row) ) ) {
368 : /* First byte is # of signatures. Branchless tail reading here is
369 : probably okay, but this seems safer. */
370 0 : ulong ed25519_signature_cnt = (data_sz>0) ? (ulong)payload[ txn->instr[i].data_off ] : 0UL;
371 0 : if( FD_UNLIKELY( data_sz<2UL+14UL*ed25519_signature_cnt ) ) return 0UL;
372 0 : precompile_sig_cnt += ed25519_signature_cnt;
373 0 : signature_cost += ed25519_signature_cnt * FD_PACK_COST_PER_ED25519_SIGNATURE;
374 321354 : } else if( FD_UNLIKELY( (in_tbl==secp256k1_precomp_row) ) ) {
375 0 : ulong secp256k1_signature_cnt = (data_sz>0) ? (ulong)payload[ txn->instr[i].data_off ] : 0UL;
376 0 : if( FD_UNLIKELY( data_sz<1UL+11UL*secp256k1_signature_cnt ) ) return 0UL;
377 0 : precompile_sig_cnt += secp256k1_signature_cnt;
378 0 : signature_cost += secp256k1_signature_cnt * FD_PACK_COST_PER_SECP256K1_SIGNATURE;
379 321354 : } else if( FD_UNLIKELY( (in_tbl==secp256r1_precomp_row) ) ) {
380 0 : ulong secp256r1_signature_cnt = (data_sz>0) ? (ulong)payload[ txn->instr[i].data_off ] : 0UL;
381 0 : if( FD_UNLIKELY( data_sz<2UL+14UL*secp256r1_signature_cnt ) ) return 0UL;
382 0 : precompile_sig_cnt += secp256r1_signature_cnt;
383 0 : signature_cost += secp256r1_signature_cnt * FD_PACK_COST_PER_SECP256R1_SIGNATURE;
384 0 : }
385 : /* BPF programs can allocate 10kB per account per instruction.
386 : The vote program (see below) and the address lookup table program
387 : (up to 56+32*256 bytes) do allocate, and several native programs
388 : can reduce the size of an account, but the only ones that can
389 : possibly allocate more than 10kB per account are below.
390 : Additionally, none of the above programs allocate at all.
391 :
392 : A normal vote does not allocate any data, but a simple vote may
393 : actually invoke one of the vote program instructions that does
394 : allocate FD_VOTE_STATE_V3_SZ or V4, which are both 3762 bytes.
395 : If it invokes the vote program but isn't a simple vote, it will
396 : be caught by the general case and overestimated at 10kB per
397 : account, which is fine.
398 :
399 : Note: This complexity here is pretty gross, but we need a better
400 : bound for allocated_data than 20MB, and this seems to be the best
401 : way. */
402 321354 : #define MAX_ALLOC (20UL*1024UL*1024UL) /* mostly to prevent attacker-induced overflow */
403 321354 : #define DEFAULT_ALLOC (10UL*1024UL) /* == MAX_PERMITTED_DATA_INCREASE */
404 321354 : else if( FD_UNLIKELY( in_tbl==system_program_row ) ) {
405 1395 : ulong discriminant = ULONG_MAX;
406 1395 : uchar const * base = payload + txn->instr[i].data_off;
407 1395 : if( FD_UNLIKELY( data_sz<12UL ) ) continue;
408 240 : discriminant = FD_LOAD( uint, base );
409 240 : base += sizeof(uint); data_sz -= sizeof(uint);
410 240 : ulong seed_len;
411 240 : switch( discriminant ) {
412 60 : case 0UL: /* FD_SYSTEM_PROGRAM_INSTR_CREATE_ACCOUNT */
413 60 : if( FD_UNLIKELY( data_sz<8UL+8UL ) ) break;
414 : /* Note: here (and below), Agave sets alloc to 0 if any of
415 : these instructions request more than 10 MB. We don't
416 : bother with that. If a transaction has an instruction that
417 : requests more than 10 MB and pays a sufficient fee for it,
418 : and then fails, that's not a big problem. We're always
419 : computing a conservative estimate. */
420 60 : allocated_data += fd_ulong_min( MAX_ALLOC, FD_LOAD( ulong, base+8UL ) );
421 60 : break;
422 60 : case 3UL: /* FD_SYSTEM_PROGRAM_INSTR_CREATE_ACCOUNT_WITH_SEED */
423 60 : if( FD_UNLIKELY( data_sz<32UL+8UL ) ) break;
424 60 : seed_len = FD_LOAD( ulong, base+32UL );
425 60 : base += 32UL+8UL; data_sz -= 32UL+8UL;
426 60 : if( FD_UNLIKELY( data_sz<seed_len ) ) break;
427 60 : base += seed_len; data_sz -= seed_len;
428 60 : if( FD_UNLIKELY( data_sz<(8UL+8UL) ) ) break;
429 60 : allocated_data += fd_ulong_min( MAX_ALLOC, FD_LOAD( ulong, base+8UL ) );
430 60 : break;
431 60 : case 8UL: /* FD_SYSTEM_PROGRAM_INSTR_ALLOCATE */
432 60 : if( FD_UNLIKELY( data_sz<8UL ) ) break;
433 60 : allocated_data += fd_ulong_min( MAX_ALLOC, FD_LOAD( ulong, base ) );
434 60 : break;
435 60 : case 9UL: /* FD_SYSTEM_PROGRAM_INSTR_ALLOCATE_WITH_SEED */
436 60 : if( FD_UNLIKELY( data_sz<32UL+8UL ) ) break;
437 60 : seed_len = FD_LOAD( ulong, base+32UL );
438 60 : base += 32UL+8UL; data_sz -= 32UL+8UL;
439 60 : if( FD_UNLIKELY( data_sz<seed_len ) ) break;
440 60 : base += seed_len; data_sz -= seed_len;
441 60 : if( FD_UNLIKELY( data_sz<8UL ) ) break;
442 60 : allocated_data += fd_ulong_min( MAX_ALLOC, FD_LOAD( ulong, base ) );
443 60 : break;
444 0 : case 13UL: /* create_account_allow_prefund */
445 : /* Agave returns 0 here until the feature gate has been
446 : activated. Rather than feature gate this, we just act
447 : conservatively. */
448 0 : if( FD_UNLIKELY( data_sz<8UL+8UL ) ) break;
449 0 : allocated_data += fd_ulong_min( MAX_ALLOC, FD_LOAD( ulong, base+8UL ) );
450 0 : break;
451 0 : default:
452 0 : break;
453 240 : }
454 : #if FD_PACK_COST_USE_TRUE_ALLOC_BOUND
455 : } else if( FD_UNLIKELY( in_tbl==upgradeable_loader_row ) ) {
456 : ulong discriminant = ULONG_MAX;
457 : uchar const * base = payload + txn->instr[i].data_off;
458 : if( FD_UNLIKELY( data_sz<8UL ) ) continue;
459 : discriminant = FD_LOAD( uint, base );
460 : base += sizeof(uint); data_sz -= sizeof(uint);
461 : switch( discriminant ) {
462 : case 6UL: /* fd_bpf_upgradeable_loader_program_instruction_enum_extend_program */
463 : case 9UL: /* fd_bpf_upgradeable_loader_program_instruction_enum_extend_program_checked */
464 : allocated_data += fd_ulong_min( MAX_ALLOC, FD_LOAD( uint, base ) );
465 : break;
466 : default:
467 : allocated_data += DEFAULT_ALLOC; /* Some other instructions may alloc a tiny bit */
468 : break;
469 : }
470 : } else if( FD_UNLIKELY( in_tbl==loader_v4_row ) ) {
471 : ulong discriminant = ULONG_MAX;
472 : uchar const * base = payload + txn->instr[i].data_off;
473 : if( FD_UNLIKELY( data_sz<8UL ) ) continue;
474 : discriminant = FD_LOAD( uint, base );
475 : base += sizeof(uint); data_sz -= sizeof(uint);
476 : switch( discriminant ) {
477 : case 2UL: /* fd_loader_v4_program_instruction_enum_set_program_length */
478 : allocated_data += fd_ulong_min( MAX_ALLOC, FD_LOAD( uint, base ) );
479 : break;
480 : default:
481 : break;
482 : }
483 : } else {
484 : /* This could be the writable accounts only, but this bound is
485 : much cheaper, and good enough. */
486 : allocated_data += DEFAULT_ALLOC * txn->instr[i].acct_cnt;
487 : #endif /* FD_PACK_COST_USE_TRUE_ALLOC_BOUND */
488 240 : }
489 501627 : }
490 :
491 : /* Firedancer's block packer limits the total amount of precompile
492 : signatures in a transaction to 16. Anything beyond this is likely
493 : spam. */
494 67062 : if( FD_UNLIKELY( precompile_sig_cnt>16UL ) ) return 0UL;
495 :
496 : /* E.g. a transaction can alloc a 10MB account, close it, and repeat
497 : many times. According to the spec, this would count as a 20MB
498 : allocation. See
499 : https://github.com/anza-xyz/agave/blob/2a61a3ecd417b0515c0b2f322d0128394f20626b/cost-model/src/cost_model.rs#L317-L318
500 : */
501 67062 : allocated_data = fd_ulong_min( allocated_data, 20UL*1024UL*1024UL ); /* MAX_PERMITTED_ACCOUNTS_DATA_ALLOCATIONS_PER_TRANSACTION */
502 67062 : #undef MAX_ALLOC
503 67062 : #undef DEFAULT_ALLOC
504 :
505 67062 : ulong instr_data_cost = instr_data_sz / FD_PACK_INV_COST_PER_INSTR_DATA_BYTE; /* <= 320 */
506 :
507 67062 : ulong fee[1];
508 67062 : uint execution_cost[1];
509 67062 : ulong loaded_account_data_cost[1];
510 :
511 : /* V1 transactions use the config mask, and silently ignore compute
512 : budget program instructions. */
513 67062 : if( txn->transaction_version==FD_TXN_V1 ) {
514 0 : ulong v1_priority_fee, v1_cu_limit, v1_loaded, v1_heap;
515 0 : uint v1_config_mask = fd_uint_load_4( payload+4 );
516 0 : fd_txn_parse_v1_config( v1_config_mask, payload+txn->v1_txn_config_values_off,
517 0 : &v1_priority_fee, &v1_cu_limit, &v1_loaded, &v1_heap );
518 0 : *fee = v1_priority_fee;
519 0 : *execution_cost = (uint)fd_ulong_min( v1_cu_limit, FD_COMPUTE_BUDGET_MAX_CU_LIMIT );
520 0 : v1_loaded = fd_ulong_min( v1_loaded, FD_COMPUTE_BUDGET_MAX_LOADED_DATA_SZ );
521 :
522 : /* V1 transactions which request a loaded accounts data size limit
523 : of 0, or do not explicitly set this limit, are not thrown out.
524 :
525 : As of define_ltds_fee_only_semantics, which is activated on
526 : all networks, the loaded accounts data size for fee-only
527 : transactions that fail due to exceeding the requested loaded
528 : accounts data size limit is the requested loaded accounts data
529 : size limit.
530 : This means that advance nonce transactions that request a loaded
531 : accounts data size limit of 0 are still packed and the nonce
532 : still advances.
533 : https://github.com/anza-xyz/agave/blob/v4.2.0-beta.1/core/src/banking_stage/qos_service.rs#L68-L72
534 :
535 : V1 transactions which request a loaded account data
536 : size limit which is not a multiple of 32 KiB have the loaded
537 : account data cost rounded up to the nearest 32 KiB. This matches
538 : Agave's cost tracker behaviour.
539 : https://github.com/anza-xyz/agave/blob/v4.2.0-beta.1/cost-model/src/cost_model.rs#L188-L203 */
540 0 : *loaded_account_data_cost = FD_COMPUTE_BUDGET_HEAP_COST *
541 0 : ( ( v1_loaded + FD_COMPUTE_BUDGET_ACCOUNT_DATA_COST_PAGE_SIZE - 1UL )
542 0 : / FD_COMPUTE_BUDGET_ACCOUNT_DATA_COST_PAGE_SIZE );
543 67062 : } else {
544 67062 : fd_compute_budget_program_finalize( cbp, txn->instr_cnt, txn->instr_cnt-non_builtin_cnt, fee, execution_cost, loaded_account_data_cost );
545 67062 : }
546 :
547 : /* As an optimization, for simple votes we can override execution cost
548 : with a known tighter upper bound. */
549 67062 : if( FD_UNLIKELY( is_simple_vote ) ) *execution_cost = (uint)FD_PACK_VOTE_MAX_COMPUTE_UNITS;
550 :
551 67062 : fd_ulong_store_if( !!opt_execution_cost, opt_execution_cost, (ulong)(*execution_cost) );
552 67062 : fd_ulong_store_if( !!opt_fee, opt_fee, *fee );
553 67062 : fd_ulong_store_if( !!opt_precompile_sig_cnt, opt_precompile_sig_cnt, precompile_sig_cnt );
554 67062 : fd_ulong_store_if( !!opt_loaded_accounts_data_cost, opt_loaded_accounts_data_cost, *loaded_account_data_cost );
555 67062 : fd_ulong_store_if( !!opt_allocated_data, opt_allocated_data, allocated_data );
556 :
557 : #if DETAILED_LOGGING
558 : FD_BASE58_ENCODE_64_BYTES( (const uchar *)fd_txn_get_signatures(txn, payload), signature_cstr );
559 : 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]",
560 : signature_cstr, signature_cost, writable_cost, instr_data_cost, non_builtin_cnt, *loaded_account_data_cost, precompile_sig_cnt, *fee));
561 : #endif
562 :
563 : /* <= FD_PACK_MAX_COST, so no overflow concerns */
564 67062 : ulong total_cost = signature_cost + writable_cost + (ulong)(*execution_cost) + instr_data_cost + *loaded_account_data_cost;
565 :
566 67062 : return total_cost;
567 67062 : }
568 : #undef MAP_PERFECT_HASH_PP
569 : #undef PERFECT_HASH
570 :
571 : #endif /* HEADER_fd_src_disco_pack_fd_pack_cost_h */
|