LCOV - code coverage report
Current view: top level - flamenco/runtime - fd_txn_account.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 167 333 50.2 %
Date: 2025-11-09 04:41:14 Functions: 21 38 55.3 %

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

Generated by: LCOV version 1.14