LCOV - code coverage report
Current view: top level - ballet/pack - fd_pack.h (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 23 32 71.9 %
Date: 2024-11-13 11:58:15 Functions: 1 60 1.7 %

          Line data    Source code
       1             : #ifndef HEADER_fd_src_ballet_pack_fd_pack_h
       2             : #define HEADER_fd_src_ballet_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 "../fd_ballet_base.h"
       9             : #include "../txn/fd_txn.h"
      10             : #include "fd_est_tbl.h"
      11             : #include "fd_microblock.h"
      12             : 
      13           0 : #define FD_PACK_ALIGN     (128UL)
      14             : 
      15       17577 : #define FD_PACK_MAX_BANK_TILES 62UL
      16             : 
      17             : /* NOTE: THE FOLLOWING CONSTANTS ARE CONSENSUS CRITICAL AND CANNOT BE
      18             :    CHANGED WITHOUT COORDINATING WITH ANZA. */
      19       15240 : #define FD_PACK_MAX_COST_PER_BLOCK      (48000000UL)
      20         318 : #define FD_PACK_MAX_VOTE_COST_PER_BLOCK (36000000UL)
      21         309 : #define FD_PACK_MAX_WRITE_COST_PER_ACCT (12000000UL)
      22    27145329 : #define FD_PACK_FEE_PER_SIGNATURE           (5000UL) /* In lamports */
      23             : 
      24             : /* Each block is limited to 32k parity shreds.  We don't want pack to
      25             :    produce a block with so many transactions we can't shred it, but the
      26             :    correspondence between transactions and parity shreds is somewhat
      27             :    complicated, so we need to use conservative limits.
      28             : 
      29             :    Except for the final batch in the block, the current version of the
      30             :    shred tile shreds microblock batches of size (25431, 63671] bytes,
      31             :    including the microblock headers, but excluding the microblock count.
      32             :    The worst case size by bytes/parity shred is a 25871 byte microblock
      33             :    batch, which produces 31 parity shreds.  The final microblock batch,
      34             :    however, may be as bad as 48 bytes triggering the creation of 17
      35             :    parity shreds.  This gives us a limit of floor((32k - 17)/31)*25871 +
      36             :    48 = 27,319,824 bytes.
      37             : 
      38             :    To get this right, the pack tile needs to add in the 48-byte
      39             :    microblock headers for each microblock, and we also need to subtract
      40             :    out the tick bytes, which aren't known until PoH initialization is
      41             :    complete.
      42             : 
      43             :    Note that the number of parity shreds in each FEC set is always at
      44             :    least as many as the number of data shreds, so we don't need to
      45             :    consider the data shreds limit. */
      46           3 : #define FD_PACK_MAX_DATA_PER_BLOCK (((32UL*1024UL-17UL)/31UL)*25871UL + 48UL)
      47             : 
      48             : /* Optionally allow up to 1M shreds per block for benchmarking. */
      49           0 : #define LARGER_MAX_DATA_PER_BLOCK  (((32UL*32UL*1024UL-17UL)/31UL)*25871UL + 48UL)
      50             : 
      51             : /* ---- End consensus-critical constants */
      52             : 
      53    27145338 : #define FD_TXN_P_FLAGS_IS_SIMPLE_VOTE   (1U)
      54       22740 : #define FD_TXN_P_FLAGS_SANITIZE_SUCCESS (2U)
      55       16737 : #define FD_TXN_P_FLAGS_EXECUTE_SUCCESS  (4U)
      56             : 
      57             : 
      58             : /* The Solana network and Firedancer implementation details impose
      59             :    several limits on what pack can produce.  These limits are grouped in
      60             :    this one struct fd_pack_limits_t, which is just a convenient way to
      61             :    pass them around.  The limits listed below are arithmetic limits.
      62             :    The limits imposed by practical constraints are almost certainly
      63             :    much, much tighter. */
      64             : struct fd_pack_limits {
      65             :   /* max_{cost, vote_cost}_per_block, max_write_cost_per_acct are
      66             :      consensus-critical limits and must be agreed on cluster-wide.  A
      67             :      block that consumes more than max_cost_per_block cost units
      68             :      (closely related to, but not identical to CUs) in total is invalid.
      69             :      Similarly, a block where the sum of the cost of all vote
      70             :      transactions exceeds max_vote_cost_per_block cost units is invalid.
      71             :      Similarly, a block in where the sum of the cost of all transactions
      72             :      that write to a given account exceeds max_write_cost_per_acct is
      73             :      invalid. */
      74             :   ulong max_cost_per_block;          /* in [0, ULONG_MAX) */
      75             :   ulong max_vote_cost_per_block;     /* in [0, max_cost_per_block] */
      76             :   ulong max_write_cost_per_acct;     /* in [0, max_cost_per_block] */
      77             : 
      78             :   /* max_data_bytes_per_block is derived from consensus-critical limits
      79             :      on the number of shreds in a block, but is not directly enforced.
      80             :      Separation of concerns means that it's not a good idea for pack to
      81             :      know exactly how the block will be shredded, but at the same time,
      82             :      we don't want to end up in a situation where we produced a block
      83             :      that had too many shreds, because the shred tile's only recourse
      84             :      would be to kill the block.  To address this, pack limits the size
      85             :      of the data it puts into the block to a limit that we can prove
      86             :      will never cause the shred tile to produce too many shreds.
      87             : 
      88             :      This limit includes transaction and microblock headers for
      89             :      non-empty microblocks that pack produces. */
      90             :   ulong max_data_bytes_per_block;    /* in [0, ULONG_MAX - 183] */
      91             : 
      92             :   /* max_txn_per_microblock and max_microblocks_per_block are
      93             :      Firedancer-imposed implementation limits to bound the amount of
      94             :      memory consumption that pack uses.  Pack will produce microblocks
      95             :      with no more than max_txn_per_microblock transactions.
      96             :      Additionally, once pack produces max_microblocks_per_block
      97             :      non-empty microblocks in a block, all subsequent attempts to
      98             :      schedule a microblock will return an empty microblock until
      99             :      fd_pack_end_block is called. */
     100             :   ulong max_txn_per_microblock;      /* in [0, 16777216] */
     101             :   ulong max_microblocks_per_block;   /* in [0, 1e12) */
     102             : 
     103             : };
     104             : typedef struct fd_pack_limits fd_pack_limits_t;
     105             : 
     106             : 
     107             : /* Forward declare opaque handle */
     108             : struct fd_pack_private;
     109             : typedef struct fd_pack_private fd_pack_t;
     110             : 
     111             : /* fd_pack_{align,footprint} return the required alignment and
     112             :    footprint in bytes for a region of memory to be used as a pack
     113             :    object.
     114             : 
     115             :    pack_depth sets the maximum number of pending transactions that pack
     116             :    stores and may eventually schedule.  pack_depth must be at least 4.
     117             : 
     118             :    bank_tile_cnt sets the number of bank tiles to which this pack object
     119             :    can schedule transactions.  bank_tile_cnt must be in [1,
     120             :    FD_PACK_MAX_BANK_TILES].
     121             : 
     122             :    limits sets various limits for the blocks and microblocks that pack
     123             :    can produce. */
     124             : 
     125           0 : FD_FN_CONST static inline ulong fd_pack_align       ( void ) { return FD_PACK_ALIGN; }
     126             : 
     127             : FD_FN_PURE ulong
     128             : fd_pack_footprint( ulong                    pack_depth,
     129             :                    ulong                    bank_tile_cnt,
     130             :                    fd_pack_limits_t const * limits );
     131             : 
     132             : 
     133             : /* fd_pack_new formats a region of memory to be suitable for use as a
     134             :    pack object.  mem is a non-NULL pointer to a region of memory in the
     135             :    local address space with the required alignment and footprint.
     136             :    pack_depth, bank_tile_cnt, and limits are as above.  rng is a local
     137             :    join to a random number generator used to perturb estimates.
     138             : 
     139             :    Returns `mem` (which will be properly formatted as a pack object) on
     140             :    success and NULL on failure.  Logs details on failure.  The caller
     141             :    will not be joined to the pack object when this function returns. */
     142             : void * fd_pack_new( void * mem,
     143             :     ulong pack_depth, ulong bank_tile_cnt, fd_pack_limits_t const * limits,
     144             :     fd_rng_t * rng );
     145             : 
     146             : /* fd_pack_join joins the caller to the pack object.  Every successful
     147             :    join should have a matching leave.  Returns mem. */
     148             : fd_pack_t * fd_pack_join( void * mem );
     149             : 
     150             : 
     151             : /* fd_pack_avail_txn_cnt returns the number of transactions that this
     152             :    pack object has available to schedule but that have not been
     153             :    scheduled yet. pack must be a valid local join.  The return value
     154             :    will be in [0, pack_depth). */
     155             : 
     156             : /* For performance reasons, implement this here.  The offset is STATIC_ASSERTed
     157             :    in fd_pack.c. */
     158       43179 : #define FD_PACK_PENDING_TXN_CNT_OFF 64
     159             : FD_FN_PURE static inline ulong
     160       43179 : fd_pack_avail_txn_cnt( fd_pack_t const * pack ) {
     161       43179 :   return *((ulong const *)((uchar const *)pack + FD_PACK_PENDING_TXN_CNT_OFF));
     162       43179 : }
     163             : 
     164             : /* fd_pack_bank_tile_cnt: returns the value of bank_tile_cnt provided in
     165             :    pack when the pack object was initialized with fd_pack_new.  pack
     166             :    must be a valid local join.  The result will be in [1,
     167             :    FD_PACK_MAX_BANK_TILES]. */
     168             : FD_FN_PURE ulong fd_pack_bank_tile_cnt( fd_pack_t const * pack );
     169             : 
     170             : /* fd_pack_set_block_limits: Updates the limits provided fd_pack_new to
     171             :    the new values.  Any future microblocks produced by this pack object
     172             :    will not cause a block to have more than max_microblocks_per_block
     173             :    non-empty microblocks or more than max_data_bytes_per_block data
     174             :    bytes (counting microblock headers as before).  Limits are inclusive,
     175             :    as per usual (i.e. a block may have exactly
     176             :    max_microblocks_per_block microblocks, but not more).  pack must be
     177             :    a valid local join.
     178             : 
     179             :    The typical place to call this is immediately after
     180             :    fd_pack_end_block; if this is called after some microblocks have been
     181             :    produced for the current block, and the current block already exceeds
     182             :    the limits, all the remaining microblocks in the block will be empty,
     183             :    but the call is valid. */
     184             : void fd_pack_set_block_limits( fd_pack_t * pack, ulong max_microblocks_per_block, ulong max_data_bytes_per_block );
     185             : 
     186             : /* Return values for fd_pack_insert_txn_fini:  Non-negative values
     187             :    indicate the transaction was accepted and may be returned in a future
     188             :    microblock.  Negative values indicate that the transaction was
     189             :    rejected and will never be returned in a future microblock.
     190             :    Transactions can be rejected through no fault of their own, so it
     191             :    doesn't necessarily imply bad behavior.
     192             : 
     193             :    The non-negative (success) codes are essentially a bitflag of two
     194             :    bits:
     195             :     * whether the transaction met the criteria for a simple vote or not,
     196             :     * whether this transaction replaced a previously accepted, low
     197             :       priority transaction, rather than being accepted in addition to all
     198             :       the previously accepted transactions.  Since pack maintains a heap
     199             :       with a fixed max size of pack_depth, replacing transaction is
     200             :       necessary whenever the heap is full.
     201             : 
     202             :    The negative (failure) codes are a normal enumeration (not a
     203             :    bitflag).
     204             :     * PRIORITY: pack's heap was full and the transaction's priority was
     205             :       lower than the worst currently accepted transaction.
     206             :     * DUPLICATE: the transaction is a duplicate of a currently accepted
     207             :       transaction.
     208             :     * UNAFFORDABLE: the fee payer could not afford the transaction fee
     209             :       (not yet implemented).
     210             :     * ADDR_LUT: the transaction tried to load an account from an address
     211             :       lookup table, which is not yet supported.
     212             :     * EXPIRED: the transaction was already expired upon insertion based
     213             :       on the provided value of expires_at compared to the last call to
     214             :       fd_pack_expire_before.
     215             :     * TOO_LARGE: the transaction requested too many CUs and would never
     216             :       be scheduled if it had been accepted.
     217             :     * ACCOUNT_CNT: the transaction tried to load more than 64 account
     218             :       addresses.
     219             :     * DUPLICATE_ACCT: the transaction included an account address twice
     220             :       in its list of account addresses to load.
     221             :     * ESTIMATION_FAIL: estimation of the transaction's compute cost and
     222             :       fee failed, typically because the transaction contained a
     223             :       malformed ComputeBudgetProgram instruction.
     224             :     * WRITES_SYSVAR: the transaction attempts to write-lock a sysvar.
     225             :       Write-locking a sysvar can cause heavy contention.  Agave
     226             :       solves this by downgrading these to read locks, but we instead
     227             :       solve it by refusing to pack such transactions.
     228             : 
     229             :     NOTE: The corresponding enum in metrics.xml must be kept in sync
     230             :     with any changes to these return values. */
     231           0 : #define FD_PACK_INSERT_ACCEPT_VOTE_REPLACE     (  3)
     232        3123 : #define FD_PACK_INSERT_ACCEPT_NONVOTE_REPLACE  (  2)
     233       37644 : #define FD_PACK_INSERT_ACCEPT_VOTE_ADD         (  1)
     234    13040262 : #define FD_PACK_INSERT_ACCEPT_NONVOTE_ADD      (  0)
     235      491526 : #define FD_PACK_INSERT_REJECT_PRIORITY         ( -1)
     236           3 : #define FD_PACK_INSERT_REJECT_DUPLICATE        ( -2)
     237           0 : #define FD_PACK_INSERT_REJECT_UNAFFORDABLE     ( -3)
     238             : #define FD_PACK_INSERT_REJECT_ADDR_LUT         ( -4)
     239          12 : #define FD_PACK_INSERT_REJECT_EXPIRED          ( -5)
     240           0 : #define FD_PACK_INSERT_REJECT_TOO_LARGE        ( -6)
     241           3 : #define FD_PACK_INSERT_REJECT_ACCOUNT_CNT      ( -7)
     242           3 : #define FD_PACK_INSERT_REJECT_DUPLICATE_ACCT   ( -8)
     243           3 : #define FD_PACK_INSERT_REJECT_ESTIMATION_FAIL  ( -9)
     244          87 : #define FD_PACK_INSERT_REJECT_WRITES_SYSVAR    (-10)
     245           0 : #define FD_PACK_INSERT_REJECT_BUNDLE_BLACKLIST (-11)
     246             : 
     247             : /* The FD_PACK_INSERT_{ACCEPT, REJECT}_* values defined above are in the
     248             :    range [-FD_PACK_INSERT_RETVAL_OFF,
     249             :    -FD_PACK_INSERT_RETVAL_OFF+FD_PACK_INSERT_RETVAL_CNT ) */
     250           0 : #define FD_PACK_INSERT_RETVAL_OFF 11
     251           0 : #define FD_PACK_INSERT_RETVAL_CNT 15
     252             : 
     253             : FD_STATIC_ASSERT( FD_PACK_INSERT_REJECT_BUNDLE_BLACKLIST>=-FD_PACK_INSERT_RETVAL_OFF, pack_retval );
     254             : FD_STATIC_ASSERT( FD_PACK_INSERT_ACCEPT_VOTE_REPLACE<FD_PACK_INSERT_RETVAL_CNT-FD_PACK_INSERT_RETVAL_OFF, pack_retval );
     255             : 
     256             : /* fd_pack_insert_txn_{init,fini,cancel} execute the process of
     257             :    inserting a new transaction into the pool of available transactions
     258             :    that may be scheduled by the pack object.
     259             : 
     260             :    fd_pack_insert_txn_init returns a piece of memory from the txnmem
     261             :    region where the transaction should be stored.  The lifetime of this
     262             :    memory is managed by fd_pack as explained below.
     263             : 
     264             :    Every call to fd_pack_insert_init must be paired with a call to
     265             :    exactly one of _fini or _cancel.  Calling fd_pack_insert_txn_fini
     266             :    finalizes the transaction insert process and makes the newly-inserted
     267             :    transaction available for scheduling.  Calling
     268             :    fd_pack_insert_txn_cancel aborts the transaction insertion process.
     269             :    The txn pointer passed to _fini or _cancel must come from the most
     270             :    recent call to _init.
     271             : 
     272             :    The caller of these methods should not retain any read or write
     273             :    interest in the transaction after _fini or _cancel have been called.
     274             : 
     275             :    expires_at (for _fini only) bounds the lifetime of the inserted
     276             :    transaction.  No particular unit is prescribed, and it need not be
     277             :    higher than the previous call to txn_fini.  If fd_pack_expire_before
     278             :    has been previously called with a value larger (strictly) than the
     279             :    provided expires_at, the transaction will be rejected with EXPIRED.
     280             :    See fd_pack_expire_before for more details.
     281             : 
     282             :    pack must be a local join of a pack object.  From the caller's
     283             :    perspective, these functions cannot fail, though pack may reject a
     284             :    transaction for a variety of reasons.  fd_pack_insert_txn_fini
     285             :    returns one of the FD_PACK_INSERT_ACCEPT_* or FD_PACK_INSERT_REJECT_*
     286             :    codes explained above.
     287             :  */
     288             : fd_txn_e_t * fd_pack_insert_txn_init  ( fd_pack_t * pack                                     );
     289             : int          fd_pack_insert_txn_fini  ( fd_pack_t * pack, fd_txn_e_t * txn, ulong expires_at );
     290             : void         fd_pack_insert_txn_cancel( fd_pack_t * pack, fd_txn_e_t * txn                   );
     291             : 
     292             : 
     293             : /* fd_pack_schedule_next_microblock schedules transactions to form a
     294             :    microblock, which is a set of non-conflicting transactions.
     295             : 
     296             :    pack must be a local join of a pack object.  Transactions part of the
     297             :    scheduled microblock are copied to out in no particular order.  The
     298             :    cumulative cost of these transactions will not exceed total_cus, and
     299             :    the number of transactions will not exceed the value of
     300             :    max_txn_per_microblock given in fd_pack_new.
     301             : 
     302             :    The requested_cus field of each transaction will be populated with
     303             :    the requested execution CUs, which is different from the total cost
     304             :    CUs used by `total_cus`.  The flags field will be set to 0 or
     305             :    FD_TXN_P_FLAGS_IS_SIMPLE_VOTE.
     306             : 
     307             :    The block will not contain more than
     308             :    vote_fraction*max_txn_per_microblock votes, and votes in total will
     309             :    not consume more than vote_fraction*total_cus of the microblock.
     310             : 
     311             :    Returns the number of transactions in the scheduled microblock.  The
     312             :    return value may be 0 if there are no eligible transactions at the
     313             :    moment. */
     314             : 
     315             : ulong fd_pack_schedule_next_microblock( fd_pack_t * pack, ulong total_cus, float vote_fraction, ulong bank_tile, fd_txn_p_t * out );
     316             : 
     317             : 
     318             : /* fd_pack_rebate_cus adjusts the compute unit accounting for the
     319             :    specified transactions to take into account the actual consumed CUs
     320             :    after execution.  When a transaction is scheduled by
     321             :    schedule_next_microblock, pack assumes that it uses all the CUs it
     322             :    requests for the purposes of several CU limits.  If it doesn't use
     323             :    all the requested CUs, this function "rebates" them to pack so that
     324             :    they can be consumed by a different transaction in the block.
     325             : 
     326             :    pack must be a valid local join of a pack object.  txns, indexed [0,
     327             :    txn_cnt), should point to the first of txn_cnt transactions scheduled
     328             :    in a microblock by pack.  Although txns does not need to be the same
     329             :    specific pointer passed to out in a call to schedule_next_microblock,
     330             :    it should point to the same data as returned by a call to
     331             :    schedule_next_microblock, other than flags and executed_cus fields.
     332             :    Similarly, txn_cnt should be the return value of the corresponding
     333             :    prior call to schedule_next_microblock.  txn_cnt==0 is okay and a
     334             :    no-op.  txns==NULL is okay if txn_cnt==0.
     335             : 
     336             :    This function reads the requested_cus and executed_cus fields of the
     337             :    transactions in txns.  It expects that both refer to just execution
     338             :    CUs, and do not include e.g. write lock cost units.
     339             : 
     340             :    IMPORTANT: CU limits are reset at the end of each block, so this
     341             :    should not be called for transactions from a prior block.
     342             :    Specifically, there must not be a call to fd_pack_end_block between
     343             :    the call to schedule_next_microblock this is paired with and the call
     344             :    to rebate_cus.
     345             : 
     346             :    This function operates independently of microblock_complete.  In
     347             :    general, you probably need to call both.  microblock_complete must be
     348             :    called before scheduling another microblock to that bank tile, while
     349             :    rebate_cus is optional and has much more relaxed ordering
     350             :    constraints.  The restriction about intervening calls to end_block
     351             :    and that this must come after schedule_next_microblock are the only
     352             :    ordering constraints. */
     353             : void fd_pack_rebate_cus( fd_pack_t * pack, fd_txn_p_t const * txns, ulong txn_cnt );
     354             : 
     355             : /* fd_pack_microblock_complete signals that the bank_tile with index
     356             :    bank_tile has completed its previously scheduled microblock.  This
     357             :    permits the scheduling of transactions that conflict with the
     358             :    previously scheduled microblock. */
     359             : void fd_pack_microblock_complete( fd_pack_t * pack, ulong bank_tile );
     360             : 
     361             : /* fd_pack_expire_before deletes all available transactions with
     362             :    expires_at values strictly less than expire_before.  pack must be a
     363             :    local join of a pack object.  Returns the number of transactions
     364             :    deleted.  Subsequent calls to fd_pack_expire_before with the same or
     365             :    a smaller value are no-ops. */
     366             : ulong fd_pack_expire_before( fd_pack_t * pack, ulong expire_before );
     367             : 
     368             : /* fd_pack_delete_txn removes a transaction (identified by its first
     369             :    signature) from the pool of available transactions.  Returns 1 if the
     370             :    transaction was found (and then removed) and 0 if not. */
     371             : int fd_pack_delete_transaction( fd_pack_t * pack, fd_ed25519_sig_t const * sig0 );
     372             : 
     373             : /* fd_pack_end_block resets some state to prepare for the next block.
     374             :    Specifically, the per-block limits are cleared and transactions in
     375             :    the microblocks scheduled after the call to this function are allowed
     376             :    to conflict with transactions in microblocks scheduled before the
     377             :    call to this function, even within gap microblocks. */
     378             : void fd_pack_end_block( fd_pack_t * pack );
     379             : 
     380             : 
     381             : /* fd_pack_clear_all resets the state associated with this pack object.
     382             :    All pending transactions are removed from the pool of available
     383             :    transactions and all limits are reset. */
     384             : void fd_pack_clear_all( fd_pack_t * pack );
     385             : 
     386             : 
     387             : /* fd_pack_metrics_write writes period metric values to the metrics
     388             :    system.  pack must be a valid local join. */
     389             : void
     390             : fd_pack_metrics_write( fd_pack_t const * pack );
     391             : 
     392             : 
     393             : /* fd_pack_leave leaves a local join of a pack object.  Returns pack. */
     394             : void * fd_pack_leave(  fd_pack_t * pack );
     395             : /* fd_pack_delete unformats a memory region used to store a pack object
     396             :    and returns ownership of the memory to the caller.  Returns mem. */
     397             : void * fd_pack_delete( void      * mem  );
     398             : 
     399             : /* fd_pack_verify (for debugging use primarily) checks to ensure several
     400             :    invariants are satisfied.  scratch must point to the first byte of a
     401             :    piece of memory meeting the same alignment and footprint constraints
     402             :    as pack.  Returns 0 on success and a negative value on failure
     403             :    (logging a warning with details). */
     404             : int fd_pack_verify( fd_pack_t * pack, void * scratch );
     405             : 
     406             : FD_PROTOTYPES_END
     407             : #endif /* HEADER_fd_src_ballet_pack_fd_pack_h */

Generated by: LCOV version 1.14