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 "../../accdb/fd_accdb.h"
25 :
26 91437 : #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 125931 : #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 bin_epoch_rewards [ FD_SYSVAR_EPOCH_REWARDS_BINCODE_SZ ] __attribute__((aligned(FD_SYSVAR_ALIGN_MAX)));
53 : uchar bin_epoch_schedule [ FD_SYSVAR_EPOCH_SCHEDULE_BINCODE_SZ ] __attribute__((aligned(FD_SYSVAR_ALIGN_MAX)));
54 : uchar bin_last_restart_slot [ FD_SYSVAR_LAST_RESTART_SLOT_BINCODE_SZ ] __attribute__((aligned(FD_SYSVAR_ALIGN_MAX)));
55 : uchar bin_recent_hashes [ FD_SYSVAR_RECENT_HASHES_BINCODE_SZ ] __attribute__((aligned(FD_SYSVAR_ALIGN_MAX)));
56 : uchar bin_rent [ FD_SYSVAR_RENT_BINCODE_SZ ] __attribute__((aligned(FD_SYSVAR_ALIGN_MAX)));
57 : uchar bin_slot_hashes [ FD_SYSVAR_SLOT_HASHES_BINCODE_SZ ] __attribute__((aligned(FD_SYSVAR_ALIGN_MAX)));
58 : uchar bin_slot_history [ FD_SYSVAR_SLOT_HISTORY_BINCODE_SZ ] __attribute__((aligned(FD_SYSVAR_ALIGN_MAX)));
59 : uchar bin_stake_history [ FD_SYSVAR_STAKE_HISTORY_BINCODE_SZ ] __attribute__((aligned(FD_SYSVAR_ALIGN_MAX)));
60 :
61 : /* Note that two sysvars are (deliberately) missing:
62 : - The 'fees' sysvar was deprecated/demoted. It is not part of the
63 : sysvar cache in Agave.
64 : - The 'instructions' sysvar is a virtual account, not a stored
65 : account. It is never written to a database, therefore it does
66 : not make sense to cache it. */
67 : };
68 :
69 : typedef struct fd_sysvar_cache fd_sysvar_cache_t;
70 :
71 21 : #define FD_SYSVAR_clock_IDX 0
72 6 : #define FD_SYSVAR_epoch_rewards_IDX 1
73 6 : #define FD_SYSVAR_epoch_schedule_IDX 2
74 3 : #define FD_SYSVAR_last_restart_slot_IDX 3
75 9 : #define FD_SYSVAR_recent_hashes_IDX 4
76 15 : #define FD_SYSVAR_rent_IDX 5
77 132 : #define FD_SYSVAR_slot_hashes_IDX 6
78 : #define FD_SYSVAR_slot_history_IDX 7
79 996 : #define FD_SYSVAR_stake_history_IDX 8
80 :
81 : FD_PROTOTYPES_BEGIN
82 :
83 : /* Constructor API */
84 :
85 : /* fd_sysvar_cache_new formats a memory region allocated according to
86 : fd_sysvar_cache_{align,footprint} for use as a sysvar_cache object. */
87 :
88 : void *
89 : fd_sysvar_cache_new( void * mem );
90 :
91 : /* fd_sysvar_cache_join joins the caller to a sysvar_cache object as
92 : writable mode. fd_sysvar_cache_join_const is the read-only version. */
93 :
94 : fd_sysvar_cache_t *
95 : fd_sysvar_cache_join( void * mem );
96 :
97 : fd_sysvar_cache_t const *
98 : fd_sysvar_cache_join_const( void const * mem );
99 :
100 : /* fd_sysvar_cache_leave undoes a join to a sysvar_cache object. */
101 :
102 : void *
103 : fd_sysvar_cache_leave( fd_sysvar_cache_t * sysvar_cache );
104 :
105 : void const *
106 : fd_sysvar_cache_leave_const( fd_sysvar_cache_t const * sysvar_cache );
107 :
108 : /* fd_sysvar_cache_delete releases the sysvar cache object's backing
109 : memory region back to the caller. */
110 :
111 : void *
112 : fd_sysvar_cache_delete( void * mem );
113 :
114 : /* fd_sysvar_cache_restore rebuilds the sysvar cache from the account
115 : database. Does blocking account database queries. Returns 1 on
116 : success, or 0 on failure (logs warnings). Reasons for failure
117 : include unexpected database error or sysvar deserialize failure. */
118 :
119 : int
120 : fd_sysvar_cache_restore( fd_bank_t * bank,
121 : fd_accdb_t * accdb );
122 :
123 : /* fd_sysvar_cache_restore_fuzz is a weaker version of the above for use
124 : with solfuzz/protosol conformance tooling. This version works around
125 : bugs in that tooling that create invalid sysvars and suppresses noisy
126 : log warning. */
127 :
128 : void
129 : fd_sysvar_cache_restore_fuzz( fd_bank_t * bank,
130 : fd_accdb_t * accdb );
131 :
132 : /* fd_sysvar_cache_restore_one restores a single sysvar cache entry from
133 : raw account fields (as opposed to a full fd_acc_t). If pubkey is not
134 : a sysvar or lamports is zero, this is a no-op. */
135 :
136 : void
137 : fd_sysvar_cache_restore_one( fd_sysvar_cache_t * cache,
138 : fd_pubkey_t const * pubkey,
139 : ulong lamports,
140 : uchar const * acc_data,
141 : ulong acc_data_len );
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 93 : ( ( FD_VOLATILE_CONST( sysvar_cache->desc[ FD_SYSVAR_##sysvar##_IDX ].flags ) \
166 93 : & ( FD_SYSVAR_FLAG_VALID ) ) \
167 93 : == 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 9 : fd_sysvar_cache_epoch_rewards_is_valid( fd_sysvar_cache_t const * sysvar_cache ) {
211 9 : return FD_SYSVAR_IS_VALID( sysvar_cache, epoch_rewards );
212 9 : }
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 9 : fd_sysvar_cache_epoch_schedule_is_valid( fd_sysvar_cache_t const * sysvar_cache ) {
222 9 : return FD_SYSVAR_IS_VALID( sysvar_cache, epoch_schedule );
223 9 : }
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 : 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 : ulong const *
240 : fd_sysvar_cache_last_restart_slot_read( fd_sysvar_cache_t const * sysvar_cache );
241 :
242 : static inline int
243 3 : fd_sysvar_cache_rent_is_valid( fd_sysvar_cache_t const * sysvar_cache ) {
244 3 : return FD_SYSVAR_IS_VALID( sysvar_cache, rent );
245 3 : }
246 :
247 : fd_rent_t *
248 : fd_sysvar_cache_rent_read(
249 : fd_sysvar_cache_t const * sysvar_cache,
250 : fd_rent_t * out
251 : );
252 :
253 : #define fd_sysvar_cache_rent_read_nofail( cache ) \
254 0 : SIMPLE_SYSVAR_READ_NOFAIL( cache, rent, fd_rent_t )
255 :
256 : /* Accessors for large sysvars. */
257 :
258 : static inline int
259 27 : fd_sysvar_cache_recent_hashes_is_valid( fd_sysvar_cache_t const * sysvar_cache ) {
260 27 : return FD_SYSVAR_IS_VALID( sysvar_cache, recent_hashes );
261 27 : }
262 :
263 : /* fd_sysvar_cache_recent_hashes_is_empty returns 0 if there is at least
264 : one valid blockhash queue entry in the 'recent blockhashes' sysvar,
265 : 1 otherwise (invalid sysvar or empty queue). */
266 :
267 : int
268 : fd_sysvar_cache_recent_hashes_is_empty( fd_sysvar_cache_t const * sysvar_cache );
269 :
270 : static inline int
271 6 : fd_sysvar_cache_slot_hashes_is_valid( fd_sysvar_cache_t const * sysvar_cache ) {
272 6 : return FD_SYSVAR_IS_VALID( sysvar_cache, slot_hashes );
273 6 : }
274 :
275 : /* fd_sysvar_cache_slot_history_{join,leave}_const {attach,detach} the
276 : caller {from,to} the "slot history" sysvar. Behavior analogous to
277 : above accessors. */
278 :
279 : static inline int
280 12 : fd_sysvar_cache_slot_history_is_valid( fd_sysvar_cache_t const * sysvar_cache ) {
281 12 : return FD_SYSVAR_IS_VALID( sysvar_cache, slot_history );
282 12 : }
283 :
284 : static inline int
285 12 : fd_sysvar_cache_stake_history_is_valid( fd_sysvar_cache_t const * sysvar_cache ) {
286 12 : return FD_SYSVAR_IS_VALID( sysvar_cache, stake_history );
287 12 : }
288 :
289 : /* View convenience wrappers. These combine fd_sysvar_cache_data_query
290 : with the corresponding fd_sysvar_*_view function. Returns view on
291 : success or NULL if the sysvar is invalid. */
292 :
293 : fd_stake_history_t *
294 : fd_sysvar_cache_stake_history_view( fd_sysvar_cache_t const * cache,
295 : fd_stake_history_t * view );
296 :
297 : fd_slot_hashes_t *
298 : fd_sysvar_cache_slot_hashes_view( fd_sysvar_cache_t const * cache,
299 : fd_slot_hashes_t * view );
300 :
301 : FD_PROTOTYPES_END
302 :
303 : #endif /* HEADER_fd_src_flamenco_runtime_sysvar_fd_sysvar_cache_h */
|