Skip to content

Commit

Permalink
Implement debugging messages and RPC for LLMQ DKGs
Browse files Browse the repository at this point in the history
  • Loading branch information
codablock committed Jan 9, 2019
1 parent 098b094 commit 324406b
Show file tree
Hide file tree
Showing 12 changed files with 706 additions and 1 deletion.
3 changes: 3 additions & 0 deletions src/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ BITCOIN_CORE_H = \
limitedmap.h \
llmq/quorums_blockprocessor.h \
llmq/quorums_commitment.h \
llmq/quorums_debug.h \
llmq/quorums_dkgsessionhandler.h \
llmq/quorums_dkgsessionmgr.h \
llmq/quorums_dkgsession.h \
Expand Down Expand Up @@ -254,6 +255,7 @@ libdash_server_a_SOURCES = \
governance-votedb.cpp \
llmq/quorums_blockprocessor.cpp \
llmq/quorums_commitment.cpp \
llmq/quorums_debug.cpp \
llmq/quorums_dkgsessionhandler.cpp \
llmq/quorums_dkgsessionmgr.cpp \
llmq/quorums_dkgsession.cpp \
Expand Down Expand Up @@ -284,6 +286,7 @@ libdash_server_a_SOURCES = \
rpc/net.cpp \
rpc/rawtransaction.cpp \
rpc/rpcevo.cpp \
rpc/rpcquorums.cpp \
rpc/server.cpp \
script/sigcache.cpp \
script/ismine.cpp \
Expand Down
347 changes: 347 additions & 0 deletions src/llmq/quorums_debug.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,347 @@
// Copyright (c) 2018 The Dash Core developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "quorums_debug.h"

#include "activemasternode.h"
#include "chainparams.h"
#include "net.h"
#include "net_processing.h"
#include "scheduler.h"
#include "validation.h"

#include "evo/deterministicmns.h"
#include "quorums_utils.h"

namespace llmq
{
CDKGDebugManager* quorumDKGDebugManager;

UniValue CDKGDebugSessionStatus::ToJson(int detailLevel) const
{
UniValue ret(UniValue::VOBJ);

if (!Params().GetConsensus().llmqs.count((Consensus::LLMQType)llmqType) || quorumHash.IsNull()) {
return ret;
}

std::vector<CDeterministicMNCPtr> dmnMembers;
if (detailLevel == 2) {
dmnMembers = CLLMQUtils::GetAllQuorumMembers((Consensus::LLMQType) llmqType, quorumHash);
}

ret.push_back(Pair("llmqType", llmqType));
ret.push_back(Pair("quorumHash", quorumHash.ToString()));
ret.push_back(Pair("quorumHeight", (int)quorumHeight));
ret.push_back(Pair("phase", (int)phase));

ret.push_back(Pair("sentContributions", sentContributions));
ret.push_back(Pair("sentComplaint", sentComplaint));
ret.push_back(Pair("sentJustification", sentJustification));
ret.push_back(Pair("sentPrematureCommitment", sentPrematureCommitment));
ret.push_back(Pair("aborted", aborted));

struct ArrOrCount {
int count{0};
UniValue arr{UniValue::VARR};
};

ArrOrCount badMembers;
ArrOrCount weComplain;
ArrOrCount receivedContributions;
ArrOrCount receivedComplaints;
ArrOrCount receivedJustifications;
ArrOrCount receivedPrematureCommitments;
ArrOrCount complaintsFromMembers;

auto add = [&](ArrOrCount& v, size_t idx, bool flag) {
if (flag) {
if (detailLevel == 0) {
v.count++;
} else if (detailLevel == 1) {
v.arr.push_back((int)idx);
} else if (detailLevel == 2) {
UniValue a(UniValue::VOBJ);
a.push_back(Pair("memberIndex", idx));
if (idx < dmnMembers.size()) {
a.push_back(Pair("proTxHash", dmnMembers[idx]->proTxHash.ToString()));
}
v.arr.push_back(a);
}
}
};
auto push = [&](ArrOrCount& v, const std::string& name) {
if (detailLevel == 0) {
ret.push_back(Pair(name, v.count));
} else {
ret.push_back(Pair(name, v.arr));
}
};

for (size_t i = 0; i < members.size(); i++) {
const auto& m = members[i];
add(badMembers, i, m.bad);
add(weComplain, i, m.weComplain);
add(receivedContributions, i, m.receivedContribution);
add(receivedComplaints, i, m.receivedComplaint);
add(receivedJustifications, i, m.receivedJustification);
add(receivedPrematureCommitments, i, m.receivedPrematureCommitment);
}
push(badMembers, "badMembers");
push(weComplain, "weComplain");
push(receivedContributions, "receivedContributions");
push(receivedComplaints, "receivedComplaints");
push(receivedJustifications, "receivedJustifications");
push(receivedPrematureCommitments, "receivedPrematureCommitments");

if (detailLevel == 2) {
UniValue arr(UniValue::VARR);
for (const auto& dmn : dmnMembers) {
arr.push_back(dmn->proTxHash.ToString());
}
ret.push_back(Pair("allMembers", arr));
}

return ret;
}

CDKGDebugManager::CDKGDebugManager(CScheduler* scheduler)
{
for (const auto& p : Params().GetConsensus().llmqs) {
ResetLocalSessionStatus(p.first, uint256(), 0);
}

if (scheduler) {
scheduler->scheduleEvery([&]() {
SendLocalStatus();
}, 10);
}
}

void CDKGDebugManager::ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vRecv, CConnman& connman)
{
if (strCommand == NetMsgType::QDEBUGSTATUS) {
CDKGDebugStatus status;
vRecv >> status;

{
LOCK(cs_main);
connman.RemoveAskFor(::SerializeHash(status));
}

ProcessDebugStatusMessage(pfrom->id, status);
}
}

void CDKGDebugManager::ProcessDebugStatusMessage(NodeId nodeId, llmq::CDKGDebugStatus& status)
{
auto dmn = deterministicMNManager->GetListAtChainTip().GetMN(status.proTxHash);
if (!dmn) {
if (nodeId != -1) {
LOCK(cs_main);
Misbehaving(nodeId, 10);
}
return;
}

{
LOCK(cs);
auto it = statusesForMasternodes.find(status.proTxHash);
if (it != statusesForMasternodes.end()) {
if (statuses[it->second].nTime >= status.nTime) {
// we know a more recent status already
return;
}
}
}

// check if all expected LLMQ types are present and valid
std::set<Consensus::LLMQType> llmqTypes;
for (const auto& p : status.sessions) {
if (!Params().GetConsensus().llmqs.count((Consensus::LLMQType)p.first)) {
if (nodeId != -1) {
LOCK(cs_main);
Misbehaving(nodeId, 10);
}
return;
}
const auto& params = Params().GetConsensus().llmqs.at((Consensus::LLMQType)p.first);
if (p.second.llmqType != p.first || p.second.members.size() != (size_t)params.size) {
if (nodeId != -1) {
LOCK(cs_main);
Misbehaving(nodeId, 10);
}
return;
}
llmqTypes.emplace((Consensus::LLMQType)p.first);
}
for (const auto& p : Params().GetConsensus().llmqs) {
if (!llmqTypes.count(p.first)) {
if (nodeId != -1) {
LOCK(cs_main);
Misbehaving(nodeId, 10);
}
return;
}
}

// TODO batch verification/processing
if (!status.sig.VerifyInsecure(dmn->pdmnState->pubKeyOperator, status.GetSignHash())) {
if (nodeId != -1) {
LOCK(cs_main);
Misbehaving(nodeId, 10);
}
return;
}

LOCK(cs);
auto it = statusesForMasternodes.find(status.proTxHash);
if (it != statusesForMasternodes.end()) {
statuses.erase(it->second);
statusesForMasternodes.erase(it);
}

auto hash = ::SerializeHash(status);

statuses[hash] = status;
statusesForMasternodes[status.proTxHash] = hash;

CInv inv(MSG_QUORUM_DEBUG_STATUS, hash);
g_connman->RelayInv(inv, DMN_PROTO_VERSION);
}

UniValue CDKGDebugStatus::ToJson(int detailLevel) const
{
UniValue ret(UniValue::VOBJ);

ret.push_back(Pair("proTxHash", proTxHash.ToString()));
ret.push_back(Pair("height", (int)nHeight));
ret.push_back(Pair("time", nTime));
ret.push_back(Pair("timeStr", DateTimeStrFormat("%Y-%m-%d %H:%M:%S", nTime)));

UniValue sessionsJson(UniValue::VOBJ);
for (const auto& p : sessions) {
if (!Params().GetConsensus().llmqs.count((Consensus::LLMQType)p.first)) {
continue;
}
const auto& params = Params().GetConsensus().llmqs.at((Consensus::LLMQType)p.first);
sessionsJson.push_back(Pair(params.name, p.second.ToJson(detailLevel)));
}

ret.push_back(Pair("session", sessionsJson));

return ret;
}

bool CDKGDebugManager::AlreadyHave(const CInv& inv)
{
LOCK(cs);
return statuses.count(inv.hash) != 0;
}

bool CDKGDebugManager::GetDebugStatus(const uint256& hash, llmq::CDKGDebugStatus& ret)
{
LOCK(cs);
auto it = statuses.find(hash);
if (it == statuses.end()) {
return false;
}
ret = it->second;
return true;
}

bool CDKGDebugManager::GetDebugStatusForMasternode(const uint256& proTxHash, llmq::CDKGDebugStatus& ret)
{
LOCK(cs);
auto it = statusesForMasternodes.find(proTxHash);
if (it == statusesForMasternodes.end()) {
return false;
}
ret = statuses.at(it->second);
return true;
}

void CDKGDebugManager::GetLocalDebugStatus(llmq::CDKGDebugStatus& ret)
{
LOCK(cs);
ret = localStatus;
ret.proTxHash = activeMasternodeInfo.proTxHash;
}

void CDKGDebugManager::ResetLocalSessionStatus(Consensus::LLMQType llmqType, const uint256& quorumHash, int quorumHeight)
{
LOCK(cs);

auto& params = Params().GetConsensus().llmqs.at(llmqType);

localStatus.nTime = GetAdjustedTime();

auto& session = localStatus.sessions[llmqType];

session.llmqType = llmqType;
session.quorumHash = quorumHash;
session.quorumHeight = (uint32_t)quorumHeight;
session.phase = 0;
session.statusBitset = 0;
session.members.clear();
session.members.resize((size_t)params.size);
}

void CDKGDebugManager::UpdateLocalStatus(std::function<bool(CDKGDebugStatus& status)>&& func)
{
LOCK(cs);
if (func(localStatus)) {
localStatus.nTime = GetAdjustedTime();
}
}

void CDKGDebugManager::UpdateLocalSessionStatus(Consensus::LLMQType llmqType, std::function<bool(CDKGDebugSessionStatus& status)>&& func)
{
LOCK(cs);
if (func(localStatus.sessions.at(llmqType))) {
localStatus.nTime = GetAdjustedTime();
}
}

void CDKGDebugManager::UpdateLocalMemberStatus(Consensus::LLMQType llmqType, size_t memberIdx, std::function<bool(CDKGDebugMemberStatus& status)>&& func)
{
LOCK(cs);
if (func(localStatus.sessions.at(llmqType).members.at(memberIdx))) {
localStatus.nTime = GetAdjustedTime();
}
}

void CDKGDebugManager::SendLocalStatus()
{
if (!fMasternodeMode) {
return;
}
if (activeMasternodeInfo.proTxHash.IsNull()) {
return;
}

CDKGDebugStatus status;
{
LOCK(cs);
status = localStatus;
}

int64_t nTime = status.nTime;
status.proTxHash = activeMasternodeInfo.proTxHash;
status.nTime = 0;
status.sig = CBLSSignature();

uint256 newHash = ::SerializeHash(status);
if (newHash == lastStatusHash) {
return;
}
lastStatusHash = newHash;

status.nTime = nTime;
status.sig = activeMasternodeInfo.blsKeyOperator->Sign(status.GetSignHash());

ProcessDebugStatusMessage(-1, status);
}

}
Loading

0 comments on commit 324406b

Please sign in to comment.