/build / zmq
ZMQ topics
TensorCash inherits Bitcoin Core's ZMQ pub/sub surface unchanged. The
publisher binds (default tcp://127.0.0.1:28332);
subscribers connect and SUBSCRIBE to topic prefixes. Every
notification is a 3-frame multipart message — the per-topic body
layout lives in the table below.
5 topics · spec_origin: inherit
Frame envelope
Publisher binds; subscribers connect and SUBSCRIBE to topic prefixes. Topic strings are matched as byte prefixes — `SUBSCRIBE "hash"` receives both `hashblock` and `hashtx`.
| Frame | Field | Type | Description |
|---|---|---|---|
| 0 | topic | utf-8 string | One of the topic names below. |
| 1 | body | per-topic | Defined by the topic. See `frames[1]` per topic. |
| 2 | sequence_counter | uint32 little-endian | Monotonically increasing per-topic counter. Subscribers detect dropped messages by checking for gaps. Resets on bcore restart. |
hashblock
-zmqpubhashblock=<address>
Semantics — Best-block-tip changed (new block connected to the active chain).
A block is connected to the main chain. Fires once per UpdatedBlockTip callback.
| Frame | Field | Type | Description |
|---|---|---|---|
| 1 | block_hash | 32 bytes, little-endian (display-reversed) | The connecting block's hash. Note Bitcoin Core's display convention reverses byte order — the wire bytes are little-endian; tooling that prints hex usually reverses them for display. |
Source: services/core-node/bcore/src/zmq/zmqpublishnotifier.cpp:228
hashtx
-zmqpubhashtx=<address>
Semantics — Transaction entered the mempool or was confirmed in a block.
Fires for every TransactionAddedToMempool and BlockConnected event; may emit duplicates for the same hash if a tx is re-broadcast or re-mined after a reorg.
| Frame | Field | Type | Description |
|---|---|---|---|
| 1 | tx_hash | 32 bytes, little-endian (display-reversed) | The transaction hash (txid). |
Source: services/core-node/bcore/src/zmq/zmqpublishnotifier.cpp:239
rawblock
-zmqpubrawblock=<address>
Semantics — Full serialized block on best-tip change.
Same trigger as hashblock — but the body is the full block, not just its hash. Heavier; only enable if a subscriber actually needs full block bytes (e.g. an indexer that does not have its own bcore peer).
| Frame | Field | Type | Description |
|---|---|---|---|
| 1 | block_serialized | variable-length bytes | The block, serialized using Bitcoin Core's standard block serialization (CBlock::Serialize). Includes the header, tx count (var_int), and all transactions in order. |
Source: services/core-node/bcore/src/zmq/zmqpublishnotifier.cpp:252
rawtx
-zmqpubrawtx=<address>
Semantics — Full serialized transaction.
Same trigger as hashtx — body is the full serialized transaction (CTransaction::Serialize). Subscribers can fully decode without round-tripping to JSON-RPC.
| Frame | Field | Type | Description |
|---|---|---|---|
| 1 | tx_serialized | variable-length bytes | The transaction, serialized with Bitcoin Core's standard transaction serialization. Witness data included when present. |
Source: services/core-node/bcore/src/zmq/zmqpublishnotifier.cpp:261
sequence
-zmqpubsequence=<address>
Semantics — Mempool + block connect/disconnect event stream.
Block-connect, block-disconnect, mempool-accept, and mempool-remove events. The body's `event` byte tells subscribers which type. Useful for indexers that need a single ordered event stream rather than the separate hashblock/hashtx topics.
| Frame | Field | Type | Description |
|---|---|---|---|
| 1 | hash + event_byte (+ optional mempool_seq) | 32-byte hash || 1-byte event || optional 8-byte LE mempool_sequence | Body layout depends on the event: • `'C'` (block connect) — hash || 'C' • `'D'` (block disconnect) — hash || 'D' • `'A'` (mempool accept) — hash || 'A' || mempool_sequence (uint64 LE) • `'R'` (mempool remove) — hash || 'R' || mempool_sequence (uint64 LE) The 32-byte hash is little-endian on the wire (same convention as the hash topics). `mempool_sequence` is a separate per-mempool counter — distinct from the topic-level sequence_counter in frame 2. |
Source: services/core-node/bcore/src/zmq/zmqpublishnotifier.cpp:264
Subscriber examples
Python (pyzmq)
import struct
import zmq
ctx = zmq.Context()
sock = ctx.socket(zmq.SUB)
sock.connect("tcp://127.0.0.1:28332")
sock.setsockopt(zmq.SUBSCRIBE, b"hashblock")
while True:
topic, body, seq_le = sock.recv_multipart()
(seq,) = struct.unpack("<I", seq_le)
# block_hash bytes are little-endian; reverse for display hex
display_hash = body[::-1].hex()
print(f"seq={seq} {topic.decode()}: {display_hash}") Node.js (zeromq.js)
import zmq from "zeromq";
const sock = new zmq.Subscriber();
sock.connect("tcp://127.0.0.1:28332");
sock.subscribe("rawtx");
for await (const [topic, body, seqLe] of sock) {
const seq = seqLe.readUInt32LE(0);
console.log(`seq=${seq} ${topic.toString()} ${body.length} bytes`);
}