news 2026/9/10 18:52:00

fuels-rs 如何锁定已解锁钱包的签名器并区分 Wallet<Locked> 可用的操作边界

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
fuels-rs 如何锁定已解锁钱包的签名器并区分 Wallet<Locked> 可用的操作边界

fuels-rs 如何锁定已解锁钱包的签名器并区分 Wallet 可用的操作边界

【免费下载链接】fuels-rsFuel Network Rust SDK项目地址: https://gitcode.com/GitHub_Trending/fu/fuels-rs

在 fuels-rs 中,一个持有签名器(signer)的Wallet实例既能查询链上状态,也能签名消息和交易。当签名任务执行完毕,你希望把私钥从内存中移出,只保留“查余额、列币、看交易”这类只读能力。fuels-rs 用两个类型把这个边界固化在编译期:Wallet<Unlocked<S>>表示附带签名器的钱包,Wallet<Locked>表示只知道自己地址、无法签名的钱包。本文的目标是:把一个已解锁的钱包通过lock方法转换(或直接用地址构造)为Wallet<Locked>,并弄清转换前后各自可用哪些操作。

两种钱包状态及设计意图

Wallet<Unlocked<S>>Wallet<Locked>的区别(见 access.md):

  • Wallet<Unlocked<S>>代表附带签名器的钱包。凡是涉及签名消息或交易的操作,钱包必须是这个类型。
  • Wallet<Locked>代表没有签名器的钱包,它只知道自己的公开地址(public address)。Wallet<Locked>不能用于签名交易,但仍可执行一系列有用的操作,包括列出交易、列出资产、查询余额等。

官方给出的设计准则是:API 开发者应尽量减少使用Wallet<Unlocked<S>>的范围,确保签名器在内存中停留的时间不超过必要,以缩小下游库和应用被攻击或出漏洞的面。也就是说,lock不是“临时禁用”,而是把签名能力从类型上拿掉。

从源码 wallet.rs 可以看到实现逻辑:Wallet内部由state(泛型参数S)与provider组成。lock的实现是:

pub fn lock(&self) -> Wallet<Locked> { Wallet::new_locked(self.state.signer.address(), self.provider.clone()) }

签名器本身没有参与转换——只取出了它的地址,provider被克隆到新钱包。注意:lock不会销毁原Wallet<Unlocked<S>>,原变量仍是解锁状态;只有让原变量离开作用域(或显式丢弃),签名器才真正不再被持有。

从解锁钱包转到 Wallet

转换方法就是文档中给出的lock(来源 access.md 的 "Transitioning States" 一节):

let wallet_locked = wallet_unlocked.lock();

约束是wallet_unlocked必须实现S: Signer,即类型为Wallet<Unlocked<S>>。转换后wallet_locked的静态类型是Wallet<Locked>,从这一刻起它不再有signer()访问器(signer()只在 wallet.rs 的Wallet<Unlocked<S>>实现中定义)。

如果场景是“我只有某个地址,从未在本进程持有过签名器”,可以跳过lock,直接用地址构造Wallet<Locked>。e2e 测试 test_wallet_get_coins 展示了这条路径:

let addr = Address::zeroed(); let coins = setup_single_asset_coins(addr, AssetId::zeroed(), NUM_COINS, AMOUNT); let provider = setup_test_provider(coins, vec![], None, None).await?; let wallet = Wallet::new_locked(addr, provider.clone());

Wallet::new_locked(addr, provider)同样来自 wallet.rs。两条路径产出的对象类型一致,操作边界完全相同。

Wallet 可用的操作边界

边界由 trait 实现决定。在 wallet.rs 中:

  • Wallet<Locked>只实现了ViewOnlyAccount(见 locked 模块):提供address()try_provider(),以及经由get_spendable_resources组装输入的能力。ViewOnlyAccount是查询余额的通用接口(见 accounts.md)。
  • Wallet<Unlocked<S>>同时实现ViewOnlyAccountAccount,额外提供add_witnesses,这是交易构建时挂签名器的入口。转账类方法transferforce_transfer_to_contractwithdraw_to_base_layer属于Accounttrait(见 accounts.md 的 "Transferring assets" 一节)。

对照 index.md 的钱包分类说明,可以归纳成下表:

操作Wallet<Unlocked>Wallet
address()/try_provider()支持支持
查询余额(get_asset_balanceget_balances,见 checking-balances-and-coins.md)支持支持
列出币(get_coins支持支持
transferforce_transfer_to_contractwithdraw_to_base_layer支持不可用(类型上不存在)
签名消息 / 为交易构建器添加签名器支持不可用(类型上不存在)

对开发者来说,这意味着边界不是靠运行时报错,而是靠编译期拒绝:在Wallet<Locked>上调transfersigner()会直接编译失败。这正是 access 文档中“minimise their usage ofWallet<Unlocked<S>>”建议能落地的原因。

完整路径与验证方式

下面是一段把三种状态串起来的代码骨架,示例值取自 examples/wallets/src/lib.rs 中create_wallet_from_mnemonic与 e2e 测试test_wallet_get_coins

use fuels::prelude::*; // 1. 构造解锁钱包(含签名器) let provider = setup_test_provider(vec![], vec![], None, None).await?; let phrase = "oblige salon price punch saddle immune slogan rare snap desert retire surprise"; let key = SecretKey::new_from_mnemonic_phrase_with_path( phrase, fuels::accounts::signers::derivation::DEFAULT_DERIVATION_PATH, )?; let wallet_unlocked = Wallet::new(PrivateKeySigner::new(key), provider.clone()); // 2. 锁定:签名器从状态中移除,只保留地址与克隆的 provider let wallet_locked: Wallet<Locked> = wallet_unlocked.lock(); // 3. 只读操作在锁定钱包上可用 let asset_id = AssetId::zeroed(); let balance: u128 = wallet_locked.get_asset_balance(&asset_id).await?; let coins = wallet_locked.get_coins(asset_id).await?; // 4. 地址一致:锁定的钱包与解锁钱包指向同一账户 assert_eq!(wallet_locked.address(), wallet_unlocked.address());

setup_test_provider是 fuels-test-helpers 提供的测试 provider 构造器,上面的代码放在#[tokio::test]中运行;在连接真实节点的场景下,用你自己的 provider 替换这一行即可。

验证方式有三层:

  1. 编译期验证:在wallet_locked上尝试调用transfersigner(),预期编译失败。能编译通过的代码才是合法的只读路径。
  2. 地址一致性:如上例,lock只从签名器提取地址,wallet_locked.address()应等于原解锁钱包的地址。
  3. 只读查询可执行:e2e 测试 test_wallet_get_coins 用Wallet::new_locked构造的钱包调用了get_coins并断言返回的 coin 数量与总额,证明Wallet<Locked>的查询路径可用。该测试中的具体数值(NUM_COINS = 3AMOUNT = 1000)是测试自身的设定值,作为文档示例理解即可,不是通用预期值。

边界与限制

  • locknew_locked都不涉及私钥的销毁或存储管理:Wallet<Locked>只持有地址。如果你还需要“把私钥加密存盘、之后凭主密码取回”,那是另一条路径——Keystore::save_key/Keystore::load_key(见 keystore.md 与 examples/wallets/src/lib.rs 的create_and_store_mnemonic_key),与本文的运行时锁定相互独立。
  • 锁定后的钱包无法再恢复为解锁状态:Wallet<Locked>没有对应的unlock方法,重建Wallet<Unlocked<S>>需要重新提供签名器(私钥、助记词或 KMS)。
  • 签名器种类(PrivateKeySigner、AWS/Google KMS、Fake Signer,见 index.md)只影响解锁钱包的构造方式;lock之后两者行为一致,因为签名器信息已不可达。

【免费下载链接】fuels-rsFuel Network Rust SDK项目地址: https://gitcode.com/GitHub_Trending/fu/fuels-rs

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/10 18:50:38

Tigshop v5.8.14更新解析:搜店铺优化与跨境装修实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/10 18:50:33

Hive与HBase核心技术对比与大数据选型指南

1. 项目概述&#xff1a;Hive与HBase的技术定位差异第一次接触大数据生态的技术选型时&#xff0c;很多工程师都会困惑于Hive和HBase的选择。这就像装修时纠结该用实木地板还是瓷砖——两者都能解决地面铺设问题&#xff0c;但材质特性和适用场景截然不同。我在金融和电商行业的…

作者头像 李华
网站建设 2026/9/10 18:50:28

图像处理基础:格式、参数与应用场景解析

1. 图像处理基础概念解析 图像&#xff08;Image&#xff09;在计算机科学中是指由像素组成的二维矩阵&#xff0c;每个像素包含颜色和亮度信息。从技术角度看&#xff0c;图像可以分为矢量图和位图两大类。矢量图由数学公式描述的几何图形构成&#xff0c;放大不会失真&#x…

作者头像 李华
网站建设 2026/9/10 18:50:19

TelegramUI导航与布局组件:创建流畅Telegram应用界面的7个秘诀

TelegramUI导航与布局组件&#xff1a;创建流畅Telegram应用界面的7个秘诀 想要为你的Telegram Mini App打造专业级用户体验吗&#xff1f;TelegramUI作为React组件库&#xff0c;专门为Telegram应用界面设计&#xff0c;提供了丰富的导航与布局组件。本文将分享7个关键秘诀&a…

作者头像 李华
网站建设 2026/9/10 18:48:56

Requirements

Requirements 【免费下载链接】get-shit-done A light-weight and powerful meta-prompting, context engineering and spec-driven development system for Claude Code by TCHES. 项目地址: https://gitcode.com/GitHub_Trending/getshi/get-shit-done Validated &am…

作者头像 李华
网站建设 2026/9/10 18:48:48

Java私有构造器与抽象类的核心区别与应用场景

1. 从两个面试题说起最近在面试Java工程师时&#xff0c;我特别喜欢问两个看似简单却暗藏玄机的问题&#xff1a;"如果一个类只有私有构造器&#xff0c;它能被继承吗&#xff1f;""抽象类可以有构造器吗&#xff1f;如果有&#xff0c;为什么需要&#xff1f;&…

作者头像 李华