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: u64 = 0;
83    for chunk in program_bytes.chunks(chunk_size) {
84        let offset_u32: u32 =
85            offset.try_into().map_err(|_| loader_instruction_error(InstructionError::Custom(0)))?;
86        let tx = Transaction::new_signed_with_payer(
87            &[bpf_loader_upgradeable::write(&buffer_pk, &payer_pk, offset_u32, chunk.to_vec())],
88            Some(&payer_pk),
89            &[payer_kp],
90            svm.latest_blockhash(),
91        );
92
93        svm.send_transaction(tx)?;
94        offset += chunk_size as u64;
95    }
96
97    Ok(buffer_pk)
98}
99
100/// Deploy an upgradeable program
101pub fn deploy_upgradeable_program(
102    svm: &mut HPSVM,
103    payer_kp: &Keypair,
104    program_kp: &Keypair,
105    program_bytes: &[u8],
106) -> Result<(), FailedTransactionMetadata> {
107    let program_pk = program_kp.pubkey();
108    let payer_pk = payer_kp.pubkey();
109    let buffer_pk = load_upgradeable_buffer(svm, payer_kp, program_bytes)?;
110
111    let lamports = svm.minimum_balance_for_rent_exemption(program_bytes.len());
112    let deploy_ixs = bpf_loader_upgradeable::deploy_with_max_program_len(
113        &payer_pk,
114        &program_pk,
115        &buffer_pk,
116        &payer_pk,
117        lamports,
118        program_bytes.len() * 2,
119    )
120    .map_err(loader_instruction_error)?;
121    let tx = Transaction::new_signed_with_payer(
122        &deploy_ixs,
123        Some(&payer_pk),
124        &[&payer_kp, &program_kp],
125        svm.latest_blockhash(),
126    );
127
128    svm.send_transaction(tx)?;
129
130    Ok(())
131}