LCOV - code coverage report
Current view: top level - flamenco/runtime/sysvar - fd_sysvar_cache.h (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 47 48 97.9 %
Date: 2026-07-11 05:27:41 Functions: 10 1251 0.8 %

          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.h"
      25             : 
      26       91437 : #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      125931 : #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          21 : #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          15 : #define FD_SYSVAR_rent_IDX                5
      77         132 : #define FD_SYSVAR_slot_hashes_IDX         6
      78             : #define FD_SYSVAR_slot_history_IDX        7
      79         996 : #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_t * accdb );
     122             : 
     123             : /* fd_sysvar_cache_restore_fuzz is a weaker version of the above for use
     124             :    with solfuzz/protosol conformance tooling.  This version works around
     125             :    bugs in that tooling that create invalid sysvars and suppresses noisy
     126             :    log warning. */
     127             : 
     128             : void
     129             : fd_sysvar_cache_restore_fuzz( fd_bank_t *  bank,
     130             :                               fd_accdb_t * accdb );
     131             : 
     132             : /* fd_sysvar_cache_restore_one restores a single sysvar cache entry from
     133             :    raw account fields (as opposed to a full fd_acc_t).  If pubkey is not
     134             :    a sysvar or lamports is zero, this is a no-op. */
     135             : 
     136             : void
     137             : fd_sysvar_cache_restore_one( fd_sysvar_cache_t * cache,
     138             :                              fd_pubkey_t const * pubkey,
     139             :                              ulong               lamports,
     140             :                              uchar const *       acc_data,
     141             :                              ulong               acc_data_len );
     142             : 
     143             : /* Generic accessors for serialized sysvar account data. */
     144             : 
     145             : /* fd_sysvar_cache_data_query returns a pointer to raw/serialized sysvar
     146             :    account data.  address points to the address of the sysvar account.
     147             :    *psz is set to the serialized data size (or 0 on failure).
     148             :    The returned pointer is valid until the next API call that takes a
     149             :    non-const pointer to sysvar_cache.  Note there are technically three
     150             :    outcomes (retval is the return value):
     151             :    - retval!=NULL && *psz!=0  sysvar is valid
     152             :    - retval==NULL && *psz==0  no sysvar with this address or sysvar
     153             :                               or sysvar contains invalid data
     154             :    - retval!=NULL && *psz==0  sysvar is valid, but empty (impossible
     155             :                               with current sysvars) */
     156             : 
     157             : uchar const *
     158             : fd_sysvar_cache_data_query(
     159             :     fd_sysvar_cache_t const * sysvar_cache,
     160             :     void const *              address, /* 32 bytes */
     161             :     ulong *                   psz
     162             : );
     163             : 
     164             : #define FD_SYSVAR_IS_VALID( sysvar_cache, sysvar ) \
     165          93 :   ( ( FD_VOLATILE_CONST( sysvar_cache->desc[ FD_SYSVAR_##sysvar##_IDX ].flags ) \
     166          93 :       & ( FD_SYSVAR_FLAG_VALID ) ) \
     167          93 :     == FD_SYSVAR_FLAG_VALID )
     168             : 
     169             : /* Accessors for small POD sysvars.  These do a copy on read.
     170             : 
     171             :    fd_sysvar_clock_is_valid returns 1 if the cached sysvar is valid
     172             :    (read, read_nofail, join_const are then guaranteed to succeed).
     173             :    Returns 0 otherwise.
     174             : 
     175             :    fd_sysvar_clock_read attempts to copy sysvar data from cache into the
     176             :    out argument.  Returns out on success, or NULL if the sysvar account
     177             :    does not exist or contains data that failed deserialization.
     178             : 
     179             :    fd_sysvar_clock_read_nofail returns a copy of the sysvar data.  If
     180             :    the sysvar does not exist or failed to deserialize, aborts the app
     181             :    with FD_LOG_ERR.
     182             : 
     183             :    Accessors for the other sysvars in this section are analogous. */
     184             : 
     185             : static inline int
     186          12 : fd_sysvar_cache_clock_is_valid( fd_sysvar_cache_t const * sysvar_cache ) {
     187          12 :   return FD_SYSVAR_IS_VALID( sysvar_cache, clock );
     188          12 : }
     189             : 
     190             : fd_sol_sysvar_clock_t *
     191             : fd_sysvar_cache_clock_read(
     192             :     fd_sysvar_cache_t const * sysvar_cache,
     193             :     fd_sol_sysvar_clock_t *   out
     194             : );
     195             : 
     196             : /* Macro to improve FD_LOG_ERR line number accuracy */
     197             : 
     198             : #define SIMPLE_SYSVAR_READ_NOFAIL( cache, name, typet )                \
     199           3 :   __extension__({                                                      \
     200           3 :     typet out;                                                         \
     201           3 :     if( FD_UNLIKELY( !fd_sysvar_cache_##name##_read( (cache), &out ) ) )\
     202           3 :       FD_LOG_ERR(( "fd_sysvar_" #name "_read_nofail failed: sysvar not valid" )); \
     203           3 :     out;                                                               \
     204           3 :   })
     205             : 
     206             : #define fd_sysvar_cache_clock_read_nofail( cache ) \
     207           3 :   SIMPLE_SYSVAR_READ_NOFAIL( cache, clock, fd_sol_sysvar_clock_t )
     208             : 
     209             : static inline int
     210           9 : fd_sysvar_cache_epoch_rewards_is_valid( fd_sysvar_cache_t const * sysvar_cache ) {
     211           9 :   return FD_SYSVAR_IS_VALID( sysvar_cache, epoch_rewards );
     212           9 : }
     213             : 
     214             : fd_sysvar_epoch_rewards_t *
     215             : fd_sysvar_cache_epoch_rewards_read(
     216             :     fd_sysvar_cache_t const *   sysvar_cache,
     217             :     fd_sysvar_epoch_rewards_t * out
     218             : );
     219             : 
     220             : static inline int
     221           9 : fd_sysvar_cache_epoch_schedule_is_valid( fd_sysvar_cache_t const * sysvar_cache ) {
     222           9 :   return FD_SYSVAR_IS_VALID( sysvar_cache, epoch_schedule );
     223           9 : }
     224             : 
     225             : fd_epoch_schedule_t *
     226             : fd_sysvar_cache_epoch_schedule_read(
     227             :     fd_sysvar_cache_t const * sysvar_cache,
     228             :     fd_epoch_schedule_t *     out
     229             : );
     230             : 
     231             : #define fd_sysvar_cache_epoch_schedule_read_nofail( cache ) \
     232             :   SIMPLE_SYSVAR_READ_NOFAIL( cache, epoch_schedule, fd_epoch_schedule_t )
     233             : 
     234             : static inline int
     235           3 : fd_sysvar_cache_last_restart_slot_is_valid( fd_sysvar_cache_t const * sysvar_cache ) {
     236           3 :   return FD_SYSVAR_IS_VALID( sysvar_cache, last_restart_slot );
     237           3 : }
     238             : 
     239             : ulong const *
     240             : fd_sysvar_cache_last_restart_slot_read( fd_sysvar_cache_t const * sysvar_cache );
     241             : 
     242             : static inline int
     243           3 : fd_sysvar_cache_rent_is_valid( fd_sysvar_cache_t const * sysvar_cache ) {
     244           3 :   return FD_SYSVAR_IS_VALID( sysvar_cache, rent );
     245           3 : }
     246             : 
     247             : fd_rent_t *
     248             : fd_sysvar_cache_rent_read(
     249             :     fd_sysvar_cache_t const * sysvar_cache,
     250             :     fd_rent_t *               out
     251             : );
     252             : 
     253             : #define fd_sysvar_cache_rent_read_nofail( cache ) \
     254           0 :   SIMPLE_SYSVAR_READ_NOFAIL( cache, rent, fd_rent_t )
     255             : 
     256             : /* Accessors for large sysvars. */
     257             : 
     258             : static inline int
     259          27 : fd_sysvar_cache_recent_hashes_is_valid( fd_sysvar_cache_t const * sysvar_cache ) {
     260          27 :   return FD_SYSVAR_IS_VALID( sysvar_cache, recent_hashes );
     261          27 : }
     262             : 
     263             : /* fd_sysvar_cache_recent_hashes_is_empty returns 0 if there is at least
     264             :    one valid blockhash queue entry in the 'recent blockhashes' sysvar,
     265             :    1 otherwise (invalid sysvar or empty queue). */
     266             : 
     267             : int
     268             : fd_sysvar_cache_recent_hashes_is_empty( fd_sysvar_cache_t const * sysvar_cache );
     269             : 
     270             : static inline int
     271           6 : fd_sysvar_cache_slot_hashes_is_valid( fd_sysvar_cache_t const * sysvar_cache ) {
     272           6 :   return FD_SYSVAR_IS_VALID( sysvar_cache, slot_hashes );
     273           6 : }
     274             : 
     275             : /* fd_sysvar_cache_slot_history_{join,leave}_const {attach,detach} the
     276             :    caller {from,to} the "slot history" sysvar.  Behavior analogous to
     277             :    above accessors. */
     278             : 
     279             : static inline int
     280          12 : fd_sysvar_cache_slot_history_is_valid( fd_sysvar_cache_t const * sysvar_cache ) {
     281          12 :   return FD_SYSVAR_IS_VALID( sysvar_cache, slot_history );
     282          12 : }
     283             : 
     284             : static inline int
     285          12 : fd_sysvar_cache_stake_history_is_valid( fd_sysvar_cache_t const * sysvar_cache ) {
     286          12 :   return FD_SYSVAR_IS_VALID( sysvar_cache, stake_history );
     287          12 : }
     288             : 
     289             : /* View convenience wrappers.  These combine fd_sysvar_cache_data_query
     290             :    with the corresponding fd_sysvar_*_view function.  Returns view on
     291             :    success or NULL if the sysvar is invalid. */
     292             : 
     293             : fd_stake_history_t *
     294             : fd_sysvar_cache_stake_history_view( fd_sysvar_cache_t const * cache,
     295             :                                     fd_stake_history_t *      view );
     296             : 
     297             : fd_slot_hashes_t *
     298             : fd_sysvar_cache_slot_hashes_view( fd_sysvar_cache_t const * cache,
     299             :                                   fd_slot_hashes_t *        view );
     300             : 
     301             : FD_PROTOTYPES_END
     302             : 
     303             : #endif /* HEADER_fd_src_flamenco_runtime_sysvar_fd_sysvar_cache_h */

Generated by: LCOV version 1.14