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