// Copyright (c) 2014-2015 The Dash developers // Copyright (c) 2015-2019 The PIVX developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #ifndef OBFUSCATION_H #define OBFUSCATION_H #include "main.h" #include "masternode-payments.h" #include "masternode-sync.h" #include "masternodeman.h" #include "obfuscation-relay.h" #include "sync.h" class CTxIn; class CObfuscationPool; class CObfuScationSigner; class CMasterNodeVote; class CBitcoinAddress; class CObfuscationQueue; class CObfuscationBroadcastTx; class CActiveMasternode; // pool states for mixing #define POOL_STATUS_UNKNOWN 0 // waiting for update #define POOL_STATUS_IDLE 1 // waiting for update #define POOL_STATUS_QUEUE 2 // waiting in a queue #define POOL_STATUS_ACCEPTING_ENTRIES 3 // accepting entries #define POOL_STATUS_FINALIZE_TRANSACTION 4 // master node will broadcast what it accepted #define POOL_STATUS_SIGNING 5 // check inputs/outputs, sign final tx #define POOL_STATUS_TRANSMISSION 6 // transmit transaction #define POOL_STATUS_ERROR 7 // error #define POOL_STATUS_SUCCESS 8 // success // status update message constants #define MASTERNODE_ACCEPTED 1 #define MASTERNODE_REJECTED 0 #define MASTERNODE_RESET -1 #define OBFUSCATION_QUEUE_TIMEOUT 30 #define OBFUSCATION_SIGNING_TIMEOUT 15 // used for anonymous relaying of inputs/outputs/sigs #define OBFUSCATION_RELAY_IN 1 #define OBFUSCATION_RELAY_OUT 2 #define OBFUSCATION_RELAY_SIG 3 static const CAmount OBFUSCATION_COLLATERAL = (10 * COIN); static const CAmount OBFUSCATION_POOL_MAX = (99999.99 * COIN); extern CObfuscationPool obfuScationPool; extern CObfuScationSigner obfuScationSigner; extern std::vector vecObfuscationQueue; extern std::string strMasterNodePrivKey; extern map mapObfuscationBroadcastTxes; extern CActiveMasternode activeMasternode; /** Holds an Obfuscation input */ class CTxDSIn : public CTxIn { public: bool fHasSig; // flag to indicate if signed CTxDSIn(const CTxIn& in) { prevout = in.prevout; scriptSig = in.scriptSig; prevPubKey = in.prevPubKey; nSequence = in.nSequence; fHasSig = false; } }; /** Holds an Obfuscation output */ class CTxDSOut : public CTxOut { public: int nSentTimes; //times we've sent this anonymously CTxDSOut(const CTxOut& out) { nValue = out.nValue; nRounds = out.nRounds; scriptPubKey = out.scriptPubKey; nSentTimes = 0; } }; // A clients transaction in the obfuscation pool class CObfuScationEntry { public: bool isSet; std::vector sev; std::vector vout; CAmount amount; CTransaction collateral; int64_t addedTime; // time in UTC milliseconds CObfuScationEntry() { isSet = false; collateral = CTransaction(); amount = 0; } /// Add entries to use for Obfuscation bool Add(const std::vector vinIn, int64_t amountIn, const CTransaction collateralIn, const std::vector voutIn) { if (isSet) { return false; } for (const CTxIn& in : vinIn) sev.push_back(in); for (const CTxOut& out : voutIn) vout.push_back(out); amount = amountIn; collateral = collateralIn; isSet = true; addedTime = GetTime(); return true; } bool IsExpired() { return (GetTime() - addedTime) > OBFUSCATION_QUEUE_TIMEOUT; // 120 seconds } }; /** * A currently inprogress Obfuscation merge and denomination information */ class CObfuscationQueue { public: CTxIn vin; int64_t time; int nDenom; bool ready; //ready for submit std::vector vchSig; CObfuscationQueue() { nDenom = 0; vin = CTxIn(); time = 0; vchSig.clear(); ready = false; } ADD_SERIALIZE_METHODS; template inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { READWRITE(nDenom); READWRITE(vin); READWRITE(time); READWRITE(ready); READWRITE(vchSig); } /** Sign this Obfuscation 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(); bool Relay(); /// Is this Obfuscation expired? bool IsExpired() { return (GetTime() - time) > OBFUSCATION_QUEUE_TIMEOUT; // 120 seconds } }; /** Helper class to store Obfuscation transaction (tx) information. */ class CObfuscationBroadcastTx { public: CTransaction tx; CTxIn vin; vector vchSig; int64_t sigTime; }; /** Helper object for signing and checking signatures */ class CObfuScationSigner { public: /// Is the inputs associated with this public key? (and there is 10000 AGR - checking if valid masternode) bool IsVinAssociatedWithPubkey(CTxIn& vin, CPubKey& pubkey); /// Set the private/public key values, returns true if successful bool GetKeysFromSecret(std::string strSecret, CKey& keyRet, CPubKey& pubkeyRet); /// Set the private/public key values, returns true if successful bool SetKey(std::string strSecret, std::string& errorMessage, CKey& key, CPubKey& pubkey); /// Sign the message, returns true if successful bool SignMessage(std::string strMessage, std::string& errorMessage, std::vector& vchSig, CKey key); /// Verify the message, returns true if succcessful bool VerifyMessage(CPubKey pubkey, std::vector& vchSig, std::string strMessage, std::string& errorMessage); }; /** Used to keep track of current status of Obfuscation pool */ class CObfuscationPool { private: std::vector entries; // Masternode/clients entries CMutableTransaction finalTransaction; // the finalized transaction ready for signing int64_t lastTimeChanged; // last time the 'state' changed, in UTC milliseconds unsigned int state; // should be one of the POOL_STATUS_XXX values unsigned int entriesCount; unsigned int lastEntryAccepted; unsigned int countEntriesAccepted; std::vector lockedCoins; std::string lastMessage; int sessionID; int sessionUsers; //N Users have said they'll join std::vector vecSessionCollateral; int cachedLastSuccess; int minBlockSpacing; //required blocks between mixes int64_t lastNewBlock; //debugging data std::string strAutoDenomResult; public: enum messages { 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, ERR_QUEUE_FULL, ERR_RECENT, ERR_SESSION, ERR_MISSING_TX, ERR_VERSION, MSG_NOERR, MSG_SUCCESS, MSG_ENTRIES_ADDED }; // where collateral should be made out to CScript collateralPubKey; CMasternode* pSubmittedToMasternode; int sessionDenom; //Users must submit an denom matching this CObfuscationPool() { /* Obfuscation uses collateral addresses to trust parties entering the pool to behave themselves. If they don't it takes their money. */ cachedLastSuccess = 0; minBlockSpacing = 0; lastNewBlock = 0; SetNull(); } void InitCollateralAddress() { SetCollateralAddress(Params().ObfuscationPoolDummyAddress()); } bool SetCollateralAddress(std::string strAddress); void Reset(); void SetNull(); void UnlockCoins(); bool IsNull() const { return state == POOL_STATUS_ACCEPTING_ENTRIES && entries.empty(); } int GetState() const { return state; } int GetEntriesCount() const { return entries.size(); } /// Get the count of the accepted entries int GetCountEntriesAccepted() const { return countEntriesAccepted; } // Set the 'state' value, with some logging and capturing when the state changed void UpdateState(unsigned int newState) { if (fMasterNode && (newState == POOL_STATUS_ERROR || newState == POOL_STATUS_SUCCESS)) { // LogPrint("obfuscation", "CObfuscationPool::UpdateState() - Can't set state to ERROR or SUCCESS as a Masternode. \n"); return; } // LogPrintf("CObfuscationPool::UpdateState() == %d | %d \n", state, newState); if (state != newState) { lastTimeChanged = GetTimeMillis(); if (fMasterNode) { RelayStatus(obfuScationPool.sessionID, obfuScationPool.GetState(), obfuScationPool.GetEntriesCount(), MASTERNODE_RESET); } } state = newState; } /// Get the maximum number of transactions for the pool int GetMaxPoolTransactions() { return Params().PoolMaxTransactions(); } /// Check for process in Obfuscation void Check(); void CheckFinalTransaction(); /// Charge fees to bad actors (Charge clients a fee if they're abusive) void ChargeFees(); /// Rarely charge fees to pay miners void ChargeRandomFees(); void CheckTimeout(); void CheckForCompleteQueue(); /// Check that all inputs are signed. (Are all inputs signed?) bool SignaturesComplete(); /// Process a new block void NewBlock(); // // Relay Obfuscation Messages // void RelayFinalTransaction(const int sessionID, const CTransaction& txNew); void RelayStatus(const int sessionID, const int newState, const int newEntriesCount, const int newAccepted, const int errorID = MSG_NOERR); void RelayCompletedTransaction(const int sessionID, const bool error, const int errorID); }; void ThreadCheckObfuScationPool(); #endif