Fix program account rent exemption (#14176) (#14334)

(cherry picked from commit 593ad80954)

Co-authored-by: Jack May <jack@solana.com>
This commit is contained in:
mergify[bot]
2020-12-29 22:32:32 +00:00
committed by GitHub
parent 1ae516fd4a
commit ebfbe5ed8a
4 changed files with 103 additions and 10 deletions

View File

@ -8,9 +8,10 @@ use solana_program::{
entrypoint!(process_instruction); entrypoint!(process_instruction);
fn process_instruction( fn process_instruction(
_program_id: &Pubkey, _program_id: &Pubkey,
_accounts: &[AccountInfo], accounts: &[AccountInfo],
_instruction_data: &[u8], _instruction_data: &[u8],
) -> ProgramResult { ) -> ProgramResult {
msg!("Upgradeable program"); msg!("Upgradeable program");
assert_eq!(accounts.len(), 2);
Err(42.into()) Err(42.into())
} }

View File

@ -8,9 +8,10 @@ use solana_program::{
entrypoint!(process_instruction); entrypoint!(process_instruction);
fn process_instruction( fn process_instruction(
_program_id: &Pubkey, _program_id: &Pubkey,
_accounts: &[AccountInfo], accounts: &[AccountInfo],
_instruction_data: &[u8], _instruction_data: &[u8],
) -> ProgramResult { ) -> ProgramResult {
msg!("Upgraded program"); msg!("Upgraded program");
assert_eq!(accounts.len(), 2);
Err(43.into()) Err(43.into())
} }

View File

@ -1387,7 +1387,14 @@ fn test_program_bpf_upgrade() {
// call upgrade program // call upgrade program
nonce += 1; nonce += 1;
let instruction = Instruction::new(program_id, &[nonce], vec![]); let instruction = Instruction::new(
program_id,
&[nonce],
vec![
AccountMeta::new(clock::id(), false),
AccountMeta::new(fees::id(), false),
],
);
let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction); let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction);
assert_eq!( assert_eq!(
result.unwrap_err().unwrap(), result.unwrap_err().unwrap(),
@ -1405,7 +1412,14 @@ fn test_program_bpf_upgrade() {
// call upgraded program // call upgraded program
nonce += 1; nonce += 1;
let instruction = Instruction::new(program_id, &[nonce], vec![]); let instruction = Instruction::new(
program_id,
&[nonce],
vec![
AccountMeta::new(clock::id(), false),
AccountMeta::new(fees::id(), false),
],
);
let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction); let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction);
assert_eq!( assert_eq!(
result.unwrap_err().unwrap(), result.unwrap_err().unwrap(),
@ -1433,7 +1447,14 @@ fn test_program_bpf_upgrade() {
// call original program // call original program
nonce += 1; nonce += 1;
let instruction = Instruction::new(program_id, &[nonce], vec![]); let instruction = Instruction::new(
program_id,
&[nonce],
vec![
AccountMeta::new(clock::id(), false),
AccountMeta::new(fees::id(), false),
],
);
let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction); let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction);
assert_eq!( assert_eq!(
result.unwrap_err().unwrap(), result.unwrap_err().unwrap(),

View File

@ -181,7 +181,7 @@ pub fn process_instruction(
let account_iter = &mut keyed_accounts.iter(); let account_iter = &mut keyed_accounts.iter();
let first_account = next_keyed_account(account_iter)?; let first_account = next_keyed_account(account_iter)?;
if first_account.executable()? { if first_account.executable()? {
let (program, offset) = if bpf_loader_upgradeable::check_id(program_id) { let (program, keyed_accounts, offset) = if bpf_loader_upgradeable::check_id(program_id) {
if let UpgradeableLoaderState::Program { if let UpgradeableLoaderState::Program {
programdata_address, programdata_address,
} = first_account.state()? } = first_account.state()?
@ -193,6 +193,7 @@ pub fn process_instruction(
} }
( (
programdata, programdata,
&keyed_accounts[1..],
UpgradeableLoaderState::programdata_data_offset()?, UpgradeableLoaderState::programdata_data_offset()?,
) )
} else { } else {
@ -200,7 +201,7 @@ pub fn process_instruction(
return Err(InstructionError::InvalidAccountData); return Err(InstructionError::InvalidAccountData);
} }
} else { } else {
(first_account, 0) (first_account, keyed_accounts, 0)
}; };
if program.owner()? != *program_id { if program.owner()? != *program_id {
@ -279,8 +280,11 @@ fn process_loader_upgradeable_instruction(
log!(logger, "Program account already initialized"); log!(logger, "Program account already initialized");
return Err(InstructionError::AccountAlreadyInitialized); return Err(InstructionError::AccountAlreadyInitialized);
} }
if program.data_len()? < UpgradeableLoaderState::program_len()? {
if program.lamports()? < rent.minimum_balance(UpgradeableLoaderState::program_len()?) { log!(logger, "Program account too small");
return Err(InstructionError::AccountDataTooSmall);
}
if program.lamports()? < rent.minimum_balance(program.data_len()?) {
log!(logger, "Program account not rent-exempt"); log!(logger, "Program account not rent-exempt");
return Err(InstructionError::ExecutableAccountNotRentExempt); return Err(InstructionError::ExecutableAccountNotRentExempt);
} }
@ -442,7 +446,8 @@ fn process_loader_upgradeable_instruction(
invoke_context, invoke_context,
)?; )?;
// Update the ProgramData account and record the upgraded data // Update the ProgramData account, record the upgraded data, and zero
// the rest
programdata.set_state(&UpgradeableLoaderState::ProgramData { programdata.set_state(&UpgradeableLoaderState::ProgramData {
slot: clock.slot, slot: clock.slot,
@ -451,6 +456,11 @@ fn process_loader_upgradeable_instruction(
programdata.try_account_ref_mut()?.data programdata.try_account_ref_mut()?.data
[programdata_data_offset..programdata_data_offset + buffer_data_len] [programdata_data_offset..programdata_data_offset + buffer_data_len]
.copy_from_slice(&buffer.try_account_ref()?.data[buffer_data_offset..]); .copy_from_slice(&buffer.try_account_ref()?.data[buffer_data_offset..]);
for i in &mut programdata.try_account_ref_mut()?.data
[programdata_data_offset + buffer_data_len..]
{
*i = 0
}
// Fund ProgramData to rent-exemption, spill the rest // Fund ProgramData to rent-exemption, spill the rest
@ -1464,6 +1474,66 @@ mod tests {
.unwrap() .unwrap()
); );
// Test program account not rent exempt because data is larger than needed
bank.clear_signatures();
bank.store_account(&buffer_address, &buffer_account);
bank.store_account(&program_keypair.pubkey(), &Account::default());
bank.store_account(&programdata_address, &Account::default());
let mut instructions = bpf_loader_upgradeable::deploy_with_max_program_len(
&mint_keypair.pubkey(),
&program_keypair.pubkey(),
&buffer_address,
None,
min_program_balance,
elf.len(),
)
.unwrap();
instructions[0] = system_instruction::create_account(
&mint_keypair.pubkey(),
&program_keypair.pubkey(),
min_program_balance,
UpgradeableLoaderState::program_len().unwrap() as u64 + 1,
&id(),
);
let message = Message::new(&instructions, Some(&mint_keypair.pubkey()));
assert_eq!(
TransactionError::InstructionError(1, InstructionError::ExecutableAccountNotRentExempt),
bank_client
.send_and_confirm_message(&[&mint_keypair, &program_keypair], message)
.unwrap_err()
.unwrap()
);
// Test program account too small
bank.clear_signatures();
bank.store_account(&buffer_address, &buffer_account);
bank.store_account(&program_keypair.pubkey(), &Account::default());
bank.store_account(&programdata_address, &Account::default());
let mut instructions = bpf_loader_upgradeable::deploy_with_max_program_len(
&mint_keypair.pubkey(),
&program_keypair.pubkey(),
&buffer_address,
None,
min_program_balance,
elf.len(),
)
.unwrap();
instructions[0] = system_instruction::create_account(
&mint_keypair.pubkey(),
&program_keypair.pubkey(),
min_program_balance,
UpgradeableLoaderState::program_len().unwrap() as u64 - 1,
&id(),
);
let message = Message::new(&instructions, Some(&mint_keypair.pubkey()));
assert_eq!(
TransactionError::InstructionError(1, InstructionError::AccountDataTooSmall),
bank_client
.send_and_confirm_message(&[&mint_keypair, &program_keypair], message)
.unwrap_err()
.unwrap()
);
// Test Insufficient payer funds // Test Insufficient payer funds
bank.clear_signatures(); bank.clear_signatures();
bank.store_account( bank.store_account(