Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 25 additions & 5 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -446,7 +446,7 @@ solana-last-restart-slot = "2.2.1"
solana-lattice-hash = { path = "lattice-hash", version = "=2.2.16" }
solana-ledger = { path = "ledger", version = "=2.2.16" }
solana-loader-v2-interface = "2.2.1"
solana-loader-v3-interface = "3.0.0"
solana-loader-v3-interface = "4.0.1"
solana-loader-v4-interface = "2.2.1"
solana-loader-v4-program = { path = "programs/loader-v4", version = "=2.2.16" }
solana-local-cluster = { path = "local-cluster", version = "=2.2.16" }
Expand Down
2 changes: 1 addition & 1 deletion cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ solana-feature-gate-interface = "=2.2.2"
solana-hash = "=2.2.1"
solana-instruction = "=2.2.1"
solana-keypair = "=2.2.1"
solana-loader-v3-interface = "=3.0.0"
solana-loader-v3-interface = { version = "=4.0.1", features = ["bincode"] }
solana-loader-v4-interface = "=2.2.1"
solana-loader-v4-program = { workspace = true }
solana-logger = "=2.3.1"
Expand Down
113 changes: 85 additions & 28 deletions cli/src/program.rs
Original file line number Diff line number Diff line change
Expand Up @@ -168,8 +168,9 @@ pub enum ProgramCliCommand {
use_lamports_unit: bool,
bypass_warning: bool,
},
ExtendProgram {
ExtendProgramChecked {
program_pubkey: Pubkey,
authority_signer_index: SignerIndex,
additional_bytes: u32,
},
MigrateProgram {
Expand Down Expand Up @@ -1008,17 +1009,22 @@ pub fn parse_program_subcommand(
let program_pubkey = pubkey_of(matches, "program_id").unwrap();
let additional_bytes = value_of(matches, "additional_bytes").unwrap();

let (authority_signer, authority_pubkey) =
signer_of(matches, "authority", wallet_manager)?;

let signer_info = default_signer.generate_unique_signers(
vec![Some(
default_signer.signer_from_path(matches, wallet_manager)?,
)],
vec![
Some(default_signer.signer_from_path(matches, wallet_manager)?),
authority_signer,
],
matches,
wallet_manager,
)?;

CliCommandInfo {
command: CliCommand::Program(ProgramCliCommand::ExtendProgram {
command: CliCommand::Program(ProgramCliCommand::ExtendProgramChecked {
program_pubkey,
authority_signer_index: signer_info.index_of(authority_pubkey).unwrap(),
additional_bytes,
}),
signers: signer_info.signers,
Expand Down Expand Up @@ -1230,10 +1236,17 @@ pub fn process_program_subcommand(
*use_lamports_unit,
*bypass_warning,
),
ProgramCliCommand::ExtendProgram {
ProgramCliCommand::ExtendProgramChecked {
program_pubkey,
authority_signer_index,
additional_bytes,
} => process_extend_program(&rpc_client, config, *program_pubkey, *additional_bytes),
} => process_extend_program(
&rpc_client,
config,
*program_pubkey,
*authority_signer_index,
*additional_bytes,
),
ProgramCliCommand::MigrateProgram {
program_pubkey,
authority_signer_index,
Expand Down Expand Up @@ -2365,9 +2378,11 @@ fn process_extend_program(
rpc_client: &RpcClient,
config: &CliConfig,
program_pubkey: Pubkey,
authority_signer_index: SignerIndex,
additional_bytes: u32,
) -> ProcessResult {
let payer_pubkey = config.signers[0].pubkey();
let authority_signer = config.signers[authority_signer_index];

if additional_bytes == 0 {
return Err("Additional bytes must be greater than zero".into());
Expand Down Expand Up @@ -2410,23 +2425,39 @@ 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()?;
if authority_signer.pubkey() != upgrade_authority_address {
return Err(format!(
"Upgrade authority {} does not match {}",
upgrade_authority_address,
authority_signer.pubkey(),
)
.into());
}

let mut tx = Transaction::new_unsigned(Message::new(
&[loader_v3_instruction::extend_program(
&program_pubkey,
Some(&payer_pubkey),
additional_bytes,
)],
Some(&payer_pubkey),
));
let blockhash = rpc_client.get_latest_blockhash()?;
let feature_set = fetch_feature_set(rpc_client)?;

let instruction =
if feature_set.is_active(&agave_feature_set::enable_extend_program_checked::id()) {
loader_v3_instruction::extend_program_checked(
&program_pubkey,
&upgrade_authority_address,
Some(&payer_pubkey),
additional_bytes,
)
} else {
loader_v3_instruction::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)?;
tx.try_sign(&[config.signers[0], authority_signer], blockhash)?;
let result = rpc_client.send_and_confirm_transaction_with_spinner_and_config(
&tx,
config.commitment,
Expand Down Expand Up @@ -2971,6 +3002,17 @@ fn extend_program_data_if_needed(
return Ok(());
};

let upgrade_authority_address = match program_data_account.state() {
Ok(UpgradeableLoaderState::ProgramData {
slot: _,
upgrade_authority_address,
}) => Ok(upgrade_authority_address),
_ => Err(format!("Program {program_id} is closed")),
}?;

let upgrade_authority_address = upgrade_authority_address
.ok_or_else(|| format!("Program {program_id} is not upgradeable"))?;

let required_len = UpgradeableLoaderState::size_of_programdata(program_len);
let max_permitted_data_length = usize::try_from(MAX_PERMITTED_DATA_LENGTH).unwrap();
if required_len > max_permitted_data_length {
Expand All @@ -2992,11 +3034,20 @@ fn extend_program_data_if_needed(

let additional_bytes =
u32::try_from(additional_bytes).expect("`u32` is big enough to hold an account size");
initial_instructions.push(loader_v3_instruction::extend_program(
program_id,
Some(fee_payer),
additional_bytes,
));

let feature_set = fetch_feature_set(rpc_client)?;
let instruction =
if feature_set.is_active(&agave_feature_set::enable_extend_program_checked::id()) {
loader_v3_instruction::extend_program_checked(
program_id,
&upgrade_authority_address,
Some(fee_payer),
additional_bytes,
)
} else {
loader_v3_instruction::extend_program(program_id, Some(fee_payer), additional_bytes)
};
initial_instructions.push(instruction);

Ok(())
}
Expand Down Expand Up @@ -3091,7 +3142,12 @@ fn send_deploy_messages(
// account to sign the transaction. One (transfer) only requires the fee-payer signature.
// This check is to ensure signing does not fail on a KeypairPubkeyMismatch error from an
// extraneous signature.
if message.header.num_required_signatures == 2 {
if message.header.num_required_signatures == 3 {
initial_transaction.try_sign(
&[fee_payer_signer, initial_signer, write_signer.unwrap()],
blockhash,
)?;
} else if message.header.num_required_signatures == 2 {
initial_transaction.try_sign(&[fee_payer_signer, initial_signer], blockhash)?;
} else {
initial_transaction.try_sign(&[fee_payer_signer], blockhash)?;
Expand Down Expand Up @@ -4401,8 +4457,9 @@ 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,
authority_signer_index: 0,
additional_bytes
}),
signers: vec![Box::new(read_keypair_file(&keypair_file).unwrap())],
Expand Down
10 changes: 6 additions & 4 deletions cli/tests/program.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1444,9 +1444,10 @@ fn test_cli_program_extend_program() {
file.read_to_end(&mut new_program_data).unwrap();
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.signers = vec![&keypair, &upgrade_authority];
config.command = CliCommand::Program(ProgramCliCommand::ExtendProgramChecked {
program_pubkey: program_keypair.pubkey(),
authority_signer_index: 1,
additional_bytes: additional_bytes - 1,
});
process_command(&config).unwrap();
Expand Down Expand Up @@ -1492,9 +1493,10 @@ fn test_cli_program_extend_program() {
wait_n_slots(&rpc_client, 1);

// Extend 1 last byte
config.signers = vec![&keypair];
config.command = CliCommand::Program(ProgramCliCommand::ExtendProgram {
config.signers = vec![&keypair, &upgrade_authority];
config.command = CliCommand::Program(ProgramCliCommand::ExtendProgramChecked {
program_pubkey: program_keypair.pubkey(),
authority_signer_index: 1,
additional_bytes: 1,
});
process_command(&config).unwrap();
Expand Down
5 changes: 5 additions & 0 deletions feature-set/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1017,6 +1017,10 @@ pub mod mask_out_rent_epoch_in_vm_serialization {
solana_pubkey::declare_id!("RENtePQcDLrAbxAsP3k8dwVcnNYQ466hi2uKvALjnXx");
}

pub mod enable_extend_program_checked {
solana_pubkey::declare_id!("97QCmR4QtfeQsAti9srfHFk5uMRFP95CvXG8EGr615HM");
}

pub static FEATURE_NAMES: LazyLock<AHashMap<Pubkey, &'static str>> = LazyLock::new(|| {
[
(secp256k1_program_enabled::id(), "secp256k1 program"),
Expand Down Expand Up @@ -1247,6 +1251,7 @@ pub static FEATURE_NAMES: LazyLock<AHashMap<Pubkey, &'static str>> = LazyLock::n
(disable_partitioned_rent_collection::id(), "SIMD-0175: Disable partitioned rent collection"),
(raise_block_limits_to_60m::id(), "Raise block limit to 60M SIMD-0256"),
(mask_out_rent_epoch_in_vm_serialization::id(), "SIMD-0267: Sets rent_epoch to a constant in the VM"),
(enable_extend_program_checked::id(), "Enable ExtendProgramChecked instruction"),
/*************** ADD NEW FEATURES HERE ***************/
]
.iter()
Expand Down
1 change: 1 addition & 0 deletions genesis/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ solana-clap-utils = { workspace = true }
solana-cli-config = { workspace = true }
solana-entry = { workspace = true }
solana-ledger = { workspace = true }
solana-loader-v3-interface = "=4.0.1"
solana-logger = "=2.3.1"
solana-rpc-client = { workspace = true }
solana-rpc-client-api = { workspace = true }
Expand Down
1 change: 1 addition & 0 deletions programs/bpf-loader-tests/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ edition = { workspace = true }
assert_matches = { workspace = true }
bincode = { workspace = true }
solana-bpf-loader-program = { workspace = true }
solana-loader-v3-interface = { workspace = true, features = ["bincode"] }
solana-program-test = { workspace = true }
solana-sdk = { workspace = true }

Expand Down
3 changes: 2 additions & 1 deletion programs/bpf-loader-tests/tests/common.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
#![allow(dead_code)]

use {
solana_loader_v3_interface::state::UpgradeableLoaderState,
solana_program_test::*,
solana_sdk::{
account::AccountSharedData,
account_utils::StateMut,
bpf_loader_upgradeable::{id, UpgradeableLoaderState},
bpf_loader_upgradeable::id,
instruction::{Instruction, InstructionError},
pubkey::Pubkey,
signature::{Keypair, Signer},
Expand Down
Loading
Loading