Line data Source code
1 : #ifndef HEADER_fd_src_flamenco_runtime_sysvar_fd_sysvar_cache_h 2 : #define HEADER_fd_src_flamenco_runtime_sysvar_fd_sysvar_cache_h 3 : 4 : /* fd_sysvar_cache.h is a read-only cache of sysvar accounts. 5 : 6 : Each block, the sysvar cache is used as follows: 7 : 8 : - Sysvar accounts are written to DB pre-execution (Bank::new) 9 : - Sysvar cache is restored from accounts (reads sysvar accounts) 10 : (Recreated from scratch using account contents) 11 : - Parallel transaction execution (reads from sysvar cache) 12 : - Sysvar accounts are written to DB post-execution (Bank::freeze) 13 : 14 : In other words, sysvars backed by stored accounts are updated before 15 : and after transaction execution. During transaction execution, they 16 : are constant. Firedancer stores a copy of these sysvars in the 17 : sysvar cache for performance (raw and typed forms). 18 : 19 : During the slot boundary (outside of transaction execution), sysvars 20 : should be accessed using the accounts directly, and the sysvar cache 21 : is considered non-existent. */ 22 : 23 : #include "fd_sysvar_base.h" 24 : #include "../../accdb/fd_accdb_ref.h" 25 : 26 82032 : #define FD_SYSVAR_CACHE_ENTRY_CNT 9 27 : 28 : /* fd_sysvar_cache_t is the header of a sysvar_cache object. 29 : A sysvar_cache object is position-independent and backed entirely by 30 : a single memory region. Each sysvar is stored in serialized/raw form 31 : and in a typed form. fd_sysvar_cache_desc_t points either form. 32 : 33 : It is safe to relocate a sysvar_cache object, or map it from multiple 34 : processes with different address spaces, or clone it via a shallow 35 : memcpy. */ 36 : 37 : struct fd_sysvar_desc { 38 : uint flags; 39 : uint data_sz; 40 : }; 41 : 42 : typedef struct fd_sysvar_desc fd_sysvar_desc_t; 43 : 44 113145 : #define FD_SYSVAR_FLAG_VALID (0x1u) 45 : 46 : struct fd_sysvar_cache { 47 : ulong magic; /* ==FD_SYSVAR_CACHE_MAGIC */ 48 : 49 : fd_sysvar_desc_t desc[ FD_SYSVAR_CACHE_ENTRY_CNT ]; 50 : 51 : uchar bin_clock [ FD_SYSVAR_CLOCK_BINCODE_SZ ] __attribute__((aligned(FD_SYSVAR_ALIGN_MAX))); 52 : uchar bin_epoch_rewards [ FD_SYSVAR_EPOCH_REWARDS_BINCODE_SZ ] __attribute__((aligned(FD_SYSVAR_ALIGN_MAX))); 53 : uchar bin_epoch_schedule [ FD_SYSVAR_EPOCH_SCHEDULE_BINCODE_SZ ] __attribute__((aligned(FD_SYSVAR_ALIGN_MAX))); 54 : uchar bin_last_restart_slot [ FD_SYSVAR_LAST_RESTART_SLOT_BINCODE_SZ ] __attribute__((aligned(FD_SYSVAR_ALIGN_MAX))); 55 : uchar bin_recent_hashes [ FD_SYSVAR_RECENT_HASHES_BINCODE_SZ ] __attribute__((aligned(FD_SYSVAR_ALIGN_MAX))); 56 : uchar bin_rent [ FD_SYSVAR_RENT_BINCODE_SZ ] __attribute__((aligned(FD_SYSVAR_ALIGN_MAX))); 57 : uchar bin_slot_hashes [ FD_SYSVAR_SLOT_HASHES_BINCODE_SZ ] __attribute__((aligned(FD_SYSVAR_ALIGN_MAX))); 58 : uchar bin_slot_history [ FD_SYSVAR_SLOT_HISTORY_BINCODE_SZ ] __attribute__((aligned(FD_SYSVAR_ALIGN_MAX))); 59 : uchar bin_stake_history [ FD_SYSVAR_STAKE_HISTORY_BINCODE_SZ ] __attribute__((aligned(FD_SYSVAR_ALIGN_MAX))); 60 : 61 : /* Note that two sysvars are (deliberately) missing: 62 : - The 'fees' sysvar was deprecated/demoted. It is not part of the 63 : sysvar cache in Agave. 64 : - The 'instructions' sysvar is a virtual account, not a stored 65 : account. It is never written to a database, therefore it does 66 : not make sense to cache it. */ 67 : }; 68 : 69 : typedef struct fd_sysvar_cache fd_sysvar_cache_t; 70 : 71 15 : #define FD_SYSVAR_clock_IDX 0 72 6 : #define FD_SYSVAR_epoch_rewards_IDX 1 73 6 : #define FD_SYSVAR_epoch_schedule_IDX 2 74 3 : #define FD_SYSVAR_last_restart_slot_IDX 3 75 9 : #define FD_SYSVAR_recent_hashes_IDX 4 76 21 : #define FD_SYSVAR_rent_IDX 5 77 111 : #define FD_SYSVAR_slot_hashes_IDX 6 78 : #define FD_SYSVAR_slot_history_IDX 7 79 414 : #define FD_SYSVAR_stake_history_IDX 8 80 : 81 : FD_PROTOTYPES_BEGIN 82 : 83 : /* Constructor API */ 84 : 85 : /* fd_sysvar_cache_new formats a memory region allocated according to 86 : fd_sysvar_cache_{align,footprint} for use as a sysvar_cache object. */ 87 : 88 : void * 89 : fd_sysvar_cache_new( void * mem ); 90 : 91 : /* fd_sysvar_cache_join joins the caller to a sysvar_cache object as 92 : writable mode. fd_sysvar_cache_join_const is the read-only version. */ 93 : 94 : fd_sysvar_cache_t * 95 : fd_sysvar_cache_join( void * mem ); 96 : 97 : fd_sysvar_cache_t const * 98 : fd_sysvar_cache_join_const( void const * mem ); 99 : 100 : /* fd_sysvar_cache_leave undoes a join to a sysvar_cache object. */ 101 : 102 : void * 103 : fd_sysvar_cache_leave( fd_sysvar_cache_t * sysvar_cache ); 104 : 105 : void const * 106 : fd_sysvar_cache_leave_const( fd_sysvar_cache_t const * sysvar_cache ); 107 : 108 : /* fd_sysvar_cache_delete releases the sysvar cache object's backing 109 : memory region back to the caller. */ 110 : 111 : void * 112 : fd_sysvar_cache_delete( void * mem ); 113 : 114 : /* fd_sysvar_cache_restore rebuilds the sysvar cache from the account 115 : database. Does blocking account database queries. Returns 1 on 116 : success, or 0 on failure (logs warnings). Reasons for failure 117 : include unexpected database error or sysvar deserialize failure. */ 118 : 119 : int 120 : fd_sysvar_cache_restore( fd_bank_t * bank, 121 : fd_accdb_user_t * accdb, 122 : fd_funk_txn_xid_t const * xid ); 123 : 124 : /* fd_sysvar_cache_restore_fuzz is a weaker version of the above for use 125 : with solfuzz/protosol conformance tooling. This version works around 126 : bugs in that tooling that create invalid sysvars and suppresses noisy 127 : log warning. */ 128 : 129 : void 130 : fd_sysvar_cache_restore_fuzz( fd_bank_t * bank, 131 : fd_accdb_user_t * accdb, 132 : fd_funk_txn_xid_t const * xid ); 133 : 134 : void 135 : fd_sysvar_cache_restore_from_ref( fd_sysvar_cache_t * cache, 136 : fd_accdb_ro_t const * ro ); 137 : 138 : /* Generic accessors for serialized sysvar account data. */ 139 : 140 : /* fd_sysvar_cache_data_query returns a pointer to raw/serialized sysvar 141 : account data. address points to the address of the sysvar account. 142 : *psz is set to the serialized data size (or 0 on failure). 143 : The returned pointer is valid until the next API call that takes a 144 : non-const pointer to sysvar_cache. Note there are technically three 145 : outcomes (retval is the return value): 146 : - retval!=NULL && *psz!=0 sysvar is valid 147 : - retval==NULL && *psz==0 no sysvar with this address or sysvar 148 : or sysvar contains invalid data 149 : - retval!=NULL && *psz==0 sysvar is valid, but empty (impossible 150 : with current sysvars) */ 151 : 152 : uchar const * 153 : fd_sysvar_cache_data_query( 154 : fd_sysvar_cache_t const * sysvar_cache, 155 : void const * address, /* 32 bytes */ 156 : ulong * psz 157 : ); 158 : 159 : #define FD_SYSVAR_IS_VALID( sysvar_cache, sysvar ) \ 160 90 : ( ( FD_VOLATILE_CONST( sysvar_cache->desc[ FD_SYSVAR_##sysvar##_IDX ].flags ) \ 161 90 : & ( FD_SYSVAR_FLAG_VALID ) ) \ 162 90 : == FD_SYSVAR_FLAG_VALID ) 163 : 164 : /* Accessors for small POD sysvars. These do a copy on read. 165 : 166 : fd_sysvar_clock_is_valid returns 1 if the cached sysvar is valid 167 : (read, read_nofail, join_const are then guaranteed to succeed). 168 : Returns 0 otherwise. 169 : 170 : fd_sysvar_clock_read attempts to copy sysvar data from cache into the 171 : out argument. Returns out on success, or NULL if the sysvar account 172 : does not exist or contains data that failed deserialization. 173 : 174 : fd_sysvar_clock_read_nofail returns a copy of the sysvar data. If 175 : the sysvar does not exist or failed to deserialize, aborts the app 176 : with FD_LOG_ERR. 177 : 178 : Accessors for the other sysvars in this section are analogous. */ 179 : 180 : static inline int 181 12 : fd_sysvar_cache_clock_is_valid( fd_sysvar_cache_t const * sysvar_cache ) { 182 12 : return FD_SYSVAR_IS_VALID( sysvar_cache, clock ); 183 12 : } 184 : 185 : fd_sol_sysvar_clock_t * 186 : fd_sysvar_cache_clock_read( 187 : fd_sysvar_cache_t const * sysvar_cache, 188 : fd_sol_sysvar_clock_t * out 189 : ); 190 : 191 : /* Macro to improve FD_LOG_ERR line number accuracy */ 192 : 193 : #define SIMPLE_SYSVAR_READ_NOFAIL( cache, name, typet ) \ 194 3 : __extension__({ \ 195 3 : typet out; \ 196 3 : if( FD_UNLIKELY( !fd_sysvar_cache_##name##_read( (cache), &out ) ) )\ 197 3 : FD_LOG_ERR(( "fd_sysvar_" #name "_read_nofail failed: sysvar not valid" )); \ 198 3 : out; \ 199 3 : }) 200 : 201 : #define fd_sysvar_cache_clock_read_nofail( cache ) \ 202 3 : SIMPLE_SYSVAR_READ_NOFAIL( cache, clock, fd_sol_sysvar_clock_t ) 203 : 204 : static inline int 205 9 : fd_sysvar_cache_epoch_rewards_is_valid( fd_sysvar_cache_t const * sysvar_cache ) { 206 9 : return FD_SYSVAR_IS_VALID( sysvar_cache, epoch_rewards ); 207 9 : } 208 : 209 : fd_sysvar_epoch_rewards_t * 210 : fd_sysvar_cache_epoch_rewards_read( 211 : fd_sysvar_cache_t const * sysvar_cache, 212 : fd_sysvar_epoch_rewards_t * out 213 : ); 214 : 215 : static inline int 216 9 : fd_sysvar_cache_epoch_schedule_is_valid( fd_sysvar_cache_t const * sysvar_cache ) { 217 9 : return FD_SYSVAR_IS_VALID( sysvar_cache, epoch_schedule ); 218 9 : } 219 : 220 : fd_epoch_schedule_t * 221 : fd_sysvar_cache_epoch_schedule_read( 222 : fd_sysvar_cache_t const * sysvar_cache, 223 : fd_epoch_schedule_t * out 224 : ); 225 : 226 : #define fd_sysvar_cache_epoch_schedule_read_nofail( cache ) \ 227 : SIMPLE_SYSVAR_READ_NOFAIL( cache, epoch_schedule, fd_epoch_schedule_t ) 228 : 229 : static inline int 230 3 : fd_sysvar_cache_last_restart_slot_is_valid( fd_sysvar_cache_t const * sysvar_cache ) { 231 3 : return FD_SYSVAR_IS_VALID( sysvar_cache, last_restart_slot ); 232 3 : } 233 : 234 : ulong const * 235 : fd_sysvar_cache_last_restart_slot_read( fd_sysvar_cache_t const * sysvar_cache ); 236 : 237 : static inline int 238 3 : fd_sysvar_cache_rent_is_valid( fd_sysvar_cache_t const * sysvar_cache ) { 239 3 : return FD_SYSVAR_IS_VALID( sysvar_cache, rent ); 240 3 : } 241 : 242 : fd_rent_t * 243 : fd_sysvar_cache_rent_read( 244 : fd_sysvar_cache_t const * sysvar_cache, 245 : fd_rent_t * out 246 : ); 247 : 248 : #define fd_sysvar_cache_rent_read_nofail( cache ) \ 249 0 : SIMPLE_SYSVAR_READ_NOFAIL( cache, rent, fd_rent_t ) 250 : 251 : /* Accessors for large sysvars. */ 252 : 253 : static inline int 254 27 : fd_sysvar_cache_recent_hashes_is_valid( fd_sysvar_cache_t const * sysvar_cache ) { 255 27 : return FD_SYSVAR_IS_VALID( sysvar_cache, recent_hashes ); 256 27 : } 257 : 258 : /* fd_sysvar_cache_recent_hashes_is_empty returns 0 if there is at least 259 : one valid blockhash queue entry in the 'recent blockhashes' sysvar, 260 : 1 otherwise (invalid sysvar or empty queue). */ 261 : 262 : int 263 : fd_sysvar_cache_recent_hashes_is_empty( fd_sysvar_cache_t const * sysvar_cache ); 264 : 265 : static inline int 266 6 : fd_sysvar_cache_slot_hashes_is_valid( fd_sysvar_cache_t const * sysvar_cache ) { 267 6 : return FD_SYSVAR_IS_VALID( sysvar_cache, slot_hashes ); 268 6 : } 269 : 270 : /* fd_sysvar_cache_slot_history_{join,leave}_const {attach,detach} the 271 : caller {from,to} the "slot history" sysvar. Behavior analogous to 272 : above accessors. */ 273 : 274 : static inline int 275 12 : fd_sysvar_cache_slot_history_is_valid( fd_sysvar_cache_t const * sysvar_cache ) { 276 12 : return FD_SYSVAR_IS_VALID( sysvar_cache, slot_history ); 277 12 : } 278 : 279 : static inline int 280 9 : fd_sysvar_cache_stake_history_is_valid( fd_sysvar_cache_t const * sysvar_cache ) { 281 9 : return FD_SYSVAR_IS_VALID( sysvar_cache, stake_history ); 282 9 : } 283 : 284 : /* View convenience wrappers. These combine fd_sysvar_cache_data_query 285 : with the corresponding fd_sysvar_*_view function. Returns view on 286 : success or NULL if the sysvar is invalid. */ 287 : 288 : fd_stake_history_t * 289 : fd_sysvar_cache_stake_history_view( fd_sysvar_cache_t const * cache, 290 : fd_stake_history_t * view ); 291 : 292 : fd_slot_hashes_t * 293 : fd_sysvar_cache_slot_hashes_view( fd_sysvar_cache_t const * cache, 294 : fd_slot_hashes_t * view ); 295 : 296 : FD_PROTOTYPES_END 297 : 298 : #endif /* HEADER_fd_src_flamenco_runtime_sysvar_fd_sysvar_cache_h */