1use base64::Engine;
2use base64::prelude::BASE64_STANDARD;
3use bincode::deserialize;
4use serde_json::json;
5use solana_loader_v2_interface::LoaderInstruction;
6use solana_loader_v3_interface::instruction::UpgradeableLoaderInstruction;
7use solana_message::AccountKeys;
8use solana_message::compiled_instruction::CompiledInstruction;
9
10use crate::parse_instruction::ParsableProgram;
11use crate::parse_instruction::ParseInstructionError;
12use crate::parse_instruction::ParsedInstructionEnum;
13use crate::parse_instruction::check_num_accounts;
14
15pub fn parse_bpf_loader(
16 instruction: &CompiledInstruction,
17 account_keys: &AccountKeys,
18) -> Result<ParsedInstructionEnum, ParseInstructionError> {
19 let bpf_loader_instruction: LoaderInstruction = deserialize(&instruction.data)
20 .map_err(|_| ParseInstructionError::InstructionNotParsable(ParsableProgram::BpfLoader))?;
21 if instruction.accounts.is_empty() || instruction.accounts[0] as usize >= account_keys.len() {
22 return Err(ParseInstructionError::InstructionKeyMismatch(
23 ParsableProgram::BpfLoader,
24 ));
25 }
26 match bpf_loader_instruction {
27 LoaderInstruction::Write { offset, bytes } => {
28 check_num_bpf_loader_accounts(&instruction.accounts, 1)?;
29 Ok(ParsedInstructionEnum {
30 instruction_type: "write".to_string(),
31 info: json!({
32 "offset": offset,
33 "bytes": BASE64_STANDARD.encode(bytes),
34 "account": account_keys[instruction.accounts[0] as usize].to_string(),
35 }),
36 })
37 }
38 LoaderInstruction::Finalize => {
39 check_num_bpf_loader_accounts(&instruction.accounts, 2)?;
40 Ok(ParsedInstructionEnum {
41 instruction_type: "finalize".to_string(),
42 info: json!({
43 "account": account_keys[instruction.accounts[0] as usize].to_string(),
44 }),
45 })
46 }
47 }
48}
49
50pub fn parse_bpf_upgradeable_loader(
51 instruction: &CompiledInstruction,
52 account_keys: &AccountKeys,
53) -> Result<ParsedInstructionEnum, ParseInstructionError> {
54 let bpf_upgradeable_loader_instruction: UpgradeableLoaderInstruction =
55 deserialize(&instruction.data).map_err(|_| {
56 ParseInstructionError::InstructionNotParsable(ParsableProgram::BpfUpgradeableLoader)
57 })?;
58 match instruction.accounts.iter().max() {
59 Some(index) if (*index as usize) < account_keys.len() => {}
60 _ => {
61 return Err(ParseInstructionError::InstructionKeyMismatch(
63 ParsableProgram::BpfUpgradeableLoader,
64 ));
65 }
66 }
67 match bpf_upgradeable_loader_instruction {
68 UpgradeableLoaderInstruction::InitializeBuffer => {
69 check_num_bpf_upgradeable_loader_accounts(&instruction.accounts, 1)?;
70 let mut value = json!({
71 "account": account_keys[instruction.accounts[0] as usize].to_string(),
72 });
73 let map = value.as_object_mut().unwrap();
74 if instruction.accounts.len() > 1 {
75 map.insert(
76 "authority".to_string(),
77 json!(account_keys[instruction.accounts[1] as usize].to_string()),
78 );
79 }
80 Ok(ParsedInstructionEnum {
81 instruction_type: "initializeBuffer".to_string(),
82 info: value,
83 })
84 }
85 UpgradeableLoaderInstruction::Write { offset, bytes } => {
86 check_num_bpf_upgradeable_loader_accounts(&instruction.accounts, 2)?;
87 Ok(ParsedInstructionEnum {
88 instruction_type: "write".to_string(),
89 info: json!({
90 "offset": offset,
91 "bytes": BASE64_STANDARD.encode(bytes),
92 "account": account_keys[instruction.accounts[0] as usize].to_string(),
93 "authority": account_keys[instruction.accounts[1] as usize].to_string(),
94 }),
95 })
96 }
97 UpgradeableLoaderInstruction::DeployWithMaxDataLen { max_data_len } => {
98 check_num_bpf_upgradeable_loader_accounts(&instruction.accounts, 8)?;
99 Ok(ParsedInstructionEnum {
100 instruction_type: "deployWithMaxDataLen".to_string(),
101 info: json!({
102 "maxDataLen": max_data_len,
103 "payerAccount": account_keys[instruction.accounts[0] as usize].to_string(),
104 "programDataAccount": account_keys[instruction.accounts[1] as usize].to_string(),
105 "programAccount": account_keys[instruction.accounts[2] as usize].to_string(),
106 "bufferAccount": account_keys[instruction.accounts[3] as usize].to_string(),
107 "rentSysvar": account_keys[instruction.accounts[4] as usize].to_string(),
108 "clockSysvar": account_keys[instruction.accounts[5] as usize].to_string(),
109 "systemProgram": account_keys[instruction.accounts[6] as usize].to_string(),
110 "authority": account_keys[instruction.accounts[7] as usize].to_string(),
111 }),
112 })
113 }
114 UpgradeableLoaderInstruction::Upgrade => {
115 check_num_bpf_upgradeable_loader_accounts(&instruction.accounts, 7)?;
116 Ok(ParsedInstructionEnum {
117 instruction_type: "upgrade".to_string(),
118 info: json!({
119 "programDataAccount": account_keys[instruction.accounts[0] as usize].to_string(),
120 "programAccount": account_keys[instruction.accounts[1] as usize].to_string(),
121 "bufferAccount": account_keys[instruction.accounts[2] as usize].to_string(),
122 "spillAccount": account_keys[instruction.accounts[3] as usize].to_string(),
123 "rentSysvar": account_keys[instruction.accounts[4] as usize].to_string(),
124 "clockSysvar": account_keys[instruction.accounts[5] as usize].to_string(),
125 "authority": account_keys[instruction.accounts[6] as usize].to_string(),
126 }),
127 })
128 }
129 UpgradeableLoaderInstruction::SetAuthority => {
130 check_num_bpf_upgradeable_loader_accounts(&instruction.accounts, 2)?;
131 Ok(ParsedInstructionEnum {
132 instruction_type: "setAuthority".to_string(),
133 info: json!({
134 "account": account_keys[instruction.accounts[0] as usize].to_string(),
135 "authority": account_keys[instruction.accounts[1] as usize].to_string(),
136 "newAuthority": if instruction.accounts.len() > 2 {
137 Some(account_keys[instruction.accounts[2] as usize].to_string())
138 } else {
139 None
140 },
141 }),
142 })
143 }
144 UpgradeableLoaderInstruction::SetAuthorityChecked => {
145 check_num_bpf_upgradeable_loader_accounts(&instruction.accounts, 3)?;
146 Ok(ParsedInstructionEnum {
147 instruction_type: "setAuthorityChecked".to_string(),
148 info: json!({
149 "account": account_keys[instruction.accounts[0] as usize].to_string(),
150 "authority": account_keys[instruction.accounts[1] as usize].to_string(),
151 "newAuthority": account_keys[instruction.accounts[2] as usize].to_string(),
152 }),
153 })
154 }
155 UpgradeableLoaderInstruction::Close => {
156 check_num_bpf_upgradeable_loader_accounts(&instruction.accounts, 3)?;
157 Ok(ParsedInstructionEnum {
158 instruction_type: "close".to_string(),
159 info: json!({
160 "account": account_keys[instruction.accounts[0] as usize].to_string(),
161 "recipient": account_keys[instruction.accounts[1] as usize].to_string(),
162 "authority": account_keys[instruction.accounts[2] as usize].to_string(),
163 "programAccount": if instruction.accounts.len() > 3 {
164 Some(account_keys[instruction.accounts[3] as usize].to_string())
165 } else {
166 None
167 }
168 }),
169 })
170 }
171 UpgradeableLoaderInstruction::ExtendProgram { additional_bytes } => {
172 check_num_bpf_upgradeable_loader_accounts(&instruction.accounts, 2)?;
173 Ok(ParsedInstructionEnum {
174 instruction_type: "extendProgram".to_string(),
175 info: json!({
176 "additionalBytes": additional_bytes,
177 "programDataAccount": account_keys[instruction.accounts[0] as usize].to_string(),
178 "programAccount": account_keys[instruction.accounts[1] as usize].to_string(),
179 "systemProgram": if instruction.accounts.len() > 2 {
180 Some(account_keys[instruction.accounts[2] as usize].to_string())
181 } else {
182 None
183 },
184 "payerAccount": if instruction.accounts.len() > 3 {
185 Some(account_keys[instruction.accounts[3] as usize].to_string())
186 } else {
187 None
188 },
189 }),
190 })
191 }
192 UpgradeableLoaderInstruction::Migrate => {
193 check_num_bpf_upgradeable_loader_accounts(&instruction.accounts, 3)?;
194 Ok(ParsedInstructionEnum {
195 instruction_type: "migrate".to_string(),
196 info: json!({
197 "programDataAccount": account_keys[instruction.accounts[0] as usize].to_string(),
198 "programAccount": account_keys[instruction.accounts[1] as usize].to_string(),
199 "authority": account_keys[instruction.accounts[2] as usize].to_string(),
200 }),
201 })
202 }
203 UpgradeableLoaderInstruction::ExtendProgramChecked { additional_bytes } => {
204 check_num_bpf_upgradeable_loader_accounts(&instruction.accounts, 3)?;
205 Ok(ParsedInstructionEnum {
206 instruction_type: "extendProgramChecked".to_string(),
207 info: json!({
208 "additionalBytes": additional_bytes,
209 "programDataAccount": account_keys[instruction.accounts[0] as usize].to_string(),
210 "programAccount": account_keys[instruction.accounts[1] as usize].to_string(),
211 "authority": account_keys[instruction.accounts[2] as usize].to_string(),
212 "systemProgram": if instruction.accounts.len() > 3 {
213 Some(account_keys[instruction.accounts[3] as usize].to_string())
214 } else {
215 None
216 },
217 "payerAccount": if instruction.accounts.len() > 4 {
218 Some(account_keys[instruction.accounts[4] as usize].to_string())
219 } else {
220 None
221 },
222 }),
223 })
224 }
225 }
226}
227
228fn check_num_bpf_loader_accounts(accounts: &[u8], num: usize) -> Result<(), ParseInstructionError> {
229 check_num_accounts(accounts, num, ParsableProgram::BpfLoader)
230}
231
232fn check_num_bpf_upgradeable_loader_accounts(
233 accounts: &[u8],
234 num: usize,
235) -> Result<(), ParseInstructionError> {
236 check_num_accounts(accounts, num, ParsableProgram::BpfUpgradeableLoader)
237}
238
239#[cfg(test)]
240mod test {
241 use serde_json::Value;
242 use solana_loader_v3_interface::instruction as bpf_loader_upgradeable;
243 use solana_message::Message;
244 use solana_pubkey::Pubkey;
245 use solana_pubkey::{self as pubkey};
246 use solana_sdk_ids::system_program;
247 use solana_sdk_ids::sysvar;
248
249 use super::*;
250
251 #[test]
252 fn test_parse_bpf_loader_instructions() {
253 let account_pubkey = pubkey::new_rand();
254 let program_id = pubkey::new_rand();
255 let offset = 4242;
256 let bytes = vec![8; 99];
257 let fee_payer = pubkey::new_rand();
258 let account_keys = vec![fee_payer, account_pubkey];
259 let missing_account_keys = vec![account_pubkey];
260
261 #[allow(deprecated)]
262 let instruction =
263 solana_loader_v2_interface::write(&account_pubkey, &program_id, offset, bytes.clone());
264 let mut message = Message::new(&[instruction], Some(&fee_payer));
265 assert_eq!(
266 parse_bpf_loader(
267 &message.instructions[0],
268 &AccountKeys::new(&account_keys, None)
269 )
270 .unwrap(),
271 ParsedInstructionEnum {
272 instruction_type: "write".to_string(),
273 info: json!({
274 "offset": offset,
275 "bytes": BASE64_STANDARD.encode(&bytes),
276 "account": account_pubkey.to_string(),
277 }),
278 }
279 );
280 assert!(
281 parse_bpf_loader(
282 &message.instructions[0],
283 &AccountKeys::new(&missing_account_keys, None)
284 )
285 .is_err()
286 );
287 message.instructions[0].accounts.pop();
288 assert!(
289 parse_bpf_loader(
290 &message.instructions[0],
291 &AccountKeys::new(&account_keys, None)
292 )
293 .is_err()
294 );
295
296 #[allow(deprecated)]
297 let instruction = solana_loader_v2_interface::finalize(&account_pubkey, &program_id);
298 let mut message = Message::new(&[instruction], Some(&fee_payer));
299 assert_eq!(
300 parse_bpf_loader(
301 &message.instructions[0],
302 &AccountKeys::new(&account_keys, None)
303 )
304 .unwrap(),
305 ParsedInstructionEnum {
306 instruction_type: "finalize".to_string(),
307 info: json!({
308 "account": account_pubkey.to_string(),
309 }),
310 }
311 );
312 assert!(
313 parse_bpf_loader(
314 &message.instructions[0],
315 &AccountKeys::new(&missing_account_keys, None)
316 )
317 .is_err()
318 );
319 message.instructions[0].accounts.pop();
320 assert!(
321 parse_bpf_loader(
322 &message.instructions[0],
323 &AccountKeys::new(&account_keys, None)
324 )
325 .is_err()
326 );
327
328 let bad_compiled_instruction = CompiledInstruction {
329 program_id_index: 3,
330 accounts: vec![1, 2],
331 data: vec![2, 0, 0, 0], };
333 assert!(
334 parse_bpf_loader(
335 &bad_compiled_instruction,
336 &AccountKeys::new(&account_keys, None)
337 )
338 .is_err()
339 );
340
341 let bad_compiled_instruction = CompiledInstruction {
342 program_id_index: 3,
343 accounts: vec![],
344 data: vec![1, 0, 0, 0],
345 };
346 assert!(
347 parse_bpf_loader(
348 &bad_compiled_instruction,
349 &AccountKeys::new(&account_keys, None)
350 )
351 .is_err()
352 );
353 }
354
355 #[test]
356 fn test_parse_bpf_upgradeable_loader_create_buffer_ix() {
357 let max_data_len = 54321;
358
359 let payer_address = Pubkey::new_unique();
360 let buffer_address = Pubkey::new_unique();
361 let authority_address = Pubkey::new_unique();
362 let instructions = bpf_loader_upgradeable::create_buffer(
363 &payer_address,
364 &buffer_address,
365 &authority_address,
366 55,
367 max_data_len,
368 )
369 .unwrap();
370 let mut message = Message::new(&instructions, None);
371 assert_eq!(
372 parse_bpf_upgradeable_loader(
373 &message.instructions[1],
374 &AccountKeys::new(&message.account_keys, None)
375 )
376 .unwrap(),
377 ParsedInstructionEnum {
378 instruction_type: "initializeBuffer".to_string(),
379 info: json!({
380 "account": buffer_address.to_string(),
381 "authority": authority_address.to_string(),
382 }),
383 }
384 );
385 assert!(
386 parse_bpf_upgradeable_loader(
387 &message.instructions[1],
388 &AccountKeys::new(&message.account_keys[0..2], None)
389 )
390 .is_err()
391 );
392 let keys = message.account_keys.clone();
393 message.instructions[1].accounts.pop();
394 message.instructions[1].accounts.pop();
395 assert!(
396 parse_bpf_upgradeable_loader(&message.instructions[1], &AccountKeys::new(&keys, None))
397 .is_err()
398 );
399 }
400
401 #[test]
402 fn test_parse_bpf_upgradeable_loader_write_ix() {
403 let offset = 4242;
404 let bytes = vec![8; 99];
405
406 let buffer_address = Pubkey::new_unique();
407 let authority_address = Pubkey::new_unique();
408 let instruction = bpf_loader_upgradeable::write(
409 &buffer_address,
410 &authority_address,
411 offset,
412 bytes.clone(),
413 );
414 let mut message = Message::new(&[instruction], None);
415 assert_eq!(
416 parse_bpf_upgradeable_loader(
417 &message.instructions[0],
418 &AccountKeys::new(&message.account_keys, None)
419 )
420 .unwrap(),
421 ParsedInstructionEnum {
422 instruction_type: "write".to_string(),
423 info: json!({
424 "offset": offset,
425 "bytes": BASE64_STANDARD.encode(&bytes),
426 "account": buffer_address.to_string(),
427 "authority": authority_address.to_string(),
428 }),
429 }
430 );
431 assert!(
432 parse_bpf_upgradeable_loader(
433 &message.instructions[0],
434 &AccountKeys::new(&message.account_keys[0..1], None)
435 )
436 .is_err()
437 );
438 let keys = message.account_keys.clone();
439 message.instructions[0].accounts.pop();
440 assert!(
441 parse_bpf_upgradeable_loader(&message.instructions[0], &AccountKeys::new(&keys, None))
442 .is_err()
443 );
444 }
445
446 #[test]
447 fn test_parse_bpf_upgradeable_loader_deploy_ix() {
448 let max_data_len = 54321;
449
450 let payer_address = Pubkey::new_unique();
451 let program_address = Pubkey::new_unique();
452 let buffer_address = Pubkey::new_unique();
453 let upgrade_authority_address = Pubkey::new_unique();
454 let programdata_address = Pubkey::find_program_address(
455 &[program_address.as_ref()],
456 &solana_sdk_ids::bpf_loader_upgradeable::id(),
457 )
458 .0;
459 #[allow(deprecated)]
460 let instructions = bpf_loader_upgradeable::deploy_with_max_program_len(
461 &payer_address,
462 &program_address,
463 &buffer_address,
464 &upgrade_authority_address,
465 55,
466 max_data_len,
467 )
468 .unwrap();
469 let mut message = Message::new(&instructions, None);
470 assert_eq!(
471 parse_bpf_upgradeable_loader(
472 &message.instructions[1],
473 &AccountKeys::new(&message.account_keys, None)
474 )
475 .unwrap(),
476 ParsedInstructionEnum {
477 instruction_type: "deployWithMaxDataLen".to_string(),
478 info: json!({
479 "maxDataLen": max_data_len,
480 "payerAccount": payer_address.to_string(),
481 "programAccount": program_address.to_string(),
482 "authority": upgrade_authority_address.to_string(),
483 "programDataAccount": programdata_address.to_string(),
484 "bufferAccount": buffer_address.to_string(),
485 "rentSysvar": sysvar::rent::ID.to_string(),
486 "clockSysvar": sysvar::clock::ID.to_string(),
487 "systemProgram": system_program::ID.to_string(),
488 }),
489 }
490 );
491 assert!(
492 parse_bpf_upgradeable_loader(
493 &message.instructions[1],
494 &AccountKeys::new(&message.account_keys[0..7], None)
495 )
496 .is_err()
497 );
498 let keys = message.account_keys.clone();
499 message.instructions[1].accounts.pop();
500 assert!(
501 parse_bpf_upgradeable_loader(&message.instructions[1], &AccountKeys::new(&keys, None))
502 .is_err()
503 );
504 }
505
506 #[test]
507 fn test_parse_bpf_upgradeable_loader_upgrade_ix() {
508 let program_address = Pubkey::new_unique();
509 let buffer_address = Pubkey::new_unique();
510 let authority_address = Pubkey::new_unique();
511 let spill_address = Pubkey::new_unique();
512 let programdata_address = Pubkey::find_program_address(
513 &[program_address.as_ref()],
514 &solana_sdk_ids::bpf_loader_upgradeable::id(),
515 )
516 .0;
517 let instruction = bpf_loader_upgradeable::upgrade(
518 &program_address,
519 &buffer_address,
520 &authority_address,
521 &spill_address,
522 );
523 let mut message = Message::new(&[instruction], None);
524 assert_eq!(
525 parse_bpf_upgradeable_loader(
526 &message.instructions[0],
527 &AccountKeys::new(&message.account_keys, None)
528 )
529 .unwrap(),
530 ParsedInstructionEnum {
531 instruction_type: "upgrade".to_string(),
532 info: json!({
533 "authority": authority_address.to_string(),
534 "programDataAccount": programdata_address.to_string(),
535 "programAccount": program_address.to_string(),
536 "bufferAccount": buffer_address.to_string(),
537 "spillAccount": spill_address.to_string(),
538 "rentSysvar": sysvar::rent::ID.to_string(),
539 "clockSysvar": sysvar::clock::ID.to_string(),
540 }),
541 }
542 );
543 assert!(
544 parse_bpf_upgradeable_loader(
545 &message.instructions[0],
546 &AccountKeys::new(&message.account_keys[0..6], None)
547 )
548 .is_err()
549 );
550 let keys = message.account_keys.clone();
551 message.instructions[0].accounts.pop();
552 assert!(
553 parse_bpf_upgradeable_loader(&message.instructions[0], &AccountKeys::new(&keys, None))
554 .is_err()
555 );
556 }
557
558 #[test]
559 fn test_parse_bpf_upgradeable_loader_set_buffer_authority_ix() {
560 let buffer_address = Pubkey::new_unique();
561 let current_authority_address = Pubkey::new_unique();
562 let new_authority_address = Pubkey::new_unique();
563 let instruction = bpf_loader_upgradeable::set_buffer_authority(
564 &buffer_address,
565 ¤t_authority_address,
566 &new_authority_address,
567 );
568 let mut message = Message::new(&[instruction], None);
569 assert_eq!(
570 parse_bpf_upgradeable_loader(
571 &message.instructions[0],
572 &AccountKeys::new(&message.account_keys, None)
573 )
574 .unwrap(),
575 ParsedInstructionEnum {
576 instruction_type: "setAuthority".to_string(),
577 info: json!({
578 "account": buffer_address.to_string(),
579 "authority": current_authority_address.to_string(),
580 "newAuthority": new_authority_address.to_string(),
581 }),
582 }
583 );
584 assert!(
585 parse_bpf_upgradeable_loader(
586 &message.instructions[0],
587 &AccountKeys::new(&message.account_keys[0..1], None)
588 )
589 .is_err()
590 );
591 let keys = message.account_keys.clone();
592 message.instructions[0].accounts.pop();
593 message.instructions[0].accounts.pop();
594 assert!(
595 parse_bpf_upgradeable_loader(&message.instructions[0], &AccountKeys::new(&keys, None))
596 .is_err()
597 );
598 }
599
600 #[test]
601 fn test_parse_bpf_upgradeable_loader_set_buffer_authority_checked_ix() {
602 let buffer_address = Pubkey::new_unique();
603 let current_authority_address = Pubkey::new_unique();
604 let new_authority_address = Pubkey::new_unique();
605 let instruction = bpf_loader_upgradeable::set_buffer_authority_checked(
606 &buffer_address,
607 ¤t_authority_address,
608 &new_authority_address,
609 );
610 let message = Message::new(&[instruction], None);
611 assert_eq!(
612 parse_bpf_upgradeable_loader(
613 &message.instructions[0],
614 &AccountKeys::new(&message.account_keys, None)
615 )
616 .unwrap(),
617 ParsedInstructionEnum {
618 instruction_type: "setAuthorityChecked".to_string(),
619 info: json!({
620 "account": buffer_address.to_string(),
621 "authority": current_authority_address.to_string(),
622 "newAuthority": new_authority_address.to_string(),
623 }),
624 }
625 );
626 assert!(
627 parse_bpf_upgradeable_loader(
628 &message.instructions[0],
629 &AccountKeys::new(&message.account_keys[0..2], None)
630 )
631 .is_err()
632 );
633 }
634
635 #[test]
636 fn test_parse_bpf_upgradeable_loader_set_upgrade_authority_ix() {
637 let program_address = Pubkey::new_unique();
638 let current_authority_address = Pubkey::new_unique();
639 let new_authority_address = Pubkey::new_unique();
640 let (programdata_address, _) = Pubkey::find_program_address(
641 &[program_address.as_ref()],
642 &solana_sdk_ids::bpf_loader_upgradeable::id(),
643 );
644 let instruction = bpf_loader_upgradeable::set_upgrade_authority(
645 &program_address,
646 ¤t_authority_address,
647 Some(&new_authority_address),
648 );
649 let mut message = Message::new(&[instruction], None);
650 assert_eq!(
651 parse_bpf_upgradeable_loader(
652 &message.instructions[0],
653 &AccountKeys::new(&message.account_keys, None)
654 )
655 .unwrap(),
656 ParsedInstructionEnum {
657 instruction_type: "setAuthority".to_string(),
658 info: json!({
659 "account": programdata_address.to_string(),
660 "authority": current_authority_address.to_string(),
661 "newAuthority": new_authority_address.to_string(),
662 }),
663 }
664 );
665 assert!(
666 parse_bpf_upgradeable_loader(
667 &message.instructions[0],
668 &AccountKeys::new(&message.account_keys[0..1], None)
669 )
670 .is_err()
671 );
672 let keys = message.account_keys.clone();
673 message.instructions[0].accounts.pop();
674 message.instructions[0].accounts.pop();
675 assert!(
676 parse_bpf_upgradeable_loader(&message.instructions[0], &AccountKeys::new(&keys, None))
677 .is_err()
678 );
679
680 let instruction = bpf_loader_upgradeable::set_upgrade_authority(
681 &program_address,
682 ¤t_authority_address,
683 None,
684 );
685 let mut message = Message::new(&[instruction], None);
686 assert_eq!(
687 parse_bpf_upgradeable_loader(
688 &message.instructions[0],
689 &AccountKeys::new(&message.account_keys, None)
690 )
691 .unwrap(),
692 ParsedInstructionEnum {
693 instruction_type: "setAuthority".to_string(),
694 info: json!({
695 "account": programdata_address.to_string(),
696 "authority": current_authority_address.to_string(),
697 "newAuthority": Value::Null,
698 }),
699 }
700 );
701 assert!(
702 parse_bpf_upgradeable_loader(
703 &message.instructions[0],
704 &AccountKeys::new(&message.account_keys[0..1], None)
705 )
706 .is_err()
707 );
708 let keys = message.account_keys.clone();
709 message.instructions[0].accounts.pop();
710 assert!(
711 parse_bpf_upgradeable_loader(&message.instructions[0], &AccountKeys::new(&keys, None))
712 .is_err()
713 );
714 }
715
716 #[test]
717 fn test_parse_bpf_upgradeable_loader_set_upgrade_authority_checked_ix() {
718 let program_address = Pubkey::new_unique();
719 let current_authority_address = Pubkey::new_unique();
720 let new_authority_address = Pubkey::new_unique();
721 let (programdata_address, _) = Pubkey::find_program_address(
722 &[program_address.as_ref()],
723 &solana_sdk_ids::bpf_loader_upgradeable::id(),
724 );
725 let instruction = bpf_loader_upgradeable::set_upgrade_authority_checked(
726 &program_address,
727 ¤t_authority_address,
728 &new_authority_address,
729 );
730 let message = Message::new(&[instruction], None);
731 assert_eq!(
732 parse_bpf_upgradeable_loader(
733 &message.instructions[0],
734 &AccountKeys::new(&message.account_keys, None)
735 )
736 .unwrap(),
737 ParsedInstructionEnum {
738 instruction_type: "setAuthorityChecked".to_string(),
739 info: json!({
740 "account": programdata_address.to_string(),
741 "authority": current_authority_address.to_string(),
742 "newAuthority": new_authority_address.to_string(),
743 }),
744 }
745 );
746
747 assert!(
748 parse_bpf_upgradeable_loader(
749 &message.instructions[0],
750 &AccountKeys::new(&message.account_keys[0..2], None)
751 )
752 .is_err()
753 );
754 }
755
756 #[test]
757 fn test_parse_bpf_upgradeable_loader_close_buffer_ix() {
758 let close_address = Pubkey::new_unique();
759 let recipient_address = Pubkey::new_unique();
760 let authority_address = Pubkey::new_unique();
761 let instruction =
762 bpf_loader_upgradeable::close(&close_address, &recipient_address, &authority_address);
763 let mut message = Message::new(&[instruction], None);
764 assert_eq!(
765 parse_bpf_upgradeable_loader(
766 &message.instructions[0],
767 &AccountKeys::new(&message.account_keys, None)
768 )
769 .unwrap(),
770 ParsedInstructionEnum {
771 instruction_type: "close".to_string(),
772 info: json!({
773 "account": close_address.to_string(),
774 "recipient": recipient_address.to_string(),
775 "authority": authority_address.to_string(),
776 "programAccount": Value::Null
777 }),
778 }
779 );
780 assert!(
781 parse_bpf_upgradeable_loader(
782 &message.instructions[0],
783 &AccountKeys::new(&message.account_keys[0..1], None)
784 )
785 .is_err()
786 );
787 let keys = message.account_keys.clone();
788 message.instructions[0].accounts.pop();
789 assert!(
790 parse_bpf_upgradeable_loader(&message.instructions[0], &AccountKeys::new(&keys, None))
791 .is_err()
792 );
793 }
794
795 #[test]
796 fn test_parse_bpf_upgradeable_loader_close_program_ix() {
797 let close_address = Pubkey::new_unique();
798 let recipient_address = Pubkey::new_unique();
799 let authority_address = Pubkey::new_unique();
800 let program_address = Pubkey::new_unique();
801 let instruction = bpf_loader_upgradeable::close_any(
802 &close_address,
803 &recipient_address,
804 Some(&authority_address),
805 Some(&program_address),
806 );
807 let mut message = Message::new(&[instruction], None);
808 assert_eq!(
809 parse_bpf_upgradeable_loader(
810 &message.instructions[0],
811 &AccountKeys::new(&message.account_keys, None)
812 )
813 .unwrap(),
814 ParsedInstructionEnum {
815 instruction_type: "close".to_string(),
816 info: json!({
817 "account": close_address.to_string(),
818 "recipient": recipient_address.to_string(),
819 "authority": authority_address.to_string(),
820 "programAccount": program_address.to_string()
821 }),
822 }
823 );
824 assert!(
825 parse_bpf_upgradeable_loader(
826 &message.instructions[0],
827 &AccountKeys::new(&message.account_keys[0..1], None)
828 )
829 .is_err()
830 );
831 let keys = message.account_keys.clone();
832 message.instructions[0].accounts.pop();
833 message.instructions[0].accounts.pop();
834 assert!(
835 parse_bpf_upgradeable_loader(&message.instructions[0], &AccountKeys::new(&keys, None))
836 .is_err()
837 );
838 }
839}