diff --git a/loader-v4-interface/src/instruction.rs b/loader-v4-interface/src/instruction.rs index 93b36b2a9..08b7a367a 100644 --- a/loader-v4-interface/src/instruction.rs +++ b/loader-v4-interface/src/instruction.rs @@ -6,13 +6,45 @@ use { solana_sdk_ids::loader_v4::id, }; -#[repr(u8)] +#[repr(u32)] #[cfg_attr( feature = "serde", derive(serde_derive::Deserialize, serde_derive::Serialize) )] #[derive(Debug, PartialEq, Eq, Clone)] pub enum LoaderV4Instruction { + /// Initialize a program account. + /// + /// # Account references + /// 0. `[writable]` The account to initialize as program account. + /// 1. `[signer]` The new authority of the program. + Initialize, + + /// Transfers the authority over a program account. + /// + /// # Account references + /// 0. `[writable]` The program account to change the authority of. + /// 1. `[signer]` The current authority of the program. + /// 2. `[signer]` The new authority of the program. + SetAuthority, + + /// Finalizes the program account, rendering it immutable. + /// + /// # Account references + /// 0. `[writable]` The program account to finalize. + /// 1. `[signer]` The current authority of the program. + Finalize, + + /// Changes the size of an undeployed program account. + /// + /// # Account references + /// 0. `[writable]` The program account to change the size of. + /// 1. `[signer]` The authority of the program. + SetAccountLength { + /// The new size after the operation. + new_size: u32, + }, + /// Write ELF data into an undeployed program account. /// /// # Account references @@ -41,24 +73,6 @@ pub enum LoaderV4Instruction { length: u32, }, - /// Changes the size of an undeployed program account. - /// - /// A program account is automatically initialized when its size is first increased. - /// In this initial truncate, this sets the authority needed for subsequent operations. - /// Decreasing to size zero closes the program account and resets it into an uninitialized state. - /// Closing the program requires a recipient account. - /// Providing additional lamports upfront might be necessary to reach rent exemption. - /// Superflous funds are transferred to the recipient account if provided. - /// - /// # Account references - /// 0. `[writable]` The program account to change the size of. - /// 1. `[signer]` The authority of the program. - /// 2. `[writable]` Optional, the recipient account. - SetProgramLength { - /// The new size after the operation. - new_size: u32, - }, - /// Verify the data of a program account to be a valid ELF. /// /// If this succeeds the program becomes executable, and is ready to use. @@ -71,7 +85,10 @@ pub enum LoaderV4Instruction { /// 0. `[writable]` The program account to deploy. /// 1. `[signer]` The authority of the program. /// 2. `[writable]` Optional, an undeployed source program account to take data and lamports from. - Deploy, + Deploy { + /// The executable length in bytes. + executable_length: u32, + }, /// Undo the deployment of a program account. /// @@ -83,88 +100,120 @@ pub enum LoaderV4Instruction { /// 1. `[signer]` The authority of the program. Retract, - /// Transfers the authority over a program account. + /// Withdraw lamports which are not needed for rent exemption. /// /// # Account references - /// 0. `[writable]` The program account to change the authority of. - /// 1. `[signer]` The current authority of the program. - /// 2. `[signer]` The new authority of the program. - TransferAuthority, + /// 0. `[writable]` The program account to withdraw from. + /// 1. `[signer]` The authority of the program. + /// 2. `[writable]` The recipient account. + WithdrawExcessLamports, - /// Finalizes the program account, rendering it immutable. + /// Erase the program account and withdraw all lamports. /// /// # Account references - /// 0. `[writable]` The program account to change the authority of. - /// 1. `[signer]` The current authority of the program. - /// 2. `[]` The next version of the program (can be itself). - Finalize, + /// 0. `[writable]` The program account to erase. + /// 1. `[signer]` The authority of the program. + /// 2. `[writable]` The recipient account. + EraseAndWithdrawAllLamports, } -pub fn is_write_instruction(instruction_data: &[u8]) -> bool { +pub fn is_initialize_instruction(instruction_data: &[u8]) -> bool { !instruction_data.is_empty() && 0 == instruction_data[0] } -pub fn is_copy_instruction(instruction_data: &[u8]) -> bool { +pub fn is_set_authority_instruction(instruction_data: &[u8]) -> bool { !instruction_data.is_empty() && 1 == instruction_data[0] } -pub fn is_set_program_length_instruction(instruction_data: &[u8]) -> bool { +pub fn is_finalize_instruction(instruction_data: &[u8]) -> bool { !instruction_data.is_empty() && 2 == instruction_data[0] } -pub fn is_deploy_instruction(instruction_data: &[u8]) -> bool { +pub fn is_set_account_length_instruction(instruction_data: &[u8]) -> bool { !instruction_data.is_empty() && 3 == instruction_data[0] } -pub fn is_retract_instruction(instruction_data: &[u8]) -> bool { +pub fn is_write_instruction(instruction_data: &[u8]) -> bool { !instruction_data.is_empty() && 4 == instruction_data[0] } -pub fn is_transfer_authority_instruction(instruction_data: &[u8]) -> bool { +pub fn is_copy_instruction(instruction_data: &[u8]) -> bool { !instruction_data.is_empty() && 5 == instruction_data[0] } -pub fn is_finalize_instruction(instruction_data: &[u8]) -> bool { +pub fn is_deploy_instruction(instruction_data: &[u8]) -> bool { !instruction_data.is_empty() && 6 == instruction_data[0] } -/// Returns the instructions required to initialize a program/buffer account. +pub fn is_retract_instruction(instruction_data: &[u8]) -> bool { + !instruction_data.is_empty() && 7 == instruction_data[0] +} + +pub fn is_withdraw_excess_lamports_instruction(instruction_data: &[u8]) -> bool { + !instruction_data.is_empty() && 8 == instruction_data[0] +} + +pub fn is_erase_and_withdraw_all_lamports_instruction(instruction_data: &[u8]) -> bool { + !instruction_data.is_empty() && 9 == instruction_data[0] +} + +/// Returns the instruction required to initialize a program account. #[cfg(feature = "bincode")] -pub fn create_buffer( - payer_address: &Pubkey, - buffer_address: &Pubkey, - lamports: u64, +pub fn initialize(program_address: &Pubkey, authority: &Pubkey) -> Instruction { + Instruction::new_with_bincode( + id(), + &LoaderV4Instruction::Initialize, + vec![ + AccountMeta::new(*program_address, false), + AccountMeta::new_readonly(*authority, true), + ], + ) +} + +/// Returns the instruction required to set the authority of a program account. +#[cfg(feature = "bincode")] +pub fn set_authority( + program_address: &Pubkey, authority: &Pubkey, - new_size: u32, - recipient_address: &Pubkey, -) -> Vec { - vec![ - solana_system_interface::instruction::create_account( - payer_address, - buffer_address, - lamports, - 0, - &id(), - ), - set_program_length(buffer_address, authority, new_size, recipient_address), - ] + new_authority: &Pubkey, +) -> Instruction { + Instruction::new_with_bincode( + id(), + &LoaderV4Instruction::SetAuthority, + vec![ + AccountMeta::new(*program_address, false), + AccountMeta::new_readonly(*authority, true), + AccountMeta::new_readonly(*new_authority, true), + ], + ) +} + +/// Returns the instruction required to finalize a program. +#[cfg(feature = "bincode")] +pub fn finalize(program_address: &Pubkey, authority: &Pubkey) -> Instruction { + Instruction::new_with_bincode( + id(), + &LoaderV4Instruction::Finalize, + vec![ + AccountMeta::new(*program_address, false), + AccountMeta::new_readonly(*authority, true), + ], + ) } -/// Returns the instructions required to set the length of the program account. +/// Returns the instruction required to set the length of the program account. #[cfg(feature = "bincode")] -pub fn set_program_length( +pub fn set_account_length( program_address: &Pubkey, authority: &Pubkey, new_size: u32, - recipient_address: &Pubkey, ) -> Instruction { Instruction::new_with_bincode( id(), - &LoaderV4Instruction::SetProgramLength { new_size }, + &LoaderV4Instruction::SetAccountLength { new_size }, vec![ AccountMeta::new(*program_address, false), AccountMeta::new_readonly(*authority, true), - AccountMeta::new(*recipient_address, false), ], ) } @@ -213,12 +262,12 @@ pub fn copy( ) } -/// Returns the instructions required to deploy a program. +/// Returns the instruction required to deploy a program. #[cfg(feature = "bincode")] -pub fn deploy(program_address: &Pubkey, authority: &Pubkey) -> Instruction { +pub fn deploy(program_address: &Pubkey, authority: &Pubkey, executable_length: u32) -> Instruction { Instruction::new_with_bincode( id(), - &LoaderV4Instruction::Deploy, + &LoaderV4Instruction::Deploy { executable_length }, vec![ AccountMeta::new(*program_address, false), AccountMeta::new_readonly(*authority, true), @@ -226,16 +275,17 @@ pub fn deploy(program_address: &Pubkey, authority: &Pubkey) -> Instruction { ) } -/// Returns the instructions required to deploy a program using a buffer. +/// Returns the instruction required to deploy a program using a buffer. #[cfg(feature = "bincode")] pub fn deploy_from_source( program_address: &Pubkey, authority: &Pubkey, source_address: &Pubkey, + executable_length: u32, ) -> Instruction { Instruction::new_with_bincode( id(), - &LoaderV4Instruction::Deploy, + &LoaderV4Instruction::Deploy { executable_length }, vec![ AccountMeta::new(*program_address, false), AccountMeta::new_readonly(*authority, true), @@ -257,36 +307,62 @@ pub fn retract(program_address: &Pubkey, authority: &Pubkey) -> Instruction { ) } -/// Returns the instructions required to transfer authority over a program. +/// Returns the instruction required to withdraw excess lamports. #[cfg(feature = "bincode")] -pub fn transfer_authority( +pub fn withdraw_excess_lamports( program_address: &Pubkey, authority: &Pubkey, - new_authority: &Pubkey, + recipient: &Pubkey, ) -> Instruction { - let accounts = vec![ - AccountMeta::new(*program_address, false), - AccountMeta::new_readonly(*authority, true), - AccountMeta::new_readonly(*new_authority, true), - ]; - - Instruction::new_with_bincode(id(), &LoaderV4Instruction::TransferAuthority, accounts) + Instruction::new_with_bincode( + id(), + &LoaderV4Instruction::WithdrawExcessLamports, + vec![ + AccountMeta::new(*program_address, false), + AccountMeta::new_readonly(*authority, true), + AccountMeta::new(*recipient, false), + ], + ) } -/// Returns the instructions required to finalize program. +/// Returns the instruction required to erase and withdraw all lamports. #[cfg(feature = "bincode")] -pub fn finalize( +pub fn erase_and_withdraw_all_lamports( program_address: &Pubkey, authority: &Pubkey, - next_version_program_address: &Pubkey, + recipient: &Pubkey, ) -> Instruction { - let accounts = vec![ - AccountMeta::new(*program_address, false), - AccountMeta::new_readonly(*authority, true), - AccountMeta::new_readonly(*next_version_program_address, false), - ]; + Instruction::new_with_bincode( + id(), + &LoaderV4Instruction::EraseAndWithdrawAllLamports, + vec![ + AccountMeta::new(*program_address, false), + AccountMeta::new_readonly(*authority, true), + AccountMeta::new(*recipient, false), + ], + ) +} - Instruction::new_with_bincode(id(), &LoaderV4Instruction::Finalize, accounts) +/// Returns the instructions required to create a program/buffer account. +#[cfg(feature = "bincode")] +pub fn create_buffer( + payer_address: &Pubkey, + buffer_address: &Pubkey, + lamports: u64, + authority: &Pubkey, + new_size: u32, +) -> Vec { + vec![ + solana_system_interface::instruction::create_account( + payer_address, + buffer_address, + lamports, + 0, + &id(), + ), + initialize(buffer_address, authority), + set_account_length(buffer_address, authority, new_size), + ] } #[cfg(test)] @@ -294,44 +370,47 @@ mod tests { use {super::*, solana_sdk_ids::system_program}; #[test] - fn test_create_buffer_instruction() { - let payer = Pubkey::new_unique(); + fn test_initialize_instruction() { let program = Pubkey::new_unique(); let authority = Pubkey::new_unique(); - let recipient = Pubkey::new_unique(); - let instructions = create_buffer(&payer, &program, 123, &authority, 10, &recipient); - assert_eq!(instructions.len(), 2); - let instruction0 = &instructions[0]; - assert_eq!(instruction0.program_id, system_program::id()); - assert_eq!(instruction0.accounts.len(), 2); - assert_eq!(instruction0.accounts[0].pubkey, payer); - assert!(instruction0.accounts[0].is_writable); - assert!(instruction0.accounts[0].is_signer); - assert_eq!(instruction0.accounts[1].pubkey, program); - assert!(instruction0.accounts[1].is_writable); - assert!(instruction0.accounts[1].is_signer); + let instruction = initialize(&program, &authority); + assert!(is_initialize_instruction(&instruction.data)); + assert_eq!(instruction.program_id, id()); + assert_eq!(instruction.accounts.len(), 2); + assert_eq!(instruction.accounts[0].pubkey, program); + assert!(instruction.accounts[0].is_writable); + assert!(!instruction.accounts[0].is_signer); + assert_eq!(instruction.accounts[1].pubkey, authority); + assert!(!instruction.accounts[1].is_writable); + assert!(instruction.accounts[1].is_signer); + } - let instruction1 = &instructions[1]; - assert!(is_set_program_length_instruction(&instruction1.data)); - assert_eq!(instruction1.program_id, id()); - assert_eq!(instruction1.accounts.len(), 3); - assert_eq!(instruction1.accounts[0].pubkey, program); - assert!(instruction1.accounts[0].is_writable); - assert!(!instruction1.accounts[0].is_signer); - assert_eq!(instruction1.accounts[1].pubkey, authority); - assert!(!instruction1.accounts[1].is_writable); - assert!(instruction1.accounts[1].is_signer); - assert_eq!(instruction1.accounts[2].pubkey, recipient); - assert!(instruction1.accounts[2].is_writable); - assert!(!instruction1.accounts[2].is_signer); + #[test] + fn test_set_authority_instruction() { + let program = Pubkey::new_unique(); + let authority = Pubkey::new_unique(); + let new_authority = Pubkey::new_unique(); + let instruction = set_authority(&program, &authority, &new_authority); + assert!(is_set_authority_instruction(&instruction.data)); + assert_eq!(instruction.program_id, id()); + assert_eq!(instruction.accounts.len(), 3); + assert_eq!(instruction.accounts[0].pubkey, program); + assert!(instruction.accounts[0].is_writable); + assert!(!instruction.accounts[0].is_signer); + assert_eq!(instruction.accounts[1].pubkey, authority); + assert!(!instruction.accounts[1].is_writable); + assert!(instruction.accounts[1].is_signer); + assert_eq!(instruction.accounts[2].pubkey, new_authority); + assert!(!instruction.accounts[2].is_writable); + assert!(instruction.accounts[2].is_signer); } #[test] - fn test_write_instruction() { + fn test_finalize_instruction() { let program = Pubkey::new_unique(); let authority = Pubkey::new_unique(); - let instruction = write(&program, &authority, 123, vec![1, 2, 3, 4]); - assert!(is_write_instruction(&instruction.data)); + let instruction = finalize(&program, &authority); + assert!(is_finalize_instruction(&instruction.data)); assert_eq!(instruction.program_id, id()); assert_eq!(instruction.accounts.len(), 2); assert_eq!(instruction.accounts[0].pubkey, program); @@ -343,32 +422,44 @@ mod tests { } #[test] - fn test_copy_instruction() { + fn test_set_account_length_instruction() { let program = Pubkey::new_unique(); let authority = Pubkey::new_unique(); - let source = Pubkey::new_unique(); - let instruction = copy(&program, &authority, &source, 1, 2, 3); - assert!(is_copy_instruction(&instruction.data)); + let instruction = set_account_length(&program, &authority, 10); + assert!(is_set_account_length_instruction(&instruction.data)); assert_eq!(instruction.program_id, id()); - assert_eq!(instruction.accounts.len(), 3); + assert_eq!(instruction.accounts.len(), 2); assert_eq!(instruction.accounts[0].pubkey, program); assert!(instruction.accounts[0].is_writable); assert!(!instruction.accounts[0].is_signer); assert_eq!(instruction.accounts[1].pubkey, authority); assert!(!instruction.accounts[1].is_writable); assert!(instruction.accounts[1].is_signer); - assert_eq!(instruction.accounts[2].pubkey, source); - assert!(!instruction.accounts[2].is_writable); - assert!(!instruction.accounts[2].is_signer); } #[test] - fn test_set_program_length_instruction() { + fn test_write_instruction() { let program = Pubkey::new_unique(); let authority = Pubkey::new_unique(); - let recipient = Pubkey::new_unique(); - let instruction = set_program_length(&program, &authority, 10, &recipient); - assert!(is_set_program_length_instruction(&instruction.data)); + let instruction = write(&program, &authority, 123, vec![1, 2, 3, 4]); + assert!(is_write_instruction(&instruction.data)); + assert_eq!(instruction.program_id, id()); + assert_eq!(instruction.accounts.len(), 2); + assert_eq!(instruction.accounts[0].pubkey, program); + assert!(instruction.accounts[0].is_writable); + assert!(!instruction.accounts[0].is_signer); + assert_eq!(instruction.accounts[1].pubkey, authority); + assert!(!instruction.accounts[1].is_writable); + assert!(instruction.accounts[1].is_signer); + } + + #[test] + fn test_copy_instruction() { + let program = Pubkey::new_unique(); + let authority = Pubkey::new_unique(); + let source = Pubkey::new_unique(); + let instruction = copy(&program, &authority, &source, 1, 2, 3); + assert!(is_copy_instruction(&instruction.data)); assert_eq!(instruction.program_id, id()); assert_eq!(instruction.accounts.len(), 3); assert_eq!(instruction.accounts[0].pubkey, program); @@ -377,8 +468,8 @@ mod tests { assert_eq!(instruction.accounts[1].pubkey, authority); assert!(!instruction.accounts[1].is_writable); assert!(instruction.accounts[1].is_signer); - assert_eq!(instruction.accounts[2].pubkey, recipient); - assert!(instruction.accounts[2].is_writable); + assert_eq!(instruction.accounts[2].pubkey, source); + assert!(!instruction.accounts[2].is_writable); assert!(!instruction.accounts[2].is_signer); } @@ -386,7 +477,7 @@ mod tests { fn test_deploy_instruction() { let program = Pubkey::new_unique(); let authority = Pubkey::new_unique(); - let instruction = deploy(&program, &authority); + let instruction = deploy(&program, &authority, 1024); assert!(is_deploy_instruction(&instruction.data)); assert_eq!(instruction.program_id, id()); assert_eq!(instruction.accounts.len(), 2); @@ -403,7 +494,7 @@ mod tests { let program = Pubkey::new_unique(); let authority = Pubkey::new_unique(); let source = Pubkey::new_unique(); - let instruction = deploy_from_source(&program, &authority, &source); + let instruction = deploy_from_source(&program, &authority, &source, 1024); assert!(is_deploy_instruction(&instruction.data)); assert_eq!(instruction.program_id, id()); assert_eq!(instruction.accounts.len(), 3); @@ -435,12 +526,12 @@ mod tests { } #[test] - fn test_transfer_authority_instruction() { + fn test_withdraw_excess_lamports_instruction() { let program = Pubkey::new_unique(); let authority = Pubkey::new_unique(); - let new_authority = Pubkey::new_unique(); - let instruction = transfer_authority(&program, &authority, &new_authority); - assert!(is_transfer_authority_instruction(&instruction.data)); + let recipient = Pubkey::new_unique(); + let instruction = withdraw_excess_lamports(&program, &authority, &recipient); + assert!(is_withdraw_excess_lamports_instruction(&instruction.data)); assert_eq!(instruction.program_id, id()); assert_eq!(instruction.accounts.len(), 3); assert_eq!(instruction.accounts[0].pubkey, program); @@ -449,18 +540,20 @@ mod tests { assert_eq!(instruction.accounts[1].pubkey, authority); assert!(!instruction.accounts[1].is_writable); assert!(instruction.accounts[1].is_signer); - assert_eq!(instruction.accounts[2].pubkey, new_authority); - assert!(!instruction.accounts[2].is_writable); - assert!(instruction.accounts[2].is_signer); + assert_eq!(instruction.accounts[2].pubkey, recipient); + assert!(instruction.accounts[2].is_writable); + assert!(!instruction.accounts[2].is_signer); } #[test] - fn test_transfer_authority_finalize_instruction() { + fn test_erase_and_withdraw_all_lamports_instruction() { let program = Pubkey::new_unique(); let authority = Pubkey::new_unique(); - let next_version = Pubkey::new_unique(); - let instruction = finalize(&program, &authority, &next_version); - assert!(is_finalize_instruction(&instruction.data)); + let recipient = Pubkey::new_unique(); + let instruction = erase_and_withdraw_all_lamports(&program, &authority, &recipient); + assert!(is_erase_and_withdraw_all_lamports_instruction( + &instruction.data + )); assert_eq!(instruction.program_id, id()); assert_eq!(instruction.accounts.len(), 3); assert_eq!(instruction.accounts[0].pubkey, program); @@ -469,8 +562,35 @@ mod tests { assert_eq!(instruction.accounts[1].pubkey, authority); assert!(!instruction.accounts[1].is_writable); assert!(instruction.accounts[1].is_signer); - assert_eq!(instruction.accounts[2].pubkey, next_version); - assert!(!instruction.accounts[2].is_writable); + assert_eq!(instruction.accounts[2].pubkey, recipient); + assert!(instruction.accounts[2].is_writable); assert!(!instruction.accounts[2].is_signer); } + + #[test] + fn test_create_buffer_instruction() { + let payer = Pubkey::new_unique(); + let program = Pubkey::new_unique(); + let authority = Pubkey::new_unique(); + let instructions = create_buffer(&payer, &program, 123, &authority, 10); + assert_eq!(instructions.len(), 3); + + let instruction0 = &instructions[0]; + assert_eq!(instruction0.program_id, system_program::id()); + assert_eq!(instruction0.accounts.len(), 2); + assert_eq!(instruction0.accounts[0].pubkey, payer); + assert!(instruction0.accounts[0].is_writable); + assert!(instruction0.accounts[0].is_signer); + assert_eq!(instruction0.accounts[1].pubkey, program); + assert!(instruction0.accounts[1].is_writable); + assert!(instruction0.accounts[1].is_signer); + + let instruction1 = &instructions[1]; + assert!(is_initialize_instruction(&instruction1.data)); + assert_eq!(instruction1.program_id, id()); + + let instruction2 = &instructions[2]; + assert!(is_set_account_length_instruction(&instruction2.data)); + assert_eq!(instruction2.program_id, id()); + } }