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-05-26 08:02:49 Functions: 10 1134 0.9 %

          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 */

Generated by: LCOV version 1.14