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 reproduces the behavior of
5 : solana_program_runtime::sysvar_cache::SysvarCache.
6 :
7 : Firedancer provides this sysvar cache to be compatible with the Agave
8 : validator. Otherwise, it serves no purpose other than to make the
9 : runtime more complicated. The sysvar cache keeps a copy of various
10 : sysvar accounts. It is part of the implicit state of the runtime.
11 :
12 : Note that not all sysvars are in this cache.
13 :
14 : ### Cache state
15 :
16 : The sysvar cache is not a pure cache. For every supported sysvar, it
17 : may store NULL or a parsed version of the sysvar account. It is
18 : populated from the accounts DB. After parsing, the contents of the
19 : cache are not identical to the original account content anymore. If
20 : a sysvar account fails to parse, the corresponding cache entry will
21 : be NULL.
22 :
23 : ### Write back
24 :
25 : The sysvar cache can be modified directly by native programs. There
26 : is no standard pattern to how these changes get written back to the
27 : accounts DB. Currently, the write back happens at arbitrary stages
28 : in the slot boundary logic and is different for every sysvar.
29 :
30 : ### Memory Management
31 :
32 : fd_sysvar_cache_t is contained by a continuous memory region and
33 : embeds a heap allocator. */
34 :
35 : #include "../fd_acc_mgr.h"
36 :
37 : /* Reuse this table to avoid code duplication */
38 : #define FD_SYSVAR_CACHE_ITER(X) \
39 0 : X( fd_sol_sysvar_clock, clock, 0 ) \
40 0 : X( fd_epoch_schedule, epoch_schedule, 0 ) \
41 0 : X( fd_sysvar_epoch_rewards, epoch_rewards, 0 ) \
42 0 : X( fd_sysvar_fees, fees, 0 ) \
43 0 : X( fd_rent, rent, 0 ) \
44 0 : X( fd_slot_hashes, slot_hashes, 1 ) \
45 0 : X( fd_recent_block_hashes, recent_block_hashes, 1 ) \
46 0 : X( fd_stake_history, stake_history, 0 ) \
47 0 : X( fd_sol_sysvar_last_restart_slot, last_restart_slot, 0 ) \
48 0 : X( fd_slot_history, slot_history, 1 )
49 :
50 : /* The memory of fd_sysvar_cache_t fits as much sysvar information into
51 : the struct as possible. Unfortunately some parts of the sysvar
52 : spill out onto the heap due to how the type generator works.
53 :
54 : The has_{...} bits specify whether a sysvar logically exists.
55 : The gaddr_{...} ulong contains the address of the sysvar.
56 : If has_{...}==0 then any heap pointers in val_{...} are NULL,
57 : allowing for safe idempotent calls to fd_sol_sysvar_{...}_destroy() */
58 :
59 : struct __attribute__((aligned(16UL))) fd_sysvar_cache_private {
60 : ulong magic; /* ==FD_SYSVAR_CACHE_MAGIC */
61 :
62 : /* Process for global types (is_global==1) */
63 :
64 : #define X(type, name, is_global) \
65 : ulong gaddr_##name;
66 : FD_SYSVAR_CACHE_ITER(X)
67 : #undef X
68 :
69 : # undef X
70 :
71 : /* Declare the has_{...} bits */
72 : #define X( _type, name, is_global ) \
73 : ulong has_##name : 1;
74 : FD_SYSVAR_CACHE_ITER(X)
75 : #undef X
76 :
77 : #undef HANDLE_GLOBAL_1
78 : #undef HANDLE_GLOBAL_0
79 : };
80 : struct fd_sysvar_cache_private;
81 : typedef struct fd_sysvar_cache_private fd_sysvar_cache_t;
82 :
83 : FD_PROTOTYPES_BEGIN
84 :
85 : /* fd_sysvar_cache_{align,footprint} return the memory region params of
86 : an fd_sysvar_cache_t. */
87 :
88 : ulong
89 : fd_sysvar_cache_align( void );
90 :
91 : ulong
92 : fd_sysvar_cache_footprint( void );
93 :
94 : /* fd_sysvar_cache_new creates a new sysvar cache object. mem is the
95 : memory region that will back the fd_sysvar_cache_t. Attaches to the
96 : given valloc for use as a heap allocator for sysvar data. Returns
97 : object (in mem) on success and NULL on failure. Logs reasons for
98 : failure. */
99 :
100 : fd_sysvar_cache_t *
101 : fd_sysvar_cache_new( void * mem );
102 :
103 : /* fd_sysvar_cache_delete destroys a given sysvar cache object and any
104 : heap allocations made. Detaches from the valloc provided in
105 : fd_sysvar_cache_new. Returns the memory region that previously
106 : backed cache back to the caller. */
107 :
108 : void *
109 : fd_sysvar_cache_delete( fd_sysvar_cache_t * cache );
110 :
111 : /* fd_sysvar_cache_restore restores all sysvars from the given slot
112 : context.
113 :
114 : Roughly compatible with Agave's
115 : solana_program_runtime::sysvar_cache::SysvarCache::fill_missing_entries
116 : https://github.com/solana-labs/solana/blob/v1.17.23/program-runtime/src/sysvar_cache.rs#L137-L208 */
117 :
118 : void
119 : fd_sysvar_cache_restore( fd_sysvar_cache_t * cache,
120 : fd_funk_t * funk,
121 : fd_funk_txn_t * funk_txn,
122 : fd_spad_t * runtime_spad,
123 : fd_wksp_t * wksp );
124 :
125 : void
126 : fd_sysvar_cache_invalidate( fd_sysvar_cache_t * cache );
127 :
128 : /* fd_sysvar_cache_restore_{name} restores only the given sysvar object from the given slot context */
129 :
130 : # define X( type, name, is_global ) \
131 : void \
132 : fd_sysvar_cache_restore_##name( fd_sysvar_cache_t * cache, \
133 : fd_funk_t * funk, \
134 : fd_funk_txn_t * funk_txn, \
135 : fd_spad_t * runtime_spad, \
136 : fd_wksp_t * wksp );
137 : FD_SYSVAR_CACHE_ITER(X)
138 : # undef X
139 :
140 : /* Accessors for sysvars. May return NULL. */
141 :
142 : FD_FN_PURE fd_sol_sysvar_clock_t * fd_sysvar_cache_clock ( fd_sysvar_cache_t const * cache, fd_wksp_t * wksp );
143 : FD_FN_PURE fd_epoch_schedule_t * fd_sysvar_cache_epoch_schedule ( fd_sysvar_cache_t const * cache, fd_wksp_t * wksp );
144 : FD_FN_PURE fd_sysvar_epoch_rewards_t * fd_sysvar_cache_epoch_rewards ( fd_sysvar_cache_t const * cache, fd_wksp_t * wksp );
145 : FD_FN_PURE fd_sysvar_fees_t * fd_sysvar_cache_fees ( fd_sysvar_cache_t const * cache, fd_wksp_t * wksp );
146 : FD_FN_PURE fd_rent_t * fd_sysvar_cache_rent ( fd_sysvar_cache_t const * cache, fd_wksp_t * wksp );
147 : FD_FN_PURE fd_slot_hashes_global_t * fd_sysvar_cache_slot_hashes ( fd_sysvar_cache_t const * cache, fd_wksp_t * wksp );
148 : FD_FN_PURE fd_recent_block_hashes_global_t * fd_sysvar_cache_recent_block_hashes( fd_sysvar_cache_t const * cache, fd_wksp_t * wksp );
149 : FD_FN_PURE fd_stake_history_t * fd_sysvar_cache_stake_history ( fd_sysvar_cache_t const * cache, fd_wksp_t * wksp );
150 : FD_FN_PURE fd_sol_sysvar_last_restart_slot_t * fd_sysvar_cache_last_restart_slot ( fd_sysvar_cache_t const * cache, fd_wksp_t * wksp );
151 : FD_FN_PURE fd_slot_history_global_t * fd_sysvar_cache_slot_history ( fd_sysvar_cache_t const * cache, fd_wksp_t * wksp );
152 :
153 : /* fd_sysvar_from_instr_acct_{...} pretends to read a sysvar from an
154 : instruction account. Checks that a given instruction account has
155 : an address matching the sysvar. Returns the sysvar from the sysvar
156 : cache. On return, *err is in FD_EXECUTOR_INSTR_{SUCCESS,ERR_{...}}.
157 :
158 : Matches Agave's
159 : solana_program_runtime::sysvar_cache::get_sysvar_with_account_check
160 : https://github.com/solana-labs/solana/blob/v1.18.8/program-runtime/src/sysvar_cache.rs#L215-L314
161 :
162 : Equivalent to:
163 :
164 : fd_sysvar_FOO_t const *
165 : fd_sysvar_from_instr_acct_FOO( fd_exec_instr_ctx_t const * ctx,
166 : ulong acct_idx ) {
167 : if( FD_UNLIKELY( idx >= ctx->instr->acct_cnt ) ) {
168 : *err = FD_EXECUTOR_INSTR_ERR_NOT_ENOUGH_ACC_KEYS;
169 : return NULL;
170 : }
171 : if( ctx->instr->acct_pubkeys[ acct_idx ] != FOO_addr ) {
172 : *err = FD_EXECUTOR_INSTR_ERR_INVALID_ARG;
173 : return NULL;
174 : }
175 : FOO_t const * value = fd_sysvar_cache_FOO( ctx->slot_ctx->sysvar_cache );
176 : *err = value ? 0 : FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_SYSVAR;
177 : return value;
178 : } */
179 :
180 : fd_sol_sysvar_clock_t const * fd_sysvar_from_instr_acct_clock ( fd_exec_instr_ctx_t const * ctx, ulong acct_idx, int * err );
181 : fd_epoch_schedule_t const * fd_sysvar_from_instr_acct_epoch_schedule ( fd_exec_instr_ctx_t const * ctx, ulong acct_idx, int * err );
182 : fd_sysvar_epoch_rewards_t const * fd_sysvar_from_instr_acct_epoch_rewards ( fd_exec_instr_ctx_t const * ctx, ulong acct_idx, int * err );
183 : fd_sysvar_fees_t const * fd_sysvar_from_instr_acct_fees ( fd_exec_instr_ctx_t const * ctx, ulong acct_idx, int * err );
184 : fd_rent_t const * fd_sysvar_from_instr_acct_rent ( fd_exec_instr_ctx_t const * ctx, ulong acct_idx, int * err );
185 : fd_slot_hashes_global_t const * fd_sysvar_from_instr_acct_slot_hashes ( fd_exec_instr_ctx_t const * ctx, ulong acct_idx, int * err );
186 : fd_recent_block_hashes_global_t const * fd_sysvar_from_instr_acct_recent_block_hashes( fd_exec_instr_ctx_t const * ctx, ulong acct_idx, int * err );
187 : fd_stake_history_t const * fd_sysvar_from_instr_acct_stake_history ( fd_exec_instr_ctx_t const * ctx, ulong acct_idx, int * err );
188 : fd_sol_sysvar_last_restart_slot_t const * fd_sysvar_from_instr_acct_last_restart_slot ( fd_exec_instr_ctx_t const * ctx, ulong acct_idx, int * err );
189 :
190 : /* fd_check_sysvar_account does a check on if an instruction account index
191 : matches the expected pubkey of a specific sysvar. */
192 :
193 : int
194 : fd_check_sysvar_account( fd_exec_instr_ctx_t const * ctx,
195 : ulong insn_acc_idx,
196 : fd_pubkey_t const * expected_id );
197 :
198 : FD_PROTOTYPES_END
199 :
200 : #endif /* HEADER_fd_src_flamenco_runtime_sysvar_fd_sysvar_cache_h */
|