Bitcoin Core 钱包迁移故障复盘:3 类根因、1 套 4 步修复 SOP 与零损失验收闭环
【免费下载链接】bitcoinBitcoin Core integration/staging tree项目地址: https://gitcode.com/GitHub_Trending/bi/bitcoin
Error: Error loading "main-wallet": Wallet is a legacy wallet. Please migrate to a descriptor wallet using the migration tool (migratewallet RPC). Error: Wallet decryption failed, the wallet passphrase was not provided or was incorrect20:00 的维护窗口里,migratewallet(Legacy 钱包迁移到 Descriptor 钱包的官方 RPC)第二次失败:加密钱包没有传口令,剪枝节点缺数据。本文基于 Bitcoin Core 源码与官方测试,带你完成三件事:10 秒对号入座 3 类迁移故障;按"前置 → 操作 → 预期 → 回退"执行修复;用基准测试加人工清单做零损失验收。
10 秒对号入座:4 种故障模式速查表
| 典型症状 | 关键日志 / 指标 | 归属类型 |
|---|---|---|
| 加密钱包迁移立刻报错 | Wallet decryption failed, the passphrase was not provided or was incorrect | 口令缺失 |
| 剪枝节点迁移中途失败 | last wallet synchronisation goes beyond pruned data. You need to -reindex | 数据不足 |
| 大钱包迁移卡死、CLI 断开 | 无输出,最终RPC error -8 / timeout | 单线程慢迁移 |
| 迁移成功但交易不全 | getwalletinfo的tx_count低于迁移前旧钱包 | 数据一致性 |
三条错误文案均出自仓库内真实断言,可在 test/functional/wallet_migration.py 与 test/functional/wallet_backwards_compatibility.py 中逐行检索到,不是猜测。
根因剖析
根因一:加密钱包的口令在迁移入口就被拒绝
现象:加密的 Legacy 钱包调用migratewallet立即返回 -4 错误。
复现:bitcoin-cli migratewallet "encrypted-wallet",不带passphrase参数。
证据链:src/wallet/rpc/wallet.cpp 第 591–651 行的migratewalletRPC 定义明确写着 "Encrypted wallets must have the passphrase provided as an argument to this call";test/functional/wallet_migration.py 第 563 行用错误口令做回归断言。注意:错误发生在迁移开始前,不产生任何脏数据,可安全重试。
根因二:剪枝节点上迁移被硬校验拦停
现象:剪枝(pruned,只保留近期区块以节省磁盘的节点)节点迁移到一定深度后失败。
复现:在启用prune=550的节点上迁移一个最后同步块早于保留区间的旧钱包。
证据链:test/functional/wallet_migration.py 第 1705 行的断言锁死了这条错误路径:last wallet synchronisation goes beyond pruned data。迁移需要读取旧钱包数据库记录的最后同步块,而剪枝节点已删除该区块数据,校验直接失败。
根因三:迁移是单线程长事务,客户端默认等不及
现象:大钱包(数万笔交易)迁移期间 CLI 长时间无响应,最终断开,留下"迁移是否完成"的悬案。
复现:对大 Legacy 钱包执行migratewallet,观察客户端超时。
证据链:src/wallet/rpc/wallet.cpp 第 601 行官方帮助文本直言 "This RPC may take a long time to complete. Increasing the RPC client timeout is recommended.";实现上整个迁移在单次 RPC 内完成(第 633 行调用MigrateLegacyToDescriptor,落点 src/wallet/wallet.cpp 第 4289 行)。仓库自带的微基准 src/bench/wallet_migration.cpp 以 500 把私钥、20 个 watch-only(只读监控)地址构造钱包并完整迁移一次,可作为"正常负载"的耗时锚点;你的真实钱包若比它大一个数量级,默认客户端超时大概率不够。
修复 SOP:4 步走
步骤 1:确认回滚底牌(所有后续步骤的前置)
前置校验:节点已停止,旧钱包在磁盘上完好。命令:检查wallets/目录下存在旧钱包数据库文件,并记下其大小:
# 记录旧钱包文件与大小,作为迁移前的基线 ls -lh ~/.bitcoin/wallets/main-wallet预期输出:文件名与旧钱包同名,大小非零。失败回退:文件缺失或为 0 字节,停止一切迁移操作,先找回备份;不要在损坏的源上迁移。
步骤 2:加密钱包带口令迁移
前置校验:已确认口令(在测试钱包或恢复流程上先验证一次)。命令:
# 迁移加密钱包,第二个参数即钱包口令 bitcoin-cli migratewallet "encrypted-wallet" "correct-passphrase"预期输出:JSON 返回wallet_name、backup_path(形如<名称>-<时间戳>.legacy.bak),如含只读脚本还会有watchonly_name。失败回退:报Wallet decryption failed时确认口令无误后原样重试——该错误不产生半成品;确认口令已遗忘则本路径走不通,需从 BIP 39(12/24 词助记词标准)种子恢复。
步骤 3:剪枝节点先补全数据再迁移
前置校验:bitcoin-cli getblockchaininfo确认pruned为 true。命令(先备份再改配置,顺序不可颠倒):
# 备份数据目录后取消剪枝并重新索引,补全历史区块 cp -r ~/.bitcoin/blockstore /backup/blockstore-$(date +%F) # bitcoin.conf: 注释掉 prune=550 bitcoind -reindex预期输出:-reindex完成后pruned变 false,区块完整。失败回退:磁盘空间不足(全量约 700 GB 级别)时放弃补数据,改用冷钱包流程:导出密钥后在全节点机器上迁移。
步骤 4:大钱包迁移与加载分离
前置校验:钱包规模明显大于基准锚点(500 钥 / 20 地址量级以上)。命令:
# 只迁移不加载,避免长事务卡死 RPC 客户端 bitcoin-cli migratewallet "big-wallet" "" false # 迁移落盘后单独加载 bitcoin-cli loadwallet "big-wallet"预期输出:migratewallet快速返回 JSON;随后loadwallet成功且getwalletinfo的tx_count与迁移前一致。失败回退:loadwallet失败或数量对不上时,用步骤 2/4 返回的backup_path执行restorewallet回滚到迁移前的 Legacy 状态(官方机制见 doc/managing-wallets.md "Migrating Legacy Wallets to Descriptor Wallets" 一节),禁止手工拼接.bak文件。
验收闭环
自动化:构建基准并跑迁移专项。
# 构建基准工具 cmake -B build -DBUILD_BENCH=ON && cmake --build build -j # 只跑钱包迁移基准 build/bin/bench_bitcoin -filter=WalletMigration -min-time=5000| 指标 | 达标标准 | 出处 |
|---|---|---|
| 迁移成功率 | 断言全过(res、wallet、watchonly_wallet非空) | src/bench/wallet_migration.cpp 第 96–101 行 |
| 单次迁移耗时 | 记录基线值,回归时不超过该值 ×1.5 | 本机-output-csv实测 |
| 功能回归 | 迁移相关用例全绿 | test/functional/wallet_migration.py |
人工:
| 检查项 | 验证命令 | 通过标准 |
|---|---|---|
| 钱包格式 | bitcoin-cli -rpcwallet="X" getwalletinfo | descriptor为true |
| 交易数量 | 对比迁移前后tx_count | 新 ≥ 旧,差值可由_solvables子钱包解释 |
| 备份存在 | ls wallets/*.legacy.bak | 有且仅有一个新备份 |
| 新备份 | 对迁移后钱包执行backupwallet | 生成不含.legacy.的新备份 |
复盘与防再发
- 先留底牌再动手:任何迁移前确认
wallets/下源文件完好并记录大小(步骤 1 已做成检查项)。 - 口令走参数不走猜:加密钱包迁移必须显式传
passphrase,在测试环境先跑通一次口令正确性。 - 剪枝节点不直接迁移:先
-reindex补数据或换全节点机器,避免pruned data校验拦腰斩。 - 大钱包拆两段:
load_wallet=false迁移 + 独立loadwallet,杜绝客户端超时造成"假死"悬案。 - 每次迁移后立即重做备份:官方文档强调迁移后必须新建备份,旧
.legacy.bak只是回滚件,不是长期备份。
migratewallet自 24.0.1 起进入实验期(见 doc/release-notes/release-notes-24.0.1.md),新选项load_wallet=false已在后续发布说明(doc/release-notes-35266)中落地;若遇到脚本被漏迁移的边界情况,按 doc/managing-wallets.md 的指引向社区提 issue,并附上.legacy.bak可复现的最小样本。
【免费下载链接】bitcoinBitcoin Core integration/staging tree项目地址: https://gitcode.com/GitHub_Trending/bi/bitcoin
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考