Adds publishing blocks and transactions over ZMQ #4594

pull jmcorgan wants to merge 2 commits into bitcoin:master from jmcorgan:zmq changing 14 files +441 −5
  1. jmcorgan commented at 9:05 pm on July 27, 2014: contributor

    Block and Transaction Broadcasting With ZeroMQ

    ZeroMQ 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 binds a “publish” port that broadcasts all newly arrived and validated blocks and transactions to one or more connected subscribers. 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.

    In this fashion, external software can use a trusted Bitcoin Core daemon to do the heavy lifting of communicating with the P2P network, while still receiving network information asynchronously in real time. As transactions and blocks arrive via the P2P network, Bitcoin Core will apply all the validation and standardization rules to this data, only passing along through ZeroMQ those items that pass.

    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 disabled. Two steps are required to enable–compiling in the ZeroMQ code, and configuring runtime operation on the command-line or configuration file.

    0$ ./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.

    Configuration

    Currently, the ZeroMQ facility only needs to have the ZeroMQ endpoint specified:

    0$ bitcoind -zmqpub=tcp://127.0.0.1/28332
    

    This will cause bitcoind to establish a PUB listening socket at the specified host address and port number. The endpoint specifier may also be provided as the equivalent line item in bitcoin.conf.

    ZeroMQ endpoint specifiers for TCP (and others) are documented in the ZeroMQ API.

    Operation

    ZeroMQ publish sockets prepend each data item with an arbitrary topic prefix that allows subscriber clients to request only those items with a matching prefix. When publishing, bitcoind will prepend the topic “TXN” (no quotes, no null terminator) to the binary, serialized form of a published transaction, and “BLK” to the binary, serialized form of a published block.

    Client side, then, the ZeroMQ subscriber socket must have the ZMQ_SUBSCRIBE option set to one or either of these prefixes; without doing so will result in no messages arriving.

    Here is a small example, in the Python language, using the python-zmq wrapper:

     0import zmq
     1import binascii
     2
     3port = 28332
     4topic1 = "BLK"
     5topic2 = "TXN"
     6topic_len = len(topic1)
     7
     8zmqContext = zmq.Context()
     9zmqSubSocket = zmqContext.socket(zmq.SUB)
    10zmqSubSocket.setsockopt(zmq.SUBSCRIBE, topic1)
    11zmqSubSocket.setsockopt(zmq.SUBSCRIBE, topic2)
    12zmqSubSocket.connect("tcp://127.0.0.1:%i" % port)
    13
    14def handleBLK(blk):
    15    print "-BLKHDR-"
    16    print binascii.hexlify(blk[:80])
    17
    18def handleTX(tx):
    19    print "-TX-"
    20    print binascii.hexlify(tx)
    21
    22try:
    23    while True:
    24        msg = zmqSubSocket.recv()
    25        msg_topic = msg[:topic_len]
    26        msg_data  = msg[topic_len:]
    27
    28        if msg_topic == "TXN":
    29            handleTX(msg_data)
    30        elif msg_topic == "BLK":
    31            handleBLK(msg_data)
    32
    33except KeyboardInterrupt:
    34    zmqContext.destroy()
    

    This example is provided in the contrib/zmq directory of the source code.

    Security Considerations

    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.

    Transactions and blocks are broadcast in their serialized form directly as received and validated by bitcoind. External software may assume that these have passed all validity/consensus/standard checks, but of course is free to perform these functions again in part or in whole.

  2. jmcorgan commented at 9:23 pm on July 27, 2014: contributor
    Pull tester unhappy, but merge, default configure, compile, and make check all pass ok here.
  3. in src/main.cpp: in 2480568975 outdated
    2429@@ -2428,7 +2430,7 @@ bool AcceptBlockHeader(CBlockHeader& block, CValidationState& state, CBlockIndex
    2430             return state.DoS(100, error("AcceptBlock() : forked chain older than last checkpoint (height %d)", nHeight));
    2431 
    2432         // Reject block.nVersion=1 blocks when 95% (75% on testnet) of the network has upgraded:
    2433-        if (block.nVersion < 2 && 
    


    laanwj commented at 7:13 am on July 28, 2014:
    No whitespace changes please, they’re going to be solved in one go at some point with clang-format (see #4498).

    jmcorgan commented at 8:02 am on July 28, 2014:
    Ok. My emacs is configured to automatically remove trailing whitespace, so this was unintended. I’ll figure out a way to make a clean diff.
  4. in src/main.h: in 2480568975 outdated
    823@@ -824,7 +824,7 @@ class CBlockIndex
    824 
    825     /**
    826      * Returns true if there are nRequired or more blocks of minVersion or above
    827-     * in the last Params().ToCheckBlockUpgradeMajority() blocks, starting at pstart 
    


    laanwj commented at 7:14 am on July 28, 2014:
    Same here - this file doesn’t even need to be touched.
  5. in contrib/zmq/README.md: in 2480568975 outdated
    0@@ -0,0 +1,145 @@
    1+# Block and Transaction Broadcasting With ZeroMQ
    


    laanwj commented at 7:15 am on July 28, 2014:
    it makes sense to make this doc/zmq.md so that it’s easier to find.
  6. laanwj commented at 7:16 am on July 28, 2014: member

    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

    Thanks for that. For obelisk I had to find so many wrappers, high level wrappers, c++ wrappers for high-level wrappers (only some of them with an ubuntu package, which ofc was too old) that at some point I gave up.

  7. in src/zmqports.cpp: in 2480568975 outdated
    20+static void *zmqPubSocket;
    21+
    22+// Internal utility functions
    23+static void zmqLog(const char *str)
    24+{
    25+  LogPrint("net", "%s %s\n", "ZMQ:", str);
    


    laanwj commented at 7:24 am on July 28, 2014:
    Shouldn’t this be its own category, zmq?

    jmcorgan commented at 8:03 am on July 28, 2014:
    I’ll change this.
  8. in src/zmqports.cpp: in 2480568975 outdated
    25+  LogPrint("net", "%s %s\n", "ZMQ:", str);
    26+}
    27+
    28+static void zmqError(const char *str)
    29+{
    30+  LogPrint("net", "%s: %s\n", str, zmq_strerror(errno));
    


    laanwj commented at 7:25 am on July 28, 2014:
    Errors should be logged without category, to make sure that they appear also if no debug category is passed.
  9. in src/zmqports.cpp: in 2480568975 outdated
    54+    return false;
    55+  }
    56+
    57+  std::ostringstream status;
    58+  status << "PUB socket listening at " << endp;
    59+  zmqLog(status.str().c_str());
    


    laanwj commented at 7:30 am on July 28, 2014:

    Try to avoid using c_str(), we just got rid of all those - just change the function to pass a string.

    In general: why do you have the one-line zmqLog/zmqError wrapper functions at all? I first assumed they would be callbacks passed to zeromq, but that doesn’t seem to be the case.

    No need to use ostringstream here either. Just

    0LogPrint("zmq",  "PUB socket listening at %s\n", endp);
    

    would do.


    laanwj commented at 10:00 am on July 28, 2014:
    Another advantage of using ogging directly is that LogPrint is ’early-out’. No formatting overhead happens at all if the log category is filtered.
  10. laanwj commented at 7:30 am on July 28, 2014: member
    Looks good to me apart from the nits – async notification is superior to polling. Only minimal impact on the current code and is a no-op if --enable-zmq isn’t passed to configure.
  11. jmcorgan commented at 3:06 pm on July 28, 2014: contributor

    Per feedback, updated and rebased commit:

    • Removed unintended whitespace changes
    • Moved contrib/zmq/README.md to doc/zmq.md
    • New logging category ‘zmq’ created
    • Cleaned up logging
  12. jgarzik commented at 3:20 pm on July 28, 2014: contributor

    Concept ACK. Supporting ZeroMQ in this fashion (publish public data) is a useful feature.

    One of the problems the Linux kernel ran into early on was a profusion of #ifdef’d features. Bitcoin Core does not have this problem, but it is creeping in that direction.

    As mentioned in another github issue days ago, conditionally compiled code is evil. Such code is only compiled by a few, rather than all. As a result, the code quickly breaks because it is not used by the primary developers.

    The solution is to create wrappers for the #ifndef MY_FEATURE branch in your my_feature.h, which creates an API that becomes a no-op at compile time. That way, zeromq code is always built, without ifdefs in the main codebase. As such, zeromq calls are built by all, and less likely to be broken.

  13. jmcorgan commented at 3:28 pm on July 28, 2014: contributor
    I intentionally made this such that you have to explicitly –enable-zmq in order to compile it in, for testing purposes. If this gets merged, at some point we’ll decide to enable it for everyone. At that point, I’ll rework things so that there are no ifdef’s–instead, if libzmq-dev is not detected at configure time, the function calls become NOPs internally.
  14. jgarzik commented at 3:30 pm on July 28, 2014: contributor

    There should be no need to disable zmq by default at compile time. Disabled at runtime is sufficient.

    Typical pattern is for configure to find a library and enable the building of that feature automatically, without –enable-foo support. You don’t want to make the user work harder, just to get new features.

    Also, I’m in general strongly against doesn’t-compile-by-default code. It’s a recipe for bitrot.

  15. jmcorgan commented at 3:31 pm on July 28, 2014: contributor
    I’ll go ahead then and rework this to eliminate the –enable-zmq and disable the feature at runtime if libzmq-dev is not detected.
  16. jmcorgan commented at 3:43 pm on July 28, 2014: contributor
    Looks like I missed a logging simplification; I’ll get this when I make the above changes.
  17. laanwj commented at 3:43 pm on July 28, 2014: member
    I strongly disagree with @jgarzik here. I don’t want a fixed dependency on zeromq.
  18. jmcorgan commented at 3:45 pm on July 28, 2014: contributor
    To be clear–my plan is to make it such that if ZeroMQ is missing, the feature is disabled, so there won’t be a dependency. Did I misunderstand?
  19. laanwj commented at 3:46 pm on July 28, 2014: member
    That’s fine with me. Defaulting to having it enabled when the library is installed sounds sane. And it should still be possible to override the autodetection with --with[out]-zeromq, IMO.
  20. sipa commented at 3:48 pm on July 28, 2014: member

    @jgarzik dislikes conditionally compiled code, and wants to have the ZeroMQ code always compiled (thus always having the dependency). @laanwj dislikes dependencies, and thus prefers the conditional compilation you’re doing now.

    I agree with @laanwj here. I really want to minimize dependencies. Another way of doing it is exposing the signals interface for such notifications, and register them from an optionally compiled object file (the same could be done fo -blocknotify, etc). That avoid #ifdefs entirely.

  21. jmcorgan commented at 10:13 pm on July 28, 2014: contributor

    Ok, this has been reworked as follows:

    • ZMQ features are automatically implemented if libzmq-dev is found, and a warning issued if not found
    • One can disable the ZMQ features with –disable-zmq
    • All conditional code is isolated to zmqports.cpp; code becomes NOP if zmq is disabled.
    • A new RPC call, ‘getzmqurl’, is provided to return the configuration option -zmqpub text.
  22. jmcorgan commented at 5:22 am on July 31, 2014: contributor
    Minor rebase to accommodate ‘rawtx’ merge.
  23. laanwj added the label RPC on Jul 31, 2014
  24. jtimon commented at 1:00 pm on July 31, 2014: contributor
    I didn’t test it but I like what it does.
  25. jmcorgan commented at 8:55 am on August 12, 2014: contributor
    Minor rebase to accommodate RPC help categorization.
  26. randy-waterhouse commented at 11:12 pm on August 17, 2014: contributor

    Suggest minor change to add check for libzmq version >=2.1 requirement.

    PKG_CHECK_MODULES([ZMQ],[libzmq >= 2.1], [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 >=2.1 not found, disabling])

  27. Adds publishing blocks and transactions over ZMQ
    ZMQ-based features are encapsulated in zmqports.{cpp,h} and disabled
    if libzmq-dev is not found, or configure is invoked with
    --disable-zmq.
    
    Use -zmqpub=<endpoint> to use feature.
    
    See contrib/zmq/README.md for details.
    3658e937e3
  28. zmq: require version >= 2.1 3c9d0c12ac
  29. jmcorgan commented at 1:50 pm on August 18, 2014: contributor
    Rebase and add zmq version check as requested.
  30. BitcoinPullTester commented at 6:48 pm on August 18, 2014: none

    Automatic sanity-testing: FAILED BUILD/TEST, see http://jenkins.bluematt.me/pull-tester/p4594_3c9d0c12ac2476fc8fb5de9293edbf27081b1d4e/ for binaries and test log.

    This could happen for one of several reasons:

    1. It chanages paths in makefile.linux-mingw or otherwise changes build scripts in a way that made them incompatible with the automated testing scripts (please tweak those patches in qa/pull-tester)
    2. It adds/modifies tests which test network rules (thanks for doing that), which conflicts with a patch applied at test time
    3. It does not build on either Linux i386 or Win32 (via MinGW cross compile)
    4. The test suite fails on either Linux i386 or Win32
    5. The block test-cases failed (lookup the first bNN identifier which failed in https://github.com/TheBlueMatt/test-scripts/blob/master/FullBlockTestGenerator.java)

    If you believe this to be in error, please ping BlueMatt on freenode or TheBlueMatt here.

    This test script verifies pulls every time they are updated. It, however, dies sometimes and fails to test properly. If you are waiting on a test, please check timestamps to verify that the test.log is moving at http://jenkins.bluematt.me/pull-tester/current/ Contact BlueMatt on freenode if something looks broken.

  31. jgarzik commented at 0:18 am on August 30, 2014: contributor
    PR #4599 (commit c7b6117debf4ebabc464a55b840bdd7bdeb94fa3) which was just merged illustrates a useful plugin approach. Stick one or more signals in the needed locations, and then ZMQ or other features add their own hooks.
  32. jgarzik commented at 2:22 am on September 15, 2014: contributor

    To make the last comment more explicit, I do not like seeing “zmq” in main.cpp or net.cpp.

    This pull request has my ACK iff the ZMQPublish*() calls are turned into ui_interface.h signals, as illustrated in c7b6117debf4ebabc464a55b840bdd7bdeb94fa3. No objections other than that.

  33. jmcorgan commented at 1:07 pm on September 18, 2014: contributor
    Thanks for the clarification. It will be a week or two until I can turn my attention back to this, but I’ll look over the signaling mechanism and see how I can refit this patch to use that.
  34. btcdrak commented at 4:42 pm on November 7, 2014: contributor
    needs rebased
  35. jgarzik commented at 5:12 pm on November 7, 2014: contributor
    I’m tempted to take this patch, add signals, and re-PR. ZMQ is conceptually acceptable AFAIK.
  36. btcdrak commented at 5:17 pm on November 7, 2014: contributor
    @jgarzik do it!
  37. theuni commented at 5:54 pm on November 7, 2014: member
    @jgarzik If you do, please take https://github.com/theuni/bitcoin/commit/dd8d00b63c4ced2c681e9cbfaaebb8cc9318d613 and https://github.com/theuni/bitcoin/commit/25844d3c6fcac8900aa68487f2dae0b3141f3ab6 (feel free to squash) so that travis can build. Those are quite old though, let me know if there are merge problems and I’ll rebase.
  38. jmcorgan commented at 6:23 pm on November 7, 2014: contributor
    @jgarzik Go for it. I haven’t had much time to spend on bitcoin development these last few months, but I’d still like to see this get integrated.
  39. laanwj commented at 11:32 am on November 19, 2014: member
    Closing in favor of #5303.
  40. laanwj closed this on Jan 8, 2015

  41. jmcorgan deleted the branch on Sep 19, 2015
  42. DrahtBot locked this on Sep 8, 2021

github-metadata-mirror

This is a metadata mirror of the GitHub repository bitcoin/bitcoin. This site is not affiliated with GitHub. Content is generated from a GitHub metadata backup.
generated: 2024-10-04 22:12 UTC

This site is hosted by @0xB10C
More mirrored repositories can be found on mirror.b10c.me