From 7d2b10ebf2fbfc2ffe49f5771414fa31c986e310 Mon Sep 17 00:00:00 2001 From: zjb0807 <zjb0807@qq.com> Date: Thu, 5 Mar 2020 15:48:39 +0800 Subject: [PATCH] Feature/schedule update (#108) * add schedule-update * add unit test * fix schedule-update/Cargo.toml * add try_for_each * add next_block_number --- Cargo.dev.toml | 3 +- auction/Cargo.toml | 3 + schedule-update/Cargo.toml | 31 ++++ schedule-update/src/lib.rs | 224 +++++++++++++++++++++++ schedule-update/src/mock.rs | 125 +++++++++++++ schedule-update/src/tests.rs | 344 +++++++++++++++++++++++++++++++++++ 6 files changed, 729 insertions(+), 1 deletion(-) create mode 100644 schedule-update/Cargo.toml create mode 100644 schedule-update/src/lib.rs create mode 100644 schedule-update/src/mock.rs create mode 100644 schedule-update/src/tests.rs diff --git a/Cargo.dev.toml b/Cargo.dev.toml index 39e5d76..e21c9c9 100644 --- a/Cargo.dev.toml +++ b/Cargo.dev.toml @@ -9,4 +9,5 @@ members = [ "auction", "vesting", "gradually-update", -] + "schedule-update", +] \ No newline at end of file diff --git a/auction/Cargo.toml b/auction/Cargo.toml index cfb695a..5b4bd1c 100644 --- a/auction/Cargo.toml +++ b/auction/Cargo.toml @@ -23,6 +23,9 @@ primitives = { package = "sp-core", version = "2.0.0-alpha.3", default-features clear_on_drop = { version = "0.2.3", features = ["no_cc"] } # https://github.com/paritytech/substrate/issues/4179 quote = "=1.0.2" # https://github.com/rust-lang-nursery/failure/issues/342 +# CI failed. Caused by issue: https://github.com/withoutboats/failure_derive/issues/13 +quote = "=1.0.2" + [features] default = ["std"] std = [ diff --git a/schedule-update/Cargo.toml b/schedule-update/Cargo.toml new file mode 100644 index 0000000..b52227f --- /dev/null +++ b/schedule-update/Cargo.toml @@ -0,0 +1,31 @@ +[package] +name = "orml-schedule-update" +version = "0.0.1" +authors = ["Laminar Developers <hello@laminar.one>"] +edition = "2018" + + +[dependencies] +codec = { package = "parity-scale-codec", version = "1.0.0", default-features = false } + +frame-support = { git = "https://github.com/paritytech/substrate.git", default-features = false } +frame-system = { git = "https://github.com/paritytech/substrate.git", default-features = false } +sp-std = { git = "https://github.com/paritytech/substrate.git", default-features = false } +sp-runtime = { git = "https://github.com/paritytech/substrate.git", default-features = false } + +[dev-dependencies] +sp-io = { git = "https://github.com/paritytech/substrate.git", default-features = false } +sp-core = { git = "https://github.com/paritytech/substrate.git", default-features = false } +pallet-balances = { git = "https://github.com/paritytech/substrate.git", default-features = false } + + +[features] +default = ["std"] +std = [ + "codec/std", + "frame-support/std", + "frame-system/std", + "sp-std/std", + "sp-runtime/std", + "pallet-balances/std", +] diff --git a/schedule-update/src/lib.rs b/schedule-update/src/lib.rs new file mode 100644 index 0000000..606ed0c --- /dev/null +++ b/schedule-update/src/lib.rs @@ -0,0 +1,224 @@ +#![cfg_attr(not(feature = "std"), no_std)] + +use codec::{Decode, Encode}; +use frame_support::{ + decl_error, decl_event, decl_module, decl_storage, + dispatch::Weight, + ensure, + traits::Get, + weights::{DispatchClass, GetDispatchInfo}, + Parameter, +}; +use frame_system::{self as system, ensure_root, ensure_signed}; +use sp_runtime::{ + traits::{CheckedAdd, Dispatchable, One}, + DispatchError, RuntimeDebug, +}; +use sp_std::{prelude::*, result}; + +mod mock; +mod tests; + +#[derive(Encode, Decode, Copy, Clone, PartialEq, Eq, RuntimeDebug)] +pub enum DelayedDispatchTime<BlockNumber> { + At(BlockNumber), + After(BlockNumber), +} + +type DispatchId = u32; +type CallOf<T> = <T as Trait>::Call; + +pub trait Trait: frame_system::Trait { + type Event: From<Event<Self>> + Into<<Self as frame_system::Trait>::Event>; + type Call: Parameter + Default + Dispatchable<Origin = <Self as frame_system::Trait>::Origin> + GetDispatchInfo; + type MaxScheduleDispatchWeight: Get<Weight>; +} + +decl_event!( + /// Event for schedule-update module. + pub enum Event<T> where + <T as frame_system::Trait>::BlockNumber, + { + /// Add schedule dispatch success (BlockNumber, DispatchId) + ScheduleDispatch(BlockNumber, DispatchId), + /// Cancel deplayed dispatch success (DispatchId) + CancelDeplayedDispatch(DispatchId), + /// Schedule dispatch success (BlockNumber, DispatchId) + ScheduleDispatchSuccess(BlockNumber, DispatchId), + /// Schedule dispatch failed (DispatchId, DispatchError) + ScheduleDispatchFail(DispatchId, DispatchError), + } +); + +decl_error! { + /// Error for schedule-update module. + pub enum Error for Module<T: Trait> { + BadOrigin, + InvalidDelayedDispatchTime, + CannotGetNextId, + NoPermission, + DispatchNotExisted, + BlockNumberOverflow, + ExceedMaxScheduleDispatchWeight, + } +} + +decl_storage! { + trait Store for Module<T: Trait> as ScheduleUpdate { + pub NextId get(fn next_id): DispatchId; + pub DelayedNormalDispatches get(fn delayed_normal_dispatches): double_map hasher(twox_64_concat) T::BlockNumber, hasher(twox_64_concat) DispatchId => (Option<T::AccountId>, CallOf<T>, DispatchId); + pub DelayedOperationalDispatches get(fn delayed_operational_dispatches): double_map hasher(twox_64_concat) T::BlockNumber, hasher(twox_64_concat) DispatchId => (Option<T::AccountId>, CallOf<T>, DispatchId); + } +} + +decl_module! { + pub struct Module<T: Trait> for enum Call where origin: T::Origin { + type Error = Error<T>; + + fn deposit_event() = default; + + const MaxScheduleDispatchWeight: Weight = T::MaxScheduleDispatchWeight::get(); + + /// Add schedule_update at block_number + pub fn schedule_dispatch(origin, call: CallOf<T>, when: DelayedDispatchTime<T::BlockNumber>) { + let who = match origin.into() { + Ok(frame_system::RawOrigin::Root) => None, + Ok(frame_system::RawOrigin::Signed(t)) => Some(t), + _ => return Err(Error::<T>::BadOrigin.into()) + }; + + let now = <frame_system::Module<T>>::block_number(); + let block_number = match when { + DelayedDispatchTime::At(block_number) => { + ensure!(block_number > now, Error::<T>::InvalidDelayedDispatchTime); + block_number + }, + DelayedDispatchTime::After(block_count) => { + now.checked_add(&block_count).ok_or(Error::<T>::BlockNumberOverflow)? + }, + }; + + let id = Self::_get_next_id()?; + + match call.get_dispatch_info().class { + DispatchClass::Normal => { + <DelayedNormalDispatches<T>>::insert(block_number, id, (who, call, id)); + }, + DispatchClass::Operational => { + <DelayedOperationalDispatches<T>>::insert(block_number, id, (who, call, id)); + }, + } + Self::deposit_event(RawEvent::ScheduleDispatch(block_number, id)); + } + + /// Cancel schedule_update + pub fn cancel_deplayed_dispatch(origin, at: T::BlockNumber, id: DispatchId) { + let is_root = ensure_root(origin.clone()).is_ok(); + + if <DelayedNormalDispatches<T>>::contains_key(at, id) { + if !is_root { + let w = ensure_signed(origin)?; + let (who, _, _) = <DelayedNormalDispatches<T>>::get(at, id); + if Some(w) != who { + return Err(Error::<T>::NoPermission.into()); + } + } + <DelayedNormalDispatches<T>>::remove(at, id); + } else if <DelayedOperationalDispatches<T>>::contains_key(at, id) { + if !is_root { + let w = ensure_signed(origin)?; + let (who, _, _) = <DelayedOperationalDispatches<T>>::get(at, id); + if Some(w) != who { + return Err(Error::<T>::NoPermission.into()); + } + } + <DelayedOperationalDispatches<T>>::remove(at, id); + } else { + return Err(Error::<T>::DispatchNotExisted.into()); + } + Self::deposit_event(RawEvent::CancelDeplayedDispatch(id)); + } + + fn on_initialize(now: T::BlockNumber) { + let mut weight: Weight = 0; + let total_weight = T::MaxScheduleDispatchWeight::get(); + let next_block_number = match now.checked_add(&One::one()) { + Some(block_number) => block_number, + _ => return + }; + + // Operational calls are dispatched first and then normal calls + // TODO: dispatches should be sorted + let mut operational_dispatches = <DelayedOperationalDispatches<T>>::iter_prefix(now); + let _ = operational_dispatches.try_for_each(|(who, call, id)| { + weight += call.get_dispatch_info().weight; + if weight > total_weight { + return Err(Error::<T>::ExceedMaxScheduleDispatchWeight); + } + + let origin: T::Origin; + if let Some(w) = who { + origin = frame_system::RawOrigin::Signed(w).into(); + } else { + origin = frame_system::RawOrigin::Root.into(); + } + + let result = call.dispatch(origin.clone()); + if let Err(e) = result { + Self::deposit_event(RawEvent::ScheduleDispatchFail(id, e)); + } else { + Self::deposit_event(RawEvent::ScheduleDispatchSuccess(now, id)); + } + <DelayedOperationalDispatches<T>>::remove(now, id); + Ok(()) + }); + + let mut normal_dispatches = <DelayedNormalDispatches<T>>::iter_prefix(now); + let _ = normal_dispatches.try_for_each(|(who, call, id)| { + weight += call.get_dispatch_info().weight; + if weight > total_weight { + return Err(Error::<T>::ExceedMaxScheduleDispatchWeight); + } + + let origin: T::Origin; + if let Some(w) = who { + origin = frame_system::RawOrigin::Signed(w).into(); + } else { + origin = frame_system::RawOrigin::Root.into(); + } + + let result = call.dispatch(origin.clone()); + if let Err(e) = result { + Self::deposit_event(RawEvent::ScheduleDispatchFail(id, e)); + } else { + Self::deposit_event(RawEvent::ScheduleDispatchSuccess(now, id)); + } + <DelayedNormalDispatches<T>>::remove(now, id); + Ok(()) + }); + + // Check Call dispatch weight and ensure they don't exceed MaxScheduleDispatchWeight + // Extra ones are moved to next block + let operational_dispatches = <DelayedOperationalDispatches<T>>::iter_prefix(now); + operational_dispatches.for_each(|(who, call, id)| { + <DelayedOperationalDispatches<T>>::insert(next_block_number, id, (who, call, id)); + <DelayedOperationalDispatches<T>>::remove(now, id); + }); + + let normal_dispatches = <DelayedNormalDispatches<T>>::iter_prefix(now); + normal_dispatches.for_each(|(who, call, id)| { + <DelayedNormalDispatches<T>>::insert(next_block_number, id, (who, call, id)); + <DelayedNormalDispatches<T>>::remove(now, id); + }); + } + } +} + +impl<T: Trait> Module<T> { + fn _get_next_id() -> result::Result<DispatchId, Error<T>> { + let id = Self::next_id(); + let next_id = id.checked_add(One::one()).ok_or(Error::<T>::CannotGetNextId)?; + NextId::put(next_id); + Ok(id) + } +} diff --git a/schedule-update/src/mock.rs b/schedule-update/src/mock.rs new file mode 100644 index 0000000..81831d6 --- /dev/null +++ b/schedule-update/src/mock.rs @@ -0,0 +1,125 @@ +//! Mocks for the schedule-update module. + +#![cfg(test)] + +use frame_support::{impl_outer_dispatch, impl_outer_event, impl_outer_origin, parameter_types}; +use frame_system as system; +use sp_core::H256; +use sp_runtime::{testing::Header, traits::IdentityLookup, Perbill}; + +use super::*; + +impl_outer_origin! { + pub enum Origin for Runtime {} +} + +mod schedule_update { + pub use crate::Event; +} + +impl_outer_event! { + pub enum TestEvent for Runtime { + frame_system<T>, + schedule_update<T>, + pallet_balances<T>, + } +} + +impl_outer_dispatch! { + pub enum Call for Runtime where origin: Origin { + pallet_balances::Balances, + } +} + +impl Default for Call { + fn default() -> Call { + Default::default() + } +} + +// Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted. +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct Runtime; +parameter_types! { + pub const BlockHashCount: u64 = 250; + pub const MaximumBlockWeight: u32 = 1024; + pub const MaximumBlockLength: u32 = 2 * 1024; + pub const AvailableBlockRatio: Perbill = Perbill::one(); +} + +pub type AccountId = u64; +pub type BlockNumber = u64; + +impl frame_system::Trait for Runtime { + type Origin = Origin; + type Index = u64; + type BlockNumber = BlockNumber; + type Call = Call; + type Hash = H256; + type Hashing = ::sp_runtime::traits::BlakeTwo256; + type AccountId = AccountId; + type Lookup = IdentityLookup<Self::AccountId>; + type Header = Header; + type Event = TestEvent; + type BlockHashCount = BlockHashCount; + type MaximumBlockWeight = MaximumBlockWeight; + type MaximumBlockLength = MaximumBlockLength; + type AvailableBlockRatio = AvailableBlockRatio; + type Version = (); + type ModuleToIndex = (); + type AccountData = pallet_balances::AccountData<u128>; + type OnNewAccount = (); + type OnKilledAccount = (); +} +pub type System = frame_system::Module<Runtime>; + +parameter_types! { + pub const ExistentialDeposit: u64 = 1; +} + +impl pallet_balances::Trait for Runtime { + type Balance = u128; + type DustRemoval = (); + type Event = TestEvent; + type ExistentialDeposit = ExistentialDeposit; + type AccountStore = System; +} + +parameter_types! { + pub const MaxScheduleDispatchWeight: Weight = 2_000_000; +} + +impl Trait for Runtime { + type Event = TestEvent; + type Call = Call; + type MaxScheduleDispatchWeight = MaxScheduleDispatchWeight; +} +pub type ScheduleUpdateModule = Module<Runtime>; + +pub type Balances = pallet_balances::Module<Runtime>; + +pub type BalancesCall = pallet_balances::Call<Runtime>; + +pub struct ExtBuilder; + +impl Default for ExtBuilder { + fn default() -> Self { + ExtBuilder + } +} + +impl ExtBuilder { + pub fn build(self) -> sp_io::TestExternalities { + let mut t = frame_system::GenesisConfig::default() + .build_storage::<Runtime>() + .unwrap(); + + pallet_balances::GenesisConfig::<Runtime> { + balances: vec![(1, 100), (2, 100), (3, 100), (4, 100), (5, 100)], + } + .assimilate_storage(&mut t) + .unwrap(); + + t.into() + } +} diff --git a/schedule-update/src/tests.rs b/schedule-update/src/tests.rs new file mode 100644 index 0000000..0eee9dc --- /dev/null +++ b/schedule-update/src/tests.rs @@ -0,0 +1,344 @@ +//! Unit tests for the gradually-update module. + +#![cfg(test)] + +use super::*; +use frame_support::{assert_noop, assert_ok}; +use mock::{BalancesCall, Call, ExtBuilder, Origin, Runtime, ScheduleUpdateModule, System, TestEvent}; +use sp_runtime::traits::OnInitialize; + +#[test] +fn schedule_dispatch_should_work() { + ExtBuilder::default().build().execute_with(|| { + // NormalDispatches + let call = Call::Balances(BalancesCall::transfer(2, 11)); + assert_ok!(ScheduleUpdateModule::schedule_dispatch( + Origin::signed(1), + call, + DelayedDispatchTime::At(2) + )); + + let schedule_dispatch_event = TestEvent::schedule_update(RawEvent::ScheduleDispatch(2, 0)); + assert!(System::events() + .iter() + .any(|record| record.event == schedule_dispatch_event)); + + // OperationalDispatches + let call = Call::Balances(BalancesCall::set_balance(1, 10, 11)); + assert_ok!(ScheduleUpdateModule::schedule_dispatch( + Origin::ROOT, + call, + DelayedDispatchTime::After(3) + )); + + let schedule_dispatch_event = TestEvent::schedule_update(RawEvent::ScheduleDispatch(4, 1)); + assert!(System::events() + .iter() + .any(|record| record.event == schedule_dispatch_event)); + }); +} + +#[test] +fn schedule_dispatch_should_fail() { + ExtBuilder::default().build().execute_with(|| { + let call = Call::Balances(BalancesCall::transfer(2, 11)); + assert_noop!( + ScheduleUpdateModule::schedule_dispatch(Origin::signed(1), call, DelayedDispatchTime::At(0)), + Error::<Runtime>::InvalidDelayedDispatchTime + ); + }); +} + +#[test] +fn cancel_deplayed_dispatch_should_work() { + ExtBuilder::default().build().execute_with(|| { + // NormalDispatches + let call = Call::Balances(BalancesCall::transfer(2, 11)); + assert_ok!(ScheduleUpdateModule::schedule_dispatch( + Origin::signed(1), + call, + DelayedDispatchTime::At(2) + )); + + let schedule_dispatch_event = TestEvent::schedule_update(RawEvent::ScheduleDispatch(2, 0)); + assert!(System::events() + .iter() + .any(|record| record.event == schedule_dispatch_event)); + + assert_ok!(ScheduleUpdateModule::cancel_deplayed_dispatch(Origin::signed(1), 2, 0)); + + let schedule_dispatch_event = TestEvent::schedule_update(RawEvent::CancelDeplayedDispatch(0)); + assert!(System::events() + .iter() + .any(|record| record.event == schedule_dispatch_event)); + + // root cancel NormalDispatches + let call = Call::Balances(BalancesCall::transfer(2, 12)); + assert_ok!(ScheduleUpdateModule::schedule_dispatch( + Origin::signed(1), + call, + DelayedDispatchTime::After(3) + )); + + let schedule_dispatch_event = TestEvent::schedule_update(RawEvent::ScheduleDispatch(4, 1)); + assert!(System::events() + .iter() + .any(|record| record.event == schedule_dispatch_event)); + + assert_ok!(ScheduleUpdateModule::cancel_deplayed_dispatch(Origin::ROOT, 4, 1)); + + let schedule_dispatch_event = TestEvent::schedule_update(RawEvent::CancelDeplayedDispatch(1)); + assert!(System::events() + .iter() + .any(|record| record.event == schedule_dispatch_event)); + + // OperationalDispatches + let call = Call::Balances(BalancesCall::set_balance(2, 10, 13)); + assert_ok!(ScheduleUpdateModule::schedule_dispatch( + Origin::ROOT, + call, + DelayedDispatchTime::At(5) + )); + + let schedule_dispatch_event = TestEvent::schedule_update(RawEvent::ScheduleDispatch(5, 2)); + assert!(System::events() + .iter() + .any(|record| record.event == schedule_dispatch_event)); + + assert_ok!(ScheduleUpdateModule::cancel_deplayed_dispatch(Origin::ROOT, 5, 2)); + + let schedule_dispatch_event = TestEvent::schedule_update(RawEvent::CancelDeplayedDispatch(2)); + assert!(System::events() + .iter() + .any(|record| record.event == schedule_dispatch_event)); + }); +} + +#[test] +fn cancel_deplayed_dispatch_should_fail() { + ExtBuilder::default().build().execute_with(|| { + assert_noop!( + ScheduleUpdateModule::cancel_deplayed_dispatch(Origin::signed(1), 2, 0), + Error::<Runtime>::DispatchNotExisted + ); + + // NormalDispatches + let call = Call::Balances(BalancesCall::transfer(2, 11)); + assert_ok!(ScheduleUpdateModule::schedule_dispatch( + Origin::signed(1), + call, + DelayedDispatchTime::At(2) + )); + + let schedule_dispatch_event = TestEvent::schedule_update(RawEvent::ScheduleDispatch(2, 0)); + assert!(System::events() + .iter() + .any(|record| record.event == schedule_dispatch_event)); + + assert_noop!( + ScheduleUpdateModule::cancel_deplayed_dispatch(Origin::signed(2), 2, 0), + Error::<Runtime>::NoPermission + ); + + // OperationalDispatches + let call = Call::Balances(BalancesCall::set_balance(2, 10, 13)); + assert_ok!(ScheduleUpdateModule::schedule_dispatch( + Origin::ROOT, + call, + DelayedDispatchTime::At(5) + )); + + let schedule_dispatch_event = TestEvent::schedule_update(RawEvent::ScheduleDispatch(5, 1)); + assert!(System::events() + .iter() + .any(|record| record.event == schedule_dispatch_event)); + + assert_noop!( + ScheduleUpdateModule::cancel_deplayed_dispatch(Origin::signed(2), 5, 1), + Error::<Runtime>::NoPermission + ); + }); +} + +#[test] +fn on_initialize_should_work() { + ExtBuilder::default().build().execute_with(|| { + // NormalDispatches + let call = Call::Balances(BalancesCall::transfer(2, 11)); + assert_ok!(ScheduleUpdateModule::schedule_dispatch( + Origin::signed(1), + call, + DelayedDispatchTime::At(2) + )); + + let call = Call::Balances(BalancesCall::transfer(2, 12)); + assert_ok!(ScheduleUpdateModule::schedule_dispatch( + Origin::signed(1), + call, + DelayedDispatchTime::At(3) + )); + + assert_eq!(System::events().len(), 7); + ScheduleUpdateModule::on_initialize(1); + assert_eq!(System::events().len(), 7); + + ScheduleUpdateModule::on_initialize(2); + println!("{:?}", System::events()); + assert_eq!(System::events().len(), 9); + let schedule_dispatch_event = TestEvent::schedule_update(RawEvent::ScheduleDispatchSuccess(2, 0)); + assert!(System::events() + .iter() + .any(|record| record.event == schedule_dispatch_event)); + + ScheduleUpdateModule::on_initialize(3); + assert_eq!(System::events().len(), 11); + let schedule_dispatch_event = TestEvent::schedule_update(RawEvent::ScheduleDispatchSuccess(3, 1)); + assert!(System::events() + .iter() + .any(|record| record.event == schedule_dispatch_event)); + + // OperationalDispatches + let call = Call::Balances(BalancesCall::set_balance(3, 10, 11)); + assert_ok!(ScheduleUpdateModule::schedule_dispatch( + Origin::ROOT, + call, + DelayedDispatchTime::After(10) + )); + + let schedule_dispatch_event = TestEvent::schedule_update(RawEvent::ScheduleDispatch(11, 2)); + assert!(System::events() + .iter() + .any(|record| record.event == schedule_dispatch_event)); + + let call = Call::Balances(BalancesCall::set_balance(3, 20, 21)); + assert_ok!(ScheduleUpdateModule::schedule_dispatch( + Origin::ROOT, + call, + DelayedDispatchTime::After(12) + )); + + let schedule_dispatch_event = TestEvent::schedule_update(RawEvent::ScheduleDispatch(13, 3)); + assert!(System::events() + .iter() + .any(|record| record.event == schedule_dispatch_event)); + + assert_eq!(System::events().len(), 13); + ScheduleUpdateModule::on_initialize(10); + assert_eq!(System::events().len(), 13); + + ScheduleUpdateModule::on_initialize(11); + println!("{:?}", System::events()); + assert_eq!(System::events().len(), 15); + let schedule_dispatch_event = TestEvent::schedule_update(RawEvent::ScheduleDispatchSuccess(11, 2)); + assert!(System::events() + .iter() + .any(|record| record.event == schedule_dispatch_event)); + + ScheduleUpdateModule::on_initialize(13); + assert_eq!(System::events().len(), 17); + let schedule_dispatch_event = TestEvent::schedule_update(RawEvent::ScheduleDispatchSuccess(13, 3)); + assert!(System::events() + .iter() + .any(|record| record.event == schedule_dispatch_event)); + }); +} + +#[test] +fn on_initialize_should_fail() { + ExtBuilder::default().build().execute_with(|| { + // NormalDispatches balance not enough + let call = Call::Balances(BalancesCall::transfer(2, 110)); + assert_ok!(ScheduleUpdateModule::schedule_dispatch( + Origin::signed(1), + call, + DelayedDispatchTime::At(2) + )); + + assert_eq!(System::events().len(), 6); + ScheduleUpdateModule::on_initialize(1); + assert_eq!(System::events().len(), 6); + + ScheduleUpdateModule::on_initialize(2); + println!("{:?}", System::events()); + assert_eq!(System::events().len(), 7); + //TODO hold the error + let schedule_dispatch_event = TestEvent::schedule_update(RawEvent::ScheduleDispatchFail( + 0, + DispatchError::Module { + index: 0, + error: 3, + message: None, + }, + )); + assert!(System::events() + .iter() + .any(|record| record.event == schedule_dispatch_event)); + + // OperationalDispatches not root + let call = Call::Balances(BalancesCall::set_balance(3, 10, 11)); + assert_ok!(ScheduleUpdateModule::schedule_dispatch( + Origin::signed(1), + call, + DelayedDispatchTime::After(10) + )); + + assert_eq!(System::events().len(), 8); + ScheduleUpdateModule::on_initialize(10); + assert_eq!(System::events().len(), 8); + + ScheduleUpdateModule::on_initialize(11); + println!("{:?}", System::events()); + assert_eq!(System::events().len(), 9); + let schedule_dispatch_event = + TestEvent::schedule_update(RawEvent::ScheduleDispatchFail(1, DispatchError::BadOrigin)); + assert!(System::events() + .iter() + .any(|record| record.event == schedule_dispatch_event)); + }); +} + +#[test] +fn on_initialize_weight_exceed() { + ExtBuilder::default().build().execute_with(|| { + // NormalDispatches + let call = Call::Balances(BalancesCall::transfer(2, 11)); + assert_ok!(ScheduleUpdateModule::schedule_dispatch( + Origin::signed(1), + call, + DelayedDispatchTime::At(2) + )); + + let call = Call::Balances(BalancesCall::transfer(2, 12)); + assert_ok!(ScheduleUpdateModule::schedule_dispatch( + Origin::signed(1), + call, + DelayedDispatchTime::At(2) + )); + + let call = Call::Balances(BalancesCall::transfer(2, 13)); + assert_ok!(ScheduleUpdateModule::schedule_dispatch( + Origin::signed(1), + call, + DelayedDispatchTime::At(2) + )); + + assert_eq!(System::events().len(), 8); + ScheduleUpdateModule::on_initialize(1); + assert_eq!(System::events().len(), 8); + + ScheduleUpdateModule::on_initialize(2); + println!("{:?}", System::events()); + assert_eq!(System::events().len(), 12); + // TODO on_initialize should be sorted + //let schedule_dispatch_event = TestEvent::schedule_update(RawEvent::ScheduleDispatchSuccess(0, 2)); + //assert!(System::events().iter().any(|record| record.event == schedule_dispatch_event)); + + //let schedule_dispatch_event = TestEvent::schedule_update(RawEvent::ScheduleDispatchSuccess(2, 2)); + //assert!(System::events().iter().any(|record| record.event == schedule_dispatch_event)); + + ScheduleUpdateModule::on_initialize(3); + assert_eq!(System::events().len(), 14); + //let schedule_dispatch_event = TestEvent::schedule_update(RawEvent::ScheduleDispatchSuccess(1, 3)); + //assert!(System::events().iter().any(|record| record.event == schedule_dispatch_event)); + }); +} -- GitLab