Skip to main content

solana_runtime/
loader_utils.rs

1#![cfg(feature = "dev-context-only-utils")]
2use {
3    crate::{bank::Bank, bank_client::BankClient, bank_forks::BankForks},
4    solana_account::{AccountSharedData, WritableAccount},
5    solana_client_traits::{Client, SyncClient},
6    solana_clock::Clock,
7    solana_instruction::{AccountMeta, Instruction},
8    solana_keypair::Keypair,
9    solana_leader_schedule::SlotLeader,
10    solana_loader_v3_interface::state::UpgradeableLoaderState,
11    solana_message::Message,
12    solana_program_binaries::{
13        bpf_loader_program_account, bpf_loader_upgradeable_program_accounts,
14    },
15    solana_pubkey::Pubkey,
16    solana_rent::Rent,
17    solana_sdk_ids::bpf_loader_upgradeable,
18    solana_signer::Signer,
19    std::{
20        env,
21        fs::File,
22        io::Read,
23        path::PathBuf,
24        sync::{Arc, RwLock},
25    },
26};
27
28const CHUNK_SIZE: usize = 512; // Size of chunk just needs to fit into tx
29
30pub fn load_program_from_file(name: &str) -> Vec<u8> {
31    let mut pathbuf = {
32        let current_exe = env::current_exe().unwrap();
33        PathBuf::from(
34            current_exe
35                .parent()
36                .unwrap()
37                .parent()
38                .unwrap()
39                .parent()
40                .unwrap(),
41        )
42    };
43    pathbuf.push("deploy");
44    pathbuf.push(name);
45    pathbuf.set_extension("so");
46    let mut file = File::open(&pathbuf).unwrap_or_else(|err| {
47        panic!("Failed to open {}: {}", pathbuf.display(), err);
48    });
49    let mut program = Vec::new();
50    file.read_to_end(&mut program).unwrap();
51    program
52}
53
54// Creates an unverified program by bypassing the loader built-in program
55pub fn create_program(bank: &Bank, loader_id: &Pubkey, name: &str) -> Pubkey {
56    create_program_with_elf(
57        bank,
58        loader_id,
59        &Pubkey::default(),
60        &load_program_from_file(name),
61    )
62}
63
64pub fn create_program_with_elf(
65    bank: &Bank,
66    loader_id: &Pubkey,
67    upgrade_authority: &Pubkey,
68    elf: &[u8],
69) -> Pubkey {
70    let program_id = Pubkey::new_unique();
71    let rent = Rent::default();
72    if bpf_loader_upgradeable::check_id(loader_id) {
73        let [(_, program_account), (programdata_id, programdata_account)] =
74            bpf_loader_upgradeable_program_accounts(&program_id, elf, upgrade_authority, &rent);
75        bank.store_account(&program_id, &AccountSharedData::from(program_account));
76        bank.store_account(
77            &programdata_id,
78            &AccountSharedData::from(programdata_account),
79        );
80    } else {
81        let (_, mut program_account) = bpf_loader_program_account(&program_id, elf, &rent);
82        program_account.owner = *loader_id;
83        bank.store_account(&program_id, &AccountSharedData::from(program_account));
84    }
85    program_id
86}
87
88pub fn create_buffer_with_elf(bank: &Bank, authority_address: &Pubkey, elf: &[u8]) -> Pubkey {
89    let buffer_address = Pubkey::new_unique();
90    let size = UpgradeableLoaderState::size_of_buffer(elf.len());
91    let mut account = AccountSharedData::new(
92        bank.get_minimum_balance_for_rent_exemption(size),
93        size,
94        &bpf_loader_upgradeable::id(),
95    );
96    wincode::serialize_into(
97        account.data_as_mut_slice(),
98        &UpgradeableLoaderState::Buffer {
99            authority_address: Some(*authority_address),
100        },
101    )
102    .unwrap();
103    account
104        .data_as_mut_slice()
105        .get_mut(UpgradeableLoaderState::size_of_buffer_metadata()..)
106        .unwrap()
107        .copy_from_slice(elf);
108    bank.store_account(&buffer_address, &account);
109    buffer_address
110}
111
112pub fn load_upgradeable_buffer<T: Client>(
113    bank_client: &T,
114    from_keypair: &Keypair,
115    buffer_keypair: &Keypair,
116    buffer_authority_keypair: &Keypair,
117    name: &str,
118) -> Vec<u8> {
119    let program = load_program_from_file(name);
120    let buffer_pubkey = buffer_keypair.pubkey();
121    let buffer_authority_pubkey = buffer_authority_keypair.pubkey();
122    let program_buffer_bytes = UpgradeableLoaderState::size_of_buffer(program.len());
123
124    bank_client
125        .send_and_confirm_message(
126            &[from_keypair, buffer_keypair],
127            Message::new(
128                &solana_loader_v3_interface::instruction::create_buffer(
129                    &from_keypair.pubkey(),
130                    &buffer_pubkey,
131                    &buffer_authority_pubkey,
132                    1.max(
133                        bank_client
134                            .get_minimum_balance_for_rent_exemption(program_buffer_bytes)
135                            .unwrap(),
136                    ),
137                    program.len(),
138                )
139                .unwrap(),
140                Some(&from_keypair.pubkey()),
141            ),
142        )
143        .unwrap();
144
145    let chunk_size = CHUNK_SIZE;
146    let mut offset = 0;
147    for chunk in program.chunks(chunk_size) {
148        let message = Message::new(
149            &[solana_loader_v3_interface::instruction::write(
150                &buffer_pubkey,
151                &buffer_authority_pubkey,
152                offset,
153                chunk.to_vec(),
154            )],
155            Some(&from_keypair.pubkey()),
156        );
157        bank_client
158            .send_and_confirm_message(&[from_keypair, buffer_authority_keypair], message)
159            .unwrap();
160        offset += chunk_size as u32;
161    }
162
163    program
164}
165
166pub fn load_upgradeable_program(
167    bank_client: &BankClient,
168    from_keypair: &Keypair,
169    buffer_keypair: &Keypair,
170    executable_keypair: &Keypair,
171    authority_keypair: &Keypair,
172    name: &str,
173) {
174    let program = load_upgradeable_buffer(
175        bank_client,
176        from_keypair,
177        buffer_keypair,
178        authority_keypair,
179        name,
180    );
181
182    #[allow(deprecated)]
183    let message = Message::new(
184        &solana_loader_v3_interface::instruction::deploy_with_max_program_len(
185            &from_keypair.pubkey(),
186            &executable_keypair.pubkey(),
187            &buffer_keypair.pubkey(),
188            &authority_keypair.pubkey(),
189            1.max(
190                bank_client
191                    .get_minimum_balance_for_rent_exemption(
192                        UpgradeableLoaderState::size_of_program(),
193                    )
194                    .unwrap(),
195            ),
196            program.len() * 2,
197        )
198        .unwrap(),
199        Some(&from_keypair.pubkey()),
200    );
201    bank_client
202        .send_and_confirm_message(
203            &[from_keypair, executable_keypair, authority_keypair],
204            message,
205        )
206        .unwrap();
207    bank_client.set_sysvar_for_tests(&Clock {
208        slot: 1,
209        ..Clock::default()
210    });
211}
212
213pub fn load_upgradeable_program_wrapper(
214    bank_client: &BankClient,
215    mint_keypair: &Keypair,
216    authority_keypair: &Keypair,
217    name: &str,
218) -> Pubkey {
219    let buffer_keypair = Keypair::new();
220    let program_keypair = Keypair::new();
221    load_upgradeable_program(
222        bank_client,
223        mint_keypair,
224        &buffer_keypair,
225        &program_keypair,
226        authority_keypair,
227        name,
228    );
229    program_keypair.pubkey()
230}
231
232pub fn load_upgradeable_program_and_advance_slot(
233    bank_client: &mut BankClient,
234    bank_forks: &RwLock<BankForks>,
235    mint_keypair: &Keypair,
236    authority_keypair: &Keypair,
237    name: &str,
238) -> (Arc<Bank>, Pubkey) {
239    let program_id =
240        load_upgradeable_program_wrapper(bank_client, mint_keypair, authority_keypair, name);
241
242    // load_upgradeable_program sets clock sysvar to 1, which causes the program to be effective
243    // after 2 slots. They need to be called individually to create the correct fork graph in between.
244    bank_client
245        .advance_slot(1, bank_forks, SlotLeader::default())
246        .expect("Failed to advance the slot");
247
248    let bank = bank_client
249        .advance_slot(1, bank_forks, SlotLeader::default())
250        .expect("Failed to advance the slot");
251
252    (bank, program_id)
253}
254
255pub fn upgrade_program<T: Client>(
256    bank_client: &T,
257    payer_keypair: &Keypair,
258    buffer_keypair: &Keypair,
259    executable_pubkey: &Pubkey,
260    authority_keypair: &Keypair,
261    name: &str,
262) {
263    load_upgradeable_buffer(
264        bank_client,
265        payer_keypair,
266        buffer_keypair,
267        authority_keypair,
268        name,
269    );
270    let message = Message::new(
271        &[solana_loader_v3_interface::instruction::upgrade(
272            executable_pubkey,
273            &buffer_keypair.pubkey(),
274            &authority_keypair.pubkey(),
275            &payer_keypair.pubkey(),
276        )],
277        Some(&payer_keypair.pubkey()),
278    );
279    bank_client
280        .send_and_confirm_message(&[payer_keypair, authority_keypair], message)
281        .unwrap();
282}
283
284pub fn set_upgrade_authority<T: Client>(
285    bank_client: &T,
286    from_keypair: &Keypair,
287    program_pubkey: &Pubkey,
288    current_authority_keypair: &Keypair,
289    new_authority_pubkey: Option<&Pubkey>,
290) {
291    let message = Message::new(
292        &[
293            solana_loader_v3_interface::instruction::set_upgrade_authority(
294                program_pubkey,
295                &current_authority_keypair.pubkey(),
296                new_authority_pubkey,
297            ),
298        ],
299        Some(&from_keypair.pubkey()),
300    );
301    bank_client
302        .send_and_confirm_message(&[from_keypair, current_authority_keypair], message)
303        .unwrap();
304}
305
306// Return an Instruction that invokes `program_id` with `data` and required
307// a signature from `from_pubkey`.
308pub fn create_invoke_instruction<T: wincode::Serialize<Src = T>>(
309    from_pubkey: Pubkey,
310    program_id: Pubkey,
311    data: &T,
312) -> Instruction {
313    let account_metas = vec![AccountMeta::new(from_pubkey, true)];
314    Instruction::new_with_wincode(program_id, data, account_metas)
315}