safecoin_account_decoder/
parse_bpf_loader.rs

1use {
2    crate::{
3        parse_account_data::{ParsableAccount, ParseAccountError},
4        UiAccountData, UiAccountEncoding,
5    },
6    bincode::{deserialize, serialized_size},
7    solana_sdk::{bpf_loader_upgradeable::UpgradeableLoaderState, pubkey::Pubkey},
8};
9
10pub fn parse_bpf_upgradeable_loader(
11    data: &[u8],
12) -> Result<BpfUpgradeableLoaderAccountType, ParseAccountError> {
13    let account_state: UpgradeableLoaderState = deserialize(data).map_err(|_| {
14        ParseAccountError::AccountNotParsable(ParsableAccount::BpfUpgradeableLoader)
15    })?;
16    let parsed_account = match account_state {
17        UpgradeableLoaderState::Uninitialized => BpfUpgradeableLoaderAccountType::Uninitialized,
18        UpgradeableLoaderState::Buffer { authority_address } => {
19            let offset = if authority_address.is_some() {
20                UpgradeableLoaderState::size_of_buffer_metadata()
21            } else {
22                // This case included for code completeness; in practice, a Buffer account will
23                // always have authority_address.is_some()
24                UpgradeableLoaderState::size_of_buffer_metadata()
25                    - serialized_size(&Pubkey::default()).unwrap() as usize
26            };
27            BpfUpgradeableLoaderAccountType::Buffer(UiBuffer {
28                authority: authority_address.map(|pubkey| pubkey.to_string()),
29                data: UiAccountData::Binary(
30                    base64::encode(&data[offset as usize..]),
31                    UiAccountEncoding::Base64,
32                ),
33            })
34        }
35        UpgradeableLoaderState::Program {
36            programdata_address,
37        } => BpfUpgradeableLoaderAccountType::Program(UiProgram {
38            program_data: programdata_address.to_string(),
39        }),
40        UpgradeableLoaderState::ProgramData {
41            slot,
42            upgrade_authority_address,
43        } => {
44            let offset = if upgrade_authority_address.is_some() {
45                UpgradeableLoaderState::size_of_programdata_metadata()
46            } else {
47                UpgradeableLoaderState::size_of_programdata_metadata()
48                    - serialized_size(&Pubkey::default()).unwrap() as usize
49            };
50            BpfUpgradeableLoaderAccountType::ProgramData(UiProgramData {
51                slot,
52                authority: upgrade_authority_address.map(|pubkey| pubkey.to_string()),
53                data: UiAccountData::Binary(
54                    base64::encode(&data[offset as usize..]),
55                    UiAccountEncoding::Base64,
56                ),
57            })
58        }
59    };
60    Ok(parsed_account)
61}
62
63#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
64#[serde(rename_all = "camelCase", tag = "type", content = "info")]
65pub enum BpfUpgradeableLoaderAccountType {
66    Uninitialized,
67    Buffer(UiBuffer),
68    Program(UiProgram),
69    ProgramData(UiProgramData),
70}
71
72#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
73#[serde(rename_all = "camelCase")]
74pub struct UiBuffer {
75    pub authority: Option<String>,
76    pub data: UiAccountData,
77}
78
79#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
80#[serde(rename_all = "camelCase")]
81pub struct UiProgram {
82    pub program_data: String,
83}
84
85#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
86#[serde(rename_all = "camelCase")]
87pub struct UiProgramData {
88    pub slot: u64,
89    pub authority: Option<String>,
90    pub data: UiAccountData,
91}
92
93#[cfg(test)]
94mod test {
95    use {super::*, bincode::serialize, solana_sdk::pubkey::Pubkey};
96
97    #[test]
98    fn test_parse_bpf_upgradeable_loader_accounts() {
99        let bpf_loader_state = UpgradeableLoaderState::Uninitialized;
100        let account_data = serialize(&bpf_loader_state).unwrap();
101        assert_eq!(
102            parse_bpf_upgradeable_loader(&account_data).unwrap(),
103            BpfUpgradeableLoaderAccountType::Uninitialized
104        );
105
106        let program = vec![7u8; 64]; // Arbitrary program data
107
108        let authority = Pubkey::new_unique();
109        let bpf_loader_state = UpgradeableLoaderState::Buffer {
110            authority_address: Some(authority),
111        };
112        let mut account_data = serialize(&bpf_loader_state).unwrap();
113        account_data.extend_from_slice(&program);
114        assert_eq!(
115            parse_bpf_upgradeable_loader(&account_data).unwrap(),
116            BpfUpgradeableLoaderAccountType::Buffer(UiBuffer {
117                authority: Some(authority.to_string()),
118                data: UiAccountData::Binary(base64::encode(&program), UiAccountEncoding::Base64),
119            })
120        );
121
122        // This case included for code completeness; in practice, a Buffer account will always have
123        // authority_address.is_some()
124        let bpf_loader_state = UpgradeableLoaderState::Buffer {
125            authority_address: None,
126        };
127        let mut account_data = serialize(&bpf_loader_state).unwrap();
128        account_data.extend_from_slice(&program);
129        assert_eq!(
130            parse_bpf_upgradeable_loader(&account_data).unwrap(),
131            BpfUpgradeableLoaderAccountType::Buffer(UiBuffer {
132                authority: None,
133                data: UiAccountData::Binary(base64::encode(&program), UiAccountEncoding::Base64),
134            })
135        );
136
137        let programdata_address = Pubkey::new_unique();
138        let bpf_loader_state = UpgradeableLoaderState::Program {
139            programdata_address,
140        };
141        let account_data = serialize(&bpf_loader_state).unwrap();
142        assert_eq!(
143            parse_bpf_upgradeable_loader(&account_data).unwrap(),
144            BpfUpgradeableLoaderAccountType::Program(UiProgram {
145                program_data: programdata_address.to_string(),
146            })
147        );
148
149        let authority = Pubkey::new_unique();
150        let slot = 42;
151        let bpf_loader_state = UpgradeableLoaderState::ProgramData {
152            slot,
153            upgrade_authority_address: Some(authority),
154        };
155        let mut account_data = serialize(&bpf_loader_state).unwrap();
156        account_data.extend_from_slice(&program);
157        assert_eq!(
158            parse_bpf_upgradeable_loader(&account_data).unwrap(),
159            BpfUpgradeableLoaderAccountType::ProgramData(UiProgramData {
160                slot,
161                authority: Some(authority.to_string()),
162                data: UiAccountData::Binary(base64::encode(&program), UiAccountEncoding::Base64),
163            })
164        );
165
166        let bpf_loader_state = UpgradeableLoaderState::ProgramData {
167            slot,
168            upgrade_authority_address: None,
169        };
170        let mut account_data = serialize(&bpf_loader_state).unwrap();
171        account_data.extend_from_slice(&program);
172        assert_eq!(
173            parse_bpf_upgradeable_loader(&account_data).unwrap(),
174            BpfUpgradeableLoaderAccountType::ProgramData(UiProgramData {
175                slot,
176                authority: None,
177                data: UiAccountData::Binary(base64::encode(&program), UiAccountEncoding::Base64),
178            })
179        );
180    }
181}