LCOV - code coverage report
Current view: top level - disco/pack - fd_pack_cost.h (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 106 132 80.3 %
Date: 2026-05-29 08:13:28 Functions: 2 136 1.5 %

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

Generated by: LCOV version 1.14