1use crate::{
19 instruction::{AccountMeta, Instruction, InstructionError},
20 loader_upgradeable_instruction::UpgradeableLoaderInstruction,
21 pubkey::Pubkey,
22 system_instruction, sysvar,
23};
24
25crate::declare_id!("BPFLoaderUpgradeab1e11111111111111111111111");
26
27#[cfg_attr(feature = "frozen-abi", derive(AbiExample))]
29#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone, Copy)]
30pub enum UpgradeableLoaderState {
31 Uninitialized,
33 Buffer {
35 authority_address: Option<Pubkey>,
37 },
40 Program {
42 programdata_address: Pubkey,
44 },
45 ProgramData {
47 slot: u64,
49 upgrade_authority_address: Option<Pubkey>,
51 },
54}
55impl UpgradeableLoaderState {
56 pub const fn size_of_uninitialized() -> usize {
58 4 }
60
61 pub const fn size_of_buffer_metadata() -> usize {
63 37 }
65
66 pub const fn size_of_programdata_metadata() -> usize {
68 45 }
70
71 pub const fn size_of_program() -> usize {
73 36 }
75
76 pub const fn size_of_buffer(program_len: usize) -> usize {
78 Self::size_of_buffer_metadata().saturating_add(program_len)
79 }
80
81 pub const fn size_of_programdata(program_len: usize) -> usize {
83 Self::size_of_programdata_metadata().saturating_add(program_len)
84 }
85}
86
87pub fn get_program_data_address(program_address: &Pubkey) -> Pubkey {
89 Pubkey::find_program_address(&[program_address.as_ref()], &id()).0
90}
91
92pub fn create_buffer(
94 payer_address: &Pubkey,
95 buffer_address: &Pubkey,
96 authority_address: &Pubkey,
97 lamports: u64,
98 program_len: usize,
99) -> Result<Vec<Instruction>, InstructionError> {
100 Ok(vec![
101 system_instruction::create_account(
102 payer_address,
103 buffer_address,
104 lamports,
105 UpgradeableLoaderState::size_of_buffer(program_len) as u64,
106 &id(),
107 ),
108 Instruction::new_with_bincode(
109 id(),
110 &UpgradeableLoaderInstruction::InitializeBuffer,
111 vec![
112 AccountMeta::new(*buffer_address, false),
113 AccountMeta::new_readonly(*authority_address, false),
114 ],
115 ),
116 ])
117}
118
119pub fn write(
122 buffer_address: &Pubkey,
123 authority_address: &Pubkey,
124 offset: u32,
125 bytes: Vec<u8>,
126) -> Instruction {
127 Instruction::new_with_bincode(
128 id(),
129 &UpgradeableLoaderInstruction::Write { offset, bytes },
130 vec![
131 AccountMeta::new(*buffer_address, false),
132 AccountMeta::new_readonly(*authority_address, true),
133 ],
134 )
135}
136
137pub fn deploy_with_max_program_len(
141 payer_address: &Pubkey,
142 program_address: &Pubkey,
143 buffer_address: &Pubkey,
144 upgrade_authority_address: &Pubkey,
145 program_lamports: u64,
146 max_data_len: usize,
147) -> Result<Vec<Instruction>, InstructionError> {
148 let programdata_address = get_program_data_address(program_address);
149 Ok(vec![
150 system_instruction::create_account(
151 payer_address,
152 program_address,
153 program_lamports,
154 UpgradeableLoaderState::size_of_program() as u64,
155 &id(),
156 ),
157 Instruction::new_with_bincode(
158 id(),
159 &UpgradeableLoaderInstruction::DeployWithMaxDataLen { max_data_len },
160 vec![
161 AccountMeta::new(*payer_address, true),
162 AccountMeta::new(programdata_address, false),
163 AccountMeta::new(*program_address, false),
164 AccountMeta::new(*buffer_address, false),
165 AccountMeta::new_readonly(sysvar::rent::id(), false),
166 AccountMeta::new_readonly(sysvar::clock::id(), false),
167 AccountMeta::new_readonly(crate::system_program::id(), false),
168 AccountMeta::new_readonly(*upgrade_authority_address, true),
169 ],
170 ),
171 ])
172}
173
174pub fn upgrade(
176 program_address: &Pubkey,
177 buffer_address: &Pubkey,
178 authority_address: &Pubkey,
179 spill_address: &Pubkey,
180) -> Instruction {
181 let programdata_address = get_program_data_address(program_address);
182 Instruction::new_with_bincode(
183 id(),
184 &UpgradeableLoaderInstruction::Upgrade,
185 vec![
186 AccountMeta::new(programdata_address, false),
187 AccountMeta::new(*program_address, false),
188 AccountMeta::new(*buffer_address, false),
189 AccountMeta::new(*spill_address, false),
190 AccountMeta::new_readonly(sysvar::rent::id(), false),
191 AccountMeta::new_readonly(sysvar::clock::id(), false),
192 AccountMeta::new_readonly(*authority_address, true),
193 ],
194 )
195}
196
197pub fn is_upgrade_instruction(instruction_data: &[u8]) -> bool {
198 !instruction_data.is_empty() && 3 == instruction_data[0]
199}
200
201pub fn is_set_authority_instruction(instruction_data: &[u8]) -> bool {
202 !instruction_data.is_empty() && 4 == instruction_data[0]
203}
204
205pub fn is_close_instruction(instruction_data: &[u8]) -> bool {
206 !instruction_data.is_empty() && 5 == instruction_data[0]
207}
208
209pub fn is_set_authority_checked_instruction(instruction_data: &[u8]) -> bool {
210 !instruction_data.is_empty() && 7 == instruction_data[0]
211}
212
213pub fn set_buffer_authority(
215 buffer_address: &Pubkey,
216 current_authority_address: &Pubkey,
217 new_authority_address: &Pubkey,
218) -> Instruction {
219 Instruction::new_with_bincode(
220 id(),
221 &UpgradeableLoaderInstruction::SetAuthority,
222 vec![
223 AccountMeta::new(*buffer_address, false),
224 AccountMeta::new_readonly(*current_authority_address, true),
225 AccountMeta::new_readonly(*new_authority_address, false),
226 ],
227 )
228}
229
230pub fn set_buffer_authority_checked(
233 buffer_address: &Pubkey,
234 current_authority_address: &Pubkey,
235 new_authority_address: &Pubkey,
236) -> Instruction {
237 Instruction::new_with_bincode(
238 id(),
239 &UpgradeableLoaderInstruction::SetAuthorityChecked,
240 vec![
241 AccountMeta::new(*buffer_address, false),
242 AccountMeta::new_readonly(*current_authority_address, true),
243 AccountMeta::new_readonly(*new_authority_address, true),
244 ],
245 )
246}
247
248pub fn set_upgrade_authority(
250 program_address: &Pubkey,
251 current_authority_address: &Pubkey,
252 new_authority_address: Option<&Pubkey>,
253) -> Instruction {
254 let programdata_address = get_program_data_address(program_address);
255
256 let mut metas = vec![
257 AccountMeta::new(programdata_address, false),
258 AccountMeta::new_readonly(*current_authority_address, true),
259 ];
260 if let Some(address) = new_authority_address {
261 metas.push(AccountMeta::new_readonly(*address, false));
262 }
263 Instruction::new_with_bincode(id(), &UpgradeableLoaderInstruction::SetAuthority, metas)
264}
265
266pub fn set_upgrade_authority_checked(
269 program_address: &Pubkey,
270 current_authority_address: &Pubkey,
271 new_authority_address: &Pubkey,
272) -> Instruction {
273 let programdata_address = get_program_data_address(program_address);
274
275 let metas = vec![
276 AccountMeta::new(programdata_address, false),
277 AccountMeta::new_readonly(*current_authority_address, true),
278 AccountMeta::new_readonly(*new_authority_address, true),
279 ];
280 Instruction::new_with_bincode(
281 id(),
282 &UpgradeableLoaderInstruction::SetAuthorityChecked,
283 metas,
284 )
285}
286
287pub fn close(
289 close_address: &Pubkey,
290 recipient_address: &Pubkey,
291 authority_address: &Pubkey,
292) -> Instruction {
293 close_any(
294 close_address,
295 recipient_address,
296 Some(authority_address),
297 None,
298 )
299}
300
301pub fn close_any(
303 close_address: &Pubkey,
304 recipient_address: &Pubkey,
305 authority_address: Option<&Pubkey>,
306 program_address: Option<&Pubkey>,
307) -> Instruction {
308 let mut metas = vec![
309 AccountMeta::new(*close_address, false),
310 AccountMeta::new(*recipient_address, false),
311 ];
312 if let Some(authority_address) = authority_address {
313 metas.push(AccountMeta::new_readonly(*authority_address, true));
314 }
315 if let Some(program_address) = program_address {
316 metas.push(AccountMeta::new(*program_address, false));
317 }
318 Instruction::new_with_bincode(id(), &UpgradeableLoaderInstruction::Close, metas)
319}
320
321pub fn extend_program(
324 program_address: &Pubkey,
325 payer_address: Option<&Pubkey>,
326 additional_bytes: u32,
327) -> Instruction {
328 let program_data_address = get_program_data_address(program_address);
329 let mut metas = vec![
330 AccountMeta::new(program_data_address, false),
331 AccountMeta::new(*program_address, false),
332 ];
333 if let Some(payer_address) = payer_address {
334 metas.push(AccountMeta::new_readonly(
335 crate::system_program::id(),
336 false,
337 ));
338 metas.push(AccountMeta::new(*payer_address, true));
339 }
340 Instruction::new_with_bincode(
341 id(),
342 &UpgradeableLoaderInstruction::ExtendProgram { additional_bytes },
343 metas,
344 )
345}
346
347#[cfg(test)]
348mod tests {
349 use {super::*, bincode::serialized_size};
350
351 #[test]
352 fn test_state_size_of_uninitialized() {
353 let buffer_state = UpgradeableLoaderState::Uninitialized;
354 let size = serialized_size(&buffer_state).unwrap();
355 assert_eq!(UpgradeableLoaderState::size_of_uninitialized() as u64, size);
356 }
357
358 #[test]
359 fn test_state_size_of_buffer_metadata() {
360 let buffer_state = UpgradeableLoaderState::Buffer {
361 authority_address: Some(Pubkey::default()),
362 };
363 let size = serialized_size(&buffer_state).unwrap();
364 assert_eq!(
365 UpgradeableLoaderState::size_of_buffer_metadata() as u64,
366 size
367 );
368 }
369
370 #[test]
371 fn test_state_size_of_programdata_metadata() {
372 let programdata_state = UpgradeableLoaderState::ProgramData {
373 upgrade_authority_address: Some(Pubkey::default()),
374 slot: 0,
375 };
376 let size = serialized_size(&programdata_state).unwrap();
377 assert_eq!(
378 UpgradeableLoaderState::size_of_programdata_metadata() as u64,
379 size
380 );
381 }
382
383 #[test]
384 fn test_state_size_of_program() {
385 let program_state = UpgradeableLoaderState::Program {
386 programdata_address: Pubkey::default(),
387 };
388 let size = serialized_size(&program_state).unwrap();
389 assert_eq!(UpgradeableLoaderState::size_of_program() as u64, size);
390 }
391
392 fn assert_is_instruction<F>(
393 is_instruction_fn: F,
394 expected_instruction: UpgradeableLoaderInstruction,
395 ) where
396 F: Fn(&[u8]) -> bool,
397 {
398 let result = is_instruction_fn(
399 &bincode::serialize(&UpgradeableLoaderInstruction::InitializeBuffer).unwrap(),
400 );
401 let expected_result = matches!(
402 expected_instruction,
403 UpgradeableLoaderInstruction::InitializeBuffer
404 );
405 assert_eq!(expected_result, result);
406
407 let result = is_instruction_fn(
408 &bincode::serialize(&UpgradeableLoaderInstruction::Write {
409 offset: 0,
410 bytes: vec![],
411 })
412 .unwrap(),
413 );
414 let expected_result = matches!(
415 expected_instruction,
416 UpgradeableLoaderInstruction::Write {
417 offset: _,
418 bytes: _,
419 }
420 );
421 assert_eq!(expected_result, result);
422
423 let result = is_instruction_fn(
424 &bincode::serialize(&UpgradeableLoaderInstruction::DeployWithMaxDataLen {
425 max_data_len: 0,
426 })
427 .unwrap(),
428 );
429 let expected_result = matches!(
430 expected_instruction,
431 UpgradeableLoaderInstruction::DeployWithMaxDataLen { max_data_len: _ }
432 );
433 assert_eq!(expected_result, result);
434
435 let result =
436 is_instruction_fn(&bincode::serialize(&UpgradeableLoaderInstruction::Upgrade).unwrap());
437 let expected_result = matches!(expected_instruction, UpgradeableLoaderInstruction::Upgrade);
438 assert_eq!(expected_result, result);
439
440 let result = is_instruction_fn(
441 &bincode::serialize(&UpgradeableLoaderInstruction::SetAuthority).unwrap(),
442 );
443 let expected_result = matches!(
444 expected_instruction,
445 UpgradeableLoaderInstruction::SetAuthority
446 );
447 assert_eq!(expected_result, result);
448
449 let result =
450 is_instruction_fn(&bincode::serialize(&UpgradeableLoaderInstruction::Close).unwrap());
451 let expected_result = matches!(expected_instruction, UpgradeableLoaderInstruction::Close);
452 assert_eq!(expected_result, result);
453 }
454
455 #[test]
456 fn test_is_set_authority_instruction() {
457 assert!(!is_set_authority_instruction(&[]));
458 assert_is_instruction(
459 is_set_authority_instruction,
460 UpgradeableLoaderInstruction::SetAuthority {},
461 );
462 }
463
464 #[test]
465 fn test_is_set_authority_checked_instruction() {
466 assert!(!is_set_authority_checked_instruction(&[]));
467 assert_is_instruction(
468 is_set_authority_checked_instruction,
469 UpgradeableLoaderInstruction::SetAuthorityChecked {},
470 );
471 }
472
473 #[test]
474 fn test_is_upgrade_instruction() {
475 assert!(!is_upgrade_instruction(&[]));
476 assert_is_instruction(
477 is_upgrade_instruction,
478 UpgradeableLoaderInstruction::Upgrade {},
479 );
480 }
481}