Sway 合约如何用 storage namespace 注解避免存储槽位冲突?
【免费下载链接】sway🌴 Empowering everyone to build reliable and efficient smart contracts.项目地址: https://gitcode.com/GitHub_Trending/sw/sway
在 Sway 中编写合约时,存储槽位(storage slot)的键由变量名等位置信息哈希计算得出。当合约以加载代码的方式与其他合约共享同一存储空间时,两个合约里同名或同位置的存储变量可能落到同一个槽位上,互相覆盖对方的值。Sway 官方文档给出的对策是 storage namespace(存储命名空间):在storage块里声明一个具名命名空间块,编译器会为槽位键的计算加入 salt,让命名空间内的变量被定位到不同的存储位置。本文基于仓库中的文档与示例项目,走一遍从声明命名空间、读写其中变量,到编译验证的完整路径。
什么时候需要 storage namespace
Advanced Storage 文档 对 namespace 一节的原始表述是:如果你希望存储中的值定位到不同的位置——例如在加载代码时避免与另一个合约的存储发生冲突——可以使用 namespace 注解为槽位计算加入 salt。
也就是说,它的适用边界很明确:
- 目标不是改变存储 API,而是改变变量在存储中的定位;
- 典型场景是多份合约代码加载进同一个存储上下文时防止槽位互相覆盖。
参考文档 Namespace 章节 给出了槽位键的计算方式:位于命名空间my_namespace中的变量foobar,其位置由sha256("storage::my_namespace.foobar")的哈希计算决定。变量名被拼进了哈希输入,这正是命名空间起到隔离作用的机制。同一文档还说明:命名空间可以作用于storage块,也可以作用于放在命名空间内部的变量;一个storage块内可以顺序放置多个命名空间,也支持嵌套。
声明命名空间与声明变量的写法
按参考文档中的示例,最简声明如下:
storage { my_storage_namespace { var: u64 = 0, } }其中my_storage_namespace是命名空间名,var: u64 = 0是该命名空间内带初始值的存储变量。
命名空间内变量的读写
命名空间内变量通过storage::<命名空间>.<变量>路径访问。参考文档给出的读、写示例:
#[storage(read)] fn read() { let variable = storage::my_storage_namespace.var.read(); } #[storage(write)] fn write() { storage::my_storage_namespace.var.write(storage::my_storage_namespace.var.read() + 1); }访问存储读/写操作的函数需要对应的#[storage(read)]、#[storage(write)]注解,这也是该示例文件的原样写法。
一个完整的示例合约
仓库的 examples/storage_namespace 给出了完整的可编译合约,包含命名空间声明、ABI 与实现:
contract; use std::storage::storage_api::{read, write}; storage { example_namespace { foo: u64 = 0, }, } abi StorageNamespaceExample { #[storage(write)] fn store_something(amount: u64); #[storage(read)] fn get_something() -> u64; } impl StorageNamespaceExample for Contract { #[storage(write)] fn store_something(amount: u64) { storage.foo.write(amount); } #[storage(read)] fn get_something() -> u64 { storage.foo.try_read().unwrap_or(0) } }对应的 Forc.toml 依赖配置:
[project] authors = ["Fuel Labs <contact@fuel.sh>"] entry = "main.sw" license = "Apache-2.0" name = "storage_namespace" [dependencies] std = { path = "../../sway-lib-std" }注意std的path = "../../sway-lib-std"是相对仓库根目录布局写的;如果把这份示例拷到自己项目里,这个相对路径需要按你的实际 std 位置替换。另外两处访问写法可以对照看:参考文档使用storage::my_storage_namespace.var完整路径,而示例合约在 impl 中写的是storage.foo,两者都是仓库文档与示例中的原样用法。
构建与验证
最小验证路径是在项目目录内执行编译:
forc build编译通过即说明storage块、命名空间块与存储注解语法正确。
更深的验证可以参考仓库自带的 e2e 测试程序 test_contracts/storage_namespace。该合约在my_storage_namespace命名空间内声明了u64、str、u256、b256等多种变量,其test_storage()函数对每个变量做写入—回读—断言的往返检查,例如:
assert_eq(storage::my_storage_namespace.c1.read(), C1); storage::my_storage_namespace.c1.write(2); assert_eq(storage::my_storage_namespace.c1.read(), 2);测试入口#[test] fn call_test_storage_exhaustive()通过 ABI 调用合约的test_storage_exhaustive触发上述检查。这份测试程序也展示了命名空间内变量的标准访问形式:storage::<命名空间>.<变量>。
边界与限制
- namespace 只影响槽位键的哈希计算(给键加入命名空间 salt),不改变
read/write等存储 API 本身;命名空间内的变量依然按storage块的常规语法声明和访问。 - 一个
storage块内可以有多个命名空间顺序排列或嵌套(参考文档说明),但文档没有给出多命名空间场景下的额外示例,命名冲突的具体处理以编译器行为为准。 - 文档中关于 storage 的其余限制(例如数组尚不能在
storage块中声明,只能借助StorageMap<K, V>)属于 storage 块整体约束,与 namespace 无关,此处不展开;如需查阅见 Advanced Storage 文档 的 Manual Storage Management 一节。
【免费下载链接】sway🌴 Empowering everyone to build reliable and efficient smart contracts.项目地址: https://gitcode.com/GitHub_Trending/sw/sway
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考