solana_runtime/
loader_utils.rs

1use {
2    crate::{bank::Bank, bank_client::BankClient},
3    serde::Serialize,
4    solana_sdk::{
5        account::{AccountSharedData, WritableAccount},
6        bpf_loader_upgradeable::{self, UpgradeableLoaderState},
7        client::{Client, SyncClient},
8        clock::Clock,
9        instruction::{AccountMeta, Instruction},
10        loader_instruction,
11        message::Message,
12        pubkey::Pubkey,
13        signature::{Keypair, Signer},
14        system_instruction,
15    },
16    std::{env, fs::File, io::Read, path::PathBuf},
17};
18
19const CHUNK_SIZE: usize = 512; // Size of chunk just needs to fit into tx
20
21pub fn load_program_from_file(name: &str) -> Vec<u8> {
22    let mut pathbuf = {
23        let current_exe = env::current_exe().unwrap();
24        PathBuf::from(current_exe.parent().unwrap().parent().unwrap())
25    };
26    pathbuf.push("sbf/");
27    pathbuf.push(name);
28    pathbuf.set_extension("so");
29    let mut file = File::open(&pathbuf).unwrap_or_else(|err| {
30        panic!("Failed to open {}: {}", pathbuf.display(), err);
31    });
32    let mut program = Vec::new();
33    file.read_to_end(&mut program).unwrap();
34    program
35}
36
37// Creates an unverified program by bypassing the loader built-in program
38pub fn create_program(bank: &Bank, loader_id: &Pubkey, name: &str) -> Pubkey {
39    let program_id = Pubkey::new_unique();
40    let elf = load_program_from_file(name);
41    let mut program_account = AccountSharedData::new(1, elf.len(), loader_id);
42    program_account
43        .data_as_mut_slice()
44        .get_mut(..)
45        .unwrap()
46        .copy_from_slice(&elf);
47    program_account.set_executable(true);
48    bank.store_account(&program_id, &program_account);
49    program_id
50}
51
52pub fn load_and_finalize_program<T: Client>(
53    bank_client: &T,
54    loader_id: &Pubkey,
55    program_keypair: Option<Keypair>,
56    payer_keypair: &Keypair,
57    name: &str,
58) -> (Keypair, Instruction) {
59    let program = load_program_from_file(name);
60    let program_keypair = program_keypair.unwrap_or_else(|| {
61        let program_keypair = Keypair::new();
62        let instruction = system_instruction::create_account(
63            &payer_keypair.pubkey(),
64            &program_keypair.pubkey(),
65            1.max(
66                bank_client
67                    .get_minimum_balance_for_rent_exemption(program.len())
68                    .unwrap(),
69            ),
70            program.len() as u64,
71            loader_id,
72        );
73        let message = Message::new(&[instruction], Some(&payer_keypair.pubkey()));
74        bank_client
75            .send_and_confirm_message(&[payer_keypair, &program_keypair], message)
76            .unwrap();
77        program_keypair
78    });
79    let chunk_size = CHUNK_SIZE;
80    let mut offset = 0;
81    for chunk in program.chunks(chunk_size) {
82        let instruction =
83            loader_instruction::write(&program_keypair.pubkey(), loader_id, offset, chunk.to_vec());
84        let message = Message::new(&[instruction], Some(&payer_keypair.pubkey()));
85        bank_client
86            .send_and_confirm_message(&[payer_keypair, &program_keypair], message)
87            .unwrap();
88        offset += chunk_size as u32;
89    }
90    let instruction = loader_instruction::finalize(&program_keypair.pubkey(), loader_id);
91    (program_keypair, instruction)
92}
93
94pub fn load_program<T: Client>(
95    bank_client: &T,
96    loader_id: &Pubkey,
97    payer_keypair: &Keypair,
98    name: &str,
99) -> Pubkey {
100    let (program_keypair, instruction) =
101        load_and_finalize_program(bank_client, loader_id, None, payer_keypair, name);
102    let message = Message::new(&[instruction], Some(&payer_keypair.pubkey()));
103    bank_client
104        .send_and_confirm_message(&[payer_keypair, &program_keypair], message)
105        .unwrap();
106    program_keypair.pubkey()
107}
108
109pub fn load_upgradeable_buffer<T: Client>(
110    bank_client: &T,
111    from_keypair: &Keypair,
112    buffer_keypair: &Keypair,
113    buffer_authority_keypair: &Keypair,
114    name: &str,
115) -> Vec<u8> {
116    let program = load_program_from_file(name);
117    let buffer_pubkey = buffer_keypair.pubkey();
118    let buffer_authority_pubkey = buffer_authority_keypair.pubkey();
119
120    bank_client
121        .send_and_confirm_message(
122            &[from_keypair, buffer_keypair],
123            Message::new(
124                &bpf_loader_upgradeable::create_buffer(
125                    &from_keypair.pubkey(),
126                    &buffer_pubkey,
127                    &buffer_authority_pubkey,
128                    1.max(
129                        bank_client
130                            .get_minimum_balance_for_rent_exemption(program.len())
131                            .unwrap(),
132                    ),
133                    program.len(),
134                )
135                .unwrap(),
136                Some(&from_keypair.pubkey()),
137            ),
138        )
139        .unwrap();
140
141    let chunk_size = CHUNK_SIZE;
142    let mut offset = 0;
143    for chunk in program.chunks(chunk_size) {
144        let message = Message::new(
145            &[bpf_loader_upgradeable::write(
146                &buffer_pubkey,
147                &buffer_authority_pubkey,
148                offset,
149                chunk.to_vec(),
150            )],
151            Some(&from_keypair.pubkey()),
152        );
153        bank_client
154            .send_and_confirm_message(&[from_keypair, buffer_authority_keypair], message)
155            .unwrap();
156        offset += chunk_size as u32;
157    }
158
159    program
160}
161
162pub fn load_upgradeable_program(
163    bank_client: &BankClient,
164    from_keypair: &Keypair,
165    buffer_keypair: &Keypair,
166    executable_keypair: &Keypair,
167    authority_keypair: &Keypair,
168    name: &str,
169) {
170    let program = load_upgradeable_buffer(
171        bank_client,
172        from_keypair,
173        buffer_keypair,
174        authority_keypair,
175        name,
176    );
177
178    let message = Message::new(
179        &bpf_loader_upgradeable::deploy_with_max_program_len(
180            &from_keypair.pubkey(),
181            &executable_keypair.pubkey(),
182            &buffer_keypair.pubkey(),
183            &authority_keypair.pubkey(),
184            1.max(
185                bank_client
186                    .get_minimum_balance_for_rent_exemption(
187                        UpgradeableLoaderState::size_of_program(),
188                    )
189                    .unwrap(),
190            ),
191            program.len() * 2,
192        )
193        .unwrap(),
194        Some(&from_keypair.pubkey()),
195    );
196    bank_client
197        .send_and_confirm_message(
198            &[from_keypair, executable_keypair, authority_keypair],
199            message,
200        )
201        .unwrap();
202    bank_client.set_sysvar_for_tests(&Clock {
203        slot: 1,
204        ..Clock::default()
205    });
206}
207
208pub fn upgrade_program<T: Client>(
209    bank_client: &T,
210    payer_keypair: &Keypair,
211    buffer_keypair: &Keypair,
212    executable_pubkey: &Pubkey,
213    authority_keypair: &Keypair,
214    name: &str,
215) {
216    load_upgradeable_buffer(
217        bank_client,
218        payer_keypair,
219        buffer_keypair,
220        authority_keypair,
221        name,
222    );
223    let message = Message::new(
224        &[bpf_loader_upgradeable::upgrade(
225            executable_pubkey,
226            &buffer_keypair.pubkey(),
227            &authority_keypair.pubkey(),
228            &payer_keypair.pubkey(),
229        )],
230        Some(&payer_keypair.pubkey()),
231    );
232    bank_client
233        .send_and_confirm_message(&[payer_keypair, authority_keypair], message)
234        .unwrap();
235}
236
237pub fn set_upgrade_authority<T: Client>(
238    bank_client: &T,
239    from_keypair: &Keypair,
240    program_pubkey: &Pubkey,
241    current_authority_keypair: &Keypair,
242    new_authority_pubkey: Option<&Pubkey>,
243) {
244    let message = Message::new(
245        &[bpf_loader_upgradeable::set_upgrade_authority(
246            program_pubkey,
247            &current_authority_keypair.pubkey(),
248            new_authority_pubkey,
249        )],
250        Some(&from_keypair.pubkey()),
251    );
252    bank_client
253        .send_and_confirm_message(&[from_keypair, current_authority_keypair], message)
254        .unwrap();
255}
256
257// Return an Instruction that invokes `program_id` with `data` and required
258// a signature from `from_pubkey`.
259pub fn create_invoke_instruction<T: Serialize>(
260    from_pubkey: Pubkey,
261    program_id: Pubkey,
262    data: &T,
263) -> Instruction {
264    let account_metas = vec![AccountMeta::new(from_pubkey, true)];
265    Instruction::new_with_bincode(program_id, data, account_metas)
266}