Skip to content

Commit b76b027

Browse files
authored
Integrate modified scheduler to reversible-transfers pallet (#15)
1 parent 65b2c8d commit b76b027

14 files changed

Lines changed: 951 additions & 382 deletions

File tree

Cargo.lock

Lines changed: 78 additions & 123 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,9 @@ pallet-conviction-voting = { version = "39.1.0", default-features = false }
5050
pallet-ranked-collective = { version = "39.0.0", default-features = false }
5151
pallet-preimage = { version = "39.0.0", default-features = false }
5252
pallet-referenda = { version = "39.1.0", default-features = false }
53+
pallet-treasury = { version = "38.1.0", default-features = false}
5354
pallet-recovery = { version = "39.1.0", default-features = false }
5455
pallet-scheduler = { path = "./pallets/scheduler", default-features = false }
55-
pallet-treasury = { version = "38.1.0", default-features = false }
5656
pallet-utility = { version = "39.1.0", default-features = false }
5757
parking_lot = { version = "0.12.1", default-features = false }
5858
pallet-vesting = { version = "39.1.0", default-features = false }
@@ -86,7 +86,7 @@ sp-blockchain = { version = "38.0.0", default-features = false }
8686
sp-consensus = { version = "0.41.0", default-features = false }
8787
sp-consensus-pow = { path = "./primitives/consensus/pow", default-features = false }
8888
sp-consensus-qpow = { path = "./primitives/consensus/qpow", default-features = false }
89-
sp-common = { path = "./primitives/common", default-features = false }
89+
qp-scheduler = { path = "./primitives/scheduler", default-features = false }
9090
sp-faucet = { path = "./primitives/faucet", default-features = false }
9191
sp-core = { version = "35.0.0", default-features = false }
9292
sp-genesis-builder = { version = "0.16.0", default-features = false }

pallets/reversible-transfers/Cargo.toml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ frame-support.workspace = true
2020
frame-system.workspace = true
2121
sp-runtime.workspace = true
2222
pallet-balances = { workspace = true }
23+
qp-scheduler = { workspace = true, default-features = false }
2324

2425
[dev-dependencies]
2526
frame-support = { workspace = true, features = ["experimental"], default-features = true }
@@ -29,7 +30,7 @@ pallet-scheduler = { workspace = true, default-features = false }
2930
pallet-timestamp = { workspace = true, default-features = false }
3031
pallet-balances = { workspace = true, features = ["std"] }
3132
pallet-preimage = { workspace = true, default-features = false }
32-
sp-common = { workspace = true, default-features = false }
33+
qp-scheduler = { workspace = true, default-features = false }
3334

3435
[features]
3536
default = ["std"]
@@ -42,6 +43,8 @@ std = [
4243
"scale-info/std",
4344
"sp-runtime/std",
4445
"pallet-balances/std",
46+
"qp-scheduler/std",
47+
"pallet-scheduler/std",
4548
]
4649
# Enable support for setting the existential deposit to zero.
4750
insecure_zero_ed = []

pallets/reversible-transfers/src/lib.rs

Lines changed: 86 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,18 @@ pub mod weights;
2222
pub use weights::WeightInfo;
2323

2424
use alloc::vec::Vec;
25+
use frame_support::pallet_prelude::*;
2526
use frame_support::traits::tokens::{Fortitude, Restriction};
26-
use frame_support::{pallet_prelude::*, traits::schedule::DispatchTime};
2727
use frame_system::pallet_prelude::*;
28+
use qp_scheduler::{BlockNumberOrTimestamp, DispatchTime, ScheduleNamed};
2829
use sp_runtime::traits::StaticLookup;
2930

31+
/// Type alias for this config's `BlockNumberOrTimestamp`.
32+
pub type BlockNumberOrTimestampOf<T> =
33+
BlockNumberOrTimestamp<BlockNumberFor<T>, <T as Config>::Moment>;
34+
3035
/// How to delay transactions
31-
/// - `Explicit`: Only delay transactions explicitly using `schedule_transfer`.
36+
/// - `Explicit`: Only delay transactions explicitly using this pallet's `schedule_transfer` extrinsic.
3237
/// - `Intercept`: Intercept and delay transactions at the `TransactionExtension` level.
3338
///
3439
/// For example, for a reversible account with `DelayPolicy::Intercept`, the transaction
@@ -49,11 +54,11 @@ pub enum DelayPolicy {
4954

5055
/// Reversible account details
5156
#[derive(Encode, Decode, MaxEncodedLen, Clone, Default, TypeInfo, Debug, PartialEq, Eq)]
52-
pub struct ReversibleAccountData<AccountId, BlockNumber> {
57+
pub struct ReversibleAccountData<AccountId, Delay> {
5358
/// The account that can reverse the transaction. `None` means the account itself.
5459
pub explicit_reverser: Option<AccountId>,
5560
/// The delay period for the account
56-
pub delay: BlockNumber,
61+
pub delay: Delay,
5762
/// The policy for the account
5863
pub policy: DelayPolicy,
5964
}
@@ -78,17 +83,15 @@ type BalanceOf<T> = <T as pallet_balances::Config>::Balance;
7883
#[frame_support::pallet]
7984
pub mod pallet {
8085
use super::*;
86+
use crate::BlockNumberOrTimestampOf;
8187
use frame_support::dispatch::PostDispatchInfo;
8288
use frame_support::traits::fungible::MutateHold;
8389
use frame_support::traits::tokens::Precision;
84-
use frame_support::traits::{Bounded, CallerTrait, QueryPreimage, StorePreimage};
85-
use frame_support::{
86-
traits::schedule::v3::{Named, TaskName},
87-
PalletId,
88-
};
89-
use sp_runtime::traits::AccountIdConversion;
90-
use sp_runtime::traits::Hash;
90+
use frame_support::traits::{Bounded, CallerTrait, QueryPreimage, StorePreimage, Time};
91+
use frame_support::{traits::schedule::v3::TaskName, PalletId};
92+
use sp_runtime::traits::{AccountIdConversion, AtLeast32Bit};
9193
use sp_runtime::traits::{BlockNumberProvider, Dispatchable};
94+
use sp_runtime::traits::{Hash, Scale};
9295
use sp_runtime::Saturating;
9396

9497
#[pallet::pallet]
@@ -106,8 +109,9 @@ pub mod pallet {
106109
type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
107110

108111
/// Scheduler for the runtime. We use the Named scheduler for cancellability.
109-
type Scheduler: Named<
112+
type Scheduler: ScheduleNamed<
110113
BlockNumberFor<Self>,
114+
Self::Moment,
111115
Self::RuntimeCall,
112116
Self::SchedulerOrigin,
113117
Hasher = Self::Hashing,
@@ -126,12 +130,18 @@ pub mod pallet {
126130
type MaxPendingPerAccount: Get<u32>;
127131

128132
/// The default delay period for reversible transactions if none is specified.
133+
///
134+
/// NOTE: default delay is always in blocks.
135+
#[pallet::constant]
136+
type DefaultDelay: Get<BlockNumberOrTimestampOf<Self>>;
137+
138+
/// The minimum delay period allowed for reversible transactions, in blocks.
129139
#[pallet::constant]
130-
type DefaultDelay: Get<BlockNumberFor<Self>>;
140+
type MinDelayPeriodBlocks: Get<BlockNumberFor<Self>>;
131141

132-
/// The minimum delay period allowed for reversible transactions.
142+
/// The minimum delay period allowed for reversible transactions, in milliseconds.
133143
#[pallet::constant]
134-
type MinDelayPeriod: Get<BlockNumberFor<Self>>;
144+
type MinDelayPeriodMoment: Get<Self::Moment>;
135145

136146
/// Pallet Id
137147
type PalletId: Get<PalletId>;
@@ -144,6 +154,17 @@ pub mod pallet {
144154

145155
/// Hold reason for the reversible transactions.
146156
type RuntimeHoldReason: From<HoldReason>;
157+
158+
/// Moment type for scheduling.
159+
type Moment: Saturating
160+
+ Copy
161+
+ Parameter
162+
+ AtLeast32Bit
163+
+ Scale<BlockNumberFor<Self>, Output = Self::Moment>
164+
+ MaxEncodedLen;
165+
166+
/// Time provider for scheduling.
167+
type TimeProvider: Time<Moment = Self::Moment>;
147168
}
148169

149170
/// Maps accounts to their chosen reversibility delay period (in milliseconds).
@@ -154,7 +175,7 @@ pub mod pallet {
154175
_,
155176
Blake2_128Concat,
156177
T::AccountId,
157-
ReversibleAccountData<T::AccountId, BlockNumberFor<T>>,
178+
ReversibleAccountData<T::AccountId, BlockNumberOrTimestampOf<T>>,
158179
OptionQuery,
159180
>;
160181

@@ -184,14 +205,14 @@ pub mod pallet {
184205
/// [who, maybe_delay: None means disabled]
185206
ReversibilitySet {
186207
who: T::AccountId,
187-
data: ReversibleAccountData<T::AccountId, BlockNumberFor<T>>,
208+
data: ReversibleAccountData<T::AccountId, BlockNumberOrTimestampOf<T>>,
188209
},
189210
/// A transaction has been intercepted and scheduled for delayed execution.
190211
/// [who, tx_id, execute_at_moment]
191212
TransactionScheduled {
192213
who: T::AccountId,
193214
tx_id: T::Hash,
194-
execute_at: DispatchTime<BlockNumberFor<T>>,
215+
execute_at: DispatchTime<BlockNumberFor<T>, T::Moment>,
195216
},
196217
/// A scheduled transaction has been successfully cancelled by the owner.
197218
/// [who, tx_id]
@@ -248,24 +269,36 @@ pub mod pallet {
248269
#[pallet::weight(<T as Config>::WeightInfo::set_reversibility())]
249270
pub fn set_reversibility(
250271
origin: OriginFor<T>,
251-
delay: Option<BlockNumberFor<T>>,
272+
delay: Option<BlockNumberOrTimestampOf<T>>,
252273
policy: DelayPolicy,
253274
reverser: Option<T::AccountId>,
254275
) -> DispatchResult {
255276
let who = ensure_signed(origin)?;
256277

257-
ensure!(
258-
!ReversibleAccounts::<T>::contains_key(&who),
259-
Error::<T>::AccountAlreadyReversible
260-
);
261278
ensure!(
262279
reverser != Some(who.clone()),
263280
Error::<T>::ExplicitReverserCanNotBeSelf
264281
);
265-
282+
ensure!(
283+
!ReversibleAccounts::<T>::contains_key(&who),
284+
Error::<T>::AccountAlreadyReversible
285+
);
266286
let delay = delay.unwrap_or(T::DefaultDelay::get());
267287

268-
ensure!(delay >= T::MinDelayPeriod::get(), Error::<T>::DelayTooShort);
288+
match delay {
289+
BlockNumberOrTimestamp::BlockNumber(x) => {
290+
ensure!(
291+
x > T::MinDelayPeriodBlocks::get(),
292+
Error::<T>::DelayTooShort
293+
)
294+
}
295+
BlockNumberOrTimestamp::Timestamp(t) => {
296+
ensure!(
297+
t > T::MinDelayPeriodMoment::get(),
298+
Error::<T>::DelayTooShort
299+
)
300+
}
301+
}
269302

270303
let reversible_account_data = ReversibleAccountData {
271304
explicit_reverser: reverser,
@@ -327,12 +360,16 @@ pub mod pallet {
327360
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
328361
fn integrity_test() {
329362
assert!(
330-
T::MinDelayPeriod::get() > Zero::zero(),
331-
"`T::MinDelayPeriod` must be greater than 0"
363+
!T::MinDelayPeriodBlocks::get().is_zero()
364+
&& !T::MinDelayPeriodMoment::get().is_zero(),
365+
"Minimum delay periods must be greater than 0"
332366
);
367+
368+
// NOTE: default delay is always in blocks
333369
assert!(
334-
T::MinDelayPeriod::get() <= T::DefaultDelay::get(),
335-
"`T::MinDelayPeriod` must be less or equal to `T::DefaultDelay`"
370+
BlockNumberOrTimestampOf::<T>::BlockNumber(T::MinDelayPeriodBlocks::get())
371+
<= T::DefaultDelay::get(),
372+
"Minimum delay periods must be less or equal to `T::DefaultDelay`"
336373
);
337374
}
338375
}
@@ -352,7 +389,7 @@ pub mod pallet {
352389
/// Check if an account has reversibility enabled and return its delay.
353390
pub fn is_reversible(
354391
who: &T::AccountId,
355-
) -> Option<ReversibleAccountData<T::AccountId, BlockNumberFor<T>>> {
392+
) -> Option<ReversibleAccountData<T::AccountId, BlockNumberOrTimestampOf<T>>> {
356393
ReversibleAccounts::<T>::get(who)
357394
}
358395

@@ -430,18 +467,8 @@ pub mod pallet {
430467
amount: BalanceOf<T>,
431468
) -> DispatchResult {
432469
let who = ensure_signed(origin)?;
433-
let ReversibleAccountData {
434-
delay,
435-
explicit_reverser,
436-
policy: _,
437-
} = Self::reversible_accounts(&who).ok_or(Error::<T>::AccountNotReversible)?;
438-
439-
match explicit_reverser {
440-
Some(reverser) => {
441-
ensure!(who != reverser, Error::<T>::InvalidReverser);
442-
}
443-
None => {}
444-
};
470+
let ReversibleAccountData { delay, .. } =
471+
Self::reversible_accounts(&who).ok_or(Error::<T>::AccountNotReversible)?;
445472

446473
let transfer_call: T::RuntimeCall = pallet_balances::Call::<T>::transfer_keep_alive {
447474
dest: dest.clone(),
@@ -461,9 +488,16 @@ pub mod pallet {
461488
Ok(())
462489
})?;
463490

464-
let dispatch_time = DispatchTime::At(
465-
T::BlockNumberProvider::current_block_number().saturating_add(delay),
466-
);
491+
let dispatch_time = match delay {
492+
BlockNumberOrTimestamp::BlockNumber(blocks) => DispatchTime::At(
493+
T::BlockNumberProvider::current_block_number().saturating_add(blocks),
494+
),
495+
BlockNumberOrTimestamp::Timestamp(millis) => {
496+
DispatchTime::After(BlockNumberOrTimestamp::Timestamp(
497+
T::TimeProvider::now().saturating_add(millis),
498+
))
499+
}
500+
};
467501

468502
let call = T::Preimages::bound(transfer_call)?;
469503

@@ -592,6 +626,7 @@ pub mod pallet {
592626
#[derive(frame_support::DefaultNoBound)]
593627
pub struct GenesisConfig<T: Config> {
594628
/// Configure initial reversible accounts. [AccountId, Delay]
629+
/// NOTE: using `(bool, BlockNumberFor<T>)` where `bool` indicates if the delay is in block numbers
595630
pub initial_reversible_accounts: Vec<(T::AccountId, BlockNumberFor<T>)>,
596631
}
597632

@@ -600,20 +635,22 @@ pub mod pallet {
600635
fn build(&self) {
601636
for (who, delay) in &self.initial_reversible_accounts {
602637
// Basic validation, ensure delay is reasonable if needed
603-
if *delay >= T::MinDelayPeriod::get() {
638+
let wrapped_delay = BlockNumberOrTimestampOf::<T>::BlockNumber(*delay);
639+
640+
if delay >= &T::MinDelayPeriodBlocks::get() {
604641
ReversibleAccounts::<T>::insert(
605642
who,
606643
ReversibleAccountData {
607644
explicit_reverser: None,
608-
delay: *delay,
645+
delay: wrapped_delay,
609646
policy: DelayPolicy::Explicit,
610647
},
611648
);
612649
} else {
613650
// Optionally log a warning during genesis build
614651
log::warn!(
615-
"Genesis config for account {:?} has delay {:?} below MinDelayPeriod {:?}, skipping.",
616-
who, delay, T::MinDelayPeriod::get()
652+
"Genesis config for account {:?} has delay {:?} below MinDelayPeriodBlocks {:?}, skipping.",
653+
who, wrapped_delay, T::MinDelayPeriodBlocks::get()
617654
);
618655
}
619656
}

0 commit comments

Comments
 (0)