Refactor - Loader v4 deployment status (#33024)

Refactors "is_deployed: bool" and "authority_address: Option<Pubkey>".
Replaces them with "status: LoaderV4Status" and "authority_address: Pubkey".
This commit is contained in:
Alexander Meißner 2023-08-28 20:14:01 +02:00 committed by GitHub
parent 35bd2df0a6
commit 1238e53c9f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 138 additions and 59 deletions

View File

@ -26,7 +26,7 @@ use {
entrypoint::{HEAP_LENGTH, SUCCESS}, entrypoint::{HEAP_LENGTH, SUCCESS},
feature_set, feature_set,
instruction::InstructionError, instruction::InstructionError,
loader_v4::{self, LoaderV4State, DEPLOYMENT_COOLDOWN_IN_SLOTS}, loader_v4::{self, LoaderV4State, LoaderV4Status, DEPLOYMENT_COOLDOWN_IN_SLOTS},
loader_v4_instruction::LoaderV4Instruction, loader_v4_instruction::LoaderV4Instruction,
program_utils::limited_deserialize, program_utils::limited_deserialize,
pubkey::Pubkey, pubkey::Pubkey,
@ -223,14 +223,14 @@ fn check_program_account(
ic_logger_msg!(log_collector, "Authority did not sign"); ic_logger_msg!(log_collector, "Authority did not sign");
return Err(InstructionError::MissingRequiredSignature); return Err(InstructionError::MissingRequiredSignature);
} }
if state.authority_address.is_none() { if state.authority_address != *authority_address {
ic_logger_msg!(log_collector, "Program is finalized");
return Err(InstructionError::Immutable);
}
if state.authority_address != Some(*authority_address) {
ic_logger_msg!(log_collector, "Incorrect authority provided"); ic_logger_msg!(log_collector, "Incorrect authority provided");
return Err(InstructionError::IncorrectAuthority); return Err(InstructionError::IncorrectAuthority);
} }
if matches!(state.status, LoaderV4Status::Finalized) {
ic_logger_msg!(log_collector, "Program is finalized");
return Err(InstructionError::Immutable);
}
Ok(*state) Ok(*state)
} }
@ -270,7 +270,7 @@ pub fn process_instruction_write(
&program, &program,
authority_address, authority_address,
)?; )?;
if state.is_deployed { if !matches!(state.status, LoaderV4Status::Retracted) {
ic_logger_msg!(log_collector, "Program is not retracted"); ic_logger_msg!(log_collector, "Program is not retracted");
return Err(InstructionError::InvalidArgument); return Err(InstructionError::InvalidArgument);
} }
@ -317,8 +317,8 @@ pub fn process_instruction_write(
if is_initialization { if is_initialization {
let state = get_state_mut(program.get_data_mut()?)?; let state = get_state_mut(program.get_data_mut()?)?;
state.slot = invoke_context.get_sysvar_cache().get_clock()?.slot; state.slot = invoke_context.get_sysvar_cache().get_clock()?.slot;
state.is_deployed = false; state.status = LoaderV4Status::Retracted;
state.authority_address = Some(*authority_address); state.authority_address = *authority_address;
} }
program program
.get_data_mut()? .get_data_mut()?
@ -350,7 +350,7 @@ pub fn process_instruction_truncate(
&program, &program,
authority_address, authority_address,
)?; )?;
if state.is_deployed { if !matches!(state.status, LoaderV4Status::Retracted) {
ic_logger_msg!(log_collector, "Program is not retracted"); ic_logger_msg!(log_collector, "Program is not retracted");
return Err(InstructionError::InvalidArgument); return Err(InstructionError::InvalidArgument);
} }
@ -405,7 +405,7 @@ pub fn process_instruction_deploy(
); );
return Err(InstructionError::InvalidArgument); return Err(InstructionError::InvalidArgument);
} }
if state.is_deployed { if !matches!(state.status, LoaderV4Status::Retracted) {
ic_logger_msg!(log_collector, "Destination program is not retracted"); ic_logger_msg!(log_collector, "Destination program is not retracted");
return Err(InstructionError::InvalidArgument); return Err(InstructionError::InvalidArgument);
} }
@ -416,7 +416,7 @@ pub fn process_instruction_deploy(
source_program, source_program,
authority_address, authority_address,
)?; )?;
if source_state.is_deployed { if !matches!(source_state.status, LoaderV4Status::Retracted) {
ic_logger_msg!(log_collector, "Source program is not retracted"); ic_logger_msg!(log_collector, "Source program is not retracted");
return Err(InstructionError::InvalidArgument); return Err(InstructionError::InvalidArgument);
} }
@ -467,7 +467,7 @@ pub fn process_instruction_deploy(
} }
let state = get_state_mut(program.get_data_mut()?)?; let state = get_state_mut(program.get_data_mut()?)?;
state.slot = current_slot; state.slot = current_slot;
state.is_deployed = true; state.status = LoaderV4Status::Deployed;
if let Some(old_entry) = invoke_context.find_program_in_cache(program.get_key()) { if let Some(old_entry) = invoke_context.find_program_in_cache(program.get_key()) {
executor.tx_usage_counter.store( executor.tx_usage_counter.store(
@ -509,12 +509,12 @@ pub fn process_instruction_retract(
); );
return Err(InstructionError::InvalidArgument); return Err(InstructionError::InvalidArgument);
} }
if !state.is_deployed { if matches!(state.status, LoaderV4Status::Retracted) {
ic_logger_msg!(log_collector, "Program is not deployed"); ic_logger_msg!(log_collector, "Program is not deployed");
return Err(InstructionError::InvalidArgument); return Err(InstructionError::InvalidArgument);
} }
let state = get_state_mut(program.get_data_mut()?)?; let state = get_state_mut(program.get_data_mut()?)?;
state.is_deployed = false; state.status = LoaderV4Status::Retracted;
Ok(()) Ok(())
} }
@ -544,7 +544,14 @@ pub fn process_instruction_transfer_authority(
return Err(InstructionError::MissingRequiredSignature); return Err(InstructionError::MissingRequiredSignature);
} }
let state = get_state_mut(program.get_data_mut()?)?; let state = get_state_mut(program.get_data_mut()?)?;
state.authority_address = new_authority_address; if let Some(new_authority_address) = new_authority_address {
state.authority_address = new_authority_address;
} else if matches!(state.status, LoaderV4Status::Deployed) {
state.status = LoaderV4Status::Finalized;
} else {
ic_logger_msg!(log_collector, "Program must be deployed to be finalized");
return Err(InstructionError::InvalidArgument);
}
Ok(()) Ok(())
} }
@ -601,7 +608,7 @@ pub fn process_instruction_inner(
return Err(Box::new(InstructionError::InvalidAccountData)); return Err(Box::new(InstructionError::InvalidAccountData));
} }
let state = get_state(program.get_data())?; let state = get_state(program.get_data())?;
if !state.is_deployed { if matches!(state.status, LoaderV4Status::Retracted) {
ic_logger_msg!(log_collector, "Program is not deployed"); ic_logger_msg!(log_collector, "Program is not deployed");
return Err(Box::new(InstructionError::InvalidArgument)); return Err(Box::new(InstructionError::InvalidArgument));
} }
@ -645,7 +652,6 @@ mod tests {
create_account_shared_data_for_test, AccountSharedData, ReadableAccount, create_account_shared_data_for_test, AccountSharedData, ReadableAccount,
WritableAccount, WritableAccount,
}, },
account_utils::StateMut,
instruction::AccountMeta, instruction::AccountMeta,
slot_history::Slot, slot_history::Slot,
sysvar::{clock, rent}, sysvar::{clock, rent},
@ -738,8 +744,8 @@ mod tests {
} }
fn load_program_account_from_elf( fn load_program_account_from_elf(
is_deployed: bool, authority_address: Pubkey,
authority_address: Option<Pubkey>, status: LoaderV4Status,
path: &str, path: &str,
) -> AccountSharedData { ) -> AccountSharedData {
let path = Path::new("test_elfs/out/").join(path).with_extension("so"); let path = Path::new("test_elfs/out/").join(path).with_extension("so");
@ -754,13 +760,10 @@ mod tests {
account_size, account_size,
&loader_v4::id(), &loader_v4::id(),
); );
program_account let state = get_state_mut(program_account.data_as_mut_slice()).unwrap();
.set_state(&loader_v4::LoaderV4State { state.slot = 0;
slot: 0, state.authority_address = authority_address;
is_deployed, state.status = status;
authority_address,
})
.unwrap();
program_account.data_as_mut_slice()[loader_v4::LoaderV4State::program_data_offset()..] program_account.data_as_mut_slice()[loader_v4::LoaderV4State::program_data_offset()..]
.copy_from_slice(&elf_bytes); .copy_from_slice(&elf_bytes);
program_account program_account
@ -780,7 +783,7 @@ mod tests {
let transaction_accounts = vec![ let transaction_accounts = vec![
( (
Pubkey::new_unique(), Pubkey::new_unique(),
load_program_account_from_elf(true, Some(authority_address), "noop"), load_program_account_from_elf(authority_address, LoaderV4Status::Deployed, "noop"),
), ),
( (
authority_address, authority_address,
@ -788,7 +791,7 @@ mod tests {
), ),
( (
Pubkey::new_unique(), Pubkey::new_unique(),
load_program_account_from_elf(false, None, "noop"), load_program_account_from_elf(authority_address, LoaderV4Status::Finalized, "noop"),
), ),
( (
clock::id(), clock::id(),
@ -882,7 +885,7 @@ mod tests {
), ),
( (
Pubkey::new_unique(), Pubkey::new_unique(),
load_program_account_from_elf(true, Some(authority_address), "noop"), load_program_account_from_elf(authority_address, LoaderV4Status::Deployed, "noop"),
), ),
( (
clock::id(), clock::id(),
@ -1103,7 +1106,7 @@ mod tests {
let transaction_accounts = vec![ let transaction_accounts = vec![
( (
Pubkey::new_unique(), Pubkey::new_unique(),
load_program_account_from_elf(false, Some(authority_address), "noop"), load_program_account_from_elf(authority_address, LoaderV4Status::Retracted, "noop"),
), ),
( (
authority_address, authority_address,
@ -1115,7 +1118,7 @@ mod tests {
), ),
( (
Pubkey::new_unique(), Pubkey::new_unique(),
load_program_account_from_elf(true, Some(authority_address), "noop"), load_program_account_from_elf(authority_address, LoaderV4Status::Deployed, "noop"),
), ),
( (
clock::id(), clock::id(),
@ -1201,7 +1204,11 @@ mod tests {
let mut transaction_accounts = vec![ let mut transaction_accounts = vec![
( (
Pubkey::new_unique(), Pubkey::new_unique(),
load_program_account_from_elf(false, Some(authority_address), "rodata"), load_program_account_from_elf(
authority_address,
LoaderV4Status::Retracted,
"rodata",
),
), ),
( (
authority_address, authority_address,
@ -1209,7 +1216,7 @@ mod tests {
), ),
( (
Pubkey::new_unique(), Pubkey::new_unique(),
load_program_account_from_elf(false, Some(authority_address), "noop"), load_program_account_from_elf(authority_address, LoaderV4Status::Retracted, "noop"),
), ),
( (
Pubkey::new_unique(), Pubkey::new_unique(),
@ -1217,7 +1224,11 @@ mod tests {
), ),
( (
Pubkey::new_unique(), Pubkey::new_unique(),
load_program_account_from_elf(false, Some(authority_address), "invalid"), load_program_account_from_elf(
authority_address,
LoaderV4Status::Retracted,
"invalid",
),
), ),
(clock::id(), clock(1000)), (clock::id(), clock(1000)),
( (
@ -1337,7 +1348,11 @@ mod tests {
let mut transaction_accounts = vec![ let mut transaction_accounts = vec![
( (
Pubkey::new_unique(), Pubkey::new_unique(),
load_program_account_from_elf(true, Some(authority_address), "rodata"), load_program_account_from_elf(
authority_address,
LoaderV4Status::Deployed,
"rodata",
),
), ),
( (
authority_address, authority_address,
@ -1349,7 +1364,11 @@ mod tests {
), ),
( (
Pubkey::new_unique(), Pubkey::new_unique(),
load_program_account_from_elf(false, Some(authority_address), "rodata"), load_program_account_from_elf(
authority_address,
LoaderV4Status::Retracted,
"rodata",
),
), ),
(clock::id(), clock(1000)), (clock::id(), clock(1000)),
( (
@ -1409,7 +1428,23 @@ mod tests {
let transaction_accounts = vec![ let transaction_accounts = vec![
( (
Pubkey::new_unique(), Pubkey::new_unique(),
load_program_account_from_elf(true, Some(authority_address), "rodata"), load_program_account_from_elf(
authority_address,
LoaderV4Status::Deployed,
"rodata",
),
),
(
Pubkey::new_unique(),
load_program_account_from_elf(
authority_address,
LoaderV4Status::Retracted,
"rodata",
),
),
(
Pubkey::new_unique(),
AccountSharedData::new(0, 0, &loader_v4::id()),
), ),
( (
authority_address, authority_address,
@ -1419,10 +1454,6 @@ mod tests {
Pubkey::new_unique(), Pubkey::new_unique(),
AccountSharedData::new(0, 0, &Pubkey::new_unique()), AccountSharedData::new(0, 0, &Pubkey::new_unique()),
), ),
(
Pubkey::new_unique(),
AccountSharedData::new(0, 0, &loader_v4::id()),
),
( (
clock::id(), clock::id(),
create_account_shared_data_for_test(&clock::Clock::default()), create_account_shared_data_for_test(&clock::Clock::default()),
@ -1438,7 +1469,7 @@ mod tests {
vec![], vec![],
&bincode::serialize(&LoaderV4Instruction::TransferAuthority).unwrap(), &bincode::serialize(&LoaderV4Instruction::TransferAuthority).unwrap(),
transaction_accounts.clone(), transaction_accounts.clone(),
&[(0, false, true), (1, true, false), (2, true, false)], &[(0, false, true), (3, true, false), (4, true, false)],
Ok(()), Ok(()),
); );
assert_eq!( assert_eq!(
@ -1452,7 +1483,7 @@ mod tests {
vec![], vec![],
&bincode::serialize(&LoaderV4Instruction::TransferAuthority).unwrap(), &bincode::serialize(&LoaderV4Instruction::TransferAuthority).unwrap(),
transaction_accounts.clone(), transaction_accounts.clone(),
&[(0, false, true), (1, true, false)], &[(0, false, true), (3, true, false)],
Ok(()), Ok(()),
); );
assert_eq!( assert_eq!(
@ -1461,12 +1492,21 @@ mod tests {
); );
assert_eq!(accounts[0].lamports(), transaction_accounts[0].1.lamports()); assert_eq!(accounts[0].lamports(), transaction_accounts[0].1.lamports());
// Error: Program must be deployed to be finalized
process_instruction(
vec![],
&bincode::serialize(&LoaderV4Instruction::TransferAuthority).unwrap(),
transaction_accounts.clone(),
&[(1, false, true), (3, true, false)],
Err(InstructionError::InvalidArgument),
);
// Error: Program is uninitialized // Error: Program is uninitialized
process_instruction( process_instruction(
vec![], vec![],
&bincode::serialize(&LoaderV4Instruction::TransferAuthority).unwrap(), &bincode::serialize(&LoaderV4Instruction::TransferAuthority).unwrap(),
transaction_accounts.clone(), transaction_accounts.clone(),
&[(3, false, true), (1, true, false), (2, true, false)], &[(2, false, true), (3, true, false), (4, true, false)],
Err(InstructionError::InvalidAccountData), Err(InstructionError::InvalidAccountData),
); );
@ -1475,7 +1515,7 @@ mod tests {
vec![], vec![],
&bincode::serialize(&LoaderV4Instruction::TransferAuthority).unwrap(), &bincode::serialize(&LoaderV4Instruction::TransferAuthority).unwrap(),
transaction_accounts, transaction_accounts,
&[(0, false, true), (1, true, false), (2, false, false)], &[(0, false, true), (3, true, false), (4, false, false)],
Err(InstructionError::MissingRequiredSignature), Err(InstructionError::MissingRequiredSignature),
); );
@ -1485,10 +1525,15 @@ mod tests {
#[test] #[test]
fn test_execute_program() { fn test_execute_program() {
let program_address = Pubkey::new_unique(); let program_address = Pubkey::new_unique();
let authority_address = Pubkey::new_unique();
let transaction_accounts = vec![ let transaction_accounts = vec![
( (
program_address, program_address,
load_program_account_from_elf(true, None, "rodata"), load_program_account_from_elf(
authority_address,
LoaderV4Status::Finalized,
"rodata",
),
), ),
( (
Pubkey::new_unique(), Pubkey::new_unique(),
@ -1500,11 +1545,19 @@ mod tests {
), ),
( (
Pubkey::new_unique(), Pubkey::new_unique(),
load_program_account_from_elf(false, None, "rodata"), load_program_account_from_elf(
authority_address,
LoaderV4Status::Retracted,
"rodata",
),
), ),
( (
Pubkey::new_unique(), Pubkey::new_unique(),
load_program_account_from_elf(true, None, "invalid"), load_program_account_from_elf(
authority_address,
LoaderV4Status::Finalized,
"invalid",
),
), ),
]; ];

View File

@ -150,7 +150,7 @@ use {
inflation::Inflation, inflation::Inflation,
instruction::InstructionError, instruction::InstructionError,
lamports::LamportsError, lamports::LamportsError,
loader_v4::{self, LoaderV4State}, loader_v4::{self, LoaderV4State, LoaderV4Status},
message::{AccountKeys, SanitizedMessage}, message::{AccountKeys, SanitizedMessage},
native_loader, native_loader,
native_token::LAMPORTS_PER_SOL, native_token::LAMPORTS_PER_SOL,
@ -4621,7 +4621,9 @@ impl Bank {
if loader_v4::check_id(program_account.owner()) { if loader_v4::check_id(program_account.owner()) {
return solana_loader_v4_program::get_state(program_account.data()) return solana_loader_v4_program::get_state(program_account.data())
.ok() .ok()
.and_then(|state| state.is_deployed.then_some(state.slot)) .and_then(|state| {
(!matches!(state.status, LoaderV4Status::Retracted)).then_some(state.slot)
})
.map(|slot| ProgramAccountLoadResult::ProgramOfLoaderV4(program_account, slot)) .map(|slot| ProgramAccountLoadResult::ProgramOfLoaderV4(program_account, slot))
.unwrap_or(ProgramAccountLoadResult::InvalidV4Program); .unwrap_or(ProgramAccountLoadResult::InvalidV4Program);
} }

View File

@ -1,4 +1,4 @@
//! The v3 built-in loader program. //! The v4 built-in loader program.
//! //!
//! This is the loader of the program runtime v2. //! This is the loader of the program runtime v2.
@ -9,16 +9,27 @@ crate::declare_id!("LoaderV411111111111111111111111111111111111");
/// Cooldown before a program can be un-/redeployed again /// Cooldown before a program can be un-/redeployed again
pub const DEPLOYMENT_COOLDOWN_IN_SLOTS: u64 = 750; pub const DEPLOYMENT_COOLDOWN_IN_SLOTS: u64 = 750;
#[repr(u64)]
#[derive(Debug, PartialEq, Eq, Clone, Copy, AbiExample)]
pub enum LoaderV4Status {
/// Program is in maintanance
Retracted,
/// Program is ready to be executed
Deployed,
/// Same as `Deployed`, but can not be retracted anymore
Finalized,
}
/// LoaderV4 account states /// LoaderV4 account states
#[repr(C)] #[repr(C)]
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone, Copy, AbiExample)] #[derive(Debug, PartialEq, Eq, Clone, Copy, AbiExample)]
pub struct LoaderV4State { pub struct LoaderV4State {
/// Slot that the program was last initialized, deployed or retracted in. /// Slot in which the program was last deployed, retracted or initialized.
pub slot: u64, pub slot: u64,
/// True if the program is ready to be executed, false if it is retracted for maintainance. /// Address of signer which can send program management instructions.
pub is_deployed: bool, pub authority_address: Pubkey,
/// Authority address, `None` means it is finalized. /// Deployment status.
pub authority_address: Option<Pubkey>, pub status: LoaderV4Status,
// The raw program data follows this serialized structure in the // The raw program data follows this serialized structure in the
// account's data. // account's data.
} }
@ -29,3 +40,16 @@ impl LoaderV4State {
std::mem::size_of::<Self>() std::mem::size_of::<Self>()
} }
} }
#[cfg(test)]
mod tests {
use {super::*, memoffset::offset_of};
#[test]
fn test_layout() {
assert_eq!(offset_of!(LoaderV4State, slot), 0x00);
assert_eq!(offset_of!(LoaderV4State, authority_address), 0x08);
assert_eq!(offset_of!(LoaderV4State, status), 0x28);
assert_eq!(LoaderV4State::program_data_offset(), 0x30);
}
}

View File

@ -1,4 +1,4 @@
//! Instructions for the SBF loader. //! Instructions for the v4 built-in loader program.
#[repr(u8)] #[repr(u8)]
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]