mirror of
https://github.com/dashpay/dash.git
synced 2024-12-24 19:42:46 +01:00
Merge pull request #2631 from codablock/pr_llmq_quorummgr
Implement CQuorum and CQuorumManager
This commit is contained in:
commit
5425e2e351
@ -141,6 +141,7 @@ BITCOIN_CORE_H = \
|
||||
keystore.h \
|
||||
dbwrapper.h \
|
||||
limitedmap.h \
|
||||
llmq/quorums.h \
|
||||
llmq/quorums_blockprocessor.h \
|
||||
llmq/quorums_commitment.h \
|
||||
llmq/quorums_debug.h \
|
||||
@ -253,6 +254,7 @@ libdash_server_a_SOURCES = \
|
||||
governance-validators.cpp \
|
||||
governance-vote.cpp \
|
||||
governance-votedb.cpp \
|
||||
llmq/quorums.cpp \
|
||||
llmq/quorums_blockprocessor.cpp \
|
||||
llmq/quorums_commitment.cpp \
|
||||
llmq/quorums_debug.cpp \
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include "masternode-payments.h"
|
||||
#include "masternode-sync.h"
|
||||
#include "privatesend.h"
|
||||
#include "llmq/quorums.h"
|
||||
#include "llmq/quorums_dkgsessionmgr.h"
|
||||
#ifdef ENABLE_WALLET
|
||||
#include "privatesend-client.h"
|
||||
@ -60,6 +61,7 @@ void CDSNotificationInterface::UpdatedBlockTip(const CBlockIndex *pindexNew, con
|
||||
#endif // ENABLE_WALLET
|
||||
instantsend.UpdatedBlockTip(pindexNew);
|
||||
governance.UpdatedBlockTip(pindexNew, connman);
|
||||
llmq::quorumManager->UpdatedBlockTip(pindexNew, pindexFork, fInitialDownload);
|
||||
llmq::quorumDKGSessionManager->UpdatedBlockTip(pindexNew, pindexFork, fInitialDownload);
|
||||
}
|
||||
|
||||
|
405
src/llmq/quorums.cpp
Normal file
405
src/llmq/quorums.cpp
Normal file
@ -0,0 +1,405 @@
|
||||
// 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.h"
|
||||
#include "quorums_blockprocessor.h"
|
||||
#include "quorums_commitment.h"
|
||||
#include "quorums_dkgsession.h"
|
||||
#include "quorums_dkgsessionmgr.h"
|
||||
#include "quorums_init.h"
|
||||
#include "quorums_utils.h"
|
||||
|
||||
#include "evo/specialtx.h"
|
||||
|
||||
#include "activemasternode.h"
|
||||
#include "chainparams.h"
|
||||
#include "init.h"
|
||||
#include "univalue.h"
|
||||
#include "validation.h"
|
||||
|
||||
#include "cxxtimer.hpp"
|
||||
|
||||
namespace llmq
|
||||
{
|
||||
|
||||
static const std::string DB_QUORUM_SK_SHARE = "q_Qsk";
|
||||
static const std::string DB_QUORUM_QUORUM_VVEC = "q_Qqvvec";
|
||||
|
||||
CQuorumManager* quorumManager;
|
||||
|
||||
static uint256 MakeQuorumKey(const CQuorum& q)
|
||||
{
|
||||
CHashWriter hw(SER_NETWORK, 0);
|
||||
hw << (uint8_t)q.params.type;
|
||||
hw << q.quorumHash;
|
||||
for (const auto& dmn : q.members) {
|
||||
hw << dmn->proTxHash;
|
||||
}
|
||||
return hw.GetHash();
|
||||
}
|
||||
|
||||
CQuorum::~CQuorum()
|
||||
{
|
||||
// most likely the thread is already done
|
||||
stopCachePopulatorThread = true;
|
||||
if (cachePopulatorThread.joinable()) {
|
||||
cachePopulatorThread.join();
|
||||
}
|
||||
}
|
||||
|
||||
void CQuorum::Init(const uint256& _quorumHash, int _height, const std::vector<CDeterministicMNCPtr>& _members, const std::vector<bool>& _validMembers, const CBLSPublicKey& _quorumPublicKey)
|
||||
{
|
||||
quorumHash = _quorumHash;
|
||||
height = _height;
|
||||
members = _members;
|
||||
validMembers = _validMembers;
|
||||
quorumPublicKey = _quorumPublicKey;
|
||||
}
|
||||
|
||||
bool CQuorum::IsMember(const uint256& proTxHash) const
|
||||
{
|
||||
for (auto& dmn : members) {
|
||||
if (dmn->proTxHash == proTxHash) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CQuorum::IsValidMember(const uint256& proTxHash) const
|
||||
{
|
||||
for (size_t i = 0; i < members.size(); i++) {
|
||||
if (members[i]->proTxHash == proTxHash) {
|
||||
return validMembers[i];
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
CBLSPublicKey CQuorum::GetPubKeyShare(size_t memberIdx) const
|
||||
{
|
||||
if (quorumVvec == nullptr || memberIdx >= members.size() || !validMembers[memberIdx]) {
|
||||
return CBLSPublicKey();
|
||||
}
|
||||
auto& m = members[memberIdx];
|
||||
return blsCache.BuildPubKeyShare(m->proTxHash, quorumVvec, CBLSId::FromHash(m->proTxHash));
|
||||
}
|
||||
|
||||
CBLSSecretKey CQuorum::GetSkShare() const
|
||||
{
|
||||
return skShare;
|
||||
}
|
||||
|
||||
int CQuorum::GetMemberIndex(const uint256& proTxHash) const
|
||||
{
|
||||
for (size_t i = 0; i < members.size(); i++) {
|
||||
if (members[i]->proTxHash == proTxHash) {
|
||||
return (int)i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void CQuorum::WriteContributions(CEvoDB& evoDb)
|
||||
{
|
||||
uint256 dbKey = MakeQuorumKey(*this);
|
||||
|
||||
if (quorumVvec != nullptr) {
|
||||
evoDb.GetRawDB().Write(std::make_pair(DB_QUORUM_QUORUM_VVEC, dbKey), *quorumVvec);
|
||||
}
|
||||
if (skShare.IsValid()) {
|
||||
evoDb.GetRawDB().Write(std::make_pair(DB_QUORUM_SK_SHARE, dbKey), skShare);
|
||||
}
|
||||
}
|
||||
|
||||
bool CQuorum::ReadContributions(CEvoDB& evoDb)
|
||||
{
|
||||
uint256 dbKey = MakeQuorumKey(*this);
|
||||
|
||||
BLSVerificationVector qv;
|
||||
if (evoDb.Read(std::make_pair(DB_QUORUM_QUORUM_VVEC, dbKey), qv)) {
|
||||
quorumVvec = std::make_shared<BLSVerificationVector>(std::move(qv));
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
// We ignore the return value here as it is ok if this fails. If it fails, it usually means that we are not a
|
||||
// member of the quorum but observed the whole DKG process to have the quorum verification vector.
|
||||
evoDb.Read(std::make_pair(DB_QUORUM_SK_SHARE, dbKey), skShare);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CQuorum::StartCachePopulatorThread(std::shared_ptr<CQuorum> _this)
|
||||
{
|
||||
if (_this->quorumVvec == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
cxxtimer::Timer t(true);
|
||||
LogPrintf("CQuorum::StartCachePopulatorThread -- start\n");
|
||||
|
||||
// this thread will exit after some time
|
||||
// when then later some other thread tries to get keys, it will be much faster
|
||||
_this->cachePopulatorThread = std::thread([_this, t]() {
|
||||
RenameThread("quorum-cachepop");
|
||||
for (size_t i = 0; i < _this->members.size() && !_this->stopCachePopulatorThread && !ShutdownRequested(); i++) {
|
||||
if (_this->validMembers[i]) {
|
||||
_this->GetPubKeyShare(i);
|
||||
}
|
||||
}
|
||||
LogPrintf("CQuorum::StartCachePopulatorThread -- done. time=%d\n", t.count());
|
||||
});
|
||||
}
|
||||
|
||||
CQuorumManager::CQuorumManager(CEvoDB& _evoDb, CBLSWorker& _blsWorker, CDKGSessionManager& _dkgManager) :
|
||||
evoDb(_evoDb),
|
||||
blsWorker(_blsWorker),
|
||||
dkgManager(_dkgManager)
|
||||
{
|
||||
}
|
||||
|
||||
void CQuorumManager::UpdatedBlockTip(const CBlockIndex* pindexNew, const CBlockIndex* pindexFork, bool fInitialDownload)
|
||||
{
|
||||
if (fInitialDownload) {
|
||||
return;
|
||||
}
|
||||
|
||||
LOCK(cs_main);
|
||||
|
||||
for (auto& p : Params().GetConsensus().llmqs) {
|
||||
EnsureQuorumConnections(p.first, pindexNew);
|
||||
}
|
||||
}
|
||||
|
||||
void CQuorumManager::EnsureQuorumConnections(Consensus::LLMQType llmqType, const CBlockIndex* pindexNew)
|
||||
{
|
||||
AssertLockHeld(cs_main);
|
||||
|
||||
const auto& params = Params().GetConsensus().llmqs.at(llmqType);
|
||||
|
||||
auto myProTxHash = activeMasternodeInfo.proTxHash;
|
||||
auto lastQuorums = ScanQuorums(llmqType, (size_t)params.keepOldConnections);
|
||||
|
||||
auto connmanQuorumsToDelete = g_connman->GetMasternodeQuorums(llmqType);
|
||||
|
||||
// don't remove connections for the currently in-progress DKG round
|
||||
int curDkgHeight = pindexNew->nHeight - (pindexNew->nHeight % params.dkgInterval);
|
||||
auto curDkgBlock = chainActive[curDkgHeight]->GetBlockHash();
|
||||
connmanQuorumsToDelete.erase(curDkgBlock);
|
||||
|
||||
for (auto& quorum : lastQuorums) {
|
||||
if (!quorum->IsMember(myProTxHash) && !GetBoolArg("-watchquorums", DEFAULT_WATCH_QUORUMS)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!g_connman->HasMasternodeQuorumNodes(llmqType, quorum->quorumHash)) {
|
||||
std::set<CService> connections;
|
||||
if (quorum->IsMember(myProTxHash)) {
|
||||
connections = CLLMQUtils::GetQuorumConnections(llmqType, quorum->quorumHash, myProTxHash);
|
||||
} else {
|
||||
auto cindexes = CLLMQUtils::CalcDeterministicWatchConnections(llmqType, quorum->quorumHash, quorum->members.size(), 1);
|
||||
for (auto idx : cindexes) {
|
||||
connections.emplace(quorum->members[idx]->pdmnState->addr);
|
||||
}
|
||||
}
|
||||
if (!connections.empty()) {
|
||||
std::string debugMsg = strprintf("CQuorumManager::%s -- adding masternodes quorum connections for quorum %s:\n", __func__, quorum->quorumHash.ToString());
|
||||
for (auto& c : connections) {
|
||||
debugMsg += strprintf(" %s\n", c.ToString(false));
|
||||
}
|
||||
LogPrintf(debugMsg);
|
||||
g_connman->AddMasternodeQuorumNodes(llmqType, quorum->quorumHash, connections);
|
||||
}
|
||||
}
|
||||
connmanQuorumsToDelete.erase(quorum->quorumHash);
|
||||
}
|
||||
|
||||
for (auto& qh : connmanQuorumsToDelete) {
|
||||
LogPrintf("CQuorumManager::%s -- removing masternodes quorum connections for quorum %s:\n", __func__, qh.ToString());
|
||||
g_connman->RemoveMasternodeQuorumNodes(llmqType, qh);
|
||||
}
|
||||
}
|
||||
|
||||
bool CQuorumManager::BuildQuorumFromCommitment(const CFinalCommitment& qc, std::shared_ptr<CQuorum>& quorum) const
|
||||
{
|
||||
AssertLockHeld(cs_main);
|
||||
|
||||
if (!mapBlockIndex.count(qc.quorumHash)) {
|
||||
LogPrintf("CQuorumManager::%s -- block %s not found", __func__, qc.quorumHash.ToString());
|
||||
return false;
|
||||
}
|
||||
auto& params = Params().GetConsensus().llmqs.at((Consensus::LLMQType)qc.llmqType);
|
||||
auto quorumIndex = mapBlockIndex[qc.quorumHash];
|
||||
auto members = CLLMQUtils::GetAllQuorumMembers((Consensus::LLMQType)qc.llmqType, qc.quorumHash);
|
||||
|
||||
quorum->Init(qc.quorumHash, quorumIndex->nHeight, members, qc.validMembers, qc.quorumPublicKey);
|
||||
|
||||
bool hasValidVvec = false;
|
||||
if (quorum->ReadContributions(evoDb)) {
|
||||
hasValidVvec = true;
|
||||
} else {
|
||||
if (BuildQuorumContributions(qc, quorum)) {
|
||||
quorum->WriteContributions(evoDb);
|
||||
hasValidVvec = true;
|
||||
} else {
|
||||
LogPrintf("CQuorumManager::%s -- quorum.ReadContributions and BuildQuorumContributions for block %s failed", __func__, qc.quorumHash.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
if (hasValidVvec) {
|
||||
// pre-populate caches in the background
|
||||
// recovering public key shares is quite expensive and would result in serious lags for the first few signing
|
||||
// sessions if the shares would be calculated on-demand
|
||||
CQuorum::StartCachePopulatorThread(quorum);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CQuorumManager::BuildQuorumContributions(const CFinalCommitment& fqc, std::shared_ptr<CQuorum>& quorum) const
|
||||
{
|
||||
std::vector<uint16_t> memberIndexes;
|
||||
std::vector<BLSVerificationVectorPtr> vvecs;
|
||||
BLSSecretKeyVector skContributions;
|
||||
if (!dkgManager.GetVerifiedContributions((Consensus::LLMQType)fqc.llmqType, fqc.quorumHash, fqc.validMembers, memberIndexes, vvecs, skContributions)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
BLSVerificationVectorPtr quorumVvec;
|
||||
CBLSSecretKey skShare;
|
||||
|
||||
cxxtimer::Timer t2(true);
|
||||
quorumVvec = blsWorker.BuildQuorumVerificationVector(vvecs);
|
||||
if (quorumVvec == nullptr) {
|
||||
LogPrintf("CQuorumManager::%s -- failed to build quorumVvec\n", __func__);
|
||||
// without the quorum vvec, there can't be a skShare, so we fail here. Failure is not fatal here, as it still
|
||||
// allows to use the quorum as a non-member (verification through the quorum pub key)
|
||||
return false;
|
||||
}
|
||||
skShare = blsWorker.AggregateSecretKeys(skContributions);
|
||||
if (!skShare.IsValid()) {
|
||||
LogPrintf("CQuorumManager::%s -- failed to build skShare\n", __func__);
|
||||
// We don't bail out here as this is not a fatal error and still allows us to recover public key shares (as we
|
||||
// have a valid quorum vvec at this point)
|
||||
}
|
||||
t2.stop();
|
||||
|
||||
LogPrintf("CQuorumManager::%s -- built quorum vvec and skShare. time=%d\n", __func__, t2.count());
|
||||
|
||||
quorum->quorumVvec = quorumVvec;
|
||||
quorum->skShare = skShare;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CQuorumManager::HasQuorum(Consensus::LLMQType llmqType, const uint256& quorumHash)
|
||||
{
|
||||
return quorumBlockProcessor->HasMinedCommitment(llmqType, quorumHash);
|
||||
}
|
||||
|
||||
std::vector<CQuorumCPtr> CQuorumManager::ScanQuorums(Consensus::LLMQType llmqType, size_t maxCount)
|
||||
{
|
||||
LOCK(cs_main);
|
||||
return ScanQuorums(llmqType, chainActive.Tip()->GetBlockHash(), maxCount);
|
||||
}
|
||||
|
||||
std::vector<CQuorumCPtr> CQuorumManager::ScanQuorums(Consensus::LLMQType llmqType, const uint256& startBlock, size_t maxCount)
|
||||
{
|
||||
std::vector<CQuorumCPtr> result;
|
||||
|
||||
LOCK(cs_main);
|
||||
if (!mapBlockIndex.count(startBlock)) {
|
||||
return result;
|
||||
}
|
||||
|
||||
result.reserve(maxCount);
|
||||
|
||||
CBlockIndex* pindex = mapBlockIndex[startBlock];
|
||||
|
||||
while (pindex != NULL && result.size() < maxCount && deterministicMNManager->IsDIP3Active(pindex->nHeight)) {
|
||||
if (HasQuorum(llmqType, pindex->GetBlockHash())) {
|
||||
auto quorum = GetQuorum(llmqType, pindex->GetBlockHash());
|
||||
if (quorum) {
|
||||
result.emplace_back(quorum);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO speedup (skip blocks where no quorums could have been mined)
|
||||
pindex = pindex->pprev;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
CQuorumCPtr CQuorumManager::SelectQuorum(Consensus::LLMQType llmqType, const uint256& selectionHash, size_t poolSize)
|
||||
{
|
||||
LOCK(cs_main);
|
||||
return SelectQuorum(llmqType, chainActive.Tip()->GetBlockHash(), selectionHash, poolSize);
|
||||
}
|
||||
|
||||
CQuorumCPtr CQuorumManager::SelectQuorum(Consensus::LLMQType llmqType, const uint256& startBlock, const uint256& selectionHash, size_t poolSize)
|
||||
{
|
||||
auto quorums = ScanQuorums(llmqType, startBlock, poolSize);
|
||||
if (quorums.empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::vector<std::pair<uint256, size_t>> scores;
|
||||
scores.reserve(quorums.size());
|
||||
for (size_t i = 0; i < quorums.size(); i++) {
|
||||
CHashWriter h(SER_NETWORK, 0);
|
||||
h << (uint8_t)llmqType;
|
||||
h << quorums[i]->quorumHash;
|
||||
h << selectionHash;
|
||||
scores.emplace_back(h.GetHash(), i);
|
||||
}
|
||||
std::sort(scores.begin(), scores.end());
|
||||
return quorums[scores.front().second];
|
||||
}
|
||||
|
||||
CQuorumCPtr CQuorumManager::GetQuorum(Consensus::LLMQType llmqType, const uint256& quorumHash)
|
||||
{
|
||||
AssertLockHeld(cs_main);
|
||||
|
||||
// we must check this before we look into the cache. Reorgs might have happened which would mean we might have
|
||||
// cached quorums which are not in the active chain anymore
|
||||
if (!HasQuorum(llmqType, quorumHash)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
LOCK(quorumsCacheCs);
|
||||
|
||||
auto it = quorumsCache.find(std::make_pair(llmqType, quorumHash));
|
||||
if (it != quorumsCache.end()) {
|
||||
return it->second;
|
||||
}
|
||||
|
||||
CFinalCommitment qc;
|
||||
if (!quorumBlockProcessor->GetMinedCommitment(llmqType, quorumHash, qc)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto& params = Params().GetConsensus().llmqs.at(llmqType);
|
||||
|
||||
auto quorum = std::make_shared<CQuorum>(params, blsWorker);
|
||||
if (!BuildQuorumFromCommitment(qc, quorum)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
quorumsCache.emplace(std::make_pair(llmqType, quorumHash), quorum);
|
||||
|
||||
return quorum;
|
||||
}
|
||||
|
||||
CQuorumCPtr CQuorumManager::GetNewestQuorum(Consensus::LLMQType llmqType)
|
||||
{
|
||||
auto quorums = ScanQuorums(llmqType, 1);
|
||||
if (quorums.empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
return quorums.front();
|
||||
}
|
||||
|
||||
}
|
116
src/llmq/quorums.h
Normal file
116
src/llmq/quorums.h
Normal file
@ -0,0 +1,116 @@
|
||||
// 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_H
|
||||
#define DASH_QUORUMS_H
|
||||
|
||||
#include "evo/evodb.h"
|
||||
#include "evo/deterministicmns.h"
|
||||
|
||||
#include "validationinterface.h"
|
||||
#include "consensus/params.h"
|
||||
|
||||
#include "bls/bls.h"
|
||||
#include "bls/bls_worker.h"
|
||||
|
||||
namespace llmq
|
||||
{
|
||||
|
||||
class CDKGSessionManager;
|
||||
|
||||
/**
|
||||
* An object of this class represents a quorum which was mined on-chain (through a quorum commitment)
|
||||
* It at least contains informations 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
|
||||
{
|
||||
friend class CQuorumManager;
|
||||
public:
|
||||
const Consensus::LLMQParams& params;
|
||||
uint256 quorumHash;
|
||||
int height;
|
||||
std::vector<CDeterministicMNCPtr> members;
|
||||
std::vector<bool> validMembers;
|
||||
CBLSPublicKey quorumPublicKey;
|
||||
|
||||
// These are only valid when we either participated in the DKG or fully watched it
|
||||
BLSVerificationVectorPtr quorumVvec;
|
||||
CBLSSecretKey skShare;
|
||||
|
||||
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;
|
||||
std::atomic<bool> stopCachePopulatorThread;
|
||||
std::thread cachePopulatorThread;
|
||||
|
||||
public:
|
||||
CQuorum(const Consensus::LLMQParams& _params, CBLSWorker& _blsWorker) : params(_params), blsCache(_blsWorker), stopCachePopulatorThread(false) {}
|
||||
~CQuorum();
|
||||
void Init(const uint256& quorumHash, int height, const std::vector<CDeterministicMNCPtr>& members, const std::vector<bool>& validMembers, const CBLSPublicKey& quorumPublicKey);
|
||||
|
||||
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);
|
||||
bool ReadContributions(CEvoDB& evoDb);
|
||||
static void StartCachePopulatorThread(std::shared_ptr<CQuorum> _this);
|
||||
};
|
||||
typedef std::shared_ptr<CQuorum> CQuorumPtr;
|
||||
typedef std::shared_ptr<const CQuorum> CQuorumCPtr;
|
||||
|
||||
/**
|
||||
* 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 inter-quorum connections for new quorums.
|
||||
*/
|
||||
class CQuorumManager
|
||||
{
|
||||
private:
|
||||
CEvoDB& evoDb;
|
||||
CBLSWorker& blsWorker;
|
||||
CDKGSessionManager& dkgManager;
|
||||
|
||||
CCriticalSection quorumsCacheCs;
|
||||
std::map<std::pair<Consensus::LLMQType, uint256>, CQuorumPtr> quorumsCache;
|
||||
|
||||
public:
|
||||
CQuorumManager(CEvoDB& _evoDb, CBLSWorker& _blsWorker, CDKGSessionManager& _dkgManager);
|
||||
|
||||
void UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload);
|
||||
|
||||
public:
|
||||
bool HasQuorum(Consensus::LLMQType llmqType, const uint256& quorumHash);
|
||||
|
||||
// all these methods will lock cs_main
|
||||
CQuorumCPtr GetQuorum(Consensus::LLMQType llmqType,const uint256& quorumHash);
|
||||
CQuorumCPtr GetNewestQuorum(Consensus::LLMQType llmqType);
|
||||
std::vector<CQuorumCPtr> ScanQuorums(Consensus::LLMQType llmqType, size_t maxCount);
|
||||
std::vector<CQuorumCPtr> ScanQuorums(Consensus::LLMQType llmqType, const uint256& startBlock, size_t maxCount);
|
||||
CQuorumCPtr SelectQuorum(Consensus::LLMQType llmqType, const uint256& selectionHash, size_t poolSize);
|
||||
CQuorumCPtr SelectQuorum(Consensus::LLMQType llmqType, const uint256& startBlock, const uint256& selectionHash, size_t poolSize);
|
||||
|
||||
private:
|
||||
void EnsureQuorumConnections(Consensus::LLMQType llmqType, const CBlockIndex *pindexNew);
|
||||
|
||||
bool BuildQuorumFromCommitment(const CFinalCommitment& qc, std::shared_ptr<CQuorum>& quorum) const;
|
||||
bool BuildQuorumContributions(const CFinalCommitment& fqc, std::shared_ptr<CQuorum>& quorum) const;
|
||||
};
|
||||
|
||||
extern CQuorumManager* quorumManager;
|
||||
|
||||
}
|
||||
|
||||
#endif //DASH_QUORUMS_H
|
@ -4,6 +4,7 @@
|
||||
|
||||
#include "quorums_init.h"
|
||||
|
||||
#include "quorums.h"
|
||||
#include "quorums_blockprocessor.h"
|
||||
#include "quorums_commitment.h"
|
||||
#include "quorums_debug.h"
|
||||
@ -21,10 +22,13 @@ void InitLLMQSystem(CEvoDB& evoDb, CScheduler* scheduler)
|
||||
quorumDKGDebugManager = new CDKGDebugManager(scheduler);
|
||||
quorumBlockProcessor = new CQuorumBlockProcessor(evoDb);
|
||||
quorumDKGSessionManager = new CDKGSessionManager(evoDb, blsWorker);
|
||||
quorumManager = new CQuorumManager(evoDb, blsWorker, *quorumDKGSessionManager);
|
||||
}
|
||||
|
||||
void DestroyLLMQSystem()
|
||||
{
|
||||
delete quorumManager;
|
||||
quorumManager = NULL;
|
||||
delete quorumDKGSessionManager;
|
||||
quorumDKGSessionManager = NULL;
|
||||
delete quorumBlockProcessor;
|
||||
|
@ -2,12 +2,116 @@
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include "chainparams.h"
|
||||
#include "server.h"
|
||||
#include "validation.h"
|
||||
|
||||
#include "llmq/quorums.h"
|
||||
#include "llmq/quorums_debug.h"
|
||||
#include "llmq/quorums_dkgsession.h"
|
||||
|
||||
void quorum_list_help()
|
||||
{
|
||||
throw std::runtime_error(
|
||||
"quorum list ( count )\n"
|
||||
"\nArguments:\n"
|
||||
"1. count (number, optional, default=10) Number of quorums to list.\n"
|
||||
);
|
||||
}
|
||||
|
||||
UniValue quorum_list(const JSONRPCRequest& request)
|
||||
{
|
||||
if (request.fHelp || (request.params.size() != 1 && request.params.size() != 2))
|
||||
quorum_list_help();
|
||||
|
||||
LOCK(cs_main);
|
||||
|
||||
int count = 10;
|
||||
if (request.params.size() > 1) {
|
||||
count = ParseInt32V(request.params[1], "count");
|
||||
}
|
||||
|
||||
UniValue ret(UniValue::VOBJ);
|
||||
|
||||
for (auto& p : Params().GetConsensus().llmqs) {
|
||||
UniValue v(UniValue::VARR);
|
||||
|
||||
auto quorums = llmq::quorumManager->ScanQuorums(p.first, chainActive.Tip()->GetBlockHash(), count);
|
||||
for (auto& q : quorums) {
|
||||
v.push_back(q->quorumHash.ToString());
|
||||
}
|
||||
|
||||
ret.push_back(Pair(p.second.name, v));
|
||||
}
|
||||
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void quorum_info_help()
|
||||
{
|
||||
throw std::runtime_error(
|
||||
"quorum info llmqType \"quorumHash\" ( includeSkShare )\n"
|
||||
"\nArguments:\n"
|
||||
"1. llmqType (int, required) LLMQ type.\n"
|
||||
"2. \"quorumHash\" (string, required) Block hash of quorum.\n"
|
||||
"3. includeSkShare (boolean, optional) Include secret key share in output.\n"
|
||||
);
|
||||
}
|
||||
|
||||
UniValue quorum_info(const JSONRPCRequest& request)
|
||||
{
|
||||
if (request.fHelp || (request.params.size() != 3 && request.params.size() != 4))
|
||||
quorum_info_help();
|
||||
|
||||
LOCK(cs_main);
|
||||
|
||||
Consensus::LLMQType llmqType = (Consensus::LLMQType)ParseInt32V(request.params[1], "llmqType");
|
||||
if (!Params().GetConsensus().llmqs.count(llmqType)) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "invalid LLMQ type");
|
||||
}
|
||||
|
||||
uint256 blockHash = ParseHashV(request.params[2], "quorumHash");
|
||||
bool includeSkShare = false;
|
||||
if (request.params.size() > 3) {
|
||||
includeSkShare = ParseBoolV(request.params[3], "includeSkShare");
|
||||
}
|
||||
|
||||
auto quorum = llmq::quorumManager->GetQuorum(llmqType, blockHash);
|
||||
if (!quorum) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "quorum not found");
|
||||
}
|
||||
|
||||
UniValue ret(UniValue::VOBJ);
|
||||
|
||||
ret.push_back(Pair("height", quorum->height));
|
||||
ret.push_back(Pair("quorumHash", quorum->quorumHash.ToString()));
|
||||
|
||||
UniValue membersArr(UniValue::VARR);
|
||||
for (size_t i = 0; i < quorum->members.size(); i++) {
|
||||
auto& dmn = quorum->members[i];
|
||||
UniValue mo(UniValue::VOBJ);
|
||||
mo.push_back(Pair("proTxHash", dmn->proTxHash.ToString()));
|
||||
mo.push_back(Pair("valid", quorum->validMembers[i]));
|
||||
if (quorum->validMembers[i]) {
|
||||
CBLSPublicKey pubKey = quorum->GetPubKeyShare(i);
|
||||
if (pubKey.IsValid()) {
|
||||
mo.push_back(Pair("pubKeyShare", pubKey.ToString()));
|
||||
}
|
||||
}
|
||||
membersArr.push_back(mo);
|
||||
}
|
||||
|
||||
ret.push_back(Pair("members", membersArr));
|
||||
ret.push_back(Pair("quorumPublicKey", quorum->quorumPublicKey.ToString()));
|
||||
CBLSSecretKey skShare = quorum->GetSkShare();
|
||||
if (includeSkShare && skShare.IsValid()) {
|
||||
ret.push_back(Pair("secretKeyShare", skShare.ToString()));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void quorum_dkgstatus_help()
|
||||
{
|
||||
throw std::runtime_error(
|
||||
@ -51,20 +155,40 @@ UniValue quorum_dkgstatus(const JSONRPCRequest& request)
|
||||
return status.ToJson(detailLevel);
|
||||
}
|
||||
|
||||
[[ noreturn ]] void quorum_help()
|
||||
{
|
||||
throw std::runtime_error(
|
||||
"quorum \"command\" ...\n"
|
||||
"Set of commands for quorums/LLMQs.\n"
|
||||
"To get help on individual commands, use \"help quorum command\".\n"
|
||||
"\nArguments:\n"
|
||||
"1. \"command\" (string, required) The command to execute\n"
|
||||
"\nAvailable commands:\n"
|
||||
" list - List of on-chain quorums\n"
|
||||
" info - Return information about a quorum\n"
|
||||
" dkgstatus - Return the status of the current DKG process\n"
|
||||
);
|
||||
}
|
||||
|
||||
UniValue quorum(const JSONRPCRequest& request)
|
||||
{
|
||||
if (request.params.empty()) {
|
||||
throw std::runtime_error(
|
||||
"quorum \"command\" ...\n"
|
||||
);
|
||||
if (request.fHelp && request.params.empty()) {
|
||||
quorum_help();
|
||||
}
|
||||
|
||||
std::string command = request.params[0].get_str();
|
||||
std::string command;
|
||||
if (request.params.size() >= 1) {
|
||||
command = request.params[0].get_str();
|
||||
}
|
||||
|
||||
if (command == "dkgstatus") {
|
||||
if (command == "list") {
|
||||
return quorum_list(request);
|
||||
} else if (command == "info") {
|
||||
return quorum_info(request);
|
||||
} else if (command == "dkgstatus") {
|
||||
return quorum_dkgstatus(request);
|
||||
} else {
|
||||
throw std::runtime_error("invalid command: " + command);
|
||||
quorum_help();
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user