From ef30f1729c91e46833247f37173655cfded7e02f Mon Sep 17 00:00:00 2001 From: Brooks Prumo Date: Wed, 6 Jul 2022 16:15:01 -0500 Subject: [PATCH] Cleanup stake_instruction tests (#26393) --- Cargo.lock | 1 + programs/stake/Cargo.toml | 1 + programs/stake/src/stake_instruction.rs | 600 +++++++----------------- 3 files changed, 163 insertions(+), 439 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 35a6fcb1df..bb1a467bcb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6096,6 +6096,7 @@ dependencies = [ "solana-program-runtime", "solana-sdk 1.11.2", "solana-vote-program", + "test-case", "thiserror", ] diff --git a/programs/stake/Cargo.toml b/programs/stake/Cargo.toml index 74838f3763..02c1a0a5ba 100644 --- a/programs/stake/Cargo.toml +++ b/programs/stake/Cargo.toml @@ -29,6 +29,7 @@ thiserror = "1.0" assert_matches = "1.5.0" proptest = "1.0" solana-logger = { path = "../../logger", version = "=1.11.2" } +test-case = "2.1.0" [build-dependencies] rustc_version = "0.4" diff --git a/programs/stake/src/stake_instruction.rs b/programs/stake/src/stake_instruction.rs index ae7bcf5e40..40a0746079 100644 --- a/programs/stake/src/stake_instruction.rs +++ b/programs/stake/src/stake_instruction.rs @@ -471,8 +471,29 @@ mod tests { }, solana_vote_program::vote_state::{self, VoteState, VoteStateVersions}, std::{borrow::BorrowMut, collections::HashSet, str::FromStr, sync::Arc}, + test_case::test_case, }; + /// The "new" behavior enables all features + fn feature_set_new_behavior() -> FeatureSet { + FeatureSet::all_enabled() + } + + /// The "old" behavior is before the stake minimum delegation was raised + fn feature_set_old_behavior() -> FeatureSet { + let mut feature_set = feature_set_new_behavior(); + feature_set.deactivate(&feature_set::stake_raise_minimum_delegation_to_1_sol::id()); + feature_set + } + + /// The "old old" behavior is both before the stake minimum delegation was raised *and* before + /// undelegated stake accounts could have zero lamports beyond rent + fn feature_set_old_old_behavior() -> FeatureSet { + let mut feature_set = feature_set_old_behavior(); + feature_set.deactivate(&feature_set::stake_allow_zero_undelegated_amount::id()); + feature_set + } + fn create_default_account() -> AccountSharedData { AccountSharedData::new(0, 0, &Pubkey::new_unique()) } @@ -599,7 +620,9 @@ mod tests { ) } - fn do_test_stake_process_instruction(feature_set: FeatureSet) { + #[test_case(feature_set_old_behavior(); "old_behavior")] + #[test_case(feature_set_new_behavior(); "new_behavior")] + fn test_stake_process_instruction(feature_set: FeatureSet) { process_instruction_as_one_arg( &feature_set, &instruction::initialize( @@ -714,7 +737,9 @@ mod tests { ); } - fn do_test_spoofed_stake_accounts(feature_set: FeatureSet) { + #[test_case(feature_set_old_behavior(); "old_behavior")] + #[test_case(feature_set_new_behavior(); "new_behavior")] + fn test_spoofed_stake_accounts(feature_set: FeatureSet) { process_instruction_as_one_arg( &feature_set, &instruction::initialize( @@ -830,7 +855,9 @@ mod tests { ); } - fn do_test_stake_process_instruction_decode_bail(feature_set: FeatureSet) { + #[test_case(feature_set_old_behavior(); "old_behavior")] + #[test_case(feature_set_new_behavior(); "new_behavior")] + fn test_stake_process_instruction_decode_bail(feature_set: FeatureSet) { // these will not call stake_state, have bogus contents let stake_address = Pubkey::new_unique(); let stake_account = create_default_stake_account(); @@ -1097,7 +1124,9 @@ mod tests { ); } - fn do_test_stake_checked_instructions(feature_set: FeatureSet) { + #[test_case(feature_set_old_behavior(); "old_behavior")] + #[test_case(feature_set_new_behavior(); "new_behavior")] + fn test_stake_checked_instructions(feature_set: FeatureSet) { let stake_address = Pubkey::new_unique(); let staker = Pubkey::new_unique(); let staker_account = create_default_account(); @@ -1457,7 +1486,9 @@ mod tests { ); } - fn do_test_stake_initialize(feature_set: FeatureSet) { + #[test_case(feature_set_old_behavior(); "old_behavior")] + #[test_case(feature_set_new_behavior(); "new_behavior")] + fn test_stake_initialize(feature_set: FeatureSet) { let rent = Rent::default(); let rent_exempt_reserve = rent.minimum_balance(StakeState::size_of()); let stake_lamports = rent_exempt_reserve; @@ -1563,7 +1594,9 @@ mod tests { ); } - fn do_test_authorize(feature_set: FeatureSet) { + #[test_case(feature_set_old_behavior(); "old_behavior")] + #[test_case(feature_set_new_behavior(); "new_behavior")] + fn test_authorize(feature_set: FeatureSet) { let authority_address = solana_sdk::pubkey::new_rand(); let authority_address_2 = solana_sdk::pubkey::new_rand(); let stake_address = solana_sdk::pubkey::new_rand(); @@ -1742,7 +1775,9 @@ mod tests { ); } - fn do_test_authorize_override(feature_set: FeatureSet) { + #[test_case(feature_set_old_behavior(); "old_behavior")] + #[test_case(feature_set_new_behavior(); "new_behavior")] + fn test_authorize_override(feature_set: FeatureSet) { let authority_address = solana_sdk::pubkey::new_rand(); let mallory_address = solana_sdk::pubkey::new_rand(); let stake_address = solana_sdk::pubkey::new_rand(); @@ -1859,7 +1894,9 @@ mod tests { ); } - fn do_test_authorize_with_seed(feature_set: FeatureSet) { + #[test_case(feature_set_old_behavior(); "old_behavior")] + #[test_case(feature_set_new_behavior(); "new_behavior")] + fn test_authorize_with_seed(feature_set: FeatureSet) { let authority_base_address = solana_sdk::pubkey::new_rand(); let authority_address = solana_sdk::pubkey::new_rand(); let seed = "42"; @@ -1974,7 +2011,9 @@ mod tests { ); } - fn do_test_authorize_delegated_stake(feature_set: FeatureSet) { + #[test_case(feature_set_old_behavior(); "old_behavior")] + #[test_case(feature_set_new_behavior(); "new_behavior")] + fn test_authorize_delegated_stake(feature_set: FeatureSet) { let authority_address = solana_sdk::pubkey::new_rand(); let stake_address = solana_sdk::pubkey::new_rand(); let minimum_delegation = crate::get_minimum_delegation(&feature_set); @@ -2163,7 +2202,9 @@ mod tests { ); } - fn do_test_stake_delegate(feature_set: FeatureSet) { + #[test_case(feature_set_old_behavior(); "old_behavior")] + #[test_case(feature_set_new_behavior(); "new_behavior")] + fn test_stake_delegate(feature_set: FeatureSet) { let mut vote_state = VoteState::default(); for i in 0..1000 { vote_state.process_slot_vote_unchecked(i); @@ -2411,7 +2452,9 @@ mod tests { ); } - fn do_test_redelegate_consider_balance_changes(feature_set: FeatureSet) { + #[test_case(feature_set_old_behavior(); "old_behavior")] + #[test_case(feature_set_new_behavior(); "new_behavior")] + fn test_redelegate_consider_balance_changes(feature_set: FeatureSet) { let mut clock = Clock::default(); let rent = Rent::default(); let rent_exempt_reserve = rent.minimum_balance(StakeState::size_of()); @@ -2627,7 +2670,9 @@ mod tests { ); } - fn do_test_split(feature_set: FeatureSet) { + #[test_case(feature_set_old_behavior(); "old_behavior")] + #[test_case(feature_set_new_behavior(); "new_behavior")] + fn test_split(feature_set: FeatureSet) { let stake_address = solana_sdk::pubkey::new_rand(); let minimum_delegation = crate::get_minimum_delegation(&feature_set); let stake_lamports = minimum_delegation * 2; @@ -2735,7 +2780,9 @@ mod tests { ); } - fn do_test_withdraw_stake(feature_set: FeatureSet) { + #[test_case(feature_set_old_behavior(); "old_behavior")] + #[test_case(feature_set_new_behavior(); "new_behavior")] + fn test_withdraw_stake(feature_set: FeatureSet) { let recipient_address = solana_sdk::pubkey::new_rand(); let authority_address = solana_sdk::pubkey::new_rand(); let custodian_address = solana_sdk::pubkey::new_rand(); @@ -3024,7 +3071,9 @@ mod tests { ); } - fn do_test_withdraw_stake_before_warmup(feature_set: FeatureSet) { + #[test_case(feature_set_old_behavior(); "old_behavior")] + #[test_case(feature_set_new_behavior(); "new_behavior")] + fn test_withdraw_stake_before_warmup(feature_set: FeatureSet) { let recipient_address = solana_sdk::pubkey::new_rand(); let stake_address = solana_sdk::pubkey::new_rand(); let minimum_delegation = crate::get_minimum_delegation(&feature_set); @@ -3155,7 +3204,9 @@ mod tests { ); } - fn do_test_withdraw_lockup(feature_set: FeatureSet) { + #[test_case(feature_set_old_behavior(); "old_behavior")] + #[test_case(feature_set_new_behavior(); "new_behavior")] + fn test_withdraw_lockup(feature_set: FeatureSet) { let recipient_address = solana_sdk::pubkey::new_rand(); let custodian_address = solana_sdk::pubkey::new_rand(); let stake_address = solana_sdk::pubkey::new_rand(); @@ -3279,7 +3330,9 @@ mod tests { assert_eq!(from(&accounts[0]).unwrap(), StakeState::Uninitialized); } - fn do_test_withdraw_rent_exempt(feature_set: FeatureSet) { + #[test_case(feature_set_old_behavior(); "old_behavior")] + #[test_case(feature_set_new_behavior(); "new_behavior")] + fn test_withdraw_rent_exempt(feature_set: FeatureSet) { let recipient_address = solana_sdk::pubkey::new_rand(); let custodian_address = solana_sdk::pubkey::new_rand(); let stake_address = solana_sdk::pubkey::new_rand(); @@ -3369,7 +3422,9 @@ mod tests { ); } - fn do_test_deactivate(feature_set: FeatureSet) { + #[test_case(feature_set_old_behavior(); "old_behavior")] + #[test_case(feature_set_new_behavior(); "new_behavior")] + fn test_deactivate(feature_set: FeatureSet) { let stake_address = solana_sdk::pubkey::new_rand(); let minimum_delegation = crate::get_minimum_delegation(&feature_set); let stake_lamports = minimum_delegation; @@ -3491,7 +3546,9 @@ mod tests { ); } - fn do_test_set_lockup(feature_set: FeatureSet) { + #[test_case(feature_set_old_behavior(); "old_behavior")] + #[test_case(feature_set_new_behavior(); "new_behavior")] + fn test_set_lockup(feature_set: FeatureSet) { let custodian_address = solana_sdk::pubkey::new_rand(); let authorized_address = solana_sdk::pubkey::new_rand(); let stake_address = solana_sdk::pubkey::new_rand(); @@ -3775,7 +3832,9 @@ mod tests { /// Ensure that `initialize()` respects the minimum balance requirements /// - Assert 1: accounts with a balance equal-to the rent exemption initialize OK /// - Assert 2: accounts with a balance less-than the rent exemption do not initialize - fn do_test_initialize_minimum_balance(feature_set: FeatureSet) { + #[test_case(feature_set_old_behavior(); "old_behavior")] + #[test_case(feature_set_new_behavior(); "new_behavior")] + fn test_initialize_minimum_balance(feature_set: FeatureSet) { let rent = Rent::default(); let rent_exempt_reserve = rent.minimum_balance(StakeState::size_of()); let stake_address = solana_sdk::pubkey::new_rand(); @@ -3831,7 +3890,9 @@ mod tests { /// withdrawing below the minimum delegation, then re-delegating successfully (see /// `test_behavior_withdrawal_then_redelegate_with_less_than_minimum_stake_delegation()` for /// more information.) - fn do_test_delegate_minimum_stake_delegation(feature_set: FeatureSet) { + #[test_case(feature_set_old_behavior(); "old_behavior")] + #[test_case(feature_set_new_behavior(); "new_behavior")] + fn test_delegate_minimum_stake_delegation(feature_set: FeatureSet) { let minimum_delegation = crate::get_minimum_delegation(&feature_set); let rent = Rent::default(); let rent_exempt_reserve = rent.minimum_balance(StakeState::size_of()); @@ -3924,7 +3985,9 @@ mod tests { /// EQ | LT | Err /// LT | EQ | Err /// LT | LT | Err - fn do_test_split_minimum_stake_delegation(feature_set: FeatureSet) { + #[test_case(feature_set_old_behavior(); "old_behavior")] + #[test_case(feature_set_new_behavior(); "new_behavior")] + fn test_split_minimum_stake_delegation(feature_set: FeatureSet) { let minimum_delegation = crate::get_minimum_delegation(&feature_set); let rent = Rent::default(); let rent_exempt_reserve = rent.minimum_balance(StakeState::size_of()); @@ -4017,7 +4080,9 @@ mod tests { /// delegation is OK /// - Assert 2: splitting the full amount from an account that has less than the minimum /// delegation is not OK - fn do_test_split_full_amount_minimum_stake_delegation(feature_set: FeatureSet) { + #[test_case(feature_set_old_behavior(); "old_behavior")] + #[test_case(feature_set_new_behavior(); "new_behavior")] + fn test_split_full_amount_minimum_stake_delegation(feature_set: FeatureSet) { let minimum_delegation = crate::get_minimum_delegation(&feature_set); let rent = Rent::default(); let rent_exempt_reserve = rent.minimum_balance(StakeState::size_of()); @@ -4088,7 +4153,9 @@ mod tests { /// Ensure that `split()` correctly handles prefunded destination accounts from /// initialized stakes. When a destination account already has funds, ensure /// the minimum split amount reduces accordingly. - fn do_test_initialized_split_destination_minimum_balance(feature_set: FeatureSet) { + #[test_case(feature_set_old_behavior(); "old_behavior")] + #[test_case(feature_set_new_behavior(); "new_behavior")] + fn test_initialized_split_destination_minimum_balance(feature_set: FeatureSet) { let rent = Rent::default(); let rent_exempt_reserve = rent.minimum_balance(StakeState::size_of()); let source_address = Pubkey::new_unique(); @@ -4183,7 +4250,9 @@ mod tests { /// Ensure that `split()` correctly handles prefunded destination accounts from staked stakes. /// When a destination account already has funds, ensure the minimum split amount reduces /// accordingly. - fn do_test_staked_split_destination_minimum_balance( + #[test_case(feature_set_old_behavior(), &[Ok(()), Ok(())]; "old_behavior")] + #[test_case(feature_set_new_behavior(), &[ Err(InstructionError::InsufficientFunds), Err(InstructionError::InsufficientFunds) ] ; "new_behavior")] + fn test_staked_split_destination_minimum_balance( feature_set: FeatureSet, expected_results: &[Result<(), InstructionError>], ) { @@ -4334,7 +4403,9 @@ mod tests { /// Ensure that `withdraw()` respects the minimum delegation requirements /// - Assert 1: withdrawing so remaining stake is equal-to the minimum is OK /// - Assert 2: withdrawing so remaining stake is less-than the minimum is not OK - fn do_test_withdraw_minimum_stake_delegation(feature_set: FeatureSet) { + #[test_case(feature_set_old_behavior(); "old_behavior")] + #[test_case(feature_set_new_behavior(); "new_behavior")] + fn test_withdraw_minimum_stake_delegation(feature_set: FeatureSet) { let minimum_delegation = crate::get_minimum_delegation(&feature_set); let rent = Rent::default(); let rent_exempt_reserve = rent.minimum_balance(StakeState::size_of()); @@ -4437,7 +4508,14 @@ mod tests { /// 3. Deactives the delegation /// 4. Withdraws from the account such that the ending balance is *below* rent + minimum delegation /// 5. Re-delegates, now with less than the minimum delegation, but it still succeeds - fn do_test_behavior_withdrawal_then_redelegate_with_less_than_minimum_stake_delegation( + // + // The "old old" behavior relies on `validate_delegated_amount()` *not* checking if the + // stake amount meets the minimum delegation. Once the + // `stake_allow_zero_undelegated_amount` feature is activated, `the expected_result` + // parameter can be removed and consolidated. + #[test_case(feature_set_old_old_behavior(), Ok(()); "old_old_behavior")] + #[test_case(feature_set_new_behavior(), Err(StakeError::InsufficientDelegation.into()); "new_behavior")] + fn test_behavior_withdrawal_then_redelegate_with_less_than_minimum_stake_delegation( feature_set: FeatureSet, expected_result: Result<(), InstructionError>, ) { @@ -4617,7 +4695,9 @@ mod tests { ); } - fn do_test_split_source_uninitialized(feature_set: FeatureSet) { + #[test_case(feature_set_old_behavior(); "old_behavior")] + #[test_case(feature_set_new_behavior(); "new_behavior")] + fn test_split_source_uninitialized(feature_set: FeatureSet) { let rent = Rent::default(); let rent_exempt_reserve = rent.minimum_balance(StakeState::size_of()); let minimum_delegation = crate::get_minimum_delegation(&feature_set); @@ -4715,7 +4795,9 @@ mod tests { ); } - fn do_test_split_split_not_uninitialized(feature_set: FeatureSet) { + #[test_case(feature_set_old_behavior(); "old_behavior")] + #[test_case(feature_set_new_behavior(); "new_behavior")] + fn test_split_split_not_uninitialized(feature_set: FeatureSet) { let stake_lamports = 42; let stake_address = solana_sdk::pubkey::new_rand(); let stake_account = AccountSharedData::new_data_with_space( @@ -4764,7 +4846,9 @@ mod tests { } } - fn do_test_split_more_than_staked(feature_set: FeatureSet) { + #[test_case(feature_set_old_behavior(); "old_behavior")] + #[test_case(feature_set_new_behavior(); "new_behavior")] + fn test_split_more_than_staked(feature_set: FeatureSet) { let rent = Rent::default(); let rent_exempt_reserve = rent.minimum_balance(StakeState::size_of()); let minimum_delegation = crate::get_minimum_delegation(&feature_set); @@ -4821,7 +4905,9 @@ mod tests { ); } - fn do_test_split_with_rent(feature_set: FeatureSet) { + #[test_case(feature_set_old_behavior(); "old_behavior")] + #[test_case(feature_set_new_behavior(); "new_behavior")] + fn test_split_with_rent(feature_set: FeatureSet) { let rent = Rent::default(); let rent_exempt_reserve = rent.minimum_balance(StakeState::size_of()); let minimum_delegation = crate::get_minimum_delegation(&feature_set); @@ -4929,7 +5015,9 @@ mod tests { } } - fn do_test_split_to_account_with_rent_exempt_reserve(feature_set: FeatureSet) { + #[test_case(feature_set_old_behavior(); "old_behavior")] + #[test_case(feature_set_new_behavior(); "new_behavior")] + fn test_split_to_account_with_rent_exempt_reserve(feature_set: FeatureSet) { let rent = Rent::default(); let rent_exempt_reserve = rent.minimum_balance(StakeState::size_of()); let minimum_delegation = crate::get_minimum_delegation(&feature_set); @@ -5052,7 +5140,9 @@ mod tests { } } - fn do_test_split_from_larger_sized_account(feature_set: FeatureSet) { + #[test_case(feature_set_old_behavior(); "old_behavior")] + #[test_case(feature_set_new_behavior(); "new_behavior")] + fn test_split_from_larger_sized_account(feature_set: FeatureSet) { let rent = Rent::default(); let source_larger_rent_exempt_reserve = rent.minimum_balance(StakeState::size_of() + 100); let split_rent_exempt_reserve = rent.minimum_balance(StakeState::size_of()); @@ -5181,7 +5271,9 @@ mod tests { } } - fn do_test_split_from_smaller_sized_account(feature_set: FeatureSet) { + #[test_case(feature_set_old_behavior(); "old_behavior")] + #[test_case(feature_set_new_behavior(); "new_behavior")] + fn test_split_from_smaller_sized_account(feature_set: FeatureSet) { let rent = Rent::default(); let source_smaller_rent_exempt_reserve = rent.minimum_balance(StakeState::size_of()); let split_rent_exempt_reserve = rent.minimum_balance(StakeState::size_of() + 100); @@ -5258,7 +5350,9 @@ mod tests { } } - fn do_test_split_100_percent_of_source(feature_set: FeatureSet) { + #[test_case(feature_set_old_behavior(); "old_behavior")] + #[test_case(feature_set_new_behavior(); "new_behavior")] + fn test_split_100_percent_of_source(feature_set: FeatureSet) { let rent = Rent::default(); let rent_exempt_reserve = rent.minimum_balance(StakeState::size_of()); let minimum_delegation = crate::get_minimum_delegation(&feature_set); @@ -5352,7 +5446,9 @@ mod tests { } } - fn do_test_split_100_percent_of_source_to_account_with_lamports(feature_set: FeatureSet) { + #[test_case(feature_set_old_behavior(); "old_behavior")] + #[test_case(feature_set_new_behavior(); "new_behavior")] + fn test_split_100_percent_of_source_to_account_with_lamports(feature_set: FeatureSet) { let rent = Rent::default(); let rent_exempt_reserve = rent.minimum_balance(StakeState::size_of()); let minimum_delegation = crate::get_minimum_delegation(&feature_set); @@ -5446,7 +5542,9 @@ mod tests { } } - fn do_test_split_rent_exemptness(feature_set: FeatureSet) { + #[test_case(feature_set_old_behavior(); "old_behavior")] + #[test_case(feature_set_new_behavior(); "new_behavior")] + fn test_split_rent_exemptness(feature_set: FeatureSet) { let rent = Rent::default(); let source_rent_exempt_reserve = rent.minimum_balance(StakeState::size_of() + 100); let split_rent_exempt_reserve = rent.minimum_balance(StakeState::size_of()); @@ -5582,7 +5680,9 @@ mod tests { } } - fn do_test_merge(feature_set: FeatureSet) { + #[test_case(feature_set_old_behavior(); "old_behavior")] + #[test_case(feature_set_new_behavior(); "new_behavior")] + fn test_merge(feature_set: FeatureSet) { let stake_address = solana_sdk::pubkey::new_rand(); let merge_from_address = solana_sdk::pubkey::new_rand(); let authorized_address = solana_sdk::pubkey::new_rand(); @@ -5710,7 +5810,9 @@ mod tests { } } - fn do_test_merge_self_fails(feature_set: FeatureSet) { + #[test_case(feature_set_old_behavior(); "old_behavior")] + #[test_case(feature_set_new_behavior(); "new_behavior")] + fn test_merge_self_fails(feature_set: FeatureSet) { let stake_address = solana_sdk::pubkey::new_rand(); let authorized_address = solana_sdk::pubkey::new_rand(); let rent = Rent::default(); @@ -5785,7 +5887,9 @@ mod tests { ); } - fn do_test_merge_incorrect_authorized_staker(feature_set: FeatureSet) { + #[test_case(feature_set_old_behavior(); "old_behavior")] + #[test_case(feature_set_new_behavior(); "new_behavior")] + fn test_merge_incorrect_authorized_staker(feature_set: FeatureSet) { let stake_address = solana_sdk::pubkey::new_rand(); let merge_from_address = solana_sdk::pubkey::new_rand(); let authorized_address = solana_sdk::pubkey::new_rand(); @@ -5877,7 +5981,9 @@ mod tests { } } - fn do_test_merge_invalid_account_data(feature_set: FeatureSet) { + #[test_case(feature_set_old_behavior(); "old_behavior")] + #[test_case(feature_set_new_behavior(); "new_behavior")] + fn test_merge_invalid_account_data(feature_set: FeatureSet) { let stake_address = solana_sdk::pubkey::new_rand(); let merge_from_address = solana_sdk::pubkey::new_rand(); let authorized_address = solana_sdk::pubkey::new_rand(); @@ -5956,7 +6062,9 @@ mod tests { } } - fn do_test_merge_fake_stake_source(feature_set: FeatureSet) { + #[test_case(feature_set_old_behavior(); "old_behavior")] + #[test_case(feature_set_new_behavior(); "new_behavior")] + fn test_merge_fake_stake_source(feature_set: FeatureSet) { let stake_address = solana_sdk::pubkey::new_rand(); let merge_from_address = solana_sdk::pubkey::new_rand(); let authorized_address = solana_sdk::pubkey::new_rand(); @@ -6025,7 +6133,9 @@ mod tests { ); } - fn do_test_merge_active_stake(feature_set: FeatureSet) { + #[test_case(feature_set_old_behavior(); "old_behavior")] + #[test_case(feature_set_new_behavior(); "new_behavior")] + fn test_merge_active_stake(feature_set: FeatureSet) { let stake_address = solana_sdk::pubkey::new_rand(); let merge_from_address = solana_sdk::pubkey::new_rand(); let authorized_address = solana_sdk::pubkey::new_rand(); @@ -6283,7 +6393,9 @@ mod tests { ); } - fn do_test_stake_get_minimum_delegation(feature_set: FeatureSet) { + #[test_case(feature_set_old_behavior(); "old_behavior")] + #[test_case(feature_set_new_behavior(); "new_behavior")] + fn test_stake_get_minimum_delegation(feature_set: FeatureSet) { let stake_address = Pubkey::new_unique(); let stake_account = create_default_stake_account(); let instruction_data = serialize(&StakeInstruction::GetMinimumDelegation).unwrap(); @@ -6336,7 +6448,9 @@ mod tests { // disabled | bad | some || Err InvalidInstructionData // disabled | good | none || Err NotEnoughAccountKeys // disabled | bad | none || Err NotEnoughAccountKeys - fn do_test_stake_process_instruction_error_ordering(feature_set: FeatureSet) { + #[test_case(feature_set_old_behavior(); "old_behavior")] + #[test_case(feature_set_new_behavior(); "new_behavior")] + fn test_stake_process_instruction_error_ordering(feature_set: FeatureSet) { let rent = Rent::default(); let rent_address = sysvar::rent::id(); let rent_account = account::create_account_shared_data_for_test(&rent); @@ -6440,7 +6554,9 @@ mod tests { } } - fn do_test_deactivate_delinquent(feature_set: FeatureSet) { + #[test_case(feature_set_old_behavior(); "old_behavior")] + #[test_case(feature_set_new_behavior(); "new_behavior")] + fn test_deactivate_delinquent(feature_set: FeatureSet) { let feature_set = Arc::new(feature_set); let mut sysvar_cache_override = SysvarCache::default(); @@ -6707,398 +6823,4 @@ mod tests { Err(StakeError::MinimumDelinquentEpochsForDeactivationNotMet.into()), ); } - - mod old_behavior { - use super::*; - - fn new_feature_set() -> FeatureSet { - let mut feature_set = FeatureSet::all_enabled(); - feature_set.deactivate(&feature_set::stake_raise_minimum_delegation_to_1_sol::id()); - feature_set - } - - #[test] - fn test_stake_process_instruction() { - do_test_stake_process_instruction(new_feature_set()); - } - #[test] - fn test_stake_process_instruction_decode_bail() { - do_test_stake_process_instruction_decode_bail(new_feature_set()); - } - #[test] - fn test_stake_checked_instructions() { - do_test_stake_checked_instructions(new_feature_set()); - } - #[test] - fn test_stake_initialize() { - do_test_stake_initialize(new_feature_set()); - } - #[test] - fn test_authorize() { - do_test_authorize(new_feature_set()); - } - #[test] - fn test_authorize_override() { - do_test_authorize_override(new_feature_set()); - } - #[test] - fn test_authorize_with_seed() { - do_test_authorize_with_seed(new_feature_set()); - } - #[test] - fn test_authorize_delegated_stake() { - do_test_authorize_delegated_stake(new_feature_set()); - } - #[test] - fn test_stake_delegate() { - do_test_stake_delegate(new_feature_set()); - } - #[test] - fn test_redelegate_consider_balance_changes() { - do_test_redelegate_consider_balance_changes(new_feature_set()); - } - #[test] - fn test_split() { - do_test_split(new_feature_set()); - } - #[test] - fn test_withdraw_stake() { - do_test_withdraw_stake(new_feature_set()); - } - #[test] - fn test_withdraw_stake_before_warmup() { - do_test_withdraw_stake_before_warmup(new_feature_set()); - } - #[test] - fn test_withdraw_lockup() { - do_test_withdraw_lockup(new_feature_set()); - } - #[test] - fn test_withdraw_rent_exempt() { - do_test_withdraw_rent_exempt(new_feature_set()); - } - #[test] - fn test_deactivate() { - do_test_deactivate(new_feature_set()); - } - #[test] - fn test_set_lockup() { - do_test_set_lockup(new_feature_set()); - } - #[test] - fn test_initialize_minimum_balance() { - do_test_initialize_minimum_balance(new_feature_set()); - } - #[test] - fn test_delegate_minimum_stake_delegation() { - do_test_delegate_minimum_stake_delegation(new_feature_set()); - } - #[test] - fn test_split_minimum_stake_delegation() { - do_test_split_minimum_stake_delegation(new_feature_set()); - } - #[test] - fn test_split_full_amount_minimum_stake_delegation() { - do_test_split_full_amount_minimum_stake_delegation(new_feature_set()); - } - #[test] - fn test_initialized_split_destination_minimum_balance() { - do_test_initialized_split_destination_minimum_balance(new_feature_set()); - } - #[test] - fn test_staked_split_destination_minimum_balance() { - do_test_staked_split_destination_minimum_balance(new_feature_set(), &[Ok(()), Ok(())]); - } - #[test] - fn test_withdraw_minimum_stake_delegation() { - do_test_withdraw_minimum_stake_delegation(new_feature_set()); - } - #[test] - fn test_behavior_withdrawal_then_redelegate_with_less_than_minimum_stake_delegation() { - let mut feature_set = new_feature_set(); - // The "old" behavior relies on `validate_delegated_amount()` *not* checking if the - // stake amount meets the minimum delegation. Once the - // `stake_allow_zero_undelegated_amount` feature is activated, `the expected_result` - // parameter can be removed and consolidated. - feature_set.deactivate(&feature_set::stake_allow_zero_undelegated_amount::id()); - do_test_behavior_withdrawal_then_redelegate_with_less_than_minimum_stake_delegation( - feature_set, - Ok(()), - ); - } - #[test] - fn test_split_source_uninitialized() { - do_test_split_source_uninitialized(new_feature_set()); - } - #[test] - fn test_split_split_not_uninitialized() { - do_test_split_split_not_uninitialized(new_feature_set()); - } - #[test] - fn test_split_more_than_staked() { - do_test_split_more_than_staked(new_feature_set()); - } - #[test] - fn test_split_with_rent() { - do_test_split_with_rent(new_feature_set()); - } - #[test] - fn test_split_to_account_with_rent_exempt_reserve() { - do_test_split_to_account_with_rent_exempt_reserve(new_feature_set()); - } - #[test] - fn test_split_from_larger_sized_account() { - do_test_split_from_larger_sized_account(new_feature_set()); - } - #[test] - fn test_split_from_smaller_sized_account() { - do_test_split_from_smaller_sized_account(new_feature_set()); - } - #[test] - fn test_split_100_percent_of_source() { - do_test_split_100_percent_of_source(new_feature_set()); - } - #[test] - fn test_split_100_percent_of_source_to_account_with_lamports() { - do_test_split_100_percent_of_source_to_account_with_lamports(new_feature_set()); - } - #[test] - fn test_split_rent_exemptness() { - do_test_split_rent_exemptness(new_feature_set()); - } - #[test] - fn test_merge() { - do_test_merge(new_feature_set()); - } - #[test] - fn test_merge_self_fails() { - do_test_merge_self_fails(new_feature_set()); - } - #[test] - fn test_merge_incorrect_authorized_staker() { - do_test_merge_incorrect_authorized_staker(new_feature_set()); - } - #[test] - fn test_merge_invalid_account_data() { - do_test_merge_invalid_account_data(new_feature_set()); - } - #[test] - fn test_merge_fake_stake_source() { - do_test_merge_fake_stake_source(new_feature_set()); - } - #[test] - fn test_merge_active_stake() { - do_test_merge_active_stake(new_feature_set()); - } - #[test] - fn test_stake_get_minimum_delegation() { - do_test_stake_get_minimum_delegation(new_feature_set()); - } - #[test] - fn test_stake_process_instruction_error_ordering() { - do_test_stake_process_instruction_error_ordering(new_feature_set()); - } - #[test] - fn test_deactivate_delinquent() { - do_test_deactivate_delinquent(new_feature_set()); - } - } - - mod new_behavior { - use super::*; - - fn new_feature_set() -> FeatureSet { - FeatureSet::all_enabled() - } - - #[test] - fn test_stake_process_instruction() { - do_test_stake_process_instruction(new_feature_set()); - } - #[test] - fn test_spoofed_stake_accounts() { - do_test_spoofed_stake_accounts(new_feature_set()); - } - #[test] - fn test_stake_process_instruction_decode_bail() { - do_test_stake_process_instruction_decode_bail(new_feature_set()); - } - #[test] - fn test_stake_checked_instructions() { - do_test_stake_checked_instructions(new_feature_set()); - } - #[test] - fn test_stake_initialize() { - do_test_stake_initialize(new_feature_set()); - } - #[test] - fn test_authorize() { - do_test_authorize(new_feature_set()); - } - #[test] - fn test_authorize_override() { - do_test_authorize_override(new_feature_set()); - } - #[test] - fn test_authorize_with_seed() { - do_test_authorize_with_seed(new_feature_set()); - } - #[test] - fn test_authorize_delegated_stake() { - do_test_authorize_delegated_stake(new_feature_set()); - } - #[test] - fn test_stake_delegate() { - do_test_stake_delegate(new_feature_set()); - } - #[test] - fn test_redelegate_consider_balance_changes() { - do_test_redelegate_consider_balance_changes(new_feature_set()); - } - #[test] - fn test_split() { - do_test_split(new_feature_set()); - } - #[test] - fn test_withdraw_stake() { - do_test_withdraw_stake(new_feature_set()); - } - #[test] - fn test_withdraw_stake_before_warmup() { - do_test_withdraw_stake_before_warmup(new_feature_set()); - } - #[test] - fn test_withdraw_lockup() { - do_test_withdraw_lockup(new_feature_set()); - } - #[test] - fn test_withdraw_rent_exempt() { - do_test_withdraw_rent_exempt(new_feature_set()); - } - #[test] - fn test_deactivate() { - do_test_deactivate(new_feature_set()); - } - #[test] - fn test_set_lockup() { - do_test_set_lockup(new_feature_set()); - } - #[test] - fn test_initialize_minimum_balance() { - do_test_initialize_minimum_balance(new_feature_set()); - } - #[test] - fn test_delegate_minimum_stake_delegation() { - do_test_delegate_minimum_stake_delegation(new_feature_set()); - } - #[test] - fn test_split_minimum_stake_delegation() { - do_test_split_minimum_stake_delegation(new_feature_set()); - } - #[test] - fn test_split_full_amount_minimum_stake_delegation() { - do_test_split_full_amount_minimum_stake_delegation(new_feature_set()); - } - #[test] - fn test_initialized_split_destination_minimum_balance() { - do_test_initialized_split_destination_minimum_balance(new_feature_set()); - } - #[test] - fn test_staked_split_destination_minimum_balance() { - do_test_staked_split_destination_minimum_balance( - new_feature_set(), - &[ - Err(InstructionError::InsufficientFunds), - Err(InstructionError::InsufficientFunds), - ], - ); - } - #[test] - fn test_withdraw_minimum_stake_delegation() { - do_test_withdraw_minimum_stake_delegation(new_feature_set()); - } - #[test] - fn test_behavior_withdrawal_then_redelegate_with_less_than_minimum_stake_delegation() { - do_test_behavior_withdrawal_then_redelegate_with_less_than_minimum_stake_delegation( - new_feature_set(), - Err(StakeError::InsufficientDelegation.into()), - ); - } - #[test] - fn test_split_source_uninitialized() { - do_test_split_source_uninitialized(new_feature_set()); - } - #[test] - fn test_split_split_not_uninitialized() { - do_test_split_split_not_uninitialized(new_feature_set()); - } - #[test] - fn test_split_more_than_staked() { - do_test_split_more_than_staked(new_feature_set()); - } - #[test] - fn test_split_with_rent() { - do_test_split_with_rent(new_feature_set()); - } - #[test] - fn test_split_to_account_with_rent_exempt_reserve() { - do_test_split_to_account_with_rent_exempt_reserve(new_feature_set()); - } - #[test] - fn test_split_from_larger_sized_account() { - do_test_split_from_larger_sized_account(new_feature_set()); - } - #[test] - fn test_split_from_smaller_sized_account() { - do_test_split_from_smaller_sized_account(new_feature_set()); - } - #[test] - fn test_split_100_percent_of_source() { - do_test_split_100_percent_of_source(new_feature_set()); - } - #[test] - fn test_split_100_percent_of_source_to_account_with_lamports() { - do_test_split_100_percent_of_source_to_account_with_lamports(new_feature_set()); - } - #[test] - fn test_split_rent_exemptness() { - do_test_split_rent_exemptness(new_feature_set()); - } - #[test] - fn test_merge() { - do_test_merge(new_feature_set()); - } - #[test] - fn test_merge_self_fails() { - do_test_merge_self_fails(new_feature_set()); - } - #[test] - fn test_merge_incorrect_authorized_staker() { - do_test_merge_incorrect_authorized_staker(new_feature_set()); - } - #[test] - fn test_merge_invalid_account_data() { - do_test_merge_invalid_account_data(new_feature_set()); - } - #[test] - fn test_merge_fake_stake_source() { - do_test_merge_fake_stake_source(new_feature_set()); - } - #[test] - fn test_merge_active_stake() { - do_test_merge_active_stake(new_feature_set()); - } - #[test] - fn test_stake_get_minimum_delegation() { - do_test_stake_get_minimum_delegation(new_feature_set()); - } - #[test] - fn test_stake_process_instruction_error_ordering() { - do_test_stake_process_instruction_error_ordering(new_feature_set()); - } - #[test] - fn test_deactivate_delinquent() { - do_test_deactivate_delinquent(new_feature_set()); - } - } }