Skip to content

Commit

Permalink
Adds UpgradeableLoaderInstruction::Migrate.
Browse files Browse the repository at this point in the history
  • Loading branch information
Lichtso committed Jan 28, 2025
1 parent f5d6ebc commit ba4a1ef
Show file tree
Hide file tree
Showing 7 changed files with 183 additions and 1 deletion.
1 change: 1 addition & 0 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions programs/bpf_loader/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ solana-feature-set = { workspace = true }
solana-hash = { workspace = true }
solana-instruction = { workspace = true }
solana-keccak-hasher = { workspace = true }
solana-loader-v4-interface = { workspace = true }
solana-log-collector = { workspace = true }
solana-measure = { workspace = true }
solana-packet = { workspace = true }
Expand Down
161 changes: 160 additions & 1 deletion programs/bpf_loader/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ use {
enable_loader_v4, remove_accounts_executable_flag_checks,
},
solana_instruction::{error::InstructionError, AccountMeta},
solana_loader_v4_interface,
solana_log_collector::{ic_logger_msg, ic_msg, LogCollector},
solana_measure::measure::Measure,
solana_program::{
Expand Down Expand Up @@ -44,7 +45,9 @@ use {
verifier::RequisiteVerifier,
vm::{ContextObject, EbpfVm},
},
solana_sdk_ids::{bpf_loader, bpf_loader_deprecated, bpf_loader_upgradeable, native_loader},
solana_sdk_ids::{
bpf_loader, bpf_loader_deprecated, bpf_loader_upgradeable, loader_v4, native_loader,
},
solana_system_interface::{instruction as system_instruction, MAX_PERMITTED_DATA_LENGTH},
solana_transaction_context::{IndexOfAccount, InstructionContext, TransactionContext},
solana_type_overrides::sync::{atomic::Ordering, Arc},
Expand Down Expand Up @@ -1337,6 +1340,162 @@ fn process_loader_upgradeable_instruction(
additional_bytes
);
}
UpgradeableLoaderInstruction::Migrate => {
if !invoke_context
.get_feature_set()
.is_active(&enable_loader_v4::id())
{
return Err(InstructionError::InvalidInstructionData);
}

instruction_context.check_number_of_instruction_accounts(3)?;
let global_authority_key = *transaction_context.get_key_of_account_at_index(
instruction_context.get_index_of_instruction_account_in_transaction(2)?,
)?;
let clock_slot = invoke_context
.get_sysvar_cache()
.get_clock()
.map(|clock| clock.slot)?;

// Verify ProgramData account
let programdata =
instruction_context.try_borrow_instruction_account(transaction_context, 0)?;
let mut authority_key = None;
let program_len = match programdata.get_state() {
Ok(UpgradeableLoaderState::Uninitialized) => 0,
Ok(UpgradeableLoaderState::ProgramData {
slot,
upgrade_authority_address,
}) => {
if clock_slot == slot {
ic_logger_msg!(log_collector, "Program was deployed in this block already");
return Err(InstructionError::InvalidArgument);
}
authority_key = upgrade_authority_address;
if !instruction_context.is_instruction_account_signer(2)? {
ic_logger_msg!(log_collector, "Upgrade authority did not sign");
return Err(InstructionError::MissingRequiredSignature);
}
programdata
.get_data()
.len()
.saturating_sub(UpgradeableLoaderState::size_of_programdata(0))
}
_ => {
ic_logger_msg!(log_collector, "Invalid ProgramData account");
return Err(InstructionError::InvalidAccountData);
}
};
let programdata_key = *programdata.get_key();
let programdata_funds = programdata.get_lamports();
drop(programdata);

// Verify Program account
let mut program =
instruction_context.try_borrow_instruction_account(transaction_context, 1)?;
#[allow(deprecated)]
if !invoke_context
.get_feature_set()
.is_active(&remove_accounts_executable_flag_checks::id())
&& !program.is_executable()
{
ic_logger_msg!(log_collector, "Program account not executable");
return Err(InstructionError::AccountNotExecutable);
}
if !program.is_writable() {
ic_logger_msg!(log_collector, "Program account not writeable");
return Err(InstructionError::InvalidArgument);
}
if program.get_owner() != program_id {
ic_logger_msg!(log_collector, "Program account not owned by loader");
return Err(InstructionError::IncorrectProgramId);
}
if let UpgradeableLoaderState::Program {
programdata_address,
} = program.get_state()?
{
if programdata_address != programdata_key {
ic_logger_msg!(log_collector, "Program and ProgramData account mismatch");
return Err(InstructionError::InvalidArgument);
}
} else {
ic_logger_msg!(log_collector, "Invalid Program account");
return Err(InstructionError::InvalidAccountData);
}
program.set_data(Vec::default())?;
let program_key = *program.get_key();
drop(program);

invoke_context.native_invoke(
system_instruction::transfer(&programdata_key, &program_key, programdata_funds)
.into(),
&[],
)?;

invoke_context.native_invoke(
system_instruction::assign(&program_key, &loader_v4::id()).into(),
&[],
)?;

invoke_context.native_invoke(
solana_loader_v4_interface::instruction::truncate_uninitialized(
&program_key,
&global_authority_key,
program_len as u32,
&program_key,
)
.into(),
&[],
)?;

if program_len > 0 {
invoke_context.native_invoke(
solana_loader_v4_interface::instruction::copy(
&program_key,
&global_authority_key,
&programdata_key,
0,
0,
program_len as u32,
)
.into(),
&[],
)?;

invoke_context.native_invoke(
solana_loader_v4_interface::instruction::deploy(
&program_key,
&global_authority_key,
)
.into(),
&[],
)?;
}

if let Some(authority_key) = authority_key {
invoke_context.native_invoke(
solana_loader_v4_interface::instruction::transfer_authority(
&program_key,
&global_authority_key,
&authority_key,
)
.into(),
&[],
)?;
} else {
invoke_context.native_invoke(
solana_loader_v4_interface::instruction::finalize(
&program_key,
&global_authority_key,
&program_key,
)
.into(),
&[],
)?;
}

ic_logger_msg!(log_collector, "Migrated program {:?}", &program_key);
}
}

Ok(())
Expand Down
1 change: 1 addition & 0 deletions programs/sbf/Cargo.lock

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

8 changes: 8 additions & 0 deletions sdk/loader-v3-interface/src/instruction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,14 @@ pub enum UpgradeableLoaderInstruction {
/// 1. `[signer]` The current authority.
/// 2. `[signer]` The new authority.
SetAuthorityChecked,

/// Migrate the program to loader-v4.
///
/// # Account references
/// 0. `[writable]` The ProgramData account.
/// 1. `[writable]` The Program account.
/// 2. `[signer]` The current authority.
Migrate,
}

#[cfg(feature = "bincode")]
Expand Down
1 change: 1 addition & 0 deletions svm/examples/Cargo.lock

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

11 changes: 11 additions & 0 deletions transaction-status/src/parse_bpf_loader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,17 @@ pub fn parse_bpf_upgradeable_loader(
}),
})
}
UpgradeableLoaderInstruction::Migrate => {
check_num_bpf_upgradeable_loader_accounts(&instruction.accounts, 3)?;
Ok(ParsedInstructionEnum {
instruction_type: "migrate".to_string(),
info: json!({
"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(),
}),
})
}
}
}

Expand Down

0 comments on commit ba4a1ef

Please sign in to comment.