Line data Source code
1 : #include "fd_accdb_impl_v1.h"
2 : #include "fd_accdb_sync.h"
3 :
4 : FD_STATIC_ASSERT( alignof(fd_accdb_user_v1_t)<=alignof(fd_accdb_user_t), layout );
5 : FD_STATIC_ASSERT( sizeof (fd_accdb_user_v1_t)<=sizeof(fd_accdb_user_t), layout );
6 :
7 : static int
8 : fd_accdb_has_xid( fd_accdb_user_v1_t const * accdb,
9 1251 : fd_funk_txn_xid_t const * rec_xid ) {
10 1251 : ulong const fork_depth = accdb->fork_depth;
11 1482 : for( ulong i=0UL; i<fork_depth; i++ ) {
12 1482 : if( fd_funk_txn_xid_eq( &accdb->fork[i], rec_xid ) ) return 1;
13 1482 : }
14 0 : return 0;
15 1251 : }
16 :
17 : static int
18 : fd_accdb_search_chain( fd_accdb_user_v1_t const * accdb,
19 : ulong chain_idx,
20 : fd_funk_rec_key_t const * key,
21 9459 : fd_funk_rec_t ** out_rec ) {
22 9459 : *out_rec = NULL;
23 :
24 9459 : fd_funk_rec_map_shmem_t const * shmap = accdb->funk->rec_map->map;
25 9459 : fd_funk_rec_map_shmem_private_chain_t const * chain_tbl = fd_funk_rec_map_shmem_private_chain_const( shmap, 0UL );
26 9459 : fd_funk_rec_map_shmem_private_chain_t const * chain = chain_tbl + chain_idx;
27 9459 : fd_funk_rec_t * rec_tbl = accdb->funk->rec_pool->ele;
28 9459 : ulong rec_max = fd_funk_rec_pool_ele_max( accdb->funk->rec_pool );
29 9459 : ulong ver_cnt = FD_VOLATILE_CONST( chain->ver_cnt );
30 :
31 : /* Start a speculative transaction for the chain containing revisions
32 : of the account key we are looking for. */
33 9459 : ulong cnt = fd_funk_rec_map_private_vcnt_cnt( ver_cnt );
34 9459 : if( FD_UNLIKELY( fd_funk_rec_map_private_vcnt_ver( ver_cnt )&1 ) ) {
35 0 : return FD_MAP_ERR_AGAIN; /* chain is locked */
36 0 : }
37 9459 : FD_COMPILER_MFENCE();
38 9459 : uint ele_idx = chain->head_cidx;
39 :
40 : /* Walk the map chain, bail at the first entry
41 : (Each chain is sorted newest-to-oldest) */
42 9459 : fd_funk_rec_t * best = NULL;
43 12570 : for( ulong i=0UL; i<cnt; i++, ele_idx=rec_tbl[ ele_idx ].map_next ) {
44 4362 : fd_funk_rec_t * rec = &rec_tbl[ ele_idx ];
45 :
46 : /* Skip over unrelated records (hash collision) */
47 4362 : if( FD_UNLIKELY( !fd_funk_rec_key_eq( rec->pair.key, key ) ) ) continue;
48 :
49 : /* Confirm that record is part of the current fork
50 : FIXME this has bad performance / pointer-chasing */
51 1251 : if( FD_UNLIKELY( !fd_accdb_has_xid( accdb, rec->pair.xid ) ) ) continue;
52 :
53 1251 : if( FD_UNLIKELY( rec->map_next==ele_idx ) ) {
54 0 : FD_LOG_CRIT(( "fd_accdb_search_chain detected cycle" ));
55 0 : }
56 1251 : if( rec->map_next > rec_max ) {
57 1113 : if( FD_UNLIKELY( !fd_funk_rec_map_private_idx_is_null( rec->map_next ) ) ) {
58 0 : FD_LOG_CRIT(( "fd_accdb_search_chain detected memory corruption: rec->map_next %u is out of bounds (rec_max %lu)",
59 0 : rec->map_next, rec_max ));
60 0 : }
61 1113 : }
62 1251 : best = rec;
63 1251 : break;
64 1251 : }
65 :
66 : /* Retry if we were overrun */
67 9459 : if( FD_UNLIKELY( FD_VOLATILE_CONST( chain->ver_cnt )!=ver_cnt ) ) {
68 0 : return FD_MAP_ERR_AGAIN;
69 0 : }
70 :
71 9459 : *out_rec = best;
72 9459 : return FD_MAP_SUCCESS;
73 9459 : }
74 :
75 : void
76 : fd_accdb_load_fork_slow( fd_accdb_user_v1_t * accdb,
77 8919 : fd_funk_txn_xid_t const * xid ) {
78 8919 : fd_funk_txn_xid_t next_xid = *xid;
79 8919 : fd_funk_txn_t const * tip = NULL;
80 :
81 : /* Walk transaction graph, recovering from overruns on-the-fly */
82 8919 : accdb->fork_depth = 0UL;
83 :
84 8919 : ulong txn_max = fd_funk_txn_pool_ele_max( accdb->funk->txn_pool );
85 8919 : ulong i;
86 8925 : for( i=0UL; i<FD_ACCDB_DEPTH_MAX; i++ ) {
87 8925 : fd_funk_txn_map_query_t query[1];
88 8925 : fd_funk_txn_t const * candidate;
89 8925 : fd_funk_txn_xid_t found_xid;
90 8925 : ulong parent_idx;
91 8925 : fd_funk_txn_xid_t parent_xid;
92 8925 : retry:
93 : /* Speculatively look up transaction from map */
94 8925 : for(;;) {
95 8925 : int query_err = fd_funk_txn_map_query_try( accdb->funk->txn_map, &next_xid, NULL, query, 0 );
96 8925 : if( FD_UNLIKELY( query_err==FD_MAP_ERR_AGAIN ) ) {
97 : /* FIXME random backoff */
98 0 : FD_SPIN_PAUSE();
99 0 : continue;
100 0 : }
101 8925 : if( query_err==FD_MAP_ERR_KEY ) goto done;
102 8916 : if( FD_UNLIKELY( query_err!=FD_MAP_SUCCESS ) ) {
103 0 : FD_LOG_CRIT(( "fd_funk_txn_map_query_try failed: %i-%s", query_err, fd_map_strerror( query_err ) ));
104 0 : }
105 8916 : break;
106 8916 : }
107 :
108 : /* Lookup parent transaction while recovering from overruns
109 : FIXME This would be a lot easier if transactions specified
110 : parent by XID instead of by pointer ... */
111 8916 : candidate = fd_funk_txn_map_query_ele_const( query );
112 8916 : FD_COMPILER_MFENCE();
113 8916 : do {
114 8916 : found_xid = FD_VOLATILE_CONST( candidate->xid );
115 8916 : parent_idx = fd_funk_txn_idx( FD_VOLATILE_CONST( candidate->parent_cidx ) );
116 8916 : if( fd_funk_txn_idx_is_null( parent_idx ) ) break;
117 6 : if( FD_UNLIKELY( parent_idx>=txn_max ) ) FD_LOG_CRIT(( "corrupt txn parent idx %lu", parent_idx ));
118 :
119 6 : FD_COMPILER_MFENCE();
120 6 : fd_funk_txn_t const * parent = &accdb->funk->txn_pool->ele[ parent_idx ];
121 6 : parent_xid = FD_VOLATILE_CONST( parent->xid );
122 6 : FD_COMPILER_MFENCE();
123 :
124 6 : parent_idx = fd_funk_txn_idx( FD_VOLATILE_CONST( candidate->parent_cidx ) );
125 6 : if( fd_funk_txn_idx_is_null( parent_idx ) ) break;
126 6 : if( FD_UNLIKELY( parent_idx>=txn_max ) ) FD_LOG_CRIT(( "corrupt txn parent idx %lu", parent_idx ));
127 6 : } while(0);
128 8916 : FD_COMPILER_MFENCE();
129 :
130 : /* Verify speculative loads by ensuring txn still exists in map */
131 8916 : if( FD_UNLIKELY( fd_funk_txn_map_query_test( query )!=FD_MAP_SUCCESS ) ) {
132 0 : FD_SPIN_PAUSE();
133 0 : goto retry;
134 0 : }
135 :
136 8916 : if( FD_UNLIKELY( !fd_funk_txn_xid_eq( &found_xid, &next_xid ) ) ) {
137 0 : FD_LOG_CRIT(( "fd_accdb_load_fork_slow detected memory corruption: expected xid %lu:%lu at %p, found %lu:%lu",
138 0 : next_xid.ul[0], next_xid.ul[1],
139 0 : (void *)candidate,
140 0 : found_xid.ul[0], found_xid.ul[1] ));
141 0 : }
142 :
143 8916 : if( !tip ) tip = candidate; /* remember head of fork */
144 8916 : accdb->fork[ i ] = next_xid;
145 8916 : if( fd_funk_txn_idx_is_null( parent_idx ) ) {
146 : /* Reached root */
147 8910 : i++;
148 8910 : break;
149 8910 : }
150 6 : next_xid = parent_xid;
151 6 : }
152 :
153 8919 : done:
154 8919 : accdb->fork_depth = i;
155 8919 : if( FD_UNLIKELY( accdb->fork_depth==FD_ACCDB_DEPTH_MAX ) ) {
156 0 : FD_LOG_CRIT(( "Account database fork depth exceeded max of %lu", FD_ACCDB_DEPTH_MAX ));
157 0 : }
158 :
159 : /* FIXME crash if fork depth greater than cache depth */
160 8919 : if( accdb->fork_depth < FD_ACCDB_DEPTH_MAX ) {
161 8919 : fd_funk_txn_xid_set_root( &accdb->fork[ accdb->fork_depth++ ] );
162 8919 : }
163 :
164 : /* Remember head of fork */
165 8919 : if( tip ) {
166 8910 : accdb->tip_txn_idx = (ulong)( tip - accdb->funk->txn_pool->ele );
167 8910 : fd_funk_txn_state_assert( tip, FD_FUNK_TXN_STATE_ACTIVE );
168 8910 : } else {
169 9 : accdb->tip_txn_idx = ULONG_MAX; /* XID is rooted */
170 9 : }
171 8919 : }
172 :
173 : static inline void
174 : fd_accdb_load_fork( fd_accdb_user_v1_t * accdb,
175 9459 : fd_funk_txn_xid_t const * xid ) {
176 : /* Skip if already on the correct fork */
177 9459 : if( FD_LIKELY( (!!accdb->fork_depth) & (!!fd_funk_txn_xid_eq( &accdb->fork[ 0 ], xid ) ) ) ) return;
178 8919 : if( FD_UNLIKELY( accdb->base.rw_active ) ) {
179 0 : FD_LOG_CRIT(( "Invariant violation: all active account references of an accdb_user must be accessed through the same XID (active XID %lu:%lu, requested XID %lu:%lu)",
180 0 : accdb->fork[0].ul[0], accdb->fork[0].ul[1],
181 0 : xid ->ul[0], xid ->ul[1] ));
182 0 : }
183 8919 : fd_accdb_load_fork_slow( accdb, xid ); /* switch fork */
184 8919 : }
185 :
186 : fd_accdb_peek_t *
187 : fd_accdb_peek_funk( fd_accdb_user_v1_t * accdb,
188 : fd_accdb_peek_t * peek,
189 : fd_funk_txn_xid_t const * xid,
190 9459 : void const * address ) {
191 9459 : fd_funk_t const * funk = accdb->funk;
192 9459 : fd_funk_rec_key_t key[1]; memcpy( key->uc, address, 32UL );
193 :
194 : /* Hash key to chain */
195 9459 : fd_funk_xid_key_pair_t pair[1];
196 9459 : fd_funk_txn_xid_copy( pair->xid, xid );
197 9459 : fd_funk_rec_key_copy( pair->key, key );
198 9459 : fd_funk_rec_map_t const * rec_map = funk->rec_map;
199 9459 : ulong hash = fd_funk_rec_map_key_hash( pair, rec_map->map->seed );
200 9459 : ulong chain_idx = (hash & (rec_map->map->chain_cnt-1UL) );
201 :
202 : /* Traverse chain for candidate */
203 9459 : fd_funk_rec_t * rec = NULL;
204 9459 : for(;;) {
205 9459 : int err = fd_accdb_search_chain( accdb, chain_idx, key, &rec );
206 9459 : if( FD_LIKELY( err==FD_MAP_SUCCESS ) ) break;
207 0 : FD_SPIN_PAUSE();
208 : /* FIXME backoff */
209 0 : }
210 9459 : if( !rec ) return NULL;
211 :
212 1251 : *peek = (fd_accdb_peek_t) {
213 1251 : .acc = {{
214 1251 : .rec = rec,
215 1251 : .meta = fd_funk_val( rec, funk->wksp )
216 1251 : }},
217 1251 : .spec = {{
218 1251 : .key = *key,
219 1251 : .keyp = rec->pair.key
220 1251 : }}
221 1251 : };
222 1251 : return peek;
223 9459 : }
224 :
225 : fd_accdb_peek_t *
226 : fd_accdb_user_v1_peek( fd_accdb_user_t * accdb,
227 : fd_accdb_peek_t * peek,
228 : fd_funk_txn_xid_t const * xid,
229 0 : void const * address ) {
230 0 : if( FD_UNLIKELY( !accdb ) ) FD_LOG_CRIT(( "NULL accdb" ));
231 0 : fd_accdb_user_v1_t * v1 = (fd_accdb_user_v1_t *)accdb;
232 0 : if( FD_UNLIKELY( !v1->funk->shmem ) ) FD_LOG_CRIT(( "NULL funk shmem" ));
233 0 : fd_accdb_load_fork( v1, xid );
234 0 : if( !fd_accdb_peek_funk( v1, peek, xid, address ) ) return NULL;
235 0 : if( FD_UNLIKELY( !peek->acc->meta->lamports ) ) return NULL;
236 0 : return peek;
237 0 : }
238 :
239 : static void
240 : fd_accdb_copy_account( fd_account_meta_t * out_meta,
241 : void * out_data,
242 72 : fd_accdb_ro_t const * acc ) {
243 72 : memset( out_meta, 0, sizeof(fd_account_meta_t) );
244 72 : out_meta->lamports = fd_accdb_ref_lamports( acc );
245 72 : if( FD_LIKELY( out_meta->lamports ) ) {
246 72 : memcpy( out_meta->owner, fd_accdb_ref_owner( acc ), 32UL );
247 72 : out_meta->executable = !!fd_accdb_ref_exec_bit( acc );
248 72 : out_meta->dlen = (uint)fd_accdb_ref_data_sz( acc );
249 72 : fd_memcpy( out_data, fd_accdb_ref_data_const( acc ), out_meta->dlen );
250 72 : }
251 72 : }
252 :
253 : static void
254 : fd_accdb_copy_truncated( fd_account_meta_t * out_meta,
255 3 : fd_accdb_ro_t const * acc ) {
256 3 : memset( out_meta, 0, sizeof(fd_account_meta_t) );
257 3 : out_meta->lamports = fd_accdb_ref_lamports( acc );
258 3 : if( FD_LIKELY( out_meta->lamports ) ) {
259 0 : memcpy( out_meta->owner, fd_accdb_ref_owner( acc ), 32UL );
260 0 : out_meta->executable = !!fd_accdb_ref_exec_bit( acc );
261 0 : out_meta->dlen = 0;
262 0 : }
263 3 : }
264 :
265 : /* fd_accdb_prep_create preps a writable handle for a newly created
266 : account. */
267 :
268 : static fd_accdb_rw_t *
269 : fd_accdb_prep_create( fd_accdb_rw_t * rw,
270 : fd_accdb_user_v1_t * accdb,
271 : fd_funk_txn_xid_t const * xid,
272 : void const * address,
273 : void * val,
274 : ulong val_sz,
275 144 : ulong val_max ) {
276 144 : FD_CRIT( val_sz >=sizeof(fd_account_meta_t), "invalid val_sz" );
277 144 : FD_CRIT( val_max>=sizeof(fd_account_meta_t), "invalid val_max" );
278 144 : FD_CRIT( val_sz<=val_max, "invalid val_max" );
279 :
280 144 : fd_funk_rec_t * rec = fd_funk_rec_pool_acquire( accdb->funk->rec_pool, NULL, 1, NULL );
281 144 : if( FD_UNLIKELY( !rec ) ) FD_LOG_CRIT(( "Failed to modify account: DB record pool is out of memory" ));
282 144 : accdb->base.created_cnt++;
283 :
284 144 : memset( rec, 0, sizeof(fd_funk_rec_t) );
285 144 : rec->val_gaddr = fd_wksp_gaddr_fast( accdb->funk->wksp, val );
286 144 : rec->val_sz = (uint)( fd_ulong_min( val_sz, FD_FUNK_REC_VAL_MAX ) & FD_FUNK_REC_VAL_MAX );
287 144 : rec->val_max = (uint)( fd_ulong_min( val_max, FD_FUNK_REC_VAL_MAX ) & FD_FUNK_REC_VAL_MAX );
288 144 : memcpy( rec->pair.key->uc, address, 32UL );
289 144 : fd_funk_txn_xid_copy( rec->pair.xid, xid );
290 144 : rec->tag = 0;
291 144 : rec->prev_idx = FD_FUNK_REC_IDX_NULL;
292 144 : rec->next_idx = FD_FUNK_REC_IDX_NULL;
293 :
294 144 : fd_account_meta_t * meta = val;
295 144 : meta->slot = xid->ul[0];
296 :
297 144 : accdb->base.rw_active++;
298 144 : *rw = (fd_accdb_rw_t) {
299 144 : .rec = rec,
300 144 : .meta = meta,
301 144 : .published = 0
302 144 : };
303 144 : return rw;
304 144 : }
305 :
306 : /* fd_accdb_prep_inplace preps a writable handle for a mutable record. */
307 :
308 : static fd_accdb_rw_t *
309 : fd_accdb_prep_inplace( fd_accdb_rw_t * rw,
310 : fd_accdb_user_v1_t * accdb,
311 456 : fd_funk_rec_t * rec ) {
312 : /* Take the opportunity to run some validation checks */
313 456 : if( FD_UNLIKELY( !rec->val_gaddr ) ) {
314 0 : FD_LOG_CRIT(( "Failed to prepare in-place account write: rec %p is not allocated", (void *)rec ));
315 0 : }
316 :
317 456 : accdb->base.rw_active++;
318 456 : *rw = (fd_accdb_rw_t) {
319 456 : .rec = rec,
320 456 : .meta = fd_funk_val( rec, accdb->funk->wksp ),
321 456 : .published = 1
322 456 : };
323 456 : if( FD_UNLIKELY( !rw->meta->lamports ) ) {
324 3 : memset( rw->meta, 0, sizeof(fd_account_meta_t) );
325 3 : }
326 456 : return rw;
327 456 : }
328 :
329 : void
330 8889 : fd_accdb_user_v1_fini( fd_accdb_user_t * accdb ) {
331 8889 : fd_accdb_user_v1_t * user = (fd_accdb_user_v1_t *)accdb;
332 :
333 8889 : if( FD_UNLIKELY( !fd_funk_leave( user->funk, NULL ) ) ) FD_LOG_CRIT(( "fd_funk_leave failed" ));
334 8889 : }
335 :
336 : fd_accdb_ro_t *
337 : fd_accdb_user_v1_open_ro( fd_accdb_user_t * accdb,
338 : fd_accdb_ro_t * ro,
339 : fd_funk_txn_xid_t const * xid,
340 8856 : void const * address ) {
341 8856 : fd_accdb_user_v1_t * v1 = (fd_accdb_user_v1_t *)accdb;
342 8856 : fd_accdb_load_fork( v1, xid );
343 :
344 8856 : fd_accdb_peek_t peek[1];
345 8856 : if( !fd_accdb_peek_funk( v1, peek, xid, address ) ) return NULL;
346 720 : if( FD_UNLIKELY( !peek->acc->meta->lamports ) ) return NULL;
347 :
348 720 : v1->base.ro_active++;
349 720 : *ro = *peek->acc;
350 720 : return ro;
351 720 : }
352 :
353 : void
354 : fd_accdb_user_v1_close_ro( fd_accdb_user_t * accdb,
355 27 : fd_accdb_ro_t * ro ) {
356 27 : fd_accdb_user_v1_t * v1 = (fd_accdb_user_v1_t *)accdb;
357 :
358 27 : v1->base.ro_active--;
359 27 : (void)ro;
360 27 : }
361 :
362 : fd_accdb_rw_t *
363 : fd_accdb_user_v1_open_rw( fd_accdb_user_t * accdb,
364 : fd_accdb_rw_t * rw,
365 : fd_funk_txn_xid_t const * xid,
366 : void const * address,
367 : ulong data_max,
368 603 : int flags ) {
369 603 : fd_accdb_user_v1_t * v1 = (fd_accdb_user_v1_t *)accdb;
370 :
371 603 : int const flag_create = !!( flags & FD_ACCDB_FLAG_CREATE );
372 603 : int const flag_truncate = !!( flags & FD_ACCDB_FLAG_TRUNCATE );
373 603 : if( FD_UNLIKELY( flags & ~(FD_ACCDB_FLAG_CREATE|FD_ACCDB_FLAG_TRUNCATE) ) ) {
374 0 : FD_LOG_CRIT(( "invalid flags for open_rw: %#02x", (uint)flags ));
375 0 : }
376 :
377 : /* Pivot to different fork */
378 603 : fd_accdb_load_fork( v1, xid );
379 603 : ulong txn_idx = v1->tip_txn_idx;
380 603 : if( FD_UNLIKELY( txn_idx==ULONG_MAX ) ) {
381 0 : FD_LOG_CRIT(( "fd_accdb_user_v1_open_rw failed: XID %lu:%lu is rooted", xid->ul[0], xid->ul[1] ));
382 0 : }
383 603 : if( FD_UNLIKELY( txn_idx >= fd_funk_txn_pool_ele_max( v1->funk->txn_pool ) ) ) {
384 0 : FD_LOG_CRIT(( "memory corruption detected: invalid txn_idx %lu (max %lu)",
385 0 : txn_idx, fd_funk_txn_pool_ele_max( v1->funk->txn_pool ) ));
386 0 : }
387 603 : fd_funk_txn_t * txn = &v1->funk->txn_pool->ele[ txn_idx ];
388 603 : if( FD_UNLIKELY( !fd_funk_txn_xid_eq( &txn->xid, xid ) ) ) {
389 0 : FD_LOG_CRIT(( "Failed to modify account: data race detected on fork node (expected XID %lu:%lu, found %lu:%lu)",
390 0 : xid->ul[0], xid->ul[1],
391 0 : txn->xid.ul[0], txn->xid.ul[1] ));
392 0 : }
393 603 : if( FD_UNLIKELY( fd_funk_txn_is_frozen( txn ) ) ) {
394 0 : FD_LOG_CRIT(( "Failed to modify account: XID %lu:%lu has children/is frozen", xid->ul[0], xid->ul[1] ));
395 0 : }
396 :
397 : /* Query old record value */
398 :
399 603 : fd_accdb_peek_t peek[1];
400 603 : if( FD_UNLIKELY( !fd_accdb_peek_funk( v1, peek, xid, address ) ) ) {
401 :
402 : /* Record not found */
403 72 : if( !flag_create ) return NULL;
404 69 : ulong val_sz_min = sizeof(fd_account_meta_t)+data_max;
405 69 : ulong val_max = 0UL;
406 69 : void * val = fd_alloc_malloc_at_least( v1->funk->alloc, 16UL, val_sz_min, &val_max );
407 69 : if( FD_UNLIKELY( !val ) ) {
408 0 : FD_LOG_CRIT(( "Failed to modify account: out of memory allocating %lu bytes", data_max ));
409 0 : }
410 69 : memset( val, 0, sizeof(fd_account_meta_t) );
411 69 : return fd_accdb_prep_create( rw, v1, xid, address, val, sizeof(fd_account_meta_t), val_max );
412 :
413 531 : } else if( fd_funk_txn_xid_eq( peek->acc->rec->pair.xid, xid ) ) {
414 :
415 : /* Mutable record found, modify in-place */
416 456 : fd_funk_rec_t * rec = (void *)( peek->acc->ref->rec_laddr );
417 456 : ulong acc_orig_sz = fd_accdb_ref_data_sz( peek->acc );
418 456 : ulong val_sz_min = sizeof(fd_account_meta_t)+fd_ulong_max( data_max, acc_orig_sz );
419 456 : void * val = fd_funk_val_truncate( rec, v1->funk->alloc, v1->funk->wksp, 16UL, val_sz_min, NULL );
420 456 : if( FD_UNLIKELY( !val ) ) {
421 0 : FD_LOG_CRIT(( "Failed to modify account: out of memory allocating %lu bytes", acc_orig_sz ));
422 0 : }
423 456 : fd_accdb_prep_inplace( rw, v1, rec );
424 456 : if( flag_truncate ) {
425 3 : rw->rec->val_sz = sizeof(fd_account_meta_t);
426 3 : rw->meta->dlen = 0;
427 3 : }
428 456 : return rw;
429 :
430 456 : } else {
431 :
432 : /* Frozen record found, copy out to new object */
433 75 : ulong acc_orig_sz = fd_accdb_ref_data_sz( peek->acc );
434 75 : ulong val_sz_min = sizeof(fd_account_meta_t)+fd_ulong_max( data_max, acc_orig_sz );
435 75 : ulong val_sz = flag_truncate ? sizeof(fd_account_meta_t) : peek->acc->rec->val_sz;
436 75 : ulong val_max = 0UL;
437 75 : void * val = fd_alloc_malloc_at_least( v1->funk->alloc, 16UL, val_sz_min, &val_max );
438 75 : if( FD_UNLIKELY( !val ) ) {
439 0 : FD_LOG_CRIT(( "Failed to modify account: out of memory allocating %lu bytes", acc_orig_sz ));
440 0 : }
441 :
442 75 : fd_account_meta_t * meta = val;
443 75 : uchar * data = (uchar *)( meta+1 );
444 75 : ulong data_max_actual = val_max - sizeof(fd_account_meta_t);
445 75 : if( flag_truncate ) fd_accdb_copy_truncated( meta, peek->acc );
446 72 : else fd_accdb_copy_account ( meta, data, peek->acc );
447 75 : if( acc_orig_sz<data_max_actual ) {
448 : /* Zero out trailing data */
449 75 : uchar * tail = data +acc_orig_sz;
450 75 : ulong tail_sz = data_max_actual-acc_orig_sz;
451 75 : fd_memset( tail, 0, tail_sz );
452 75 : }
453 75 : if( FD_UNLIKELY( !fd_accdb_peek_test( peek ) ) ) {
454 0 : FD_LOG_CRIT(( "Failed to modify account: data race detected, account was removed while being read" ));
455 0 : }
456 :
457 75 : return fd_accdb_prep_create( rw, v1, xid, address, val, val_sz, val_max );
458 :
459 75 : }
460 603 : }
461 :
462 : void
463 : fd_accdb_user_v1_close_rw( fd_accdb_user_t * accdb,
464 15 : fd_accdb_rw_t * write ) {
465 15 : if( FD_UNLIKELY( !accdb ) ) FD_LOG_CRIT(( "NULL accdb" ));
466 15 : fd_accdb_user_v1_t * v1 = (fd_accdb_user_v1_t *)accdb;
467 :
468 15 : if( FD_UNLIKELY( !v1->base.rw_active ) ) {
469 0 : FD_LOG_CRIT(( "Failed to modify account: ref count underflow" ));
470 0 : }
471 :
472 15 : if( !write->published ) {
473 12 : if( FD_UNLIKELY( v1->tip_txn_idx==ULONG_MAX ) ) {
474 0 : FD_LOG_CRIT(( "accdb_user corrupt: not joined to a transaction" ));
475 0 : }
476 12 : fd_funk_txn_t * txn = v1->funk->txn_pool->ele + v1->tip_txn_idx;
477 12 : fd_funk_rec_prepare_t prepare = {
478 12 : .rec = write->rec,
479 12 : .rec_head_idx = &txn->rec_head_idx,
480 12 : .rec_tail_idx = &txn->rec_tail_idx
481 12 : };
482 12 : fd_funk_rec_publish( v1->funk, &prepare );
483 12 : }
484 :
485 15 : v1->base.rw_active--;
486 15 : }
487 :
488 : fd_accdb_user_vt_t const fd_accdb_user_v1_vt = {
489 : .fini = fd_accdb_user_v1_fini,
490 : .peek = fd_accdb_user_v1_peek,
491 : .open_ro = fd_accdb_user_v1_open_ro,
492 : .close_ro = fd_accdb_user_v1_close_ro,
493 : .open_rw = fd_accdb_user_v1_open_rw,
494 : .close_rw = fd_accdb_user_v1_close_rw
495 : };
496 :
497 : fd_accdb_user_t *
498 : fd_accdb_user_v1_init( fd_accdb_user_t * accdb,
499 8892 : void * shfunk ) {
500 8892 : fd_accdb_user_v1_t * ljoin = (fd_accdb_user_v1_t *)accdb;
501 :
502 8892 : if( FD_UNLIKELY( !ljoin ) ) {
503 0 : FD_LOG_WARNING(( "NULL ljoin" ));
504 0 : return NULL;
505 0 : }
506 8892 : if( FD_UNLIKELY( !shfunk ) ) {
507 0 : FD_LOG_WARNING(( "NULL shfunk" ));
508 0 : return NULL;
509 0 : }
510 :
511 8892 : memset( ljoin, 0, sizeof(fd_accdb_user_v1_t) );
512 8892 : if( FD_UNLIKELY( !fd_funk_join( ljoin->funk, shfunk ) ) ) {
513 0 : FD_LOG_CRIT(( "fd_funk_join failed" ));
514 0 : }
515 :
516 8892 : accdb->base.accdb_type = FD_ACCDB_TYPE_V1;
517 8892 : accdb->base.vt = &fd_accdb_user_v1_vt;
518 8892 : return accdb;
519 8892 : }
520 :
521 : fd_funk_t *
522 2967 : fd_accdb_user_v1_funk( fd_accdb_user_t * accdb ) {
523 2967 : fd_accdb_user_v1_t * v1 = (fd_accdb_user_v1_t *)accdb;
524 2967 : uint accdb_type = accdb->base.accdb_type;
525 2967 : if( FD_UNLIKELY( accdb_type!=FD_ACCDB_TYPE_V1 && accdb_type!=FD_ACCDB_TYPE_V2 ) ) {
526 0 : FD_LOG_CRIT(( "fd_accdb_user_v1_funk called on non-v1 accdb_user (type %u)", accdb->base.accdb_type ));
527 0 : }
528 2967 : return v1->funk;
529 2967 : }
|