Add Close instrruction and tooling to upgradeable loader (#15887)

This commit is contained in:
Jack May
2021-03-17 21:39:29 -07:00
committed by GitHub
parent 12399157f5
commit 7f500d610c
9 changed files with 980 additions and 64 deletions

View File

@ -30,7 +30,7 @@ use solana_sdk::{
bpf_loader_upgradeable::{self, UpgradeableLoaderState},
clock::Clock,
entrypoint::SUCCESS,
feature_set::skip_ro_deserialization,
feature_set::{skip_ro_deserialization, upgradeable_close_instruction},
ic_logger_msg, ic_msg,
instruction::InstructionError,
keyed_account::{from_keyed_account, next_keyed_account, KeyedAccount},
@ -566,11 +566,9 @@ fn process_loader_upgradeable_instruction(
programdata.try_account_ref_mut()?.data_as_mut_slice()
[programdata_data_offset..programdata_data_offset + buffer_data_len]
.copy_from_slice(&buffer.try_account_ref()?.data()[buffer_data_offset..]);
for i in &mut programdata.try_account_ref_mut()?.data_as_mut_slice()
programdata.try_account_ref_mut()?.data_as_mut_slice()
[programdata_data_offset + buffer_data_len..]
{
*i = 0
}
.fill(0);
// Fund ProgramData to rent-exemption, spill the rest
@ -634,12 +632,49 @@ fn process_loader_upgradeable_instruction(
}
_ => {
ic_logger_msg!(logger, "Account does not support authorities");
return Err(InstructionError::InvalidAccountData);
return Err(InstructionError::InvalidArgument);
}
}
ic_logger_msg!(logger, "New authority {:?}", new_authority);
}
UpgradeableLoaderInstruction::Close => {
if !invoke_context.is_feature_active(&upgradeable_close_instruction::id()) {
return Err(InstructionError::InvalidInstructionData);
}
let close_account = next_keyed_account(account_iter)?;
let recipient_account = next_keyed_account(account_iter)?;
let authority = next_keyed_account(account_iter)?;
if close_account.unsigned_key() == recipient_account.unsigned_key() {
ic_logger_msg!(logger, "Recipient is the same as the account being closed");
return Err(InstructionError::InvalidArgument);
}
if let UpgradeableLoaderState::Buffer { authority_address } = close_account.state()? {
if authority_address.is_none() {
ic_logger_msg!(logger, "Buffer is immutable");
return Err(InstructionError::Immutable);
}
if authority_address != Some(*authority.unsigned_key()) {
ic_logger_msg!(logger, "Incorrect buffer authority provided");
return Err(InstructionError::IncorrectAuthority);
}
if authority.signer_key().is_none() {
ic_logger_msg!(logger, "Buffer authority did not sign");
return Err(InstructionError::MissingRequiredSignature);
}
recipient_account.try_account_ref_mut()?.lamports += close_account.lamports()?;
close_account.try_account_ref_mut()?.lamports = 0;
close_account.try_account_ref_mut()?.data.fill(0);
} else {
ic_logger_msg!(logger, "Account does not support closing");
return Err(InstructionError::InvalidArgument);
}
ic_logger_msg!(logger, "Closed {}", close_account.unsigned_key());
}
}
Ok(())
@ -3011,7 +3046,7 @@ mod tests {
})
.unwrap();
assert_eq!(
Err(InstructionError::InvalidAccountData),
Err(InstructionError::InvalidArgument),
process_instruction(
&bpf_loader_upgradeable::id(),
&[
@ -3184,7 +3219,7 @@ mod tests {
})
.unwrap();
assert_eq!(
Err(InstructionError::InvalidAccountData),
Err(InstructionError::InvalidArgument),
process_instruction(
&bpf_loader_upgradeable::id(),
&[
@ -3217,6 +3252,87 @@ mod tests {
);
}
#[test]
fn test_bpf_loader_upgradeable_close() {
let instruction = bincode::serialize(&UpgradeableLoaderInstruction::Close).unwrap();
let authority_address = Pubkey::new_unique();
let authority_account = AccountSharedData::new_ref(1, 0, &Pubkey::new_unique());
let recipient_address = Pubkey::new_unique();
let recipient_account = AccountSharedData::new_ref(1, 0, &Pubkey::new_unique());
let buffer_address = Pubkey::new_unique();
let buffer_account = AccountSharedData::new_ref(
1,
UpgradeableLoaderState::buffer_len(0).unwrap(),
&bpf_loader_upgradeable::id(),
);
// Case: close a buffer account
buffer_account
.borrow_mut()
.set_state(&UpgradeableLoaderState::Buffer {
authority_address: Some(authority_address),
})
.unwrap();
assert_eq!(
Ok(()),
process_instruction(
&bpf_loader_upgradeable::id(),
&[
KeyedAccount::new(&buffer_address, false, &buffer_account),
KeyedAccount::new(&recipient_address, false, &recipient_account),
KeyedAccount::new_readonly(&authority_address, true, &authority_account),
],
&instruction,
&mut MockInvokeContext::default()
)
);
assert_eq!(0, buffer_account.borrow().lamports());
assert_eq!(2, recipient_account.borrow().lamports());
assert!(buffer_account.borrow().data.iter().all(|&value| value == 0));
// Case: close with wrong authority
buffer_account
.borrow_mut()
.set_state(&UpgradeableLoaderState::Buffer {
authority_address: Some(authority_address),
})
.unwrap();
assert_eq!(
Err(InstructionError::IncorrectAuthority),
process_instruction(
&bpf_loader_upgradeable::id(),
&[
KeyedAccount::new(&buffer_address, false, &buffer_account),
KeyedAccount::new(&recipient_address, false, &recipient_account),
KeyedAccount::new_readonly(&Pubkey::new_unique(), true, &authority_account),
],
&instruction,
&mut MockInvokeContext::default()
)
);
// Case: close but not a buffer account
buffer_account
.borrow_mut()
.set_state(&UpgradeableLoaderState::Program {
programdata_address: Pubkey::new_unique(),
})
.unwrap();
assert_eq!(
Err(InstructionError::InvalidArgument),
process_instruction(
&bpf_loader_upgradeable::id(),
&[
KeyedAccount::new(&buffer_address, false, &buffer_account),
KeyedAccount::new(&recipient_address, false, &recipient_account),
KeyedAccount::new_readonly(&Pubkey::new_unique(), true, &authority_account),
],
&instruction,
&mut MockInvokeContext::default()
)
);
}
/// fuzzing utility function
fn fuzz<F>(
bytes: &[u8],