Rust区块链调度器库pallet-scheduler的使用,实现Substrate链上任务定时执行与自动化管理
Rust区块链调度器库pallet-scheduler的使用,实现Substrate链上任务定时执行与自动化管理
概述
pallet-scheduler模块提供了在指定区块号或指定周期调度调用的能力。这些调度可以是命名的或匿名的,并且可以被取消。
注意:调度调用将使用默认的origin过滤器进行分发,即除了root origin外所有origin都使用frame_system::Config::BaseCallFilter
过滤器。而调用fn schedule
时使用的origin中的过滤器不会被使用。
如果使用proxy或其他添加过滤器的机制调度调用,那么在分发调度调用时不会使用这些过滤器。
接口
可调度函数
schedule
- 调度一个可能在指定区块周期发生的调用,并指定优先级cancel
- 取消一个已调度的调用,由区块号和索引指定schedule_named
- 通过附加的Vec<u8>
参数扩展schedule
接口,可用于识别cancel_named
-cancel
函数的命名对应函数
完整示例
以下是使用pallet-scheduler实现Substrate链上任务定时执行的完整示例:
use frame_support::{dispatch::DispatchResult, traits::schedule::DispatchTime};
use frame_system::pallet_prelude::OriginFor;
use pallet_scheduler::{self as scheduler};
// 1. 定义你的Runtime配置
pub trait Config: frame_system::Config + scheduler::Config {
// 你的其他配置
}
// 2. 实现定时任务调度
pub fn schedule_task<T: Config>(
origin: OriginFor<T>,
call: <T as Config>::Call,
when: DispatchTime<T::BlockNumber>,
) -> DispatchResult {
// 使用pallet-scheduler调度任务
scheduler::Pallet::<T>::schedule(
origin,
when, // 调度时间(区块号)
None, // 优先级(可选)
Box::new(call), // 要调度的Call
)?;
Ok(())
}
// 3. 实现命名定时任务调度
pub fn schedule_named_task<T: Config>(
origin: OriginFor<T>,
id: Vec<u8>, // 任务ID
call: <T as Config>::Call,
when: DispatchTime<T::BlockNumber>,
) -> DispatchResult {
// 使用pallet-scheduler调度命名任务
scheduler::Pallet::<T>::schedule_named(
origin,
id, // 任务标识符
when, // 调度时间(区块号)
None, // 优先级(可选)
Box::new(call), // 要调度的Call
)?;
Ok(())
}
// 4. 取消任务
pub fn cancel_task<T: Config>(
origin: OriginFor<T>,
when: T::BlockNumber,
index: u32,
) -> DispatchResult {
// 取消指定区块和索引的任务
scheduler::Pallet::<T>::cancel(origin, when, index)?;
Ok(())
}
// 5. 取消命名任务
pub fn cancel_named_task<T: Config>(
origin: OriginFor<T>,
id: Vec<u8>,
) -> DispatchResult {
// 取消命名任务
scheduler::Pallet::<T>::cancel_named(origin, id)?;
Ok(())
}
安装
要使用pallet-scheduler,请将以下内容添加到你的Cargo.toml中:
pallet-scheduler = "43.0.0"
或者运行以下Cargo命令:
cargo add pallet-scheduler
许可证
Apache-2.0
1 回复
Rust区块链调度器库pallet-scheduler使用指南
概述
pallet-scheduler
是Substrate框架中的一个核心模块,用于在区块链上实现定时任务的调度和执行。它允许开发者安排未来某个区块高度或特定时间执行特定的调用,为区块链自动化提供了强大的支持。
主要特性
- 支持基于区块高度和时间的任务调度
- 提供优先级队列管理任务执行顺序
- 可配置的任务权重和费用计算
- 与Substrate其他模块无缝集成
基本使用方法
1. 在runtime中引入pallet-scheduler
首先需要在你的runtime中引入这个pallet:
// runtime/src/lib.rs
impl pallet_scheduler::Config for Runtime {
type Event = Event;
type Origin = Origin;
type PalletsOrigin = OriginCaller;
type Call = Call;
type MaximumWeight = MaximumSchedulerWeight;
type ScheduleOrigin = EnsureRoot<AccountId>;
type MaxScheduledPerBlock = ConstU32<50>;
type WeightInfo = pallet_scheduler::weights::SubstrateWeight<Runtime>;
}
construct_runtime!(
pub enum Runtime where
Block = Block,
NodeBlock = opaque::Block,
UncheckedExtrinsic = UncheckedExtrinsic
{
// ... 其他pallet
Scheduler: pallet_scheduler::{Pallet, Call, Storage, Event<T>},
}
);
2. 调度任务的基本API
安排任务
// 安排一个任务在100个区块后执行
let call = Box::new(Call::System::remark(b"Hello Scheduler".to_vec()));
let _ = Scheduler::schedule(
RuntimeOrigin::root(),
100, // 延迟的区块数
None, // 优先级
None, // 任务ID
call,
);
安排命名任务
let call = Box::new(Call::Balances::transfer(dest, amount));
let task_id = b"monthly_payment".to_vec();
let _ = Scheduler::schedule_named(
RuntimeOrigin::root(),
task_id,
100, // 延迟的区块数
None, // 优先级
None, // 周期
call,
);
取消任务
let task_id = b"monthly_payment".to_vec();
let _ = Scheduler::cancel_named(RuntimeOrigin::root(), task_id);
高级用法
周期性任务
let call = Box::new(Cull::System::remark(b"Recurring task".to_vec()));
let _ = Scheduler::schedule(
RuntimeOrigin::root(),
10, // 初始延迟
None, // 优先级
Some((10, 5)), // (周期, 重复次数)
call,
);
带优先级的任务
let call = Box::new(Call::System::remark(b"High priority task".to_vec()));
let _ = Scheduler::schedule(
RuntimeOrigin::root(),
5, // 延迟
Some(100), // 高优先级
None,
call,
);
实际应用示例
DAO自动投票关闭
// 安排7天后自动关闭投票
let close_call = Box::new(Call::Dao::close_voting(poll_id));
let blocks_in_7_days = 7 * 24 * 60 * 60 / 6; // 假设6秒一个区块
let _ = Scheduler::schedule_named(
RuntimeOrigin::signed(dao_account),
poll_id.to_le_bytes().to_vec(),
blocks_in_7_days,
None,
None,
close_call,
);
定期代币分发
// 每月1号分发代币
fn schedule_monthly_distribution() {
let distribution_call = Box::new(Call::Rewards::distribute_monthly());
let blocks_in_month = 30 * 24 * 60 * 60 / 6; // 约30天的区块数
let _ = Scheduler::schedule_named(
RuntimeOrigin::root(),
b"monthly_distrib".to_vec(),
blocks_in_month,
None,
Some((blocks_in_month, None)), // 无限重复
distribution_call,
);
}
完整示例Demo
下面是一个完整的pallet-scheduler使用示例,包含runtime集成和实际调用:
// runtime/src/lib.rs
// 1. 引入必要的依赖
use frame_support::traits::Contains;
use frame_system::EnsureRoot;
// 2. 配置Scheduler pallet
impl pallet_scheduler::Config for Runtime {
type Event = Event;
type Origin = Origin;
type PalletsOrigin = OriginCaller;
type Call = Call;
type MaximumWeight = MaximumSchedulerWeight;
type ScheduleOrigin = EnsureRoot<AccountId>; // 只有root可以调度任务
type MaxScheduledPerBlock = ConstU32<50>; // 每个区块最多50个调度任务
type WeightInfo = pallet_scheduler::weights::SubstrateWeight<Runtime>;
}
// 3. 在construct_runtime!宏中添加Scheduler
construct_runtime!(
pub enum Runtime where
Block = Block,
NodeBlock = opaque::Block,
UncheckedExtrinsic = UncheckedExtrinsic
{
// ... 其他pallet
Scheduler: pallet_scheduler::{Pallet, Call, Storage, Event<T>},
}
);
// 4. 在某个pallet中使用调度器
impl<T: Config> Pallet<T> {
pub fn schedule_delayed_action(origin: OriginFor<T>, delay: u32) -> DispatchResult {
// 验证调用者权限
ensure_root(origin)?;
// 准备要调用的函数
let call = Box::new(Call::System::remark(b"Delayed action".to_vec()));
// 调度任务
Scheduler::schedule(
RuntimeOrigin::root(),
delay,
None, // 默认优先级
None, // 不指定任务ID
call,
)?;
Ok(())
}
pub fn schedule_recurring_payment(
origin: OriginFor<T>,
recipient: AccountId,
amount: Balance,
interval: u32,
repeats: Option<u32>
) -> DispatchResult {
// 验证调用者权限
ensure_root(origin)?;
// 准备转账调用
let call = Box::new(Call::Balances::transfer(recipient, amount));
// 任务ID使用recipient地址和amount组合
let task_id = format!("payment_{}_{}", recipient, amount).into_bytes();
// 调度周期性任务
Scheduler::schedule_named(
RuntimeOrigin::root(),
task_id,
interval, // 初始延迟
None, // 默认优先级
Some((interval, repeats)), // 周期和重复次数
call,
)?;
Ok(())
}
}
注意事项
- 调度任务会占用链上存储空间,需要支付相应的费用
- 任务执行时消耗的计算资源会影响区块执行时间
- 合理安排任务优先级,避免高优先级任务阻塞其他操作
- 考虑使用命名任务以便后续管理
- 测试时注意模拟区块前进以验证任务执行
pallet-scheduler
为Substrate链提供了强大的自动化能力,合理使用可以大大增强链的功能性和灵活性。