Skip to content

Commit 3bc4fd9

Browse files
committed
Merge pull request #4558
8f3f94a Revert "CBloomFilter::clear() method" (Wladimir J. van der Laan) 98e84aa Revert "Relay double-spends, subject to anti-DOS" (Wladimir J. van der Laan) 3015e0b Revert "UI to alert of respend attempt affecting wallet." (Wladimir J. van der Laan) 39d3f2c Revert "Add -respendnotify option and new RPC data" (Wladimir J. van der Laan) 680f725 Revert "Add release notes entry" (Wladimir J. van der Laan) ad26dc9 Revert "Formatting, spelling, comment fixes." (Wladimir J. van der Laan) cd057bf Revert "Check signatures before respend relay" (Wladimir J. van der Laan) 67cc8f2 Revert "Remove signal DoubleSpendDetected, use function" (Wladimir J. van der Laan)
2 parents 0de61e7 + 8f3f94a commit 3bc4fd9

21 files changed

+42
-297
lines changed

contrib/debian/manpages/bitcoin-qt.1

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -139,9 +139,6 @@ Execute command when the best block changes (%s in cmd is replaced by block hash
139139
\fB\-walletnotify=\fR<cmd>
140140
Execute command when a wallet transaction changes (%s in cmd is replaced by TxID)
141141
.TP
142-
\fB\-respendnotify=\fR<cmd>
143-
Execute command when a network tx respends wallet tx input (%s=respend TxID, %t=wallet TxID)
144-
.TP
145142
\fB\-alertnotify=\fR<cmd>
146143
Execute command when a relevant alert is received (%s in cmd is replaced by message)
147144
.TP

doc/release-notes.md

Lines changed: 0 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -39,49 +39,3 @@ estimate.
3939
Statistics used to estimate fees and priorities are saved in the
4040
data directory in the 'fee_estimates.dat' file just before
4141
program shutdown, and are read in at startup.
42-
43-
Double-Spend Relay and Alerts
44-
=============================
45-
VERY IMPORTANT: *It has never been safe, and remains unsafe, to rely*
46-
*on unconfirmed transactions.*
47-
48-
Relay
49-
-----
50-
When an attempt is seen on the network to spend the same unspent funds
51-
more than once, it is no longer ignored. Instead, it is broadcast, to
52-
serve as an alert. This broadcast is subject to protections against
53-
denial-of-service attacks.
54-
55-
Wallets and other bitcoin services should alert their users to
56-
double-spends that affect them. Merchants and other users may have
57-
enough time to withhold goods or services when payment becomes
58-
uncertain, until confirmation.
59-
60-
Bitcoin Core Wallet Alerts
61-
--------------------------
62-
The Bitcoin Core wallet now makes respend attempts visible in several
63-
ways.
64-
65-
If you are online, and a respend affecting one of your wallet
66-
transactions is seen, a notification is immediately issued to the
67-
command registered with `-respendnotify=<cmd>`. Additionally, if
68-
using the GUI:
69-
- An alert box is immediately displayed.
70-
- The affected wallet transaction is highlighted in red until it is
71-
confirmed (and it may never be confirmed).
72-
73-
A `respendsobserved` array is added to `gettransaction`, `listtransactions`,
74-
and `listsinceblock` RPC results.
75-
76-
Warning
77-
-------
78-
*If you rely on an unconfirmed transaction, these change do VERY*
79-
*LITTLE to protect you from a malicious double-spend, because:*
80-
81-
- You may learn about the respend too late to avoid doing whatever
82-
you were being paid for
83-
- Using other relay rules, a double-spender can craft his crime to
84-
resist broadcast
85-
- Miners can choose which conflicting spend to confirm, and some
86-
miners may not confirm the first acceptable spend they see
87-

src/bloom.cpp

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -94,13 +94,6 @@ bool CBloomFilter::contains(const uint256& hash) const
9494
return contains(data);
9595
}
9696

97-
void CBloomFilter::clear()
98-
{
99-
vData.assign(vData.size(),0);
100-
isFull = false;
101-
isEmpty = true;
102-
}
103-
10497
bool CBloomFilter::IsWithinSizeConstraints() const
10598
{
10699
return vData.size() <= MAX_BLOOM_FILTER_SIZE && nHashFuncs <= MAX_HASH_FUNCS;

src/bloom.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,6 @@ class CBloomFilter
7878
bool contains(const COutPoint& outpoint) const;
7979
bool contains(const uint256& hash) const;
8080

81-
void clear();
82-
8381
// True if the size is <= MAX_BLOOM_FILTER_SIZE and the number of hash functions is <= MAX_HASH_FUNCS
8482
// (catch a filter which was just deserialized which was too big)
8583
bool IsWithinSizeConstraints() const;

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-
InitRespendFilter();
12331232
StartNode(threadGroup);
12341233
if (fServer)
12351234
StartRPCThreads();

src/main.cpp

Lines changed: 18 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,15 +125,6 @@ namespace {
126125

127126
} // anon namespace
128127

129-
// Bloom filter to limit respend relays to one
130-
static const unsigned int MAX_DOUBLESPEND_BLOOM = 1000;
131-
static CBloomFilter doubleSpendFilter;
132-
void InitRespendFilter() {
133-
seed_insecure_rand();
134-
doubleSpendFilter = CBloomFilter(MAX_DOUBLESPEND_BLOOM, 0.01, insecure_rand(), BLOOM_UPDATE_NONE);
135-
}
136-
137-
138128
//////////////////////////////////////////////////////////////////////////////
139129
//
140130
// dispatching functions
@@ -161,7 +151,6 @@ struct CMainSignals {
161151

162152
} // anon namespace
163153

164-
165154
void RegisterWallet(CWalletInterface* pwalletIn) {
166155
g_signals.SyncTransaction.connect(boost::bind(&CWalletInterface::SyncTransaction, pwalletIn, _1, _2));
167156
g_signals.EraseTransaction.connect(boost::bind(&CWalletInterface::EraseFromWallet, pwalletIn, _1));
@@ -881,60 +870,6 @@ int64_t GetMinRelayFee(const CTransaction& tx, unsigned int nBytes, bool fAllowF
881870
return nMinFee;
882871
}
883872

884-
// Exponentially limit the rate of nSize flow to nLimit. nLimit unit is thousands-per-minute.
885-
bool RateLimitExceeded(double& dCount, int64_t& nLastTime, int64_t nLimit, unsigned int nSize)
886-
{
887-
static CCriticalSection csLimiter;
888-
int64_t nNow = GetTime();
889-
890-
LOCK(csLimiter);
891-
892-
dCount *= pow(1.0 - 1.0/600.0, (double)(nNow - nLastTime));
893-
nLastTime = nNow;
894-
if (dCount >= nLimit*10*1000)
895-
return true;
896-
dCount += nSize;
897-
return false;
898-
}
899-
900-
static bool RelayableRespend(const COutPoint& outPoint, const CTransaction& doubleSpend, bool fInBlock, CBloomFilter& filter)
901-
{
902-
// Relaying double-spend attempts to our peers lets them detect when
903-
// somebody might be trying to cheat them. However, blindly relaying
904-
// every double-spend across the entire network gives attackers
905-
// a denial-of-service attack: just generate a stream of double-spends
906-
// re-spending the same (limited) set of outpoints owned by the attacker.
907-
// So, we use a bloom filter and only relay (at most) the first double
908-
// spend for each outpoint. False-positives ("we have already relayed")
909-
// are OK, because if the peer doesn't hear about the double-spend
910-
// from us they are very likely to hear about it from another peer, since
911-
// each peer uses a different, randomized bloom filter.
912-
913-
if (fInBlock || filter.contains(outPoint)) return false;
914-
915-
// Apply an independent rate limit to double-spend relays
916-
static double dRespendCount;
917-
static int64_t nLastRespendTime;
918-
static int64_t nRespendLimit = GetArg("-limitrespendrelay", 100);
919-
unsigned int nSize = ::GetSerializeSize(doubleSpend, SER_NETWORK, PROTOCOL_VERSION);
920-
921-
if (RateLimitExceeded(dRespendCount, nLastRespendTime, nRespendLimit, nSize))
922-
{
923-
LogPrint("mempool", "Double-spend relay rejected by rate limiter\n");
924-
return false;
925-
}
926-
927-
LogPrint("mempool", "Rate limit dRespendCount: %g => %g\n", dRespendCount, dRespendCount+nSize);
928-
929-
// Clear the filter on average every MAX_DOUBLE_SPEND_BLOOM
930-
// insertions
931-
if (insecure_rand()%MAX_DOUBLESPEND_BLOOM == 0)
932-
filter.clear();
933-
934-
filter.insert(outPoint);
935-
936-
return true;
937-
}
938873

939874
bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree,
940875
bool* pfMissingInputs, bool fRejectInsaneFee)
@@ -964,18 +899,15 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
964899
return false;
965900

966901
// Check for conflicts with in-memory transactions
967-
bool relayableRespend = false;
968902
{
969903
LOCK(pool.cs); // protect pool.mapNextTx
970904
for (unsigned int i = 0; i < tx.vin.size(); i++)
971905
{
972906
COutPoint outpoint = tx.vin[i].prevout;
973-
// Does tx conflict with a member of the pool, and is it not equivalent to that member?
974-
if (pool.mapNextTx.count(outpoint) && !tx.IsEquivalentTo(*pool.mapNextTx[outpoint].ptx))
907+
if (pool.mapNextTx.count(outpoint))
975908
{
976-
relayableRespend = RelayableRespend(outpoint, tx, false, doubleSpendFilter);
977-
if (!relayableRespend)
978-
return false;
909+
// Disable replacement feature for now
910+
return false;
979911
}
980912
}
981913
}
@@ -1046,15 +978,23 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
1046978
// be annoying or make others' transactions take longer to confirm.
1047979
if (fLimitFree && nFees < ::minRelayTxFee.GetFee(nSize))
1048980
{
981+
static CCriticalSection csFreeLimiter;
1049982
static double dFreeCount;
1050-
static int64_t nLastFreeTime;
1051-
static int64_t nFreeLimit = GetArg("-limitfreerelay", 15);
983+
static int64_t nLastTime;
984+
int64_t nNow = GetTime();
1052985

1053-
if (RateLimitExceeded(dFreeCount, nLastFreeTime, nFreeLimit, nSize))
986+
LOCK(csFreeLimiter);
987+
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)
1054994
return state.DoS(0, error("AcceptToMemoryPool : free transaction rejected by rate limiter"),
1055995
REJECT_INSUFFICIENTFEE, "insufficient priority");
1056-
1057996
LogPrint("mempool", "Rate limit dFreeCount: %g => %g\n", dFreeCount, dFreeCount+nSize);
997+
dFreeCount += nSize;
1058998
}
1059999

10601000
if (fRejectInsaneFee && nFees > ::minRelayTxFee.GetFee(nSize) * 10000)
@@ -1068,21 +1008,13 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
10681008
{
10691009
return error("AcceptToMemoryPool: : ConnectInputs failed %s", hash.ToString());
10701010
}
1071-
1072-
if (relayableRespend)
1073-
{
1074-
RelayTransaction(tx);
1075-
}
1076-
else
1077-
{
1078-
// Store transaction in memory
1079-
pool.addUnchecked(hash, entry);
1080-
}
1011+
// Store transaction in memory
1012+
pool.addUnchecked(hash, entry);
10811013
}
10821014

10831015
g_signals.SyncTransaction(tx, NULL);
10841016

1085-
return !relayableRespend;
1017+
return true;
10861018
}
10871019

10881020

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-
/** Initialize respend bloom filter **/
116-
void InitRespendFilter();
117-
118115
/** Register a wallet to receive updates from core */
119116
void RegisterWallet(CWalletInterface* pwalletIn);
120117
/** Unregister a wallet from core */

src/qt/guiconstants.h

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,6 @@ static const int STATUSBAR_ICONSIZE = 16;
2323
#define COLOR_NEGATIVE QColor(255, 0, 0)
2424
/* Transaction list -- bare address (without label) */
2525
#define COLOR_BAREADDRESS QColor(140, 140, 140)
26-
/* Transaction list -- has conflicting transactions */
27-
#define COLOR_HASCONFLICTING QColor(255, 255, 255)
28-
/* Transaction list -- has conflicting transactions - background */
29-
#define COLOR_HASCONFLICTING_BG QColor(192, 0, 0)
3026

3127
/* Tooltips longer than this (in characters) are converted into rich text,
3228
so that they can be word-wrapped.

0 commit comments

Comments
 (0)