Line data Source code
1 : // Copyright (c) 2019-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 <chainlock/signing.h>
6 :
7 : #include <chainlock/clsig.h>
8 : #include <chainlock/handler.h>
9 : #include <instantsend/instantsend.h>
10 : #include <llmq/signing_shares.h>
11 : #include <masternode/sync.h>
12 : #include <msg_result.h>
13 : #include <node/blockstorage.h>
14 : #include <scheduler.h>
15 : #include <util/thread.h>
16 : #include <validation.h>
17 :
18 : #include <thread>
19 :
20 : using node::ReadBlockFromDisk;
21 :
22 : namespace chainlock {
23 3300 : ChainLockSigner::ChainLockSigner(CChainState& chainstate, const chainlock::Chainlocks& chainlocks,
24 : ChainlockHandler& clhandler, const llmq::CInstantSendManager& isman,
25 : const llmq::CQuorumManager& qman, llmq::CSigningManager& sigman,
26 : llmq::CSigSharesManager& shareman, const CMasternodeSync& mn_sync) :
27 660 : m_chainstate{chainstate},
28 660 : m_chainlocks{chainlocks},
29 660 : m_clhandler{clhandler},
30 660 : m_isman{isman},
31 660 : m_qman{qman},
32 660 : m_sigman{sigman},
33 660 : m_shareman{shareman},
34 660 : m_mn_sync{mn_sync},
35 660 : m_scheduler{std::make_unique<CScheduler>()},
36 1320 : m_scheduler_thread{
37 1320 : std::make_unique<std::thread>(std::thread(util::TraceThread, "cls-schdlr", [&] { m_scheduler->serviceQueue(); }))}
38 1980 : {
39 660 : }
40 :
41 1320 : ChainLockSigner::~ChainLockSigner() { Stop(); }
42 :
43 660 : void ChainLockSigner::Start()
44 : {
45 1320 : m_scheduler->scheduleEvery(
46 8452 : [&]() {
47 7792 : if (!m_chainlocks.IsSigningEnabled()) return;
48 : // regularly retry signing the current chaintip as it might have failed before due to missing islocks
49 5905 : TrySignChainTip();
50 5905 : Cleanup();
51 7792 : },
52 660 : std::chrono::seconds{5});
53 660 : }
54 :
55 1320 : void ChainLockSigner::Stop()
56 : {
57 1320 : m_scheduler->stop();
58 1320 : if (m_scheduler_thread->joinable()) m_scheduler_thread->join();
59 1320 : }
60 :
61 660 : void ChainLockSigner::RegisterRecoveryInterface()
62 : {
63 660 : m_sigman.RegisterRecoveredSigsListener(this);
64 660 : }
65 :
66 660 : void ChainLockSigner::UnregisterRecoveryInterface()
67 : {
68 660 : m_sigman.UnregisterRecoveredSigsListener(this);
69 660 : }
70 :
71 86421 : void ChainLockSigner::UpdatedBlockTip(const CBlockIndex* pindexNew, const CBlockIndex* pindexFork, bool fInitialDownload)
72 : {
73 86421 : TrySignChainTip();
74 86421 : }
75 :
76 92326 : void ChainLockSigner::TrySignChainTip()
77 : {
78 92326 : TRY_LOCK(cs_try_sign, locked);
79 92326 : if (!locked) {
80 116 : return;
81 : }
82 :
83 92210 : if (!m_mn_sync.IsBlockchainSynced()) {
84 6779 : return;
85 : }
86 :
87 85431 : if (!m_chainlocks.IsEnabled() || !m_chainlocks.IsSigningEnabled()) {
88 25366 : return;
89 : }
90 :
91 120130 : const CBlockIndex* pindex = WITH_LOCK(::cs_main, return m_chainstate.m_chain.Tip());
92 :
93 60065 : if (!pindex || !pindex->pprev) {
94 0 : return;
95 : }
96 :
97 : // DIP8 defines a process called "Signing attempts" which should run before the CLSIG is finalized
98 : // To simplify the initial implementation, we skip this process and directly try to create a CLSIG
99 : // This will fail when multiple blocks compete, but we accept this for the initial implementation.
100 : // Later, we'll add the multiple attempts process.
101 :
102 : {
103 60065 : LOCK(cs_signer);
104 :
105 60065 : if (pindex->nHeight == lastSignedHeight) {
106 : // already signed this one
107 18197 : return;
108 : }
109 60065 : }
110 :
111 41868 : if (m_chainlocks.GetBestChainLockHeight() >= pindex->nHeight) {
112 : // already got the same CLSIG or a better one
113 143 : return;
114 : }
115 :
116 41725 : if (m_chainlocks.HasConflictingChainLock(pindex->nHeight, pindex->GetBlockHash())) {
117 : // don't sign if another conflicting CLSIG is already present. EnforceBestChainLock will later enforce
118 : // the correct chain.
119 0 : return;
120 : }
121 :
122 41725 : LogPrint(BCLog::CHAINLOCKS, "%s -- trying to sign %s, height=%d\n", __func__, pindex->GetBlockHash().ToString(),
123 : pindex->nHeight);
124 :
125 : // When the new IX system is activated, we only try to ChainLock blocks which include safe transactions. A TX is
126 : // considered safe when it is islocked or at least known since 10 minutes (from mempool or block). These checks are
127 : // performed for the tip (which we try to sign) and the previous 5 blocks. If a ChainLocked block is found on the
128 : // way down, we consider all TXs to be safe.
129 41725 : if (m_isman.IsInstantSendEnabled()) {
130 40375 : const auto* pindexWalk = pindex;
131 193560 : while (pindexWalk != nullptr) {
132 193560 : if (pindex->nHeight - pindexWalk->nHeight > TX_CONFIRM_THRESHOLD) {
133 : // no need to check further down, safe to assume that TXs below this height won't be
134 : // islocked anymore if they aren't already
135 18537 : LogPrint(BCLog::CHAINLOCKS, "%s -- tip and previous %d blocks all safe\n", __func__, TX_CONFIRM_THRESHOLD);
136 18537 : break;
137 : }
138 175023 : if (m_chainlocks.HasChainLock(pindexWalk->nHeight, pindexWalk->GetBlockHash())) {
139 : // we don't care about islocks for TXs that are ChainLocked already
140 19517 : LogPrint(BCLog::CHAINLOCKS, "%s -- chainlock at height %d\n", __func__, pindexWalk->nHeight);
141 19517 : break;
142 : }
143 :
144 155506 : auto txids = GetBlockTxs(pindexWalk->GetBlockHash());
145 155506 : if (!txids) {
146 0 : pindexWalk = pindexWalk->pprev;
147 0 : continue;
148 : }
149 :
150 156650 : for (const auto& txid : *txids) {
151 3465 : if (!m_clhandler.IsTxSafeForMining(txid) && !m_isman.IsLocked(txid)) {
152 2321 : LogPrint(BCLog::CHAINLOCKS, /* Continued */
153 : "%s -- not signing block %s due to TX %s not being islocked and not old enough.\n",
154 : __func__, pindexWalk->GetBlockHash().ToString(), txid.ToString());
155 2321 : return;
156 : }
157 : }
158 :
159 153185 : pindexWalk = pindexWalk->pprev;
160 155506 : }
161 38054 : }
162 :
163 39404 : uint256 requestId = GenSigRequestId(pindex->nHeight);
164 39404 : uint256 msgHash = pindex->GetBlockHash();
165 :
166 39404 : if (m_chainlocks.GetBestChainLockHeight() >= pindex->nHeight) {
167 : // might have happened while we didn't hold cs
168 1 : return;
169 : }
170 : {
171 39403 : LOCK(cs_signer);
172 39403 : lastSignedHeight = pindex->nHeight;
173 39403 : lastSignedRequestId = requestId;
174 39403 : lastSignedMsgHash = msgHash;
175 39403 : }
176 :
177 39403 : m_shareman.AsyncSignIfMember(Params().GetConsensus().llmqTypeChainLocks, m_sigman, requestId, msgHash);
178 92326 : }
179 :
180 2945 : void ChainLockSigner::BlockDisconnected(const std::shared_ptr<const CBlock>& block, const CBlockIndex* pindex)
181 : {
182 2945 : AssertLockNotHeld(cs_signer);
183 2945 : LOCK(cs_signer);
184 2945 : blockTxs.erase(pindex->GetBlockHash());
185 2945 : }
186 :
187 :
188 86746 : void ChainLockSigner::BlockConnected(const std::shared_ptr<const CBlock>& block, const CBlockIndex* pindex)
189 : {
190 86746 : if (!m_mn_sync.IsBlockchainSynced()) {
191 6649 : return;
192 : }
193 :
194 : // We need this information later when we try to sign a new tip, so that we can determine if all included TXs are safe.
195 80097 : const uint256& hash = pindex->GetBlockHash();
196 :
197 80097 : AssertLockNotHeld(cs_signer);
198 80097 : LOCK(cs_signer);
199 80097 : auto it = blockTxs.find(hash);
200 80097 : if (it == blockTxs.end()) {
201 : // We must create this entry even if there are no lockable transactions in the block, so that TrySignChainTip
202 : // later knows about this block
203 61908 : it = blockTxs.emplace(hash, std::make_shared<Uint256HashSet>()).first;
204 61908 : }
205 80097 : auto& txids = *it->second;
206 259203 : for (const auto& tx : block->vtx) {
207 179106 : if (!tx->IsCoinBase() && !tx->vin.empty()) {
208 6757 : txids.emplace(tx->GetHash());
209 6757 : }
210 : }
211 86746 : }
212 155506 : ChainLockSigner::BlockTxs::mapped_type ChainLockSigner::GetBlockTxs(const uint256& blockHash)
213 : {
214 155506 : AssertLockNotHeld(cs_signer);
215 155506 : AssertLockNotHeld(::cs_main);
216 :
217 155506 : ChainLockSigner::BlockTxs::mapped_type ret;
218 :
219 : {
220 155506 : LOCK(cs_signer);
221 155506 : auto it = blockTxs.find(blockHash);
222 155506 : if (it != blockTxs.end()) {
223 136203 : ret = it->second;
224 136203 : }
225 155506 : }
226 155506 : if (!ret) {
227 : // This should only happen when freshly started.
228 : // If running for some time, SyncTransaction should have been called before which fills blockTxs.
229 19303 : LogPrint(BCLog::CHAINLOCKS, "%s -- blockTxs for %s not found. Trying ReadBlockFromDisk\n", __func__,
230 : blockHash.ToString());
231 :
232 : uint32_t blockTime;
233 : {
234 19303 : LOCK(::cs_main);
235 19303 : const auto* pindex = m_chainstate.m_blockman.LookupBlockIndex(blockHash);
236 19303 : if (!pindex) {
237 0 : return nullptr;
238 : }
239 19303 : CBlock block;
240 19303 : if (!ReadBlockFromDisk(block, pindex, Params().GetConsensus())) {
241 0 : return nullptr;
242 : }
243 :
244 19303 : ret = std::make_shared<Uint256HashSet>();
245 61454 : for (const auto& tx : block.vtx) {
246 42151 : if (tx->IsCoinBase() || tx->vin.empty()) {
247 41999 : continue;
248 : }
249 152 : ret->emplace(tx->GetHash());
250 : }
251 :
252 19303 : blockTime = block.nTime;
253 19303 : }
254 : {
255 19303 : LOCK(cs_signer);
256 19303 : blockTxs.emplace(blockHash, ret);
257 19303 : }
258 19303 : m_clhandler.UpdateTxFirstSeenMap(*ret, blockTime);
259 19303 : }
260 155506 : return ret;
261 155506 : }
262 :
263 26962 : llmq::RecoveredSigResult ChainLockSigner::HandleNewRecoveredSig(const llmq::CRecoveredSig& recoveredSig)
264 : {
265 26962 : if (!m_chainlocks.IsEnabled()) {
266 341 : return std::monostate{};
267 : }
268 :
269 26621 : ChainLockSig clsig;
270 : {
271 26621 : LOCK(cs_signer);
272 :
273 26621 : if (recoveredSig.getId() != lastSignedRequestId || recoveredSig.getMsgHash() != lastSignedMsgHash) {
274 : // this is not what we signed, so lets not create a CLSIG for it
275 16624 : return std::monostate{};
276 : }
277 9997 : if (m_chainlocks.GetBestChainLockHeight() >= lastSignedHeight) {
278 : // already got the same or a better CLSIG through the CLSIG message
279 4938 : return std::monostate{};
280 : }
281 :
282 5059 : clsig = ChainLockSig(lastSignedHeight, lastSignedMsgHash, recoveredSig.sig.Get());
283 26621 : }
284 : // TODO: split ProcessNewChainLock into network and non-network variants; when no peer
285 : // is specified (node == -1), only m_inventory is ever populated
286 5059 : auto clresult = m_clhandler.ProcessNewChainLock(-1, clsig, m_qman, ::SerializeHash(clsig));
287 5059 : if (!clresult.m_inventory.empty()) {
288 4888 : return clresult.m_inventory.front();
289 : }
290 171 : return std::monostate{};
291 26962 : }
292 :
293 5905 : void ChainLockSigner::Cleanup()
294 : {
295 5905 : constexpr auto CLEANUP_INTERVAL{30s};
296 5905 : if (!m_mn_sync.IsBlockchainSynced()) {
297 130 : return;
298 : }
299 :
300 5775 : if (!m_cleanup_throttler.TryCleanup(CLEANUP_INTERVAL)) {
301 3676 : return;
302 : }
303 :
304 2099 : AssertLockNotHeld(cs_signer);
305 2099 : std::vector<std::shared_ptr<Uint256HashSet>> removed;
306 2099 : LOCK2(::cs_main, cs_signer);
307 65697 : for (auto it = blockTxs.begin(); it != blockTxs.end();) {
308 63598 : const auto* pindex = m_chainstate.m_blockman.LookupBlockIndex(it->first);
309 63598 : if (!pindex) {
310 0 : it = blockTxs.erase(it);
311 63598 : } else if (m_chainlocks.HasChainLock(pindex->nHeight, pindex->GetBlockHash())) {
312 44120 : removed.push_back(it->second);
313 44120 : it = blockTxs.erase(it);
314 63598 : } else if (m_chainlocks.HasConflictingChainLock(pindex->nHeight, pindex->GetBlockHash())) {
315 0 : it = blockTxs.erase(it);
316 0 : } else {
317 19478 : ++it;
318 : }
319 : }
320 2099 : m_clhandler.CleanupFromSigner(removed);
321 5905 : }
322 : } // namespace chainlock
|