Line data Source code
1 : #include "fd_acc_mgr.h"
2 : #include "fd_runtime.h"
3 : #include "../../ballet/base58/fd_base58.h"
4 : #include "context/fd_exec_epoch_ctx.h"
5 : #include "context/fd_exec_slot_ctx.h"
6 : #include "fd_rent_lists.h"
7 : #include "fd_rocksdb.h"
8 : #include "sysvar/fd_sysvar_rent.h"
9 : #include "fd_system_ids.h"
10 : #include <assert.h>
11 :
12 : fd_acc_mgr_t *
13 : fd_acc_mgr_new( void * mem,
14 3 : fd_funk_t * funk ) {
15 :
16 3 : if( FD_UNLIKELY( !mem ) ) {
17 0 : FD_LOG_WARNING(( "NULL mem" ));
18 0 : return NULL;
19 0 : }
20 :
21 3 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)mem, FD_ACC_MGR_ALIGN ) ) ) {
22 0 : FD_LOG_WARNING(( "misaligned mem" ));
23 0 : return NULL;
24 0 : }
25 :
26 3 : fd_memset( mem, 0, FD_ACC_MGR_FOOTPRINT );
27 :
28 3 : fd_acc_mgr_t * acc_mgr = fd_type_pun( mem );
29 3 : acc_mgr->funk = funk;
30 3 : return acc_mgr;
31 :
32 3 : }
33 :
34 : void *
35 3 : fd_acc_mgr_delete( fd_acc_mgr_t * acc_mgr ) {
36 :
37 3 : if( FD_UNLIKELY( !acc_mgr ) ) return NULL;
38 :
39 3 : memset( acc_mgr, 0, FD_ACC_MGR_FOOTPRINT );
40 3 : return acc_mgr;
41 3 : }
42 :
43 : void
44 : fd_acc_mgr_set_slots_per_epoch( fd_exec_slot_ctx_t * slot_ctx,
45 0 : ulong slots_per_epoch ) {
46 0 : fd_acc_mgr_t * acc_mgr = slot_ctx->acc_mgr;
47 0 : acc_mgr->slots_per_epoch = slots_per_epoch;
48 0 : acc_mgr->part_width = fd_rent_partition_width( slots_per_epoch );
49 0 : }
50 :
51 : fd_account_meta_t const *
52 : fd_acc_mgr_view_raw( fd_acc_mgr_t * acc_mgr,
53 : fd_funk_txn_t const * txn,
54 : fd_pubkey_t const * pubkey,
55 : fd_funk_rec_t const ** orec,
56 : int * opt_err,
57 39 : fd_funk_txn_t const ** txn_out ) {
58 :
59 39 : fd_funk_rec_key_t id = fd_acc_funk_key( pubkey );
60 39 : fd_funk_t * funk = acc_mgr->funk;
61 :
62 39 : fd_funk_rec_t const * rec = fd_funk_rec_query_global( funk, txn, &id, txn_out );
63 :
64 39 : if( FD_UNLIKELY( !rec || !!( rec->flags & FD_FUNK_REC_FLAG_ERASE ) ) ) {
65 9 : fd_int_store_if( !!opt_err, opt_err, FD_ACC_MGR_ERR_UNKNOWN_ACCOUNT );
66 9 : return NULL;
67 9 : }
68 30 : if( NULL != orec )
69 15 : *orec = rec;
70 :
71 30 : void const * raw = fd_funk_val( rec, fd_funk_wksp(funk) );
72 : // TODO/FIXME: this check causes issues with some metadata writes
73 :
74 30 : fd_account_meta_t const * metadata = fd_type_pun_const( raw );
75 30 : if( FD_UNLIKELY( metadata->magic != FD_ACCOUNT_META_MAGIC ) ) {
76 0 : fd_int_store_if( !!opt_err, opt_err, FD_ACC_MGR_ERR_WRONG_MAGIC );
77 0 : return NULL;
78 0 : }
79 :
80 30 : return metadata;
81 30 : }
82 :
83 : int
84 : fd_acc_mgr_view( fd_acc_mgr_t * acc_mgr,
85 : fd_funk_txn_t const * txn,
86 : fd_pubkey_t const * pubkey,
87 0 : fd_txn_account_t * account) {
88 : /* TODO: re-add this check after consulting on why this builtin program check.
89 : Is it the case that the */
90 : // if( fd_pubkey_is_builtin_program( pubkey )
91 : // || memcmp(pubkey->uc, fd_solana_compute_budget_program_id.uc, sizeof(fd_pubkey_t))==0 ) {
92 : // txn = NULL;
93 : // }
94 0 : int err = FD_ACC_MGR_SUCCESS;
95 0 : fd_account_meta_t const * meta = fd_acc_mgr_view_raw( acc_mgr, txn, pubkey, &account->const_rec, &err, NULL );
96 0 : if( FD_UNLIKELY( !fd_acc_exists( meta ) ) ) {
97 0 : if( FD_UNLIKELY( err!=FD_ACC_MGR_SUCCESS ) ) {
98 0 : return err;
99 0 : }
100 0 : return FD_ACC_MGR_ERR_UNKNOWN_ACCOUNT;
101 0 : }
102 :
103 0 : if( FD_UNLIKELY( FD_TXN_ACCOUNT_MAGIC != account->magic ) ) {
104 0 : FD_LOG_ERR(( "bad magic for borrowed account - acc: %s, expected: %016lx, got: %016lx", FD_BASE58_ENC_32_ALLOCA( pubkey->uc ), FD_TXN_ACCOUNT_MAGIC, account->magic ));
105 0 : }
106 :
107 0 : fd_memcpy(account->pubkey, pubkey, sizeof(fd_pubkey_t));
108 :
109 0 : account->orig_rec = account->const_rec;
110 0 : account->orig_meta = account->const_meta = meta;
111 0 : account->orig_data = account->const_data = (uchar const *)meta + meta->hlen;
112 :
113 0 : if( ULONG_MAX == account->starting_dlen )
114 0 : account->starting_dlen = meta->dlen;
115 :
116 0 : if( ULONG_MAX == account->starting_lamports )
117 0 : account->starting_lamports = meta->info.lamports;
118 :
119 0 : return FD_ACC_MGR_SUCCESS;
120 0 : }
121 :
122 : fd_account_meta_t *
123 : fd_acc_mgr_modify_raw( fd_acc_mgr_t * acc_mgr,
124 : fd_funk_txn_t * txn,
125 : fd_pubkey_t const * pubkey,
126 : int do_create,
127 : ulong min_data_sz,
128 : fd_funk_rec_t const * opt_con_rec,
129 : fd_funk_rec_t ** opt_out_rec,
130 24 : int * opt_err ) {
131 :
132 24 : fd_funk_t * funk = acc_mgr->funk;
133 :
134 24 : fd_funk_rec_key_t id = fd_acc_funk_key( pubkey );
135 :
136 : //#ifdef VLOG
137 : // ulong rec_cnt = 0;
138 : // for( fd_funk_rec_t const * rec = fd_funk_txn_first_rec( funk, txn );
139 : // NULL != rec;
140 : // rec = fd_funk_txn_next_rec( funk, rec ) ) {
141 : //
142 : // if( !fd_funk_key_is_acc( rec->pair.key ) ) continue;
143 : //
144 : // FD_LOG_DEBUG(( "fd_acc_mgr_modify_raw: %s create: %s rec_cnt: %d", FD_BASE58_ENC_32_ALLOCA( rec->pair.key->uc ), do_create ? "true" : "false", rec_cnt));
145 : //
146 : // rec_cnt++;
147 : // }
148 : //
149 : // FD_LOG_DEBUG(( "fd_acc_mgr_modify_raw: %s create: %s", FD_BASE58_ENC_32_ALLOCA( pubkey->uc ), do_create ? "true" : "false"));
150 : //#endif
151 :
152 24 : int funk_err = FD_FUNK_SUCCESS;
153 24 : fd_funk_rec_t * rec = fd_funk_rec_write_prepare( funk, txn, &id, sizeof(fd_account_meta_t)+min_data_sz, do_create, opt_con_rec, &funk_err );
154 :
155 24 : if( FD_UNLIKELY( !rec ) ) {
156 0 : if( FD_LIKELY( funk_err==FD_FUNK_ERR_KEY ) ) {
157 0 : fd_int_store_if( !!opt_err, opt_err, FD_ACC_MGR_ERR_UNKNOWN_ACCOUNT );
158 0 : return NULL;
159 0 : }
160 : /* Irrecoverable funky internal error [[noreturn]] */
161 0 : FD_LOG_ERR(( "fd_funk_rec_write_prepare(%s) failed (%i-%s)", FD_BASE58_ENC_32_ALLOCA( pubkey->key ), funk_err, fd_funk_strerror( funk_err ) ));
162 0 : }
163 :
164 24 : if (NULL != opt_out_rec)
165 24 : *opt_out_rec = rec;
166 :
167 24 : fd_account_meta_t * ret = fd_funk_val( rec, fd_funk_wksp( funk ) );
168 :
169 24 : if( do_create && ret->magic==0UL ) {
170 15 : fd_account_meta_init( ret );
171 15 : }
172 :
173 24 : if( ret->magic != FD_ACCOUNT_META_MAGIC ) {
174 0 : fd_int_store_if( !!opt_err, opt_err, FD_ACC_MGR_ERR_WRONG_MAGIC );
175 0 : return NULL;
176 0 : }
177 :
178 24 : return ret;
179 24 : }
180 :
181 : int
182 : fd_acc_mgr_modify( fd_acc_mgr_t * acc_mgr,
183 : fd_funk_txn_t * txn,
184 : fd_pubkey_t const * pubkey,
185 : int do_create,
186 : ulong min_data_sz,
187 18 : fd_txn_account_t * account ) {
188 18 : int err = FD_ACC_MGR_SUCCESS;
189 :
190 18 : fd_account_meta_t * meta = fd_acc_mgr_modify_raw( acc_mgr, txn, pubkey, do_create, min_data_sz, account->const_rec, &account->rec, &err );
191 18 : if( FD_UNLIKELY( !meta ) ) return err;
192 :
193 18 : assert( account->magic == FD_TXN_ACCOUNT_MAGIC );
194 :
195 0 : fd_memcpy(account->pubkey, pubkey, sizeof(fd_pubkey_t));
196 :
197 18 : if( FD_UNLIKELY( meta->magic != FD_ACCOUNT_META_MAGIC ) )
198 0 : return FD_ACC_MGR_ERR_WRONG_MAGIC;
199 :
200 : #ifdef VLOG
201 : FD_LOG_DEBUG(( "fd_acc_mgr_modify: %s create: %s lamports: %ld owner: %s executable: %s, rent_epoch: %ld, data_len: %ld",
202 : FD_BASE58_ENC_32_ALLOCA( pubkey->uc ),
203 : do_create ? "true" : "false",
204 : meta->info.lamports,
205 : FD_BASE58_ENC_32_ALLOCA( meta->info.owner ),
206 : meta->info.executable ? "true" : "false",
207 : meta->info.rent_epoch, meta->dlen ));
208 : #endif
209 :
210 18 : account->orig_rec = account->const_rec = account->rec;
211 18 : account->orig_meta = account->const_meta = account->meta = meta;
212 18 : account->orig_data = account->const_data = account->data = (uchar *)meta + meta->hlen;
213 :
214 18 : if( ULONG_MAX == account->starting_dlen )
215 18 : account->starting_dlen = meta->dlen;
216 :
217 18 : if( ULONG_MAX == account->starting_lamports )
218 18 : account->starting_lamports = meta->info.lamports;
219 :
220 18 : return FD_ACC_MGR_SUCCESS;
221 18 : }
222 :
223 : FD_FN_CONST char const *
224 0 : fd_acc_mgr_strerror( int err ) {
225 0 : switch( err ) {
226 0 : case FD_ACC_MGR_SUCCESS:
227 0 : return "success";
228 0 : case FD_ACC_MGR_ERR_UNKNOWN_ACCOUNT:
229 0 : return "unknown account";
230 0 : case FD_ACC_MGR_ERR_WRITE_FAILED:
231 0 : return "write failed";
232 0 : case FD_ACC_MGR_ERR_READ_FAILED:
233 0 : return "read failed";
234 0 : case FD_ACC_MGR_ERR_WRONG_MAGIC:
235 0 : return "wrong magic";
236 0 : default:
237 0 : return "unknown";
238 0 : }
239 0 : }
240 :
241 : int
242 : fd_acc_mgr_save( fd_acc_mgr_t * acc_mgr,
243 0 : fd_txn_account_t * account ) {
244 0 : if( account->meta == NULL || account->rec == NULL ) {
245 : // The meta is NULL so the account is not writable.
246 0 : FD_LOG_DEBUG(( "fd_acc_mgr_save: account is not writable: %s", FD_BASE58_ENC_32_ALLOCA( account->pubkey ) ));
247 0 : return FD_ACC_MGR_SUCCESS;
248 0 : }
249 :
250 0 : fd_wksp_t * wksp = fd_funk_wksp( acc_mgr->funk );
251 0 : ulong reclen = sizeof(fd_account_meta_t)+account->const_meta->dlen;
252 0 : uchar * raw = fd_funk_val( account->rec, wksp );
253 0 : fd_memcpy( raw, account->meta, reclen );
254 :
255 0 : return FD_ACC_MGR_SUCCESS;
256 0 : }
257 :
258 : int
259 : fd_acc_mgr_save_non_tpool( fd_acc_mgr_t * acc_mgr,
260 : fd_funk_txn_t * txn,
261 0 : fd_txn_account_t * account ) {
262 :
263 0 : fd_funk_start_write( acc_mgr->funk );
264 0 : fd_funk_rec_key_t key = fd_acc_funk_key( account->pubkey );
265 0 : fd_funk_t * funk = acc_mgr->funk;
266 0 : fd_funk_rec_t * rec = (fd_funk_rec_t *)fd_funk_rec_query( funk, txn, &key );
267 0 : if( rec == NULL ) {
268 0 : int err;
269 0 : rec = (fd_funk_rec_t *)fd_funk_rec_insert( funk, txn, &key, &err );
270 0 : if( rec == NULL ) FD_LOG_ERR(( "unable to insert a new record, error %d", err ));
271 0 : }
272 0 : account->rec = rec;
273 0 : ulong reclen = sizeof(fd_account_meta_t)+account->const_meta->dlen;
274 0 : fd_wksp_t * wksp = fd_funk_wksp( acc_mgr->funk );
275 0 : int err;
276 0 : if( fd_funk_val_truncate( account->rec, reclen, fd_funk_alloc( acc_mgr->funk, wksp ), wksp, &err ) == NULL ) {
277 0 : FD_LOG_ERR(( "unable to allocate account value, err %d", err ));
278 0 : }
279 0 : err = fd_acc_mgr_save( acc_mgr, account );
280 0 : fd_funk_end_write( acc_mgr->funk );
281 0 : return err;
282 0 : }
283 :
284 : void
285 0 : fd_acc_mgr_lock( fd_acc_mgr_t * acc_mgr ) {
286 0 : FD_TEST( !acc_mgr->is_locked );
287 0 : acc_mgr->is_locked = 1;
288 0 : }
289 :
290 : void
291 0 : fd_acc_mgr_unlock( fd_acc_mgr_t * acc_mgr ) {
292 0 : FD_TEST( acc_mgr->is_locked );
293 0 : acc_mgr->is_locked = 0;
294 0 : }
295 :
296 : struct fd_acc_mgr_save_task_args {
297 : fd_acc_mgr_t * acc_mgr;
298 : };
299 : typedef struct fd_acc_mgr_save_task_args fd_acc_mgr_save_task_args_t;
300 :
301 : struct fd_acc_mgr_save_task_info {
302 : fd_txn_account_t * * accounts;
303 : ulong accounts_cnt;
304 : int result;
305 : };
306 : typedef struct fd_acc_mgr_save_task_info fd_acc_mgr_save_task_info_t;
307 :
308 : static void
309 : fd_acc_mgr_save_task( void *tpool,
310 : ulong t0 FD_PARAM_UNUSED, ulong t1 FD_PARAM_UNUSED,
311 : void *args,
312 : void *reduce FD_PARAM_UNUSED, ulong stride FD_PARAM_UNUSED,
313 : ulong l0 FD_PARAM_UNUSED, ulong l1 FD_PARAM_UNUSED,
314 : ulong m0, ulong m1 FD_PARAM_UNUSED,
315 0 : ulong n0 FD_PARAM_UNUSED, ulong n1 FD_PARAM_UNUSED ) {
316 0 : fd_acc_mgr_save_task_args_t * task_args = (fd_acc_mgr_save_task_args_t *)args;
317 0 : fd_acc_mgr_save_task_info_t * task_info = (fd_acc_mgr_save_task_info_t *)tpool + m0;
318 :
319 0 : for( ulong i = 0; i < task_info->accounts_cnt; i++ ) {
320 0 : int err = fd_acc_mgr_save(task_args->acc_mgr,task_info->accounts[i] );
321 0 : if( FD_UNLIKELY( err != FD_ACC_MGR_SUCCESS ) ) {
322 0 : task_info->result = err;
323 0 : return;
324 0 : }
325 0 : }
326 0 : task_info->result = FD_ACC_MGR_SUCCESS;
327 0 : }
328 :
329 : int
330 : fd_acc_mgr_save_many_tpool( fd_acc_mgr_t * acc_mgr,
331 : fd_funk_txn_t * txn,
332 : fd_txn_account_t * * accounts,
333 : ulong accounts_cnt,
334 : fd_tpool_t * tpool,
335 0 : fd_spad_t * runtime_spad ) {
336 :
337 0 : FD_SPAD_FRAME_BEGIN( runtime_spad ) {
338 :
339 0 : fd_funk_t * funk = acc_mgr->funk;
340 0 : fd_wksp_t * wksp = fd_funk_wksp( funk );
341 0 : fd_funk_rec_t * rec_map = fd_funk_rec_map( funk, wksp );
342 :
343 0 : ulong batch_cnt = fd_ulong_min(
344 0 : fd_funk_rec_map_private_list_cnt( fd_funk_rec_map_key_max( rec_map ) ),
345 0 : fd_ulong_pow2_up( fd_tpool_worker_cnt( tpool ) )
346 0 : );
347 0 : ulong batch_mask = (batch_cnt - 1UL);
348 :
349 0 : ulong * batch_szs = fd_spad_alloc( runtime_spad, 8UL, batch_cnt * sizeof(ulong) );
350 0 : fd_memset( batch_szs, 0, batch_cnt * sizeof(ulong) );
351 :
352 : /* Compute the batch sizes */
353 0 : for( ulong i = 0; i < accounts_cnt; i++ ) {
354 0 : ulong batch_idx = i & batch_mask;
355 0 : batch_szs[batch_idx]++;
356 0 : }
357 :
358 0 : fd_txn_account_t * * task_accounts = fd_spad_alloc( runtime_spad, 8UL, accounts_cnt * sizeof(fd_txn_account_t *) );
359 0 : fd_acc_mgr_save_task_info_t * task_infos = fd_spad_alloc( runtime_spad, 8UL, batch_cnt * sizeof(fd_acc_mgr_save_task_info_t) );
360 0 : fd_txn_account_t * * task_accounts_cursor = task_accounts;
361 :
362 : /* Construct the batches */
363 0 : for( ulong i = 0; i < batch_cnt; i++ ) {
364 0 : ulong batch_sz = batch_szs[i];
365 0 : fd_acc_mgr_save_task_info_t * task_info = &task_infos[i];
366 :
367 0 : task_info->accounts_cnt = 0;
368 0 : task_info->accounts = task_accounts_cursor;
369 0 : task_info->result = 0;
370 :
371 0 : task_accounts_cursor += batch_sz;
372 0 : }
373 :
374 0 : fd_funk_start_write( funk );
375 :
376 0 : for( ulong i = 0; i < accounts_cnt; i++ ) {
377 0 : fd_txn_account_t * account = accounts[i];
378 :
379 0 : ulong batch_idx = i & batch_mask;
380 0 : fd_acc_mgr_save_task_info_t * task_info = &task_infos[batch_idx];
381 0 : task_info->accounts[task_info->accounts_cnt++] = account;
382 0 : fd_funk_rec_key_t key = fd_acc_funk_key( account->pubkey );
383 0 : fd_funk_rec_t * rec = (fd_funk_rec_t *)fd_funk_rec_query( funk, txn, &key );
384 0 : if( rec == NULL ) {
385 0 : int err;
386 0 : rec = (fd_funk_rec_t *)fd_funk_rec_insert( funk, txn, &key, &err );
387 0 : if( rec == NULL ) FD_LOG_ERR(( "unable to insert a new record, error %d", err ));
388 0 : }
389 0 : account->rec = rec;
390 :
391 : /* This check is to prevent a seg fault in the case where an account with
392 : null data tries to get saved. This notably happens if firedancer is
393 : attemping to execute a bad block. This should NEVER happen in the case
394 : of a proper replay. */
395 0 : if( FD_UNLIKELY( !account->const_meta ) ) {
396 0 : FD_LOG_ERR(( "An account likely does not exist. This block could be invalid." ));
397 0 : }
398 :
399 0 : ulong reclen = sizeof(fd_account_meta_t)+account->const_meta->dlen;
400 0 : int err;
401 0 : if( FD_UNLIKELY( NULL == fd_funk_val_truncate( account->rec,
402 0 : reclen,
403 0 : fd_funk_alloc( acc_mgr->funk, wksp ),
404 0 : wksp,
405 0 : &err ) ) ) {
406 0 : FD_LOG_ERR(( "unable to allocate account value, err %d", err ));
407 0 : }
408 0 : }
409 :
410 0 : fd_acc_mgr_save_task_args_t task_args = {
411 0 : .acc_mgr = acc_mgr
412 0 : };
413 :
414 : /* Save accounts in a thread pool */
415 :
416 0 : fd_tpool_exec_all_rrobin( tpool, 0, fd_tpool_worker_cnt( tpool ), fd_acc_mgr_save_task,
417 0 : task_infos, &task_args, NULL, 1, 0, batch_cnt );
418 :
419 0 : fd_funk_end_write( funk );
420 :
421 : /* Check results */
422 0 : for( ulong i = 0; i < batch_cnt; i++ ) {
423 0 : fd_acc_mgr_save_task_info_t * task_info = &task_infos[i];
424 0 : if( task_info->result != FD_ACC_MGR_SUCCESS ) {
425 0 : return task_info->result;
426 0 : }
427 0 : }
428 :
429 0 : return FD_ACC_MGR_SUCCESS;
430 :
431 0 : } FD_SPAD_FRAME_END;
432 0 : }
|