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