2021-12-03 09:00:31 -08:00
|
|
|
use {
|
|
|
|
crate::{
|
|
|
|
parse_account_data::{ParsableAccount, ParseAccountError},
|
|
|
|
UiAccountData, UiAccountEncoding,
|
|
|
|
},
|
|
|
|
bincode::{deserialize, serialized_size},
|
|
|
|
solana_sdk::{bpf_loader_upgradeable::UpgradeableLoaderState, pubkey::Pubkey},
|
2021-02-08 16:18:10 -08:00
|
|
|
};
|
|
|
|
|
|
|
|
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() {
|
2022-05-11 07:22:59 -07:00
|
|
|
UpgradeableLoaderState::size_of_buffer_metadata()
|
2021-02-08 16:18:10 -08:00
|
|
|
} else {
|
|
|
|
// This case included for code completeness; in practice, a Buffer account will
|
|
|
|
// always have authority_address.is_some()
|
2022-05-11 07:22:59 -07:00
|
|
|
UpgradeableLoaderState::size_of_buffer_metadata()
|
2021-02-08 16:18:10 -08:00
|
|
|
- serialized_size(&Pubkey::default()).unwrap() as usize
|
|
|
|
};
|
|
|
|
BpfUpgradeableLoaderAccountType::Buffer(UiBuffer {
|
|
|
|
authority: authority_address.map(|pubkey| pubkey.to_string()),
|
|
|
|
data: UiAccountData::Binary(
|
2022-11-09 11:39:38 -08:00
|
|
|
base64::encode(&data[offset..]),
|
2021-02-08 16:18:10 -08:00
|
|
|
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() {
|
2022-05-11 07:22:59 -07:00
|
|
|
UpgradeableLoaderState::size_of_programdata_metadata()
|
2021-02-08 16:18:10 -08:00
|
|
|
} else {
|
2022-05-11 07:22:59 -07:00
|
|
|
UpgradeableLoaderState::size_of_programdata_metadata()
|
2021-02-08 16:18:10 -08:00
|
|
|
- serialized_size(&Pubkey::default()).unwrap() as usize
|
|
|
|
};
|
|
|
|
BpfUpgradeableLoaderAccountType::ProgramData(UiProgramData {
|
|
|
|
slot,
|
|
|
|
authority: upgrade_authority_address.map(|pubkey| pubkey.to_string()),
|
|
|
|
data: UiAccountData::Binary(
|
2022-11-09 11:39:38 -08:00
|
|
|
base64::encode(&data[offset..]),
|
2021-02-08 16:18:10 -08:00
|
|
|
UiAccountEncoding::Base64,
|
|
|
|
),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
};
|
|
|
|
Ok(parsed_account)
|
|
|
|
}
|
|
|
|
|
2022-05-22 18:00:42 -07:00
|
|
|
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
|
2021-02-08 16:18:10 -08:00
|
|
|
#[serde(rename_all = "camelCase", tag = "type", content = "info")]
|
|
|
|
pub enum BpfUpgradeableLoaderAccountType {
|
|
|
|
Uninitialized,
|
|
|
|
Buffer(UiBuffer),
|
|
|
|
Program(UiProgram),
|
|
|
|
ProgramData(UiProgramData),
|
|
|
|
}
|
|
|
|
|
2022-05-22 18:00:42 -07:00
|
|
|
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
|
2021-02-08 16:18:10 -08:00
|
|
|
#[serde(rename_all = "camelCase")]
|
|
|
|
pub struct UiBuffer {
|
|
|
|
pub authority: Option<String>,
|
|
|
|
pub data: UiAccountData,
|
|
|
|
}
|
|
|
|
|
2022-05-22 18:00:42 -07:00
|
|
|
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
|
2021-02-08 16:18:10 -08:00
|
|
|
#[serde(rename_all = "camelCase")]
|
|
|
|
pub struct UiProgram {
|
|
|
|
pub program_data: String,
|
|
|
|
}
|
|
|
|
|
2022-05-22 18:00:42 -07:00
|
|
|
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
|
2021-02-08 16:18:10 -08:00
|
|
|
#[serde(rename_all = "camelCase")]
|
|
|
|
pub struct UiProgramData {
|
|
|
|
pub slot: u64,
|
|
|
|
pub authority: Option<String>,
|
|
|
|
pub data: UiAccountData,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod test {
|
2021-12-03 09:00:31 -08:00
|
|
|
use {super::*, bincode::serialize, solana_sdk::pubkey::Pubkey};
|
2021-02-08 16:18:10 -08:00
|
|
|
|
|
|
|
#[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),
|
|
|
|
})
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|