| 
 | 1 | +#include "fd_prog_load.h"  | 
 | 2 | +#include "../runtime/program/fd_bpf_loader_program.h"  | 
 | 3 | +#include "../runtime/program/fd_loader_v4_program.h"  | 
 | 4 | + | 
 | 5 | +/* Similar to the below function, but gets the executable program content for the v4 loader.  | 
 | 6 | +   Unlike the v3 loader, the programdata is stored in a single program account. The program must  | 
 | 7 | +   NOT be retracted to be added to the cache. Returns a pointer to the programdata on success,  | 
 | 8 | +   and NULL on failure.  | 
 | 9 | +
  | 
 | 10 | +   Reasons for failure include:  | 
 | 11 | +   - The program state cannot be read from the account data or is in the `retracted` state. */  | 
 | 12 | +static uchar const *  | 
 | 13 | +fd_get_executable_program_content_for_v4_loader( fd_txn_account_t const * program_acc,  | 
 | 14 | +                                                 ulong *                  program_data_len ) {  | 
 | 15 | +  int err;  | 
 | 16 | + | 
 | 17 | +  /* Get the current loader v4 state. This implicitly also checks the dlen. */  | 
 | 18 | +  fd_loader_v4_state_t const * state = fd_loader_v4_get_state( program_acc, &err );  | 
 | 19 | +  if( FD_UNLIKELY( err ) ) {  | 
 | 20 | +    return NULL;  | 
 | 21 | +  }  | 
 | 22 | + | 
 | 23 | +  /* The program must be deployed or finalized. */  | 
 | 24 | +  if( FD_UNLIKELY( fd_loader_v4_status_is_retracted( state ) ) ) {  | 
 | 25 | +    return NULL;  | 
 | 26 | +  }  | 
 | 27 | + | 
 | 28 | +  /* This subtraction is safe because get_state() implicitly checks the  | 
 | 29 | +     dlen. */  | 
 | 30 | +  *program_data_len = fd_txn_account_get_data_len( program_acc )-LOADER_V4_PROGRAM_DATA_OFFSET;  | 
 | 31 | +  return fd_txn_account_get_data( program_acc )+LOADER_V4_PROGRAM_DATA_OFFSET;  | 
 | 32 | +}  | 
 | 33 | + | 
 | 34 | +/* Gets the programdata for a v3 loader-owned account by decoding the account data  | 
 | 35 | +   as well as the programdata account. Returns a pointer to the programdata on success,  | 
 | 36 | +   and NULL on failure.  | 
 | 37 | +
  | 
 | 38 | +   Reasons for failure include:  | 
 | 39 | +   - The program account data cannot be decoded or is not in the `program` state.  | 
 | 40 | +   - The programdata account is not large enough to hold at least `PROGRAMDATA_METADATA_SIZE` bytes. */  | 
 | 41 | +static uchar const *  | 
 | 42 | +fd_get_executable_program_content_for_upgradeable_loader( fd_funk_t const *         funk,  | 
 | 43 | +                                                          fd_funk_txn_xid_t const * xid,  | 
 | 44 | +                                                          fd_txn_account_t const *  program_acc,  | 
 | 45 | +                                                          ulong *                   program_data_len,  | 
 | 46 | +                                                          fd_funk_txn_xid_t *       out_xid ) {  | 
 | 47 | +  fd_bpf_upgradeable_loader_state_t program_account_state[1];  | 
 | 48 | +  if( FD_UNLIKELY( !fd_bincode_decode_static(  | 
 | 49 | +      bpf_upgradeable_loader_state,  | 
 | 50 | +      program_account_state,  | 
 | 51 | +      fd_txn_account_get_data( program_acc ),  | 
 | 52 | +      fd_txn_account_get_data_len( program_acc ),  | 
 | 53 | +      NULL ) ) ) {  | 
 | 54 | +    return NULL;  | 
 | 55 | +  }  | 
 | 56 | +  if( !fd_bpf_upgradeable_loader_state_is_program( program_account_state ) ) {  | 
 | 57 | +    return NULL;  | 
 | 58 | +  }  | 
 | 59 | + | 
 | 60 | +  fd_pubkey_t * programdata_address = &program_account_state->inner.program.programdata_address;  | 
 | 61 | + | 
 | 62 | +  fd_funk_txn_t const * acc_txn = NULL;  | 
 | 63 | +  fd_account_meta_t const * meta = fd_funk_get_acc_meta_readonly(  | 
 | 64 | +      funk, xid, programdata_address, NULL, NULL, &acc_txn );  | 
 | 65 | +  if( FD_UNLIKELY( !meta ) ) return NULL;  | 
 | 66 | +  *out_xid = acc_txn->xid;  | 
 | 67 | +  fd_txn_account_t _rec[1];  | 
 | 68 | +  fd_txn_account_t * programdata_acc = fd_txn_account_join( fd_txn_account_new( _rec, programdata_address, (void *)meta, 0 ), funk->wksp );  | 
 | 69 | +  if( FD_UNLIKELY( !programdata_acc ) ) FD_LOG_CRIT(( "fd_txn_account_new failed" ));  | 
 | 70 | + | 
 | 71 | +  /* We don't actually need to decode here, just make sure that the account  | 
 | 72 | +     can be decoded successfully. */  | 
 | 73 | +  fd_bincode_decode_ctx_t ctx_programdata = {  | 
 | 74 | +    .data    = fd_txn_account_get_data( programdata_acc ),  | 
 | 75 | +    .dataend = fd_txn_account_get_data( programdata_acc ) + fd_txn_account_get_data_len( programdata_acc ),  | 
 | 76 | +  };  | 
 | 77 | + | 
 | 78 | +  ulong total_sz = 0UL;  | 
 | 79 | +  if( FD_UNLIKELY( fd_bpf_upgradeable_loader_state_decode_footprint( &ctx_programdata, &total_sz ) ) ) {  | 
 | 80 | +    return NULL;  | 
 | 81 | +  }  | 
 | 82 | + | 
 | 83 | +  if( FD_UNLIKELY( fd_txn_account_get_data_len( programdata_acc )<PROGRAMDATA_METADATA_SIZE ) ) {  | 
 | 84 | +    return NULL;  | 
 | 85 | +  }  | 
 | 86 | + | 
 | 87 | +  *program_data_len = fd_txn_account_get_data_len( programdata_acc ) - PROGRAMDATA_METADATA_SIZE;  | 
 | 88 | +  return fd_txn_account_get_data( programdata_acc ) + PROGRAMDATA_METADATA_SIZE;  | 
 | 89 | +}  | 
 | 90 | + | 
 | 91 | +/* Gets the programdata for a v1/v2 loader-owned account by returning a  | 
 | 92 | +   pointer to the account data. Returns a pointer to the programdata on  | 
 | 93 | +   success. Given the txn account API always returns a handle to the  | 
 | 94 | +   account data, this function should NEVER return NULL (since the  | 
 | 95 | +   programdata of v1 and v2 loader) accounts start at the beginning of  | 
 | 96 | +   the data. */  | 
 | 97 | +static uchar const *  | 
 | 98 | +fd_get_executable_program_content_for_v1_v2_loaders( fd_txn_account_t const * program_acc,  | 
 | 99 | +                                                     ulong *                  program_data_len ) {  | 
 | 100 | +  *program_data_len = fd_txn_account_get_data_len( program_acc );  | 
 | 101 | +  return fd_txn_account_get_data( program_acc );  | 
 | 102 | +}  | 
 | 103 | + | 
 | 104 | +uchar const *  | 
 | 105 | +fd_prog_load_elf( fd_funk_t const *         accdb,  | 
 | 106 | +                  fd_funk_txn_xid_t const * xid,  | 
 | 107 | +                  void const *              _prog_addr,  | 
 | 108 | +                  ulong *                   out_sz,  | 
 | 109 | +                  fd_funk_txn_xid_t *       out_xid ) {  | 
 | 110 | +  fd_pubkey_t prog_addr = FD_LOAD( fd_pubkey_t, _prog_addr );  | 
 | 111 | + | 
 | 112 | +  fd_funk_txn_t const * acc_txn = NULL;  | 
 | 113 | +  fd_account_meta_t const * meta = fd_funk_get_acc_meta_readonly(  | 
 | 114 | +      accdb, xid, &prog_addr, NULL, NULL, &acc_txn );  | 
 | 115 | +  if( FD_UNLIKELY( !meta ) ) return NULL;  | 
 | 116 | +  fd_funk_txn_xid_t rec_xid = acc_txn->xid;  | 
 | 117 | +  fd_txn_account_t _rec[1];  | 
 | 118 | +  fd_txn_account_t * rec = fd_txn_account_join( fd_txn_account_new( _rec, &prog_addr, (void *)meta, 0 ), accdb->wksp );  | 
 | 119 | +  if( FD_UNLIKELY( !rec ) ) FD_LOG_CRIT(( "fd_txn_account_new failed" ));  | 
 | 120 | + | 
 | 121 | +  /* v1/v2 loaders: Programdata is just the account data.  | 
 | 122 | +     v3 loader: Programdata lives in a separate account. Deserialize the  | 
 | 123 | +                program account and lookup the programdata account.  | 
 | 124 | +                 Deserialize the programdata account.  | 
 | 125 | +     v4 loader: Programdata lives in the program account, offset by  | 
 | 126 | +                LOADER_V4_PROGRAM_DATA_OFFSET. */  | 
 | 127 | +  fd_pubkey_t const * owner = fd_txn_account_get_owner( rec );  | 
 | 128 | +  uchar const * elf = NULL;  | 
 | 129 | +  fd_funk_txn_xid_t sub_xid = { .ul={ 0UL, 0UL } };  | 
 | 130 | +  if( !memcmp( owner, fd_solana_bpf_loader_upgradeable_program_id.key, sizeof(fd_pubkey_t) ) ) {  | 
 | 131 | +    elf = fd_get_executable_program_content_for_upgradeable_loader( accdb, xid, rec, out_sz, &sub_xid );  | 
 | 132 | +  } else if( !memcmp( owner, fd_solana_bpf_loader_v4_program_id.key, sizeof(fd_pubkey_t) ) ) {  | 
 | 133 | +    elf = fd_get_executable_program_content_for_v4_loader( rec, out_sz );  | 
 | 134 | +  } else if( !memcmp( owner, fd_solana_bpf_loader_program_id.key, sizeof(fd_pubkey_t) ) ||  | 
 | 135 | +             !memcmp( owner, fd_solana_bpf_loader_deprecated_program_id.key, sizeof(fd_pubkey_t) ) ) {  | 
 | 136 | +    elf = fd_get_executable_program_content_for_v1_v2_loaders( rec, out_sz );  | 
 | 137 | +  }  | 
 | 138 | + | 
 | 139 | +  if( FD_LIKELY( elf ) ) {  | 
 | 140 | +    /* Return the XID with the last modification slow (unclear if they can differ) */  | 
 | 141 | +    *out_xid = sub_xid.ul[0]>rec_xid.ul[0] ? sub_xid : rec_xid;  | 
 | 142 | +  } else {  | 
 | 143 | +    fd_funk_txn_xid_set_root( out_xid );  | 
 | 144 | +  }  | 
 | 145 | + | 
 | 146 | +  return elf;  | 
 | 147 | +}  | 
 | 148 | + | 
 | 149 | +fd_prog_versions_t  | 
 | 150 | +fd_prog_versions( fd_features_t const * features,  | 
 | 151 | +                  ulong                 slot ) {  | 
 | 152 | +  int disable_v0  = FD_FEATURE_ACTIVE( slot, features, disable_sbpf_v0_execution );  | 
 | 153 | +  int reenable_v0 = FD_FEATURE_ACTIVE( slot, features, reenable_sbpf_v0_execution );  | 
 | 154 | +  int enable_v0   = !disable_v0 || reenable_v0;  | 
 | 155 | +  int enable_v1   = FD_FEATURE_ACTIVE( slot, features, enable_sbpf_v1_deployment_and_execution );  | 
 | 156 | +  int enable_v2   = FD_FEATURE_ACTIVE( slot, features, enable_sbpf_v2_deployment_and_execution );  | 
 | 157 | +  int enable_v3   = FD_FEATURE_ACTIVE( slot, features, enable_sbpf_v3_deployment_and_execution );  | 
 | 158 | + | 
 | 159 | +  fd_prog_versions_t v = {0};  | 
 | 160 | +  v.min_sbpf_version = enable_v0 ? FD_SBPF_V0 : FD_SBPF_V3;  | 
 | 161 | +  if( enable_v3 ) {  | 
 | 162 | +    v.max_sbpf_version = FD_SBPF_V3;  | 
 | 163 | +  } else if( enable_v2 ) {  | 
 | 164 | +    v.max_sbpf_version = FD_SBPF_V2;  | 
 | 165 | +  } else if( enable_v1 ) {  | 
 | 166 | +    v.max_sbpf_version = FD_SBPF_V1;  | 
 | 167 | +  } else {  | 
 | 168 | +    v.max_sbpf_version = FD_SBPF_V0;  | 
 | 169 | +  }  | 
 | 170 | +  return v;  | 
 | 171 | +}  | 
0 commit comments