LCOV - code coverage report
Current view: top level - flamenco/runtime - fd_txn_account.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 169 329 51.4 %
Date: 2025-12-03 04:50:34 Functions: 21 37 56.8 %

          Line data    Source code
       1             : #include "fd_txn_account.h"
       2             : #include "fd_runtime.h"
       3             : #include "../accdb/fd_accdb_sync.h"
       4             : #include "../accdb/fd_accdb_impl_v1.h"
       5             : #include "program/fd_program_util.h"
       6             : 
       7             : void *
       8             : fd_txn_account_new( void *              mem,
       9             :                     fd_pubkey_t const * pubkey,
      10             :                     fd_account_meta_t * meta,
      11        1026 :                     int                 is_mutable ) {
      12        1026 :   if( FD_UNLIKELY( !mem ) ) {
      13           3 :     FD_LOG_WARNING(( "NULL mem" ));
      14           3 :     return NULL;
      15           3 :   }
      16             : 
      17        1023 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)mem, alignof(fd_txn_account_t) ) ) ) {
      18           0 :     FD_LOG_WARNING(( "misaligned mem" ));
      19           0 :     return NULL;
      20           0 :   }
      21             : 
      22        1023 :   if( FD_UNLIKELY( !pubkey ) ) {
      23           3 :     FD_LOG_WARNING(( "NULL pubkey" ));
      24           3 :     return NULL;
      25           3 :   }
      26             : 
      27        1020 :   if( FD_UNLIKELY( !meta ) ) {
      28           3 :     FD_LOG_WARNING(( "NULL meta" ));
      29           3 :     return NULL;
      30           3 :   }
      31             : 
      32        1017 :   fd_txn_account_t * txn_account = (fd_txn_account_t *)mem;
      33             : 
      34        1017 :   fd_memcpy( txn_account->pubkey, pubkey, sizeof(fd_pubkey_t) );
      35             : 
      36        1017 :   txn_account->magic             = FD_TXN_ACCOUNT_MAGIC;
      37             : 
      38        1017 :   txn_account->starting_dlen     = meta->dlen;
      39        1017 :   txn_account->starting_lamports = meta->lamports;
      40             : 
      41        1017 :   uchar * data = (uchar *)meta + sizeof(fd_account_meta_t);
      42             : 
      43        1017 :   txn_account->meta_soff = (long)( (ulong)meta - (ulong)mem );
      44             : 
      45        1017 :   txn_account->meta       = meta;
      46        1017 :   txn_account->data       = data;
      47        1017 :   txn_account->is_mutable = is_mutable;
      48             : 
      49        1017 :   return mem;
      50        1020 : }
      51             : 
      52             : fd_txn_account_t *
      53         558 : fd_txn_account_join( void * mem ) {
      54         558 :   if( FD_UNLIKELY( !mem ) ) {
      55           0 :     FD_LOG_WARNING(( "NULL mem" ));
      56           0 :     return NULL;
      57           0 :   }
      58             : 
      59         558 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)mem, alignof(fd_txn_account_t) ) ) ) {
      60           0 :     FD_LOG_WARNING(( "misaligned mem" ));
      61           0 :     return NULL;
      62           0 :   }
      63             : 
      64         558 :   fd_txn_account_t * txn_account = (fd_txn_account_t *)mem;
      65             : 
      66         558 :   if( FD_UNLIKELY( txn_account->magic != FD_TXN_ACCOUNT_MAGIC ) ) {
      67           3 :     FD_LOG_WARNING(( "wrong magic" ));
      68           3 :     return NULL;
      69           3 :   }
      70             : 
      71         555 :   if( FD_UNLIKELY( txn_account->meta_soff==0UL ) ) {
      72           0 :     FD_LOG_CRIT(( "invalid meta_soff" ));
      73           0 :   }
      74             : 
      75         555 :   return txn_account;
      76         555 : }
      77             : 
      78             : void *
      79          15 : fd_txn_account_leave( fd_txn_account_t * acct ) {
      80             : 
      81          15 :   if( FD_UNLIKELY( !acct ) ) {
      82           3 :     FD_LOG_WARNING(( "NULL acct" ));
      83           3 :     return NULL;
      84           3 :   }
      85             : 
      86          12 :   if( FD_UNLIKELY( acct->magic != FD_TXN_ACCOUNT_MAGIC ) ) {
      87           0 :     FD_LOG_WARNING(( "wrong magic" ));
      88           0 :     return NULL;
      89           0 :   }
      90             : 
      91          12 :   acct->meta = NULL;
      92          12 :   acct->data = NULL;
      93             : 
      94          12 :   return acct;
      95          12 : }
      96             : 
      97             : void *
      98           9 : fd_txn_account_delete( void * mem ) {
      99           9 :   if( FD_UNLIKELY( !mem ) ) {
     100           3 :     FD_LOG_WARNING(( "NULL mem" ));
     101           3 :     return NULL;
     102           3 :   }
     103             : 
     104           6 :   if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)mem, alignof(fd_txn_account_t) ) ) ) {
     105           0 :     FD_LOG_WARNING(( "misaligned mem" ));
     106           0 :     return NULL;
     107           0 :   }
     108             : 
     109           6 :   fd_txn_account_t * txn_account = (fd_txn_account_t *)mem;
     110             : 
     111           6 :   if( FD_UNLIKELY( txn_account->magic != FD_TXN_ACCOUNT_MAGIC ) ) {
     112           0 :     FD_LOG_WARNING(( "wrong magic" ));
     113           0 :     return NULL;
     114           0 :   }
     115             : 
     116           6 :   txn_account->magic = 0UL;
     117             : 
     118           6 :   return mem;
     119           6 : }
     120             : 
     121             : /* Factory constructors from funk */
     122             : 
     123             : int
     124             : fd_txn_account_init_from_funk_readonly( fd_txn_account_t *        acct,
     125             :                                         fd_pubkey_t const *       pubkey,
     126             :                                         fd_funk_t const *         funk,
     127        4137 :                                         fd_funk_txn_xid_t const * xid ) {
     128             : 
     129        4137 :   int err = FD_ACC_MGR_SUCCESS;
     130        4137 :   fd_account_meta_t const * meta = fd_funk_get_acc_meta_readonly(
     131        4137 :       funk,
     132        4137 :       xid,
     133        4137 :       pubkey,
     134        4137 :       NULL,
     135        4137 :       &err,
     136        4137 :       NULL );
     137             : 
     138        4137 :   if( FD_UNLIKELY( err!=FD_ACC_MGR_SUCCESS ) ) {
     139        3672 :     return err;
     140        3672 :   }
     141             : 
     142         465 :   if( FD_UNLIKELY( !fd_txn_account_new(
     143         465 :         acct,
     144         465 :         pubkey,
     145         465 :         (fd_account_meta_t *)meta,
     146         465 :         0 ) ) ) {
     147           0 :     FD_LOG_CRIT(( "Failed to join txn account" ));
     148           0 :   }
     149             : 
     150         465 :   return FD_ACC_MGR_SUCCESS;
     151         465 : }
     152             : 
     153             : fd_account_meta_t *
     154             : fd_txn_account_init_from_funk_mutable( fd_txn_account_t *        acct,
     155             :                                        fd_pubkey_t const *       pubkey,
     156             :                                        fd_accdb_user_t *         accdb,
     157             :                                        fd_funk_txn_xid_t const * xid,
     158             :                                        int                       do_create,
     159             :                                        ulong                     min_data_sz,
     160         498 :                                        fd_funk_rec_prepare_t *   prepare_out ) {
     161         498 :   memset( prepare_out, 0, sizeof(fd_funk_rec_prepare_t) );
     162             : 
     163         498 :   fd_accdb_rw_t rw[1];
     164         498 :   if( FD_UNLIKELY( !fd_accdb_open_rw( accdb, rw, xid, pubkey->uc, min_data_sz, do_create ) ) ) {
     165           0 :     return NULL;
     166           0 :   }
     167             : 
     168         498 :   if( FD_UNLIKELY( !fd_txn_account_join( fd_txn_account_new(
     169         498 :         acct,
     170         498 :         pubkey,
     171         498 :         (fd_account_meta_t *)rw->meta,
     172         498 :         1 ) ) ) ) {
     173           0 :     FD_LOG_CRIT(( "Failed to join txn account" ));
     174           0 :   }
     175             : 
     176             :   /* HACKY: Convert accdb_rw writable reference into txn_account.
     177             :      In the future, use fd_accdb_modify_publish instead */
     178         498 :   accdb->base.rw_active--;
     179         498 :   fd_accdb_user_v1_t * accdb_v1 = fd_type_pun( accdb );
     180         498 :   fd_funk_txn_t * txn = accdb_v1->funk->txn_pool->ele + accdb_v1->tip_txn_idx;
     181         498 :   if( FD_UNLIKELY( !fd_funk_txn_xid_eq( &txn->xid, xid ) ) ) FD_LOG_CRIT(( "accdb_user corrupt: not joined to the expected transaction" ));
     182         498 :   if( !rw->published ) {
     183          45 :     *prepare_out = (fd_funk_rec_prepare_t) {
     184          45 :       .rec          = rw->rec,
     185          45 :       .rec_head_idx = &txn->rec_head_idx,
     186          45 :       .rec_tail_idx = &txn->rec_tail_idx
     187          45 :     };
     188         453 :   } else {
     189         453 :     memset( prepare_out, 0, sizeof(fd_funk_rec_prepare_t) );
     190         453 :   }
     191             : 
     192         498 :   return rw->meta;
     193         498 : }
     194             : 
     195             : void
     196             : fd_txn_account_mutable_fini( fd_txn_account_t *      acct,
     197             :                              fd_accdb_user_t *       accdb,
     198         498 :                              fd_funk_rec_prepare_t * prepare ) {
     199         498 :   fd_funk_rec_key_t key = fd_funk_acc_key( acct->pubkey );
     200         498 :   fd_funk_t * funk = fd_accdb_user_v1_funk( accdb );
     201             : 
     202             :   /* Check that the prepared record is still valid -
     203             :      if these invariants are broken something is very wrong. */
     204         498 :   if( prepare->rec ) {
     205             :     /* Check that the prepared record is not the Funk null value */
     206          45 :     if( !prepare->rec->val_gaddr ) {
     207           0 :       FD_BASE58_ENCODE_32_BYTES( acct->pubkey->uc, acct_pubkey_b58 );
     208           0 :       FD_LOG_CRIT(( "invalid prepared record for %s: unexpected NULL funk record value. the record might have been modified by another thread",
     209           0 :                     acct_pubkey_b58 ));
     210           0 :     }
     211             : 
     212             :     /* Ensure that the prepared record key still matches our key. */
     213          45 :     if( FD_UNLIKELY( memcmp( prepare->rec->pair.key, &key, sizeof(fd_funk_rec_key_t) )!=0 ) ) {
     214           0 :       FD_BASE58_ENCODE_32_BYTES( acct->pubkey->uc, acct_pubkey_b58 );
     215           0 :       FD_LOG_CRIT(( "invalid prepared record for %s: the record might have been modified by another thread",
     216           0 :                     acct_pubkey_b58 ));
     217           0 :     }
     218             : 
     219             :     /* Crashes the app if this key already exists in funk (conflicting
     220             :        write) */
     221          45 :     fd_funk_rec_publish( funk, prepare );
     222          45 :   }
     223         498 : }
     224             : 
     225             : /* read/write mutual exclusion */
     226             : 
     227             : FD_FN_PURE int
     228           0 : fd_txn_account_acquire_write_is_safe( fd_txn_account_t const * acct ) {
     229           0 :   return !acct->refcnt_excl;
     230           0 : }
     231             : 
     232             : /* fd_txn_account_acquire_write acquires write/exclusive access.
     233             :    Causes all other write or read acquire attempts will fail.  Returns 1
     234             :    on success, 0 on failure.
     235             : 
     236             :    Mirrors a try_borrow_mut() call in Agave. */
     237             : int
     238           0 : fd_txn_account_acquire_write( fd_txn_account_t * acct ) {
     239           0 :   if( FD_UNLIKELY( !fd_txn_account_acquire_write_is_safe( acct ) ) ) {
     240           0 :     return 0;
     241           0 :   }
     242           0 :   acct->refcnt_excl = (ushort)1;
     243           0 :   return 1;
     244           0 : }
     245             : 
     246             : /* fd_txn_account_release_write{_private} releases a write/exclusive
     247             :    access handle. The private version should only be used by fd_borrowed_account_drop
     248             :    and fd_borrowed_account_destroy. */
     249             : void
     250           0 : fd_txn_account_release_write( fd_txn_account_t * acct ) {
     251           0 :   if( FD_UNLIKELY( acct->refcnt_excl!=1 ) ) {
     252           0 :     FD_LOG_CRIT(( "refcnt_excl is %d, expected 1", acct->refcnt_excl ));
     253           0 :   }
     254           0 :   acct->refcnt_excl = (ushort)0;
     255           0 : }
     256             : 
     257             : void
     258           0 : fd_txn_account_release_write_private( fd_txn_account_t * acct ) {
     259             :   /* Only release if it is not yet released */
     260           0 :   if( !fd_txn_account_acquire_write_is_safe( acct ) ) {
     261           0 :     fd_txn_account_release_write( acct );
     262           0 :   }
     263           0 : }
     264             : 
     265             : fd_pubkey_t const *
     266          51 : fd_txn_account_get_owner( fd_txn_account_t const * acct ) {
     267          51 :   if( FD_UNLIKELY( !acct->meta ) ) {
     268           0 :     FD_LOG_CRIT(( "account is not setup" ));
     269           0 :   }
     270          51 :   return (fd_pubkey_t const *)acct->meta->owner;
     271          51 : }
     272             : 
     273             : fd_account_meta_t const *
     274         927 : fd_txn_account_get_meta( fd_txn_account_t const * acct ) {
     275         927 :   return acct->meta;
     276         927 : }
     277             : 
     278             : uchar const *
     279        1437 : fd_txn_account_get_data( fd_txn_account_t const * acct ) {
     280        1437 :   return acct->data;
     281        1437 : }
     282             : 
     283             : uchar *
     284           0 : fd_txn_account_get_data_mut( fd_txn_account_t const * acct ) {
     285           0 :   return acct->data;
     286           0 : }
     287             : 
     288             : ulong
     289         519 : fd_txn_account_get_data_len( fd_txn_account_t const * acct ) {
     290         519 :   if( FD_UNLIKELY( !acct->meta ) ) {
     291           0 :     FD_LOG_CRIT(( "account is not setup" ));
     292           0 :   }
     293         519 :   return acct->meta->dlen;
     294         519 : }
     295             : 
     296             : int
     297           0 : fd_txn_account_is_executable( fd_txn_account_t const * acct ) {
     298           0 :   if( FD_UNLIKELY( !acct->meta ) ) {
     299           0 :     FD_LOG_CRIT(( "account is not setup" ));
     300           0 :   }
     301           0 :   return !!acct->meta->executable;
     302           0 : }
     303             : 
     304             : ulong
     305         936 : fd_txn_account_get_lamports( fd_txn_account_t const * acct ) {
     306         936 :   if( FD_UNLIKELY( !acct->meta ) ) {
     307           0 :     FD_LOG_CRIT(( "account is not setup" ));
     308           0 :   }
     309         936 :   return acct->meta->lamports;
     310         936 : }
     311             : 
     312             : ulong
     313           6 : fd_txn_account_get_rent_epoch( fd_txn_account_t const * acct ) {
     314           6 :   (void)acct;
     315           6 :   return ULONG_MAX;
     316           6 : }
     317             : 
     318             : void
     319           0 : fd_txn_account_set_meta( fd_txn_account_t * acct, fd_account_meta_t * meta ) {
     320           0 :   if( FD_UNLIKELY( !acct->is_mutable ) ) {
     321           0 :     FD_LOG_CRIT(( "account is not mutable" ));
     322           0 :   }
     323           0 :   if( FD_UNLIKELY( !meta ) ) {
     324           0 :     FD_LOG_CRIT(( "account is not setup" ));
     325           0 :   }
     326           0 :   acct->meta = meta;
     327           0 : }
     328             : 
     329             : void
     330          36 : fd_txn_account_set_executable( fd_txn_account_t * acct, int is_executable ) {
     331          36 :   if( FD_UNLIKELY( !acct->is_mutable ) ) {
     332           0 :     FD_LOG_CRIT(( "account is not mutable" ));
     333           0 :   }
     334          36 :   if( FD_UNLIKELY( !acct->meta ) ) {
     335           0 :     FD_LOG_CRIT(( "account is not setup" ));
     336           0 :   }
     337          36 :   acct->meta->executable = !!is_executable;
     338          36 : }
     339             : 
     340             : void
     341         498 : fd_txn_account_set_owner( fd_txn_account_t * acct, fd_pubkey_t const * owner ) {
     342         498 :   if( FD_UNLIKELY( !acct->is_mutable ) ) {
     343           0 :     FD_LOG_CRIT(( "account is not mutable" ));
     344           0 :   }
     345         498 :   if( FD_UNLIKELY( !acct->meta ) ) {
     346           0 :     FD_LOG_CRIT(( "account is not setup" ));
     347           0 :   }
     348         498 :   fd_memcpy( acct->meta->owner, owner, sizeof(fd_pubkey_t) );
     349         498 : }
     350             : 
     351             : void
     352         501 : fd_txn_account_set_lamports( fd_txn_account_t * acct, ulong lamports ) {
     353         501 :   if( FD_UNLIKELY( !acct->is_mutable ) ) {
     354           0 :     FD_LOG_CRIT(( "account is not mutable" ));
     355           0 :   }
     356         501 :   if( FD_UNLIKELY( !acct->meta ) ) {
     357           0 :     FD_LOG_CRIT(( "account is not setup" ));
     358           0 :   }
     359         501 :   acct->meta->lamports = lamports;
     360         501 : }
     361             : 
     362             : int
     363           0 : fd_txn_account_checked_add_lamports( fd_txn_account_t * acct, ulong lamports ) {
     364           0 :   ulong balance_post = 0UL;
     365           0 :   int err = fd_ulong_checked_add( fd_txn_account_get_lamports( acct ), lamports, &balance_post );
     366           0 :   if( FD_UNLIKELY( err ) ) {
     367           0 :     return FD_EXECUTOR_INSTR_ERR_ARITHMETIC_OVERFLOW;
     368           0 :   }
     369             : 
     370           0 :   fd_txn_account_set_lamports( acct, balance_post );
     371           0 :   return FD_EXECUTOR_INSTR_SUCCESS;
     372           0 : }
     373             : 
     374             : int
     375           0 : fd_txn_account_checked_sub_lamports( fd_txn_account_t * acct, ulong lamports ) {
     376           0 :   ulong balance_post = 0UL;
     377           0 :   int err = fd_ulong_checked_sub( fd_txn_account_get_lamports( acct ),
     378           0 :                                   lamports,
     379           0 :                                   &balance_post );
     380           0 :   if( FD_UNLIKELY( err ) ) {
     381           0 :     return FD_EXECUTOR_INSTR_ERR_ARITHMETIC_OVERFLOW;
     382           0 :   }
     383             : 
     384           0 :   fd_txn_account_set_lamports( acct, balance_post );
     385           0 :   return FD_EXECUTOR_INSTR_SUCCESS;
     386           0 : }
     387             : 
     388             : void
     389             : fd_txn_account_set_data( fd_txn_account_t * acct,
     390             :                          void const *       data,
     391         498 :                          ulong              data_sz ) {
     392         498 :   if( FD_UNLIKELY( !acct->is_mutable ) ) {
     393           0 :     FD_LOG_CRIT(( "account is not mutable" ));
     394           0 :   }
     395         498 :   if( FD_UNLIKELY( !acct->meta ) ) {
     396           0 :     FD_LOG_CRIT(( "account is not setup" ));
     397           0 :   }
     398         498 :   acct->meta->dlen = (uint)data_sz;
     399         498 :   fd_memcpy( acct->data, data, data_sz );
     400         498 : }
     401             : 
     402             : void
     403           3 : fd_txn_account_set_data_len( fd_txn_account_t * acct, ulong data_len ) {
     404           3 :   if( FD_UNLIKELY( !acct->is_mutable ) ) {
     405           0 :     FD_LOG_CRIT(( "account is not mutable" ));
     406           0 :   }
     407           3 :   if( FD_UNLIKELY( !acct->meta ) ) {
     408           0 :     FD_LOG_CRIT(( "account is not setup" ));
     409           0 :   }
     410           3 :   acct->meta->dlen = (uint)data_len;
     411           3 : }
     412             : 
     413             : void
     414         462 : fd_txn_account_set_slot( fd_txn_account_t * acct, ulong slot ) {
     415         462 :   if( FD_UNLIKELY( !acct->is_mutable ) ) {
     416           0 :     FD_LOG_CRIT(( "account is not mutable" ));
     417           0 :   }
     418         462 :   if( FD_UNLIKELY( !acct->meta ) ) {
     419           0 :     FD_LOG_CRIT(( "account is not setup" ));
     420           0 :   }
     421         462 :   acct->meta->slot = slot;
     422         462 : }
     423             : 
     424             : void
     425           0 : fd_txn_account_clear_owner( fd_txn_account_t * acct ) {
     426           0 :   if( FD_UNLIKELY( !acct->is_mutable ) ) {
     427           0 :     FD_LOG_CRIT(( "account is not mutable" ));
     428           0 :   }
     429           0 :   if( FD_UNLIKELY( !acct->meta ) ) {
     430           0 :     FD_LOG_CRIT(( "account is not setup" ));
     431           0 :   }
     432           0 :   fd_memset( acct->meta->owner, 0, sizeof(fd_pubkey_t) );
     433           0 : }
     434             : 
     435             : void
     436             : fd_txn_account_resize( fd_txn_account_t * acct,
     437           0 :                        ulong              dlen ) {
     438           0 :   if( FD_UNLIKELY( !acct->is_mutable ) ) {
     439           0 :     FD_LOG_CRIT(( "account is not mutable" ));
     440           0 :   }
     441           0 :   if( FD_UNLIKELY( !acct->meta ) ) {
     442           0 :     FD_LOG_CRIT(( "account is not setup" ));
     443           0 :   }
     444             :   /* Because the memory for an account is preallocated for the transaction
     445             :      up to the max account size, we only need to zero out bytes (for the case
     446             :      where the account grew) and update the account dlen. */
     447           0 :   ulong old_sz    = acct->meta->dlen;
     448           0 :   ulong new_sz    = dlen;
     449           0 :   ulong memset_sz = fd_ulong_sat_sub( new_sz, old_sz );
     450           0 :   fd_memset( acct->data+old_sz, 0, memset_sz );
     451             : 
     452           0 :   acct->meta->dlen = (uint)dlen;
     453           0 : }
     454             : 
     455             : ushort
     456           0 : fd_txn_account_is_borrowed( fd_txn_account_t const * acct ) {
     457           0 :   return !!acct->refcnt_excl;
     458           0 : }
     459             : 
     460             : int
     461           6 : fd_txn_account_is_mutable( fd_txn_account_t const * acct ) {
     462             :   /* A txn account is mutable if meta is non NULL */
     463           6 :   return acct->is_mutable;
     464           6 : }
     465             : 
     466             : int
     467           6 : fd_txn_account_is_readonly( fd_txn_account_t const * acct ) {
     468             :   /* A txn account is readonly if only the meta_ field is non NULL */
     469           6 :   return !acct->is_mutable;
     470           6 : }
     471             : 
     472             : int
     473           0 : fd_txn_account_try_borrow_mut( fd_txn_account_t * acct ) {
     474           0 :   return fd_txn_account_acquire_write( acct );
     475           0 : }
     476             : 
     477             : void
     478           0 : fd_txn_account_drop( fd_txn_account_t * acct ) {
     479           0 :   fd_txn_account_release_write_private( acct );
     480           0 : }
     481             : 
     482             : void
     483           0 : fd_txn_account_set_readonly( fd_txn_account_t * acct ) {
     484           0 :   acct->is_mutable = 0;
     485           0 : }
     486             : 
     487             : void
     488           0 : fd_txn_account_set_mutable( fd_txn_account_t * acct ) {
     489           0 :   acct->is_mutable = 1;
     490           0 : }

Generated by: LCOV version 1.14