Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reduce payer balance needed to deploy programs #19645

Merged
merged 4 commits into from
Sep 18, 2021
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 78 additions & 33 deletions programs/bpf_loader/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,11 @@ use solana_sdk::{
clock::Clock,
entrypoint::{HEAP_LENGTH, SUCCESS},
feature_set::{
add_missing_program_error_mappings, close_upgradeable_program_accounts,
add_missing_program_error_mappings, close_upgradeable_program_accounts, fix_write_privs,
stop_verify_mul64_imm_nonzero,
},
ic_logger_msg, ic_msg,
instruction::InstructionError,
instruction::{AccountMeta, InstructionError},
keyed_account::{from_keyed_account, keyed_account_at_index, KeyedAccount},
loader_instruction::LoaderInstruction,
loader_upgradeable_instruction::UpgradeableLoaderInstruction,
Expand Down Expand Up @@ -390,13 +390,27 @@ fn process_loader_upgradeable_instruction(
return Err(InstructionError::InvalidArgument);
}

let instruction = system_instruction::create_account(
if invoke_context.is_feature_active(&fix_write_privs::id()) {
// Drain the Buffer account to payer before paying for programdata account
payer
.try_account_ref_mut()?
.checked_add_lamports(buffer.lamports()?)?;
buffer.try_account_ref_mut()?.set_lamports(0);
}

let mut instruction = system_instruction::create_account(
payer.unsigned_key(),
programdata.unsigned_key(),
1.max(rent.minimum_balance(programdata_len)),
programdata_len as u64,
program_id,
);

// pass an extra account to avoid the overly strict UnbalancedInstruction error
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI @jackcmay I'm bumping into this issue now: #9711

instruction
.accounts
.push(AccountMeta::new(*buffer.unsigned_key(), false));

let caller_program_id = invoke_context.get_caller()?;
let signers = [&[new_program_id.as_ref(), &[bump_seed]]]
.iter()
Expand All @@ -405,7 +419,7 @@ fn process_loader_upgradeable_instruction(
InstructionProcessor::native_invoke(
invoke_context,
instruction,
&[0, 1, 6],
&[0, 1, 3, 6],
signers.as_slice(),
)?;

Expand Down Expand Up @@ -434,11 +448,13 @@ fn process_loader_upgradeable_instruction(
})?;
program.try_account_ref_mut()?.set_executable(true);

// Drain the Buffer account back to the payer
payer
.try_account_ref_mut()?
.checked_add_lamports(buffer.lamports()?)?;
buffer.try_account_ref_mut()?.set_lamports(0);
if !invoke_context.is_feature_active(&fix_write_privs::id()) {
// Drain the Buffer account back to the payer
payer
.try_account_ref_mut()?
.checked_add_lamports(buffer.lamports()?)?;
buffer.try_account_ref_mut()?.set_lamports(0);
}

ic_logger_msg!(logger, "Deployed program {:?}", new_program_id);
}
Expand Down Expand Up @@ -987,6 +1003,7 @@ mod tests {
instruction::{AccountMeta, InstructionError},
keyed_account::KeyedAccount,
message::Message,
native_token::LAMPORTS_PER_SOL,
process_instruction::{MockComputeMeter, MockInvokeContext},
pubkey::Pubkey,
rent::Rent,
Expand Down Expand Up @@ -1644,34 +1661,48 @@ mod tests {
let bank = Arc::new(bank);
let bank_client = BankClient::new_shared(&bank);

// Setup initial accounts
// Setup keypairs and addresses
let payer_keypair = Keypair::new();
let program_keypair = Keypair::new();
let buffer_address = Pubkey::new_unique();
let (programdata_address, _) = Pubkey::find_program_address(
&[program_keypair.pubkey().as_ref()],
&bpf_loader_upgradeable::id(),
);
let upgrade_authority_keypair = Keypair::new();

// Load program file
let mut file = File::open("test_elfs/noop_aligned.so").expect("file open failed");
let mut elf = Vec::new();
file.read_to_end(&mut elf).unwrap();

// Compute rent exempt balances
let program_len = elf.len();
let min_program_balance = bank
.get_minimum_balance_for_rent_exemption(UpgradeableLoaderState::program_len().unwrap());
let min_programdata_balance = bank.get_minimum_balance_for_rent_exemption(
UpgradeableLoaderState::programdata_len(elf.len()).unwrap(),
let min_buffer_balance = bank.get_minimum_balance_for_rent_exemption(
UpgradeableLoaderState::buffer_len(program_len).unwrap(),
);
let buffer_address = Pubkey::new_unique();
let mut buffer_account = AccountSharedData::new(
min_programdata_balance,
UpgradeableLoaderState::buffer_len(elf.len()).unwrap(),
&bpf_loader_upgradeable::id(),
let min_programdata_balance = bank.get_minimum_balance_for_rent_exemption(
UpgradeableLoaderState::programdata_len(program_len).unwrap(),
);
buffer_account
.set_state(&UpgradeableLoaderState::Buffer {
authority_address: Some(upgrade_authority_keypair.pubkey()),
})
.unwrap();
buffer_account.data_as_mut_slice()[UpgradeableLoaderState::buffer_data_offset().unwrap()..]
.copy_from_slice(&elf);

// Setup accounts
let buffer_account = {
let mut account = AccountSharedData::new(
min_buffer_balance,
UpgradeableLoaderState::buffer_len(elf.len()).unwrap(),
&bpf_loader_upgradeable::id(),
);
account
.set_state(&UpgradeableLoaderState::Buffer {
authority_address: Some(upgrade_authority_keypair.pubkey()),
})
.unwrap();
account.data_as_mut_slice()[UpgradeableLoaderState::buffer_data_offset().unwrap()..]
.copy_from_slice(&elf);
account
};
let program_account = AccountSharedData::new(
min_programdata_balance,
UpgradeableLoaderState::program_len().unwrap(),
Expand All @@ -1684,32 +1715,45 @@ mod tests {
);

// Test successful deploy
bank.clear_signatures();
let payer_base_balance = LAMPORTS_PER_SOL;
let deploy_fees = {
let fee_calculator = genesis_config.fee_rate_governor.create_fee_calculator();
3 * fee_calculator.lamports_per_signature
};
let min_payer_balance =
min_program_balance + min_programdata_balance - min_buffer_balance + deploy_fees;
bank.store_account(
&payer_keypair.pubkey(),
&AccountSharedData::new(
payer_base_balance + min_payer_balance,
0,
&system_program::id(),
),
);
bank.store_account(&buffer_address, &buffer_account);
bank.store_account(&program_keypair.pubkey(), &AccountSharedData::default());
bank.store_account(&programdata_address, &AccountSharedData::default());
let before = bank.get_balance(&mint_keypair.pubkey());
let message = Message::new(
&bpf_loader_upgradeable::deploy_with_max_program_len(
&mint_keypair.pubkey(),
&payer_keypair.pubkey(),
&program_keypair.pubkey(),
&buffer_address,
&upgrade_authority_keypair.pubkey(),
min_program_balance,
elf.len(),
)
.unwrap(),
Some(&mint_keypair.pubkey()),
Some(&payer_keypair.pubkey()),
);
assert!(bank_client
.send_and_confirm_message(
&[&mint_keypair, &program_keypair, &upgrade_authority_keypair],
&[&payer_keypair, &program_keypair, &upgrade_authority_keypair],
message
)
.is_ok());
assert_eq!(
bank.get_balance(&mint_keypair.pubkey()),
before - min_program_balance
bank.get_balance(&payer_keypair.pubkey()),
payer_base_balance
);
assert_eq!(bank.get_balance(&buffer_address), 0);
assert_eq!(None, bank.get_account(&buffer_address));
Expand Down Expand Up @@ -1991,11 +2035,12 @@ mod tests {
.unwrap()
);

// Test Insufficient payer funds
// Test Insufficient payer funds (need more funds to cover the
// difference between buffer lamports and programdata lamports)
bank.clear_signatures();
bank.store_account(
&mint_keypair.pubkey(),
&AccountSharedData::new(min_program_balance, 0, &system_program::id()),
&AccountSharedData::new(deploy_fees + min_program_balance, 0, &system_program::id()),
);
bank.store_account(&buffer_address, &buffer_account);
bank.store_account(&program_keypair.pubkey(), &AccountSharedData::default());
Expand Down