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