LCOV - code coverage report
Current view: top level - flamenco/runtime/sysvar - fd_sysvar_cache.h (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 44 46 95.7 %
Date: 2025-09-18 04:41:32 Functions: 12 1017 1.2 %

          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 "../../types/fd_types.h"
      25             : 
      26        5094 : #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        1557 : #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 obj_clock             [ FD_SYSVAR_CLOCK_FOOTPRINT              ] __attribute__((aligned(FD_SYSVAR_ALIGN_MAX)));
      53             :   uchar bin_epoch_rewards     [ FD_SYSVAR_EPOCH_REWARDS_BINCODE_SZ     ] __attribute__((aligned(FD_SYSVAR_ALIGN_MAX)));
      54             :   uchar obj_epoch_rewards     [ FD_SYSVAR_EPOCH_REWARDS_FOOTPRINT      ] __attribute__((aligned(FD_SYSVAR_ALIGN_MAX)));
      55             :   uchar bin_epoch_schedule    [ FD_SYSVAR_EPOCH_SCHEDULE_BINCODE_SZ    ] __attribute__((aligned(FD_SYSVAR_ALIGN_MAX)));
      56             :   uchar obj_epoch_schedule    [ FD_SYSVAR_EPOCH_SCHEDULE_FOOTPRINT     ] __attribute__((aligned(FD_SYSVAR_ALIGN_MAX)));
      57             :   uchar bin_last_restart_slot [ FD_SYSVAR_LAST_RESTART_SLOT_BINCODE_SZ ] __attribute__((aligned(FD_SYSVAR_ALIGN_MAX)));
      58             :   uchar obj_last_restart_slot [ FD_SYSVAR_LAST_RESTART_SLOT_FOOTPRINT  ] __attribute__((aligned(FD_SYSVAR_ALIGN_MAX)));
      59             :   uchar bin_recent_hashes     [ FD_SYSVAR_RECENT_HASHES_BINCODE_SZ     ] __attribute__((aligned(FD_SYSVAR_ALIGN_MAX)));
      60             :   uchar bin_rent              [ FD_SYSVAR_RENT_BINCODE_SZ              ] __attribute__((aligned(FD_SYSVAR_ALIGN_MAX)));
      61             :   uchar obj_rent              [ FD_SYSVAR_RENT_FOOTPRINT               ] __attribute__((aligned(FD_SYSVAR_ALIGN_MAX)));
      62             :   uchar bin_slot_hashes       [ FD_SYSVAR_SLOT_HASHES_BINCODE_SZ       ] __attribute__((aligned(FD_SYSVAR_ALIGN_MAX)));
      63             :   uchar obj_slot_hashes       [ FD_SYSVAR_SLOT_HASHES_FOOTPRINT        ] __attribute__((aligned(FD_SYSVAR_ALIGN_MAX)));
      64             :   uchar bin_slot_history      [ FD_SYSVAR_SLOT_HISTORY_BINCODE_SZ      ] __attribute__((aligned(FD_SYSVAR_ALIGN_MAX)));
      65             :   uchar obj_slot_history      [ FD_SYSVAR_SLOT_HISTORY_FOOTPRINT       ] __attribute__((aligned(FD_SYSVAR_ALIGN_MAX)));
      66             :   uchar bin_stake_history     [ FD_SYSVAR_STAKE_HISTORY_BINCODE_SZ     ] __attribute__((aligned(FD_SYSVAR_ALIGN_MAX)));
      67             :   uchar obj_stake_history     [ FD_SYSVAR_STAKE_HISTORY_FOOTPRINT      ] __attribute__((aligned(FD_SYSVAR_ALIGN_MAX)));
      68             : 
      69             :   /* Note that two sysvars are (deliberately) missing:
      70             :      - The 'fees' sysvar was deprecated/demoted.  It is not part of the
      71             :        sysvar cache in Agave.
      72             :      - The 'instructions' sysvar is a virtual account, not a stored
      73             :        account.  It is never written to a database, therefore it does
      74             :        not make sense to cache it. */
      75             : };
      76             : 
      77             : typedef struct fd_sysvar_cache fd_sysvar_cache_t;
      78             : 
      79           9 : #define FD_SYSVAR_clock_IDX               0
      80           3 : #define FD_SYSVAR_epoch_rewards_IDX       1
      81           3 : #define FD_SYSVAR_epoch_schedule_IDX      2
      82           3 : #define FD_SYSVAR_last_restart_slot_IDX   3
      83             : #define FD_SYSVAR_recent_hashes_IDX       4
      84           3 : #define FD_SYSVAR_rent_IDX                5
      85             : #define FD_SYSVAR_slot_hashes_IDX         6
      86             : #define FD_SYSVAR_slot_history_IDX        7
      87             : #define FD_SYSVAR_stake_history_IDX       8
      88             : 
      89             : FD_PROTOTYPES_BEGIN
      90             : 
      91             : /* Constructor API */
      92             : 
      93             : /* fd_sysvar_cache_new formats a memory region allocated according to
      94             :    fd_sysvar_cache_{align,footprint} for use as a sysvar_cache object. */
      95             : 
      96             : void *
      97             : fd_sysvar_cache_new( void * mem );
      98             : 
      99             : /* fd_sysvar_cache_join joins the caller to a sysvar_cache object as
     100             :    writable mode.  fd_sysvar_cache_join_const is the read-only version. */
     101             : 
     102             : fd_sysvar_cache_t *
     103             : fd_sysvar_cache_join( void * mem );
     104             : 
     105             : fd_sysvar_cache_t const *
     106             : fd_sysvar_cache_join_const( void const * mem );
     107             : 
     108             : /* fd_sysvar_cache_leave undoes a join to a sysvar_cache object. */
     109             : 
     110             : void *
     111             : fd_sysvar_cache_leave( fd_sysvar_cache_t * sysvar_cache );
     112             : 
     113             : void const *
     114             : fd_sysvar_cache_leave_const( fd_sysvar_cache_t const * sysvar_cache );
     115             : 
     116             : /* fd_sysvar_cache_delete releases the sysvar cache object's backing
     117             :    memory region back to the caller. */
     118             : 
     119             : void *
     120             : fd_sysvar_cache_delete( void * mem );
     121             : 
     122             : /* fd_sysvar_cache_restore rebuilds the sysvar cache from the account
     123             :    database.  Does blocking account database queries.  Returns 1 on
     124             :    success, or 0 on failure (logs warnings).  Reasons for failure
     125             :    include unexpected database error or sysvar deserialize failure. */
     126             : 
     127             : int
     128             : fd_sysvar_cache_restore( fd_exec_slot_ctx_t * slot_ctx );
     129             : 
     130             : /* fd_sysvar_cache_restore_fuzz is a weaker version of the above for use
     131             :    with solfuzz/protosol conformance tooling.  This version works around
     132             :    bugs in that tooling that create invalid sysvars and suppresses noisy
     133             :    log warning. */
     134             : 
     135             : void
     136             : fd_sysvar_cache_restore_fuzz( fd_exec_slot_ctx_t * slot_ctx );
     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          69 :   ( ( FD_VOLATILE_CONST( sysvar_cache->desc[ FD_SYSVAR_##sysvar##_IDX ].flags ) \
     161          69 :       & ( FD_SYSVAR_FLAG_VALID ) ) \
     162          69 :     == 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           3 : fd_sysvar_cache_epoch_rewards_is_valid( fd_sysvar_cache_t const * sysvar_cache ) {
     206           3 :   return FD_SYSVAR_IS_VALID( sysvar_cache, epoch_rewards );
     207           3 : }
     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           3 : fd_sysvar_cache_epoch_schedule_is_valid( fd_sysvar_cache_t const * sysvar_cache ) {
     217           3 :   return FD_SYSVAR_IS_VALID( sysvar_cache, epoch_schedule );
     218           3 : }
     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           0 :   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             : fd_sol_sysvar_last_restart_slot_t *
     235             : fd_sysvar_cache_last_restart_slot_read(
     236             :     fd_sysvar_cache_t const *           sysvar_cache,
     237             :     fd_sol_sysvar_last_restart_slot_t * out
     238             : );
     239             : 
     240             : static inline int
     241           3 : fd_sysvar_cache_rent_is_valid( fd_sysvar_cache_t const * sysvar_cache ) {
     242           3 :   return FD_SYSVAR_IS_VALID( sysvar_cache, rent );
     243           3 : }
     244             : 
     245             : fd_rent_t *
     246             : fd_sysvar_cache_rent_read(
     247             :     fd_sysvar_cache_t const * sysvar_cache,
     248             :     fd_rent_t *               out
     249             : );
     250             : 
     251             : #define fd_sysvar_cache_rent_read_nofail( cache ) \
     252           0 :   SIMPLE_SYSVAR_READ_NOFAIL( cache, rent, fd_rent_t )
     253             : 
     254             : /* Accessors for large sysvars. */
     255             : 
     256             : static inline int
     257          18 : fd_sysvar_cache_recent_hashes_is_valid( fd_sysvar_cache_t const * sysvar_cache ) {
     258          18 :   return FD_SYSVAR_IS_VALID( sysvar_cache, recent_hashes );
     259          18 : }
     260             : 
     261             : /* fd_sysvar_cache_slot_hashes_{join,leave}_const {attach,detach} the
     262             :    caller {from,to} the slot hashes deque contained in the slot hashes
     263             :    sysvar.
     264             : 
     265             :    The join API returns a pointer into the sysvar cache.  If the sysvar
     266             :    account is in an invalid state (non-existent, failed to deserialize),
     267             :    join returns NULL. */
     268             : 
     269             : static inline int
     270           6 : fd_sysvar_cache_slot_hashes_is_valid( fd_sysvar_cache_t const * sysvar_cache ) {
     271           6 :   return FD_SYSVAR_IS_VALID( sysvar_cache, slot_hashes );
     272           6 : }
     273             : 
     274             : fd_slot_hash_t const *
     275             : fd_sysvar_cache_slot_hashes_join_const(
     276             :     fd_sysvar_cache_t const * sysvar_cache
     277             : );
     278             : 
     279             : void
     280             : fd_sysvar_cache_slot_hashes_leave_const(
     281             :     fd_sysvar_cache_t const * sysvar_cache,
     282             :     fd_slot_hash_t const *    slot_hashes
     283             : );
     284             : 
     285             : /* fd_sysvar_cache_slot_history_{join,leave}_const {attach,detach} the
     286             :    caller {from,to} the "slot history" sysvar.  Behavior analogous to
     287             :    above accessors. */
     288             : 
     289             : static inline int
     290           6 : fd_sysvar_cache_slot_history_is_valid( fd_sysvar_cache_t const * sysvar_cache ) {
     291           6 :   return FD_SYSVAR_IS_VALID( sysvar_cache, slot_history );
     292           6 : }
     293             : 
     294             : fd_slot_history_global_t const *
     295             : fd_sysvar_cache_slot_history_join_const(
     296             :     fd_sysvar_cache_t const * sysvar_cache
     297             : );
     298             : 
     299             : void
     300             : fd_sysvar_cache_slot_history_leave_const(
     301             :     fd_sysvar_cache_t const *        sysvar_cache,
     302             :     fd_slot_history_global_t const * slot_history
     303             : );
     304             : 
     305             : /* fd_sysvar_cache_stake_history_{join,leave}_const {attach,detach} the
     306             :    caller {from,to} the "stake history" sysvar.  Behavior analogous to
     307             :    above accessors. */
     308             : 
     309             : static inline int
     310          15 : fd_sysvar_cache_stake_history_is_valid( fd_sysvar_cache_t const * sysvar_cache ) {
     311          15 :   return FD_SYSVAR_IS_VALID( sysvar_cache, stake_history );
     312          15 : }
     313             : 
     314             : fd_stake_history_t const *
     315             : fd_sysvar_cache_stake_history_join_const(
     316             :     fd_sysvar_cache_t const * sysvar_cache
     317             : );
     318             : 
     319             : void
     320             : fd_sysvar_cache_stake_history_leave_const(
     321             :     fd_sysvar_cache_t const *  sysvar_cache,
     322             :     fd_stake_history_t const * stake_history
     323             : );
     324             : 
     325             : FD_PROTOTYPES_END
     326             : 
     327             : #endif /* HEADER_fd_src_flamenco_runtime_sysvar_fd_sysvar_cache_h */

Generated by: LCOV version 1.14