importdescriptors 参数逐格拆:时间戳、区间与 active 的边界 图 1
importdescriptors 参数逐格拆:时间戳、区间与 active 的边界 · 图 1

描述符钱包时代,往比特币核心里搬地址只剩一扇门:importdescriptors。命令只有一条,参数却环环相扣——时间戳决定重扫的起点,active 决定这类地址以后归谁签发,range 与 next_index 决定派生推进的位置。v31.0 源码里这条命令的帮助文本与实现逻辑加起来不到百行,把规则读全并不困难,难的是很多人只看了参数表就动手,结果要么扫了三年区块,要么找零地址悄悄换了归属。

请求是一批,动作是一次重扫

命令接收一个对象数组,每个对象导入一条描述符;响应是等长数组,逐条给出 success、可选的 warnings 与 error。一个容易忽略的批量语义:多条描述符一起提交时,重扫只按其中最早的那个时间戳触发一次,帮助文本明确写到”会按所有被导入描述符中最早时间戳触发重扫”,且扫描起点还会再向前放宽两小时,内存池也会被过一遍。批量导入能省掉重复扫描,混着不同年代的历史描述符时尤其划算。

importdescriptors 参数逐格拆:时间戳、区间与 active 的边界 图 2
importdescriptors 参数逐格拆:时间戳、区间与 active 的边界 · 图 2

timestamp:三种填法三种代价

时间戳是必填项,类型可以是整数或字符串。填 0 表示从创世扫起,全链逐块过一遍;填具体秒值表示从那个时刻对应的区块开始;填字符串 now 则借用当前同步链尖的时刻,效果近似跳过扫描——帮助文本给的用法就是”对已知从未用过的输出填 now”。实现里还有个下限钳制:早于 1 的时间戳会被抬到 1。要记住的连带效应是,导入历史时间戳后,钱包档案里的 birthtime 会被拉到更早(getwalletinfo 里可见),之后再想”只扫新段”就不是导入参数能控制的事了。帮助文本同时警告:早时间戳的导入可能耗时超过一小时,期间其他 RPC 会看到密钥已就位而交易还没补齐的中间态;若开了 -blockfilterindex=1,重扫会显著提速。

range、next_index 与 active:派生推进的三格

对带通配的派生描述符,range 参数指定导入哪一段索引,写成区间或直接给终点;next_index 只在这条描述符同时被标为 active 时有意义——它告诉钱包下一个要签发的地址索引是哪一格,恢复老钱包时填错它,轻则重复派生浪费索引,重则签出新账本对不上的地址。active 默认 false,含义是把这条描述符设为该输出类型与内外属性的活动签发者:钱包每一类输出只认一条活动描述符作为新地址的模板,导入新的 active 描述符等于让新模板接管签发,旧描述符退回旁观记账的位置;具体到与既有 active 描述符冲突时的告警与接管细节,以你运行版本的导入返回信息为准。

internal 与 label:归属与记账的约束

internal 标记匹配到的输出按找零而非收入记账,默认 false。label 只允许配在非 internal 的描述符上,且对带 range 的派生导入直接禁用——批量派生的每一格地址共用一个标签在账本上语义不清,源码选择了干脆不允许。另一条实现层的规则藏在帮助文本里:导入带多路径键表达式(multipath)的描述符时,若路径选择器恰有两个元素,第二个元素会被自动作为 internal 描述符导入——一次导入同时安置收付两条腿。

执行前后的硬前提

命令运行期间钱包必须保持解锁:实现里会持有防止中途回锁的互斥量,因为补密钥池要用口令派生;已有重扫在跑时会直接被拒,报”钱包正在重扫,中止现有扫描或等待”。帮助文本第一行还有一句最容易被跳过的话:导入会触发链重扫,并且需要重新做一次钱包备份——新描述符不在旧备份里,拿旧备份恢复就会假装这批地址从未存在。

风险提示:本文以比特币核心 v31.0 源码为基准,参数语义可能随版本调整,请以实际运行版本的命令帮助为准。内容为技术说明,不构成任何投资建议。