:深入理解 `require` 的日志与回滚语义)
Sway 智能合约断言机制二深入理解require的日志与回滚语义【免费下载链接】sway Empowering everyone to build reliable and efficient smart contracts.项目地址: https://gitcode.com/GitHub_Trending/sw/swayrequire是 Sway 语言中最常用的运行时条件检查函数它由标准库 prelude 自动导入每个程序用于在条件不满足时记录一条日志并让 Fuel 虚拟机VM安全回滚revert。本篇技术指南以 Sway 官方文档的require章节为骨架结合 sway-lib-std 标准库的源码实现完整讲解require的调用方式、泛型消息类型、底层回滚信号以及它与assert、revert系列函数的区别帮助你写出更健壮、更易排查的智能合约。一、require是什么require是 Sway 标准库提供的一个运行时断言函数。它接受一个必须求值为 Boolean 类型的表达式如果该表达式求值为true什么都不会发生程序继续执行后续代码如果求值为false标准库会先发出一条日志log随后让虚拟机回滚revert。与普通assert的最大区别在于require允许你附带一条有意义的错误消息该消息会作为日志被记录到链上供链下索引、区块浏览器或前端应用读取从而极大提升合约失败时的可诊断性。二、自动导入prelude 的作用require并不需要你手动use引入。在 Sway 中每个程序都会隐式导入标准库的 prelude其中声明了对require的导出。这一点可以从 prelude.sw 源码中得到直接验证// Error handling pub use ::assert::{assert, assert_eq, assert_ne}; pub use ::option::Option::{self, *}; pub use ::result::Result::{self, *}; pub use ::revert::{require, revert, revert_with_log};也就是说require、revert、revert_with_log三个函数全部由 revert 模块 定义并经 prelude 自动暴露给所有 Sway 程序。完整的 prelude 内容可参考官方文档 Standard Library Prelude。三、核心示例安全的减法函数官方文档给出的经典示例是一个减法函数。u64是无符号 64 位整数不可能为负数因此必须断言b小于等于a否则减法会下溢underflow导致错误结果。该示例的完整源码位于 req.swlibrary; fn subtract(a: u64, b: u64) - u64 { require(b a, b is too large); a - b }运行逻辑如下调用subtract(10, 5)b a为true函数继续执行并返回5调用subtract(5, 10)b a为false日志b is too large被发出虚拟机立即回滚函数不返回任何值链上状态也回到调用前的快照。这个模式在真实合约中非常典型任何依赖外部输入且对取值范围有硬性要求的计算都应当用require在入口处把守。四、消息类型是泛型的require的第二参数value是泛型的即文档所说的 generic message这意味着它可以是任意类型字符串、数值、结构体、枚举甚至自定义类型。例如// 数值作为错误码 require(amount 0, 1001); // 枚举作为错误类型 enum Error { InsufficientBalance: (), Overflow: (), } require(balance amount, Error::InsufficientBalance(()));泛型的能力来自标准库中require的声明方式。在 revert.sw 中可以看到它有两个#[cfg(experimental_new_encoding ...)]条件下的实现变体#[cfg(experimental_new_encoding false)] pub fn requireT(condition: bool, value: T) { if !condition { log(value); revert(FAILED_REQUIRE_SIGNAL) } } #[cfg(experimental_new_encoding true)] pub fn requireT(condition: bool, value: T) where T: AbiEncode, { if !condition { log(value); revert(FAILED_REQUIRE_SIGNAL) } }两种变体的行为完全一致——条件为假时先log(value)再revert(FAILED_REQUIRE_SIGNAL)。差异仅在于当开启experimental_new_encoding特性时要求消息类型T实现AbiEncodetrait以保证新的编码方案能正确序列化该日志值。五、底层原理日志 专属回滚信号理解require的关键在于它回滚时使用的专属错误信号。该常量定义在 error_signals.sw/// A revert with this value signals that it was caused by a failing call to std::revert::require. /// /// # Additional Information /// /// The value is: 18446744073709486080 pub const FAILED_REQUIRE_SIGNAL 0xffff_ffff_ffff_0000;也就是说当require失败时虚拟机回滚的退出码是0xffff_ffff_ffff_0000十进制 18446744073709486080。这个值并非随机选择高位的0xffff_ffff_ffff前缀使它与普通用户自定义回滚码区分开专门标识由标准库内置函数触发的失败链下工具如区块浏览器、SDK、索引服务可以通过检查回滚码是否为FAILED_REQUIRE_SIGNAL快速判定失败是否由某个require断言引起进而解析出附带的消息日志。同一文件中还定义了其他标准库回滚信号例如FAILED_ASSERT_SIGNALassert触发、FAILED_ASSERT_EQ_SIGNALassert_eq触发、FAILED_ASSERT_NE_SIGNALassert_ne触发和REVERT_WITH_LOG_SIGNALrevert_with_log触发它们共同构成了一套可区分的链上错误语义。六、require与assert、revert家族对比Sway 的断言体系在 Assertions 文档 中统称为防止不期望计算发生的手段共包含五个函数。require与它们的关系如下函数行为失败时是否附带日志回滚信号assert条件为假则回滚否FAILED_ASSERT_SIGNALrequire条件为假则记录消息并回滚是泛型消息FAILED_REQUIRE_SIGNALrevert无条件回滚可指定退出码否用户指定码assert_eqa ! b则回滚并记录两值是两个输入值FAILED_ASSERT_EQ_SIGNALassert_nea b则回滚并记录两值是两个输入值FAILED_ASSERT_NE_SIGNAL从实现上看assert.sw 中的assert与require结构几乎相同只是省去了log(value)这一步pub fn assert(condition: bool) { if !condition { revert(FAILED_ASSERT_SIGNAL); } }而assert_eq/assert_ne则在失败时把参与比较的两个值都记录下来帮助开发者定位是哪对值不匹配。标准库自身的注释也给出了使用建议见 assert.sw如果条件可能不为真例如依赖外部输入应优先使用require并携带错误信息assert更适合表达理应永远为真的不变式。实践选型建议参数合法性校验如余额不足、额度超限、地址非空用require附带可读错误消息方便前端与用户反馈内部不变量如算法中间步骤的正确性用assert节省一条日志的 gas 开销不可达分支 / 显式失败用revert无条件回滚或revert_with_log无条件回滚并附带日志需要对比两个值时用assert_eq/assert_ne它们会自动记录双方取值。七、实战要点CEI 模式与外部调用中的require在合约开发中require最常见的应用场景是检查-生效-交互Checks-Effects-InteractionsCEI模式的第一环即函数入口处的守卫条件。例如在转账类函数中fn withdraw(amount: u64) { require(amount 0, amount must be greater than zero); require(balance_of(msg_sender().unwrap()) amount, insufficient balance); // 状态更新与外部交互…… }使用require时有几点值得注意失败即原子回滚require失败后整个事务状态回滚到调用前不存在部分成功的中间态因此非常适合前置校验日志不会被回滚Fuel 的日志receipt是回滚时仍会保留的记录信息这正是require能既回滚又留下错误线索的原理所在消息内容选择由于消息会留在链上日志中建议写入简洁、稳定的错误码或短句避免把敏感或易变的数据直接放入日志。八、小结require是 Sway 合约编写中最基础也最实用的安全原语它由 prelude 自动导入、支持任意泛型类型的错误消息、失败时以专属信号FAILED_REQUIRE_SIGNAL回滚并留下日志。通过对照标准库 revert.sw、assert.sw 与 error_signals.sw 的源码实现可以清晰看到它在整个断言体系中的定位与取舍。掌握require与assert、revert、assert_eq、assert_ne的适用场景是写出可靠、高效且易于诊断的 Sway 智能合约的第一步。【免费下载链接】sway Empowering everyone to build reliable and efficient smart contracts.项目地址: https://gitcode.com/GitHub_Trending/sw/sway创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考