use {
litesvm::LiteSVM,
solana_account::{Account, ReadableAccount, WritableAccount},
solana_address::Address,
solana_clock::Clock,
solana_epoch_schedule::EpochSchedule,
solana_hash::Hash,
solana_instruction::Instruction,
solana_keypair::Keypair,
solana_program_error::{ProgramError, ProgramResult},
solana_rent::Rent,
solana_sdk_ids::system_program,
solana_signer::{signers::Signers, Signer},
solana_stake_interface::{
self as stake,
error::StakeError,
instruction::{self as ixn, LockupArgs},
state::{Authorized, Delegation, Lockup, Meta, Stake, StakeAuthorize, StakeStateV2},
},
solana_system_interface::instruction as system_instruction,
solana_transaction::Transaction,
solana_transaction_error::TransactionError,
solana_vote_interface::{
instruction as vote_instruction,
state::{VoteInit, VoteStateV4, VoteStateVersions},
},
};
fn from<T: ReadableAccount>(key: &Address, account: &T) -> Option<VoteStateV4> {
VoteStateV4::deserialize(account.data(), key).ok()
}
fn to<T: WritableAccount>(versioned: &VoteStateVersions, account: &mut T) -> Option<()> {
VoteStateV4::serialize(versioned, account.data_as_mut_slice()).ok()
}
fn increment_vote_account_credits(
svm: &mut LiteSVM,
vote_account_address: Address,
number_of_credits: u64,
) {
let mut vote_account = svm.get_account(&vote_account_address).unwrap();
let mut vote_state = from(&vote_account_address, &vote_account).unwrap();
let epoch = svm.get_sysvar::<Clock>().epoch;
const MAX_EPOCH_CREDITS_HISTORY: usize = 64;
for _ in 0..number_of_credits {
let credits = 1;
if vote_state.epoch_credits.is_empty() {
vote_state.epoch_credits.push((epoch, 0, 0));
} else if epoch != vote_state.epoch_credits.last().unwrap().0 {
let (_, credits_val, prev_credits) = *vote_state.epoch_credits.last().unwrap();
if credits_val != prev_credits {
vote_state
.epoch_credits
.push((epoch, credits_val, credits_val));
} else {
vote_state.epoch_credits.last_mut().unwrap().0 = epoch;
}
if vote_state.epoch_credits.len() > MAX_EPOCH_CREDITS_HISTORY {
vote_state.epoch_credits.remove(0);
}
}
vote_state.epoch_credits.last_mut().unwrap().1 = vote_state
.epoch_credits
.last()
.unwrap()
.1
.saturating_add(credits);
}
let versioned = VoteStateVersions::V4(Box::new(vote_state));
to(&versioned, &mut vote_account).unwrap();
svm.set_account(vote_account_address, vote_account).unwrap();
}
#[derive(Debug, PartialEq)]
struct Accounts {
pub validator: Keypair,
pub voter: Keypair,
pub withdrawer: Keypair,
pub vote_account: Keypair,
}
impl Accounts {
fn initialize(&self, svm: &mut LiteSVM, payer: &Keypair) {
let epoch_schedule = svm.get_sysvar::<EpochSchedule>();
let slot = epoch_schedule.first_normal_slot + 1;
svm.warp_to_slot(slot);
create_vote(
svm,
payer,
&svm.latest_blockhash(),
&self.validator,
&self.voter.pubkey(),
&self.withdrawer.pubkey(),
&self.vote_account,
);
}
}
impl Default for Accounts {
fn default() -> Self {
let vote_account = Keypair::new();
Self {
validator: Keypair::new(),
voter: Keypair::new(),
withdrawer: Keypair::new(),
vote_account,
}
}
}
fn create_vote(
svm: &mut LiteSVM,
payer: &Keypair,
recent_blockhash: &Hash,
validator: &Keypair,
voter: &Address,
withdrawer: &Address,
vote_account: &Keypair,
) {
let rent = svm.get_sysvar::<Rent>();
let rent_voter = rent.minimum_balance(VoteStateV4::size_of());
let mut instructions = vec![system_instruction::create_account(
&payer.pubkey(),
&validator.pubkey(),
rent.minimum_balance(0),
0,
&system_program::id(),
)];
instructions.append(&mut vote_instruction::create_account_with_config(
&payer.pubkey(),
&vote_account.pubkey(),
&VoteInit {
node_pubkey: validator.pubkey(),
authorized_voter: *voter,
authorized_withdrawer: *withdrawer,
..VoteInit::default()
},
rent_voter,
vote_instruction::CreateVoteAccountConfig {
space: VoteStateV4::size_of() as u64,
..Default::default()
},
));
let transaction = Transaction::new_signed_with_payer(
&instructions,
Some(&payer.pubkey()),
&[validator, vote_account, payer],
*recent_blockhash,
);
let _ = svm.send_transaction(transaction);
}
fn advance_epoch(svm: &mut LiteSVM) {
refresh_blockhash(svm);
let old_clock = svm.get_sysvar::<Clock>();
let root_slot = old_clock.slot;
let slots_per_epoch = svm.get_sysvar::<EpochSchedule>().slots_per_epoch;
svm.warp_to_slot(root_slot + slots_per_epoch);
let mut new_clock = old_clock;
new_clock.epoch += 1;
svm.set_sysvar::<Clock>(&new_clock)
}
fn refresh_blockhash(svm: &mut LiteSVM) {
svm.expire_blockhash()
}
fn get_account(svm: &mut LiteSVM, pubkey: &Address) -> Account {
svm.get_account(pubkey).expect("account not found")
}
fn get_stake_account(svm: &mut LiteSVM, pubkey: &Address) -> (Meta, Option<Stake>, u64) {
let stake_account = get_account(svm, pubkey);
let lamports = stake_account.lamports;
match bincode::deserialize::<StakeStateV2>(&stake_account.data).unwrap() {
StakeStateV2::Initialized(meta) => (meta, None, lamports),
StakeStateV2::Stake(meta, stake, _) => (meta, Some(stake), lamports),
StakeStateV2::Uninitialized => panic!("panic: uninitialized"),
_ => unimplemented!(),
}
}
fn get_stake_account_rent(svm: &mut LiteSVM) -> u64 {
let rent = svm.get_sysvar::<Rent>();
rent.minimum_balance(std::mem::size_of::<stake::state::StakeStateV2>())
}
fn get_minimum_delegation(svm: &mut LiteSVM, payer: &Keypair) -> u64 {
let transaction = Transaction::new_signed_with_payer(
&[stake::instruction::get_minimum_delegation()],
Some(&payer.pubkey()),
&[&payer],
svm.latest_blockhash(),
);
let mut data = svm
.simulate_transaction(transaction)
.unwrap()
.meta
.return_data
.data;
data.resize(8, 0);
data.try_into().map(u64::from_le_bytes).unwrap()
}
fn create_independent_stake_account(
svm: &mut LiteSVM,
authorized: &Authorized,
stake_amount: u64,
payer: &Keypair,
) -> Address {
create_independent_stake_account_with_lockup(
svm,
authorized,
&Lockup::default(),
stake_amount,
payer,
)
}
fn create_independent_stake_account_with_lockup(
svm: &mut LiteSVM,
authorized: &Authorized,
lockup: &Lockup,
stake_amount: u64,
payer: &Keypair,
) -> Address {
let stake = Keypair::new();
let lamports = get_stake_account_rent(svm) + stake_amount;
let instructions = vec![
solana_system_interface::instruction::create_account(
&payer.pubkey(),
&stake.pubkey(),
lamports,
std::mem::size_of::<stake::state::StakeStateV2>() as u64,
&solana_sdk_ids::stake::id(),
),
stake::instruction::initialize(&stake.pubkey(), authorized, lockup),
];
let transaction = Transaction::new_signed_with_payer(
&instructions,
Some(&payer.pubkey()),
&[payer, &stake],
svm.latest_blockhash(),
);
svm.send_transaction(transaction).unwrap();
stake.pubkey()
}
fn create_blank_stake_account(svm: &mut LiteSVM, payer: &Keypair) -> Address {
let stake = Keypair::new();
create_blank_stake_account_from_keypair(svm, &stake, payer)
}
fn create_blank_stake_account_from_keypair(
svm: &mut LiteSVM,
stake: &Keypair,
payer: &Keypair,
) -> Address {
let lamports = get_stake_account_rent(svm);
let transaction = Transaction::new_signed_with_payer(
&[system_instruction::create_account(
&payer.pubkey(),
&stake.pubkey(),
lamports,
StakeStateV2::size_of() as u64,
&solana_sdk_ids::stake::id(),
)],
Some(&payer.pubkey()),
&[&payer, &stake],
svm.latest_blockhash(),
);
svm.send_transaction(transaction).unwrap();
stake.pubkey()
}
fn process_instruction<T: Signers + ?Sized>(
svm: &mut LiteSVM,
instruction: &Instruction,
additional_signers: &T,
payer: &Keypair,
) -> ProgramResult {
let mut transaction =
Transaction::new_with_payer(std::slice::from_ref(instruction), Some(&payer.pubkey()));
transaction.partial_sign(&[&payer], svm.latest_blockhash());
transaction.sign(additional_signers, svm.latest_blockhash());
match svm.send_transaction(transaction) {
Ok(_) => Ok(()),
Err(e) => {
match e.err {
TransactionError::InstructionError(_, e) => Err(e.try_into().unwrap()),
TransactionError::InsufficientFundsForRent { .. } => {
Err(ProgramError::InsufficientFunds)
}
_ => panic!("couldnt convert {e:?} to ProgramError"),
}
}
}
}
fn test_instruction_with_missing_signers(
svm: &mut LiteSVM,
instruction: &Instruction,
additional_signers: &Vec<&Keypair>,
payer: &Keypair,
) {
for i in 0..instruction.accounts.len() {
if instruction.accounts[i].is_signer {
let mut instruction = instruction.clone();
instruction.accounts[i].is_signer = false;
let reduced_signers: Vec<_> = additional_signers
.iter()
.filter(|s| s.pubkey() != instruction.accounts[i].pubkey)
.collect();
let e = process_instruction(svm, &instruction, &reduced_signers, payer).unwrap_err();
assert_eq!(e, ProgramError::MissingRequiredSignature);
}
}
process_instruction(svm, instruction, additional_signers, payer).unwrap();
}
#[test]
fn test_stake_checked_instructions() {
let mut svm = LiteSVM::new();
let accounts = Accounts::default();
let payer = Keypair::new();
svm.airdrop(&payer.pubkey(), 1_000_000_000_000).unwrap();
accounts.initialize(&mut svm, &payer);
let staker_keypair = Keypair::new();
let withdrawer_keypair = Keypair::new();
let authorized_keypair = Keypair::new();
let seed_base_keypair = Keypair::new();
let custodian_keypair = Keypair::new();
let staker = staker_keypair.pubkey();
let withdrawer = withdrawer_keypair.pubkey();
let authorized = authorized_keypair.pubkey();
let seed_base = seed_base_keypair.pubkey();
let custodian = custodian_keypair.pubkey();
let seed = "test seed";
let seeded_address =
Address::create_with_seed(&seed_base, seed, &system_program::id()).unwrap();
let stake = create_blank_stake_account(&mut svm, &payer);
let instruction = ixn::initialize_checked(&stake, &Authorized { staker, withdrawer });
test_instruction_with_missing_signers(
&mut svm,
&instruction,
&vec![&withdrawer_keypair],
&payer,
);
let stake =
create_independent_stake_account(&mut svm, &Authorized { staker, withdrawer }, 0, &payer);
let instruction =
ixn::authorize_checked(&stake, &staker, &authorized, StakeAuthorize::Staker, None);
test_instruction_with_missing_signers(
&mut svm,
&instruction,
&vec![&staker_keypair, &authorized_keypair],
&payer,
);
let stake =
create_independent_stake_account(&mut svm, &Authorized { staker, withdrawer }, 0, &payer);
let instruction = ixn::authorize_checked(
&stake,
&withdrawer,
&authorized,
StakeAuthorize::Withdrawer,
None,
);
test_instruction_with_missing_signers(
&mut svm,
&instruction,
&vec![&withdrawer_keypair, &authorized_keypair],
&payer,
);
for authority_type in [StakeAuthorize::Staker, StakeAuthorize::Withdrawer] {
let stake = create_independent_stake_account(
&mut svm,
&Authorized::auto(&seeded_address),
0,
&payer,
);
let instruction = ixn::authorize_checked_with_seed(
&stake,
&seed_base,
seed.to_string(),
&system_program::id(),
&authorized,
authority_type,
None,
);
test_instruction_with_missing_signers(
&mut svm,
&instruction,
&vec![&seed_base_keypair, &authorized_keypair],
&payer,
);
}
let stake =
create_independent_stake_account(&mut svm, &Authorized { staker, withdrawer }, 0, &payer);
let instruction = ixn::set_lockup_checked(
&stake,
&LockupArgs {
unix_timestamp: None,
epoch: Some(1),
custodian: Some(custodian),
},
&withdrawer,
);
test_instruction_with_missing_signers(
&mut svm,
&instruction,
&vec![&withdrawer_keypair, &custodian_keypair],
&payer,
);
}
#[test]
fn test_stake_initialize() {
let mut svm = LiteSVM::new();
let accounts = Accounts::default();
let payer = Keypair::new();
svm.airdrop(&payer.pubkey(), 1_000_000_000_000).unwrap();
accounts.initialize(&mut svm, &payer);
let rent_exempt_reserve = get_stake_account_rent(&mut svm);
let no_signers: [&Keypair; 0] = [];
let staker_keypair = Keypair::new();
let withdrawer_keypair = Keypair::new();
let custodian_keypair = Keypair::new();
let staker = staker_keypair.pubkey();
let withdrawer = withdrawer_keypair.pubkey();
let custodian = custodian_keypair.pubkey();
let authorized = Authorized { staker, withdrawer };
let lockup = Lockup {
epoch: 1,
unix_timestamp: 0,
custodian,
};
let stake = create_blank_stake_account(&mut svm, &payer);
let instruction = ixn::initialize(&stake, &authorized, &lockup);
process_instruction(&mut svm, &instruction, &no_signers, &payer).unwrap();
let account = get_account(&mut svm, &stake);
let stake_state: StakeStateV2 = bincode::deserialize(&account.data).unwrap();
assert_eq!(
stake_state,
StakeStateV2::Initialized(Meta {
authorized,
rent_exempt_reserve,
lockup,
}),
);
refresh_blockhash(&mut svm);
let e = process_instruction(&mut svm, &instruction, &no_signers, &payer).unwrap_err();
assert_eq!(e, ProgramError::InvalidAccountData);
let stake = Address::new_unique();
let account = Account {
lamports: rent_exempt_reserve / 2,
data: vec![0; StakeStateV2::size_of()],
owner: solana_sdk_ids::stake::id(),
executable: false,
rent_epoch: 1000,
};
svm.set_account(stake, account).unwrap();
let instruction = ixn::initialize(&stake, &authorized, &lockup);
let e = process_instruction(&mut svm, &instruction, &no_signers, &payer).unwrap_err();
assert_eq!(e, ProgramError::InsufficientFunds);
let stake_keypair = Keypair::new();
let stake = stake_keypair.pubkey();
let payer = Keypair::new();
svm.airdrop(&payer.pubkey(), 1_000_000_000_000).unwrap();
let instruction = solana_system_interface::instruction::create_account(
&payer.pubkey(),
&stake,
rent_exempt_reserve * 2,
StakeStateV2::size_of() as u64 + 1,
&solana_sdk_ids::stake::id(),
);
process_instruction(&mut svm, &instruction, &vec![&stake_keypair], &payer).unwrap();
let instruction = ixn::initialize(&stake, &authorized, &lockup);
let e = process_instruction(&mut svm, &instruction, &no_signers, &payer).unwrap_err();
assert_eq!(e, ProgramError::InvalidAccountData);
let stake_keypair = Keypair::new();
let stake = stake_keypair.pubkey();
let instruction = solana_system_interface::instruction::create_account(
&payer.pubkey(),
&stake,
rent_exempt_reserve,
StakeStateV2::size_of() as u64 - 1,
&solana_sdk_ids::stake::id(),
);
process_instruction(&mut svm, &instruction, &vec![&stake_keypair], &payer).unwrap();
let instruction = ixn::initialize(&stake, &authorized, &lockup);
let e = process_instruction(&mut svm, &instruction, &no_signers, &payer).unwrap_err();
assert_eq!(e, ProgramError::InvalidAccountData);
}
#[test]
fn test_authorize() {
let mut svm = LiteSVM::new();
let payer = Keypair::new();
svm.airdrop(&payer.pubkey(), 1_000_000_000_000).unwrap();
let accounts = Accounts::default();
accounts.initialize(&mut svm, &payer);
let rent_exempt_reserve = get_stake_account_rent(&mut svm);
let no_signers: [&Keypair; 0] = [];
let stakers: [_; 3] = std::array::from_fn(|_| Keypair::new());
let withdrawers: [_; 3] = std::array::from_fn(|_| Keypair::new());
let stake_keypair = Keypair::new();
let stake = create_blank_stake_account_from_keypair(&mut svm, &stake_keypair, &payer);
for (authority, authority_type) in [
(&stakers[0], StakeAuthorize::Staker),
(&withdrawers[0], StakeAuthorize::Withdrawer),
] {
let instruction = ixn::authorize(&stake, &stake, &authority.pubkey(), authority_type, None);
let e =
process_instruction(&mut svm, &instruction, &vec![&stake_keypair], &payer).unwrap_err();
assert_eq!(e, ProgramError::InvalidAccountData);
}
let authorized = Authorized {
staker: stakers[0].pubkey(),
withdrawer: withdrawers[0].pubkey(),
};
let instruction = ixn::initialize(&stake, &authorized, &Lockup::default());
process_instruction(&mut svm, &instruction, &no_signers, &payer).unwrap();
for (old_authority, new_authority, authority_type) in [
(&stakers[0], &stakers[1], StakeAuthorize::Staker),
(&withdrawers[0], &withdrawers[1], StakeAuthorize::Withdrawer),
] {
let instruction = ixn::authorize(
&stake,
&old_authority.pubkey(),
&new_authority.pubkey(),
authority_type,
None,
);
test_instruction_with_missing_signers(&mut svm, &instruction, &vec![old_authority], &payer);
let (meta, _, _) = get_stake_account(&mut svm, &stake);
let actual_authority = match authority_type {
StakeAuthorize::Staker => meta.authorized.staker,
StakeAuthorize::Withdrawer => meta.authorized.withdrawer,
};
assert_eq!(actual_authority, new_authority.pubkey());
}
for (old_authority, new_authority, authority_type) in [
(&stakers[0], Address::new_unique(), StakeAuthorize::Staker),
(
&withdrawers[0],
Address::new_unique(),
StakeAuthorize::Withdrawer,
),
] {
let instruction = ixn::authorize(
&stake,
&old_authority.pubkey(),
&new_authority,
authority_type,
None,
);
let e =
process_instruction(&mut svm, &instruction, &vec![old_authority], &payer).unwrap_err();
assert_eq!(e, ProgramError::MissingRequiredSignature);
}
for (old_authority, new_authority, authority_type) in [
(&stakers[1], &stakers[2], StakeAuthorize::Staker),
(&withdrawers[1], &withdrawers[2], StakeAuthorize::Withdrawer),
] {
let instruction = ixn::authorize(
&stake,
&old_authority.pubkey(),
&new_authority.pubkey(),
authority_type,
None,
);
test_instruction_with_missing_signers(&mut svm, &instruction, &vec![old_authority], &payer);
let (meta, _, _) = get_stake_account(&mut svm, &stake);
let actual_authority = match authority_type {
StakeAuthorize::Staker => meta.authorized.staker,
StakeAuthorize::Withdrawer => meta.authorized.withdrawer,
};
assert_eq!(actual_authority, new_authority.pubkey());
}
let instruction = ixn::authorize(
&stake,
&stakers[2].pubkey(),
&Address::new_unique(),
StakeAuthorize::Withdrawer,
None,
);
let e = process_instruction(&mut svm, &instruction, &vec![&stakers[2]], &payer).unwrap_err();
assert_eq!(e, ProgramError::MissingRequiredSignature);
let instruction = ixn::authorize(
&stake,
&withdrawers[2].pubkey(),
&stakers[0].pubkey(),
StakeAuthorize::Staker,
None,
);
test_instruction_with_missing_signers(&mut svm, &instruction, &vec![&withdrawers[2]], &payer);
let (meta, _, _) = get_stake_account(&mut svm, &stake);
assert_eq!(meta.authorized.staker, stakers[0].pubkey());
for staker in stakers {
let recipient = Address::new_unique();
let instruction = ixn::withdraw(
&stake,
&staker.pubkey(),
&recipient,
rent_exempt_reserve,
None,
);
let e = process_instruction(&mut svm, &instruction, &vec![&staker], &payer).unwrap_err();
assert_eq!(e, ProgramError::MissingRequiredSignature);
}
}
#[test]
#[ignore]
fn test_stake_delegate() {
let mut svm = LiteSVM::new();
let accounts = Accounts::default();
let payer = Keypair::new();
svm.airdrop(&payer.pubkey(), 1_000_000_000_000).unwrap();
accounts.initialize(&mut svm, &payer);
let vote_account2 = Keypair::new();
let latest_blockhash = svm.latest_blockhash();
create_vote(
&mut svm,
&payer,
&latest_blockhash,
&Keypair::new(),
&Address::new_unique(),
&Address::new_unique(),
&vote_account2,
);
let staker_keypair = Keypair::new();
let withdrawer_keypair = Keypair::new();
let staker = staker_keypair.pubkey();
let withdrawer = withdrawer_keypair.pubkey();
let authorized = Authorized { staker, withdrawer };
let vote_state_credits = 100;
increment_vote_account_credits(&mut svm, accounts.vote_account.pubkey(), vote_state_credits);
let minimum_delegation = get_minimum_delegation(&mut svm, &payer);
let stake = create_independent_stake_account(&mut svm, &authorized, minimum_delegation, &payer);
let instruction = ixn::delegate_stake(&stake, &staker, &accounts.vote_account.pubkey());
test_instruction_with_missing_signers(&mut svm, &instruction, &vec![&staker_keypair], &payer);
let clock = svm.get_sysvar::<Clock>();
let (_, stake_data, _) = get_stake_account(&mut svm, &stake);
assert_eq!(
stake_data.unwrap(),
Stake {
delegation: Delegation {
voter_pubkey: accounts.vote_account.pubkey(),
stake: minimum_delegation,
activation_epoch: clock.epoch,
deactivation_epoch: u64::MAX,
..Delegation::default()
},
credits_observed: vote_state_credits,
}
);
advance_epoch(&mut svm);
let instruction = ixn::delegate_stake(&stake, &staker, &accounts.vote_account.pubkey());
let e =
process_instruction(&mut svm, &instruction, &vec![&staker_keypair], &payer).unwrap_err();
assert_eq!(e, StakeError::TooSoonToRedelegate.into());
let instruction = ixn::deactivate_stake(&stake, &staker);
process_instruction(&mut svm, &instruction, &vec![&staker_keypair], &payer).unwrap();
let instruction = ixn::delegate_stake(&stake, &staker, &vote_account2.pubkey());
let e =
process_instruction(&mut svm, &instruction, &vec![&staker_keypair], &payer).unwrap_err();
assert_eq!(e, StakeError::TooSoonToRedelegate.into());
refresh_blockhash(&mut svm);
let instruction = ixn::delegate_stake(&stake, &staker, &accounts.vote_account.pubkey());
process_instruction(&mut svm, &instruction, &vec![&staker_keypair], &payer).unwrap();
let (_, stake_data, _) = get_stake_account(&mut svm, &stake);
assert_eq!(stake_data.unwrap().delegation.deactivation_epoch, u64::MAX);
let instruction = ixn::delegate_stake(&stake, &staker, &vote_account2.pubkey());
let e =
process_instruction(&mut svm, &instruction, &vec![&staker_keypair], &payer).unwrap_err();
assert_eq!(e, StakeError::TooSoonToRedelegate.into());
advance_epoch(&mut svm);
let instruction = ixn::delegate_stake(&stake, &staker, &vote_account2.pubkey());
let e =
process_instruction(&mut svm, &instruction, &vec![&staker_keypair], &payer).unwrap_err();
assert_eq!(e, StakeError::TooSoonToRedelegate.into());
let mut fake_vote_account = get_account(&mut svm, &accounts.vote_account.pubkey());
fake_vote_account.owner = Address::new_unique();
let fake_vote_address = Address::new_unique();
svm.set_account(fake_vote_address, fake_vote_account)
.unwrap();
let stake = create_independent_stake_account(&mut svm, &authorized, minimum_delegation, &payer);
let instruction = ixn::delegate_stake(&stake, &staker, &fake_vote_address);
let e =
process_instruction(&mut svm, &instruction, &vec![&staker_keypair], &payer).unwrap_err();
assert_eq!(e, ProgramError::IncorrectProgramId);
let rewards_pool_address = Address::new_unique();
let rewards_pool = Account {
lamports: get_stake_account_rent(&mut svm),
data: bincode::serialize(&StakeStateV2::RewardsPool)
.unwrap()
.to_vec(),
owner: solana_sdk_ids::stake::id(),
executable: false,
rent_epoch: u64::MAX,
};
svm.set_account(rewards_pool_address, rewards_pool).unwrap();
let instruction = ixn::delegate_stake(
&rewards_pool_address,
&staker,
&accounts.vote_account.pubkey(),
);
let e =
process_instruction(&mut svm, &instruction, &vec![&staker_keypair], &payer).unwrap_err();
assert_eq!(e, ProgramError::InvalidAccountData);
}