Line data Source code
1 : #ifndef HEADER_fd_src_disco_pack_fd_pack_h
2 : #define HEADER_fd_src_disco_pack_fd_pack_h
3 :
4 : /* fd_pack defines methods that prioritizes Solana transactions,
5 : selecting a subset (potentially all) and ordering them to attempt to
6 : maximize the overall profitability of the validator. */
7 :
8 : #include "../../ballet/fd_ballet_base.h"
9 : #include "../../ballet/txn/fd_txn.h"
10 : #include "../shred/fd_shred_batch.h"
11 : #include "fd_est_tbl.h"
12 : #include "fd_microblock.h"
13 : #include "fd_pack_rebate_sum.h"
14 : #include "fd_pack_acct_blocklist.h"
15 : #include "../metrics/generated/fd_metrics_enums.h"
16 :
17 0 : #define FD_PACK_ALIGN (128UL)
18 :
19 0 : #define FD_PACK_MAX_EXECLE_TILES 62UL
20 :
21 0 : #define FD_PACK_FEE_PER_SIGNATURE (5000UL) /* In lamports */
22 :
23 : /* Each block is limited to 32k parity shreds. We don't want pack to
24 : produce a block with so many transactions we can't shred it, but the
25 : correspondence between transactions and parity shreds is somewhat
26 : complicated, so we need to use conservative limits. */
27 0 : #define FD_PACK_MAX_DATA_PER_BLOCK (FD_SHRED_BATCH_BLOCK_DATA_SZ_MAX)
28 :
29 : /* ---- End consensus-critical constants */
30 :
31 0 : #define FD_TXN_P_FLAGS_BUNDLE ( 2U)
32 0 : #define FD_TXN_P_FLAGS_INITIALIZER_BUNDLE ( 4U)
33 0 : #define FD_TXN_P_FLAGS_SANITIZE_SUCCESS ( 8U)
34 0 : #define FD_TXN_P_FLAGS_EXECUTE_SUCCESS (16U)
35 0 : #define FD_TXN_P_FLAGS_FEES_ONLY (32U)
36 0 : #define FD_TXN_P_FLAGS_DURABLE_NONCE (64U)
37 :
38 0 : #define FD_TXN_P_FLAGS_RESULT_MASK (0xFF000000U)
39 :
40 : /* The percentage of the transaction fees that are burned */
41 0 : #define FD_PACK_TXN_FEE_BURN_PCT 50UL
42 :
43 : /* Durable nonce transactions don't have a fixed lifetime (see
44 : expire_before, etc.) but we arbitrarily give them one to prevent them
45 : from remaining perpetually e.g. between leader rotations. The unit
46 : here is arbitrary, but 500 makes the most sense for slots. TODO:
47 : make this configurable. */
48 0 : #define FD_PACK_NONCE_SYNTHETIC_LIFETIME 500UL
49 :
50 :
51 : /* The Solana network and Firedancer implementation details impose
52 : several limits on what pack can produce. These limits are grouped in
53 : this one struct fd_pack_limits_t, which is just a convenient way to
54 : pass them around. The limits listed below are arithmetic limits.
55 : The limits imposed by practical constraints are almost certainly
56 : much, much tighter. */
57 : struct fd_pack_limits {
58 : /* max_cost_per_block, max_write_cost_per_acct are
59 : consensus-critical limits and must be agreed on cluster-wide. A
60 : block that consumes more than max_cost_per_block cost units
61 : (closely related to, but not identical to CUs) in total is invalid.
62 : Similarly, a block in where the sum of the cost of all transactions
63 : that write to a given account exceeds max_write_cost_per_acct is
64 : invalid. */
65 : ulong max_cost_per_block; /* in [0, UINT_MAX) */
66 : ulong max_write_cost_per_acct; /* in [0, max_cost_per_block] */
67 :
68 : /* max_data_bytes_per_block is derived from consensus-critical limits
69 : on the number of shreds in a block, but is not directly enforced.
70 : Separation of concerns means that it's not a good idea for pack to
71 : know exactly how the block will be shredded, but at the same time,
72 : we don't want to end up in a situation where we produced a block
73 : that had too many shreds, because the shred tile's only recourse
74 : would be to kill the block. To address this, pack limits the size
75 : of the data it puts into the block to a limit that we can prove
76 : will never cause the shred tile to produce too many shreds.
77 : At runtime this is how many entry bytes fit in max_shred_idx
78 : after FEC padding (we close a shred batch when the next
79 : microblock would not fit in two FEC sets). Empty ticks are
80 : subtracted. Large microblocks pad more, so the cap
81 : tightens.
82 :
83 : This limit includes transaction and microblock headers for
84 : non-empty microblocks that pack produces. */
85 : ulong max_data_bytes_per_block; /* in [0, ULONG_MAX - 183] */
86 :
87 : /* max_txn_per_microblock and max_microblocks_per_block are
88 : Firedancer-imposed implementation limits to bound the amount of
89 : memory consumption that pack uses. Pack will produce microblocks
90 : with no more than max_txn_per_microblock transactions.
91 : Additionally, once pack produces max_microblocks_per_block
92 : non-empty microblocks in a block, all subsequent attempts to
93 : schedule a microblock will return an empty microblock until
94 : fd_pack_end_block is called. */
95 : ulong max_txn_per_microblock; /* in [0, 16777216] */
96 : ulong max_microblocks_per_block; /* in [0, 1e12) */
97 :
98 : /* max_allocated_data_per_block is a consensus-critical constant that
99 : limits the total amount of data that can be allocated by
100 : transactions in a block. This includes new accounts and extending
101 : existing accounts. This limit is not especially well specified,
102 : and rarely comes close to being hit in production, so we use a
103 : conservative estimate when packing to ensure we never hit it. */
104 : ulong max_allocated_data_per_block; /* in [1, 100,000,000 ] */
105 : };
106 : typedef struct fd_pack_limits fd_pack_limits_t;
107 :
108 : /* fd_pack_addr_use_t: Used for three distinct purposes:
109 : - to record that an address is in use and can't be used again until
110 : certain microblocks finish execution
111 : - to keep track of the cost of all transactions that write to the
112 : specified account.
113 : - to keep track of the write cost for accounts referenced by
114 : transactions in a bundle and which transactions use which
115 : accounts.
116 : Making these separate structs might make it more clear, but then
117 : they'd have identical shape and result in several fd_map_dynamic sets
118 : of functions with identical code. It doesn't seem like the compiler
119 : is very good at merging code like that, so in order to reduce code
120 : bloat, we'll just combine them. */
121 : struct fd_pack_private_addr_use_record {
122 : fd_acct_addr_t key; /* account address */
123 : union {
124 : ulong _;
125 : ulong in_use_by; /* Bitmask indicating which banks */
126 : ulong total_cost; /* In cost units/CUs */
127 : struct { uint carried_cost; /* In cost units */
128 : ushort ref_cnt; /* In transactions */
129 : ushort last_use_in; }; /* In transactions */
130 : };
131 : };
132 : typedef struct fd_pack_private_addr_use_record fd_pack_addr_use_t;
133 :
134 : /* The point of this array it to keep a simple heap of the top 5
135 : writable accounts by cus in a given slot. The top writers heap is
136 : constructed and available at the end of a leader slot, after all CUs
137 : have been rebated. */
138 0 : #define FD_PACK_TOP_WRITERS_CNT (5UL)
139 0 : #define FD_PACK_TOP_WRITERS_SORT_BEFORE(writer1,writer2) ( (memcmp( (writer1).key.b, &(uchar[32]){ 0 }, FD_TXN_ACCT_ADDR_SZ ) && (writer1).total_cost>(writer2).total_cost) || !memcmp( (writer2).key.b, &(uchar[32]){ 0 }, FD_TXN_ACCT_ADDR_SZ ))
140 :
141 : /* fd_pack_limit_usage_t is used to store the actual per-slot resource
142 : utilization. Each utilization field has a corresponding limit field
143 : in fd_pack_limits_t which is greater than or equal to the utilization
144 : field. */
145 : struct fd_pack_limits_usage {
146 : ulong block_cost;
147 :
148 : /* Contains the top 5 writers in the block. If there are less than 5
149 : writeable accounts, unused slots will have their pubkey zeroed out. */
150 : fd_pack_addr_use_t top_writers[ FD_PACK_TOP_WRITERS_CNT ];
151 : ulong block_data_bytes;
152 : ulong microblocks;
153 : ulong alloc;
154 : };
155 :
156 : typedef struct fd_pack_limits_usage fd_pack_limits_usage_t;
157 :
158 : /* fd_pack_smallest: We want to keep track of the smallest transaction
159 : in each treap. That way, if we know the amount of space left in the
160 : block is less than the smallest transaction in the heap, we can just
161 : skip the heap. Since transactions can be deleted, etc. maintaining
162 : this precisely is hard, but we can maintain a conservative value
163 : fairly cheaply. Since the CU limit or the byte limit can be the one
164 : that matters, we keep track of the smallest by both. */
165 : struct fd_pack_smallest {
166 : ulong cus;
167 : ulong bytes;
168 : };
169 : typedef struct fd_pack_smallest fd_pack_smallest_t;
170 :
171 : /* Forward declare opaque handle */
172 : struct fd_pack_private;
173 : typedef struct fd_pack_private fd_pack_t;
174 :
175 : /* fd_pack_{align,footprint} return the required alignment and
176 : footprint in bytes for a region of memory to be used as a pack
177 : object.
178 :
179 : pack_depth sets the maximum number of pending transactions that pack
180 : stores and may eventually schedule. pack_depth must be at least 4.
181 :
182 : If bundle_meta_sz is non-zero, then the bundle-related functions on
183 : this pack object can be used, and it can schedule bundles.
184 : Additionally, if bundle_meta_sz is non-zero, then a region of size
185 : bundle_meta_sz bytes (with no additional alignment) will be reserved
186 : for each bundle.
187 :
188 : Note: if you'd like to use bundles, but don't require metadata for
189 : the bundles, simply use a small positive value (e.g. 1), always pass
190 : NULL in insert_bundle_fini, and never call fd_pack_peek_bundle_meta.
191 :
192 : bank_tile_cnt sets the number of bank tiles to which this pack object
193 : can schedule transactions. bank_tile_cnt must be in [1,
194 : FD_PACK_MAX_EXECLE_TILES].
195 :
196 : limits sets various limits for the blocks and microblocks that pack
197 : can produce. */
198 :
199 0 : FD_FN_CONST static inline ulong fd_pack_align ( void ) { return FD_PACK_ALIGN; }
200 :
201 : FD_FN_PURE ulong
202 : fd_pack_footprint( ulong pack_depth,
203 : ulong bundle_meta_sz,
204 : ulong bank_tile_cnt,
205 : fd_pack_limits_t const * limits );
206 :
207 :
208 : /* fd_pack_new formats a region of memory to be suitable for use as a
209 : pack object. mem is a non-NULL pointer to a region of memory in the
210 : local address space with the required alignment and footprint.
211 : pack_depth, bundle_meta_sz, bank_tile_cnt, and limits are as above.
212 : The limits provided in this function are the maximum possible limits
213 : this pack object can handle without being completely reformatted.
214 : rng is a local join to a random number generator used to perturb
215 : estimates. acct_blocklist is a list of accounts that cannot be read
216 : or written to. acct_blocklist is accessed acct_blocklist[i] for i in
217 : [0, acct_blocklist_cnt). acct_blocklist==NULL is okay if
218 : acct_blocklist_cnt==0. acct_blocklist_cnt must be no more than
219 : FD_PACK_ACCT_BLOCKLIST_MAX. acct_blocklist must not contain
220 : duplicates or the zero address.
221 :
222 : Returns `mem` (which will be properly formatted as a pack object) on
223 : success and NULL on failure. Logs details on failure. The caller
224 : will not be joined to the pack object when this function returns. */
225 : void *
226 : fd_pack_new( void * mem,
227 : ulong pack_depth,
228 : ulong bundle_meta_sz,
229 : ulong bank_tile_cnt,
230 : fd_pack_limits_t const * limits,
231 : fd_acct_addr_t const * acct_blocklist,
232 : ulong acct_blocklist_cnt,
233 : fd_rng_t * rng );
234 :
235 : /* fd_pack_join joins the caller to the pack object. Every successful
236 : join should have a matching leave. Returns mem. */
237 : fd_pack_t * fd_pack_join( void * mem );
238 :
239 :
240 : /* fd_pack_avail_txn_cnt returns the number of transactions that this
241 : pack object has available to schedule but that have not been
242 : scheduled yet. pack must be a valid local join. The return value
243 : will be in [0, pack_depth). */
244 :
245 : /* For performance reasons, implement this here. The offset is STATIC_ASSERTed
246 : in fd_pack.c. */
247 0 : #define FD_PACK_PENDING_TXN_CNT_OFF 72
248 : FD_FN_PURE static inline ulong
249 0 : fd_pack_avail_txn_cnt( fd_pack_t const * pack ) {
250 0 : return *((ulong const *)((uchar const *)pack + FD_PACK_PENDING_TXN_CNT_OFF));
251 0 : }
252 :
253 : /* fd_pack_current_block_cost returns the number of CUs that have been
254 : scheduled in the current block, net of any rebates. It should be
255 : between 0 and the specified value of max_cost_per_block, but it can
256 : be slightly higher due to temporary cost model nonsense. Due to
257 : rebates, this number may decrease as the block progresses. pack must
258 : be a valid local join. */
259 : FD_FN_PURE ulong fd_pack_current_block_cost( fd_pack_t const * pack );
260 :
261 : /* fd_pack_auction_begin starts an auction, which will last until
262 : fd_pack_auction_end is called.
263 :
264 : During the auction period, newly arrived transactions are not
265 : immediately scheduled, but instead held and can't be scheduled until
266 : the auction ends. Newly arrived bundles are eligible for scheduling
267 : immediately. Transactions that arrived before the auction started
268 : are eligible for scheduling throughout the auction.
269 :
270 : pack must be a valid local join. */
271 : void fd_pack_auction_begin ( fd_pack_t * pack );
272 :
273 : /* fd_pack_auction_end ends the current auction, if there is one. Any
274 : transactions that were held back during the auction now become
275 : eligible for scheduling. pack must be a valid local join. */
276 : void fd_pack_auction_end ( fd_pack_t * pack );
277 :
278 : /* fd_pack_auction_running returns 1 if an auction is currently running
279 : and 0 otherwise. pack must be a valid local join. */
280 : FD_FN_PURE int fd_pack_auction_running ( fd_pack_t const * pack );
281 :
282 : /* fd_pack_next_auction_txn_cnt returns the number of transactions
283 : currently being held back until the current auction ends. This will
284 : be 0 if no auction is running. pack must be a valid local join. */
285 : FD_FN_PURE ulong fd_pack_next_auction_txn_cnt( fd_pack_t const * pack );
286 :
287 : /* fd_pack_bank_tile_cnt: returns the value of bank_tile_cnt provided in
288 : pack when the pack object was initialized with fd_pack_new. pack
289 : must be a valid local join. The result will be in [1,
290 : FD_PACK_MAX_EXECLE_TILES]. */
291 : FD_FN_PURE ulong fd_pack_bank_tile_cnt( fd_pack_t const * pack );
292 :
293 : /* fd_pack_set_block_limits: Updates the limits provided fd_pack_new to
294 : these new values. Any future microblocks produced by this pack
295 : object will not cause a block to have more than
296 : limits->max_microblocks_per_block non-empty microblocks or more than
297 : limits->max_data_bytes_per_block data bytes (counting microblock
298 : headers as before). future microblocks will also exclude those that
299 : cause the total block cost to exceed limits->max_cost_per_block.
300 : Also, those that cause the total
301 : per-account, per block write cost to exceed
302 : limits->max_write_cost_per_acct. Note that
303 : limits->max_txn_per_microblock is ignored. Limits are inclusive, as
304 : per usual (i.e. a block may have exactly max_microblocks_per_block
305 : microblocks, but not more). pack must be a valid local join.
306 :
307 : Limits used to size pack's storage must be no larger than those
308 : provided to fd_pack_new. max_data_bytes_per_block does not size
309 : storage and may exceed its initial value.
310 :
311 : The typical place to call this is immediately after
312 : fd_pack_end_block; if this is called after some microblocks have been
313 : produced for the current block, and the current block already exceeds
314 : the limits, all the remaining microblocks in the block will be empty,
315 : but the call is valid. */
316 : void fd_pack_set_block_limits( fd_pack_t * pack, fd_pack_limits_t const * limits );
317 :
318 : /* fd_pack_get_block_limits: Copies the currently active pack limits
319 : into opt_limits, if opt_limits is not NULL. Copies the current limit
320 : utilization in opt_limits_usage, if opt_limits_usage is not NULL.
321 :
322 : Limit utilization is updated both when each transactions is scheduled
323 : and when any used resources are rebated.
324 :
325 : The opt_limits_usage->top_writers field is ignored. */
326 : void fd_pack_get_block_limits( fd_pack_t * pack, fd_pack_limits_usage_t * opt_limits_usage, fd_pack_limits_t * opt_limits );
327 :
328 : /* fd_pack_get_top_writers copies the top FD_PACK_TOP_WRITERS_CNT by
329 : writer cost writers into top_writers. The copied elements will be
330 : sorted by writer cost in descending order.
331 :
332 : This should be called at the end of the relevant leader slot right
333 : after fd_pack_end_block, otherwise the copied data will be stale. */
334 : void fd_pack_get_top_writers( fd_pack_t const * pack, fd_pack_addr_use_t top_writers[static FD_PACK_TOP_WRITERS_CNT] );
335 :
336 : /* Copies the currently smallest pending, non-conflicting, transaction
337 : into opt_pending_smallest iff it is not NULL. This value is
338 : updated any time a new transaction is inserted into the pending
339 : treap, or moved from another treap into the pending treap. */
340 : void fd_pack_get_pending_smallest( fd_pack_t * pack, fd_pack_smallest_t * opt_pending_smallest );
341 :
342 : /* Return values for fd_pack_insert_txn_fini: Non-negative values
343 : indicate the transaction was accepted and may be returned in a future
344 : microblock. Negative values indicate that the transaction was
345 : rejected and will never be returned in a future microblock.
346 : Transactions can be rejected through no fault of their own, so it
347 : doesn't necessarily imply bad behavior.
348 :
349 : The non-negative (success) codes are essentially a bitflag of two
350 : bits:
351 : * (1) unused. This bit was previously used to indicate that the
352 : transaction was a simple vote.
353 : * (2) whether this transaction replaced a previously accepted, low
354 : priority transaction, rather than being accepted in addition to
355 : all the previously accepted transactions.
356 : * (4) whether this transaction is a durable nonce transaction
357 :
358 : Since pack maintains a heap with a fixed max size of pack_depth,
359 : replacing transaction is necessary whenever the heap is full.
360 : Additionally, only one transaction with a given (nonce account, nonce
361 : authority, recent blockhash) value is allowed in pack's heap at a
362 : time, which means if there's already a lower priority transaction
363 : with the same nonce info, then this transaction will replace it.
364 : When the heap is full, and a nonce transaction is inserted, these
365 : return values don't allow you to disambiguate whether the replaced
366 : transaction had the same nonce info or not.
367 :
368 : The negative (failure) codes are a normal enumeration (not a
369 : bitflag).
370 : * PRIORITY: pack's heap was full and the transaction's priority was
371 : lower than the worst currently accepted transaction.
372 : * NONCE_PRIORITY: pack's heap had a transaction with the same
373 : durable nonce info that was higher priority.
374 : * DUPLICATE: the transaction is a duplicate of a currently accepted
375 : transaction.
376 : * UNAFFORDABLE: the fee payer could not afford the transaction fee
377 : (not yet implemented).
378 : * ADDR_LUT: the transaction tried to load an account from an address
379 : lookup table, which is not yet supported.
380 : * EXPIRED: the transaction was already expired upon insertion based
381 : on the provided value of expires_at compared to the last call to
382 : fd_pack_expire_before.
383 : * TOO_LARGE: the transaction requested too many CUs and would never
384 : be scheduled if it had been accepted.
385 : * ACCOUNT_CNT: the transaction tried to load more than 64 account
386 : addresses.
387 : * DUPLICATE_ACCT: the transaction included an account address twice
388 : in its list of account addresses to load.
389 : * ESTIMATION_FAIL: estimation of the transaction's compute cost and
390 : fee failed, typically because the transaction contained a
391 : malformed ComputeBudgetProgram instruction.
392 : * WRITES_SYSVAR: the transaction attempts to write-lock a sysvar.
393 : Write-locking a sysvar can cause heavy contention. Agave
394 : solves this by downgrading these to read locks, but we instead
395 : solve it by refusing to pack such transactions.
396 : * INVALID_NONCE: the transaction looks like a durable nonce
397 : transaction, but the nonce authority did not sign the transaction.
398 : * BUNDLE_BLACKLIST: bundles are enabled and the transaction uses an
399 : account in the bundle blacklist.
400 : * ACCT_BLOCKLIST: the transaction included an account address in the
401 : account blocklist.
402 : * NONCE_CONFLICT: bundle with two transactions that attempt to lock
403 : the same durable nonce account and authority.
404 : * INSTR_ACCT_CNT: the transaction includes an instruction that
405 : references too many accounts.
406 :
407 : NOTE: The corresponding enum in metrics.xml must be kept in sync
408 : with any changes to these return values. */
409 : #define FD_PACK_INSERT_ACCEPT_NONCE_NONVOTE_REPLACE ( 6)
410 : #define FD_PACK_INSERT_ACCEPT_NONCE_NONVOTE_ADD ( 4)
411 : #define FD_PACK_INSERT_ACCEPT_NONVOTE_REPLACE ( 2)
412 : #define FD_PACK_INSERT_ACCEPT_NONVOTE_ADD ( 0)
413 0 : #define FD_PACK_INSERT_REJECT_PRIORITY ( -1)
414 0 : #define FD_PACK_INSERT_REJECT_NONCE_PRIORITY ( -2)
415 : #define FD_PACK_INSERT_REJECT_DUPLICATE ( -3)
416 0 : #define FD_PACK_INSERT_REJECT_UNAFFORDABLE ( -4)
417 : #define FD_PACK_INSERT_REJECT_ADDR_LUT ( -5)
418 0 : #define FD_PACK_INSERT_REJECT_EXPIRED ( -6)
419 0 : #define FD_PACK_INSERT_REJECT_TOO_LARGE ( -7)
420 0 : #define FD_PACK_INSERT_REJECT_ACCOUNT_CNT ( -8)
421 0 : #define FD_PACK_INSERT_REJECT_DUPLICATE_ACCT ( -9)
422 0 : #define FD_PACK_INSERT_REJECT_ESTIMATION_FAIL (-10)
423 0 : #define FD_PACK_INSERT_REJECT_WRITES_SYSVAR (-11)
424 0 : #define FD_PACK_INSERT_REJECT_INVALID_NONCE (-12)
425 0 : #define FD_PACK_INSERT_REJECT_BUNDLE_BLACKLIST (-13)
426 0 : #define FD_PACK_INSERT_REJECT_ACCT_BLOCKLIST (-14)
427 0 : #define FD_PACK_INSERT_REJECT_NONCE_CONFLICT (-15)
428 : #define FD_PACK_INSERT_REJECT_INSTR_ACCT_CNT (-16)
429 :
430 : /* The FD_PACK_INSERT_{ACCEPT, REJECT}_* values defined above are in the
431 : range [-FD_PACK_INSERT_RETVAL_OFF,
432 : -FD_PACK_INSERT_RETVAL_OFF+FD_PACK_INSERT_RETVAL_CNT ) */
433 0 : #define FD_PACK_INSERT_RETVAL_OFF 16
434 0 : #define FD_PACK_INSERT_RETVAL_CNT 23
435 :
436 : FD_STATIC_ASSERT( FD_PACK_INSERT_REJECT_INSTR_ACCT_CNT>=-FD_PACK_INSERT_RETVAL_OFF, pack_retval );
437 : FD_STATIC_ASSERT( FD_PACK_INSERT_ACCEPT_NONCE_NONVOTE_REPLACE<FD_PACK_INSERT_RETVAL_CNT-FD_PACK_INSERT_RETVAL_OFF, pack_retval );
438 :
439 : /* fd_pack_insert_txn_{init,fini,cancel} execute the process of
440 : inserting a new transaction into the pool of available transactions
441 : that may be scheduled by the pack object.
442 :
443 : fd_pack_insert_txn_init returns a piece of memory from the txnmem
444 : region where the transaction should be stored. The lifetime of this
445 : memory is managed by fd_pack as explained below.
446 :
447 : Every call to fd_pack_insert_init must be paired with a call to
448 : exactly one of _fini or _cancel. Calling fd_pack_insert_txn_fini
449 : finalizes the transaction insert process and makes the newly-inserted
450 : transaction available for scheduling. Calling
451 : fd_pack_insert_txn_cancel aborts the transaction insertion process.
452 : The txn pointer passed to _fini or _cancel must come from the most
453 : recent call to _init.
454 :
455 : The caller of these methods should not retain any read or write
456 : interest in the transaction after _fini or _cancel have been called.
457 :
458 : expires_at (for _fini only) bounds the lifetime of the inserted
459 : transaction. No particular unit is prescribed, and it need not be
460 : higher than the previous call to txn_fini. For durable nonce
461 : transactions and non-durable nonce transactions, it is interpreted
462 : somewhat differently. For normal (non-durable nonce) transactions,
463 : if fd_pack_expire_before has been previously called with a value
464 : larger (strictly) than the provided expires_at, the transaction will
465 : be rejected with EXPIRED. See fd_pack_expire_before for more
466 : details. The Solana protocol doesn't subject durable nonce
467 : transactions to the same expiration process, however pack gives them
468 : a synthetic expires_at of FD_PACK_NONCE_SYNTHETIC_LIFETIME larger
469 : than largest value with which fd_pack_expire_before has been
470 : previously called. Instead, the expires_at value for durable nonce
471 : transactions is primarily used as an ordering key: pack only keeps
472 : the transaction with the highest expires_at value for a given (nonce,
473 : nonce authority) tuple.
474 :
475 : pack must be a local join of a pack object. From the caller's
476 : perspective, these functions cannot fail, though pack may reject a
477 : transaction for a variety of reasons. fd_pack_insert_txn_fini
478 : returns one of the FD_PACK_INSERT_ACCEPT_* or FD_PACK_INSERT_REJECT_*
479 : codes explained above.
480 : */
481 : fd_txn_e_t * fd_pack_insert_txn_init ( fd_pack_t * pack );
482 : int fd_pack_insert_txn_fini ( fd_pack_t * pack, fd_txn_e_t * txn, ulong expires_at, ulong * delete_cnt );
483 : void fd_pack_insert_txn_cancel( fd_pack_t * pack, fd_txn_e_t * txn );
484 :
485 : /* fd_pack_insert_bundle_{init,fini,cancel} are parallel to the
486 : similarly named fd_pack_insert_txn functions but can be used to
487 : insert a bundle instead of a transaction.
488 :
489 : fd_pack_insert_bundle_init populates and returns bundle.
490 : Specifically, it populates bundle[0], ... bundle[txn_cnt-1] with
491 : pointers to fd_txn_p_t structs that should receive a new transaction.
492 : The pointers themselves should not be changed which is what the const
493 : indicates, but the contents of the fd_txn_p_t structs must be changed
494 : in order for this to be useful. bundle must be a pointer to the
495 : first element of an array of at least txn_cnt pointers.
496 :
497 : The bundle consists of the transactions in the order they are
498 : provided. I.e. bundle[0] will execute first in the bundle.
499 :
500 : Like with insert_txn, every call to fd_pack_insert_bundle_init must
501 : be paired with a call to exactly one of _fini or _cancel. Calling
502 : fd_pack_insert_bundle_fini finalizes the bundle insertion process and
503 : makes the newly-inserted bundle available for scheduling. Calling
504 : fd_pack_insert_bundle_cancel aborts the transaction insertion
505 : process. There can be at most two outstanding bundles, of which one
506 : should be an initializer bundle. The bundle argument passed to _fini
507 : or _cancel must be the return value of a call to _init with the same
508 : value of txn_cnt. Additionally, it is okay to interleave calls to
509 : the insert_txn family of functions with calls to the insert_bundle
510 : family of functions.
511 :
512 : The caller of these methods should not retain any read or write
513 : interest in the fd_txn_p_t structs that the entries of bundle
514 : point to after _fini or _cancel have been called.
515 :
516 : expires_at has the same meaning as above. Although transactions in
517 : the bundle may have different recent blockhashes, all transactions in
518 : the bundle have the same expires_at value, which should be that of
519 : the oldest, since if one expires, the whole bundle becomes invalid.
520 : The alternative interpretation of expires_at for durable nonce
521 : transactions means that they should be excluded in the calculation of
522 : the oldest.
523 :
524 : Bundles containing durable nonce transactions are always treated as
525 : having a newer nonce than any non-bundle transaction with the same
526 : (nonce account, authority) pair. Within bundles, the older bundle is
527 : always treated as having a newer nonce (slightly counter-intuitively,
528 : but keeping in line with the bundle FIFO policy).
529 :
530 : If initializer_bundle is non-zero, this bundle will be inserted at
531 : the front of the bundle queue so that it is the next bundle
532 : scheduled. Otherwise, the bundle will be inserted at the back of the
533 : bundle queue, and will be scheduled in FIFO order with the rest of
534 : the bundles. If an initializer bundle is already present in pack's
535 : pending transactions, that bundle will be deleted. Additionally, if
536 : initializer_bundle is non-zero, the transactions in the bundle will
537 : not be checked against the bundle blacklist; otherwise, the check
538 : will be performed as normal. See the section below on initializer
539 : bundles for more details.
540 :
541 : Other than the blacklist check, transactions in a bundle are subject
542 : to the same checks as other transactions. If any transaction in the
543 : bundle fails validation, the whole bundle will be rejected.
544 :
545 : _fini also accepts bundle_meta, an optional opaque pointer to a
546 : region of memory of size bundle_meta_sz (as provided in pack_new).
547 : If bundle_meta is non-NULL, the contents of the memory will be copied
548 : to a metadata region associated with this bundle and can be retrieved
549 : later with fd_pack_peek_bundle_meta. The contents of bundle_meta is
550 : not retrievable if initializer_bundle is non-zero, so you may wish to
551 : just pass NULL in that case. This function does not retain any
552 : interest in the contents of bundle_meta after it returns.
553 :
554 : txn_cnt must be in [1, MAX_TXN_PER_BUNDLE]. A txn_cnt of 1 inserts a
555 : single-transaction bundle which is transaction with extremely high
556 : priority. That said, inserting transactions as bundles instead of
557 : transactions can hurt performance and throughput by introducing
558 : unnecessary stalls.
559 :
560 : fd_pack_insert_bundle_fini returns one of the FD_PACK_INSERT_ACCEPT_*
561 : or FD_PACK_INSERT_REJECT_* codes explained above. If there are
562 : multiple reasons for rejecting a bundle, the which of the reasons it
563 : returns is unspecified. delete_cnt is the number of existing
564 : transactions that were deleted as a side effect of insertion.
565 :
566 : These functions must not be called if the pack object was initialized
567 : with bundle_meta_sz==0. */
568 :
569 : fd_txn_e_t * const * fd_pack_insert_bundle_init ( fd_pack_t * pack, fd_txn_e_t * * bundle, ulong txn_cnt );
570 : int fd_pack_insert_bundle_fini ( fd_pack_t * pack, fd_txn_e_t * const * bundle, ulong txn_cnt,
571 : ulong expires_at, int initializer_bundle, void const * bundle_meta, ulong * delete_cnt );
572 : void fd_pack_insert_bundle_cancel( fd_pack_t * pack, fd_txn_e_t * const * bundle, ulong txn_cnt );
573 :
574 :
575 : /* =========== More details about initializer bundles ===============
576 : Initializer bundles are a special type of bundle with special support
577 : from the pack object to facilitate preparing on-chain state for the
578 : execution of bundles by this validator. This design is a bit
579 : complicated, but it eliminates excessive coupling between pack and
580 : block engine details.
581 :
582 : The pack object maintains a small state machine (initializer bundle
583 : abbreviated IB):
584 :
585 : [Not Initialized] ------------------------->|
586 : ^ | Schedule an
587 : | End Rebate shows | IB
588 : | block IB failed |
589 : |<----------[Failed]--------------| v
590 : | --===[Pending]
591 : |<------------------------------/ ^ |
592 : | End block /---| |
593 : | | | Rebate shows
594 : | Schedule | | IB succeeded
595 : | another IB | |
596 : | End block | V
597 : -----------------------------------===[Ready]
598 :
599 :
600 : When attempting to schedule a bundle the pack object checks the
601 : state, and employs the following rules:
602 : * [Not Initialized]: If the top bundle is an IB, schedule it,
603 : removing it like normal, then transition to [Pending]. Otherwise,
604 : do not schedule a bundle.
605 : * [Pending]: Do not schedule a bundle.
606 : * [Failed]: Do not schedule a bundle
607 : * [Ready]: Attempt to schedule the next bundle. If scheduling an IB,
608 : transition to [Pending].
609 :
610 : As described in the state machine, ending the block (via
611 : fd_pack_end_block) transitions to [Not Initialized], and calls to
612 : fd_pack_rebate_cus control the transition out of [Pending].
613 :
614 : This design supports a typical block engine system where some state
615 : may need to be initialized at the start of the slot and some state
616 : may need to change between runs of transactions (e.g. 5 transactions
617 : from block builder A followed by 5 transactions from block builder
618 : B). This can be done by inserting an initializer bundle whenever the
619 : top non-initializer bundle's metadata state (retrievable with
620 : fd_pack_peek_bundle_meta) doesn't match the current on-chain state.
621 : Since the initializer bundle will execute before the bundle that was
622 : previously the top one, by the time the non-initializer bundle
623 : executes, the on-chain state will be correctly configured. In this
624 : scheme, in the rare case that an initializer bundle was inserted but
625 : never executed, it should be deleted at the end of the slot.
626 :
627 : If at the start of the slot, it is determined that the on-chain state
628 : is in good shape, the state machine can transition directly to
629 : [Ready] by calling fd_pack_set_initializer_bundles_ready.
630 :
631 : Initializer bundles are not exempt from expiration, but it should not
632 : be a problem if they are always inserted with the most recent
633 : blockhash and deleted at the end of the slot.
634 :
635 : Additionally, a bundle marked as an IB is exempted from the bundle
636 : account blacklist checks. For this reason, it's important that IB be
637 : generated by trusted code with minimal or sanitized
638 : attacker-controlled input. */
639 :
640 :
641 : /* fd_pack_peek_bundle_meta returns a constant pointer to the bundle
642 : metadata associated with the bundle currently in line to be scheduled
643 : next, or NULL in any of the following cases:
644 : * There are no bundles
645 : * The bundle currently in line to be scheduled next is an IB
646 : * The bundle state is currently [Pending] or [Failed].
647 :
648 : The lifetime of the returned pointer is until the next pack insert,
649 : schedule, delete, or expire call. The size of the region pointed to
650 : by the returned pointer is bundle_meta_sz. If this bundle was
651 : inserted with bundle_meta==NULL, then the contents of the region
652 : pointed to by the returned pointer are arbitrary, but it will be safe
653 : to read.
654 :
655 : Pack doesn't do anything special to ensure the returned pointer
656 : points to memory with any particular alignment. It will naturally
657 : have an alignment of at least GCD( 64, bundle_meta_sz ). */
658 : void const * fd_pack_peek_bundle_meta( fd_pack_t const * pack );
659 :
660 : /* fd_pack_set_initializer_bundles_ready sets the IB state machine state
661 : (see long initializer bundle comment above) to the [Ready] state.
662 : This function makes it easy to use bundles without initializer
663 : bundles. pack must be a valid local join. */
664 : void fd_pack_set_initializer_bundles_ready( fd_pack_t * pack );
665 :
666 : /* fd_pack_set_rotation_end_ns informs pack of the wallclock time when
667 : the current leader rotation ends, in ns. Bundles that arrive later
668 : than this are treated as pre-rotation bundles for the subsequent
669 : rotation. */
670 : void fd_pack_set_rotation_end_ns( fd_pack_t * pack, long end_ns );
671 :
672 :
673 : /* FD_PACK_SCHEDULE_{BUNDLE,TXN} form a set of bitflags used in
674 : fd_pack_schedule_next_microblock below. They control what types of
675 : scheduling are allowed. The names should be self-explanatory. */
676 0 : #define FD_PACK_SCHEDULE_BUNDLE 2
677 0 : #define FD_PACK_SCHEDULE_TXN 4
678 :
679 : /* fd_pack_schedule_next_microblock schedules pending transactions.
680 : These transaction either form a microblock, which is a set of
681 : non-conflicting transactions, or a bundle. The semantics of this
682 : function are a bit different depending on which one it picks, but
683 : there are some reasons why they both use this function.
684 :
685 : For both codepaths, pack must be a local join of a pack object.
686 : schedule_flags must be a bitwise combination of the
687 : FD_PACK_SCHEDULE_* values defined above. When the bit is set
688 : corresponding to a transaction type, this function will consider
689 : scheduling transactions of that type. Passing 0 for schedule_flags
690 : is a no-op. The full policy is as follows:
691 : 1. If the BUNDLE bit is set, attempt to schedule bundles. This is
692 : the bundle case.
693 : 2. If the TXN bit is set, and step 1 did not schedule any bundles
694 : for a reason other than account conflicts, attempt to schedule
695 : normal transactions. This is the microblock case.
696 : Note that bundles cannot be combined with normal transactions.
697 : Additionally, if the BUNDLE bit is not set, step 1 will not schedule
698 : any bundles for that reason, which is a reason other than account
699 : conflicts, so that clause will always be satisfied.
700 :
701 : Microblock case:
702 : Transactions part of the scheduled microblock are copied to out in no
703 : particular order. The cumulative cost of these transactions will not
704 : exceed total_cus, and the number of transactions will not exceed the
705 : value of max_txn_per_microblock given in fd_pack_new.
706 :
707 : Bundle case:
708 : Transactions part of the scheduled bundled are copied in execution
709 : order (i.e. out[0] must be executed first). The number of
710 : transactions will not exceed FD_PACK_MAX_TXN_PER_BUNDLE.
711 : max_txn_per_microblock and total_cus are ignored, though the
712 : block-level limits are respected.
713 :
714 : Both cases:
715 : The non_execution_cus and requested_exec_plus_acct_data_cus fields of
716 : each transaction will be populated with the non execution CUs and
717 : requested execution CUs (including cus derived from the requested
718 : loaded accounts data size), respectively. The sum of these two
719 : values is the total cost of the transaction, i.e. what is used for
720 : all limits, including the total_cus value. The bundle and
721 : initializer bundle bits of the flags field will be populated.
722 : Inspecting these flags is the proper way to tell which codepath
723 : executed.
724 :
725 : Returns the number of transactions in the scheduled microblock or
726 : bundle. The return value may be 0 if there are no eligible
727 : transactions at the moment. */
728 :
729 : ulong
730 : fd_pack_schedule_next_microblock( fd_pack_t * pack,
731 : ulong total_cus,
732 : ulong bank_tile,
733 : int schedule_flags,
734 : fd_txn_e_t * out );
735 :
736 :
737 : /* fd_pack_rebate_cus adjusts the compute unit accounting for the
738 : specified transactions to take into account the actual consumed CUs
739 : after execution. When a transaction is scheduled by
740 : schedule_next_microblock, pack assumes that it uses all the CUs it
741 : requests for the purposes of several CU limits. If it doesn't use
742 : all the requested CUs, this function "rebates" them to pack so that
743 : they can be consumed by a different transaction in the block.
744 :
745 : pack must be a valid local join of a pack object. rebate must point
746 : to a valid rebate report produced by fd_pack_rebate_sum_t.
747 :
748 : IMPORTANT: CU limits are reset at the end of each block, so this
749 : should not be called for transactions from a prior block.
750 : Specifically, there must not be a call to fd_pack_end_block between
751 : the call to schedule_next_microblock this is paired with and the call
752 : to rebate_cus.
753 :
754 : This function operates independently of microblock_complete. In
755 : general, you probably need to call both. microblock_complete must be
756 : called before scheduling another microblock to that bank tile, while
757 : rebate_cus is optional and has much more relaxed ordering
758 : constraints. The restriction about intervening calls to end_block
759 : and that this must come after schedule_next_microblock are the only
760 : ordering constraints. */
761 : void fd_pack_rebate_cus( fd_pack_t * pack, fd_pack_rebate_t const * rebate );
762 :
763 : /* fd_pack_microblock_complete signals that the bank_tile with index
764 : bank_tile has completed its previously scheduled microblock. This
765 : permits the scheduling of transactions that conflict with the
766 : previously scheduled microblock. It is safe to call this multiple
767 : times after a microblock or even if bank_tile does not have a
768 : previously scheduled; in this case, the function will return 0 and
769 : act as a no-op. Returns 1 if the bank_tile had an outstanding,
770 : previously scheduled microblock to mark as completed. */
771 : int fd_pack_microblock_complete( fd_pack_t * pack, ulong bank_tile );
772 :
773 : /* fd_pack_expire_before deletes all available transactions with
774 : expires_at values strictly less than expire_before. For durable
775 : nonce transactions, this considers the synthetic expiration value as
776 : explained in insert_txn_fini. pack must be a local join of a pack
777 : object. Returns the number of transactions deleted. Subsequent
778 : calls to fd_pack_expire_before with the same or a smaller value are
779 : no-ops. */
780 : ulong fd_pack_expire_before( fd_pack_t * pack, ulong expire_before );
781 :
782 : /* fd_pack_delete_txn removes a transaction (identified by its first
783 : signature) from the pool of available transactions. Returns a
784 : nonzero count of the number of transactions deleted, if the
785 : transaction was found (and then removed) and 0 if not. The count
786 : might be >1 if a bundle was caused to be deleted. */
787 : ulong fd_pack_delete_transaction( fd_pack_t * pack, fd_ed25519_sig_t const * sig0 );
788 :
789 : /* fd_pack_end_block resets some state to prepare for the next block.
790 : Specifically, the per-block limits are cleared and transactions in
791 : the microblocks scheduled after the call to this function are allowed
792 : to conflict with transactions in microblocks scheduled before the
793 : call to this function, even within gap microblocks. */
794 : void fd_pack_end_block( fd_pack_t * pack );
795 :
796 :
797 : /* fd_pack_clear_all resets the state associated with this pack object.
798 : All pending transactions are removed from the pool of available
799 : transactions and all limits are reset. */
800 : void fd_pack_clear_all( fd_pack_t * pack );
801 :
802 :
803 : /* fd_pack_metrics_write writes period metric values to the metrics
804 : system. pack must be a valid local join. */
805 : void
806 : fd_pack_metrics_write( fd_pack_t const * pack );
807 :
808 : /* fd_pack_get_sched_metrics: copies the current
809 : FD_METRICS_ENUM_PACK_TXN_SCHEDULE_RESULT_CNT counters to metrics */
810 : void
811 : fd_pack_get_sched_metrics( fd_pack_t const * pack, ulong * metrics );
812 :
813 : /* fd_pack_leave leaves a local join of a pack object. Returns pack. */
814 : void * fd_pack_leave( fd_pack_t * pack );
815 : /* fd_pack_delete unformats a memory region used to store a pack object
816 : and returns ownership of the memory to the caller. Returns mem. */
817 : void * fd_pack_delete( void * mem );
818 :
819 : /* fd_pack_verify (for debugging use primarily) checks to ensure several
820 : invariants are satisfied. scratch must point to the first byte of a
821 : piece of memory meeting the same alignment and footprint constraints
822 : as pack. Returns 0 on success and a negative value on failure
823 : (logging a warning with details). */
824 : int fd_pack_verify( fd_pack_t * pack, void * scratch );
825 :
826 : FD_PROTOTYPES_END
827 :
828 : #endif /* HEADER_fd_src_disco_pack_fd_pack_h */
|