solana_account_decoder_wasm/
parse_bpf_loader.rs

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