forked from Moonsong-Labs/moonkit
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
de13eac
commit eabbfef
Showing
8 changed files
with
979 additions
and
0 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
[package] | ||
name = "pallet-relay-storage-roots" | ||
authors = { workspace = true } | ||
description = "Stores the last N relay storage roots" | ||
edition = "2021" | ||
version = "0.1.0" | ||
|
||
[dependencies] | ||
cumulus-primitives-core = { workspace = true } | ||
frame-support = { workspace = true } | ||
frame-system = { workspace = true } | ||
environmental = { workspace = true } | ||
log = { workspace = true } | ||
nimbus-primitives = { workspace = true } | ||
parity-scale-codec = { workspace = true, features = [ "derive" ] } | ||
scale-info = { workspace = true, features = [ "derive" ] } | ||
serde = { workspace = true, optional = true } | ||
sp-core = { workspace = true } | ||
sp-io = { workspace = true } | ||
sp-runtime = { workspace = true } | ||
sp-std = { workspace = true } | ||
|
||
# Benchmarks | ||
frame-benchmarking = { workspace = true, optional = true } | ||
hex = { workspace = true } | ||
|
||
|
||
[dev-dependencies] | ||
derive_more = { workspace = true } | ||
pallet-author-mapping = { workspace = true, features = [ "std" ] } | ||
pallet-balances = { workspace = true, features = [ "std", "insecure_zero_ed" ] } | ||
|
||
[features] | ||
default = [ "std" ] | ||
std = [ | ||
"cumulus-primitives-core/std", | ||
"environmental/std", | ||
"frame-benchmarking/std", | ||
"frame-support/std", | ||
"frame-system/std", | ||
"hex/std", | ||
"nimbus-primitives/std", | ||
"parity-scale-codec/std", | ||
"scale-info/std", | ||
"serde", | ||
"sp-core/std", | ||
"sp-io/std", | ||
"sp-runtime/std", | ||
"sp-std/std", | ||
] | ||
runtime-benchmarks = [ | ||
"frame-benchmarking", | ||
] | ||
try-runtime = [ "frame-support/try-runtime" ] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
// Copyright Moonsong Labs | ||
// This file is part of Moonkit. | ||
|
||
// Moonkit is free software: you can redistribute it and/or modify | ||
// it under the terms of the GNU General Public License as published by | ||
// the Free Software Foundation, either version 3 of the License, or | ||
// (at your option) any later version. | ||
|
||
// Moonkit is distributed in the hope that it will be useful, | ||
// but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
// GNU General Public License for more details. | ||
|
||
// You should have received a copy of the GNU General Public License | ||
// along with Moonkit. If not, see <http://www.gnu.org/licenses/>. | ||
|
||
#![cfg(feature = "runtime-benchmarks")] | ||
|
||
//! Benchmarking | ||
use crate::{ | ||
Call, Config, InherentIncluded, Pallet, PersistedValidationData, RelayStorageRoot, | ||
RelayStorageRootKeys, | ||
}; | ||
use frame_benchmarking::{benchmarks, impl_benchmark_test_suite}; | ||
use frame_support::{assert_ok, traits::Get}; | ||
use frame_system::RawOrigin; | ||
use sp_core::H256; | ||
|
||
fn fill_relay_storage_roots<T: Config>() { | ||
for i in 0..T::MaxStorageRoots::get() { | ||
let relay_parent_number = i; | ||
let relay_parent_storage_root = H256::default(); | ||
let validation_data: PersistedValidationData = PersistedValidationData { | ||
relay_parent_number, | ||
relay_parent_storage_root, | ||
..Default::default() | ||
}; | ||
|
||
frame_support::storage::unhashed::put(b"MOCK_PERSISTED_VALIDATION_DATA", &validation_data); | ||
assert_ok!(Pallet::<T>::set_relay_storage_root(RawOrigin::None.into())); | ||
} | ||
|
||
assert!( | ||
u32::try_from(RelayStorageRootKeys::<T>::get().len()).unwrap() >= T::MaxStorageRoots::get() | ||
); | ||
} | ||
|
||
benchmarks! { | ||
// Benchmark for inherent included in every block | ||
set_relay_storage_root { | ||
// Worst case is when `RelayStorageRoot` has len of `MaxStorageRoots` | ||
fill_relay_storage_roots::<T>(); | ||
|
||
let relay_parent_number = 1000; | ||
let relay_parent_storage_root = H256::default(); | ||
let validation_data: PersistedValidationData = PersistedValidationData { | ||
relay_parent_number, | ||
relay_parent_storage_root, | ||
..Default::default() | ||
}; | ||
frame_support::storage::unhashed::put(b"MOCK_PERSISTED_VALIDATION_DATA", &validation_data); | ||
}: _(RawOrigin::None) | ||
verify { | ||
// verify randomness result | ||
assert_eq!( | ||
RelayStorageRoot::<T>::get( | ||
relay_parent_number | ||
), | ||
Some(relay_parent_storage_root) | ||
); | ||
assert!(InherentIncluded::<T>::get().is_some()); | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use crate::mock::Test; | ||
use sp_io::TestExternalities; | ||
use sp_runtime::BuildStorage; | ||
|
||
pub fn new_test_ext() -> TestExternalities { | ||
let t = frame_system::GenesisConfig::<Test>::default() | ||
.build_storage() | ||
.unwrap(); | ||
TestExternalities::new(t) | ||
} | ||
} | ||
|
||
impl_benchmark_test_suite!( | ||
Pallet, | ||
crate::benchmarks::tests::new_test_ext(), | ||
crate::mock::Test | ||
); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,191 @@ | ||
// Copyright Moonsong Labs | ||
// This file is part of Moonkit. | ||
|
||
// Moonkit is free software: you can redistribute it and/or modify | ||
// it under the terms of the GNU General Public License as published by | ||
// the Free Software Foundation, either version 3 of the License, or | ||
// (at your option) any later version. | ||
|
||
// Moonkit is distributed in the hope that it will be useful, | ||
// but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
// GNU General Public License for more details. | ||
|
||
// You should have received a copy of the GNU General Public License | ||
// along with Moonkit. If not, see <http://www.gnu.org/licenses/>. | ||
|
||
#![cfg_attr(not(feature = "std"), no_std)] | ||
|
||
pub use crate::weights::WeightInfo; | ||
use cumulus_primitives_core::relay_chain::BlockNumber as RelayBlockNumber; | ||
use cumulus_primitives_core::PersistedValidationData; | ||
use frame_support::inherent::IsFatalError; | ||
use frame_support::pallet; | ||
use frame_support::pallet_prelude::*; | ||
use frame_system::pallet_prelude::*; | ||
pub use pallet::*; | ||
use sp_core::Get; | ||
use sp_core::H256; | ||
use sp_runtime::RuntimeString; | ||
use sp_std::collections::vec_deque::VecDeque; | ||
|
||
#[cfg(any(test, feature = "runtime-benchmarks"))] | ||
mod benchmarks; | ||
pub mod weights; | ||
|
||
#[cfg(test)] | ||
mod mock; | ||
#[cfg(test)] | ||
mod tests; | ||
|
||
#[pallet] | ||
pub mod pallet { | ||
use super::*; | ||
|
||
/// The InherentIdentifier "relay storage root" | ||
pub const INHERENT_IDENTIFIER: InherentIdentifier = *b"relsroot"; | ||
|
||
#[pallet::pallet] | ||
#[pallet::without_storage_info] | ||
pub struct Pallet<T>(PhantomData<T>); | ||
|
||
/// Configuration trait of this pallet. | ||
#[pallet::config] | ||
pub trait Config: frame_system::Config { | ||
/// Overarching event type | ||
type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>; | ||
type GetPersistedValidationData: Get<PersistedValidationData>; | ||
#[pallet::constant] | ||
type MaxStorageRoots: Get<u32>; | ||
/// Weight info | ||
type WeightInfo: WeightInfo; | ||
} | ||
|
||
#[pallet::error] | ||
pub enum Error<T> { | ||
RequestCounterOverflowed, | ||
} | ||
|
||
#[pallet::event] | ||
#[pallet::generate_deposit(pub(crate) fn deposit_event)] | ||
pub enum Event<T: Config> { | ||
RequestExpirationExecuted { id: u8 }, | ||
} | ||
|
||
#[derive(Encode)] | ||
#[cfg_attr(feature = "std", derive(Debug, Decode))] | ||
pub enum InherentError { | ||
Other(RuntimeString), | ||
} | ||
|
||
impl IsFatalError for InherentError { | ||
fn is_fatal_error(&self) -> bool { | ||
match *self { | ||
InherentError::Other(_) => true, | ||
} | ||
} | ||
} | ||
|
||
impl InherentError { | ||
/// Try to create an instance ouf of the given identifier and data. | ||
#[cfg(feature = "std")] | ||
pub fn try_from(id: &InherentIdentifier, data: &[u8]) -> Option<Self> { | ||
if id == &INHERENT_IDENTIFIER { | ||
<InherentError as parity_scale_codec::Decode>::decode(&mut &data[..]).ok() | ||
} else { | ||
None | ||
} | ||
} | ||
} | ||
|
||
/// Ensures the mandatory inherent was included in the block | ||
#[pallet::storage] | ||
#[pallet::getter(fn inherent_included)] | ||
pub(crate) type InherentIncluded<T: Config> = StorageValue<_, ()>; | ||
|
||
/// Map of relay block number to relay storage root | ||
#[pallet::storage] | ||
pub type RelayStorageRoot<T: Config> = | ||
StorageMap<_, Twox64Concat, RelayBlockNumber, H256, OptionQuery>; | ||
|
||
/// List of all the keys in `RelayStorageRoot`. | ||
/// Used to remove the oldest key without having to iterate over all of them. | ||
#[pallet::storage] | ||
pub type RelayStorageRootKeys<T: Config> = | ||
StorageValue<_, VecDeque<RelayBlockNumber>, ValueQuery>; | ||
|
||
#[pallet::call] | ||
impl<T: Config> Pallet<T> { | ||
/// Populates `RelayStorageRoot` using this block's `PersistedValidationData`. | ||
#[pallet::call_index(0)] | ||
#[pallet::weight(( | ||
T::WeightInfo::set_relay_storage_root(), | ||
DispatchClass::Mandatory | ||
))] | ||
pub fn set_relay_storage_root(origin: OriginFor<T>) -> DispatchResultWithPostInfo { | ||
ensure_none(origin)?; | ||
|
||
let validation_data = T::GetPersistedValidationData::get(); | ||
|
||
if <RelayStorageRoot<T>>::contains_key(validation_data.relay_parent_number) { | ||
<InherentIncluded<T>>::put(()); | ||
return Ok(Pays::No.into()); | ||
} | ||
|
||
<RelayStorageRoot<T>>::insert( | ||
validation_data.relay_parent_number, | ||
validation_data.relay_parent_storage_root, | ||
); | ||
|
||
let mut keys = <RelayStorageRootKeys<T>>::get(); | ||
keys.push_back(validation_data.relay_parent_number); | ||
// Delete the oldest stored root if the total number is greater than MaxStorageRoots | ||
if u32::try_from(keys.len()).unwrap() > T::MaxStorageRoots::get() { | ||
let first_key = keys.pop_front().unwrap(); | ||
<RelayStorageRoot<T>>::remove(first_key); | ||
} | ||
|
||
<RelayStorageRootKeys<T>>::put(keys); | ||
<InherentIncluded<T>>::put(()); | ||
Ok(Pays::No.into()) | ||
} | ||
} | ||
|
||
#[pallet::inherent] | ||
impl<T: Config> ProvideInherent for Pallet<T> { | ||
type Call = Call<T>; | ||
type Error = InherentError; | ||
const INHERENT_IDENTIFIER: InherentIdentifier = INHERENT_IDENTIFIER; | ||
|
||
fn is_inherent_required(_: &InherentData) -> Result<Option<Self::Error>, Self::Error> { | ||
// Return Ok(Some(_)) unconditionally because this inherent is required in every block | ||
Ok(Some(InherentError::Other( | ||
sp_runtime::RuntimeString::Borrowed("Inherent required to set relay storage roots"), | ||
))) | ||
} | ||
|
||
// The empty-payload inherent extrinsic. | ||
fn create_inherent(_data: &InherentData) -> Option<Self::Call> { | ||
Some(Call::set_relay_storage_root {}) | ||
} | ||
|
||
fn is_inherent(call: &Self::Call) -> bool { | ||
matches!(call, Call::set_relay_storage_root { .. }) | ||
} | ||
} | ||
|
||
#[pallet::hooks] | ||
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> { | ||
fn on_initialize(_now: BlockNumberFor<T>) -> Weight { | ||
// 1 read and 1 write in on_finalize | ||
T::DbWeight::get().reads_writes(1, 1) | ||
} | ||
fn on_finalize(_now: BlockNumberFor<T>) { | ||
// Ensure the mandatory inherent was included in the block or the block is invalid | ||
assert!( | ||
<InherentIncluded<T>>::take().is_some(), | ||
"Mandatory pallet_relay_storage_roots inherent not included; InherentIncluded storage item is empty" | ||
); | ||
} | ||
} | ||
} |
Oops, something went wrong.