solana_transaction_status/
parse_bpf_loader.rs

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