Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions pallets/author-slot-filter/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ edition = "2021"
version = "0.9.0"

[dependencies]
environmental = { workspace = true }
frame-support = { workspace = true }
frame-system = { workspace = true }
log = { workspace = true }
Expand Down
28 changes: 27 additions & 1 deletion pallets/author-slot-filter/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,14 +48,23 @@ pub mod pallet {
use super::*;
use crate::num::NonZeroU32;
use crate::weights::WeightInfo;
use frame_support::{pallet_prelude::*, traits::Randomness};
use frame_support::{pallet_prelude::*, storage::unhashed, traits::Randomness};
use frame_system::pallet_prelude::*;
use log::debug;
use nimbus_primitives::CanAuthor;
use sp_core::H256;
use sp_runtime::Percent;
use sp_std::vec::Vec;

/// Storage key to be used for fake author logic
pub(crate) const IS_FAKE_AUTHOR_KEY: &[u8] = b"AuthorSlotFilter:IsFakeAuthor";

environmental::environmental!(IS_FAKE_AUTHOR: ());
/// Use fake author logic
pub fn using_fake_author<R, F: FnOnce() -> R>(mutator: F) -> R {
IS_FAKE_AUTHOR::using(&mut (), mutator)
}

/// The Author Filter pallet
#[pallet::pallet]
#[pallet::without_storage_info]
Expand All @@ -76,6 +85,18 @@ pub mod pallet {
type WeightInfo: WeightInfo;
}

/// Hook to set the fake author logic storage key when using fake author logic
#[pallet::hooks]
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
fn on_initialize(_now: BlockNumberFor<T>) -> Weight {
if IS_FAKE_AUTHOR::with(|_| ()).is_some() {
unhashed::put(IS_FAKE_AUTHOR_KEY, &());
}
// Account for 1 write to the fake author logic storage key
T::DbWeight::get().writes(1)
}
}

/// Compute a pseudo-random subset of the input accounts by using Pallet's
/// source of randomness, `Config::RandomnessSource`.
/// Returns (Eligible, Ineligible), each is a set of accounts
Expand Down Expand Up @@ -127,6 +148,11 @@ pub mod pallet {
impl<T: Config> CanAuthor<T::AccountId> for Pallet<T> {
#[cfg(not(feature = "try-runtime"))]
fn can_author(author: &T::AccountId, slot: &u32) -> bool {
// Check if fake author logic is being used
if unhashed::get::<()>(IS_FAKE_AUTHOR_KEY).is_some() {
return true;
}

// Compute pseudo-random subset of potential authors
let (eligible, ineligible) =
compute_pseudo_random_subset::<T>(T::PotentialAuthors::get(), slot);
Expand Down
60 changes: 59 additions & 1 deletion pallets/author-slot-filter/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,18 @@ pub use crate::mock::*;
use crate::num::NonZeroU32;

use frame_support::assert_ok;
use frame_support::traits::OnRuntimeUpgrade;
use frame_support::storage::unhashed;
use frame_support::traits::{OnInitialize, OnRuntimeUpgrade};
use frame_support::weights::Weight;
use nimbus_primitives::CanAuthor;
use sp_runtime::Percent;

#[test]
fn test_set_eligibility_works() {
new_test_ext().execute_with(|| {
// Fake logic storage key should be empty
assert!(unhashed::get::<()>(IS_FAKE_AUTHOR_KEY).is_none());

let value = num::NonZeroU32::new_unchecked(34);

assert_ok!(AuthorSlotFilter::set_eligible(
Expand All @@ -40,6 +45,9 @@ fn test_set_eligibility_works() {
#[test]
fn test_migration_works_for_converting_existing_eligible_ratio_to_eligible_count() {
new_test_ext().execute_with(|| {
// Fake logic storage key should be empty
assert!(unhashed::get::<()>(IS_FAKE_AUTHOR_KEY).is_none());

let input_eligible_ratio = Percent::from_percent(50);
let total_author_count = mock::Authors::get().len();
let eligible_author_count = input_eligible_ratio.mul_ceil(total_author_count) as u32;
Expand All @@ -63,6 +71,9 @@ fn test_migration_works_for_converting_existing_eligible_ratio_to_eligible_count
#[test]
fn test_migration_works_for_converting_existing_zero_eligible_ratio_to_default_eligible_count() {
new_test_ext().execute_with(|| {
// Fake logic storage key should be empty
assert!(unhashed::get::<()>(IS_FAKE_AUTHOR_KEY).is_none());

let input_eligible_ratio = Percent::from_percent(0);
let expected_eligible_count = EligibilityValue::default();
let expected_weight =
Expand All @@ -84,6 +95,9 @@ fn test_migration_works_for_converting_existing_zero_eligible_ratio_to_default_e
#[test]
fn test_migration_inserts_default_value_for_missing_eligible_ratio() {
new_test_ext().execute_with(|| {
// Fake logic storage key should be empty
assert!(unhashed::get::<()>(IS_FAKE_AUTHOR_KEY).is_none());

let default_eligible_ratio = Percent::from_percent(50);
let expected_default_eligible_count =
NonZeroU32::new_unchecked(default_eligible_ratio.mul_ceil(Authors::get().len() as u32));
Expand All @@ -97,3 +111,47 @@ fn test_migration_inserts_default_value_for_missing_eligible_ratio() {
assert_eq!(expected_default_eligible_count, actual_eligible_count);
});
}

#[test]
fn test_can_author_works() {
new_test_ext().execute_with(|| {
// Fake logic storage key should be empty
assert!(unhashed::get::<()>(IS_FAKE_AUTHOR_KEY).is_none());
AuthorSlotFilter::on_initialize(System::block_number());
// Fake logic storage key should still be empty
assert!(unhashed::get::<()>(IS_FAKE_AUTHOR_KEY).is_none());

// PotentialAuthors can author every slot
for author in <Test as pallet::Config>::PotentialAuthors::get() {
assert!(AuthorSlotFilter::can_author(&author, &0));
assert!(AuthorSlotFilter::can_author(&author, &1));
assert!(AuthorSlotFilter::can_author(&author, &42));
}

// Author outside of PotentialAuthors cannot author any slot
assert!(!AuthorSlotFilter::can_author(&42, &0));
assert!(!AuthorSlotFilter::can_author(&42, &1));
assert!(!AuthorSlotFilter::can_author(&42, &42));
});
}

#[test]
fn test_using_fake_author_works() {
new_test_ext().execute_with(|| {
using_fake_author(|| {
// Fake logic storage key should be empty
assert!(unhashed::get::<()>(IS_FAKE_AUTHOR_KEY).is_none());
AuthorSlotFilter::on_initialize(System::block_number());
// Fake logic storage key should now be set
assert!(unhashed::get::<()>(IS_FAKE_AUTHOR_KEY).is_some());

// Author 1 can still author every slot
assert!(AuthorSlotFilter::can_author(&1, &0));

// Fake (any) author can also author every slot when using fake author logic
assert!(AuthorSlotFilter::can_author(&42, &0));
assert!(AuthorSlotFilter::can_author(&42, &1));
assert!(AuthorSlotFilter::can_author(&42, &42));
});
});
}