Skip to content

Commit bae5a54

Browse files
authored
add lockup violation to interface tests (#128)
1 parent cb910b8 commit bae5a54

File tree

1 file changed

+89
-2
lines changed

1 file changed

+89
-2
lines changed

program/tests/interface.rs

Lines changed: 89 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,7 @@ use {
5050

5151
// NOTE ideas for future tests:
5252
// * fail with different vote accounts on operations that require them to match
53-
// * fail with different authorities on operations that require them to match
54-
// * adding/changing lockups to ensure we always fail when violating lockup
53+
// * fail with different authorities/lockups on operations that require metas to match
5554

5655
// arbitrary, gives us room to set up activations/deactivations
5756
const EXECUTION_EPOCH: u64 = 8;
@@ -360,6 +359,32 @@ impl StakeInterface {
360359
128
361360
}
362361

362+
// state of any existing lockup on the stake accounts
363+
fn lockup_state(self) -> LockupState {
364+
match self {
365+
Self::Initialize { .. }
366+
| Self::InitializeChecked
367+
| Self::Withdraw {
368+
source_status: WithdrawStatus::Uninitialized,
369+
..
370+
} => LockupState::None,
371+
Self::Authorize { lockup_state, .. }
372+
| Self::AuthorizeWithSeed { lockup_state, .. }
373+
| Self::SetLockup {
374+
existing_lockup_state: lockup_state,
375+
..
376+
}
377+
| Self::DelegateStake { lockup_state, .. }
378+
| Self::Split { lockup_state, .. }
379+
| Self::Merge { lockup_state, .. }
380+
| Self::MoveStake { lockup_state, .. }
381+
| Self::MoveLamports { lockup_state, .. }
382+
| Self::Withdraw { lockup_state, .. }
383+
| Self::Deactivate { lockup_state, .. }
384+
| Self::DeactivateDelinquent { lockup_state, .. } => lockup_state,
385+
}
386+
}
387+
363388
// creates an instruction with the given combination of settings that is guaranteed to succeed
364389
fn to_instruction(self, env: &mut Env) -> Instruction {
365390
let minimum_delegation = get_minimum_delegation();
@@ -975,6 +1000,68 @@ fn test_no_signer_bypass() {
9751000
}
9761001
}
9771002

1003+
// operations that require a custodian fail without it
1004+
#[test]
1005+
fn test_no_custodian_bypass() {
1006+
let mut env = Env::init();
1007+
1008+
for declaration in &*INSTRUCTION_DECLARATIONS {
1009+
// skip if no lockup
1010+
if declaration.lockup_state() != LockupState::Active {
1011+
continue;
1012+
}
1013+
1014+
// changing staker does not require custodian
1015+
match declaration {
1016+
StakeInterface::Authorize {
1017+
authority_type: AuthorityType::Staker,
1018+
..
1019+
}
1020+
| StakeInterface::AuthorizeWithSeed {
1021+
authority_type: AuthorityType::Staker,
1022+
..
1023+
} => {
1024+
continue;
1025+
}
1026+
_ => (),
1027+
}
1028+
1029+
let mut instruction = declaration.to_instruction(&mut env);
1030+
1031+
// skip if successful instruction requires no custodian
1032+
if !instruction
1033+
.accounts
1034+
.iter()
1035+
.any(|account| account.pubkey == CUSTODIAN_LEFT)
1036+
{
1037+
continue;
1038+
}
1039+
1040+
// remove the custodian
1041+
// active custodian is always Left. we only use Right as a new lockup target
1042+
// the only instruction that takes Right as account data is SetLockupChecked
1043+
// and we must remove it too because it comes after Left in the accounts list
1044+
instruction.accounts.retain(|account| {
1045+
account.pubkey != CUSTODIAN_LEFT && account.pubkey != CUSTODIAN_RIGHT
1046+
});
1047+
1048+
env.process_fail(&instruction);
1049+
env.reset();
1050+
1051+
let mut instruction = declaration.to_instruction(&mut env);
1052+
1053+
// change to the wrong custodian
1054+
instruction.accounts.iter_mut().for_each(|account| {
1055+
if account.pubkey == CUSTODIAN_LEFT {
1056+
account.pubkey = CUSTODIAN_RIGHT
1057+
}
1058+
});
1059+
1060+
env.process_fail(&instruction);
1061+
env.reset();
1062+
}
1063+
}
1064+
9781065
// the stake program cannot be used during the epoch rewards period
9791066
// the only exception to this is GetMinimumDelegation
9801067
#[test]

0 commit comments

Comments
 (0)