#![deny(missing_docs)]
use crate::{impl_for_tuples_attr, weights::Weight};
use subsoil::runtime::traits::AtLeast32BitUnsigned;
use subsoil::weights::WeightMeter;
#[cfg(feature = "try-runtime")]
use alloc::vec::Vec;
#[cfg(feature = "try-runtime")]
use subsoil::runtime::TryRuntimeError;
pub trait PreInherents {
fn pre_inherents() {}
}
impl_for_tuples_attr! {
impl PreInherents for Tuple {
fn pre_inherents() {
for_tuples!( #( Tuple::pre_inherents(); )* );
}
}
}
pub trait PostInherents {
fn post_inherents() {}
}
impl_for_tuples_attr! {
impl PostInherents for Tuple {
fn post_inherents() {
for_tuples!( #( Tuple::post_inherents(); )* );
}
}
}
pub trait PostTransactions {
fn post_transactions() {}
}
impl_for_tuples_attr! {
impl PostTransactions for Tuple {
fn post_transactions() {
for_tuples!( #( Tuple::post_transactions(); )* );
}
}
}
pub trait OnPoll<BlockNumber> {
fn on_poll(_n: BlockNumber, _weight: &mut WeightMeter) {}
}
impl_for_tuples_attr! {
impl<BlockNumber: Clone> OnPoll<BlockNumber> for Tuple {
fn on_poll(n: BlockNumber, weight: &mut WeightMeter) {
for_tuples!( #( Tuple::on_poll(n.clone(), weight); )* );
}
}
}
pub trait OnInitialize<BlockNumber> {
fn on_initialize(_n: BlockNumber) -> Weight {
Weight::zero()
}
}
impl_for_tuples_attr! {
impl<BlockNumber: Clone> OnInitialize<BlockNumber> for Tuple {
fn on_initialize(n: BlockNumber) -> Weight {
let mut weight = Weight::zero();
for_tuples!( #( weight = weight.saturating_add(Tuple::on_initialize(n.clone())); )* );
weight
}
}
}
impl_for_tuples_attr! {
pub trait OnFinalize<BlockNumber> {
fn on_finalize(_n: BlockNumber) {}
}
}
pub trait OnIdle<BlockNumber> {
fn on_idle(_n: BlockNumber, _remaining_weight: Weight) -> Weight {
Weight::zero()
}
}
impl_for_tuples_attr! {
impl<BlockNumber: Copy + AtLeast32BitUnsigned> OnIdle<BlockNumber> for Tuple {
fn on_idle(n: BlockNumber, remaining_weight: Weight) -> Weight {
let on_idle_functions: &[fn(BlockNumber, Weight) -> Weight] =
&[for_tuples!( #( Tuple::on_idle ),* )];
let mut weight = Weight::zero();
let len = on_idle_functions.len();
let start_index = n % (len as u32).into();
let start_index = start_index.try_into().ok().expect(
"`start_index % len` always fits into `usize`, because `len` can be in maximum `usize::MAX`; qed"
);
for on_idle_fn in on_idle_functions.iter().cycle().skip(start_index).take(len) {
let adjusted_remaining_weight = remaining_weight.saturating_sub(weight);
weight = weight.saturating_add(on_idle_fn(n, adjusted_remaining_weight));
}
weight
}
}
}
impl_for_tuples_attr! {
pub trait OnGenesis {
fn on_genesis() {}
}
}
pub trait BeforeAllRuntimeMigrations {
fn before_all_runtime_migrations() -> Weight {
Weight::zero()
}
}
pub trait OnRuntimeUpgrade {
fn on_runtime_upgrade() -> Weight {
Weight::zero()
}
#[cfg(feature = "try-runtime")]
fn try_on_runtime_upgrade(checks: bool) -> Result<Weight, TryRuntimeError> {
let maybe_state = if checks {
let _guard = topsoil_core::StorageNoopGuard::default();
let state = Self::pre_upgrade()?;
Some(state)
} else {
None
};
let weight = Self::on_runtime_upgrade();
if let Some(state) = maybe_state {
let _guard = topsoil_core::StorageNoopGuard::default();
Self::post_upgrade(state)?
}
Ok(weight)
}
#[cfg(feature = "try-runtime")]
fn pre_upgrade() -> Result<Vec<u8>, TryRuntimeError> {
Ok(Vec::new())
}
#[cfg(feature = "try-runtime")]
fn post_upgrade(_state: Vec<u8>) -> Result<(), TryRuntimeError> {
Ok(())
}
}
pub trait UncheckedOnRuntimeUpgrade {
fn on_runtime_upgrade() -> Weight {
Weight::zero()
}
#[cfg(feature = "try-runtime")]
fn pre_upgrade() -> Result<Vec<u8>, TryRuntimeError> {
Ok(Vec::new())
}
#[cfg(feature = "try-runtime")]
fn post_upgrade(_state: Vec<u8>) -> Result<(), TryRuntimeError> {
Ok(())
}
}
impl_for_tuples_attr! {
impl BeforeAllRuntimeMigrations for Tuple {
fn before_all_runtime_migrations() -> Weight {
let mut weight = Weight::zero();
for_tuples!( #( weight = weight.saturating_add(Tuple::before_all_runtime_migrations()); )* );
weight
}
}
}
impl_for_tuples_attr! {
impl OnRuntimeUpgrade for Tuple {
fn on_runtime_upgrade() -> Weight {
let mut weight = Weight::zero();
for_tuples!( #( weight = weight.saturating_add(Tuple::on_runtime_upgrade()); )* );
weight
}
#[cfg(feature = "try-runtime")]
fn try_on_runtime_upgrade(checks: bool) -> Result<Weight, TryRuntimeError> {
let mut cumulative_weight = Weight::zero();
let mut errors = Vec::new();
for_tuples!(#(
match Tuple::try_on_runtime_upgrade(checks) {
Ok(weight) => { cumulative_weight.saturating_accrue(weight); },
Err(err) => { errors.push(err); },
}
)*);
if errors.len() == 1 {
return Err(errors[0])
} else if !errors.is_empty() {
log::error!(
target: "try-runtime",
"Detected multiple errors while executing `try_on_runtime_upgrade`:",
);
errors.iter().for_each(|err| {
log::error!(
target: "try-runtime",
"{:?}",
err
);
});
return Err("Detected multiple errors while executing `try_on_runtime_upgrade`, check the logs!".into())
}
Ok(cumulative_weight)
}
#[cfg(feature = "try-runtime")]
fn pre_upgrade() -> Result<Vec<u8>, TryRuntimeError> {
Err("Usage of `pre_upgrade` with Tuples is not expected. Please use `try_on_runtime_upgrade` instead, which internally calls `pre_upgrade` -> `on_runtime_upgrade` -> `post_upgrade` for each tuple member.".into())
}
#[cfg(feature = "try-runtime")]
fn post_upgrade(_state: Vec<u8>) -> Result<(), TryRuntimeError> {
Err("Usage of `post_upgrade` with Tuples is not expected. Please use `try_on_runtime_upgrade` instead, which internally calls `pre_upgrade` -> `on_runtime_upgrade` -> `post_upgrade` for each tuple member.".into())
}
}
}
impl_for_tuples_attr! {
pub trait IntegrityTest {
fn integrity_test() {}
}
}
#[cfg_attr(doc, aquamarine::aquamarine)]
pub trait Hooks<BlockNumber> {
fn on_initialize(_n: BlockNumber) -> Weight {
Weight::zero()
}
fn on_finalize(_n: BlockNumber) {}
fn on_idle(_n: BlockNumber, _remaining_weight: Weight) -> Weight {
Weight::zero()
}
fn on_poll(_n: BlockNumber, _weight: &mut WeightMeter) {}
fn on_runtime_upgrade() -> Weight {
Weight::zero()
}
#[cfg(feature = "try-runtime")]
fn try_state(_n: BlockNumber) -> Result<(), TryRuntimeError> {
Ok(())
}
#[cfg(feature = "try-runtime")]
fn pre_upgrade() -> Result<Vec<u8>, TryRuntimeError> {
Ok(Vec::new())
}
#[cfg(feature = "try-runtime")]
fn post_upgrade(_state: Vec<u8>) -> Result<(), TryRuntimeError> {
Ok(())
}
fn offchain_worker(_n: BlockNumber) {}
fn integrity_test() {}
}
pub trait BuildGenesisConfig: subsoil::runtime::traits::MaybeSerializeDeserialize {
fn build(&self);
}
impl BuildGenesisConfig for () {
fn build(&self) {}
}
#[deprecated(
note = "GenesisBuild is planned to be removed in December 2023. Use BuildGenesisConfig instead of it."
)]
pub trait GenesisBuild<T, I = ()>: subsoil::runtime::traits::MaybeSerializeDeserialize {
fn build(&self);
#[cfg(feature = "std")]
fn build_storage(&self) -> Result<subsoil::runtime::Storage, String> {
let mut storage = Default::default();
self.assimilate_storage(&mut storage)?;
Ok(storage)
}
#[cfg(feature = "std")]
fn assimilate_storage(&self, storage: &mut subsoil::runtime::Storage) -> Result<(), String> {
subsoil::state_machine::BasicExternalities::execute_with_storage(storage, || {
self.build();
Ok(())
})
}
}
impl_for_tuples_attr! {
pub trait OnTimestampSet<Moment> {
fn on_timestamp_set(moment: Moment);
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::parameter_types;
use alloc::vec::Vec;
use subsoil::io::TestExternalities;
#[cfg(feature = "try-runtime")]
#[test]
fn on_runtime_upgrade_pre_post_executed_tuple() {
crate::parameter_types! {
pub static Pre: Vec<&'static str> = Default::default();
pub static Post: Vec<&'static str> = Default::default();
}
macro_rules! impl_test_type {
($name:ident) => {
struct $name;
impl OnRuntimeUpgrade for $name {
fn on_runtime_upgrade() -> Weight {
Default::default()
}
#[cfg(feature = "try-runtime")]
fn pre_upgrade() -> Result<Vec<u8>, TryRuntimeError> {
Pre::mutate(|s| s.push(stringify!($name)));
Ok(Vec::new())
}
#[cfg(feature = "try-runtime")]
fn post_upgrade(_: Vec<u8>) -> Result<(), TryRuntimeError> {
Post::mutate(|s| s.push(stringify!($name)));
Ok(())
}
}
};
}
impl_test_type!(Foo);
impl_test_type!(Bar);
impl_test_type!(Baz);
TestExternalities::default().execute_with(|| {
Foo::try_on_runtime_upgrade(true).unwrap();
assert_eq!(Pre::take(), vec!["Foo"]);
assert_eq!(Post::take(), vec!["Foo"]);
<(Foo, Bar, Baz)>::try_on_runtime_upgrade(true).unwrap();
assert_eq!(Pre::take(), vec!["Foo", "Bar", "Baz"]);
assert_eq!(Post::take(), vec!["Foo", "Bar", "Baz"]);
<((Foo, Bar), Baz)>::try_on_runtime_upgrade(true).unwrap();
assert_eq!(Pre::take(), vec!["Foo", "Bar", "Baz"]);
assert_eq!(Post::take(), vec!["Foo", "Bar", "Baz"]);
<(Foo, (Bar, Baz))>::try_on_runtime_upgrade(true).unwrap();
assert_eq!(Pre::take(), vec!["Foo", "Bar", "Baz"]);
assert_eq!(Post::take(), vec!["Foo", "Bar", "Baz"]);
assert!(<(Foo, (Bar, Baz))>::pre_upgrade().is_err());
assert!(<(Foo, (Bar, Baz))>::post_upgrade(vec![]).is_err());
});
}
#[test]
fn on_initialize_and_on_runtime_upgrade_weight_merge_works() {
struct Test;
impl OnInitialize<u8> for Test {
fn on_initialize(_n: u8) -> Weight {
Weight::from_parts(10, 0)
}
}
impl OnRuntimeUpgrade for Test {
fn on_runtime_upgrade() -> Weight {
Weight::from_parts(20, 0)
}
}
TestExternalities::default().execute_with(|| {
assert_eq!(<(Test, Test)>::on_initialize(0), Weight::from_parts(20, 0));
assert_eq!(<(Test, Test)>::on_runtime_upgrade(), Weight::from_parts(40, 0));
});
}
#[test]
fn on_idle_round_robin_works() {
parameter_types! {
static OnIdleInvocationOrder: Vec<&'static str> = Vec::new();
}
struct Test1;
struct Test2;
struct Test3;
type TestTuple = (Test1, Test2, Test3);
impl OnIdle<u32> for Test1 {
fn on_idle(_n: u32, _weight: Weight) -> Weight {
OnIdleInvocationOrder::mutate(|o| o.push("Test1"));
Weight::zero()
}
}
impl OnIdle<u32> for Test2 {
fn on_idle(_n: u32, _weight: Weight) -> Weight {
OnIdleInvocationOrder::mutate(|o| o.push("Test2"));
Weight::zero()
}
}
impl OnIdle<u32> for Test3 {
fn on_idle(_n: u32, _weight: Weight) -> Weight {
OnIdleInvocationOrder::mutate(|o| o.push("Test3"));
Weight::zero()
}
}
TestTuple::on_idle(0, Weight::zero());
assert_eq!(OnIdleInvocationOrder::get(), ["Test1", "Test2", "Test3"].to_vec());
OnIdleInvocationOrder::mutate(|o| o.clear());
TestTuple::on_idle(1, Weight::zero());
assert_eq!(OnIdleInvocationOrder::get(), ["Test2", "Test3", "Test1"].to_vec());
OnIdleInvocationOrder::mutate(|o| o.clear());
TestTuple::on_idle(2, Weight::zero());
assert_eq!(OnIdleInvocationOrder::get(), ["Test3", "Test1", "Test2"].to_vec());
OnIdleInvocationOrder::mutate(|o| o.clear());
TestTuple::on_idle(3, Weight::zero());
assert_eq!(OnIdleInvocationOrder::get(), ["Test1", "Test2", "Test3"].to_vec());
OnIdleInvocationOrder::mutate(|o| o.clear());
TestTuple::on_idle(4, Weight::zero());
assert_eq!(OnIdleInvocationOrder::get(), ["Test2", "Test3", "Test1"].to_vec());
OnIdleInvocationOrder::mutate(|o| o.clear());
}
}