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
11 changes: 11 additions & 0 deletions Cargo.lock

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

27 changes: 27 additions & 0 deletions crates/pallet-fixed-validators-set/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
[package]
name = "pallet-fixed-validators-set"
version = "0.1.0"
edition = "2021"
publish = false

[dependencies]
codec = { workspace = true, features = ["derive"] }
frame-benchmarking = { workspace = true, optional = true }
frame-support = { workspace = true }
frame-system = { workspace = true }
scale-info = { workspace = true, features = ["derive"] }

[features]
default = ["std"]
runtime-benchmarks = [
"frame-benchmarking/runtime-benchmarks",
"frame-support/runtime-benchmarks",
"frame-system/runtime-benchmarks",
]
std = [
"codec/std",
"frame-support/std",
"frame-system/std",
"scale-info/std",
]
try-runtime = ["frame-support/try-runtime", "frame-system/try-runtime"]
50 changes: 50 additions & 0 deletions crates/pallet-fixed-validators-set/src/benchmarking.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
//! The benchmarks for the pallet.

// Allow integer and float arithmetic in tests.
#![allow(clippy::float_arithmetic)]

use frame_benchmarking::benchmarks;
use frame_support::pallet_prelude::*;
use frame_system::RawOrigin;

use crate::*;

/// The benchmark interface into the environment.
pub trait Interface: super::Config {
/// Provide a fixed amount of validator IDs.
///
/// This is used for benchmarking the set update.
fn provide_validator_id(index: u32) -> <Self as Config>::ValidatorId;
}

benchmarks! {
where_clause {
where
T: Interface,
}

update_set {
use frame_support::traits::TryCollect as _;
let new_set = (0..<T as Config>::MaxValidators::get())
.map(|index| <T as Interface>::provide_validator_id(index))
.try_collect()
.unwrap();

}: _(RawOrigin::Root, new_set.clone())
verify {
assert_eq!(Validators::<T>::get(), new_set);
}

impl_benchmark_test_suite!(
Pallet,
crate::mock::new_test_ext(),
crate::mock::Test,
);
}

#[cfg(test)]
impl Interface for crate::mock::Test {
fn provide_validator_id(index: u32) -> <Self as Config>::ValidatorId {
index.into()
}
}
97 changes: 97 additions & 0 deletions crates/pallet-fixed-validators-set/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
//! A substrate pallet for providing a fixed validators set.

#![cfg_attr(not(feature = "std"), no_std)]

use frame_support::traits::StorageVersion;

pub use self::pallet::*;
pub use self::weights::*;

#[cfg(feature = "runtime-benchmarks")]
pub mod benchmarking;
#[cfg(test)]
mod mock;
#[cfg(test)]
mod tests;
mod weights;

/// The current storage version.
const STORAGE_VERSION: StorageVersion = StorageVersion::new(0);

// We have to temporarily allow some clippy lints. Later on we'll send patches to substrate to
// fix them at their end.
#[allow(clippy::missing_docs_in_private_items)]
#[frame_support::pallet]
pub mod pallet {
use frame_support::{pallet_prelude::*, storage::in_storage_layer};
use frame_system::pallet_prelude::*;

use super::*;

/// Configure the pallet by specifying the parameters and types on which it depends.
#[pallet::config]
pub trait Config: frame_system::Config {
/// Overarching event type.
type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;

/// The maximum number of validators to provide.
type MaxValidators: Get<u32>;

/// The type of the validator in the fixed validator set.
type ValidatorId: Member + Parameter + MaybeSerializeDeserialize + MaxEncodedLen;

/// The weight information provider type.
type WeightInfo: WeightInfo;
}

#[pallet::pallet]
#[pallet::storage_version(STORAGE_VERSION)]
pub struct Pallet<T>(_);

/// A list of the validators.
#[pallet::storage]
#[pallet::getter(fn validators)]
pub type Validators<T: Config> =
StorageValue<_, BoundedVec<T::ValidatorId, T::MaxValidators>, ValueQuery>;

#[pallet::genesis_config]
#[derive(frame_support::DefaultNoBound)]
pub struct GenesisConfig<T: Config> {
/// The set of validators to initialize the chain with.
pub validators: BoundedVec<T::ValidatorId, T::MaxValidators>,
}

// The build of genesis for the pallet.
#[pallet::genesis_build]
impl<T: Config> GenesisBuild<T> for GenesisConfig<T> {
fn build(&self) {
<Validators<T>>::put(&self.validators);
}
}

#[pallet::event]
#[pallet::generate_deposit(pub(super) fn deposit_event)]
pub enum Event<T: Config> {
/// The fixed validators set has been updated.
SetUpdated,
}

#[pallet::call(weight(T::WeightInfo))]
impl<T: Config> Pallet<T> {
/// Update the fixed validators set.
/// New set extirely replaces the previous state.
#[pallet::call_index(0)]
pub fn update_set(
origin: OriginFor<T>,
new_set: BoundedVec<T::ValidatorId, T::MaxValidators>,
) -> DispatchResult {
ensure_root(origin)?;

in_storage_layer(|| {
Validators::<T>::set(new_set);
Self::deposit_event(Event::SetUpdated);
Ok(())
})
}
}
}
78 changes: 78 additions & 0 deletions crates/pallet-fixed-validators-set/src/mock.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
//! The mock for the pallet.

use frame_support::{
sp_io,
sp_runtime::{
testing::{Header, H256},
traits::{BlakeTwo256, IdentityLookup},
BuildStorage as _,
},
traits::{ConstU32, ConstU64},
};

use crate::{self as pallet_fixed_validators_set};

type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic<Test>;
type Block = frame_system::mocking::MockBlock<Test>;

frame_support::construct_runtime!(
pub struct Test where
Block = Block,
NodeBlock = Block,
UncheckedExtrinsic = UncheckedExtrinsic,
{
System: frame_system::{Pallet, Call, Config, Storage, Event<T>},
FixedValidatorSet: pallet_fixed_validators_set::{Pallet, Call, Storage, Config<T>, Event<T>},
}
);

impl frame_system::Config for Test {
type BaseCallFilter = frame_support::traits::Everything;
type BlockWeights = ();
type BlockLength = ();
type DbWeight = ();
type RuntimeOrigin = RuntimeOrigin;
type RuntimeCall = RuntimeCall;
type Index = u64;
type BlockNumber = u64;
type Hash = H256;
type Hashing = BlakeTwo256;
type AccountId = u64;
type Lookup = IdentityLookup<u64>;
type Header = Header;
type RuntimeEvent = RuntimeEvent;
type BlockHashCount = ConstU64<250>;
type Version = ();
type PalletInfo = PalletInfo;
type AccountData = ();
type OnNewAccount = ();
type OnKilledAccount = ();
type SystemWeightInfo = ();
type SS58Prefix = ();
type OnSetCode = ();
type MaxConsumers = ConstU32<16>;
}

impl pallet_fixed_validators_set::Config for Test {
type RuntimeEvent = RuntimeEvent;
type WeightInfo = ();
type MaxValidators = ConstU32<16>;
type ValidatorId = Self::AccountId;
}

pub fn new_test_ext() -> sp_io::TestExternalities {
let genesis_config = GenesisConfig {
system: Default::default(),
fixed_validator_set: FixedValidatorSetConfig {
validators: Default::default(),
},
};
new_test_ext_with(genesis_config)
}

// This function basically just builds a genesis storage key/value store according to
// our desired mockup.
pub fn new_test_ext_with(genesis_config: GenesisConfig) -> sp_io::TestExternalities {
let storage = genesis_config.build_storage().unwrap();
storage.into()
}
64 changes: 64 additions & 0 deletions crates/pallet-fixed-validators-set/src/tests.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
//! The tests for the pallet.

use frame_support::{assert_noop, assert_ok, BoundedVec};

use crate::{
mock::{new_test_ext, FixedValidatorSet, RuntimeOrigin, Test},
*,
};

/// This test verifies that the test setup for the genesis is adequate.
#[test]
fn basic_setup_works() {
new_test_ext().execute_with(|| {
// Check the initial set.
assert_eq!(<Validators<Test>>::get(), vec![]);
});
}

/// This test verifies that updating the set works in the happy path.
#[test]
fn updating_set_works() {
new_test_ext().execute_with(|| {
// Check test preconditions.
assert_eq!(<Validators<Test>>::get(), vec![]);

// Set block number to enable events.
mock::System::set_block_number(1);

// Invoke the function under test.
assert_ok!(FixedValidatorSet::update_set(
RuntimeOrigin::root(),
BoundedVec::try_from(vec![1]).unwrap()
));

// Assert state changes.
assert_eq!(<Validators<Test>>::get(), vec![1]);
mock::System::assert_has_event(mock::RuntimeEvent::FixedValidatorSet(Event::SetUpdated));
});
}

/// This test verifies that updating the set fails with a non-root origin.
#[test]
fn updating_set_requires_root() {
new_test_ext().execute_with(|| {
// Check test preconditions.
assert_eq!(<Validators<Test>>::get(), vec![]);

// Invoke the function under test and assert it fails.
assert_noop!(
FixedValidatorSet::update_set(
RuntimeOrigin::signed(1),
BoundedVec::try_from(vec![1]).unwrap()
),
frame_support::dispatch::DispatchError::BadOrigin
);
assert_noop!(
FixedValidatorSet::update_set(
RuntimeOrigin::none(),
BoundedVec::try_from(vec![1]).unwrap(),
),
frame_support::dispatch::DispatchError::BadOrigin
);
});
}
15 changes: 15 additions & 0 deletions crates/pallet-fixed-validators-set/src/weights.rs

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

4 changes: 4 additions & 0 deletions utils/checks/snapshots/features.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1985,6 +1985,10 @@
- std
- name: pallet-evm-test-vector-support 1.0.0-dev
features: []
- name: pallet-fixed-validators-set 0.1.0
features:
- default
- std
- name: pallet-grandpa 4.0.0-dev
features:
- std
Expand Down