mirror of
https://github.com/dashpay/dash.git
synced 2024-12-26 20:42:59 +01:00
79a5b197b0
## Issue being fixed or feature implemented should fix "qdata: Already received" discouraging issue the root of the issue is that we remove expired requests on UpdatedBlockTip which is too late sometimes. ## What was done? replacing expired requests with a new one in RequestQuorumData kind of does the same (drops the expired request) but without waiting for UpdatedBlockTip ## How Has This Been Tested? ## Breaking Changes ## Checklist: - [x] I have performed a self-review of my own code - [ ] I have commented my code, particularly in hard-to-understand areas - [ ] I have added or updated relevant unit/integration/functional/e2e tests - [ ] I have made corresponding changes to the documentation **For repository code-owners and collaborators only** - [ ] I have assigned this pull request to a milestone
294 lines
11 KiB
C++
294 lines
11 KiB
C++
// Copyright (c) 2018-2023 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 BITCOIN_LLMQ_QUORUMS_H
|
|
#define BITCOIN_LLMQ_QUORUMS_H
|
|
|
|
#include <chain.h>
|
|
#include <consensus/params.h>
|
|
#include <saltedhasher.h>
|
|
#include <threadinterrupt.h>
|
|
#include <unordered_lru_cache.h>
|
|
|
|
#include <bls/bls.h>
|
|
#include <bls/bls_worker.h>
|
|
|
|
#include <evo/evodb.h>
|
|
|
|
class CNode;
|
|
|
|
class CConnman;
|
|
class CBlockIndex;
|
|
|
|
class CDeterministicMN;
|
|
using CDeterministicMNCPtr = std::shared_ptr<const CDeterministicMN>;
|
|
class CMasternodeSync;
|
|
|
|
namespace llmq
|
|
{
|
|
class CDKGSessionManager;
|
|
class CQuorumBlockProcessor;
|
|
|
|
// If true, we will connect to all new quorums and watch their communication
|
|
static constexpr bool DEFAULT_WATCH_QUORUMS{false};
|
|
|
|
/**
|
|
* Object used as a key to store CQuorumDataRequest
|
|
*/
|
|
struct CQuorumDataRequestKey
|
|
{
|
|
uint256 proRegTx;
|
|
bool m_we_requested;
|
|
uint256 quorumHash;
|
|
Consensus::LLMQType llmqType;
|
|
|
|
CQuorumDataRequestKey(const uint256& proRegTxIn, const bool _m_we_requested, const uint256& quorumHashIn, const Consensus::LLMQType llmqTypeIn) :
|
|
proRegTx(proRegTxIn),
|
|
m_we_requested(_m_we_requested),
|
|
quorumHash(quorumHashIn),
|
|
llmqType(llmqTypeIn)
|
|
{}
|
|
|
|
bool operator ==(const CQuorumDataRequestKey& obj) const
|
|
{
|
|
return (proRegTx == obj.proRegTx && m_we_requested == obj.m_we_requested && quorumHash == obj.quorumHash && llmqType == obj.llmqType);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* An object of this class represents a QGETDATA request or a QDATA response header
|
|
*/
|
|
class CQuorumDataRequest
|
|
{
|
|
public:
|
|
|
|
enum Flags : uint16_t {
|
|
QUORUM_VERIFICATION_VECTOR = 0x0001,
|
|
ENCRYPTED_CONTRIBUTIONS = 0x0002,
|
|
};
|
|
enum Errors : uint8_t {
|
|
NONE = 0x00,
|
|
QUORUM_TYPE_INVALID = 0x01,
|
|
QUORUM_BLOCK_NOT_FOUND = 0x02,
|
|
QUORUM_NOT_FOUND = 0x03,
|
|
MASTERNODE_IS_NO_MEMBER = 0x04,
|
|
QUORUM_VERIFICATION_VECTOR_MISSING = 0x05,
|
|
ENCRYPTED_CONTRIBUTIONS_MISSING = 0x06,
|
|
UNDEFINED = 0xFF,
|
|
};
|
|
|
|
private:
|
|
Consensus::LLMQType llmqType{Consensus::LLMQType::LLMQ_NONE};
|
|
uint256 quorumHash;
|
|
uint16_t nDataMask{0};
|
|
uint256 proTxHash;
|
|
Errors nError{UNDEFINED};
|
|
|
|
int64_t nTime{GetTime()};
|
|
bool fProcessed{false};
|
|
|
|
static constexpr int64_t EXPIRATION_TIMEOUT{300};
|
|
static constexpr int64_t EXPIRATION_BIAS{60};
|
|
|
|
public:
|
|
|
|
CQuorumDataRequest() : nTime(GetTime()) {}
|
|
CQuorumDataRequest(const Consensus::LLMQType llmqTypeIn, const uint256& quorumHashIn, const uint16_t nDataMaskIn, const uint256& proTxHashIn = uint256()) :
|
|
llmqType(llmqTypeIn),
|
|
quorumHash(quorumHashIn),
|
|
nDataMask(nDataMaskIn),
|
|
proTxHash(proTxHashIn)
|
|
{}
|
|
|
|
SERIALIZE_METHODS(CQuorumDataRequest, obj)
|
|
{
|
|
bool fRead{false};
|
|
SER_READ(obj, fRead = true);
|
|
READWRITE(obj.llmqType, obj.quorumHash, obj.nDataMask, obj.proTxHash);
|
|
if (fRead) {
|
|
try {
|
|
READWRITE(obj.nError);
|
|
} catch (...) {
|
|
SER_READ(obj, obj.nError = UNDEFINED);
|
|
}
|
|
} else if (obj.nError != UNDEFINED) {
|
|
READWRITE(obj.nError);
|
|
}
|
|
}
|
|
|
|
Consensus::LLMQType GetLLMQType() const { return llmqType; }
|
|
const uint256& GetQuorumHash() const { return quorumHash; }
|
|
uint16_t GetDataMask() const { return nDataMask; }
|
|
const uint256& GetProTxHash() const { return proTxHash; }
|
|
|
|
void SetError(Errors nErrorIn) { nError = nErrorIn; }
|
|
Errors GetError() const { return nError; }
|
|
std::string GetErrorString() const;
|
|
|
|
bool IsExpired(bool add_bias) const { return (GetTime() - nTime) >= (EXPIRATION_TIMEOUT + (add_bias ? EXPIRATION_BIAS : 0)); }
|
|
bool IsProcessed() const { return fProcessed; }
|
|
void SetProcessed() { fProcessed = true; }
|
|
|
|
bool operator==(const CQuorumDataRequest& other) const
|
|
{
|
|
return llmqType == other.llmqType &&
|
|
quorumHash == other.quorumHash &&
|
|
nDataMask == other.nDataMask &&
|
|
proTxHash == other.proTxHash;
|
|
}
|
|
bool operator!=(const CQuorumDataRequest& other) const
|
|
{
|
|
return !(*this == other);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* An object of this class represents a quorum which was mined on-chain (through a quorum commitment)
|
|
* It at least contains information about the members and the quorum public key which is needed to verify recovered
|
|
* signatures from this quorum.
|
|
*
|
|
* In case the local node is a member of the same quorum and successfully participated in the DKG, the quorum object
|
|
* will also contain the secret key share and the quorum verification vector. The quorum vvec is then used to recover
|
|
* the public key shares of individual members, which are needed to verify signature shares of these members.
|
|
*/
|
|
|
|
class CQuorum;
|
|
using CQuorumPtr = std::shared_ptr<CQuorum>;
|
|
using CQuorumCPtr = std::shared_ptr<const CQuorum>;
|
|
|
|
class CFinalCommitment;
|
|
using CFinalCommitmentPtr = std::unique_ptr<CFinalCommitment>;
|
|
|
|
|
|
class CQuorum
|
|
{
|
|
friend class CQuorumManager;
|
|
public:
|
|
const Consensus::LLMQParams params;
|
|
CFinalCommitmentPtr qc;
|
|
const CBlockIndex* m_quorum_base_block_index{nullptr};
|
|
uint256 minedBlockHash;
|
|
std::vector<CDeterministicMNCPtr> members;
|
|
|
|
private:
|
|
// Recovery of public key shares is very slow, so we start a background thread that pre-populates a cache so that
|
|
// the public key shares are ready when needed later
|
|
mutable CBLSWorkerCache blsCache;
|
|
mutable std::atomic<bool> fQuorumDataRecoveryThreadRunning{false};
|
|
|
|
mutable CCriticalSection cs;
|
|
// These are only valid when we either participated in the DKG or fully watched it
|
|
BLSVerificationVectorPtr quorumVvec GUARDED_BY(cs);
|
|
CBLSSecretKey skShare GUARDED_BY(cs);
|
|
|
|
public:
|
|
CQuorum(const Consensus::LLMQParams& _params, CBLSWorker& _blsWorker);
|
|
~CQuorum() = default;
|
|
void Init(CFinalCommitmentPtr _qc, const CBlockIndex* _pQuorumBaseBlockIndex, const uint256& _minedBlockHash, const std::vector<CDeterministicMNCPtr>& _members);
|
|
|
|
bool SetVerificationVector(const BLSVerificationVector& quorumVecIn);
|
|
bool SetSecretKeyShare(const CBLSSecretKey& secretKeyShare);
|
|
|
|
bool HasVerificationVector() const;
|
|
bool IsMember(const uint256& proTxHash) const;
|
|
bool IsValidMember(const uint256& proTxHash) const;
|
|
int GetMemberIndex(const uint256& proTxHash) const;
|
|
|
|
CBLSPublicKey GetPubKeyShare(size_t memberIdx) const;
|
|
CBLSSecretKey GetSkShare() const;
|
|
|
|
private:
|
|
void WriteContributions(CEvoDB& evoDb) const;
|
|
bool ReadContributions(CEvoDB& evoDb);
|
|
};
|
|
|
|
/**
|
|
* The quorum manager maintains quorums which were mined on chain. When a quorum is requested from the manager,
|
|
* it will lookup the commitment (through CQuorumBlockProcessor) and build a CQuorum object from it.
|
|
*
|
|
* It is also responsible for initialization of the intra-quorum connections for new quorums.
|
|
*/
|
|
class CQuorumManager
|
|
{
|
|
private:
|
|
CEvoDB& m_evoDb;
|
|
CConnman& connman;
|
|
CBLSWorker& blsWorker;
|
|
CDKGSessionManager& dkgManager;
|
|
CQuorumBlockProcessor& quorumBlockProcessor;
|
|
const std::unique_ptr<CMasternodeSync>& m_mn_sync;
|
|
|
|
mutable CCriticalSection cs_map_quorums;
|
|
mutable std::map<Consensus::LLMQType, unordered_lru_cache<uint256, CQuorumPtr, StaticSaltedHasher>> mapQuorumsCache GUARDED_BY(cs_map_quorums);
|
|
mutable CCriticalSection cs_scan_quorums;
|
|
mutable std::map<Consensus::LLMQType, unordered_lru_cache<uint256, std::vector<CQuorumCPtr>, StaticSaltedHasher>> scanQuorumsCache GUARDED_BY(cs_scan_quorums);
|
|
|
|
mutable ctpl::thread_pool workerPool;
|
|
mutable CThreadInterrupt quorumThreadInterrupt;
|
|
|
|
public:
|
|
CQuorumManager(CEvoDB& _evoDb, CConnman& _connman, CBLSWorker& _blsWorker, CQuorumBlockProcessor& _quorumBlockProcessor,
|
|
CDKGSessionManager& _dkgManager, const std::unique_ptr<CMasternodeSync>& mnSync);
|
|
~CQuorumManager() { Stop(); };
|
|
|
|
void Start();
|
|
void Stop();
|
|
|
|
void TriggerQuorumDataRecoveryThreads(const CBlockIndex* pIndex) const;
|
|
|
|
void UpdatedBlockTip(const CBlockIndex *pindexNew, bool fInitialDownload) const;
|
|
|
|
void ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRecv);
|
|
|
|
static bool HasQuorum(Consensus::LLMQType llmqType, const CQuorumBlockProcessor& quorum_block_processor, const uint256& quorumHash);
|
|
|
|
bool RequestQuorumData(CNode* pfrom, Consensus::LLMQType llmqType, const CBlockIndex* pQuorumBaseBlockIndex, uint16_t nDataMask, const uint256& proTxHash = uint256()) const;
|
|
|
|
// all these methods will lock cs_main for a short period of time
|
|
CQuorumCPtr GetQuorum(Consensus::LLMQType llmqType, const uint256& quorumHash) const;
|
|
std::vector<CQuorumCPtr> ScanQuorums(Consensus::LLMQType llmqType, size_t nCountRequested) const;
|
|
|
|
// this one is cs_main-free
|
|
std::vector<CQuorumCPtr> ScanQuorums(Consensus::LLMQType llmqType, const CBlockIndex* pindexStart, size_t nCountRequested) const;
|
|
|
|
private:
|
|
// all private methods here are cs_main-free
|
|
void CheckQuorumConnections(const Consensus::LLMQParams& llmqParams, const CBlockIndex *pindexNew) const;
|
|
|
|
CQuorumPtr BuildQuorumFromCommitment(Consensus::LLMQType llmqType, const CBlockIndex* pQuorumBaseBlockIndex) const EXCLUSIVE_LOCKS_REQUIRED(cs_map_quorums);
|
|
bool BuildQuorumContributions(const CFinalCommitmentPtr& fqc, const std::shared_ptr<CQuorum>& quorum) const;
|
|
|
|
CQuorumCPtr GetQuorum(Consensus::LLMQType llmqType, const CBlockIndex* pindex) const;
|
|
/// Returns the start offset for the masternode with the given proTxHash. This offset is applied when picking data recovery members of a quorum's
|
|
/// memberlist and is calculated based on a list of all member of all active quorums for the given llmqType in a way that each member
|
|
/// should receive the same number of request if all active llmqType members requests data from one llmqType quorum.
|
|
size_t GetQuorumRecoveryStartOffset(const CQuorumCPtr pQuorum, const CBlockIndex* pIndex) const;
|
|
|
|
void StartCachePopulatorThread(const CQuorumCPtr pQuorum) const;
|
|
void StartQuorumDataRecoveryThread(const CQuorumCPtr pQuorum, const CBlockIndex* pIndex, uint16_t nDataMask) const;
|
|
};
|
|
|
|
extern std::unique_ptr<CQuorumManager> quorumManager;
|
|
|
|
} // namespace llmq
|
|
|
|
template<typename T> struct SaltedHasherImpl;
|
|
template<>
|
|
struct SaltedHasherImpl<llmq::CQuorumDataRequestKey>
|
|
{
|
|
static std::size_t CalcHash(const llmq::CQuorumDataRequestKey& v, uint64_t k0, uint64_t k1)
|
|
{
|
|
CSipHasher c(k0, k1);
|
|
c.Write((unsigned char*)&(v.proRegTx), sizeof(v.proRegTx));
|
|
c.Write((unsigned char*)&(v.m_we_requested), sizeof(v.m_we_requested));
|
|
c.Write((unsigned char*)&(v.quorumHash), sizeof(v.quorumHash));
|
|
c.Write((unsigned char*)&(v.llmqType), sizeof(v.llmqType));
|
|
return c.Finalize();
|
|
}
|
|
};
|
|
|
|
template<> struct is_serializable_enum<llmq::CQuorumDataRequest::Errors> : std::true_type {};
|
|
|
|
#endif // BITCOIN_LLMQ_QUORUMS_H
|