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