Skip to content

Commit

Permalink
cli: add tests for auto-extend
Browse files Browse the repository at this point in the history
  • Loading branch information
buffalojoec committed Apr 24, 2024
1 parent 4ee4dd2 commit 9b189a7
Show file tree
Hide file tree
Showing 2 changed files with 150 additions and 2 deletions.
3 changes: 1 addition & 2 deletions cli/src/program.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,6 @@ use {
fs::File,
io::{Read, Write},
mem::size_of,
ops::Sub,
path::PathBuf,
rc::Rc,
str::FromStr,
Expand Down Expand Up @@ -2553,7 +2552,7 @@ fn do_process_program_upgrade(
let program_len = UpgradeableLoaderState::size_of_programdata(program_len);
let account_data_len = program_data_account.data.len();
if program_len > account_data_len {
let additional_bytes = program_len.sub(account_data_len);
let additional_bytes = program_len.saturating_sub(account_data_len);
let additional_bytes: u32 = additional_bytes.try_into().map_err(|_| {
format!(
"Cannot auto-extend Program Data Account space due to size limit \
Expand Down
149 changes: 149 additions & 0 deletions cli/tests/program.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ use {
solana_rpc_client::rpc_client::RpcClient,
solana_rpc_client_nonce_utils::blockhash_query::BlockhashQuery,
solana_sdk::{
account::ReadableAccount,
account_utils::StateMut,
borsh1::try_from_slice_unchecked,
bpf_loader_upgradeable::{self, UpgradeableLoaderState},
Expand Down Expand Up @@ -728,6 +729,154 @@ fn test_cli_program_deploy_with_authority() {
assert_eq!("none", authority_pubkey_str);
}

#[test]
fn test_cli_program_upgrade_auto_extend() {
solana_logger::setup();

let mut noop_path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
noop_path.push("tests");
noop_path.push("fixtures");
noop_path.push("noop");
noop_path.set_extension("so");

let mut noop_large_path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
noop_large_path.push("tests");
noop_large_path.push("fixtures");
noop_large_path.push("noop_large");
noop_large_path.set_extension("so");

let mint_keypair = Keypair::new();
let mint_pubkey = mint_keypair.pubkey();
let faucet_addr = run_local_faucet(mint_keypair, None);
let test_validator =
TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr), SocketAddrSpace::Unspecified);

let rpc_client =
RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());

let mut file = File::open(noop_path.to_str().unwrap()).unwrap();
let mut program_data = Vec::new();
file.read_to_end(&mut program_data).unwrap();

let mut file = File::open(noop_large_path.to_str().unwrap()).unwrap();
let mut program_data_large = Vec::new();
file.read_to_end(&mut program_data_large).unwrap();

// Use the larger program to calculate rent.
let max_len = program_data_large.len();
let minimum_balance_for_programdata = rpc_client
.get_minimum_balance_for_rent_exemption(UpgradeableLoaderState::size_of_programdata(
max_len,
))
.unwrap();
let minimum_balance_for_program = rpc_client
.get_minimum_balance_for_rent_exemption(UpgradeableLoaderState::size_of_program())
.unwrap();
let upgrade_authority = Keypair::new();

let mut config = CliConfig::recent_for_tests();
let keypair = Keypair::new();
config.json_rpc_url = test_validator.rpc_url();
config.signers = vec![&keypair];
config.command = CliCommand::Airdrop {
pubkey: None,
lamports: 100 * minimum_balance_for_programdata + minimum_balance_for_program,
};
process_command(&config).unwrap();

// Deploy the first, smaller program.
let program_keypair = Keypair::new();
config.signers = vec![&keypair, &upgrade_authority, &program_keypair];
config.command = CliCommand::Program(ProgramCliCommand::Deploy {
program_location: Some(noop_path.to_str().unwrap().to_string()),
fee_payer_signer_index: 0,
program_signer_index: Some(2),
program_pubkey: Some(program_keypair.pubkey()),
buffer_signer_index: None,
buffer_pubkey: None,
allow_excessive_balance: false,
upgrade_authority_signer_index: 1,
is_final: false,
max_len: None,
skip_fee_check: false,
compute_unit_price: None,
max_sign_attempts: 5,
no_extend: false,
use_rpc: false,
});
config.output_format = OutputFormat::JsonCompact;
process_command(&config).unwrap();

// Attempt to upgrade the program with a larger program, but with the
// --no-extend flag.
config.signers = vec![&keypair, &upgrade_authority];
config.command = CliCommand::Program(ProgramCliCommand::Deploy {
program_location: Some(noop_large_path.to_str().unwrap().to_string()),
fee_payer_signer_index: 0,
program_signer_index: None,
program_pubkey: Some(program_keypair.pubkey()),
buffer_signer_index: None,
buffer_pubkey: None,
allow_excessive_balance: false,
upgrade_authority_signer_index: 1,
is_final: true,
max_len: None,
skip_fee_check: false,
compute_unit_price: None,
max_sign_attempts: 5,
no_extend: true, // --no-extend (true)
use_rpc: false,
});
process_command(&config).unwrap_err();

// Attempt to upgrade the program with a larger program, this time without
// the --no-extend flag. This should automatically extend the program data.
config.signers = vec![&keypair, &upgrade_authority];
config.command = CliCommand::Program(ProgramCliCommand::Deploy {
program_location: Some(noop_large_path.to_str().unwrap().to_string()),
fee_payer_signer_index: 0,
program_signer_index: None,
program_pubkey: Some(program_keypair.pubkey()),
buffer_signer_index: None,
buffer_pubkey: None,
allow_excessive_balance: false,
upgrade_authority_signer_index: 1,
is_final: true,
max_len: None,
skip_fee_check: false,
compute_unit_price: None,
max_sign_attempts: 5,
no_extend: false, // --no-extend (false)
use_rpc: false,
});
let response = process_command(&config);
let json: Value = serde_json::from_str(&response.unwrap()).unwrap();
let program_pubkey_str = json
.as_object()
.unwrap()
.get("programId")
.unwrap()
.as_str()
.unwrap();
let program_pubkey = Pubkey::from_str(program_pubkey_str).unwrap();
let (programdata_pubkey, _) =
Pubkey::find_program_address(&[program_pubkey.as_ref()], &bpf_loader_upgradeable::id());
let programdata_account = rpc_client.get_account(&programdata_pubkey).unwrap();
if let UpgradeableLoaderState::ProgramData {
slot: _,
upgrade_authority_address,
} = programdata_account.state().unwrap()
{
assert_eq!(upgrade_authority_address, None);
} else {
panic!("not a ProgramData account");
}
assert_eq!(
programdata_account.data().len(),
UpgradeableLoaderState::size_of_programdata(program_data_large.len()),
);
}

#[test]
fn test_cli_program_close_program() {
solana_logger::setup();
Expand Down

0 comments on commit 9b189a7

Please sign in to comment.