diff --git a/src/init.cpp b/src/init.cpp index 7d500c897..57a3ed6b6 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -1521,7 +1521,6 @@ bool AppInit2(Config& config, boost::thread_group& threadGroup, CScheduler& sche if (GetBoolArg("-listenonion", DEFAULT_LISTEN_ONION)) StartTorControl(threadGroup, scheduler); - InitRespendFilter(); StartNode(threadGroup, scheduler); #ifdef ENABLE_WALLET diff --git a/src/main.cpp b/src/main.cpp index 757f212ce..a19719620 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -634,14 +634,6 @@ void FindNextBlocksToDownload(NodeId nodeid, unsigned int count, std::vector= nLimit*10*1000) - return true; - dCount += nSize; - return false; -} - -static bool RespendRelayExceeded(const CTransaction& doubleSpend) -{ - // Apply an independent rate limit to double-spend relays - static double dRespendCount; - static int64_t nLastRespendTime; - static int64_t nRespendLimit = GetArg("-limitrespendrelay", 100); - unsigned int nSize = ::GetSerializeSize(doubleSpend, SER_NETWORK, PROTOCOL_VERSION); - - if (RateLimitExceeded(dRespendCount, nLastRespendTime, nRespendLimit, nSize)) - { - LogPrint("mempool", "Double-spend relay rejected by rate limiter\n"); - return true; - } - - LogPrint("mempool", "Rate limit dRespendCount: %g => %g\n", dRespendCount, dRespendCount+nSize); - - return false; -} - bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const CTransaction& tx, bool fLimitFree, bool* pfMissingInputs, bool fOverrideMempoolLimit, const CAmount& nAbsurdFee, std::vector& vHashTxnToUncache) @@ -1202,65 +1159,18 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C return state.Invalid(false, REJECT_ALREADY_KNOWN, "txn-already-in-mempool"); // Check for conflicts with in-memory transactions - bool fRespend = false; - COutPoint relayForOutpoint; - set setConflicts; { LOCK(pool.cs); // protect pool.mapNextTx BOOST_FOREACH(const CTxIn &txin, tx.vin) { - COutPoint outpoint = txin.prevout; - // A respend is a tx that conflicts with a member of the pool - auto itConflicting = pool.mapNextTx.find(outpoint); + auto itConflicting = pool.mapNextTx.find(txin.prevout); if (itConflicting != pool.mapNextTx.end()) { - fRespend = true; - // Relay only one tx per respent outpoint, but not if tx is equivalent to pool member - if (!doubleSpendFilter.contains(outpoint) && !tx.IsEquivalentTo(*itConflicting->second)) - { - relayForOutpoint = outpoint; - break; - } - /* - const CTransaction *ptxConflicting = itConflicting->second; - if (!setConflicts.count(ptxConflicting->GetHash())) - { - // Allow opt-out of transaction replacement by setting - // nSequence >= maxint-1 on all inputs. - // - // maxint-1 is picked to still allow use of nLockTime by - // non-replaceable transactions. All inputs rather than just one - // is for the sake of multi-party protocols, where we don't - // want a single party to be able to disable replacement. - // - // The opt-out ignores descendants as anyone relying on - // first-seen mempool behavior should be checking all - // unconfirmed ancestors anyway; doing otherwise is hopelessly - // insecure. - bool fReplacementOptOut = true; - if (0) - { - BOOST_FOREACH(const CTxIn &txin, ptxConflicting->vin) - { - if (txin.nSequence < std::numeric_limits::max()-1) - { - fReplacementOptOut = false; - break; - } - } - } - if (fReplacementOptOut) - return state.Invalid(false, REJECT_CONFLICT, "txn-mempool-conflict"); - - setConflicts.insert(ptxConflicting->GetHash()); - } - */ - + // Disable replacement feature for good + return state.Invalid(false, REJECT_CONFLICT, "txn-mempool-conflict"); } } - if (fRespend && (relayForOutpoint.IsNull() || RespendRelayExceeded(tx))) - return false; } { @@ -1369,15 +1279,22 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C // be annoying or make others' transactions take longer to confirm. if (fLimitFree && nModifiedFees < ::minRelayTxFee.GetFee(nSize)) { + static CCriticalSection csFreeLimiter; static double dFreeCount; - static int64_t nLastFreeTime; - static int64_t nFreeLimit = GetArg("-limitfreerelay", DEFAULT_LIMITFREERELAY); + static int64_t nLastTime; + int64_t nNow = GetTime(); + LOCK(csFreeLimiter); + + // Use an exponentially decaying ~10-minute window: + dFreeCount *= pow(1.0 - 1.0/600.0, (double)(nNow - nLastTime)); + nLastTime = nNow; // -limitfreerelay unit is thousand-bytes-per-minute // At default rate it would take over a month to fill 1GB - if (RateLimitExceeded(dFreeCount, nLastFreeTime, nFreeLimit, nSize)) + if (dFreeCount + nSize >= GetArg("-limitfreerelay", DEFAULT_LIMITFREERELAY) * 10 * 1000) return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "rate limited free transaction"); LogPrint("mempool", "Rate limit dFreeCount: %g => %g\n", dFreeCount, dFreeCount+nSize); + dFreeCount += nSize; } if (nAbsurdFee && nFees > nAbsurdFee) @@ -1571,19 +1488,8 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C } pool.RemoveStaged(allConflicting, false); - if (fRespend) - { - // Clear the filter on average every MAX_DOUBLE_SPEND_BLOOM insertions - if (insecure_rand()%MAX_DOUBLESPEND_BLOOM == 0) - doubleSpendFilter.clear(); - doubleSpendFilter.insert(relayForOutpoint); - RelayTransaction(tx); - } - else - { - // Store transaction in memory - pool.addUnchecked(hash, entry, !IsInitialBlockDownload()); - } + // Store transaction in memory + pool.addUnchecked(hash, entry, setAncestors, !IsInitialBlockDownload()); // trim mempool and check if tx was trimmed if (!fOverrideMempoolLimit) { @@ -1593,9 +1499,9 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C } } - SyncWithWallets(tx, NULL, NULL, fRespend); + SyncWithWallets(tx, NULL, NULL); - return !fRespend; + return true; } bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree, @@ -2888,7 +2794,7 @@ bool static DisconnectTip(CValidationState& state, const CChainParams& chainpara // Let wallets know transactions went from 1-confirmed to // 0-confirmed or conflicted: BOOST_FOREACH(const CTransaction &tx, block.vtx) { - SyncWithWallets(tx, pindexDelete->pprev, NULL, false); + SyncWithWallets(tx, pindexDelete->pprev, NULL); } return true; } @@ -2947,11 +2853,11 @@ bool static ConnectTip(CValidationState& state, const CChainParams& chainparams, // Tell wallet about transactions that went from mempool // to conflicted: BOOST_FOREACH(const CTransaction &tx, txConflicted) { - SyncWithWallets(tx, pindexNew, NULL, false); + SyncWithWallets(tx, pindexNew, NULL); } // ... and about transactions that got confirmed: BOOST_FOREACH(const CTransaction &tx, pblock->vtx) { - SyncWithWallets(tx, pindexNew, pblock, false); + SyncWithWallets(tx, pindexNew, pblock); } int64_t nTime6 = GetTimeMicros(); nTimePostConnect += nTime6 - nTime5; nTimeTotal += nTime6 - nTime1; diff --git a/src/main.h b/src/main.h index 5a97bebb4..782d5b42a 100644 --- a/src/main.h +++ b/src/main.h @@ -189,9 +189,6 @@ extern CBlockIndex *pindexBestHeader; /** Minimum disk space required - used in CheckDiskSpace() */ static const uint64_t nMinDiskSpace = 52428800; -/** Initialize respend bloom filter **/ -void InitRespendFilter(); - /** Pruning-related variables and constants */ /** True if any block files have ever been pruned. */ extern bool fHavePruned; diff --git a/src/primitives/transaction.cpp b/src/primitives/transaction.cpp index fc08e06ea..374f978ba 100644 --- a/src/primitives/transaction.cpp +++ b/src/primitives/transaction.cpp @@ -95,15 +95,6 @@ CTransaction& CTransaction::operator=(const CTransaction &tx) { return *this; } -bool CTransaction::IsEquivalentTo(const CTransaction& tx) const -{ - CMutableTransaction tx1 = *this; - CMutableTransaction tx2 = tx; - for (unsigned int i = 0; i < tx1.vin.size(); i++) tx1.vin[i].scriptSig = CScript(); - for (unsigned int i = 0; i < tx2.vin.size(); i++) tx2.vin[i].scriptSig = CScript(); - return CTransaction(tx1) == CTransaction(tx2); -} - CAmount CTransaction::GetValueOut() const { CAmount nValueOut = 0; diff --git a/src/primitives/transaction.h b/src/primitives/transaction.h index 64692a182..7d922d46b 100644 --- a/src/primitives/transaction.h +++ b/src/primitives/transaction.h @@ -272,9 +272,6 @@ public: return hash; } - // True if only scriptSigs are different - bool IsEquivalentTo(const CTransaction& tx) const; - // Return sum of txouts. CAmount GetValueOut() const; // GetValueIn() is a method on CCoinsViewCache, because diff --git a/src/txmempool.cpp b/src/txmempool.cpp index 824b00548..94a5f46a9 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -571,6 +571,7 @@ void CTxMemPool::removeForReorg(const CCoinsViewCache *pcoins, unsigned int nMem void CTxMemPool::removeConflicts(const CTransaction &tx, std::list& removed) { // Remove transactions which depend on inputs of tx, recursively + list result; LOCK(cs); BOOST_FOREACH(const CTxIn &txin, tx.vin) { auto it = mapNextTx.find(txin.prevout); diff --git a/src/validationinterface.cpp b/src/validationinterface.cpp index 56b4d55db..8da0c7285 100644 --- a/src/validationinterface.cpp +++ b/src/validationinterface.cpp @@ -14,7 +14,7 @@ CMainSignals& GetMainSignals() void RegisterValidationInterface(CValidationInterface* pwalletIn) { g_signals.UpdatedBlockTip.connect(boost::bind(&CValidationInterface::UpdatedBlockTip, pwalletIn, _1)); - g_signals.SyncTransaction.connect(boost::bind(&CValidationInterface::SyncTransaction, pwalletIn, _1, _2, _3, _4)); + g_signals.SyncTransaction.connect(boost::bind(&CValidationInterface::SyncTransaction, pwalletIn, _1, _2, _3)); g_signals.UpdatedTransaction.connect(boost::bind(&CValidationInterface::UpdatedTransaction, pwalletIn, _1)); g_signals.SetBestChain.connect(boost::bind(&CValidationInterface::SetBestChain, pwalletIn, _1)); g_signals.Inventory.connect(boost::bind(&CValidationInterface::Inventory, pwalletIn, _1)); @@ -32,7 +32,7 @@ void UnregisterValidationInterface(CValidationInterface* pwalletIn) { g_signals.Inventory.disconnect(boost::bind(&CValidationInterface::Inventory, pwalletIn, _1)); g_signals.SetBestChain.disconnect(boost::bind(&CValidationInterface::SetBestChain, pwalletIn, _1)); g_signals.UpdatedTransaction.disconnect(boost::bind(&CValidationInterface::UpdatedTransaction, pwalletIn, _1)); - g_signals.SyncTransaction.disconnect(boost::bind(&CValidationInterface::SyncTransaction, pwalletIn, _1, _2, _3, _4)); + g_signals.SyncTransaction.disconnect(boost::bind(&CValidationInterface::SyncTransaction, pwalletIn, _1, _2, _3)); g_signals.UpdatedBlockTip.disconnect(boost::bind(&CValidationInterface::UpdatedBlockTip, pwalletIn, _1)); } @@ -48,6 +48,6 @@ void UnregisterAllValidationInterfaces() { g_signals.UpdatedBlockTip.disconnect_all_slots(); } -void SyncWithWallets(const CTransaction &tx, const CBlockIndex *pindex, const CBlock *pblock, bool fRespend) { - g_signals.SyncTransaction(tx, pindex, pblock, fRespend); +void SyncWithWallets(const CTransaction &tx, const CBlockIndex *pindex, const CBlock *pblock) { + g_signals.SyncTransaction(tx, pindex, pblock); } diff --git a/src/validationinterface.h b/src/validationinterface.h index 604df4650..68c35430c 100644 --- a/src/validationinterface.h +++ b/src/validationinterface.h @@ -27,12 +27,12 @@ void UnregisterValidationInterface(CValidationInterface* pwalletIn); /** Unregister all wallets from core */ void UnregisterAllValidationInterfaces(); /** Push an updated transaction to all registered wallets */ -void SyncWithWallets(const CTransaction& tx, const CBlockIndex *pindex, const CBlock* pblock = NULL, bool fRespend = false); +void SyncWithWallets(const CTransaction& tx, const CBlockIndex *pindex, const CBlock* pblock = NULL); class CValidationInterface { protected: virtual void UpdatedBlockTip(const CBlockIndex *pindex) {} - virtual void SyncTransaction(const CTransaction &tx, const CBlockIndex *pindex, const CBlock *pblock, bool fRespend) {} + virtual void SyncTransaction(const CTransaction &tx, const CBlockIndex *pindex, const CBlock *pblock) {} virtual void SetBestChain(const CBlockLocator &locator) {} virtual void UpdatedTransaction(const uint256 &hash) {} virtual void Inventory(const uint256 &hash) {} @@ -49,7 +49,7 @@ struct CMainSignals { /** Notifies listeners of updated block chain tip */ boost::signals2::signal UpdatedBlockTip; /** Notifies listeners of updated transaction data (transaction, optionally the block it is found in, and whether this is a known respend. */ - boost::signals2::signal SyncTransaction; + boost::signals2::signal SyncTransaction; /** Notifies listeners of an updated transaction without new data (for now: a coinbase potentially becoming visible). */ boost::signals2::signal UpdatedTransaction; /** Notifies listeners of a new active block chain. */ diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index b09baf89d..91a7cf3a7 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -398,7 +398,7 @@ bool CWallet::SetMaxVersion(int nVersion) return true; } -set CWallet::GetConflicts(const uint256& txid, bool includeEquivalent) const +set CWallet::GetConflicts(const uint256& txid) const { set result; AssertLockHeld(cs_wallet); @@ -416,8 +416,7 @@ set CWallet::GetConflicts(const uint256& txid, bool includeEquivalent) continue; // No conflict if zero or one spends range = mapTxSpends.equal_range(txin.prevout); for (TxSpends::const_iterator it = range.first; it != range.second; ++it) - if (includeEquivalent || !wtx.IsEquivalentTo(mapWallet.at(it->second))) - result.insert(it->second); + result.insert(it->second); } return result; } @@ -1199,7 +1198,7 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFromLoadWallet, CWalletD * pblock is optional, but should be provided if the transaction is known to be in a block. * If fUpdate is true, existing transactions will be updated. */ -bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pblock, bool fUpdate, bool fRespend) +bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pblock, bool fUpdate) { { AssertLockHeld(cs_wallet); @@ -1219,15 +1218,7 @@ bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pbl bool fExisted = mapWallet.count(tx.GetHash()) != 0; if (fExisted && !fUpdate) return false; - - bool fIsConflicting = IsConflicting(tx); - // Don't add respends that pay us, unless they conflict with us. Prevents resource exhaustion. - if (!fIsConflicting && fRespend) return false; - - if (fIsConflicting) - nConflictsReceived++; - - if (fExisted || IsMine(tx) || IsFromMe(tx) || fIsConflicting) + if (fExisted || IsMine(tx) || IsFromMe(tx)) { CWalletTx wtx(this,tx); @@ -1362,7 +1353,7 @@ void CWallet::MarkConflicted(const uint256& hashBlock, const uint256& hashTx) } } -void CWallet::SyncTransaction(const CTransaction& tx, const CBlockIndex *pindex, const CBlock* pblock, bool fRespend) +void CWallet::SyncTransaction(const CTransaction& tx, const CBlockIndex *pindex, const CBlock* pblock) { LOCK2(cs_main, cs_wallet); @@ -1376,7 +1367,7 @@ void CWallet::SyncTransaction(const CTransaction& tx, const CBlockIndex *pindex, } } - if (!AddToWalletIfInvolvingMe(tx, pblock, true, fRespend)) + if (!AddToWalletIfInvolvingMe(tx, pblock, true)) return; // Not one of ours // If a transaction changes 'conflicted' state, that changes the balance @@ -1389,7 +1380,6 @@ void CWallet::SyncTransaction(const CTransaction& tx, const CBlockIndex *pindex, } } - isminetype CWallet::IsMine(const CTxIn &txin) const { { @@ -1405,14 +1395,6 @@ isminetype CWallet::IsMine(const CTxIn &txin) const return ISMINE_NO; } -bool CWallet::IsConflicting(const CTransaction& tx) const -{ - BOOST_FOREACH(const CTxIn& txin, tx.vin) - if (mapTxSpends.count(txin.prevout)) - return true; - return false; -} - CAmount CWallet::GetDebit(const CTxIn &txin, const isminefilter& filter) const { { @@ -1756,7 +1738,7 @@ int CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate) ReadBlockFromDisk(block, pindex, Params().GetConsensus()); BOOST_FOREACH(CTransaction& tx, block.vtx) { - if (AddToWalletIfInvolvingMe(tx, &block, fUpdate, false)) + if (AddToWalletIfInvolvingMe(tx, &block, fUpdate)) ret++; } pindex = chainActive.Next(pindex); @@ -1791,7 +1773,8 @@ void CWallet::ReacceptWalletTransactions() assert(wtx.GetHash() == wtxid); int nDepth = wtx.GetDepthInMainChain(); - if (!(wtx.IsCoinBase() || wtx.IsCoinStake()) && (nDepth == 0 && !wtx.isAbandoned()) && (IsMine(wtx) || IsFromMe(wtx))){ + + if (!wtx.IsCoinBase() && !wtx.IsCoinStake() && (nDepth == 0 && !wtx.isAbandoned())) { mapSorted.insert(std::make_pair(wtx.nOrderPos, &wtx)); } } @@ -1823,13 +1806,13 @@ bool CWalletTx::RelayWalletTransaction() return false; } -set CWalletTx::GetConflicts(bool includeEquivalent) const +set CWalletTx::GetConflicts() const { set result; if (pwallet != NULL) { uint256 myHash = GetHash(); - result = pwallet->GetConflicts(myHash, includeEquivalent); + result = pwallet->GetConflicts(myHash); result.erase(myHash); } return result; diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 03014458a..374818abd 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -412,7 +412,7 @@ public: bool RelayWalletTransaction(); - std::set GetConflicts(bool includeEquivalent=true) const; + std::set GetConflicts() const; }; @@ -759,8 +759,8 @@ public: void MarkDirty(); bool AddToWallet(const CWalletTx& wtxIn, bool fFromLoadWallet, CWalletDB* pwalletdb); - void SyncTransaction(const CTransaction& tx, const CBlockIndex *pindex, const CBlock* pblock, bool fRespend); - bool AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pblock, bool fUpdate, bool fRespend); + void SyncTransaction(const CTransaction& tx, const CBlockIndex *pindex, const CBlock* pblock); + bool AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pblock, bool fUpdate); int ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate = false); void ReacceptWalletTransactions(); void ResendWalletTransactions(int64_t nBestBlockTime); @@ -830,7 +830,6 @@ public: bool IsMine(const CTransaction& tx) const; /** should probably be renamed to IsRelevantToMe */ bool IsFromMe(const CTransaction& tx) const; - bool IsConflicting(const CTransaction& tx) const; CAmount GetDebit(const CTransaction& tx, const isminefilter& filter) const; CAmount GetCredit(const CTransaction& tx, const isminefilter& filter) const; CAmount GetChange(const CTransaction& tx) const; @@ -883,7 +882,7 @@ public: void DisableTransaction(const CTransaction &tx); //! Get wallet transactions that conflict with given transaction (spend same outputs) - std::set GetConflicts(const uint256& txid, bool includeEquivalent) const; + std::set GetConflicts(const uint256& txid) const; //! Flush wallet (bitdb flush) void Flush(bool shutdown=false);