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