Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Try State Hook for Beefy #3246

Merged
merged 29 commits into from
Mar 28, 2024
Merged
Show file tree
Hide file tree
Changes from 28 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
24e9366
Try State Hook for Beefy
dharjeezy Feb 7, 2024
c95f46d
validator invariants
dharjeezy Feb 8, 2024
b83a16c
Merge branch 'master' into dami/try-state-beefy
dharjeezy Feb 8, 2024
289e941
nit
dharjeezy Feb 11, 2024
33b2343
Merge remote-tracking branch 'origin/dami/try-state-beefy' into dami/…
dharjeezy Feb 11, 2024
6344a45
nit
dharjeezy Feb 11, 2024
04f61ff
nit
dharjeezy Feb 11, 2024
24edffb
nit
dharjeezy Feb 11, 2024
39b6569
Merge branch 'master' into dami/try-state-beefy
dharjeezy Mar 15, 2024
a89655b
Try State Hook for Beefy
dharjeezy Feb 7, 2024
b596237
validator invariants
dharjeezy Feb 8, 2024
0428b78
nit
dharjeezy Feb 11, 2024
bcebf96
nit
dharjeezy Feb 11, 2024
0237710
nit
dharjeezy Feb 11, 2024
eb582e6
nit
dharjeezy Feb 11, 2024
e3f87e9
merge changes
dharjeezy Mar 23, 2024
7866f12
Merge remote-tracking branch 'origin/dami/try-state-beefy' into dami/…
dharjeezy Mar 23, 2024
7e50bfd
Try State Hook for Beefy
dharjeezy Feb 7, 2024
3351279
validator invariants
dharjeezy Feb 8, 2024
8771b32
nit
dharjeezy Feb 11, 2024
4062a37
nit
dharjeezy Feb 11, 2024
70de774
Merge remote-tracking branch 'origin/dami/try-state-beefy' into dami/…
dharjeezy Mar 23, 2024
50688cf
Try State Hook for Beefy
dharjeezy Feb 7, 2024
c446031
validator invariants
dharjeezy Feb 8, 2024
ea4c817
revert
dharjeezy Mar 24, 2024
e7fbc71
nit
dharjeezy Mar 24, 2024
c1ba286
Merge branch 'master' into dami/try-state-beefy
dharjeezy Mar 24, 2024
5c49102
use decode_len
dharjeezy Mar 24, 2024
2856dd6
nit
dharjeezy Mar 25, 2024
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 prdoc/pr_3246.prdoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
title: Try State Hook for Beefy.

doc:
- audience: Runtime User
description: |
Invariants for storage items in the beefy pallet. Enforces the following Invariants:
1. `Authorities` should not exceed the `MaxAuthorities` capacity.
2. `NextAuthorities` should not exceed the `MaxAuthorities` capacity.
3. `ValidatorSetId` must be present in `SetIdSession`.
crates:
- name: pallet-beefy
62 changes: 62 additions & 0 deletions substrate/frame/beefy/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,14 @@ pub mod pallet {
}
}

#[pallet::hooks]
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
#[cfg(feature = "try-runtime")]
fn try_state(_n: BlockNumberFor<T>) -> Result<(), sp_runtime::TryRuntimeError> {
Self::do_try_state()
}
}

#[pallet::validate_unsigned]
impl<T: Config> ValidateUnsigned for Pallet<T> {
type Call = Call<T>;
Expand All @@ -294,6 +302,60 @@ pub mod pallet {
}
}

#[cfg(any(feature = "try-runtime", test))]
impl<T: Config> Pallet<T> {
/// Ensure the correctness of the state of this pallet.
///
/// This should be valid before or after each state transition of this pallet.
pub fn do_try_state() -> Result<(), sp_runtime::TryRuntimeError> {
Self::try_state_authorities()?;
Self::try_state_validators()?;

Ok(())
}

/// # Invariants
///
/// * `Authorities` should not exceed the `MaxAuthorities` capacity.
/// * `NextAuthorities` should not exceed the `MaxAuthorities` capacity.
fn try_state_authorities() -> Result<(), sp_runtime::TryRuntimeError> {
if let Some(authorities_len) = <Authorities<T>>::decode_len() {
ensure!(
authorities_len as u32 <= T::MaxAuthorities::get(),
"Shouldn't have authorities than the pallet config allows."
dharjeezy marked this conversation as resolved.
Show resolved Hide resolved
);
} else {
return Err(sp_runtime::TryRuntimeError::Other(
"Failed to decode length of authorities",
));
}

if let Some(next_authorities_len) = <NextAuthorities<T>>::decode_len() {
ensure!(
next_authorities_len as u32 <= T::MaxAuthorities::get(),
"Shouldn't have next authorities than the pallet config allows."
dharjeezy marked this conversation as resolved.
Show resolved Hide resolved
);
} else {
return Err(sp_runtime::TryRuntimeError::Other(
"Failed to decode length of authorities",
dharjeezy marked this conversation as resolved.
Show resolved Hide resolved
));
}
Ok(())
}

/// # Invariants
///
/// `ValidatorSetId` must be present in `SetIdSession`
fn try_state_validators() -> Result<(), sp_runtime::TryRuntimeError> {
let validator_set_id = <ValidatorSetId<T>>::get();
ensure!(
SetIdSession::<T>::get(validator_set_id).is_some(),
"Validator set id must be present in SetIdSession"
);
Ok(())
}
}

impl<T: Config> Pallet<T> {
/// Return the current active BEEFY validator set.
pub fn validator_set() -> Option<ValidatorSet<T::BeefyId>> {
Expand Down
116 changes: 67 additions & 49 deletions substrate/frame/beefy/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ use frame_support::{
};
use pallet_session::historical as pallet_session_historical;
use sp_core::{crypto::KeyTypeId, ConstU128};
use sp_io::TestExternalities;
use sp_runtime::{
app_crypto::ecdsa::Public, curve::PiecewiseLinear, impl_opaque_keys, testing::TestXt,
traits::OpaqueKeys, BuildStorage, Perbill,
Expand Down Expand Up @@ -210,6 +209,73 @@ impl pallet_offences::Config for Test {
type OnOffenceHandler = Staking;
}

#[derive(Default)]
pub struct ExtBuilder {
authorities: Vec<BeefyId>,
}

impl ExtBuilder {
/// Add some AccountIds to insert into `List`.
#[cfg(test)]
pub(crate) fn add_authorities(mut self, ids: Vec<BeefyId>) -> Self {
self.authorities = ids;
self
}

pub fn build(self) -> sp_io::TestExternalities {
let mut t = frame_system::GenesisConfig::<Test>::default().build_storage().unwrap();

let balances: Vec<_> =
(0..self.authorities.len()).map(|i| (i as u64, 10_000_000)).collect();

pallet_balances::GenesisConfig::<Test> { balances }
.assimilate_storage(&mut t)
.unwrap();

let session_keys: Vec<_> = self
.authorities
.iter()
.enumerate()
.map(|(i, k)| (i as u64, i as u64, MockSessionKeys { dummy: k.clone() }))
.collect();

BasicExternalities::execute_with_storage(&mut t, || {
for (ref id, ..) in &session_keys {
frame_system::Pallet::<Test>::inc_providers(id);
}
});

pallet_session::GenesisConfig::<Test> { keys: session_keys }
.assimilate_storage(&mut t)
.unwrap();

// controllers are same as stash
let stakers: Vec<_> = (0..self.authorities.len())
.map(|i| (i as u64, i as u64, 10_000, pallet_staking::StakerStatus::<u64>::Validator))
.collect();

let staking_config = pallet_staking::GenesisConfig::<Test> {
stakers,
validator_count: 2,
force_era: pallet_staking::Forcing::ForceNew,
minimum_validator_count: 0,
invulnerables: vec![],
..Default::default()
};

staking_config.assimilate_storage(&mut t).unwrap();

t.into()
}

pub fn build_and_execute(self, test: impl FnOnce() -> ()) {
self.build().execute_with(|| {
test();
Beefy::do_try_state().expect("All invariants must hold after a test");
})
}
}

// Note, that we can't use `UintAuthorityId` here. Reason is that the implementation
// of `to_public_key()` assumes, that a public key is 32 bytes long. This is true for
// ed25519 and sr25519 but *not* for ecdsa. A compressed ecdsa public key is 33 bytes,
Expand All @@ -226,54 +292,6 @@ pub fn mock_authorities(vec: Vec<u8>) -> Vec<BeefyId> {
vec.into_iter().map(|id| mock_beefy_id(id)).collect()
}

pub fn new_test_ext(ids: Vec<u8>) -> TestExternalities {
new_test_ext_raw_authorities(mock_authorities(ids))
}

pub fn new_test_ext_raw_authorities(authorities: Vec<BeefyId>) -> TestExternalities {
let mut t = frame_system::GenesisConfig::<Test>::default().build_storage().unwrap();

let balances: Vec<_> = (0..authorities.len()).map(|i| (i as u64, 10_000_000)).collect();

pallet_balances::GenesisConfig::<Test> { balances }
.assimilate_storage(&mut t)
.unwrap();

let session_keys: Vec<_> = authorities
.iter()
.enumerate()
.map(|(i, k)| (i as u64, i as u64, MockSessionKeys { dummy: k.clone() }))
.collect();

BasicExternalities::execute_with_storage(&mut t, || {
for (ref id, ..) in &session_keys {
frame_system::Pallet::<Test>::inc_providers(id);
}
});

pallet_session::GenesisConfig::<Test> { keys: session_keys }
.assimilate_storage(&mut t)
.unwrap();

// controllers are same as stash
let stakers: Vec<_> = (0..authorities.len())
.map(|i| (i as u64, i as u64, 10_000, pallet_staking::StakerStatus::<u64>::Validator))
.collect();

let staking_config = pallet_staking::GenesisConfig::<Test> {
stakers,
validator_count: 2,
force_era: pallet_staking::Forcing::ForceNew,
minimum_validator_count: 0,
invulnerables: vec![],
..Default::default()
};

staking_config.assimilate_storage(&mut t).unwrap();

t.into()
}

pub fn start_session(session_index: SessionIndex) {
for i in Session::current_index()..session_index {
System::on_finalize(System::block_number());
Expand Down
Loading
Loading