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