Skip to content
Snippets Groups Projects
Unverified Commit 7d2b10eb authored by zjb0807's avatar zjb0807 Committed by GitHub
Browse files

Feature/schedule update (#108)

* add schedule-update

* add unit test

* fix schedule-update/Cargo.toml

* add try_for_each

* add next_block_number
parent e1b2a130
No related branches found
No related tags found
No related merge requests found
......@@ -9,4 +9,5 @@ members = [
"auction",
"vesting",
"gradually-update",
]
"schedule-update",
]
\ No newline at end of file
......@@ -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 = [
......
[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",
]
#![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)
}
}
//! 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()
}
}
//! 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));
});
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment