// Copyright (c) 2014-2019 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 PRIVATESEND_H #define PRIVATESEND_H #include "bls/bls.h" #include "chain.h" #include "chainparams.h" #include "primitives/transaction.h" #include "pubkey.h" #include "sync.h" #include "timedata.h" #include "tinyformat.h" class CPrivateSend; class CConnman; // timeouts static const int PRIVATESEND_AUTO_TIMEOUT_MIN = 5; static const int PRIVATESEND_AUTO_TIMEOUT_MAX = 15; static const int PRIVATESEND_QUEUE_TIMEOUT = 30; static const int PRIVATESEND_SIGNING_TIMEOUT = 15; //! minimum peer version accepted by mixing pool static const int MIN_PRIVATESEND_PEER_PROTO_VERSION = 70213; static const size_t PRIVATESEND_ENTRY_MAX_SIZE = 9; // pool responses enum PoolMessage { ERR_ALREADY_HAVE, ERR_DENOM, ERR_ENTRIES_FULL, ERR_EXISTING_TX, ERR_FEES, ERR_INVALID_COLLATERAL, ERR_INVALID_INPUT, ERR_INVALID_SCRIPT, ERR_INVALID_TX, ERR_MAXIMUM, ERR_MN_LIST, ERR_MODE, ERR_NON_STANDARD_PUBKEY, ERR_NOT_A_MN, // not used ERR_QUEUE_FULL, ERR_RECENT, ERR_SESSION, ERR_MISSING_TX, ERR_VERSION, MSG_NOERR, MSG_SUCCESS, MSG_ENTRIES_ADDED, MSG_POOL_MIN = ERR_ALREADY_HAVE, MSG_POOL_MAX = MSG_ENTRIES_ADDED }; // pool states enum PoolState { POOL_STATE_IDLE, POOL_STATE_QUEUE, POOL_STATE_ACCEPTING_ENTRIES, POOL_STATE_SIGNING, POOL_STATE_ERROR, POOL_STATE_SUCCESS, POOL_STATE_MIN = POOL_STATE_IDLE, POOL_STATE_MAX = POOL_STATE_SUCCESS }; // status update message constants enum PoolStatusUpdate { STATUS_REJECTED, STATUS_ACCEPTED }; /** Holds an mixing input */ class CTxDSIn : public CTxIn { public: // memory only CScript prevPubKey; bool fHasSig; // flag to indicate if signed CTxDSIn(const CTxIn& txin, const CScript& script) : CTxIn(txin), prevPubKey(script), fHasSig(false) { } CTxDSIn() : CTxIn(), prevPubKey(), fHasSig(false) { } }; class CPrivateSendAccept { public: int nDenom; CMutableTransaction txCollateral; CPrivateSendAccept() : nDenom(0), txCollateral(CMutableTransaction()){}; CPrivateSendAccept(int nDenom, const CMutableTransaction& txCollateral) : nDenom(nDenom), txCollateral(txCollateral){}; ADD_SERIALIZE_METHODS; template inline void SerializationOp(Stream& s, Operation ser_action) { READWRITE(nDenom); READWRITE(txCollateral); } friend bool operator==(const CPrivateSendAccept& a, const CPrivateSendAccept& b) { return a.nDenom == b.nDenom && a.txCollateral == b.txCollateral; } }; // A clients transaction in the mixing pool class CPrivateSendEntry { public: std::vector vecTxDSIn; std::vector vecTxOut; CTransactionRef txCollateral; // memory only CService addr; CPrivateSendEntry() : vecTxDSIn(std::vector()), vecTxOut(std::vector()), txCollateral(MakeTransactionRef()), addr(CService()) { } CPrivateSendEntry(const std::vector& vecTxDSIn, const std::vector& vecTxOut, const CTransaction& txCollateral) : vecTxDSIn(vecTxDSIn), vecTxOut(vecTxOut), txCollateral(MakeTransactionRef(txCollateral)), addr(CService()) { } ADD_SERIALIZE_METHODS; template inline void SerializationOp(Stream& s, Operation ser_action) { READWRITE(vecTxDSIn); READWRITE(txCollateral); READWRITE(vecTxOut); } bool AddScriptSig(const CTxIn& txin); }; /** * A currently in progress mixing merge and denomination information */ class CPrivateSendQueue { public: int nDenom; COutPoint masternodeOutpoint; int64_t nTime; bool fReady; //ready for submit std::vector vchSig; // memory only bool fTried; CPrivateSendQueue() : nDenom(0), masternodeOutpoint(COutPoint()), nTime(0), fReady(false), vchSig(std::vector()), fTried(false) { } CPrivateSendQueue(int nDenom, COutPoint outpoint, int64_t nTime, bool fReady) : nDenom(nDenom), masternodeOutpoint(outpoint), nTime(nTime), fReady(fReady), vchSig(std::vector()), fTried(false) { } ADD_SERIALIZE_METHODS; template inline void SerializationOp(Stream& s, Operation ser_action) { READWRITE(nDenom); READWRITE(masternodeOutpoint); READWRITE(nTime); READWRITE(fReady); if (!(s.GetType() & SER_GETHASH)) { READWRITE(vchSig); } } uint256 GetSignatureHash() const; /** Sign this mixing transaction * \return true if all conditions are met: * 1) we have an active Masternode, * 2) we have a valid Masternode private key, * 3) we signed the message successfully, and * 4) we verified the message successfully */ bool Sign(); /// Check if we have a valid Masternode address bool CheckSignature(const CBLSPublicKey& blsPubKey) const; bool Relay(CConnman& connman); /// Is this queue expired? bool IsExpired() { return GetAdjustedTime() - nTime > PRIVATESEND_QUEUE_TIMEOUT; } std::string ToString() const { return strprintf("nDenom=%d, nTime=%lld, fReady=%s, fTried=%s, masternode=%s", nDenom, nTime, fReady ? "true" : "false", fTried ? "true" : "false", masternodeOutpoint.ToStringShort()); } friend bool operator==(const CPrivateSendQueue& a, const CPrivateSendQueue& b) { return a.nDenom == b.nDenom && a.masternodeOutpoint == b.masternodeOutpoint && a.nTime == b.nTime && a.fReady == b.fReady; } }; /** Helper class to store mixing transaction (tx) information. */ class CPrivateSendBroadcastTx { private: // memory only // when corresponding tx is 0-confirmed or conflicted, nConfirmedHeight is -1 int nConfirmedHeight; public: CTransactionRef tx; COutPoint masternodeOutpoint; std::vector vchSig; int64_t sigTime; CPrivateSendBroadcastTx() : nConfirmedHeight(-1), tx(MakeTransactionRef()), masternodeOutpoint(), vchSig(), sigTime(0) { } CPrivateSendBroadcastTx(const CTransactionRef& _tx, COutPoint _outpoint, int64_t _sigTime) : nConfirmedHeight(-1), tx(_tx), masternodeOutpoint(_outpoint), vchSig(), sigTime(_sigTime) { } ADD_SERIALIZE_METHODS; template inline void SerializationOp(Stream& s, Operation ser_action) { READWRITE(tx); READWRITE(masternodeOutpoint); if (!(s.GetType() & SER_GETHASH)) { READWRITE(vchSig); } READWRITE(sigTime); } friend bool operator==(const CPrivateSendBroadcastTx& a, const CPrivateSendBroadcastTx& b) { return *a.tx == *b.tx; } friend bool operator!=(const CPrivateSendBroadcastTx& a, const CPrivateSendBroadcastTx& b) { return !(a == b); } explicit operator bool() const { return *this != CPrivateSendBroadcastTx(); } uint256 GetSignatureHash() const; bool Sign(); bool CheckSignature(const CBLSPublicKey& blsPubKey) const; void SetConfirmedHeight(int nConfirmedHeightIn) { nConfirmedHeight = nConfirmedHeightIn; } bool IsExpired(int nHeight); }; // base class class CPrivateSendBaseSession { protected: mutable CCriticalSection cs_privatesend; std::vector vecEntries; // Masternode/clients entries PoolState nState; // should be one of the POOL_STATE_XXX values int64_t nTimeLastSuccessfulStep; // the time when last successful mixing step was performed int nSessionID; // 0 if no mixing session is active CMutableTransaction finalMutableTransaction; // the finalized transaction ready for signing void SetNull(); public: int nSessionDenom; //Users must submit a denom matching this CPrivateSendBaseSession() : vecEntries(), nState(POOL_STATE_IDLE), nTimeLastSuccessfulStep(0), nSessionID(0), finalMutableTransaction(), nSessionDenom(0) { } int GetState() const { return nState; } std::string GetStateString() const; int GetEntriesCount() const { return vecEntries.size(); } }; // base class class CPrivateSendBaseManager { protected: mutable CCriticalSection cs_vecqueue; // The current mixing sessions in progress on the network std::vector vecPrivateSendQueue; void SetNull(); void CheckQueue(); public: CPrivateSendBaseManager() : vecPrivateSendQueue() {} int GetQueueSize() const { return vecPrivateSendQueue.size(); } bool GetQueueItemAndTry(CPrivateSendQueue& dsqRet); }; // helper class class CPrivateSend { private: // make constructor, destructor and copying not available CPrivateSend() {} ~CPrivateSend() {} CPrivateSend(CPrivateSend const&) = delete; CPrivateSend& operator=(CPrivateSend const&) = delete; // static members static std::vector vecStandardDenominations; static std::map mapDSTX; static CCriticalSection cs_mapdstx; static void CheckDSTXes(int nHeight); public: static void InitStandardDenominations(); static std::vector GetStandardDenominations() { return vecStandardDenominations; } static CAmount GetSmallestDenomination() { return vecStandardDenominations.back(); } /// Get the denominations for a specific amount of dash. static int GetDenominationsByAmounts(const std::vector& vecAmount); static bool IsDenominatedAmount(CAmount nInputAmount); /// Get the denominations for a list of outputs (returns a bitshifted integer) static int GetDenominations(const std::vector& vecTxOut, bool fSingleRandomDenom = false); static std::string GetDenominationsToString(int nDenom); static bool GetDenominationsBits(int nDenom, std::vector& vecBitsRet); static std::string GetMessageByID(PoolMessage nMessageID); /// Get the minimum/maximum number of participants for the pool static int GetMinPoolParticipants() { return Params().PoolMinParticipants(); } static int GetMaxPoolParticipants() { return Params().PoolMaxParticipants(); } static CAmount GetMaxPoolAmount() { return vecStandardDenominations.empty() ? 0 : PRIVATESEND_ENTRY_MAX_SIZE * vecStandardDenominations.front(); } /// If the collateral is valid given by a client static bool IsCollateralValid(const CTransaction& txCollateral); static CAmount GetCollateralAmount() { return GetSmallestDenomination() / 10; } static CAmount GetMaxCollateralAmount() { return GetCollateralAmount() * 4; } static bool IsCollateralAmount(CAmount nInputAmount); static void AddDSTX(const CPrivateSendBroadcastTx& dstx); static CPrivateSendBroadcastTx GetDSTX(const uint256& hash); static void UpdatedBlockTip(const CBlockIndex* pindex); static void SyncTransaction(const CTransaction& tx, const CBlockIndex* pindex, int posInBlock); }; #endif