Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add loader-v4 instruction constructors #33151

Merged
merged 3 commits into from
Sep 6, 2023
Merged
Changes from 1 commit
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
255 changes: 254 additions & 1 deletion sdk/program/src/loader_v4.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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");

Expand Down Expand Up @@ -41,6 +46,130 @@ 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,
) -> Instruction {
system_instruction::create_account(payer_address, buffer_address, lamports, 0, &id())
pgarg66 marked this conversation as resolved.
Show resolved Hide resolved
}

/// 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, true),
pgarg66 marked this conversation as resolved.
Show resolved Hide resolved
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<u8>,
) -> 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.
pgarg66 marked this conversation as resolved.
Show resolved Hide resolved
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};
Expand All @@ -52,4 +181,128 @@ mod tests {
assert_eq!(offset_of!(LoaderV4State, status), 0x28);
assert_eq!(LoaderV4State::program_data_offset(), 0x30);
}

#[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);
}
}