Line data Source code
1 : // Copyright (c) 2014-2025 The Dash Core developers
2 : // Distributed under the MIT/X11 software license, see the accompanying
3 : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4 :
5 : #ifndef BITCOIN_COINJOIN_COINJOIN_H
6 : #define BITCOIN_COINJOIN_COINJOIN_H
7 :
8 : #include <coinjoin/common.h>
9 :
10 : #include <util/helpers.h>
11 :
12 : #include <core_io.h>
13 : #include <netaddress.h>
14 : #include <primitives/block.h>
15 : #include <primitives/transaction.h>
16 : #include <sync.h>
17 : #include <timedata.h>
18 : #include <util/translation.h>
19 : #include <version.h>
20 :
21 : #include <atomic>
22 : #include <map>
23 : #include <optional>
24 : #include <utility>
25 :
26 : #include <univalue.h>
27 :
28 : class CChainState;
29 : class CBLSPublicKey;
30 : class CBlockIndex;
31 : class ChainstateManager;
32 : class CTxMemPool;
33 :
34 : namespace chainlock {
35 : class Chainlocks;
36 : } // namespace chainlock
37 :
38 : namespace llmq {
39 : class CInstantSendManager;
40 : } // namespace llmq
41 :
42 : extern RecursiveMutex cs_main; // NOLINT(readability-redundant-declaration)
43 :
44 : // timeouts
45 : static constexpr int COINJOIN_AUTO_TIMEOUT_MIN = 5;
46 : static constexpr int COINJOIN_AUTO_TIMEOUT_MAX = 15;
47 : static constexpr int COINJOIN_QUEUE_TIMEOUT = 30;
48 : static constexpr int COINJOIN_SIGNING_TIMEOUT = 15;
49 :
50 : static constexpr size_t COINJOIN_ENTRY_MAX_SIZE = 9;
51 :
52 : // pool responses
53 : enum PoolMessage : int32_t {
54 : ERR_ALREADY_HAVE,
55 : ERR_DENOM,
56 : ERR_ENTRIES_FULL,
57 : ERR_EXISTING_TX,
58 : ERR_FEES,
59 : ERR_INVALID_COLLATERAL,
60 : ERR_INVALID_INPUT,
61 : ERR_INVALID_SCRIPT,
62 : ERR_INVALID_TX,
63 : ERR_MAXIMUM,
64 : ERR_MN_LIST,
65 : ERR_MODE,
66 : ERR_NON_STANDARD_PUBKEY, // not used
67 : ERR_NOT_A_MN, // not used
68 : ERR_QUEUE_FULL,
69 : ERR_RECENT,
70 : ERR_SESSION,
71 : ERR_MISSING_TX,
72 : ERR_VERSION,
73 : MSG_NOERR,
74 : MSG_SUCCESS,
75 : MSG_ENTRIES_ADDED,
76 : ERR_SIZE_MISMATCH,
77 : MSG_POOL_MIN = ERR_ALREADY_HAVE,
78 : MSG_POOL_MAX = ERR_SIZE_MISMATCH
79 : };
80 : template<> struct is_serializable_enum<PoolMessage> : std::true_type {};
81 :
82 : // pool states
83 : enum PoolState : int32_t {
84 : POOL_STATE_IDLE,
85 : POOL_STATE_QUEUE,
86 : POOL_STATE_ACCEPTING_ENTRIES,
87 : POOL_STATE_SIGNING,
88 : POOL_STATE_ERROR,
89 : POOL_STATE_MIN = POOL_STATE_IDLE,
90 : POOL_STATE_MAX = POOL_STATE_ERROR
91 : };
92 : template<> struct is_serializable_enum<PoolState> : std::true_type {};
93 :
94 : // status update message constants
95 : enum PoolStatusUpdate : int32_t {
96 : STATUS_REJECTED,
97 : STATUS_ACCEPTED
98 : };
99 : template<> struct is_serializable_enum<PoolStatusUpdate> : std::true_type {};
100 :
101 : class CCoinJoinStatusUpdate
102 : {
103 : public:
104 1 : int nSessionID{0};
105 1 : PoolState nState{POOL_STATE_IDLE};
106 1 : int nEntriesCount{0}; // deprecated, kept for backwards compatibility
107 1 : PoolStatusUpdate nStatusUpdate{STATUS_ACCEPTED};
108 1 : PoolMessage nMessageID{MSG_NOERR};
109 :
110 3 : constexpr CCoinJoinStatusUpdate() = default;
111 :
112 2 : constexpr CCoinJoinStatusUpdate(int nSessionID, PoolState nState, int nEntriesCount, PoolStatusUpdate nStatusUpdate, PoolMessage nMessageID) :
113 1 : nSessionID(nSessionID),
114 1 : nState(nState),
115 1 : nEntriesCount(nEntriesCount),
116 1 : nStatusUpdate(nStatusUpdate),
117 2 : nMessageID(nMessageID) {};
118 :
119 0 : SERIALIZE_METHODS(CCoinJoinStatusUpdate, obj)
120 : {
121 0 : READWRITE(obj.nSessionID, obj.nState, obj.nStatusUpdate, obj.nMessageID);
122 0 : }
123 : };
124 :
125 : class CCoinJoinAccept
126 : {
127 : public:
128 7 : int nDenom{0};
129 : CMutableTransaction txCollateral;
130 :
131 21 : CCoinJoinAccept() = default;
132 :
133 0 : CCoinJoinAccept(int nDenom, CMutableTransaction txCollateral) :
134 0 : nDenom(nDenom),
135 0 : txCollateral(std::move(txCollateral)){};
136 :
137 0 : SERIALIZE_METHODS(CCoinJoinAccept, obj)
138 : {
139 0 : READWRITE(obj.nDenom, obj.txCollateral);
140 0 : }
141 :
142 4 : friend bool operator==(const CCoinJoinAccept& a, const CCoinJoinAccept& b)
143 : {
144 8 : return a.nDenom == b.nDenom && CTransaction(a.txCollateral) == CTransaction(b.txCollateral);
145 0 : }
146 : };
147 :
148 : // A client's transaction in the mixing pool
149 0 : class CCoinJoinEntry
150 : {
151 : public:
152 : std::vector<CTxDSIn> vecTxDSIn;
153 : std::vector<CTxOut> vecTxOut;
154 : CTransactionRef txCollateral;
155 : // memory only
156 : CService addr;
157 :
158 0 : CCoinJoinEntry() :
159 0 : txCollateral(MakeTransactionRef(CMutableTransaction{}))
160 0 : {
161 0 : }
162 :
163 0 : CCoinJoinEntry(std::vector<CTxDSIn> vecTxDSIn, std::vector<CTxOut> vecTxOut, const CTransaction& txCollateral) :
164 0 : vecTxDSIn(std::move(vecTxDSIn)),
165 0 : vecTxOut(std::move(vecTxOut)),
166 0 : txCollateral(MakeTransactionRef(txCollateral))
167 0 : {
168 0 : }
169 :
170 0 : SERIALIZE_METHODS(CCoinJoinEntry, obj)
171 : {
172 0 : READWRITE(obj.vecTxDSIn, obj.txCollateral, obj.vecTxOut);
173 0 : }
174 :
175 : bool AddScriptSig(const CTxIn& txin);
176 : };
177 :
178 :
179 : /**
180 : * A currently in progress mixing merge and denomination information
181 : */
182 : class CCoinJoinQueue
183 : {
184 : public:
185 11 : int nDenom{0};
186 : COutPoint masternodeOutpoint;
187 : uint256 m_protxHash;
188 11 : int64_t nTime{0};
189 11 : bool fReady{false}; //ready for submit
190 : std::vector<unsigned char> vchSig;
191 : // memory only
192 11 : bool fTried{false};
193 :
194 55 : CCoinJoinQueue() = default;
195 :
196 0 : CCoinJoinQueue(int nDenom, const COutPoint& outpoint, const uint256& proTxHash, int64_t nTime, bool fReady) :
197 0 : nDenom(nDenom),
198 0 : masternodeOutpoint(outpoint),
199 0 : m_protxHash(proTxHash),
200 0 : nTime(nTime),
201 0 : fReady(fReady)
202 0 : {
203 0 : }
204 :
205 18 : SERIALIZE_METHODS(CCoinJoinQueue, obj)
206 : {
207 6 : READWRITE(obj.nDenom, obj.m_protxHash, obj.nTime, obj.fReady);
208 6 : if (!(s.GetType() & SER_GETHASH)) {
209 2 : READWRITE(obj.vchSig);
210 2 : }
211 6 : }
212 :
213 : [[nodiscard]] uint256 GetHash() const;
214 : [[nodiscard]] uint256 GetSignatureHash() const;
215 :
216 : /// Check if we have a valid Masternode address
217 : [[nodiscard]] bool CheckSignature(const CBLSPublicKey& blsPubKey) const;
218 :
219 : /// Check if a queue is too old or too far into the future
220 : [[nodiscard]] bool IsTimeOutOfBounds(int64_t current_time = GetAdjustedTime()) const;
221 :
222 : [[nodiscard]] std::string ToString() const;
223 :
224 1 : friend bool operator==(const CCoinJoinQueue& a, const CCoinJoinQueue& b)
225 : {
226 1 : return a.nDenom == b.nDenom && a.masternodeOutpoint == b.masternodeOutpoint && a.nTime == b.nTime && a.fReady == b.fReady;
227 : }
228 : };
229 :
230 : /** Helper class to store mixing transaction (tx) information.
231 : */
232 12 : class CCoinJoinBroadcastTx
233 : {
234 : private:
235 : // memory only
236 : // when corresponding tx is 0-confirmed or conflicted, nConfirmedHeight is std::nullopt
237 198815 : std::optional<int> nConfirmedHeight{std::nullopt};
238 :
239 : public:
240 : CTransactionRef tx;
241 : COutPoint masternodeOutpoint;
242 : uint256 m_protxHash;
243 : std::vector<unsigned char> vchSig;
244 198795 : int64_t sigTime{0};
245 397633 : CCoinJoinBroadcastTx() :
246 198815 : tx(MakeTransactionRef(CMutableTransaction{}))
247 198818 : {
248 397633 : }
249 :
250 0 : CCoinJoinBroadcastTx(CTransactionRef _tx, const COutPoint& _outpoint, const uint256& proTxHash, int64_t _sigTime) :
251 0 : tx(std::move(_tx)),
252 0 : masternodeOutpoint(_outpoint),
253 0 : m_protxHash(proTxHash),
254 0 : sigTime(_sigTime)
255 0 : {
256 0 : }
257 :
258 612 : SERIALIZE_METHODS(CCoinJoinBroadcastTx, obj)
259 : {
260 204 : READWRITE(obj.tx, obj.m_protxHash);
261 :
262 204 : if (!(s.GetType() & SER_GETHASH)) {
263 204 : READWRITE(obj.vchSig);
264 204 : }
265 204 : READWRITE(obj.sigTime);
266 204 : }
267 :
268 88765 : friend bool operator==(const CCoinJoinBroadcastTx& a, const CCoinJoinBroadcastTx& b)
269 : {
270 88765 : return *a.tx == *b.tx;
271 : }
272 88767 : friend bool operator!=(const CCoinJoinBroadcastTx& a, const CCoinJoinBroadcastTx& b)
273 : {
274 88767 : return !(a == b);
275 : }
276 88767 : explicit operator bool() const
277 : {
278 88767 : return *this != CCoinJoinBroadcastTx();
279 0 : }
280 :
281 : [[nodiscard]] uint256 GetSignatureHash() const;
282 :
283 : [[nodiscard]] bool CheckSignature(const CBLSPublicKey& blsPubKey) const;
284 :
285 5 : [[nodiscard]] const std::optional<int>& GetConfirmedHeight() const { return nConfirmedHeight; }
286 3 : void SetConfirmedHeight(std::optional<int> nConfirmedHeightIn) { assert(nConfirmedHeightIn == std::nullopt || *nConfirmedHeightIn > 0); nConfirmedHeight = nConfirmedHeightIn; }
287 : [[nodiscard]] bool IsValidStructure() const;
288 : };
289 :
290 : // base class
291 : class CCoinJoinBaseSession
292 : {
293 : protected:
294 : mutable Mutex cs_coinjoin;
295 :
296 : std::vector<CCoinJoinEntry> vecEntries GUARDED_BY(cs_coinjoin); // Masternode/clients entries
297 :
298 660 : std::atomic<PoolState> nState{POOL_STATE_IDLE}; // should be one of the POOL_STATE_XXX values
299 660 : std::atomic<int64_t> nTimeLastSuccessfulStep{0}; // the time when last successful mixing step was performed
300 :
301 660 : std::atomic<int> nSessionID{0}; // 0 if no mixing session is active
302 :
303 : CMutableTransaction finalMutableTransaction GUARDED_BY(cs_coinjoin); // the finalized transaction ready for signing
304 :
305 : virtual void SetNull() EXCLUSIVE_LOCKS_REQUIRED(cs_coinjoin);
306 :
307 : bool IsValidInOuts(CChainState& active_chainstate, const llmq::CInstantSendManager& isman,
308 : const CTxMemPool& mempool, const std::vector<CTxIn>& vin, const std::vector<CTxOut>& vout,
309 : PoolMessage& nMessageIDRet, bool* fConsumeCollateralRet) const;
310 :
311 : public:
312 660 : int nSessionDenom{0}; // Users must submit a denom matching this
313 :
314 1320 : CCoinJoinBaseSession() = default;
315 660 : virtual ~CCoinJoinBaseSession() = default;
316 :
317 0 : int GetState() const { return nState; }
318 : std::string GetStateString() const;
319 :
320 55208 : int GetEntriesCount() const EXCLUSIVE_LOCKS_REQUIRED(!cs_coinjoin) { LOCK(cs_coinjoin); return vecEntries.size(); }
321 : int GetEntriesCountLocked() const EXCLUSIVE_LOCKS_REQUIRED(cs_coinjoin) { return vecEntries.size(); }
322 : };
323 :
324 : class CoinJoinQueueManager
325 : {
326 : private:
327 : mutable Mutex cs_vecqueue;
328 :
329 : // The current mixing sessions in progress on the network
330 : std::vector<CCoinJoinQueue> vecCoinJoinQueue GUARDED_BY(cs_vecqueue);
331 :
332 : public:
333 : void SetNull() EXCLUSIVE_LOCKS_REQUIRED(!cs_vecqueue);
334 :
335 : //! Remove timed-out queue entries. Call periodically (e.g. every second).
336 : void CheckQueue() EXCLUSIVE_LOCKS_REQUIRED(!cs_vecqueue);
337 :
338 24 : int GetQueueSize() const EXCLUSIVE_LOCKS_REQUIRED(!cs_vecqueue) { LOCK(cs_vecqueue); return vecCoinJoinQueue.size(); }
339 : bool GetQueueItemAndTry(CCoinJoinQueue& dsqRet) EXCLUSIVE_LOCKS_REQUIRED(!cs_vecqueue);
340 :
341 0 : bool HasQueue(const uint256& queueHash) EXCLUSIVE_LOCKS_REQUIRED(!cs_vecqueue)
342 : {
343 0 : LOCK(cs_vecqueue);
344 0 : return std::any_of(vecCoinJoinQueue.begin(), vecCoinJoinQueue.end(),
345 0 : [&queueHash](auto q) { return q.GetHash() == queueHash; });
346 0 : }
347 0 : std::optional<CCoinJoinQueue> GetQueueFromHash(const uint256& queueHash) EXCLUSIVE_LOCKS_REQUIRED(!cs_vecqueue)
348 : {
349 0 : LOCK(cs_vecqueue);
350 0 : return util::find_if_opt(vecCoinJoinQueue, [&queueHash](const auto& q) { return q.GetHash() == queueHash; });
351 0 : }
352 :
353 : //! True if any queue entry matches the given masternode outpoint and readiness state.
354 : //! Used to detect when a masternode is broadcasting queues too quickly.
355 0 : bool HasQueueFromMasternode(const COutPoint& outpoint, bool fReady) const EXCLUSIVE_LOCKS_REQUIRED(!cs_vecqueue)
356 : {
357 0 : LOCK(cs_vecqueue);
358 0 : return std::any_of(vecCoinJoinQueue.begin(), vecCoinJoinQueue.end(),
359 0 : [&](const auto& q) { return q.masternodeOutpoint == outpoint && q.fReady == fReady; });
360 0 : }
361 : //! TRY_LOCK variant: returns nullopt if lock can't be acquired; true if any queue entry has this
362 : //! outpoint (any readiness).
363 : [[nodiscard]] std::optional<bool> TryHasQueueFromMasternode(const COutPoint& outpoint) const EXCLUSIVE_LOCKS_REQUIRED(!cs_vecqueue);
364 : //! TRY_LOCK combined duplicate check: returns nullopt if lock can't be acquired; true if dsq is
365 : //! an exact duplicate or the masternode is sending too many dsqs with the same readiness.
366 : [[nodiscard]] std::optional<bool> TryCheckDuplicate(const CCoinJoinQueue& dsq) const EXCLUSIVE_LOCKS_REQUIRED(!cs_vecqueue);
367 :
368 : //! Append a queue entry (caller must have already checked for duplicates).
369 3 : void AddQueue(CCoinJoinQueue dsq) EXCLUSIVE_LOCKS_REQUIRED(!cs_vecqueue)
370 : {
371 3 : LOCK(cs_vecqueue);
372 3 : vecCoinJoinQueue.push_back(std::move(dsq));
373 3 : }
374 : //! TRY_LOCK variant of AddQueue: returns false if the lock cannot be acquired.
375 : bool TryAddQueue(CCoinJoinQueue dsq) EXCLUSIVE_LOCKS_REQUIRED(!cs_vecqueue);
376 : };
377 :
378 : // Various helpers and dstx manager implementation
379 : namespace CoinJoin
380 : {
381 : bilingual_str GetMessageByID(PoolMessage nMessageID);
382 :
383 : /// Get the minimum/maximum number of participants for the pool
384 : int GetMinPoolParticipants();
385 : int GetMaxPoolParticipants();
386 :
387 0 : constexpr CAmount GetMaxPoolAmount() { return COINJOIN_ENTRY_MAX_SIZE * vecStandardDenominations.front(); }
388 :
389 : /// If the collateral is valid given by a client
390 : bool IsCollateralValid(ChainstateManager& chainman, const llmq::CInstantSendManager& isman,
391 : const CTxMemPool& mempool, const CTransaction& txCollateral);
392 : }
393 :
394 : class CDSTXManager
395 : {
396 : const chainlock::Chainlocks& m_chainlocks;
397 : Mutex cs_mapdstx;
398 : std::map<uint256, CCoinJoinBroadcastTx> mapDSTX GUARDED_BY(cs_mapdstx);
399 :
400 : public:
401 : CDSTXManager(const CDSTXManager&) = delete;
402 : CDSTXManager& operator=(const CDSTXManager&) = delete;
403 : CDSTXManager(const chainlock::Chainlocks& chainlocks);
404 : ~CDSTXManager();
405 :
406 : void AddDSTX(const CCoinJoinBroadcastTx& dstx) EXCLUSIVE_LOCKS_REQUIRED(!cs_mapdstx);
407 : CCoinJoinBroadcastTx GetDSTX(const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(!cs_mapdstx);
408 :
409 : // CDSNotificationInterface
410 : void UpdatedBlockTip(const CBlockIndex* pindex) EXCLUSIVE_LOCKS_REQUIRED(!cs_mapdstx);
411 : void NotifyChainLock(const CBlockIndex* pindex) EXCLUSIVE_LOCKS_REQUIRED(!cs_mapdstx);
412 : void TransactionAddedToMempool(const CTransactionRef& tx) EXCLUSIVE_LOCKS_REQUIRED(!cs_mapdstx);
413 : void BlockConnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex* pindex)
414 : EXCLUSIVE_LOCKS_REQUIRED(!cs_mapdstx);
415 : void BlockDisconnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex*)
416 : EXCLUSIVE_LOCKS_REQUIRED(!cs_mapdstx);
417 :
418 : private:
419 : bool IsTxExpired(const CCoinJoinBroadcastTx& tx, const CBlockIndex* pindex) const EXCLUSIVE_LOCKS_REQUIRED(cs_mapdstx);
420 : void CheckDSTXes(const CBlockIndex* pindex) EXCLUSIVE_LOCKS_REQUIRED(!cs_mapdstx);
421 : void UpdateDSTXConfirmedHeight(const CTransactionRef& tx, std::optional<int> nHeight)
422 : EXCLUSIVE_LOCKS_REQUIRED(cs_mapdstx);
423 : };
424 :
425 : bool ATMPIfSaneFee(ChainstateManager& chainman, const CTransactionRef& tx, bool test_accept = false)
426 : EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
427 :
428 : #endif // BITCOIN_COINJOIN_COINJOIN_H
|