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#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone, Copy, AbiExample)]
29pub enum UpgradeableLoaderState {
30 Uninitialized,
32 Buffer {
34 authority_address: Option<Pubkey>,
36 },
39 Program {
41 programdata_address: Pubkey,
43 },
44 ProgramData {
46 slot: u64,
48 upgrade_authority_address: Option<Pubkey>,
50 },
53}
54impl UpgradeableLoaderState {
55 pub const fn size_of_uninitialized() -> usize {
57 4 }
59
60 pub const fn size_of_buffer_metadata() -> usize {
62 37 }
64
65 pub const fn size_of_programdata_metadata() -> usize {
67 45 }
69
70 pub const fn size_of_program() -> usize {
72 36 }
74
75 pub const fn size_of_buffer(program_len: usize) -> usize {
77 Self::size_of_buffer_metadata().saturating_add(program_len)
78 }
79
80 pub const fn size_of_programdata(program_len: usize) -> usize {
82 Self::size_of_programdata_metadata().saturating_add(program_len)
83 }
84
85 #[deprecated(since = "1.11.0", note = "Please use `size_of_buffer` instead")]
87 pub fn buffer_len(program_len: usize) -> Result<usize, InstructionError> {
88 Ok(Self::size_of_buffer(program_len))
89 }
90
91 #[deprecated(
93 since = "1.11.0",
94 note = "Please use `size_of_buffer_metadata` instead"
95 )]
96 pub fn buffer_data_offset() -> Result<usize, InstructionError> {
97 Ok(Self::size_of_buffer_metadata())
98 }
99
100 #[deprecated(since = "1.11.0", note = "Please use `size_of_program` instead")]
102 pub fn program_len() -> Result<usize, InstructionError> {
103 Ok(Self::size_of_program())
104 }
105
106 #[deprecated(since = "1.11.0", note = "Please use `size_of_programdata` instead")]
108 pub fn programdata_len(program_len: usize) -> Result<usize, InstructionError> {
109 Ok(Self::size_of_programdata(program_len))
110 }
111
112 #[deprecated(
114 since = "1.11.0",
115 note = "Please use `size_of_programdata_metadata` instead"
116 )]
117 pub fn programdata_data_offset() -> Result<usize, InstructionError> {
118 Ok(Self::size_of_programdata_metadata())
119 }
120}
121
122pub fn create_buffer(
124 payer_address: &Pubkey,
125 buffer_address: &Pubkey,
126 authority_address: &Pubkey,
127 scoobies: u64,
128 program_len: usize,
129) -> Result<Vec<Instruction>, InstructionError> {
130 Ok(vec![
131 system_instruction::create_account(
132 payer_address,
133 buffer_address,
134 scoobies,
135 UpgradeableLoaderState::size_of_buffer(program_len) as u64,
136 &id(),
137 ),
138 Instruction::new_with_bincode(
139 id(),
140 &UpgradeableLoaderInstruction::InitializeBuffer,
141 vec![
142 AccountMeta::new(*buffer_address, false),
143 AccountMeta::new_readonly(*authority_address, false),
144 ],
145 ),
146 ])
147}
148
149pub fn write(
152 buffer_address: &Pubkey,
153 authority_address: &Pubkey,
154 offset: u32,
155 bytes: Vec<u8>,
156) -> Instruction {
157 Instruction::new_with_bincode(
158 id(),
159 &UpgradeableLoaderInstruction::Write { offset, bytes },
160 vec![
161 AccountMeta::new(*buffer_address, false),
162 AccountMeta::new_readonly(*authority_address, true),
163 ],
164 )
165}
166
167pub fn deploy_with_max_program_len(
171 payer_address: &Pubkey,
172 program_address: &Pubkey,
173 buffer_address: &Pubkey,
174 upgrade_authority_address: &Pubkey,
175 program_scoobies: u64,
176 max_data_len: usize,
177) -> Result<Vec<Instruction>, InstructionError> {
178 let (programdata_address, _) = Pubkey::find_program_address(&[program_address.as_ref()], &id());
179 Ok(vec![
180 system_instruction::create_account(
181 payer_address,
182 program_address,
183 program_scoobies,
184 UpgradeableLoaderState::size_of_program() as u64,
185 &id(),
186 ),
187 Instruction::new_with_bincode(
188 id(),
189 &UpgradeableLoaderInstruction::DeployWithMaxDataLen { max_data_len },
190 vec![
191 AccountMeta::new(*payer_address, true),
192 AccountMeta::new(programdata_address, false),
193 AccountMeta::new(*program_address, false),
194 AccountMeta::new(*buffer_address, false),
195 AccountMeta::new_readonly(sysvar::rent::id(), false),
196 AccountMeta::new_readonly(sysvar::clock::id(), false),
197 AccountMeta::new_readonly(crate::system_program::id(), false),
198 AccountMeta::new_readonly(*upgrade_authority_address, true),
199 ],
200 ),
201 ])
202}
203
204pub fn upgrade(
206 program_address: &Pubkey,
207 buffer_address: &Pubkey,
208 authority_address: &Pubkey,
209 spill_address: &Pubkey,
210) -> Instruction {
211 let (programdata_address, _) = Pubkey::find_program_address(&[program_address.as_ref()], &id());
212 Instruction::new_with_bincode(
213 id(),
214 &UpgradeableLoaderInstruction::Upgrade,
215 vec![
216 AccountMeta::new(programdata_address, false),
217 AccountMeta::new(*program_address, false),
218 AccountMeta::new(*buffer_address, false),
219 AccountMeta::new(*spill_address, false),
220 AccountMeta::new_readonly(sysvar::rent::id(), false),
221 AccountMeta::new_readonly(sysvar::clock::id(), false),
222 AccountMeta::new_readonly(*authority_address, true),
223 ],
224 )
225}
226
227pub fn is_upgrade_instruction(instruction_data: &[u8]) -> bool {
228 !instruction_data.is_empty() && 3 == instruction_data[0]
229}
230
231pub fn is_set_authority_instruction(instruction_data: &[u8]) -> bool {
232 !instruction_data.is_empty() && 4 == instruction_data[0]
233}
234
235pub fn is_close_instruction(instruction_data: &[u8]) -> bool {
236 !instruction_data.is_empty() && 5 == instruction_data[0]
237}
238
239pub fn is_set_authority_checked_instruction(instruction_data: &[u8]) -> bool {
240 !instruction_data.is_empty() && 7 == instruction_data[0]
241}
242
243pub fn set_buffer_authority(
245 buffer_address: &Pubkey,
246 current_authority_address: &Pubkey,
247 new_authority_address: &Pubkey,
248) -> Instruction {
249 Instruction::new_with_bincode(
250 id(),
251 &UpgradeableLoaderInstruction::SetAuthority,
252 vec![
253 AccountMeta::new(*buffer_address, false),
254 AccountMeta::new_readonly(*current_authority_address, true),
255 AccountMeta::new_readonly(*new_authority_address, false),
256 ],
257 )
258}
259
260pub fn set_buffer_authority_checked(
263 buffer_address: &Pubkey,
264 current_authority_address: &Pubkey,
265 new_authority_address: &Pubkey,
266) -> Instruction {
267 Instruction::new_with_bincode(
268 id(),
269 &UpgradeableLoaderInstruction::SetAuthorityChecked,
270 vec![
271 AccountMeta::new(*buffer_address, false),
272 AccountMeta::new_readonly(*current_authority_address, true),
273 AccountMeta::new_readonly(*new_authority_address, true),
274 ],
275 )
276}
277
278pub fn set_upgrade_authority(
280 program_address: &Pubkey,
281 current_authority_address: &Pubkey,
282 new_authority_address: Option<&Pubkey>,
283) -> Instruction {
284 let (programdata_address, _) = Pubkey::find_program_address(&[program_address.as_ref()], &id());
285
286 let mut metas = vec![
287 AccountMeta::new(programdata_address, false),
288 AccountMeta::new_readonly(*current_authority_address, true),
289 ];
290 if let Some(address) = new_authority_address {
291 metas.push(AccountMeta::new_readonly(*address, false));
292 }
293 Instruction::new_with_bincode(id(), &UpgradeableLoaderInstruction::SetAuthority, metas)
294}
295
296pub fn set_upgrade_authority_checked(
299 program_address: &Pubkey,
300 current_authority_address: &Pubkey,
301 new_authority_address: &Pubkey,
302) -> Instruction {
303 let (programdata_address, _) = Pubkey::find_program_address(&[program_address.as_ref()], &id());
304
305 let metas = vec![
306 AccountMeta::new(programdata_address, false),
307 AccountMeta::new_readonly(*current_authority_address, true),
308 AccountMeta::new_readonly(*new_authority_address, true),
309 ];
310 Instruction::new_with_bincode(
311 id(),
312 &UpgradeableLoaderInstruction::SetAuthorityChecked,
313 metas,
314 )
315}
316
317pub fn close(
319 close_address: &Pubkey,
320 recipient_address: &Pubkey,
321 authority_address: &Pubkey,
322) -> Instruction {
323 close_any(
324 close_address,
325 recipient_address,
326 Some(authority_address),
327 None,
328 )
329}
330
331pub fn close_any(
333 close_address: &Pubkey,
334 recipient_address: &Pubkey,
335 authority_address: Option<&Pubkey>,
336 program_address: Option<&Pubkey>,
337) -> Instruction {
338 let mut metas = vec![
339 AccountMeta::new(*close_address, false),
340 AccountMeta::new(*recipient_address, false),
341 ];
342 if let Some(authority_address) = authority_address {
343 metas.push(AccountMeta::new_readonly(*authority_address, true));
344 }
345 if let Some(program_address) = program_address {
346 metas.push(AccountMeta::new(*program_address, false));
347 }
348 Instruction::new_with_bincode(id(), &UpgradeableLoaderInstruction::Close, metas)
349}
350
351pub fn extend_program(
354 program_address: &Pubkey,
355 payer_address: Option<&Pubkey>,
356 additional_bytes: u32,
357) -> Instruction {
358 let (program_data_address, _) =
359 Pubkey::find_program_address(&[program_address.as_ref()], &id());
360 let mut metas = vec![
361 AccountMeta::new(program_data_address, false),
362 AccountMeta::new(*program_address, false),
363 ];
364 if let Some(payer_address) = payer_address {
365 metas.push(AccountMeta::new_readonly(
366 crate::system_program::id(),
367 false,
368 ));
369 metas.push(AccountMeta::new(*payer_address, true));
370 }
371 Instruction::new_with_bincode(
372 id(),
373 &UpgradeableLoaderInstruction::ExtendProgram { additional_bytes },
374 metas,
375 )
376}
377
378#[cfg(test)]
379mod tests {
380 use {super::*, bincode::serialized_size};
381
382 #[test]
383 fn test_state_size_of_uninitialized() {
384 let buffer_state = UpgradeableLoaderState::Uninitialized;
385 let size = serialized_size(&buffer_state).unwrap();
386 assert_eq!(UpgradeableLoaderState::size_of_uninitialized() as u64, size);
387 }
388
389 #[test]
390 fn test_state_size_of_buffer_metadata() {
391 let buffer_state = UpgradeableLoaderState::Buffer {
392 authority_address: Some(Pubkey::default()),
393 };
394 let size = serialized_size(&buffer_state).unwrap();
395 assert_eq!(
396 UpgradeableLoaderState::size_of_buffer_metadata() as u64,
397 size
398 );
399 }
400
401 #[test]
402 fn test_state_size_of_programdata_metadata() {
403 let programdata_state = UpgradeableLoaderState::ProgramData {
404 upgrade_authority_address: Some(Pubkey::default()),
405 slot: 0,
406 };
407 let size = serialized_size(&programdata_state).unwrap();
408 assert_eq!(
409 UpgradeableLoaderState::size_of_programdata_metadata() as u64,
410 size
411 );
412 }
413
414 #[test]
415 fn test_state_size_of_program() {
416 let program_state = UpgradeableLoaderState::Program {
417 programdata_address: Pubkey::default(),
418 };
419 let size = serialized_size(&program_state).unwrap();
420 assert_eq!(UpgradeableLoaderState::size_of_program() as u64, size);
421 }
422
423 #[test]
424 #[allow(deprecated)]
425 fn test_account_lengths() {
426 assert_eq!(
427 4,
428 serialized_size(&UpgradeableLoaderState::Uninitialized).unwrap()
429 );
430 assert_eq!(36, UpgradeableLoaderState::program_len().unwrap());
431 assert_eq!(
432 45,
433 UpgradeableLoaderState::programdata_data_offset().unwrap()
434 );
435 assert_eq!(
436 45 + 42,
437 UpgradeableLoaderState::programdata_len(42).unwrap()
438 );
439 }
440
441 fn assert_is_instruction<F>(
442 is_instruction_fn: F,
443 expected_instruction: UpgradeableLoaderInstruction,
444 ) where
445 F: Fn(&[u8]) -> bool,
446 {
447 let result = is_instruction_fn(
448 &bincode::serialize(&UpgradeableLoaderInstruction::InitializeBuffer).unwrap(),
449 );
450 let expected_result = matches!(
451 expected_instruction,
452 UpgradeableLoaderInstruction::InitializeBuffer
453 );
454 assert_eq!(expected_result, result);
455
456 let result = is_instruction_fn(
457 &bincode::serialize(&UpgradeableLoaderInstruction::Write {
458 offset: 0,
459 bytes: vec![],
460 })
461 .unwrap(),
462 );
463 let expected_result = matches!(
464 expected_instruction,
465 UpgradeableLoaderInstruction::Write {
466 offset: _,
467 bytes: _,
468 }
469 );
470 assert_eq!(expected_result, result);
471
472 let result = is_instruction_fn(
473 &bincode::serialize(&UpgradeableLoaderInstruction::DeployWithMaxDataLen {
474 max_data_len: 0,
475 })
476 .unwrap(),
477 );
478 let expected_result = matches!(
479 expected_instruction,
480 UpgradeableLoaderInstruction::DeployWithMaxDataLen { max_data_len: _ }
481 );
482 assert_eq!(expected_result, result);
483
484 let result =
485 is_instruction_fn(&bincode::serialize(&UpgradeableLoaderInstruction::Upgrade).unwrap());
486 let expected_result = matches!(expected_instruction, UpgradeableLoaderInstruction::Upgrade);
487 assert_eq!(expected_result, result);
488
489 let result = is_instruction_fn(
490 &bincode::serialize(&UpgradeableLoaderInstruction::SetAuthority).unwrap(),
491 );
492 let expected_result = matches!(
493 expected_instruction,
494 UpgradeableLoaderInstruction::SetAuthority
495 );
496 assert_eq!(expected_result, result);
497
498 let result =
499 is_instruction_fn(&bincode::serialize(&UpgradeableLoaderInstruction::Close).unwrap());
500 let expected_result = matches!(expected_instruction, UpgradeableLoaderInstruction::Close);
501 assert_eq!(expected_result, result);
502 }
503
504 #[test]
505 fn test_is_set_authority_instruction() {
506 assert!(!is_set_authority_instruction(&[]));
507 assert_is_instruction(
508 is_set_authority_instruction,
509 UpgradeableLoaderInstruction::SetAuthority {},
510 );
511 }
512
513 #[test]
514 fn test_is_set_authority_checked_instruction() {
515 assert!(!is_set_authority_checked_instruction(&[]));
516 assert_is_instruction(
517 is_set_authority_checked_instruction,
518 UpgradeableLoaderInstruction::SetAuthorityChecked {},
519 );
520 }
521
522 #[test]
523 fn test_is_upgrade_instruction() {
524 assert!(!is_upgrade_instruction(&[]));
525 assert_is_instruction(
526 is_upgrade_instruction,
527 UpgradeableLoaderInstruction::Upgrade {},
528 );
529 }
530}