Fulcrum/BitcoinD.cpp
2019-11-13 01:23:20 +02:00

144 lines
4 KiB
C++

#include "BitcoinD.h"
BitcoinDMgr::BitcoinDMgr(const QHostAddress &host, quint16 port,
const QString &user, const QString &pass)
: Mgr(nullptr), IdMixin(newId()), host(host), port(port), user(user), pass(pass)
{
setObjectName("BitcoinDMgr");
_thread.setObjectName(objectName());
}
BitcoinDMgr::~BitcoinDMgr() { cleanup(); }
void BitcoinDMgr::startup() {
Log() << objectName() << ": starting " << N_CLIENTS << " bitcoin rpc clients ...";
for (auto & client : clients) {
client = std::make_unique<BitcoinD>(host, port, user, pass);
client->start();
}
start();
Log() << objectName() << ": started ok";
}
void BitcoinDMgr::cleanup() {
stop();
for (auto & client : clients) {
client.reset(); /// implicitly calls client->stop()
}
Debug() << "BitcoinDMgr cleaned up";
}
QObject *BitcoinDMgr::qobj() { return this; }
auto BitcoinDMgr::stats() const -> Stats
{
Stats ret;
QVariantList l;
for (const auto & client : clients) {
if (!client) continue;
l += Util::LambdaOnObjectNoThrow<QVariantMap>(client.get(), [c=client.get()]{ return c->getStats(); }, 250).value_or(QVariantMap());
}
ret["Bitcoin Daemon"] = l;
return ret;
}
QVariantMap BitcoinD::getStats() const
{
QVariantMap ret, m;
m["connectedTime"] = isGood() ? QVariant(double(Util::getTime() - connectedTS)/1e3) : QVariant();
m["nBytesSent"] = nSent.load();
m["nBytesReceived"] = nReceived.load();
m["host:port"] = QString("%1:%2").arg(host.toString()).arg(port);
m["idleTime"] = isGood() ? QVariant(double(Util::getTime() - lastGood)/1e3) : QVariant();
m["lastPeerError"] = badAuth ? "Auth Failure" : lastPeerError;
m["lastSocketError"] = lastSocketError;
m["nDisconnects"] = nDisconnects.load();
m["nSocketErrors"] = nSocketErrors.load();
ret[objectName()] = m;
return ret;
}
BitcoinD::BitcoinD(const QHostAddress &host, quint16 port, const QString & user, const QString &pass)
: RPC::HttpConnection(RPC::MethodMap{}, newId(), nullptr), host(host), port(port)
{
static int N = 1;
setObjectName(QString("BitcoinD.%1").arg(N++));
_thread.setObjectName(objectName());
setAuth(user, pass);
setV1(true); // bitcoind uses jsonrpc v1
pingtime_ms = 10000;
stale_threshold = pingtime_ms * 2;
}
BitcoinD::~BitcoinD()
{
stop();
}
QObject * BitcoinD::qobj() { return this; }
void BitcoinD::on_started()
{
ThreadObjectMixin::on_started();
{ // setup the "reconnect timer"
const auto SetTimer = [this] {
callOnTimerSoon(5000, "reconnectTimer", [this]{
if (!isGood()) {
Debug() << prettyName() << " reconnecting...";
reconnect();
return true;
}
return false;
});
};
conns += connect(this, &BitcoinD::lostConnection, this, [SetTimer]{
Log() << "Lost connection to bitcoind, will retry every 5 seconds ...";
SetTimer();
});
conns += connect(this, &BitcoinD::authFailure, this, [SetTimer, this] {
Error() << "Authentication to bitcoind rpc failed. Please check the rpcuser and rpcpass are correct and restart!";
badAuth = true;
SetTimer();
});
conns += connect(this, &BitcoinD::connected, this, [this] { stopTimer("reconnectTimer"); });
SetTimer();
}
reconnect();
}
void BitcoinD::reconnect()
{
if (socket) delete socket;
socket = new QTcpSocket(this);
socketConnectSignals();
socket->connectToHost(host, port);
}
void BitcoinD::on_connected()
{
RPC::HttpConnection::on_connected();
lastGood = Util::getTime();
nSent = nReceived = 0;
lastPeerError.clear();
lastSocketError.clear();
badAuth = false;
emit connected(this);
do_ping();
}
void BitcoinD::do_ping()
{
if (isStale()) {
Debug() << "Stale connection, reconnecting.";
reconnect();
} else
emit sendRequest(newId(), "getblockcount");
}