Skip to content

Commit

Permalink
bchd support: added --bitcoind-tls option
Browse files Browse the repository at this point in the history
This option can be specified on the CLI as `--bitcoind-tls` or in the conf
file as `bitcoind-tls = true`.  If enabled, then we will connect to the
remote bitcoind using TLS (https) rather than a bare TCP socket (http).
This allows Fulcrum to work flawlessly with default bchd setups, which
enable TLS for all HTTP connections by default (unless the `notls` option
is given to bchd).

See issues #43 and #28.
  • Loading branch information
cculianu committed Jul 27, 2020
1 parent 00c959e commit ce4b969
Show file tree
Hide file tree
Showing 9 changed files with 66 additions and 12 deletions.
11 changes: 11 additions & 0 deletions doc/fulcrum-example-config.conf
Original file line number Diff line number Diff line change
Expand Up @@ -538,6 +538,17 @@ rpcpassword = hunter1
#bitcoind_throttle = 50 20 5


# Bitcoin daemon RPC uses TLS (HTTPS) - 'bitcoind-tls' - DEFAULT: false
#
# If true, connect to the remote bitcoind via HTTPS rather than the usual HTTP.
# Historically, bitcoind supported only JSON-RPC over HTTP; however, some
# implementations such as bchd support HTTPS. If you are using Fulcrum with
# bchd, you either need to start bchd with the `notls` option, or you need to
# specify this option to Fulcrum.
#
#bitcoind-tls = false


# Keep RocksDB Log Files - 'db_keep_log_file_num' - DEAFULT: 5
#
# The maximum number of database log files to keep around on disk, per database.
Expand Down
4 changes: 4 additions & 0 deletions doc/fulcrum-quick-config.conf
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ datadir = /path/to/a/dir # Windows: datadir = D:\FulcrumData\mainnet
# rpcport= in your bitcoind .conf file.
bitcoind = 127.0.0.1:8332

# *OPTIONAL* Use this option to connect to bitcoind via HTTPS rather than the
# usual HTTP. This option typically is only used with `bchd`.
#bitcoind-tls = true

# *REQUIRED* This is the bitcoind RPC username you specified in your bitciond
# .conf file. This corresponds to the rpcuser= from that file.
rpcuser = Bob_The_Banker
Expand Down
3 changes: 3 additions & 0 deletions doc/unix-man-page.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,9 @@ Once the server finishes synching it will behave like an ElectronX/ElectrumX ser
-b, --bitcoind <hostname:port>
: Specify a <hostname:port> to connect to the bitcoind rpc service. This is a required option, along with -u and -p. This hostname:port should be the same as you specified in your bitcoin.conf file under rpcbind- and rpcport-.

--bitcoind-tls
: If specified, connect to the remote bitcoind via HTTPS rather than the usual HTTP. Historically, bitcoind supported only JSON-RPC over HTTP; however, some implementations such as *bchd* support HTTPS. If you are using *fulcrum* with *bchd*, you either need to start *bchd* with the `notls` option, or you need to specify this option to *fulcrum*.

-u, --rpcuser <username>
: Specify a username to use for authenticating to bitcoind. This is a required option, along with -b and -p. This option should be the same username you specified in your bitcoind.conf file under rpcuser-. For security, you may omit this option from the command-line and use the RPCUSER environment variable instead (the CLI arg takes precedence if both are present).

Expand Down
9 changes: 9 additions & 0 deletions src/App.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,12 @@ void App::parseArgs()
" under rpcbind= and rpcport=."),
QString("hostname:port"),
},
{ "bitcoind-tls",
QString("If specified, connect to the remote bitcoind via HTTPS rather than the usual HTTP. Historically,"
" bitcoind supported only JSON-RPC over HTTP; however, some implementations such as bchd support"
" HTTPS. If you are using " APPNAME " with bchd, you either need to start bchd with the `notls`"
" option, or you need to specify this option to " APPNAME "."),
},
{ { "u", "rpcuser" },
QString("Specify a username to use for authenticating to bitcoind. This is a required option, along"
" with -b and -p. This option should be the same username you specified in your bitcoind.conf file"
Expand Down Expand Up @@ -513,6 +519,9 @@ void App::parseArgs()

// parse bitcoind - conf.value is always unset if parser.value is set, hence this strange constrcution below (parser.value takes precedence)
options->bitcoind = parseHostnamePortPair(conf.value("bitcoind", parser.value("b")));
// --bitcoind-tls
if ((options->bitcoindUsesTls = parser.isSet("bitcoind-tls") || conf.boolValue("bitcoind-tls")))
Util::AsyncOnObject(this, []{ Debug() << "config: bitcoind-tls = true"; });
// grab rpcuser
options->rpcuser = conf.value("rpcuser", parser.isSet("u") ? parser.value("u") : std::getenv(RPCUSER));
// grab rpcpass
Expand Down
40 changes: 31 additions & 9 deletions src/BitcoinD.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
#include <QHostInfo>
#include <QMetaType>
#include <QPointer>
#include <QSslConfiguration>
#include <QSslSocket>

#include <mutex>

Expand All @@ -33,9 +35,8 @@ namespace {
};
}

BitcoinDMgr::BitcoinDMgr(const QString &hostName, quint16 port,
const QString &user, const QString &pass)
: Mgr(nullptr), IdMixin(newId()), hostName(hostName), port(port), user(user), pass(pass)
BitcoinDMgr::BitcoinDMgr(const QString &hostName, quint16 port, const QString &user, const QString &pass, bool useSsl)
: Mgr(nullptr), IdMixin(newId()), hostName(hostName), port(port), user(user), pass(pass), useSsl(useSsl)
{
setObjectName("BitcoinDMgr");
_thread.setObjectName(objectName());
Expand All @@ -54,7 +55,7 @@ void BitcoinDMgr::startup() {
for (auto & client : clients) {
// initial resolvedAddress may be invalid if user specified a hostname, in which case we will resolve it and
// tell bitcoind's to update themselves and reconnect
client = std::make_unique<BitcoinD>(hostName, port, user, pass);
client = std::make_unique<BitcoinD>(hostName, port, user, pass, useSsl);

// connect client to us -- TODO: figure out workflow: how requests for work and results will get dispatched
connect(client.get(), &BitcoinD::gotMessage, this, &BitcoinDMgr::on_Message);
Expand Down Expand Up @@ -581,8 +582,8 @@ auto BitcoinD::stats() const -> Stats
return m;
}

BitcoinD::BitcoinD(const QString &host, quint16 port, const QString & user, const QString &pass, qint64 maxBuffer_)
: RPC::HttpConnection(RPC::MethodMap{}, newId(), nullptr, maxBuffer_), host(host), port(port)
BitcoinD::BitcoinD(const QString &host, quint16 port, const QString & user, const QString &pass, bool useSsl_, qint64 maxBuffer_)
: RPC::HttpConnection(RPC::MethodMap{}, newId(), nullptr, maxBuffer_), host(host), port(port), useSsl(useSsl_)
{
static int N = 1;
setObjectName(QString("BitcoinD.%1").arg(N++));
Expand Down Expand Up @@ -654,9 +655,30 @@ void BitcoinD::on_started()
void BitcoinD::reconnect()
{
if (socket) delete socket;
socket = new QTcpSocket(this);
socketConnectSignals();
socket->connectToHost(host, port);
if (useSsl) {
// remote bitcoind expects https (--bitcoind-tls CLI option); usually this is only for bchd
QSslSocket *ssl;
socket = ssl = new QSslSocket(this);

auto conf = ssl->sslConfiguration();
conf.setPeerVerifyMode(QSslSocket::PeerVerifyMode::VerifyNone);
conf.setProtocol(QSsl::SslProtocol::AnyProtocol);
ssl->setSslConfiguration(conf);

socketConnectSignals();
connect(ssl, qOverload<const QList<QSslError> &>(&QSslSocket::sslErrors), ssl, [ssl](auto errs) {
for (const auto & err : errs)
DebugM("Ignoring SSL error for ", ssl->peerName(), ": ", err.errorString());
ssl->ignoreSslErrors();
});

ssl->connectToHostEncrypted(host, port);
} else {
// regular http bitcoind (default)
socket = new QTcpSocket(this);
socketConnectSignals();
socket->connectToHost(host, port);
}
}

void BitcoinD::on_connected()
Expand Down
7 changes: 5 additions & 2 deletions src/BitcoinD.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ class BitcoinDMgr : public Mgr, public IdMixin, public ThreadObjectMixin, public
{
Q_OBJECT
public:
BitcoinDMgr(const QString &hostnameOrIP, quint16 port, const QString &user, const QString &pass);
BitcoinDMgr(const QString &hostnameOrIP, quint16 port, const QString &user, const QString &pass, bool useSsl);
~BitcoinDMgr() override;

void startup() override; ///< from Mgr
Expand Down Expand Up @@ -114,6 +114,7 @@ protected slots:
const QString hostName;
const quint16 port;
const QString user, pass;
const bool useSsl;

static constexpr int miniTimeout = 333, tinyTimeout = 167, medTimeout = 500, longTimeout = 1000;

Expand Down Expand Up @@ -184,7 +185,8 @@ class BitcoinD : public RPC::HttpConnection, public ThreadObjectMixin /* NB: als
/// This should work for now since we are on 32MiB max block size on BCH anyway right now.
static constexpr qint64 BTCD_DEFAULT_MAX_BUFFER = 100'000'000;

explicit BitcoinD(const QString &host, quint16 port, const QString & user, const QString &pass, qint64 maxBuffer = BTCD_DEFAULT_MAX_BUFFER);
explicit BitcoinD(const QString &host, quint16 port, const QString & user, const QString &pass, bool useSsl,
qint64 maxBuffer = BTCD_DEFAULT_MAX_BUFFER);
~BitcoinD() override;

using ThreadObjectMixin::start;
Expand Down Expand Up @@ -223,6 +225,7 @@ class BitcoinD : public RPC::HttpConnection, public ThreadObjectMixin /* NB: als

const QString host;
const quint16 port;
const bool useSsl;
std::atomic_bool badAuth = false, needAuth = true;
};

Expand Down
2 changes: 1 addition & 1 deletion src/Controller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ void Controller::startup()
// this may take a long time but normally this branch is not taken
dumpScriptHashes(options->dumpScriptHashes);

bitcoindmgr = std::make_shared<BitcoinDMgr>(options->bitcoind.first, options->bitcoind.second, options->rpcuser, options->rpcpassword);
bitcoindmgr = std::make_shared<BitcoinDMgr>(options->bitcoind.first, options->bitcoind.second, options->rpcuser, options->rpcpassword, options->bitcoindUsesTls);
{
auto constexpr waitTimer = "wait4bitcoind", callProcessTimer = "callProcess";
int constexpr msgPeriod = 10000, // 10sec
Expand Down
1 change: 1 addition & 0 deletions src/Options.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ QVariantMap Options::toMap() const
m["cert"] = certFile;
m["key"] = keyFile;
m["bitcoind"] = QString("%1:%2").arg(bitcoind.first).arg(bitcoind.second);
m["bitcoind-tls"] = bitcoindUsesTls;
m["hasIPv6 listener"] = hasIPv6Listener;
m["rpcuser"] = rpcuser.isNull() ? QVariant() : QVariant("<hidden>");
m["rpcpassword"] = rpcpassword.isNull() ? QVariant() : QVariant("<hidden>");
Expand Down
1 change: 1 addition & 0 deletions src/Options.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ struct Options {
QSslKey sslKey; ///< this must be valid if we have any SSL or WSS interfaces.
QString keyFile; ///< saved here for toMap() to remember what was specified in config file
QPair<QString, quint16> bitcoind; ///< hostname, port pair. We resolve bitcoind's actual IP address each time if it's a hostname and not an IP address string.
bool bitcoindUsesTls = false; ///< CLI: --bitcoind-tls. If true, we will connect to the remote bitcoind via SSL/TLS. See BitcoinD.cpp.
QString rpcuser, rpcpassword;
QString datadir; ///< The directory to store the database. It exists and has appropriate permissions (otherwise the app would have quit on startup).
/// If true, on db open/startup, we will perform some slow/paranoid db consistency checks
Expand Down

0 comments on commit ce4b969

Please sign in to comment.