LCOV - code coverage report
Current view: top level - flamenco/accdb - fd_accdb_impl_v1.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 283 364 77.7 %
Date: 2025-12-28 05:17:03 Functions: 16 17 94.1 %

          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 : }

Generated by: LCOV version 1.14