From c169e49fce95ff587ccdf221e536ccc9737dbd05 Mon Sep 17 00:00:00 2001 From: Evan Duffield Date: Fri, 6 Mar 2015 15:17:51 -0700 Subject: [PATCH 1/2] Added input/output/sig trickle - Send a few inputs/output each cycle - Mix other users inputs/outputs into relay each cycle - Send signatures as a tickle, also mixed with other users signatures - Fixed a bug with validating the final transaction --- src/darksend.cpp | 142 ++++++++++++++++++++++++++++++++++++------ src/darksend.h | 39 +++++++++--- src/masternode.cpp | 2 + src/masternodeman.cpp | 3 + 4 files changed, 158 insertions(+), 28 deletions(-) diff --git a/src/darksend.cpp b/src/darksend.cpp index d69143d63..0b04a06a5 100644 --- a/src/darksend.cpp +++ b/src/darksend.cpp @@ -173,6 +173,11 @@ void CDarksendPool::ProcessMessageDarksend(CNode* pfrom, std::string& strCommand return; } + if(state != POOL_STATUS_IDLE){ + LogPrintf("dsr -- wrong state to relay! \n"); + return; + } + CDarkSendRelay dsr; vRecv >> dsr; @@ -450,8 +455,6 @@ void CDarksendPool::ProcessMessageDarksend(CNode* pfrom, std::string& strCommand bool success = false; int count = 0; - LogPrintf(" -- sigs count %d %d\n", (int)sigs.size(), count); - BOOST_FOREACH(const CTxIn item, sigs) { if(AddScriptSig(item)) success = true; @@ -621,7 +624,7 @@ void CDarksendPool::SetNull(bool clearEverything){ entries.clear(); anonTx.vin.clear(); anonTx.vout.clear(); - fResentInputsOutputs = false; + nTrickleInputsOutputs = 0; state = POOL_STATUS_IDLE; @@ -719,6 +722,7 @@ void CDarksendPool::Check() } // shuffle the outputs for improved anonymity + std::random_shuffle ( txNew.vin.begin(), txNew.vin.end(), randomizeList); std::random_shuffle ( txNew.vout.begin(), txNew.vout.end(), randomizeList); } else { BOOST_FOREACH(CTxDSIn& v, anonTx.vin) @@ -729,7 +733,7 @@ void CDarksendPool::Check() } if(fDebug) LogPrintf("Transaction 1: %s\n", txNew.ToString().c_str()); - SignFinalTransaction(txNew, NULL); + finalTransaction = txNew; // request signatures from clients RelayFinalTransaction(sessionID, finalTransaction); @@ -765,7 +769,7 @@ void CDarksendPool::CheckFinalTransaction() if(fDebug) LogPrintf("Transaction 2: %s\n", txNew.ToString().c_str()); // See if the transaction is valid - if (!txNew.AcceptToMemoryPool(true)) + if (!txNew.AcceptToMemoryPool(false)) { if(nCountAttempts > 10) { LogPrintf("CDarksendPool::Check() - CommitTransaction : Error: Transaction not valid\n"); @@ -939,7 +943,7 @@ void CDarksendPool::ChargeFees(){ CWalletTx wtxCollateral = CWalletTx(pwalletMain, v.collateral); // Broadcast - if (!wtxCollateral.AcceptToMemoryPool(true)) + if (!wtxCollateral.AcceptToMemoryPool(false)) { // This must not fail. The transaction has already been signed and recorded. LogPrintf("CDarksendPool::ChargeFees() : Error: Transaction not valid"); @@ -1393,7 +1397,7 @@ void CDarksendPool::SendDarksendDenominate(std::vector& vin, std::vector< myEntries.push_back(e); // submit inputs/outputs through relays - RelayInAnon(vin, vout); + TrickleInputsOutputs(); Check(); } @@ -1433,7 +1437,6 @@ bool CDarksendPool::StatusUpdate(int newState, int newEntriesCount, int newAccep sessionFoundMasternode = true; //wait for other users. Masternode will report when ready UpdateState(POOL_STATUS_QUEUE); - LogPrintf("Updated 1\n"); } else if (newAccepted == 0 && sessionID == 0 && !sessionFoundMasternode) { LogPrintf("CDarksendPool::StatusUpdate - entry not accepted by Masternode \n"); UnlockCoins(); @@ -1452,6 +1455,7 @@ bool CDarksendPool::StatusUpdate(int newState, int newEntriesCount, int newAccep // If we refuse to sign, it's possible we'll be charged collateral // bool CDarksendPool::SignFinalTransaction(CTransaction& finalTransactionNew, CNode* node){ + if(fMasterNode) return false; if(fDebug) LogPrintf("CDarksendPool::SignFinalTransaction - Got Finalized Transaction - fSubmitAnonymousFailed %d\n", fSubmitAnonymousFailed); finalTransaction = finalTransactionNew; @@ -1475,7 +1479,12 @@ bool CDarksendPool::SignFinalTransaction(CTransaction& finalTransactionNew, CNod } } + if(mine >= 0){ //might have to do this one input at a time? + //already signed + if(finalTransaction.vin[mine].scriptSig != CScript()) continue; + if(sigs.size() > 7) break; //send 7 each signing + int foundOutputs = 0; int64_t nValue1 = 0; int64_t nValue2 = 0; @@ -1497,7 +1506,7 @@ bool CDarksendPool::SignFinalTransaction(CTransaction& finalTransactionNew, CNod // in this case, something went wrong and we'll refuse to sign. It's possible we'll be charged collateral. But that's // better then signing if the transaction doesn't look like what we wanted. LogPrintf("CDarksendPool::Sign - My entries are not correct! Refusing to sign. %d entries %d target. \n", foundOutputs, targetOuputs); - ResendMissingInputsOutputs(); + TrickleInputsOutputs(); return false; } @@ -1517,6 +1526,15 @@ bool CDarksendPool::SignFinalTransaction(CTransaction& finalTransactionNew, CNod } if(!fSubmitAnonymousFailed){ + //resubmit some other sigs from the transaction, so nodes can't tell who's inputs/outputs are whos + BOOST_FOREACH(CTxIn& in, finalTransaction.vin) + if((rand() % 100) > 75 && in.scriptSig != CScript()) + sigs.push_back(in); + + std::random_shuffle ( sigs.begin(), sigs.end(), randomizeList); + + printf("sigs count %d\n", (int)sigs.size()); + RelaySignaturesAnon(sigs); } else { // push all of our signatures to the Masternode @@ -1851,6 +1869,7 @@ bool CDarksendPool::PrepareDarksendDenominate() bool CDarksendPool::Downgrade() { + if(fSubmitAnonymousFailed) return true; if(myEntries.size() == 0) return false; @@ -1863,17 +1882,72 @@ bool CDarksendPool::Downgrade() return true; } -bool CDarksendPool::ResendMissingInputsOutputs() +struct SortByTimesSent { - if(fResentInputsOutputs) { + bool operator()(const CTxDSIn & t1, + const CTxDSIn & t2) const + { + return t1.nSentTimes > t2.nSentTimes; + } + + bool operator()(const CTxDSOut & t1, + const CTxDSOut & t2) const + { + return t1.nSentTimes > t2.nSentTimes; + } +}; + +bool CDarksendPool::TrickleInputsOutputs() +{ + if(nTrickleInputsOutputs >= 20) { Downgrade(); return true; } if(myEntries.size() == 0) return false; - LogPrintf("CDarksendPool::Downgrade() : Downgrading and submitting directly\n"); - RelayInAnon(myEntries[0].sev, myEntries[0].vout); - fResentInputsOutputs = true; + std::vector vin; + std::vector vout; + + sort(myEntries[0].sev.rbegin(), myEntries[0].sev.rend(), SortByTimesSent()); + sort(myEntries[0].vout.rbegin(), myEntries[0].vout.rend(), SortByTimesSent()); + + int nCount1 = 0; + int nCount2 = 0; + int nMax = max(nTrickleInputsOutputs*3, 15)+3; + + //trickle some of our inputs/outputs + BOOST_FOREACH(CTxDSIn& in, myEntries[0].sev) { + if(nCount1 < (rand() % nMax)+5){ + in.nSentTimes++; + vin.push_back((CTxIn)in); + nCount1++; + } else {break;} + } + + BOOST_FOREACH(CTxDSOut& out, myEntries[0].vout) { + if(nCount2 < (rand() % nMax)+5){ + out.nSentTimes++; + vout.push_back((CTxOut)out); + nCount2++; + } else {break;} + } + + //resubmit some other inputs/outputs from the transaction, so nodes can't tell who's inputs/outputs are whos + BOOST_FOREACH(CTxIn& in, finalTransaction.vin) + if((rand() % 100) > 75) + vin.push_back(in); + + BOOST_FOREACH(CTxOut& out, finalTransaction.vout) + if((rand() % 100) > 75) + vout.push_back(out); + + //shuffle everything around + std::random_shuffle ( vin.begin(), vin.end(), randomizeList); + std::random_shuffle ( vout.begin(), vout.end(), randomizeList); + + LogPrintf("CDarksendPool::TrickleInputsOutputs() : Sending %d inputs and %d outputs\n", (int)vin.size(), (int)vout.size()); + RelayInAnon(vin, vout); + nTrickleInputsOutputs++; return true; } @@ -2070,7 +2144,6 @@ bool CDarksendPool::IsCompatibleWithSession(int64_t nDenom, CTransaction txColla } UpdateState(POOL_STATUS_QUEUE); - LogPrintf("Updated 2\n"); vecSessionCollateral.push_back(txCollateral); return true; } @@ -2135,6 +2208,15 @@ void CDarksendPool::GetDenominationsToString(int nDenom, std::string& strDenom){ } } +int CDarksendPool::GetDenominations(const std::vector& vout){ + std::vector vout2; + + BOOST_FOREACH(CTxDSOut out, vout) + vout2.push_back(out); + + return GetDenominations(vout2); +} + // return a bitshifted integer representing the denominations in this list int CDarksendPool::GetDenominations(const std::vector& vout){ std::vector > denomUsed; @@ -2427,16 +2509,25 @@ void CDarksendPool::RelayInAnon(std::vector& vin, std::vector& vo } } -void CDarksendPool::RelayIn(const std::vector& vin, const int64_t& nAmount, const CTransaction& txCollateral, const std::vector& vout) +void CDarksendPool::RelayIn(const std::vector& vin, const int64_t& nAmount, const CTransaction& txCollateral, const std::vector& vout) { LOCK(cs_vNodes); + std::vector vin2; + std::vector vout2; + + BOOST_FOREACH(CTxDSIn in, vin) + vin2.push_back(in); + + BOOST_FOREACH(CTxDSOut out, vout) + vout2.push_back(out); + BOOST_FOREACH(CNode* pnode, vNodes) { if(!pSubmittedToMasternode) return; if((CNetAddr)darkSendPool.pSubmittedToMasternode->addr != (CNetAddr)pnode->addr) continue; LogPrintf("RelayIn - found master, relaying message - %s \n", pnode->addr.ToString().c_str()); - pnode->PushMessage("dsi", vin, nAmount, txCollateral, vout); + pnode->PushMessage("dsi", vin2, nAmount, txCollateral, vout2); } } @@ -2455,10 +2546,13 @@ void CDarksendPool::RelayCompletedTransaction(const int sessionID, const bool er } bool CDSAnonTx::AddOutput(const CTxOut out){ + LOCK(cs_darksend); + if(fDebug) LogPrintf("CDSAnonTx::AddOutput -- new %s\n", out.ToString().substr(0,24).c_str()); - //already have this output - if(std::find(vout.begin(), vout.end(), out) != vout.end()) return false; + BOOST_FOREACH(CTxOut& out2, vout) + if(out2.nValue == out.nValue && out.scriptPubKey == out2.scriptPubKey) + return false; vout.push_back(out); std::random_shuffle ( vout.begin(), vout.end(), randomizeList); @@ -2467,17 +2561,24 @@ bool CDSAnonTx::AddOutput(const CTxOut out){ } bool CDSAnonTx::AddInput(const CTxIn in){ + LOCK(cs_darksend); + if(fDebug) LogPrintf("CDSAnonTx::AddInput -- new %s\n", in.ToString().substr(0,24).c_str()); //already have this input - if(std::find(vin.begin(), vin.end(), in) != vin.end()) return false; + BOOST_FOREACH(CTxDSIn& in2, vin) + if(in2.prevout == in.prevout && in.nSequence == in2.nSequence) + return false; vin.push_back(in); + std::random_shuffle ( vin.begin(), vin.end(), randomizeList); return true; } bool CDSAnonTx::AddSig(const CTxIn newIn){ + LOCK(cs_darksend); + if(fDebug) LogPrintf("CDSAnonTx::AddSig -- new %s\n", newIn.ToString().substr(0,24).c_str()); BOOST_FOREACH(CTxDSIn& in, vin){ @@ -2511,6 +2612,7 @@ void ThreadCheckDarkSendPool() //LogPrintf("ThreadCheckDarkSendPool::check timeout\n"); if(c % 10 == 0) darkSendPool.Check(); + if(c % 3 == 0) darkSendPool.TrickleInputsOutputs(); darkSendPool.CheckTimeout(); darkSendPool.CheckForCompleteQueue(); diff --git a/src/darksend.h b/src/darksend.h index 7b997f06a..ecda13022 100644 --- a/src/darksend.h +++ b/src/darksend.h @@ -63,6 +63,7 @@ class CTxDSIn : public CTxIn { public: bool fHasSig; // flag to indicate if signed + int nSentTimes; //times we've sent this anonymously CTxDSIn(const CTxIn& in) { @@ -70,9 +71,25 @@ public: scriptSig = in.scriptSig; prevPubKey = in.prevPubKey; nSequence = in.nSequence; + nSentTimes = 0; } }; +/** Holds an Darksend 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 Darksend pool * -- holds the input/output mapping for each user in the pool @@ -81,10 +98,10 @@ class CDarkSendEntry { public: bool isSet; - std::vector sev; + std::vector sev; + std::vector vout; int64_t amount; CTransaction collateral; - std::vector vout; CTransaction txSupporting; int64_t addedTime; // time in UTC milliseconds @@ -100,8 +117,12 @@ public: { if(isSet){return false;} - sev = vinIn; - vout = voutIn; + BOOST_FOREACH(const CTxIn& in, vinIn) + sev.push_back(in); + + BOOST_FOREACH(const CTxOut& out, voutIn) + vout.push_back(out); + amount = amountIn; collateral = collateralIn; isSet = true; @@ -314,7 +335,7 @@ public: vector vchMasternodeRelaySig; int nMasternodeBlockHeight; std::string strMasternodeSharedKey; - bool fResentInputsOutputs; + int nTrickleInputsOutputs; CDarksendPool() { @@ -328,7 +349,7 @@ public: minBlockSpacing = 1; lastNewBlock = 0; strMasternodeSharedKey = ""; - fResentInputsOutputs = false; + nTrickleInputsOutputs = 0; SetNull(); } @@ -367,7 +388,7 @@ public: bool SetCollateralAddress(std::string strAddress); void Reset(); bool Downgrade(); - bool ResendMissingInputsOutputs(); + bool TrickleInputsOutputs(); void SetNull(bool clearEverything=false); @@ -507,6 +528,8 @@ public: /// Get the denominations for a list of outputs (returns a bitshifted integer) int GetDenominations(const std::vector& vout); + int GetDenominations(const std::vector& vout); + void GetDenominationsToString(int nDenom, std::string& strDenom); /// Get the denominations for a specific amount of darkcoin. @@ -521,7 +544,7 @@ public: 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 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); }; diff --git a/src/masternode.cpp b/src/masternode.cpp index cd1b52c40..2dccd7b53 100644 --- a/src/masternode.cpp +++ b/src/masternode.cpp @@ -208,6 +208,8 @@ uint256 CMasternode::CalculateScore(int mod, int64_t nBlockHeight) void CMasternode::Check() { + LOCK(cs_main); + //once spent, stop doing the checks if(activeState == MASTERNODE_VIN_SPENT) return; diff --git a/src/masternodeman.cpp b/src/masternodeman.cpp index b707482fc..8c3972cb3 100644 --- a/src/masternodeman.cpp +++ b/src/masternodeman.cpp @@ -436,6 +436,9 @@ CMasternode* CMasternodeMan::GetMasternodeByRank(int nRank, int64_t nBlockHeight void CMasternodeMan::ProcessMasternodeConnections() { + //we don't care about this for testing + if(TestNet() || RegTest()) return; + LOCK(cs_vNodes); if(!darkSendPool.pSubmittedToMasternode) return; From a8f57439993f347338f558737caa6ee461f6f524 Mon Sep 17 00:00:00 2001 From: Evan Duffield Date: Sat, 7 Mar 2015 07:51:23 -0700 Subject: [PATCH 2/2] version bump --- configure.ac | 2 +- src/clientversion.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index f82826d55..97bc2f87e 100644 --- a/configure.ac +++ b/configure.ac @@ -3,7 +3,7 @@ AC_PREREQ([2.60]) define(_CLIENT_VERSION_MAJOR, 0) define(_CLIENT_VERSION_MINOR, 11) define(_CLIENT_VERSION_REVISION, 2) -define(_CLIENT_VERSION_BUILD, 1) +define(_CLIENT_VERSION_BUILD, 2) define(_CLIENT_VERSION_IS_RELEASE, true) define(_COPYRIGHT_YEAR, 2015) AC_INIT([Darkcoin Core],[_CLIENT_VERSION_MAJOR._CLIENT_VERSION_MINOR._CLIENT_VERSION_REVISION],[info@darkcoin.io],[darkcoin]) diff --git a/src/clientversion.h b/src/clientversion.h index 94ee7361c..53f036bda 100644 --- a/src/clientversion.h +++ b/src/clientversion.h @@ -12,7 +12,7 @@ #define CLIENT_VERSION_MAJOR 0 #define CLIENT_VERSION_MINOR 11 #define CLIENT_VERSION_REVISION 2 -#define CLIENT_VERSION_BUILD 0 +#define CLIENT_VERSION_BUILD 2