ZMQ 토픽

TensorCash는 Bitcoin Core의 ZMQ pub/sub 인터페이스를 변경 없이 그대로 물려받았습니다. 퍼블리셔가 바인딩하고(기본값 tcp://127.0.0.1:28332), 구독자는 연결해 토픽 접두사를 구독합니다. 모든 알림은 프레임 세 개로 된 멀티파트 메시지이며, 토픽별 본문 구조는 아래에 정리되어 있습니다.

토픽 5개 · 출처: inherit

이 페이지는 Bitcoin Core에서 물려받은 pub/sub를 다룹니다. 노드-검증자 간 검증 채널은 별도의 ZMQ PUSH/PULL 스트림이며, 그 페이로드는 FlatBuffer 스키마를 사용합니다: proof.fbs, validation.fbs, blockheader.fbs.

프레임 엔벌로프

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

프레임필드타입설명
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.

토픽

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.

프레임필드타입설명
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.

소스: 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.

프레임필드타입설명
1tx_hash32 bytes, little-endian (display-reversed)The transaction hash (txid).

소스: 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).

프레임필드타입설명
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.

소스: 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.

프레임필드타입설명
1tx_serializedvariable-length bytesThe transaction, serialized with Bitcoin Core's standard transaction serialization. Witness data included when present.

소스: 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.

프레임필드타입설명
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.

소스: services/core-node/bcore/src/zmq/zmqpublishnotifier.cpp:264

구독자 예제

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