182 lines
6.9 KiB
Rust
182 lines
6.9 KiB
Rust
use crate::{
|
|
parse_account_data::{ParsableAccount, ParseAccountError},
|
|
UiAccountData, UiAccountEncoding,
|
|
};
|
|
use bincode::{deserialize, serialized_size};
|
|
use solana_sdk::{bpf_loader_upgradeable::UpgradeableLoaderState, pubkey::Pubkey};
|
|
|
|
pub fn parse_bpf_upgradeable_loader(
|
|
data: &[u8],
|
|
) -> Result<BpfUpgradeableLoaderAccountType, ParseAccountError> {
|
|
let account_state: UpgradeableLoaderState = deserialize(data).map_err(|_| {
|
|
ParseAccountError::AccountNotParsable(ParsableAccount::BpfUpgradeableLoader)
|
|
})?;
|
|
let parsed_account = match account_state {
|
|
UpgradeableLoaderState::Uninitialized => BpfUpgradeableLoaderAccountType::Uninitialized,
|
|
UpgradeableLoaderState::Buffer { authority_address } => {
|
|
let offset = if authority_address.is_some() {
|
|
UpgradeableLoaderState::buffer_data_offset().unwrap()
|
|
} else {
|
|
// This case included for code completeness; in practice, a Buffer account will
|
|
// always have authority_address.is_some()
|
|
UpgradeableLoaderState::buffer_data_offset().unwrap()
|
|
- serialized_size(&Pubkey::default()).unwrap() as usize
|
|
};
|
|
BpfUpgradeableLoaderAccountType::Buffer(UiBuffer {
|
|
authority: authority_address.map(|pubkey| pubkey.to_string()),
|
|
data: UiAccountData::Binary(
|
|
base64::encode(&data[offset as usize..]),
|
|
UiAccountEncoding::Base64,
|
|
),
|
|
})
|
|
}
|
|
UpgradeableLoaderState::Program {
|
|
programdata_address,
|
|
} => BpfUpgradeableLoaderAccountType::Program(UiProgram {
|
|
program_data: programdata_address.to_string(),
|
|
}),
|
|
UpgradeableLoaderState::ProgramData {
|
|
slot,
|
|
upgrade_authority_address,
|
|
} => {
|
|
let offset = if upgrade_authority_address.is_some() {
|
|
UpgradeableLoaderState::programdata_data_offset().unwrap()
|
|
} else {
|
|
UpgradeableLoaderState::programdata_data_offset().unwrap()
|
|
- serialized_size(&Pubkey::default()).unwrap() as usize
|
|
};
|
|
BpfUpgradeableLoaderAccountType::ProgramData(UiProgramData {
|
|
slot,
|
|
authority: upgrade_authority_address.map(|pubkey| pubkey.to_string()),
|
|
data: UiAccountData::Binary(
|
|
base64::encode(&data[offset as usize..]),
|
|
UiAccountEncoding::Base64,
|
|
),
|
|
})
|
|
}
|
|
};
|
|
Ok(parsed_account)
|
|
}
|
|
|
|
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
|
#[serde(rename_all = "camelCase", tag = "type", content = "info")]
|
|
pub enum BpfUpgradeableLoaderAccountType {
|
|
Uninitialized,
|
|
Buffer(UiBuffer),
|
|
Program(UiProgram),
|
|
ProgramData(UiProgramData),
|
|
}
|
|
|
|
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct UiBuffer {
|
|
pub authority: Option<String>,
|
|
pub data: UiAccountData,
|
|
}
|
|
|
|
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct UiProgram {
|
|
pub program_data: String,
|
|
}
|
|
|
|
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct UiProgramData {
|
|
pub slot: u64,
|
|
pub authority: Option<String>,
|
|
pub data: UiAccountData,
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod test {
|
|
use super::*;
|
|
use bincode::serialize;
|
|
use solana_sdk::pubkey::Pubkey;
|
|
|
|
#[test]
|
|
fn test_parse_bpf_upgradeable_loader_accounts() {
|
|
let bpf_loader_state = UpgradeableLoaderState::Uninitialized;
|
|
let account_data = serialize(&bpf_loader_state).unwrap();
|
|
assert_eq!(
|
|
parse_bpf_upgradeable_loader(&account_data).unwrap(),
|
|
BpfUpgradeableLoaderAccountType::Uninitialized
|
|
);
|
|
|
|
let program = vec![7u8; 64]; // Arbitrary program data
|
|
|
|
let authority = Pubkey::new_unique();
|
|
let bpf_loader_state = UpgradeableLoaderState::Buffer {
|
|
authority_address: Some(authority),
|
|
};
|
|
let mut account_data = serialize(&bpf_loader_state).unwrap();
|
|
account_data.extend_from_slice(&program);
|
|
assert_eq!(
|
|
parse_bpf_upgradeable_loader(&account_data).unwrap(),
|
|
BpfUpgradeableLoaderAccountType::Buffer(UiBuffer {
|
|
authority: Some(authority.to_string()),
|
|
data: UiAccountData::Binary(base64::encode(&program), UiAccountEncoding::Base64),
|
|
})
|
|
);
|
|
|
|
// This case included for code completeness; in practice, a Buffer account will always have
|
|
// authority_address.is_some()
|
|
let bpf_loader_state = UpgradeableLoaderState::Buffer {
|
|
authority_address: None,
|
|
};
|
|
let mut account_data = serialize(&bpf_loader_state).unwrap();
|
|
account_data.extend_from_slice(&program);
|
|
assert_eq!(
|
|
parse_bpf_upgradeable_loader(&account_data).unwrap(),
|
|
BpfUpgradeableLoaderAccountType::Buffer(UiBuffer {
|
|
authority: None,
|
|
data: UiAccountData::Binary(base64::encode(&program), UiAccountEncoding::Base64),
|
|
})
|
|
);
|
|
|
|
let programdata_address = Pubkey::new_unique();
|
|
let bpf_loader_state = UpgradeableLoaderState::Program {
|
|
programdata_address,
|
|
};
|
|
let account_data = serialize(&bpf_loader_state).unwrap();
|
|
assert_eq!(
|
|
parse_bpf_upgradeable_loader(&account_data).unwrap(),
|
|
BpfUpgradeableLoaderAccountType::Program(UiProgram {
|
|
program_data: programdata_address.to_string(),
|
|
})
|
|
);
|
|
|
|
let authority = Pubkey::new_unique();
|
|
let slot = 42;
|
|
let bpf_loader_state = UpgradeableLoaderState::ProgramData {
|
|
slot,
|
|
upgrade_authority_address: Some(authority),
|
|
};
|
|
let mut account_data = serialize(&bpf_loader_state).unwrap();
|
|
account_data.extend_from_slice(&program);
|
|
assert_eq!(
|
|
parse_bpf_upgradeable_loader(&account_data).unwrap(),
|
|
BpfUpgradeableLoaderAccountType::ProgramData(UiProgramData {
|
|
slot,
|
|
authority: Some(authority.to_string()),
|
|
data: UiAccountData::Binary(base64::encode(&program), UiAccountEncoding::Base64),
|
|
})
|
|
);
|
|
|
|
let bpf_loader_state = UpgradeableLoaderState::ProgramData {
|
|
slot,
|
|
upgrade_authority_address: None,
|
|
};
|
|
let mut account_data = serialize(&bpf_loader_state).unwrap();
|
|
account_data.extend_from_slice(&program);
|
|
assert_eq!(
|
|
parse_bpf_upgradeable_loader(&account_data).unwrap(),
|
|
BpfUpgradeableLoaderAccountType::ProgramData(UiProgramData {
|
|
slot,
|
|
authority: None,
|
|
data: UiAccountData::Binary(base64::encode(&program), UiAccountEncoding::Base64),
|
|
})
|
|
);
|
|
}
|
|
}
|