From a547c57ff4a144dad3e3b1cfe7a6f46ed0703751 Mon Sep 17 00:00:00 2001 From: NagaprasadVr <nagaprasadvr246@gmail.com> Date: Sat, 13 Apr 2024 13:13:54 +0530 Subject: [PATCH 01/14] add checks for program data account overflow during upgrade and add --auto-extend-program flag to solana program deploy command --- cli/src/program.rs | 114 +++++++++++++++++------------- cli/tests/program.rs | 165 ++++++++++++++++++++++++++++++++++++------- 2 files changed, 204 insertions(+), 75 deletions(-) diff --git a/cli/src/program.rs b/cli/src/program.rs index 5640a89738eedf..ded59595f7b36a 100644 --- a/cli/src/program.rs +++ b/cli/src/program.rs @@ -54,7 +54,7 @@ use { account::Account, account_utils::StateMut, bpf_loader, bpf_loader_deprecated, - bpf_loader_upgradeable::{self, UpgradeableLoaderState}, + bpf_loader_upgradeable::{self, get_program_data_address, UpgradeableLoaderState}, compute_budget, feature_set::FeatureSet, instruction::{Instruction, InstructionError}, @@ -71,6 +71,7 @@ use { fs::File, io::{Read, Write}, mem::size_of, + ops::Sub, path::PathBuf, rc::Rc, str::FromStr, @@ -99,7 +100,7 @@ pub enum ProgramCliCommand { skip_fee_check: bool, compute_unit_price: Option<u64>, max_sign_attempts: usize, - use_rpc: bool, + auto_extend_program: bool, }, Upgrade { fee_payer_signer_index: SignerIndex, @@ -270,10 +271,15 @@ impl ProgramSubCommands for App<'_, '_> { whichever comes first.", ), ) - .arg(Arg::with_name("use_rpc").long("use-rpc").help( - "Send write transactions to the configured RPC instead of validator TPUs", - )) - .arg(compute_unit_price_arg()), + .arg(compute_unit_price_arg()) + .arg( + Arg::with_name("auto_extend_program") + .long("auto-extend-program") + .takes_value(false) + .help( + "Automatically extend the program data account size to match the updated program's data size", + ), + ), ) .subcommand( SubCommand::with_name("upgrade") @@ -665,6 +671,8 @@ pub fn parse_program_subcommand( let compute_unit_price = value_of(matches, "compute_unit_price"); let max_sign_attempts = value_of(matches, "max_sign_attempts").unwrap(); + let auto_extend_program = matches.is_present("auto_extend_program"); + CliCommandInfo { command: CliCommand::Program(ProgramCliCommand::Deploy { program_location, @@ -682,7 +690,7 @@ pub fn parse_program_subcommand( skip_fee_check, compute_unit_price, max_sign_attempts, - use_rpc: matches.is_present("use_rpc"), + auto_extend_program, }), signers: signer_info.signers, } @@ -970,7 +978,7 @@ pub fn process_program_subcommand( skip_fee_check, compute_unit_price, max_sign_attempts, - use_rpc, + auto_extend_program, } => process_program_deploy( rpc_client, config, @@ -987,7 +995,7 @@ pub fn process_program_subcommand( *skip_fee_check, *compute_unit_price, *max_sign_attempts, - *use_rpc, + *auto_extend_program, ), ProgramCliCommand::Upgrade { fee_payer_signer_index, @@ -1165,7 +1173,7 @@ fn process_program_deploy( skip_fee_check: bool, compute_unit_price: Option<u64>, max_sign_attempts: usize, - use_rpc: bool, + auto_extend_program: bool, ) -> ProcessResult { let fee_payer_signer = config.signers[fee_payer_signer_index]; let upgrade_authority_signer = config.signers[upgrade_authority_signer_index]; @@ -1350,7 +1358,7 @@ fn process_program_deploy( skip_fee_check, compute_unit_price, max_sign_attempts, - use_rpc, + auto_extend_program, ) }; if result.is_ok() && is_final { @@ -2490,9 +2498,38 @@ fn do_process_program_upgrade( skip_fee_check: bool, compute_unit_price: Option<u64>, max_sign_attempts: usize, - use_rpc: bool, + auto_extend_program: bool, ) -> ProcessResult { let blockhash = rpc_client.get_latest_blockhash()?; + let program_data_address = get_program_data_address(program_id); + let program_data_account = rpc_client.get_account(&program_data_address)?; + + //CHECK: if passed program size is greater than the current program data account size, + //throw an err along with a message including additional bytes required + let add_program_extend_ix = if program_len > program_data_account.data.len() { + let metadata_size = UpgradeableLoaderState::size_of_programdata_metadata(); + let additional_bytes = program_len + .sub(program_data_account.data.len()) + .saturating_add(metadata_size); + println!("additional_bytes: {}", additional_bytes); + if auto_extend_program { + Some(bpf_loader_upgradeable::extend_program( + program_id, + Some(&fee_payer_signer.pubkey()), + additional_bytes as u32, + )) + } else { + let err_string = format!( + r#"Program Data Account space is not enough +please extend the program data account with command: solana program extend {} {} +or pass flag --auto-extend-program to extend program data account automatically"#, + program_id, additional_bytes + ); + return Err(err_string.into()); + } + } else { + None + }; let (initial_message, write_messages, balance_needed) = if let Some(buffer_signer) = buffer_signer @@ -2526,6 +2563,11 @@ fn do_process_program_upgrade( ) }; + // Add extend program instruction if ProgramData account needs to be extended + if let Some(add_program_extend_ix) = add_program_extend_ix { + initial_instructions.push(add_program_extend_ix); + } + let initial_message = if !initial_instructions.is_empty() { Some(Message::new_with_blockhash( &initial_instructions @@ -2959,7 +3001,7 @@ mod tests { skip_fee_check: false, compute_unit_price: None, max_sign_attempts: 5, - use_rpc: false, + auto_extend_program: false, }), signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())], } @@ -2990,7 +3032,7 @@ mod tests { skip_fee_check: false, compute_unit_price: None, max_sign_attempts: 5, - use_rpc: false, + auto_extend_program: false }), signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())], } @@ -3023,7 +3065,7 @@ mod tests { skip_fee_check: false, compute_unit_price: None, max_sign_attempts: 5, - use_rpc: false, + auto_extend_program: false }), signers: vec![ Box::new(read_keypair_file(&keypair_file).unwrap()), @@ -3058,7 +3100,7 @@ mod tests { skip_fee_check: false, compute_unit_price: None, max_sign_attempts: 5, - use_rpc: false, + auto_extend_program: false }), signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())], } @@ -3092,7 +3134,7 @@ mod tests { skip_fee_check: false, compute_unit_price: None, max_sign_attempts: 5, - use_rpc: false, + auto_extend_program: false }), signers: vec![ Box::new(read_keypair_file(&keypair_file).unwrap()), @@ -3129,7 +3171,7 @@ mod tests { skip_fee_check: false, compute_unit_price: None, max_sign_attempts: 5, - use_rpc: false, + auto_extend_program: false }), signers: vec![ Box::new(read_keypair_file(&keypair_file).unwrap()), @@ -3162,7 +3204,7 @@ mod tests { allow_excessive_balance: false, compute_unit_price: None, max_sign_attempts: 5, - use_rpc: false, + auto_extend_program: false }), signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())], } @@ -3193,37 +3235,7 @@ mod tests { skip_fee_check: false, compute_unit_price: None, max_sign_attempts: 1, - use_rpc: false, - }), - signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())], - } - ); - - let test_command = test_commands.clone().get_matches_from(vec![ - "test", - "program", - "deploy", - "/Users/test/program.so", - "--use-rpc", - ]); - assert_eq!( - parse_command(&test_command, &default_signer, &mut None).unwrap(), - CliCommandInfo { - command: CliCommand::Program(ProgramCliCommand::Deploy { - program_location: Some("/Users/test/program.so".to_string()), - fee_payer_signer_index: 0, - buffer_signer_index: None, - buffer_pubkey: None, - program_signer_index: None, - program_pubkey: None, - upgrade_authority_signer_index: 0, - is_final: false, - max_len: None, - allow_excessive_balance: false, - skip_fee_check: false, - compute_unit_price: None, - max_sign_attempts: 5, - use_rpc: true, + auto_extend_program: false }), signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())], } @@ -3966,7 +3978,7 @@ mod tests { skip_fee_check: false, compute_unit_price: None, max_sign_attempts: 5, - use_rpc: false, + auto_extend_program: false, }), signers: vec![&default_keypair], output_format: OutputFormat::JsonCompact, diff --git a/cli/tests/program.rs b/cli/tests/program.rs index 5e2098de9c455a..db5d04cfea72c1 100644 --- a/cli/tests/program.rs +++ b/cli/tests/program.rs @@ -99,7 +99,7 @@ fn test_cli_program_deploy_non_upgradeable() { skip_fee_check: false, compute_unit_price: None, max_sign_attempts: 5, - use_rpc: false, + auto_extend_program: false, }); config.output_format = OutputFormat::JsonCompact; let response = process_command(&config); @@ -148,7 +148,7 @@ fn test_cli_program_deploy_non_upgradeable() { skip_fee_check: false, compute_unit_price: None, max_sign_attempts: 5, - use_rpc: false, + auto_extend_program: false, }); process_command(&config).unwrap(); let account1 = rpc_client @@ -206,7 +206,7 @@ fn test_cli_program_deploy_non_upgradeable() { skip_fee_check: false, compute_unit_price: None, max_sign_attempts: 5, - use_rpc: false, + auto_extend_program: false, }); let err = process_command(&config).unwrap_err(); assert_eq!( @@ -232,7 +232,7 @@ fn test_cli_program_deploy_non_upgradeable() { skip_fee_check: false, compute_unit_price: None, max_sign_attempts: 5, - use_rpc: false, + auto_extend_program: false, }); process_command(&config).unwrap_err(); } @@ -296,7 +296,7 @@ fn test_cli_program_deploy_no_authority() { skip_fee_check: false, compute_unit_price: None, max_sign_attempts: 5, - use_rpc: false, + auto_extend_program: false, }); config.output_format = OutputFormat::JsonCompact; let response = process_command(&config); @@ -326,7 +326,7 @@ fn test_cli_program_deploy_no_authority() { skip_fee_check: false, compute_unit_price: None, max_sign_attempts: 5, - use_rpc: false, + auto_extend_program: false, }); process_command(&config).unwrap_err(); } @@ -391,7 +391,7 @@ fn test_cli_program_deploy_with_authority() { skip_fee_check: false, compute_unit_price: None, max_sign_attempts: 5, - use_rpc: false, + auto_extend_program: false, }); config.output_format = OutputFormat::JsonCompact; let response = process_command(&config); @@ -443,7 +443,7 @@ fn test_cli_program_deploy_with_authority() { skip_fee_check: false, compute_unit_price: None, max_sign_attempts: 5, - use_rpc: false, + auto_extend_program: false, }); let response = process_command(&config); let json: Value = serde_json::from_str(&response.unwrap()).unwrap(); @@ -489,7 +489,7 @@ fn test_cli_program_deploy_with_authority() { skip_fee_check: false, compute_unit_price: None, max_sign_attempts: 5, - use_rpc: false, + auto_extend_program: false, }); process_command(&config).unwrap(); let program_account = rpc_client.get_account(&program_pubkey).unwrap(); @@ -567,7 +567,7 @@ fn test_cli_program_deploy_with_authority() { skip_fee_check: false, compute_unit_price: None, max_sign_attempts: 5, - use_rpc: false, + auto_extend_program: false, }); process_command(&config).unwrap(); let program_account = rpc_client.get_account(&program_pubkey).unwrap(); @@ -649,7 +649,7 @@ fn test_cli_program_deploy_with_authority() { skip_fee_check: false, compute_unit_price: None, max_sign_attempts: 5, - use_rpc: false, + auto_extend_program: false, }); process_command(&config).unwrap_err(); @@ -669,7 +669,7 @@ fn test_cli_program_deploy_with_authority() { skip_fee_check: false, compute_unit_price: None, max_sign_attempts: 5, - use_rpc: false, + auto_extend_program: false, }); let response = process_command(&config); let json: Value = serde_json::from_str(&response.unwrap()).unwrap(); @@ -776,7 +776,7 @@ fn test_cli_program_close_program() { skip_fee_check: false, compute_unit_price: None, max_sign_attempts: 5, - use_rpc: false, + auto_extend_program: false, }); config.output_format = OutputFormat::JsonCompact; process_command(&config).unwrap(); @@ -889,7 +889,7 @@ fn test_cli_program_extend_program() { skip_fee_check: false, compute_unit_price: None, max_sign_attempts: 5, - use_rpc: false, + auto_extend_program: false, }); config.output_format = OutputFormat::JsonCompact; process_command(&config).unwrap(); @@ -939,7 +939,7 @@ fn test_cli_program_extend_program() { skip_fee_check: false, compute_unit_price: None, max_sign_attempts: 5, - use_rpc: false, + auto_extend_program: false, }); process_command(&config).unwrap_err(); @@ -974,7 +974,7 @@ fn test_cli_program_extend_program() { skip_fee_check: false, compute_unit_price: None, max_sign_attempts: 5, - use_rpc: false, + auto_extend_program: false, }); process_command(&config).unwrap(); } @@ -1329,7 +1329,7 @@ fn test_cli_program_write_buffer() { skip_fee_check: false, compute_unit_price: None, max_sign_attempts: 5, - use_rpc: false, + auto_extend_program: false, }); config.output_format = OutputFormat::JsonCompact; let error = process_command(&config).unwrap_err(); @@ -1459,7 +1459,7 @@ fn test_cli_program_set_buffer_authority() { skip_fee_check: false, compute_unit_price: None, max_sign_attempts: 5, - use_rpc: false, + auto_extend_program: false, }); config.output_format = OutputFormat::JsonCompact; process_command(&config).unwrap_err(); @@ -1507,7 +1507,7 @@ fn test_cli_program_set_buffer_authority() { skip_fee_check: false, compute_unit_price: None, max_sign_attempts: 5, - use_rpc: false, + auto_extend_program: false, }); config.output_format = OutputFormat::JsonCompact; process_command(&config).unwrap(); @@ -1593,7 +1593,7 @@ fn test_cli_program_mismatch_buffer_authority() { skip_fee_check: false, compute_unit_price: None, max_sign_attempts: 5, - use_rpc: false, + auto_extend_program: false, }); process_command(&config).unwrap_err(); @@ -1613,7 +1613,7 @@ fn test_cli_program_mismatch_buffer_authority() { skip_fee_check: false, compute_unit_price: None, max_sign_attempts: 5, - use_rpc: false, + auto_extend_program: false, }); process_command(&config).unwrap(); } @@ -1699,7 +1699,7 @@ fn test_cli_program_deploy_with_offline_signing(use_offline_signer_as_fee_payer: skip_fee_check: false, compute_unit_price: None, max_sign_attempts: 5, - use_rpc: false, + auto_extend_program: false, }); config.output_format = OutputFormat::JsonCompact; process_command(&config).unwrap(); @@ -1933,7 +1933,7 @@ fn test_cli_program_show() { skip_fee_check: false, compute_unit_price: None, max_sign_attempts: 5, - use_rpc: false, + auto_extend_program: false, }); config.output_format = OutputFormat::JsonCompact; let min_slot = rpc_client.get_slot().unwrap(); @@ -2210,7 +2210,7 @@ fn test_cli_program_deploy_with_args(compute_unit_price: Option<u64>, use_rpc: b skip_fee_check: false, compute_unit_price, max_sign_attempts: 5, - use_rpc, + auto_extend_program: false, }); config.output_format = OutputFormat::JsonCompact; let response = process_command(&config); @@ -2312,3 +2312,120 @@ fn test_cli_program_deploy_with_args(compute_unit_price: Option<u64>, use_rpc: b ); } } + +#[test] +fn test_cli_program_deploy_with_compute_unit_price() { + cli_program_deploy_with_args(Some(1000)); + cli_program_deploy_with_args(None); +} + +#[test] +fn test_cli_program_upgrade_auto_extend_program_data_account() { + 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 max_len = program_data.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 upgradeable 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, // Use None to check that it defaults to the max length + skip_fee_check: false, + compute_unit_price: None, + max_sign_attempts: 5, + auto_extend_program: false, + }); + config.output_format = OutputFormat::JsonCompact; + process_command(&config).unwrap(); + + let (programdata_pubkey, _) = Pubkey::find_program_address( + &[program_keypair.pubkey().as_ref()], + &bpf_loader_upgradeable::id(), + ); + + let programdata_account = rpc_client.get_account(&programdata_pubkey).unwrap(); + let expected_len = UpgradeableLoaderState::size_of_programdata(max_len); + assert_eq!(expected_len, programdata_account.data.len()); + + // Wait one slot to avoid "Program was deployed in this block already" error + wait_n_slots(&rpc_client, 1); + + // Extend program for larger program, minus 1 required byte + let mut file = File::open(noop_large_path.to_str().unwrap()).unwrap(); + let mut new_program_data = Vec::new(); + file.read_to_end(&mut new_program_data).unwrap(); + + let programdata_account = rpc_client.get_account(&programdata_pubkey).unwrap(); + assert_eq!(expected_len, programdata_account.data.len()); + + 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: false, + max_len: None, + skip_fee_check: false, + compute_unit_price: None, + max_sign_attempts: 5, + auto_extend_program: true, + }); + process_command(&config).unwrap(); +} From b44539310cc76414113b95285bae45020e225c21 Mon Sep 17 00:00:00 2001 From: NagaprasadVr <nagaprasadvr246@gmail.com> Date: Sat, 13 Apr 2024 13:24:49 +0530 Subject: [PATCH 02/14] remove logs --- cli/src/program.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cli/src/program.rs b/cli/src/program.rs index ded59595f7b36a..5ff13ae7bfa3f8 100644 --- a/cli/src/program.rs +++ b/cli/src/program.rs @@ -277,7 +277,7 @@ impl ProgramSubCommands for App<'_, '_> { .long("auto-extend-program") .takes_value(false) .help( - "Automatically extend the program data account size to match the updated program's data size", + "Automatically extend the program data account's size to match the updated program's data size", ), ), ) @@ -2511,7 +2511,6 @@ fn do_process_program_upgrade( let additional_bytes = program_len .sub(program_data_account.data.len()) .saturating_add(metadata_size); - println!("additional_bytes: {}", additional_bytes); if auto_extend_program { Some(bpf_loader_upgradeable::extend_program( program_id, From a19c506965acf2a0ef6de909a83a0dc348c81f44 Mon Sep 17 00:00:00 2001 From: NagaprasadVr <nagaprasadvr246@gmail.com> Date: Sat, 13 Apr 2024 13:25:56 +0530 Subject: [PATCH 03/14] cleanup tests --- cli/tests/program.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/tests/program.rs b/cli/tests/program.rs index db5d04cfea72c1..017cb17e9bd2dc 100644 --- a/cli/tests/program.rs +++ b/cli/tests/program.rs @@ -2402,7 +2402,6 @@ fn test_cli_program_upgrade_auto_extend_program_data_account() { // Wait one slot to avoid "Program was deployed in this block already" error wait_n_slots(&rpc_client, 1); - // Extend program for larger program, minus 1 required byte let mut file = File::open(noop_large_path.to_str().unwrap()).unwrap(); let mut new_program_data = Vec::new(); file.read_to_end(&mut new_program_data).unwrap(); @@ -2425,6 +2424,7 @@ fn test_cli_program_upgrade_auto_extend_program_data_account() { skip_fee_check: false, compute_unit_price: None, max_sign_attempts: 5, + // Enable auto_extend_program auto_extend_program: true, }); process_command(&config).unwrap(); From d5aae8c6109ad9751b9b543eed67ea54034df5ad Mon Sep 17 00:00:00 2001 From: NagaprasadVr <nagaprasadvr246@gmail.com> Date: Wed, 17 Apr 2024 21:18:43 +0530 Subject: [PATCH 04/14] automatically extend program data account and add a --no-auto-extend-program falg --- cli/src/program.rs | 48 ++++++++++++++++---------------- cli/tests/program.rs | 65 ++++++++++++++++++++++++-------------------- 2 files changed, 59 insertions(+), 54 deletions(-) diff --git a/cli/src/program.rs b/cli/src/program.rs index 5ff13ae7bfa3f8..2cb66e2f13e577 100644 --- a/cli/src/program.rs +++ b/cli/src/program.rs @@ -100,7 +100,7 @@ pub enum ProgramCliCommand { skip_fee_check: bool, compute_unit_price: Option<u64>, max_sign_attempts: usize, - auto_extend_program: bool, + no_auto_extend_program: bool, }, Upgrade { fee_payer_signer_index: SignerIndex, @@ -273,12 +273,10 @@ impl ProgramSubCommands for App<'_, '_> { ) .arg(compute_unit_price_arg()) .arg( - Arg::with_name("auto_extend_program") - .long("auto-extend-program") + Arg::with_name("no_auto_extend_program") + .long("no-auto-extend-program") .takes_value(false) - .help( - "Automatically extend the program data account's size to match the updated program's data size", - ), + .help("Don't automatically extend the program's data account size"), ), ) .subcommand( @@ -671,7 +669,7 @@ pub fn parse_program_subcommand( let compute_unit_price = value_of(matches, "compute_unit_price"); let max_sign_attempts = value_of(matches, "max_sign_attempts").unwrap(); - let auto_extend_program = matches.is_present("auto_extend_program"); + let no_auto_extend_program = matches.is_present("no_auto_extend_program"); CliCommandInfo { command: CliCommand::Program(ProgramCliCommand::Deploy { @@ -690,7 +688,7 @@ pub fn parse_program_subcommand( skip_fee_check, compute_unit_price, max_sign_attempts, - auto_extend_program, + no_auto_extend_program, }), signers: signer_info.signers, } @@ -978,7 +976,7 @@ pub fn process_program_subcommand( skip_fee_check, compute_unit_price, max_sign_attempts, - auto_extend_program, + no_auto_extend_program, } => process_program_deploy( rpc_client, config, @@ -995,7 +993,7 @@ pub fn process_program_subcommand( *skip_fee_check, *compute_unit_price, *max_sign_attempts, - *auto_extend_program, + *no_auto_extend_program, ), ProgramCliCommand::Upgrade { fee_payer_signer_index, @@ -1173,7 +1171,7 @@ fn process_program_deploy( skip_fee_check: bool, compute_unit_price: Option<u64>, max_sign_attempts: usize, - auto_extend_program: bool, + no_auto_extend_program: bool, ) -> ProcessResult { let fee_payer_signer = config.signers[fee_payer_signer_index]; let upgrade_authority_signer = config.signers[upgrade_authority_signer_index]; @@ -1358,7 +1356,7 @@ fn process_program_deploy( skip_fee_check, compute_unit_price, max_sign_attempts, - auto_extend_program, + no_auto_extend_program, ) }; if result.is_ok() && is_final { @@ -2498,7 +2496,7 @@ fn do_process_program_upgrade( skip_fee_check: bool, compute_unit_price: Option<u64>, max_sign_attempts: usize, - auto_extend_program: bool, + no_auto_extend_program: bool, ) -> ProcessResult { let blockhash = rpc_client.get_latest_blockhash()?; let program_data_address = get_program_data_address(program_id); @@ -2511,7 +2509,7 @@ fn do_process_program_upgrade( let additional_bytes = program_len .sub(program_data_account.data.len()) .saturating_add(metadata_size); - if auto_extend_program { + if !no_auto_extend_program { Some(bpf_loader_upgradeable::extend_program( program_id, Some(&fee_payer_signer.pubkey()), @@ -2520,8 +2518,8 @@ fn do_process_program_upgrade( } else { let err_string = format!( r#"Program Data Account space is not enough -please extend the program data account with command: solana program extend {} {} -or pass flag --auto-extend-program to extend program data account automatically"#, +please extend the program data account with command: solana program extend {} {}, +please disable the --no-extend-program flag to automatically extend the program account size"#, program_id, additional_bytes ); return Err(err_string.into()); @@ -3000,7 +2998,7 @@ mod tests { skip_fee_check: false, compute_unit_price: None, max_sign_attempts: 5, - auto_extend_program: false, + no_auto_extend_program: false, }), signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())], } @@ -3031,7 +3029,7 @@ mod tests { skip_fee_check: false, compute_unit_price: None, max_sign_attempts: 5, - auto_extend_program: false + no_auto_extend_program: false }), signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())], } @@ -3064,7 +3062,7 @@ mod tests { skip_fee_check: false, compute_unit_price: None, max_sign_attempts: 5, - auto_extend_program: false + no_auto_extend_program: false }), signers: vec![ Box::new(read_keypair_file(&keypair_file).unwrap()), @@ -3099,7 +3097,7 @@ mod tests { skip_fee_check: false, compute_unit_price: None, max_sign_attempts: 5, - auto_extend_program: false + no_auto_extend_program: false }), signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())], } @@ -3133,7 +3131,7 @@ mod tests { skip_fee_check: false, compute_unit_price: None, max_sign_attempts: 5, - auto_extend_program: false + no_auto_extend_program: false }), signers: vec![ Box::new(read_keypair_file(&keypair_file).unwrap()), @@ -3170,7 +3168,7 @@ mod tests { skip_fee_check: false, compute_unit_price: None, max_sign_attempts: 5, - auto_extend_program: false + no_auto_extend_program: false }), signers: vec![ Box::new(read_keypair_file(&keypair_file).unwrap()), @@ -3203,7 +3201,7 @@ mod tests { allow_excessive_balance: false, compute_unit_price: None, max_sign_attempts: 5, - auto_extend_program: false + no_auto_extend_program: false }), signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())], } @@ -3234,7 +3232,7 @@ mod tests { skip_fee_check: false, compute_unit_price: None, max_sign_attempts: 1, - auto_extend_program: false + no_auto_extend_program: false }), signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())], } @@ -3977,7 +3975,7 @@ mod tests { skip_fee_check: false, compute_unit_price: None, max_sign_attempts: 5, - auto_extend_program: false, + no_auto_extend_program: false, }), signers: vec![&default_keypair], output_format: OutputFormat::JsonCompact, diff --git a/cli/tests/program.rs b/cli/tests/program.rs index 017cb17e9bd2dc..c12eeab174877e 100644 --- a/cli/tests/program.rs +++ b/cli/tests/program.rs @@ -99,7 +99,7 @@ fn test_cli_program_deploy_non_upgradeable() { skip_fee_check: false, compute_unit_price: None, max_sign_attempts: 5, - auto_extend_program: false, + no_auto_extend_program: false, }); config.output_format = OutputFormat::JsonCompact; let response = process_command(&config); @@ -148,7 +148,7 @@ fn test_cli_program_deploy_non_upgradeable() { skip_fee_check: false, compute_unit_price: None, max_sign_attempts: 5, - auto_extend_program: false, + no_auto_extend_program: false, }); process_command(&config).unwrap(); let account1 = rpc_client @@ -206,7 +206,7 @@ fn test_cli_program_deploy_non_upgradeable() { skip_fee_check: false, compute_unit_price: None, max_sign_attempts: 5, - auto_extend_program: false, + no_auto_extend_program: false, }); let err = process_command(&config).unwrap_err(); assert_eq!( @@ -232,7 +232,7 @@ fn test_cli_program_deploy_non_upgradeable() { skip_fee_check: false, compute_unit_price: None, max_sign_attempts: 5, - auto_extend_program: false, + no_auto_extend_program: false, }); process_command(&config).unwrap_err(); } @@ -296,7 +296,7 @@ fn test_cli_program_deploy_no_authority() { skip_fee_check: false, compute_unit_price: None, max_sign_attempts: 5, - auto_extend_program: false, + no_auto_extend_program: false, }); config.output_format = OutputFormat::JsonCompact; let response = process_command(&config); @@ -326,7 +326,7 @@ fn test_cli_program_deploy_no_authority() { skip_fee_check: false, compute_unit_price: None, max_sign_attempts: 5, - auto_extend_program: false, + no_auto_extend_program: false, }); process_command(&config).unwrap_err(); } @@ -391,7 +391,7 @@ fn test_cli_program_deploy_with_authority() { skip_fee_check: false, compute_unit_price: None, max_sign_attempts: 5, - auto_extend_program: false, + no_auto_extend_program: false, }); config.output_format = OutputFormat::JsonCompact; let response = process_command(&config); @@ -443,7 +443,7 @@ fn test_cli_program_deploy_with_authority() { skip_fee_check: false, compute_unit_price: None, max_sign_attempts: 5, - auto_extend_program: false, + no_auto_extend_program: false, }); let response = process_command(&config); let json: Value = serde_json::from_str(&response.unwrap()).unwrap(); @@ -489,7 +489,7 @@ fn test_cli_program_deploy_with_authority() { skip_fee_check: false, compute_unit_price: None, max_sign_attempts: 5, - auto_extend_program: false, + no_auto_extend_program: false, }); process_command(&config).unwrap(); let program_account = rpc_client.get_account(&program_pubkey).unwrap(); @@ -567,7 +567,7 @@ fn test_cli_program_deploy_with_authority() { skip_fee_check: false, compute_unit_price: None, max_sign_attempts: 5, - auto_extend_program: false, + no_auto_extend_program: false, }); process_command(&config).unwrap(); let program_account = rpc_client.get_account(&program_pubkey).unwrap(); @@ -649,7 +649,7 @@ fn test_cli_program_deploy_with_authority() { skip_fee_check: false, compute_unit_price: None, max_sign_attempts: 5, - auto_extend_program: false, + no_auto_extend_program: false, }); process_command(&config).unwrap_err(); @@ -669,7 +669,7 @@ fn test_cli_program_deploy_with_authority() { skip_fee_check: false, compute_unit_price: None, max_sign_attempts: 5, - auto_extend_program: false, + no_auto_extend_program: false, }); let response = process_command(&config); let json: Value = serde_json::from_str(&response.unwrap()).unwrap(); @@ -776,7 +776,7 @@ fn test_cli_program_close_program() { skip_fee_check: false, compute_unit_price: None, max_sign_attempts: 5, - auto_extend_program: false, + no_auto_extend_program: false, }); config.output_format = OutputFormat::JsonCompact; process_command(&config).unwrap(); @@ -889,7 +889,7 @@ fn test_cli_program_extend_program() { skip_fee_check: false, compute_unit_price: None, max_sign_attempts: 5, - auto_extend_program: false, + no_auto_extend_program: false, }); config.output_format = OutputFormat::JsonCompact; process_command(&config).unwrap(); @@ -939,7 +939,7 @@ fn test_cli_program_extend_program() { skip_fee_check: false, compute_unit_price: None, max_sign_attempts: 5, - auto_extend_program: false, + no_auto_extend_program: false, }); process_command(&config).unwrap_err(); @@ -974,7 +974,7 @@ fn test_cli_program_extend_program() { skip_fee_check: false, compute_unit_price: None, max_sign_attempts: 5, - auto_extend_program: false, + no_auto_extend_program: false, }); process_command(&config).unwrap(); } @@ -1329,7 +1329,7 @@ fn test_cli_program_write_buffer() { skip_fee_check: false, compute_unit_price: None, max_sign_attempts: 5, - auto_extend_program: false, + no_auto_extend_program: false, }); config.output_format = OutputFormat::JsonCompact; let error = process_command(&config).unwrap_err(); @@ -1459,7 +1459,7 @@ fn test_cli_program_set_buffer_authority() { skip_fee_check: false, compute_unit_price: None, max_sign_attempts: 5, - auto_extend_program: false, + no_auto_extend_program: false, }); config.output_format = OutputFormat::JsonCompact; process_command(&config).unwrap_err(); @@ -1507,7 +1507,7 @@ fn test_cli_program_set_buffer_authority() { skip_fee_check: false, compute_unit_price: None, max_sign_attempts: 5, - auto_extend_program: false, + no_auto_extend_program: false, }); config.output_format = OutputFormat::JsonCompact; process_command(&config).unwrap(); @@ -1593,7 +1593,7 @@ fn test_cli_program_mismatch_buffer_authority() { skip_fee_check: false, compute_unit_price: None, max_sign_attempts: 5, - auto_extend_program: false, + no_auto_extend_program: false, }); process_command(&config).unwrap_err(); @@ -1613,7 +1613,7 @@ fn test_cli_program_mismatch_buffer_authority() { skip_fee_check: false, compute_unit_price: None, max_sign_attempts: 5, - auto_extend_program: false, + no_auto_extend_program: false, }); process_command(&config).unwrap(); } @@ -1699,7 +1699,7 @@ fn test_cli_program_deploy_with_offline_signing(use_offline_signer_as_fee_payer: skip_fee_check: false, compute_unit_price: None, max_sign_attempts: 5, - auto_extend_program: false, + no_auto_extend_program: false, }); config.output_format = OutputFormat::JsonCompact; process_command(&config).unwrap(); @@ -1933,7 +1933,7 @@ fn test_cli_program_show() { skip_fee_check: false, compute_unit_price: None, max_sign_attempts: 5, - auto_extend_program: false, + no_auto_extend_program: false, }); config.output_format = OutputFormat::JsonCompact; let min_slot = rpc_client.get_slot().unwrap(); @@ -2210,7 +2210,7 @@ fn test_cli_program_deploy_with_args(compute_unit_price: Option<u64>, use_rpc: b skip_fee_check: false, compute_unit_price, max_sign_attempts: 5, - auto_extend_program: false, + no_auto_extend_program: false, }); config.output_format = OutputFormat::JsonCompact; let response = process_command(&config); @@ -2320,7 +2320,7 @@ fn test_cli_program_deploy_with_compute_unit_price() { } #[test] -fn test_cli_program_upgrade_auto_extend_program_data_account() { +fn test_cli_program_upgrade_results_in_error_if_no_extend_program_flag_is_set() { solana_logger::setup(); let mut noop_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); @@ -2385,7 +2385,7 @@ fn test_cli_program_upgrade_auto_extend_program_data_account() { skip_fee_check: false, compute_unit_price: None, max_sign_attempts: 5, - auto_extend_program: false, + no_auto_extend_program: false, }); config.output_format = OutputFormat::JsonCompact; process_command(&config).unwrap(); @@ -2424,8 +2424,15 @@ fn test_cli_program_upgrade_auto_extend_program_data_account() { skip_fee_check: false, compute_unit_price: None, max_sign_attempts: 5, - // Enable auto_extend_program - auto_extend_program: true, + // set no_auto_extend_program + no_auto_extend_program: true, }); - process_command(&config).unwrap(); + let err = process_command(&config).unwrap_err(); + let err_string_to_match = format!( + r#"Program Data Account space is not enough +please extend the program data account with command: solana program extend {} 3184, +please disable the --no-extend-program flag to automatically extend the program account size"#, + program_keypair.pubkey() + ); + assert_eq!(err.to_string(), err_string_to_match); } From ed895275f4037e25059b1d114ac30c81c4c98f85 Mon Sep 17 00:00:00 2001 From: NagaprasadVr <nagaprasadvr246@gmail.com> Date: Wed, 17 Apr 2024 21:27:17 +0530 Subject: [PATCH 05/14] change comments in do_process_program_upgrade --- cli/src/program.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/cli/src/program.rs b/cli/src/program.rs index 2cb66e2f13e577..833ee577744199 100644 --- a/cli/src/program.rs +++ b/cli/src/program.rs @@ -2503,6 +2503,7 @@ fn do_process_program_upgrade( let program_data_account = rpc_client.get_account(&program_data_address)?; //CHECK: if passed program size is greater than the current program data account size, + //auto extend the program data account .if no-auto-extend-program flag is set then //throw an err along with a message including additional bytes required let add_program_extend_ix = if program_len > program_data_account.data.len() { let metadata_size = UpgradeableLoaderState::size_of_programdata_metadata(); From fe201ae0bdebce8928d8fce9bdadd993f9381362 Mon Sep 17 00:00:00 2001 From: NagaprasadVr <nagaprasadvr246@gmail.com> Date: Wed, 17 Apr 2024 21:29:00 +0530 Subject: [PATCH 06/14] simplify comments in do_process_program_upgrade --- cli/src/program.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cli/src/program.rs b/cli/src/program.rs index 833ee577744199..060a79899c227b 100644 --- a/cli/src/program.rs +++ b/cli/src/program.rs @@ -2502,8 +2502,7 @@ fn do_process_program_upgrade( let program_data_address = get_program_data_address(program_id); let program_data_account = rpc_client.get_account(&program_data_address)?; - //CHECK: if passed program size is greater than the current program data account size, - //auto extend the program data account .if no-auto-extend-program flag is set then + //CHECK: auto extend program data account in case of insufficient space .if no-auto-extend-program flag is set then //throw an err along with a message including additional bytes required let add_program_extend_ix = if program_len > program_data_account.data.len() { let metadata_size = UpgradeableLoaderState::size_of_programdata_metadata(); From 6ed4600c745a3b2eab44f4bbe37e8c8ebfb7d0ed Mon Sep 17 00:00:00 2001 From: NagaprasadVr <nagaprasadvr246@gmail.com> Date: Thu, 18 Apr 2024 09:36:22 +0530 Subject: [PATCH 07/14] resolve comments --- cli/src/program.rs | 97 ++++++++++++++----------- cli/tests/program.rs | 166 +++++++------------------------------------ 2 files changed, 79 insertions(+), 184 deletions(-) diff --git a/cli/src/program.rs b/cli/src/program.rs index 060a79899c227b..7a81aad9922660 100644 --- a/cli/src/program.rs +++ b/cli/src/program.rs @@ -100,7 +100,7 @@ pub enum ProgramCliCommand { skip_fee_check: bool, compute_unit_price: Option<u64>, max_sign_attempts: usize, - no_auto_extend_program: bool, + no_extend: bool, }, Upgrade { fee_payer_signer_index: SignerIndex, @@ -273,8 +273,8 @@ impl ProgramSubCommands for App<'_, '_> { ) .arg(compute_unit_price_arg()) .arg( - Arg::with_name("no_auto_extend_program") - .long("no-auto-extend-program") + Arg::with_name("no_extend") + .long("no-extend") .takes_value(false) .help("Don't automatically extend the program's data account size"), ), @@ -669,7 +669,7 @@ pub fn parse_program_subcommand( let compute_unit_price = value_of(matches, "compute_unit_price"); let max_sign_attempts = value_of(matches, "max_sign_attempts").unwrap(); - let no_auto_extend_program = matches.is_present("no_auto_extend_program"); + let no_extend = matches.is_present("no_extend"); CliCommandInfo { command: CliCommand::Program(ProgramCliCommand::Deploy { @@ -688,7 +688,7 @@ pub fn parse_program_subcommand( skip_fee_check, compute_unit_price, max_sign_attempts, - no_auto_extend_program, + no_extend, }), signers: signer_info.signers, } @@ -976,7 +976,7 @@ pub fn process_program_subcommand( skip_fee_check, compute_unit_price, max_sign_attempts, - no_auto_extend_program, + no_extend, } => process_program_deploy( rpc_client, config, @@ -993,7 +993,7 @@ pub fn process_program_subcommand( *skip_fee_check, *compute_unit_price, *max_sign_attempts, - *no_auto_extend_program, + *no_extend, ), ProgramCliCommand::Upgrade { fee_payer_signer_index, @@ -1171,7 +1171,7 @@ fn process_program_deploy( skip_fee_check: bool, compute_unit_price: Option<u64>, max_sign_attempts: usize, - no_auto_extend_program: bool, + no_extend: bool, ) -> ProcessResult { let fee_payer_signer = config.signers[fee_payer_signer_index]; let upgrade_authority_signer = config.signers[upgrade_authority_signer_index]; @@ -1356,7 +1356,7 @@ fn process_program_deploy( skip_fee_check, compute_unit_price, max_sign_attempts, - no_auto_extend_program, + no_extend, ) }; if result.is_ok() && is_final { @@ -2496,36 +2496,49 @@ fn do_process_program_upgrade( skip_fee_check: bool, compute_unit_price: Option<u64>, max_sign_attempts: usize, - no_auto_extend_program: bool, + no_extend: bool, ) -> ProcessResult { let blockhash = rpc_client.get_latest_blockhash()?; let program_data_address = get_program_data_address(program_id); - let program_data_account = rpc_client.get_account(&program_data_address)?; - - //CHECK: auto extend program data account in case of insufficient space .if no-auto-extend-program flag is set then - //throw an err along with a message including additional bytes required - let add_program_extend_ix = if program_len > program_data_account.data.len() { - let metadata_size = UpgradeableLoaderState::size_of_programdata_metadata(); - let additional_bytes = program_len - .sub(program_data_account.data.len()) - .saturating_add(metadata_size); - if !no_auto_extend_program { - Some(bpf_loader_upgradeable::extend_program( - program_id, - Some(&fee_payer_signer.pubkey()), - additional_bytes as u32, - )) - } else { - let err_string = format!( - r#"Program Data Account space is not enough + let program_data_account = rpc_client.get_account(&program_data_address); + let program_len_with_metadata = UpgradeableLoaderState::size_of_programdata(program_len); + // Check - auto extend program data account in case of insufficient space . + // if no-extend flag is set then + // throw an err along with a message including additional bytes required + let add_program_extend_ix = match program_data_account { + Ok(program_data_account) => { + if program_len_with_metadata > program_data_account.data.len() { + let additional_bytes = + program_len_with_metadata.sub(program_data_account.data.len()); + if !no_extend { + if additional_bytes > u32::MAX as usize { + let err_string = format!( + r#"Cannot auto-extend Program Data Account space due to size limit +please extend it manually by using the command: solana program extend {} {},"#, + program_id, additional_bytes + ); + + return Err(err_string.into()); + } + Some(bpf_loader_upgradeable::extend_program( + program_id, + Some(&fee_payer_signer.pubkey()), + additional_bytes as u32, + )) + } else { + let err_string = format!( + r#"Program Data Account space is not enough please extend the program data account with command: solana program extend {} {}, please disable the --no-extend-program flag to automatically extend the program account size"#, - program_id, additional_bytes - ); - return Err(err_string.into()); + program_id, additional_bytes + ); + return Err(err_string.into()); + } + } else { + None + } } - } else { - None + Err(_) => None, }; let (initial_message, write_messages, balance_needed) = if let Some(buffer_signer) = @@ -2998,7 +3011,7 @@ mod tests { skip_fee_check: false, compute_unit_price: None, max_sign_attempts: 5, - no_auto_extend_program: false, + no_extend: false, }), signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())], } @@ -3029,7 +3042,7 @@ mod tests { skip_fee_check: false, compute_unit_price: None, max_sign_attempts: 5, - no_auto_extend_program: false + no_extend: false }), signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())], } @@ -3062,7 +3075,7 @@ mod tests { skip_fee_check: false, compute_unit_price: None, max_sign_attempts: 5, - no_auto_extend_program: false + no_extend: false }), signers: vec![ Box::new(read_keypair_file(&keypair_file).unwrap()), @@ -3097,7 +3110,7 @@ mod tests { skip_fee_check: false, compute_unit_price: None, max_sign_attempts: 5, - no_auto_extend_program: false + no_extend: false }), signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())], } @@ -3131,7 +3144,7 @@ mod tests { skip_fee_check: false, compute_unit_price: None, max_sign_attempts: 5, - no_auto_extend_program: false + no_extend: false }), signers: vec![ Box::new(read_keypair_file(&keypair_file).unwrap()), @@ -3168,7 +3181,7 @@ mod tests { skip_fee_check: false, compute_unit_price: None, max_sign_attempts: 5, - no_auto_extend_program: false + no_extend: false }), signers: vec![ Box::new(read_keypair_file(&keypair_file).unwrap()), @@ -3201,7 +3214,7 @@ mod tests { allow_excessive_balance: false, compute_unit_price: None, max_sign_attempts: 5, - no_auto_extend_program: false + no_extend: false }), signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())], } @@ -3232,7 +3245,7 @@ mod tests { skip_fee_check: false, compute_unit_price: None, max_sign_attempts: 1, - no_auto_extend_program: false + no_extend: false }), signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())], } @@ -3975,7 +3988,7 @@ mod tests { skip_fee_check: false, compute_unit_price: None, max_sign_attempts: 5, - no_auto_extend_program: false, + no_extend: false, }), signers: vec![&default_keypair], output_format: OutputFormat::JsonCompact, diff --git a/cli/tests/program.rs b/cli/tests/program.rs index c12eeab174877e..5193559e8e3052 100644 --- a/cli/tests/program.rs +++ b/cli/tests/program.rs @@ -99,7 +99,7 @@ fn test_cli_program_deploy_non_upgradeable() { skip_fee_check: false, compute_unit_price: None, max_sign_attempts: 5, - no_auto_extend_program: false, + no_extend: false, }); config.output_format = OutputFormat::JsonCompact; let response = process_command(&config); @@ -148,7 +148,7 @@ fn test_cli_program_deploy_non_upgradeable() { skip_fee_check: false, compute_unit_price: None, max_sign_attempts: 5, - no_auto_extend_program: false, + no_extend: false, }); process_command(&config).unwrap(); let account1 = rpc_client @@ -206,7 +206,7 @@ fn test_cli_program_deploy_non_upgradeable() { skip_fee_check: false, compute_unit_price: None, max_sign_attempts: 5, - no_auto_extend_program: false, + no_extend: false, }); let err = process_command(&config).unwrap_err(); assert_eq!( @@ -232,7 +232,7 @@ fn test_cli_program_deploy_non_upgradeable() { skip_fee_check: false, compute_unit_price: None, max_sign_attempts: 5, - no_auto_extend_program: false, + no_extend: false, }); process_command(&config).unwrap_err(); } @@ -296,7 +296,7 @@ fn test_cli_program_deploy_no_authority() { skip_fee_check: false, compute_unit_price: None, max_sign_attempts: 5, - no_auto_extend_program: false, + no_extend: false, }); config.output_format = OutputFormat::JsonCompact; let response = process_command(&config); @@ -326,7 +326,7 @@ fn test_cli_program_deploy_no_authority() { skip_fee_check: false, compute_unit_price: None, max_sign_attempts: 5, - no_auto_extend_program: false, + no_extend: false, }); process_command(&config).unwrap_err(); } @@ -391,7 +391,7 @@ fn test_cli_program_deploy_with_authority() { skip_fee_check: false, compute_unit_price: None, max_sign_attempts: 5, - no_auto_extend_program: false, + no_extend: false, }); config.output_format = OutputFormat::JsonCompact; let response = process_command(&config); @@ -443,7 +443,7 @@ fn test_cli_program_deploy_with_authority() { skip_fee_check: false, compute_unit_price: None, max_sign_attempts: 5, - no_auto_extend_program: false, + no_extend: false, }); let response = process_command(&config); let json: Value = serde_json::from_str(&response.unwrap()).unwrap(); @@ -489,7 +489,7 @@ fn test_cli_program_deploy_with_authority() { skip_fee_check: false, compute_unit_price: None, max_sign_attempts: 5, - no_auto_extend_program: false, + no_extend: false, }); process_command(&config).unwrap(); let program_account = rpc_client.get_account(&program_pubkey).unwrap(); @@ -567,7 +567,7 @@ fn test_cli_program_deploy_with_authority() { skip_fee_check: false, compute_unit_price: None, max_sign_attempts: 5, - no_auto_extend_program: false, + no_extend: false, }); process_command(&config).unwrap(); let program_account = rpc_client.get_account(&program_pubkey).unwrap(); @@ -649,7 +649,7 @@ fn test_cli_program_deploy_with_authority() { skip_fee_check: false, compute_unit_price: None, max_sign_attempts: 5, - no_auto_extend_program: false, + no_extend: false, }); process_command(&config).unwrap_err(); @@ -669,7 +669,7 @@ fn test_cli_program_deploy_with_authority() { skip_fee_check: false, compute_unit_price: None, max_sign_attempts: 5, - no_auto_extend_program: false, + no_extend: false, }); let response = process_command(&config); let json: Value = serde_json::from_str(&response.unwrap()).unwrap(); @@ -776,7 +776,7 @@ fn test_cli_program_close_program() { skip_fee_check: false, compute_unit_price: None, max_sign_attempts: 5, - no_auto_extend_program: false, + no_extend: false, }); config.output_format = OutputFormat::JsonCompact; process_command(&config).unwrap(); @@ -889,7 +889,7 @@ fn test_cli_program_extend_program() { skip_fee_check: false, compute_unit_price: None, max_sign_attempts: 5, - no_auto_extend_program: false, + no_extend: false, }); config.output_format = OutputFormat::JsonCompact; process_command(&config).unwrap(); @@ -939,7 +939,7 @@ fn test_cli_program_extend_program() { skip_fee_check: false, compute_unit_price: None, max_sign_attempts: 5, - no_auto_extend_program: false, + no_extend: false, }); process_command(&config).unwrap_err(); @@ -974,7 +974,7 @@ fn test_cli_program_extend_program() { skip_fee_check: false, compute_unit_price: None, max_sign_attempts: 5, - no_auto_extend_program: false, + no_extend: false, }); process_command(&config).unwrap(); } @@ -1329,7 +1329,7 @@ fn test_cli_program_write_buffer() { skip_fee_check: false, compute_unit_price: None, max_sign_attempts: 5, - no_auto_extend_program: false, + no_extend: false, }); config.output_format = OutputFormat::JsonCompact; let error = process_command(&config).unwrap_err(); @@ -1459,7 +1459,7 @@ fn test_cli_program_set_buffer_authority() { skip_fee_check: false, compute_unit_price: None, max_sign_attempts: 5, - no_auto_extend_program: false, + no_extend: false, }); config.output_format = OutputFormat::JsonCompact; process_command(&config).unwrap_err(); @@ -1507,7 +1507,7 @@ fn test_cli_program_set_buffer_authority() { skip_fee_check: false, compute_unit_price: None, max_sign_attempts: 5, - no_auto_extend_program: false, + no_extend: false, }); config.output_format = OutputFormat::JsonCompact; process_command(&config).unwrap(); @@ -1593,7 +1593,7 @@ fn test_cli_program_mismatch_buffer_authority() { skip_fee_check: false, compute_unit_price: None, max_sign_attempts: 5, - no_auto_extend_program: false, + no_extend: false, }); process_command(&config).unwrap_err(); @@ -1613,7 +1613,7 @@ fn test_cli_program_mismatch_buffer_authority() { skip_fee_check: false, compute_unit_price: None, max_sign_attempts: 5, - no_auto_extend_program: false, + no_extend: false, }); process_command(&config).unwrap(); } @@ -1699,7 +1699,7 @@ fn test_cli_program_deploy_with_offline_signing(use_offline_signer_as_fee_payer: skip_fee_check: false, compute_unit_price: None, max_sign_attempts: 5, - no_auto_extend_program: false, + no_extend: false, }); config.output_format = OutputFormat::JsonCompact; process_command(&config).unwrap(); @@ -1933,7 +1933,7 @@ fn test_cli_program_show() { skip_fee_check: false, compute_unit_price: None, max_sign_attempts: 5, - no_auto_extend_program: false, + no_extend: false, }); config.output_format = OutputFormat::JsonCompact; let min_slot = rpc_client.get_slot().unwrap(); @@ -2210,7 +2210,7 @@ fn test_cli_program_deploy_with_args(compute_unit_price: Option<u64>, use_rpc: b skip_fee_check: false, compute_unit_price, max_sign_attempts: 5, - no_auto_extend_program: false, + no_extend: false, }); config.output_format = OutputFormat::JsonCompact; let response = process_command(&config); @@ -2318,121 +2318,3 @@ fn test_cli_program_deploy_with_compute_unit_price() { cli_program_deploy_with_args(Some(1000)); cli_program_deploy_with_args(None); } - -#[test] -fn test_cli_program_upgrade_results_in_error_if_no_extend_program_flag_is_set() { - 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 max_len = program_data.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 upgradeable 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, // Use None to check that it defaults to the max length - skip_fee_check: false, - compute_unit_price: None, - max_sign_attempts: 5, - no_auto_extend_program: false, - }); - config.output_format = OutputFormat::JsonCompact; - process_command(&config).unwrap(); - - let (programdata_pubkey, _) = Pubkey::find_program_address( - &[program_keypair.pubkey().as_ref()], - &bpf_loader_upgradeable::id(), - ); - - let programdata_account = rpc_client.get_account(&programdata_pubkey).unwrap(); - let expected_len = UpgradeableLoaderState::size_of_programdata(max_len); - assert_eq!(expected_len, programdata_account.data.len()); - - // Wait one slot to avoid "Program was deployed in this block already" error - wait_n_slots(&rpc_client, 1); - - let mut file = File::open(noop_large_path.to_str().unwrap()).unwrap(); - let mut new_program_data = Vec::new(); - file.read_to_end(&mut new_program_data).unwrap(); - - let programdata_account = rpc_client.get_account(&programdata_pubkey).unwrap(); - assert_eq!(expected_len, programdata_account.data.len()); - - 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: false, - max_len: None, - skip_fee_check: false, - compute_unit_price: None, - max_sign_attempts: 5, - // set no_auto_extend_program - no_auto_extend_program: true, - }); - let err = process_command(&config).unwrap_err(); - let err_string_to_match = format!( - r#"Program Data Account space is not enough -please extend the program data account with command: solana program extend {} 3184, -please disable the --no-extend-program flag to automatically extend the program account size"#, - program_keypair.pubkey() - ); - assert_eq!(err.to_string(), err_string_to_match); -} From d58e62f46afbb98c6e5775c11476011445d84d4c Mon Sep 17 00:00:00 2001 From: NagaprasadVr <nagaprasadvr246@gmail.com> Date: Thu, 18 Apr 2024 09:42:11 +0530 Subject: [PATCH 08/14] cleanup --- cli/src/program.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/src/program.rs b/cli/src/program.rs index 7a81aad9922660..71a7a04c692451 100644 --- a/cli/src/program.rs +++ b/cli/src/program.rs @@ -2502,7 +2502,7 @@ fn do_process_program_upgrade( let program_data_address = get_program_data_address(program_id); let program_data_account = rpc_client.get_account(&program_data_address); let program_len_with_metadata = UpgradeableLoaderState::size_of_programdata(program_len); - // Check - auto extend program data account in case of insufficient space . + // Check - auto extend program data account in case of insufficient space // if no-extend flag is set then // throw an err along with a message including additional bytes required let add_program_extend_ix = match program_data_account { From d92dcbc79d8039287c2f98f182affed23da5744a Mon Sep 17 00:00:00 2001 From: NagaprasadVr <nagaprasadvr246@gmail.com> Date: Thu, 18 Apr 2024 10:12:02 +0530 Subject: [PATCH 09/14] change error messages --- cli/src/program.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cli/src/program.rs b/cli/src/program.rs index 71a7a04c692451..62666669c54f0b 100644 --- a/cli/src/program.rs +++ b/cli/src/program.rs @@ -2514,7 +2514,8 @@ fn do_process_program_upgrade( if additional_bytes > u32::MAX as usize { let err_string = format!( r#"Cannot auto-extend Program Data Account space due to size limit -please extend it manually by using the command: solana program extend {} {},"#, +please extend it manually by runinng the command solana program extend {} <BYTES> multiple times +using chunks of additional bytes required:{}"#, program_id, additional_bytes ); From 1ed7c6574994f2c66aa70b38c99ea8232ea7a12e Mon Sep 17 00:00:00 2001 From: NagaprasadVr <nagaprasadvr246@gmail.com> Date: Tue, 23 Apr 2024 09:28:59 +0530 Subject: [PATCH 10/14] resolve comments --- cli/src/program.rs | 74 ++++++++++++++----------------------- transaction-dos/src/main.rs | 1 + 2 files changed, 29 insertions(+), 46 deletions(-) diff --git a/cli/src/program.rs b/cli/src/program.rs index 62666669c54f0b..254eb336fc46fe 100644 --- a/cli/src/program.rs +++ b/cli/src/program.rs @@ -2499,54 +2499,12 @@ fn do_process_program_upgrade( no_extend: bool, ) -> ProcessResult { let blockhash = rpc_client.get_latest_blockhash()?; - let program_data_address = get_program_data_address(program_id); - let program_data_account = rpc_client.get_account(&program_data_address); - let program_len_with_metadata = UpgradeableLoaderState::size_of_programdata(program_len); - // Check - auto extend program data account in case of insufficient space - // if no-extend flag is set then - // throw an err along with a message including additional bytes required - let add_program_extend_ix = match program_data_account { - Ok(program_data_account) => { - if program_len_with_metadata > program_data_account.data.len() { - let additional_bytes = - program_len_with_metadata.sub(program_data_account.data.len()); - if !no_extend { - if additional_bytes > u32::MAX as usize { - let err_string = format!( - r#"Cannot auto-extend Program Data Account space due to size limit -please extend it manually by runinng the command solana program extend {} <BYTES> multiple times -using chunks of additional bytes required:{}"#, - program_id, additional_bytes - ); - - return Err(err_string.into()); - } - Some(bpf_loader_upgradeable::extend_program( - program_id, - Some(&fee_payer_signer.pubkey()), - additional_bytes as u32, - )) - } else { - let err_string = format!( - r#"Program Data Account space is not enough -please extend the program data account with command: solana program extend {} {}, -please disable the --no-extend-program flag to automatically extend the program account size"#, - program_id, additional_bytes - ); - return Err(err_string.into()); - } - } else { - None - } - } - Err(_) => None, - }; let (initial_message, write_messages, balance_needed) = if let Some(buffer_signer) = buffer_signer { // Check Buffer account to see if partial initialization has occurred - let (initial_instructions, balance_needed, buffer_program_data) = + let (mut initial_instructions, balance_needed, buffer_program_data) = if let Some(mut account) = buffer_account { let (ixs, balance_needed) = complete_partial_program_init( &fee_payer_signer.pubkey(), @@ -2574,9 +2532,33 @@ please disable the --no-extend-program flag to automatically extend the program ) }; - // Add extend program instruction if ProgramData account needs to be extended - if let Some(add_program_extend_ix) = add_program_extend_ix { - initial_instructions.push(add_program_extend_ix); + if !no_extend { + // Attempt to look up the existing program's size, and automatically + // add an extend instruction if the program data account is too small. + let program_data_address = get_program_data_address(program_id); + if let Some(program_data_account) = rpc_client + .get_account_with_commitment(&program_data_address, config.commitment)? + .value + { + 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: u32 = additional_bytes.try_into().map_err(|_| { + format!( + "Cannot auto-extend Program Data Account space due to size limit \ + please extend it manually with command `solana program extend {} \ + <BYTES>`. Additional bytes required: {}", + program_id, additional_bytes + ) + })?; + initial_instructions.push(bpf_loader_upgradeable::extend_program( + program_id, + Some(&fee_payer_signer.pubkey()), + additional_bytes, + )); + } + } } let initial_message = if !initial_instructions.is_empty() { diff --git a/transaction-dos/src/main.rs b/transaction-dos/src/main.rs index 1e45a1fa5ccda4..8cd02173d62591 100644 --- a/transaction-dos/src/main.rs +++ b/transaction-dos/src/main.rs @@ -251,6 +251,7 @@ fn run_transactions_dos( max_sign_attempts: 5, use_rpc: false, skip_fee_check: true, // skip_fee_check + no_extend: false, }); process_command(&config).expect("deploy didn't pass"); From c0af1f9d6d537cb0c24110afc9652a9c0cade334 Mon Sep 17 00:00:00 2001 From: NagaprasadVr <nagaprasadvr246@gmail.com> Date: Tue, 23 Apr 2024 20:46:21 +0530 Subject: [PATCH 11/14] Fix after rebase --- cli/src/program.rs | 50 ++++++++++++++++++++++++++++++++++++++++++++ cli/tests/program.rs | 30 ++++++++++++++++++++------ 2 files changed, 74 insertions(+), 6 deletions(-) diff --git a/cli/src/program.rs b/cli/src/program.rs index 254eb336fc46fe..9ca3d1c568a767 100644 --- a/cli/src/program.rs +++ b/cli/src/program.rs @@ -101,6 +101,7 @@ pub enum ProgramCliCommand { compute_unit_price: Option<u64>, max_sign_attempts: usize, no_extend: bool, + use_rpc: bool, }, Upgrade { fee_payer_signer_index: SignerIndex, @@ -271,6 +272,9 @@ impl ProgramSubCommands for App<'_, '_> { whichever comes first.", ), ) + .arg(Arg::with_name("use_rpc").long("use-rpc").help( + "Send transactions to the configured RPC instead of validator TPUs", + )) .arg(compute_unit_price_arg()) .arg( Arg::with_name("no_extend") @@ -688,6 +692,7 @@ pub fn parse_program_subcommand( skip_fee_check, compute_unit_price, max_sign_attempts, + use_rpc: matches.is_present("use_rpc"), no_extend, }), signers: signer_info.signers, @@ -977,6 +982,7 @@ pub fn process_program_subcommand( compute_unit_price, max_sign_attempts, no_extend, + use_rpc, } => process_program_deploy( rpc_client, config, @@ -994,6 +1000,7 @@ pub fn process_program_subcommand( *compute_unit_price, *max_sign_attempts, *no_extend, + *use_rpc, ), ProgramCliCommand::Upgrade { fee_payer_signer_index, @@ -1172,6 +1179,7 @@ fn process_program_deploy( compute_unit_price: Option<u64>, max_sign_attempts: usize, no_extend: bool, + use_rpc: bool, ) -> ProcessResult { let fee_payer_signer = config.signers[fee_payer_signer_index]; let upgrade_authority_signer = config.signers[upgrade_authority_signer_index]; @@ -1357,6 +1365,7 @@ fn process_program_deploy( compute_unit_price, max_sign_attempts, no_extend, + use_rpc, ) }; if result.is_ok() && is_final { @@ -2497,6 +2506,7 @@ fn do_process_program_upgrade( compute_unit_price: Option<u64>, max_sign_attempts: usize, no_extend: bool, + use_rpc: bool, ) -> ProcessResult { let blockhash = rpc_client.get_latest_blockhash()?; @@ -2995,6 +3005,7 @@ mod tests { compute_unit_price: None, max_sign_attempts: 5, no_extend: false, + use_rpc: false, }), signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())], } @@ -3026,6 +3037,7 @@ mod tests { compute_unit_price: None, max_sign_attempts: 5, no_extend: false + use_rpc: false, }), signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())], } @@ -3059,6 +3071,7 @@ mod tests { compute_unit_price: None, max_sign_attempts: 5, no_extend: false + use_rpc: false, }), signers: vec![ Box::new(read_keypair_file(&keypair_file).unwrap()), @@ -3094,6 +3107,7 @@ mod tests { compute_unit_price: None, max_sign_attempts: 5, no_extend: false + use_rpc: false, }), signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())], } @@ -3128,6 +3142,7 @@ mod tests { compute_unit_price: None, max_sign_attempts: 5, no_extend: false + use_rpc: false, }), signers: vec![ Box::new(read_keypair_file(&keypair_file).unwrap()), @@ -3165,6 +3180,7 @@ mod tests { compute_unit_price: None, max_sign_attempts: 5, no_extend: false + use_rpc: false, }), signers: vec![ Box::new(read_keypair_file(&keypair_file).unwrap()), @@ -3198,6 +3214,7 @@ mod tests { compute_unit_price: None, max_sign_attempts: 5, no_extend: false + use_rpc: false, }), signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())], } @@ -3229,6 +3246,38 @@ mod tests { compute_unit_price: None, max_sign_attempts: 1, no_extend: false + use_rpc: false, + }), + signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())], + } + ); + + let test_command = test_commands.clone().get_matches_from(vec![ + "test", + "program", + "deploy", + "/Users/test/program.so", + "--use-rpc", + ]); + assert_eq!( + parse_command(&test_command, &default_signer, &mut None).unwrap(), + CliCommandInfo { + command: CliCommand::Program(ProgramCliCommand::Deploy { + program_location: Some("/Users/test/program.so".to_string()), + fee_payer_signer_index: 0, + buffer_signer_index: None, + buffer_pubkey: None, + program_signer_index: None, + program_pubkey: None, + upgrade_authority_signer_index: 0, + is_final: false, + max_len: None, + allow_excessive_balance: false, + skip_fee_check: false, + compute_unit_price: None, + max_sign_attempts: 5, + no_extend: false, + use_rpc: true, }), signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())], } @@ -3972,6 +4021,7 @@ mod tests { compute_unit_price: None, max_sign_attempts: 5, no_extend: false, + use_rpc: false, }), signers: vec![&default_keypair], output_format: OutputFormat::JsonCompact, diff --git a/cli/tests/program.rs b/cli/tests/program.rs index 5193559e8e3052..7224b1f94098f3 100644 --- a/cli/tests/program.rs +++ b/cli/tests/program.rs @@ -100,6 +100,7 @@ fn test_cli_program_deploy_non_upgradeable() { compute_unit_price: None, max_sign_attempts: 5, no_extend: false, + use_rpc: false, }); config.output_format = OutputFormat::JsonCompact; let response = process_command(&config); @@ -149,6 +150,7 @@ fn test_cli_program_deploy_non_upgradeable() { compute_unit_price: None, max_sign_attempts: 5, no_extend: false, + use_rpc: false, }); process_command(&config).unwrap(); let account1 = rpc_client @@ -207,6 +209,7 @@ fn test_cli_program_deploy_non_upgradeable() { compute_unit_price: None, max_sign_attempts: 5, no_extend: false, + use_rpc: false, }); let err = process_command(&config).unwrap_err(); assert_eq!( @@ -233,6 +236,7 @@ fn test_cli_program_deploy_non_upgradeable() { compute_unit_price: None, max_sign_attempts: 5, no_extend: false, + use_rpc: false, }); process_command(&config).unwrap_err(); } @@ -297,6 +301,7 @@ fn test_cli_program_deploy_no_authority() { compute_unit_price: None, max_sign_attempts: 5, no_extend: false, + use_rpc: false, }); config.output_format = OutputFormat::JsonCompact; let response = process_command(&config); @@ -327,6 +332,7 @@ fn test_cli_program_deploy_no_authority() { compute_unit_price: None, max_sign_attempts: 5, no_extend: false, + use_rpc: false, }); process_command(&config).unwrap_err(); } @@ -392,6 +398,7 @@ fn test_cli_program_deploy_with_authority() { compute_unit_price: None, max_sign_attempts: 5, no_extend: false, + use_rpc: false, }); config.output_format = OutputFormat::JsonCompact; let response = process_command(&config); @@ -444,6 +451,7 @@ fn test_cli_program_deploy_with_authority() { compute_unit_price: None, max_sign_attempts: 5, no_extend: false, + use_rpc: false, }); let response = process_command(&config); let json: Value = serde_json::from_str(&response.unwrap()).unwrap(); @@ -490,6 +498,7 @@ fn test_cli_program_deploy_with_authority() { compute_unit_price: None, max_sign_attempts: 5, no_extend: false, + use_rpc: false, }); process_command(&config).unwrap(); let program_account = rpc_client.get_account(&program_pubkey).unwrap(); @@ -568,6 +577,7 @@ fn test_cli_program_deploy_with_authority() { compute_unit_price: None, max_sign_attempts: 5, no_extend: false, + use_rpc: false, }); process_command(&config).unwrap(); let program_account = rpc_client.get_account(&program_pubkey).unwrap(); @@ -650,6 +660,7 @@ fn test_cli_program_deploy_with_authority() { compute_unit_price: None, max_sign_attempts: 5, no_extend: false, + use_rpc: false, }); process_command(&config).unwrap_err(); @@ -670,6 +681,7 @@ fn test_cli_program_deploy_with_authority() { compute_unit_price: None, max_sign_attempts: 5, no_extend: false, + use_rpc: false, }); let response = process_command(&config); let json: Value = serde_json::from_str(&response.unwrap()).unwrap(); @@ -777,6 +789,7 @@ fn test_cli_program_close_program() { compute_unit_price: None, max_sign_attempts: 5, no_extend: false, + use_rpc: false, }); config.output_format = OutputFormat::JsonCompact; process_command(&config).unwrap(); @@ -890,6 +903,7 @@ fn test_cli_program_extend_program() { compute_unit_price: None, max_sign_attempts: 5, no_extend: false, + use_rpc: false, }); config.output_format = OutputFormat::JsonCompact; process_command(&config).unwrap(); @@ -940,6 +954,7 @@ fn test_cli_program_extend_program() { compute_unit_price: None, max_sign_attempts: 5, no_extend: false, + use_rpc: false, }); process_command(&config).unwrap_err(); @@ -975,6 +990,7 @@ fn test_cli_program_extend_program() { compute_unit_price: None, max_sign_attempts: 5, no_extend: false, + use_rpc: false, }); process_command(&config).unwrap(); } @@ -1330,6 +1346,7 @@ fn test_cli_program_write_buffer() { compute_unit_price: None, max_sign_attempts: 5, no_extend: false, + use_rpc: false, }); config.output_format = OutputFormat::JsonCompact; let error = process_command(&config).unwrap_err(); @@ -1460,6 +1477,7 @@ fn test_cli_program_set_buffer_authority() { compute_unit_price: None, max_sign_attempts: 5, no_extend: false, + use_rpc: false, }); config.output_format = OutputFormat::JsonCompact; process_command(&config).unwrap_err(); @@ -1508,6 +1526,7 @@ fn test_cli_program_set_buffer_authority() { compute_unit_price: None, max_sign_attempts: 5, no_extend: false, + use_rpc: false, }); config.output_format = OutputFormat::JsonCompact; process_command(&config).unwrap(); @@ -1594,6 +1613,7 @@ fn test_cli_program_mismatch_buffer_authority() { compute_unit_price: None, max_sign_attempts: 5, no_extend: false, + use_rpc: false, }); process_command(&config).unwrap_err(); @@ -1614,6 +1634,7 @@ fn test_cli_program_mismatch_buffer_authority() { compute_unit_price: None, max_sign_attempts: 5, no_extend: false, + use_rpc: false, }); process_command(&config).unwrap(); } @@ -1700,6 +1721,7 @@ fn test_cli_program_deploy_with_offline_signing(use_offline_signer_as_fee_payer: compute_unit_price: None, max_sign_attempts: 5, no_extend: false, + use_rpc: false, }); config.output_format = OutputFormat::JsonCompact; process_command(&config).unwrap(); @@ -1934,6 +1956,7 @@ fn test_cli_program_show() { compute_unit_price: None, max_sign_attempts: 5, no_extend: false, + use_rpc: false, }); config.output_format = OutputFormat::JsonCompact; let min_slot = rpc_client.get_slot().unwrap(); @@ -2211,6 +2234,7 @@ fn test_cli_program_deploy_with_args(compute_unit_price: Option<u64>, use_rpc: b compute_unit_price, max_sign_attempts: 5, no_extend: false, + use_rpc, }); config.output_format = OutputFormat::JsonCompact; let response = process_command(&config); @@ -2312,9 +2336,3 @@ fn test_cli_program_deploy_with_args(compute_unit_price: Option<u64>, use_rpc: b ); } } - -#[test] -fn test_cli_program_deploy_with_compute_unit_price() { - cli_program_deploy_with_args(Some(1000)); - cli_program_deploy_with_args(None); -} From 581f8813459ceafe96cc65c78f7604d37f89a3d8 Mon Sep 17 00:00:00 2001 From: NagaprasadVr <nagaprasadvr246@gmail.com> Date: Wed, 24 Apr 2024 09:14:46 +0530 Subject: [PATCH 12/14] Fix after rebase --- cli/src/program.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/src/program.rs b/cli/src/program.rs index 9ca3d1c568a767..8fb4821852586a 100644 --- a/cli/src/program.rs +++ b/cli/src/program.rs @@ -273,7 +273,7 @@ impl ProgramSubCommands for App<'_, '_> { ), ) .arg(Arg::with_name("use_rpc").long("use-rpc").help( - "Send transactions to the configured RPC instead of validator TPUs", + "Send write transactions to the configured RPC instead of validator TPUs", )) .arg(compute_unit_price_arg()) .arg( From 4ee4dd239af5d46d76c1c087b8d4c08da4f68863 Mon Sep 17 00:00:00 2001 From: NagaprasadVr <nagaprasadvr246@gmail.com> Date: Wed, 24 Apr 2024 09:57:40 +0530 Subject: [PATCH 13/14] fix cargo clippy --- cli/src/program.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/cli/src/program.rs b/cli/src/program.rs index 8fb4821852586a..2b3b733da2e097 100644 --- a/cli/src/program.rs +++ b/cli/src/program.rs @@ -3036,7 +3036,7 @@ mod tests { skip_fee_check: false, compute_unit_price: None, max_sign_attempts: 5, - no_extend: false + no_extend: false, use_rpc: false, }), signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())], @@ -3070,7 +3070,7 @@ mod tests { skip_fee_check: false, compute_unit_price: None, max_sign_attempts: 5, - no_extend: false + no_extend: false, use_rpc: false, }), signers: vec![ @@ -3106,7 +3106,7 @@ mod tests { skip_fee_check: false, compute_unit_price: None, max_sign_attempts: 5, - no_extend: false + no_extend: false, use_rpc: false, }), signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())], @@ -3141,7 +3141,7 @@ mod tests { skip_fee_check: false, compute_unit_price: None, max_sign_attempts: 5, - no_extend: false + no_extend: false, use_rpc: false, }), signers: vec![ @@ -3179,7 +3179,7 @@ mod tests { skip_fee_check: false, compute_unit_price: None, max_sign_attempts: 5, - no_extend: false + no_extend: false, use_rpc: false, }), signers: vec![ @@ -3213,7 +3213,7 @@ mod tests { allow_excessive_balance: false, compute_unit_price: None, max_sign_attempts: 5, - no_extend: false + no_extend: false, use_rpc: false, }), signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())], @@ -3245,7 +3245,7 @@ mod tests { skip_fee_check: false, compute_unit_price: None, max_sign_attempts: 1, - no_extend: false + no_extend: false, use_rpc: false, }), signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())], From 9b189a7b3955707264bb518c80a98f0a8b8d8a57 Mon Sep 17 00:00:00 2001 From: Joe Caulfield <joe.caulfield@anza.xyz> Date: Wed, 24 Apr 2024 00:32:12 -0500 Subject: [PATCH 14/14] cli: add tests for auto-extend --- cli/src/program.rs | 3 +- cli/tests/program.rs | 149 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 150 insertions(+), 2 deletions(-) diff --git a/cli/src/program.rs b/cli/src/program.rs index 2b3b733da2e097..26b7db382e4764 100644 --- a/cli/src/program.rs +++ b/cli/src/program.rs @@ -71,7 +71,6 @@ use { fs::File, io::{Read, Write}, mem::size_of, - ops::Sub, path::PathBuf, rc::Rc, str::FromStr, @@ -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 \ diff --git a/cli/tests/program.rs b/cli/tests/program.rs index 7224b1f94098f3..2215f84a453af9 100644 --- a/cli/tests/program.rs +++ b/cli/tests/program.rs @@ -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}, @@ -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();