Skip to main content

hpsvm_loader/
lib.rs

1//! Loader operations for the HPSVM
2
3use hpsvm::{HPSVM, types::FailedTransactionMetadata};
4use solana_address::Address;
5use solana_keypair::Keypair;
6use solana_loader_v3_interface::{
7    instruction as bpf_loader_upgradeable, state::UpgradeableLoaderState,
8};
9use solana_signer::Signer;
10use solana_transaction::Transaction;
11
12const CHUNK_SIZE: usize = 512;
13
14/// Set the upgrade authority for an upgradeable program
15pub fn set_upgrade_authority(
16    svm: &mut HPSVM,
17    from_keypair: &Keypair,
18    program_address: &Address,
19    current_authority_keypair: &Keypair,
20    new_authority_address: Option<&Address>,
21) -> Result<(), FailedTransactionMetadata> {
22    let mut signers: Vec<&dyn Signer> = vec![from_keypair];
23    if from_keypair.pubkey() != current_authority_keypair.pubkey() {
24        signers.push(current_authority_keypair);
25    }
26
27    let tx = Transaction::new_signed_with_payer(
28        &[bpf_loader_upgradeable::set_upgrade_authority(
29            program_address,
30            &current_authority_keypair.pubkey(),
31            new_authority_address,
32        )],
33        Some(&from_keypair.pubkey()),
34        &signers,
35        svm.latest_blockhash(),
36    );
37
38    svm.send_transaction(tx)?;
39
40    Ok(())
41}
42
43fn load_upgradeable_buffer(
44    svm: &mut HPSVM,
45    payer_kp: &Keypair,
46    program_bytes: &[u8],
47) -> Result<Address, FailedTransactionMetadata> {
48    let payer_pk = payer_kp.pubkey();
49    let buffer_kp = Keypair::new();
50    let buffer_pk = buffer_kp.pubkey();
51    // loader
52    let buffer_len = UpgradeableLoaderState::size_of_buffer(program_bytes.len());
53    let lamports = svm.minimum_balance_for_rent_exemption(buffer_len);
54
55    let tx = Transaction::new_signed_with_payer(
56        &bpf_loader_upgradeable::create_buffer(
57            &payer_pk,
58            &buffer_pk,
59            &payer_pk,
60            lamports,
61            program_bytes.len(),
62        )
63        .expect("Failed to create buffer instruction"),
64        Some(&payer_pk),
65        &[payer_kp, &buffer_kp],
66        svm.latest_blockhash(),
67    );
68
69    svm.send_transaction(tx)?;
70
71    let chunk_size = CHUNK_SIZE;
72    let mut offset = 0;
73    for chunk in program_bytes.chunks(chunk_size) {
74        let tx = Transaction::new_signed_with_payer(
75            &[bpf_loader_upgradeable::write(&buffer_pk, &payer_pk, offset, chunk.to_vec())],
76            Some(&payer_pk),
77            &[payer_kp],
78            svm.latest_blockhash(),
79        );
80
81        svm.send_transaction(tx)?;
82        offset += chunk_size as u32;
83    }
84
85    Ok(buffer_pk)
86}
87
88/// Deploy an upgradeable program
89pub fn deploy_upgradeable_program(
90    svm: &mut HPSVM,
91    payer_kp: &Keypair,
92    program_kp: &Keypair,
93    program_bytes: &[u8],
94) -> Result<(), FailedTransactionMetadata> {
95    let program_pk = program_kp.pubkey();
96    let payer_pk = payer_kp.pubkey();
97    let buffer_pk = load_upgradeable_buffer(svm, payer_kp, program_bytes)?;
98
99    let lamports = svm.minimum_balance_for_rent_exemption(program_bytes.len());
100    let tx = Transaction::new_signed_with_payer(
101        &bpf_loader_upgradeable::deploy_with_max_program_len(
102            &payer_pk,
103            &program_pk,
104            &buffer_pk,
105            &payer_pk,
106            lamports,
107            program_bytes.len() * 2,
108        )
109        .expect("Failed to create deploy instruction"),
110        Some(&payer_pk),
111        &[&payer_kp, &program_kp],
112        svm.latest_blockhash(),
113    );
114
115    svm.send_transaction(tx)?;
116
117    Ok(())
118}