LCOV - code coverage report
Current view: top level - flamenco/runtime - fd_account.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 127 191 66.5 %
Date: 2024-11-13 11:58:15 Functions: 6 7 85.7 %

          Line data    Source code
       1             : #include "fd_account.h"
       2             : #include "context/fd_exec_instr_ctx.h"
       3             : 
       4             : /* https://github.com/anza-xyz/agave/blob/b5f5c3cdd3f9a5859c49ebc27221dc27e143d760/sdk/src/transaction_context.rs#L740-L767 */
       5             : /* Assignes the owner of this account (transaction wide) */
       6             : int
       7             : fd_account_set_owner( fd_exec_instr_ctx_t const * ctx,
       8             :                       ulong                       instr_acc_idx,
       9        2250 :                       fd_pubkey_t const *         owner ) {
      10             : 
      11        2250 :   fd_instr_info_t const * instr = ctx->instr;
      12        2250 :   fd_borrowed_account_t * account = NULL;
      13        2250 :   do {
      14        2250 :     int err = fd_instr_borrowed_account_view_idx( ctx, (uchar)instr_acc_idx, &account );
      15        2250 :     if( FD_UNLIKELY( err ) ) {
      16           0 :       FD_LOG_ERR(( "fd_instr_borrowed_account_view_idx failed (%d-%s)", err, fd_acc_mgr_strerror( err ) ));
      17           0 :     }
      18        2250 :   } while(0);
      19             : 
      20        2250 :   fd_account_meta_t const * meta = account->const_meta;
      21             : 
      22             :   /* Only the owner can assign a new owner */
      23        2250 :   if( !fd_account_is_owned_by_current_program( instr, meta ) ) {
      24         204 :     return FD_EXECUTOR_INSTR_ERR_MODIFIED_PROGRAM_ID;
      25         204 :   }
      26             :   /* And only if the account is writable */
      27        2046 :   if( !fd_instr_acc_is_writable_idx( instr, instr_acc_idx ) ) {
      28          48 :     return FD_EXECUTOR_INSTR_ERR_MODIFIED_PROGRAM_ID;
      29          48 :   }
      30             :   /* And only if the account is not executable */
      31        1998 :   if( fd_account_is_executable( meta ) ) {
      32          24 :     return FD_EXECUTOR_INSTR_ERR_MODIFIED_PROGRAM_ID;
      33          24 :   }
      34             :   /* And only if the data is zero-initialized or empty */
      35        1974 :   if( !fd_account_is_zeroed( meta ) ) {
      36          57 :     return FD_EXECUTOR_INSTR_ERR_MODIFIED_PROGRAM_ID;
      37          57 :   }
      38             :   /* Don't copy the account if the owner does not change */
      39        1917 :   if( !memcmp( account->const_meta->info.owner, owner, sizeof( fd_pubkey_t ) ) ) {
      40           0 :     return FD_EXECUTOR_INSTR_SUCCESS;
      41           0 :   }
      42             : 
      43        1917 :   do {
      44        1917 :     int err = fd_instr_borrowed_account_modify_idx( ctx, (uchar)instr_acc_idx, 0UL, &account );
      45        1917 :     if( FD_UNLIKELY( err ) ) {
      46           0 :       FD_LOG_ERR(( "fd_instr_borrowed_account_modify_idx failed (%d-%s)", err, fd_acc_mgr_strerror( err ) ));
      47           0 :     }
      48        1917 :   } while(0);
      49             : 
      50             :   /* Agave self.touch() is a no-op */
      51             : 
      52        1917 :   memcpy( account->meta->info.owner, owner, sizeof(fd_pubkey_t) );
      53        1917 :   return FD_EXECUTOR_INSTR_SUCCESS;
      54        1917 : }
      55             : 
      56             : /* https://github.com/anza-xyz/agave/blob/b5f5c3cdd3f9a5859c49ebc27221dc27e143d760/sdk/src/transaction_context.rs#L774-L796 */
      57             : /* Overwrites the number of lamports of this account (transaction wide) */
      58             : int
      59             : fd_account_set_lamports( fd_exec_instr_ctx_t const * ctx,
      60             :                          ulong                       instr_acc_idx,
      61        8226 :                          ulong                       lamports ) {
      62             : 
      63        8226 :   fd_borrowed_account_t * account = NULL;
      64        8226 :   do {
      65        8226 :     int err = fd_instr_borrowed_account_view_idx( ctx, (uchar)instr_acc_idx, &account );
      66        8226 :     if( FD_UNLIKELY( err ) ) {
      67           0 :       FD_LOG_ERR(( "fd_instr_borrowed_account_view_idx failed (%d-%s)", err, fd_acc_mgr_strerror( err ) ));
      68           0 :     }
      69        8226 :   } while(0);
      70             : 
      71             :   /* An account not owned by the program cannot have its blanace decrease */
      72        8226 :   if( FD_UNLIKELY( ( !fd_account_is_owned_by_current_program( ctx->instr, account->const_meta ) ) &&
      73        8226 :                    ( lamports < account->const_meta->info.lamports ) ) ) {
      74          66 :     return FD_EXECUTOR_INSTR_ERR_EXTERNAL_ACCOUNT_LAMPORT_SPEND;
      75          66 :   }
      76             : 
      77             :   /* The balance of read-only may not change */
      78        8160 :   if( FD_UNLIKELY( !fd_instr_acc_is_writable_idx( ctx->instr, instr_acc_idx ) ) ) {
      79         225 :     return FD_EXECUTOR_INSTR_ERR_READONLY_LAMPORT_CHANGE;
      80         225 :   }
      81             : 
      82             :   /* The balance of executable accounts may not change */
      83        7935 :   if( FD_UNLIKELY( fd_account_is_executable( account->const_meta ) ) ) {
      84         174 :     return FD_EXECUTOR_INSTR_ERR_EXECUTABLE_LAMPORT_CHANGE;
      85         174 :   }
      86             : 
      87             :   /* Don't copy the account if the lamports do not change */
      88        7761 :   if( lamports==account->const_meta->info.lamports ) {
      89         708 :    return FD_EXECUTOR_INSTR_SUCCESS;
      90         708 :   }
      91             : 
      92        7053 :   do {
      93        7053 :     int err = fd_instr_borrowed_account_modify_idx( ctx, (uchar)instr_acc_idx, 0UL, &account );
      94        7053 :     if( FD_UNLIKELY( err ) ) {
      95           0 :       FD_LOG_ERR(( "fd_instr_borrowed_account_modify_idx failed (%d-%s)", err, fd_acc_mgr_strerror( err ) ));
      96           0 :     }
      97        7053 :   } while(0);
      98             : 
      99             :   /* Agave self.touch() is a no-op */
     100             : 
     101        7053 :   account->meta->info.lamports = lamports;
     102        7053 :   return FD_EXECUTOR_INSTR_SUCCESS;
     103        7053 : }
     104             : 
     105             : int
     106             : fd_account_get_data_mut( fd_exec_instr_ctx_t const * ctx,
     107             :                          ulong                       instr_acc_idx,
     108             :                          uchar * *                   data_out,
     109        4155 :                          ulong *                     dlen_out ) {
     110             : 
     111        4155 :   int err;
     112        4155 :   if( FD_UNLIKELY( !fd_account_can_data_be_changed( ctx->instr, instr_acc_idx, &err ) ) ) {
     113         453 :     return err;
     114         453 :   }
     115             : 
     116        3702 :   fd_borrowed_account_t * account = NULL;
     117        3702 :   do {
     118        3702 :     int err = fd_instr_borrowed_account_modify_idx( ctx, (uchar)instr_acc_idx, 0UL, &account );
     119        3702 :     if( FD_UNLIKELY( err ) ) {
     120           0 :       FD_LOG_ERR(( "fd_instr_borrowed_account_modify_idx failed (%d-%s)", err, fd_acc_mgr_strerror( err ) ));
     121           0 :     }
     122        3702 :   } while(0);
     123             : 
     124             :   /* Agave self.touch() is a no-op */
     125             : 
     126        3702 :   if (NULL != data_out)
     127        3702 :     *data_out = account->data;
     128        3702 :   if (NULL != dlen_out)
     129        3702 :     *dlen_out = account->meta->dlen;
     130             : 
     131        3702 :   return FD_EXECUTOR_INSTR_SUCCESS;
     132        3702 : }
     133             : 
     134             : int
     135             : fd_account_set_data_from_slice( fd_exec_instr_ctx_t const * ctx,
     136             :                                 ulong                       instr_acc_idx,
     137             :                                 uchar const *               data,
     138         777 :                                 ulong                       data_sz ) {
     139             : 
     140         777 :   fd_borrowed_account_t * account = NULL;
     141         777 :   do {
     142         777 :     int err = fd_instr_borrowed_account_view_idx( ctx, (uchar)instr_acc_idx, &account );
     143         777 :     if( FD_UNLIKELY( err ) ) {
     144           0 :       FD_LOG_ERR(( "fd_instr_borrowed_account_view_idx failed (%d-%s)", err, fd_acc_mgr_strerror( err ) ));
     145           0 :     }
     146         777 :   } while(0);
     147             : 
     148         777 :   int err;
     149         777 :   if( FD_UNLIKELY( !fd_account_can_data_be_resized( ctx, account->const_meta, data_sz, &err ) ) ) {
     150           0 :     return err;
     151           0 :   }
     152             : 
     153         777 :   if( FD_UNLIKELY( !fd_account_can_data_be_changed( ctx->instr, instr_acc_idx, &err ) ) ) {
     154           3 :     return err;
     155           3 :   }
     156             : 
     157             :   /* Agave self.touch() is a no-op */
     158             : 
     159         774 :   if( FD_UNLIKELY( !fd_account_update_accounts_resize_delta( ctx, instr_acc_idx, data_sz, &err ) ) ) {
     160           0 :     return err;
     161           0 :   }
     162             : 
     163         774 :   do {
     164         774 :     int err = fd_instr_borrowed_account_modify_idx( ctx, (uchar)instr_acc_idx, data_sz, &account );
     165         774 :     if( FD_UNLIKELY( err ) ) {
     166           0 :       FD_LOG_ERR(( "fd_instr_borrowed_account_modify_idx failed (%d-%s)", err, fd_acc_mgr_strerror( err ) ));
     167           0 :     }
     168         774 :   } while(0);
     169             : 
     170             :   /* AccountSharedData::set_data_from_slice() */
     171         774 :   account->meta->dlen = data_sz;
     172         774 :   fd_memcpy( account->data, data, data_sz );
     173             : 
     174         774 :   return FD_EXECUTOR_INSTR_SUCCESS;
     175         774 : }
     176             : 
     177             : int
     178             : fd_account_set_data_length( fd_exec_instr_ctx_t const * ctx,
     179             :                             ulong                       instr_acc_idx,
     180        3021 :                             ulong                       new_len ) {
     181             : 
     182        3021 :   fd_borrowed_account_t * account = NULL;
     183        3021 :   do {
     184        3021 :     int err = fd_instr_borrowed_account_view_idx( ctx, (uchar)instr_acc_idx, &account );
     185        3021 :     if( FD_UNLIKELY( err ) ) {
     186           0 :       FD_LOG_ERR(( "fd_instr_borrowed_account_view_idx failed (%d-%s)", err, fd_acc_mgr_strerror( err ) ));
     187           0 :     }
     188        3021 :   } while(0);
     189             : 
     190        3021 :   int err = FD_EXECUTOR_INSTR_SUCCESS;
     191        3021 :   if( !fd_account_can_data_be_resized( ctx, account->const_meta, new_len, &err ) ) {
     192           3 :     return err;
     193           3 :   }
     194             : 
     195        3018 :   if( !fd_account_can_data_be_changed( ctx->instr, instr_acc_idx, &err ) ) {
     196         318 :     return err;
     197         318 :   }
     198             : 
     199        2700 :   ulong old_len = account->const_meta->dlen;
     200             : 
     201             :   /* Don't copy the account if the length does not change */
     202        2700 :   if( old_len==new_len ) {
     203          45 :     return FD_EXECUTOR_INSTR_SUCCESS;
     204          45 :   }
     205             : 
     206             :   /* Agave self.touch() is a no-op */
     207             : 
     208        2655 :   if( !fd_account_update_accounts_resize_delta( ctx, instr_acc_idx, new_len, &err ) ) {
     209           0 :     return err;
     210           0 :   }
     211             : 
     212        2655 :   do {
     213        2655 :     int err = fd_instr_borrowed_account_modify_idx( ctx, (uchar)instr_acc_idx, new_len, &account );
     214        2655 :     if( FD_UNLIKELY( err ) ) {
     215           0 :       FD_LOG_ERR(( "fd_instr_borrowed_account_modify_idx failed (%d-%s)", err, fd_acc_mgr_strerror( err ) ));
     216           0 :     }
     217        2655 :   } while(0);
     218             : 
     219        2655 :   if( new_len>old_len ) {
     220        2529 :     fd_memset( account->data+old_len, 0, new_len-old_len );
     221        2529 :   }
     222             : 
     223        2655 :   account->meta->dlen = new_len;
     224        2655 :   return FD_EXECUTOR_INSTR_SUCCESS;
     225        2655 : }
     226             : 
     227             : int
     228             : fd_account_set_executable( fd_exec_instr_ctx_t const * ctx,
     229             :                            ulong                       instr_acc_idx,
     230           0 :                            int                         is_executable ) {
     231             : 
     232           0 :   fd_instr_info_t const * instr = ctx->instr;
     233             : 
     234           0 :   fd_borrowed_account_t * account = NULL;
     235           0 :   do {
     236           0 :     int err = fd_instr_borrowed_account_view_idx( ctx, (uchar)instr_acc_idx, &account );
     237           0 :     if( FD_UNLIKELY( err ) ) {
     238           0 :       FD_LOG_ERR(( "fd_instr_borrowed_account_view_idx failed (%d-%s)", err, fd_acc_mgr_strerror( err ) ));
     239           0 :     }
     240           0 :   } while(0);
     241             : 
     242           0 :   fd_account_meta_t const * meta = account->const_meta;
     243             : 
     244             :   /* To become executable an account must be rent exempt */
     245           0 :   fd_epoch_bank_t const * epoch_bank = fd_exec_epoch_ctx_epoch_bank_const( ctx->epoch_ctx );
     246           0 :   fd_rent_t const * rent = &epoch_bank->rent;
     247           0 :   if( FD_UNLIKELY( !fd_rent_exempt_minimum_balance( rent, meta->dlen ) ) ) {
     248           0 :     return FD_EXECUTOR_INSTR_ERR_EXECUTABLE_ACCOUNT_NOT_RENT_EXEMPT;
     249           0 :   }
     250             : 
     251             :   /* Only the owner can set the exectuable flag */
     252           0 :   if( FD_UNLIKELY( !fd_account_is_owned_by_current_program( instr, meta ) ) ) {
     253           0 :     return FD_EXECUTOR_INSTR_ERR_EXECUTABLE_MODIFIED;
     254           0 :   }
     255             : 
     256             :   /* And only if the account is writable */
     257           0 :   if( FD_UNLIKELY( !fd_instr_acc_is_writable_idx( instr, instr_acc_idx ) ) ) {
     258           0 :     return FD_EXECUTOR_INSTR_ERR_EXECUTABLE_MODIFIED;
     259           0 :   }
     260             : 
     261             :   /* One can not clear the executable flag  */
     262           0 :   if( FD_UNLIKELY( fd_account_is_executable( meta ) && !is_executable ) ) {
     263           0 :     return FD_EXECUTOR_INSTR_ERR_EXECUTABLE_MODIFIED;
     264           0 :   }
     265             : 
     266             :   /* Don't copy the account if the exectuable flag does not change */
     267           0 :   if( fd_account_is_executable( meta ) == is_executable ) {
     268           0 :     return FD_EXECUTOR_INSTR_SUCCESS;
     269           0 :   }
     270             : 
     271           0 :   do {
     272           0 :     int err = fd_instr_borrowed_account_modify_idx( ctx, (uchar)instr_acc_idx, 0UL, &account );
     273           0 :     if( FD_UNLIKELY( err ) ) {
     274           0 :       FD_LOG_ERR(( "fd_instr_borrowed_account_modify_idx failed (%d-%s)", err, fd_acc_mgr_strerror( err ) ));
     275           0 :     }
     276           0 :   } while(0);
     277             : 
     278             :   /* Agave self.touch() is a no-op */
     279             : 
     280           0 :   account->meta->info.executable = !!is_executable;
     281             : 
     282           0 :   return FD_EXECUTOR_INSTR_SUCCESS;
     283           0 : }
     284             : 
     285             : /* https://github.com/anza-xyz/agave/blob/b5f5c3cdd3f9a5859c49ebc27221dc27e143d760/sdk/src/transaction_context.rs#L1128-L1138 */
     286             : int
     287             : fd_account_update_accounts_resize_delta( fd_exec_instr_ctx_t const * ctx,
     288             :                                          ulong                       instr_acc_idx,
     289             :                                          ulong                       new_len,
     290        3429 :                                          int *                       err ) {
     291             : 
     292        3429 :   fd_borrowed_account_t * account = NULL;
     293        3429 :   *err = fd_instr_borrowed_account_view_idx( ctx, (uchar)instr_acc_idx, &account );
     294        3429 :   if( FD_UNLIKELY( *err ) ) {
     295           0 :     return 0;
     296           0 :   }
     297             : 
     298        3429 :   ulong size_delta = fd_ulong_sat_sub( new_len, account->const_meta->dlen );
     299             : 
     300             :   /* TODO: The size delta should never exceed the value of ULONG_MAX so this
     301             :      could be replaced with a normal addition. However to match execution with
     302             :      the agave client, this is being left as a sat add */
     303        3429 :   ctx->txn_ctx->accounts_resize_delta = fd_ulong_sat_add( ctx->txn_ctx->accounts_resize_delta, size_delta );
     304        3429 :   *err = FD_EXECUTOR_INSTR_SUCCESS;
     305        3429 :   return 1;
     306        3429 : }

Generated by: LCOV version 1.14