// Copyright (c) 2014-2015 The Darkcoin developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #ifndef DARKSEND_H #define DARKSEND_H #include "core.h" #include "main.h" #include "activemasternode.h" #include "masternodeman.h" #include "darksend-relay.h" class CTxIn; class CDarksendPool; class CDarkSendSigner; class CMasterNodeVote; class CBitcoinAddress; class CDarksendQueue; class CDarksendBroadcastTx; class CActiveMasternode; // pool states for mixing #define POOL_MAX_TRANSACTIONS 3 // wait for X transactions to merge and publish #define POOL_MAX_TRANSACTIONS_TESTNET 2 // wait for X transactions to merge and publish #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 DARKSEND_QUEUE_TIMEOUT 120 #define DARKSEND_SIGNING_TIMEOUT 30 #define DARKSEND_DOWNGRADE_TIMEOUT 30 // used for anonymous relaying of inputs/outputs/sigs #define DARKSEND_RELAY_IN 1 #define DARKSEND_RELAY_OUT 2 #define DARKSEND_RELAY_SIG 3 extern CDarksendPool darkSendPool; extern CDarkSendSigner darkSendSigner; extern std::vector vecDarksendQueue; extern std::string strMasterNodePrivKey; extern map mapDarksendBroadcastTxes; extern CActiveMasternode activeMasternode; // get the darksend chain depth for a given input int GetInputDarksendRounds(CTxIn in, int rounds=0); // // Holds an Darksend input // class CTxDSIn : public CTxIn { public: bool fHasSig; CTxDSIn(const CTxIn& in) { prevout = in.prevout; scriptSig = in.scriptSig; prevPubKey = in.prevPubKey; nSequence = in.nSequence; } }; // A clients transaction in the darksend pool // -- holds the input/output mapping for each user in the pool class CDarkSendEntry { public: bool isSet; std::vector sev; int64_t amount; CTransaction collateral; std::vector vout; CTransaction txSupporting; int64_t addedTime; CDarkSendEntry() { isSet = false; collateral = CTransaction(); amount = 0; } bool Add(const std::vector vinIn, int64_t amountIn, const CTransaction collateralIn, const std::vector voutIn) { if(isSet){return false;} sev = vinIn; vout = voutIn; amount = amountIn; collateral = collateralIn; isSet = true; addedTime = GetTime(); return true; } bool IsExpired() { return (GetTime() - addedTime) > DARKSEND_QUEUE_TIMEOUT;// 120 seconds } }; // // A currently inprogress darksend merge and denomination information // class CDarksendQueue { public: CTxIn vin; int64_t time; int nDenom; bool ready; //ready for submit std::vector vchSig; //information used for the anonymous relay system int nBlockHeight; std::vector vchRelaySig; std::string strSharedKey; CDarksendQueue() { nDenom = 0; vin = CTxIn(); time = 0; vchSig.clear(); vchRelaySig.clear(); nBlockHeight = 0; strSharedKey = ""; ready = false; } IMPLEMENT_SERIALIZE ( READWRITE(nDenom); READWRITE(vin); READWRITE(time); READWRITE(ready); READWRITE(vchSig); if(ready){ READWRITE(vchRelaySig); READWRITE(nBlockHeight); READWRITE(strSharedKey); } ) bool GetAddress(CService &addr) { CMasternode* pmn = mnodeman.Find(vin); if(pmn != NULL) { addr = pmn->addr; return true; } return false; } bool GetProtocolVersion(int &protocolVersion) { CMasternode* pmn = mnodeman.Find(vin); if(pmn != NULL) { protocolVersion = pmn->protocolVersion; return true; } return false; } void SetSharedKey(std::string strSharedKey); bool Sign(); bool Relay(); bool IsExpired() { return (GetTime() - time) > DARKSEND_QUEUE_TIMEOUT;// 120 seconds } bool CheckSignature(); }; // store darksend tx signature information class CDarksendBroadcastTx { public: CTransaction tx; CTxIn vin; vector vchSig; int64_t sigTime; }; // // Helper object for signing and checking signatures // class CDarkSendSigner { public: bool IsVinAssociatedWithPubkey(CTxIn& vin, CPubKey& pubkey); bool SetKey(std::string strSecret, std::string& errorMessage, CKey& key, CPubKey& pubkey); bool SignMessage(std::string strMessage, std::string& errorMessage, std::vector& vchSig, CKey key); bool VerifyMessage(CPubKey pubkey, std::vector& vchSig, std::string strMessage, std::string& errorMessage); }; // // Build a transaction anonymously // class CDSAnonTx { public: std::vector vin; std::vector vout; bool IsTransactionValid(); bool AddOutput(const CTxOut out); bool AddInput(const CTxIn in); bool AddSig(const CTxIn in); int CountEntries() {return (int)vin.size() + (int)vout.size();} }; void ConnectToDarkSendMasterNodeWinner(); // // Used to keep track of current status of darksend pool // class CDarksendPool { public: // clients entries std::vector myEntries; // masternode entries std::vector entries; // the finalized transaction ready for signing CTransaction finalTransaction; // anonymous inputs/outputs CDSAnonTx anonTx; bool fSubmitAnonymousFailed; int nCountAttempts; int64_t lastTimeChanged; int64_t lastAutoDenomination; unsigned int state; unsigned int entriesCount; unsigned int lastEntryAccepted; unsigned int countEntriesAccepted; // where collateral should be made out to CScript collateralPubKey; std::vector lockedCoins; uint256 masterNodeBlockHash; std::string lastMessage; bool completedTransaction; bool unitTest; CMasternode* pSubmittedToMasternode; int sessionID; int sessionDenom; //Users must submit an denom matching this int sessionUsers; //N Users have said they'll join bool sessionFoundMasternode; //If we've found a compatible masternode int64_t sessionTotalValue; //used for autoDenom std::vector vecSessionCollateral; int cachedLastSuccess; int cachedNumBlocks; //used for the overview screen int minBlockSpacing; //required blocks between mixes CTransaction txCollateral; int64_t lastNewBlock; //debugging data std::string strAutoDenomResult; // used for securing the anonymous relay system vector vchMasternodeRelaySig; int nMasternodeBlockHeight; std::string strMasternodeSharedKey; bool fResentInputsOutputs; //incremented whenever a DSQ comes through int64_t nDsqCount; CDarksendPool() { /* DarkSend uses collateral addresses to trust parties entering the pool to behave themselves. If they don't it takes their money. */ cachedLastSuccess = 0; cachedNumBlocks = 0; unitTest = false; txCollateral = CTransaction(); minBlockSpacing = 1; nDsqCount = 0; lastNewBlock = 0; strMasternodeSharedKey = ""; fResentInputsOutputs = false; SetNull(); } //specific messages for the Darksend protocol void ProcessMessageDarksend(CNode* pfrom, std::string& strCommand, CDataStream& vRecv); void InitCollateralAddress(){ std::string strAddress = ""; if(Params().NetworkID() == CChainParams::MAIN) { strAddress = "Xq19GqFvajRrEdDHYRKGYjTsQfpV5jyipF"; } else { strAddress = "y1EZuxhhNMAUofTBEeLqGE1bJrpC2TWRNp"; } SetCollateralAddress(strAddress); } void SetMinBlockSpacing(int minBlockSpacingIn){ minBlockSpacing = minBlockSpacingIn; } bool SetCollateralAddress(std::string strAddress); void Reset(); bool Downgrade(); bool ResendMissingInputsOutputs(); void SetNull(bool clearEverything=false); void UnlockCoins(); bool IsNull() const { return (state == POOL_STATUS_ACCEPTING_ENTRIES && entries.empty() && myEntries.empty()); } int GetState() const { return state; } int GetEntriesCount() const { if(fMasterNode){ return entries.size(); } else { return entriesCount; } } int GetLastEntryAccepted() const { return lastEntryAccepted; } int GetCountEntriesAccepted() const { return countEntriesAccepted; } int GetMyTransactionCount() const { return myEntries.size(); } void UpdateState(unsigned int newState) { if (fMasterNode && (newState == POOL_STATUS_ERROR || newState == POOL_STATUS_SUCCESS)){ LogPrintf("CDarksendPool::UpdateState() - Can't set state to ERROR or SUCCESS as a masternode. \n"); return; } LogPrintf("CDarksendPool::UpdateState() == %d | %d \n", state, newState); if(state != newState){ lastTimeChanged = GetTimeMillis(); if(fMasterNode) { RelayStatus(darkSendPool.sessionID, darkSendPool.GetState(), darkSendPool.GetEntriesCount(), MASTERNODE_RESET); } } state = newState; } int GetMaxPoolTransactions() { //if we're on testnet, just use two transactions per merge if(Params().NetworkID() == CChainParams::TESTNET || Params().NetworkID() == CChainParams::REGTEST) return POOL_MAX_TRANSACTIONS_TESTNET; //use the production amount return POOL_MAX_TRANSACTIONS; } //Do we have enough users to take entries? bool IsSessionReady(){ return sessionUsers >= GetMaxPoolTransactions(); } // Are these outputs compatible with other client in the pool? bool IsCompatibleWithEntries(std::vector& vout); // Is this amount compatible with other client in the pool? bool IsCompatibleWithSession(int64_t nAmount, CTransaction txCollateral, std::string& strReason); // Passively run Darksend in the background according to the configuration in settings (only for QT) bool DoAutomaticDenominating(bool fDryRun=false, bool ready=false); bool PrepareDarksendDenominate(); // check for process in Darksend void Check(); void CheckFinalTransaction(); // charge fees to bad actors void ChargeFees(); // rarely charge fees to pay miners void ChargeRandomFees(); void CheckTimeout(); void CheckForCompleteQueue(); // check to make sure a signature matches an input in the pool bool SignatureValid(const CScript& newSig, const CTxIn& newVin); // if the collateral is valid given by a client bool IsCollateralValid(const CTransaction& txCollateral); // add a clients entry to the pool bool AddEntry(const std::vector& newInput, const int64_t& nAmount, const CTransaction& txCollateral, const std::vector& newOutput, std::string& error); // add an anonymous output/inputs/sig bool AddAnonymousOutput(const CTxOut& out) {return anonTx.AddOutput(out);} bool AddAnonymousInput(const CTxIn& in) {return anonTx.AddInput(in);} bool AddAnonymousSig(const CTxIn& in) {return anonTx.AddSig(in);} bool AddRelaySignature(vector vchMasternodeRelaySigIn, int nMasternodeBlockHeightIn, std::string strSharedKey) { vchMasternodeRelaySig = vchMasternodeRelaySigIn; nMasternodeBlockHeight = nMasternodeBlockHeightIn; strMasternodeSharedKey = strSharedKey; return true; } // add signature to a vin bool AddScriptSig(const CTxIn& newVin); // are all inputs signed? bool SignaturesComplete(); // as a client, send a transaction to a masternode to start the denomination process void SendDarksendDenominate(std::vector& vin, std::vector& vout, int64_t amount); // get masternode updates about the progress of darksend bool StatusUpdate(int newState, int newEntriesCount, int newAccepted, std::string& error, int newSessionID=0); // as a client, check and sign the final transaction bool SignFinalTransaction(CTransaction& finalTransactionNew, CNode* node); // get the last valid block hash for a given modulus bool GetLastValidBlockHash(uint256& hash, int mod=1, int nBlockHeight=0); // process a new block void NewBlock(); void CompletedTransaction(bool error, std::string lastMessageNew); void ClearLastMessage(); // used for liquidity providers bool SendRandomPaymentToSelf(); // split up large inputs or make fee sized inputs bool MakeCollateralAmounts(); bool CreateDenominated(int64_t nTotalValue); // get the denominations for a list of outputs (returns a bitshifted integer) int GetDenominations(const std::vector& vout); void GetDenominationsToString(int nDenom, std::string& strDenom); // get the denominations for a specific amount of darkcoin. int GetDenominationsByAmount(int64_t nAmount, int nDenomTarget=0); int GetDenominationsByAmounts(std::vector& vecAmount); // // Relay Darksend Messages // void RelayFinalTransaction(const int sessionID, const CTransaction& txNew); void RelaySignaturesAnon(std::vector& vin); void RelayInAnon(std::vector& vin, std::vector& vout); void RelayIn(const std::vector& vin, const int64_t& nAmount, const CTransaction& txCollateral, const std::vector& vout); void RelayStatus(const int sessionID, const int newState, const int newEntriesCount, const int newAccepted, const std::string error=""); void RelayCompletedTransaction(const int sessionID, const bool error, const std::string errorMessage); }; void ThreadCheckDarkSendPool(); #endif