LCOV - code coverage report
Current view: top level - flamenco/runtime - fd_cost_tracker.h (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 10 36 27.8 %
Date: 2026-05-26 08:02:49 Functions: 0 124 0.0 %

          Line data    Source code
       1             : #ifndef HEADER_fd_src_flamenco_runtime_fd_cost_tracker_h
       2             : #define HEADER_fd_src_flamenco_runtime_fd_cost_tracker_h
       3             : 
       4             : /* fd_cost_tracker_t is a block-level tracker for various limits
       5             :    including CU consumption, writable account usage, and account data
       6             :    size.  A cost is calculated per-transaction and is accumulated to the
       7             :    block.  If a block's limits are exceeded, then the block is marked as
       8             :    dead. */
       9             : 
      10             : #include "fd_executor.h"
      11             : #include "fd_runtime_err.h"
      12             : #include "../../disco/pack/fd_pack.h" /* TODO: Layering violation */
      13             : #include "../../disco/pack/fd_pack_cost.h"
      14             : 
      15             : /* https://github.com/anza-xyz/agave/blob/v2.2.0/cost-model/src/cost_tracker.rs#L62-L79 */
      16             : 
      17         198 : #define FD_WRITE_LOCK_UNITS                   (      300U) /* https://github.com/anza-xyz/agave/blob/v2.2.0/cost-model/src/block_cost_limits.rs#L20 */
      18             : #define FD_MAX_BLOCK_ACCOUNTS_DATA_SIZE_DELTA (100000000U) /* https://github.com/anza-xyz/agave/blob/v2.2.0/cost-model/src/block_cost_limits.rs#L42 */
      19        3528 : #define FD_MAX_WRITABLE_ACCOUNT_UNITS         ( 12000000U) /* https://github.com/anza-xyz/agave/blob/v2.2.0/cost-model/src/block_cost_limits.rs#L34 */
      20        3516 : #define FD_MAX_BLOCK_UNITS_SIMD_0207          ( 50000000U) /* https://github.com/anza-xyz/agave/blob/v2.2.0/cost-model/src/block_cost_limits.rs#L50-L56 */
      21           0 : #define FD_MAX_BLOCK_UNITS_SIMD_0256          ( 60000000U) /* https://github.com/anza-xyz/agave/blob/v2.3.0/cost-model/src/block_cost_limits.rs#L50-L56 */
      22          12 : #define FD_MAX_BLOCK_UNITS_SIMD_0286          (100000000U) /* https://github.com/anza-xyz/agave/blob/v3.0.0/cost-model/src/block_cost_limits.rs#L30 */
      23        3528 : #define FD_MAX_VOTE_UNITS                     ( 36000000U) /* https://github.com/anza-xyz/agave/blob/v2.2.0/cost-model/src/block_cost_limits.rs#L38 */
      24           0 : #define FD_SIMPLE_VOTE_USAGE_COST             (     3428U) /* https://github.com/anza-xyz/agave/blob/v3.1.8/cost-model/src/transaction_cost.rs#L21 */
      25             : 
      26             : /* https://github.com/anza-xyz/agave/blob/v2.2.0/cost-model/src/cost_tracker.rs#L18-L33 */
      27         114 : #define FD_COST_TRACKER_SUCCESS                                     (0)
      28           0 : #define FD_COST_TRACKER_ERROR_WOULD_EXCEED_BLOCK_MAX_LIMIT          (1)
      29           0 : #define FD_COST_TRACKER_ERROR_WOULD_EXCEED_VOTE_MAX_LIMIT           (2)
      30           0 : #define FD_COST_TRACKER_ERROR_WOULD_EXCEED_ACCOUNT_MAX_LIMIT        (3)
      31           0 : #define FD_COST_TRACKER_ERROR_WOULD_EXCEED_ACCOUNT_DATA_BLOCK_LIMIT (4)
      32           0 : #define FD_COST_TRACKER_ERROR_WOULD_EXCEED_ACCOUNT_DATA_TOTAL_LIMIT (5)
      33             : 
      34             : /* A reasonably tight bound of the number of writable accounts per slot
      35             :    can be derived from worst-case CU limits.  For the cost tracker,
      36             :    there are two main codepaths:
      37             :    - Simple vote transactions: legacy transactions that invoke the vote
      38             :      program.  These can have unused writable accounts.
      39             :    - Everything else: these transactions are charged based on CUs per
      40             :      signature and write lock.
      41             : 
      42             :    For the simple vote transactions, the worst-case transaction has 35
      43             :    writable accounts.  These transactions can not use ALTs and are
      44             :    subject to fitting within the transaction size limit (1232B).
      45             : 
      46             :    36000000 vote CUs per slot
      47             :    3428 CUs per simple vote transaction
      48             :    35 writable accounts per simple vote transaction
      49             :    Therefore, the number of simple vote transactions per slot is:
      50             :    (36000000 / 3428) = 10501 vote txns per slot
      51             :    Therefore, the number of writable accounts per slot is:
      52             :    (10501 * 35) = 367535 writable accounts per slot
      53             : 
      54             :    Now consider the general case.  This means we should try to pack as
      55             :    many writable accounts as possible into each transaction.  Each
      56             :    transaction requires at least one signature.  We will assume that all
      57             :    of these accounts have no account data.
      58             : 
      59             :    64 - Max number of accounts per transaction.  In this case we will
      60             :    assume that all of these accounts are writable and have no data.
      61             :    100000000 - CUs per slot
      62             :    720 - Cost of a signature
      63             :    300 - Cost of a writable account write lock
      64             : 
      65             :    We can have (100000000 / (720 + 64 * 300)) = 5020 transactions per
      66             :    slot with maximum writable account utilization.
      67             : 
      68             :    So, 5020 transactions per slot * 64 accounts per transaction =
      69             :    321280 writable accounts per slot.
      70             : 
      71             :    We should use the bound derived from simple vote transactions.
      72             : 
      73             :    TODO: After remove_simple_vote_from_cost_model is activated, the
      74             :    smaller bound can be used. */
      75             : 
      76             : #define FD_RUNTIME_MAX_VOTE_TXNS_IN_SLOT               (FD_MAX_VOTE_UNITS / FD_SIMPLE_VOTE_USAGE_COST)
      77             : #define FD_RUNTIME_MAX_WRITABLE_ACCOUNTS_PER_VOTE_TXNS (35UL) /* see FD_TXN_ACCT_ADDR_MAX */
      78             : #define FD_RUNTIME_MAX_WRITABLE_ACCOUNTS_PER_SLOT      (FD_RUNTIME_MAX_VOTE_TXNS_IN_SLOT * FD_RUNTIME_MAX_WRITABLE_ACCOUNTS_PER_VOTE_TXNS)
      79             : FD_STATIC_ASSERT( FD_RUNTIME_MAX_WRITABLE_ACCOUNTS_PER_SLOT==367535UL, "Incorrect FD_RUNTIME_MAX_WRITABLE_ACCOUNTS_PER_SLOT" );
      80             : 
      81             : /* TODO: Extremely gross.  Used because these are in a pool which needs
      82             :    to be compile time sized T. */
      83             : 
      84             : /* The average mainnet block uses around 4200 distinct writable
      85             :    accounts. We size the cost tracker's account map at 8192 chains
      86             :    to give ~2x headroom over the observed average load. */
      87         801 : #define FD_COST_TRACKER_CHAIN_CNT_EST (8192UL)
      88             : #define FD_COST_TRACKER_FOOTPRINT                                                                   \
      89             :   ( FD_LAYOUT_FINI( FD_LAYOUT_APPEND( FD_LAYOUT_APPEND( FD_LAYOUT_APPEND( FD_LAYOUT_APPEND(         \
      90             :     FD_LAYOUT_INIT,                                                                                 \
      91             :       128UL /* alignof(fd_cost_tracker_t) */,  128UL /* sizeof(fd_cost_tracker_t) */          ),    \
      92             :       128UL /* alignof(cost_tracker_out_t )*/, 128UL /* sizeof(cost_tracker_out_t ) */        ),    \
      93             :       8UL   /* alignof(account_cost_map_t) */, FD_COST_TRACKER_CHAIN_CNT_EST*4UL /*sizeof(uint)*/ +24UL /* sizeof(account_cost_map_t) */ ), \
      94             :       4UL   /* alignof(account_cost_t) */,     FD_RUNTIME_MAX_WRITABLE_ACCOUNTS_PER_SLOT*40UL /*sizeof(account_cost_t)*/ ), \
      95             :       128UL ) )                                               \
      96             : 
      97         792 : #define FD_COST_TRACKER_MAGIC (0xF17EDA2CE7C05170UL) /* FIREDANCER COST V0 */
      98             : 
      99        2424 : #define FD_COST_TRACKER_ALIGN (128UL)
     100             : 
     101             : struct __attribute__((aligned(FD_COST_TRACKER_ALIGN))) fd_cost_tracker {
     102             :   ulong block_cost;
     103             :   ulong vote_cost;
     104             :   ulong allocated_accounts_data_size;
     105             : 
     106             :   ulong block_cost_limit;
     107             :   ulong vote_cost_limit;
     108             :   ulong account_cost_limit;
     109             : 
     110             :   int larger_max_cost_per_block;
     111             :   int remove_simple_vote_from_cost_model;
     112             : };
     113             : 
     114             : typedef struct fd_cost_tracker fd_cost_tracker_t;
     115             : 
     116             : /* https://github.com/anza-xyz/agave/blob/v2.2.0/cost-model/src/transaction_cost.rs#L153-L161 */
     117             : 
     118             : struct fd_usage_cost_details {
     119             :   uint  signature_cost;
     120             :   uint  write_lock_cost;
     121             :   uint  data_bytes_cost;
     122             :   uint  programs_execution_cost;
     123             :   uint  loaded_accounts_data_size_cost;
     124             :   ulong allocated_accounts_data_size;
     125             : };
     126             : typedef struct fd_usage_cost_details fd_usage_cost_details_t;
     127             : 
     128             : /* https://github.com/anza-xyz/agave/blob/v2.2.0/cost-model/src/transaction_cost.rs#L20-L23 */
     129             : 
     130             : struct fd_transaction_cost {
     131             :   uint type; /* FD_TXN_COST_TYPE_* */
     132             :   union {
     133             :     fd_usage_cost_details_t transaction;
     134             :   };
     135             : };
     136             : 
     137             : typedef struct fd_transaction_cost fd_transaction_cost_t;
     138             : 
     139           0 : #define FD_TXN_COST_TYPE_SIMPLE_VOTE 0U
     140         426 : #define FD_TXN_COST_TYPE_TRANSACTION 1U
     141             : 
     142             : FD_PROTOTYPES_BEGIN
     143             : 
     144             : static inline int
     145           0 : fd_cost_tracker_err_to_runtime_err( int err ) {
     146           0 :   switch( err ) {
     147           0 :     case FD_COST_TRACKER_SUCCESS:
     148           0 :       return FD_RUNTIME_EXECUTE_SUCCESS;
     149           0 :     case FD_COST_TRACKER_ERROR_WOULD_EXCEED_BLOCK_MAX_LIMIT:
     150           0 :       return FD_RUNTIME_TXN_ERR_WOULD_EXCEED_MAX_BLOCK_COST_LIMIT;
     151           0 :     case FD_COST_TRACKER_ERROR_WOULD_EXCEED_VOTE_MAX_LIMIT:
     152           0 :       return FD_RUNTIME_TXN_ERR_WOULD_EXCEED_MAX_VOTE_COST_LIMIT;
     153           0 :     case FD_COST_TRACKER_ERROR_WOULD_EXCEED_ACCOUNT_MAX_LIMIT:
     154           0 :       return FD_RUNTIME_TXN_ERR_WOULD_EXCEED_MAX_ACCOUNT_COST_LIMIT;
     155           0 :     case FD_COST_TRACKER_ERROR_WOULD_EXCEED_ACCOUNT_DATA_BLOCK_LIMIT:
     156           0 :       return FD_RUNTIME_TXN_ERR_WOULD_EXCEED_ACCOUNT_DATA_BLOCK_LIMIT;
     157           0 :     case FD_COST_TRACKER_ERROR_WOULD_EXCEED_ACCOUNT_DATA_TOTAL_LIMIT:
     158           0 :       return FD_RUNTIME_TXN_ERR_WOULD_EXCEED_ACCOUNT_DATA_TOTAL_LIMIT;
     159           0 :     default:
     160           0 :       FD_LOG_CRIT(( "unexpected cost tracker error %d", err ));
     161           0 :   }
     162           0 : }
     163             : 
     164             : FD_FN_CONST ulong
     165             : fd_cost_tracker_align( void );
     166             : 
     167             : FD_FN_CONST ulong
     168             : fd_cost_tracker_footprint( void );
     169             : 
     170             : void *
     171             : fd_cost_tracker_new( void * shmem,
     172             :                      int    larger_max_cost_per_block,
     173             :                      ulong  seed );
     174             : 
     175             : fd_cost_tracker_t *
     176             : fd_cost_tracker_join( void * shct );
     177             : 
     178             : void
     179             : fd_cost_tracker_init( fd_cost_tracker_t *   cost_tracker,
     180             :                       fd_features_t const * features,
     181             :                       ulong                 slot );
     182             : 
     183             : /* https://github.com/anza-xyz/agave/blob/v2.2.0/cost-model/src/cost_model.rs#L323-L328 */
     184             : FD_FN_PURE uint
     185             : fd_cost_tracker_calculate_loaded_accounts_data_size_cost( fd_txn_out_t const * txn_out );
     186             : 
     187             : /* fd_cost_tracker_calculate_cost_and_add takes a transaction,
     188             :    calculates the cost of the transaction in terms of various block
     189             :    level limits and adds it to the cost tracker.  If the incremental
     190             :    transaction fits in the block, then the cost tracking is updated and
     191             :    FD_COST_TRACKER_SUCCESS is returned.  If the transaction does not
     192             :    fit then FD_COST_TRACKER_ERROR_{*} is returned depending on what
     193             :    limit is violated.
     194             : 
     195             :    This function assumes that the caller is responsible for managing
     196             :    concurrent callers.
     197             : 
     198             :    This function represents the Agave client function:
     199             :    `CostModel::calculate_cost_for_executed_transaction()`
     200             : 
     201             :     https://github.com/anza-xyz/agave/blob/v2.2.0/cost-model/src/cost_model.rs#L69-L95
     202             : */
     203             : 
     204             : void
     205             : fd_cost_tracker_calculate_cost( fd_bank_t *         bank,
     206             :                                 fd_txn_in_t const * txn_in,
     207             :                                 fd_txn_out_t *      txn_out );
     208             : 
     209             : /* fd_cost_tracker_try_add_cost takes the cost as calculated by
     210             :    fd_cost_tracker_calculate_cost and tries to accumulate it into the
     211             :    cost tracker.  If the cost fits, the cost is added and
     212             :    FD_COST_TRACKER_SUCCESS is returned.  If the cost does not fit, the
     213             :    cost will not be updated and FD_COST_TRACKER_ERROR_* is returned
     214             :    depending on what limit is violated.
     215             : 
     216             :    This function is the Agave client function: 'CostTracker::try_add()'
     217             :    https://github.com/anza-xyz/agave/blob/v2.2.0/cost-model/src/cost_tracker.rs#L163-L173 */
     218             : 
     219             : int
     220             : fd_cost_tracker_try_add_cost( fd_cost_tracker_t * cost_tracker,
     221             :                               fd_txn_out_t *      txn_out );
     222             : 
     223             : FD_PROTOTYPES_END
     224             : 
     225             : #endif /* HEADER_fd_src_flamenco_runtime_fd_cost_tracker_h */

Generated by: LCOV version 1.14