Inside Bitcoin
Opening the selected file…
Inside Bitcoin
Opening the selected file…
The code behind the network, with room to understand it.
Release published 2026-10-28 · Snapshot fetched 2026-11-01. Latest stable release at fetch time; development branches and Git history are excluded. Only the selected code window and visible folders load here.
1// Copyright (c) 2015-present The Bitcoin Core developers2// Distributed under the MIT software license, see the accompanying3// file COPYING or http://www.opensource.org/licenses/mit-license.php.5#include <zmq/zmqpublishnotifier.h>7#include <chain.h>8#include <crypto/common.h>9#include <netaddress.h>10#include <netbase.h>11#include <primitives/transaction.h>12#include <serialize.h>13#include <streams.h>14#include <uint256.h>15#include <util/check.h>16#include <util/log.h>17#include <zmq/zmqutil.h>19#include <zmq.h>21#include <cstdarg>22#include <cstddef>23#include <cstdint>24#include <cstring>25#include <map>26#include <optional>27#include <string>28#include <utility>29#include <vector>31static std::multimap<std::string, CZMQAbstractPublishNotifier*> mapPublishNotifiers;33static const char *MSG_HASHBLOCK = "hashblock";34static const char *MSG_HASHTX = "hashtx";35static const char *MSG_RAWBLOCK = "rawblock";36static const char *MSG_RAWTX = "rawtx";37static const char *MSG_SEQUENCE = "sequence";39// Internal function to send multipart message40static int zmq_send_multipart(void *sock, const void* data, size_t size, ...)41{42va_list args;43va_start(args, size);45while (1)46{47zmq_msg_t msg;49int rc = zmq_msg_init_size(&msg, size);50if (rc != 0)51{52zmqError("Unable to initialize ZMQ msg");53va_end(args);54return -1;55}57void *buf = zmq_msg_data(&msg);58memcpy(buf, data, size);60data = va_arg(args, const void*);62rc = zmq_msg_send(&msg, sock, data ? ZMQ_SNDMORE : 0);63if (rc == -1)64{65zmqError("Unable to send ZMQ msg");66zmq_msg_close(&msg);67va_end(args);68return -1;69}71zmq_msg_close(&msg);73if (!data)74break;76size = va_arg(args, size_t);77}78va_end(args);79return 0;80}82static bool IsZMQAddressIPV6(const std::string &zmq_address)83{84const std::string tcp_prefix = "tcp://";85const size_t tcp_index = zmq_address.rfind(tcp_prefix);86const size_t colon_index = zmq_address.rfind(':');87if (tcp_index == 0 && colon_index != std::string::npos) {88const std::string ip = zmq_address.substr(tcp_prefix.length(), colon_index - tcp_prefix.length());89const std::optional<CNetAddr> addr{LookupHost(ip, false)};90if (addr.has_value() && addr.value().IsIPv6()) return true;91}92return false;93}95bool CZMQAbstractPublishNotifier::Initialize(void *pcontext)96{97assert(!psocket);99// check if address is being used by other publish notifier100std::multimap<std::string, CZMQAbstractPublishNotifier*>::iterator i = mapPublishNotifiers.find(address);102if (i==mapPublishNotifiers.end())103{104psocket = zmq_socket(pcontext, ZMQ_PUB);105if (!psocket)106{107zmqError("Failed to create socket");108return false;109}111LogDebug(BCLog::ZMQ, "Outbound message high water mark for %s at %s is %d\n", type, address, outbound_message_high_water_mark);113int rc = zmq_setsockopt(psocket, ZMQ_SNDHWM, &outbound_message_high_water_mark, sizeof(outbound_message_high_water_mark));114if (rc != 0)115{116zmqError("Failed to set outbound message high water mark");117zmq_close(psocket);118return false;119}121const int so_keepalive_option {1};122rc = zmq_setsockopt(psocket, ZMQ_TCP_KEEPALIVE, &so_keepalive_option, sizeof(so_keepalive_option));123if (rc != 0) {124zmqError("Failed to set SO_KEEPALIVE");125zmq_close(psocket);126return false;127}129// On some systems (e.g. OpenBSD) the ZMQ_IPV6 must not be enabled, if the address to bind isn't IPv6130const int enable_ipv6 { IsZMQAddressIPV6(address) ? 1 : 0};131rc = zmq_setsockopt(psocket, ZMQ_IPV6, &enable_ipv6, sizeof(enable_ipv6));132if (rc != 0) {133zmqError("Failed to set ZMQ_IPV6");134zmq_close(psocket);135return false;136}138rc = zmq_bind(psocket, address.c_str());139if (rc != 0)140{141zmqError("Failed to bind address");142zmq_close(psocket);143return false;144}146// register this notifier for the address, so it can be reused for other publish notifier147mapPublishNotifiers.insert(std::make_pair(address, this));148return true;149}150else151{152LogDebug(BCLog::ZMQ, "Reusing socket for address %s\n", address);153LogDebug(BCLog::ZMQ, "Outbound message high water mark for %s at %s is %d\n", type, address, outbound_message_high_water_mark);155psocket = i->second->psocket;156mapPublishNotifiers.insert(std::make_pair(address, this));158return true;159}160}162void CZMQAbstractPublishNotifier::Shutdown()163{164// Early return if Initialize was not called165if (!psocket) return;167int count = mapPublishNotifiers.count(address);169// remove this notifier from the list of publishers using this address170typedef std::multimap<std::string, CZMQAbstractPublishNotifier*>::iterator iterator;171std::pair<iterator, iterator> iterpair = mapPublishNotifiers.equal_range(address);173for (iterator it = iterpair.first; it != iterpair.second; ++it)174{175if (it->second==this)176{177mapPublishNotifiers.erase(it);178break;179}180}182if (count == 1)183{184LogDebug(BCLog::ZMQ, "Close socket at address %s\n", address);185int linger = 0;186zmq_setsockopt(psocket, ZMQ_LINGER, &linger, sizeof(linger));187zmq_close(psocket);188}190psocket = nullptr;191}193bool CZMQAbstractPublishNotifier::SendZmqMessage(const char *command, const void* data, size_t size)194{195assert(psocket);197/* send three parts, command & data & a LE 4byte sequence number */198unsigned char msgseq[sizeof(uint32_t)];199WriteLE32(msgseq, nSequence);200int rc = zmq_send_multipart(psocket, command, strlen(command), data, size, msgseq, (size_t)sizeof(uint32_t), nullptr);201if (rc == -1)202return false;204/* increment memory only sequence number after sending */205nSequence++;207return true;208}210bool CZMQPublishHashBlockNotifier::NotifyBlock(const CBlockIndex *pindex)211{212uint256 hash = pindex->GetBlockHash();213LogDebug(BCLog::ZMQ, "Publish hashblock %s to %s\n", hash.GetHex(), this->address);214uint8_t data[32];215for (unsigned int i = 0; i < 32; i++) {216data[31 - i] = hash.begin()[i];217}218return SendZmqMessage(MSG_HASHBLOCK, data, 32);219}221bool CZMQPublishHashTransactionNotifier::NotifyTransaction(const CTransaction &transaction)222{223uint256 hash = transaction.GetHash().ToUint256();224LogDebug(BCLog::ZMQ, "Publish hashtx %s to %s\n", hash.GetHex(), this->address);225uint8_t data[32];226for (unsigned int i = 0; i < 32; i++) {227data[31 - i] = hash.begin()[i];228}229return SendZmqMessage(MSG_HASHTX, data, 32);230}232bool CZMQPublishRawBlockNotifier::NotifyBlock(const CBlockIndex *pindex)233{234LogDebug(BCLog::ZMQ, "Publish rawblock %s to %s\n", pindex->GetBlockHash().GetHex(), this->address);236std::vector<std::byte> block{};237if (!m_get_block_by_index(block, *pindex)) {238zmqError("Can't read block from disk");239return false;240}242return SendZmqMessage(MSG_RAWBLOCK, block.data(), block.size());243}245bool CZMQPublishRawTransactionNotifier::NotifyTransaction(const CTransaction &transaction)246{247uint256 hash = transaction.GetHash().ToUint256();248LogDebug(BCLog::ZMQ, "Publish rawtx %s to %s\n", hash.GetHex(), this->address);249DataStream ss;250ss << TX_WITH_WITNESS(transaction);
Independent explanations by MyBitcoinExplorer. Not affiliated with or endorsed by Bitcoin Core. Guides are educational and do not replace the source or its tests.
Bitcoin Core source © its contributors, distributed under the MIT license. Original notices and bundled third-party licenses are retained in the source tree.