Các topic ZMQ

TensorCash kế thừa nguyên vẹn giao diện pub/sub ZMQ của Bitcoin Core. Publisher bind (mặc định tcp://127.0.0.1:28332); subscriber kết nối và đăng ký theo tiền tố topic. Mỗi thông báo là một thông điệp multipart ba frame, và cấu trúc body của từng topic được liệt kê bên dưới.

5 topic · nguồn gốc: inherit

Trang này trình bày cơ chế pub/sub kế thừa từ Bitcoin Core. Kênh kiểm chứng từ node tới verifier là một luồng ZMQ PUSH/PULL riêng, có payload dùng Schema FlatBuffer: proof.fbs, validation.fbs và blockheader.fbs.

Cấu trúc frame

Publisher binds; subscribers connect and SUBSCRIBE to topic prefixes. Topic strings are matched as byte prefixes — SUBSCRIBE "hash" receives both hashblock and hashtx.

KhungTrườngKiểuMô tả
0topicutf-8 stringOne of the topic names below.
1bodyper-topicDefined by the topic. See frames[1] per topic.
2sequence_counteruint32 little-endianMonotonically increasing per-topic counter. Subscribers detect dropped messages by checking for gaps. Resets on bcore restart.

Topic

hashblock

-zmqpubhashblock=<address>

Best-block-tip changed (new block connected to the active chain).

A block is connected to the main chain. Fires once per UpdatedBlockTip callback.

KhungTrườngKiểuMô tả
1block_hash32 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.

Mã nguồn: services/core-node/bcore/src/zmq/zmqpublishnotifier.cpp:228

hashtx

-zmqpubhashtx=<address>

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.

KhungTrườngKiểuMô tả
1tx_hash32 bytes, little-endian (display-reversed)The transaction hash (txid).

Mã nguồn: services/core-node/bcore/src/zmq/zmqpublishnotifier.cpp:239

rawblock

-zmqpubrawblock=<address>

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).

KhungTrườngKiểuMô tả
1block_serializedvariable-length bytesThe block, serialized using Bitcoin Core's standard block serialization (CBlock::Serialize). Includes the header, tx count (var_int), and all transactions in order.

Mã nguồn: services/core-node/bcore/src/zmq/zmqpublishnotifier.cpp:252

rawtx

-zmqpubrawtx=<address>

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.

KhungTrườngKiểuMô tả
1tx_serializedvariable-length bytesThe transaction, serialized with Bitcoin Core's standard transaction serialization. Witness data included when present.

Mã nguồn: services/core-node/bcore/src/zmq/zmqpublishnotifier.cpp:261

sequence

-zmqpubsequence=<address>

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.

KhungTrườngKiểuMô tả
1hash + event_byte (+ optional mempool_seq)32-byte hash || 1-byte event || optional 8-byte LE mempool_sequenceBody 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.

Mã nguồn: services/core-node/bcore/src/zmq/zmqpublishnotifier.cpp:264

Ví dụ subscriber

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