Skip to content

Commit 98e84aa

Browse files
committed
Revert "Relay double-spends, subject to anti-DOS"
This reverts commit d640a3c.
1 parent 3015e0b commit 98e84aa

File tree

6 files changed

+15
-109
lines changed

6 files changed

+15
-109
lines changed

src/core.cpp

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -124,22 +124,6 @@ CTransaction& CTransaction::operator=(const CTransaction &tx) {
124124
return *this;
125125
}
126126

127-
bool CTransaction::IsEquivalentTo(const CTransaction& tx) const
128-
{
129-
if (nVersion != tx.nVersion ||
130-
nLockTime != tx.nLockTime ||
131-
vin.size() != tx.vin.size() ||
132-
vout != tx.vout)
133-
return false;
134-
for (unsigned int i = 0; i < vin.size(); i++)
135-
{
136-
if (vin[i].nSequence != tx.vin[i].nSequence ||
137-
vin[i].prevout != tx.vin[i].prevout)
138-
return false;
139-
}
140-
return true;
141-
}
142-
143127
int64_t CTransaction::GetValueOut() const
144128
{
145129
int64_t nValueOut = 0;

src/core.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -255,9 +255,6 @@ class CTransaction
255255
return hash;
256256
}
257257

258-
// True if only scriptSigs are different
259-
bool IsEquivalentTo(const CTransaction& tx) const;
260-
261258
// Return sum of txouts.
262259
int64_t GetValueOut() const;
263260
// GetValueIn() is a method on CCoinsViewCache, because

src/init.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1229,7 +1229,6 @@ bool AppInit2(boost::thread_group& threadGroup)
12291229
LogPrintf("mapAddressBook.size() = %u\n", pwalletMain ? pwalletMain->mapAddressBook.size() : 0);
12301230
#endif
12311231

1232-
RegisterInternalSignals();
12331232
StartNode(threadGroup);
12341233
if (fServer)
12351234
StartRPCThreads();

src/main.cpp

Lines changed: 14 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77

88
#include "addrman.h"
99
#include "alert.h"
10-
#include "bloom.h"
1110
#include "chainparams.h"
1211
#include "checkpoints.h"
1312
#include "checkqueue.h"
@@ -126,10 +125,6 @@ namespace {
126125

127126
} // anon namespace
128127

129-
// Forward reference functions defined here:
130-
static const unsigned int MAX_DOUBLESPEND_BLOOM = 1000;
131-
static void RelayDoubleSpend(const COutPoint& outPoint, const CTransaction& doubleSpend, bool fInBlock, CBloomFilter& filter);
132-
133128
//////////////////////////////////////////////////////////////////////////////
134129
//
135130
// dispatching functions
@@ -152,25 +147,10 @@ struct CMainSignals {
152147
boost::signals2::signal<void (const uint256 &)> Inventory;
153148
// Tells listeners to broadcast their data.
154149
boost::signals2::signal<void ()> Broadcast;
155-
// Notifies listeners of detection of a double-spent transaction. Arguments are outpoint that is
156-
// double-spent, first transaction seen, double-spend transaction, and whether the second double-spend
157-
// transaction was first seen in a block.
158-
// Note: only notifies if the previous transaction is in the memory pool; if previous transction was in a block,
159-
// then the double-spend simply fails when we try to lookup the inputs in the current UTXO set.
160-
boost::signals2::signal<void (const COutPoint&, const CTransaction&, bool)> DetectedDoubleSpend;
161150
} g_signals;
162151

163152
} // anon namespace
164153

165-
void RegisterInternalSignals() {
166-
static CBloomFilter doubleSpendFilter;
167-
seed_insecure_rand();
168-
doubleSpendFilter = CBloomFilter(MAX_DOUBLESPEND_BLOOM, 0.01, insecure_rand(), BLOOM_UPDATE_NONE);
169-
170-
g_signals.DetectedDoubleSpend.connect(boost::bind(RelayDoubleSpend, _1, _2, _3, doubleSpendFilter));
171-
}
172-
173-
174154
void RegisterWallet(CWalletInterface* pwalletIn) {
175155
g_signals.SyncTransaction.connect(boost::bind(&CWalletInterface::SyncTransaction, pwalletIn, _1, _2));
176156
g_signals.EraseTransaction.connect(boost::bind(&CWalletInterface::EraseFromWallet, pwalletIn, _1));
@@ -890,22 +870,6 @@ int64_t GetMinRelayFee(const CTransaction& tx, unsigned int nBytes, bool fAllowF
890870
return nMinFee;
891871
}
892872

893-
// Exponentially limit the rate of nSize flow to nLimit. nLimit unit is thousands-per-minute.
894-
bool RateLimitExceeded(double& dCount, int64_t& nLastTime, int64_t nLimit, unsigned int nSize)
895-
{
896-
static CCriticalSection csLimiter;
897-
int64_t nNow = GetTime();
898-
899-
LOCK(csLimiter);
900-
901-
// Use an exponentially decaying ~10-minute window:
902-
dCount *= pow(1.0 - 1.0/600.0, (double)(nNow - nLastTime));
903-
nLastTime = nNow;
904-
if (dCount >= nLimit*10*1000)
905-
return true;
906-
dCount += nSize;
907-
return false;
908-
}
909873

910874
bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree,
911875
bool* pfMissingInputs, bool fRejectInsaneFee)
@@ -940,10 +904,9 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
940904
for (unsigned int i = 0; i < tx.vin.size(); i++)
941905
{
942906
COutPoint outpoint = tx.vin[i].prevout;
943-
// Does tx conflict with a member of the pool, and is it not equivalent to that member?
944-
if (pool.mapNextTx.count(outpoint) && !tx.IsEquivalentTo(*pool.mapNextTx[outpoint].ptx))
907+
if (pool.mapNextTx.count(outpoint))
945908
{
946-
g_signals.DetectedDoubleSpend(outpoint, tx, false);
909+
// Disable replacement feature for now
947910
return false;
948911
}
949912
}
@@ -1015,15 +978,23 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
1015978
// be annoying or make others' transactions take longer to confirm.
1016979
if (fLimitFree && nFees < ::minRelayTxFee.GetFee(nSize))
1017980
{
981+
static CCriticalSection csFreeLimiter;
1018982
static double dFreeCount;
1019-
static int64_t nLastFreeTime;
1020-
static int64_t nFreeLimit = GetArg("-limitfreerelay", 15);
983+
static int64_t nLastTime;
984+
int64_t nNow = GetTime();
985+
986+
LOCK(csFreeLimiter);
1021987

1022-
if (RateLimitExceeded(dFreeCount, nLastFreeTime, nFreeLimit, nSize))
988+
// Use an exponentially decaying ~10-minute window:
989+
dFreeCount *= pow(1.0 - 1.0/600.0, (double)(nNow - nLastTime));
990+
nLastTime = nNow;
991+
// -limitfreerelay unit is thousand-bytes-per-minute
992+
// At default rate it would take over a month to fill 1GB
993+
if (dFreeCount >= GetArg("-limitfreerelay", 15)*10*1000)
1023994
return state.DoS(0, error("AcceptToMemoryPool : free transaction rejected by rate limiter"),
1024995
REJECT_INSUFFICIENTFEE, "insufficient priority");
1025-
1026996
LogPrint("mempool", "Rate limit dFreeCount: %g => %g\n", dFreeCount, dFreeCount+nSize);
997+
dFreeCount += nSize;
1027998
}
1028999

10291000
if (fRejectInsaneFee && nFees > ::minRelayTxFee.GetFee(nSize) * 10000)
@@ -1046,49 +1017,6 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
10461017
return true;
10471018
}
10481019

1049-
static void
1050-
RelayDoubleSpend(const COutPoint& outPoint, const CTransaction& doubleSpend, bool fInBlock, CBloomFilter& filter)
1051-
{
1052-
// Relaying double-spend attempts to our peers lets them detect when
1053-
// somebody might be trying to cheat them. However, blindly relaying
1054-
// every double-spend across the entire network gives attackers
1055-
// a denial-of-service attack: just generate a stream of double-spends
1056-
// re-spending the same (limited) set of outpoints owned by the attacker.
1057-
// So, we use a bloom filter and only relay (at most) the first double
1058-
// spend for each outpoint. False-positives ("we have already relayed")
1059-
// are OK, because if the peer doesn't hear about the double-spend
1060-
// from us they are very likely to hear about it from another peer, since
1061-
// each peer uses a different, randomized bloom filter.
1062-
1063-
if (fInBlock || filter.contains(outPoint)) return;
1064-
1065-
// Apply an independent rate limit to double-spend relays
1066-
static double dRespendCount;
1067-
static int64_t nLastRespendTime;
1068-
static int64_t nRespendLimit = GetArg("-limitrespendrelay", 100);
1069-
unsigned int nSize = ::GetSerializeSize(doubleSpend, SER_NETWORK, PROTOCOL_VERSION);
1070-
1071-
if (RateLimitExceeded(dRespendCount, nLastRespendTime, nRespendLimit, nSize))
1072-
{
1073-
LogPrint("mempool", "Double-spend relay rejected by rate limiter\n");
1074-
return;
1075-
}
1076-
1077-
LogPrint("mempool", "Rate limit dRespendCount: %g => %g\n", dRespendCount, dRespendCount+nSize);
1078-
1079-
// Clear the filter on average every MAX_DOUBLE_SPEND_BLOOM
1080-
// insertions
1081-
if (insecure_rand()%MAX_DOUBLESPEND_BLOOM == 0)
1082-
filter.clear();
1083-
1084-
filter.insert(outPoint);
1085-
1086-
RelayTransaction(doubleSpend);
1087-
1088-
// Share conflict with wallet
1089-
g_signals.SyncTransaction(doubleSpend, NULL);
1090-
}
1091-
10921020

10931021
int CMerkleTx::GetDepthInMainChainINTERNAL(CBlockIndex* &pindexRet) const
10941022
{

src/main.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -112,9 +112,6 @@ struct CNodeStateStats;
112112

113113
struct CBlockTemplate;
114114

115-
/** Set up internal signal handlers **/
116-
void RegisterInternalSignals();
117-
118115
/** Register a wallet to receive updates from core */
119116
void RegisterWallet(CWalletInterface* pwalletIn);
120117
/** Unregister a wallet from core */

src/txmempool.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -420,6 +420,7 @@ void CTxMemPool::remove(const CTransaction &tx, std::list<CTransaction>& removed
420420
void CTxMemPool::removeConflicts(const CTransaction &tx, std::list<CTransaction>& removed)
421421
{
422422
// Remove transactions which depend on inputs of tx, recursively
423+
list<CTransaction> result;
423424
LOCK(cs);
424425
BOOST_FOREACH(const CTxIn &txin, tx.vin) {
425426
std::map<COutPoint, CInPoint>::iterator it = mapNextTx.find(txin.prevout);

0 commit comments

Comments
 (0)