diff --git a/src/Makefile.am b/src/Makefile.am index be2464264bb10..42dbfacb18008 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -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 \ @@ -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 \ @@ -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 \ diff --git a/src/llmq/quorums_debug.cpp b/src/llmq/quorums_debug.cpp new file mode 100644 index 0000000000000..5304f2ca05798 --- /dev/null +++ b/src/llmq/quorums_debug.cpp @@ -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 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 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&& func) +{ + LOCK(cs); + if (func(localStatus)) { + localStatus.nTime = GetAdjustedTime(); + } +} + +void CDKGDebugManager::UpdateLocalSessionStatus(Consensus::LLMQType llmqType, std::function&& func) +{ + LOCK(cs); + if (func(localStatus.sessions.at(llmqType))) { + localStatus.nTime = GetAdjustedTime(); + } +} + +void CDKGDebugManager::UpdateLocalMemberStatus(Consensus::LLMQType llmqType, size_t memberIdx, std::function&& 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); +} + +} diff --git a/src/llmq/quorums_debug.h b/src/llmq/quorums_debug.h new file mode 100644 index 0000000000000..1e5e333a1956b --- /dev/null +++ b/src/llmq/quorums_debug.h @@ -0,0 +1,178 @@ +// 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. + +#ifndef DASH_QUORUMS_DEBUG_H +#define DASH_QUORUMS_DEBUG_H + +#include "consensus/params.h" +#include "serialize.h" +#include "bls/bls.h" +#include "sync.h" +#include "univalue.h" +#include "net.h" + +class CDataStream; +class CInv; +class CScheduler; + +namespace llmq +{ + +class CDKGDebugMemberStatus +{ +public: + union { + struct + { + // is it locally considered as bad (and thus removed from the validMembers set) + bool bad : 1; + // did we complain about this member + bool weComplain : 1; + + // received message for DKG phases + bool receivedContribution : 1; + bool receivedComplaint : 1; + bool receivedJustification : 1; + bool receivedPrematureCommitment : 1; + }; + uint16_t statusBitset; + }; + + std::set complaintsFromMembers; + +public: + CDKGDebugMemberStatus() : statusBitset(0) {} + +public: + ADD_SERIALIZE_METHODS + + template + inline void SerializationOp(Stream& s, Operation ser_action) + { + READWRITE(statusBitset); + READWRITE(complaintsFromMembers); + } +}; + +class CDKGDebugSessionStatus +{ +public: + uint8_t llmqType{Consensus::LLMQ_NONE}; + uint256 quorumHash; + uint32_t quorumHeight{0}; + uint8_t phase{0}; + + union { + struct + { + // sent messages for DKG phases + bool sentContributions : 1; + bool sentComplaint : 1; + bool sentJustification : 1; + bool sentPrematureCommitment : 1; + + bool aborted : 1; + }; + uint16_t statusBitset; + }; + + std::vector members; + +public: + CDKGDebugSessionStatus() : statusBitset(0) {} + +public: + ADD_SERIALIZE_METHODS + + template + inline void SerializationOp(Stream& s, Operation ser_action) + { + READWRITE(llmqType); + READWRITE(quorumHash); + READWRITE(quorumHeight); + READWRITE(phase); + READWRITE(statusBitset); + READWRITE(members); + } + + UniValue ToJson(int detailLevel) const; +}; + +class CDKGDebugStatus +{ +public: + uint256 proTxHash; + int64_t nTime{0}; + uint32_t nHeight{0}; + + std::map sessions; + + CBLSSignature sig; + +public: + ADD_SERIALIZE_METHODS + + template + inline void SerializationOpWithoutSig(Stream& s, Operation ser_action) + { + READWRITE(proTxHash); + READWRITE(nTime); + READWRITE(nHeight); + READWRITE(sessions); + } + + template + inline void SerializationOp(Stream& s, Operation ser_action) + { + SerializationOpWithoutSig(s, ser_action); + READWRITE(sig); + } + + uint256 GetSignHash() const + { + CHashWriter hw(SER_GETHASH, 0); + NCONST_PTR(this)->SerializationOpWithoutSig(hw, CSerActionSerialize()); + hw << CBLSSignature(); + return hw.GetHash(); + } + + UniValue ToJson(int detailLevel) const; +}; + +class CDKGDebugManager +{ +private: + CCriticalSection cs; + + std::map statuses; + std::map statusesForMasternodes; + + CDKGDebugStatus localStatus; + uint256 lastStatusHash; + +public: + CDKGDebugManager(CScheduler* scheduler); + + void ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vRecv, CConnman& connman); + void ProcessDebugStatusMessage(NodeId nodeId, CDKGDebugStatus& status); + + bool AlreadyHave(const CInv& inv); + bool GetDebugStatus(const uint256& hash, CDKGDebugStatus& ret); + bool GetDebugStatusForMasternode(const uint256& proTxHash, CDKGDebugStatus& ret); + void GetLocalDebugStatus(CDKGDebugStatus& ret); + + void ResetLocalSessionStatus(Consensus::LLMQType llmqType, const uint256& quorumHash, int quorumHeight); + + void UpdateLocalStatus(std::function&& func); + void UpdateLocalSessionStatus(Consensus::LLMQType llmqType, std::function&& func); + void UpdateLocalMemberStatus(Consensus::LLMQType llmqType, size_t memberIdx, std::function&& func); + + void SendLocalStatus(); +}; + +extern CDKGDebugManager* quorumDKGDebugManager; + +} + +#endif //DASH_QUORUMS_DEBUG_H diff --git a/src/llmq/quorums_dkgsession.cpp b/src/llmq/quorums_dkgsession.cpp index 268f587bff012..f668df3a096f6 100644 --- a/src/llmq/quorums_dkgsession.cpp +++ b/src/llmq/quorums_dkgsession.cpp @@ -5,6 +5,7 @@ #include "quorums_dkgsession.h" #include "quorums_commitment.h" +#include "quorums_debug.h" #include "quorums_dkgsessionmgr.h" #include "quorums_utils.h" @@ -177,6 +178,11 @@ void CDKGSession::SendContributions() logger.Flush(); + quorumDKGDebugManager->UpdateLocalSessionStatus(params.type, [&](CDKGDebugSessionStatus& status) { + status.sentContributions = true; + return true; + }); + uint256 hash = ::SerializeHash(qc); bool ban = false; if (PreVerifyMessage(hash, qc, ban)) { @@ -265,6 +271,11 @@ void CDKGSession::ReceiveMessage(const uint256& hash, const CDKGContribution& qc invSet.emplace(inv); RelayInvToParticipants(inv); + quorumDKGDebugManager->UpdateLocalMemberStatus(params.type, member->idx, [&](CDKGDebugMemberStatus& status) { + status.receivedContribution = true; + return true; + }); + if (member->contributions.size() > 1) { // don't do any further processing if we got more than 1 contribution. we already relayed it, // so others know about his bad behavior @@ -306,6 +317,10 @@ void CDKGSession::ReceiveMessage(const uint256& hash, const CDKGContribution& qc if (complain) { member->weComplain = true; + quorumDKGDebugManager->UpdateLocalMemberStatus(params.type, member->idx, [&](CDKGDebugMemberStatus& status) { + status.weComplain = true; + return true; + }); return; } @@ -364,6 +379,10 @@ void CDKGSession::VerifyPendingContributions() auto& m = members[memberIndexes[i]]; logger.Printf("invalid contribution from %s. will complain later\n", m->dmn->proTxHash.ToString()); m->weComplain = true; + quorumDKGDebugManager->UpdateLocalMemberStatus(params.type, m->idx, [&](CDKGDebugMemberStatus& status) { + status.weComplain = true; + return true; + }); } else { size_t memberIdx = memberIndexes[i]; dkgManager.WriteVerifiedSkContribution(params.type, quorumHash, members[memberIdx]->dmn->proTxHash, skContributions[i]); @@ -443,6 +462,11 @@ void CDKGSession::SendComplaint() logger.Flush(); + quorumDKGDebugManager->UpdateLocalSessionStatus(params.type, [&](CDKGDebugSessionStatus& status) { + status.sentComplaint = true; + return true; + }); + uint256 hash = ::SerializeHash(qc); bool ban = false; if (PreVerifyMessage(hash, qc, ban)) { @@ -522,6 +546,11 @@ void CDKGSession::ReceiveMessage(const uint256& hash, const CDKGComplaint& qc, b invSet.emplace(inv); RelayInvToParticipants(inv); + quorumDKGDebugManager->UpdateLocalMemberStatus(params.type, member->idx, [&](CDKGDebugMemberStatus& status) { + status.receivedComplaint = true; + return true; + }); + if (member->complaints.size() > 1) { // don't do any further processing if we got more than 1 complaint. we already relayed it, // so others know about his bad behavior @@ -544,6 +573,9 @@ void CDKGSession::ReceiveMessage(const uint256& hash, const CDKGComplaint& qc, b if (qc.complainForMembers[i]) { m->complaintsFromOthers.emplace(qc.proTxHash); m->someoneComplain = true; + quorumDKGDebugManager->UpdateLocalMemberStatus(params.type, m->idx, [&](CDKGDebugMemberStatus& status) { + return status.complaintsFromMembers.emplace(member->idx).second; + }); if (AreWeMember() && i == myIdx) { logger.Printf("%s complained about us\n", member->dmn->proTxHash.ToString()); } @@ -636,6 +668,11 @@ void CDKGSession::SendJustification(const std::set& forMembers) logger.Flush(); + quorumDKGDebugManager->UpdateLocalSessionStatus(params.type, [&](CDKGDebugSessionStatus& status) { + status.sentJustification = true; + return true; + }); + uint256 hash = ::SerializeHash(qj); bool ban = false; if (PreVerifyMessage(hash, qj, ban)) { @@ -732,6 +769,11 @@ void CDKGSession::ReceiveMessage(const uint256& hash, const CDKGJustification& q invSet.emplace(inv); RelayInvToParticipants(inv); + quorumDKGDebugManager->UpdateLocalMemberStatus(params.type, member->idx, [&](CDKGDebugMemberStatus& status) { + status.receivedJustification = true; + return true; + }); + if (member->justifications.size() > 1) { // don't do any further processing if we got more than 1 justification. we already relayed it, // so others know about his bad behavior @@ -956,6 +998,11 @@ void CDKGSession::SendCommitment() logger.Flush(); + quorumDKGDebugManager->UpdateLocalSessionStatus(params.type, [&](CDKGDebugSessionStatus& status) { + status.sentPrematureCommitment = true; + return true; + }); + uint256 hash = ::SerializeHash(qc); bool ban = false; if (PreVerifyMessage(hash, qc, ban)) { @@ -1099,6 +1146,11 @@ void CDKGSession::ReceiveMessage(const uint256& hash, const CDKGPrematureCommitm invSet.emplace(inv); RelayInvToParticipants(inv); + quorumDKGDebugManager->UpdateLocalMemberStatus(params.type, member->idx, [&](CDKGDebugMemberStatus& status) { + status.receivedPrematureCommitment = true; + return true; + }); + int receivedCount = 0; for (auto& m : members) { if (!m->prematureCommitments.empty()) { @@ -1223,6 +1275,10 @@ void CDKGSession::MarkBadMember(size_t idx) if (member->bad) { return; } + quorumDKGDebugManager->UpdateLocalMemberStatus(params.type, idx, [&](CDKGDebugMemberStatus& status) { + status.bad = true; + return true; + }); member->bad = true; } @@ -1256,4 +1312,4 @@ void CDKGSession::RelayInvToParticipants(const CInv& inv) }); } -} \ No newline at end of file +} diff --git a/src/llmq/quorums_dkgsessionhandler.cpp b/src/llmq/quorums_dkgsessionhandler.cpp index a74bb5830eca7..7fe3d6e34e47e 100644 --- a/src/llmq/quorums_dkgsessionhandler.cpp +++ b/src/llmq/quorums_dkgsessionhandler.cpp @@ -123,6 +123,7 @@ void CDKGSessionHandler::UpdatedBlockTip(const CBlockIndex* pindexNew, const CBl QuorumPhase newPhase = phase; if (quorumStageInt == 0) { newPhase = QuorumPhase_Initialized; + quorumDKGDebugManager->ResetLocalSessionStatus(params.type, quorumHash, quorumHeight); } else if (quorumStageInt == params.dkgPhaseBlocks * 1) { newPhase = QuorumPhase_Contribute; } else if (quorumStageInt == params.dkgPhaseBlocks * 2) { @@ -137,6 +138,17 @@ void CDKGSessionHandler::UpdatedBlockTip(const CBlockIndex* pindexNew, const CBl newPhase = QuorumPhase_Idle; } phase = newPhase; + + quorumDKGDebugManager->UpdateLocalStatus([&](CDKGDebugStatus& status) { + bool changed = status.nHeight != pindexNew->nHeight; + status.nHeight = (uint32_t)pindexNew->nHeight; + return changed; + }); + quorumDKGDebugManager->UpdateLocalSessionStatus(params.type, [&](CDKGDebugSessionStatus& status) { + bool changed = status.phase != (uint8_t)phase; + status.phase = (uint8_t)phase; + return changed; + }); } void CDKGSessionHandler::ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vRecv, CConnman& connman) @@ -530,6 +542,10 @@ void CDKGSessionHandler::PhaseHandlerThread() try { HandleDKGRound(); } catch (AbortPhaseException& e) { + quorumDKGDebugManager->UpdateLocalSessionStatus(params.type, [&](CDKGDebugSessionStatus& status) { + status.aborted = true; + return true; + }); LogPrintf("CDKGSessionHandler::%s -- aborted current DKG session\n", __func__); } } diff --git a/src/llmq/quorums_dkgsessionmgr.cpp b/src/llmq/quorums_dkgsessionmgr.cpp index f397f8e5a3910..b70bdc22ebf97 100644 --- a/src/llmq/quorums_dkgsessionmgr.cpp +++ b/src/llmq/quorums_dkgsessionmgr.cpp @@ -4,6 +4,7 @@ #include "quorums_dkgsessionmgr.h" #include "quorums_blockprocessor.h" +#include "quorums_debug.h" #include "quorums_init.h" #include "quorums_utils.h" diff --git a/src/llmq/quorums_init.cpp b/src/llmq/quorums_init.cpp index 133b3166daa68..2e8739b9376b4 100644 --- a/src/llmq/quorums_init.cpp +++ b/src/llmq/quorums_init.cpp @@ -6,6 +6,7 @@ #include "quorums_blockprocessor.h" #include "quorums_commitment.h" +#include "quorums_debug.h" #include "quorums_dkgsessionmgr.h" #include "scheduler.h" @@ -17,6 +18,7 @@ static CBLSWorker blsWorker; void InitLLMQSystem(CEvoDB& evoDb, CScheduler* scheduler) { + quorumDKGDebugManager = new CDKGDebugManager(scheduler); quorumBlockProcessor = new CQuorumBlockProcessor(evoDb); quorumDKGSessionManager = new CDKGSessionManager(evoDb, blsWorker); } @@ -27,6 +29,8 @@ void DestroyLLMQSystem() quorumDKGSessionManager = NULL; delete quorumBlockProcessor; quorumBlockProcessor = nullptr; + delete quorumDKGDebugManager; + quorumDKGDebugManager = nullptr; } } diff --git a/src/net_processing.cpp b/src/net_processing.cpp index 4611c72c5c50b..eab41786c8641 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -46,6 +46,7 @@ #include "evo/simplifiedmns.h" #include "llmq/quorums_blockprocessor.h" #include "llmq/quorums_commitment.h" +#include "llmq/quorums_debug.h" #include "llmq/quorums_dkgsessionmgr.h" #include "llmq/quorums_init.h" @@ -956,6 +957,8 @@ bool static AlreadyHave(const CInv& inv) EXCLUSIVE_LOCKS_REQUIRED(cs_main) case MSG_QUORUM_JUSTIFICATION: case MSG_QUORUM_PREMATURE_COMMITMENT: return llmq::quorumDKGSessionManager->AlreadyHave(inv); + case MSG_QUORUM_DEBUG_STATUS: + return llmq::quorumDKGDebugManager->AlreadyHave(inv); } // Don't know what it is, just say we already got one @@ -1253,6 +1256,13 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam push = true; } } + if (!push && (inv.type == MSG_QUORUM_DEBUG_STATUS)) { + llmq::CDKGDebugStatus o; + if (llmq::quorumDKGDebugManager->GetDebugStatus(inv.hash, o)) { + connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::QDEBUGSTATUS, o)); + push = true; + } + } if (!push) vNotFound.push_back(inv); @@ -2911,6 +2921,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr governance.ProcessMessage(pfrom, strCommand, vRecv, connman); llmq::quorumBlockProcessor->ProcessMessage(pfrom, strCommand, vRecv, connman); llmq::quorumDKGSessionManager->ProcessMessage(pfrom, strCommand, vRecv, connman); + llmq::quorumDKGDebugManager->ProcessMessage(pfrom, strCommand, vRecv, connman); } else { diff --git a/src/protocol.cpp b/src/protocol.cpp index 76611d0568cbe..bd7de4d8c1ce5 100644 --- a/src/protocol.cpp +++ b/src/protocol.cpp @@ -64,6 +64,7 @@ const char *QCOMPLAINT="qcomplaint"; const char *QJUSTIFICATION="qjustify"; const char *QPCOMMITMENT="qpcommit"; const char *QWATCH="qwatch"; +const char *QDEBUGSTATUS="qdebugstatus"; }; static const char* ppszTypeName[] = @@ -97,6 +98,7 @@ static const char* ppszTypeName[] = NetMsgType::QCOMPLAINT, NetMsgType::QJUSTIFICATION, NetMsgType::QPCOMMITMENT, + NetMsgType::QDEBUGSTATUS, }; /** All known message types. Keep this in the same order as the list of @@ -155,6 +157,7 @@ const static std::string allNetMessageTypes[] = { NetMsgType::QJUSTIFICATION, NetMsgType::QPCOMMITMENT, NetMsgType::QWATCH, + NetMsgType::QDEBUGSTATUS, }; const static std::vector allNetMessageTypesVec(allNetMessageTypes, allNetMessageTypes+ARRAYLEN(allNetMessageTypes)); diff --git a/src/protocol.h b/src/protocol.h index 5a8ecb831dfbe..0bfd5d9e55fbe 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -270,6 +270,7 @@ extern const char *QCOMPLAINT; extern const char *QJUSTIFICATION; extern const char *QPCOMMITMENT; extern const char *QWATCH; +extern const char *QDEBUGSTATUS; }; /* Get a vector of all valid message types (see above) */ @@ -369,6 +370,7 @@ enum GetDataMsg { MSG_QUORUM_COMPLAINT = 24, MSG_QUORUM_JUSTIFICATION = 25, MSG_QUORUM_PREMATURE_COMMITMENT = 26, + MSG_QUORUM_DEBUG_STATUS = 27, }; /** inv message data */ diff --git a/src/rpc/register.h b/src/rpc/register.h index de5cd4f2e5ea2..7907ca5039a96 100644 --- a/src/rpc/register.h +++ b/src/rpc/register.h @@ -25,6 +25,8 @@ void RegisterMasternodeRPCCommands(CRPCTable &tableRPC); void RegisterGovernanceRPCCommands(CRPCTable &tableRPC); /** Register Evo RPC commands */ void RegisterEvoRPCCommands(CRPCTable &tableRPC); +/** Register Quorums RPC commands */ +void RegisterQuorumsRPCCommands(CRPCTable &tableRPC); static inline void RegisterAllCoreRPCCommands(CRPCTable &t) { @@ -36,6 +38,7 @@ static inline void RegisterAllCoreRPCCommands(CRPCTable &t) RegisterMasternodeRPCCommands(t); RegisterGovernanceRPCCommands(t); RegisterEvoRPCCommands(t); + RegisterQuorumsRPCCommands(t); } #endif diff --git a/src/rpc/rpcquorums.cpp b/src/rpc/rpcquorums.cpp new file mode 100644 index 0000000000000..cfe0024ffb148 --- /dev/null +++ b/src/rpc/rpcquorums.cpp @@ -0,0 +1,81 @@ +// Copyright (c) 2017 The Dash Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include "server.h" +#include "validation.h" + +#include "llmq/quorums_debug.h" +#include "llmq/quorums_dkgsession.h" + +void quorum_dkgstatus_help() +{ + throw std::runtime_error( + "quorum dkgstatus (detailed)\n" + "\nArguments:\n" + "1. \"proTxHash\" (string, optional, default=0) ProTxHash of masternode to show status for.\n" + " If set to an empty string or 0, the local status is shown.\n" + "2. \"detailLevel\" (number, optional, default=0) Detail level of output.\n" + " 0=Only show counts. 1=Show member indexes. 2=Show member's ProTxHashes.\n" + ); +} + +UniValue quorum_dkgstatus(const JSONRPCRequest& request) +{ + if (request.fHelp || (request.params.size() != 1 && request.params.size() != 2 && request.params.size() != 3)) { + quorum_dkgstatus_help(); + } + + uint256 proTxHash; + if (request.params.size() > 1 && request.params[1].get_str() != "" && request.params[1].get_str() != "0") { + proTxHash = ParseHashV(request.params[1], "proTxHash"); + } + + int detailLevel = 0; + if (request.params.size() > 2) { + detailLevel = ParseInt32V(request.params[2], "detailLevel"); + if (detailLevel < 0 || detailLevel > 2) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "invalid detailLevel"); + } + } + + llmq::CDKGDebugStatus status; + if (proTxHash.IsNull()) { + llmq::quorumDKGDebugManager->GetLocalDebugStatus(status); + } else { + if (!llmq::quorumDKGDebugManager->GetDebugStatusForMasternode(proTxHash, status)) { + throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("no status for %s found", proTxHash.ToString())); + } + } + + return status.ToJson(detailLevel); +} + +UniValue quorum(const JSONRPCRequest& request) +{ + if (request.params.empty()) { + throw std::runtime_error( + "quorum \"command\" ...\n" + ); + } + + std::string command = request.params[0].get_str(); + + if (command == "dkgstatus") { + return quorum_dkgstatus(request); + } else { + throw std::runtime_error("invalid command: " + command); + } +} + +static const CRPCCommand commands[] = +{ // category name actor (function) okSafeMode + // --------------------- ------------------------ ----------------------- ---------- + { "evo", "quorum", &quorum, false, {} }, +}; + +void RegisterQuorumsRPCCommands(CRPCTable &tableRPC) +{ + for (unsigned int vcidx = 0; vcidx < ARRAYLEN(commands); vcidx++) + tableRPC.appendCommand(commands[vcidx].name, &commands[vcidx]); +}