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 516 : #define FD_WRITE_LOCK_UNITS ( 300UL) /* 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 (100000000UL) /* https://github.com/anza-xyz/agave/blob/v2.2.0/cost-model/src/block_cost_limits.rs#L42 */ 19 60 : #define FD_MAX_WRITABLE_ACCOUNT_UNITS ( 12000000UL) /* https://github.com/anza-xyz/agave/blob/v2.2.0/cost-model/src/block_cost_limits.rs#L34 */ 20 36 : #define FD_MAX_BLOCK_UNITS_SIMD_0207 ( 50000000UL) /* 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 ( 60000000UL) /* https://github.com/anza-xyz/agave/blob/v2.3.0/cost-model/src/block_cost_limits.rs#L50-L56 */ 22 462 : #define FD_MAX_BLOCK_UNITS_SIMD_0286 (100000000UL) /* https://github.com/anza-xyz/agave/blob/v3.0.0/cost-model/src/block_cost_limits.rs#L30 */ 23 60 : #define FD_MAX_VOTE_UNITS ( 36000000UL) /* 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 ( 3428UL) /* 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 36 : #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 can be derived based on CUs. The most 35 : optimal use of CUs is to pack as many writable accounts as possible 36 : for as cheaply as possible. This means we should try to pack as many 37 : writable accounts as possible into each transaction. Each 38 : transaction requires at least one signature. We will assume that all 39 : of these accounts have no account data. 40 : 41 : 64 - Max number of accounts per transaction. In this case we will 42 : assume that all of these accounts are writable and have no data. 43 : 100000000 - CUs per slot 44 : 720 - Cost of a signature 45 : 300 - Cost of a writable account write lock 46 : 47 : We can have (100000000 / (720 + 64 * 300)) = 5020 transactions per 48 : slot with maximum writable account utilization. 49 : 50 : So, 5020 transactions per slot * 64 accounts per transaction = 51 : 321280 writable accounts per slot. 52 : 53 : NOTE: A slightly tighter bound can probably be derived. */ 54 : 55 438 : #define FD_RUNTIME_MAX_WRITABLE_ACCOUNTS_PER_SLOT ( \ 56 438 : 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)) ) 57 : FD_STATIC_ASSERT( FD_RUNTIME_MAX_WRITABLE_ACCOUNTS_PER_SLOT==321280UL, "Incorrect FD_RUNTIME_MAX_WRITABLE_ACCOUNTS_PER_SLOT" ); 58 : 59 : /* TODO: Extremely gross. Used because these are in a pool which needs 60 : to be compile time sized T. */ 61 : #define FD_COST_TRACKER_CHAIN_CNT_EST (262144UL) 62 : #define FD_COST_TRACKER_FOOTPRINT \ 63 : ( FD_LAYOUT_FINI( FD_LAYOUT_APPEND( FD_LAYOUT_APPEND( FD_LAYOUT_APPEND( FD_LAYOUT_APPEND( \ 64 : FD_LAYOUT_INIT, \ 65 : 128UL /* alignof(fd_cost_tracker_t) */, 128UL /* sizeof(fd_cost_tracker_t) */ ), \ 66 : 128UL /* alignof(cost_tracker_out_t )*/, 128UL /* sizeof(cost_tracker_out_t ) */ ), \ 67 : 8UL /* alignof(account_cost_map_t) */, FD_COST_TRACKER_CHAIN_CNT_EST*8UL /*sizeof(ulong)*/ +24UL /* sizeof(account_cost_map_t) */ ), \ 68 : 8UL /* alignof(account_cost_t) */, FD_RUNTIME_MAX_WRITABLE_ACCOUNTS_PER_SLOT*48UL /*sizeof(account_cost_t)*/ ), \ 69 : 128UL ) ) \ 70 : 71 429 : #define FD_COST_TRACKER_MAGIC (0xF17EDA2CE7C05170UL) /* FIREDANCER COST V0 */ 72 : 73 1335 : #define FD_COST_TRACKER_ALIGN (128UL) 74 : 75 : struct __attribute__((aligned(FD_COST_TRACKER_ALIGN))) fd_cost_tracker { 76 : ulong block_cost; 77 : ulong vote_cost; 78 : ulong allocated_accounts_data_size; 79 : 80 : ulong block_cost_limit; 81 : ulong vote_cost_limit; 82 : ulong account_cost_limit; 83 : 84 : int larger_max_cost_per_block; 85 : int remove_simple_vote_from_cost_model; 86 : }; 87 : 88 : typedef struct fd_cost_tracker fd_cost_tracker_t; 89 : 90 : /* https://github.com/anza-xyz/agave/blob/v2.2.0/cost-model/src/transaction_cost.rs#L153-L161 */ 91 : 92 : struct fd_usage_cost_details { 93 : ulong signature_cost; 94 : ulong write_lock_cost; 95 : ulong data_bytes_cost; 96 : ulong programs_execution_cost; 97 : ulong loaded_accounts_data_size_cost; 98 : ulong allocated_accounts_data_size; 99 : }; 100 : typedef struct fd_usage_cost_details fd_usage_cost_details_t; 101 : 102 : /* https://github.com/anza-xyz/agave/blob/v2.2.0/cost-model/src/transaction_cost.rs#L20-L23 */ 103 : 104 : struct fd_transaction_cost { 105 : uint type; /* FD_TXN_COST_TYPE_* */ 106 : union { 107 : fd_usage_cost_details_t transaction; 108 : }; 109 : }; 110 : 111 : typedef struct fd_transaction_cost fd_transaction_cost_t; 112 : 113 0 : #define FD_TXN_COST_TYPE_SIMPLE_VOTE 0U 114 150 : #define FD_TXN_COST_TYPE_TRANSACTION 1U 115 : 116 : FD_PROTOTYPES_BEGIN 117 : 118 : static inline int 119 0 : fd_cost_tracker_err_to_runtime_err( int err ) { 120 0 : switch( err ) { 121 0 : case FD_COST_TRACKER_SUCCESS: 122 0 : return FD_RUNTIME_EXECUTE_SUCCESS; 123 0 : case FD_COST_TRACKER_ERROR_WOULD_EXCEED_BLOCK_MAX_LIMIT: 124 0 : return FD_RUNTIME_TXN_ERR_WOULD_EXCEED_MAX_BLOCK_COST_LIMIT; 125 0 : case FD_COST_TRACKER_ERROR_WOULD_EXCEED_VOTE_MAX_LIMIT: 126 0 : return FD_RUNTIME_TXN_ERR_WOULD_EXCEED_MAX_VOTE_COST_LIMIT; 127 0 : case FD_COST_TRACKER_ERROR_WOULD_EXCEED_ACCOUNT_MAX_LIMIT: 128 0 : return FD_RUNTIME_TXN_ERR_WOULD_EXCEED_MAX_ACCOUNT_COST_LIMIT; 129 0 : case FD_COST_TRACKER_ERROR_WOULD_EXCEED_ACCOUNT_DATA_BLOCK_LIMIT: 130 0 : return FD_RUNTIME_TXN_ERR_WOULD_EXCEED_ACCOUNT_DATA_BLOCK_LIMIT; 131 0 : case FD_COST_TRACKER_ERROR_WOULD_EXCEED_ACCOUNT_DATA_TOTAL_LIMIT: 132 0 : return FD_RUNTIME_TXN_ERR_WOULD_EXCEED_ACCOUNT_DATA_TOTAL_LIMIT; 133 0 : default: 134 0 : __builtin_unreachable(); 135 0 : } 136 0 : } 137 : 138 : FD_FN_CONST ulong 139 : fd_cost_tracker_align( void ); 140 : 141 : FD_FN_CONST ulong 142 : fd_cost_tracker_footprint( void ); 143 : 144 : void * 145 : fd_cost_tracker_new( void * shmem, 146 : int larger_max_cost_per_block, 147 : ulong seed ); 148 : 149 : fd_cost_tracker_t * 150 : fd_cost_tracker_join( void * shct ); 151 : 152 : void 153 : fd_cost_tracker_init( fd_cost_tracker_t * cost_tracker, 154 : fd_features_t const * features, 155 : ulong slot ); 156 : 157 : /* https://github.com/anza-xyz/agave/blob/v2.2.0/cost-model/src/cost_model.rs#L323-L328 */ 158 : FD_FN_PURE ulong 159 : fd_cost_tracker_calculate_loaded_accounts_data_size_cost( fd_txn_out_t const * txn_out ); 160 : 161 : /* fd_cost_tracker_calculate_cost_and_add takes a transaction, 162 : calculates the cost of the transaction in terms of various block 163 : level limits and adds it to the cost tracker. If the incremental 164 : transaction fits in the block, then the cost tracking is updated and 165 : FD_COST_TRACKER_SUCCESS is returned. If the transaction does not 166 : fit then FD_COST_TRACKER_ERROR_{*} is returned depending on what 167 : limit is violated. 168 : 169 : This function assumes that the caller is responsible for managing 170 : concurrent callers. 171 : 172 : This function represents the Agave client function: 173 : `CostModel::calculate_cost_for_executed_transaction()` 174 : 175 : https://github.com/anza-xyz/agave/blob/v2.2.0/cost-model/src/cost_model.rs#L69-L95 176 : */ 177 : 178 : void 179 : fd_cost_tracker_calculate_cost( fd_bank_t * bank, 180 : fd_txn_in_t const * txn_in, 181 : fd_txn_out_t * txn_out ); 182 : 183 : /* fd_cost_tracker_try_add_cost takes the cost as calculated by 184 : fd_cost_tracker_calculate_cost and tries to accumulate it into the 185 : cost tracker. If the cost fits, the cost is added and 186 : FD_COST_TRACKER_SUCCESS is returned. If the cost does not fit, the 187 : cost will not be updated and FD_COST_TRACKER_ERROR_* is returned 188 : depending on what limit is violated. 189 : 190 : This function is the Agave client function: 'CostTracker::try_add()' 191 : https://github.com/anza-xyz/agave/blob/v2.2.0/cost-model/src/cost_tracker.rs#L163-L173 */ 192 : 193 : int 194 : fd_cost_tracker_try_add_cost( fd_cost_tracker_t * cost_tracker, 195 : fd_txn_out_t * txn_out ); 196 : 197 : FD_PROTOTYPES_END 198 : 199 : #endif /* HEADER_fd_src_flamenco_runtime_fd_cost_tracker_h */