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