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 : #include "../fd_flamenco_base.h" 5 : #include "../types/fd_types.h" 6 : #include "../vm/fd_vm_base.h" 7 : #include "fd_system_ids.h" 8 : #include "fd_executor.h" 9 : #include "fd_runtime_const.h" 10 : #include "../../disco/pack/fd_pack.h" 11 : #include "../../disco/pack/fd_pack_cost.h" 12 : 13 : /* fd_cost_tracker_t is a block-level tracker for various limits 14 : including CU consumption, writable account usage, and account data 15 : size. A cost is calculated per-transaction and is accumulated to the 16 : block. If a block's limits are exceeded, then the block is marked as 17 : dead. */ 18 : 19 : /* https://github.com/anza-xyz/agave/blob/v2.2.0/cost-model/src/cost_tracker.rs#L62-L79 */ 20 : 21 : /* https://github.com/anza-xyz/agave/blob/v2.2.0/cost-model/src/block_cost_limits.rs#L20 */ 22 33 : #define FD_WRITE_LOCK_UNITS ( 300UL ) 23 : 24 : /* https://github.com/anza-xyz/agave/blob/v2.2.0/cost-model/src/block_cost_limits.rs#L42 */ 25 : #define FD_MAX_BLOCK_ACCOUNTS_DATA_SIZE_DELTA ( 100000000UL ) 26 : 27 : /* https://github.com/anza-xyz/agave/blob/v2.2.0/cost-model/src/block_cost_limits.rs#L34 */ 28 3 : #define FD_MAX_WRITABLE_ACCOUNT_UNITS ( 12000000UL ) 29 : 30 : /* https://github.com/anza-xyz/agave/blob/v2.2.0/cost-model/src/block_cost_limits.rs#L50-L56 */ 31 0 : #define FD_MAX_BLOCK_UNITS_SIMD_0207 ( 50000000UL ) 32 : 33 : /* https://github.com/anza-xyz/agave/blob/v2.3.0/cost-model/src/block_cost_limits.rs#L50-L56 */ 34 0 : #define FD_MAX_BLOCK_UNITS_SIMD_0256 ( 60000000UL ) 35 : 36 : /* https://github.com/anza-xyz/agave/blob/v3.0.0/cost-model/src/block_cost_limits.rs#L30 */ 37 36 : #define FD_MAX_BLOCK_UNITS_SIMD_0286 ( 100000000UL ) 38 : 39 : /* https://github.com/anza-xyz/agave/blob/v2.2.0/cost-model/src/block_cost_limits.rs#L38 */ 40 3 : #define FD_MAX_VOTE_UNITS ( 36000000UL ) 41 : 42 : /* https://github.com/anza-xyz/agave/blob/v2.2.0/cost-model/src/cost_tracker.rs#L18-L33 */ 43 0 : #define FD_COST_TRACKER_SUCCESS ( 0 ) 44 0 : #define FD_COST_TRACKER_ERROR_WOULD_EXCEED_BLOCK_MAX_LIMIT ( 1 ) 45 0 : #define FD_COST_TRACKER_ERROR_WOULD_EXCEED_VOTE_MAX_LIMIT ( 2 ) 46 0 : #define FD_COST_TRACKER_ERROR_WOULD_EXCEED_ACCOUNT_MAX_LIMIT ( 3 ) 47 0 : #define FD_COST_TRACKER_ERROR_WOULD_EXCEED_ACCOUNT_DATA_BLOCK_LIMIT ( 4 ) 48 : #define FD_COST_TRACKER_ERROR_WOULD_EXCEED_ACCOUNT_DATA_TOTAL_LIMIT ( 5 ) 49 : 50 : /* A reasonably tight bound can be derived based on CUs. The most 51 : optimal use of CUs is to pack as many writable accounts as possible 52 : for as cheaply as possible. This means we should try to pack as many 53 : writable accounts as possible into each transaction. Each 54 : transaction requires at least one signature. We will assume that all 55 : of these accounts have no account data. 56 : 57 : 64 - Max number of accounts per transaction. In this case we will 58 : assume that all of these accounts are writable and have no data. 59 : 100000000 - CUs per slot 60 : 720 - Cost of a signature 61 : 300 - Cost of a writable account write lock 62 : 63 : We can have (100000000 / (720 + 64 * 300)) = 5020 transactions per 64 : slot with maximum writable account utilization. 65 : 66 : So, 5020 transactions per slot * 64 accounts per transaction = 67 : 321280 writable accounts per slot. 68 : 69 : NOTE: A slightly tighter bound can probably be derived. 70 : */ 71 : 72 18 : #define FD_RUNTIME_MAX_WRITABLE_ACCOUNTS_PER_SLOT ( \ 73 18 : FD_RUNTIME_MAX_WRITABLE_ACCOUNTS_PER_TRANSACTION * (FD_MAX_BLOCK_UNITS_SIMD_0286 / ( FD_WRITE_LOCK_UNITS * FD_RUNTIME_MAX_WRITABLE_ACCOUNTS_PER_TRANSACTION + FD_PACK_COST_PER_SIGNATURE)) ) 74 : FD_STATIC_ASSERT( FD_RUNTIME_MAX_WRITABLE_ACCOUNTS_PER_SLOT==321280UL, "Incorrect FD_RUNTIME_MAX_WRITABLE_ACCOUNTS_PER_SLOT" ); 75 : 76 : FD_PROTOTYPES_BEGIN 77 : 78 : struct fd_cost_tracker { 79 : ulong magic; 80 : 81 : /* Current accumulated values */ 82 : ulong block_cost; 83 : ulong vote_cost; 84 : ulong allocated_accounts_data_size; 85 : 86 : /* Limits */ 87 : ulong block_cost_limit; 88 : ulong vote_cost_limit; 89 : ulong account_cost_limit; 90 : 91 : ulong pool_offset_; 92 : ulong map_offset_; 93 : }; 94 : typedef struct fd_cost_tracker fd_cost_tracker_t; 95 : 96 : #define FD_COST_TRACKER_ALIGN (128UL) 97 : 98 : struct fd_account_cost { 99 : fd_pubkey_t account; 100 : ulong cost; 101 : ulong next_; 102 : }; 103 : typedef struct fd_account_cost fd_account_cost_t; 104 : 105 : #define FD_COST_TRACKER_CHAIN_CNT_EST (262144UL) 106 : #define FD_COST_TRACKER_FOOTPRINT \ 107 : /* First, layout the struct with alignment */ \ 108 : sizeof(fd_cost_tracker_t) + FD_COST_TRACKER_ALIGN + \ 109 : /* Now layout the pool's data footprint */ \ 110 : FD_COST_TRACKER_ALIGN + sizeof(fd_account_cost_t) * FD_RUNTIME_MAX_WRITABLE_ACCOUNTS_PER_SLOT + \ 111 : /* Now layout the pool's meta footprint */ \ 112 : FD_COST_TRACKER_ALIGN + 128UL /* POOL_ALIGN */ + \ 113 : /* Now layout the map. We must make assumptions about the chain */ \ 114 : /* count to be equivalent to chain_cnt_est. */ \ 115 : FD_COST_TRACKER_ALIGN + 128UL /* MAP_ALIGN */ + (FD_COST_TRACKER_CHAIN_CNT_EST * sizeof(ulong)) 116 3 : #define FD_COST_TRACKER_MAGIC (0xF17EDA2CE7C05170UL) /* FIREDANCER COST V0 */ 117 : 118 : /* https://github.com/anza-xyz/agave/blob/v2.2.0/cost-model/src/cost_model.rs#L323-L328 */ 119 : FD_FN_PURE static inline ulong 120 0 : fd_cost_tracker_calculate_loaded_accounts_data_size_cost( fd_exec_txn_ctx_t const * txn_ctx ) { 121 0 : ulong cost = fd_ulong_sat_sub( fd_ulong_sat_add( txn_ctx->loaded_accounts_data_size, 122 0 : FD_ACCOUNT_DATA_COST_PAGE_SIZE ), 123 0 : 1UL ); 124 0 : cost /= FD_ACCOUNT_DATA_COST_PAGE_SIZE; 125 0 : return fd_ulong_sat_mul( cost, FD_VM_HEAP_COST ); 126 0 : } 127 : 128 : ulong 129 : fd_cost_tracker_footprint( void ); 130 : 131 : ulong 132 : fd_cost_tracker_align( void ); 133 : 134 : void * 135 : fd_cost_tracker_new( void * mem, 136 : fd_features_t const * features, 137 : ulong slot, 138 : ulong seed ); 139 : 140 : fd_cost_tracker_t * 141 : fd_cost_tracker_join( void * mem ); 142 : 143 : /* fd_cost_tracker_calculate_cost_and_add takes a transaction, 144 : calculates the cost of the transaction in terms of various block 145 : level limits and adds it to the cost tracker. If the incremental 146 : transaction fits in the block, then the cost tracking is updated and 147 : FD_COST_TRACKER_SUCCESS is returned. If the transaction does not 148 : fit then FD_COST_TRACKER_ERROR_{*} is returned depending on what 149 : limit is violated. 150 : 151 : This function assumes that the caller is responsible for managing 152 : concurrent callers. 153 : 154 : This function represents the combination of Agave client functions: 155 : `CostModel::calculate_cost_for_executed_transaction()` and 156 : `CostTracker::try_add()`. 157 : 158 : https://github.com/anza-xyz/agave/blob/v2.2.0/cost-model/src/cost_model.rs#L69-L95 159 : https://github.com/anza-xyz/agave/blob/v2.2.0/cost-model/src/cost_tracker.rs#L163-L173 */ 160 : int 161 : fd_cost_tracker_calculate_cost_and_add( fd_cost_tracker_t * cost_tracker, 162 : fd_exec_txn_ctx_t const * txn_ctx ); 163 : 164 : FD_PROTOTYPES_END 165 : 166 : #endif /* HEADER_fd_src_flamenco_runtime_fd_cost_tracker_h */