solana_runtime/
loader_utils.rs

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