尧图精选

Sway Predicate 编程指南:用 UTXO 花费条件构建无需部署的链上可执行程序

🕒 发布时间:2026/9/12 6:01:12 📁 来源:尧图网络
Sway Predicate 编程指南用 UTXO 花费条件构建无需部署的链上可执行程序【免费下载链接】sway Empowering everyone to build reliable and efficient smart contracts.项目地址: https://gitcode.com/GitHub_Trending/sw/sway本文基于 Fuel 生态 Sway 语言仓库中的官方文档 docs/reference/src/documentation/language/program-types/predicate.md 编写并以仓库源码、示例项目与端到端测试为佐证。Predicate谓词是 Sway 四种程序类型之一常用于多签钱包、时间锁、条件支付等交易预置条件场景。读完本文你将掌握 Predicate 的定位、运行约束、UTXO 转账与花费机制以及如何编写、配置并验证一个可用的 Predicate 程序。Predicate 是什么Predicate 是一段可执行程序它表示的是一种UTXO 花费条件spending condition典型场景如多签 Predicate——需要多把私钥共同授权才能花费对应资产。在 Sway Program Types 总览 中Sway 程序共有四种类型contract、library、script和predicate。其中 Predicate 被定义为A set of preconditions to the construction of a transaction, the result of which must be a Boolean value oftruein order for the transaction to be considered valid.即Predicate 是构造交易时必须满足的一组前置条件其执行结果必须为true交易才被认为有效。这与合约、脚本有着本质区别——Predicate 不执行业务逻辑、不存储状态它只回答一个问题这笔交易里的 UTXO 是否被允许花费与 Contract / Script 的对比维度PredicateScriptContract是否需要部署否仅在交易执行期间存在否仅在交易执行期间存在是通过交易部署上链链上持久形态仅 Predicate Root 作为 UTXO 拥有者无字节码与状态常驻链上入口main()函数返回boolmain()函数ABIabi声明 impl实现是否可读/写合约状态不可以无状态但可通过合约交互存储可以主要用途交易花费条件多签、时间锁等复杂多步链上交互协议与业务系统从仓库示例看三者文件头声明不同Predicate 以predicate;开头见 simple_predicate/src/main.sw脚本以script;开头合约以contract;开头。入口方面合约暴露 ABI而 Script 与 Predicate 都暴露main()函数作为执行入口见 program-types/index.md。Predicate 的核心运行约束根据官方文档Predicate 有三条关键约束无需部署Predicate 不需要部署到区块链因为它只在交易执行期间存在。但注意Predicate Root谓词根会留在链上作为一条或多条 UTXO 的拥有者owner。不能访问合约状态Predicate 既不能读取、也不能写入任何合约状态contract state。禁止使用合约指令Predicate 不能使用 FuelVM 指令集中的合约指令contract instructions这类指令用于CALL、RETD等与合约交互相关的操作。从语义分析实现上看Sway 编译器在 sway-core/src/semantic_analysis/module.rs 等模块中会对 Predicate 程序进行专门的类型检查确保其符合上述限制同时在 sway-core/src/semantic_analysis/cei_pattern_analysis.rs 中可以看到对 Predicate 代码路径的处理。若 Predicate 试图执行不允许的操作会被编译期或 VM 执行期拒绝。此外还有一条自动失败规则如果 Predicate 在执行时发生 revert回滚或尝试访问不纯impure的 VM 操作码则其评估结果自动为false。这意味着 Predicate 是一种保守失败的设计任何异常情况都不会被当作成功未通过验证的交易将无法花费对应 UTXO。如何向 Predicate 转账在 Fuel 中普通交易可以把资产发送到某个地址。而 Predicate 的字节码会计算出一个字节码根bytecode root这个根就是 Predicate 在链上的唯一身份。因此向 Predicate 转账的方式是将 coins 发送到一个唯一表示该 Predicate 字节码的地址即 bytecode root 对应的地址。这样链上 UTXO 的 owner 字段就会指向该 Predicate Root。资金到达后Predicate 字节码本身并不上链——链上只保留了它的根哈希。这种设计与合约地址不同合约地址是部署后的固定地址而 Predicate 地址是字节码的哈希派生物任何人生成相同的字节码就能推导出相同的地址无需任何部署操作。如何花费 Predicate 持有的资金Predicate 持有的 coin UTXO 的可花费条件与传统签名不同coin UTXO 变得可花费不是靠提供有效签名而是要求所提供的 Predicate 的 Root 与 UTXO 的 owner 匹配并且 Predicate 执行评估结果为true。也就是说花费流程包含两个必须同时满足的条件Root 匹配交易中提交的 Predicate 字节码计算出的根必须等于该 UTXO 的 owner评估通过Predicate 的main()执行返回true。若 Predicate 在执行过程中 revert或试图访问不纯 VM 操作码评估结果自动为false交易将无法花费该 UTXO。花费条件Spending ConditionsPredicate 的能力不止于返回 true。它可以内省introspect当前交易读取正在花费其 coins 的交易信息包括输入inputs、输出outputs、脚本字节码等接收运行时参数predicateData在提交交易时附带的自定义数据。这两者或两者结合都可以影响 Predicate 的最终评估结果。例如一个多签 Predicate 可以检查交易中的签名数量是否达到阈值一个时间锁 Predicate 可以检查交易的date/time输入是否晚于某个时间点一个条件支付 Predicate 可以验证predicateData中携带的接收者地址是否与输出匹配。正因为 Predicate 可以看到整笔交易和运行时数据它才能实现灵活、可组合的花费策略。编写一个 Predicate完整示例官方文档给出与 Script 类似的写法Predicate 由一个main()函数组成可以接受任意数量的参数但必须返回bool。要使 Predicate 有效返回的布尔值必须为true。仓库中的示例位于 docs/reference/src/code/language/program-types/predicates/simple_predicate/src/main.swpredicate; // All predicates require a main function which return a Boolean value. fn main(amount: u64) - bool { true }要点解析predicate;文件第一行声明程序类型为 Predicate与contract;、script;、library;对应fn main(...) - bool唯一入口函数可接收任意数量、任意类型的参数返回值true本例无条件放行是最简单的白名单 Predicate实际项目中应在此处编写真实的花费条件逻辑。项目配置Forc.toml该示例的 Forc.toml 展示了 Predicate 项目的基本配置[project] authors [Fuel Labs contactfuel.sh] entry main.sw license Apache-2.0 name simple_predicate [dependencies] std { path ../../../../../../../../sway-lib-std }entry main.sw指定入口文件name simple_predicate为项目名也是后续计算 Predicate Root 时的标识基础[dependencies]引入std标准库路径以仓库根目录为基准本文示例使用相对路径引用本仓库的 sway-lib-std 标准库。一个带真实条件的 Predicate仓库测试目录中提供了更贴近实战的变体例如 test/src/e2e_vm_tests/test_programs/should_pass/language/predicate_while/src/main.swpredicate; fn main() - bool { let mut x 10; while x ! 0 { x - 1; } true }该示例演示了 Predicate 中可以使用循环等常规流程控制指令只要不触碰合约指令与不纯操作码。而 forc-test/test_data/test_predicate/src/main.sw 则展示了一个返回false的 Predicate 及其内联单元测试写法predicate; fn main() - bool { false } #[test] fn test_bam() { assert(1 1) } #[test] fn test_bum() { assert(1 1) }这也印证了官方文档的规则Predicate 的main()必须返回布尔值false即拒绝花费。端到端测试与验证仓库的端到端测试系统test/src/e2e_vm_tests为 Predicate 提供了完整的验证闭环。以基础 Predicate 测试 basic_predicate/src/main.sw 为例predicate; fn main() - bool { true }其对应的测试配置 test.toml 声明了期望结果category run expected_result { action return, value 1 } expected_result_new_encoding { action return, value 1 }category run表示该测试会实际运行 Predicate 字节码expected_result.action return、value 1表明期望 Predicate 正常返回且返回值为1即true验证其通过花费验证。这类测试与 forc-test 配合可以在本地对 Predicate 进行构建、运行和单元测试无需部署到任何网络。构建与使用 Predicate 的命令流程结合 forcFuel OrchestratorCLIPredicate 的典型工作流如下初始化项目forc new --predicate my_predicate创建带predicate;文件头的模板项目编写逻辑在src/main.sw中实现main()花费条件构建forc build编译为字节码计算 Predicate Root使用forc predicate-root见 forc/src/ops/forc_predicate_root.rs计算字节码根得到资产接收地址转账向该 Root 地址发送资产UTXO 的 owner 即为此 Predicate花费构造交易时附带 Predicate 字节码与predicateData运行时参数链上校验 Root 匹配且评估为true后放行。注意上述命令中的--predicate模板参数与forc predicate-root子命令以当前仓库 forc 的实际实现为准完整的命令列表可查阅 forc 命令文档 与 forc/src/cli/commands 源码。实战注意事项永远返回布尔值Predicate 的main()签名必须返回bool且只有true才能通过验证false、revert 或访问不纯指令都会被判定为无效不要在 Predicate 中依赖合约状态它既不能读也不能写合约状态需要状态信息时应通过交易输入inputs或predicateData传入Predicate Root 即地址理解链上只有根、没有字节码这一点有助于设计资金回收、升级与审计方案——字节码变更会改变 Root从而改变地址可以内省交易充分利用对 inputs/outputs/脚本字节码的读取能力实现多签、时间锁、条件支付等复杂策略组合使用Predicate 与 Script、Contract 可以组合例如由 Script 调用多个合约的同时用 Predicate 守护特定 UTXO 的花费权。小结Predicate 是 Sway 语言中用于表达 UTXO 花费条件的专用程序类型无需部署、只在交易期间存在、不能访问合约状态与合约指令、评估失败即拒绝。通过Predicate Root 匹配 评估为true的双重校验机制开发者可以在 Fuel 上构建多签钱包、时间锁、条件支付等无需智能合约状态即可运行的资产控制方案。仓库中的 simple_predicate 示例、e2e 测试 与 forc-test 夹具 提供了从编写、配置到验证的完整参照可直接作为新 Predicate 项目的起点。【免费下载链接】sway Empowering everyone to build reliable and efficient smart contracts.项目地址: https://gitcode.com/GitHub_Trending/sw/sway创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
上一篇/下一篇内容由系统自动关联 返回资讯列表 →