OpenHuman 本地多链加密钱包模块解析:密钥托管、prepare-confirm-execute 转账流与 Agent 工具面
【免费下载链接】openhumanOpenHuman is an open source personal AI for Mac, Windows and Linux — local-first memory, agent orchestration, and deep research.项目地址: https://gitcode.com/GitHub_Trending/op/openhuman
本文深入解析 OpenHuman(本地优先的开源个人 AI 助手)中由核心层自有的多链加密钱包模块 ——src/openhuman/web3/wallet。该模块承担密钥/账户管理与最基础的链上原语操作(原生转账与 ERC20/SPL/TRC20/BEP20 代币转账),是 swap、bridge、dapp 等高层 DeFi 能力的地基。读完本文,你将掌握该钱包的持久化与密钥托管机制、跨 EVM/BTC/Solana/Tron 四类链的 prepare→confirm→execute 转账流程、12+1 个 RPC 控制器与 6 个 Agent 工具的完整调用面,以及 quote 防劫持的线程归属设计原理。
模块定位:刻意保持"基础"的核心自有钱包
wallet是 OpenHuman 核心层直接拥有的本地多链加密钱包,其设计哲学是刻意基础(deliberately basic):只管密钥/账户管理加最原始的在链操作,不包含 swap、bridge 或通用合约调用等高层 DeFi 能力。这些高层能力由独立的web3模块提供,它构建在钱包的crate 内部签名原语sign_and_broadcast_evm/sign_and_broadcast_solana之上,并不属于钱包的 Agent / RPC 暴露面。
钱包覆盖的能力边界包括:
- 密钥/账户管理:加密恢复短语(recovery phrase)在 OS keychain 或 workspace JSON 中静态加密存储;
- 面向 Agent 的只读面:地址与余额查询、网络/资产目录、链就绪状态;
- 面向 Agent 的写操作:prepare→confirm→execute 三步式原生转账与代币转账;
- 交易广播与只读交易检查:状态(status)、回执(receipt)、原始交易查询(lookup);
- 链覆盖:EVM(Ethereum + Base/Arbitrum/Optimism/Polygon/BNB Chain)、Bitcoin(P2WPKH)、Solana(原生 + SPL)、Tron(原生 + TRC20)。
签名的安全边界非常明确:签名与广播都发生在核心进程内,由解密后的恢复短语派生密钥完成,任何私钥都不经过网络传输。
编译期特性门控(web3feature)
从 mod.rs 的模块结构可以看到,pub mod wallet;是始终编译的 facade,真正的实现子模块全部由默认开启的web3Cargo feature 门控(与openhuman::web3、openhuman::web3::x402共享同一特性开关)。当特性关闭时,stub接管并暴露相同签名的 no-op / 禁用错误实现,保证cargo check --no-default-features时调用方代码不漂移。
职责全景
README 明确列出该模块的 11 项核心职责,对应源码实现均可验证:
- 持久化 onboarding 状态:consent 标记、助记词词数、设置来源(生成/导入)、每条链恰好一个派生账户,以及加密恢复短语;
- 密钥托管优先级:加密助记词优先存 OS keychain,加载/保存时自动把秘密从
wallet-state.json迁入 keychain,无 keychain 的无头环境回退到 JSON; - 只读钱包信息:状态、各账户原生余额(EVM 实时读取,其余链由 provider 门控)、支持资产目录、各网络默认值(RPC/浏览器/能力标记)、逐链就绪状态;
- 预构建转账报价:经过校验、费用预估、带 TTL 的 prepared-quote,必须显式确认才能执行;
- 签名与广播:按链签名并广播已确认的 quote;失败时恢复 quote 并刷新 TTL 以保持可重试;
- quote 线程绑定:把每个 quote 绑定到发起它的聊天线程,防止共享频道中泄露的
quote_id被其他 Agent 会话劫持; - 双接口暴露:6 个 Agent 工具(
wallet_status、wallet_chain_status、wallet_prepare_transfer、wallet_tx_status、wallet_tx_receipt、wallet_lookup_tx)与 12 个wallet.*RPC 控制器; - crate 内部签名原语:
sign_and_broadcast_evm/sign_and_broadcast_solana供web3层签名广播外部构造的未签名交易,不暴露给 Agent / RPC 面。
关键文件地图
| 文件 | 职责 |
|---|---|
| mod.rs | 面向导出的模块根:模块文档、mod/pub use重导出、特性门控 facade |
| ops.rs(实现拆分于 ops_part_01.rs / ops_part_02.rs) | Onboarding 元数据 + 密钥持久化:WalletChain/WalletAccount/WalletStatus类型、setup/status、wallet-state.json原子写(临时文件 + fsync)、损坏状态隔离、keychain 加载/保存/迁移、validate_setup、crate 内部secret_material |
| execution.rs(实现拆分于 execution_part_01.rs / execution_part_02.rs) | 执行面:余额/网络默认值/支持资产/链状态读取、prepare_transfer/execute_prepared、tx_status/tx_receipt/lookup_tx、crate 内部sign_and_broadcast_evm/sign_and_broadcast_solana、内存 quote 存储(TTL、上限 64)、QuoteOwner聊天线程绑定、金额/地址/calldata 校验、费用预估、hex/u256 工具 |
| defaults.rs | EvmNetwork枚举(chain id、默认 RPC、浏览器基址、环境变量)、BTC/Solana/Tron 默认 RPC/REST URL、环境变量覆盖解析、逐链/逐网络资产目录 |
| abi.rs | encode_erc20_transfer—— 通过tinywallet_bus::abi编码transfer(address,uint256)calldata |
| schemas.rs | RPC 控制器 schema +handle_*分发器,委托给ops/execution;all_wallet_controller_schemas/all_wallet_registered_controllers |
| rpc.rs | 网络传输层(非 RPC 控制器):共享reqwest::Client、JSON-RPC POST(rpc_call、evm_rpc_call、rpc_call_to)、REST GET/POST 助手、日志 URL 脱敏 |
| transport.rs | 钱包原语Transport接缝的主机侧:端点解析、故障转移与 URL 脱敏 |
| tools.rs | 重导出tools/下的三个 Agent 工具结构体 |
| tools/status.rs | WalletStatusTool(wallet_status) |
| tools/chain_status.rs | WalletChainStatusTool(wallet_chain_status) |
| tools/prepare_transfer.rs | WalletPrepareTransferTool(wallet_prepare_transfer) |
| tools/tx_query.rs | WalletTxStatusTool/WalletTxReceiptTool/WalletLookupTxTool |
| chains/mod.rs | 逐链执行器命名空间,定义每链小型面(execute_*_quote、native_balance、validate_*_address) |
| chains/evm.rs | EVM 密钥派生(BIP-39)、EIP-1559/typed-tx 签名、eth_*余额/gas/广播 |
| chains/btc.rs | Bitcoin P2WPKH 派生/签名(bitcoincrate、secp256k1、BIP-32)+ Esplora REST 余额/广播 |
| chains/solana.rs | Solana ed25519 派生、原生 + SPL 转账、JSON-RPC 余额/广播(手写 wire format,不引入solana-sdk) |
| chains/tron.rs | Tron 派生/签名 + TronGrid REST 原生 + TRC20 转账 |
| test_support.rs | #[cfg(test)]共享设施:TEST_LOCK、setup_wallet_in(确定性 "abandon … about" 助记词)、逐链样本地址 |
密钥与状态持久化:keychain 优先、JSON 兜底
钱包的持久化状态分三处,各有明确职责:
wallet-state.json(非秘密元数据)
路径为{workspace_dir}/state/wallet-state.json,对应源码中的wallet_state_path()(ops_part_01.rs)。内容为StoredWalletState:consent_granted、source、mnemonic_word_count、accounts、updated_at_ms,以及仅作为兜底的encrypted_mnemonic字段。
写入采用严格的原子策略(save_stored_wallet_state_unlocked):
- 在目标目录创建
NamedTempFile; write_all写入序列化 JSON,随后sync_all刷盘;sync_parent_dir对父目录执行sync_all(确保目录项落盘);temp_file.persist(&path)原子替换;- 再次
sync_parent_dir。
整个过程由进程级全局锁WALLET_STATE_FILE_LOCK保护,防止多任务并发读写竞态。当文件不可读、JSON 解析失败或校验不通过时,文件会被**隔离(quarantine)**重命名为wallet-state.json.corrupted.<timestamp>(见quarantine_corrupted_wallet_state),而不是静默丢弃数据。
OS keychain(加密助记词的首选归宿)
keychain 条目键为wallet.mnemonic,并通过wallet_user_id()(取 workspace 目录的父目录名,兜底用 FNV-1a 哈希)按 workspace 作用域隔离(ops_part_01.rs)。加载与保存都会先检查 keyring 可用性与 keyring 同意策略(keyring_consent::policy::check_secret_access),不可用时优雅降级。
keychain 与 JSON 之间存在自动双向迁移逻辑(load_stored_wallet_state_unlocked步骤 3):
- keychain 有助记词且 JSON 仍带
encrypted_mnemonic→ 清除 JSON 字段并重写; - keychain 不可用但 JSON 带助记词 → 尝试提升(promote)到 keychain,成功则从 JSON 剥离,内存中保留值以便校验通过;
- 加载时还会重新探测(re-probe)一次 keychain,兼容并发任务写入尚未可见的场景。
保存路径同理:keychain 可用时助记词只写 keychain,JSON 中剥离秘密字段(save_stored_wallet_state_unlocked开头部分)。
内存 quote 存储(不跨重启)
PreparedTransaction存放在 execution_part_01.rs 的进程内QUOTE_STORE(Lazy<Mutex<Vec<PreparedTransaction>>>),TTL 为 5 分钟(QUOTE_TTL_MS = 5 * 60 * 1000),容量上限 64(QUOTE_STORE_CAP = 64),访问时惰性清理过期项。不持久化,进程重启后清空。
校验规则(validate_setup)
setup成功前必须通过严格校验(validate_setup):
consent_granted必须为true(显式同意,无默认值);- 助记词词数必须是
12/15/18/21/24之一; encrypted_mnemonic非空(本地可签名钱包的硬性要求);- 每个账户的
address与derivation_path非空; - 每条链恰好一个账户(EVM、BTC、Solana、Tron 各一)。
setup还有幂等保护:已配置钱包再次 setup 必须显式传force=true,否则报 "wallet is already configured; pass force=true to overwrite"。WalletSetupParams.force默认false,前端需二次确认后才可置真,防止静默覆盖。
四链一 EVM:网络、资产与派生路径
EVM 六网络的参数化
EVM 是单个WalletChain::Evm变体,覆盖 6 条网络,由EvmNetwork枚举选择(默认ethereum_mainnet),其他链忽略evmNetwork字段。从 defaults.rs 可得到完整参数表:
EvmNetwork变体 | chain id | 默认 RPC | 浏览器 tx 基址 | 环境变量覆盖 |
|---|---|---|---|---|
EthereumMainnet | 1 | https://ethereum-rpc.publicnode.com | etherscan.io/tx | OPENHUMAN_WALLET_RPC_EVM |
BaseMainnet | 8453 | https://mainnet.base.org | basescan.org/tx | OPENHUMAN_WALLET_RPC_BASE |
ArbitrumOne | 42161 | https://arb1.arbitrum.io/rpc | arbiscan.io/tx | OPENHUMAN_WALLET_RPC_ARBITRUM |
OptimismMainnet | 10 | https://mainnet.optimism.io | optimistic.etherscan.io/tx | OPENHUMAN_WALLET_RPC_OPTIMISM |
PolygonMainnet | 137 | https://polygon-rpc.com | polygonscan.com/tx | OPENHUMAN_WALLET_RPC_POLYGON |
BscMainnet | 56 | https://bsc-dataseed.binance.org | bscscan.com/tx | OPENHUMAN_WALLET_RPC_BSC |
RPC 解析逻辑(EvmNetwork::rpc_url()):优先读对应环境变量,非空则用之,否则回落默认 URL,并以RpcSource::EnvOverride/RpcSource::Default标注来源。这正是测试用 axum mock 指向自定义端点的机制。
BTC / Solana / Tron 默认端点
- BTC:Esplora REST
https://blockstream.info/api(可被OPENHUMAN_WALLET_RPC_BTC覆盖); - Solana:JSON-RPC
https://api.mainnet-beta.solana.com(devnet 为https://api.devnet.solana.com); - Tron:TronGrid REST
https://api.trongrid.io(可被OPENHUMAN_WALLET_RPC_TRON覆盖)。
Solana 还支持集群选择:环境变量OPENHUMAN_SOLANA_CLUSTER=devnet切到 Devnet(SolanaCluster::Devnet),同时驱动 RPC 端点与 USDC SPL mint 地址 —— mainnet mint 为EPjFWdd5...TDt1v,devnet mint 为4zMMC9s...ncDU,确保 devnet 上的 x402 支付挑战落链到正确的 mint。
派生路径(每条链一个确定性地址)
从 test_support.rs 的样本账户可确认各链的标准 BIP 派生路径:
| 链 | 路径 | 说明 |
|---|---|---|
| EVM | m/44'/60'/0'/0/0 | BIP-44,Ethereum 币种号 60,六条 EVM 网络共享同一地址 |
| Bitcoin | m/84'/0'/0'/0/0 | BIP-84,原生隔离见证 P2WPKH(bc1q…) |
| Solana | m/44'/501'/0'/0' | SLIP-0010 ed25519 |
| Tron | m/44'/195'/0'/0/0 | BIP-44,币种号 195 |
注意余额读取时的特例:EVM_BALANCE_NETWORKS常量(execution_part_02.rs)只把 Ethereum、Base、BNB Chain 三条网络作为独立余额行展示,单一派生 EVM 地址在这三条网络上分别读取原生资产(ETH/ETH/BNB)。
资产目录
内置资产目录按链/网络组织:每条 EVM 网络有默认 ERC-20 目录(含 BEP20),BTC/Solana/Tron 各有原生资产目录。supported_assets汇总输出,find_asset_for_network按链+网络+符号解析资产(asset_symbol缺省时取该链/网络的原生资产)。
执行面:prepare → confirm → execute 三步转账流
所有写操作遵循"先 prepare 报价、再显式确认执行"的安全模式。这是 execution.rs 的核心设计:任何写操作必须先 prepare,再显式 confirm;秘密材料始终保持加密静态存储。
第一步:prepare_transfer(报价)
PrepareTransferParams(camelCase JSON 反序列化):
{ "chain": "evm | btc | solana | tron", "toAddress": "目标地址", "amountRaw": "链最小单位整数(EVM 为 wei)", "assetSymbol": "可选,资产符号(缺省为原生资产)", "evmNetwork": "可选,仅 chain='evm' 时有效,缺省 ethereum_mainnet" }Agent 工具 prepare_transfer.rs 的参数 schema 与之一一对应,evmNetwork的枚举值为ethereum_mainnet/base_mainnet/arbitrum_one/optimism_mainnet/polygon_mainnet/bsc_mainnet。
预处理校验(execution_part_01.rs):
validate_address:按链校验目标地址,委托给 vendoredtinywallet_buscrate 的四种地址格式;validate_amount:解析为非负 u128 整数,拒绝 0 金额;- BTC 拒绝代币转账(
token transfers are not supported on Bitcoin); - 解析资产(native 或 token),生成
PreparedKind::NativeTransfer/TokenTransfer。
报价的estimated_fee_raw按链/类型估算(estimated_fee_raw,execution_part_01.rs):
| 链 × 类型 | 预估费用 |
|---|---|
| EVM 原生转账 | 21,000 gas × 30 Gwei = 630,000,000,000,000 wei |
| EVM 代币转账 | 65,000 gas × 30 Gwei |
| BTC | 5,000 sats |
| Solana | 5,000 lamports |
| Tron 原生 | 1,000,000 SUN |
| Tron TRC20 | 15,000,000 SUN |
(BTC 的实际费率常量在 btc.rs:DEFAULT_FEE_RATE_SAT_VB = 20sat/vB ×TYPICAL_TX_VBYTES = 141vbytes(单输入双输出 P2WPKH)。)
报价对象PreparedTransaction带quote_id(格式q_<毫秒时间戳>_<自增序号>)、created_at_ms、expires_at_ms(prepare 时刻 + 5 分钟)、owner(内部门控数据,不序列化上线)。
第二步:execute_prepared(确认 + 广播)
ExecutePreparedParams只有两个字段:quote_id与confirmed。执行前强制要求confirmed: true,否则直接报 "execute_prepared requiresconfirmed: true"。
执行路径的关键设计(execution_part_02.rs):
- 通过
current_owner()读取调用方的APPROVAL_CHAT_CONTEXT; - 原子取走 quote(
take_quote_for):在广播前先从存储中移除,使两个并发确认不可能同时通过get_quote造成重复提交(double-submit); - 按链分发到
execute_evm_quote/execute_btc_quote/execute_solana_quote/execute_tron_quote; - 失败时恢复 quote 并刷新 TTL(
expires_at_ms = now + QUOTE_TTL_MS),避免慢链调用(网络超时)耗尽原 5 分钟预算后交回一个立即过期的 quote;用户可修复原因后重试; - 成功则回填实际费用、置状态为
Broadcasted,若链执行器未返回 explorer URL 则用explorer_tx_url兜底生成。
第三步:链上执行器(四链签名广播)
EVM(chains/evm.rs)的签名广播流程完整可循:
eth_chainId读取链 ID,与目标EvmNetwork.chain_id()比对,不一致直接拒绝(防止 RPC 端点配置错误导致跨链误发);eth_getTransactionCount(状态"pending")取 nonce,避免连续两次确认时 nonce 碰撞;eth_estimateGas预估 gas,eth_gasPrice取 gas price;- 把交易规格与解密后的助记词交给
modules::wallet::sign_transaction_in_module在独立钱包模块中派生、编码、签名、组装; eth_sendRawTransaction广播,返回 tx hash 与模拟费用(gas × gasPrice)。
原生转账直接value=amount, data=空;ERC20 代币转账则value=0,data=encode_erc20_transfer(recipient, amount)(由 abi.rs 委托tinywallet_bus::abi编码 4 字节选择器 + 两个 32 字节字)。链 ID 校验失败时给出的错误形如EVM RPC chain_id mismatch: rpc reported X but network Y expects Z,具备明确的可诊断性。
Bitcoin(chains/btc.rs):Esplora REST 做 UTXO 发现(/address/{addr}/utxo)与余额(/address/{addr},confirmed + mempool 余额),/txPOST 广播原始交易。收款地址允许任意合法主网地址(P2WPKH/P2TR/P2SH 均可编码),但发送方地址强制 P2WPKH(validate_btc_sender_address),因为只支持 native segwit 派生签名。
Solana(chains/solana.rs):手写 wire format 组装交易(System Program 转账或 SPL token 转账,含 ATA 程序与 Token 程序 ID),不引入solana-sdk(其传递依赖约 150 个 crate)。余额走getBalanceJSON-RPC。
Tron(chains/tron.rs):TronGrid REST 的wallet/createtransaction(原生)与wallet/triggersmartcontract(TRC20),wallet/broadcasttransaction广播。TRC20 固定fee_limit15,000,000 SUN(TRC20_FEE_LIMIT_SUN)。节点返回的交易会经tinywallet_bus::tx::tron::verify_contract结构化校验并重算 txid 后才交给签名模块。
crate 内部签名原语(web3 层的地基)
sign_and_broadcast_evm/sign_and_broadcast_solana是钱包提供给web3模块的 crate 内部原语:对外部构造的未签名交易(如 deBridge swap/bridge 的 unsigned tx、dapp 的 calldata)做签名+广播。EVM 版本签名(to, data, value),Solana 版本签名 hex 编码的VersionedTransaction。它们不出现在 Agent 工具或 RPC 控制器中,从而保证私钥永不离开钱包域。
交易只读查询:tx_status / tx_receipt / lookup_tx
三个只读查询覆盖交易的全生命周期:
tx_status(chain, evmNetwork?, hash)→TxStatusInfo,归一化出四种TxState(execution_part_01.rs):Pending:节点已见但未打包;Confirmed:已打包且成功;Failed:已打包但回滚/失败;NotFound:节点无此 hash 记录。- EVM 实现区分逻辑(chains/evm.rs):先查
eth_getTransactionReceipt,receipt 为 null 时再用eth_getTransactionByHash区分 Pending 与 NotFound;有回执时读status字段判断成功与否,并用eth_blockNumber计算确认数。
tx_receipt(chain, evmNetwork?, hash)→TxReceiptInfo:成功标志、区块号、gas used、费用(fee_raw)、以及 provider 原始回执(raw透传)。lookup_tx(chain, evmNetwork?, hash)→TxLookupInfo:按 hash 查原始交易负载(raw透传)。
RPC 控制器与 Agent 工具:双面接口
RPC 命名空间wallet(方法形式openhuman.wallet_<function>)
README 列出 12 个控制器;当前 schemas.rs 实际注册了13 个(含reveal_recovery_phrase):
| 函数 | 用途 |
|---|---|
status | Onboarding 状态 + 安全账户元数据(地址) |
setup | 持久化 consent + 派生账户 + 加密助记词(全部输入必填) |
balances | 各账户原生资产余额(EVM 实时;其他链 provider 门控) |
network_defaults | 逐链 RPC/浏览器/能力标记 + 资产目录 |
supported_assets | 内置资产目录(含默认 EVM ERC-20 / BEP20) |
encode_erc20_transfer | 编码transfer(address,uint256)calldata(仅 EVM) |
chain_status | 逐链就绪状态 + 生效中 RPC URL |
prepare_transfer | 报价原生/代币转账(四条链) |
execute_prepared | 传confirmed: true确认并执行quoteId(发交易) |
tx_status | 查询交易生命周期状态(pending/confirmed/failed/not_found) |
tx_receipt | 查询交易回执(成功、费用、区块) |
lookup_tx | 按 hash 查询原始交易负载 |
reveal_recovery_phrase | 向用户披露恢复短语(RevealRecoveryPhraseResult) |
控制器通过all_wallet_registered_controllers注册,并在 src/core/all.rs 中接入注册表(含 schema 与 capability 描述)。
Agent 工具(6 个)
工具实现位于 tools/,由 tools.rs 重导出,全部实现crate::openhuman::tools::traits::Tool并委托给同名wallet::*函数:
WalletStatusTool→wallet_statusWalletChainStatusTool→wallet_chain_statusWalletPrepareTransferTool→wallet_prepare_transferWalletTxStatusTool→wallet_tx_statusWalletTxReceiptTool→wallet_tx_receiptWalletLookupTxTool→wallet_lookup_tx
注意:这里没有execute_prepared的 Agent 工具 —— 执行面仅通过 RPC 到达,Agent 只能准备/查询,不能绕过确认直接发交易。
工具的注册链路:src/openhuman/tools/ops.rs注册三个钱包工具,src/openhuman/agent/agents/loader.rs组装 Agent 工具集时引用,src/openhuman/test_support/introspect.rs在测试中做内省。此外 toolpacks/registry.rs 将 6 个钱包工具归入Web3域组,可被工具包(toolpack)按需 withhold 或授予。
Quote 归属绑定:防劫持设计
这是本模块最值得关注的安全设计。quote_id会随 prepared-tx 摘要回到共享聊天频道中,同频道的其他调用方可能读到别人的quote_id并尝试驱动其执行。为此:
- prepare 时通过
current_owner()(execution_part_01.rs)从 task-local 的APPROVAL_CHAT_CONTEXT读取thread_id+client_id,盖在 quote 的owner字段上; execute_prepared时再次读取调用方 owner,交给take_quote_for强制与 prepare 时 owner 相等(take_quote_for);- owner 不匹配时返回字节级相同的
quote '…' not found错误,与真正的未命中不可区分 —— 不存在枚举 oracle,泄露的quote_id无法被探测; - owner 检查先于状态/过期检查,且移除 quote 只发生在检查通过之后,误匹配的调用方无法"毒化"存储消费他人 quote;
- 非聊天调用方(CLI / 直接 JSON-RPC / 后台 cron)owner 为
None,只能执行同样无聊天上下文时准备的 quote —— 防止后台流程"捡走"交互用户的 quote 造成权限跌落。
对应测试在 execution_tests.rs:execute_prepared_rejects_cross_owner_execution验证跨 owner 拒绝且错误形如 not found;execute_prepared_owner_mismatch_error_matches_not_found_shape显式断言两种错误字节一致。
实现警示(README 明确标注):current_owner()依赖web_chat::run_chat_task内联.await链。tokio::task_local!能跨.await传播但不跨tokio::spawn—— 若聊天路径把工具循环拆到新 spawn 任务且未重新 scopeAPPROVAL_CHAT_CONTEXT,该门控会静默失效。
密钥安全边界与签名委托
- 恢复短语在 rest 态加密存储(keychain 或 JSON),派生前经
security::encryption::rpc::decrypt_secret解密; - 解密后的助记词交给钱包模块
modules::wallet::attested_proxy(构建设置固定的工件代理)签名,核心进程本身不持有待发交易的私钥; - EVM 广播前做
eth_chainId一致性校验,杜绝 RPC 端点错配造成的跨链误发; - 日志中的 RPC URL 统一脱敏为 scheme+host(
rpc.rs的 URL redaction); - 未配置钱包时的错误信息统一为常量
WALLET_NOT_CONFIGURED_MESSAGE = "wallet is not configured; run wallet setup first",下游边界把它归类为expected_user_state,不进 Sentry。
依赖关系与协作模块
crate::openhuman::config(Config、config::rpc::load_config_with_timeout):解析 workspace 目录与配置,用于状态路径、keychain user id 与解密;crate::openhuman::security::keyring(is_available/get/set):OS keychain 存储加密助记词;crate::openhuman::security::encryption::rpc(encrypt_secret/decrypt_secret):链签名器派生前解密恢复短语;crate::openhuman::security::approval::APPROVAL_CHAT_CONTEXT:task-local 聊天 owner(thread_id/client_id),用于 quote 线程绑定;crate::openhuman::tools::traits:Tool/ToolResult/ToolCallOptions;crate::core::all与crate::core:RPC 控制器注册表接线;crate::rpc::RpcOutcome:标准 RPC 返回形态;- 外部 crate:
ethers_core/ethers_signers/coins_bip39(EVM + ABI + BIP-39)、bitcoin+secp256k1(BTC)、ed25519_dalek(Solana)、sha2、hex、reqwest、serde/serde_json、tempfile、parking_lot、once_cell。
被引用方:src/openhuman/tools/ops.rs(注册 Agent 工具)、src/openhuman/agent/agents/loader.rs(组装工具集)、src/core/all.rs(接线控制器)、src/openhuman/test_support/introspect.rs(测试内省)。
测试与验证
- test_support.rs 提供确定性助记词
"abandon × 11 + about"(BIP-39 标准测试词),四条链各自派生出固定地址,配合setup_wallet_in在临时目录写入已配置钱包状态,所有链签名器测试可复现; - 逐链测试文件:
evm_tests.rs、btc_tests.rs、solana_tests.rs、tron_tests.rs、abi_tests.rs、defaults_tests.rs、rpc_tests.rs、transport_tests.rs、ops_tests.rs、execution_tests.rs、schemas_tests.rs、stub_tests.rs; - 测试通过
OPENHUMAN_WALLET_RPC_*环境变量把链端点指向 axum mock,验证余额、广播与错误路径。
已知约束与注意事项(gotchas)
rpc.rs是网络传输而非 RPC 控制器—— RPC 控制器在schemas.rs。这是对仓库"rpc.rs= 域 API"惯例的唯一例外,阅读代码时务必区分;- quote 原子消费:
take_quote_for在广播前移除 quote 防并发重复提交;失败恢复并刷新 TTL; - setup 强约束:恰好每链一个账户、词数 12/15/18/21/24、consent 必真、助记词非空;
- EVM 是 6 网络单变体:
EvmNetwork默认ethereum_mainnet,其他链忽略evmNetwork;BTC 拒绝代币转账;swap/bridge/合约调用不在钱包内,位于web3模块; - RPC 端点可覆盖:逐链/逐网络
OPENHUMAN_WALLET_RPC_*环境变量(测试借此指向 mock),日志 URL 脱敏为 scheme+host; balances的 provider 门控:仅 EVM(Ethereum mainnet)实时读取;BTC/Solana/Tron 调用 provider 失败时回退零值并标ProviderStatus::Missing;- 事件面为空:模块不发布/订阅任何
DomainEvent,无bus.rs;聊天上下文耦合通过 task-localAPPROVAL_CHAT_CONTEXT完成,而非事件总线; reveal_recovery_phrase的敏感性:披露恢复短语是高风险操作,README 未展开其授权细节,从代码看它返回RevealRecoveryPhraseResult { phrase, word_count },应在理解其风险边界后再接入上层 UI。
小结
src/openhuman/web3/wallet是一个刻意克制、安全边界清晰的核心层多链钱包:keychain 优先的密钥托管、原子化的状态持久化、prepare→confirm→execute 的强制两步确认流、quote 线程归属绑定防劫持、以及"Agent 只能准备/查询、执行仅走 RPC"的接口分工。它不追求 DeFi 功能的广度,而是把"密钥安全 + 转账原语 + 可审计的只读查询"做到扎实,并向上层web3模块提供 crate 内部签名原语,构成 OpenHuman 链上能力的信任地基。
【免费下载链接】openhumanOpenHuman is an open source personal AI for Mac, Windows and Linux — local-first memory, agent orchestration, and deep research.项目地址: https://gitcode.com/GitHub_Trending/op/openhuman
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考