Skip to content

Commit 596d485

Browse files
Merge branch 'develop' of https://github.com/dashpay/dash into develop
2 parents 53bebdd + e376079 commit 596d485

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+2331
-1437
lines changed

src/Makefile.am

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ endif
151151
.PHONY: FORCE check-symbols check-security
152152
# dash core #
153153
BITCOIN_CORE_H = \
154+
active/quorums.h \
154155
addrdb.h \
155156
addressindex.h \
156157
spentindex.h \
@@ -278,6 +279,7 @@ BITCOIN_CORE_H = \
278279
llmq/options.h \
279280
llmq/params.h \
280281
llmq/quorums.h \
282+
llmq/quorumsman.h \
281283
llmq/signhash.h \
282284
llmq/signing.h \
283285
llmq/net_signing.h \
@@ -286,6 +288,7 @@ BITCOIN_CORE_H = \
286288
llmq/types.h \
287289
llmq/utils.h \
288290
llmq/observer/context.h \
291+
llmq/observer/quorums.h \
289292
logging.h \
290293
logging/timer.h \
291294
mapport.h \
@@ -479,6 +482,7 @@ libbitcoin_util_a-clientversion.$(OBJEXT): obj/build.h
479482
libbitcoin_node_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(BOOST_CPPFLAGS) $(MINIUPNPC_CPPFLAGS) $(NATPMP_CPPFLAGS) $(EVENT_CFLAGS) $(EVENT_PTHREADS_CFLAGS)
480483
libbitcoin_node_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
481484
libbitcoin_node_a_SOURCES = \
485+
active/quorums.cpp \
482486
addrdb.cpp \
483487
addressindex.cpp \
484488
addrman.cpp \
@@ -549,12 +553,14 @@ libbitcoin_node_a_SOURCES = \
549553
llmq/net_signing.cpp \
550554
llmq/options.cpp \
551555
llmq/quorums.cpp \
556+
llmq/quorumsman.cpp \
552557
llmq/signhash.cpp \
553558
llmq/signing.cpp \
554559
llmq/signing_shares.cpp \
555560
llmq/snapshot.cpp \
556561
llmq/utils.cpp \
557562
llmq/observer/context.cpp \
563+
llmq/observer/quorums.cpp \
558564
mapport.cpp \
559565
masternode/active/context.cpp \
560566
masternode/active/notificationinterface.cpp \

src/active/quorums.cpp

Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,235 @@
1+
// Copyright (c) 2018-2025 The Dash Core developers
2+
// Distributed under the MIT software license, see the accompanying
3+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4+
5+
#include <active/quorums.h>
6+
7+
#include <bls/bls_ies.h>
8+
#include <bls/bls_worker.h>
9+
#include <evo/deterministicmns.h>
10+
#include <llmq/commitment.h>
11+
#include <llmq/dkgsessionmgr.h>
12+
#include <llmq/options.h>
13+
#include <llmq/quorums.h>
14+
#include <llmq/utils.h>
15+
#include <masternode/node.h>
16+
#include <masternode/sync.h>
17+
18+
#include <chain.h>
19+
#include <chainparams.h>
20+
#include <logging.h>
21+
#include <net.h>
22+
#include <netmessagemaker.h>
23+
#include <validation.h>
24+
25+
#include <cxxtimer.hpp>
26+
27+
namespace llmq {
28+
QuorumParticipant::QuorumParticipant(CBLSWorker& bls_worker, CConnman& connman, CDeterministicMNManager& dmnman,
29+
QuorumObserverParent& qman, CQuorumSnapshotManager& qsnapman,
30+
const CActiveMasternodeManager& mn_activeman, const ChainstateManager& chainman,
31+
const CMasternodeSync& mn_sync, const CSporkManager& sporkman,
32+
const llmq::QvvecSyncModeMap& sync_map, bool quorums_recovery, bool quorums_watch) :
33+
QuorumObserver(connman, dmnman, qman, qsnapman, chainman, mn_sync, sporkman, sync_map, quorums_recovery),
34+
m_bls_worker{bls_worker},
35+
m_mn_activeman{mn_activeman},
36+
m_quorums_watch{quorums_watch}
37+
{
38+
}
39+
40+
QuorumParticipant::~QuorumParticipant() = default;
41+
42+
void QuorumParticipant::CheckQuorumConnections(const Consensus::LLMQParams& llmqParams,
43+
gsl::not_null<const CBlockIndex*> pindexNew) const
44+
{
45+
auto lastQuorums = m_qman.ScanQuorums(llmqParams.type, pindexNew, (size_t)llmqParams.keepOldConnections);
46+
auto deletableQuorums = GetQuorumsToDelete(llmqParams, pindexNew);
47+
48+
const uint256 proTxHash = m_mn_activeman.GetProTxHash();
49+
const bool watchOtherISQuorums = llmqParams.type == Params().GetConsensus().llmqTypeDIP0024InstantSend &&
50+
ranges::any_of(lastQuorums, [&proTxHash](const auto& old_quorum){ return old_quorum->IsMember(proTxHash); });
51+
52+
for (const auto& quorum : lastQuorums) {
53+
if (utils::EnsureQuorumConnections(llmqParams, m_connman, m_sporkman, {m_dmnman, m_qsnapman, m_chainman, quorum->m_quorum_base_block_index},
54+
m_dmnman.GetListAtChainTip(), proTxHash, /*is_masternode=*/true, m_quorums_watch)) {
55+
if (deletableQuorums.erase(quorum->qc->quorumHash) > 0) {
56+
LogPrint(BCLog::LLMQ, "QuorumParticipant::%s -- llmqType[%d] h[%d] keeping mn quorum connections for quorum: [%d:%s]\n", __func__, ToUnderlying(llmqParams.type), pindexNew->nHeight, quorum->m_quorum_base_block_index->nHeight, quorum->m_quorum_base_block_index->GetBlockHash().ToString());
57+
}
58+
} else if (watchOtherISQuorums && !quorum->IsMember(proTxHash)) {
59+
Uint256HashSet connections;
60+
const auto& cindexes = utils::CalcDeterministicWatchConnections(llmqParams.type, quorum->m_quorum_base_block_index, quorum->members.size(), 1);
61+
for (auto idx : cindexes) {
62+
connections.emplace(quorum->members[idx]->proTxHash);
63+
}
64+
if (!connections.empty()) {
65+
if (!m_connman.HasMasternodeQuorumNodes(llmqParams.type, quorum->m_quorum_base_block_index->GetBlockHash())) {
66+
LogPrint(BCLog::LLMQ, "QuorumParticipant::%s -- llmqType[%d] h[%d] adding mn inter-quorum connections for quorum: [%d:%s]\n", __func__, ToUnderlying(llmqParams.type), pindexNew->nHeight, quorum->m_quorum_base_block_index->nHeight, quorum->m_quorum_base_block_index->GetBlockHash().ToString());
67+
m_connman.SetMasternodeQuorumNodes(llmqParams.type, quorum->m_quorum_base_block_index->GetBlockHash(), connections);
68+
m_connman.SetMasternodeQuorumRelayMembers(llmqParams.type, quorum->m_quorum_base_block_index->GetBlockHash(), connections);
69+
}
70+
if (deletableQuorums.erase(quorum->qc->quorumHash) > 0) {
71+
LogPrint(BCLog::LLMQ, "QuorumParticipant::%s -- llmqType[%d] h[%d] keeping mn inter-quorum connections for quorum: [%d:%s]\n", __func__, ToUnderlying(llmqParams.type), pindexNew->nHeight, quorum->m_quorum_base_block_index->nHeight, quorum->m_quorum_base_block_index->GetBlockHash().ToString());
72+
}
73+
}
74+
}
75+
}
76+
77+
for (const auto& quorumHash : deletableQuorums) {
78+
LogPrint(BCLog::LLMQ, "QuorumParticipant::%s -- removing masternodes quorum connections for quorum %s:\n", __func__, quorumHash.ToString());
79+
m_connman.RemoveMasternodeQuorumNodes(llmqParams.type, quorumHash);
80+
}
81+
}
82+
83+
bool QuorumParticipant::SetQuorumSecretKeyShare(CQuorum& quorum, Span<CBLSSecretKey> skContributions) const
84+
{
85+
return quorum.SetSecretKeyShare(m_bls_worker.AggregateSecretKeys(skContributions), m_mn_activeman.GetProTxHash());
86+
}
87+
88+
size_t QuorumParticipant::GetQuorumRecoveryStartOffset(const CQuorum& quorum, gsl::not_null<const CBlockIndex*> pIndex) const
89+
{
90+
auto mns = m_dmnman.GetListForBlock(pIndex);
91+
std::vector<uint256> vecProTxHashes;
92+
vecProTxHashes.reserve(mns.GetValidMNsCount());
93+
mns.ForEachMN(/*onlyValid=*/true,
94+
[&](const auto& pMasternode) { vecProTxHashes.emplace_back(pMasternode.proTxHash); });
95+
std::sort(vecProTxHashes.begin(), vecProTxHashes.end());
96+
size_t nIndex{0};
97+
{
98+
auto my_protx_hash = m_mn_activeman.GetProTxHash();
99+
for (const auto i : irange::range(vecProTxHashes.size())) {
100+
// cppcheck-suppress useStlAlgorithm
101+
if (my_protx_hash == vecProTxHashes[i]) {
102+
nIndex = i;
103+
break;
104+
}
105+
}
106+
}
107+
return nIndex % quorum.qc->validMembers.size();
108+
}
109+
110+
MessageProcessingResult QuorumParticipant::ProcessContribQGETDATA(bool request_limit_exceeded, CDataStream& vStream,
111+
const CQuorum& quorum, CQuorumDataRequest& request,
112+
gsl::not_null<const CBlockIndex*> block_index)
113+
{
114+
if (request.GetDataMask() & CQuorumDataRequest::ENCRYPTED_CONTRIBUTIONS) {
115+
assert(block_index);
116+
117+
int memberIdx = quorum.GetMemberIndex(request.GetProTxHash());
118+
if (memberIdx == -1) {
119+
request.SetError(CQuorumDataRequest::Errors::MASTERNODE_IS_NO_MEMBER);
120+
return request_limit_exceeded ? MisbehavingError{25, "request limit exceeded"} : MessageProcessingResult{};
121+
}
122+
123+
std::vector<CBLSIESEncryptedObject<CBLSSecretKey>> vecEncrypted;
124+
if (!m_qman.GetEncryptedContributions(request.GetLLMQType(), block_index,
125+
quorum.qc->validMembers, request.GetProTxHash(), vecEncrypted)) {
126+
request.SetError(CQuorumDataRequest::Errors::ENCRYPTED_CONTRIBUTIONS_MISSING);
127+
return request_limit_exceeded ? MisbehavingError{25, "request limit exceeded"} : MessageProcessingResult{};
128+
}
129+
130+
vStream << vecEncrypted;
131+
}
132+
133+
return {};
134+
}
135+
136+
MessageProcessingResult QuorumParticipant::ProcessContribQDATA(CNode& pfrom, CDataStream& vStream,
137+
CQuorum& quorum, CQuorumDataRequest& request)
138+
{
139+
if (request.GetDataMask() & CQuorumDataRequest::ENCRYPTED_CONTRIBUTIONS) {
140+
if (WITH_LOCK(quorum.cs_vvec_shShare, return !quorum.HasVerificationVectorInternal()
141+
|| quorum.quorumVvec->size() != size_t(quorum.params.threshold))) {
142+
// Don't bump score because we asked for it
143+
LogPrint(BCLog::LLMQ, "QuorumParticipant::%s -- %s: No valid quorum verification vector available, from peer=%d\n", __func__, NetMsgType::QDATA, pfrom.GetId());
144+
return {};
145+
}
146+
147+
int memberIdx = quorum.GetMemberIndex(request.GetProTxHash());
148+
if (memberIdx == -1) {
149+
// Don't bump score because we asked for it
150+
LogPrint(BCLog::LLMQ, "QuorumParticipant::%s -- %s: Not a member of the quorum, from peer=%d\n", __func__, NetMsgType::QDATA, pfrom.GetId());
151+
return {};
152+
}
153+
154+
std::vector<CBLSIESEncryptedObject<CBLSSecretKey>> vecEncrypted;
155+
vStream >> vecEncrypted;
156+
157+
std::vector<CBLSSecretKey> vecSecretKeys;
158+
vecSecretKeys.resize(vecEncrypted.size());
159+
for (const auto i : irange::range(vecEncrypted.size())) {
160+
if (!m_mn_activeman.Decrypt(vecEncrypted[i], memberIdx, vecSecretKeys[i], PROTOCOL_VERSION)) {
161+
return MisbehavingError{10, "failed to decrypt"};
162+
}
163+
}
164+
165+
if (!quorum.SetSecretKeyShare(m_bls_worker.AggregateSecretKeys(vecSecretKeys), m_mn_activeman.GetProTxHash())) {
166+
return MisbehavingError{10, "invalid secret key share received"};
167+
}
168+
}
169+
170+
return {};
171+
}
172+
173+
bool QuorumParticipant::IsMasternode() const
174+
{
175+
// We are only initialized if masternode mode is enabled
176+
return true;
177+
}
178+
179+
bool QuorumParticipant::IsWatching() const
180+
{
181+
// Watch-only mode can co-exist with masternode mode
182+
return m_quorums_watch;
183+
}
184+
185+
void QuorumParticipant::StartDataRecoveryThread(gsl::not_null<const CBlockIndex*> pIndex, CQuorumCPtr pQuorum,
186+
uint16_t nDataMaskIn) const
187+
{
188+
bool expected = false;
189+
if (!pQuorum->fQuorumDataRecoveryThreadRunning.compare_exchange_strong(expected, true)) {
190+
LogPrint(BCLog::LLMQ, "QuorumParticipant::%s -- Already running\n", __func__);
191+
return;
192+
}
193+
194+
workerPool.push([pQuorum = std::move(pQuorum), pIndex, nDataMaskIn, this](int threadId) mutable {
195+
const size_t size_offset = GetQuorumRecoveryStartOffset(*pQuorum, pIndex);
196+
DataRecoveryThread(pIndex, std::move(pQuorum), nDataMaskIn, m_mn_activeman.GetProTxHash(), size_offset);
197+
});
198+
}
199+
200+
void QuorumParticipant::TriggerQuorumDataRecoveryThreads(gsl::not_null<const CBlockIndex*> block_index) const
201+
{
202+
if (!m_quorums_recovery) {
203+
return;
204+
}
205+
206+
LogPrint(BCLog::LLMQ, "QuorumParticipant::%s -- Process block %s\n", __func__, block_index->GetBlockHash().ToString());
207+
208+
const uint256 proTxHash = m_mn_activeman.GetProTxHash();
209+
210+
for (const auto& params : Params().GetConsensus().llmqs) {
211+
auto vecQuorums = m_qman.ScanQuorums(params.type, block_index, params.keepOldConnections);
212+
const bool fWeAreQuorumTypeMember = ranges::any_of(vecQuorums, [&proTxHash](const auto& pQuorum) { return pQuorum->IsValidMember(proTxHash); });
213+
214+
for (auto& pQuorum : vecQuorums) {
215+
if (pQuorum->IsValidMember(proTxHash)) {
216+
uint16_t nDataMask{0};
217+
if (!pQuorum->HasVerificationVector()) {
218+
nDataMask |= CQuorumDataRequest::QUORUM_VERIFICATION_VECTOR;
219+
}
220+
if (!pQuorum->GetSkShare().IsValid()) {
221+
nDataMask |= CQuorumDataRequest::ENCRYPTED_CONTRIBUTIONS;
222+
}
223+
if (nDataMask != 0) {
224+
StartDataRecoveryThread(block_index, std::move(pQuorum), nDataMask);
225+
} else {
226+
LogPrint(BCLog::LLMQ, "QuorumParticipant::%s -- No data needed from (%d, %s) at height %d\n", __func__,
227+
ToUnderlying(pQuorum->qc->llmqType), pQuorum->qc->quorumHash.ToString(), block_index->nHeight);
228+
}
229+
} else {
230+
TryStartVvecSyncThread(block_index, std::move(pQuorum), fWeAreQuorumTypeMember);
231+
}
232+
}
233+
}
234+
}
235+
} // namespace llmq

src/active/quorums.h

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
// Copyright (c) 2018-2025 The Dash Core developers
2+
// Distributed under the MIT software license, see the accompanying
3+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4+
5+
#ifndef BITCOIN_ACTIVE_QUORUMS_H
6+
#define BITCOIN_ACTIVE_QUORUMS_H
7+
8+
#include <llmq/observer/quorums.h>
9+
#include <llmq/types.h>
10+
11+
#include <consensus/params.h>
12+
#include <saltedhasher.h>
13+
#include <span.h>
14+
#include <sync.h>
15+
#include <threadsafety.h>
16+
#include <uint256.h>
17+
18+
#include <map>
19+
20+
class CActiveMasternodeManager;
21+
class CBlockIndex;
22+
class CBLSWorker;
23+
class CConnman;
24+
class CDeterministicMNManager;
25+
class CDKGSessionManager;
26+
class CNode;
27+
class CSporkManager;
28+
struct MessageProcessingResult;
29+
namespace llmq {
30+
class CQuorum;
31+
class CQuorumDataRequest;
32+
class CQuorumSnapshotManager;
33+
enum class QvvecSyncMode : int8_t;
34+
} // namespace llmq
35+
36+
namespace llmq {
37+
class QuorumParticipant final : public QuorumObserver
38+
{
39+
private:
40+
CBLSWorker& m_bls_worker;
41+
const CActiveMasternodeManager& m_mn_activeman;
42+
const bool m_quorums_watch{false};
43+
44+
public:
45+
QuorumParticipant() = delete;
46+
QuorumParticipant(const QuorumParticipant&) = delete;
47+
QuorumParticipant& operator=(const QuorumParticipant&) = delete;
48+
explicit QuorumParticipant(CBLSWorker& bls_worker, CConnman& connman, CDeterministicMNManager& dmnman,
49+
QuorumObserverParent& qman, CQuorumSnapshotManager& qsnapman,
50+
const CActiveMasternodeManager& mn_activeman, const ChainstateManager& chainman,
51+
const CMasternodeSync& mn_sync, const CSporkManager& sporkman,
52+
const llmq::QvvecSyncModeMap& sync_map, bool quorums_recovery, bool quorums_watch);
53+
~QuorumParticipant();
54+
55+
public:
56+
// QuorumObserver
57+
bool IsMasternode() const override;
58+
bool IsWatching() const override;
59+
bool SetQuorumSecretKeyShare(CQuorum& quorum, Span<CBLSSecretKey> skContributions) const override;
60+
[[nodiscard]] MessageProcessingResult ProcessContribQGETDATA(bool request_limit_exceeded, CDataStream& vStream,
61+
const CQuorum& quorum, CQuorumDataRequest& request,
62+
gsl::not_null<const CBlockIndex*> block_index) override;
63+
[[nodiscard]] MessageProcessingResult ProcessContribQDATA(CNode& pfrom, CDataStream& vStream, CQuorum& quorum,
64+
CQuorumDataRequest& request) override;
65+
66+
protected:
67+
// QuorumObserver
68+
void CheckQuorumConnections(const Consensus::LLMQParams& llmqParams,
69+
gsl::not_null<const CBlockIndex*> pindexNew) const override;
70+
void TriggerQuorumDataRecoveryThreads(gsl::not_null<const CBlockIndex*> block_index) const override;
71+
72+
private:
73+
/// Returns the start offset for the masternode with the given proTxHash. This offset is applied when picking data
74+
/// recovery members of a quorum's memberlist and is calculated based on a list of all member of all active quorums
75+
/// for the given llmqType in a way that each member should receive the same number of request if all active
76+
/// llmqType members requests data from one llmqType quorum.
77+
size_t GetQuorumRecoveryStartOffset(const CQuorum& quorum, gsl::not_null<const CBlockIndex*> pIndex) const;
78+
79+
void StartDataRecoveryThread(gsl::not_null<const CBlockIndex*> pIndex, CQuorumCPtr pQuorum, uint16_t nDataMaskIn) const;
80+
};
81+
} // namespace llmq
82+
83+
#endif // BITCOIN_ACTIVE_QUORUMS_H

src/chainlock/chainlock.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
#include <validationinterface.h>
1818

1919
#include <instantsend/instantsend.h>
20-
#include <llmq/quorums.h>
20+
#include <llmq/quorumsman.h>
2121
#include <masternode/sync.h>
2222
#include <spork.h>
2323
#include <stats/client.h>

src/chainlock/chainlock.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ namespace llmq {
3838
class CInstantSendManager;
3939
class CQuorumManager;
4040
class CSigningManager;
41-
enum class VerifyRecSigStatus;
41+
enum class VerifyRecSigStatus : uint8_t;
4242

4343
class CChainLocksHandler final : public chainlock::ChainLockSignerParent
4444
{

0 commit comments

Comments
 (0)