Language

Choose a language

/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`);
}