LCOV - code coverage report
Current view: top level - flamenco/runtime - fd_txn_account.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 167 327 51.1 %
Date: 2025-11-23 04:57:59 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 "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_BASE58_ENCODE_32_BYTES( acct->pubkey->uc, acct_pubkey_b58 );
     205           0 :       FD_LOG_CRIT(( "invalid prepared record for %s: unexpected NULL funk record value. the record might have been modified by another thread",
     206           0 :                     acct_pubkey_b58 ));
     207           0 :     }
     208             : 
     209             :     /* Ensure that the prepared record key still matches our key. */
     210          45 :     if( FD_UNLIKELY( memcmp( prepare->rec->pair.key, &key, sizeof(fd_funk_rec_key_t) )!=0 ) ) {
     211           0 :       FD_BASE58_ENCODE_32_BYTES( acct->pubkey->uc, acct_pubkey_b58 );
     212           0 :       FD_LOG_CRIT(( "invalid prepared record for %s: the record might have been modified by another thread",
     213           0 :                     acct_pubkey_b58 ));
     214           0 :     }
     215             : 
     216             :     /* Crashes the app if this key already exists in funk (conflicting
     217             :        write) */
     218          45 :     fd_funk_rec_publish( accdb->funk, prepare );
     219          45 :   }
     220         498 : }
     221             : 
     222             : /* read/write mutual exclusion */
     223             : 
     224             : FD_FN_PURE int
     225           0 : fd_txn_account_acquire_write_is_safe( fd_txn_account_t const * acct ) {
     226           0 :   return !acct->refcnt_excl;
     227           0 : }
     228             : 
     229             : /* fd_txn_account_acquire_write acquires write/exclusive access.
     230             :    Causes all other write or read acquire attempts will fail.  Returns 1
     231             :    on success, 0 on failure.
     232             : 
     233             :    Mirrors a try_borrow_mut() call in Agave. */
     234             : int
     235           0 : fd_txn_account_acquire_write( fd_txn_account_t * acct ) {
     236           0 :   if( FD_UNLIKELY( !fd_txn_account_acquire_write_is_safe( acct ) ) ) {
     237           0 :     return 0;
     238           0 :   }
     239           0 :   acct->refcnt_excl = (ushort)1;
     240           0 :   return 1;
     241           0 : }
     242             : 
     243             : /* fd_txn_account_release_write{_private} releases a write/exclusive
     244             :    access handle. The private version should only be used by fd_borrowed_account_drop
     245             :    and fd_borrowed_account_destroy. */
     246             : void
     247           0 : fd_txn_account_release_write( fd_txn_account_t * acct ) {
     248           0 :   if( FD_UNLIKELY( acct->refcnt_excl!=1 ) ) {
     249           0 :     FD_LOG_CRIT(( "refcnt_excl is %d, expected 1", acct->refcnt_excl ));
     250           0 :   }
     251           0 :   acct->refcnt_excl = (ushort)0;
     252           0 : }
     253             : 
     254             : void
     255           0 : fd_txn_account_release_write_private( fd_txn_account_t * acct ) {
     256             :   /* Only release if it is not yet released */
     257           0 :   if( !fd_txn_account_acquire_write_is_safe( acct ) ) {
     258           0 :     fd_txn_account_release_write( acct );
     259           0 :   }
     260           0 : }
     261             : 
     262             : fd_pubkey_t const *
     263          51 : fd_txn_account_get_owner( fd_txn_account_t const * acct ) {
     264          51 :   if( FD_UNLIKELY( !acct->meta ) ) {
     265           0 :     FD_LOG_CRIT(( "account is not setup" ));
     266           0 :   }
     267          51 :   return (fd_pubkey_t const *)acct->meta->owner;
     268          51 : }
     269             : 
     270             : fd_account_meta_t const *
     271         927 : fd_txn_account_get_meta( fd_txn_account_t const * acct ) {
     272         927 :   return acct->meta;
     273         927 : }
     274             : 
     275             : uchar const *
     276        1437 : fd_txn_account_get_data( fd_txn_account_t const * acct ) {
     277        1437 :   return acct->data;
     278        1437 : }
     279             : 
     280             : uchar *
     281           0 : fd_txn_account_get_data_mut( fd_txn_account_t const * acct ) {
     282           0 :   return acct->data;
     283           0 : }
     284             : 
     285             : ulong
     286         519 : fd_txn_account_get_data_len( fd_txn_account_t const * acct ) {
     287         519 :   if( FD_UNLIKELY( !acct->meta ) ) {
     288           0 :     FD_LOG_CRIT(( "account is not setup" ));
     289           0 :   }
     290         519 :   return acct->meta->dlen;
     291         519 : }
     292             : 
     293             : int
     294           0 : fd_txn_account_is_executable( fd_txn_account_t const * acct ) {
     295           0 :   if( FD_UNLIKELY( !acct->meta ) ) {
     296           0 :     FD_LOG_CRIT(( "account is not setup" ));
     297           0 :   }
     298           0 :   return !!acct->meta->executable;
     299           0 : }
     300             : 
     301             : ulong
     302         936 : fd_txn_account_get_lamports( fd_txn_account_t const * acct ) {
     303         936 :   if( FD_UNLIKELY( !acct->meta ) ) {
     304           0 :     FD_LOG_CRIT(( "account is not setup" ));
     305           0 :   }
     306         936 :   return acct->meta->lamports;
     307         936 : }
     308             : 
     309             : ulong
     310           6 : fd_txn_account_get_rent_epoch( fd_txn_account_t const * acct ) {
     311           6 :   (void)acct;
     312           6 :   return ULONG_MAX;
     313           6 : }
     314             : 
     315             : void
     316           0 : fd_txn_account_set_meta( fd_txn_account_t * acct, fd_account_meta_t * meta ) {
     317           0 :   if( FD_UNLIKELY( !acct->is_mutable ) ) {
     318           0 :     FD_LOG_CRIT(( "account is not mutable" ));
     319           0 :   }
     320           0 :   if( FD_UNLIKELY( !meta ) ) {
     321           0 :     FD_LOG_CRIT(( "account is not setup" ));
     322           0 :   }
     323           0 :   acct->meta = meta;
     324           0 : }
     325             : 
     326             : void
     327          36 : fd_txn_account_set_executable( fd_txn_account_t * acct, int is_executable ) {
     328          36 :   if( FD_UNLIKELY( !acct->is_mutable ) ) {
     329           0 :     FD_LOG_CRIT(( "account is not mutable" ));
     330           0 :   }
     331          36 :   if( FD_UNLIKELY( !acct->meta ) ) {
     332           0 :     FD_LOG_CRIT(( "account is not setup" ));
     333           0 :   }
     334          36 :   acct->meta->executable = !!is_executable;
     335          36 : }
     336             : 
     337             : void
     338         498 : fd_txn_account_set_owner( fd_txn_account_t * acct, fd_pubkey_t const * owner ) {
     339         498 :   if( FD_UNLIKELY( !acct->is_mutable ) ) {
     340           0 :     FD_LOG_CRIT(( "account is not mutable" ));
     341           0 :   }
     342         498 :   if( FD_UNLIKELY( !acct->meta ) ) {
     343           0 :     FD_LOG_CRIT(( "account is not setup" ));
     344           0 :   }
     345         498 :   fd_memcpy( acct->meta->owner, owner, sizeof(fd_pubkey_t) );
     346         498 : }
     347             : 
     348             : void
     349         501 : fd_txn_account_set_lamports( fd_txn_account_t * acct, ulong lamports ) {
     350         501 :   if( FD_UNLIKELY( !acct->is_mutable ) ) {
     351           0 :     FD_LOG_CRIT(( "account is not mutable" ));
     352           0 :   }
     353         501 :   if( FD_UNLIKELY( !acct->meta ) ) {
     354           0 :     FD_LOG_CRIT(( "account is not setup" ));
     355           0 :   }
     356         501 :   acct->meta->lamports = lamports;
     357         501 : }
     358             : 
     359             : int
     360           0 : fd_txn_account_checked_add_lamports( fd_txn_account_t * acct, ulong lamports ) {
     361           0 :   ulong balance_post = 0UL;
     362           0 :   int err = fd_ulong_checked_add( fd_txn_account_get_lamports( acct ), lamports, &balance_post );
     363           0 :   if( FD_UNLIKELY( err ) ) {
     364           0 :     return FD_EXECUTOR_INSTR_ERR_ARITHMETIC_OVERFLOW;
     365           0 :   }
     366             : 
     367           0 :   fd_txn_account_set_lamports( acct, balance_post );
     368           0 :   return FD_EXECUTOR_INSTR_SUCCESS;
     369           0 : }
     370             : 
     371             : int
     372           0 : fd_txn_account_checked_sub_lamports( fd_txn_account_t * acct, ulong lamports ) {
     373           0 :   ulong balance_post = 0UL;
     374           0 :   int err = fd_ulong_checked_sub( fd_txn_account_get_lamports( acct ),
     375           0 :                                   lamports,
     376           0 :                                   &balance_post );
     377           0 :   if( FD_UNLIKELY( err ) ) {
     378           0 :     return FD_EXECUTOR_INSTR_ERR_ARITHMETIC_OVERFLOW;
     379           0 :   }
     380             : 
     381           0 :   fd_txn_account_set_lamports( acct, balance_post );
     382           0 :   return FD_EXECUTOR_INSTR_SUCCESS;
     383           0 : }
     384             : 
     385             : void
     386             : fd_txn_account_set_data( fd_txn_account_t * acct,
     387             :                          void const *       data,
     388         498 :                          ulong              data_sz ) {
     389         498 :   if( FD_UNLIKELY( !acct->is_mutable ) ) {
     390           0 :     FD_LOG_CRIT(( "account is not mutable" ));
     391           0 :   }
     392         498 :   if( FD_UNLIKELY( !acct->meta ) ) {
     393           0 :     FD_LOG_CRIT(( "account is not setup" ));
     394           0 :   }
     395         498 :   acct->meta->dlen = (uint)data_sz;
     396         498 :   fd_memcpy( acct->data, data, data_sz );
     397         498 : }
     398             : 
     399             : void
     400           3 : fd_txn_account_set_data_len( fd_txn_account_t * acct, ulong data_len ) {
     401           3 :   if( FD_UNLIKELY( !acct->is_mutable ) ) {
     402           0 :     FD_LOG_CRIT(( "account is not mutable" ));
     403           0 :   }
     404           3 :   if( FD_UNLIKELY( !acct->meta ) ) {
     405           0 :     FD_LOG_CRIT(( "account is not setup" ));
     406           0 :   }
     407           3 :   acct->meta->dlen = (uint)data_len;
     408           3 : }
     409             : 
     410             : void
     411         462 : fd_txn_account_set_slot( fd_txn_account_t * acct, ulong slot ) {
     412         462 :   if( FD_UNLIKELY( !acct->is_mutable ) ) {
     413           0 :     FD_LOG_CRIT(( "account is not mutable" ));
     414           0 :   }
     415         462 :   if( FD_UNLIKELY( !acct->meta ) ) {
     416           0 :     FD_LOG_CRIT(( "account is not setup" ));
     417           0 :   }
     418         462 :   acct->meta->slot = slot;
     419         462 : }
     420             : 
     421             : void
     422           0 : fd_txn_account_clear_owner( fd_txn_account_t * acct ) {
     423           0 :   if( FD_UNLIKELY( !acct->is_mutable ) ) {
     424           0 :     FD_LOG_CRIT(( "account is not mutable" ));
     425           0 :   }
     426           0 :   if( FD_UNLIKELY( !acct->meta ) ) {
     427           0 :     FD_LOG_CRIT(( "account is not setup" ));
     428           0 :   }
     429           0 :   fd_memset( acct->meta->owner, 0, sizeof(fd_pubkey_t) );
     430           0 : }
     431             : 
     432             : void
     433             : fd_txn_account_resize( fd_txn_account_t * acct,
     434           0 :                        ulong              dlen ) {
     435           0 :   if( FD_UNLIKELY( !acct->is_mutable ) ) {
     436           0 :     FD_LOG_CRIT(( "account is not mutable" ));
     437           0 :   }
     438           0 :   if( FD_UNLIKELY( !acct->meta ) ) {
     439           0 :     FD_LOG_CRIT(( "account is not setup" ));
     440           0 :   }
     441             :   /* Because the memory for an account is preallocated for the transaction
     442             :      up to the max account size, we only need to zero out bytes (for the case
     443             :      where the account grew) and update the account dlen. */
     444           0 :   ulong old_sz    = acct->meta->dlen;
     445           0 :   ulong new_sz    = dlen;
     446           0 :   ulong memset_sz = fd_ulong_sat_sub( new_sz, old_sz );
     447           0 :   fd_memset( acct->data+old_sz, 0, memset_sz );
     448             : 
     449           0 :   acct->meta->dlen = (uint)dlen;
     450           0 : }
     451             : 
     452             : ushort
     453           0 : fd_txn_account_is_borrowed( fd_txn_account_t const * acct ) {
     454           0 :   return !!acct->refcnt_excl;
     455           0 : }
     456             : 
     457             : int
     458           6 : fd_txn_account_is_mutable( fd_txn_account_t const * acct ) {
     459             :   /* A txn account is mutable if meta is non NULL */
     460           6 :   return acct->is_mutable;
     461           6 : }
     462             : 
     463             : int
     464           6 : fd_txn_account_is_readonly( fd_txn_account_t const * acct ) {
     465             :   /* A txn account is readonly if only the meta_ field is non NULL */
     466           6 :   return !acct->is_mutable;
     467           6 : }
     468             : 
     469             : int
     470           0 : fd_txn_account_try_borrow_mut( fd_txn_account_t * acct ) {
     471           0 :   return fd_txn_account_acquire_write( acct );
     472           0 : }
     473             : 
     474             : void
     475           0 : fd_txn_account_drop( fd_txn_account_t * acct ) {
     476           0 :   fd_txn_account_release_write_private( acct );
     477           0 : }
     478             : 
     479             : void
     480           0 : fd_txn_account_set_readonly( fd_txn_account_t * acct ) {
     481           0 :   acct->is_mutable = 0;
     482           0 : }
     483             : 
     484             : void
     485           0 : fd_txn_account_set_mutable( fd_txn_account_t * acct ) {
     486           0 :   acct->is_mutable = 1;
     487           0 : }

Generated by: LCOV version 1.14