use crate::{
instruction::{AccountMeta, Instruction, InstructionError},
loader_upgradeable_instruction::UpgradeableLoaderInstruction,
pubkey::Pubkey,
system_instruction, sysvar,
};
use bincode::serialized_size;
crate::declare_id!("BPFLoaderUpgradeab1e11111111111111111111111");
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone, Copy, AbiExample)]
pub enum UpgradeableLoaderState {
Uninitialized,
Buffer {
authority_address: Option<Pubkey>,
},
Program {
programdata_address: Pubkey,
},
ProgramData {
slot: u64,
upgrade_authority_address: Option<Pubkey>,
},
}
impl UpgradeableLoaderState {
pub fn buffer_len(program_len: usize) -> Result<usize, InstructionError> {
Ok(serialized_size(&Self::Buffer {
authority_address: Some(Pubkey::default()),
})
.map(|len| len as usize)
.map_err(|_| InstructionError::InvalidInstructionData)?
.saturating_add(program_len))
}
pub fn buffer_data_offset() -> Result<usize, InstructionError> {
Self::buffer_len(0)
}
pub fn program_len() -> Result<usize, InstructionError> {
serialized_size(&Self::Program {
programdata_address: Pubkey::default(),
})
.map(|len| len as usize)
.map_err(|_| InstructionError::InvalidInstructionData)
}
pub fn programdata_len(program_len: usize) -> Result<usize, InstructionError> {
Ok(serialized_size(&Self::ProgramData {
slot: 0,
upgrade_authority_address: Some(Pubkey::default()),
})
.map(|len| len as usize)
.map_err(|_| InstructionError::InvalidInstructionData)?
.saturating_add(program_len))
}
pub fn programdata_data_offset() -> Result<usize, InstructionError> {
Self::programdata_len(0)
}
}
pub fn create_buffer(
payer_address: &Pubkey,
buffer_address: &Pubkey,
authority_address: &Pubkey,
lamports: u64,
program_len: usize,
) -> Result<Vec<Instruction>, InstructionError> {
Ok(vec![
system_instruction::create_account(
payer_address,
buffer_address,
lamports,
UpgradeableLoaderState::buffer_len(program_len)? as u64,
&id(),
),
Instruction::new(
id(),
&UpgradeableLoaderInstruction::InitializeBuffer,
vec![
AccountMeta::new(*buffer_address, false),
AccountMeta::new_readonly(*authority_address, false),
],
),
])
}
pub fn write(
buffer_address: &Pubkey,
authority_address: &Pubkey,
offset: u32,
bytes: Vec<u8>,
) -> Instruction {
Instruction::new(
id(),
&UpgradeableLoaderInstruction::Write { offset, bytes },
vec![
AccountMeta::new(*buffer_address, false),
AccountMeta::new_readonly(*authority_address, true),
],
)
}
pub fn deploy_with_max_program_len(
payer_address: &Pubkey,
program_address: &Pubkey,
buffer_address: &Pubkey,
upgrade_authority_address: &Pubkey,
program_lamports: u64,
max_data_len: usize,
) -> Result<Vec<Instruction>, InstructionError> {
let (programdata_address, _) = Pubkey::find_program_address(&[program_address.as_ref()], &id());
Ok(vec![
system_instruction::create_account(
payer_address,
program_address,
program_lamports,
UpgradeableLoaderState::program_len()? as u64,
&id(),
),
Instruction::new(
id(),
&UpgradeableLoaderInstruction::DeployWithMaxDataLen { max_data_len },
vec![
AccountMeta::new(*payer_address, true),
AccountMeta::new(programdata_address, false),
AccountMeta::new(*program_address, false),
AccountMeta::new(*buffer_address, false),
AccountMeta::new_readonly(sysvar::rent::id(), false),
AccountMeta::new_readonly(sysvar::clock::id(), false),
AccountMeta::new_readonly(crate::system_program::id(), false),
AccountMeta::new_readonly(*upgrade_authority_address, true),
],
),
])
}
pub fn upgrade(
program_address: &Pubkey,
buffer_address: &Pubkey,
authority_address: &Pubkey,
spill_address: &Pubkey,
) -> Instruction {
let (programdata_address, _) = Pubkey::find_program_address(&[program_address.as_ref()], &id());
Instruction::new(
id(),
&UpgradeableLoaderInstruction::Upgrade,
vec![
AccountMeta::new(programdata_address, false),
AccountMeta::new(*program_address, false),
AccountMeta::new(*buffer_address, false),
AccountMeta::new(*spill_address, false),
AccountMeta::new_readonly(sysvar::rent::id(), false),
AccountMeta::new_readonly(sysvar::clock::id(), false),
AccountMeta::new_readonly(*authority_address, true),
],
)
}
pub fn is_upgrade_instruction(instruction_data: &[u8]) -> bool {
3 == instruction_data[0]
}
pub fn set_buffer_authority(
buffer_address: &Pubkey,
current_authority_address: &Pubkey,
new_authority_address: &Pubkey,
) -> Instruction {
Instruction::new(
id(),
&UpgradeableLoaderInstruction::SetAuthority,
vec![
AccountMeta::new(*buffer_address, false),
AccountMeta::new_readonly(*current_authority_address, true),
AccountMeta::new_readonly(*new_authority_address, false),
],
)
}
pub fn set_upgrade_authority(
program_address: &Pubkey,
current_authority_address: &Pubkey,
new_authority_address: Option<&Pubkey>,
) -> Instruction {
let (programdata_address, _) = Pubkey::find_program_address(&[program_address.as_ref()], &id());
let mut metas = vec![
AccountMeta::new(programdata_address, false),
AccountMeta::new_readonly(*current_authority_address, true),
];
if let Some(address) = new_authority_address {
metas.push(AccountMeta::new_readonly(*address, false));
}
Instruction::new(id(), &UpgradeableLoaderInstruction::SetAuthority, metas)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_account_lengths() {
assert_eq!(
4,
serialized_size(&UpgradeableLoaderState::Uninitialized).unwrap()
);
assert_eq!(36, UpgradeableLoaderState::program_len().unwrap());
assert_eq!(
45,
UpgradeableLoaderState::programdata_data_offset().unwrap()
);
assert_eq!(
45 + 42,
UpgradeableLoaderState::programdata_len(42).unwrap()
);
}
#[test]
fn test_is_upgrade_instruction() {
assert_eq!(
false,
is_upgrade_instruction(
&bincode::serialize(&UpgradeableLoaderInstruction::InitializeBuffer).unwrap()
)
);
assert_eq!(
false,
is_upgrade_instruction(
&bincode::serialize(&UpgradeableLoaderInstruction::Write {
offset: 0,
bytes: vec![],
})
.unwrap()
)
);
assert_eq!(
false,
is_upgrade_instruction(
&bincode::serialize(&UpgradeableLoaderInstruction::DeployWithMaxDataLen {
max_data_len: 0,
})
.unwrap()
)
);
assert_eq!(
true,
is_upgrade_instruction(
&bincode::serialize(&UpgradeableLoaderInstruction::Upgrade).unwrap()
)
);
assert_eq!(
false,
is_upgrade_instruction(
&bincode::serialize(&UpgradeableLoaderInstruction::SetAuthority).unwrap()
)
);
}
}