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