本文介绍了Cronos,一个为EOS智能合约提供定期任务处理的解决方案。文章深入阐述了Cronos的工作原理、使用方法以及后端逻辑,特别强调了如何通过智能合约实现类似于Unix系统中的cron功能,以满足DApp开发者的需求。该项目还提供了示例代码和潜在的改进方向。
作者: MixBytes 团队
简介
软件开发通常涉及需要定期执行的任务。例如,Unix 系统有 crond 来满足这些目的。
在我们开始讨论解决方案之前,先回答一下我们为什么在区块链中需要 cron。
有很多应用,以下是其中一些:
你是一个 Dapp 开发者,在表中存储数据,占用了一些 RAM 空间。内存是需要付费的,因此应用需要定期清理这些表。
你正在开发一个货币转换器,应用需要定期从预言机获取数据。
你是一名加密基金经理,时时需要向客户发送一部分利润分成。
我们经常需要审核和编写智能合约,并且经常遇到上述问题。不幸的是,EOS 不允许创建定期任务,因此我们尝试通过编写我们的 cron 智能合约(Cronos)来填补这个空白。
什么是 Cronos
Cronos 是一个针对 EOS 的 cron 智能合约,帮助 DApps 执行定期任务。
当然,你可以手动调用合约方法或编写一个脚本来为你做到这一点。然而,支付少量费用以进行定期的 cronos 调用并停止担心要容易和安全得多。这样,你就得到了某种 cron-as-a-service :)
使用 Cronos(内存清理)
服务的工作原理如下:用户向智能合约发送所需数量的 EOS 代币。然后,智能合约使用调度函数创建任务。在任务执行过程中,合约将自动扣除一定量的 EOS 代币。
例如,如果你想每 42 秒清理智能合约存储,可以使用 Cronos 创建这样的任务:
cleos push action cron schedule '["andrew", "mydapp", "cleanup", 42]' -p andrew
如果开发者正确实现了合约的清理操作,则清理将成功进行。Cronos 表中将创建任务及其执行时间的条目。
底层实现
Cronos 相当简单。账号所有者在给定间隔(polling_interval)内调用一次 run。然后,在合约执行过程中,会创建一个带有相同 polling_interval 的 deferred transaction。
你可以在 官方文档 中了解有关延迟交易的更多信息。别忘了查看 使用案例。
注意,在 EOS 交易执行时间限制为 30 毫秒。因此,为单个交易中处理的记录数设置限制(rows_count)是合理的。
以下是带注释的代码库:
ACTION run(uint32_t polling_interval, uint32_t rows_count) {
// 确保事务是由当前合约创建的
require_auth(get_self());
// 检查是否应停止执行
if (stop_execution.get())
return;
// 记录处理
scan_schedules(rows_count);
// 在 polling_interval 秒后再次调用 run
create_transaction(_self, _self, "run", polling_interval, make_tuple(polling_interval, rows_count));
}
template<class ...TParams>
void create_transaction(name payer, name account, const string &action, uint32_t delay,
const std::tuple<TParams...>& args) {
// 创建一个具有所需延迟的延迟事务
eosio::transaction t;
t.actions.emplace_back(
permission_level(_code, "active"_n),
account,
name(action),
args);
t.delay_sec = delay;
// 出于调试目的,你需要一个唯一 ID
auto sender_id = unique_id.get();
t.send(sender_id, payer);
unique_id.set(sender_id + 1, _code);
}
表中任务的处理逻辑相当简单。Cronos 选择执行时间最短的任务,确保它必须被执行(即当前时间小于或等于执行时间),并在确保用户有足够的 EOS 代币余额后创建一个延迟交易。在任务处理结束时,Cronos 自动重新计算下一次调用的时间。
// 我们获取下一个区块创建的 unix 时间
time_point_sec current_time(now());
// 我们检查是否是执行任务的时间
if (current_time >= item.next_run) {
const name& account_from = item.from;
// 确保用户在他的钱包账户上有足够的资金
if (get_balance(account_from) >= CALL_PRICE) {
reduce_balance(account_from, CALL_PRICE);
create_transaction(account_from, item.account, item.action, item.period, tuple<name>(account_from));
// 刷新运行时间
cron_table.modify(item, _self, [&](auto& row) {
row.next_run = item.next_run + item.period;
});
}
延迟交易的陷阱
我必须说,延迟交易的方法有一个显著缺点。例如,如果当前交易因某种原因失败,则不会创建延迟交易。为避免这种情况,你需要在调试时加倍小心。否则,mydapp 合约的所有者将不得不再次手动调用 run 操作。
下一步
我们的团队将添加一些功能,允许设置类似于原始 crond Unix 的任务规则。
参考
Github 仓库:
https://github.com/mixbytes/cronos
MixBytes 是一支由专家区块链审计师和安全研究人员组成的团队,专注于为 EVM 兼容和 Substrate 基础的项目提供全面的智能合约审计和技术咨询服务。请加入我们 X,以随时了解最新的行业趋势和见解。
- 原文链接: mixbytes.io/blog/schedul...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!