Close buffer accounts (bp #15887) (#15971)

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

(cherry picked from commit 7f500d610c)

# Conflicts:
#	cli/src/program.rs
#	programs/bpf_loader/src/lib.rs

* resolve conflicts

* slice fill not supported on older rust

Co-authored-by: Jack May <jack@solana.com>
This commit is contained in:
mergify[bot]
2021-03-18 07:33:40 +00:00
committed by GitHub
parent 9c596cfd6c
commit e0119e7de7
9 changed files with 980 additions and 60 deletions

View File

@@ -30,7 +30,7 @@ use solana_sdk::{
entrypoint::SUCCESS,
feature_set::{
bpf_compute_budget_balancing, matching_buffer_upgrade_authorities,
prevent_upgrade_and_invoke,
prevent_upgrade_and_invoke, upgradeable_close_instruction,
},
ic_logger_msg, ic_msg,
instruction::InstructionError,
@@ -665,12 +665,51 @@ 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;
for i in &mut close_account.try_account_ref_mut()?.data {
*i = 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(())
@@ -3009,7 +3048,7 @@ mod tests {
})
.unwrap();
assert_eq!(
Err(InstructionError::InvalidAccountData),
Err(InstructionError::InvalidArgument),
process_instruction(
&bpf_loader_upgradeable::id(),
&[
@@ -3182,7 +3221,7 @@ mod tests {
})
.unwrap();
assert_eq!(
Err(InstructionError::InvalidAccountData),
Err(InstructionError::InvalidArgument),
process_instruction(
&bpf_loader_upgradeable::id(),
&[
@@ -3215,6 +3254,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 = Account::new_ref(1, 0, &Pubkey::new_unique());
let recipient_address = Pubkey::new_unique();
let recipient_account = Account::new_ref(1, 0, &Pubkey::new_unique());
let buffer_address = Pubkey::new_unique();
let buffer_account = Account::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],