Line data Source code
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/dkgsessionhandler.h>
6 :
7 : #include <active/dkgsession.h>
8 : #include <active/masternode.h>
9 : #include <llmq/debug.h>
10 : #include <llmq/dkgsession.h>
11 : #include <llmq/net_quorum.h>
12 :
13 : #include <chainparams.h>
14 : #include <deploymentstatus.h>
15 : #include <logging.h>
16 : #include <util/time.h>
17 :
18 : namespace llmq {
19 0 : ActiveDKGSessionHandler::ActiveDKGSessionHandler(
20 : CBLSWorker& bls_worker, CDeterministicMNManager& dmnman, CMasternodeMetaMan& mn_metaman,
21 : llmq::CDKGDebugManager& dkgdbgman, llmq::CDKGSessionManager& qdkgsman, llmq::CQuorumBlockProcessor& qblockman,
22 : llmq::CQuorumSnapshotManager& qsnapman, const CActiveMasternodeManager& mn_activeman, const ChainstateManager& chainman,
23 : const CSporkManager& sporkman, const Consensus::LLMQParams& llmq_params, bool quorums_watch, int quorums_idx) :
24 0 : llmq::CDKGSessionHandler(llmq_params),
25 0 : m_bls_worker{bls_worker},
26 0 : m_dmnman{dmnman},
27 0 : m_mn_metaman{mn_metaman},
28 0 : m_dkgdbgman{dkgdbgman},
29 0 : m_qdkgsman{qdkgsman},
30 0 : m_qblockman{qblockman},
31 0 : m_qsnapman{qsnapman},
32 0 : m_mn_activeman{mn_activeman},
33 0 : m_chainman{chainman},
34 0 : m_sporkman{sporkman},
35 0 : m_quorums_watch{quorums_watch},
36 0 : quorumIndex{quorums_idx}
37 0 : {
38 0 : }
39 :
40 0 : ActiveDKGSessionHandler::~ActiveDKGSessionHandler() = default;
41 :
42 0 : void ActiveDKGSessionHandler::UpdatedBlockTip(const CBlockIndex* pindexNew)
43 : {
44 : //AssertLockNotHeld(cs_main);
45 : //Indexed quorums (greater than 0) are enabled with Quorum Rotation
46 0 : if (quorumIndex > 0 && !IsQuorumRotationEnabled(params, pindexNew)) {
47 0 : return;
48 : }
49 0 : LOCK(cs_phase_qhash);
50 :
51 0 : int quorumStageInt = (pindexNew->nHeight - quorumIndex) % params.dkgInterval;
52 :
53 0 : const CBlockIndex* pQuorumBaseBlockIndex = pindexNew->GetAncestor(pindexNew->nHeight - quorumStageInt);
54 :
55 0 : currentHeight = pindexNew->nHeight;
56 0 : quorumHash = pQuorumBaseBlockIndex->GetBlockHash();
57 :
58 0 : bool fNewPhase = (quorumStageInt % params.dkgPhaseBlocks) == 0;
59 0 : int phaseInt = quorumStageInt / params.dkgPhaseBlocks + 1;
60 0 : QuorumPhase oldPhase = phase;
61 0 : if (fNewPhase && phaseInt >= std23::to_underlying(QuorumPhase::Initialized) && phaseInt <= std23::to_underlying(QuorumPhase::Idle)) {
62 0 : phase = static_cast<QuorumPhase>(phaseInt);
63 0 : }
64 :
65 0 : LogPrint(BCLog::LLMQ_DKG, "ActiveDKGSessionHandler::%s -- %s qi[%d] currentHeight=%d, pQuorumBaseBlockIndex->nHeight=%d, oldPhase=%d, newPhase=%d\n", __func__,
66 : params.name, quorumIndex, currentHeight, pQuorumBaseBlockIndex->nHeight, std23::to_underlying(oldPhase), std23::to_underlying(phase));
67 0 : }
68 :
69 0 : uint256 ActiveDKGSessionHandler::GetCurrentQuorumHash() const { return WITH_LOCK(cs_phase_qhash, return quorumHash); }
70 :
71 0 : std::pair<QuorumPhase, uint256> ActiveDKGSessionHandler::GetPhaseAndQuorumHash() const
72 : {
73 0 : LOCK(cs_phase_qhash);
74 0 : return std::make_pair(phase, quorumHash);
75 0 : }
76 :
77 0 : bool ActiveDKGSessionHandler::InitNewQuorum(gsl::not_null<const CBlockIndex*> pQuorumBaseBlockIndex)
78 : {
79 0 : if (!DeploymentDIP0003Enforced(pQuorumBaseBlockIndex->nHeight, Params().GetConsensus())) {
80 0 : return false;
81 : }
82 :
83 0 : curSession = std::make_unique<ActiveDKGSession>(m_bls_worker, m_dmnman, m_dkgdbgman, m_qdkgsman, m_mn_metaman,
84 0 : m_qsnapman, m_mn_activeman, m_chainman, m_sporkman,
85 0 : pQuorumBaseBlockIndex, params);
86 :
87 0 : if (!curSession->Init(m_mn_activeman.GetProTxHash(), quorumIndex)) {
88 0 : LogPrintf("ActiveDKGSessionHandler::%s -- height[%d] quorum initialization failed for %s qi[%d]\n", __func__,
89 : pQuorumBaseBlockIndex->nHeight, params.name, quorumIndex);
90 0 : return false;
91 : }
92 :
93 0 : LogPrintf("ActiveDKGSessionHandler::%s -- height[%d] quorum initialization OK for %s qi[%d]\n", __func__, pQuorumBaseBlockIndex->nHeight, params.name, quorumIndex);
94 0 : return true;
95 0 : }
96 :
97 0 : void ActiveDKGSessionHandler::WaitForNextPhase(std::optional<QuorumPhase> curPhase, QuorumPhase nextPhase,
98 : const uint256& expectedQuorumHash, const WhileWaitFunc& shouldNotWait) const
99 : {
100 0 : LogPrint(BCLog::LLMQ_DKG, "ActiveDKGSessionHandler::%s -- %s qi[%d] - starting, curPhase=%d, nextPhase=%d\n", __func__, params.name, quorumIndex, curPhase.has_value() ? std23::to_underlying(*curPhase) : -1, std23::to_underlying(nextPhase));
101 :
102 0 : while (true) {
103 0 : if (stopRequested) {
104 0 : LogPrint(BCLog::LLMQ_DKG, "ActiveDKGSessionHandler::%s -- %s qi[%d] - aborting due to stop/shutdown requested\n", __func__, params.name, quorumIndex);
105 0 : throw AbortPhaseException();
106 : }
107 0 : auto [_phase, _quorumHash] = GetPhaseAndQuorumHash();
108 0 : if (!expectedQuorumHash.IsNull() && _quorumHash != expectedQuorumHash) {
109 0 : LogPrint(BCLog::LLMQ_DKG, "ActiveDKGSessionHandler::%s -- %s qi[%d] - aborting due unexpected expectedQuorumHash change\n", __func__, params.name, quorumIndex);
110 0 : throw AbortPhaseException();
111 : }
112 0 : if (_phase == nextPhase) {
113 0 : break;
114 : }
115 0 : if (curPhase.has_value() && _phase != curPhase) {
116 0 : LogPrint(BCLog::LLMQ_DKG, "ActiveDKGSessionHandler::%s -- %s qi[%d] - aborting due unexpected phase change, _phase=%d, curPhase=%d\n", __func__, params.name, quorumIndex, std23::to_underlying(_phase), curPhase.has_value() ? std23::to_underlying(*curPhase) : -1);
117 0 : throw AbortPhaseException();
118 : }
119 0 : if (!shouldNotWait()) {
120 0 : UninterruptibleSleep(std::chrono::milliseconds{100});
121 0 : }
122 : }
123 :
124 0 : LogPrint(BCLog::LLMQ_DKG, "ActiveDKGSessionHandler::%s -- %s qi[%d] - done, curPhase=%d, nextPhase=%d\n", __func__, params.name, quorumIndex, curPhase.has_value() ? std23::to_underlying(*curPhase) : -1, std23::to_underlying(nextPhase));
125 :
126 0 : if (nextPhase == QuorumPhase::Initialized) {
127 0 : m_dkgdbgman.ResetLocalSessionStatus(params.type, quorumIndex);
128 0 : } else {
129 0 : m_dkgdbgman.UpdateLocalSessionStatus(params.type, quorumIndex, [&](CDKGDebugSessionStatus& status) {
130 0 : bool changed = status.phase != nextPhase;
131 0 : status.phase = nextPhase;
132 0 : return changed;
133 : });
134 : }
135 0 : }
136 :
137 0 : void ActiveDKGSessionHandler::WaitForNewQuorum(const uint256& oldQuorumHash) const
138 : {
139 0 : LogPrint(BCLog::LLMQ_DKG, "ActiveDKGSessionHandler::%s -- %s qi[%d]- starting\n", __func__, params.name, quorumIndex);
140 :
141 0 : while (true) {
142 0 : if (stopRequested) {
143 0 : LogPrint(BCLog::LLMQ_DKG, "ActiveDKGSessionHandler::%s -- %s qi[%d] - aborting due to stop/shutdown requested\n", __func__, params.name, quorumIndex);
144 0 : throw AbortPhaseException();
145 : }
146 0 : auto [_, _quorumHash] = GetPhaseAndQuorumHash();
147 0 : if (_quorumHash != oldQuorumHash) {
148 0 : break;
149 : }
150 0 : UninterruptibleSleep(std::chrono::milliseconds{100});
151 : }
152 :
153 0 : LogPrint(BCLog::LLMQ_DKG, "ActiveDKGSessionHandler::%s -- %s qi[%d] - done\n", __func__, params.name, quorumIndex);
154 0 : }
155 :
156 : // Sleep some time to not fully overload the whole network
157 0 : void ActiveDKGSessionHandler::SleepBeforePhase(QuorumPhase curPhase, const uint256& expectedQuorumHash,
158 : double randomSleepFactor, const WhileWaitFunc& runWhileWaiting) const
159 : {
160 0 : if (!curSession->AreWeMember()) {
161 : // Non-members do not participate and do not create any network load, no need to sleep.
162 0 : return;
163 : }
164 :
165 0 : if (Params().MineBlocksOnDemand()) {
166 : // On regtest, blocks can be mined on demand without any significant time passing between these.
167 : // We shouldn't wait before phases in this case.
168 0 : return;
169 : }
170 :
171 : // Two blocks can come very close to each other, this happens pretty regularly. We don't want to be
172 : // left behind and marked as a bad member. This means that we should not count the last block of the
173 : // phase as a safe one to keep sleeping, that's why we calculate the phase sleep time as a time of
174 : // the full phase minus one block here.
175 0 : double phaseSleepTime = (params.dkgPhaseBlocks - 1) * Params().GetConsensus().nPowTargetSpacing * 1000;
176 : // Expected phase sleep time per member
177 0 : double phaseSleepTimePerMember = phaseSleepTime / params.size;
178 : // Don't expect perfect block times and thus reduce the phase time to be on the secure side (caller chooses factor)
179 0 : double adjustedPhaseSleepTimePerMember = phaseSleepTimePerMember * randomSleepFactor;
180 :
181 0 : int64_t sleepTime = (int64_t)(adjustedPhaseSleepTimePerMember * curSession->GetMyMemberIndex().value_or(0));
182 0 : const auto endTime = SteadyClock::now() + std::chrono::milliseconds{sleepTime};
183 0 : int heightTmp{currentHeight.load()};
184 0 : int heightStart{heightTmp};
185 :
186 0 : LogPrint(BCLog::LLMQ_DKG, "ActiveDKGSessionHandler::%s -- %s qi[%d] - starting sleep for %d ms, curPhase=%d\n", __func__, params.name, quorumIndex, sleepTime, std23::to_underlying(curPhase));
187 :
188 0 : while (SteadyClock::now() < endTime) {
189 0 : if (stopRequested) {
190 0 : LogPrint(BCLog::LLMQ_DKG, "ActiveDKGSessionHandler::%s -- %s qi[%d] - aborting due to stop/shutdown requested\n", __func__, params.name, quorumIndex);
191 0 : throw AbortPhaseException();
192 : }
193 0 : auto cur_height = currentHeight.load();
194 0 : if (cur_height > heightTmp) {
195 : // New block(s) just came in
196 0 : int64_t expectedBlockTime = (cur_height - heightStart) * Params().GetConsensus().nPowTargetSpacing * 1000;
197 0 : if (expectedBlockTime > sleepTime) {
198 : // Blocks came faster than we expected, jump into the phase func asap
199 0 : break;
200 : }
201 0 : heightTmp = cur_height;
202 0 : }
203 0 : if (WITH_LOCK(cs_phase_qhash, return phase != curPhase || quorumHash != expectedQuorumHash)) {
204 : // Something went wrong and/or we missed quite a few blocks and it's just too late now
205 0 : LogPrint(BCLog::LLMQ_DKG, "ActiveDKGSessionHandler::%s -- %s qi[%d] - aborting due unexpected phase/expectedQuorumHash change\n", __func__, params.name, quorumIndex);
206 0 : throw AbortPhaseException();
207 : }
208 0 : if (!runWhileWaiting()) {
209 0 : UninterruptibleSleep(std::chrono::milliseconds{100});
210 0 : }
211 : }
212 :
213 0 : LogPrint(BCLog::LLMQ_DKG, "ActiveDKGSessionHandler::%s -- %s qi[%d] - done, curPhase=%d\n", __func__, params.name, quorumIndex, std23::to_underlying(curPhase));
214 0 : }
215 :
216 0 : void ActiveDKGSessionHandler::HandlePhase(QuorumPhase curPhase, QuorumPhase nextPhase,
217 : const uint256& expectedQuorumHash, double randomSleepFactor,
218 : const StartPhaseFunc& startPhaseFunc, const WhileWaitFunc& runWhileWaiting)
219 : {
220 0 : LogPrint(BCLog::LLMQ_DKG, "ActiveDKGSessionHandler::%s -- %s qi[%d] - starting, curPhase=%d, nextPhase=%d\n", __func__, params.name, quorumIndex, std23::to_underlying(curPhase), std23::to_underlying(nextPhase));
221 :
222 0 : SleepBeforePhase(curPhase, expectedQuorumHash, randomSleepFactor, runWhileWaiting);
223 0 : startPhaseFunc();
224 0 : WaitForNextPhase(curPhase, nextPhase, expectedQuorumHash, runWhileWaiting);
225 :
226 0 : LogPrint(BCLog::LLMQ_DKG, "ActiveDKGSessionHandler::%s -- %s qi[%d] - done, curPhase=%d, nextPhase=%d\n", __func__, params.name, quorumIndex, std23::to_underlying(curPhase), std23::to_underlying(nextPhase));
227 0 : }
228 :
229 0 : bool ActiveDKGSessionHandler::GetContribution(const uint256& hash, CDKGContribution& ret) const
230 : {
231 0 : return curSession && curSession->GetContribution(hash, ret);
232 : }
233 :
234 0 : bool ActiveDKGSessionHandler::GetComplaint(const uint256& hash, CDKGComplaint& ret) const
235 : {
236 0 : return curSession && curSession->GetComplaint(hash, ret);
237 : }
238 :
239 0 : bool ActiveDKGSessionHandler::GetJustification(const uint256& hash, CDKGJustification& ret) const
240 : {
241 0 : return curSession && curSession->GetJustification(hash, ret);
242 : }
243 :
244 0 : bool ActiveDKGSessionHandler::GetPrematureCommitment(const uint256& hash, CDKGPrematureCommitment& ret) const
245 : {
246 0 : return curSession && curSession->GetPrematureCommitment(hash, ret);
247 : }
248 :
249 0 : QuorumPhase ActiveDKGSessionHandler::GetPhase() const
250 : {
251 0 : return WITH_LOCK(cs_phase_qhash, return phase);
252 : }
253 : } // namespace llmq
|