scanblocks 加 getdescriptoractivity:不导入私钥的地址活动审计 图 1
scanblocks 加 getdescriptoractivity:不导入私钥的地址活动审计 · 图 1

一、不建钱包也能问出地址活动

比特币核心近年的版本里给了监控脚本一条捷径:getdescriptoractivity。它的分工很明确——你递给它一组区块哈希和一组描述符(可以带派生范围),它返回这些描述符在指定区块里花过什么、收过什么,逐条列出金额、交易编号、输入位置,甚至把被花掉的上一手输出脚本都还原给你。官方文档特意提醒:这个调用可能跑上几分钟,客户端要放宽 RPC 超时。

它的设计搭档是 scanblocks:后者用区块过滤器先扫出哪些区块可能碰过你的描述符,把一小串区块哈希交给前者细查。两步组合的妙处在于,你不必维护钱包文件、不必导入私钥、甚至不必持有完整交易索引——只要节点开着 blockfilterindex,一个纯观察用途的监控器就立起来了。这正是它与 scantxoutset 的分界:后者扫的是全历史 UTXO 快照、一次给全量余额,前者扫的是指定区块里的活动事件、面向增量对账。

scanblocks 加 getdescriptoractivity:不导入私钥的地址活动审计 图 2
scanblocks 加 getdescriptoractivity:不导入私钥的地址活动审计 · 图 2

二、参数里的三个坑

第一个坑在区块哈希列表:必须都在主链上,混进一个重组掉的旧哈希,整个调用直接报错而不是跳过。稳健写法是先从 getblockhash 按当前主链生成高度区间,别缓存上一次的哈希直接复用——重组之后缓存就是毒药。第二个坑在派生范围:文档默认探查前一千个地址索引,范围太大拖慢扫描,太小则漏掉高索引地址;用对象形式显式给范围,比依赖默认值诚实得多。第三个坑是内存池开关:默认把未确认活动也算进结果,对账逻辑若不区分确认与未确认,会把孤儿交易当成真收入。

返回结果的字段结构值得提前熟悉:每条形如一次 spend 或 receive 事件,spend 带 spend_txidspend_vinprevout_txidprevout_vout 两对坐标,receive 则给出新输出的金额与脚本归属。拿到事件后按 prevout 坐标反查原始交易,是所有审计脚本绕不开的第二跳。

三、和 rescanblockchain 的成本对账

同样的问题,钱包内置的重扫也能回答,两者的账法不同。rescanblockchain 是钱包自己的全量翻页:从指定高度起逐块检视脚本匹配,历史越长耗时越接近线性,好在它结果直接进钱包账本。getdescriptoractivityscanblocks 的组合则是过滤先行:绝大部分区块被过滤器一句话排除,只有命中区块进入逐笔核对,历史越长优势越明显——代价是结果只是临时报告,不落钱包账本,也不改钱包余额口径。

所以选型很清楚:恢复自有钱包的完整账目,用钱包重扫或描述符导入;搭一个不碰私钥的观察服务、或对一批外部地址做定向审计,用这对组合拳。两条路的隐私代价也不同:观察服务把”谁在关注哪些地址”暴露在节点管理员眼前,节点因此成了信任链的一环,这是托管审计绕不开的角色问题。

四、验收清单

上线前跑三组测试。第一组,regtest 上造一笔已确认收款与一笔花费,确认两类事件各出现一次且坐标互指正确。第二组,制造一次分叉:先记一笔收款进旧链尖,重组后重跑调用,确认旧哈希被拒或旧事件不再出现——这一步专门验证你对重组缓存的处理。第三组,用未确认交易验证 include_mempool 真假两分支,确认下游逻辑不重复计账。

最后提醒一层边界:这对组合依赖过滤器的完整性与节点同步状态,刚启动的节点上 scanblocks 可能报索引未就绪;而过滤器只保证脚本粗匹配,把粗匹配当终局判断的脚本,迟早被误报磨掉信任。另一层容易被忽略的是成本形态:过滤器扫描的开销随区块数量而非钱包大小增长,全历史扫描在机械硬盘上可以比预想慢一个数量级,增量巡检才是它的舒适区——把起点记在检查点文件里,每轮只看新增高度,才是这对命令被设计出来的用法。

风险提示:本文仅为节点 RPC 机制科普,不构成任何投资建议。