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
10 changes: 6 additions & 4 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 @@ -430,7 +430,7 @@ solana-lattice-hash = { path = "lattice-hash", version = "=4.1.0-alpha.0", featu
solana-leader-schedule = { path = "leader-schedule", version = "=4.1.0-alpha.0", features = ["agave-unstable-api"] }
solana-ledger = { path = "ledger", version = "=4.1.0-alpha.0", features = ["agave-unstable-api"] }
solana-loader-v2-interface = "3.0.0"
solana-loader-v3-interface = "6.1.0"
solana-loader-v3-interface = "6.1.1"
solana-loader-v4-interface = "3.1.0"
solana-loader-v4-program = { path = "programs/loader-v4", version = "=4.1.0-alpha.0", default-features = false, features = ["agave-unstable-api"] }
solana-local-cluster = { path = "local-cluster", version = "=4.1.0-alpha.0", features = ["agave-unstable-api"] }
Expand Down
46 changes: 44 additions & 2 deletions cli/src/program.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ use {
solana_instruction::{Instruction, error::InstructionError},
solana_keypair::{Keypair, keypair_from_seed, read_keypair_file},
solana_loader_v3_interface::{
get_program_data_address, instruction as loader_v3_instruction,
get_program_data_address,
instruction::{self as loader_v3_instruction, MINIMUM_EXTEND_PROGRAM_BYTES},
state::UpgradeableLoaderState,
},
solana_message::Message,
Expand Down Expand Up @@ -2452,6 +2453,35 @@ async fn process_extend_program(
.ok_or_else(|| format!("Program {program_pubkey} is not upgradeable"))?;

let blockhash = rpc_client.get_latest_blockhash().await?;
let feature_set = fetch_feature_set(rpc_client).await?;
let feature_snapshot = feature_set.snapshot();

if feature_snapshot.loader_v3_minimum_extend_program_size {
// SIMD-0431: Minimum Extend Program Size
//
// All extensions must be >= 10 KiB in additional_bytes, unless
// MAX_PERMITTED_DATA_LENGTH - current_len < 10 KiB. In that case,
// additional_bytes must be equal to the remaining free space.
let current_len = programdata_account.data.len();
let headroom = (MAX_PERMITTED_DATA_LENGTH as usize).saturating_sub(current_len);
if additional_bytes < MINIMUM_EXTEND_PROGRAM_BYTES
&& (additional_bytes as usize) != headroom
{
let err_msg = if (headroom as u32) < MINIMUM_EXTEND_PROGRAM_BYTES {
format!(
"Program is {headroom} bytes from maximum size, but {additional_bytes} were \
requested. Please re-run the command with {headroom} additional bytes."
)
} else {
format!(
"ExtendProgram requires a minimum of {MINIMUM_EXTEND_PROGRAM_BYTES} \
additional bytes or to extend to maximum size, but only {additional_bytes} \
were requested"
)
};
return Err(err_msg.into());
}
}

let instruction = loader_v3_instruction::extend_program(
&program_pubkey,
Expand Down Expand Up @@ -2951,9 +2981,21 @@ async fn extend_program_data_if_needed(
return Ok(());
}

let additional_bytes =
let mut additional_bytes =
u32::try_from(additional_bytes).expect("`u32` is big enough to hold an account size");

let feature_set = fetch_feature_set(rpc_client).await?;
let feature_snapshot = feature_set.snapshot();

if feature_snapshot.loader_v3_minimum_extend_program_size {
// SIMD-0431: Have to bump `additional_bytes` to satisfy either the
// minimum size requirement or the remaining headroom to
// MAX_PERMITTED_DATA_SIZE.
let headroom =
u32::try_from(max_permitted_data_length.saturating_sub(current_len)).unwrap();
additional_bytes = additional_bytes.max(MINIMUM_EXTEND_PROGRAM_BYTES.min(headroom));
}

let instruction =
loader_v3_instruction::extend_program(program_id, Some(fee_payer), additional_bytes);
initial_instructions.push(instruction);
Expand Down
Loading
Loading