Skip to content
This repository has been archived by the owner on Jan 13, 2025. It is now read-only.

Add loader-v4 instruction constructors #33151

Merged
merged 3 commits into from
Sep 6, 2023
Merged
Changes from all commits
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
304 changes: 302 additions & 2 deletions 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,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<Instruction> {
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),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

recipient_address is not needed and should be payer_address here as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought about it. The caller can pass the payer address as the recipient_address.

],
),
]
}

/// 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<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 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() {
Expand All @@ -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);
}
}