mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-04-29 14:59:39 -04:00
Merge bitcoin/bitcoin#27679: ZMQ: Support UNIX domain sockets
21d0e6c7b7
doc: release notes for PR 27679 (Matthew Zipkin)791dea204e
test: cover unix sockets in zmq interface (Matthew Zipkin)c87b0a0ff4
zmq: accept unix domain socket address for notifier (Matthew Zipkin) Pull request description: This is a follow-up to https://github.com/bitcoin/bitcoin/pull/27375, allowing ZMQ notifications to be published to a UNIX domain socket. Fortunately, libzmq handles unix sockets already, all we really have to do to support it is allow the format in the actual option. [libzmq](https://libzmq.readthedocs.io/en/latest/zmq_ipc.html) uses the prefix `ipc://` as opposed to `unix:` which is [used by Tor](https://gitlab.torproject.org/tpo/core/tor/-/blob/main/doc/man/tor.1.txt?ref_type=heads#L1475) and now also by [bitcoind](a85e5a7c9a/doc/release-notes-27375.md (L5)
) so we need to switch that internally. As far as I can tell, [LND](d20a764486/zmq.go (L38)
) supports `ipc://` and `unix://` (notice the double slashes). With this patch, LND can connect to bitcoind using unix sockets: Example: *bitcoin.conf*: ``` zmqpubrawblock=unix:/tmp/zmqsb zmqpubrawtx=unix:/tmp/zmqst ``` *lnd.conf*: ``` bitcoind.zmqpubrawblock=ipc:///tmp/zmqsb bitcoind.zmqpubrawtx=ipc:///tmp/zmqst ``` ACKs for top commit: laanwj: Code review ACK21d0e6c7b7
tdb3: crACK for21d0e6c7b7
. Changes lgtm. Will follow up with some testing within the next few days as time allows. achow101: ACK21d0e6c7b7
guggero: Tested and code review ACK21d0e6c7b7
Tree-SHA512: ffd50222e80dd029d903e5ddde37b83f72dfec1856a3f7ce49da3b54a45de8daaf80eea1629a30f58559f4b8ded0b29809548c0638cd1c2811b2736ad8b73030
This commit is contained in:
commit
04c90f1059
5 changed files with 50 additions and 21 deletions
2
doc/release-notes-27679.md
Normal file
2
doc/release-notes-27679.md
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
- unix socket paths are now accepted for `-zmqpubrawblock` and `-zmqpubrawtx` with
|
||||||
|
the format `-zmqpubrawtx=unix:/path/to/file`
|
37
src/init.cpp
37
src/init.cpp
|
@ -1301,30 +1301,33 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const std::string port_option : {
|
for (const auto &port_option : std::vector<std::pair<std::string, bool>>{
|
||||||
"-i2psam",
|
// arg name UNIX socket support
|
||||||
"-onion",
|
{"-i2psam", false},
|
||||||
"-proxy",
|
{"-onion", true},
|
||||||
"-rpcbind",
|
{"-proxy", true},
|
||||||
"-torcontrol",
|
{"-rpcbind", false},
|
||||||
"-whitebind",
|
{"-torcontrol", false},
|
||||||
"-zmqpubhashblock",
|
{"-whitebind", false},
|
||||||
"-zmqpubhashtx",
|
{"-zmqpubhashblock", true},
|
||||||
"-zmqpubrawblock",
|
{"-zmqpubhashtx", true},
|
||||||
"-zmqpubrawtx",
|
{"-zmqpubrawblock", true},
|
||||||
"-zmqpubsequence",
|
{"-zmqpubrawtx", true},
|
||||||
|
{"-zmqpubsequence", true}
|
||||||
}) {
|
}) {
|
||||||
for (const std::string& socket_addr : args.GetArgs(port_option)) {
|
const std::string arg{port_option.first};
|
||||||
|
const bool unix{port_option.second};
|
||||||
|
for (const std::string& socket_addr : args.GetArgs(arg)) {
|
||||||
std::string host_out;
|
std::string host_out;
|
||||||
uint16_t port_out{0};
|
uint16_t port_out{0};
|
||||||
if (!SplitHostPort(socket_addr, port_out, host_out)) {
|
if (!SplitHostPort(socket_addr, port_out, host_out)) {
|
||||||
#if HAVE_SOCKADDR_UN
|
#if HAVE_SOCKADDR_UN
|
||||||
// Allow unix domain sockets for -proxy and -onion e.g. unix:/some/file/path
|
// Allow unix domain sockets for some options e.g. unix:/some/file/path
|
||||||
if ((port_option != "-proxy" && port_option != "-onion") || socket_addr.find(ADDR_PREFIX_UNIX) != 0) {
|
if (!unix || socket_addr.find(ADDR_PREFIX_UNIX) != 0) {
|
||||||
return InitError(InvalidPortErrMsg(port_option, socket_addr));
|
return InitError(InvalidPortErrMsg(arg, socket_addr));
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
return InitError(InvalidPortErrMsg(port_option, socket_addr));
|
return InitError(InvalidPortErrMsg(arg, socket_addr));
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
#include <kernel/chain.h>
|
#include <kernel/chain.h>
|
||||||
#include <kernel/mempool_entry.h>
|
#include <kernel/mempool_entry.h>
|
||||||
#include <logging.h>
|
#include <logging.h>
|
||||||
|
#include <netbase.h>
|
||||||
#include <primitives/block.h>
|
#include <primitives/block.h>
|
||||||
#include <primitives/transaction.h>
|
#include <primitives/transaction.h>
|
||||||
#include <validationinterface.h>
|
#include <validationinterface.h>
|
||||||
|
@ -57,7 +58,12 @@ std::unique_ptr<CZMQNotificationInterface> CZMQNotificationInterface::Create(std
|
||||||
{
|
{
|
||||||
std::string arg("-zmq" + entry.first);
|
std::string arg("-zmq" + entry.first);
|
||||||
const auto& factory = entry.second;
|
const auto& factory = entry.second;
|
||||||
for (const std::string& address : gArgs.GetArgs(arg)) {
|
for (std::string& address : gArgs.GetArgs(arg)) {
|
||||||
|
// libzmq uses prefix "ipc://" for UNIX domain sockets
|
||||||
|
if (address.substr(0, ADDR_PREFIX_UNIX.length()) == ADDR_PREFIX_UNIX) {
|
||||||
|
address.replace(0, ADDR_PREFIX_UNIX.length(), ADDR_PREFIX_IPC);
|
||||||
|
}
|
||||||
|
|
||||||
std::unique_ptr<CZMQAbstractNotifier> notifier = factory();
|
std::unique_ptr<CZMQAbstractNotifier> notifier = factory();
|
||||||
notifier->SetType(entry.first);
|
notifier->SetType(entry.first);
|
||||||
notifier->SetAddress(address);
|
notifier->SetAddress(address);
|
||||||
|
|
|
@ -9,4 +9,7 @@
|
||||||
|
|
||||||
void zmqError(const std::string& str);
|
void zmqError(const std::string& str);
|
||||||
|
|
||||||
|
/** Prefix for unix domain socket addresses (which are local filesystem paths) */
|
||||||
|
const std::string ADDR_PREFIX_IPC = "ipc://"; // used by libzmq, example "ipc:///root/path/to/file"
|
||||||
|
|
||||||
#endif // BITCOIN_ZMQ_ZMQUTIL_H
|
#endif // BITCOIN_ZMQ_ZMQUTIL_H
|
||||||
|
|
|
@ -3,7 +3,9 @@
|
||||||
# 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.
|
||||||
"""Test the ZMQ notification interface."""
|
"""Test the ZMQ notification interface."""
|
||||||
|
import os
|
||||||
import struct
|
import struct
|
||||||
|
import tempfile
|
||||||
from time import sleep
|
from time import sleep
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
|
|
||||||
|
@ -30,7 +32,7 @@ from test_framework.util import (
|
||||||
from test_framework.wallet import (
|
from test_framework.wallet import (
|
||||||
MiniWallet,
|
MiniWallet,
|
||||||
)
|
)
|
||||||
from test_framework.netutil import test_ipv6_local
|
from test_framework.netutil import test_ipv6_local, test_unix_socket
|
||||||
|
|
||||||
|
|
||||||
# Test may be skipped and not have zmq installed
|
# Test may be skipped and not have zmq installed
|
||||||
|
@ -119,6 +121,10 @@ class ZMQTest (BitcoinTestFramework):
|
||||||
self.ctx = zmq.Context()
|
self.ctx = zmq.Context()
|
||||||
try:
|
try:
|
||||||
self.test_basic()
|
self.test_basic()
|
||||||
|
if test_unix_socket():
|
||||||
|
self.test_basic(unix=True)
|
||||||
|
else:
|
||||||
|
self.log.info("Skipping ipc test, because UNIX sockets are not supported.")
|
||||||
self.test_sequence()
|
self.test_sequence()
|
||||||
self.test_mempool_sync()
|
self.test_mempool_sync()
|
||||||
self.test_reorg()
|
self.test_reorg()
|
||||||
|
@ -139,7 +145,7 @@ class ZMQTest (BitcoinTestFramework):
|
||||||
socket.setsockopt(zmq.IPV6, 1)
|
socket.setsockopt(zmq.IPV6, 1)
|
||||||
subscribers.append(ZMQSubscriber(socket, topic.encode()))
|
subscribers.append(ZMQSubscriber(socket, topic.encode()))
|
||||||
|
|
||||||
self.restart_node(0, [f"-zmqpub{topic}={address}" for topic, address in services])
|
self.restart_node(0, [f"-zmqpub{topic}={address.replace('ipc://', 'unix:')}" for topic, address in services])
|
||||||
|
|
||||||
for i, sub in enumerate(subscribers):
|
for i, sub in enumerate(subscribers):
|
||||||
sub.socket.connect(services[i][1])
|
sub.socket.connect(services[i][1])
|
||||||
|
@ -176,12 +182,19 @@ class ZMQTest (BitcoinTestFramework):
|
||||||
|
|
||||||
return subscribers
|
return subscribers
|
||||||
|
|
||||||
def test_basic(self):
|
def test_basic(self, unix = False):
|
||||||
|
self.log.info(f"Running basic test with {'ipc' if unix else 'tcp'} protocol")
|
||||||
|
|
||||||
# Invalid zmq arguments don't take down the node, see #17185.
|
# Invalid zmq arguments don't take down the node, see #17185.
|
||||||
self.restart_node(0, ["-zmqpubrawtx=foo", "-zmqpubhashtx=bar"])
|
self.restart_node(0, ["-zmqpubrawtx=foo", "-zmqpubhashtx=bar"])
|
||||||
|
|
||||||
address = f"tcp://127.0.0.1:{self.zmq_port_base}"
|
address = f"tcp://127.0.0.1:{self.zmq_port_base}"
|
||||||
|
|
||||||
|
if unix:
|
||||||
|
# Use the shortest temp path possible since paths may have as little as 92-char limit
|
||||||
|
socket_path = tempfile.NamedTemporaryFile().name
|
||||||
|
address = f"ipc://{socket_path}"
|
||||||
|
|
||||||
subs = self.setup_zmq_test([(topic, address) for topic in ["hashblock", "hashtx", "rawblock", "rawtx"]])
|
subs = self.setup_zmq_test([(topic, address) for topic in ["hashblock", "hashtx", "rawblock", "rawtx"]])
|
||||||
|
|
||||||
hashblock = subs[0]
|
hashblock = subs[0]
|
||||||
|
@ -247,6 +260,8 @@ class ZMQTest (BitcoinTestFramework):
|
||||||
])
|
])
|
||||||
|
|
||||||
assert_equal(self.nodes[1].getzmqnotifications(), [])
|
assert_equal(self.nodes[1].getzmqnotifications(), [])
|
||||||
|
if unix:
|
||||||
|
os.unlink(socket_path)
|
||||||
|
|
||||||
def test_reorg(self):
|
def test_reorg(self):
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue