821 lines
24 KiB
Rust
821 lines
24 KiB
Rust
use {
|
|
assert_matches::assert_matches,
|
|
common::{add_upgradeable_loader_account, assert_ix_error, setup_test_context},
|
|
solana_program_test::*,
|
|
solana_sdk::{
|
|
account::{AccountSharedData, ReadableAccount, WritableAccount},
|
|
bpf_loader_upgradeable::{extend_program, id, UpgradeableLoaderState},
|
|
instruction::InstructionError,
|
|
pubkey::Pubkey,
|
|
signature::{Keypair, Signer},
|
|
system_instruction::{self, SystemError, MAX_PERMITTED_DATA_LENGTH},
|
|
system_program,
|
|
transaction::{Transaction, TransactionError},
|
|
},
|
|
};
|
|
|
|
mod common;
|
|
|
|
#[tokio::test]
|
|
async fn test_extend_program() {
|
|
let mut context = setup_test_context().await;
|
|
let program_file = find_file("noop.so").expect("Failed to find the file");
|
|
let data = read_file(program_file);
|
|
|
|
let program_address = Pubkey::new_unique();
|
|
let (programdata_address, _) = Pubkey::find_program_address(&[program_address.as_ref()], &id());
|
|
add_upgradeable_loader_account(
|
|
&mut context,
|
|
&program_address,
|
|
&UpgradeableLoaderState::Program {
|
|
programdata_address,
|
|
},
|
|
UpgradeableLoaderState::size_of_program(),
|
|
|_| {},
|
|
)
|
|
.await;
|
|
let programdata_data_offset = UpgradeableLoaderState::size_of_programdata_metadata();
|
|
let program_data_len = data.len() + programdata_data_offset;
|
|
add_upgradeable_loader_account(
|
|
&mut context,
|
|
&programdata_address,
|
|
&UpgradeableLoaderState::ProgramData {
|
|
slot: 0,
|
|
upgrade_authority_address: Some(Pubkey::new_unique()),
|
|
},
|
|
program_data_len,
|
|
|account| account.data_as_mut_slice()[programdata_data_offset..].copy_from_slice(&data),
|
|
)
|
|
.await;
|
|
|
|
let client = &mut context.banks_client;
|
|
let payer = &context.payer;
|
|
let recent_blockhash = context.last_blockhash;
|
|
const ADDITIONAL_BYTES: u32 = 42;
|
|
let transaction = Transaction::new_signed_with_payer(
|
|
&[extend_program(
|
|
&program_address,
|
|
Some(&payer.pubkey()),
|
|
ADDITIONAL_BYTES,
|
|
)],
|
|
Some(&payer.pubkey()),
|
|
&[payer],
|
|
recent_blockhash,
|
|
);
|
|
|
|
assert_matches!(client.process_transaction(transaction).await, Ok(()));
|
|
let updated_program_data_account = client
|
|
.get_account(programdata_address)
|
|
.await
|
|
.unwrap()
|
|
.unwrap();
|
|
assert_eq!(
|
|
updated_program_data_account.data().len(),
|
|
program_data_len + ADDITIONAL_BYTES as usize
|
|
);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_failed_extend_twice_in_same_slot() {
|
|
let mut context = setup_test_context().await;
|
|
let program_file = find_file("noop.so").expect("Failed to find the file");
|
|
let data = read_file(program_file);
|
|
|
|
let program_address = Pubkey::new_unique();
|
|
let (programdata_address, _) = Pubkey::find_program_address(&[program_address.as_ref()], &id());
|
|
add_upgradeable_loader_account(
|
|
&mut context,
|
|
&program_address,
|
|
&UpgradeableLoaderState::Program {
|
|
programdata_address,
|
|
},
|
|
UpgradeableLoaderState::size_of_program(),
|
|
|_| {},
|
|
)
|
|
.await;
|
|
let programdata_data_offset = UpgradeableLoaderState::size_of_programdata_metadata();
|
|
let program_data_len = data.len() + programdata_data_offset;
|
|
add_upgradeable_loader_account(
|
|
&mut context,
|
|
&programdata_address,
|
|
&UpgradeableLoaderState::ProgramData {
|
|
slot: 0,
|
|
upgrade_authority_address: Some(Pubkey::new_unique()),
|
|
},
|
|
program_data_len,
|
|
|account| account.data_as_mut_slice()[programdata_data_offset..].copy_from_slice(&data),
|
|
)
|
|
.await;
|
|
|
|
let client = &mut context.banks_client;
|
|
let payer = &context.payer;
|
|
let recent_blockhash = context.last_blockhash;
|
|
const ADDITIONAL_BYTES: u32 = 42;
|
|
let transaction = Transaction::new_signed_with_payer(
|
|
&[extend_program(
|
|
&program_address,
|
|
Some(&payer.pubkey()),
|
|
ADDITIONAL_BYTES,
|
|
)],
|
|
Some(&payer.pubkey()),
|
|
&[payer],
|
|
recent_blockhash,
|
|
);
|
|
|
|
assert_matches!(client.process_transaction(transaction).await, Ok(()));
|
|
let updated_program_data_account = client
|
|
.get_account(programdata_address)
|
|
.await
|
|
.unwrap()
|
|
.unwrap();
|
|
assert_eq!(
|
|
updated_program_data_account.data().len(),
|
|
program_data_len + ADDITIONAL_BYTES as usize
|
|
);
|
|
|
|
let recent_blockhash = client
|
|
.get_new_latest_blockhash(&recent_blockhash)
|
|
.await
|
|
.unwrap();
|
|
// Extending the program in the same slot should fail
|
|
let transaction = Transaction::new_signed_with_payer(
|
|
&[extend_program(
|
|
&program_address,
|
|
Some(&payer.pubkey()),
|
|
ADDITIONAL_BYTES,
|
|
)],
|
|
Some(&payer.pubkey()),
|
|
&[payer],
|
|
recent_blockhash,
|
|
);
|
|
|
|
assert_matches!(
|
|
client
|
|
.process_transaction(transaction)
|
|
.await
|
|
.unwrap_err()
|
|
.unwrap(),
|
|
TransactionError::InstructionError(0, InstructionError::InvalidArgument)
|
|
);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_extend_program_not_upgradeable() {
|
|
let mut context = setup_test_context().await;
|
|
|
|
let program_address = Pubkey::new_unique();
|
|
let (programdata_address, _) = Pubkey::find_program_address(&[program_address.as_ref()], &id());
|
|
add_upgradeable_loader_account(
|
|
&mut context,
|
|
&program_address,
|
|
&UpgradeableLoaderState::Program {
|
|
programdata_address,
|
|
},
|
|
UpgradeableLoaderState::size_of_program(),
|
|
|_| {},
|
|
)
|
|
.await;
|
|
add_upgradeable_loader_account(
|
|
&mut context,
|
|
&programdata_address,
|
|
&UpgradeableLoaderState::ProgramData {
|
|
slot: 0,
|
|
upgrade_authority_address: None,
|
|
},
|
|
100,
|
|
|_| {},
|
|
)
|
|
.await;
|
|
|
|
let payer_address = context.payer.pubkey();
|
|
assert_ix_error(
|
|
&mut context,
|
|
extend_program(&program_address, Some(&payer_address), 42),
|
|
None,
|
|
InstructionError::Immutable,
|
|
"should fail because the program data account isn't upgradeable",
|
|
)
|
|
.await;
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_extend_program_by_zero_bytes() {
|
|
let mut context = setup_test_context().await;
|
|
|
|
let program_address = Pubkey::new_unique();
|
|
let (programdata_address, _) = Pubkey::find_program_address(&[program_address.as_ref()], &id());
|
|
add_upgradeable_loader_account(
|
|
&mut context,
|
|
&program_address,
|
|
&UpgradeableLoaderState::Program {
|
|
programdata_address,
|
|
},
|
|
UpgradeableLoaderState::size_of_program(),
|
|
|_| {},
|
|
)
|
|
.await;
|
|
add_upgradeable_loader_account(
|
|
&mut context,
|
|
&programdata_address,
|
|
&UpgradeableLoaderState::ProgramData {
|
|
slot: 0,
|
|
upgrade_authority_address: Some(Pubkey::new_unique()),
|
|
},
|
|
100,
|
|
|_| {},
|
|
)
|
|
.await;
|
|
|
|
let payer_address = context.payer.pubkey();
|
|
assert_ix_error(
|
|
&mut context,
|
|
extend_program(&program_address, Some(&payer_address), 0),
|
|
None,
|
|
InstructionError::InvalidInstructionData,
|
|
"should fail because the program data account must be extended by more than 0 bytes",
|
|
)
|
|
.await;
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_extend_program_past_max_size() {
|
|
let mut context = setup_test_context().await;
|
|
|
|
let program_address = Pubkey::new_unique();
|
|
let (programdata_address, _) = Pubkey::find_program_address(&[program_address.as_ref()], &id());
|
|
add_upgradeable_loader_account(
|
|
&mut context,
|
|
&program_address,
|
|
&UpgradeableLoaderState::Program {
|
|
programdata_address,
|
|
},
|
|
UpgradeableLoaderState::size_of_program(),
|
|
|_| {},
|
|
)
|
|
.await;
|
|
add_upgradeable_loader_account(
|
|
&mut context,
|
|
&programdata_address,
|
|
&UpgradeableLoaderState::ProgramData {
|
|
slot: 0,
|
|
upgrade_authority_address: Some(Pubkey::new_unique()),
|
|
},
|
|
MAX_PERMITTED_DATA_LENGTH as usize,
|
|
|_| {},
|
|
)
|
|
.await;
|
|
|
|
let payer_address = context.payer.pubkey();
|
|
assert_ix_error(
|
|
&mut context,
|
|
extend_program(&program_address, Some(&payer_address), 1),
|
|
None,
|
|
InstructionError::InvalidRealloc,
|
|
"should fail because the program data account cannot be extended past the max data size",
|
|
)
|
|
.await;
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_extend_program_with_invalid_payer() {
|
|
let mut context = setup_test_context().await;
|
|
let rent = context.banks_client.get_rent().await.unwrap();
|
|
|
|
let program_address = Pubkey::new_unique();
|
|
let (programdata_address, _) = Pubkey::find_program_address(&[program_address.as_ref()], &id());
|
|
add_upgradeable_loader_account(
|
|
&mut context,
|
|
&program_address,
|
|
&UpgradeableLoaderState::Program {
|
|
programdata_address,
|
|
},
|
|
UpgradeableLoaderState::size_of_program(),
|
|
|_| {},
|
|
)
|
|
.await;
|
|
add_upgradeable_loader_account(
|
|
&mut context,
|
|
&programdata_address,
|
|
&UpgradeableLoaderState::ProgramData {
|
|
slot: 0,
|
|
upgrade_authority_address: Some(Pubkey::new_unique()),
|
|
},
|
|
100,
|
|
|_| {},
|
|
)
|
|
.await;
|
|
|
|
let payer_with_sufficient_funds = Keypair::new();
|
|
context.set_account(
|
|
&payer_with_sufficient_funds.pubkey(),
|
|
&AccountSharedData::new(10_000_000_000, 0, &system_program::id()),
|
|
);
|
|
|
|
let payer_with_insufficient_funds = Keypair::new();
|
|
context.set_account(
|
|
&payer_with_insufficient_funds.pubkey(),
|
|
&AccountSharedData::new(rent.minimum_balance(0), 0, &system_program::id()),
|
|
);
|
|
|
|
let payer_with_invalid_owner = Keypair::new();
|
|
context.set_account(
|
|
&payer_with_invalid_owner.pubkey(),
|
|
&AccountSharedData::new(rent.minimum_balance(0), 0, &id()),
|
|
);
|
|
|
|
assert_ix_error(
|
|
&mut context,
|
|
extend_program(
|
|
&program_address,
|
|
Some(&payer_with_insufficient_funds.pubkey()),
|
|
1024,
|
|
),
|
|
Some(&payer_with_insufficient_funds),
|
|
InstructionError::from(SystemError::ResultWithNegativeLamports),
|
|
"should fail because the payer has insufficient funds to cover program data account rent",
|
|
)
|
|
.await;
|
|
|
|
assert_ix_error(
|
|
&mut context,
|
|
extend_program(
|
|
&program_address,
|
|
Some(&payer_with_invalid_owner.pubkey()),
|
|
1,
|
|
),
|
|
Some(&payer_with_invalid_owner),
|
|
InstructionError::ExternalAccountLamportSpend,
|
|
"should fail because the payer is not a system account",
|
|
)
|
|
.await;
|
|
|
|
let mut ix = extend_program(
|
|
&program_address,
|
|
Some(&payer_with_sufficient_funds.pubkey()),
|
|
1,
|
|
);
|
|
|
|
// Demote payer account meta to non-signer so that transaction signing succeeds
|
|
{
|
|
let payer_meta = ix
|
|
.accounts
|
|
.iter_mut()
|
|
.find(|meta| meta.pubkey == payer_with_sufficient_funds.pubkey())
|
|
.expect("expected to find payer account meta");
|
|
payer_meta.is_signer = false;
|
|
}
|
|
|
|
assert_ix_error(
|
|
&mut context,
|
|
ix,
|
|
None,
|
|
InstructionError::PrivilegeEscalation,
|
|
"should fail because the payer did not sign",
|
|
)
|
|
.await;
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_extend_program_without_payer() {
|
|
let mut context = setup_test_context().await;
|
|
let rent = context.banks_client.get_rent().await.unwrap();
|
|
|
|
let program_file = find_file("noop.so").expect("Failed to find the file");
|
|
let data = read_file(program_file);
|
|
|
|
let program_address = Pubkey::new_unique();
|
|
let (programdata_address, _) = Pubkey::find_program_address(&[program_address.as_ref()], &id());
|
|
add_upgradeable_loader_account(
|
|
&mut context,
|
|
&program_address,
|
|
&UpgradeableLoaderState::Program {
|
|
programdata_address,
|
|
},
|
|
UpgradeableLoaderState::size_of_program(),
|
|
|_| {},
|
|
)
|
|
.await;
|
|
let programdata_data_offset = UpgradeableLoaderState::size_of_programdata_metadata();
|
|
let program_data_len = data.len() + programdata_data_offset;
|
|
add_upgradeable_loader_account(
|
|
&mut context,
|
|
&programdata_address,
|
|
&UpgradeableLoaderState::ProgramData {
|
|
slot: 0,
|
|
upgrade_authority_address: Some(Pubkey::new_unique()),
|
|
},
|
|
program_data_len,
|
|
|account| account.data_as_mut_slice()[programdata_data_offset..].copy_from_slice(&data),
|
|
)
|
|
.await;
|
|
|
|
assert_ix_error(
|
|
&mut context,
|
|
extend_program(&program_address, None, 1024),
|
|
None,
|
|
InstructionError::NotEnoughAccountKeys,
|
|
"should fail because program data has insufficient funds to cover rent",
|
|
)
|
|
.await;
|
|
|
|
let client = &mut context.banks_client;
|
|
let payer = &context.payer;
|
|
let recent_blockhash = context.last_blockhash;
|
|
|
|
const ADDITIONAL_BYTES: u32 = 42;
|
|
let min_balance_increase_for_extend = rent
|
|
.minimum_balance(ADDITIONAL_BYTES as usize)
|
|
.saturating_sub(rent.minimum_balance(0));
|
|
|
|
let transaction = Transaction::new_signed_with_payer(
|
|
&[
|
|
system_instruction::transfer(
|
|
&payer.pubkey(),
|
|
&programdata_address,
|
|
min_balance_increase_for_extend,
|
|
),
|
|
extend_program(&program_address, None, ADDITIONAL_BYTES),
|
|
],
|
|
Some(&payer.pubkey()),
|
|
&[payer],
|
|
recent_blockhash,
|
|
);
|
|
|
|
assert_matches!(client.process_transaction(transaction).await, Ok(()));
|
|
let updated_program_data_account = client
|
|
.get_account(programdata_address)
|
|
.await
|
|
.unwrap()
|
|
.unwrap();
|
|
assert_eq!(
|
|
updated_program_data_account.data().len(),
|
|
program_data_len + ADDITIONAL_BYTES as usize
|
|
);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_extend_program_with_invalid_system_program() {
|
|
let mut context = setup_test_context().await;
|
|
|
|
let program_address = Pubkey::new_unique();
|
|
let (programdata_address, _) = Pubkey::find_program_address(&[program_address.as_ref()], &id());
|
|
add_upgradeable_loader_account(
|
|
&mut context,
|
|
&program_address,
|
|
&UpgradeableLoaderState::Program {
|
|
programdata_address,
|
|
},
|
|
UpgradeableLoaderState::size_of_program(),
|
|
|_| {},
|
|
)
|
|
.await;
|
|
let program_data_len = 100;
|
|
add_upgradeable_loader_account(
|
|
&mut context,
|
|
&programdata_address,
|
|
&UpgradeableLoaderState::ProgramData {
|
|
slot: 0,
|
|
upgrade_authority_address: Some(Pubkey::new_unique()),
|
|
},
|
|
program_data_len,
|
|
|_| {},
|
|
)
|
|
.await;
|
|
|
|
let payer_address = context.payer.pubkey();
|
|
let mut ix = extend_program(&program_address, Some(&payer_address), 1);
|
|
|
|
// Change system program to an invalid key
|
|
{
|
|
let system_program_meta = ix
|
|
.accounts
|
|
.iter_mut()
|
|
.find(|meta| meta.pubkey == crate::system_program::ID)
|
|
.expect("expected to find system program account meta");
|
|
system_program_meta.pubkey = Pubkey::new_unique();
|
|
}
|
|
|
|
assert_ix_error(
|
|
&mut context,
|
|
ix,
|
|
None,
|
|
InstructionError::MissingAccount,
|
|
"should fail because the system program is missing",
|
|
)
|
|
.await;
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_extend_program_with_mismatch_program_data() {
|
|
let mut context = setup_test_context().await;
|
|
let payer_address = context.payer.pubkey();
|
|
|
|
let program_address = Pubkey::new_unique();
|
|
let (programdata_address, _) = Pubkey::find_program_address(&[program_address.as_ref()], &id());
|
|
add_upgradeable_loader_account(
|
|
&mut context,
|
|
&program_address,
|
|
&UpgradeableLoaderState::Program {
|
|
programdata_address,
|
|
},
|
|
UpgradeableLoaderState::size_of_program(),
|
|
|_| {},
|
|
)
|
|
.await;
|
|
|
|
let mismatch_programdata_address = Pubkey::new_unique();
|
|
add_upgradeable_loader_account(
|
|
&mut context,
|
|
&mismatch_programdata_address,
|
|
&UpgradeableLoaderState::ProgramData {
|
|
slot: 0,
|
|
upgrade_authority_address: Some(Pubkey::new_unique()),
|
|
},
|
|
100,
|
|
|_| {},
|
|
)
|
|
.await;
|
|
|
|
let mut ix = extend_program(&program_address, Some(&payer_address), 1);
|
|
|
|
// Replace ProgramData account meta with invalid account
|
|
{
|
|
let program_data_meta = ix
|
|
.accounts
|
|
.iter_mut()
|
|
.find(|meta| meta.pubkey == programdata_address)
|
|
.expect("expected to find program data account meta");
|
|
program_data_meta.pubkey = mismatch_programdata_address;
|
|
}
|
|
|
|
assert_ix_error(
|
|
&mut context,
|
|
ix,
|
|
None,
|
|
InstructionError::InvalidArgument,
|
|
"should fail because the program data account doesn't match the program",
|
|
)
|
|
.await;
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_extend_program_with_readonly_program_data() {
|
|
let mut context = setup_test_context().await;
|
|
let payer_address = context.payer.pubkey();
|
|
|
|
let program_address = Pubkey::new_unique();
|
|
let (programdata_address, _) = Pubkey::find_program_address(&[program_address.as_ref()], &id());
|
|
add_upgradeable_loader_account(
|
|
&mut context,
|
|
&program_address,
|
|
&UpgradeableLoaderState::Program {
|
|
programdata_address,
|
|
},
|
|
UpgradeableLoaderState::size_of_program(),
|
|
|_| {},
|
|
)
|
|
.await;
|
|
add_upgradeable_loader_account(
|
|
&mut context,
|
|
&programdata_address,
|
|
&UpgradeableLoaderState::ProgramData {
|
|
slot: 0,
|
|
upgrade_authority_address: Some(Pubkey::new_unique()),
|
|
},
|
|
100,
|
|
|_| {},
|
|
)
|
|
.await;
|
|
|
|
let mut ix = extend_program(&program_address, Some(&payer_address), 1);
|
|
|
|
// Demote ProgramData account meta to read-only
|
|
{
|
|
let program_data_meta = ix
|
|
.accounts
|
|
.iter_mut()
|
|
.find(|meta| meta.pubkey == programdata_address)
|
|
.expect("expected to find program data account meta");
|
|
program_data_meta.is_writable = false;
|
|
}
|
|
|
|
assert_ix_error(
|
|
&mut context,
|
|
ix,
|
|
None,
|
|
InstructionError::InvalidArgument,
|
|
"should fail because the program data account is not writable",
|
|
)
|
|
.await;
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_extend_program_with_invalid_program_data_state() {
|
|
let mut context = setup_test_context().await;
|
|
let payer_address = context.payer.pubkey();
|
|
|
|
let program_address = Pubkey::new_unique();
|
|
let (programdata_address, _) = Pubkey::find_program_address(&[program_address.as_ref()], &id());
|
|
add_upgradeable_loader_account(
|
|
&mut context,
|
|
&program_address,
|
|
&UpgradeableLoaderState::Program {
|
|
programdata_address,
|
|
},
|
|
UpgradeableLoaderState::size_of_program(),
|
|
|_| {},
|
|
)
|
|
.await;
|
|
add_upgradeable_loader_account(
|
|
&mut context,
|
|
&programdata_address,
|
|
&UpgradeableLoaderState::Buffer {
|
|
authority_address: Some(payer_address),
|
|
},
|
|
100,
|
|
|_| {},
|
|
)
|
|
.await;
|
|
|
|
assert_ix_error(
|
|
&mut context,
|
|
extend_program(&program_address, Some(&payer_address), 1024),
|
|
None,
|
|
InstructionError::InvalidAccountData,
|
|
"should fail because the program data account state isn't valid",
|
|
)
|
|
.await;
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_extend_program_with_invalid_program_data_owner() {
|
|
let mut context = setup_test_context().await;
|
|
let payer_address = context.payer.pubkey();
|
|
|
|
let program_address = Pubkey::new_unique();
|
|
let (programdata_address, _) = Pubkey::find_program_address(&[program_address.as_ref()], &id());
|
|
add_upgradeable_loader_account(
|
|
&mut context,
|
|
&program_address,
|
|
&UpgradeableLoaderState::Program {
|
|
programdata_address,
|
|
},
|
|
UpgradeableLoaderState::size_of_program(),
|
|
|_| {},
|
|
)
|
|
.await;
|
|
|
|
let invalid_owner = Pubkey::new_unique();
|
|
add_upgradeable_loader_account(
|
|
&mut context,
|
|
&program_address,
|
|
&UpgradeableLoaderState::ProgramData {
|
|
slot: 0,
|
|
upgrade_authority_address: Some(payer_address),
|
|
},
|
|
100,
|
|
|account| account.set_owner(invalid_owner),
|
|
)
|
|
.await;
|
|
|
|
assert_ix_error(
|
|
&mut context,
|
|
extend_program(&program_address, Some(&payer_address), 1024),
|
|
None,
|
|
InstructionError::InvalidAccountOwner,
|
|
"should fail because the program data account owner isn't valid",
|
|
)
|
|
.await;
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_extend_program_with_readonly_program() {
|
|
let mut context = setup_test_context().await;
|
|
let payer_address = context.payer.pubkey();
|
|
|
|
let program_address = Pubkey::new_unique();
|
|
let (programdata_address, _) = Pubkey::find_program_address(&[program_address.as_ref()], &id());
|
|
add_upgradeable_loader_account(
|
|
&mut context,
|
|
&program_address,
|
|
&UpgradeableLoaderState::Program {
|
|
programdata_address,
|
|
},
|
|
UpgradeableLoaderState::size_of_program(),
|
|
|_| {},
|
|
)
|
|
.await;
|
|
add_upgradeable_loader_account(
|
|
&mut context,
|
|
&programdata_address,
|
|
&UpgradeableLoaderState::ProgramData {
|
|
slot: 0,
|
|
upgrade_authority_address: Some(Pubkey::new_unique()),
|
|
},
|
|
100,
|
|
|_| {},
|
|
)
|
|
.await;
|
|
|
|
let mut ix = extend_program(&program_address, Some(&payer_address), 1);
|
|
|
|
// Demote Program account meta to read-only
|
|
{
|
|
let program_meta = ix
|
|
.accounts
|
|
.iter_mut()
|
|
.find(|meta| meta.pubkey == program_address)
|
|
.expect("expected to find program account meta");
|
|
program_meta.is_writable = false;
|
|
}
|
|
|
|
assert_ix_error(
|
|
&mut context,
|
|
ix,
|
|
None,
|
|
InstructionError::InvalidArgument,
|
|
"should fail because the program account is not writable",
|
|
)
|
|
.await;
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_extend_program_with_invalid_program_owner() {
|
|
let mut context = setup_test_context().await;
|
|
let payer_address = context.payer.pubkey();
|
|
|
|
let program_address = Pubkey::new_unique();
|
|
let (programdata_address, _) = Pubkey::find_program_address(&[program_address.as_ref()], &id());
|
|
let invalid_owner = Pubkey::new_unique();
|
|
add_upgradeable_loader_account(
|
|
&mut context,
|
|
&program_address,
|
|
&UpgradeableLoaderState::Program {
|
|
programdata_address,
|
|
},
|
|
UpgradeableLoaderState::size_of_program(),
|
|
|account| account.set_owner(invalid_owner),
|
|
)
|
|
.await;
|
|
add_upgradeable_loader_account(
|
|
&mut context,
|
|
&programdata_address,
|
|
&UpgradeableLoaderState::ProgramData {
|
|
slot: 0,
|
|
upgrade_authority_address: Some(Pubkey::new_unique()),
|
|
},
|
|
100,
|
|
|_| {},
|
|
)
|
|
.await;
|
|
|
|
assert_ix_error(
|
|
&mut context,
|
|
extend_program(&program_address, Some(&payer_address), 1024),
|
|
None,
|
|
InstructionError::InvalidAccountOwner,
|
|
"should fail because the program account owner isn't valid",
|
|
)
|
|
.await;
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_extend_program_with_invalid_program_state() {
|
|
let mut context = setup_test_context().await;
|
|
let payer_address = context.payer.pubkey();
|
|
|
|
let program_address = Pubkey::new_unique();
|
|
let (programdata_address, _) = Pubkey::find_program_address(&[program_address.as_ref()], &id());
|
|
add_upgradeable_loader_account(
|
|
&mut context,
|
|
&program_address,
|
|
&UpgradeableLoaderState::Buffer {
|
|
authority_address: Some(payer_address),
|
|
},
|
|
100,
|
|
|_| {},
|
|
)
|
|
.await;
|
|
|
|
add_upgradeable_loader_account(
|
|
&mut context,
|
|
&programdata_address,
|
|
&UpgradeableLoaderState::ProgramData {
|
|
slot: 0,
|
|
upgrade_authority_address: Some(Pubkey::new_unique()),
|
|
},
|
|
100,
|
|
|_| {},
|
|
)
|
|
.await;
|
|
|
|
assert_ix_error(
|
|
&mut context,
|
|
extend_program(&program_address, Some(&payer_address), 1024),
|
|
None,
|
|
InstructionError::InvalidAccountData,
|
|
"should fail because the program account state isn't valid",
|
|
)
|
|
.await;
|
|
}
|