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