diff --git a/Cargo.lock b/Cargo.lock index 4d2324b..96f4d94 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7265,6 +7265,7 @@ dependencies = [ name = "pallet-author-slot-filter" version = "0.9.0" dependencies = [ + "environmental", "frame-benchmarking", "frame-support", "frame-support-test", diff --git a/pallets/author-slot-filter/Cargo.toml b/pallets/author-slot-filter/Cargo.toml index ea721b1..a5a7e8f 100644 --- a/pallets/author-slot-filter/Cargo.toml +++ b/pallets/author-slot-filter/Cargo.toml @@ -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 } diff --git a/pallets/author-slot-filter/src/lib.rs b/pallets/author-slot-filter/src/lib.rs index e142202..0a241e7 100644 --- a/pallets/author-slot-filter/src/lib.rs +++ b/pallets/author-slot-filter/src/lib.rs @@ -48,7 +48,7 @@ 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; @@ -56,6 +56,15 @@ pub mod pallet { 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>(mutator: F) -> R { + IS_FAKE_AUTHOR::using(&mut (), mutator) + } + /// The Author Filter pallet #[pallet::pallet] #[pallet::without_storage_info] @@ -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 Hooks> for Pallet { + fn on_initialize(_now: BlockNumberFor) -> 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 @@ -127,6 +148,11 @@ pub mod pallet { impl CanAuthor for Pallet { #[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::PotentialAuthors::get(), slot); diff --git a/pallets/author-slot-filter/src/tests.rs b/pallets/author-slot-filter/src/tests.rs index 8b8f02d..4f7827c 100644 --- a/pallets/author-slot-filter/src/tests.rs +++ b/pallets/author-slot-filter/src/tests.rs @@ -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( @@ -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; @@ -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 = @@ -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)); @@ -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 ::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)); + }); + }); +}