diff --git a/sdk/program/src/loader_v4.rs b/sdk/program/src/loader_v4.rs index c1728cf1e078c7..9180c00a718243 100644 --- a/sdk/program/src/loader_v4.rs +++ b/sdk/program/src/loader_v4.rs @@ -2,7 +2,12 @@ //! //! This is the loader of the program runtime v2. -use crate::pubkey::Pubkey; +use crate::{ + instruction::{AccountMeta, Instruction}, + loader_v4_instruction::LoaderV4Instruction, + pubkey::Pubkey, + system_instruction, +}; crate::declare_id!("LoaderV411111111111111111111111111111111111"); @@ -41,9 +46,147 @@ impl LoaderV4State { } } +pub fn is_write_instruction(instruction_data: &[u8]) -> bool { + !instruction_data.is_empty() && 0 == instruction_data[0] +} + +pub fn is_truncate_instruction(instruction_data: &[u8]) -> bool { + !instruction_data.is_empty() && 1 == instruction_data[0] +} + +pub fn is_deploy_instruction(instruction_data: &[u8]) -> bool { + !instruction_data.is_empty() && 2 == instruction_data[0] +} + +pub fn is_retract_instruction(instruction_data: &[u8]) -> bool { + !instruction_data.is_empty() && 3 == instruction_data[0] +} + +pub fn is_transfer_authority_instruction(instruction_data: &[u8]) -> bool { + !instruction_data.is_empty() && 4 == instruction_data[0] +} + +/// Returns the instructions required to initialize a program/buffer account. +pub fn create_buffer( + payer_address: &Pubkey, + buffer_address: &Pubkey, + lamports: u64, + authority: &Pubkey, + new_size: u32, + recipient_address: &Pubkey, +) -> Vec { + vec![ + system_instruction::create_account(payer_address, buffer_address, lamports, 0, &id()), + Instruction::new_with_bincode( + id(), + &LoaderV4Instruction::Truncate { new_size }, + vec![ + AccountMeta::new(*buffer_address, true), + AccountMeta::new_readonly(*authority, true), + AccountMeta::new(*recipient_address, false), + ], + ), + ] +} + +/// Returns the instructions required to set the length of the program account. +pub fn truncate( + program_address: &Pubkey, + authority: &Pubkey, + new_size: u32, + recipient_address: &Pubkey, +) -> Instruction { + Instruction::new_with_bincode( + id(), + &LoaderV4Instruction::Truncate { new_size }, + vec![ + AccountMeta::new(*program_address, false), + AccountMeta::new_readonly(*authority, true), + AccountMeta::new(*recipient_address, false), + ], + ) +} + +/// Returns the instructions required to write a chunk of program data to a +/// buffer account. +pub fn write( + program_address: &Pubkey, + authority: &Pubkey, + offset: u32, + bytes: Vec, +) -> Instruction { + Instruction::new_with_bincode( + id(), + &LoaderV4Instruction::Write { offset, bytes }, + vec![ + AccountMeta::new(*program_address, false), + AccountMeta::new_readonly(*authority, true), + ], + ) +} + +/// Returns the instructions required to deploy a program. +pub fn deploy(program_address: &Pubkey, authority: &Pubkey) -> Instruction { + Instruction::new_with_bincode( + id(), + &LoaderV4Instruction::Deploy, + vec![ + AccountMeta::new(*program_address, false), + AccountMeta::new_readonly(*authority, true), + ], + ) +} + +/// Returns the instructions required to deploy a program using a buffer. +pub fn deploy_from_source( + program_address: &Pubkey, + authority: &Pubkey, + source_address: &Pubkey, +) -> Instruction { + Instruction::new_with_bincode( + id(), + &LoaderV4Instruction::Deploy, + vec![ + AccountMeta::new(*program_address, false), + AccountMeta::new_readonly(*authority, true), + AccountMeta::new(*source_address, false), + ], + ) +} + +/// Returns the instructions required to retract a program. +pub fn retract(program_address: &Pubkey, authority: &Pubkey) -> Instruction { + Instruction::new_with_bincode( + id(), + &LoaderV4Instruction::Retract, + vec![ + AccountMeta::new(*program_address, false), + AccountMeta::new_readonly(*authority, true), + ], + ) +} + +/// Returns the instructions required to transfer authority over a program. +pub fn transfer_authority( + program_address: &Pubkey, + authority: &Pubkey, + new_authority: Option<&Pubkey>, +) -> Instruction { + let mut accounts = vec![ + AccountMeta::new(*program_address, false), + AccountMeta::new_readonly(*authority, true), + ]; + + if let Some(new_auth) = new_authority { + accounts.push(AccountMeta::new_readonly(*new_auth, true)); + } + + Instruction::new_with_bincode(id(), &LoaderV4Instruction::TransferAuthority, accounts) +} + #[cfg(test)] mod tests { - use {super::*, memoffset::offset_of}; + use {super::*, crate::system_program, memoffset::offset_of}; #[test] fn test_layout() { @@ -52,4 +195,161 @@ mod tests { assert_eq!(offset_of!(LoaderV4State, status), 0x28); assert_eq!(LoaderV4State::program_data_offset(), 0x30); } + + #[test] + fn test_create_buffer_instruction() { + let payer = Pubkey::new_unique(); + 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 instruction1 = &instructions[1]; + assert!(is_truncate_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_write_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)); + 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_truncate_instruction() { + let program = Pubkey::new_unique(); + let authority = Pubkey::new_unique(); + let recipient = Pubkey::new_unique(); + let instruction = truncate(&program, &authority, 10, &recipient); + assert!(is_truncate_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, recipient); + assert!(instruction.accounts[2].is_writable); + assert!(!instruction.accounts[2].is_signer); + } + + #[test] + fn test_deploy_instruction() { + let program = Pubkey::new_unique(); + let authority = Pubkey::new_unique(); + let instruction = deploy(&program, &authority); + assert!(is_deploy_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_deploy_from_source_instruction() { + let program = Pubkey::new_unique(); + let authority = Pubkey::new_unique(); + let source = Pubkey::new_unique(); + let instruction = deploy_from_source(&program, &authority, &source); + assert!(is_deploy_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, source); + assert!(instruction.accounts[2].is_writable); + assert!(!instruction.accounts[2].is_signer); + } + + #[test] + fn test_retract_instruction() { + let program = Pubkey::new_unique(); + let authority = Pubkey::new_unique(); + let instruction = retract(&program, &authority); + assert!(is_retract_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_transfer_authority_instruction() { + let program = Pubkey::new_unique(); + let authority = Pubkey::new_unique(); + let new_authority = Pubkey::new_unique(); + let instruction = transfer_authority(&program, &authority, Some(&new_authority)); + assert!(is_transfer_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_transfer_authority_finalize_instruction() { + let program = Pubkey::new_unique(); + let authority = Pubkey::new_unique(); + let instruction = transfer_authority(&program, &authority, None); + assert!(is_transfer_authority_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); + } }