Line data Source code
1 : #ifndef HEADER_fd_src_flamenco_runtime_fd_acc_mgr_h
2 : #define HEADER_fd_src_flamenco_runtime_fd_acc_mgr_h
3 :
4 : /* fd_acc_mgr provides APIs for the Solana account database. */
5 :
6 : #include "../fd_flamenco_base.h"
7 : #include "../../ballet/txn/fd_txn.h"
8 : #include "../../funk/fd_funk.h"
9 : #include "fd_borrowed_account.h"
10 :
11 : /* FD_ACC_MGR_{SUCCESS,ERR{...}} are fd_acc_mgr_t specific error codes.
12 : To be stored in an int. */
13 :
14 10432341 : #define FD_ACC_MGR_SUCCESS (0)
15 8192913 : #define FD_ACC_MGR_ERR_UNKNOWN_ACCOUNT (-1)
16 0 : #define FD_ACC_MGR_ERR_WRITE_FAILED (-2)
17 0 : #define FD_ACC_MGR_ERR_READ_FAILED (-3)
18 0 : #define FD_ACC_MGR_ERR_WRONG_MAGIC (-4)
19 :
20 : /* FD_ACC_SZ_MAX is the hardcoded size limit of a Solana account. */
21 :
22 1817598 : #define FD_ACC_SZ_MAX (10UL<<20) /* 10MiB */
23 :
24 : /* FD_ACC_TOT_SZ_MAX is the size limit of a Solana account in the firedancer
25 : client. This means that it includes the max size of the account (10MiB)
26 : and the associated metadata. */
27 :
28 999810 : #define FD_ACC_TOT_SZ_MAX (FD_ACC_SZ_MAX + FD_ACCOUNT_META_FOOTPRINT)
29 :
30 : /* fd_acc_mgr_t translates between the runtime account DB abstraction
31 : and the actual funk database. Also manages rent collection.
32 : fd_acc_mgr_t cannot be relocated to another address space.
33 :
34 : ### Translation
35 :
36 : Each runtime account is backed by a funk record. However, not all
37 : funk records contain an account. Funk records may temporarily hold
38 : "deleted accounts".
39 :
40 : The memory layout of the acc_mgr funk record data is
41 : (fd_account_meta_t, padding, account data). */
42 :
43 : struct __attribute__((aligned(16UL))) fd_acc_mgr {
44 : fd_funk_t * funk;
45 :
46 : ulong slots_per_epoch; /* see epoch schedule. do not update directly */
47 :
48 : /* part_width is the width of rent partition. Each partition is a
49 : contiguous sub-range of [0,2^256) where each element is an
50 : account address. */
51 :
52 : ulong part_width;
53 :
54 : /* skip_rent_rewrites is a feature flag controlling rent collection
55 : behavior during eager rent collection passes. */
56 :
57 : uchar skip_rent_rewrites : 1;
58 :
59 : uint is_locked;
60 : };
61 :
62 : /* FD_ACC_MGR_{ALIGN,FOOTPRINT} specify the parameters for the memory
63 : region backing an fd_acc_mgr_t. */
64 :
65 407862 : #define FD_ACC_MGR_ALIGN (alignof(fd_acc_mgr_t))
66 1223586 : #define FD_ACC_MGR_FOOTPRINT ( sizeof(fd_acc_mgr_t))
67 :
68 : FD_PROTOTYPES_BEGIN
69 :
70 : /* Management API *****************************************************/
71 :
72 : /* fd_acc_mgr_new formats a memory region suitable to hold an
73 : fd_acc_mgr_t. Binds newly created object to global and returns
74 : cast. */
75 :
76 : fd_acc_mgr_t *
77 : fd_acc_mgr_new( void * mem,
78 : fd_funk_t * funk );
79 :
80 : /* fd_acc_mgr_delete releases the memory region used by an fd_acc_mgr_t
81 : and returns it to the caller. */
82 :
83 : void *
84 : fd_acc_mgr_delete( fd_acc_mgr_t * acc_mgr );
85 :
86 : /* Funk key handling **************************************************/
87 :
88 : /* fd_acc_funk_key returns a fd_funk database key given an account
89 : address. */
90 :
91 : FD_FN_PURE static inline fd_funk_rec_key_t
92 7087644 : fd_acc_funk_key( fd_pubkey_t const * pubkey ) {
93 7087644 : fd_funk_rec_key_t key = {0};
94 7087644 : fd_memcpy( key.c, pubkey, sizeof(fd_pubkey_t) );
95 7087644 : key.c[ FD_FUNK_REC_KEY_FOOTPRINT - 1 ] = FD_FUNK_KEY_TYPE_ACC;
96 7087644 : return key;
97 7087644 : }
98 :
99 : /* fd_funk_key_is_acc returns 1 if given fd_funk key is an account
100 : managed by fd_acc_mgr_t, and 0 otherwise. */
101 :
102 : FD_FN_PURE static inline int
103 729702 : fd_funk_key_is_acc( fd_funk_rec_key_t const * id ) {
104 729702 : return id->c[ FD_FUNK_REC_KEY_FOOTPRINT - 1 ] == FD_FUNK_KEY_TYPE_ACC;
105 729702 : }
106 :
107 : /* fd_funk_key_to_acc reinterprets a funk rec key as an account address.
108 : Safe assuming fd_funk_key_is_acc( id )==1. */
109 :
110 : FD_FN_CONST static inline fd_pubkey_t const *
111 0 : fd_funk_key_to_acc( fd_funk_rec_key_t const * id ) {
112 0 : return (fd_pubkey_t const *)fd_type_pun_const( id->c );
113 0 : }
114 :
115 : /* Account Access API *************************************************/
116 :
117 : static inline void
118 809571 : fd_account_meta_init( fd_account_meta_t * m ) {
119 809571 : fd_memset( m, 0, sizeof(fd_account_meta_t) );
120 809571 : m->magic = FD_ACCOUNT_META_MAGIC;
121 809571 : m->hlen = sizeof(fd_account_meta_t);
122 809571 : }
123 :
124 : /* fd_acc_exists checks if the account in a funk record exists or was
125 : deleted. Handles NULL input safely. Returns 0 if the account was
126 : deleted (zero lamports, empty data, zero owner). Otherwise, returns
127 : 1. */
128 :
129 : static inline int
130 4889934 : fd_acc_exists( fd_account_meta_t const * m ) {
131 :
132 4889934 : if( !m ) return 0;
133 :
134 1174488 : # if FD_HAS_AVX
135 1174488 : wl_t o = wl_ldu( m->info.owner );
136 1174488 : int has_owner = !_mm256_testz_si256( o, o );
137 : # else
138 : int has_owner = 0;
139 : for( ulong i=0UL; i<32UL; i++ )
140 : has_owner |= m->info.owner[i];
141 : has_owner = !!has_owner;
142 : # endif
143 :
144 1174488 : return ( ( m->info.lamports > 0 ) |
145 1174488 : ( m->dlen > 0 ) |
146 1174488 : ( has_owner ) );
147 :
148 4889934 : }
149 :
150 : /* fd_acc_mgr_view_raw requests a read-only handle to account data.
151 : acc_mgr is the global account manager object. txn is the database
152 : transaction to query. pubkey is the account key to query.
153 :
154 : On success:
155 : - loads the account data into in-memory cache
156 : - returns a pointer to it in the caller's local address space
157 : - if out_rec!=NULL, sets *out_rec to a pointer to the funk rec.
158 : This handle is suitable as opt_con_rec for fd_acc_mgr_modify_raw.
159 : - notably, leaves *opt_err untouched, even if opt_err!=NULL
160 :
161 : First byte of returned pointer is first byte of fd_account_meta_t.
162 : To find data region of account, add (fd_account_meta_t)->hlen.
163 :
164 : Lifetime of returned fd_funk_rec_t and account record pointers ends
165 : when user calls modify_data for same account, or tranasction ends.
166 :
167 : On failure, returns NULL, and sets *opt_err if opt_err!=NULL.
168 : Reasons for error include
169 : - account not found
170 : - internal database or user error (out of memory, attempting to view
171 : record which has an active modify_data handle, etc.)
172 :
173 : It is always wrong to cast return value to a non-const pointer.
174 : Instead, use fd_acc_mgr_modify_raw to acquire a mutable handle.
175 :
176 : if txn_out is supplied (non-null), the txn the key was found in
177 : is returned. If *txn_out == NULL, the key was found in the root
178 : context */
179 :
180 : fd_account_meta_t const *
181 : fd_acc_mgr_view_raw( fd_acc_mgr_t * acc_mgr,
182 : fd_funk_txn_t const * txn,
183 : fd_pubkey_t const * pubkey,
184 : fd_funk_rec_t const ** opt_out_rec,
185 : int * opt_err,
186 : fd_funk_txn_t const ** txn_out );
187 :
188 : int
189 : fd_acc_mgr_view( fd_acc_mgr_t * acc_mgr,
190 : fd_funk_txn_t const * txn,
191 : fd_pubkey_t const * pubkey,
192 : fd_borrowed_account_t * account );
193 :
194 : /* fd_acc_mgr_modify_raw requests a writable handle to an account.
195 : Follows interface of fd_acc_mgr_modify_raw with the following
196 : changes:
197 :
198 : - do_create controls behavior if account does not exist. If set to
199 : 0, returns error. If set to 1, creates account with given size
200 : and zero-initializes metadata. Caller must initialize metadata of
201 : returned handle in this case.
202 : - min_data_sz is the minimum writable data size that the caller will
203 : accept. This parameter will never shrink an existing account. If
204 : do_create, specifies the new account's size. Otherwise, increases
205 : record size if necessary.
206 : - When resizing or creating an account, the caller should also always
207 : set the account meta's size field. This is not done automatically.
208 : - If caller already has a read-only handle to the requested account,
209 : opt_con_rec can be used to skip query by pubkey.
210 : - In most cases, account is copied to "dirty cache".
211 :
212 : On success:
213 : - If opt_out_rec!=NULL, sets *opt_out_rec to a pointer to writable
214 : funk rec. Suitable as rec parameter to fd_acc_mgr_commit_raw.
215 : - Returns pointer to mutable account metadata and data analogous to
216 : fd_acc_mgr_view_raw.
217 : - IMPORTANT: Return value may point to the same memory region as a
218 : previous calls to fd_acc_mgr_view_raw or fd_acc_mgr_modify_raw do,
219 : for the same funk rec (account/txn pair). fd_acc_mgr only promises
220 : that account handles requested for different funk txns will not
221 : alias. Generally, for each funk txn, the user should only ever
222 : access the latest handle returned by view/modify.
223 :
224 : Caller must eventually commit funk record. During replay, this is
225 : done automatically by slot freeze. */
226 :
227 : fd_account_meta_t *
228 : fd_acc_mgr_modify_raw( fd_acc_mgr_t * acc_mgr,
229 : fd_funk_txn_t * txn,
230 : fd_pubkey_t const * pubkey,
231 : int do_create,
232 : ulong min_data_sz,
233 : fd_funk_rec_t const * opt_con_rec,
234 : fd_funk_rec_t ** opt_out_rec,
235 : int * opt_err );
236 :
237 : int
238 : fd_acc_mgr_modify( fd_acc_mgr_t * acc_mgr,
239 : fd_funk_txn_t * txn,
240 : fd_pubkey_t const * pubkey,
241 : int do_create,
242 : ulong min_data_sz,
243 : fd_borrowed_account_t * account );
244 :
245 : int
246 : fd_acc_mgr_save( fd_acc_mgr_t * acc_mgr,
247 : fd_borrowed_account_t * account );
248 :
249 : /* This version of save is for old code written before tpool integration */
250 :
251 : int
252 : fd_acc_mgr_save_non_tpool( fd_acc_mgr_t * acc_mgr,
253 : fd_funk_txn_t * txn,
254 : fd_borrowed_account_t * account );
255 :
256 : int
257 : fd_acc_mgr_save_many_tpool( fd_acc_mgr_t * acc_mgr,
258 : fd_funk_txn_t * txn,
259 : fd_borrowed_account_t ** accounts,
260 : ulong accounts_cnt,
261 : fd_tpool_t * tpool );
262 :
263 : void
264 : fd_acc_mgr_lock( fd_acc_mgr_t * acc_mgr );
265 :
266 : void
267 : fd_acc_mgr_unlock( fd_acc_mgr_t * acc_mgr );
268 :
269 : /* fd_acc_mgr_set_slots_per_epoch updates the slots_per_epoch setting
270 : and rebalances rent partitions. No-op unless 'skip_rent_rewrites'
271 : feature is activated or 'slots_per_epoch' changes. */
272 :
273 : void
274 : fd_acc_mgr_set_slots_per_epoch( fd_exec_slot_ctx_t * slot_ctx,
275 : ulong slots_per_epoch );
276 :
277 : /* fd_acc_mgr_strerror converts an fd_acc_mgr error code into a human
278 : readable cstr. The lifetime of the returned pointer is infinite and
279 : the call itself is thread safe. The returned pointer is always to a
280 : non-NULL cstr. */
281 :
282 : FD_FN_CONST char const *
283 : fd_acc_mgr_strerror( int err );
284 :
285 : FD_PROTOTYPES_END
286 :
287 : #endif /* HEADER_fd_src_flamenco_runtime_fd_acc_mgr_h */
|