Skip to content

Commit 0d1ca5c

Browse files
committed
add delay-tasks
1 parent 7e05827 commit 0d1ca5c

File tree

11 files changed

+522
-31
lines changed

11 files changed

+522
-31
lines changed

Cargo.dev.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ members = [
66
"benchmarking",
77
"build-script-utils",
88
"currencies",
9+
"delay-tasks",
910
"gradually-update",
1011
"nft",
1112
"oracle",

delay-tasks/Cargo.toml

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
[package]
2+
name = "orml-delay-tasks"
3+
description = "Delayed xtokens transfer assets executor."
4+
repository = "https://github.com/open-web3-stack/open-runtime-module-library/tree/master/auction"
5+
license = "Apache-2.0"
6+
version = "0.7.0"
7+
authors = ["Acala Developers"]
8+
edition = "2021"
9+
10+
[dependencies]
11+
log = { workspace = true }
12+
parity-scale-codec = { workspace = true }
13+
scale-info = { workspace = true }
14+
serde = { workspace = true, optional = true }
15+
16+
frame-support = { workspace = true }
17+
frame-system = { workspace = true }
18+
sp-runtime = { workspace = true }
19+
sp-std = { workspace = true }
20+
21+
xcm = { workspace = true }
22+
23+
orml-traits = { path = "../traits", version = "0.7.0", default-features = false }
24+
orml-xtokens = { path = "../xtokens", version = "0.7.0", default-features = false }
25+
26+
[dev-dependencies]
27+
sp-core = { workspace = true, features = ["std"] }
28+
sp-io = { workspace = true, features = ["std"] }
29+
30+
[features]
31+
default = [ "std" ]
32+
std = [
33+
"frame-support/std",
34+
"frame-system/std",
35+
"orml-traits/std",
36+
"orml-xtokens/std",
37+
"parity-scale-codec/std",
38+
"scale-info/std",
39+
"serde",
40+
"sp-runtime/std",
41+
"sp-std/std",
42+
"xcm/std",
43+
]
44+
try-runtime = [
45+
"frame-support/try-runtime",
46+
"frame-system/try-runtime",
47+
"sp-runtime/try-runtime",
48+
]

delay-tasks/src/lib.rs

Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
#![cfg_attr(not(feature = "std"), no_std)]
2+
3+
use frame_support::{pallet_prelude::*, traits::schedule::DispatchTime, transactional, weights::Weight};
4+
use frame_system::{ensure_signed, pallet_prelude::*};
5+
use orml_traits::{
6+
delay_tasks::{DelayTasks, DelayedTask},
7+
NamedMultiReservableCurrency,
8+
};
9+
use parity_scale_codec::{Decode, Encode, FullCodec};
10+
use scale_info::TypeInfo;
11+
use sp_runtime::{
12+
traits::{CheckedAdd, Convert, Zero},
13+
ArithmeticError,
14+
};
15+
use sp_std::fmt::Debug;
16+
use xcm::v4::prelude::*;
17+
18+
pub use module::*;
19+
20+
mod mock;
21+
mod tests;
22+
23+
#[frame_support::pallet]
24+
pub mod module {
25+
use super::*;
26+
27+
type Nonce = u64;
28+
29+
#[pallet::config]
30+
pub trait Config: frame_system::Config + orml_xtokens::Config {
31+
type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
32+
33+
type GovernanceOrigin: EnsureOrigin<<Self as frame_system::Config>::RuntimeOrigin>;
34+
35+
type Task: DelayedTask + FullCodec + Debug + Clone + PartialEq + TypeInfo;
36+
}
37+
38+
#[pallet::error]
39+
pub enum Error<T> {
40+
BelowMinBondThreshold,
41+
InvalidDelayBlock,
42+
InvalidId,
43+
}
44+
45+
#[pallet::event]
46+
#[pallet::generate_deposit(pub(crate) fn deposit_event)]
47+
pub enum Event<T: Config> {
48+
/// A task has been dispatched on_idle.
49+
DelayedTaskAdded {
50+
id: Nonce,
51+
task: T::Task,
52+
},
53+
DelayedTaskExecuted {
54+
id: Nonce,
55+
result: DispatchResult,
56+
},
57+
DelayedTaskPreExecuteFailed {
58+
id: Nonce,
59+
},
60+
}
61+
62+
#[pallet::pallet]
63+
#[pallet::without_storage_info]
64+
pub struct Pallet<T>(_);
65+
66+
#[pallet::hooks]
67+
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
68+
/// `on_initialize` to return the weight used in `on_finalize`.
69+
fn on_initialize(now: BlockNumberFor<T>) -> Weight {
70+
Weight::zero()
71+
}
72+
73+
fn on_finalize(now: BlockNumberFor<T>) {
74+
Self::_on_finalize(now);
75+
}
76+
}
77+
78+
#[pallet::storage]
79+
#[pallet::getter(fn next_delayed_task_id)]
80+
pub type NextDelayedTaskId<T: Config> = StorageValue<_, Nonce, ValueQuery>;
81+
82+
#[pallet::storage]
83+
#[pallet::getter(fn delayed_tasks)]
84+
pub type DelayedTasks<T: Config> = StorageMap<_, Twox64Concat, Nonce, (T::Task, BlockNumberFor<T>), OptionQuery>;
85+
86+
#[pallet::storage]
87+
#[pallet::getter(fn delayed_task_queue)]
88+
pub type DelayedTaskQueue<T: Config> =
89+
StorageDoubleMap<_, Twox64Concat, BlockNumberFor<T>, Twox64Concat, Nonce, (), OptionQuery>;
90+
91+
#[pallet::call]
92+
impl<T: Config> Pallet<T> {
93+
#[pallet::call_index(0)]
94+
#[pallet::weight(Weight::zero())]
95+
pub fn reset_execute_block(
96+
origin: OriginFor<T>,
97+
id: Nonce,
98+
when: DispatchTime<BlockNumberFor<T>>,
99+
) -> DispatchResult {
100+
T::GovernanceOrigin::ensure_origin(origin)?;
101+
102+
DelayedTasks::<T>::try_mutate_exists(id, |maybe_task| -> DispatchResult {
103+
let (_, execute_block) = maybe_task.as_mut().ok_or(Error::<T>::InvalidId)?;
104+
105+
let now = frame_system::Pallet::<T>::block_number();
106+
let new_execute_block = match when {
107+
DispatchTime::At(x) => x,
108+
DispatchTime::After(x) => x.checked_add(&now).ok_or(ArithmeticError::Overflow)?,
109+
};
110+
ensure!(new_execute_block > now, Error::<T>::InvalidDelayBlock);
111+
112+
DelayedTaskQueue::<T>::remove(*execute_block, id);
113+
DelayedTaskQueue::<T>::insert(new_execute_block, id, ());
114+
*execute_block = new_execute_block;
115+
116+
Ok(())
117+
})?;
118+
119+
Ok(())
120+
}
121+
122+
#[pallet::call_index(1)]
123+
#[pallet::weight(Weight::zero())]
124+
pub fn cancel_delayed_task(origin: OriginFor<T>, id: Nonce) -> DispatchResult {
125+
T::GovernanceOrigin::ensure_origin(origin)?;
126+
127+
let (delay_task, execute_block) = DelayedTasks::<T>::take(id).ok_or(Error::<T>::InvalidId)?;
128+
delay_task.on_cancel()?;
129+
DelayedTaskQueue::<T>::remove(execute_block, id);
130+
131+
Ok(())
132+
}
133+
}
134+
135+
impl<T: Config> Pallet<T> {
136+
fn _on_finalize(now: BlockNumberFor<T>) {
137+
for (id, _) in DelayedTaskQueue::<T>::drain_prefix(now) {
138+
match Self::do_execute_delayed_task(id) {
139+
Ok(result) => {
140+
Self::deposit_event(Event::<T>::DelayedTaskExecuted { id, result });
141+
}
142+
Err(e) => {
143+
Self::deposit_event(Event::<T>::DelayedTaskPreExecuteFailed { id });
144+
}
145+
}
146+
}
147+
}
148+
149+
#[transactional]
150+
fn do_execute_delayed_task(id: Nonce) -> sp_std::result::Result<DispatchResult, DispatchError> {
151+
let (delayed_task, _) = DelayedTasks::<T>::take(id).ok_or(Error::<T>::InvalidId)?;
152+
delayed_task.pre_delayed_execute().map_err(|e| {
153+
log::debug!(
154+
target: "delay-tasks",
155+
"delayed task#{:?}:\n {:?}\n do pre_execute_delayed failed for: {:?}",
156+
id,
157+
delayed_task,
158+
e
159+
);
160+
e
161+
})?;
162+
163+
Ok(delayed_task.delayed_execute())
164+
}
165+
166+
/// Retrieves the next delayed task ID from storage, and increment it by
167+
/// one.
168+
fn get_next_delayed_task_id() -> Result<Nonce, DispatchError> {
169+
NextDelayedTaskId::<T>::mutate(|current| -> Result<Nonce, DispatchError> {
170+
let id = *current;
171+
172+
*current = current.checked_add(1).ok_or(ArithmeticError::Overflow)?;
173+
Ok(id)
174+
})
175+
}
176+
}
177+
178+
impl<T: Config> DelayTasks<T::Task, BlockNumberFor<T>> for Pallet<T> {
179+
fn schedule_delay_task(task: T::Task, delay_blocks: BlockNumberFor<T>) -> DispatchResult {
180+
ensure!(!delay_blocks.is_zero(), Error::<T>::InvalidDelayBlock);
181+
182+
task.pre_delay()?;
183+
184+
let id = Self::get_next_delayed_task_id()?;
185+
let execute_block_number = frame_system::Pallet::<T>::block_number()
186+
.checked_add(&delay_blocks)
187+
.ok_or(ArithmeticError::Overflow)?;
188+
189+
DelayedTasks::<T>::insert(id, (&task, execute_block_number));
190+
DelayedTaskQueue::<T>::insert(execute_block_number, id, ());
191+
192+
Self::deposit_event(Event::<T>::DelayedTaskAdded { id, task });
193+
Ok(())
194+
}
195+
}
196+
}

delay-tasks/src/mock.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+

delay-tasks/src/tests.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+

traits/src/delay_tasks.rs

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
use sp_runtime::DispatchResult;
2+
3+
pub trait DelayedTask {
4+
fn pre_delay(&self) -> DispatchResult;
5+
fn pre_delayed_execute(&self) -> DispatchResult;
6+
fn delayed_execute(&self) -> DispatchResult;
7+
fn on_cancel(&self) -> DispatchResult;
8+
}
9+
10+
pub trait DelayTasks<Task, BlockNumber> {
11+
fn schedule_delay_task(task: Task, delay_blocks: BlockNumber) -> DispatchResult;
12+
}
13+
14+
#[macro_export]
15+
macro_rules! define_combined_delayed_task {
16+
(
17+
$(#[$meta:meta])*
18+
$vis:vis enum $combined_name:ident {
19+
$(
20+
$task:ident ( $vtask:ident $(<$($generic:tt),*>)? )
21+
),+ $(,)?
22+
}
23+
) => {
24+
$(#[$meta])*
25+
$vis enum $combined_name {
26+
$(
27+
$task($vtask $(<$($generic),*>)?),
28+
)*
29+
}
30+
31+
impl DelayedTask for $combined_name {
32+
fn pre_delay(&self) -> DispatchResult {
33+
match self {
34+
$(
35+
$combined_name::$task(t) => t.pre_delay(),
36+
)*
37+
}
38+
}
39+
fn pre_delayed_execute(&self) -> DispatchResult {
40+
match self {
41+
$(
42+
$combined_name::$task(t) => t.pre_delayed_execute(),
43+
)*
44+
}
45+
}
46+
fn delayed_execute(&self) -> DispatchResult {
47+
match self {
48+
$(
49+
$combined_name::$task(t) => t.delayed_execute(),
50+
)*
51+
}
52+
}
53+
fn on_cancel(&self) -> DispatchResult {
54+
match self {
55+
$(
56+
$combined_name::$task(t) => t.on_cancel(),
57+
)*
58+
}
59+
}
60+
}
61+
62+
$(
63+
impl From<$vtask $(<$($generic),*>)?> for $combined_name {
64+
fn from(t: $vtask $(<$($generic),*>)?) -> Self{
65+
$combined_name::$task(t)
66+
}
67+
}
68+
)*
69+
};
70+
}

traits/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ pub mod asset_registry;
3232
pub mod auction;
3333
pub mod currency;
3434
pub mod data_provider;
35+
pub mod delay_tasks;
3536
pub mod get_by_key;
3637
pub mod location;
3738
pub mod multi_asset;

0 commit comments

Comments
 (0)