在描述符钱包里,keypoolsize 归零不一定是有事要修,也可能是无事发生——但 getnewaddress 报出 “Keypool ran out, please call keypoolrefill first” 时,就确实有事了。这一篇把密钥池的机制、补池的触发时机和三个故障场景一次讲清。
先校准结构。v31.0 的 keypoolrefill 文档写明:描述符钱包默认有 4 条活跃的范围描述符——legacy、p2sh-segwit、bech32、bech32m——每条默认 1000 个条目;keypoolrefill 接受一个可选的 newsize,默认 1000,语义是把每条描述符的池子补到这个数。注意”池”不是地址总数的上限:范围描述符的地址由索引推导,理论上取之不尽,池子只是”已经推导出密钥并写进钱包数据库”的那一段索引窗口。派生新地址时窗口内现取现用,用完则推进窗口。所以看到 keypoolsize 从 1000 缓慢降到 973,那不是故障倒计时,而是钱包在用预生成的余量,钱包在需要时会自己补。
补池在什么时候静默发生:getnewaddress、找零地址分配、以及钱包启动时都可能触发。补池失败最常见的两个原因:钱包锁定(加密钱包的密钥派生需要先解锁)与数据库不可写。报错文本已经自带处置建议——先 walletpassphrase 解锁,再手动 keypoolrefill。补完之后 keypoolsize 与 keypoolsize_hd_internal(内部找零密钥那池)应双双回升。
第二个场景:恢复备份后余额”少一截”。助记词或描述符导入后,钱包从记录的 birthtime 向后扫描链上历史,地址的发现依赖派生索引扫描窗口。如果你的收款地址当年是池子静默前推产生的、索引靠后,恢复扫描没盖住那段索引,就会出现”链上有钱、钱包说没有”。处置不是重新导入一遍(重复导入不会扩大搜索),而是明确补一段:用 importdescriptors 导入带显式 range 区间的描述符,或 deriveaddresses 手工验证目标索引段的地址确实属于这条派生树,再针对性导入。派生是确定性的,索引段是可补的,这是密钥池体系里最重要的安全网。
第三个场景值得警惕:把 -keypool 调小。参数帮助文本对此的警告很直白——较小的池子可能增加从旧备份恢复时丢币的风险,如果原始 keypool 里的地址一个都没用过。翻译成场景:你缩小了窗口、钱包只推导到索引 N 就停下,之后收到的款落进了索引 N+1 的地址(比如某些场景下地址由服务端先行展示),旧备份恢复时扫描不到 N+1,钱就”看起来”丢了。链上当然没丢,但你得靠 range 导入把它捞回来——而捞的前提是你还知道要找它。默认 1000 是维护者取的折中:池内密钥都在钱包数据库里占元数据行,调大钱包变胖、恢复扫描变慢;调小则前推余量薄。普通用户没有理由动它。
最后一个容易被忽略的点:keypool 与 HD 种子无关,与派生路径有关。同一句助记词在不同派生路径下生成的池子是完全不相干的两套地址;gethdkeys(v28 引入,用于列出钱包各描述符实际使用的 BIP32 密钥)能列出钱包各描述符使用的密钥与路径,排查”导入后地址对不上”时先看这个而不是先看余额。密钥池的一切行为,都建立在”钱包已经知道自己该往哪条树爬”的前提上——池子只决定爬多快,路径才决定爬哪棵树。顺带一句监控建议:keypoolsize_hd_internal 归零比外部池归零更值得警惕,因为找零地址的分配往往发生在用户毫无感知的转账过程中,内部池补不上会直接卡住造交易,而不是等到某次手动生成地址才暴露。
风险提示:命令行为与默认值以 Bitcoin Core v31.0 官方 RPC 文档为准,版本升级可能调整;恢复操作请先在小额或测试网演练,本文不构成投资建议。

发表评论
还没有评论,来说两句吧。
评论区为展示样式,提交不会被处理。