
Bitcoin CoreexportwatchonlywalletRPC 详解:一键导出 Watch-Only 钱包文件【免费下载链接】bitcoinBitcoin Core integration/staging tree项目地址: https://gitcode.com/GitHub_Trending/bi/bitcoin本篇基于 Bitcoin Core 发布说明 release-notes-32489 展开,讲解新增的exportwatchonlywalletRPC 的完整用法、底层导出机制与安全边界。读完后,你将掌握如何从现有描述符钱包导出一份不含私钥的 watch-only 钱包文件,通过restorewallet在另一台节点上导入,并能在离线签名 在线观察场景中正确评估硬派生描述符的地址限制。功能概述:一条 RPC 替代手工描述符导入发布说明 release-notes-32489 对该功能的官方定义是:A newexportwatchonlywalletRPC creates a watchonly wallet file from an existing descriptor wallet. The exported file contains the wallets public descriptors (with derived key caches where needed), transactions, and address book data, but no private keys. It can be imported on another node using the existingrestorewalletRPC.即:该 RPC 从当前已加载的描述符钱包直接生成一个 watch-only 钱包文件,文件内包含:钱包的公钥描述符(需要时附带派生密钥缓存);钱包已知的交易记录;地址簿数据(标签、用途、收款请求等);以及钱包的持久化标记(如避免复用的锁定 UTXO)。不包含任何私钥。导出的文件可随后续有(或没有)网络的介质带到另一台节点,用既有restorewalletRPC 导入。此前的做法是用listdescriptors/importdescriptors手工搬运描述符并重建地址簿,这一流程在 离线签名教程 中已被更新为直接使用exportwatchonlywallet完成在线 watch-only 钱包的搭建。RPC 接口:参数、返回值与错误RPC 的定义位于 src/wallet/rpc/wallet.cpp,注册在walletRPC 组中:项目说明方法名exportwatchonlywallet参数destination(字符串,必填):导出的 watch-only 钱包文件的保存路径返回值对象,字段exported_file:文件实际导出到的完整路径执行前置锁定当前钱包(cs_wallet)并先调用TopUpKeyPool()补齐密钥池,再执行导出官方帮助中的示例:bitcoin-cli -rpcwallet钱包名 -named exportwatchonlywallet \ destination/path/to/export.dat帮助文本还特别说明了硬派生的限制(源码原文):Descriptors that use hardened derivation will only have a limited number of derived keys included in the export due to hardened derivation requiring private keys. Descriptors with unhardened derivation do not have this limitation.也就是说:含硬派生分叉(xprv.../0/*这类)的描述符,由于无法从公钥做硬派生,导出文件里只能携带有限数量的已派生密钥(即密钥缓存);非硬派生描述符则不存在此限制,可以在目标节点上自行扩展。从 功能测试 中可以确认两类典型报错,均返回 RPC 错误码-4(RPC_WALLET_ERROR):目标路径为空:Error: Export destination cannot be empty;目标已存在:Error: Export destination path already exists(导出不会覆盖已有文件);从没有任何描述符的空钱包(例如createwallet时blanktrue或仅disable_private_keystrue且无导入)导出:Error: Wallet has no descriptors to export。导出文件里到底有什么:逐项对照源码核心实现是 src/wallet/export.cpp 中的ExportWatchOnlyWallet函数(L46-L195)。按代码顺序,导出过程依次做了以下事情:校验目标路径:非空、不存在,且可以成功创建文件句柄,任一失败立即返回错误。登记失败清理:通过interfaces::MakeCleanupHandler注册回调——只要后续任一步骤失败,自动删除已创建的不完整导出文件,避免在磁盘上留下半成品钱包。导出描述符:调用ExportDescriptors(wallet, /*export_private*/false)(L18-L44),只取公钥描述符字符串。注意这里传入export_privatefalse,随后在解析回描述符对象时,源码还会断言解析出的临时 key 集合为空(dummy_keys.keys.size() 0,L96),即双重保证导出描述符中不含私钥。创建临时钱包:以原钱包标记 WALLET_FLAG_DISABLE_PRIVATE_KEYS作为标志,在内存数据库(MakeInMemoryWalletDatabase())中创建一个名为原钱包名_watchonly_temp的临时钱包(L74-L84)。选择在内存中操作,是为了失败时不会把临时文件遗留在磁盘上。逐条导入描述符:解析每个描述符并加回新钱包;对于不能自我扩展(缺少私钥或缓存)的描述符,从原钱包拷贝其派生密钥缓存(L108-L113)——这正是发布说明中with derived key caches where needed的出处。同时保留描述符的激活状态(active)与 internal 标记。在一个原子数据库事务中批量写入(L138-L185)。事务内拷贝的数据包括:orderPosNext(交易排序计数,保持listtransactions顺序一致);最佳区块定位器(best block locator):避免导入节点重新扫描区块;全部交易(mapWallet逐笔序列化写入);地址簿:每个条目的用途(purpose)、标签(label)、收款请求(receive requests)以及该地址曾被花费过的标记(previously_spent)。拷贝持久化的锁定 UTXO:只有persistenttrue的锁定项会被复制到 watch-only 钱包(临时锁定不跨文件保留)。落盘:最后调用BackupWallet把内存中的 watch-only 钱包写入destination,成功才将清理标记置为无需删除,并返回导出路径。由此可以整理出导出内容的完整清单:数据项是否导出依据描述符(仅公钥形式)是export.cpp L29-L41不可自扩展描述符的派生密钥缓存是(有限)export.cpp L108-L113钱包标志(含 avoid-reuse 等)是(叠加禁用私钥)export.cpp L74持久化锁定的 UTXO是export.cpp L132-L136交易历史是export.cpp L160-L169地址簿(用途/标签/收款请求/复用标记)是export.cpp L171-L180私钥否export_privatefalse 空 key 集合断言另外值得注意的是:加密且处于锁定状态的钱包也可以导出。因为导出全程只需要公开数据,功能测试test_encrypted_wallet专门验证了这一点——从未解锁的加密钱包导出的 watch-only 钱包既不含unlocked_until字段(没有私钥也就无需解锁),其listdescriptors结果却与源钱包完全一致(wallet_exported_watchonly.py L246-L261)。硬派生限制:地址池能生成多少地址?由于硬派生无法从公钥继续派生,导出文件只能携带已派生的密钥缓存。缓存里有多少地址,取决于导出时源钱包的密钥池规模。RPC 实现里先调用TopUpKeyPool()(wallet.cpp L946)再导出,意味着默认会把密钥池补到默认大小(-keypool,默认 1000)再冻结进导出文件。功能测试用一个-keypool10的节点量化了这一行为(L200-L203):对一条硬派生的sh(wpkh(xprv.../0/*))描述符,导入方连续生成KEYPOOL_SIZE - 1个新地址后,第 10 次getnewaddress(address_typep2sh-segwit)抛出No addresses available(错误码-12)。而非硬派生的描述符没有这个上限:测试中源钱包与导入钱包连续生成 2 倍密钥池大小的地址后,两边生成的地址仍然逐一对应(L84-L89)。实践建议:如果目标节点需要大量新地址且描述符含硬派生分叉,应在导出前用-keypool调大密钥池,或优先使用非硬派生描述符。实战工作流:离线签名场景离线签名教程 已改用exportwatchonlywallet替代了原先的手工描述符导入流程。在 signet 上的典型操作如下(引自教程原文):离线机创建带密码的私钥钱包:[offline]$ ./build/bin/bitcoin-cli -signet -named createwallet \ wallet_nameoffline_wallet \ passphrase** enter passphrase **离线机导出 watch-only 钱包文件(该文件通过 U 盘等介质转移到在线机):[offline]$ ./build/bin/bitcoin-cli -signet -rpcwalletoffline_wallet -named exportwatchonlywallet \ destination/path/to/watch_only_wallet.dat在线机用restorewallet导入为 watch-only 钱包:[online]$ ./build/bin/bitcoin-cli -signet -named restorewallet \ wallet_namewatch_only_wallet \ backup_file/path/to/watch_only_wallet.dat由于两个钱包共享同一组公钥描述符,它们生成的地址完全相同:在线 watch-only 钱包负责追踪入账、创建未签名 PSBT、广播已签名交易;真正的签名只能由离线钱包完成。功能测试对这条链路的端到端验证是:在线钱包send得到completefalse且返回psbt字段,离线钱包walletprocesspsbt签名后,在线节点finalizepsbtsendrawtransaction完成上链,且找零地址在两个钱包中都被识别为ismine(L63-L82)。此外,restorewallet导入后不会触发重新扫描:因为导出文件写入了源钱包的最佳区块定位器,测试中通过日志断言Rescanning last未出现(L138-L140)。前提是导出前两个节点区块高度一致;若在线节点落后,导入后仍可能自行触发扫描以追上链高。功能测试覆盖的行为矩阵wallet_exported_watchonly.py 是这条功能的验收测试,覆盖 7 组场景,可作为自查清单:测试函数验证内容test_basic_export基本导出/导入;错误路径报错;从 watch-only 钱包再导出 watch-only 也成立;两边listdescriptors一致;未签名 PSBT 协作转账;密钥池之外的地址两侧一致test_export_with_address_bookreceive/send 两类标签、getaddressesbylabel在两个钱包中一致test_export_with_txs_and_locked_coins交易逐笔一致、listtransactions顺序一致;持久锁定在两侧都在,临时锁定只在源钱包;导入不触发重扫test_export_imported_descriptors单密钥、硬派生、multisig、taproot、miniscript 等各类导入描述符均被正确导出,且各地址类型的收款地址逐一对应test_avoid_reuseavoid_reuse钱包标志及其地址曾被复用标记随导出保留test_encrypted_wallet锁定的加密钱包可导出,导出的 watch-only 钱包无解锁字段test_export_blank_wallet无描述符的空钱包导出报Wallet has no descriptors to export使用限制小结源必须是描述符钱包:实现只接受DescriptorScriptPubKeyMan类型的密钥管理器,遇到其他类型直接报错(export.cpp L22-L26);目标路径不能已存在,重复导出需换路径(测试对已存在路径断言报错);不导出私钥、不导出加密状态:导出结果永远是 watch-only 钱包,restorewallet导入后不能发起需签名的完整支付;硬派生描述符的地址生成上限等于导出时刻的派生缓存规模;临时锁定的 UTXO 不会跟随导出,只有persistenttrue的锁定会被拷贝;功能位于钱包 RPC 组,需以钱包已加载的节点上下文调用(-rpcwallet指定目标钱包),GUI 侧同样有对应的导出入口(参见 src/qt/bitcoingui.cpp 对ExportWatchOnlyWallet的调用)。综合来看,exportwatchonlywallet把搬运描述符 手工恢复地址簿/交易状态这一系列容易出错的步骤收敛为一条命令,并通过内存构建、原子事务、失败清理和无私钥断码这几道工程手段保证了导出文件的安全性与一致性,是搭建离线签名工作流时值得优先使用的入口 RPC。【免费下载链接】bitcoinBitcoin Core integration/staging tree项目地址: https://gitcode.com/GitHub_Trending/bi/bitcoin创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考