use super::*;
use crate::{asset, ledger::StakingLedgerInspect, mock::Session};
use frame_election_provider_support::{
bounds::{DataProviderBounds, ElectionBoundsBuilder},
SortedListProvider,
};
use frame_support::{
assert_noop, assert_ok, assert_storage_noop, hypothetically,
pallet_prelude::*,
traits::{InspectLockableCurrency, ReservableCurrency},
};
use mock::*;
use sp_runtime::{
assert_eq_error_rate, bounded_vec, traits::BadOrigin, Perbill, Percent, TokenError,
};
use sp_staking::{Stake, StakingAccount, StakingInterface};
use substrate_test_utils::assert_eq_uvec;
mod bonding;
mod configs;
mod controller;
mod election_data_provider;
mod election_provider;
mod era_rotation;
mod force_unstake_kill_stash;
mod ledger;
mod nominators_no_slashing;
mod payout_stakers;
mod slashing;
mod try_state;
#[test]
fn basic_setup_session_queuing_should_work() {
ExtBuilder::default().nominate(false).build_and_execute(|| {
assert_eq!(Session::current_index(), 3);
for i in 1..5 {
let _ = asset::set_stakeable_balance::<Test>(&i, 2000);
}
assert_ok!(Staking::bond(RuntimeOrigin::signed(3), 1500, RewardDestination::Account(3)));
assert_ok!(Staking::validate(RuntimeOrigin::signed(3), ValidatorPrefs::default()));
assert_eq_uvec!(Session::validators(), vec![21, 11]);
Session::roll_until_session(4);
assert_eq_uvec!(Session::validators(), vec![21, 11]);
assert_eq!(Session::queued_validators(), None);
Session::roll_until_session(5);
assert_eq_uvec!(Session::validators(), vec![21, 11]);
assert_eq_uvec!(Session::queued_validators().unwrap(), vec![21, 3]);
Session::roll_until_session(6);
assert_eq_uvec!(Session::validators(), vec![21, 3]);
assert_eq!(Session::queued_validators(), None);
Staking::chill(RuntimeOrigin::signed(3)).unwrap();
Session::roll_until_session(7);
assert_eq_uvec!(Session::validators(), vec![21, 3]);
Session::roll_until_session(8);
assert_eq_uvec!(Session::validators(), vec![21, 3]);
Session::roll_until_session(9);
assert_eq_uvec!(Session::validators(), vec![21, 11]);
assert_eq!(
Staking::ledger(3.into()).unwrap(),
StakingLedgerInspect {
stash: 3,
total: 1500,
active: 1500,
unlocking: Default::default(),
}
);
assert_noop!(Balances::reserve(&3, 501), DispatchError::ConsumerRemaining);
assert_ok!(Balances::reserve(&3, 409));
});
}
#[test]
fn basic_setup_works() {
ExtBuilder::default().build_and_execute(|| {
assert_eq!(Staking::bonded(&11), Some(11));
assert_eq!(Staking::bonded(&21), Some(21));
assert_eq!(Staking::bonded(&1), None);
assert_eq!(
Ledger::get(&11).unwrap(),
StakingLedgerInspect::<Test> {
stash: 11,
total: 1000,
active: 1000,
unlocking: Default::default(),
}
);
assert_eq!(
Ledger::get(&21).unwrap(),
StakingLedgerInspect::<Test> {
stash: 21,
total: 1000,
active: 1000,
unlocking: Default::default(),
}
);
assert!(Staking::ledger(1.into()).is_err());
assert_eq_uvec!(
<Validators<Test>>::iter().collect::<Vec<_>>(),
vec![
(31, ValidatorPrefs::default()),
(21, ValidatorPrefs::default()),
(11, ValidatorPrefs::default())
]
);
assert_eq!(
Staking::ledger(101.into()).unwrap(),
StakingLedgerInspect {
stash: 101,
total: 500,
active: 500,
unlocking: Default::default(),
}
);
assert_eq!(Nominators::<Test>::get(101).unwrap().targets, vec![11, 21]);
assert_eq!(
Staking::eras_stakers(active_era(), &11),
Exposure {
total: 1250,
own: 1000,
others: vec![IndividualExposure { who: 101, value: 250 }]
},
);
assert_eq!(
Staking::eras_stakers(active_era(), &21),
Exposure {
total: 1250,
own: 1000,
others: vec![IndividualExposure { who: 101, value: 250 }]
},
);
assert_eq!(active_era(), 1);
assert_eq!(current_era(), 1);
assert_eq!(Session::current_index(), 3);
assert_eq_uvec!(Session::validators(), vec![11, 21]);
assert_eq!(ErasTotalStake::<Test>::get(active_era()), 2500);
assert_eq!(ValidatorCount::<Test>::get(), 2);
assert_eq!(ForceEra::<Test>::get(), Forcing::NotForcing);
assert_eq!(
staking_events(),
vec![
Event::SessionRotated { starting_session: 1, active_era: 0, planned_era: 1 },
Event::PagedElectionProceeded { page: 0, result: Ok(2) },
Event::SessionRotated { starting_session: 2, active_era: 0, planned_era: 1 },
Event::EraPaid { era_index: 0, validator_payout: 7500, remainder: 7500 },
Event::SessionRotated { starting_session: 3, active_era: 1, planned_era: 1 }
]
);
});
}
#[test]
fn basic_setup_session_rotation() {
ExtBuilder::default().build_and_execute(|| {
assert_eq!(active_era(), 1);
assert_eq!(current_era(), 1);
assert_eq!(Session::current_index(), 3);
assert_eq_uvec!(Session::validators(), vec![11, 21]);
Session::roll_to_next_session();
assert_eq!(Session::current_index(), 4);
assert_eq!(current_era(), 2);
assert_eq!(active_era(), 1);
Session::roll_to_next_session();
assert_eq!(Session::current_index(), 5);
assert_eq!(active_era(), 1);
assert_eq!(current_era(), 2);
Session::roll_to_next_session();
assert_eq!(Session::current_index(), 6);
assert_eq!(active_era(), 2);
assert_eq!(current_era(), 2);
});
}
#[test]
fn basic_setup_sessions_per_era() {
ExtBuilder::default()
.session_per_era(6)
.no_flush_events()
.build_and_execute(|| {
assert_eq!(
staking_events_since_last_call(),
vec![
Event::SessionRotated { starting_session: 1, active_era: 0, planned_era: 0 },
Event::SessionRotated { starting_session: 2, active_era: 0, planned_era: 0 },
Event::SessionRotated { starting_session: 3, active_era: 0, planned_era: 0 },
Event::SessionRotated { starting_session: 4, active_era: 0, planned_era: 1 },
Event::PagedElectionProceeded { page: 0, result: Ok(2) },
Event::SessionRotated { starting_session: 5, active_era: 0, planned_era: 1 },
Event::EraPaid { era_index: 0, validator_payout: 15000, remainder: 15000 },
Event::SessionRotated { starting_session: 6, active_era: 1, planned_era: 1 }
]
);
assert_eq!(Session::current_index(), 6);
assert_eq!(active_era(), 1);
Session::roll_until_active_era(2);
assert_eq!(Session::current_index(), 12);
assert_eq!(active_era(), 2);
assert_eq!(
staking_events_since_last_call(),
vec![
Event::SessionRotated { starting_session: 7, active_era: 1, planned_era: 1 },
Event::SessionRotated { starting_session: 8, active_era: 1, planned_era: 1 },
Event::SessionRotated { starting_session: 9, active_era: 1, planned_era: 1 },
Event::SessionRotated { starting_session: 10, active_era: 1, planned_era: 2 },
Event::PagedElectionProceeded { page: 0, result: Ok(2) },
Event::SessionRotated { starting_session: 11, active_era: 1, planned_era: 2 },
Event::EraPaid { era_index: 1, validator_payout: 15000, remainder: 15000 },
Event::SessionRotated { starting_session: 12, active_era: 2, planned_era: 2 }
]
);
});
}
mod try_state_assertions {
use super::*;
#[test]
#[should_panic = "called `Result::unwrap()` on an `Err` value: Other(\"number of entries in payee storage items does not match the number of bonded ledgers\")"]
fn check_payee_invariant1_works() {
ExtBuilder::default().build_and_execute(|| {
let rogue_ledger = StakingLedger::<Test>::new(123456, 20);
Ledger::<Test>::insert(123456, rogue_ledger);
})
}
#[test]
#[should_panic = "called `Result::unwrap()` on an `Err` value: Other(\"number of entries in payee storage items does not match the number of bonded ledgers\")"]
fn check_payee_invariant2_works() {
ExtBuilder::default().build_and_execute(|| {
Payee::<Test>::insert(1111, RewardDestination::Staked);
})
}
}
mod validator_count {
use super::*;
#[test]
fn increase_validator_count_errors() {
ExtBuilder::default().build_and_execute(|| {
MaxValidatorSet::set(50);
MaxWinnersPerPage::set(50);
assert_ok!(Staking::set_validator_count(RuntimeOrigin::root(), 40));
assert_ok!(Staking::increase_validator_count(RuntimeOrigin::root(), 6));
assert_eq!(ValidatorCount::<Test>::get(), 46);
assert_noop!(
Staking::increase_validator_count(RuntimeOrigin::root(), 5),
Error::<Test>::TooManyValidators,
);
})
}
#[test]
fn scale_validator_count_errors() {
ExtBuilder::default().build_and_execute(|| {
MaxValidatorSet::set(50);
MaxWinnersPerPage::set(50);
assert_ok!(Staking::set_validator_count(RuntimeOrigin::root(), 20));
assert_ok!(Staking::scale_validator_count(
RuntimeOrigin::root(),
Percent::from_percent(200)
));
assert_eq!(ValidatorCount::<Test>::get(), 40);
assert_noop!(
Staking::scale_validator_count(RuntimeOrigin::root(), Percent::from_percent(126)),
Error::<Test>::TooManyValidators,
);
})
}
#[test]
fn cannot_set_unsupported_validator_count() {
ExtBuilder::default().build_and_execute(|| {
MaxValidatorSet::set(50);
MaxWinnersPerPage::set(50);
assert_ok!(Staking::set_validator_count(RuntimeOrigin::root(), 30));
assert_ok!(Staking::set_validator_count(RuntimeOrigin::root(), 50));
assert_noop!(
Staking::set_validator_count(RuntimeOrigin::root(), 51),
Error::<Test>::TooManyValidators,
);
})
}
}
mod staking_interface {
use frame_support::storage::with_storage_layer;
use sp_staking::StakingInterface;
use super::*;
#[test]
fn force_unstake_with_slash_works() {
ExtBuilder::default().build_and_execute(|| {
let _ = with_storage_layer::<(), _, _>(|| {
assert_eq!(Staking::bonded(&11), Some(11));
assert_ok!(<Staking as StakingInterface>::force_unstake(11));
Err(DispatchError::from("revert"))
});
assert_eq!(Staking::bonded(&11), Some(11));
add_slash(11);
assert_ok!(<Staking as StakingInterface>::force_unstake(11));
});
}
#[test]
fn do_withdraw_unbonded_can_kill_stash_with_existential_deposit_zero() {
ExtBuilder::default()
.existential_deposit(0)
.nominate(false)
.build_and_execute(|| {
assert_eq!(Staking::bonded(&11), Some(11));
assert_eq!(
Staking::ledger(11.into()).unwrap(),
StakingLedgerInspect {
stash: 11,
total: 1000,
active: 1000,
unlocking: Default::default(),
}
);
assert_eq!(
Staking::eras_stakers(active_era(), &11),
Exposure { total: 1000, own: 1000, others: vec![] }
);
Staking::chill(RuntimeOrigin::signed(11)).unwrap();
Staking::unbond(RuntimeOrigin::signed(11), 1000).unwrap();
assert_eq!(
Staking::ledger(11.into()).unwrap(),
StakingLedgerInspect {
stash: 11,
total: 1000,
active: 0,
unlocking: bounded_vec![UnlockChunk { value: 1000, era: 4 }],
},
);
Session::roll_until_active_era(4);
assert_ok!(Staking::withdraw_unbonded(RuntimeOrigin::signed(11), 0));
assert!(!<Ledger<Test>>::contains_key(&11));
assert!(!<Bonded<Test>>::contains_key(&11));
assert!(!<Validators<Test>>::contains_key(&11));
assert!(!<Payee<Test>>::contains_key(&11));
assert_eq!(asset::staked::<Test>(&11), 0);
});
}
#[test]
fn status() {
ExtBuilder::default().build_and_execute(|| {
assert_eq!(Staking::status(&11).unwrap(), StakerStatus::Validator);
assert!(Staking::status(&10).is_err());
assert_eq!(Staking::status(&101).unwrap(), StakerStatus::Nominator(vec![11, 21]));
assert!(Staking::status(&100).is_err());
assert_eq!(Staking::status(&41).unwrap(), StakerStatus::Idle);
assert!(Staking::status(&40).is_err());
assert!(Staking::status(&42).is_err());
})
}
}
mod staking_unchecked {
use sp_staking::{Stake, StakingInterface, StakingUnchecked};
use super::*;
#[test]
fn virtual_bond_does_not_lock_or_hold() {
ExtBuilder::default().build_and_execute(|| {
assert_eq!(asset::total_balance::<Test>(&10), 0);
assert_ok!(<Staking as StakingUnchecked>::virtual_bond(&10, 100, &15));
assert_eq!(asset::staked::<Test>(&10), 0);
assert_ok!(<Staking as StakingInterface>::bond_extra(&10, 1000));
assert_eq!(
<Staking as StakingInterface>::stake(&10),
Ok(Stake { total: 1100, active: 1100 })
);
assert_ok!(<Staking as StakingInterface>::unbond(&10, 200));
assert_eq!(
Staking::ledger(10.into()).unwrap(),
StakingLedgerInspect {
stash: 10,
total: 1100,
active: 1100 - 200,
unlocking: bounded_vec![UnlockChunk { value: 200, era: 1 + 3 }],
}
);
assert_eq!(
<Staking as StakingInterface>::stake(&10),
Ok(Stake { total: 1100, active: 900 })
);
assert_eq!(asset::staked::<Test>(&10), 0);
Session::roll_until_active_era(2);
assert_ok!(<Staking as StakingInterface>::withdraw_unbonded(10, 0));
assert_eq!(
<Staking as StakingInterface>::stake(&10),
Ok(Stake { total: 1100, active: 900 })
);
Session::roll_until_active_era(4);
assert_ok!(<Staking as StakingInterface>::withdraw_unbonded(10, 0));
assert_eq!(
<Staking as StakingInterface>::stake(&10),
Ok(Stake { total: 900, active: 900 })
);
assert_ok!(<Staking as StakingInterface>::unbond(&10, 900));
assert_eq!(
<Staking as StakingInterface>::stake(&10),
Ok(Stake { total: 900, active: 0 })
);
Session::roll_until_active_era(7);
assert_ok!(<Staking as StakingInterface>::withdraw_unbonded(10, 0));
assert_eq!(Staking::ledger(10.into()), Err(Error::<Test>::NotStash));
assert_eq!(VirtualStakers::<Test>::contains_key(10), false);
})
}
#[test]
fn virtual_staker_cannot_pay_reward_to_self_account() {
ExtBuilder::default().build_and_execute(|| {
assert_noop!(
<Staking as StakingUnchecked>::virtual_bond(&10, 100, &10),
Error::<Test>::RewardDestinationRestricted
);
assert_ok!(<Staking as StakingUnchecked>::virtual_bond(&10, 100, &11));
assert_noop!(
<Staking as StakingInterface>::set_payee(&10, &10),
Error::<Test>::RewardDestinationRestricted
);
});
}
#[test]
fn virtual_staker_cannot_bond_again() {
ExtBuilder::default().build_and_execute(|| {
bond_virtual_nominator(200, 201, 500, vec![11, 21]);
assert_noop!(
<Staking as StakingUnchecked>::virtual_bond(&200, 200, &201),
Error::<Test>::AlreadyBonded
);
assert_noop!(
<Staking as StakingUnchecked>::virtual_bond(&200, 200, &202),
Error::<Test>::AlreadyBonded
);
assert_noop!(
<Staking as StakingInterface>::bond(&200, 200, &202),
Error::<Test>::AlreadyBonded
);
});
}
#[test]
fn normal_staker_cannot_virtual_bond() {
ExtBuilder::default().build_and_execute(|| {
assert_noop!(
<Staking as StakingUnchecked>::virtual_bond(&101, 200, &102),
Error::<Test>::AlreadyBonded
);
assert_noop!(
<Staking as StakingUnchecked>::virtual_bond(&21, 200, &22),
Error::<Test>::AlreadyBonded
);
});
}
#[test]
fn migrate_virtual_staker() {
ExtBuilder::default().build_and_execute(|| {
asset::set_stakeable_balance::<Test>(&200, 2000);
assert_ok!(Staking::bond(RuntimeOrigin::signed(200), 1000, RewardDestination::Staked));
assert_eq!(asset::staked::<Test>(&200), 1000);
assert_ok!(<Staking as StakingUnchecked>::migrate_to_virtual_staker(&200));
assert_ok!(<Staking as StakingInterface>::set_payee(&200, &201));
assert_eq!(asset::staked::<Test>(&200), 0);
assert_eq!(Pallet::<Test>::is_virtual_staker(&200), true);
});
}
#[test]
fn virtual_nominators_are_lazily_slashed() {
ExtBuilder::default().build_and_execute(|| {
let slash_percent = Perbill::from_percent(5);
let initial_exposure = Staking::eras_stakers(active_era(), &11);
assert_eq!(initial_exposure.others.first().unwrap().who, 101);
assert_ok!(<Staking as StakingUnchecked>::migrate_to_virtual_staker(&101));
assert_ok!(<Staking as StakingInterface>::set_payee(&101, &102));
let nominator_stake = Staking::ledger(101.into()).unwrap().active;
let nominator_balance = asset::stakeable_balance::<Test>(&101);
let validator_stake = Staking::ledger(11.into()).unwrap().active;
let validator_balance = asset::stakeable_balance::<Test>(&11);
let exposed_stake = initial_exposure.total;
let exposed_validator = initial_exposure.own;
let exposed_nominator = initial_exposure.others.first().unwrap().value;
add_slash_with_percent(11, 5);
Session::roll_next();
let slash_amount = slash_percent * exposed_stake;
let validator_share =
Perbill::from_rational(exposed_validator, exposed_stake) * slash_amount;
let nominator_share =
Perbill::from_rational(exposed_nominator, exposed_stake) * slash_amount;
assert!(validator_share > 0);
assert!(nominator_share > 0);
assert_eq!(
Staking::ledger(101.into()).unwrap().active,
nominator_stake - nominator_share
);
assert_eq!(
Staking::ledger(11.into()).unwrap().active,
validator_stake - validator_share
);
assert_eq!(asset::stakeable_balance::<Test>(&11), validator_balance - validator_share);
assert_eq!(asset::stakeable_balance::<Test>(&101), nominator_balance);
assert_eq!(SlashObserver::get().get(&101).unwrap(), &nominator_share);
})
}
#[test]
fn virtual_stakers_cannot_be_reaped() {
ExtBuilder::default()
.set_status(101, StakerStatus::Nominator(vec![11]))
.build_and_execute(|| {
let initial_exposure = Staking::eras_stakers(active_era(), &11);
assert_eq!(initial_exposure.others.first().unwrap().who, 101);
assert_ok!(<Staking as StakingUnchecked>::migrate_to_virtual_staker(&101));
assert_ok!(<Staking as StakingInterface>::set_payee(&101, &102));
let validator_balance = asset::stakeable_balance::<Test>(&11);
let validator_stake = Staking::ledger(11.into()).unwrap().total;
let nominator_balance = asset::stakeable_balance::<Test>(&101);
let nominator_stake = Staking::ledger(101.into()).unwrap().total;
add_slash_with_percent(11, 100);
Session::roll_next();
assert_eq!(
staking_events_since_last_call(),
vec![
Event::OffenceReported {
offence_era: 1,
validator: 11,
fraction: Perbill::from_percent(100),
},
Event::SlashComputed {
offence_era: 1,
slash_era: 1,
offender: 11,
page: 0
},
Event::Slashed { staker: 11, amount: 1000 },
Event::Slashed { staker: 101, amount: 500 }
]
);
assert_eq!(Staking::ledger(11.into()).unwrap().active, 0);
assert_eq!(Staking::ledger(101.into()).unwrap().active, 0);
assert_eq_error_rate!(
validator_balance - validator_stake,
asset::stakeable_balance::<Test>(&11),
1
);
assert_eq!(asset::stakeable_balance::<Test>(&101), nominator_balance);
assert_eq!(SlashObserver::get().get(&101).unwrap(), &nominator_stake);
assert_ok!(Staking::reap_stash(RuntimeOrigin::signed(10), 11, 0));
assert_noop!(
Staking::reap_stash(RuntimeOrigin::signed(10), 101, 0),
Error::<Test>::VirtualStakerNotAllowed
);
})
}
#[test]
fn restore_ledger_not_allowed_for_virtual_stakers() {
ExtBuilder::default().has_stakers(true).build_and_execute(|| {
setup_double_bonded_ledgers();
assert_eq!(Staking::inspect_bond_state(&333).unwrap(), LedgerIntegrityState::Ok);
set_controller_no_checks(&444);
assert_eq!(Staking::inspect_bond_state(&333).unwrap(), LedgerIntegrityState::Corrupted);
assert_ok!(<Staking as StakingUnchecked>::migrate_to_virtual_staker(&333));
assert_noop!(
Staking::restore_ledger(RuntimeOrigin::root(), 333, None, None, None),
Error::<Test>::VirtualStakerNotAllowed
);
<VirtualStakers<Test>>::remove(333);
assert_ok!(Staking::restore_ledger(RuntimeOrigin::root(), 333, None, None, None));
})
}
}
mod hold_migration {
use super::*;
#[test]
fn ledger_update_creates_hold() {
ExtBuilder::default().has_stakers(true).build_and_execute(|| {
let alice = 300;
bond_nominator(alice, 1000, vec![11]);
assert_eq!(asset::staked::<Test>(&alice), 1000);
assert_eq!(Balances::balance_locked(STAKING_ID, &alice), 0);
testing_utils::migrate_to_old_currency::<Test>(alice);
assert_eq!(asset::staked::<Test>(&alice), 0);
assert_eq!(Balances::balance_locked(STAKING_ID, &alice), 1000);
assert_eq!(
<Staking as StakingInterface>::stake(&alice),
Ok(Stake { total: 1000, active: 1000 })
);
hypothetically!({
let _ = asset::mint_into_existing::<Test>(&alice, 100);
assert_ok!(Staking::bond_extra(RuntimeOrigin::signed(alice), 100));
assert_eq!(asset::staked::<Test>(&alice), 1000 + 100);
assert_eq!(
<Staking as StakingInterface>::stake(&alice),
Ok(Stake { total: 1100, active: 1100 })
);
assert_eq!(Balances::balance_locked(STAKING_ID, &alice), 1000);
});
hypothetically!({
assert_ok!(Staking::unbond(RuntimeOrigin::signed(alice), 100));
assert_eq!(asset::staked::<Test>(&alice), 1000);
assert_eq!(
<Staking as StakingInterface>::stake(&alice),
Ok(Stake { total: 1000, active: 900 })
);
assert_eq!(Balances::balance_locked(STAKING_ID, &alice), 1000);
});
assert_ok!(Staking::migrate_currency(RuntimeOrigin::signed(1), alice));
assert_eq!(asset::staked::<Test>(&alice), 1000);
assert_eq!(
<Staking as StakingInterface>::stake(&alice),
Ok(Stake { total: 1000, active: 1000 })
);
assert_noop!(
Staking::migrate_currency(RuntimeOrigin::signed(1), alice),
Error::<Test>::AlreadyMigrated
);
assert_eq!(Balances::balance_locked(STAKING_ID, &alice), 0);
});
}
#[test]
fn migrate_removes_old_lock() {
ExtBuilder::default().has_stakers(true).build_and_execute(|| {
let alice = 300;
bond_nominator(alice, 1000, vec![11]);
testing_utils::migrate_to_old_currency::<Test>(alice);
assert_eq!(asset::staked::<Test>(&alice), 0);
assert_eq!(Balances::balance_locked(STAKING_ID, &alice), 1000);
let pre_migrate_consumer = System::consumers(&alice);
let _ = staking_events_since_last_call();
assert_ok!(Staking::migrate_currency(RuntimeOrigin::signed(1), alice));
assert_eq!(System::consumers(&alice), pre_migrate_consumer - 1);
assert_eq!(Balances::balance_locked(STAKING_ID, &alice), 0);
assert_eq!(
<Staking as StakingInterface>::stake(&alice),
Ok(Stake { total: 1000, active: 1000 })
);
assert_eq!(asset::staked::<Test>(&alice), 1000);
assert_eq!(
staking_events_since_last_call(),
vec![Event::CurrencyMigrated { stash: alice, force_withdraw: 0 }]
);
assert_noop!(
Staking::migrate_currency(RuntimeOrigin::signed(1), alice),
Error::<Test>::AlreadyMigrated
);
});
}
#[test]
fn cannot_hold_all_stake() {
ExtBuilder::default().has_stakers(true).build_and_execute(|| {
let alice = 300;
let stake = 1000;
bond_nominator(alice, stake, vec![11]);
testing_utils::migrate_to_old_currency::<Test>(alice);
assert_eq!(asset::staked::<Test>(&alice), 0);
assert_eq!(Balances::balance_locked(STAKING_ID, &alice), stake);
assert_eq!(
<Staking as StakingInterface>::stake(&alice),
Ok(Stake { total: stake, active: stake })
);
assert_ok!(Balances::transfer_allow_death(
RuntimeOrigin::signed(alice),
10,
ExistentialDeposit::get()
));
let expected_force_withdraw = ExistentialDeposit::get();
assert_noop!(
Staking::unbond(RuntimeOrigin::signed(alice), 100),
Error::<Test>::NotEnoughFunds
);
let _ = staking_events_since_last_call();
assert_ok!(Staking::migrate_currency(RuntimeOrigin::signed(1), alice));
let expected_hold = stake - expected_force_withdraw;
assert_eq!(Balances::balance_locked(STAKING_ID, &alice), 0);
assert_eq!(
<Staking as StakingInterface>::stake(&alice),
Ok(Stake { total: expected_hold, active: expected_hold })
);
assert_eq!(asset::staked::<Test>(&alice), expected_hold);
assert_eq!(
staking_events_since_last_call(),
vec![Event::CurrencyMigrated {
stash: alice,
force_withdraw: expected_force_withdraw
}]
);
assert_noop!(
Staking::migrate_currency(RuntimeOrigin::signed(1), alice),
Error::<Test>::AlreadyMigrated
);
assert_ok!(Staking::unbond(RuntimeOrigin::signed(alice), 100));
});
}
#[test]
fn virtual_staker_consumer_provider_dec() {
ExtBuilder::default().has_stakers(true).build_and_execute(|| {
bond_virtual_nominator(200, 201, 500, vec![11, 21]);
System::inc_providers(&200);
System::inc_consumers(&200).expect("has provider, can consume");
hypothetically!({
assert_ok!(Staking::migrate_currency(RuntimeOrigin::signed(1), 200));
assert_eq!(System::consumers(&200), 0);
assert_eq!(System::providers(&200), 0);
assert!(!System::account_exists(&200));
assert_noop!(
Staking::migrate_currency(RuntimeOrigin::signed(1), 200),
Error::<Test>::AlreadyMigrated
);
});
hypothetically!({
System::inc_providers(&200);
assert_noop!(
Staking::migrate_currency(RuntimeOrigin::signed(1), 200),
Error::<Test>::BadState
);
});
assert_ok!(Balances::transfer_allow_death(RuntimeOrigin::signed(999), 200, 10));
assert_eq!(System::providers(&200), 2);
assert_ok!(Staking::migrate_currency(RuntimeOrigin::signed(1), 200));
assert_eq!(System::providers(&200), 1);
assert_eq!(System::consumers(&200), 0);
assert_noop!(
Staking::migrate_currency(RuntimeOrigin::signed(1), 200),
Error::<Test>::AlreadyMigrated
);
});
}
}