Fix program account rent exemption (#14176)
This commit is contained in:
parent
dc0f5adc36
commit
593ad80954
|
@ -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())
|
||||||
}
|
}
|
||||||
|
|
|
@ -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())
|
||||||
}
|
}
|
||||||
|
|
|
@ -1483,7 +1483,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(),
|
||||||
|
@ -1501,7 +1508,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(),
|
||||||
|
@ -1529,7 +1543,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(),
|
||||||
|
|
|
@ -228,7 +228,7 @@ fn process_instruction_common(
|
||||||
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()?
|
||||||
|
@ -240,6 +240,7 @@ fn process_instruction_common(
|
||||||
}
|
}
|
||||||
(
|
(
|
||||||
programdata,
|
programdata,
|
||||||
|
&keyed_accounts[1..],
|
||||||
UpgradeableLoaderState::programdata_data_offset()?,
|
UpgradeableLoaderState::programdata_data_offset()?,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
|
@ -247,7 +248,7 @@ fn process_instruction_common(
|
||||||
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 {
|
||||||
|
@ -341,8 +342,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);
|
||||||
}
|
}
|
||||||
|
@ -506,7 +510,8 @@ fn process_loader_upgradeable_instruction(
|
||||||
use_jit,
|
use_jit,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// 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,
|
||||||
|
@ -515,6 +520,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
|
||||||
|
|
||||||
|
@ -1541,6 +1551,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(
|
||||||
|
|
Loading…
Reference in New Issue