mirror of
https://github.com/dashpay/dash.git
synced 2024-12-29 05:49:11 +01:00
a79b928532
Backports 0.15 pr6
449 lines
12 KiB
C++
449 lines
12 KiB
C++
// Copyright (c) 2018-2019 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 "bls/bls_batchverifier.h"
|
|
#include "chainparams.h"
|
|
#include "net.h"
|
|
#include "net_processing.h"
|
|
#include "scheduler.h"
|
|
#include "spork.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", (int)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) :
|
|
scheduler(_scheduler)
|
|
{
|
|
}
|
|
|
|
void CDKGDebugManager::StartScheduler()
|
|
{
|
|
if (scheduler) {
|
|
scheduler->scheduleEvery([&]() {
|
|
SendLocalStatus();
|
|
}, 10 * 1000);
|
|
}
|
|
}
|
|
|
|
void CDKGDebugManager::ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vRecv, CConnman& connman)
|
|
{
|
|
if (!sporkManager.IsSporkActive(SPORK_18_QUORUM_DEBUG_ENABLED)) {
|
|
return;
|
|
}
|
|
|
|
if (strCommand == NetMsgType::QDEBUGSTATUS) {
|
|
CDKGDebugStatus status;
|
|
vRecv >> status;
|
|
|
|
uint256 hash = ::SerializeHash(status);
|
|
|
|
{
|
|
LOCK(cs_main);
|
|
connman.RemoveAskFor(hash);
|
|
}
|
|
|
|
bool ban = false;
|
|
if (!PreVerifyDebugStatusMessage(hash, status, ban)) {
|
|
if (ban) {
|
|
LOCK(cs_main);
|
|
Misbehaving(pfrom->id, 10);
|
|
return;
|
|
}
|
|
}
|
|
|
|
LOCK(cs);
|
|
|
|
pendingIncomingStatuses.emplace(hash, std::make_pair(std::move(status), pfrom->id));
|
|
|
|
ScheduleProcessPending();
|
|
}
|
|
}
|
|
|
|
bool CDKGDebugManager::PreVerifyDebugStatusMessage(const uint256& hash, llmq::CDKGDebugStatus& status, bool& retBan)
|
|
{
|
|
retBan = false;
|
|
|
|
auto dmn = deterministicMNManager->GetListAtChainTip().GetMN(status.proTxHash);
|
|
if (!dmn) {
|
|
retBan = true;
|
|
return false;
|
|
}
|
|
|
|
{
|
|
LOCK(cs);
|
|
|
|
if (!seenStatuses.emplace(hash, GetTimeMillis()).second) {
|
|
return false;
|
|
}
|
|
|
|
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 false;
|
|
}
|
|
}
|
|
}
|
|
|
|
// check if all present LLMQ types are valid
|
|
for (const auto& p : status.sessions) {
|
|
if (!Params().GetConsensus().llmqs.count((Consensus::LLMQType)p.first)) {
|
|
retBan = true;
|
|
return false;
|
|
}
|
|
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) {
|
|
retBan = true;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void CDKGDebugManager::ScheduleProcessPending()
|
|
{
|
|
AssertLockHeld(cs);
|
|
|
|
if (hasScheduledProcessPending) {
|
|
return;
|
|
}
|
|
|
|
scheduler->schedule([&] {
|
|
ProcessPending();
|
|
}, boost::chrono::system_clock::now() + boost::chrono::milliseconds(100));
|
|
}
|
|
|
|
void CDKGDebugManager::ProcessPending()
|
|
{
|
|
decltype(pendingIncomingStatuses) pend;
|
|
|
|
{
|
|
LOCK(cs);
|
|
hasScheduledProcessPending = false;
|
|
pend = std::move(pendingIncomingStatuses);
|
|
}
|
|
|
|
if (!sporkManager.IsSporkActive(SPORK_18_QUORUM_DEBUG_ENABLED)) {
|
|
return;
|
|
}
|
|
|
|
CBLSBatchVerifier<NodeId, uint256> batchVerifier(true, true, 8);
|
|
for (const auto& p : pend) {
|
|
const auto& hash = p.first;
|
|
const auto& status = p.second.first;
|
|
auto nodeId = p.second.second;
|
|
auto dmn = deterministicMNManager->GetListAtChainTip().GetMN(status.proTxHash);
|
|
if (!dmn) {
|
|
continue;
|
|
}
|
|
batchVerifier.PushMessage(nodeId, hash, status.GetSignHash(), status.sig, dmn->pdmnState->pubKeyOperator);
|
|
}
|
|
|
|
batchVerifier.Verify();
|
|
|
|
if (!batchVerifier.badSources.empty()) {
|
|
LOCK(cs_main);
|
|
for (auto& nodeId : batchVerifier.badSources) {
|
|
Misbehaving(nodeId, 100);
|
|
}
|
|
}
|
|
for (const auto& p : pend) {
|
|
const auto& hash = p.first;
|
|
const auto& status = p.second.first;
|
|
auto nodeId = p.second.second;
|
|
if (batchVerifier.badMessages.count(p.first)) {
|
|
continue;
|
|
}
|
|
|
|
ProcessDebugStatusMessage(hash, status);
|
|
}
|
|
}
|
|
|
|
// status must have a validated signature
|
|
void CDKGDebugManager::ProcessDebugStatusMessage(const uint256& hash, const llmq::CDKGDebugStatus& status)
|
|
{
|
|
auto dmn = deterministicMNManager->GetListAtChainTip().GetMN(status.proTxHash);
|
|
if (!dmn) {
|
|
return;
|
|
}
|
|
|
|
LOCK(cs);
|
|
auto it = statusesForMasternodes.find(status.proTxHash);
|
|
if (it != statusesForMasternodes.end()) {
|
|
statuses.erase(it->second);
|
|
statusesForMasternodes.erase(it);
|
|
}
|
|
|
|
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("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);
|
|
|
|
if (!sporkManager.IsSporkActive(SPORK_18_QUORUM_DEBUG_ENABLED)) {
|
|
return true;
|
|
}
|
|
|
|
return statuses.count(inv.hash) != 0 || seenStatuses.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)
|
|
{
|
|
LOCK(cs);
|
|
|
|
auto it = localStatus.sessions.find(llmqType);
|
|
if (it == localStatus.sessions.end()) {
|
|
return;
|
|
}
|
|
|
|
localStatus.sessions.erase(it);
|
|
localStatus.nTime = GetAdjustedTime();
|
|
}
|
|
|
|
void CDKGDebugManager::InitLocalSessionStatus(Consensus::LLMQType llmqType, const uint256& quorumHash, int quorumHeight)
|
|
{
|
|
LOCK(cs);
|
|
|
|
auto it = localStatus.sessions.find(llmqType);
|
|
if (it == localStatus.sessions.end()) {
|
|
it = localStatus.sessions.emplace((uint8_t)llmqType, CDKGDebugSessionStatus()).first;
|
|
}
|
|
|
|
auto& params = Params().GetConsensus().llmqs.at(llmqType);
|
|
auto& session = it->second;
|
|
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);
|
|
|
|
auto it = localStatus.sessions.find(llmqType);
|
|
if (it == localStatus.sessions.end()) {
|
|
return;
|
|
}
|
|
|
|
if (func(it->second)) {
|
|
localStatus.nTime = GetAdjustedTime();
|
|
}
|
|
}
|
|
|
|
void CDKGDebugManager::UpdateLocalMemberStatus(Consensus::LLMQType llmqType, size_t memberIdx, std::function<bool(CDKGDebugMemberStatus& status)>&& func)
|
|
{
|
|
LOCK(cs);
|
|
|
|
auto it = localStatus.sessions.find(llmqType);
|
|
if (it == localStatus.sessions.end()) {
|
|
return;
|
|
}
|
|
|
|
if (func(it->second.members.at(memberIdx))) {
|
|
localStatus.nTime = GetAdjustedTime();
|
|
}
|
|
}
|
|
|
|
void CDKGDebugManager::SendLocalStatus()
|
|
{
|
|
if (!fMasternodeMode) {
|
|
return;
|
|
}
|
|
if (activeMasternodeInfo.proTxHash.IsNull()) {
|
|
return;
|
|
}
|
|
if (!sporkManager.IsSporkActive(SPORK_18_QUORUM_DEBUG_ENABLED)) {
|
|
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(newHash, status);
|
|
}
|
|
|
|
}
|