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