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 : #include "../../accdb/fd_accdb_ref.h"
26 :
27 5622 : #define FD_SYSVAR_CACHE_ENTRY_CNT 9
28 :
29 : /* fd_sysvar_cache_t is the header of a sysvar_cache object.
30 : A sysvar_cache object is position-independent and backed entirely by
31 : a single memory region. Each sysvar is stored in serialized/raw form
32 : and in a typed form. fd_sysvar_cache_desc_t points either form.
33 :
34 : It is safe to relocate a sysvar_cache object, or map it from multiple
35 : processes with different address spaces, or clone it via a shallow
36 : memcpy. */
37 :
38 : struct fd_sysvar_desc {
39 : uint flags;
40 : uint data_sz;
41 : };
42 :
43 : typedef struct fd_sysvar_desc fd_sysvar_desc_t;
44 :
45 2265 : #define FD_SYSVAR_FLAG_VALID (0x1u)
46 :
47 : struct fd_sysvar_cache {
48 : ulong magic; /* ==FD_SYSVAR_CACHE_MAGIC */
49 :
50 : fd_sysvar_desc_t desc[ FD_SYSVAR_CACHE_ENTRY_CNT ];
51 :
52 : uchar bin_clock [ FD_SYSVAR_CLOCK_BINCODE_SZ ] __attribute__((aligned(FD_SYSVAR_ALIGN_MAX)));
53 : uchar obj_clock [ FD_SYSVAR_CLOCK_FOOTPRINT ] __attribute__((aligned(FD_SYSVAR_ALIGN_MAX)));
54 : uchar bin_epoch_rewards [ FD_SYSVAR_EPOCH_REWARDS_BINCODE_SZ ] __attribute__((aligned(FD_SYSVAR_ALIGN_MAX)));
55 : uchar obj_epoch_rewards [ FD_SYSVAR_EPOCH_REWARDS_FOOTPRINT ] __attribute__((aligned(FD_SYSVAR_ALIGN_MAX)));
56 : uchar bin_epoch_schedule [ FD_SYSVAR_EPOCH_SCHEDULE_BINCODE_SZ ] __attribute__((aligned(FD_SYSVAR_ALIGN_MAX)));
57 : uchar obj_epoch_schedule [ FD_SYSVAR_EPOCH_SCHEDULE_FOOTPRINT ] __attribute__((aligned(FD_SYSVAR_ALIGN_MAX)));
58 : uchar bin_last_restart_slot [ FD_SYSVAR_LAST_RESTART_SLOT_BINCODE_SZ ] __attribute__((aligned(FD_SYSVAR_ALIGN_MAX)));
59 : uchar obj_last_restart_slot [ FD_SYSVAR_LAST_RESTART_SLOT_FOOTPRINT ] __attribute__((aligned(FD_SYSVAR_ALIGN_MAX)));
60 : uchar bin_recent_hashes [ FD_SYSVAR_RECENT_HASHES_BINCODE_SZ ] __attribute__((aligned(FD_SYSVAR_ALIGN_MAX)));
61 : uchar obj_recent_hashes [ FD_SYSVAR_RECENT_HASHES_FOOTPRINT ] __attribute__((aligned(FD_SYSVAR_ALIGN_MAX)));
62 : uchar bin_rent [ FD_SYSVAR_RENT_BINCODE_SZ ] __attribute__((aligned(FD_SYSVAR_ALIGN_MAX)));
63 : uchar obj_rent [ FD_SYSVAR_RENT_FOOTPRINT ] __attribute__((aligned(FD_SYSVAR_ALIGN_MAX)));
64 : uchar bin_slot_hashes [ FD_SYSVAR_SLOT_HASHES_BINCODE_SZ ] __attribute__((aligned(FD_SYSVAR_ALIGN_MAX)));
65 : uchar obj_slot_hashes [ FD_SYSVAR_SLOT_HASHES_FOOTPRINT ] __attribute__((aligned(FD_SYSVAR_ALIGN_MAX)));
66 : uchar bin_slot_history [ FD_SYSVAR_SLOT_HISTORY_BINCODE_SZ ] __attribute__((aligned(FD_SYSVAR_ALIGN_MAX)));
67 : uchar obj_slot_history [ FD_SYSVAR_SLOT_HISTORY_FOOTPRINT ] __attribute__((aligned(FD_SYSVAR_ALIGN_MAX)));
68 : uchar bin_stake_history [ FD_SYSVAR_STAKE_HISTORY_BINCODE_SZ ] __attribute__((aligned(FD_SYSVAR_ALIGN_MAX)));
69 : uchar obj_stake_history [ FD_SYSVAR_STAKE_HISTORY_FOOTPRINT ] __attribute__((aligned(FD_SYSVAR_ALIGN_MAX)));
70 :
71 : /* Note that two sysvars are (deliberately) missing:
72 : - The 'fees' sysvar was deprecated/demoted. It is not part of the
73 : sysvar cache in Agave.
74 : - The 'instructions' sysvar is a virtual account, not a stored
75 : account. It is never written to a database, therefore it does
76 : not make sense to cache it. */
77 : };
78 :
79 : typedef struct fd_sysvar_cache fd_sysvar_cache_t;
80 :
81 9 : #define FD_SYSVAR_clock_IDX 0
82 3 : #define FD_SYSVAR_epoch_rewards_IDX 1
83 3 : #define FD_SYSVAR_epoch_schedule_IDX 2
84 3 : #define FD_SYSVAR_last_restart_slot_IDX 3
85 : #define FD_SYSVAR_recent_hashes_IDX 4
86 18 : #define FD_SYSVAR_rent_IDX 5
87 : #define FD_SYSVAR_slot_hashes_IDX 6
88 : #define FD_SYSVAR_slot_history_IDX 7
89 : #define FD_SYSVAR_stake_history_IDX 8
90 :
91 : FD_PROTOTYPES_BEGIN
92 :
93 : /* Constructor API */
94 :
95 : /* fd_sysvar_cache_new formats a memory region allocated according to
96 : fd_sysvar_cache_{align,footprint} for use as a sysvar_cache object. */
97 :
98 : void *
99 : fd_sysvar_cache_new( void * mem );
100 :
101 : /* fd_sysvar_cache_join joins the caller to a sysvar_cache object as
102 : writable mode. fd_sysvar_cache_join_const is the read-only version. */
103 :
104 : fd_sysvar_cache_t *
105 : fd_sysvar_cache_join( void * mem );
106 :
107 : fd_sysvar_cache_t const *
108 : fd_sysvar_cache_join_const( void const * mem );
109 :
110 : /* fd_sysvar_cache_leave undoes a join to a sysvar_cache object. */
111 :
112 : void *
113 : fd_sysvar_cache_leave( fd_sysvar_cache_t * sysvar_cache );
114 :
115 : void const *
116 : fd_sysvar_cache_leave_const( fd_sysvar_cache_t const * sysvar_cache );
117 :
118 : /* fd_sysvar_cache_delete releases the sysvar cache object's backing
119 : memory region back to the caller. */
120 :
121 : void *
122 : fd_sysvar_cache_delete( void * mem );
123 :
124 : /* fd_sysvar_cache_restore rebuilds the sysvar cache from the account
125 : database. Does blocking account database queries. Returns 1 on
126 : success, or 0 on failure (logs warnings). Reasons for failure
127 : include unexpected database error or sysvar deserialize failure. */
128 :
129 : int
130 : fd_sysvar_cache_restore( fd_bank_t * bank,
131 : fd_accdb_user_t * accdb,
132 : fd_funk_txn_xid_t const * xid );
133 :
134 : /* fd_sysvar_cache_restore_fuzz is a weaker version of the above for use
135 : with solfuzz/protosol conformance tooling. This version works around
136 : bugs in that tooling that create invalid sysvars and suppresses noisy
137 : log warning. */
138 :
139 : void
140 : fd_sysvar_cache_restore_fuzz( fd_bank_t * bank,
141 : fd_accdb_user_t * accdb,
142 : fd_funk_txn_xid_t const * xid );
143 :
144 : void
145 : fd_sysvar_cache_restore_from_ref( fd_sysvar_cache_t * cache,
146 : fd_accdb_ro_t const * ro );
147 :
148 : /* Generic accessors for serialized sysvar account data. */
149 :
150 : /* fd_sysvar_cache_data_query returns a pointer to raw/serialized sysvar
151 : account data. address points to the address of the sysvar account.
152 : *psz is set to the serialized data size (or 0 on failure).
153 : The returned pointer is valid until the next API call that takes a
154 : non-const pointer to sysvar_cache. Note there are technically three
155 : outcomes (retval is the return value):
156 : - retval!=NULL && *psz!=0 sysvar is valid
157 : - retval==NULL && *psz==0 no sysvar with this address or sysvar
158 : or sysvar contains invalid data
159 : - retval!=NULL && *psz==0 sysvar is valid, but empty (impossible
160 : with current sysvars) */
161 :
162 : uchar const *
163 : fd_sysvar_cache_data_query(
164 : fd_sysvar_cache_t const * sysvar_cache,
165 : void const * address, /* 32 bytes */
166 : ulong * psz
167 : );
168 :
169 : #define FD_SYSVAR_IS_VALID( sysvar_cache, sysvar ) \
170 129 : ( ( FD_VOLATILE_CONST( sysvar_cache->desc[ FD_SYSVAR_##sysvar##_IDX ].flags ) \
171 129 : & ( FD_SYSVAR_FLAG_VALID ) ) \
172 129 : == FD_SYSVAR_FLAG_VALID )
173 :
174 : /* Accessors for small POD sysvars. These do a copy on read.
175 :
176 : fd_sysvar_clock_is_valid returns 1 if the cached sysvar is valid
177 : (read, read_nofail, join_const are then guaranteed to succeed).
178 : Returns 0 otherwise.
179 :
180 : fd_sysvar_clock_read attempts to copy sysvar data from cache into the
181 : out argument. Returns out on success, or NULL if the sysvar account
182 : does not exist or contains data that failed deserialization.
183 :
184 : fd_sysvar_clock_read_nofail returns a copy of the sysvar data. If
185 : the sysvar does not exist or failed to deserialize, aborts the app
186 : with FD_LOG_ERR.
187 :
188 : Accessors for the other sysvars in this section are analogous. */
189 :
190 : static inline int
191 12 : fd_sysvar_cache_clock_is_valid( fd_sysvar_cache_t const * sysvar_cache ) {
192 12 : return FD_SYSVAR_IS_VALID( sysvar_cache, clock );
193 12 : }
194 :
195 : fd_sol_sysvar_clock_t *
196 : fd_sysvar_cache_clock_read(
197 : fd_sysvar_cache_t const * sysvar_cache,
198 : fd_sol_sysvar_clock_t * out
199 : );
200 :
201 : /* Macro to improve FD_LOG_ERR line number accuracy */
202 :
203 : #define SIMPLE_SYSVAR_READ_NOFAIL( cache, name, typet ) \
204 3 : __extension__({ \
205 3 : typet out; \
206 3 : if( FD_UNLIKELY( !fd_sysvar_cache_##name##_read( (cache), &out ) ) )\
207 3 : FD_LOG_ERR(( "fd_sysvar_" #name "_read_nofail failed: sysvar not valid" )); \
208 3 : out; \
209 3 : })
210 :
211 : #define fd_sysvar_cache_clock_read_nofail( cache ) \
212 3 : SIMPLE_SYSVAR_READ_NOFAIL( cache, clock, fd_sol_sysvar_clock_t )
213 :
214 : static inline int
215 3 : fd_sysvar_cache_epoch_rewards_is_valid( fd_sysvar_cache_t const * sysvar_cache ) {
216 3 : return FD_SYSVAR_IS_VALID( sysvar_cache, epoch_rewards );
217 3 : }
218 :
219 : fd_sysvar_epoch_rewards_t *
220 : fd_sysvar_cache_epoch_rewards_read(
221 : fd_sysvar_cache_t const * sysvar_cache,
222 : fd_sysvar_epoch_rewards_t * out
223 : );
224 :
225 : static inline int
226 3 : fd_sysvar_cache_epoch_schedule_is_valid( fd_sysvar_cache_t const * sysvar_cache ) {
227 3 : return FD_SYSVAR_IS_VALID( sysvar_cache, epoch_schedule );
228 3 : }
229 :
230 : fd_epoch_schedule_t *
231 : fd_sysvar_cache_epoch_schedule_read(
232 : fd_sysvar_cache_t const * sysvar_cache,
233 : fd_epoch_schedule_t * out
234 : );
235 :
236 : #define fd_sysvar_cache_epoch_schedule_read_nofail( cache ) \
237 : SIMPLE_SYSVAR_READ_NOFAIL( cache, epoch_schedule, fd_epoch_schedule_t )
238 :
239 : static inline int
240 3 : fd_sysvar_cache_last_restart_slot_is_valid( fd_sysvar_cache_t const * sysvar_cache ) {
241 3 : return FD_SYSVAR_IS_VALID( sysvar_cache, last_restart_slot );
242 3 : }
243 :
244 : fd_sol_sysvar_last_restart_slot_t *
245 : fd_sysvar_cache_last_restart_slot_read(
246 : fd_sysvar_cache_t const * sysvar_cache,
247 : fd_sol_sysvar_last_restart_slot_t * out
248 : );
249 :
250 : static inline int
251 3 : fd_sysvar_cache_rent_is_valid( fd_sysvar_cache_t const * sysvar_cache ) {
252 3 : return FD_SYSVAR_IS_VALID( sysvar_cache, rent );
253 3 : }
254 :
255 : fd_rent_t *
256 : fd_sysvar_cache_rent_read(
257 : fd_sysvar_cache_t const * sysvar_cache,
258 : fd_rent_t * out
259 : );
260 :
261 : #define fd_sysvar_cache_rent_read_nofail( cache ) \
262 0 : SIMPLE_SYSVAR_READ_NOFAIL( cache, rent, fd_rent_t )
263 :
264 : /* Accessors for large sysvars. */
265 :
266 : static inline int
267 18 : fd_sysvar_cache_recent_hashes_is_valid( fd_sysvar_cache_t const * sysvar_cache ) {
268 18 : return FD_SYSVAR_IS_VALID( sysvar_cache, recent_hashes );
269 18 : }
270 :
271 : fd_block_block_hash_entry_t const * /* deque */
272 : fd_sysvar_cache_recent_hashes_join_const(
273 : fd_sysvar_cache_t const * sysvar_cache
274 : );
275 :
276 : void
277 : fd_sysvar_cache_recent_hashes_leave_const(
278 : fd_sysvar_cache_t const * sysvar_cache,
279 : fd_block_block_hash_entry_t const * hashes_deque
280 : );
281 :
282 : /* fd_sysvar_cache_slot_hashes_{join,leave}_const {attach,detach} the
283 : caller {from,to} the slot hashes deque contained in the slot hashes
284 : sysvar.
285 :
286 : The join API returns a pointer into the sysvar cache. If the sysvar
287 : account is in an invalid state (non-existent, failed to deserialize),
288 : join returns NULL. */
289 :
290 : static inline int
291 66 : fd_sysvar_cache_slot_hashes_is_valid( fd_sysvar_cache_t const * sysvar_cache ) {
292 66 : return FD_SYSVAR_IS_VALID( sysvar_cache, slot_hashes );
293 66 : }
294 :
295 : fd_slot_hash_t const *
296 : fd_sysvar_cache_slot_hashes_join_const(
297 : fd_sysvar_cache_t const * sysvar_cache
298 : );
299 :
300 : void
301 : fd_sysvar_cache_slot_hashes_leave_const(
302 : fd_sysvar_cache_t const * sysvar_cache,
303 : fd_slot_hash_t const * slot_hashes
304 : );
305 :
306 : /* fd_sysvar_cache_slot_history_{join,leave}_const {attach,detach} the
307 : caller {from,to} the "slot history" sysvar. Behavior analogous to
308 : above accessors. */
309 :
310 : static inline int
311 6 : fd_sysvar_cache_slot_history_is_valid( fd_sysvar_cache_t const * sysvar_cache ) {
312 6 : return FD_SYSVAR_IS_VALID( sysvar_cache, slot_history );
313 6 : }
314 :
315 : fd_slot_history_global_t const *
316 : fd_sysvar_cache_slot_history_join_const(
317 : fd_sysvar_cache_t const * sysvar_cache
318 : );
319 :
320 : void
321 : fd_sysvar_cache_slot_history_leave_const(
322 : fd_sysvar_cache_t const * sysvar_cache,
323 : fd_slot_history_global_t const * slot_history
324 : );
325 :
326 : /* fd_sysvar_cache_stake_history_{join,leave}_const {attach,detach} the
327 : caller {from,to} the "stake history" sysvar. Behavior analogous to
328 : above accessors. */
329 :
330 : static inline int
331 15 : fd_sysvar_cache_stake_history_is_valid( fd_sysvar_cache_t const * sysvar_cache ) {
332 15 : return FD_SYSVAR_IS_VALID( sysvar_cache, stake_history );
333 15 : }
334 :
335 : fd_stake_history_t const *
336 : fd_sysvar_cache_stake_history_join_const(
337 : fd_sysvar_cache_t const * sysvar_cache
338 : );
339 :
340 : void
341 : fd_sysvar_cache_stake_history_leave_const(
342 : fd_sysvar_cache_t const * sysvar_cache,
343 : fd_stake_history_t const * stake_history
344 : );
345 :
346 : FD_PROTOTYPES_END
347 :
348 : #endif /* HEADER_fd_src_flamenco_runtime_sysvar_fd_sysvar_cache_h */
|