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

Generated by: LCOV version 1.14