executing-plans
executing-plans
Use when executing an implementation plan in the current session as the implementer yourself — your human partner chose inline execution, or no subagent tool is available
bitcoin-core-zmqZeroMQ notifications from Bitcoin Core: rawblock, rawtx, hashblock, hashtx, sequence. Subscribe over TCP/IPC, integration patterns for Electrs, BTCPay, Lightning nodes. USE WHEN: building real-time integrations with bitcoind, monitoring mempool sequence, integrating LN nodes.
Independent GitHub snapshot · Automated classification · Original documentation governs
Bitcoin Core can publish events via ZeroMQ for low-latency, push-based integration. Beats polling RPC for real-time apps.
bitcoin.conf:
zmqpubrawblock=tcp://127.0.0.1:28332
zmqpubrawtx=tcp://127.0.0.1:28333
zmqpubhashblock=tcp://127.0.0.1:28334
zmqpubhashtx=tcp://127.0.0.1:28335
zmqpubsequence=tcp://127.0.0.1:28336
Each topic on its own port (or share — but separate is cleaner).
Bind address forms:
tcp://127.0.0.1:port — local-only.tcp://0.0.0.0:port — all interfaces (firewall it!).ipc:///tmp/socket.ipc — Unix socket, fast for local.inproc://... — same-process only (rarely useful from external client).| Topic | Payload | Notes |
|---|---|---|
rawblock | full block bytes | At every new tip block. |
rawtx | full tx bytes | Mempool admission + block inclusion. |
hashblock | 32-byte block hash | Lightweight tip notification. |
hashtx | 32-byte txid | Lightweight tx notification. |
sequence | txid + status byte | Mempool sequence (Add/Remove/...). |
All hashes on the wire (hashblock, hashtx, and the hash prefix of
sequence) are already in reversed byte order — the same order RPC
and block explorers display — and Core reverses them in
src/zmq/zmqpublishnotifier.cpp before sending. Hex-encode the bytes
as received. Reversing again yields a hash no RPC lookup will match.
When assumeutxo is in use, rawblock and hashblock are not issued
for historical blocks connected to the background validation
chainstate (documented in doc/zmq.md since Core 26.0, December
2023; still stated in v31.1, July 2026).
[32 bytes] hash
[1 byte] status: 'A' = mempool add
'R' = mempool remove (eviction or mined)
'C' = block connect (new tip)
'D' = block disconnect (reorg)
[8 bytes] mempool sequence number (only for A/R)
The sequence number is monotonic per node restart and lets clients detect missed messages.
Python (pyzmq):
import zmq
ctx = zmq.Context()
sock = ctx.socket(zmq.SUB)
sock.connect("tcp://127.0.0.1:28333")
sock.setsockopt_string(zmq.SUBSCRIBE, "rawtx")
while True:
topic, body, seq = sock.recv_multipart()
print(topic, len(body), int.from_bytes(seq, "little"))
The third frame is a 4-byte little-endian message counter (per topic since node start) — useful to detect drops.
Node (zeromq package):
const zmq = require("zeromq");
const sock = new zmq.Subscriber();
sock.connect("tcp://127.0.0.1:28332");
sock.subscribe("rawblock");
for await (const [topic, msg, seq] of sock) {
console.log(topic.toString(), msg.length);
}
set HighWaterMark) on subscriber to drop old
messages instead.LND, CLN, LDK use ZMQ to know when mempool / block events affect their channels:
rawblock → check if commitment tx confirmed.rawtx → fee bump opportunities.sequence → detect a counterparty broadcast quickly.Subscribes to rawblock + rawtx for invoice status updates.
Use hashblock for tip notification → trigger incremental update of
the index.
sock.subscribe("sequence")
seen = set()
while True:
topic, body, _ = sock.recv_multipart()
txid = body[:32].hex()
status = chr(body[32])
if status == "A": seen.add(txid)
elif status in ("R", "C"): seen.discard(txid)
sock.set(zmq.RCVHWM, 1000) to cap.getzmqnotificationsbitcoin-cli getzmqnotifications
Returns currently configured notification endpoints — useful for debugging "is my client listening on the right port?".
SUBSCRIBE to "" =
receive all from publisher) — gets blocks even if subscribed only
to "tx".hashblock as ordered with rawtx for the same tx that
just confirmed — they're independent topics.zmqpubrawblock=tcp://0.0.0.0:28332 and exposing to
internet — a malicious peer can drain bandwidth.