Files
pezkuwi-subxt/cumulus/pallets/collator-selection/src/tests.rs
T
Matteo Muraca 610987a19d removed pallet::getter usage from cumulus pallets (#3471)
Part of #3326 

@ggwpez @kianenigma @shawntabrizi

polkadot address: 12poSUQPtcF1HUPQGY3zZu2P8emuW9YnsPduA4XG3oCEfJVp

---------

Signed-off-by: Matteo Muraca <mmuraca247@gmail.com>
2024-03-18 19:19:20 +00:00

1593 lines
53 KiB
Rust

// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate as collator_selection;
use crate::{
mock::*, CandidacyBond, CandidateInfo, CandidateList, DesiredCandidates, Error, Invulnerables,
LastAuthoredBlock,
};
use frame_support::{
assert_noop, assert_ok,
traits::{Currency, OnInitialize},
};
use pallet_balances::Error as BalancesError;
use sp_runtime::{testing::UintAuthorityId, traits::BadOrigin, BuildStorage};
#[test]
fn basic_setup_works() {
new_test_ext().execute_with(|| {
assert_eq!(DesiredCandidates::<Test>::get(), 2);
assert_eq!(CandidacyBond::<Test>::get(), 10);
assert_eq!(CandidateList::<Test>::get().iter().count(), 0);
// genesis should sort input
assert_eq!(Invulnerables::<Test>::get(), vec![1, 2]);
});
}
#[test]
fn it_should_set_invulnerables() {
new_test_ext().execute_with(|| {
let mut new_set = vec![1, 4, 3, 2];
assert_ok!(CollatorSelection::set_invulnerables(
RuntimeOrigin::signed(RootAccount::get()),
new_set.clone()
));
new_set.sort();
assert_eq!(Invulnerables::<Test>::get(), new_set);
// cannot set with non-root.
assert_noop!(
CollatorSelection::set_invulnerables(RuntimeOrigin::signed(1), new_set),
BadOrigin
);
});
}
#[test]
fn it_should_set_invulnerables_even_with_some_invalid() {
new_test_ext().execute_with(|| {
assert_eq!(Invulnerables::<Test>::get(), vec![1, 2]);
let new_with_invalid = vec![1, 4, 3, 42, 2];
assert_ok!(CollatorSelection::set_invulnerables(
RuntimeOrigin::signed(RootAccount::get()),
new_with_invalid
));
// should succeed and order them, but not include 42
assert_eq!(Invulnerables::<Test>::get(), vec![1, 2, 3, 4]);
});
}
#[test]
fn add_invulnerable_works() {
new_test_ext().execute_with(|| {
initialize_to_block(1);
assert_eq!(Invulnerables::<Test>::get(), vec![1, 2]);
let new = 3;
// function runs
assert_ok!(CollatorSelection::add_invulnerable(
RuntimeOrigin::signed(RootAccount::get()),
new
));
System::assert_last_event(RuntimeEvent::CollatorSelection(
crate::Event::InvulnerableAdded { account_id: new },
));
// same element cannot be added more than once
assert_noop!(
CollatorSelection::add_invulnerable(RuntimeOrigin::signed(RootAccount::get()), new),
Error::<Test>::AlreadyInvulnerable
);
// new element is now part of the invulnerables list
assert!(Invulnerables::<Test>::get().to_vec().contains(&new));
// cannot add with non-root
assert_noop!(CollatorSelection::add_invulnerable(RuntimeOrigin::signed(1), new), BadOrigin);
// cannot add invulnerable without associated validator keys
let not_validator = 42;
assert_noop!(
CollatorSelection::add_invulnerable(
RuntimeOrigin::signed(RootAccount::get()),
not_validator
),
Error::<Test>::ValidatorNotRegistered
);
});
}
#[test]
fn invulnerable_limit_works() {
new_test_ext().execute_with(|| {
assert_eq!(Invulnerables::<Test>::get(), vec![1, 2]);
// MaxInvulnerables: u32 = 20
for ii in 3..=21 {
// only keys were registered in mock for 1 to 5
if ii > 5 {
Balances::make_free_balance_be(&ii, 100);
let key = MockSessionKeys { aura: UintAuthorityId(ii) };
Session::set_keys(RuntimeOrigin::signed(ii).into(), key, Vec::new()).unwrap();
}
assert_eq!(Balances::free_balance(ii), 100);
if ii < 21 {
assert_ok!(CollatorSelection::add_invulnerable(
RuntimeOrigin::signed(RootAccount::get()),
ii
));
} else {
assert_noop!(
CollatorSelection::add_invulnerable(
RuntimeOrigin::signed(RootAccount::get()),
ii
),
Error::<Test>::TooManyInvulnerables
);
}
}
let expected: Vec<u64> = (1..=20).collect();
assert_eq!(Invulnerables::<Test>::get(), expected);
// Cannot set too many Invulnerables
let too_many_invulnerables: Vec<u64> = (1..=21).collect();
assert_noop!(
CollatorSelection::set_invulnerables(
RuntimeOrigin::signed(RootAccount::get()),
too_many_invulnerables
),
Error::<Test>::TooManyInvulnerables
);
assert_eq!(Invulnerables::<Test>::get(), expected);
});
}
#[test]
fn remove_invulnerable_works() {
new_test_ext().execute_with(|| {
initialize_to_block(1);
assert_eq!(Invulnerables::<Test>::get(), vec![1, 2]);
assert_ok!(CollatorSelection::add_invulnerable(
RuntimeOrigin::signed(RootAccount::get()),
4
));
assert_ok!(CollatorSelection::add_invulnerable(
RuntimeOrigin::signed(RootAccount::get()),
3
));
assert_eq!(Invulnerables::<Test>::get(), vec![1, 2, 3, 4]);
assert_ok!(CollatorSelection::remove_invulnerable(
RuntimeOrigin::signed(RootAccount::get()),
2
));
System::assert_last_event(RuntimeEvent::CollatorSelection(
crate::Event::InvulnerableRemoved { account_id: 2 },
));
assert_eq!(Invulnerables::<Test>::get(), vec![1, 3, 4]);
// cannot remove invulnerable not in the list
assert_noop!(
CollatorSelection::remove_invulnerable(RuntimeOrigin::signed(RootAccount::get()), 2),
Error::<Test>::NotInvulnerable
);
// cannot remove without privilege
assert_noop!(
CollatorSelection::remove_invulnerable(RuntimeOrigin::signed(1), 3),
BadOrigin
);
});
}
#[test]
fn candidate_to_invulnerable_works() {
new_test_ext().execute_with(|| {
initialize_to_block(1);
assert_eq!(DesiredCandidates::<Test>::get(), 2);
assert_eq!(CandidacyBond::<Test>::get(), 10);
assert_eq!(CandidateList::<Test>::get().iter().count(), 0);
assert_eq!(Invulnerables::<Test>::get(), vec![1, 2]);
assert_eq!(Balances::free_balance(3), 100);
assert_eq!(Balances::free_balance(4), 100);
assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(3)));
assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(4)));
assert_eq!(Balances::free_balance(3), 90);
assert_eq!(Balances::free_balance(4), 90);
assert_ok!(CollatorSelection::add_invulnerable(
RuntimeOrigin::signed(RootAccount::get()),
3
));
System::assert_has_event(RuntimeEvent::CollatorSelection(crate::Event::CandidateRemoved {
account_id: 3,
}));
System::assert_has_event(RuntimeEvent::CollatorSelection(
crate::Event::InvulnerableAdded { account_id: 3 },
));
assert!(Invulnerables::<Test>::get().to_vec().contains(&3));
assert_eq!(Balances::free_balance(3), 100);
assert_eq!(CandidateList::<Test>::get().iter().count(), 1);
assert_ok!(CollatorSelection::add_invulnerable(
RuntimeOrigin::signed(RootAccount::get()),
4
));
System::assert_has_event(RuntimeEvent::CollatorSelection(crate::Event::CandidateRemoved {
account_id: 4,
}));
System::assert_has_event(RuntimeEvent::CollatorSelection(
crate::Event::InvulnerableAdded { account_id: 4 },
));
assert!(Invulnerables::<Test>::get().to_vec().contains(&4));
assert_eq!(Balances::free_balance(4), 100);
assert_eq!(CandidateList::<Test>::get().iter().count(), 0);
});
}
#[test]
fn set_desired_candidates_works() {
new_test_ext().execute_with(|| {
// given
assert_eq!(DesiredCandidates::<Test>::get(), 2);
// can set
assert_ok!(CollatorSelection::set_desired_candidates(
RuntimeOrigin::signed(RootAccount::get()),
7
));
assert_eq!(DesiredCandidates::<Test>::get(), 7);
// rejects bad origin
assert_noop!(
CollatorSelection::set_desired_candidates(RuntimeOrigin::signed(1), 8),
BadOrigin
);
});
}
#[test]
fn set_candidacy_bond_empty_candidate_list() {
new_test_ext().execute_with(|| {
// given
assert_eq!(CandidacyBond::<Test>::get(), 10);
assert!(CandidateList::<Test>::get().is_empty());
// can decrease without candidates
assert_ok!(CollatorSelection::set_candidacy_bond(
RuntimeOrigin::signed(RootAccount::get()),
7
));
assert_eq!(CandidacyBond::<Test>::get(), 7);
assert!(CandidateList::<Test>::get().is_empty());
// rejects bad origin.
assert_noop!(CollatorSelection::set_candidacy_bond(RuntimeOrigin::signed(1), 8), BadOrigin);
// can increase without candidates
assert_ok!(CollatorSelection::set_candidacy_bond(
RuntimeOrigin::signed(RootAccount::get()),
20
));
assert!(CandidateList::<Test>::get().is_empty());
assert_eq!(CandidacyBond::<Test>::get(), 20);
});
}
#[test]
fn set_candidacy_bond_with_one_candidate() {
new_test_ext().execute_with(|| {
// given
assert_eq!(CandidacyBond::<Test>::get(), 10);
assert!(CandidateList::<Test>::get().is_empty());
let candidate_3 = CandidateInfo { who: 3, deposit: 10 };
assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(3)));
assert_eq!(CandidateList::<Test>::get(), vec![candidate_3.clone()]);
// can decrease with one candidate
assert_ok!(CollatorSelection::set_candidacy_bond(
RuntimeOrigin::signed(RootAccount::get()),
7
));
assert_eq!(CandidacyBond::<Test>::get(), 7);
assert_eq!(CandidateList::<Test>::get(), vec![candidate_3.clone()]);
// can increase up to initial deposit
assert_ok!(CollatorSelection::set_candidacy_bond(
RuntimeOrigin::signed(RootAccount::get()),
10
));
assert_eq!(CandidacyBond::<Test>::get(), 10);
assert_eq!(CandidateList::<Test>::get(), vec![candidate_3.clone()]);
// can increase past initial deposit, should kick existing candidate
assert_ok!(CollatorSelection::set_candidacy_bond(
RuntimeOrigin::signed(RootAccount::get()),
20
));
assert!(CandidateList::<Test>::get().is_empty());
});
}
#[test]
fn set_candidacy_bond_with_many_candidates_same_deposit() {
new_test_ext().execute_with(|| {
// given
assert_eq!(CandidacyBond::<Test>::get(), 10);
assert!(CandidateList::<Test>::get().is_empty());
let candidate_3 = CandidateInfo { who: 3, deposit: 10 };
let candidate_4 = CandidateInfo { who: 4, deposit: 10 };
let candidate_5 = CandidateInfo { who: 5, deposit: 10 };
assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(3)));
assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(4)));
assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(5)));
assert_eq!(
CandidateList::<Test>::get(),
vec![candidate_5.clone(), candidate_4.clone(), candidate_3.clone()]
);
// can decrease with multiple candidates
assert_ok!(CollatorSelection::set_candidacy_bond(
RuntimeOrigin::signed(RootAccount::get()),
7
));
assert_eq!(CandidacyBond::<Test>::get(), 7);
assert_eq!(
CandidateList::<Test>::get(),
vec![candidate_5.clone(), candidate_4.clone(), candidate_3.clone()]
);
// can increase up to initial deposit
assert_ok!(CollatorSelection::set_candidacy_bond(
RuntimeOrigin::signed(RootAccount::get()),
10
));
assert_eq!(CandidacyBond::<Test>::get(), 10);
assert_eq!(
CandidateList::<Test>::get(),
vec![candidate_5.clone(), candidate_4.clone(), candidate_3.clone()]
);
// can increase past initial deposit, should kick existing candidates
assert_ok!(CollatorSelection::set_candidacy_bond(
RuntimeOrigin::signed(RootAccount::get()),
20
));
assert!(CandidateList::<Test>::get().is_empty());
});
}
#[test]
fn set_candidacy_bond_with_many_candidates_different_deposits() {
new_test_ext().execute_with(|| {
// given
assert_eq!(CandidacyBond::<Test>::get(), 10);
assert!(CandidateList::<Test>::get().is_empty());
let candidate_3 = CandidateInfo { who: 3, deposit: 10 };
let candidate_4 = CandidateInfo { who: 4, deposit: 20 };
let candidate_5 = CandidateInfo { who: 5, deposit: 30 };
assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(3)));
assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(4)));
assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(5)));
assert_ok!(CollatorSelection::update_bond(RuntimeOrigin::signed(5), 30));
assert_ok!(CollatorSelection::update_bond(RuntimeOrigin::signed(4), 20));
assert_eq!(
CandidateList::<Test>::get(),
vec![candidate_3.clone(), candidate_4.clone(), candidate_5.clone()]
);
// can decrease with multiple candidates
assert_ok!(CollatorSelection::set_candidacy_bond(
RuntimeOrigin::signed(RootAccount::get()),
7
));
assert_eq!(CandidacyBond::<Test>::get(), 7);
assert_eq!(
CandidateList::<Test>::get(),
vec![candidate_3.clone(), candidate_4.clone(), candidate_5.clone()]
);
// can increase up to initial deposit
assert_ok!(CollatorSelection::set_candidacy_bond(
RuntimeOrigin::signed(RootAccount::get()),
10
));
assert_eq!(CandidacyBond::<Test>::get(), 10);
assert_eq!(
CandidateList::<Test>::get(),
vec![candidate_3.clone(), candidate_4.clone(), candidate_5.clone()]
);
// can increase to 4's deposit, should kick 3
assert_ok!(CollatorSelection::set_candidacy_bond(
RuntimeOrigin::signed(RootAccount::get()),
20
));
assert_eq!(CandidacyBond::<Test>::get(), 20);
assert_eq!(CandidateList::<Test>::get(), vec![candidate_4.clone(), candidate_5.clone()]);
// can increase past 4's deposit, should kick 4
assert_ok!(CollatorSelection::set_candidacy_bond(
RuntimeOrigin::signed(RootAccount::get()),
25
));
assert_eq!(CandidacyBond::<Test>::get(), 25);
assert_eq!(CandidateList::<Test>::get(), vec![candidate_5.clone()]);
// lowering the minimum deposit should have no effect
assert_ok!(CollatorSelection::set_candidacy_bond(
RuntimeOrigin::signed(RootAccount::get()),
5
));
assert_eq!(CandidacyBond::<Test>::get(), 5);
assert_eq!(CandidateList::<Test>::get(), vec![candidate_5.clone()]);
// add 3 and 4 back but with higher deposits than minimum
assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(3)));
assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(4)));
assert_ok!(CollatorSelection::update_bond(RuntimeOrigin::signed(3), 10));
assert_ok!(CollatorSelection::update_bond(RuntimeOrigin::signed(4), 20));
assert_eq!(
CandidateList::<Test>::get(),
vec![candidate_3.clone(), candidate_4.clone(), candidate_5.clone()]
);
// can increase the deposit above the current max in the list, all candidates should be
// kicked
assert_ok!(CollatorSelection::set_candidacy_bond(
RuntimeOrigin::signed(RootAccount::get()),
40
));
assert_eq!(CandidacyBond::<Test>::get(), 40);
assert!(CandidateList::<Test>::get().is_empty());
});
}
#[test]
fn cannot_register_candidate_if_too_many() {
new_test_ext().execute_with(|| {
<crate::DesiredCandidates<Test>>::put(1);
// MaxCandidates: u32 = 20
// Aside from 3, 4, and 5, create enough accounts to have 21 potential
// candidates.
for i in 6..=23 {
Balances::make_free_balance_be(&i, 100);
let key = MockSessionKeys { aura: UintAuthorityId(i) };
Session::set_keys(RuntimeOrigin::signed(i).into(), key, Vec::new()).unwrap();
}
for c in 3..=22 {
assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(c)));
}
assert_noop!(
CollatorSelection::register_as_candidate(RuntimeOrigin::signed(23)),
Error::<Test>::TooManyCandidates,
);
})
}
#[test]
fn cannot_unregister_candidate_if_too_few() {
new_test_ext().execute_with(|| {
assert_eq!(CandidateList::<Test>::get().iter().count(), 0);
assert_eq!(Invulnerables::<Test>::get(), vec![1, 2]);
assert_ok!(CollatorSelection::remove_invulnerable(
RuntimeOrigin::signed(RootAccount::get()),
1
));
assert_noop!(
CollatorSelection::remove_invulnerable(RuntimeOrigin::signed(RootAccount::get()), 2),
Error::<Test>::TooFewEligibleCollators,
);
// reset desired candidates:
<crate::DesiredCandidates<Test>>::put(1);
assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(4)));
// now we can remove `2`
assert_ok!(CollatorSelection::remove_invulnerable(
RuntimeOrigin::signed(RootAccount::get()),
2
));
// can not remove too few
assert_noop!(
CollatorSelection::leave_intent(RuntimeOrigin::signed(4)),
Error::<Test>::TooFewEligibleCollators,
);
})
}
#[test]
fn cannot_register_as_candidate_if_invulnerable() {
new_test_ext().execute_with(|| {
assert_eq!(Invulnerables::<Test>::get(), vec![1, 2]);
// can't 1 because it is invulnerable.
assert_noop!(
CollatorSelection::register_as_candidate(RuntimeOrigin::signed(1)),
Error::<Test>::AlreadyInvulnerable,
);
})
}
#[test]
fn cannot_register_as_candidate_if_keys_not_registered() {
new_test_ext().execute_with(|| {
// can't 7 because keys not registered.
assert_noop!(
CollatorSelection::register_as_candidate(RuntimeOrigin::signed(42)),
Error::<Test>::ValidatorNotRegistered
);
})
}
#[test]
fn cannot_register_dupe_candidate() {
new_test_ext().execute_with(|| {
// can add 3 as candidate
assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(3)));
// tuple of (id, deposit).
let addition = CandidateInfo { who: 3, deposit: 10 };
assert_eq!(
CandidateList::<Test>::get().iter().cloned().collect::<Vec<_>>(),
vec![addition]
);
assert_eq!(LastAuthoredBlock::<Test>::get(3), 10);
assert_eq!(Balances::free_balance(3), 90);
// but no more
assert_noop!(
CollatorSelection::register_as_candidate(RuntimeOrigin::signed(3)),
Error::<Test>::AlreadyCandidate,
);
})
}
#[test]
fn cannot_register_as_candidate_if_poor() {
new_test_ext().execute_with(|| {
assert_eq!(Balances::free_balance(3), 100);
assert_eq!(Balances::free_balance(33), 0);
// works
assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(3)));
// poor
assert_noop!(
CollatorSelection::register_as_candidate(RuntimeOrigin::signed(33)),
BalancesError::<Test>::InsufficientBalance,
);
});
}
#[test]
fn register_as_candidate_works() {
new_test_ext().execute_with(|| {
// given
assert_eq!(DesiredCandidates::<Test>::get(), 2);
assert_eq!(CandidacyBond::<Test>::get(), 10);
assert_eq!(CandidateList::<Test>::get().iter().count(), 0);
assert_eq!(Invulnerables::<Test>::get(), vec![1, 2]);
// take two endowed, non-invulnerables accounts.
assert_eq!(Balances::free_balance(3), 100);
assert_eq!(Balances::free_balance(4), 100);
assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(3)));
assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(4)));
assert_eq!(Balances::free_balance(3), 90);
assert_eq!(Balances::free_balance(4), 90);
assert_eq!(CandidateList::<Test>::get().iter().count(), 2);
});
}
#[test]
fn cannot_take_candidate_slot_if_invulnerable() {
new_test_ext().execute_with(|| {
assert_eq!(Invulnerables::<Test>::get(), vec![1, 2]);
// can't 1 because it is invulnerable.
assert_noop!(
CollatorSelection::take_candidate_slot(RuntimeOrigin::signed(1), 50u64.into(), 2),
Error::<Test>::AlreadyInvulnerable,
);
})
}
#[test]
fn cannot_take_candidate_slot_if_keys_not_registered() {
new_test_ext().execute_with(|| {
assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(3)));
assert_noop!(
CollatorSelection::take_candidate_slot(RuntimeOrigin::signed(42), 50u64.into(), 3),
Error::<Test>::ValidatorNotRegistered
);
})
}
#[test]
fn cannot_take_candidate_slot_if_duplicate() {
new_test_ext().execute_with(|| {
// can add 3 as candidate
assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(3)));
assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(4)));
// tuple of (id, deposit).
let candidate_3 = CandidateInfo { who: 3, deposit: 10 };
let candidate_4 = CandidateInfo { who: 4, deposit: 10 };
let actual_candidates = CandidateList::<Test>::get().iter().cloned().collect::<Vec<_>>();
assert_eq!(actual_candidates, vec![candidate_4, candidate_3]);
assert_eq!(LastAuthoredBlock::<Test>::get(3), 10);
assert_eq!(LastAuthoredBlock::<Test>::get(4), 10);
assert_eq!(Balances::free_balance(3), 90);
// but no more
assert_noop!(
CollatorSelection::take_candidate_slot(RuntimeOrigin::signed(3), 50u64.into(), 4),
Error::<Test>::AlreadyCandidate,
);
})
}
#[test]
fn cannot_take_candidate_slot_if_target_invalid() {
new_test_ext().execute_with(|| {
// can add 3 as candidate
assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(3)));
// tuple of (id, deposit).
let candidate_3 = CandidateInfo { who: 3, deposit: 10 };
assert_eq!(
CandidateList::<Test>::get().iter().cloned().collect::<Vec<_>>(),
vec![candidate_3]
);
assert_eq!(LastAuthoredBlock::<Test>::get(3), 10);
assert_eq!(Balances::free_balance(3), 90);
assert_eq!(Balances::free_balance(4), 100);
assert_noop!(
CollatorSelection::take_candidate_slot(RuntimeOrigin::signed(4), 50u64.into(), 5),
Error::<Test>::TargetIsNotCandidate,
);
})
}
#[test]
fn cannot_take_candidate_slot_if_poor() {
new_test_ext().execute_with(|| {
assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(4)));
assert_eq!(Balances::free_balance(3), 100);
assert_eq!(Balances::free_balance(33), 0);
// works
assert_ok!(CollatorSelection::take_candidate_slot(
RuntimeOrigin::signed(3),
20u64.into(),
4
));
// poor
assert_noop!(
CollatorSelection::take_candidate_slot(RuntimeOrigin::signed(33), 30u64.into(), 3),
BalancesError::<Test>::InsufficientBalance,
);
});
}
#[test]
fn cannot_take_candidate_slot_if_insufficient_deposit() {
new_test_ext().execute_with(|| {
assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(3)));
assert_ok!(CollatorSelection::update_bond(RuntimeOrigin::signed(3), 60u64.into()));
assert_eq!(Balances::free_balance(3), 40);
assert_eq!(Balances::free_balance(4), 100);
assert_noop!(
CollatorSelection::take_candidate_slot(RuntimeOrigin::signed(4), 5u64.into(), 3),
Error::<Test>::InsufficientBond,
);
assert_eq!(Balances::free_balance(3), 40);
assert_eq!(Balances::free_balance(4), 100);
});
}
#[test]
fn cannot_take_candidate_slot_if_deposit_less_than_target() {
new_test_ext().execute_with(|| {
assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(3)));
assert_ok!(CollatorSelection::update_bond(RuntimeOrigin::signed(3), 60u64.into()));
assert_eq!(Balances::free_balance(3), 40);
assert_eq!(Balances::free_balance(4), 100);
assert_noop!(
CollatorSelection::take_candidate_slot(RuntimeOrigin::signed(4), 20u64.into(), 3),
Error::<Test>::InsufficientBond,
);
assert_eq!(Balances::free_balance(3), 40);
assert_eq!(Balances::free_balance(4), 100);
});
}
#[test]
fn take_candidate_slot_works() {
new_test_ext().execute_with(|| {
// given
assert_eq!(DesiredCandidates::<Test>::get(), 2);
assert_eq!(CandidacyBond::<Test>::get(), 10);
assert_eq!(CandidateList::<Test>::get().iter().count(), 0);
assert_eq!(Invulnerables::<Test>::get(), vec![1, 2]);
// take two endowed, non-invulnerables accounts.
assert_eq!(Balances::free_balance(3), 100);
assert_eq!(Balances::free_balance(4), 100);
assert_eq!(Balances::free_balance(5), 100);
assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(3)));
assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(4)));
assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(5)));
assert_eq!(Balances::free_balance(3), 90);
assert_eq!(Balances::free_balance(4), 90);
assert_eq!(Balances::free_balance(5), 90);
assert_eq!(CandidateList::<Test>::get().iter().count(), 3);
Balances::make_free_balance_be(&6, 100);
let key = MockSessionKeys { aura: UintAuthorityId(6) };
Session::set_keys(RuntimeOrigin::signed(6).into(), key, Vec::new()).unwrap();
assert_ok!(CollatorSelection::take_candidate_slot(
RuntimeOrigin::signed(6),
50u64.into(),
4
));
assert_eq!(Balances::free_balance(3), 90);
assert_eq!(Balances::free_balance(4), 100);
assert_eq!(Balances::free_balance(5), 90);
assert_eq!(Balances::free_balance(6), 50);
// tuple of (id, deposit).
let candidate_3 = CandidateInfo { who: 3, deposit: 10 };
let candidate_6 = CandidateInfo { who: 6, deposit: 50 };
let candidate_5 = CandidateInfo { who: 5, deposit: 10 };
let mut actual_candidates =
CandidateList::<Test>::get().iter().cloned().collect::<Vec<_>>();
actual_candidates.sort_by(|info_1, info_2| info_1.deposit.cmp(&info_2.deposit));
assert_eq!(
CandidateList::<Test>::get().iter().cloned().collect::<Vec<_>>(),
vec![candidate_5, candidate_3, candidate_6]
);
});
}
#[test]
fn increase_candidacy_bond_non_candidate_account() {
new_test_ext().execute_with(|| {
// given
assert_eq!(DesiredCandidates::<Test>::get(), 2);
assert_eq!(CandidacyBond::<Test>::get(), 10);
assert_eq!(CandidateList::<Test>::get().iter().count(), 0);
assert_eq!(Invulnerables::<Test>::get(), vec![1, 2]);
assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(3)));
assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(4)));
assert_noop!(
CollatorSelection::update_bond(RuntimeOrigin::signed(5), 20),
Error::<Test>::NotCandidate
);
});
}
#[test]
fn increase_candidacy_bond_insufficient_balance() {
new_test_ext().execute_with(|| {
// given
assert_eq!(DesiredCandidates::<Test>::get(), 2);
assert_eq!(CandidacyBond::<Test>::get(), 10);
assert_eq!(CandidateList::<Test>::get().iter().count(), 0);
assert_eq!(Invulnerables::<Test>::get(), vec![1, 2]);
// take two endowed, non-invulnerables accounts.
assert_eq!(Balances::free_balance(3), 100);
assert_eq!(Balances::free_balance(4), 100);
assert_eq!(Balances::free_balance(5), 100);
assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(3)));
assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(4)));
assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(5)));
assert_eq!(Balances::free_balance(3), 90);
assert_eq!(Balances::free_balance(4), 90);
assert_eq!(Balances::free_balance(5), 90);
assert_noop!(
CollatorSelection::update_bond(RuntimeOrigin::signed(3), 110),
BalancesError::<Test>::InsufficientBalance
);
assert_eq!(Balances::free_balance(3), 90);
});
}
#[test]
fn increase_candidacy_bond_works() {
new_test_ext().execute_with(|| {
// given
assert_eq!(DesiredCandidates::<Test>::get(), 2);
assert_eq!(CandidacyBond::<Test>::get(), 10);
assert_eq!(CandidateList::<Test>::get().iter().count(), 0);
assert_eq!(Invulnerables::<Test>::get(), vec![1, 2]);
// take three endowed, non-invulnerables accounts.
assert_eq!(Balances::free_balance(3), 100);
assert_eq!(Balances::free_balance(4), 100);
assert_eq!(Balances::free_balance(5), 100);
assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(3)));
assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(4)));
assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(5)));
assert_eq!(Balances::free_balance(3), 90);
assert_eq!(Balances::free_balance(4), 90);
assert_eq!(Balances::free_balance(5), 90);
assert_ok!(CollatorSelection::update_bond(RuntimeOrigin::signed(3), 20));
assert_ok!(CollatorSelection::update_bond(RuntimeOrigin::signed(4), 30));
assert_ok!(CollatorSelection::update_bond(RuntimeOrigin::signed(5), 40));
assert_eq!(CandidateList::<Test>::get().iter().count(), 3);
assert_eq!(Balances::free_balance(3), 80);
assert_eq!(Balances::free_balance(4), 70);
assert_eq!(Balances::free_balance(5), 60);
assert_ok!(CollatorSelection::update_bond(RuntimeOrigin::signed(3), 40));
assert_ok!(CollatorSelection::update_bond(RuntimeOrigin::signed(4), 60));
assert_eq!(CandidateList::<Test>::get().iter().count(), 3);
assert_eq!(Balances::free_balance(3), 60);
assert_eq!(Balances::free_balance(4), 40);
assert_eq!(Balances::free_balance(5), 60);
});
}
#[test]
fn decrease_candidacy_bond_non_candidate_account() {
new_test_ext().execute_with(|| {
// given
assert_eq!(DesiredCandidates::<Test>::get(), 2);
assert_eq!(CandidacyBond::<Test>::get(), 10);
assert_eq!(CandidateList::<Test>::get().iter().count(), 0);
assert_eq!(Invulnerables::<Test>::get(), vec![1, 2]);
assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(3)));
assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(4)));
assert_eq!(Balances::free_balance(5), 100);
assert_noop!(
CollatorSelection::update_bond(RuntimeOrigin::signed(5), 10),
Error::<Test>::NotCandidate
);
assert_eq!(Balances::free_balance(5), 100);
});
}
#[test]
fn decrease_candidacy_bond_insufficient_funds() {
new_test_ext().execute_with(|| {
// given
assert_eq!(DesiredCandidates::<Test>::get(), 2);
assert_eq!(CandidacyBond::<Test>::get(), 10);
assert_eq!(CandidateList::<Test>::get().iter().count(), 0);
assert_eq!(Invulnerables::<Test>::get(), vec![1, 2]);
// take two endowed, non-invulnerables accounts.
assert_eq!(Balances::free_balance(3), 100);
assert_eq!(Balances::free_balance(4), 100);
assert_eq!(Balances::free_balance(5), 100);
assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(3)));
assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(4)));
assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(5)));
assert_ok!(CollatorSelection::update_bond(RuntimeOrigin::signed(3), 60));
assert_ok!(CollatorSelection::update_bond(RuntimeOrigin::signed(4), 60));
assert_ok!(CollatorSelection::update_bond(RuntimeOrigin::signed(5), 60));
assert_eq!(Balances::free_balance(3), 40);
assert_eq!(Balances::free_balance(4), 40);
assert_eq!(Balances::free_balance(5), 40);
assert_noop!(
CollatorSelection::update_bond(RuntimeOrigin::signed(3), 0),
Error::<Test>::DepositTooLow
);
assert_noop!(
CollatorSelection::update_bond(RuntimeOrigin::signed(4), 5),
Error::<Test>::DepositTooLow
);
assert_noop!(
CollatorSelection::update_bond(RuntimeOrigin::signed(5), 9),
Error::<Test>::DepositTooLow
);
assert_eq!(Balances::free_balance(3), 40);
assert_eq!(Balances::free_balance(4), 40);
assert_eq!(Balances::free_balance(5), 40);
});
}
#[test]
fn decrease_candidacy_bond_occupying_top_slot() {
new_test_ext().execute_with(|| {
assert_eq!(DesiredCandidates::<Test>::get(), 2);
// Register 3 candidates.
assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(3)));
assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(4)));
assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(5)));
// And update their bids.
assert_ok!(CollatorSelection::update_bond(RuntimeOrigin::signed(3), 30u64.into()));
assert_ok!(CollatorSelection::update_bond(RuntimeOrigin::signed(4), 30u64.into()));
assert_ok!(CollatorSelection::update_bond(RuntimeOrigin::signed(5), 60u64.into()));
// tuple of (id, deposit).
let candidate_3 = CandidateInfo { who: 3, deposit: 30 };
let candidate_4 = CandidateInfo { who: 4, deposit: 30 };
let candidate_5 = CandidateInfo { who: 5, deposit: 60 };
assert_eq!(
CandidateList::<Test>::get().iter().cloned().collect::<Vec<_>>(),
vec![candidate_4, candidate_3, candidate_5]
);
// Candidates 5 and 3 can't decrease their deposits because they are the 2 top candidates.
assert_noop!(
CollatorSelection::update_bond(RuntimeOrigin::signed(5), 29),
Error::<Test>::InvalidUnreserve,
);
assert_noop!(
CollatorSelection::update_bond(RuntimeOrigin::signed(3), 29),
Error::<Test>::InvalidUnreserve,
);
// But candidate 4 should have be able to decrease the deposit up to the minimum.
assert_ok!(CollatorSelection::update_bond(RuntimeOrigin::signed(4), 29u64.into()));
// Make candidate 4 outbid candidate 3, taking their spot as the second highest bid.
assert_ok!(CollatorSelection::update_bond(RuntimeOrigin::signed(4), 35u64.into()));
// tuple of (id, deposit).
let candidate_3 = CandidateInfo { who: 3, deposit: 30 };
let candidate_4 = CandidateInfo { who: 4, deposit: 35 };
let candidate_5 = CandidateInfo { who: 5, deposit: 60 };
assert_eq!(
CandidateList::<Test>::get().iter().cloned().collect::<Vec<_>>(),
vec![candidate_3, candidate_4, candidate_5]
);
// Now candidates 5 and 4 are the 2 top candidates, so they can't decrease their deposits.
assert_noop!(
CollatorSelection::update_bond(RuntimeOrigin::signed(5), 34),
Error::<Test>::InvalidUnreserve,
);
assert_noop!(
CollatorSelection::update_bond(RuntimeOrigin::signed(4), 34),
Error::<Test>::InvalidUnreserve,
);
// Candidate 3 should have be able to decrease the deposit up to the minimum now that
// they've fallen out of the top spots.
assert_ok!(CollatorSelection::update_bond(RuntimeOrigin::signed(3), 10u64.into()));
});
}
#[test]
fn decrease_candidacy_bond_works() {
new_test_ext().execute_with(|| {
// given
assert_eq!(DesiredCandidates::<Test>::get(), 2);
assert_eq!(CandidacyBond::<Test>::get(), 10);
assert_eq!(CandidateList::<Test>::get().iter().count(), 0);
assert_eq!(Invulnerables::<Test>::get(), vec![1, 2]);
// take three endowed, non-invulnerables accounts.
assert_eq!(Balances::free_balance(3), 100);
assert_eq!(Balances::free_balance(4), 100);
assert_eq!(Balances::free_balance(5), 100);
assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(3)));
assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(4)));
assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(5)));
assert_eq!(Balances::free_balance(3), 90);
assert_eq!(Balances::free_balance(4), 90);
assert_eq!(Balances::free_balance(5), 90);
assert_ok!(CollatorSelection::update_bond(RuntimeOrigin::signed(3), 20));
assert_ok!(CollatorSelection::update_bond(RuntimeOrigin::signed(4), 30));
assert_ok!(CollatorSelection::update_bond(RuntimeOrigin::signed(5), 40));
assert_eq!(CandidateList::<Test>::get().iter().count(), 3);
assert_eq!(Balances::free_balance(3), 80);
assert_eq!(Balances::free_balance(4), 70);
assert_eq!(Balances::free_balance(5), 60);
assert_ok!(CollatorSelection::update_bond(RuntimeOrigin::signed(3), 10));
assert_eq!(CandidateList::<Test>::get().iter().count(), 3);
assert_eq!(Balances::free_balance(3), 90);
assert_eq!(Balances::free_balance(4), 70);
assert_eq!(Balances::free_balance(5), 60);
});
}
#[test]
fn update_candidacy_bond_with_identical_amount() {
new_test_ext().execute_with(|| {
// given
assert_eq!(DesiredCandidates::<Test>::get(), 2);
assert_eq!(CandidacyBond::<Test>::get(), 10);
assert_eq!(CandidateList::<Test>::get().iter().count(), 0);
assert_eq!(Invulnerables::<Test>::get(), vec![1, 2]);
// take three endowed, non-invulnerables accounts.
assert_eq!(Balances::free_balance(3), 100);
assert_eq!(Balances::free_balance(4), 100);
assert_eq!(Balances::free_balance(5), 100);
assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(3)));
assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(4)));
assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(5)));
assert_eq!(Balances::free_balance(3), 90);
assert_eq!(Balances::free_balance(4), 90);
assert_eq!(Balances::free_balance(5), 90);
assert_ok!(CollatorSelection::update_bond(RuntimeOrigin::signed(3), 20));
assert_ok!(CollatorSelection::update_bond(RuntimeOrigin::signed(4), 30));
assert_ok!(CollatorSelection::update_bond(RuntimeOrigin::signed(5), 40));
assert_eq!(CandidateList::<Test>::get().iter().count(), 3);
assert_eq!(Balances::free_balance(3), 80);
assert_eq!(Balances::free_balance(4), 70);
assert_eq!(Balances::free_balance(5), 60);
assert_noop!(
CollatorSelection::update_bond(RuntimeOrigin::signed(3), 20),
Error::<Test>::IdenticalDeposit
);
assert_eq!(Balances::free_balance(3), 80);
});
}
#[test]
fn candidate_list_works() {
new_test_ext().execute_with(|| {
// given
assert_eq!(DesiredCandidates::<Test>::get(), 2);
assert_eq!(CandidacyBond::<Test>::get(), 10);
assert_eq!(CandidateList::<Test>::get().iter().count(), 0);
assert_eq!(Invulnerables::<Test>::get(), vec![1, 2]);
// take three endowed, non-invulnerables accounts.
assert_eq!(Balances::free_balance(3), 100);
assert_eq!(Balances::free_balance(4), 100);
assert_eq!(Balances::free_balance(5), 100);
assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(3)));
assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(4)));
assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(5)));
assert_ok!(CollatorSelection::update_bond(RuntimeOrigin::signed(5), 20));
assert_ok!(CollatorSelection::update_bond(RuntimeOrigin::signed(3), 30));
assert_ok!(CollatorSelection::update_bond(RuntimeOrigin::signed(4), 25));
assert_ok!(CollatorSelection::update_bond(RuntimeOrigin::signed(5), 10));
let candidate_3 = CandidateInfo { who: 3, deposit: 30 };
let candidate_4 = CandidateInfo { who: 4, deposit: 25 };
let candidate_5 = CandidateInfo { who: 5, deposit: 10 };
assert_eq!(
CandidateList::<Test>::get().iter().cloned().collect::<Vec<_>>(),
vec![candidate_5, candidate_4, candidate_3]
);
});
}
#[test]
fn leave_intent() {
new_test_ext().execute_with(|| {
// register a candidate.
assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(3)));
assert_eq!(Balances::free_balance(3), 90);
// register too so can leave above min candidates
assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(5)));
assert_eq!(Balances::free_balance(5), 90);
// cannot leave if not candidate.
assert_noop!(
CollatorSelection::leave_intent(RuntimeOrigin::signed(4)),
Error::<Test>::NotCandidate
);
// bond is returned
assert_ok!(CollatorSelection::leave_intent(RuntimeOrigin::signed(3)));
assert_eq!(Balances::free_balance(3), 100);
assert_eq!(LastAuthoredBlock::<Test>::get(3), 0);
});
}
#[test]
fn authorship_event_handler() {
new_test_ext().execute_with(|| {
// put 100 in the pot + 5 for ED
Balances::make_free_balance_be(&CollatorSelection::account_id(), 105);
// 4 is the default author.
assert_eq!(Balances::free_balance(4), 100);
assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(4)));
// triggers `note_author`
Authorship::on_initialize(1);
// tuple of (id, deposit).
let collator = CandidateInfo { who: 4, deposit: 10 };
assert_eq!(
CandidateList::<Test>::get().iter().cloned().collect::<Vec<_>>(),
vec![collator]
);
assert_eq!(LastAuthoredBlock::<Test>::get(4), 0);
// half of the pot goes to the collator who's the author (4 in tests).
assert_eq!(Balances::free_balance(4), 140);
// half + ED stays.
assert_eq!(Balances::free_balance(CollatorSelection::account_id()), 55);
});
}
#[test]
fn fees_edgecases() {
new_test_ext().execute_with(|| {
// Nothing panics, no reward when no ED in balance
Authorship::on_initialize(1);
// put some money into the pot at ED
Balances::make_free_balance_be(&CollatorSelection::account_id(), 5);
// 4 is the default author.
assert_eq!(Balances::free_balance(4), 100);
assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(4)));
// triggers `note_author`
Authorship::on_initialize(1);
// tuple of (id, deposit).
let collator = CandidateInfo { who: 4, deposit: 10 };
assert_eq!(
CandidateList::<Test>::get().iter().cloned().collect::<Vec<_>>(),
vec![collator]
);
assert_eq!(LastAuthoredBlock::<Test>::get(4), 0);
// Nothing received
assert_eq!(Balances::free_balance(4), 90);
// all fee stays
assert_eq!(Balances::free_balance(CollatorSelection::account_id()), 5);
});
}
#[test]
fn session_management_single_candidate() {
new_test_ext().execute_with(|| {
initialize_to_block(1);
assert_eq!(SessionChangeBlock::get(), 0);
assert_eq!(SessionHandlerCollators::get(), vec![1, 2]);
initialize_to_block(4);
assert_eq!(SessionChangeBlock::get(), 0);
assert_eq!(SessionHandlerCollators::get(), vec![1, 2]);
// add a new collator
assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(3)));
// session won't see this.
assert_eq!(SessionHandlerCollators::get(), vec![1, 2]);
// but we have a new candidate.
assert_eq!(CandidateList::<Test>::get().iter().count(), 1);
initialize_to_block(10);
assert_eq!(SessionChangeBlock::get(), 10);
// pallet-session has 1 session delay; current validators are the same.
assert_eq!(Session::validators(), vec![1, 2]);
// queued ones are changed, and now we have 3.
assert_eq!(Session::queued_keys().len(), 3);
// session handlers (aura, et. al.) cannot see this yet.
assert_eq!(SessionHandlerCollators::get(), vec![1, 2]);
initialize_to_block(20);
assert_eq!(SessionChangeBlock::get(), 20);
// changed are now reflected to session handlers.
assert_eq!(SessionHandlerCollators::get(), vec![1, 2, 3]);
});
}
#[test]
fn session_management_max_candidates() {
new_test_ext().execute_with(|| {
initialize_to_block(1);
assert_eq!(SessionChangeBlock::get(), 0);
assert_eq!(SessionHandlerCollators::get(), vec![1, 2]);
initialize_to_block(4);
assert_eq!(SessionChangeBlock::get(), 0);
assert_eq!(SessionHandlerCollators::get(), vec![1, 2]);
assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(3)));
assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(4)));
assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(5)));
// session won't see this.
assert_eq!(SessionHandlerCollators::get(), vec![1, 2]);
// but we have a new candidate.
assert_eq!(CandidateList::<Test>::get().iter().count(), 3);
initialize_to_block(10);
assert_eq!(SessionChangeBlock::get(), 10);
// pallet-session has 1 session delay; current validators are the same.
assert_eq!(Session::validators(), vec![1, 2]);
// queued ones are changed, and now we have 4.
assert_eq!(Session::queued_keys().len(), 4);
// session handlers (aura, et. al.) cannot see this yet.
assert_eq!(SessionHandlerCollators::get(), vec![1, 2]);
initialize_to_block(20);
assert_eq!(SessionChangeBlock::get(), 20);
// changed are now reflected to session handlers.
assert_eq!(SessionHandlerCollators::get(), vec![1, 2, 3, 4]);
});
}
#[test]
fn session_management_increase_bid_with_list_update() {
new_test_ext().execute_with(|| {
initialize_to_block(1);
assert_eq!(SessionChangeBlock::get(), 0);
assert_eq!(SessionHandlerCollators::get(), vec![1, 2]);
initialize_to_block(4);
assert_eq!(SessionChangeBlock::get(), 0);
assert_eq!(SessionHandlerCollators::get(), vec![1, 2]);
assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(3)));
assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(4)));
assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(5)));
assert_ok!(CollatorSelection::update_bond(RuntimeOrigin::signed(5), 60));
// session won't see this.
assert_eq!(SessionHandlerCollators::get(), vec![1, 2]);
// but we have a new candidate.
assert_eq!(CandidateList::<Test>::get().iter().count(), 3);
initialize_to_block(10);
assert_eq!(SessionChangeBlock::get(), 10);
// pallet-session has 1 session delay; current validators are the same.
assert_eq!(Session::validators(), vec![1, 2]);
// queued ones are changed, and now we have 4.
assert_eq!(Session::queued_keys().len(), 4);
// session handlers (aura, et. al.) cannot see this yet.
assert_eq!(SessionHandlerCollators::get(), vec![1, 2]);
initialize_to_block(20);
assert_eq!(SessionChangeBlock::get(), 20);
// changed are now reflected to session handlers.
assert_eq!(SessionHandlerCollators::get(), vec![1, 2, 5, 3]);
});
}
#[test]
fn session_management_candidate_list_eager_sort() {
new_test_ext().execute_with(|| {
initialize_to_block(1);
assert_eq!(SessionChangeBlock::get(), 0);
assert_eq!(SessionHandlerCollators::get(), vec![1, 2]);
initialize_to_block(4);
assert_eq!(SessionChangeBlock::get(), 0);
assert_eq!(SessionHandlerCollators::get(), vec![1, 2]);
assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(3)));
assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(4)));
assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(5)));
assert_ok!(CollatorSelection::update_bond(RuntimeOrigin::signed(5), 60));
// session won't see this.
assert_eq!(SessionHandlerCollators::get(), vec![1, 2]);
// but we have a new candidate.
assert_eq!(CandidateList::<Test>::get().iter().count(), 3);
initialize_to_block(10);
assert_eq!(SessionChangeBlock::get(), 10);
// pallet-session has 1 session delay; current validators are the same.
assert_eq!(Session::validators(), vec![1, 2]);
// queued ones are changed, and now we have 4.
assert_eq!(Session::queued_keys().len(), 4);
// session handlers (aura, et. al.) cannot see this yet.
assert_eq!(SessionHandlerCollators::get(), vec![1, 2]);
initialize_to_block(20);
assert_eq!(SessionChangeBlock::get(), 20);
// changed are now reflected to session handlers.
assert_eq!(SessionHandlerCollators::get(), vec![1, 2, 5, 3]);
});
}
#[test]
fn session_management_reciprocal_outbidding() {
new_test_ext().execute_with(|| {
initialize_to_block(1);
assert_eq!(SessionChangeBlock::get(), 0);
assert_eq!(SessionHandlerCollators::get(), vec![1, 2]);
initialize_to_block(4);
assert_eq!(SessionChangeBlock::get(), 0);
assert_eq!(SessionHandlerCollators::get(), vec![1, 2]);
assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(3)));
assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(4)));
assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(5)));
assert_ok!(CollatorSelection::update_bond(RuntimeOrigin::signed(5), 60));
initialize_to_block(5);
// candidates 3 and 4 saw they were outbid and preemptively bid more
// than 5 in the next block.
assert_ok!(CollatorSelection::update_bond(RuntimeOrigin::signed(4), 70));
assert_ok!(CollatorSelection::update_bond(RuntimeOrigin::signed(3), 70));
// session won't see this.
assert_eq!(SessionHandlerCollators::get(), vec![1, 2]);
// but we have a new candidate.
assert_eq!(CandidateList::<Test>::get().iter().count(), 3);
initialize_to_block(10);
assert_eq!(SessionChangeBlock::get(), 10);
// pallet-session has 1 session delay; current validators are the same.
assert_eq!(Session::validators(), vec![1, 2]);
// queued ones are changed, and now we have 4.
assert_eq!(Session::queued_keys().len(), 4);
// session handlers (aura, et. al.) cannot see this yet.
assert_eq!(SessionHandlerCollators::get(), vec![1, 2]);
initialize_to_block(20);
assert_eq!(SessionChangeBlock::get(), 20);
// changed are now reflected to session handlers.
assert_eq!(SessionHandlerCollators::get(), vec![1, 2, 4, 3]);
});
}
#[test]
fn session_management_decrease_bid_after_auction() {
new_test_ext().execute_with(|| {
initialize_to_block(1);
assert_eq!(SessionChangeBlock::get(), 0);
assert_eq!(SessionHandlerCollators::get(), vec![1, 2]);
initialize_to_block(4);
assert_eq!(SessionChangeBlock::get(), 0);
assert_eq!(SessionHandlerCollators::get(), vec![1, 2]);
assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(3)));
assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(4)));
assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(5)));
assert_ok!(CollatorSelection::update_bond(RuntimeOrigin::signed(5), 60));
initialize_to_block(5);
assert_ok!(CollatorSelection::update_bond(RuntimeOrigin::signed(4), 70));
assert_ok!(CollatorSelection::update_bond(RuntimeOrigin::signed(3), 70));
initialize_to_block(5);
// candidate 5 saw it was outbid and wants to take back its bid, but
// not entirely so they still keep their place in the candidate list
// in case there is an opportunity in the future.
assert_ok!(CollatorSelection::update_bond(RuntimeOrigin::signed(5), 10));
// session won't see this.
assert_eq!(SessionHandlerCollators::get(), vec![1, 2]);
// but we have a new candidate.
assert_eq!(CandidateList::<Test>::get().iter().count(), 3);
initialize_to_block(10);
assert_eq!(SessionChangeBlock::get(), 10);
// pallet-session has 1 session delay; current validators are the same.
assert_eq!(Session::validators(), vec![1, 2]);
// queued ones are changed, and now we have 4.
assert_eq!(Session::queued_keys().len(), 4);
// session handlers (aura, et. al.) cannot see this yet.
assert_eq!(SessionHandlerCollators::get(), vec![1, 2]);
initialize_to_block(20);
assert_eq!(SessionChangeBlock::get(), 20);
// changed are now reflected to session handlers.
assert_eq!(SessionHandlerCollators::get(), vec![1, 2, 4, 3]);
});
}
#[test]
fn kick_mechanism() {
new_test_ext().execute_with(|| {
// add a new collator
assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(3)));
assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(4)));
initialize_to_block(10);
assert_eq!(CandidateList::<Test>::get().iter().count(), 2);
initialize_to_block(20);
assert_eq!(SessionChangeBlock::get(), 20);
// 4 authored this block, gets to stay 3 was kicked
assert_eq!(CandidateList::<Test>::get().iter().count(), 1);
// 3 will be kicked after 1 session delay
assert_eq!(SessionHandlerCollators::get(), vec![1, 2, 3, 4]);
// tuple of (id, deposit).
let collator = CandidateInfo { who: 4, deposit: 10 };
assert_eq!(
CandidateList::<Test>::get().iter().cloned().collect::<Vec<_>>(),
vec![collator]
);
assert_eq!(LastAuthoredBlock::<Test>::get(4), 20);
initialize_to_block(30);
// 3 gets kicked after 1 session delay
assert_eq!(SessionHandlerCollators::get(), vec![1, 2, 4]);
// kicked collator gets funds back
assert_eq!(Balances::free_balance(3), 100);
});
}
#[test]
fn should_not_kick_mechanism_too_few() {
new_test_ext().execute_with(|| {
// remove the invulnerables and add new collators 3 and 5
assert_eq!(CandidateList::<Test>::get().iter().count(), 0);
assert_eq!(Invulnerables::<Test>::get(), vec![1, 2]);
assert_ok!(CollatorSelection::remove_invulnerable(
RuntimeOrigin::signed(RootAccount::get()),
1
));
assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(3)));
assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(5)));
assert_ok!(CollatorSelection::remove_invulnerable(
RuntimeOrigin::signed(RootAccount::get()),
2
));
initialize_to_block(10);
assert_eq!(CandidateList::<Test>::get().iter().count(), 2);
initialize_to_block(20);
assert_eq!(SessionChangeBlock::get(), 20);
// 4 authored this block, 3 is kicked, 5 stays because of too few collators
assert_eq!(CandidateList::<Test>::get().iter().count(), 1);
// 3 will be kicked after 1 session delay
assert_eq!(SessionHandlerCollators::get(), vec![3, 5]);
// tuple of (id, deposit).
let collator = CandidateInfo { who: 3, deposit: 10 };
assert_eq!(
CandidateList::<Test>::get().iter().cloned().collect::<Vec<_>>(),
vec![collator]
);
assert_eq!(LastAuthoredBlock::<Test>::get(4), 20);
initialize_to_block(30);
// 3 gets kicked after 1 session delay
assert_eq!(SessionHandlerCollators::get(), vec![3]);
// kicked collator gets funds back
assert_eq!(Balances::free_balance(5), 100);
});
}
#[test]
fn should_kick_invulnerables_from_candidates_on_session_change() {
new_test_ext().execute_with(|| {
assert_eq!(CandidateList::<Test>::get().iter().count(), 0);
assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(3)));
assert_ok!(CollatorSelection::register_as_candidate(RuntimeOrigin::signed(4)));
assert_eq!(Balances::free_balance(3), 90);
assert_eq!(Balances::free_balance(4), 90);
assert_ok!(CollatorSelection::set_invulnerables(
RuntimeOrigin::signed(RootAccount::get()),
vec![1, 2, 3]
));
// tuple of (id, deposit).
let collator_3 = CandidateInfo { who: 3, deposit: 10 };
let collator_4 = CandidateInfo { who: 4, deposit: 10 };
let actual_candidates = CandidateList::<Test>::get().iter().cloned().collect::<Vec<_>>();
assert_eq!(actual_candidates, vec![collator_4.clone(), collator_3]);
assert_eq!(Invulnerables::<Test>::get(), vec![1, 2, 3]);
// session change
initialize_to_block(10);
// 3 is removed from candidates
assert_eq!(
CandidateList::<Test>::get().iter().cloned().collect::<Vec<_>>(),
vec![collator_4]
);
// but not from invulnerables
assert_eq!(Invulnerables::<Test>::get(), vec![1, 2, 3]);
// and it got its deposit back
assert_eq!(Balances::free_balance(3), 100);
});
}
#[test]
#[should_panic = "duplicate invulnerables in genesis."]
fn cannot_set_genesis_value_twice() {
sp_tracing::try_init_simple();
let mut t = frame_system::GenesisConfig::<Test>::default().build_storage().unwrap();
let invulnerables = vec![1, 1];
let collator_selection = collator_selection::GenesisConfig::<Test> {
desired_candidates: 2,
candidacy_bond: 10,
invulnerables,
};
// collator selection must be initialized before session.
collator_selection.assimilate_storage(&mut t).unwrap();
}