Newer
Older
7001
7002
7003
7004
7005
7006
7007
7008
7009
7010
7011
7012
7013
7014
7015
7016
7017
7018
7019
7020
7021
7022
7023
7024
7025
7026
7027
7028
7029
7030
7031
7032
7033
7034
// validator 21 tries to virtual bond
assert_noop!(
<Staking as StakingUnchecked>::virtual_bond(&21, 200, &22),
Error::<Test>::AlreadyBonded
);
});
}
#[test]
fn migrate_virtual_staker() {
ExtBuilder::default().build_and_execute(|| {
// give some balance to 200
Balances::make_free_balance_be(&200, 2000);
// stake
assert_ok!(Staking::bond(RuntimeOrigin::signed(200), 1000, RewardDestination::Staked));
assert_eq!(Balances::balance_locked(crate::STAKING_ID, &200), 1000);
// migrate them to virtual staker
<Staking as StakingUnchecked>::migrate_to_virtual_staker(&200);
// payee needs to be updated to a non-stash account.
assert_ok!(<Staking as StakingInterface>::update_payee(&200, &201));
// ensure the balance is not locked anymore
assert_eq!(Balances::balance_locked(crate::STAKING_ID, &200), 0);
// and they are marked as virtual stakers
assert_eq!(Pallet::<Test>::is_virtual_staker(&200), true);
});
}
#[test]
fn virtual_nominators_are_lazily_slashed() {
7035
7036
7037
7038
7039
7040
7041
7042
7043
7044
7045
7046
7047
7048
7049
7050
7051
7052
7053
7054
7055
7056
7057
7058
7059
7060
7061
7062
7063
7064
7065
7066
7067
7068
ExtBuilder::default()
.validator_count(7)
.set_status(41, StakerStatus::Validator)
.set_status(51, StakerStatus::Validator)
.set_status(201, StakerStatus::Validator)
.set_status(202, StakerStatus::Validator)
.build_and_execute(|| {
mock::start_active_era(1);
let slash_percent = Perbill::from_percent(5);
let initial_exposure = Staking::eras_stakers(active_era(), &11);
// 101 is a nominator for 11
assert_eq!(initial_exposure.others.first().unwrap().who, 101);
// make 101 a virtual nominator
<Staking as StakingUnchecked>::migrate_to_virtual_staker(&101);
// set payee different to self.
assert_ok!(<Staking as StakingInterface>::update_payee(&101, &102));
// cache values
let nominator_stake = Staking::ledger(101.into()).unwrap().active;
let nominator_balance = balances(&101).0;
let validator_stake = Staking::ledger(11.into()).unwrap().active;
let validator_balance = balances(&11).0;
let exposed_stake = initial_exposure.total;
let exposed_validator = initial_exposure.own;
let exposed_nominator = initial_exposure.others.first().unwrap().value;
// 11 goes offline
on_offence_now(
&[OffenceDetails {
offender: (11, initial_exposure.clone()),
reporters: vec![],
}],
&[slash_percent],
);
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;
// both slash amounts need to be positive for the test to make sense.
assert!(validator_share > 0);
assert!(nominator_share > 0);
// both stakes must have been decreased pro-rata.
assert_eq!(
Staking::ledger(101.into()).unwrap().active,
nominator_stake - nominator_share
);
assert_eq!(
Staking::ledger(11.into()).unwrap().active,
validator_stake - validator_share
);
// validator balance is slashed as usual
assert_eq!(balances(&11).0, validator_balance - validator_share);
// Because slashing happened.
assert!(is_disabled(11));
// but virtual nominator's balance is not slashed.
assert_eq!(Balances::free_balance(&101), nominator_balance);
// but slash is broadcasted to slash observers.
assert_eq!(SlashObserver::get().get(&101).unwrap(), &nominator_share);
})
mod ledger {
use super::*;
#[test]
fn paired_account_works() {
ExtBuilder::default().try_state(false).build_and_execute(|| {
assert_ok!(Staking::bond(
RuntimeOrigin::signed(10),
100,
RewardDestination::Account(10)
7112
7113
7114
7115
7116
7117
7118
7119
7120
7121
7122
7123
7124
7125
7126
7127
7128
7129
7130
7131
7132
7133
7134
7135
7136
7137
7138
7139
7140
7141
));
assert_eq!(<Bonded<Test>>::get(&10), Some(10));
assert_eq!(
StakingLedger::<Test>::paired_account(StakingAccount::Controller(10)),
Some(10)
);
assert_eq!(StakingLedger::<Test>::paired_account(StakingAccount::Stash(10)), Some(10));
assert_eq!(<Bonded<Test>>::get(&42), None);
assert_eq!(StakingLedger::<Test>::paired_account(StakingAccount::Controller(42)), None);
assert_eq!(StakingLedger::<Test>::paired_account(StakingAccount::Stash(42)), None);
// bond manually stash with different controller. This is deprecated but the migration
// has not been complete yet (controller: 100, stash: 200)
assert_ok!(bond_controller_stash(100, 200));
assert_eq!(<Bonded<Test>>::get(&200), Some(100));
assert_eq!(
StakingLedger::<Test>::paired_account(StakingAccount::Controller(100)),
Some(200)
);
assert_eq!(
StakingLedger::<Test>::paired_account(StakingAccount::Stash(200)),
Some(100)
);
})
}
#[test]
fn get_ledger_works() {
ExtBuilder::default().try_state(false).build_and_execute(|| {
7143
7144
7145
7146
7147
7148
7149
7150
7151
7152
7153
7154
7155
7156
7157
7158
7159
7160
7161
7162
7163
7164
7165
7166
7167
7168
7169
7170
7171
7172
7173
7174
7175
7176
7177
7178
7179
// stash does not exist
assert!(StakingLedger::<Test>::get(StakingAccount::Stash(42)).is_err());
// bonded and paired
assert_eq!(<Bonded<Test>>::get(&11), Some(11));
match StakingLedger::<Test>::get(StakingAccount::Stash(11)) {
Ok(ledger) => {
assert_eq!(ledger.controller(), Some(11));
assert_eq!(ledger.stash, 11);
},
Err(_) => panic!("staking ledger must exist"),
};
// bond manually stash with different controller. This is deprecated but the migration
// has not been complete yet (controller: 100, stash: 200)
assert_ok!(bond_controller_stash(100, 200));
assert_eq!(<Bonded<Test>>::get(&200), Some(100));
match StakingLedger::<Test>::get(StakingAccount::Stash(200)) {
Ok(ledger) => {
assert_eq!(ledger.controller(), Some(100));
assert_eq!(ledger.stash, 200);
},
Err(_) => panic!("staking ledger must exist"),
};
match StakingLedger::<Test>::get(StakingAccount::Controller(100)) {
Ok(ledger) => {
assert_eq!(ledger.controller(), Some(100));
assert_eq!(ledger.stash, 200);
},
Err(_) => panic!("staking ledger must exist"),
};
})
}
#[test]
fn get_ledger_bad_state_fails() {
ExtBuilder::default().has_stakers(false).try_state(false).build_and_execute(|| {
setup_double_bonded_ledgers();
// Case 1: double bonded but not corrupted:
// stash 444 has controller 555:
assert_eq!(Bonded::<Test>::get(444), Some(555));
assert_eq!(Ledger::<Test>::get(555).unwrap().stash, 444);
// stash 444 is also a controller of 333:
assert_eq!(Bonded::<Test>::get(333), Some(444));
assert_eq!(
StakingLedger::<Test>::paired_account(StakingAccount::Stash(333)),
Some(444)
);
assert_eq!(Ledger::<Test>::get(444).unwrap().stash, 333);
// although 444 is double bonded (it is a controller and a stash of different ledgers),
// we can safely retrieve the ledger and mutate it since the correct ledger is
// returned.
let ledger_result = StakingLedger::<Test>::get(StakingAccount::Stash(444));
assert_eq!(ledger_result.unwrap().stash, 444); // correct ledger.
let ledger_result = StakingLedger::<Test>::get(StakingAccount::Controller(444));
assert_eq!(ledger_result.unwrap().stash, 333); // correct ledger.
// fetching ledger 333 by its stash works.
let ledger_result = StakingLedger::<Test>::get(StakingAccount::Stash(333));
assert_eq!(ledger_result.unwrap().stash, 333);
// Case 2: corrupted ledger bonding.
// in this case, we simulate what happens when fetching a ledger by stash returns a
// ledger with a different stash. when this happens, we return an error instead of the
// ledger to prevent ledger mutations.
let mut ledger = Ledger::<Test>::get(444).unwrap();
assert_eq!(ledger.stash, 333);
ledger.stash = 444;
Ledger::<Test>::insert(444, ledger);
// now, we are prevented from fetching the ledger by stash from 1. It's associated
// controller (2) is now bonding a ledger with a different stash (2, not 1).
assert!(StakingLedger::<Test>::get(StakingAccount::Stash(333)).is_err());
#[test]
fn bond_works() {
ExtBuilder::default().build_and_execute(|| {
assert!(!StakingLedger::<Test>::is_bonded(StakingAccount::Stash(42)));
assert!(<Bonded<Test>>::get(&42).is_none());
let mut ledger: StakingLedger<Test> = StakingLedger::default_from(42);
let reward_dest = RewardDestination::Account(10);
assert_ok!(ledger.clone().bond(reward_dest));
assert!(StakingLedger::<Test>::is_bonded(StakingAccount::Stash(42)));
assert!(<Bonded<Test>>::get(&42).is_some());
assert_eq!(<Payee<Test>>::get(&42), Some(reward_dest));
// cannot bond again.
assert!(ledger.clone().bond(reward_dest).is_err());
// once bonded, update works as expected.
ledger.legacy_claimed_rewards = bounded_vec![1];
assert_ok!(ledger.update());
})
}
7249
7250
7251
7252
7253
7254
7255
7256
7257
7258
7259
7260
7261
7262
7263
7264
7265
7266
7267
7268
7269
7270
#[test]
fn bond_controller_cannot_be_stash_works() {
ExtBuilder::default().build_and_execute(|| {
let (stash, controller) = testing_utils::create_unique_stash_controller::<Test>(
0,
10,
RewardDestination::Staked,
false,
)
.unwrap();
assert_eq!(Bonded::<Test>::get(stash), Some(controller));
assert_eq!(Ledger::<Test>::get(controller).map(|l| l.stash), Some(stash));
// existing controller should not be able become a stash.
assert_noop!(
Staking::bond(RuntimeOrigin::signed(controller), 10, RewardDestination::Staked),
Error::<Test>::AlreadyPaired,
);
})
}
#[test]
fn is_bonded_works() {
ExtBuilder::default().build_and_execute(|| {
assert!(!StakingLedger::<Test>::is_bonded(StakingAccount::Stash(42)));
assert!(!StakingLedger::<Test>::is_bonded(StakingAccount::Controller(42)));
// adds entry to Bonded without Ledger pair (should not happen).
<Bonded<Test>>::insert(42, 42);
assert!(!StakingLedger::<Test>::is_bonded(StakingAccount::Controller(42)));
assert_eq!(<Bonded<Test>>::get(&11), Some(11));
assert!(StakingLedger::<Test>::is_bonded(StakingAccount::Stash(11)));
assert!(StakingLedger::<Test>::is_bonded(StakingAccount::Controller(11)));
<Bonded<Test>>::remove(42); // ensures try-state checks pass.
})
}
#[test]
#[allow(deprecated)]
fn set_payee_errors_on_controller_destination() {
ExtBuilder::default().build_and_execute(|| {
Payee::<Test>::insert(11, RewardDestination::Staked);
assert_noop!(
Staking::set_payee(RuntimeOrigin::signed(11), RewardDestination::Controller),
Error::<Test>::ControllerDeprecated
);
assert_eq!(Payee::<Test>::get(&11), Some(RewardDestination::Staked));
})
}
#[test]
#[allow(deprecated)]
fn update_payee_migration_works() {
ExtBuilder::default().build_and_execute(|| {
// migrate a `Controller` variant to `Account` variant.
Payee::<Test>::insert(11, RewardDestination::Controller);
assert_eq!(Payee::<Test>::get(&11), Some(RewardDestination::Controller));
assert_ok!(Staking::update_payee(RuntimeOrigin::signed(11), 11));
assert_eq!(Payee::<Test>::get(&11), Some(RewardDestination::Account(11)));
// Do not migrate a variant if not `Controller`.
Payee::<Test>::insert(21, RewardDestination::Stash);
assert_eq!(Payee::<Test>::get(&21), Some(RewardDestination::Stash));
assert_noop!(
Staking::update_payee(RuntimeOrigin::signed(11), 21),
Error::<Test>::NotController
);
assert_eq!(Payee::<Test>::get(&21), Some(RewardDestination::Stash));
#[test]
fn deprecate_controller_batch_works_full_weight() {
ExtBuilder::default().try_state(false).build_and_execute(|| {
7326
7327
7328
7329
7330
7331
7332
7333
7334
7335
7336
7337
7338
7339
7340
7341
7342
7343
7344
7345
7346
7347
7348
7349
7350
7351
7352
7353
7354
7355
7356
7357
7358
7359
7360
7361
7362
7363
7364
7365
7366
7367
7368
7369
7370
7371
7372
7373
7374
7375
7376
7377
7378
7379
7380
7381
7382
7383
7384
7385
7386
7387
7388
7389
7390
7391
7392
7393
7394
7395
7396
7397
7398
7399
7400
7401
7402
7403
7404
7405
7406
7407
7408
7409
7410
7411
7412
7413
7414
7415
7416
7417
7418
7419
7420
7421
7422
7423
7424
7425
7426
7427
7428
7429
7430
7431
7432
7433
7434
7435
7436
7437
7438
7439
7440
7441
7442
7443
7444
7445
7446
7447
7448
7449
7450
7451
7452
7453
7454
7455
7456
// Given:
let start = 1001;
let mut controllers: Vec<_> = vec![];
for n in start..(start + MaxControllersInDeprecationBatch::get()).into() {
let ctlr: u64 = n.into();
let stash: u64 = (n + 10000).into();
Ledger::<Test>::insert(
ctlr,
StakingLedger {
controller: None,
total: (10 + ctlr).into(),
active: (10 + ctlr).into(),
..StakingLedger::default_from(stash)
},
);
Bonded::<Test>::insert(stash, ctlr);
Payee::<Test>::insert(stash, RewardDestination::Staked);
controllers.push(ctlr);
}
// When:
let bounded_controllers: BoundedVec<
_,
<Test as Config>::MaxControllersInDeprecationBatch,
> = BoundedVec::try_from(controllers).unwrap();
// Only `AdminOrigin` can sign.
assert_noop!(
Staking::deprecate_controller_batch(
RuntimeOrigin::signed(2),
bounded_controllers.clone()
),
BadOrigin
);
let result =
Staking::deprecate_controller_batch(RuntimeOrigin::root(), bounded_controllers);
assert_ok!(result);
assert_eq!(
result.unwrap().actual_weight.unwrap(),
<Test as Config>::WeightInfo::deprecate_controller_batch(
<Test as Config>::MaxControllersInDeprecationBatch::get()
)
);
// Then:
for n in start..(start + MaxControllersInDeprecationBatch::get()).into() {
let ctlr: u64 = n.into();
let stash: u64 = (n + 10000).into();
// Ledger no longer keyed by controller.
assert_eq!(Ledger::<Test>::get(ctlr), None);
// Bonded now maps to the stash.
assert_eq!(Bonded::<Test>::get(stash), Some(stash));
// Ledger is now keyed by stash.
let ledger_updated = Ledger::<Test>::get(stash).unwrap();
assert_eq!(ledger_updated.stash, stash);
// Check `active` and `total` values match the original ledger set by controller.
assert_eq!(ledger_updated.active, (10 + ctlr).into());
assert_eq!(ledger_updated.total, (10 + ctlr).into());
}
})
}
#[test]
fn deprecate_controller_batch_works_half_weight() {
ExtBuilder::default().build_and_execute(|| {
// Given:
let start = 1001;
let mut controllers: Vec<_> = vec![];
for n in start..(start + MaxControllersInDeprecationBatch::get()).into() {
let ctlr: u64 = n.into();
// Only half of entries are unique pairs.
let stash: u64 = if n % 2 == 0 { (n + 10000).into() } else { ctlr };
Ledger::<Test>::insert(
ctlr,
StakingLedger { controller: None, ..StakingLedger::default_from(stash) },
);
Bonded::<Test>::insert(stash, ctlr);
Payee::<Test>::insert(stash, RewardDestination::Staked);
controllers.push(ctlr);
}
// When:
let bounded_controllers: BoundedVec<
_,
<Test as Config>::MaxControllersInDeprecationBatch,
> = BoundedVec::try_from(controllers.clone()).unwrap();
let result =
Staking::deprecate_controller_batch(RuntimeOrigin::root(), bounded_controllers);
assert_ok!(result);
assert_eq!(
result.unwrap().actual_weight.unwrap(),
<Test as Config>::WeightInfo::deprecate_controller_batch(controllers.len() as u32)
);
// Then:
for n in start..(start + MaxControllersInDeprecationBatch::get()).into() {
let unique_pair = n % 2 == 0;
let ctlr: u64 = n.into();
let stash: u64 = if unique_pair { (n + 10000).into() } else { ctlr };
// Side effect of migration for unique pair.
if unique_pair {
assert_eq!(Ledger::<Test>::get(ctlr), None);
}
// Bonded maps to the stash.
assert_eq!(Bonded::<Test>::get(stash), Some(stash));
// Ledger is keyed by stash.
let ledger_updated = Ledger::<Test>::get(stash).unwrap();
assert_eq!(ledger_updated.stash, stash);
}
})
}
#[test]
fn deprecate_controller_batch_skips_unmigrated_controller_payees() {
ExtBuilder::default().try_state(false).build_and_execute(|| {
7458
7459
7460
7461
7462
7463
7464
7465
7466
7467
7468
7469
7470
7471
7472
7473
7474
7475
7476
7477
7478
7479
7480
7481
7482
7483
7484
7485
7486
7487
7488
7489
7490
7491
7492
7493
7494
7495
7496
7497
7498
// Given:
let stash: u64 = 1000;
let ctlr: u64 = 1001;
Ledger::<Test>::insert(
ctlr,
StakingLedger { controller: None, ..StakingLedger::default_from(stash) },
);
Bonded::<Test>::insert(stash, ctlr);
#[allow(deprecated)]
Payee::<Test>::insert(stash, RewardDestination::Controller);
// When:
let bounded_controllers: BoundedVec<
_,
<Test as Config>::MaxControllersInDeprecationBatch,
> = BoundedVec::try_from(vec![ctlr]).unwrap();
let result =
Staking::deprecate_controller_batch(RuntimeOrigin::root(), bounded_controllers);
assert_ok!(result);
assert_eq!(
result.unwrap().actual_weight.unwrap(),
<Test as Config>::WeightInfo::deprecate_controller_batch(1 as u32)
);
// Then:
// Esure deprecation did not happen.
assert_eq!(Ledger::<Test>::get(ctlr).is_some(), true);
// Bonded still keyed by controller.
assert_eq!(Bonded::<Test>::get(stash), Some(ctlr));
// Ledger is still keyed by controller.
let ledger_updated = Ledger::<Test>::get(ctlr).unwrap();
assert_eq!(ledger_updated.stash, stash);
})
}
#[test]
fn deprecate_controller_batch_with_bad_state_ok() {
ExtBuilder::default().has_stakers(false).nominate(false).build_and_execute(|| {
setup_double_bonded_ledgers();
// now let's deprecate all the controllers for all the existing ledgers.
let bounded_controllers: BoundedVec<
_,
<Test as Config>::MaxControllersInDeprecationBatch,
> = BoundedVec::try_from(vec![333, 444, 555, 777]).unwrap();
7510
7511
7512
7513
7514
7515
7516
7517
7518
7519
7520
7521
7522
7523
7524
7525
7526
7527
7528
7529
7530
7531
assert_ok!(Staking::deprecate_controller_batch(
RuntimeOrigin::root(),
bounded_controllers
));
assert_eq!(
*staking_events().last().unwrap(),
Event::ControllerBatchDeprecated { failures: 0 }
);
})
}
#[test]
fn deprecate_controller_batch_with_bad_state_failures() {
ExtBuilder::default().has_stakers(false).try_state(false).build_and_execute(|| {
setup_double_bonded_ledgers();
// now let's deprecate all the controllers for all the existing ledgers.
let bounded_controllers: BoundedVec<
_,
<Test as Config>::MaxControllersInDeprecationBatch,
> = BoundedVec::try_from(vec![777, 555, 444, 333]).unwrap();
assert_ok!(Staking::deprecate_controller_batch(
RuntimeOrigin::root(),
bounded_controllers
));
assert_eq!(
*staking_events().last().unwrap(),
Event::ControllerBatchDeprecated { failures: 2 }
);
})
}
#[test]
fn set_controller_with_bad_state_ok() {
ExtBuilder::default().has_stakers(false).nominate(false).build_and_execute(|| {
setup_double_bonded_ledgers();
// in this case, setting controller works due to the ordering of the calls.
assert_ok!(Staking::set_controller(RuntimeOrigin::signed(333)));
assert_ok!(Staking::set_controller(RuntimeOrigin::signed(444)));
assert_ok!(Staking::set_controller(RuntimeOrigin::signed(555)));
})
}
#[test]
fn set_controller_with_bad_state_fails() {
ExtBuilder::default().has_stakers(false).try_state(false).build_and_execute(|| {
setup_double_bonded_ledgers();
// setting the controller of ledger associated with stash 555 fails since its stash is a
// controller of another ledger.
assert_noop!(
Staking::set_controller(RuntimeOrigin::signed(555)),
Error::<Test>::BadState
);
assert_noop!(
Staking::set_controller(RuntimeOrigin::signed(444)),
7573
7574
7575
7576
7577
7578
7579
7580
7581
7582
7583
7584
7585
7586
7587
7588
7589
7590
7591
7592
7593
7594
7595
7596
7597
7598
7599
7600
7601
7602
7603
7604
7605
7606
7607
7608
7609
7610
7611
7612
7613
7614
7615
7616
7617
7618
7619
7620
7621
7622
7623
7624
7625
7626
7627
7628
7629
7630
7631
7632
7633
7634
7635
7636
7637
7638
7639
7640
7641
7642
7643
7644
7645
7646
7647
7648
7649
7650
7651
7652
7653
7654
7655
7656
7657
7658
7659
7660
7661
7662
7663
7664
7665
7666
7667
7668
7669
7670
7671
7672
7673
7674
7675
7676
7677
7678
7679
7680
7681
7682
7683
7684
7685
7686
7687
7688
7689
7690
7691
7692
7693
7694
7695
7696
7697
7698
7699
7700
7701
7702
7703
7704
7705
7706
7707
7708
7709
7710
7711
7712
7713
7714
7715
7716
7717
7718
7719
7720
7721
7722
7723
7724
7725
7726
7727
7728
7729
7730
7731
7732
7733
7734
7735
7736
7737
7738
7739
7740
7741
7742
7743
7744
7745
7746
7747
7748
7749
7750
7751
7752
7753
7754
7755
7756
7757
7758
7759
7760
7761
7762
7763
7764
7765
7766
7767
7768
7769
7770
7771
7772
7773
7774
7775
7776
7777
7778
7779
7780
7781
7782
7783
7784
7785
7786
7787
7788
7789
7790
7791
7792
7793
7794
7795
7796
7797
7798
7799
7800
7801
7802
7803
7804
7805
7806
7807
7808
7809
7810
7811
7812
7813
7814
7815
7816
7817
7818
7819
7820
7821
7822
7823
7824
7825
7826
7827
7828
7829
7830
7831
7832
7833
7834
7835
7836
7837
7838
7839
7840
7841
7842
7843
7844
7845
7846
7847
7848
7849
7850
7851
7852
7853
7854
7855
7856
7857
7858
7859
7860
7861
7862
7863
7864
7865
7866
7867
7868
7869
7870
7871
7872
7873
7874
7875
7876
7877
7878
7879
7880
7881
7882
7883
7884
7885
7886
7887
7888
7889
7890
7891
7892
7893
7894
7895
7896
7897
7898
7899
7900
7901
7902
7903
7904
7905
7906
7907
7908
7909
7910
7911
7912
7913
7914
7915
7916
7917
7918
7919
7920
7921
7922
7923
7924
7925
7926
7927
7928
7929
7930
7931
7932
7933
7934
7935
7936
7937
7938
7939
7940
7941
7942
7943
7944
7945
7946
7947
7948
7949
7950
7951
7952
7953
7954
7955
assert_ok!(Staking::set_controller(RuntimeOrigin::signed(333)));
})
}
}
mod ledger_recovery {
use super::*;
#[test]
fn inspect_recovery_ledger_simple_works() {
ExtBuilder::default().has_stakers(true).try_state(false).build_and_execute(|| {
setup_double_bonded_ledgers();
// non corrupted ledger.
assert_eq!(Staking::inspect_bond_state(&11).unwrap(), LedgerIntegrityState::Ok);
// non bonded stash.
assert!(Bonded::<Test>::get(&1111).is_none());
assert!(Staking::inspect_bond_state(&1111).is_err());
// double bonded but not corrupted.
assert_eq!(Staking::inspect_bond_state(&333).unwrap(), LedgerIntegrityState::Ok);
})
}
#[test]
fn inspect_recovery_ledger_corupted_killed_works() {
ExtBuilder::default().has_stakers(true).try_state(false).build_and_execute(|| {
setup_double_bonded_ledgers();
let lock_333_before = Balances::balance_locked(crate::STAKING_ID, &333);
// get into corrupted and killed ledger state by killing a corrupted ledger:
// init state:
// (333, 444)
// (444, 555)
// set_controller(444) to 444
// (333, 444) -> corrupted
// (444, 444)
// kill(333)
// (444, 444) -> corrupted and None.
assert_eq!(Staking::inspect_bond_state(&333).unwrap(), LedgerIntegrityState::Ok);
set_controller_no_checks(&444);
// now try-state fails.
assert!(Staking::do_try_state(System::block_number()).is_err());
// 333 is corrupted since it's controller is linking 444 ledger.
assert_eq!(Staking::inspect_bond_state(&333).unwrap(), LedgerIntegrityState::Corrupted);
// 444 however is OK.
assert_eq!(Staking::inspect_bond_state(&444).unwrap(), LedgerIntegrityState::Ok);
// kill the corrupted ledger that is associated with stash 333.
assert_ok!(StakingLedger::<Test>::kill(&333));
// 333 bond is no more but it returns `BadState` because the lock on this stash is
// still set (see checks below).
assert_eq!(Staking::inspect_bond_state(&333), Err(Error::<Test>::BadState));
// now the *other* ledger associated with 444 has been corrupted and killed (None).
assert_eq!(
Staking::inspect_bond_state(&444),
Ok(LedgerIntegrityState::CorruptedKilled)
);
// side effects on 333 - ledger, bonded, payee, lock should be completely empty.
// however, 333 lock remains.
assert_eq!(Balances::balance_locked(crate::STAKING_ID, &333), lock_333_before); // NOK
assert!(Bonded::<Test>::get(&333).is_none()); // OK
assert!(Payee::<Test>::get(&333).is_none()); // OK
assert!(Ledger::<Test>::get(&444).is_none()); // OK
// side effects on 444 - ledger, bonded, payee, lock should remain be intact.
// however, 444 lock was removed.
assert_eq!(Balances::balance_locked(crate::STAKING_ID, &444), 0); // NOK
assert!(Bonded::<Test>::get(&444).is_some()); // OK
assert!(Payee::<Test>::get(&444).is_some()); // OK
assert!(Ledger::<Test>::get(&555).is_none()); // NOK
assert!(Staking::do_try_state(System::block_number()).is_err());
})
}
#[test]
fn inspect_recovery_ledger_corupted_killed_other_works() {
ExtBuilder::default().has_stakers(true).try_state(false).build_and_execute(|| {
setup_double_bonded_ledgers();
let lock_333_before = Balances::balance_locked(crate::STAKING_ID, &333);
// get into corrupted and killed ledger state by killing a corrupted ledger:
// init state:
// (333, 444)
// (444, 555)
// set_controller(444) to 444
// (333, 444) -> corrupted
// (444, 444)
// kill(444)
// (333, 444) -> corrupted and None
assert_eq!(Staking::inspect_bond_state(&333).unwrap(), LedgerIntegrityState::Ok);
set_controller_no_checks(&444);
// now try-state fails.
assert!(Staking::do_try_state(System::block_number()).is_err());
// 333 is corrupted since it's controller is linking 444 ledger.
assert_eq!(Staking::inspect_bond_state(&333).unwrap(), LedgerIntegrityState::Corrupted);
// 444 however is OK.
assert_eq!(Staking::inspect_bond_state(&444).unwrap(), LedgerIntegrityState::Ok);
// kill the *other* ledger that is double bonded but not corrupted.
assert_ok!(StakingLedger::<Test>::kill(&444));
// now 333 is corrupted and None through the *other* ledger being killed.
assert_eq!(
Staking::inspect_bond_state(&333).unwrap(),
LedgerIntegrityState::CorruptedKilled,
);
// 444 is cleaned and not a stash anymore; no lock left behind.
assert_eq!(Ledger::<Test>::get(&444), None);
assert_eq!(Staking::inspect_bond_state(&444), Err(Error::<Test>::NotStash));
// side effects on 333 - ledger, bonded, payee, lock should be intact.
assert_eq!(Balances::balance_locked(crate::STAKING_ID, &333), lock_333_before); // OK
assert_eq!(Bonded::<Test>::get(&333), Some(444)); // OK
assert!(Payee::<Test>::get(&333).is_some()); // OK
// however, ledger associated with its controller was killed.
assert!(Ledger::<Test>::get(&444).is_none()); // NOK
// side effects on 444 - ledger, bonded, payee, lock should be completely removed.
assert_eq!(Balances::balance_locked(crate::STAKING_ID, &444), 0); // OK
assert!(Bonded::<Test>::get(&444).is_none()); // OK
assert!(Payee::<Test>::get(&444).is_none()); // OK
assert!(Ledger::<Test>::get(&555).is_none()); // OK
assert!(Staking::do_try_state(System::block_number()).is_err());
})
}
#[test]
fn inspect_recovery_ledger_lock_corrupted_works() {
ExtBuilder::default().has_stakers(true).try_state(false).build_and_execute(|| {
setup_double_bonded_ledgers();
// get into lock corrupted ledger state by bond_extra on a ledger that is double bonded
// with a corrupted ledger.
// init state:
// (333, 444)
// (444, 555)
// set_controller(444) to 444
// (333, 444) -> corrupted
// (444, 444)
// bond_extra(333, 10) -> lock corrupted on 444
assert_eq!(Staking::inspect_bond_state(&333).unwrap(), LedgerIntegrityState::Ok);
set_controller_no_checks(&444);
bond_extra_no_checks(&333, 10);
// now try-state fails.
assert!(Staking::do_try_state(System::block_number()).is_err());
// 333 is corrupted since it's controller is linking 444 ledger.
assert_eq!(Staking::inspect_bond_state(&333).unwrap(), LedgerIntegrityState::Corrupted);
// 444 ledger is not corrupted but locks got out of sync.
assert_eq!(
Staking::inspect_bond_state(&444).unwrap(),
LedgerIntegrityState::LockCorrupted
);
})
}
// Corrupted ledger restore.
//
// * Double bonded and corrupted ledger.
#[test]
fn restore_ledger_corrupted_works() {
ExtBuilder::default().has_stakers(true).build_and_execute(|| {
setup_double_bonded_ledgers();
// get into corrupted and killed ledger state.
// init state:
// (333, 444)
// (444, 555)
// set_controller(444) to 444
// (333, 444) -> corrupted
// (444, 444)
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);
// now try-state fails.
assert!(Staking::do_try_state(System::block_number()).is_err());
// recover the ledger bonded by 333 stash.
assert_ok!(Staking::restore_ledger(RuntimeOrigin::root(), 333, None, None, None));
// try-state checks are ok now.
assert_ok!(Staking::do_try_state(System::block_number()));
})
}
// Corrupted and killed ledger restore.
//
// * Double bonded and corrupted ledger.
// * Ledger killed by own controller.
#[test]
fn restore_ledger_corrupted_killed_works() {
ExtBuilder::default().has_stakers(true).build_and_execute(|| {
setup_double_bonded_ledgers();
// ledger.total == lock
let total_444_before_corruption = Balances::balance_locked(crate::STAKING_ID, &444);
// get into corrupted and killed ledger state by killing a corrupted ledger:
// init state:
// (333, 444)
// (444, 555)
// set_controller(444) to 444
// (333, 444) -> corrupted
// (444, 444)
// kill(333)
// (444, 444) -> corrupted and None.
assert_eq!(Staking::inspect_bond_state(&333).unwrap(), LedgerIntegrityState::Ok);
set_controller_no_checks(&444);
// kill the corrupted ledger that is associated with stash 333.
assert_ok!(StakingLedger::<Test>::kill(&333));
// 333 bond is no more but it returns `BadState` because the lock on this stash is
// still set (see checks below).
assert_eq!(Staking::inspect_bond_state(&333), Err(Error::<Test>::BadState));
// now the *other* ledger associated with 444 has been corrupted and killed (None).
assert!(Staking::ledger(StakingAccount::Stash(444)).is_err());
// try-state should fail.
assert!(Staking::do_try_state(System::block_number()).is_err());
// recover the ledger bonded by 333 stash.
assert_ok!(Staking::restore_ledger(RuntimeOrigin::root(), 333, None, None, None));
// for the try-state checks to pass, we also need to recover the stash 444 which is
// corrupted too by proxy of kill(333). Currently, both the lock and the ledger of 444
// have been cleared so we need to provide the new amount to restore the ledger.
assert_noop!(
Staking::restore_ledger(RuntimeOrigin::root(), 444, None, None, None),
Error::<Test>::CannotRestoreLedger
);
assert_ok!(Staking::restore_ledger(
RuntimeOrigin::root(),
444,
None,
Some(total_444_before_corruption),
None,
));
// try-state checks are ok now.
assert_ok!(Staking::do_try_state(System::block_number()));
})
}
// Corrupted and killed by *other* ledger restore.
//
// * Double bonded and corrupted ledger.
// * Ledger killed by own controller.
#[test]
fn restore_ledger_corrupted_killed_other_works() {
ExtBuilder::default().has_stakers(true).build_and_execute(|| {
setup_double_bonded_ledgers();
// get into corrupted and killed ledger state by killing a corrupted ledger:
// init state:
// (333, 444)
// (444, 555)
// set_controller(444) to 444
// (333, 444) -> corrupted
// (444, 444)
// kill(444)
// (333, 444) -> corrupted and None
assert_eq!(Staking::inspect_bond_state(&333).unwrap(), LedgerIntegrityState::Ok);
set_controller_no_checks(&444);
// now try-state fails.
assert!(Staking::do_try_state(System::block_number()).is_err());
// 333 is corrupted since it's controller is linking 444 ledger.
assert_eq!(Staking::inspect_bond_state(&333).unwrap(), LedgerIntegrityState::Corrupted);
// 444 however is OK.
assert_eq!(Staking::inspect_bond_state(&444).unwrap(), LedgerIntegrityState::Ok);
// kill the *other* ledger that is double bonded but not corrupted.
assert_ok!(StakingLedger::<Test>::kill(&444));
// recover the ledger bonded by 333 stash.
assert_ok!(Staking::restore_ledger(RuntimeOrigin::root(), 333, None, None, None));
// 444 does not need recover in this case since it's been killed successfully.
assert_eq!(Staking::inspect_bond_state(&444), Err(Error::<Test>::NotStash));
// try-state checks are ok now.
assert_ok!(Staking::do_try_state(System::block_number()));
})
}
// Corrupted with bond_extra.
//
// * Double bonded and corrupted ledger.
// * Corrupted ledger calls `bond_extra`
#[test]
fn restore_ledger_corrupted_bond_extra_works() {
ExtBuilder::default().has_stakers(true).build_and_execute(|| {
setup_double_bonded_ledgers();
let lock_333_before = Balances::balance_locked(crate::STAKING_ID, &333);
let lock_444_before = Balances::balance_locked(crate::STAKING_ID, &444);
// get into corrupted and killed ledger state by killing a corrupted ledger:
// init state:
// (333, 444)
// (444, 555)
// set_controller(444) to 444
// (333, 444) -> corrupted
// (444, 444)
// bond_extra(444, 40) -> OK
// bond_extra(333, 30) -> locks out of sync
assert_eq!(Staking::inspect_bond_state(&333).unwrap(), LedgerIntegrityState::Ok);
set_controller_no_checks(&444);
// now try-state fails.
assert!(Staking::do_try_state(System::block_number()).is_err());
// if 444 bonds extra, the locks remain in sync.
bond_extra_no_checks(&444, 40);
assert_eq!(Balances::balance_locked(crate::STAKING_ID, &333), lock_333_before);
assert_eq!(Balances::balance_locked(crate::STAKING_ID, &444), lock_444_before + 40);
// however if 333 bonds extra, the wrong lock is updated.
bond_extra_no_checks(&333, 30);
assert_eq!(
Balances::balance_locked(crate::STAKING_ID, &333),
lock_444_before + 40 + 30
); //not OK
assert_eq!(Balances::balance_locked(crate::STAKING_ID, &444), lock_444_before + 40); // OK
// recover the ledger bonded by 333 stash. Note that the total/lock needs to be
// re-written since on-chain data lock has become out of sync.
assert_ok!(Staking::restore_ledger(
RuntimeOrigin::root(),
333,
None,
Some(lock_333_before + 30),
None
));
// now recover 444 that although it's not corrupted, its lock and ledger.total are out
// of sync. in which case, we need to explicitly set the ledger's lock and amount,
// otherwise the ledger recover will fail.
assert_noop!(
Staking::restore_ledger(RuntimeOrigin::root(), 444, None, None, None),
Error::<Test>::CannotRestoreLedger
);
//and enforcing a new ledger lock/total on this non-corrupted ledger will work.
assert_ok!(Staking::restore_ledger(
RuntimeOrigin::root(),
444,
None,
Some(lock_444_before + 40),
None
));
// double-check that ledgers got to expected state and bond_extra done during the
// corrupted state is part of the recovered ledgers.
let ledger_333 = Bonded::<Test>::get(&333).and_then(Ledger::<Test>::get).unwrap();
let ledger_444 = Bonded::<Test>::get(&444).and_then(Ledger::<Test>::get).unwrap();
assert_eq!(ledger_333.total, lock_333_before + 30);
assert_eq!(Balances::balance_locked(crate::STAKING_ID, &333), ledger_333.total);
assert_eq!(ledger_444.total, lock_444_before + 40);
assert_eq!(Balances::balance_locked(crate::STAKING_ID, &444), ledger_444.total);
// try-state checks are ok now.
assert_ok!(Staking::do_try_state(System::block_number()));
7959
7960
7961
7962
7963
7964
7965
7966
7967
7968
7969
7970
7971
7972
7973
7974
7975
7976
7977
7978
7979
7980
7981
7982
7983
7984
7985
7986
7987
7988
7989
7990
7991
7992
7993
7994
7995
7996
7997
7998
7999
8000
mod byzantine_threshold_disabling_strategy {
use crate::{
tests::Test, ActiveEra, ActiveEraInfo, DisablingStrategy, UpToLimitDisablingStrategy,
};
use sp_staking::EraIndex;
// Common test data - the stash of the offending validator, the era of the offence and the
// active set
const OFFENDER_ID: <Test as frame_system::Config>::AccountId = 7;
const SLASH_ERA: EraIndex = 1;
const ACTIVE_SET: [<Test as pallet_session::Config>::ValidatorId; 7] = [1, 2, 3, 4, 5, 6, 7];
const OFFENDER_VALIDATOR_IDX: u32 = 6; // the offender is with index 6 in the active set
#[test]
fn dont_disable_for_ancient_offence() {
sp_io::TestExternalities::default().execute_with(|| {
let initially_disabled = vec![];
pallet_session::Validators::<Test>::put(ACTIVE_SET.to_vec());
ActiveEra::<Test>::put(ActiveEraInfo { index: 2, start: None });
let disable_offender =
<UpToLimitDisablingStrategy as DisablingStrategy<Test>>::decision(
&OFFENDER_ID,
SLASH_ERA,
&initially_disabled,
);
assert!(disable_offender.is_none());
});
}
#[test]
fn dont_disable_beyond_byzantine_threshold() {
sp_io::TestExternalities::default().execute_with(|| {
let initially_disabled = vec![1, 2];
pallet_session::Validators::<Test>::put(ACTIVE_SET.to_vec());
let disable_offender =
<UpToLimitDisablingStrategy as DisablingStrategy<Test>>::decision(
&OFFENDER_ID,
SLASH_ERA,