LCOV - code coverage report
Current view: top level - flamenco/runtime - fd_core_bpf_migration.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 289 0.0 %
Date: 2025-11-23 04:57:59 Functions: 0 10 0.0 %

          Line data    Source code
       1             : #include "sysvar/fd_sysvar_rent.h"
       2             : #include "program/fd_bpf_loader_program.h"
       3             : #include "program/fd_builtin_programs.h"
       4             : #include "fd_runtime_stack.h"
       5             : #include "fd_pubkey_utils.h"
       6             : #include "fd_system_ids.h"
       7             : #include "fd_acc_mgr.h"
       8             : #include "fd_hashes.h"
       9             : #include <assert.h>
      10             : 
      11             : static fd_pubkey_t
      12           0 : get_program_data_address( fd_pubkey_t const * program_addr ) {
      13           0 :   uchar const * seed    = program_addr->uc;
      14           0 :   ulong         seed_sz = 32UL;
      15           0 :   fd_pubkey_t   out;
      16           0 :   uint          custom_err;
      17           0 :   uchar         out_bump_seed;
      18           0 :   fd_pubkey_find_program_address( &fd_solana_bpf_loader_upgradeable_program_id, 1UL, &seed, &seed_sz, &out, &out_bump_seed, &custom_err );
      19           0 :   return out;
      20           0 : }
      21             : 
      22             : fd_tmp_account_t *
      23             : tmp_account_new( fd_tmp_account_t * acc,
      24           0 :                  ulong              acc_sz ) {
      25           0 :   acc->data_sz = acc_sz;
      26           0 :   fd_memset( acc->data, 0, acc_sz );
      27           0 :   return acc;
      28           0 : }
      29             : 
      30             : fd_tmp_account_t *
      31             : tmp_account_read( fd_tmp_account_t *        acc,
      32             :                   fd_funk_t *               funk,
      33             :                   fd_funk_txn_xid_t const * xid,
      34           0 :                   fd_pubkey_t const *       addr ) {
      35           0 :   int opt_err = 0;
      36           0 :   fd_account_meta_t const * meta = fd_funk_get_acc_meta_readonly(
      37           0 :       funk,
      38           0 :       xid,
      39           0 :       addr,
      40           0 :       NULL,
      41           0 :       &opt_err,
      42           0 :       NULL );
      43           0 :   if( FD_UNLIKELY( opt_err!=FD_ACC_MGR_SUCCESS ) ) {
      44           0 :     if( FD_LIKELY( opt_err==FD_ACC_MGR_ERR_UNKNOWN_ACCOUNT ) ) return NULL;
      45           0 :     FD_LOG_CRIT(( "fd_funk_get_acc_meta_readonly failed (%d)", opt_err ));
      46           0 :   }
      47           0 :   tmp_account_new( acc, meta->dlen );
      48           0 :   acc->meta = *meta;
      49           0 :   acc->addr = *addr;
      50           0 :   fd_memcpy( acc->data, fd_account_meta_get_data_const( meta ), meta->dlen );
      51           0 :   acc->data_sz = meta->dlen;
      52           0 :   return acc;
      53           0 : }
      54             : 
      55             : void
      56             : tmp_account_store( fd_tmp_account_t *        acc,
      57             :                    fd_accdb_user_t *         accdb,
      58             :                    fd_funk_txn_xid_t const * xid,
      59             :                    fd_bank_t *               bank,
      60           0 :                    fd_capture_ctx_t *        capture_ctx ) {
      61           0 :   if( FD_UNLIKELY( fd_pubkey_eq( &acc->addr, &fd_solana_system_program_id ) ) ) {
      62           0 :     FD_LOG_ERR(( "Attempted to write to the system program account" ));
      63           0 :   }
      64             : 
      65             :   /* FIXME usage of "txn_account" */
      66           0 :   fd_txn_account_t rec[1];
      67           0 :   fd_funk_rec_prepare_t prepare = {0};
      68           0 :   int ok = !!fd_txn_account_init_from_funk_mutable(
      69           0 :       rec,
      70           0 :       &acc->addr,
      71           0 :       accdb,
      72           0 :       xid,
      73           0 :       1,
      74           0 :       acc->data_sz,
      75           0 :       &prepare );
      76           0 :   if( FD_UNLIKELY( !ok ) ) {
      77           0 :     FD_LOG_CRIT(( "fd_txn_account_init_from_funk_mutable failed" ));
      78           0 :   }
      79             : 
      80           0 :   fd_lthash_value_t prev_hash[1];
      81           0 :   fd_hashes_account_lthash( &acc->addr, fd_txn_account_get_meta( rec ), fd_txn_account_get_data( rec ), prev_hash );
      82             : 
      83           0 :   fd_txn_account_set_executable( rec, acc->meta.executable    );
      84           0 :   fd_txn_account_set_owner     ( rec, fd_type_pun_const( acc->meta.owner ) );
      85           0 :   fd_txn_account_set_lamports  ( rec, acc->meta.lamports      );
      86           0 :   fd_txn_account_set_data      ( rec, acc->data, acc->data_sz );
      87             : 
      88           0 :   fd_hashes_update_lthash( rec, prev_hash, bank, capture_ctx );
      89           0 :   fd_txn_account_mutable_fini( rec, accdb, &prepare );
      90           0 : }
      91             : 
      92             : /* https://github.com/anza-xyz/agave/blob/v3.0.2/runtime/src/bank/builtins/core_bpf_migration/target_core_bpf.rs#L12 */
      93             : 
      94             : struct target_core_bpf {
      95             :   fd_pubkey_t        program_address;
      96             :   fd_tmp_account_t * program_data_account;
      97             :   fd_pubkey_t        upgrade_authority_address;
      98             :   uint               has_upgrade_authority_address : 1;
      99             : };
     100             : 
     101             : typedef struct target_core_bpf target_core_bpf_t;
     102             : 
     103             : /* https://github.com/anza-xyz/agave/blob/v3.0.2/runtime/src/bank/builtins/core_bpf_migration/target_builtin.rs#L13 */
     104             : 
     105             : struct target_builtin {
     106             :   fd_tmp_account_t * program_account;
     107             :   fd_pubkey_t        program_data_address;
     108             : };
     109             : 
     110             : typedef struct target_builtin target_builtin_t;
     111             : 
     112             : /* https://github.com/anza-xyz/agave/blob/v3.0.2/runtime/src/bank/builtins/core_bpf_migration/target_builtin.rs#L22 */
     113             : 
     114             : target_builtin_t *
     115             : target_builtin_new_checked( target_builtin_t *        target_builtin,
     116             :                             fd_pubkey_t const *       program_address,
     117             :                             int                       migration_target,
     118             :                             fd_funk_t *               funk,
     119             :                             fd_funk_txn_xid_t const * xid,
     120           0 :                             fd_runtime_stack_t *      runtime_stack ) {
     121             : 
     122             :   /* https://github.com/anza-xyz/agave/blob/v3.0.2/runtime/src/bank/builtins/core_bpf_migration/target_builtin.rs#L27-L49 */
     123             : 
     124           0 :   fd_tmp_account_t * program_account = &runtime_stack->bpf_migration.program_account;
     125           0 :   switch( migration_target ) {
     126           0 :   case FD_CORE_BPF_MIGRATION_TARGET_BUILTIN:
     127           0 :     if( FD_UNLIKELY( !tmp_account_read( program_account, funk, xid, program_address ) ) ) {
     128             :       /* CoreBpfMigrationError::AccountNotFound(*program_address) */
     129           0 :       return NULL;
     130           0 :     }
     131           0 :     if( FD_UNLIKELY( 0!=memcmp( program_account->meta.owner, &fd_solana_native_loader_id, 32 ) ) ) {
     132             :       /* CoreBpfMigrationError::IncorrectOwner(*program_address) */
     133           0 :       return NULL;
     134           0 :     }
     135           0 :     break;
     136           0 :   case FD_CORE_BPF_MIGRATION_TARGET_STATELESS: {
     137             :     /* Program account should not exist */
     138           0 :     int opt_err = 0;
     139           0 :     fd_funk_get_acc_meta_readonly(
     140           0 :         funk,
     141           0 :         xid,
     142           0 :         program_address,
     143           0 :         NULL,
     144           0 :         &opt_err,
     145           0 :         NULL );
     146           0 :     if( opt_err==FD_ACC_MGR_SUCCESS ) {
     147             :       /* CoreBpfMigrationError::AccountAlreadyExists(*program_address) */
     148           0 :       return NULL;
     149           0 :     } else if( opt_err!=FD_ACC_MGR_ERR_UNKNOWN_ACCOUNT ) {
     150           0 :       FD_LOG_ERR(( "database error: %d", opt_err ));
     151           0 :     }
     152           0 :     break;
     153           0 :   }
     154           0 :   default:
     155           0 :     FD_LOG_ERR(( "invalid migration_target %d", migration_target ));
     156           0 :   }
     157             : 
     158             :   /* https://github.com/anza-xyz/agave/blob/v3.0.2/runtime/src/bank/builtins/core_bpf_migration/target_builtin.rs#L51 */
     159             : 
     160           0 :   fd_pubkey_t program_data_address = get_program_data_address( program_address );
     161             : 
     162             :   /* https://github.com/anza-xyz/agave/blob/v3.0.2/runtime/src/bank/builtins/core_bpf_migration/target_builtin.rs#L53-L61 */
     163             : 
     164           0 :   do {
     165             :     /* Program data account should not exist */
     166           0 :     int opt_err = 0;
     167           0 :     fd_funk_get_acc_meta_readonly(
     168           0 :         funk,
     169           0 :         xid,
     170           0 :         &program_data_address,
     171           0 :         NULL,
     172           0 :         &opt_err,
     173           0 :         NULL );
     174           0 :     if( opt_err==FD_ACC_MGR_SUCCESS ) {
     175             :       /* CoreBpfMigrationError::AccountAlreadyExists(*program_address) */
     176           0 :       return NULL;
     177           0 :     } else if( opt_err!=FD_ACC_MGR_ERR_UNKNOWN_ACCOUNT ) {
     178           0 :       FD_LOG_ERR(( "database error: %d", opt_err ));
     179           0 :     }
     180           0 :   } while(0);
     181             : 
     182             :   /* https://github.com/anza-xyz/agave/blob/v3.0.2/runtime/src/bank/builtins/core_bpf_migration/target_builtin.rs#L63-L67 */
     183             : 
     184           0 :   *target_builtin = (target_builtin_t) {
     185           0 :     .program_account      = program_account,
     186           0 :     .program_data_address = program_data_address
     187           0 :   };
     188           0 :   return target_builtin;
     189           0 : }
     190             : 
     191             : /* https://github.com/anza-xyz/agave/blob/v3.0.2/runtime/src/bank/builtins/core_bpf_migration/source_buffer.rs#L22-L49 */
     192             : 
     193             : static fd_tmp_account_t *
     194             : source_buffer_new_checked( fd_tmp_account_t *        acc,
     195             :                            fd_funk_t *               funk,
     196             :                            fd_funk_txn_xid_t const * xid,
     197           0 :                            fd_pubkey_t const *       pubkey ) {
     198             : 
     199           0 :   if( FD_UNLIKELY( !tmp_account_read( acc, funk, xid, pubkey ) ) ) {
     200             :     /* CoreBpfMigrationError::AccountNotFound(*buffer_address) */
     201           0 :     return NULL;
     202           0 :   }
     203             : 
     204           0 :   if( FD_UNLIKELY( 0!=memcmp( acc->meta.owner, &fd_solana_bpf_loader_upgradeable_program_id, 32 ) ) ) {
     205             :     /* CoreBpfMigrationError::IncorrectOwner(*buffer_address) */
     206           0 :     return NULL;
     207           0 :   }
     208             : 
     209           0 :   ulong const buffer_metadata_sz = 37UL;
     210           0 :   if( acc->data_sz < buffer_metadata_sz ) {
     211             :     /* CoreBpfMigrationError::InvalidBufferAccount(*buffer_address) */
     212           0 :     return NULL;
     213           0 :   }
     214             : 
     215           0 :   fd_bpf_upgradeable_loader_state_t state[1];
     216           0 :   if( FD_UNLIKELY( !fd_bincode_decode_static(
     217           0 :       bpf_upgradeable_loader_state, state,
     218           0 :       acc->data, acc->data_sz,
     219           0 :       NULL ) ) ) {
     220           0 :     return NULL;
     221           0 :   }
     222             : 
     223           0 :   return acc;
     224           0 : }
     225             : 
     226             : /* https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L82-L95 */
     227             : 
     228             : static fd_tmp_account_t *
     229             : new_target_program_account( fd_tmp_account_t *        acc,
     230             :                             target_builtin_t const *  target,
     231           0 :                             fd_rent_t const *         rent ) {
     232             :   /* https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L86-L88 */
     233           0 :   fd_bpf_upgradeable_loader_state_t state = {
     234           0 :     .discriminant = fd_bpf_upgradeable_loader_state_enum_program,
     235           0 :     .inner = {
     236           0 :       .program = {
     237           0 :         .programdata_address = target->program_data_address,
     238           0 :       }
     239           0 :     }
     240           0 :   };
     241             : 
     242           0 :   tmp_account_new( acc, fd_bpf_upgradeable_loader_state_size( &state ) );
     243           0 :   acc->meta.lamports   = fd_rent_exempt_minimum_balance( rent, SIZE_OF_PROGRAM );
     244           0 :   acc->meta.executable = 1;
     245           0 :   memcpy( acc->meta.owner, fd_solana_bpf_loader_upgradeable_program_id.uc, sizeof(fd_pubkey_t) );
     246             : 
     247           0 :   return acc;
     248           0 : }
     249             : 
     250             : /* https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L108-L153 */
     251             : static fd_tmp_account_t *
     252             : new_target_program_data_account( fd_tmp_account_t *       acc,
     253             :                                  fd_tmp_account_t const * source,
     254             :                                  fd_pubkey_t const *      upgrade_authority_address,
     255             :                                  fd_rent_t const *        rent,
     256           0 :                                  ulong                    slot ) {
     257           0 :   ulong const buffer_metadata_sz = BUFFER_METADATA_SIZE;
     258             : 
     259           0 :   if( FD_UNLIKELY( source->data_sz < buffer_metadata_sz ) )
     260           0 :     return NULL; /* CoreBpfMigrationError::InvalidBufferAccount */
     261             : 
     262           0 :   fd_bpf_upgradeable_loader_state_t state;
     263           0 :   if( !fd_bincode_decode_static(
     264           0 :       bpf_upgradeable_loader_state,
     265           0 :       &state,
     266           0 :       source->data,
     267           0 :       buffer_metadata_sz,
     268           0 :       NULL ) )
     269           0 :     return NULL;
     270             : 
     271           0 :   if( FD_UNLIKELY( state.discriminant!=fd_bpf_upgradeable_loader_state_enum_buffer ) )
     272           0 :     return NULL; /* CoreBpfMigrationError::InvalidBufferAccount */
     273             : 
     274           0 :   if( FD_UNLIKELY( state.inner.buffer.has_authority_address != (!!upgrade_authority_address) ) )
     275           0 :     return NULL; /* CoreBpfMigrationError::InvalidBufferAccount */
     276             : 
     277           0 :   if( FD_UNLIKELY( upgrade_authority_address &&
     278           0 :                    !fd_pubkey_eq( upgrade_authority_address, &state.inner.buffer.authority_address ) ) )
     279           0 :     return NULL; /* CoreBpfMigrationError::UpgradeAuthorityMismatch */
     280             : 
     281           0 :   void const * elf      = (uchar const *)source->data    + buffer_metadata_sz;
     282           0 :   ulong        elf_sz   = /*           */source->data_sz - buffer_metadata_sz;
     283             : 
     284           0 :   ulong        space    = PROGRAMDATA_METADATA_SIZE + elf_sz;
     285           0 :   ulong        lamports = fd_rent_exempt_minimum_balance( rent, space );
     286           0 :   fd_pubkey_t  owner    = fd_solana_bpf_loader_upgradeable_program_id;
     287             : 
     288           0 :   fd_bpf_upgradeable_loader_state_t programdata_meta = {
     289           0 :     .discriminant = fd_bpf_upgradeable_loader_state_enum_program_data,
     290           0 :     .inner = {
     291           0 :       .program_data = {
     292           0 :         .slot = slot,
     293           0 :         .has_upgrade_authority_address = !!upgrade_authority_address,
     294           0 :         .upgrade_authority_address     = upgrade_authority_address ? *upgrade_authority_address : (fd_pubkey_t){{0}}
     295           0 :       }
     296           0 :     }
     297           0 :   };
     298             : 
     299           0 :   tmp_account_new( acc, space );
     300           0 :   acc->meta.lamports = lamports;
     301           0 :   memcpy( acc->meta.owner, owner.uc, sizeof(fd_pubkey_t) );
     302           0 :   fd_bincode_encode_ctx_t ctx = { .data=acc->data, .dataend=(uchar *)acc->data+PROGRAMDATA_METADATA_SIZE };
     303           0 :   if( FD_UNLIKELY( fd_bpf_upgradeable_loader_state_encode( &programdata_meta, &ctx )!=FD_BINCODE_SUCCESS ) ) {
     304           0 :     FD_LOG_ERR(( "fd_bpf_upgradeable_loader_state_encode failed" ));
     305           0 :   }
     306           0 :   fd_memcpy( (uchar *)acc->data+PROGRAMDATA_METADATA_SIZE, elf, elf_sz );
     307             : 
     308           0 :   return acc;
     309           0 : }
     310             : 
     311             : void
     312             : migrate_builtin_to_core_bpf1( fd_core_bpf_migration_config_t const * config,
     313             :                               fd_accdb_user_t *                      accdb,
     314             :                               fd_funk_txn_xid_t const *              xid,
     315             :                               fd_bank_t *                            bank,
     316             :                               fd_runtime_stack_t *                   runtime_stack,
     317             :                               fd_pubkey_t const *                    builtin_program_id,
     318           0 :                               fd_capture_ctx_t *                     capture_ctx ) {
     319             : 
     320           0 :   target_builtin_t target[1];
     321           0 :   if( FD_UNLIKELY( !target_builtin_new_checked(
     322           0 :       target,
     323           0 :       builtin_program_id,
     324           0 :       config->migration_target,
     325           0 :       accdb->funk,
     326           0 :       xid,
     327           0 :       runtime_stack ) ) )
     328           0 :     return;
     329             : 
     330           0 :   fd_tmp_account_t * source = &runtime_stack->bpf_migration.source;
     331           0 :   if( FD_UNLIKELY( !source_buffer_new_checked(
     332           0 :       source,
     333           0 :       accdb->funk,
     334           0 :       xid,
     335           0 :       config->source_buffer_address ) ) )
     336           0 :     return;
     337             : 
     338           0 :   fd_rent_t const * rent = fd_bank_rent_query( bank );
     339           0 :   ulong const       slot = fd_bank_slot_get  ( bank );
     340             : 
     341           0 :   fd_tmp_account_t * new_target_program = &runtime_stack->bpf_migration.new_target_program;
     342           0 :   if( FD_UNLIKELY( !new_target_program_account(
     343           0 :       new_target_program,
     344           0 :       target,
     345           0 :       rent ) ) )
     346           0 :     return;
     347             : 
     348           0 :   fd_tmp_account_t * new_target_program_data = &runtime_stack->bpf_migration.new_target_program_data;
     349           0 :   if( FD_UNLIKELY( !new_target_program_data_account(
     350           0 :       new_target_program_data,
     351           0 :       source,
     352           0 :       config->upgrade_authority_address,
     353           0 :       rent,
     354           0 :       slot ) ) )
     355           0 :     return;
     356             : 
     357           0 :   ulong old_data_sz;
     358           0 :   if( FD_UNLIKELY( __builtin_uaddl_overflow(
     359           0 :       target->program_account->data_sz,
     360           0 :       source->data_sz,
     361           0 :       &old_data_sz ) ) ) {
     362           0 :     return;
     363           0 :   }
     364           0 :   ulong new_data_sz;
     365           0 :   if( FD_UNLIKELY( __builtin_uaddl_overflow(
     366           0 :       new_target_program     ->data_sz,
     367           0 :       new_target_program_data->data_sz,
     368           0 :       &new_data_sz ) ) ) {
     369           0 :     return;
     370           0 :   }
     371             : 
     372           0 :   assert( new_target_program_data->data_sz>=PROGRAMDATA_METADATA_SIZE );
     373           0 :   if( FD_UNLIKELY( !fd_directly_invoke_loader_v3_deploy(
     374           0 :       bank,
     375           0 :       accdb->funk,
     376           0 :       accdb->funk->shmem,
     377           0 :       &target->program_account->addr,
     378           0 :       new_target_program_data->data   +PROGRAMDATA_METADATA_SIZE,
     379           0 :       new_target_program_data->data_sz-PROGRAMDATA_METADATA_SIZE ) ) ) {
     380           0 :     return;
     381           0 :   }
     382             : 
     383           0 :   ulong lamports_to_burn;
     384           0 :   if( FD_UNLIKELY( __builtin_uaddl_overflow(
     385           0 :       target->program_account->meta.lamports,
     386           0 :       source->meta.lamports,
     387           0 :       &lamports_to_burn ) ) ) {
     388           0 :     return;
     389           0 :   }
     390           0 :   ulong lamports_to_fund;
     391           0 :   if( FD_UNLIKELY( __builtin_uaddl_overflow(
     392           0 :       new_target_program     ->meta.lamports,
     393           0 :       new_target_program_data->meta.lamports,
     394           0 :       &lamports_to_fund ) ) ) {
     395           0 :     return;
     396           0 :   }
     397             : 
     398             :   /* https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L286-L297 */
     399           0 :   ulong capitalization = fd_bank_capitalization_get( bank );
     400           0 :   int cap_ok = 1;
     401           0 :   if( lamports_to_burn>lamports_to_fund ) {
     402           0 :     cap_ok = __builtin_usubl_overflow(
     403           0 :         capitalization,
     404           0 :         (lamports_to_burn-lamports_to_fund),
     405           0 :         &capitalization );
     406           0 :   } else if( lamports_to_burn<lamports_to_fund ) {
     407           0 :     cap_ok = __builtin_uaddl_overflow(
     408           0 :         capitalization,
     409           0 :         (lamports_to_fund-lamports_to_burn),
     410           0 :         &capitalization );
     411           0 :   }
     412           0 :   if( FD_UNLIKELY( !cap_ok ) ) {
     413           0 :     FD_LOG_ERR(( "Capitalization overflow while migrating builtin program to core BPF" ));
     414           0 :   }
     415           0 :   fd_bank_capitalization_set( bank, capitalization );
     416             : 
     417             :   /* Write back accounts */
     418           0 :   tmp_account_store( new_target_program,      accdb, xid, bank, capture_ctx );
     419           0 :   tmp_account_store( new_target_program_data, accdb, xid, bank, capture_ctx );
     420           0 :   fd_tmp_account_t * empty = &runtime_stack->bpf_migration.empty;
     421           0 :   tmp_account_new( empty, 0UL );
     422           0 :   empty->addr = source->addr;
     423           0 :   tmp_account_store( empty, accdb, xid, bank, capture_ctx );
     424             : 
     425             :   /* FIXME "remove the built-in program from the bank's list of builtins" */
     426             :   /* FIXME "update account data size delta" */
     427           0 : }
     428             : 
     429             : /* Mimics migrate_builtin_to_core_bpf().
     430             :    https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L235-L318 */
     431             : void
     432             : fd_migrate_builtin_to_core_bpf( fd_bank_t *                            bank,
     433             :                                 fd_accdb_user_t *                      accdb,
     434             :                                 fd_funk_txn_xid_t const *              xid,
     435             :                                 fd_runtime_stack_t *                   runtime_stack,
     436             :                                 fd_core_bpf_migration_config_t const * config,
     437           0 :                                 fd_capture_ctx_t *                     capture_ctx ) {
     438           0 :   migrate_builtin_to_core_bpf1( config, accdb, xid, bank, runtime_stack, config->builtin_program_id, capture_ctx );
     439           0 : }

Generated by: LCOV version 1.14