Jeff Garzik
9 years ago
23 changed files with 850 additions and 3 deletions
@ -0,0 +1,37 @@ |
|||||||
|
#!/usr/bin/env python2 |
||||||
|
|
||||||
|
import array |
||||||
|
import binascii |
||||||
|
import zmq |
||||||
|
|
||||||
|
port = 28332 |
||||||
|
|
||||||
|
zmqContext = zmq.Context() |
||||||
|
zmqSubSocket = zmqContext.socket(zmq.SUB) |
||||||
|
zmqSubSocket.setsockopt(zmq.SUBSCRIBE, "hashblock") |
||||||
|
zmqSubSocket.setsockopt(zmq.SUBSCRIBE, "hashtx") |
||||||
|
zmqSubSocket.setsockopt(zmq.SUBSCRIBE, "rawblock") |
||||||
|
zmqSubSocket.setsockopt(zmq.SUBSCRIBE, "rawtx") |
||||||
|
zmqSubSocket.connect("tcp://127.0.0.1:%i" % port) |
||||||
|
|
||||||
|
try: |
||||||
|
while True: |
||||||
|
msg = zmqSubSocket.recv_multipart() |
||||||
|
topic = str(msg[0]) |
||||||
|
body = msg[1] |
||||||
|
|
||||||
|
if topic == "hashblock": |
||||||
|
print "- HASH BLOCK -" |
||||||
|
print binascii.hexlify(body) |
||||||
|
elif topic == "hashtx": |
||||||
|
print '- HASH TX -' |
||||||
|
print binascii.hexlify(body) |
||||||
|
elif topic == "rawblock": |
||||||
|
print "- RAW BLOCK HEADER -" |
||||||
|
print binascii.hexlify(body[:80]) |
||||||
|
elif topic == "rawtx": |
||||||
|
print '- RAW TX -' |
||||||
|
print binascii.hexlify(body) |
||||||
|
|
||||||
|
except KeyboardInterrupt: |
||||||
|
zmqContext.destroy() |
@ -0,0 +1,26 @@ |
|||||||
|
package=zeromq |
||||||
|
$(package)_version=4.0.4 |
||||||
|
$(package)_download_path=http://download.zeromq.org |
||||||
|
$(package)_file_name=$(package)-$($(package)_version).tar.gz |
||||||
|
$(package)_sha256_hash=1ef71d46e94f33e27dd5a1661ed626cd39be4d2d6967792a275040e34457d399 |
||||||
|
|
||||||
|
define $(package)_set_vars |
||||||
|
$(package)_config_opts=--without-documentation --disable-shared |
||||||
|
$(package)_config_opts_linux=--with-pic |
||||||
|
endef |
||||||
|
|
||||||
|
define $(package)_config_cmds |
||||||
|
$($(package)_autoconf) |
||||||
|
endef |
||||||
|
|
||||||
|
define $(package)_build_cmds |
||||||
|
$(MAKE) -C src |
||||||
|
endef |
||||||
|
|
||||||
|
define $(package)_stage_cmds |
||||||
|
$(MAKE) -C src DESTDIR=$($(package)_staging_dir) install |
||||||
|
endef |
||||||
|
|
||||||
|
define $(package)_postprocess_cmds |
||||||
|
rm -rf bin share |
||||||
|
endef |
@ -0,0 +1,98 @@ |
|||||||
|
# Block and Transaction Broadcasting With ZeroMQ |
||||||
|
|
||||||
|
[ZeroMQ](http://zeromq.org/) is a lightweight wrapper around TCP |
||||||
|
connections, inter-process communications, and shared-memory, |
||||||
|
providing various message-oriented semantics such as publish/subcribe, |
||||||
|
request/reply, and push/pull. |
||||||
|
|
||||||
|
The Bitcoin Core daemon can be configured to act as a trusted "border |
||||||
|
router", implementing the bitcoin wire protocol and relay, making |
||||||
|
consensus decisions, maintaining the local blockchain database, |
||||||
|
broadcasting locally generated transactions into the network, and |
||||||
|
providing a queryable RPC interface to interact on a polled basis for |
||||||
|
requesting blockchain related data. However, there exists only a |
||||||
|
limited service to notify external software of events like the arrival |
||||||
|
of new blocks or transactions. |
||||||
|
|
||||||
|
The ZeroMQ facility implements a notification interface through a |
||||||
|
set of specific notifiers. Currently there are notifiers that publish |
||||||
|
blocks and transactions. This read-only facility requires only the |
||||||
|
connection of a corresponding ZeroMQ subscriber port in receiving |
||||||
|
software; it is not authenticated nor is there any two-way protocol |
||||||
|
involvement. Therefore, subscribers should validate the received data |
||||||
|
since it may be out of date, incomplete or even invalid. |
||||||
|
|
||||||
|
ZeroMQ sockets are self-connecting and self-healing; that is, connects |
||||||
|
made between two endpoints will be automatically restored after an |
||||||
|
outage, and either end may be freely started or stopped in any order. |
||||||
|
|
||||||
|
Because ZeroMQ is message oriented, subscribers receive transactions |
||||||
|
and blocks all-at-once and do not need to implement any sort of |
||||||
|
buffering or reassembly. |
||||||
|
|
||||||
|
## Prerequisites |
||||||
|
|
||||||
|
The ZeroMQ feature in Bitcoin Core uses only a very small part of the |
||||||
|
ZeroMQ C API, and is thus compatible with any version of ZeroMQ |
||||||
|
from 2.1 onward, including all versions in the 3.x and 4.x release |
||||||
|
series. Typically, it is packaged by distributions as something like |
||||||
|
*libzmq-dev*. |
||||||
|
|
||||||
|
The C++ wrapper for ZeroMQ is *not* needed. |
||||||
|
|
||||||
|
## Enabling |
||||||
|
|
||||||
|
By default, the ZeroMQ port functionality is enabled. Two steps are |
||||||
|
required to enable--compiling in the ZeroMQ code, and configuring |
||||||
|
runtime operation on the command-line or configuration file. |
||||||
|
|
||||||
|
$ ./configure --enable-zmq (other options) |
||||||
|
|
||||||
|
This will produce a binary that is capable of providing the ZeroMQ |
||||||
|
facility, but will not do so until also configured properly. |
||||||
|
|
||||||
|
## Usage |
||||||
|
|
||||||
|
Currently, the following notifications are supported: |
||||||
|
|
||||||
|
-zmqpubhashtx=address |
||||||
|
-zmqpubhashblock=address |
||||||
|
-zmqpubrawblock=address |
||||||
|
-zmqpubrawtx=address |
||||||
|
|
||||||
|
The socket type is PUB and the address must be a valid ZeroMQ |
||||||
|
socket address. The same address can be used in more than one notification. |
||||||
|
|
||||||
|
For instance: |
||||||
|
|
||||||
|
$ bitcoind -zmqpubhashtx=tcp://127.0.0.1:28332 -zmqpubrawtx=ipc:///tmp/bitcoind.tx.raw |
||||||
|
|
||||||
|
Each PUB notification has a topic and body, where the header |
||||||
|
corresponds to the notification type. For instance, for the notification |
||||||
|
`-zmqpubhashtx` the topic is `hashtx` (no null terminator) and the body is the |
||||||
|
hexadecimal transaction hash (32 bytes). |
||||||
|
|
||||||
|
These options can also be provided in bitcoin.conf. |
||||||
|
|
||||||
|
ZeroMQ endpoint specifiers for TCP (and others) are documented in the |
||||||
|
[ZeroMQ API](http://api.zeromq.org). |
||||||
|
|
||||||
|
Client side, then, the ZeroMQ subscriber socket must have the |
||||||
|
ZMQ_SUBSCRIBE option set to one or either of these prefixes (for instance, just `hash`); without |
||||||
|
doing so will result in no messages arriving. Please see `contrib/zmq/zmq_sub.py` |
||||||
|
for a working example. |
||||||
|
|
||||||
|
## Remarks |
||||||
|
|
||||||
|
From the perspective of bitcoind, the ZeroMQ socket is write-only; PUB |
||||||
|
sockets don't even have a read function. Thus, there is no state |
||||||
|
introduced into bitcoind directly. Furthermore, no information is |
||||||
|
broadcast that wasn't already received from the public P2P network. |
||||||
|
|
||||||
|
No authentication or authorization is done on connecting clients; it |
||||||
|
is assumed that the ZeroMQ port is exposed only to trusted entities, |
||||||
|
using other means such as firewalling. |
||||||
|
|
||||||
|
Note that when the block chain tip changes, a reorganisation may occur and just |
||||||
|
the tip will be notified. It is up to the subscriber to retrieve the chain |
||||||
|
from the last known block to the new tip. |
@ -0,0 +1,93 @@ |
|||||||
|
#!/usr/bin/env python2 |
||||||
|
# Copyright (c) 2015 The Bitcoin Core developers |
||||||
|
# Distributed under the MIT software license, see the accompanying |
||||||
|
# file COPYING or http://www.opensource.org/licenses/mit-license.php. |
||||||
|
|
||||||
|
# |
||||||
|
# Test ZMQ interface |
||||||
|
# |
||||||
|
|
||||||
|
from test_framework.test_framework import BitcoinTestFramework |
||||||
|
from test_framework.util import * |
||||||
|
import zmq |
||||||
|
import binascii |
||||||
|
from test_framework.mininode import hash256 |
||||||
|
|
||||||
|
try: |
||||||
|
import http.client as httplib |
||||||
|
except ImportError: |
||||||
|
import httplib |
||||||
|
try: |
||||||
|
import urllib.parse as urlparse |
||||||
|
except ImportError: |
||||||
|
import urlparse |
||||||
|
|
||||||
|
class ZMQTest (BitcoinTestFramework): |
||||||
|
|
||||||
|
port = 28332 |
||||||
|
|
||||||
|
def setup_nodes(self): |
||||||
|
self.zmqContext = zmq.Context() |
||||||
|
self.zmqSubSocket = self.zmqContext.socket(zmq.SUB) |
||||||
|
self.zmqSubSocket.setsockopt(zmq.SUBSCRIBE, "hashblock") |
||||||
|
self.zmqSubSocket.setsockopt(zmq.SUBSCRIBE, "hashtx") |
||||||
|
self.zmqSubSocket.connect("tcp://127.0.0.1:%i" % self.port) |
||||||
|
# Note: proxies are not used to connect to local nodes |
||||||
|
# this is because the proxy to use is based on CService.GetNetwork(), which return NET_UNROUTABLE for localhost |
||||||
|
return start_nodes(4, self.options.tmpdir, extra_args=[ |
||||||
|
['-zmqpubhashtx=tcp://127.0.0.1:'+str(self.port), '-zmqpubhashblock=tcp://127.0.0.1:'+str(self.port)], |
||||||
|
[], |
||||||
|
[], |
||||||
|
[] |
||||||
|
]) |
||||||
|
|
||||||
|
def run_test(self): |
||||||
|
self.sync_all() |
||||||
|
|
||||||
|
genhashes = self.nodes[0].generate(1); |
||||||
|
self.sync_all() |
||||||
|
|
||||||
|
print "listen..." |
||||||
|
msg = self.zmqSubSocket.recv_multipart() |
||||||
|
topic = str(msg[0]) |
||||||
|
body = msg[1] |
||||||
|
|
||||||
|
msg = self.zmqSubSocket.recv_multipart() |
||||||
|
topic = str(msg[0]) |
||||||
|
body = msg[1] |
||||||
|
blkhash = binascii.hexlify(body) |
||||||
|
|
||||||
|
assert_equal(genhashes[0], blkhash) #blockhash from generate must be equal to the hash received over zmq |
||||||
|
|
||||||
|
n = 10 |
||||||
|
genhashes = self.nodes[1].generate(n); |
||||||
|
self.sync_all() |
||||||
|
|
||||||
|
zmqHashes = [] |
||||||
|
for x in range(0,n*2): |
||||||
|
msg = self.zmqSubSocket.recv_multipart() |
||||||
|
topic = str(msg[0]) |
||||||
|
body = msg[1] |
||||||
|
if topic == "hashblock": |
||||||
|
zmqHashes.append(binascii.hexlify(body)) |
||||||
|
|
||||||
|
for x in range(0,n): |
||||||
|
assert_equal(genhashes[x], zmqHashes[x]) #blockhash from generate must be equal to the hash received over zmq |
||||||
|
|
||||||
|
#test tx from a second node |
||||||
|
hashRPC = self.nodes[1].sendtoaddress(self.nodes[0].getnewaddress(), 1.0) |
||||||
|
self.sync_all() |
||||||
|
|
||||||
|
#now we should receive a zmq msg because the tx was broadcastet |
||||||
|
msg = self.zmqSubSocket.recv_multipart() |
||||||
|
topic = str(msg[0]) |
||||||
|
body = msg[1] |
||||||
|
hashZMQ = "" |
||||||
|
if topic == "hashtx": |
||||||
|
hashZMQ = binascii.hexlify(body) |
||||||
|
|
||||||
|
assert_equal(hashRPC, hashZMQ) #blockhash from generate must be equal to the hash received over zmq |
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__': |
||||||
|
ZMQTest ().main () |
@ -0,0 +1,22 @@ |
|||||||
|
// Copyright (c) 2015 The Bitcoin Core developers
|
||||||
|
// Distributed under the MIT software license, see the accompanying
|
||||||
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
|
#include "zmqabstractnotifier.h" |
||||||
|
#include "util.h" |
||||||
|
|
||||||
|
|
||||||
|
CZMQAbstractNotifier::~CZMQAbstractNotifier() |
||||||
|
{ |
||||||
|
assert(!psocket); |
||||||
|
} |
||||||
|
|
||||||
|
bool CZMQAbstractNotifier::NotifyBlock(const uint256 &/*hash*/) |
||||||
|
{ |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
bool CZMQAbstractNotifier::NotifyTransaction(const CTransaction &/*transaction*/) |
||||||
|
{ |
||||||
|
return true; |
||||||
|
} |
@ -0,0 +1,42 @@ |
|||||||
|
// Copyright (c) 2015 The Bitcoin Core developers
|
||||||
|
// Distributed under the MIT software license, see the accompanying
|
||||||
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
|
#ifndef BITCOIN_ZMQ_ZMQABSTRACTNOTIFIER_H |
||||||
|
#define BITCOIN_ZMQ_ZMQABSTRACTNOTIFIER_H |
||||||
|
|
||||||
|
#include "zmqconfig.h" |
||||||
|
|
||||||
|
class CZMQAbstractNotifier; |
||||||
|
typedef CZMQAbstractNotifier* (*CZMQNotifierFactory)(); |
||||||
|
|
||||||
|
class CZMQAbstractNotifier |
||||||
|
{ |
||||||
|
public: |
||||||
|
CZMQAbstractNotifier() : psocket(0) { } |
||||||
|
virtual ~CZMQAbstractNotifier(); |
||||||
|
|
||||||
|
template <typename T> |
||||||
|
static CZMQAbstractNotifier* Create() |
||||||
|
{ |
||||||
|
return new T(); |
||||||
|
} |
||||||
|
|
||||||
|
std::string GetType() const { return type; } |
||||||
|
void SetType(const std::string &t) { type = t; } |
||||||
|
std::string GetAddress() const { return address; } |
||||||
|
void SetAddress(const std::string &a) { address = a; } |
||||||
|
|
||||||
|
virtual bool Initialize(void *pcontext) = 0; |
||||||
|
virtual void Shutdown() = 0; |
||||||
|
|
||||||
|
virtual bool NotifyBlock(const uint256 &hash); |
||||||
|
virtual bool NotifyTransaction(const CTransaction &transaction); |
||||||
|
|
||||||
|
protected: |
||||||
|
void *psocket; |
||||||
|
std::string type; |
||||||
|
std::string address; |
||||||
|
}; |
||||||
|
|
||||||
|
#endif // BITCOIN_ZMQ_ZMQABSTRACTNOTIFIER_H
|
@ -0,0 +1,24 @@ |
|||||||
|
// Copyright (c) 2015 The Bitcoin Core developers
|
||||||
|
// Distributed under the MIT software license, see the accompanying
|
||||||
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
|
#ifndef BITCOIN_ZMQ_ZMQCONFIG_H |
||||||
|
#define BITCOIN_ZMQ_ZMQCONFIG_H |
||||||
|
|
||||||
|
#if defined(HAVE_CONFIG_H) |
||||||
|
#include "config/bitcoin-config.h" |
||||||
|
#endif |
||||||
|
|
||||||
|
#include <stdarg.h> |
||||||
|
#include <string> |
||||||
|
|
||||||
|
#if ENABLE_ZMQ |
||||||
|
#include <zmq.h> |
||||||
|
#endif |
||||||
|
|
||||||
|
#include "primitives/block.h" |
||||||
|
#include "primitives/transaction.h" |
||||||
|
|
||||||
|
void zmqError(const char *str); |
||||||
|
|
||||||
|
#endif // BITCOIN_ZMQ_ZMQCONFIG_H
|
@ -0,0 +1,155 @@ |
|||||||
|
// Copyright (c) 2015 The Bitcoin Core developers
|
||||||
|
// Distributed under the MIT software license, see the accompanying
|
||||||
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
|
#include "zmqnotificationinterface.h" |
||||||
|
#include "zmqpublishnotifier.h" |
||||||
|
|
||||||
|
#include "version.h" |
||||||
|
#include "main.h" |
||||||
|
#include "streams.h" |
||||||
|
#include "util.h" |
||||||
|
|
||||||
|
void zmqError(const char *str) |
||||||
|
{ |
||||||
|
LogPrint("zmq", "Error: %s, errno=%s\n", str, zmq_strerror(errno)); |
||||||
|
} |
||||||
|
|
||||||
|
CZMQNotificationInterface::CZMQNotificationInterface() : pcontext(NULL) |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
CZMQNotificationInterface::~CZMQNotificationInterface() |
||||||
|
{ |
||||||
|
// ensure Shutdown if Initialize is called
|
||||||
|
assert(!pcontext); |
||||||
|
|
||||||
|
for (std::list<CZMQAbstractNotifier*>::iterator i=notifiers.begin(); i!=notifiers.end(); ++i) |
||||||
|
{ |
||||||
|
delete *i; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
CZMQNotificationInterface* CZMQNotificationInterface::CreateWithArguments(const std::map<std::string, std::string> &args) |
||||||
|
{ |
||||||
|
CZMQNotificationInterface* notificationInterface = NULL; |
||||||
|
std::map<std::string, CZMQNotifierFactory> factories; |
||||||
|
std::list<CZMQAbstractNotifier*> notifiers; |
||||||
|
|
||||||
|
factories["pubhashblock"] = CZMQAbstractNotifier::Create<CZMQPublishHashBlockNotifier>; |
||||||
|
factories["pubhashtx"] = CZMQAbstractNotifier::Create<CZMQPublishHashTransactionNotifier>; |
||||||
|
factories["pubrawblock"] = CZMQAbstractNotifier::Create<CZMQPublishRawBlockNotifier>; |
||||||
|
factories["pubrawtx"] = CZMQAbstractNotifier::Create<CZMQPublishRawTransactionNotifier>; |
||||||
|
|
||||||
|
for (std::map<std::string, CZMQNotifierFactory>::const_iterator i=factories.begin(); i!=factories.end(); ++i) |
||||||
|
{ |
||||||
|
std::map<std::string, std::string>::const_iterator j = args.find("-zmq" + i->first); |
||||||
|
if (j!=args.end()) |
||||||
|
{ |
||||||
|
CZMQNotifierFactory factory = i->second; |
||||||
|
std::string address = j->second; |
||||||
|
CZMQAbstractNotifier *notifier = factory(); |
||||||
|
notifier->SetType(i->first); |
||||||
|
notifier->SetAddress(address); |
||||||
|
notifiers.push_back(notifier); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (!notifiers.empty()) |
||||||
|
{ |
||||||
|
notificationInterface = new CZMQNotificationInterface(); |
||||||
|
notificationInterface->notifiers = notifiers; |
||||||
|
} |
||||||
|
|
||||||
|
return notificationInterface; |
||||||
|
} |
||||||
|
|
||||||
|
// Called at startup to conditionally set up ZMQ socket(s)
|
||||||
|
bool CZMQNotificationInterface::Initialize() |
||||||
|
{ |
||||||
|
LogPrint("zmq", "Initialize notification interface\n"); |
||||||
|
assert(!pcontext); |
||||||
|
|
||||||
|
pcontext = zmq_init(1); |
||||||
|
|
||||||
|
if (!pcontext) |
||||||
|
{ |
||||||
|
zmqError("Unable to initialize context"); |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
std::list<CZMQAbstractNotifier*>::iterator i=notifiers.begin(); |
||||||
|
for (; i!=notifiers.end(); ++i) |
||||||
|
{ |
||||||
|
CZMQAbstractNotifier *notifier = *i; |
||||||
|
if (notifier->Initialize(pcontext)) |
||||||
|
{ |
||||||
|
LogPrint("zmq", " Notifier %s ready (address = %s)\n", notifier->GetType(), notifier->GetAddress()); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
LogPrint("zmq", " Notifier %s failed (address = %s)\n", notifier->GetType(), notifier->GetAddress()); |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (i!=notifiers.end()) |
||||||
|
{ |
||||||
|
Shutdown(); |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
// Called during shutdown sequence
|
||||||
|
void CZMQNotificationInterface::Shutdown() |
||||||
|
{ |
||||||
|
LogPrint("zmq", "Shutdown notification interface\n"); |
||||||
|
if (pcontext) |
||||||
|
{ |
||||||
|
for (std::list<CZMQAbstractNotifier*>::iterator i=notifiers.begin(); i!=notifiers.end(); ++i) |
||||||
|
{ |
||||||
|
CZMQAbstractNotifier *notifier = *i; |
||||||
|
LogPrint("zmq", " Shutdown notifier %s at %s\n", notifier->GetType(), notifier->GetAddress()); |
||||||
|
notifier->Shutdown(); |
||||||
|
} |
||||||
|
zmq_ctx_destroy(pcontext); |
||||||
|
|
||||||
|
pcontext = 0; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void CZMQNotificationInterface::UpdatedBlockTip(const uint256 &hash) |
||||||
|
{ |
||||||
|
for (std::list<CZMQAbstractNotifier*>::iterator i = notifiers.begin(); i!=notifiers.end(); ) |
||||||
|
{ |
||||||
|
CZMQAbstractNotifier *notifier = *i; |
||||||
|
if (notifier->NotifyBlock(hash)) |
||||||
|
{ |
||||||
|
i++; |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
notifier->Shutdown(); |
||||||
|
i = notifiers.erase(i); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void CZMQNotificationInterface::SyncTransaction(const CTransaction &tx, const CBlock *pblock) |
||||||
|
{ |
||||||
|
for (std::list<CZMQAbstractNotifier*>::iterator i = notifiers.begin(); i!=notifiers.end(); ) |
||||||
|
{ |
||||||
|
CZMQAbstractNotifier *notifier = *i; |
||||||
|
if (notifier->NotifyTransaction(tx)) |
||||||
|
{ |
||||||
|
i++; |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
notifier->Shutdown(); |
||||||
|
i = notifiers.erase(i); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,35 @@ |
|||||||
|
// Copyright (c) 2015 The Bitcoin Core developers
|
||||||
|
// Distributed under the MIT software license, see the accompanying
|
||||||
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
|
#ifndef BITCOIN_ZMQ_ZMQNOTIFICATIONINTERFACE_H |
||||||
|
#define BITCOIN_ZMQ_ZMQNOTIFICATIONINTERFACE_H |
||||||
|
|
||||||
|
#include "validationinterface.h" |
||||||
|
#include <string> |
||||||
|
#include <map> |
||||||
|
|
||||||
|
class CZMQAbstractNotifier; |
||||||
|
|
||||||
|
class CZMQNotificationInterface : public CValidationInterface |
||||||
|
{ |
||||||
|
public: |
||||||
|
virtual ~CZMQNotificationInterface(); |
||||||
|
|
||||||
|
static CZMQNotificationInterface* CreateWithArguments(const std::map<std::string, std::string> &args); |
||||||
|
|
||||||
|
bool Initialize(); |
||||||
|
void Shutdown(); |
||||||
|
|
||||||
|
protected: // CValidationInterface
|
||||||
|
void SyncTransaction(const CTransaction &tx, const CBlock *pblock); |
||||||
|
void UpdatedBlockTip(const uint256 &newHashTip); |
||||||
|
|
||||||
|
private: |
||||||
|
CZMQNotificationInterface(); |
||||||
|
|
||||||
|
void *pcontext; |
||||||
|
std::list<CZMQAbstractNotifier*> notifiers; |
||||||
|
}; |
||||||
|
|
||||||
|
#endif // BITCOIN_ZMQ_ZMQNOTIFICATIONINTERFACE_H
|
@ -0,0 +1,172 @@ |
|||||||
|
// Copyright (c) 2015 The Bitcoin Core developers
|
||||||
|
// Distributed under the MIT software license, see the accompanying
|
||||||
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
|
#include "zmqpublishnotifier.h" |
||||||
|
#include "main.h" |
||||||
|
#include "util.h" |
||||||
|
|
||||||
|
static std::multimap<std::string, CZMQAbstractPublishNotifier*> mapPublishNotifiers; |
||||||
|
|
||||||
|
// Internal function to send multipart message
|
||||||
|
static int zmq_send_multipart(void *sock, const void* data, size_t size, ...) |
||||||
|
{ |
||||||
|
va_list args; |
||||||
|
va_start(args, size); |
||||||
|
|
||||||
|
while (1) |
||||||
|
{ |
||||||
|
zmq_msg_t msg; |
||||||
|
|
||||||
|
int rc = zmq_msg_init_size(&msg, size); |
||||||
|
if (rc != 0) |
||||||
|
{ |
||||||
|
zmqError("Unable to initialize ZMQ msg"); |
||||||
|
return -1; |
||||||
|
} |
||||||
|
|
||||||
|
void *buf = zmq_msg_data(&msg); |
||||||
|
memcpy(buf, data, size); |
||||||
|
|
||||||
|
data = va_arg(args, const void*); |
||||||
|
|
||||||
|
rc = zmq_msg_send(&msg, sock, data ? ZMQ_SNDMORE : 0); |
||||||
|
if (rc == -1) |
||||||
|
{ |
||||||
|
zmqError("Unable to send ZMQ msg"); |
||||||
|
zmq_msg_close(&msg); |
||||||
|
return -1; |
||||||
|
} |
||||||
|
|
||||||
|
zmq_msg_close(&msg); |
||||||
|
|
||||||
|
if (!data) |
||||||
|
break; |
||||||
|
|
||||||
|
size = va_arg(args, size_t); |
||||||
|
} |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
bool CZMQAbstractPublishNotifier::Initialize(void *pcontext) |
||||||
|
{ |
||||||
|
assert(!psocket); |
||||||
|
|
||||||
|
// check if address is being used by other publish notifier
|
||||||
|
std::multimap<std::string, CZMQAbstractPublishNotifier*>::iterator i = mapPublishNotifiers.find(address); |
||||||
|
|
||||||
|
if (i==mapPublishNotifiers.end()) |
||||||
|
{ |
||||||
|
psocket = zmq_socket(pcontext, ZMQ_PUB); |
||||||
|
if (!psocket) |
||||||
|
{ |
||||||
|
zmqError("Failed to create socket"); |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
int rc = zmq_bind(psocket, address.c_str()); |
||||||
|
if (rc!=0) |
||||||
|
{ |
||||||
|
zmqError("Failed to bind address"); |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
// register this notifier for the address, so it can be reused for other publish notifier
|
||||||
|
mapPublishNotifiers.insert(std::make_pair(address, this)); |
||||||
|
return true; |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
LogPrint("zmq", " Reuse socket for address %s\n", address); |
||||||
|
|
||||||
|
psocket = i->second->psocket; |
||||||
|
mapPublishNotifiers.insert(std::make_pair(address, this)); |
||||||
|
|
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void CZMQAbstractPublishNotifier::Shutdown() |
||||||
|
{ |
||||||
|
assert(psocket); |
||||||
|
|
||||||
|
int count = mapPublishNotifiers.count(address); |
||||||
|
|
||||||
|
// remove this notifier from the list of publishers using this address
|
||||||
|
typedef std::multimap<std::string, CZMQAbstractPublishNotifier*>::iterator iterator; |
||||||
|
std::pair<iterator, iterator> iterpair = mapPublishNotifiers.equal_range(address); |
||||||
|
|
||||||
|
for (iterator it = iterpair.first; it != iterpair.second; ++it) |
||||||
|
{ |
||||||
|
if (it->second==this) |
||||||
|
{ |
||||||
|
mapPublishNotifiers.erase(it); |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (count == 1) |
||||||
|
{ |
||||||
|
LogPrint("zmq", "Close socket at address %s\n", address); |
||||||
|
int linger = 0; |
||||||
|
zmq_setsockopt(psocket, ZMQ_LINGER, &linger, sizeof(linger)); |
||||||
|
zmq_close(psocket); |
||||||
|
} |
||||||
|
|
||||||
|
psocket = 0; |
||||||
|
} |
||||||
|
|
||||||
|
bool CZMQPublishHashBlockNotifier::NotifyBlock(const uint256 &hash) |
||||||
|
{ |
||||||
|
LogPrint("zmq", "Publish hash block %s\n", hash.GetHex()); |
||||||
|
char data[32]; |
||||||
|
for (unsigned int i = 0; i < 32; i++) |
||||||
|
data[31 - i] = hash.begin()[i]; |
||||||
|
int rc = zmq_send_multipart(psocket, "hashblock", 9, data, 32, 0); |
||||||
|
return rc == 0; |
||||||
|
} |
||||||
|
|
||||||
|
bool CZMQPublishHashTransactionNotifier::NotifyTransaction(const CTransaction &transaction) |
||||||
|
{ |
||||||
|
uint256 hash = transaction.GetHash(); |
||||||
|
LogPrint("zmq", "Publish hash transaction %s\n", hash.GetHex()); |
||||||
|
char data[32]; |
||||||
|
for (unsigned int i = 0; i < 32; i++) |
||||||
|
data[31 - i] = hash.begin()[i]; |
||||||
|
int rc = zmq_send_multipart(psocket, "hashtx", 6, data, 32, 0); |
||||||
|
return rc == 0; |
||||||
|
} |
||||||
|
|
||||||
|
bool CZMQPublishRawBlockNotifier::NotifyBlock(const uint256 &hash) |
||||||
|
{ |
||||||
|
LogPrint("zmq", "Publish raw block %s\n", hash.GetHex()); |
||||||
|
|
||||||
|
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); |
||||||
|
{ |
||||||
|
LOCK(cs_main); |
||||||
|
|
||||||
|
CBlock block; |
||||||
|
CBlockIndex* pblockindex = mapBlockIndex[hash]; |
||||||
|
|
||||||
|
if(!ReadBlockFromDisk(block, pblockindex)) |
||||||
|
{ |
||||||
|
zmqError("Can't read block from disk"); |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
ss << block; |
||||||
|
} |
||||||
|
|
||||||
|
int rc = zmq_send_multipart(psocket, "rawblock", 8, &(*ss.begin()), ss.size(), 0); |
||||||
|
return rc == 0; |
||||||
|
} |
||||||
|
|
||||||
|
bool CZMQPublishRawTransactionNotifier::NotifyTransaction(const CTransaction &transaction) |
||||||
|
{ |
||||||
|
uint256 hash = transaction.GetHash(); |
||||||
|
LogPrint("zmq", "Publish raw transaction %s\n", hash.GetHex()); |
||||||
|
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); |
||||||
|
ss << transaction; |
||||||
|
int rc = zmq_send_multipart(psocket, "rawtx", 5, &(*ss.begin()), ss.size(), 0); |
||||||
|
return rc == 0; |
||||||
|
} |
@ -0,0 +1,41 @@ |
|||||||
|
// Copyright (c) 2015 The Bitcoin Core developers
|
||||||
|
// Distributed under the MIT software license, see the accompanying
|
||||||
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
|
#ifndef BITCOIN_ZMQ_ZMQPUBLISHNOTIFIER_H |
||||||
|
#define BITCOIN_ZMQ_ZMQPUBLISHNOTIFIER_H |
||||||
|
|
||||||
|
#include "zmqabstractnotifier.h" |
||||||
|
|
||||||
|
class CZMQAbstractPublishNotifier : public CZMQAbstractNotifier |
||||||
|
{ |
||||||
|
public: |
||||||
|
bool Initialize(void *pcontext); |
||||||
|
void Shutdown(); |
||||||
|
}; |
||||||
|
|
||||||
|
class CZMQPublishHashBlockNotifier : public CZMQAbstractPublishNotifier |
||||||
|
{ |
||||||
|
public: |
||||||
|
bool NotifyBlock(const uint256 &hash); |
||||||
|
}; |
||||||
|
|
||||||
|
class CZMQPublishHashTransactionNotifier : public CZMQAbstractPublishNotifier |
||||||
|
{ |
||||||
|
public: |
||||||
|
bool NotifyTransaction(const CTransaction &transaction); |
||||||
|
}; |
||||||
|
|
||||||
|
class CZMQPublishRawBlockNotifier : public CZMQAbstractPublishNotifier |
||||||
|
{ |
||||||
|
public: |
||||||
|
bool NotifyBlock(const uint256 &hash); |
||||||
|
}; |
||||||
|
|
||||||
|
class CZMQPublishRawTransactionNotifier : public CZMQAbstractPublishNotifier |
||||||
|
{ |
||||||
|
public: |
||||||
|
bool NotifyTransaction(const CTransaction &transaction); |
||||||
|
}; |
||||||
|
|
||||||
|
#endif // BITCOIN_ZMQ_ZMQPUBLISHNOTIFIER_H
|
Loading…
Reference in new issue