gemachain_account_decoder/
parse_bpf_loader.rs

1use crate::{
2    parse_account_data::{ParsableAccount, ParseAccountError},
3    UiAccountData, UiAccountEncoding,
4};
5use bincode::{deserialize, serialized_size};
6use gemachain_sdk::{bpf_loader_upgradeable::UpgradeableLoaderState, pubkey::Pubkey};
7
8pub fn parse_bpf_upgradeable_loader(
9    data: &[u8],
10) -> Result<BpfUpgradeableLoaderAccountType, ParseAccountError> {
11    let account_state: UpgradeableLoaderState = deserialize(data).map_err(|_| {
12        ParseAccountError::AccountNotParsable(ParsableAccount::BpfUpgradeableLoader)
13    })?;
14    let parsed_account = match account_state {
15        UpgradeableLoaderState::Uninitialized => BpfUpgradeableLoaderAccountType::Uninitialized,
16        UpgradeableLoaderState::Buffer { authority_address } => {
17            let offset = if authority_address.is_some() {
18                UpgradeableLoaderState::buffer_data_offset().unwrap()
19            } else {
20                // This case included for code completeness; in practice, a Buffer account will
21                // always have authority_address.is_some()
22                UpgradeableLoaderState::buffer_data_offset().unwrap()
23                    - serialized_size(&Pubkey::default()).unwrap() as usize
24            };
25            BpfUpgradeableLoaderAccountType::Buffer(UiBuffer {
26                authority: authority_address.map(|pubkey| pubkey.to_string()),
27                data: UiAccountData::Binary(
28                    base64::encode(&data[offset as usize..]),
29                    UiAccountEncoding::Base64,
30                ),
31            })
32        }
33        UpgradeableLoaderState::Program {
34            programdata_address,
35        } => BpfUpgradeableLoaderAccountType::Program(UiProgram {
36            program_data: programdata_address.to_string(),
37        }),
38        UpgradeableLoaderState::ProgramData {
39            slot,
40            upgrade_authority_address,
41        } => {
42            let offset = if upgrade_authority_address.is_some() {
43                UpgradeableLoaderState::programdata_data_offset().unwrap()
44            } else {
45                UpgradeableLoaderState::programdata_data_offset().unwrap()
46                    - serialized_size(&Pubkey::default()).unwrap() as usize
47            };
48            BpfUpgradeableLoaderAccountType::ProgramData(UiProgramData {
49                slot,
50                authority: upgrade_authority_address.map(|pubkey| pubkey.to_string()),
51                data: UiAccountData::Binary(
52                    base64::encode(&data[offset as usize..]),
53                    UiAccountEncoding::Base64,
54                ),
55            })
56        }
57    };
58    Ok(parsed_account)
59}
60
61#[derive(Debug, Serialize, Deserialize, PartialEq)]
62#[serde(rename_all = "camelCase", tag = "type", content = "info")]
63pub enum BpfUpgradeableLoaderAccountType {
64    Uninitialized,
65    Buffer(UiBuffer),
66    Program(UiProgram),
67    ProgramData(UiProgramData),
68}
69
70#[derive(Debug, Serialize, Deserialize, PartialEq)]
71#[serde(rename_all = "camelCase")]
72pub struct UiBuffer {
73    pub authority: Option<String>,
74    pub data: UiAccountData,
75}
76
77#[derive(Debug, Serialize, Deserialize, PartialEq)]
78#[serde(rename_all = "camelCase")]
79pub struct UiProgram {
80    pub program_data: String,
81}
82
83#[derive(Debug, Serialize, Deserialize, PartialEq)]
84#[serde(rename_all = "camelCase")]
85pub struct UiProgramData {
86    pub slot: u64,
87    pub authority: Option<String>,
88    pub data: UiAccountData,
89}
90
91#[cfg(test)]
92mod test {
93    use super::*;
94    use bincode::serialize;
95    use gemachain_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}