diff --git a/.github/workflows/integration_test_calamari.yml b/.github/workflows/integration_test_calamari.yml index a8d4b4a4e..6ce37d0a1 100644 --- a/.github/workflows/integration_test_calamari.yml +++ b/.github/workflows/integration_test_calamari.yml @@ -76,7 +76,7 @@ jobs: - name: init run: | sudo apt update - sudo apt install -y pkg-config libssl-dev + sudo apt install -y pkg-config libssl-dev yarn curl -s https://sh.rustup.rs -sSf | sh -s -- -y source ${HOME}/.cargo/env rustup toolchain install stable diff --git a/.github/workflows/run_linters.yml b/.github/workflows/run_linters.yml index 0dbff7c60..8f3ce4764 100644 --- a/.github/workflows/run_linters.yml +++ b/.github/workflows/run_linters.yml @@ -72,8 +72,8 @@ jobs: run: | curl -s https://sh.rustup.rs -sSf | sh -s -- -y source ${HOME}/.cargo/env - rustup toolchain install nightly - rustup default nightly + rustup toolchain install nightly-2023-03-13 + rustup default nightly-2023-03-13 cargo install taplo-cli - name: Check Formatting env: diff --git a/pallets/parachain-staking/src/lib.rs b/pallets/parachain-staking/src/lib.rs index c072e195e..7e7b281d5 100644 --- a/pallets/parachain-staking/src/lib.rs +++ b/pallets/parachain-staking/src/lib.rs @@ -1687,8 +1687,17 @@ pub mod pallet { let mut collators = candidates .into_iter() .rev() + .filter(|x| { + // Only consider collators above minimum total stake and self-bond + x.amount >= T::MinCollatorStk::get() && + if let Some(info) = Self::candidate_info(x.owner.clone()) { + info.bond >= T::MinCandidateStk::get() + } else { + log::error!("Candidate did not have CandidateInfo in storage, this should not happen"); + false + } + }) .take(top_n) - .filter(|x| x.amount >= T::MinCollatorStk::get()) .map(|x| x.owner) .collect::>(); collators.sort(); diff --git a/runtime/calamari/tests/integrations_mock/integration_tests.rs b/runtime/calamari/tests/integrations_mock/integration_tests.rs index d9fdeb85e..a312b7910 100644 --- a/runtime/calamari/tests/integrations_mock/integration_tests.rs +++ b/runtime/calamari/tests/integrations_mock/integration_tests.rs @@ -604,6 +604,73 @@ fn collator_cant_join_below_standard_bond() { }); } +#[test] +fn collator_with_large_stake_but_too_low_self_bond_not_selected_for_block_production() { + ExtBuilder::default() + .with_balances(vec![ + (ALICE.clone(), EARLY_COLLATOR_MINIMUM_STAKE + 100), + (BOB.clone(), MIN_BOND_TO_BE_CONSIDERED_COLLATOR + 100), + (CHARLIE.clone(), MIN_BOND_TO_BE_CONSIDERED_COLLATOR + 100), + (DAVE.clone(), MIN_BOND_TO_BE_CONSIDERED_COLLATOR + 100), + (EVE.clone(), MIN_BOND_TO_BE_CONSIDERED_COLLATOR + 100), + (FERDIE.clone(), MIN_BOND_TO_BE_CONSIDERED_COLLATOR + 100), + (USER.clone(), 400_000_000 * KMA), + ]) + .with_invulnerables(vec![]) + .with_authorities(vec![ + (ALICE.clone(), ALICE_SESSION_KEYS.clone()), + (BOB.clone(), BOB_SESSION_KEYS.clone()), + (CHARLIE.clone(), CHARLIE_SESSION_KEYS.clone()), + (DAVE.clone(), DAVE_SESSION_KEYS.clone()), + (EVE.clone(), EVE_SESSION_KEYS.clone()), + (FERDIE.clone(), FERDIE_SESSION_KEYS.clone()), + ]) + .build() + .execute_with(|| { + initialize_collators_through_whitelist(vec![ + ALICE.clone(), + BOB.clone(), + CHARLIE.clone(), + DAVE.clone(), + EVE.clone(), + FERDIE.clone(), + ]); + // Increase self-bond for everyone but ALICE + for collator in vec![ + BOB.clone(), + CHARLIE.clone(), + DAVE.clone(), + EVE.clone(), + FERDIE.clone(), + ] { + assert_ok!(ParachainStaking::candidate_bond_more( + Origin::signed(collator.clone()), + MIN_BOND_TO_BE_CONSIDERED_COLLATOR - EARLY_COLLATOR_MINIMUM_STAKE + )); + } + + // Delegate a large amount of tokens to EVE and ALICE + for collator in vec![EVE.clone(), ALICE.clone()] { + assert_ok!(ParachainStaking::delegate( + Origin::signed(USER.clone()), + collator, + 100_000_000 * KMA, + 50, + 50 + )); + } + + // Ensure ALICE is not selected despite having a large total stake through delegation + // NOTE: Must use 6 or more collators because 5 is the minimum on calamari + assert!(!ParachainStaking::compute_top_candidates().contains(&ALICE)); + assert!(ParachainStaking::compute_top_candidates().contains(&BOB)); + assert!(ParachainStaking::compute_top_candidates().contains(&CHARLIE)); + assert!(ParachainStaking::compute_top_candidates().contains(&DAVE)); + assert!(ParachainStaking::compute_top_candidates().contains(&EVE)); + assert!(ParachainStaking::compute_top_candidates().contains(&FERDIE)); + }); +} + #[test] fn collator_can_leave_if_below_standard_bond() { ExtBuilder::default() @@ -646,12 +713,12 @@ fn collator_can_leave_if_below_standard_bond() { fn collator_with_400k_not_selected_for_block_production() { ExtBuilder::default() .with_balances(vec![ - (ALICE.clone(), MIN_BOND_TO_BE_CONSIDERED_COLLATOR + 100), + (ALICE.clone(), EARLY_COLLATOR_MINIMUM_STAKE + 100), (BOB.clone(), MIN_BOND_TO_BE_CONSIDERED_COLLATOR + 100), (CHARLIE.clone(), MIN_BOND_TO_BE_CONSIDERED_COLLATOR + 100), (DAVE.clone(), MIN_BOND_TO_BE_CONSIDERED_COLLATOR + 100), (EVE.clone(), MIN_BOND_TO_BE_CONSIDERED_COLLATOR + 100), - (FERDIE.clone(), EARLY_COLLATOR_MINIMUM_STAKE + 100), + (FERDIE.clone(), MIN_BOND_TO_BE_CONSIDERED_COLLATOR + 100), ]) .with_invulnerables(vec![]) .with_authorities(vec![ @@ -674,11 +741,11 @@ fn collator_with_400k_not_selected_for_block_production() { ]); // Increase bond for everyone but FERDIE for collator in vec![ - ALICE.clone(), BOB.clone(), CHARLIE.clone(), DAVE.clone(), EVE.clone(), + FERDIE.clone(), ] { assert_ok!(ParachainStaking::candidate_bond_more( Origin::signed(collator.clone()), @@ -688,12 +755,12 @@ fn collator_with_400k_not_selected_for_block_production() { // Ensure CHARLIE and later are not selected // NOTE: Must use 6 or more collators because 5 is the minimum on calamari - assert!(ParachainStaking::compute_top_candidates().contains(&ALICE)); + assert!(!ParachainStaking::compute_top_candidates().contains(&ALICE)); assert!(ParachainStaking::compute_top_candidates().contains(&BOB)); assert!(ParachainStaking::compute_top_candidates().contains(&CHARLIE)); assert!(ParachainStaking::compute_top_candidates().contains(&DAVE)); assert!(ParachainStaking::compute_top_candidates().contains(&EVE)); - assert!(!ParachainStaking::compute_top_candidates().contains(&FERDIE)); + assert!(ParachainStaking::compute_top_candidates().contains(&FERDIE)); }); } diff --git a/runtime/calamari/tests/integrations_mock/mod.rs b/runtime/calamari/tests/integrations_mock/mod.rs index 8a7b126aa..2a01ad17f 100644 --- a/runtime/calamari/tests/integrations_mock/mod.rs +++ b/runtime/calamari/tests/integrations_mock/mod.rs @@ -44,6 +44,7 @@ lazy_static! { pub(crate) static ref DAVE: AccountId = unchecked_account_id::("Dave"); pub(crate) static ref EVE: AccountId = unchecked_account_id::("Eve"); pub(crate) static ref FERDIE: AccountId = unchecked_account_id::("Ferdie"); + pub(crate) static ref USER: AccountId = unchecked_account_id::("User"); pub(crate) static ref ALICE_SESSION_KEYS: SessionKeys = SessionKeys::from_seed_unchecked("Alice"); pub(crate) static ref BOB_SESSION_KEYS: SessionKeys = SessionKeys::from_seed_unchecked("Bob"); @@ -96,4 +97,8 @@ pub fn initialize_collators_through_whitelist(collators: Vec) { ParachainStaking::candidate_pool().len(), candidate_count as usize ); + assert_ok!(ParachainStaking::set_total_selected( + root_origin(), + candidate_count + )); }