ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Sway 如何用 [trivial] 属性消除合约调用的编码解码开销?

Sway 如何用 [trivial] 属性消除合约调用的编码解码开销? Sway 如何用 #[trivial] 属性消除合约调用的编码解码开销【免费下载链接】sway Empowering everyone to build reliable and efficient smart contracts.项目地址: https://gitcode.com/GitHub_Trending/sw/sway在 Sway 中当一个合约调用另一个合约时所有参数都会在实际执行调用前被编码而被调用方在目标方法开始前会解码这些参数。这个过程带来一笔虽小但不可忽略的 gas 开销——根据参数复杂程度从几百到几千 gas 不等。如果你的 ABI 方法参数恰好都是平凡trivial类型编译器可以完全跳过编码/解码步骤省掉这笔开销并简化生成代码。Sway 编译器会对一个类型子集做这种优化只要某个类型的运行时表示类型字节在 VM 内的布局与编码表示编码缓冲区中的字节布局完全一致它就可以被平凡编码或平凡解码——即把编码/解码替换成一次简单的 transmute。编译器可以分别跳过编码和解码但全部收益只有在两者同时被跳过时才会产生。本文基于当前仓库的 Trivial Encoding 文档、属性参考、实验特性文档 和测试程序说明如何给参数类型标注#[trivial]检查、如何让编译器替你验证 trivial 性以及bool、enum这类不可平凡解码类型的处理办法。哪些类型是 trivial 的对照下面的判定表决定哪些字段可以直接进 ABI类型平凡可编码平凡可解码说明bool✅❌bool编码为单个字节0或1但解码时必须校验该字节是合法值u8、u64、u256、b256✅✅u16、u32❌❌它们的运行时表示实际是u64结构体✅ 当所有成员都 trivial 时✅ 当所有成员都 trivial 时递归评估枚举✅ 当所有变体都 trivial 时❌枚举带有u64判别值无法平凡解码数组✅ 当元素类型 trivial 时✅ 当元素类型 trivial 时字符串数组str[N]✅ 见 *✅ 见 *见下文说明Vec、Dictionary、String等❌❌数据 structures 永远不是 trivial* 关于str[N]只有在实验特性str_array_no_padding开启时字符串数组才算 trivial特性关闭时只有长度是 8 的倍数的字符串数组才是 trivial源码依据见 codec.sw 中按#[cfg(experimental_str_array_no_padding true/false)]分开的两组AbiEncode实现。bool和enum不可平凡解码的原因值得理解缓冲区里不保证只会出现0/1比如出现2直接 transmute 成bool会产生未定义行为枚举同理它作为 tagged union 携带一个u64判别值缓冲区中不保证判别值有效。如何用 #[trivial] 属性开启检查按文档的写法把希望按平凡编解码处理的类型标注#[trivial]属性#[trivial(encode require, decode require)] pub struct SomeArgument { a: bool, b: SomeEnum, }两个参数的取值encode require——编译器检查该类型是否平凡可编码不满足则构建失败decode require——对解码做同样检查。可选值有三档required检查失败时报错、optional不满足时只发警告、any不做检查。该属性可以直接用在类型上也可以用在入口点上——script 和 predicate 的main函数、contract 的方法。注意文档与仓库测试代码的属性写法不一致。文档给出的是#[trivial(encode ..., decode ...)]取值required/optional/any而仓库测试程序 attribute_require 使用的是另一种形式#[require(trivially_decodable yes | as_warning | no)]。两种写法都出现在当前仓库中文档未说明二者关系实际使用时请以你所用编译器版本接受的写法为准先用最小例子验证编译通过。最小示例给 ABI 方法参数做 trivial 校验下面是一个最小的调用方/被调用方组合。先定义一个看起来 trivial的参数结构体并标注检查让编译器替你把关// 被调用合约contract_a.sw contract; #[trivial(encode require, decode require)] pub struct TransferArgs { pub amount: u64, pub nonce: u256, pub ref_hash: b256, } abi Transfer { fn apply(args: TransferArgs) - u64; } impl Transfer for Contract { fn apply(args: TransferArgs) - u64 { args.amount } }// 调用方合约contract_b.sw contract; use contract_a::{Transfer, TransferArgs}; abi Caller { fn make_call(); } const contract_id 0x79fa8779bed2f36c3581d01c79df8da45eee09fac1fd76a5a656e16326317ef0; impl Caller for Contract { fn make_call() { let transfer abi(Transfer, contract_id); let args TransferArgs { amount: 100, nonce: 1, ref_hash: 0x0 }; let result transfer.apply(args); result } }这里的contract_id是文档示例中的常量实际部署时替换为你的合约 ID调用形态abi(...)加const contract_id取自 Calling Contracts 文档。TransferArgs的字段全部来自平凡可编码且平凡可解码的类型集合因此标注检查后编译器应直接通过调用时该参数的编解码步骤会被跳过。验证方式就是直接构建forc build构建通过类型满足标注的 trivial 要求编译器已确认可以跳过编解码构建失败并出现 Type is not trivially encodable/decodable 一类诊断某个字段不满足要求按诊断提示修改类型。仓库测试快照 attribute_require/stdout.snap 中保留了真实的诊断输出文档示例可以看到编译器会精确到字段给出帮助例如error: Type is not trivially decodable -- .../src/main.sw:4:8 | 4 | struct MyStruct { | ^^^^^^^^ MyStruct is not trivially decodable. | -------- help: Consider changing MyStruct to make it trivially decodable. 5 | f0: bool, | -- info: This field is not trivially decodable. | ---- help: Consider changing this type to TrivialBool. ... 6 | f1: u8, ... 7 | f2: u16, | -- info: This field is not trivially decodable. | --- help: Consider changing this type to u64. ... 14 | f9: SomeEnum, | -- info: This field is not trivially decodable. | -------- help: Consider wrapping this type like TrivialEnumSomeEnum.以及只发警告的模式as_warningwarning: Type is not trivially decodable -- .../src/main.sw:27:8 | 27 | struct MyStructOnlyWarning { | ------------------- MyStructOnlyWarning is not trivially decodable. 28 | #[allow(dead_code)] 29 | a: u32 | - info: This field is not trivially decodable. | --- help: Consider changing this type to u64.这两段都是文档示例来自仓库测试快照实际输出中的类型名、字段名会换成你自己代码里的内容诊断里help:给出的建议改成u64、用TrivialBool包、用TrivialEnum...包与判定表一致。bool 和 enum 作为公开参数怎么办如果 ABI 方法必须暴露bool或 enum有两条文档给出的路径。路径一手动校验——不直接暴露bool改为暴露原始u64或u8在被调用方自己检查取值#[trivial(encode require, decode require)] pub struct Flag(u8); // 手动校验 value 1路径二使用 std 库的包装类型——sway-lib-std 提供TrivialBool和TrivialEnumT在编译期强制取值范围同时让编译器仍把它们视为 trivial。用法类似Optionbooluse sway::codec::TrivialBool; use sway::codec::TrivialEnum; #[trivial(encode require, decode require)] pub struct SomeArgument { a: TrivialBool, b: TrivialEnumSomeEnum, }取值时用unwrap()对 enum 还有is_valid()两者定义见 codec.swlet a: bool some_argument.a.unwrap(); let b: SomeEnum some_argument.b.unwrap();包装类型本身自带范围校验TrivialBool内部存u64is_valid()只接受0/1unwrap()遇到其他值会 revert对应 error_signals.sw 中的错误信号TrivialEnum::unwrap()会查判别值是否在合法变体表内不合法同样 revert。仓库的 trivial_helpers 测试 展示了用#[test(should_revert)]固化这个运行时行为的写法文档示例enum SomeEnum { A: u64, B: u16, } fn encode_decode(s: SomeEnum) - SomeEnum { let bytes encode(s); abi_decode::TrivialEnumSomeEnum(bytes).unwrap() } #[test] fn unwrap_trivial_variant() { let _ encode_decode(SomeEnum::A(1)); } #[test(should_revert)] fn unwrap_non_trivial_variant() { let _ encode_decode(SomeEnum::B(2)); }用forc test跑这两个用例合法变体解码成功非法变体如预期 revert。注意文档与源码冲突文档同时声称 std 附带TrivialVecT, N包装但当前仓库的 codec.sw 中没有TrivialVec的定义仓库里引用它的测试快照也是编译失败的。因此本文不使用TrivialVec的任何写法需要定长数组参数时按判定表直接用元素类型 trivial 的原生数组如[u64; N]。字符串数组参数的边界条件如果你的参数包含str[N]先确认str_array_no_padding实验特性的状态特性开启时字符串数组整体 trivial关闭时仅长度是 8 的倍数的str[N]是 trivial。实验特性的开关方式有三种优先级为环境变量 CLI Forc.toml依据 实验特性文档# Forc.toml[project] 段内 [project] experimental { str_array_no_padding true }# CLI 方式 forc build --experimental str_array_no_padding # 关闭已默认开启的特性 forc build --no-experimental str_array_no_padding# 环境变量方式 FORC_EXPERIMENTALstr_array_no_padding forc build FORC_NO_EXPERIMENTALstr_array_no_padding forc build切换特性只改变str[N]的 trivial 判定不影响本文其他步骤。适用边界小结收益成立的前提是参数类型同时平凡可编码和解码只满足其一时编译器只跳过对应方向。bool、u16、u32永远不可平凡解码enum 永远不可平凡解码Vec、Dictionary、String等数据结构永远不是 trivial。属性检查失败的行为取决于取值档位强制档构建失败宽松档只有警告——不要把强制档当成建议它是硬约束。#[trivial]文档形式与#[require(trivially_decodable ...)]仓库测试形式两种写法并存具体可用哪种以当前编译器版本为准。文档对 gas 节省的表述是从几百到几千 gas取决于参数复杂度仓库未提供具体的节省数值不要按固定比例估算。完整背景可继续读 Trivial Encoding 文档 与 Calling Contracts 文档后者说明合约调用整体机制与 fallback 行为。【免费下载链接】sway Empowering everyone to build reliable and efficient smart contracts.项目地址: https://gitcode.com/GitHub_Trending/sw/sway创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表