比特币节点的三种等待:waitfornewblock、waitforblock 与 waitforblockheight 图 1
比特币节点的三种等待:waitfornewblock、waitforblock 与 waitforblockheight · 图 1

写一个盯比特币节点的脚本,最常见的第一个需求是”出新块了告诉我”。拿 getblockcount 每秒轮询一遍是最直觉的做法,但它有两个毛病:大多数请求只是问了一句”还是老样子吗”,白白占用 RPC 工作队列;而且”查高度”和”用高度”之间永远有时间差,你拿到的高度可能在下一毫秒就被超越了。Bitcoin Core v31.1 提供了三个阻塞式 RPC,把等待交给节点自己完成。

三个命令各自等什么

  • waitfornewblock:等任意一个新区块成为链 tip。v31.1 的帮助文本里还有第二个可选参数 current_tip,可以把你已知的那个小费哈希传进去,让节点等”与它不同的 tip 出现”。帮助文本同时提醒:不传这个参数的模式下,如果两次查询之间链 tip 已经换过手,这个调用需要等到 tip 再变第二次才返回——想可靠地”接着上次的块继续等”,就把上次返回的哈希传进来。
  • waitforblock:等一个指定哈希的区块出现。适合你已经通过别的路径(比如 submitheader 或某个提议)知道某个块应该来,专门等它。
  • waitforblockheight:等链高度达到至少某个值。适合”追到 900000 块再开始干活”这类按高度推进的任务。

三者共用同一套参数语言:timeout 以毫秒计,缺省为 0,而 0 的语义是无限等待;传负数会直接报 Negative timeout。等不到也不报错——超时返回的是”当前这一块”,程序必须自己检查返回的高度或哈希,判断到底是等到了还是超时了,这是官方帮助明说的行为(“Returns the current block on timeout or exit.”)。

返回什么

v31.1 中三个调用返回同样的两个字段:hash(区块哈希)和 height(高度),没有别的附赠字段,解析器照这两格写就够了。

客户端自己的超时

三个命令的帮助文本都带同一句加粗式的提醒:“Make sure to use no RPC timeout (bitcoin-cli -rpcclienttimeout=0)“。这里的区别经常被混淆:RPC 服务端的 timeout 参数控制”等多块的更新”;而你自己的 HTTP 客户端(bitcoin-cli 也有)另有一个连接超时,控制”这次请求最多挂多久”。服务端愿意陪你等十分钟,客户端却三十秒就掐线,脚本就会拿到一个假性失败。长等待要么显式设 -rpcclienttimeout=0,要么把 timeout 参数控制在客户端预算之内。

它和推送式通知怎么选

节点还有另一族”新块来了”的机制:-blocknotify 在最佳块变化时执行你配的外部命令,ZMQ 推送(-zmqpubhashblock 等)把事件发到订阅端口。三者分工不同:notify 是节点主动找你的脚本,适合常驻集成;waitfor* 是你主动挂住一条 RPC 连接等答案,适合一次性脚本、调试和不想开额外监听端口的场合。用 waitfornewblock 写死循环,也能拼出一个简易的”准推送”服务。

一个最小可用的守候循环

把三个命令串起来,就是一个不带外部依赖的新块跟跑器:先取当前高度与哈希打底,循环里调 waitfornewblock,把上次返回的哈希作为 current_tip 传入;返回高度大于已知值就处理新区块,然后继续挂下一次等待。整个循环不产生任何轮询请求;节点关停时它会带着当前 tip 返回,脚本也能体面收尾。这个骨架适合喂日志、监控打点或触发下游任务;若需要交易级事件(每笔入池都收到通知),那是 ZMQ 推送的粒度,长轮询 RPC 不提供。

两个容易踩的坑提前说。其一,timeout 缺省为 0,而 0 的语义是无限等待——生产脚本务必传有限超时,否则对端节点一动不动,你的 RPC 连接就永远挂在那。其二,等特定哈希的 waitforblock 遇到重组没有魔法:如果等的那个块成了孤儿,它同样要等到超时才回,程序必须把返回了和等到了当成两件事来判断。

风险提示:以上接口行为以 v31.1 源码为准,升级大版本后请以对应版本的帮助文本复核;本文仅为技术说明,不构成投资建议。