From cfe6cee6e192311cd20e31a97307f157c78b8770 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Mei=C3=9Fner?= Date: Mon, 5 Aug 2024 12:35:40 +0200 Subject: [PATCH 1/5] Adds the feature. --- programs/bpf-loader-tests/tests/common.rs | 5 ++++- sdk/src/feature_set.rs | 5 +++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/programs/bpf-loader-tests/tests/common.rs b/programs/bpf-loader-tests/tests/common.rs index 99cae212c7f481..10a0e9419d0116 100644 --- a/programs/bpf-loader-tests/tests/common.rs +++ b/programs/bpf-loader-tests/tests/common.rs @@ -6,6 +6,7 @@ use { account::AccountSharedData, account_utils::StateMut, bpf_loader_upgradeable::{id, UpgradeableLoaderState}, + feature_set::enable_extend_program_checked, instruction::{Instruction, InstructionError}, pubkey::Pubkey, signature::{Keypair, Signer}, @@ -14,7 +15,9 @@ use { }; pub async fn setup_test_context() -> ProgramTestContext { - let program_test = ProgramTest::new("", id(), Some(solana_bpf_loader_program::Entrypoint::vm)); + let mut program_test = + ProgramTest::new("", id(), Some(solana_bpf_loader_program::Entrypoint::vm)); + program_test.deactivate_feature(enable_extend_program_checked::id()); program_test.start_with_context().await } diff --git a/sdk/src/feature_set.rs b/sdk/src/feature_set.rs index 887d2e547f19b2..eab552297dcae3 100644 --- a/sdk/src/feature_set.rs +++ b/sdk/src/feature_set.rs @@ -841,6 +841,10 @@ pub mod vote_only_retransmitter_signed_fec_sets { solana_sdk::declare_id!("RfEcA95xnhuwooVAhUUksEJLZBF7xKCLuqrJoqk4Zph"); } +pub mod enable_extend_program_checked { + solana_sdk::declare_id!("97QCmR4QtfeQsAti9srfHFk5uMRFP95CvXG8EGr615HM"); +} + lazy_static! { /// Map of feature identifiers to user-visible description pub static ref FEATURE_NAMES: HashMap = [ @@ -1046,6 +1050,7 @@ lazy_static! { (move_stake_and_move_lamports_ixs::id(), "Enable MoveStake and MoveLamports stake program instructions #1610"), (ed25519_precompile_verify_strict::id(), "Use strict verification in ed25519 precompile SIMD-0152"), (vote_only_retransmitter_signed_fec_sets::id(), "vote only on retransmitter signed fec sets"), + (enable_extend_program_checked::id(), "Enable ExtendProgramChecked instruction"), /*************** ADD NEW FEATURES HERE ***************/ ] .iter() From f8962500f9f802c6f100510b46200863dcac7749 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Mei=C3=9Fner?= Date: Mon, 5 Aug 2024 12:36:28 +0200 Subject: [PATCH 2/5] Adds UpgradeableLoaderInstruction::ExtendProgramChecked. --- sdk/program/src/bpf_loader_upgradeable.rs | 28 +++++++++++++++++++ .../src/loader_upgradeable_instruction.rs | 19 +++++++++++++ 2 files changed, 47 insertions(+) diff --git a/sdk/program/src/bpf_loader_upgradeable.rs b/sdk/program/src/bpf_loader_upgradeable.rs index 82e9292fde2429..1a45eea86a57c3 100644 --- a/sdk/program/src/bpf_loader_upgradeable.rs +++ b/sdk/program/src/bpf_loader_upgradeable.rs @@ -344,6 +344,34 @@ pub fn extend_program( ) } +/// Returns the instruction required to extend the size of a program's +/// executable data account +pub fn extend_program_checked( + program_address: &Pubkey, + authority_address: &Pubkey, + payer_address: Option<&Pubkey>, + additional_bytes: u32, +) -> Instruction { + let program_data_address = get_program_data_address(program_address); + let mut metas = vec![ + AccountMeta::new(program_data_address, false), + AccountMeta::new(*program_address, false), + AccountMeta::new(*authority_address, false), + ]; + if let Some(payer_address) = payer_address { + metas.push(AccountMeta::new_readonly( + crate::system_program::id(), + false, + )); + metas.push(AccountMeta::new(*payer_address, true)); + } + Instruction::new_with_bincode( + id(), + &UpgradeableLoaderInstruction::ExtendProgramChecked { additional_bytes }, + metas, + ) +} + #[cfg(test)] mod tests { use {super::*, bincode::serialized_size}; diff --git a/sdk/program/src/loader_upgradeable_instruction.rs b/sdk/program/src/loader_upgradeable_instruction.rs index 1d2a9a8f9bfad9..706d194193b99c 100644 --- a/sdk/program/src/loader_upgradeable_instruction.rs +++ b/sdk/program/src/loader_upgradeable_instruction.rs @@ -160,4 +160,23 @@ pub enum UpgradeableLoaderInstruction { /// 1. `[signer]` The current authority. /// 2. `[signer]` The new authority. SetAuthorityChecked, + + /// Extend a program's ProgramData account by the specified number of bytes. + /// Only upgradeable program's can be extended. + /// + /// This instruction differs from ExtendProgram in that the authority is a + /// required signer. + /// + /// # Account references + /// 0. `[writable]` The ProgramData account. + /// 1. `[writable]` The ProgramData account's associated Program account. + /// 2. `[signer]` The authority. + /// 3. `[]` System program (`solana_sdk::system_program::id()`), optional, used to transfer + /// lamports from the payer to the ProgramData account. + /// 4. `[signer]` The payer account, optional, that will pay necessary rent exemption costs + /// for the increased storage size. + ExtendProgramChecked { + /// Number of bytes to extend the program data. + additional_bytes: u32, + }, } From 12855577216dc442623b6d187e03cc6f06d26b77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Mei=C3=9Fner?= Date: Mon, 5 Aug 2024 12:37:04 +0200 Subject: [PATCH 3/5] Adds feature gating logic. --- programs/bpf_loader/src/lib.rs | 315 +++++++++++++++++++-------------- 1 file changed, 180 insertions(+), 135 deletions(-) diff --git a/programs/bpf_loader/src/lib.rs b/programs/bpf_loader/src/lib.rs index 58ef907746f5ac..611143a230f2a9 100644 --- a/programs/bpf_loader/src/lib.rs +++ b/programs/bpf_loader/src/lib.rs @@ -36,6 +36,7 @@ use { entrypoint::{MAX_PERMITTED_DATA_INCREASE, SUCCESS}, feature_set::{ bpf_account_data_direct_mapping, enable_bpf_loader_set_authority_checked_ix, + enable_extend_program_checked, }, instruction::{AccountMeta, InstructionError}, loader_upgradeable_instruction::UpgradeableLoaderInstruction, @@ -1136,165 +1137,209 @@ fn process_loader_upgradeable_instruction( } } UpgradeableLoaderInstruction::ExtendProgram { additional_bytes } => { - if additional_bytes == 0 { - ic_logger_msg!(log_collector, "Additional bytes must be greater than 0"); + if invoke_context + .get_feature_set() + .is_active(&enable_extend_program_checked::id()) + { + return Err(InstructionError::InvalidInstructionData); + } + common_extend_program(invoke_context, additional_bytes, false)?; + } + UpgradeableLoaderInstruction::ExtendProgramChecked { additional_bytes } => { + if !invoke_context + .get_feature_set() + .is_active(&enable_extend_program_checked::id()) + { return Err(InstructionError::InvalidInstructionData); } + common_extend_program(invoke_context, additional_bytes, true)?; + } + } - const PROGRAM_DATA_ACCOUNT_INDEX: IndexOfAccount = 0; - const PROGRAM_ACCOUNT_INDEX: IndexOfAccount = 1; - #[allow(dead_code)] - // System program is only required when a CPI is performed - const OPTIONAL_SYSTEM_PROGRAM_ACCOUNT_INDEX: IndexOfAccount = 2; - const OPTIONAL_PAYER_ACCOUNT_INDEX: IndexOfAccount = 3; + Ok(()) +} - let programdata_account = instruction_context - .try_borrow_instruction_account(transaction_context, PROGRAM_DATA_ACCOUNT_INDEX)?; - let programdata_key = *programdata_account.get_key(); +fn common_extend_program( + invoke_context: &mut InvokeContext, + additional_bytes: u32, + check_authority: bool, +) -> Result<(), InstructionError> { + let log_collector = invoke_context.get_log_collector(); + let transaction_context = &invoke_context.transaction_context; + let instruction_context = transaction_context.get_current_instruction_context()?; + let program_id = instruction_context.get_last_program_key(transaction_context)?; - if program_id != programdata_account.get_owner() { - ic_logger_msg!(log_collector, "ProgramData owner is invalid"); - return Err(InstructionError::InvalidAccountOwner); - } - if !programdata_account.is_writable() { - ic_logger_msg!(log_collector, "ProgramData is not writable"); - return Err(InstructionError::InvalidArgument); - } + const PROGRAM_DATA_ACCOUNT_INDEX: IndexOfAccount = 0; + const PROGRAM_ACCOUNT_INDEX: IndexOfAccount = 1; + const AUTHORITY_ACCOUNT_INDEX: IndexOfAccount = 2; + let optional_payer_account_index = if check_authority { 4 } else { 3 }; - let program_account = instruction_context - .try_borrow_instruction_account(transaction_context, PROGRAM_ACCOUNT_INDEX)?; - if !program_account.is_writable() { - ic_logger_msg!(log_collector, "Program account is not writable"); - return Err(InstructionError::InvalidArgument); - } - if program_account.get_owner() != program_id { - ic_logger_msg!(log_collector, "Program account not owned by loader"); - return Err(InstructionError::InvalidAccountOwner); - } - let program_key = *program_account.get_key(); - match program_account.get_state()? { - UpgradeableLoaderState::Program { - programdata_address, - } => { - if programdata_address != programdata_key { - ic_logger_msg!( - log_collector, - "Program account does not match ProgramData account" - ); - return Err(InstructionError::InvalidArgument); - } - } - _ => { - ic_logger_msg!(log_collector, "Invalid Program account"); - return Err(InstructionError::InvalidAccountData); - } - } - drop(program_account); + if additional_bytes == 0 { + ic_logger_msg!(log_collector, "Additional bytes must be greater than 0"); + return Err(InstructionError::InvalidInstructionData); + } + + let programdata_account = instruction_context + .try_borrow_instruction_account(transaction_context, PROGRAM_DATA_ACCOUNT_INDEX)?; + let programdata_key = *programdata_account.get_key(); - let old_len = programdata_account.get_data().len(); - let new_len = old_len.saturating_add(additional_bytes as usize); - if new_len > MAX_PERMITTED_DATA_LENGTH as usize { + if program_id != programdata_account.get_owner() { + ic_logger_msg!(log_collector, "ProgramData owner is invalid"); + return Err(InstructionError::InvalidAccountOwner); + } + if !programdata_account.is_writable() { + ic_logger_msg!(log_collector, "ProgramData is not writable"); + return Err(InstructionError::InvalidArgument); + } + + let program_account = instruction_context + .try_borrow_instruction_account(transaction_context, PROGRAM_ACCOUNT_INDEX)?; + if !program_account.is_writable() { + ic_logger_msg!(log_collector, "Program account is not writable"); + return Err(InstructionError::InvalidArgument); + } + if program_account.get_owner() != program_id { + ic_logger_msg!(log_collector, "Program account not owned by loader"); + return Err(InstructionError::InvalidAccountOwner); + } + let program_key = *program_account.get_key(); + match program_account.get_state()? { + UpgradeableLoaderState::Program { + programdata_address, + } => { + if programdata_address != programdata_key { ic_logger_msg!( log_collector, - "Extended ProgramData length of {} bytes exceeds max account data length of {} bytes", - new_len, - MAX_PERMITTED_DATA_LENGTH + "Program account does not match ProgramData account" ); - return Err(InstructionError::InvalidRealloc); + return Err(InstructionError::InvalidArgument); } + } + _ => { + ic_logger_msg!(log_collector, "Invalid Program account"); + return Err(InstructionError::InvalidAccountData); + } + } + drop(program_account); - let clock_slot = invoke_context - .get_sysvar_cache() - .get_clock() - .map(|clock| clock.slot)?; - - let upgrade_authority_address = if let UpgradeableLoaderState::ProgramData { - slot, - upgrade_authority_address, - } = programdata_account.get_state()? - { - if clock_slot == slot { - ic_logger_msg!(log_collector, "Program was extended in this block already"); - return Err(InstructionError::InvalidArgument); - } + let old_len = programdata_account.get_data().len(); + let new_len = old_len.saturating_add(additional_bytes as usize); + if new_len > MAX_PERMITTED_DATA_LENGTH as usize { + ic_logger_msg!( + log_collector, + "Extended ProgramData length of {} bytes exceeds max account data length of {} bytes", + new_len, + MAX_PERMITTED_DATA_LENGTH + ); + return Err(InstructionError::InvalidRealloc); + } - if upgrade_authority_address.is_none() { - ic_logger_msg!( - log_collector, - "Cannot extend ProgramData accounts that are not upgradeable" - ); - return Err(InstructionError::Immutable); - } - upgrade_authority_address - } else { - ic_logger_msg!(log_collector, "ProgramData state is invalid"); - return Err(InstructionError::InvalidAccountData); - }; + let clock_slot = invoke_context + .get_sysvar_cache() + .get_clock() + .map(|clock| clock.slot)?; - let required_payment = { - let balance = programdata_account.get_lamports(); - let rent = invoke_context.get_sysvar_cache().get_rent()?; - let min_balance = rent.minimum_balance(new_len).max(1); - min_balance.saturating_sub(balance) - }; + let upgrade_authority_address = if let UpgradeableLoaderState::ProgramData { + slot, + upgrade_authority_address, + } = programdata_account.get_state()? + { + if clock_slot == slot { + ic_logger_msg!(log_collector, "Program was extended in this block already"); + return Err(InstructionError::InvalidArgument); + } - // Borrowed accounts need to be dropped before native_invoke - drop(programdata_account); + if upgrade_authority_address.is_none() { + ic_logger_msg!( + log_collector, + "Cannot extend ProgramData accounts that are not upgradeable" + ); + return Err(InstructionError::Immutable); + } - // Dereference the program ID to prevent overlapping mutable/immutable borrow of invoke context - let program_id = *program_id; - if required_payment > 0 { - let payer_key = *transaction_context.get_key_of_account_at_index( - instruction_context.get_index_of_instruction_account_in_transaction( - OPTIONAL_PAYER_ACCOUNT_INDEX, - )?, - )?; - - invoke_context.native_invoke( - system_instruction::transfer(&payer_key, &programdata_key, required_payment) - .into(), - &[], - )?; + if check_authority { + let authority_key = Some( + *transaction_context.get_key_of_account_at_index( + instruction_context + .get_index_of_instruction_account_in_transaction(AUTHORITY_ACCOUNT_INDEX)?, + )?, + ); + if upgrade_authority_address != authority_key { + ic_logger_msg!(log_collector, "Incorrect upgrade authority provided"); + return Err(InstructionError::IncorrectAuthority); } + if !instruction_context.is_instruction_account_signer(AUTHORITY_ACCOUNT_INDEX)? { + ic_logger_msg!(log_collector, "Upgrade authority did not sign"); + return Err(InstructionError::MissingRequiredSignature); + } + } - let transaction_context = &invoke_context.transaction_context; - let instruction_context = transaction_context.get_current_instruction_context()?; - let mut programdata_account = instruction_context - .try_borrow_instruction_account(transaction_context, PROGRAM_DATA_ACCOUNT_INDEX)?; - programdata_account.set_data_length(new_len)?; + upgrade_authority_address + } else { + ic_logger_msg!(log_collector, "ProgramData state is invalid"); + return Err(InstructionError::InvalidAccountData); + }; - let programdata_data_offset = UpgradeableLoaderState::size_of_programdata_metadata(); + let required_payment = { + let balance = programdata_account.get_lamports(); + let rent = invoke_context.get_sysvar_cache().get_rent()?; + let min_balance = rent.minimum_balance(new_len).max(1); + min_balance.saturating_sub(balance) + }; - deploy_program!( - invoke_context, - program_key, - &program_id, - UpgradeableLoaderState::size_of_program().saturating_add(new_len), - clock_slot, - { - drop(programdata_account); - }, - programdata_account - .get_data() - .get(programdata_data_offset..) - .ok_or(InstructionError::AccountDataTooSmall)?, - ); + // Borrowed accounts need to be dropped before native_invoke + drop(programdata_account); - let mut programdata_account = instruction_context - .try_borrow_instruction_account(transaction_context, PROGRAM_DATA_ACCOUNT_INDEX)?; - programdata_account.set_state(&UpgradeableLoaderState::ProgramData { - slot: clock_slot, - upgrade_authority_address, - })?; + // Dereference the program ID to prevent overlapping mutable/immutable borrow of invoke context + let program_id = *program_id; + if required_payment > 0 { + let payer_key = *transaction_context.get_key_of_account_at_index( + instruction_context + .get_index_of_instruction_account_in_transaction(optional_payer_account_index)?, + )?; - ic_logger_msg!( - log_collector, - "Extended ProgramData account by {} bytes", - additional_bytes - ); - } + invoke_context.native_invoke( + system_instruction::transfer(&payer_key, &programdata_key, required_payment).into(), + &[], + )?; } + let transaction_context = &invoke_context.transaction_context; + let instruction_context = transaction_context.get_current_instruction_context()?; + let mut programdata_account = instruction_context + .try_borrow_instruction_account(transaction_context, PROGRAM_DATA_ACCOUNT_INDEX)?; + programdata_account.set_data_length(new_len)?; + + let programdata_data_offset = UpgradeableLoaderState::size_of_programdata_metadata(); + + deploy_program!( + invoke_context, + program_key, + &program_id, + UpgradeableLoaderState::size_of_program().saturating_add(new_len), + clock_slot, + { + drop(programdata_account); + }, + programdata_account + .get_data() + .get(programdata_data_offset..) + .ok_or(InstructionError::AccountDataTooSmall)?, + ); + + let mut programdata_account = instruction_context + .try_borrow_instruction_account(transaction_context, PROGRAM_DATA_ACCOUNT_INDEX)?; + programdata_account.set_state(&UpgradeableLoaderState::ProgramData { + slot: clock_slot, + upgrade_authority_address, + })?; + + ic_logger_msg!( + log_collector, + "Extended ProgramData account by {} bytes", + additional_bytes + ); + Ok(()) } From 7263d9680a8bf82d14c5b6f060c57315d8b75318 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Mei=C3=9Fner?= Date: Mon, 5 Aug 2024 12:37:39 +0200 Subject: [PATCH 4/5] Adjusts transaction-status parser. --- transaction-status/src/parse_bpf_loader.rs | 26 ++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/transaction-status/src/parse_bpf_loader.rs b/transaction-status/src/parse_bpf_loader.rs index f4d531a48247fd..61782298a0f8ca 100644 --- a/transaction-status/src/parse_bpf_loader.rs +++ b/transaction-status/src/parse_bpf_loader.rs @@ -175,12 +175,12 @@ pub fn parse_bpf_upgradeable_loader( "additionalBytes": additional_bytes, "programDataAccount": account_keys[instruction.accounts[0] as usize].to_string(), "programAccount": account_keys[instruction.accounts[1] as usize].to_string(), - "systemProgram": if instruction.accounts.len() > 3 { + "systemProgram": if instruction.accounts.len() > 2 { Some(account_keys[instruction.accounts[2] as usize].to_string()) } else { None }, - "payerAccount": if instruction.accounts.len() > 4 { + "payerAccount": if instruction.accounts.len() > 3 { Some(account_keys[instruction.accounts[3] as usize].to_string()) } else { None @@ -188,6 +188,28 @@ pub fn parse_bpf_upgradeable_loader( }), }) } + UpgradeableLoaderInstruction::ExtendProgramChecked { additional_bytes } => { + check_num_bpf_upgradeable_loader_accounts(&instruction.accounts, 3)?; + Ok(ParsedInstructionEnum { + instruction_type: "extendProgramChecked".to_string(), + info: json!({ + "additionalBytes": additional_bytes, + "programDataAccount": account_keys[instruction.accounts[0] as usize].to_string(), + "programAccount": account_keys[instruction.accounts[1] as usize].to_string(), + "authority": account_keys[instruction.accounts[2] as usize].to_string(), + "systemProgram": if instruction.accounts.len() > 3 { + Some(account_keys[instruction.accounts[3] as usize].to_string()) + } else { + None + }, + "payerAccount": if instruction.accounts.len() > 4 { + Some(account_keys[instruction.accounts[4] as usize].to_string()) + } else { + None + }, + }), + }) + } } } From 1a99d701c077fb43013079804a117a3461cca308 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Mei=C3=9Fner?= Date: Mon, 5 Aug 2024 12:37:39 +0200 Subject: [PATCH 5/5] Adjusts CLI parser. --- cli/src/program.rs | 34 ++++++++++++++++++++-------------- cli/tests/program.rs | 4 ++-- 2 files changed, 22 insertions(+), 16 deletions(-) diff --git a/cli/src/program.rs b/cli/src/program.rs index a879ab3347c91a..0880d5f3c54d65 100644 --- a/cli/src/program.rs +++ b/cli/src/program.rs @@ -58,7 +58,7 @@ use { bpf_loader_upgradeable::{self, get_program_data_address, UpgradeableLoaderState}, commitment_config::CommitmentConfig, compute_budget, - feature_set::{FeatureSet, FEATURE_NAMES}, + feature_set::{enable_extend_program_checked, FeatureSet, FEATURE_NAMES}, instruction::{Instruction, InstructionError}, message::Message, packet::PACKET_DATA_SIZE, @@ -166,7 +166,7 @@ pub enum ProgramCliCommand { use_lamports_unit: bool, bypass_warning: bool, }, - ExtendProgram { + ExtendProgramChecked { program_pubkey: Pubkey, additional_bytes: u32, }, @@ -983,7 +983,7 @@ pub fn parse_program_subcommand( )?; CliCommandInfo { - command: CliCommand::Program(ProgramCliCommand::ExtendProgram { + command: CliCommand::Program(ProgramCliCommand::ExtendProgramChecked { program_pubkey, additional_bytes, }), @@ -1170,7 +1170,7 @@ pub fn process_program_subcommand( *use_lamports_unit, *bypass_warning, ), - ProgramCliCommand::ExtendProgram { + ProgramCliCommand::ExtendProgramChecked { program_pubkey, additional_bytes, } => process_extend_program(&rpc_client, config, *program_pubkey, *additional_bytes), @@ -2338,21 +2338,27 @@ fn process_extend_program( _ => Err(format!("Program {program_pubkey} is closed")), }?; - match upgrade_authority_address { - None => Err(format!("Program {program_pubkey} is not upgradeable")), - _ => Ok(()), - }?; + let upgrade_authority_address = upgrade_authority_address + .ok_or_else(|| format!("Program {program_pubkey} is not upgradeable"))?; let blockhash = rpc_client.get_latest_blockhash()?; + let feature_set = fetch_feature_set(rpc_client)?; - let mut tx = Transaction::new_unsigned(Message::new( - &[bpf_loader_upgradeable::extend_program( + let instruction = if feature_set.is_active(&enable_extend_program_checked::id()) { + bpf_loader_upgradeable::extend_program_checked( &program_pubkey, + &upgrade_authority_address, Some(&payer_pubkey), additional_bytes, - )], - Some(&payer_pubkey), - )); + ) + } else { + bpf_loader_upgradeable::extend_program( + &program_pubkey, + Some(&payer_pubkey), + additional_bytes, + ) + }; + let mut tx = Transaction::new_unsigned(Message::new(&[instruction], Some(&payer_pubkey))); tx.try_sign(&[config.signers[0]], blockhash)?; let result = rpc_client.send_and_confirm_transaction_with_spinner_and_config( @@ -4227,7 +4233,7 @@ mod tests { assert_eq!( parse_command(&test_command, &default_signer, &mut None).unwrap(), CliCommandInfo { - command: CliCommand::Program(ProgramCliCommand::ExtendProgram { + command: CliCommand::Program(ProgramCliCommand::ExtendProgramChecked { program_pubkey, additional_bytes }), diff --git a/cli/tests/program.rs b/cli/tests/program.rs index 6bec3bcc28b36f..d94e6355f93ffd 100644 --- a/cli/tests/program.rs +++ b/cli/tests/program.rs @@ -1423,7 +1423,7 @@ fn test_cli_program_extend_program() { let new_max_len = new_program_data.len(); let additional_bytes = (new_max_len - max_len) as u32; config.signers = vec![&keypair]; - config.command = CliCommand::Program(ProgramCliCommand::ExtendProgram { + config.command = CliCommand::Program(ProgramCliCommand::ExtendProgramChecked { program_pubkey: program_keypair.pubkey(), additional_bytes: additional_bytes - 1, }); @@ -1470,7 +1470,7 @@ fn test_cli_program_extend_program() { // Extend 1 last byte config.signers = vec![&keypair]; - config.command = CliCommand::Program(ProgramCliCommand::ExtendProgram { + config.command = CliCommand::Program(ProgramCliCommand::ExtendProgramChecked { program_pubkey: program_keypair.pubkey(), additional_bytes: 1, });