Auto merge of #2050 - str4d:2020-zmq, r=bitcartel
Add ZeroMQ notifications Cherry-picked from the following upstream PRs: - bitcoin/bitcoin#6103 - bitcoin/bitcoin#6684 - bitcoin/bitcoin#6686 - bitcoin/bitcoin#6736 - bitcoin/bitcoin#6739 - bitcoin/bitcoin#6743 - bitcoin/bitcoin#6768 - bitcoin/bitcoin#6779 - bitcoin/bitcoin#6810 - bitcoin/bitcoin#6927 - bitcoin/bitcoin#6980 (only upgrading zeromq) - bitcoin/bitcoin#6680 - bitcoin/bitcoin#7058 - bitcoin/bitcoin#7621 - bitcoin/bitcoin#7335 (only parts affecting `zmq_test.py`) - bitcoin/bitcoin#7853 (only parts affecting `zmq_test.py`) - bitcoin/bitcoin#7762 - bitcoin/bitcoin#7993 (only upgrading zeromq) - bitcoin/bitcoin#8238 - bitcoin/bitcoin#8701 - bitcoin/bitcoin#6685 Closes #2020.
This commit is contained in:
commit
253c610783
|
@ -33,11 +33,11 @@ matrix:
|
||||||
- compiler: ": Win32"
|
- compiler: ": Win32"
|
||||||
env: HOST=i686-w64-mingw32 PACKAGES="nsis gcc-mingw-w64-i686 g++-mingw-w64-i686 binutils-mingw-w64-i686 mingw-w64-dev wine bc" RUN_TESTS=true GOAL="deploy" BITCOIN_CONFIG="--enable-gui --enable-reduce-exports" MAKEJOBS="-j2"
|
env: HOST=i686-w64-mingw32 PACKAGES="nsis gcc-mingw-w64-i686 g++-mingw-w64-i686 binutils-mingw-w64-i686 mingw-w64-dev wine bc" RUN_TESTS=true GOAL="deploy" BITCOIN_CONFIG="--enable-gui --enable-reduce-exports" MAKEJOBS="-j2"
|
||||||
- compiler: ": 32-bit + dash"
|
- compiler: ": 32-bit + dash"
|
||||||
env: HOST=i686-pc-linux-gnu PACKAGES="g++-multilib bc" RUN_TESTS=true GOAL="install" BITCOIN_CONFIG="--enable-glibc-back-compat --enable-reduce-exports LDFLAGS=-static-libstdc++" USE_SHELL="/bin/dash"
|
env: HOST=i686-pc-linux-gnu PACKAGES="g++-multilib bc python-zmq" PPA="ppa:chris-lea/zeromq" RUN_TESTS=true GOAL="install" BITCOIN_CONFIG="--enable-zmq --enable-glibc-back-compat --enable-reduce-exports LDFLAGS=-static-libstdc++" USE_SHELL="/bin/dash"
|
||||||
- compiler: ": Win64"
|
- compiler: ": Win64"
|
||||||
env: HOST=x86_64-w64-mingw32 PACKAGES="nsis gcc-mingw-w64-x86-64 g++-mingw-w64-x86-64 binutils-mingw-w64-x86-64 mingw-w64-dev wine bc" RUN_TESTS=true GOAL="deploy" BITCOIN_CONFIG="--enable-gui --enable-reduce-exports" MAKEJOBS="-j2"
|
env: HOST=x86_64-w64-mingw32 PACKAGES="nsis gcc-mingw-w64-x86-64 g++-mingw-w64-x86-64 binutils-mingw-w64-x86-64 mingw-w64-dev wine bc" RUN_TESTS=true GOAL="deploy" BITCOIN_CONFIG="--enable-gui --enable-reduce-exports" MAKEJOBS="-j2"
|
||||||
- compiler: ": bitcoind"
|
- compiler: ": bitcoind"
|
||||||
env: HOST=x86_64-unknown-linux-gnu PACKAGES="bc" DEP_OPTS="NO_QT=1 NO_UPNP=1 DEBUG=1" RUN_TESTS=true GOAL="install" BITCOIN_CONFIG="--enable-glibc-back-compat --enable-reduce-exports CPPFLAGS=-DDEBUG_LOCKORDER"
|
env: HOST=x86_64-unknown-linux-gnu PACKAGES="bc python-zmq" PPA="ppa:chris-lea/zeromq" DEP_OPTS="NO_QT=1 NO_UPNP=1 DEBUG=1" RUN_TESTS=true GOAL="install" BITCOIN_CONFIG="--enable-zmq --enable-glibc-back-compat --enable-reduce-exports CPPFLAGS=-DDEBUG_LOCKORDER"
|
||||||
- compiler: ": No wallet"
|
- compiler: ": No wallet"
|
||||||
env: HOST=x86_64-unknown-linux-gnu DEP_OPTS="NO_WALLET=1" RUN_TESTS=true GOAL="install" BITCOIN_CONFIG="--enable-glibc-back-compat --enable-reduce-exports"
|
env: HOST=x86_64-unknown-linux-gnu DEP_OPTS="NO_WALLET=1" RUN_TESTS=true GOAL="install" BITCOIN_CONFIG="--enable-glibc-back-compat --enable-reduce-exports"
|
||||||
- compiler: ": Cross-Mac"
|
- compiler: ": Cross-Mac"
|
||||||
|
@ -45,6 +45,7 @@ matrix:
|
||||||
exclude:
|
exclude:
|
||||||
- compiler: gcc
|
- compiler: gcc
|
||||||
install:
|
install:
|
||||||
|
- if [ -n "$PACKAGES" ]; then sudo rm -f /etc/apt/sources.list.d/travis_ci_zeromq3-source.list; fi
|
||||||
- if [ -n "$PACKAGES" ]; then travis_retry sudo apt-get update; fi
|
- if [ -n "$PACKAGES" ]; then travis_retry sudo apt-get update; fi
|
||||||
- if [ -n "$PACKAGES" ]; then travis_retry sudo apt-get install --no-install-recommends --no-upgrade -qq $PACKAGES; fi
|
- if [ -n "$PACKAGES" ]; then travis_retry sudo apt-get install --no-install-recommends --no-upgrade -qq $PACKAGES; fi
|
||||||
before_script:
|
before_script:
|
||||||
|
|
47
configure.ac
47
configure.ac
|
@ -156,9 +156,15 @@ AC_ARG_ENABLE([glibc-back-compat],
|
||||||
[use_glibc_compat=$enableval],
|
[use_glibc_compat=$enableval],
|
||||||
[use_glibc_compat=no])
|
[use_glibc_compat=no])
|
||||||
|
|
||||||
|
AC_ARG_ENABLE([zmq],
|
||||||
|
[AS_HELP_STRING([--disable-zmq],
|
||||||
|
[disable ZMQ notifications])],
|
||||||
|
[use_zmq=$enableval],
|
||||||
|
[use_zmq=yes])
|
||||||
|
|
||||||
AC_ARG_WITH([protoc-bindir],[AS_HELP_STRING([--with-protoc-bindir=BIN_DIR],[specify protoc bin path])], [protoc_bin_path=$withval], [])
|
AC_ARG_WITH([protoc-bindir],[AS_HELP_STRING([--with-protoc-bindir=BIN_DIR],[specify protoc bin path])], [protoc_bin_path=$withval], [])
|
||||||
|
|
||||||
# Enable debug
|
# Enable debug
|
||||||
AC_ARG_ENABLE([debug],
|
AC_ARG_ENABLE([debug],
|
||||||
[AS_HELP_STRING([--enable-debug],
|
[AS_HELP_STRING([--enable-debug],
|
||||||
[use debug compiler flags and macros (default is no)])],
|
[use debug compiler flags and macros (default is no)])],
|
||||||
|
@ -169,11 +175,11 @@ if test "x$enable_debug" = xyes; then
|
||||||
if test "x$GCC" = xyes; then
|
if test "x$GCC" = xyes; then
|
||||||
CFLAGS="-g3 -O0 -DDEBUG"
|
CFLAGS="-g3 -O0 -DDEBUG"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if test "x$GXX" = xyes; then
|
if test "x$GXX" = xyes; then
|
||||||
CXXFLAGS="-g3 -O0 -DDEBUG"
|
CXXFLAGS="-g3 -O0 -DDEBUG"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
## TODO: Remove these hard-coded paths and flags. They are here for the sake of
|
## TODO: Remove these hard-coded paths and flags. They are here for the sake of
|
||||||
## compatibility with the legacy buildsystem.
|
## compatibility with the legacy buildsystem.
|
||||||
|
@ -693,6 +699,16 @@ if test x$use_pkgconfig = xyes; then
|
||||||
if test x$use_qr != xno; then
|
if test x$use_qr != xno; then
|
||||||
BITCOIN_QT_CHECK([PKG_CHECK_MODULES([QR], [libqrencode], [have_qrencode=yes], [have_qrencode=no])])
|
BITCOIN_QT_CHECK([PKG_CHECK_MODULES([QR], [libqrencode], [have_qrencode=yes], [have_qrencode=no])])
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if test "x$use_zmq" = "xyes"; then
|
||||||
|
PKG_CHECK_MODULES([ZMQ],[libzmq >= 4],
|
||||||
|
[AC_DEFINE([ENABLE_ZMQ],[1],[Define to 1 to enable ZMQ functions])],
|
||||||
|
[AC_DEFINE([ENABLE_ZMQ],[0],[Define to 1 to enable ZMQ functions])
|
||||||
|
AC_MSG_WARN([libzmq version 4.x or greater not found, disabling])
|
||||||
|
use_zmq=no])
|
||||||
|
else
|
||||||
|
AC_DEFINE_UNQUOTED([ENABLE_ZMQ],[0],[Define to 1 to enable ZMQ functions])
|
||||||
|
fi
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
else
|
else
|
||||||
|
@ -705,6 +721,29 @@ else
|
||||||
AC_CHECK_HEADER([openssl/ssl.h],, AC_MSG_ERROR(libssl headers missing),)
|
AC_CHECK_HEADER([openssl/ssl.h],, AC_MSG_ERROR(libssl headers missing),)
|
||||||
AC_CHECK_LIB([ssl], [main],SSL_LIBS=-lssl, AC_MSG_ERROR(libssl missing))
|
AC_CHECK_LIB([ssl], [main],SSL_LIBS=-lssl, AC_MSG_ERROR(libssl missing))
|
||||||
|
|
||||||
|
if test "x$use_zmq" = "xyes"; then
|
||||||
|
AC_CHECK_HEADER([zmq.h],
|
||||||
|
[AC_DEFINE([ENABLE_ZMQ],[1],[Define to 1 to enable ZMQ functions])],
|
||||||
|
[AC_MSG_WARN([zmq.h not found, disabling zmq support])
|
||||||
|
use_zmq=no
|
||||||
|
AC_DEFINE([ENABLE_ZMQ],[0],[Define to 1 to enable ZMQ functions])])
|
||||||
|
AC_CHECK_LIB([zmq],[zmq_ctx_shutdown],ZMQ_LIBS=-lzmq,
|
||||||
|
[AC_MSG_WARN([libzmq >= 4.0 not found, disabling zmq support])
|
||||||
|
use_zmq=no
|
||||||
|
AC_DEFINE([ENABLE_ZMQ],[0],[Define to 1 to enable ZMQ functions])])
|
||||||
|
else
|
||||||
|
AC_DEFINE_UNQUOTED([ENABLE_ZMQ],[0],[Define to 1 to enable ZMQ functions])
|
||||||
|
fi
|
||||||
|
|
||||||
|
if test "x$use_zmq" = "xyes"; then
|
||||||
|
dnl Assume libzmq was built for static linking
|
||||||
|
case $host in
|
||||||
|
*mingw*)
|
||||||
|
ZMQ_CFLAGS="$ZMQ_CFLAGS -DZMQ_STATIC"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
BITCOIN_QT_CHECK(AC_CHECK_LIB([protobuf] ,[main],[PROTOBUF_LIBS=-lprotobuf], BITCOIN_QT_FAIL(libprotobuf not found)))
|
BITCOIN_QT_CHECK(AC_CHECK_LIB([protobuf] ,[main],[PROTOBUF_LIBS=-lprotobuf], BITCOIN_QT_FAIL(libprotobuf not found)))
|
||||||
if test x$use_qr != xno; then
|
if test x$use_qr != xno; then
|
||||||
BITCOIN_QT_CHECK([AC_CHECK_LIB([qrencode], [main],[QR_LIBS=-lqrencode], [have_qrencode=no])])
|
BITCOIN_QT_CHECK([AC_CHECK_LIB([qrencode], [main],[QR_LIBS=-lqrencode], [have_qrencode=no])])
|
||||||
|
@ -873,6 +912,8 @@ if test x$bitcoin_enable_qt != xno; then
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
AM_CONDITIONAL([ENABLE_ZMQ], [test "x$use_zmq" = "xyes"])
|
||||||
|
|
||||||
AC_MSG_CHECKING([whether to build test_bitcoin])
|
AC_MSG_CHECKING([whether to build test_bitcoin])
|
||||||
if test x$use_tests = xyes; then
|
if test x$use_tests = xyes; then
|
||||||
AC_MSG_RESULT([yes])
|
AC_MSG_RESULT([yes])
|
||||||
|
|
|
@ -100,6 +100,19 @@ Files: depends/sources/miniupnpc-*.tar.gz
|
||||||
Copyright: 2005-2016 Thomas BERNARD
|
Copyright: 2005-2016 Thomas BERNARD
|
||||||
License: BSD-3clause
|
License: BSD-3clause
|
||||||
|
|
||||||
|
Files: depends/sources/zeromq-*.tar.gz
|
||||||
|
Copyright:
|
||||||
|
1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
|
||||||
|
2007-2014 iMatix Corporation
|
||||||
|
2009-2011 250bpm s.r.o.
|
||||||
|
2010-2011 Miru Limited
|
||||||
|
2011 VMware, Inc.
|
||||||
|
2012 Spotify AB
|
||||||
|
2013 Ericsson AB
|
||||||
|
2014 AppDynamics Inc.
|
||||||
|
2015-2016 Brocade Communications Systems Inc.
|
||||||
|
License: LGPL-with-ZeroMQ-exception
|
||||||
|
|
||||||
Files: depends/sources/google*.tar.gz
|
Files: depends/sources/google*.tar.gz
|
||||||
Copyright: 2008 Google Inc.
|
Copyright: 2008 Google Inc.
|
||||||
License: BSD-3clause-Google
|
License: BSD-3clause-Google
|
||||||
|
@ -1140,3 +1153,32 @@ Comment:
|
||||||
|
|
||||||
License: PUB-DOM
|
License: PUB-DOM
|
||||||
This work is in the public domain.
|
This work is in the public domain.
|
||||||
|
|
||||||
|
License: LGPL-with-ZeroMQ-exception
|
||||||
|
GNU LESSER GENERAL PUBLIC LICENSE
|
||||||
|
Version 3, 29 June 2007
|
||||||
|
.
|
||||||
|
On Debian systems the GNU Lesser General Public License (LGPL) is
|
||||||
|
located in '/usr/share/common-licenses/LGPL'.
|
||||||
|
.
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
SPECIAL EXCEPTION GRANTED BY COPYRIGHT HOLDERS
|
||||||
|
.
|
||||||
|
As a special exception, copyright holders give you permission to link this
|
||||||
|
library with independent modules to produce an executable, regardless of
|
||||||
|
the license terms of these independent modules, and to copy and distribute
|
||||||
|
the resulting executable under terms of your choice, provided that you also
|
||||||
|
meet, for each linked independent module, the terms and conditions of
|
||||||
|
the license of that module. An independent module is a module which is not
|
||||||
|
derived from or based on this library. If you modify this library, you must
|
||||||
|
extend this exception to your version of the library.
|
||||||
|
|
||||||
|
Note: this exception relieves you of any obligations under sections 4 and 5
|
||||||
|
of this license, and section 6 of the GNU General Public License.
|
||||||
|
Comment:
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
#!/usr/bin/env python2
|
||||||
|
# Copyright (c) 2014-2016 The Bitcoin Core developers
|
||||||
|
# Distributed under the MIT software license, see the accompanying
|
||||||
|
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
|
import array
|
||||||
|
import binascii
|
||||||
|
import zmq
|
||||||
|
import struct
|
||||||
|
|
||||||
|
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]
|
||||||
|
sequence = "Unknown";
|
||||||
|
if len(msg[-1]) == 4:
|
||||||
|
msgSequence = struct.unpack('<I', msg[-1])[-1]
|
||||||
|
sequence = str(msgSequence)
|
||||||
|
if topic == "hashblock":
|
||||||
|
print '- HASH BLOCK ('+sequence+') -'
|
||||||
|
print binascii.hexlify(body)
|
||||||
|
elif topic == "hashtx":
|
||||||
|
print '- HASH TX ('+sequence+') -'
|
||||||
|
print binascii.hexlify(body)
|
||||||
|
elif topic == "rawblock":
|
||||||
|
print '- RAW BLOCK HEADER ('+sequence+') -'
|
||||||
|
print binascii.hexlify(body[:80])
|
||||||
|
elif topic == "rawtx":
|
||||||
|
print '- RAW TX ('+sequence+') -'
|
||||||
|
print binascii.hexlify(body)
|
||||||
|
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
zmqContext.destroy()
|
|
@ -1,5 +1,5 @@
|
||||||
zcash_packages := libsnark libgmp libsodium
|
zcash_packages := libsnark libgmp libsodium
|
||||||
packages := boost openssl $(zcash_packages) googletest googlemock
|
packages := boost openssl zeromq $(zcash_packages) googletest googlemock
|
||||||
native_packages := native_ccache
|
native_packages := native_ccache
|
||||||
|
|
||||||
wallet_packages=bdb
|
wallet_packages=bdb
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
package=zeromq
|
||||||
|
$(package)_version=4.2.1
|
||||||
|
$(package)_download_path=https://github.com/zeromq/libzmq/releases/download/v$($(package)_version)/
|
||||||
|
$(package)_file_name=$(package)-$($(package)_version).tar.gz
|
||||||
|
$(package)_sha256_hash=27d1e82a099228ee85a7ddb2260f40830212402c605a4a10b5e5498a7e0e9d03
|
||||||
|
|
||||||
|
define $(package)_set_vars
|
||||||
|
$(package)_config_opts=--without-documentation --disable-shared --disable-curve
|
||||||
|
$(package)_config_opts_linux=--with-pic
|
||||||
|
endef
|
||||||
|
|
||||||
|
define $(package)_config_cmds
|
||||||
|
$($(package)_autoconf)
|
||||||
|
endef
|
||||||
|
|
||||||
|
define $(package)_build_cmds
|
||||||
|
$(MAKE) src/libzmq.la
|
||||||
|
endef
|
||||||
|
|
||||||
|
define $(package)_stage_cmds
|
||||||
|
$(MAKE) DESTDIR=$($(package)_staging_dir) install-libLTLIBRARIES install-includeHEADERS install-pkgconfigDATA
|
||||||
|
endef
|
||||||
|
|
||||||
|
define $(package)_postprocess_cmds
|
||||||
|
rm -rf bin share
|
||||||
|
endef
|
|
@ -0,0 +1,107 @@
|
||||||
|
# Block and Transaction Broadcasting With ZeroMQ
|
||||||
|
|
||||||
|
[ZeroMQ](http://zeromq.org/) is a lightweight wrapper around TCP
|
||||||
|
connections, inter-process communication, and shared-memory,
|
||||||
|
providing various message-oriented semantics such as publish/subcribe,
|
||||||
|
request/reply, and push/pull.
|
||||||
|
|
||||||
|
The Zcash daemon can be configured to act as a trusted "border
|
||||||
|
router", implementing the zcash 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,
|
||||||
|
connections 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 Zcash requires ZeroMQ API version 4.x or
|
||||||
|
newer, which you will need to install if you are not using the depends
|
||||||
|
system. Typically, it is packaged by distributions as something like
|
||||||
|
*libzmq5-dev*. The C++ wrapper for ZeroMQ is *not* needed.
|
||||||
|
|
||||||
|
In order to run the example Python client scripts in contrib/ one must
|
||||||
|
also install *python-zmq*, though this is not necessary for daemon
|
||||||
|
operation.
|
||||||
|
|
||||||
|
## Enabling
|
||||||
|
|
||||||
|
By default, the ZeroMQ feature is automatically compiled in if the
|
||||||
|
necessary prerequisites are found. To disable, use --disable-zmq
|
||||||
|
during the *configure* step of building zcashd:
|
||||||
|
|
||||||
|
$ ./configure --disable-zmq (other options)
|
||||||
|
|
||||||
|
To actually enable operation, one must set the appropriate options on
|
||||||
|
the commandline or in the configuration file.
|
||||||
|
|
||||||
|
## 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:
|
||||||
|
|
||||||
|
$ zcashd -zmqpubhashtx=tcp://127.0.0.1:28332 \
|
||||||
|
-zmqpubrawtx=ipc:///tmp/zcashd.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 zcash.conf.
|
||||||
|
|
||||||
|
ZeroMQ endpoint specifiers for TCP (and others) are documented in the
|
||||||
|
[ZeroMQ API](http://api.zeromq.org/4-0:_start).
|
||||||
|
|
||||||
|
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 zcashd, the ZeroMQ socket is write-only; PUB
|
||||||
|
sockets don't even have a read function. Thus, there is no state
|
||||||
|
introduced into zcashd 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.
|
||||||
|
|
||||||
|
There are several possibilities that ZMQ notification can get lost
|
||||||
|
during transmission depending on the communication type your are
|
||||||
|
using. Zcashd appends an up-counting sequence number to each
|
||||||
|
notification which allows listeners to detect lost notifications.
|
|
@ -57,6 +57,10 @@ testScriptsExt=(
|
||||||
'p2p-acceptblock.py'
|
'p2p-acceptblock.py'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if [ "x$ENABLE_ZMQ" = "x1" ]; then
|
||||||
|
testScripts+=('zmq_test.py')
|
||||||
|
fi
|
||||||
|
|
||||||
extArg="-extended"
|
extArg="-extended"
|
||||||
passOn=${@#$extArg}
|
passOn=${@#$extArg}
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@ EXEEXT="@EXEEXT@"
|
||||||
@ENABLE_WALLET_TRUE@ENABLE_WALLET=1
|
@ENABLE_WALLET_TRUE@ENABLE_WALLET=1
|
||||||
@BUILD_BITCOIN_UTILS_TRUE@ENABLE_UTILS=1
|
@BUILD_BITCOIN_UTILS_TRUE@ENABLE_UTILS=1
|
||||||
@BUILD_BITCOIND_TRUE@ENABLE_BITCOIND=1
|
@BUILD_BITCOIND_TRUE@ENABLE_BITCOIND=1
|
||||||
|
@ENABLE_ZMQ_TRUE@ENABLE_ZMQ=1
|
||||||
|
|
||||||
REAL_BITCOIND="$BUILDDIR/src/zcashd${EXEEXT}"
|
REAL_BITCOIND="$BUILDDIR/src/zcashd${EXEEXT}"
|
||||||
REAL_BITCOINCLI="$BUILDDIR/src/zcash-cli${EXEEXT}"
|
REAL_BITCOINCLI="$BUILDDIR/src/zcash-cli${EXEEXT}"
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
# Copyright (c) 2014 The Bitcoin Core developers
|
# Copyright (c) 2014 The Bitcoin Core developers
|
||||||
# Distributed under the MIT software license, see the accompanying
|
# Distributed under the MIT software license, see the accompanying
|
||||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Helpful routines for regression testing
|
# Helpful routines for regression testing
|
||||||
#
|
#
|
||||||
|
@ -9,6 +11,8 @@
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
from binascii import hexlify, unhexlify
|
||||||
|
from base64 import b64encode
|
||||||
from decimal import Decimal, ROUND_DOWN
|
from decimal import Decimal, ROUND_DOWN
|
||||||
import json
|
import json
|
||||||
import random
|
import random
|
||||||
|
@ -32,6 +36,15 @@ def check_json_precision():
|
||||||
if satoshis != 2000000000000003:
|
if satoshis != 2000000000000003:
|
||||||
raise RuntimeError("JSON encode/decode loses precision")
|
raise RuntimeError("JSON encode/decode loses precision")
|
||||||
|
|
||||||
|
def bytes_to_hex_str(byte_str):
|
||||||
|
return hexlify(byte_str).decode('ascii')
|
||||||
|
|
||||||
|
def hex_str_to_bytes(hex_str):
|
||||||
|
return unhexlify(hex_str.encode('ascii'))
|
||||||
|
|
||||||
|
def str_to_b64str(string):
|
||||||
|
return b64encode(string.encode('utf-8')).decode('ascii')
|
||||||
|
|
||||||
def sync_blocks(rpc_connections, wait=1):
|
def sync_blocks(rpc_connections, wait=1):
|
||||||
"""
|
"""
|
||||||
Wait until everybody has the same block count
|
Wait until everybody has the same block count
|
||||||
|
|
|
@ -0,0 +1,103 @@
|
||||||
|
#!/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
|
||||||
|
import struct
|
||||||
|
|
||||||
|
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, b"hashblock")
|
||||||
|
self.zmqSubSocket.setsockopt(zmq.SUBSCRIBE, b"hashtx")
|
||||||
|
self.zmqSubSocket.connect("tcp://127.0.0.1:%i" % self.port)
|
||||||
|
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 = msg[0]
|
||||||
|
assert_equal(topic, b"hashtx")
|
||||||
|
body = msg[1]
|
||||||
|
nseq = msg[2]
|
||||||
|
msgSequence = struct.unpack('<I', msg[-1])[-1]
|
||||||
|
assert_equal(msgSequence, 0) #must be sequence 0 on hashtx
|
||||||
|
|
||||||
|
msg = self.zmqSubSocket.recv_multipart()
|
||||||
|
topic = msg[0]
|
||||||
|
body = msg[1]
|
||||||
|
msgSequence = struct.unpack('<I', msg[-1])[-1]
|
||||||
|
assert_equal(msgSequence, 0) #must be sequence 0 on hashblock
|
||||||
|
blkhash = bytes_to_hex_str(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 = []
|
||||||
|
blockcount = 0
|
||||||
|
for x in range(0,n*2):
|
||||||
|
msg = self.zmqSubSocket.recv_multipart()
|
||||||
|
topic = msg[0]
|
||||||
|
body = msg[1]
|
||||||
|
if topic == b"hashblock":
|
||||||
|
zmqHashes.append(bytes_to_hex_str(body))
|
||||||
|
msgSequence = struct.unpack('<I', msg[-1])[-1]
|
||||||
|
assert_equal(msgSequence, blockcount+1)
|
||||||
|
blockcount += 1
|
||||||
|
|
||||||
|
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 broadcast
|
||||||
|
msg = self.zmqSubSocket.recv_multipart()
|
||||||
|
topic = msg[0]
|
||||||
|
body = msg[1]
|
||||||
|
hashZMQ = ""
|
||||||
|
if topic == b"hashtx":
|
||||||
|
hashZMQ = bytes_to_hex_str(body)
|
||||||
|
msgSequence = struct.unpack('<I', msg[-1])[-1]
|
||||||
|
assert_equal(msgSequence, blockcount+1)
|
||||||
|
|
||||||
|
assert_equal(hashRPC, hashZMQ) #blockhash from generate must be equal to the hash received over zmq
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
ZMQTest ().main ()
|
|
@ -50,6 +50,9 @@ if ENABLE_WALLET
|
||||||
BITCOIN_INCLUDES += $(BDB_CPPFLAGS)
|
BITCOIN_INCLUDES += $(BDB_CPPFLAGS)
|
||||||
EXTRA_LIBRARIES += libbitcoin_wallet.a
|
EXTRA_LIBRARIES += libbitcoin_wallet.a
|
||||||
endif
|
endif
|
||||||
|
if ENABLE_ZMQ
|
||||||
|
EXTRA_LIBRARIES += libbitcoin_zmq.a
|
||||||
|
endif
|
||||||
|
|
||||||
if BUILD_BITCOIN_LIBS
|
if BUILD_BITCOIN_LIBS
|
||||||
lib_LTLIBRARIES = libzcashconsensus.la
|
lib_LTLIBRARIES = libzcashconsensus.la
|
||||||
|
@ -172,7 +175,12 @@ BITCOIN_CORE_H = \
|
||||||
wallet/db.h \
|
wallet/db.h \
|
||||||
wallet/wallet.h \
|
wallet/wallet.h \
|
||||||
wallet/wallet_ismine.h \
|
wallet/wallet_ismine.h \
|
||||||
wallet/walletdb.h
|
wallet/walletdb.h \
|
||||||
|
zmq/zmqabstractnotifier.h \
|
||||||
|
zmq/zmqconfig.h\
|
||||||
|
zmq/zmqnotificationinterface.h \
|
||||||
|
zmq/zmqpublishnotifier.h
|
||||||
|
|
||||||
|
|
||||||
JSON_H = \
|
JSON_H = \
|
||||||
json/json_spirit.h \
|
json/json_spirit.h \
|
||||||
|
@ -229,6 +237,17 @@ libbitcoin_server_a_SOURCES = \
|
||||||
$(BITCOIN_CORE_H) \
|
$(BITCOIN_CORE_H) \
|
||||||
$(LIBZCASH_H)
|
$(LIBZCASH_H)
|
||||||
|
|
||||||
|
if ENABLE_ZMQ
|
||||||
|
LIBBITCOIN_ZMQ=libbitcoin_zmq.a
|
||||||
|
|
||||||
|
libbitcoin_zmq_a_CPPFLAGS = $(BITCOIN_INCLUDES) $(ZMQ_CFLAGS)
|
||||||
|
libbitcoin_zmq_a_SOURCES = \
|
||||||
|
zmq/zmqabstractnotifier.cpp \
|
||||||
|
zmq/zmqnotificationinterface.cpp \
|
||||||
|
zmq/zmqpublishnotifier.cpp
|
||||||
|
endif
|
||||||
|
|
||||||
|
|
||||||
# wallet: shared between bitcoind and bitcoin-qt, but only linked
|
# wallet: shared between bitcoind and bitcoin-qt, but only linked
|
||||||
# when wallet enabled
|
# when wallet enabled
|
||||||
libbitcoin_wallet_a_CPPFLAGS = $(BITCOIN_INCLUDES)
|
libbitcoin_wallet_a_CPPFLAGS = $(BITCOIN_INCLUDES)
|
||||||
|
@ -375,6 +394,10 @@ zcashd_LDADD = \
|
||||||
$(LIBMEMENV) \
|
$(LIBMEMENV) \
|
||||||
$(LIBSECP256K1)
|
$(LIBSECP256K1)
|
||||||
|
|
||||||
|
if ENABLE_ZMQ
|
||||||
|
zcashd_LDADD += $(LIBBITCOIN_ZMQ) $(ZMQ_LIBS)
|
||||||
|
endif
|
||||||
|
|
||||||
if ENABLE_WALLET
|
if ENABLE_WALLET
|
||||||
zcashd_LDADD += libbitcoin_wallet.a
|
zcashd_LDADD += libbitcoin_wallet.a
|
||||||
endif
|
endif
|
||||||
|
@ -388,7 +411,6 @@ zcashd_LDADD += \
|
||||||
$(LIBZCASH) \
|
$(LIBZCASH) \
|
||||||
$(LIBBITCOIN_CRYPTO) \
|
$(LIBBITCOIN_CRYPTO) \
|
||||||
$(LIBZCASH_LIBS)
|
$(LIBZCASH_LIBS)
|
||||||
#
|
|
||||||
|
|
||||||
# bitcoin-cli binary #
|
# bitcoin-cli binary #
|
||||||
zcash_cli_SOURCES = bitcoin-cli.cpp
|
zcash_cli_SOURCES = bitcoin-cli.cpp
|
||||||
|
|
|
@ -44,6 +44,9 @@ zcash_gtest_CPPFLAGS = -DMULTICORE -fopenmp -DBINARY_OUTPUT -DCURVE_ALT_BN128 -D
|
||||||
|
|
||||||
zcash_gtest_LDADD = -lgtest -lgmock $(LIBBITCOIN_SERVER) $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBBITCOIN_UTIL) $(LIBBITCOIN_CRYPTO) $(LIBBITCOIN_UNIVALUE) $(LIBLEVELDB) $(LIBMEMENV) \
|
zcash_gtest_LDADD = -lgtest -lgmock $(LIBBITCOIN_SERVER) $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBBITCOIN_UTIL) $(LIBBITCOIN_CRYPTO) $(LIBBITCOIN_UNIVALUE) $(LIBLEVELDB) $(LIBMEMENV) \
|
||||||
$(BOOST_LIBS) $(BOOST_UNIT_TEST_FRAMEWORK_LIB) $(LIBSECP256K1)
|
$(BOOST_LIBS) $(BOOST_UNIT_TEST_FRAMEWORK_LIB) $(LIBSECP256K1)
|
||||||
|
if ENABLE_ZMQ
|
||||||
|
zcash_gtest_LDADD += $(LIBBITCOIN_ZMQ) $(ZMQ_LIBS)
|
||||||
|
endif
|
||||||
if ENABLE_WALLET
|
if ENABLE_WALLET
|
||||||
zcash_gtest_LDADD += $(LIBBITCOIN_WALLET)
|
zcash_gtest_LDADD += $(LIBBITCOIN_WALLET)
|
||||||
endif
|
endif
|
||||||
|
|
|
@ -361,6 +361,9 @@ qt_bitcoin_qt_LDADD = qt/libbitcoinqt.a $(LIBBITCOIN_SERVER)
|
||||||
if ENABLE_WALLET
|
if ENABLE_WALLET
|
||||||
qt_bitcoin_qt_LDADD += $(LIBBITCOIN_WALLET)
|
qt_bitcoin_qt_LDADD += $(LIBBITCOIN_WALLET)
|
||||||
endif
|
endif
|
||||||
|
if ENABLE_ZMQ
|
||||||
|
qt_bitcoin_qt_LDADD += $(LIBBITCOIN_ZMQ) $(ZMQ_LIBS)
|
||||||
|
endif
|
||||||
qt_bitcoin_qt_LDADD += $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBBITCOIN_UTIL) $(LIBBITCOIN_CRYPTO) $(LIBBITCOIN_UNIVALUE) $(LIBLEVELDB) $(LIBMEMENV) \
|
qt_bitcoin_qt_LDADD += $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBBITCOIN_UTIL) $(LIBBITCOIN_CRYPTO) $(LIBBITCOIN_UNIVALUE) $(LIBLEVELDB) $(LIBMEMENV) \
|
||||||
$(BOOST_LIBS) $(QT_LIBS) $(QT_DBUS_LIBS) $(QR_LIBS) $(PROTOBUF_LIBS) $(BDB_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) $(LIBSECP256K1) $(LIBZCASH_LIBS)
|
$(BOOST_LIBS) $(QT_LIBS) $(QT_DBUS_LIBS) $(QR_LIBS) $(PROTOBUF_LIBS) $(BDB_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) $(LIBSECP256K1) $(LIBZCASH_LIBS)
|
||||||
qt_bitcoin_qt_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(QT_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
|
qt_bitcoin_qt_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(QT_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
|
||||||
|
|
|
@ -30,6 +30,9 @@ qt_test_test_bitcoin_qt_LDADD = $(LIBBITCOINQT) $(LIBBITCOIN_SERVER)
|
||||||
if ENABLE_WALLET
|
if ENABLE_WALLET
|
||||||
qt_test_test_bitcoin_qt_LDADD += $(LIBBITCOIN_WALLET)
|
qt_test_test_bitcoin_qt_LDADD += $(LIBBITCOIN_WALLET)
|
||||||
endif
|
endif
|
||||||
|
if ENABLE_ZMQ
|
||||||
|
qt_test_test_bitcoin_qt_LDADD += $(LIBBITCOIN_ZMQ) $(ZMQ_LIBS)
|
||||||
|
endif
|
||||||
qt_test_test_bitcoin_qt_LDADD += $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBBITCOIN_UTIL) $(LIBBITCOIN_CRYPTO) $(LIBBITCOIN_UNIVALUE) $(LIBLEVELDB) \
|
qt_test_test_bitcoin_qt_LDADD += $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBBITCOIN_UTIL) $(LIBBITCOIN_CRYPTO) $(LIBBITCOIN_UNIVALUE) $(LIBLEVELDB) \
|
||||||
$(LIBMEMENV) $(BOOST_LIBS) $(QT_DBUS_LIBS) $(QT_TEST_LIBS) $(QT_LIBS) \
|
$(LIBMEMENV) $(BOOST_LIBS) $(QT_DBUS_LIBS) $(QT_TEST_LIBS) $(QT_LIBS) \
|
||||||
$(QR_LIBS) $(PROTOBUF_LIBS) $(BDB_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) $(LIBSECP256K1) $(LIBZCASH_LIBS)
|
$(QR_LIBS) $(PROTOBUF_LIBS) $(BDB_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) $(LIBSECP256K1) $(LIBZCASH_LIBS)
|
||||||
|
|
|
@ -107,6 +107,10 @@ endif
|
||||||
test_test_bitcoin_LDADD += $(LIBZCASH_CONSENSUS) $(BDB_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) $(LIBZCASH) $(LIBZCASH_LIBS)
|
test_test_bitcoin_LDADD += $(LIBZCASH_CONSENSUS) $(BDB_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) $(LIBZCASH) $(LIBZCASH_LIBS)
|
||||||
test_test_bitcoin_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) -static
|
test_test_bitcoin_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) -static
|
||||||
|
|
||||||
|
if ENABLE_ZMQ
|
||||||
|
test_test_bitcoin_LDADD += $(ZMQ_LIBS)
|
||||||
|
endif
|
||||||
|
|
||||||
nodist_test_test_bitcoin_SOURCES = $(GENERATED_TEST_FILES)
|
nodist_test_test_bitcoin_SOURCES = $(GENERATED_TEST_FILES)
|
||||||
|
|
||||||
$(BITCOIN_TESTS): $(GENERATED_TEST_FILES)
|
$(BITCOIN_TESTS): $(GENERATED_TEST_FILES)
|
||||||
|
|
36
src/init.cpp
36
src/init.cpp
|
@ -31,7 +31,6 @@
|
||||||
#include "wallet/wallet.h"
|
#include "wallet/wallet.h"
|
||||||
#include "wallet/walletdb.h"
|
#include "wallet/walletdb.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
|
@ -50,6 +49,10 @@
|
||||||
|
|
||||||
#include "libsnark/common/profiling.hpp"
|
#include "libsnark/common/profiling.hpp"
|
||||||
|
|
||||||
|
#if ENABLE_ZMQ
|
||||||
|
#include "zmq/zmqnotificationinterface.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
extern void ThreadSendAlert();
|
extern void ThreadSendAlert();
|
||||||
|
@ -61,6 +64,10 @@ CWallet* pwalletMain = NULL;
|
||||||
#endif
|
#endif
|
||||||
bool fFeeEstimatesInitialized = false;
|
bool fFeeEstimatesInitialized = false;
|
||||||
|
|
||||||
|
#if ENABLE_ZMQ
|
||||||
|
static CZMQNotificationInterface* pzmqNotificationInterface = NULL;
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
// Win32 LevelDB doesn't use file descriptors, and the ones used for
|
// Win32 LevelDB doesn't use file descriptors, and the ones used for
|
||||||
// accessing block files don't count towards the fd_set size limit
|
// accessing block files don't count towards the fd_set size limit
|
||||||
|
@ -199,6 +206,15 @@ void Shutdown()
|
||||||
if (pwalletMain)
|
if (pwalletMain)
|
||||||
pwalletMain->Flush(true);
|
pwalletMain->Flush(true);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if ENABLE_ZMQ
|
||||||
|
if (pzmqNotificationInterface) {
|
||||||
|
UnregisterValidationInterface(pzmqNotificationInterface);
|
||||||
|
delete pzmqNotificationInterface;
|
||||||
|
pzmqNotificationInterface = NULL;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef WIN32
|
#ifndef WIN32
|
||||||
try {
|
try {
|
||||||
boost::filesystem::remove(GetPidFile());
|
boost::filesystem::remove(GetPidFile());
|
||||||
|
@ -367,6 +383,14 @@ std::string HelpMessage(HelpMessageMode mode)
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if ENABLE_ZMQ
|
||||||
|
strUsage += HelpMessageGroup(_("ZeroMQ notification options:"));
|
||||||
|
strUsage += HelpMessageOpt("-zmqpubhashblock=<address>", _("Enable publish hash block in <address>"));
|
||||||
|
strUsage += HelpMessageOpt("-zmqpubhashtx=<address>", _("Enable publish hash transaction in <address>"));
|
||||||
|
strUsage += HelpMessageOpt("-zmqpubrawblock=<address>", _("Enable publish raw block in <address>"));
|
||||||
|
strUsage += HelpMessageOpt("-zmqpubrawtx=<address>", _("Enable publish raw transaction in <address>"));
|
||||||
|
#endif
|
||||||
|
|
||||||
strUsage += HelpMessageGroup(_("Debugging/Testing options:"));
|
strUsage += HelpMessageGroup(_("Debugging/Testing options:"));
|
||||||
if (showDebug)
|
if (showDebug)
|
||||||
{
|
{
|
||||||
|
@ -380,7 +404,7 @@ std::string HelpMessage(HelpMessageMode mode)
|
||||||
strUsage += HelpMessageOpt("-stopafterblockimport", strprintf("Stop running after importing blocks from disk (default: %u)", 0));
|
strUsage += HelpMessageOpt("-stopafterblockimport", strprintf("Stop running after importing blocks from disk (default: %u)", 0));
|
||||||
}
|
}
|
||||||
string debugCategories = "addrman, alert, bench, coindb, db, estimatefee, lock, mempool, net, partitioncheck, pow, proxy, prune, "
|
string debugCategories = "addrman, alert, bench, coindb, db, estimatefee, lock, mempool, net, partitioncheck, pow, proxy, prune, "
|
||||||
"rand, reindex, rpc, selectcoins, zrpc, zrpcunsafe (implies zrpc)"; // Don't translate these and qt below
|
"rand, reindex, rpc, selectcoins, zmq, zrpc, zrpcunsafe (implies zrpc)"; // Don't translate these and qt below
|
||||||
if (mode == HMM_BITCOIN_QT)
|
if (mode == HMM_BITCOIN_QT)
|
||||||
debugCategories += ", qt";
|
debugCategories += ", qt";
|
||||||
strUsage += HelpMessageOpt("-debug=<category>", strprintf(_("Output debugging information (default: %u, supplying <category> is optional)"), 0) + ". " +
|
strUsage += HelpMessageOpt("-debug=<category>", strprintf(_("Output debugging information (default: %u, supplying <category> is optional)"), 0) + ". " +
|
||||||
|
@ -1143,6 +1167,14 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
|
||||||
BOOST_FOREACH(string strDest, mapMultiArgs["-seednode"])
|
BOOST_FOREACH(string strDest, mapMultiArgs["-seednode"])
|
||||||
AddOneShot(strDest);
|
AddOneShot(strDest);
|
||||||
|
|
||||||
|
#if ENABLE_ZMQ
|
||||||
|
pzmqNotificationInterface = CZMQNotificationInterface::CreateWithArguments(mapArgs);
|
||||||
|
|
||||||
|
if (pzmqNotificationInterface) {
|
||||||
|
RegisterValidationInterface(pzmqNotificationInterface);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// ********************************************************* Step 7: load block chain
|
// ********************************************************* Step 7: load block chain
|
||||||
|
|
||||||
fReindex = GetBoolArg("-reindex", false);
|
fReindex = GetBoolArg("-reindex", false);
|
||||||
|
|
|
@ -2707,6 +2707,7 @@ bool ActivateBestChain(CValidationState &state, CBlock *pblock) {
|
||||||
pnode->PushInventory(CInv(MSG_BLOCK, hashNewTip));
|
pnode->PushInventory(CInv(MSG_BLOCK, hashNewTip));
|
||||||
}
|
}
|
||||||
// Notify external listeners about the new tip.
|
// Notify external listeners about the new tip.
|
||||||
|
GetMainSignals().UpdatedBlockTip(pindexNewTip);
|
||||||
uiInterface.NotifyBlockTip(hashNewTip);
|
uiInterface.NotifyBlockTip(hashNewTip);
|
||||||
}
|
}
|
||||||
} while(pindexMostWork != chainActive.Tip());
|
} while(pindexMostWork != chainActive.Tip());
|
||||||
|
|
|
@ -13,6 +13,7 @@ CMainSignals& GetMainSignals()
|
||||||
}
|
}
|
||||||
|
|
||||||
void RegisterValidationInterface(CValidationInterface* pwalletIn) {
|
void RegisterValidationInterface(CValidationInterface* pwalletIn) {
|
||||||
|
g_signals.UpdatedBlockTip.connect(boost::bind(&CValidationInterface::UpdatedBlockTip, pwalletIn, _1));
|
||||||
g_signals.SyncTransaction.connect(boost::bind(&CValidationInterface::SyncTransaction, pwalletIn, _1, _2));
|
g_signals.SyncTransaction.connect(boost::bind(&CValidationInterface::SyncTransaction, pwalletIn, _1, _2));
|
||||||
g_signals.EraseTransaction.connect(boost::bind(&CValidationInterface::EraseFromWallet, pwalletIn, _1));
|
g_signals.EraseTransaction.connect(boost::bind(&CValidationInterface::EraseFromWallet, pwalletIn, _1));
|
||||||
g_signals.UpdatedTransaction.connect(boost::bind(&CValidationInterface::UpdatedTransaction, pwalletIn, _1));
|
g_signals.UpdatedTransaction.connect(boost::bind(&CValidationInterface::UpdatedTransaction, pwalletIn, _1));
|
||||||
|
@ -32,6 +33,7 @@ void UnregisterValidationInterface(CValidationInterface* pwalletIn) {
|
||||||
g_signals.UpdatedTransaction.disconnect(boost::bind(&CValidationInterface::UpdatedTransaction, pwalletIn, _1));
|
g_signals.UpdatedTransaction.disconnect(boost::bind(&CValidationInterface::UpdatedTransaction, pwalletIn, _1));
|
||||||
g_signals.EraseTransaction.disconnect(boost::bind(&CValidationInterface::EraseFromWallet, pwalletIn, _1));
|
g_signals.EraseTransaction.disconnect(boost::bind(&CValidationInterface::EraseFromWallet, pwalletIn, _1));
|
||||||
g_signals.SyncTransaction.disconnect(boost::bind(&CValidationInterface::SyncTransaction, pwalletIn, _1, _2));
|
g_signals.SyncTransaction.disconnect(boost::bind(&CValidationInterface::SyncTransaction, pwalletIn, _1, _2));
|
||||||
|
g_signals.UpdatedBlockTip.disconnect(boost::bind(&CValidationInterface::UpdatedBlockTip, pwalletIn, _1));
|
||||||
}
|
}
|
||||||
|
|
||||||
void UnregisterAllValidationInterfaces() {
|
void UnregisterAllValidationInterfaces() {
|
||||||
|
@ -43,6 +45,7 @@ void UnregisterAllValidationInterfaces() {
|
||||||
g_signals.UpdatedTransaction.disconnect_all_slots();
|
g_signals.UpdatedTransaction.disconnect_all_slots();
|
||||||
g_signals.EraseTransaction.disconnect_all_slots();
|
g_signals.EraseTransaction.disconnect_all_slots();
|
||||||
g_signals.SyncTransaction.disconnect_all_slots();
|
g_signals.SyncTransaction.disconnect_all_slots();
|
||||||
|
g_signals.UpdatedBlockTip.disconnect_all_slots();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SyncWithWallets(const CTransaction &tx, const CBlock *pblock) {
|
void SyncWithWallets(const CTransaction &tx, const CBlock *pblock) {
|
||||||
|
|
|
@ -31,6 +31,7 @@ void SyncWithWallets(const CTransaction& tx, const CBlock* pblock = NULL);
|
||||||
|
|
||||||
class CValidationInterface {
|
class CValidationInterface {
|
||||||
protected:
|
protected:
|
||||||
|
virtual void UpdatedBlockTip(const CBlockIndex *pindex) {}
|
||||||
virtual void SyncTransaction(const CTransaction &tx, const CBlock *pblock) {}
|
virtual void SyncTransaction(const CTransaction &tx, const CBlock *pblock) {}
|
||||||
virtual void EraseFromWallet(const uint256 &hash) {}
|
virtual void EraseFromWallet(const uint256 &hash) {}
|
||||||
virtual void ChainTip(const CBlockIndex *pindex, const CBlock *pblock, ZCIncrementalMerkleTree tree, bool added) {}
|
virtual void ChainTip(const CBlockIndex *pindex, const CBlock *pblock, ZCIncrementalMerkleTree tree, bool added) {}
|
||||||
|
@ -45,6 +46,8 @@ protected:
|
||||||
};
|
};
|
||||||
|
|
||||||
struct CMainSignals {
|
struct CMainSignals {
|
||||||
|
/** Notifies listeners of updated block chain tip */
|
||||||
|
boost::signals2::signal<void (const CBlockIndex *)> UpdatedBlockTip;
|
||||||
/** Notifies listeners of updated transaction data (transaction, and optionally the block it is found in. */
|
/** Notifies listeners of updated transaction data (transaction, and optionally the block it is found in. */
|
||||||
boost::signals2::signal<void (const CTransaction &, const CBlock *)> SyncTransaction;
|
boost::signals2::signal<void (const CTransaction &, const CBlock *)> SyncTransaction;
|
||||||
/** Notifies listeners of an erased transaction (currently disabled, requires transaction replacement). */
|
/** Notifies listeners of an erased transaction (currently disabled, requires transaction replacement). */
|
||||||
|
|
|
@ -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 CBlockIndex * /*CBlockIndex*/)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CZMQAbstractNotifier::NotifyTransaction(const CTransaction &/*transaction*/)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
// 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 CBlockIndex;
|
||||||
|
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 CBlockIndex *pindex);
|
||||||
|
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,159 @@
|
||||||
|
// 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", "zmq: Error: %s, errno=%s\n", str, zmq_strerror(errno));
|
||||||
|
}
|
||||||
|
|
||||||
|
CZMQNotificationInterface::CZMQNotificationInterface() : pcontext(NULL)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
CZMQNotificationInterface::~CZMQNotificationInterface()
|
||||||
|
{
|
||||||
|
Shutdown();
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
if (!notificationInterface->Initialize())
|
||||||
|
{
|
||||||
|
delete notificationInterface;
|
||||||
|
notificationInterface = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return notificationInterface;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called at startup to conditionally set up ZMQ socket(s)
|
||||||
|
bool CZMQNotificationInterface::Initialize()
|
||||||
|
{
|
||||||
|
LogPrint("zmq", "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())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called during shutdown sequence
|
||||||
|
void CZMQNotificationInterface::Shutdown()
|
||||||
|
{
|
||||||
|
LogPrint("zmq", "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 CBlockIndex *pindex)
|
||||||
|
{
|
||||||
|
for (std::list<CZMQAbstractNotifier*>::iterator i = notifiers.begin(); i!=notifiers.end(); )
|
||||||
|
{
|
||||||
|
CZMQAbstractNotifier *notifier = *i;
|
||||||
|
if (notifier->NotifyBlock(pindex))
|
||||||
|
{
|
||||||
|
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,37 @@
|
||||||
|
// 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 CBlockIndex;
|
||||||
|
class CZMQAbstractNotifier;
|
||||||
|
|
||||||
|
class CZMQNotificationInterface : public CValidationInterface
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~CZMQNotificationInterface();
|
||||||
|
|
||||||
|
static CZMQNotificationInterface* CreateWithArguments(const std::map<std::string, std::string> &args);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool Initialize();
|
||||||
|
void Shutdown();
|
||||||
|
|
||||||
|
// CValidationInterface
|
||||||
|
void SyncTransaction(const CTransaction &tx, const CBlock *pblock);
|
||||||
|
void UpdatedBlockTip(const CBlockIndex *pindex);
|
||||||
|
|
||||||
|
private:
|
||||||
|
CZMQNotificationInterface();
|
||||||
|
|
||||||
|
void *pcontext;
|
||||||
|
std::list<CZMQAbstractNotifier*> notifiers;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // BITCOIN_ZMQ_ZMQNOTIFICATIONINTERFACE_H
|
|
@ -0,0 +1,189 @@
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
static const char *MSG_HASHBLOCK = "hashblock";
|
||||||
|
static const char *MSG_HASHTX = "hashtx";
|
||||||
|
static const char *MSG_RAWBLOCK = "rawblock";
|
||||||
|
static const char *MSG_RAWTX = "rawtx";
|
||||||
|
|
||||||
|
// 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");
|
||||||
|
zmq_close(psocket);
|
||||||
|
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", "zmq: Reusing 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 CZMQAbstractPublishNotifier::SendMessage(const char *command, const void* data, size_t size)
|
||||||
|
{
|
||||||
|
assert(psocket);
|
||||||
|
|
||||||
|
/* send three parts, command & data & a LE 4byte sequence number */
|
||||||
|
unsigned char msgseq[sizeof(uint32_t)];
|
||||||
|
WriteLE32(&msgseq[0], nSequence);
|
||||||
|
int rc = zmq_send_multipart(psocket, command, strlen(command), data, size, msgseq, (size_t)sizeof(uint32_t), (void*)0);
|
||||||
|
if (rc == -1)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* increment memory only sequence number after sending */
|
||||||
|
nSequence++;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CZMQPublishHashBlockNotifier::NotifyBlock(const CBlockIndex *pindex)
|
||||||
|
{
|
||||||
|
uint256 hash = pindex->GetBlockHash();
|
||||||
|
LogPrint("zmq", "zmq: Publish hashblock %s\n", hash.GetHex());
|
||||||
|
char data[32];
|
||||||
|
for (unsigned int i = 0; i < 32; i++)
|
||||||
|
data[31 - i] = hash.begin()[i];
|
||||||
|
return SendMessage(MSG_HASHBLOCK, data, 32);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CZMQPublishHashTransactionNotifier::NotifyTransaction(const CTransaction &transaction)
|
||||||
|
{
|
||||||
|
uint256 hash = transaction.GetHash();
|
||||||
|
LogPrint("zmq", "zmq: Publish hashtx %s\n", hash.GetHex());
|
||||||
|
char data[32];
|
||||||
|
for (unsigned int i = 0; i < 32; i++)
|
||||||
|
data[31 - i] = hash.begin()[i];
|
||||||
|
return SendMessage(MSG_HASHTX, data, 32);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CZMQPublishRawBlockNotifier::NotifyBlock(const CBlockIndex *pindex)
|
||||||
|
{
|
||||||
|
LogPrint("zmq", "zmq: Publish rawblock %s\n", pindex->GetBlockHash().GetHex());
|
||||||
|
|
||||||
|
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
|
||||||
|
{
|
||||||
|
LOCK(cs_main);
|
||||||
|
CBlock block;
|
||||||
|
if(!ReadBlockFromDisk(block, pindex))
|
||||||
|
{
|
||||||
|
zmqError("Can't read block from disk");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ss << block;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SendMessage(MSG_RAWBLOCK, &(*ss.begin()), ss.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CZMQPublishRawTransactionNotifier::NotifyTransaction(const CTransaction &transaction)
|
||||||
|
{
|
||||||
|
uint256 hash = transaction.GetHash();
|
||||||
|
LogPrint("zmq", "zmq: Publish rawtx %s\n", hash.GetHex());
|
||||||
|
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
|
||||||
|
ss << transaction;
|
||||||
|
return SendMessage(MSG_RAWTX, &(*ss.begin()), ss.size());
|
||||||
|
}
|
|
@ -0,0 +1,55 @@
|
||||||
|
// 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 CBlockIndex;
|
||||||
|
|
||||||
|
class CZMQAbstractPublishNotifier : public CZMQAbstractNotifier
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
uint32_t nSequence; //! upcounting per message sequence number
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
/* send zmq multipart message
|
||||||
|
parts:
|
||||||
|
* command
|
||||||
|
* data
|
||||||
|
* message sequence number
|
||||||
|
*/
|
||||||
|
bool SendMessage(const char *command, const void* data, size_t size);
|
||||||
|
|
||||||
|
bool Initialize(void *pcontext);
|
||||||
|
void Shutdown();
|
||||||
|
};
|
||||||
|
|
||||||
|
class CZMQPublishHashBlockNotifier : public CZMQAbstractPublishNotifier
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
bool NotifyBlock(const CBlockIndex *pindex);
|
||||||
|
};
|
||||||
|
|
||||||
|
class CZMQPublishHashTransactionNotifier : public CZMQAbstractPublishNotifier
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
bool NotifyTransaction(const CTransaction &transaction);
|
||||||
|
};
|
||||||
|
|
||||||
|
class CZMQPublishRawBlockNotifier : public CZMQAbstractPublishNotifier
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
bool NotifyBlock(const CBlockIndex *pindex);
|
||||||
|
};
|
||||||
|
|
||||||
|
class CZMQPublishRawTransactionNotifier : public CZMQAbstractPublishNotifier
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
bool NotifyTransaction(const CTransaction &transaction);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // BITCOIN_ZMQ_ZMQPUBLISHNOTIFIER_H
|
Loading…
Reference in New Issue