refactor: create an enum for DKGError, instead of passing around potentially invalid strings (#4998)

* refactor: create an enum for DKGError, instead of passing around potentially invalid strings

This also enables us to utilize an std::array instead of a std::map
This also removes the CCriticalSection and instead utilizes atomic doubles
This also adds safety to the dkgsimerror rpc rejecting invalid types

* test: add some tests for DKGError
This commit is contained in:
PastaPastaPasta 2022-09-06 19:32:53 +02:00 committed by GitHub
parent f72ebeec8c
commit 0f3e00ce03
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 81 additions and 39 deletions

View File

@ -257,6 +257,7 @@ BITCOIN_TESTS =\
test/key_tests.cpp \ test/key_tests.cpp \
test/lcg.h \ test/lcg.h \
test/limitedmap_tests.cpp \ test/limitedmap_tests.cpp \
test/llmq_dkg_tests.cpp \
test/logging_tests.cpp \ test/logging_tests.cpp \
test/dbwrapper_tests.cpp \ test/dbwrapper_tests.cpp \
test/validation_tests.cpp \ test/validation_tests.cpp \

View File

@ -24,36 +24,21 @@
namespace llmq namespace llmq
{ {
static std::array<std::atomic<double>, int(DKGError::type::_COUNT)> simDkgErrorMap{};
// Supported error types: void SetSimulatedDKGErrorRate(DKGError::type type, double rate)
// - contribution-omit
// - contribution-lie
// - complain-lie
// - justify-lie
// - justify-omit
// - commit-omit
// - commit-lie
static CCriticalSection cs_simDkgError;
static std::map<std::string, double> simDkgErrorMap;
void SetSimulatedDKGErrorRate(const std::string& type, double rate)
{ {
LOCK(cs_simDkgError); if (int(type) >= DKGError::type::_COUNT) return;
simDkgErrorMap[type] = rate; simDkgErrorMap[int(type)] = rate;
} }
static double GetSimulatedErrorRate(const std::string& type) double GetSimulatedErrorRate(DKGError::type type)
{ {
LOCK(cs_simDkgError); if (int(type) >= DKGError::type::_COUNT) return 0;
auto it = simDkgErrorMap.find(type); return simDkgErrorMap[int(type)];
if (it != simDkgErrorMap.end()) {
return it->second;
}
return 0;
} }
bool CDKGSession::ShouldSimulateError(const std::string& type) const bool CDKGSession::ShouldSimulateError(DKGError::type type) const
{ {
if (params.type != Consensus::LLMQType::LLMQ_TEST) { if (params.type != Consensus::LLMQType::LLMQ_TEST) {
return false; return false;
@ -162,7 +147,7 @@ void CDKGSession::SendContributions(CDKGPendingMessages& pendingMessages)
logger.Batch("sending contributions"); logger.Batch("sending contributions");
if (ShouldSimulateError("contribution-omit")) { if (ShouldSimulateError(DKGError::type::CONTRIBUTION_OMIT)) {
logger.Batch("omitting"); logger.Batch("omitting");
return; return;
} }
@ -181,7 +166,7 @@ void CDKGSession::SendContributions(CDKGPendingMessages& pendingMessages)
const auto& m = members[i]; const auto& m = members[i];
CBLSSecretKey skContrib = m_sk_contributions[i]; CBLSSecretKey skContrib = m_sk_contributions[i];
if (i != myIdx && ShouldSimulateError("contribution-lie")) { if (i != myIdx && ShouldSimulateError(DKGError::type::CONTRIBUTION_LIE)) {
logger.Batch("lying for %s", m->dmn->proTxHash.ToString()); logger.Batch("lying for %s", m->dmn->proTxHash.ToString());
skContrib.MakeNewKey(); skContrib.MakeNewKey();
} }
@ -314,7 +299,7 @@ void CDKGSession::ReceiveMessage(const CDKGContribution& qc, bool& retBan)
if (!qc.contributions->Decrypt(*myIdx, WITH_LOCK(activeMasternodeInfoCs, return *activeMasternodeInfo.blsKeyOperator), skContribution, PROTOCOL_VERSION)) { if (!qc.contributions->Decrypt(*myIdx, WITH_LOCK(activeMasternodeInfoCs, return *activeMasternodeInfo.blsKeyOperator), skContribution, PROTOCOL_VERSION)) {
logger.Batch("contribution from %s could not be decrypted", member->dmn->proTxHash.ToString()); logger.Batch("contribution from %s could not be decrypted", member->dmn->proTxHash.ToString());
complain = true; complain = true;
} else if (member->idx != myIdx && ShouldSimulateError("complain-lie")) { } else if (member->idx != myIdx && ShouldSimulateError(DKGError::type::COMPLAIN_LIE)) {
logger.Batch("lying/complaining for %s", member->dmn->proTxHash.ToString()); logger.Batch("lying/complaining for %s", member->dmn->proTxHash.ToString());
complain = true; complain = true;
} }
@ -691,7 +676,7 @@ void CDKGSession::SendJustification(CDKGPendingMessages& pendingMessages, const
CBLSSecretKey skContribution = m_sk_contributions[i]; CBLSSecretKey skContribution = m_sk_contributions[i];
if (i != myIdx && ShouldSimulateError("justify-lie")) { if (i != myIdx && ShouldSimulateError(DKGError::type::JUSTIFY_LIE)) {
logger.Batch("lying for %s", m->dmn->proTxHash.ToString()); logger.Batch("lying for %s", m->dmn->proTxHash.ToString());
skContribution.MakeNewKey(); skContribution.MakeNewKey();
} }
@ -699,7 +684,7 @@ void CDKGSession::SendJustification(CDKGPendingMessages& pendingMessages, const
qj.contributions.emplace_back(i, skContribution); qj.contributions.emplace_back(i, skContribution);
} }
if (ShouldSimulateError("justify-omit")) { if (ShouldSimulateError(DKGError::type::JUSTIFY_OMIT)) {
logger.Batch("omitting"); logger.Batch("omitting");
return; return;
} }
@ -941,7 +926,7 @@ void CDKGSession::SendCommitment(CDKGPendingMessages& pendingMessages)
return; return;
} }
if (ShouldSimulateError("commit-omit")) { if (ShouldSimulateError(DKGError::type::COMMIT_OMIT)) {
logger.Batch("omitting"); logger.Batch("omitting");
return; return;
} }
@ -979,7 +964,7 @@ void CDKGSession::SendCommitment(CDKGPendingMessages& pendingMessages)
qc.quorumVvecHash = ::SerializeHash(*vvec); qc.quorumVvecHash = ::SerializeHash(*vvec);
int lieType = -1; int lieType = -1;
if (ShouldSimulateError("commit-lie")) { if (ShouldSimulateError(DKGError::type::COMMIT_LIE)) {
lieType = GetRandInt(5); lieType = GetRandInt(5);
logger.Batch("lying on commitment. lieType=%d", lieType); logger.Batch("lying on commitment. lieType=%d", lieType);
} }

View File

@ -210,6 +210,30 @@ public:
bool someoneComplain{false}; bool someoneComplain{false};
}; };
class DKGError {
public:
enum type {
COMPLAIN_LIE = 0,
COMMIT_OMIT,
COMMIT_LIE,
CONTRIBUTION_OMIT,
CONTRIBUTION_LIE,
JUSTIFY_OMIT,
JUSTIFY_LIE,
_COUNT
};
static constexpr DKGError::type from_string(std::string_view in) {
if (in == "complain-lie") return COMPLAIN_LIE;
if (in == "commit-omit") return COMMIT_OMIT;
if (in == "commit-lie") return COMMIT_LIE;
if (in == "contribution-omit") return CONTRIBUTION_OMIT;
if (in == "contribution-lie") return CONTRIBUTION_LIE;
if (in == "justify-lie") return JUSTIFY_LIE;
if (in == "justify-omit") return JUSTIFY_OMIT;
return _COUNT;
}
};
/** /**
* The DKG session is a single instance of the DKG process. It is owned and called by CDKGSessionHandler, which passes * The DKG session is a single instance of the DKG process. It is owned and called by CDKGSessionHandler, which passes
* received DKG messages to the session. The session is not persistent and will loose it's state (the whole object is * received DKG messages to the session. The session is not persistent and will loose it's state (the whole object is
@ -281,7 +305,7 @@ public:
bool Init(const CBlockIndex* pQuorumBaseBlockIndex, const std::vector<CDeterministicMNCPtr>& mns, const uint256& _myProTxHash, int _quorumIndex); bool Init(const CBlockIndex* pQuorumBaseBlockIndex, const std::vector<CDeterministicMNCPtr>& mns, const uint256& _myProTxHash, int _quorumIndex);
std::optional<size_t> GetMyMemberIndex() const { return myIdx; } [[nodiscard]] std::optional<size_t> GetMyMemberIndex() const { return myIdx; }
/** /**
* The following sets of methods are for the first 4 phases handled in the session. The flow of message calls * The following sets of methods are for the first 4 phases handled in the session. The flow of message calls
@ -325,16 +349,16 @@ public:
// Phase 5: aggregate/finalize // Phase 5: aggregate/finalize
std::vector<CFinalCommitment> FinalizeCommitments(); std::vector<CFinalCommitment> FinalizeCommitments();
bool AreWeMember() const { return !myProTxHash.IsNull(); } [[nodiscard]] bool AreWeMember() const { return !myProTxHash.IsNull(); }
void MarkBadMember(size_t idx); void MarkBadMember(size_t idx);
void RelayInvToParticipants(const CInv& inv) const; void RelayInvToParticipants(const CInv& inv) const;
public: public:
CDKGMember* GetMember(const uint256& proTxHash) const; [[nodiscard]] CDKGMember* GetMember(const uint256& proTxHash) const;
private: private:
bool ShouldSimulateError(const std::string& type) const; [[nodiscard]] bool ShouldSimulateError(DKGError::type type) const;
}; };
class CDKGLogger : public CBatchedLogger class CDKGLogger : public CBatchedLogger
@ -346,7 +370,8 @@ public:
CBatchedLogger(BCLog::LLMQ_DKG, strprintf("QuorumDKG(type=%s, quorumIndex=%d, height=%d, member=%d, func=%s)", _llmqTypeName, _quorumIndex, _height, _areWeMember, _func)){}; CBatchedLogger(BCLog::LLMQ_DKG, strprintf("QuorumDKG(type=%s, quorumIndex=%d, height=%d, member=%d, func=%s)", _llmqTypeName, _quorumIndex, _height, _areWeMember, _func)){};
}; };
void SetSimulatedDKGErrorRate(const std::string& type, double rate); void SetSimulatedDKGErrorRate(DKGError::type type, double rate);
double GetSimulatedErrorRate(DKGError::type type);
} // namespace llmq } // namespace llmq

View File

@ -573,16 +573,20 @@ static UniValue quorum_dkgsimerror(const JSONRPCRequest& request)
{ {
quorum_dkgsimerror_help(request); quorum_dkgsimerror_help(request);
std::string type = request.params[0].get_str(); std::string type_str = request.params[0].get_str();
double rate = ParseDoubleV(request.params[1], "rate"); double rate = ParseDoubleV(request.params[1], "rate");
if (rate < 0 || rate > 1) { if (rate < 0 || rate > 1) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "invalid rate. Must be between 0 and 1"); throw JSONRPCError(RPC_INVALID_PARAMETER, "invalid rate. Must be between 0 and 1");
} }
if (const llmq::DKGError::type type = llmq::DKGError::from_string(type_str);
type == llmq::DKGError::type::_COUNT) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "invalid type. See DKGError class implementation");
} else {
llmq::SetSimulatedDKGErrorRate(type, rate); llmq::SetSimulatedDKGErrorRate(type, rate);
return UniValue(); return UniValue();
}
} }
static void quorum_getdata_help(const JSONRPCRequest& request) static void quorum_getdata_help(const JSONRPCRequest& request)

View File

@ -0,0 +1,27 @@
// Copyright (c) 2022 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 <llmq/dkgsession.h>
#include <util/irange.h>
#include <boost/test/unit_test.hpp>
BOOST_AUTO_TEST_SUITE(llmq_dkg_tests)
BOOST_AUTO_TEST_CASE(llmq_dkgerror)
{
using namespace llmq;
for (auto i : irange::range(int(llmq::DKGError::type::_COUNT))) {
BOOST_ASSERT(GetSimulatedErrorRate(llmq::DKGError::type(i)) == 0.0);
SetSimulatedDKGErrorRate(llmq::DKGError::type(i), 1.0);
BOOST_ASSERT(GetSimulatedErrorRate(llmq::DKGError::type(i)) == 1.0);
}
BOOST_ASSERT(GetSimulatedErrorRate(llmq::DKGError::type::_COUNT) == 0.0);
SetSimulatedDKGErrorRate(llmq::DKGError::type::_COUNT, 1.0);
BOOST_ASSERT(GetSimulatedErrorRate(llmq::DKGError::type::_COUNT) == 0.0);
}
BOOST_AUTO_TEST_SUITE_END()