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 274 0.0 %
Date: 2025-10-13 04:42:14 Functions: 0 5 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_pubkey_utils.h"
       5             : 
       6             : /* Mimics bank.new_target_program_account(). Assumes out_rec is a
       7             :    modifiable record.
       8             : 
       9             :    From the calling context, out_rec points to a native program record
      10             :    (e.g. Config, ALUT native programs). There should be enough space in
      11             :    out_rec->data to hold at least 36 bytes (the size of a BPF
      12             :    upgradeable program account) when calling this function. The native
      13             :    program account's owner is set to the BPF loader upgradeable program
      14             :    ID, and lamports are increased / deducted to contain the rent exempt
      15             :    minimum balance.
      16             : 
      17             :    https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L79-L95 */
      18             : static int
      19             : fd_new_target_program_account( fd_bank_t *         bank,
      20             :                                fd_pubkey_t const * target_program_data_address,
      21           0 :                                fd_txn_account_t *  out_rec ) {
      22             :   /* https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L86-L88 */
      23           0 :   fd_bpf_upgradeable_loader_state_t state = {
      24           0 :     .discriminant = fd_bpf_upgradeable_loader_state_enum_program,
      25           0 :     .inner = {
      26           0 :       .program = {
      27           0 :         .programdata_address = *target_program_data_address,
      28           0 :       }
      29           0 :     }
      30           0 :   };
      31             : 
      32             :   /* https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L89-L90 */
      33           0 :   fd_rent_t const * rent = fd_bank_rent_query( bank );
      34           0 :   if( FD_UNLIKELY( rent==NULL ) ) {
      35           0 :     return -1;
      36           0 :   }
      37             : 
      38           0 :   fd_txn_account_set_lamports( out_rec, fd_rent_exempt_minimum_balance( rent, SIZE_OF_PROGRAM ) );
      39           0 :   fd_bincode_encode_ctx_t ctx = {
      40           0 :     .data    = fd_txn_account_get_data_mut( out_rec ),
      41           0 :     .dataend = fd_txn_account_get_data_mut( out_rec ) + SIZE_OF_PROGRAM,
      42           0 :   };
      43             : 
      44             :   /* https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L91-L9 */
      45           0 :   int err = fd_bpf_upgradeable_loader_state_encode( &state, &ctx );
      46           0 :   if( FD_UNLIKELY( err ) ) {
      47           0 :     return err;
      48           0 :   }
      49           0 :   fd_txn_account_set_owner( out_rec, &fd_solana_bpf_loader_upgradeable_program_id );
      50             : 
      51             :   /* https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L93-L94 */
      52           0 :   fd_txn_account_set_executable( out_rec, 1 );
      53           0 :   return FD_RUNTIME_EXECUTE_SUCCESS;
      54           0 : }
      55             : 
      56             : /* Mimics bank.new_target_program_data_account(). Assumes
      57             :    new_target_program_data_account is a modifiable record.
      58             :    config_upgrade_authority_address may be NULL.
      59             : 
      60             :    This function uses an existing buffer account buffer_acc_rec to set
      61             :    the program data account data for a core program BPF migration. Sets
      62             :    the lamports and data fields of new_target_program_data_account
      63             :    based on the ELF data length, and sets the owner to the BPF loader
      64             :    upgradeable program ID.
      65             : 
      66             :    https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L97-L153 */
      67             : static int
      68             : fd_new_target_program_data_account( fd_bank_t *          bank,
      69             :                                     fd_pubkey_t *        config_upgrade_authority_address,
      70             :                                     fd_txn_account_t *   buffer_acc_rec,
      71             :                                     fd_txn_account_t *   new_target_program_data_account,
      72           0 :                                     fd_spad_t *          runtime_spad ) {
      73             : 
      74           0 :   FD_SPAD_FRAME_BEGIN( runtime_spad ) {
      75             : 
      76             :   /* https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L113-L116 */
      77           0 :   int err;
      78           0 :   fd_bpf_upgradeable_loader_state_t * state = fd_bincode_decode_spad(
      79           0 :       bpf_upgradeable_loader_state, runtime_spad,
      80           0 :       fd_txn_account_get_data( buffer_acc_rec ),
      81           0 :       fd_txn_account_get_data_len( buffer_acc_rec ),
      82           0 :       &err );
      83           0 :   if( FD_UNLIKELY( err ) ) return err;
      84             : 
      85           0 :   if( FD_UNLIKELY( !fd_bpf_upgradeable_loader_state_is_buffer( state ) ) ) {
      86           0 :     return -1;
      87           0 :   }
      88             : 
      89             :   /* https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L118-L125 */
      90           0 :   if( config_upgrade_authority_address!=NULL ) {
      91           0 :     if( FD_UNLIKELY( !state->inner.buffer.has_authority_address ||
      92           0 :                      !fd_pubkey_eq( config_upgrade_authority_address, &state->inner.buffer.authority_address ) ) ) {
      93           0 :       return -1;
      94           0 :     }
      95           0 :   }
      96             : 
      97             :   /* https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L127-L132 */
      98           0 :   fd_rent_t const * rent = fd_bank_rent_query( bank );
      99           0 :   if( FD_UNLIKELY( rent==NULL ) ) {
     100           0 :     return -1;
     101           0 :   }
     102             : 
     103           0 :   const uchar * elf = fd_txn_account_get_data( buffer_acc_rec ) + BUFFER_METADATA_SIZE;
     104           0 :   ulong space = PROGRAMDATA_METADATA_SIZE - BUFFER_METADATA_SIZE + fd_txn_account_get_data_len( buffer_acc_rec );
     105           0 :   ulong lamports = fd_rent_exempt_minimum_balance( rent, space );
     106             : 
     107             :   /* https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L134-L137 */
     108           0 :   fd_bpf_upgradeable_loader_state_t programdata_metadata = {
     109           0 :     .discriminant = fd_bpf_upgradeable_loader_state_enum_program_data,
     110           0 :     .inner = {
     111           0 :       .program_data = {
     112           0 :         .slot = fd_bank_slot_get( bank ),
     113           0 :         .has_upgrade_authority_address = !!config_upgrade_authority_address,
     114           0 :         .upgrade_authority_address     = config_upgrade_authority_address ? *config_upgrade_authority_address : (fd_pubkey_t){{0}}
     115           0 :       }
     116           0 :     }
     117           0 :   };
     118             : 
     119             :   /* https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L139-L144 */
     120           0 :   fd_txn_account_set_lamports( new_target_program_data_account, lamports );
     121           0 :   fd_bincode_encode_ctx_t encode_ctx = {
     122           0 :     .data    = fd_txn_account_get_data_mut( new_target_program_data_account ),
     123           0 :     .dataend = fd_txn_account_get_data_mut( new_target_program_data_account ) + PROGRAMDATA_METADATA_SIZE,
     124           0 :   };
     125           0 :   err = fd_bpf_upgradeable_loader_state_encode( &programdata_metadata, &encode_ctx );
     126           0 :   if( FD_UNLIKELY( err ) ) {
     127           0 :     return err;
     128           0 :   }
     129           0 :   fd_txn_account_set_owner( new_target_program_data_account, &fd_solana_bpf_loader_upgradeable_program_id );
     130             : 
     131             :   /* Copy the ELF data over
     132             :      https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L145 */
     133           0 :   fd_memcpy( fd_txn_account_get_data_mut( new_target_program_data_account ) + PROGRAMDATA_METADATA_SIZE, elf, fd_txn_account_get_data_len( buffer_acc_rec ) - BUFFER_METADATA_SIZE );
     134             : 
     135           0 :   return FD_RUNTIME_EXECUTE_SUCCESS;
     136             : 
     137           0 :   } FD_SPAD_FRAME_END;
     138           0 : }
     139             : 
     140             : /* Initializes a source buffer account from funk. Returns 1 if the
     141             :    buffer account does not exist or is not owned by the upgradeable
     142             :    loader. Returns 0 on success.
     143             : 
     144             :    https://github.com/anza-xyz/agave/blob/v2.3.0/runtime/src/bank/builtins/core_bpf_migration/source_buffer.rs#L22-L49 */
     145             : static int
     146             : fd_source_buffer_account_new( fd_funk_t *               funk,
     147             :                               fd_funk_txn_xid_t const * xid,
     148             :                               fd_txn_account_t *        buffer_account,
     149             :                               fd_pubkey_t const *       buffer_address,
     150           0 :                               fd_funk_rec_prepare_t *   prepare ) {
     151             :   /* The buffer account should exist.
     152             :      https://github.com/anza-xyz/agave/blob/v2.3.0/runtime/src/bank/builtins/core_bpf_migration/source_buffer.rs#L27-L29 */
     153           0 :   if( FD_UNLIKELY( fd_txn_account_init_from_funk_mutable( buffer_account, buffer_address, funk, xid, 0, 0UL, prepare )!=FD_ACC_MGR_SUCCESS ) ) {
     154           0 :     FD_LOG_DEBUG(( "Buffer account %s does not exist, skipping migration...", FD_BASE58_ENC_32_ALLOCA( buffer_address ) ));
     155           0 :     return 1;
     156           0 :   }
     157             : 
     158             :   /* The buffer account should be owned by the upgradeable loader.
     159             :      https://github.com/anza-xyz/agave/blob/v2.3.0/runtime/src/bank/builtins/core_bpf_migration/source_buffer.rs#L31-L34 */
     160           0 :   if( FD_UNLIKELY( memcmp( fd_txn_account_get_owner( buffer_account ), fd_solana_bpf_loader_upgradeable_program_id.uc, sizeof(fd_pubkey_t) ) ) ) {
     161           0 :     FD_LOG_DEBUG(( "Buffer account %s is not owned by the upgradeable loader, skipping migration...", FD_BASE58_ENC_32_ALLOCA( buffer_address ) ));
     162           0 :     return 1;
     163           0 :   }
     164             : 
     165             :   /* The buffer account should have the correct state. We already check
     166             :      the buffer account state in fd_new_target_program_data_account(),
     167             :      so we can skip the checks here.
     168             :      https://github.com/anza-xyz/agave/blob/v2.3.0/runtime/src/bank/builtins/core_bpf_migration/source_buffer.rs#L37-L47 */
     169             : 
     170           0 :   return 0;
     171           0 : }
     172             : 
     173             : /* Similar to fd_source_buffer_account_new() but also checks the build
     174             :    hash of the buffer account for verification. verified_build_hash must
     175             :    be valid and non-NULL. Returns 1 if fd_source_buffer_account_new()
     176             :    fails, the buffer dlen is too small, or if the build hash mismatches.
     177             :    Returns 0 on success.
     178             : 
     179             :    https://github.com/anza-xyz/agave/blob/v2.3.0/runtime/src/bank/builtins/core_bpf_migration/source_buffer.rs#L51-L75 */
     180             : static int
     181             : fd_source_buffer_account_new_with_hash( fd_funk_t *               funk,
     182             :                                         fd_funk_txn_xid_t const * xid,
     183             :                                         fd_txn_account_t *        buffer_account,
     184             :                                         fd_pubkey_t const *       buffer_address,
     185             :                                         fd_hash_t const *         verified_build_hash,
     186           0 :                                         fd_funk_rec_prepare_t *   prepare ) {
     187             :   /* https://github.com/anza-xyz/agave/blob/v2.3.0/runtime/src/bank/builtins/core_bpf_migration/source_buffer.rs#L58 */
     188           0 :   int err = fd_source_buffer_account_new( funk, xid, buffer_account, buffer_address, prepare );
     189           0 :   if( FD_UNLIKELY( err ) ) {
     190           0 :     return err;
     191           0 :   }
     192             : 
     193             :   /* https://github.com/anza-xyz/agave/blob/v2.3.0/runtime/src/bank/builtins/core_bpf_migration/source_buffer.rs#L59 */
     194           0 :   uchar const * data = fd_txn_account_get_data( buffer_account );
     195           0 :   ulong         data_len = fd_txn_account_get_data_len( buffer_account );
     196             : 
     197             :   /* https://github.com/anza-xyz/agave/blob/v2.3.0/runtime/src/bank/builtins/core_bpf_migration/source_buffer.rs#L61 */
     198           0 :   ulong offset = BUFFER_METADATA_SIZE;
     199           0 :   if( FD_UNLIKELY( data_len<offset ) ) {
     200           0 :     return 1;
     201           0 :   }
     202             : 
     203             :   /* Search for the first nonzero byte in the buffer account data starting
     204             :      from the right.
     205             :      https://github.com/anza-xyz/agave/blob/v2.3.0/runtime/src/bank/builtins/core_bpf_migration/source_buffer.rs#L62 */
     206           0 :   ulong end_offset = offset;
     207           0 :   for( ulong i=data_len-1UL; i>=offset; i-- ) {
     208           0 :     if( data[i]!=0 ) {
     209           0 :       end_offset = i+1UL;
     210           0 :       break;
     211           0 :     }
     212           0 :   }
     213             : 
     214             :   /* Compute and verify the hash.
     215             :      https://github.com/anza-xyz/agave/blob/v2.3.0/runtime/src/bank/builtins/core_bpf_migration/source_buffer.rs#L64-L71 */
     216           0 :   fd_hash_t hash[1];
     217           0 :   fd_sha256_hash( data+offset, end_offset-offset, hash );
     218           0 :   if( FD_UNLIKELY( memcmp( verified_build_hash, hash, sizeof(fd_hash_t) ) ) ) {
     219           0 :     FD_LOG_WARNING(( "Mismatching build hash for Buffer account %s (expected=%s, actual=%s). Skipping migration...", FD_BASE58_ENC_32_ALLOCA( buffer_address ), FD_BASE58_ENC_32_ALLOCA( verified_build_hash ), FD_BASE58_ENC_32_ALLOCA( hash ) ));
     220           0 :     return 1;
     221           0 :   }
     222             : 
     223           0 :   return 0;
     224           0 : }
     225             : 
     226             : /* Mimics migrate_builtin_to_core_bpf().
     227             :    https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L235-L318 */
     228             : void
     229             : fd_migrate_builtin_to_core_bpf( fd_bank_t *                            bank,
     230             :                                 fd_funk_t *                            funk,
     231             :                                 fd_funk_txn_xid_t const *              xid,
     232             :                                 fd_core_bpf_migration_config_t const * config,
     233           0 :                                 fd_spad_t *                            runtime_spad ) {
     234           0 :   int err;
     235             : 
     236             :   /* Initialize local variables from the config */
     237           0 :   fd_pubkey_t const * source_buffer_address     = config->source_buffer_address;
     238           0 :   fd_pubkey_t *       upgrade_authority_address = config->upgrade_authority_address;
     239           0 :   uchar               stateless                 = !!( config->migration_target==FD_CORE_BPF_MIGRATION_TARGET_STATELESS );
     240           0 :   fd_pubkey_t const * builtin_program_id        = config->builtin_program_id;
     241           0 :   fd_hash_t const *   verified_build_hash       = config->verified_build_hash;
     242             : 
     243             :   /* https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L242-L243
     244             : 
     245             :      The below logic is used to obtain a TargetBuiltin account. There
     246             :      are three fields of TargetBuiltin returned:
     247             :       - target.program_address: builtin_program_id
     248             :       - target.program_account:
     249             :           - if stateless: an AccountSharedData::default() (i.e. system
     250             :             program id, 0 lamports, 0 data, non-executable, system
     251             :             program owner)
     252             :           - if NOT stateless: the existing account (for us its called
     253             :             target_program_account)
     254             :       - target.program_data_address: target_program_data_address for
     255             :         us, derived below. */
     256             : 
     257             :   /* These checks will fail if the core program has already been migrated to BPF, since the account will exist + the program owner
     258             :      will no longer be the native loader.
     259             :      https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/target_builtin.rs#L23-L50 */
     260           0 :   FD_TXN_ACCOUNT_DECL( target_program_account );
     261           0 :   uchar program_exists = ( fd_txn_account_init_from_funk_readonly( target_program_account, builtin_program_id, funk, xid )==FD_ACC_MGR_SUCCESS );
     262           0 :   if( !stateless ) {
     263             :     /* The program account should exist.
     264             :        https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/target_builtin.rs#L30-L33 */
     265           0 :     if( FD_UNLIKELY( !program_exists ) ) {
     266           0 :       FD_LOG_DEBUG(( "Builtin program %s does not exist, skipping migration...", FD_BASE58_ENC_32_ALLOCA( builtin_program_id ) ));
     267           0 :       return;
     268           0 :     }
     269             : 
     270             :     /* The program account should be owned by the native loader.
     271             :        https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/target_builtin.rs#L35-L38 */
     272           0 :     if( FD_UNLIKELY( memcmp( fd_txn_account_get_owner( target_program_account ), fd_solana_native_loader_id.uc, sizeof(fd_pubkey_t) ) ) ) {
     273           0 :       FD_LOG_DEBUG(( "Builtin program %s is not owned by the native loader, skipping migration...", FD_BASE58_ENC_32_ALLOCA( builtin_program_id ) ));
     274           0 :       return;
     275           0 :     }
     276           0 :   } else {
     277             :     /* The program account should _not_ exist.
     278             :        https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/target_builtin.rs#L42-L46 */
     279           0 :     if( FD_UNLIKELY( program_exists ) ) {
     280           0 :       FD_LOG_DEBUG(( "Stateless program %s already exists, skipping migration...", FD_BASE58_ENC_32_ALLOCA( builtin_program_id ) ));
     281           0 :       return;
     282           0 :     }
     283           0 :   }
     284             : 
     285             :   /* The program data account should not exist.
     286             :      https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/target_builtin.rs#L52-L62 */
     287           0 :   uint custom_err = UINT_MAX;
     288           0 :   fd_pubkey_t target_program_data_address[ 1UL ];
     289           0 :   uchar const * seeds[ 1UL ];
     290           0 :   seeds[ 0UL ]    = (uchar const *)builtin_program_id;
     291           0 :   ulong seed_sz   = sizeof(fd_pubkey_t);
     292           0 :   uchar bump_seed = 0;
     293           0 :   err = fd_pubkey_find_program_address( &fd_solana_bpf_loader_upgradeable_program_id, 1UL, seeds, &seed_sz, target_program_data_address, &bump_seed, &custom_err );
     294           0 :   if( FD_UNLIKELY( err ) ) {
     295             :     /* TODO: We should handle these errors more gracefully instead of just killing the client. */
     296           0 :     FD_LOG_ERR(( "Unable to find a viable program address bump seed" )); // Solana panics, error code is undefined
     297           0 :     return;
     298           0 :   }
     299           0 :   FD_TXN_ACCOUNT_DECL( program_data_account );
     300           0 :   if( FD_UNLIKELY( fd_txn_account_init_from_funk_readonly( program_data_account, target_program_data_address, funk, xid )==FD_ACC_MGR_SUCCESS ) ) {
     301           0 :     FD_LOG_WARNING(( "Program data account %s already exists, skipping migration...", FD_BASE58_ENC_32_ALLOCA( target_program_data_address ) ));
     302           0 :     return;
     303           0 :   }
     304             : 
     305             :   /* https://github.com/anza-xyz/agave/blob/v2.3.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L221-L229
     306             : 
     307             :      Obtains a SourceBuffer account. There are two fields returned:
     308             :       - source.buffer_address: source_buffer_address
     309             :       - source.buffer_account: the existing buffer account
     310             :      Depending on if the verified build hash is provided,  */
     311           0 :   FD_TXN_ACCOUNT_DECL( source_buffer_account );
     312           0 :   fd_funk_rec_prepare_t source_buffer_prepare = {0};
     313           0 :   if( verified_build_hash!=NULL ) {
     314           0 :     if( FD_UNLIKELY( fd_source_buffer_account_new_with_hash( funk, xid, source_buffer_account, source_buffer_address, verified_build_hash, &source_buffer_prepare ) ) ) {
     315           0 :       return;
     316           0 :     }
     317           0 :   } else {
     318           0 :     if( FD_UNLIKELY( fd_source_buffer_account_new( funk, xid, source_buffer_account, source_buffer_address, &source_buffer_prepare ) ) ) {
     319           0 :       return;
     320           0 :     }
     321           0 :   }
     322             : 
     323           0 :   fd_lthash_value_t prev_source_buffer_hash[1];
     324           0 :   fd_hashes_account_lthash(
     325           0 :     source_buffer_address,
     326           0 :     fd_txn_account_get_meta( source_buffer_account ),
     327           0 :     fd_txn_account_get_data( source_buffer_account ),
     328           0 :     prev_source_buffer_hash );
     329             : 
     330             :   /* This check is done a bit prematurely because we calculate the
     331             :      previous account state's lamports. We use 0 for starting lamports
     332             :      for stateless accounts because they don't yet exist.
     333             : 
     334             :      https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L277-L280 */
     335           0 :   ulong lamports_to_burn = ( stateless ? 0UL : fd_txn_account_get_lamports( target_program_account ) ) + fd_txn_account_get_lamports( source_buffer_account );
     336             : 
     337             :   /* Start a funk write txn */
     338           0 :   fd_funk_txn_xid_t migration_xid = fd_funk_generate_xid();
     339           0 :   fd_funk_txn_prepare( funk, xid, &migration_xid );
     340             : 
     341             :   /* Attempt serialization of program account. If the program is
     342             :      stateless, we want to create the account. Otherwise, we want a
     343             :      writable handle to modify the existing account.
     344             :      https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L246-L249 */
     345           0 :   FD_TXN_ACCOUNT_DECL( new_target_program_account );
     346           0 :   fd_funk_rec_prepare_t new_target_program_prepare = {0};
     347           0 :   err = fd_txn_account_init_from_funk_mutable(
     348           0 :       new_target_program_account,
     349           0 :       builtin_program_id,
     350           0 :       funk,
     351           0 :       &migration_xid,
     352           0 :       stateless,
     353           0 :       SIZE_OF_PROGRAM,
     354           0 :       &new_target_program_prepare );
     355           0 :   if( FD_UNLIKELY( err ) ) {
     356           0 :     FD_LOG_WARNING(( "Builtin program ID %s does not exist", FD_BASE58_ENC_32_ALLOCA( builtin_program_id ) ));
     357           0 :     goto fail;
     358           0 :   }
     359           0 :   fd_lthash_value_t prev_new_target_program_account_hash[1];
     360           0 :   fd_hashes_account_lthash(
     361           0 :     builtin_program_id,
     362           0 :     fd_txn_account_get_meta( new_target_program_account ),
     363           0 :     fd_txn_account_get_data( new_target_program_account ),
     364           0 :     prev_new_target_program_account_hash );
     365           0 :   fd_txn_account_set_data_len( new_target_program_account, SIZE_OF_PROGRAM );
     366           0 :   fd_txn_account_set_slot( new_target_program_account, fd_bank_slot_get( bank ) );
     367             : 
     368             :   /* Create a new target program account. This modifies the existing record. */
     369           0 :   err = fd_new_target_program_account( bank, target_program_data_address, new_target_program_account );
     370           0 :   if( FD_UNLIKELY( err ) ) {
     371           0 :     FD_LOG_WARNING(( "Failed to write new program state to %s", FD_BASE58_ENC_32_ALLOCA( builtin_program_id ) ));
     372           0 :     goto fail;
     373           0 :   }
     374             : 
     375           0 :   fd_hashes_update_lthash(
     376           0 :     new_target_program_account,
     377           0 :     prev_new_target_program_account_hash,
     378           0 :     bank,
     379           0 :     NULL ); /* TODO: add capture_ctx */
     380           0 :   fd_txn_account_mutable_fini( new_target_program_account, funk, &new_target_program_prepare );
     381             : 
     382             :   /* Create a new target program data account. */
     383           0 :   ulong new_target_program_data_account_sz = PROGRAMDATA_METADATA_SIZE - BUFFER_METADATA_SIZE + fd_txn_account_get_data_len( source_buffer_account );
     384           0 :   FD_TXN_ACCOUNT_DECL( new_target_program_data_account );
     385           0 :   fd_funk_rec_prepare_t new_target_program_data_prepare = {0};
     386           0 :   err = fd_txn_account_init_from_funk_mutable(
     387           0 :       new_target_program_data_account,
     388           0 :       target_program_data_address,
     389           0 :       funk,
     390           0 :       &migration_xid,
     391           0 :       1,
     392           0 :       new_target_program_data_account_sz,
     393           0 :       &new_target_program_data_prepare );
     394           0 :   if( FD_UNLIKELY( err ) ) {
     395           0 :     FD_LOG_WARNING(( "Failed to create new program data account to %s", FD_BASE58_ENC_32_ALLOCA( target_program_data_address ) ));
     396           0 :     goto fail;
     397           0 :   }
     398           0 :   fd_lthash_value_t prev_new_target_program_data_account_hash[1];
     399           0 :   fd_hashes_account_lthash(
     400           0 :     target_program_data_address,
     401           0 :     fd_txn_account_get_meta( new_target_program_data_account ),
     402           0 :     fd_txn_account_get_data( new_target_program_data_account ),
     403           0 :     prev_new_target_program_data_account_hash );
     404           0 :   fd_txn_account_set_data_len( new_target_program_data_account, new_target_program_data_account_sz );
     405           0 :   fd_txn_account_set_slot( new_target_program_data_account, fd_bank_slot_get( bank ) );
     406             : 
     407           0 :   err = fd_new_target_program_data_account( bank,
     408           0 :                                             upgrade_authority_address,
     409           0 :                                             source_buffer_account,
     410           0 :                                             new_target_program_data_account,
     411           0 :                                             runtime_spad );
     412           0 :   if( FD_UNLIKELY( err ) ) {
     413           0 :     FD_LOG_WARNING(( "Failed to write new program data state to %s", FD_BASE58_ENC_32_ALLOCA( target_program_data_address ) ));
     414           0 :     goto fail;
     415           0 :   }
     416             : 
     417           0 :   fd_hashes_update_lthash(
     418           0 :     new_target_program_data_account,
     419           0 :     prev_new_target_program_data_account_hash,
     420           0 :     bank,
     421           0 :     NULL ); /* TODO: add capture_ctx */
     422           0 :   fd_txn_account_mutable_fini( new_target_program_data_account, funk, &new_target_program_data_prepare );
     423             : 
     424             :   /* Deploy the new target Core BPF program.
     425             :      https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L268-L271 */
     426           0 :   err = fd_directly_invoke_loader_v3_deploy( bank,
     427           0 :                                              funk,
     428           0 :                                              &migration_xid,
     429           0 :                                              builtin_program_id,
     430           0 :                                              fd_txn_account_get_data( new_target_program_data_account ) + PROGRAMDATA_METADATA_SIZE,
     431           0 :                                              fd_txn_account_get_data_len( new_target_program_data_account ) - PROGRAMDATA_METADATA_SIZE,
     432           0 :                                              runtime_spad );
     433           0 :   if( FD_UNLIKELY( err ) ) {
     434           0 :     FD_LOG_WARNING(( "Failed to deploy program %s", FD_BASE58_ENC_32_ALLOCA( builtin_program_id ) ));
     435           0 :     goto fail;
     436           0 :   }
     437             : 
     438             :   /* https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L281-L284 */
     439           0 :   ulong lamports_to_fund = fd_txn_account_get_lamports( new_target_program_account ) + fd_txn_account_get_lamports( new_target_program_data_account );
     440             : 
     441             :   /* Update capitalization.
     442             :      https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L286-L297 */
     443           0 :   if( lamports_to_burn>lamports_to_fund ) {
     444           0 :     fd_bank_capitalization_set( bank, fd_bank_capitalization_get( bank ) - ( lamports_to_burn - lamports_to_fund ) );
     445           0 :   } else {
     446           0 :     fd_bank_capitalization_set( bank, fd_bank_capitalization_get( bank ) + ( lamports_to_fund - lamports_to_burn ) );
     447           0 :   }
     448             : 
     449             :   /* Reclaim the source buffer account
     450             :      https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/bank/builtins/core_bpf_migration/mod.rs#L305 */
     451           0 :   fd_txn_account_set_lamports( source_buffer_account, 0 );
     452           0 :   fd_txn_account_set_data_len( source_buffer_account, 0 );
     453           0 :   fd_txn_account_clear_owner( source_buffer_account );
     454             : 
     455           0 :   fd_hashes_update_lthash(
     456           0 :     source_buffer_account,
     457           0 :     prev_source_buffer_hash,
     458           0 :     bank,
     459           0 :     NULL ); /* TODO: add capture_ctx */
     460           0 :   fd_txn_account_mutable_fini( source_buffer_account, funk, &source_buffer_prepare );
     461             : 
     462             :   /* Publish the in-preparation transaction into the parent. We should not have to create
     463             :      a BPF cache entry here because the program is technically "delayed visibility", so the program
     464             :      should not be invokable until the next slot. The cache entry will be created at the end of the
     465             :      block as a part of the finalize routine. */
     466           0 :   fd_funk_txn_publish_into_parent( funk, &migration_xid );
     467           0 :   return;
     468             : 
     469           0 : fail:
     470             :   /* Cancel the in-preparation transaction and discard any in-progress changes. */
     471           0 :   fd_funk_txn_cancel( funk, &migration_xid );
     472           0 : }

Generated by: LCOV version 1.14