Skip to content

Commit e61c6d6

Browse files
committed
Merge pull request #4450
0da6b3f Remove signal DoubleSpendDetected, use function (Tom Harding) 88dd359 Check signatures before respend relay (Tom Harding)
2 parents 21876d3 + 0da6b3f commit e61c6d6

File tree

3 files changed

+64
-66
lines changed

3 files changed

+64
-66
lines changed

src/init.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1183,7 +1183,7 @@ bool AppInit2(boost::thread_group& threadGroup)
11831183
LogPrintf("mapAddressBook.size() = %u\n", pwalletMain ? pwalletMain->mapAddressBook.size() : 0);
11841184
#endif
11851185

1186-
RegisterInternalSignals();
1186+
InitRespendFilter();
11871187
StartNode(threadGroup);
11881188
if (fServer)
11891189
StartRPCThreads();

src/main.cpp

Lines changed: 61 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -123,9 +123,14 @@ namespace {
123123

124124
} // anon namespace
125125

126-
// Forward reference functions defined here:
126+
// Bloom filter to limit respend relays to one
127127
static const unsigned int MAX_DOUBLESPEND_BLOOM = 1000;
128-
static void RelayDoubleSpend(const COutPoint& outPoint, const CTransaction& doubleSpend, bool fInBlock, CBloomFilter& filter);
128+
static CBloomFilter doubleSpendFilter;
129+
void InitRespendFilter() {
130+
seed_insecure_rand();
131+
doubleSpendFilter = CBloomFilter(MAX_DOUBLESPEND_BLOOM, 0.01, insecure_rand(), BLOOM_UPDATE_NONE);
132+
}
133+
129134

130135
//////////////////////////////////////////////////////////////////////////////
131136
//
@@ -149,24 +154,10 @@ struct CMainSignals {
149154
boost::signals2::signal<void (const uint256 &)> Inventory;
150155
// Tells listeners to broadcast their data.
151156
boost::signals2::signal<void ()> Broadcast;
152-
// Notifies listeners of detection of a double-spent transaction. Arguments are outpoint that is
153-
// double-spent, first transaction seen, double-spend transaction, and whether the second double-spend
154-
// transaction was first seen in a block.
155-
// Note: only notifies if the previous transaction is in the memory pool; if previous transction was in a block,
156-
// then the double-spend simply fails when we try to lookup the inputs in the current UTXO set.
157-
boost::signals2::signal<void (const COutPoint&, const CTransaction&, bool)> DetectedDoubleSpend;
158157
} g_signals;
159158

160159
} // anon namespace
161160

162-
void RegisterInternalSignals() {
163-
static CBloomFilter doubleSpendFilter;
164-
seed_insecure_rand();
165-
doubleSpendFilter = CBloomFilter(MAX_DOUBLESPEND_BLOOM, 0.01, insecure_rand(), BLOOM_UPDATE_NONE);
166-
167-
g_signals.DetectedDoubleSpend.connect(boost::bind(RelayDoubleSpend, _1, _2, _3, doubleSpendFilter));
168-
}
169-
170161

171162
void RegisterWallet(CWalletInterface* pwalletIn) {
172163
g_signals.SyncTransaction.connect(boost::bind(&CWalletInterface::SyncTransaction, pwalletIn, _1, _2));
@@ -901,6 +892,45 @@ bool RateLimitExceeded(double& dCount, int64_t& nLastTime, int64_t nLimit, unsig
901892
return false;
902893
}
903894

895+
static bool RelayableRespend(const COutPoint& outPoint, const CTransaction& doubleSpend, bool fInBlock, CBloomFilter& filter)
896+
{
897+
// Relaying double-spend attempts to our peers lets them detect when
898+
// somebody might be trying to cheat them. However, blindly relaying
899+
// every double-spend across the entire network gives attackers
900+
// a denial-of-service attack: just generate a stream of double-spends
901+
// re-spending the same (limited) set of outpoints owned by the attacker.
902+
// So, we use a bloom filter and only relay (at most) the first double
903+
// spend for each outpoint. False-positives ("we have already relayed")
904+
// are OK, because if the peer doesn't hear about the double-spend
905+
// from us they are very likely to hear about it from another peer, since
906+
// each peer uses a different, randomized bloom filter.
907+
908+
if (fInBlock || filter.contains(outPoint)) return false;
909+
910+
// Apply an independent rate limit to double-spend relays
911+
static double dRespendCount;
912+
static int64_t nLastRespendTime;
913+
static int64_t nRespendLimit = GetArg("-limitrespendrelay", 100);
914+
unsigned int nSize = ::GetSerializeSize(doubleSpend, SER_NETWORK, PROTOCOL_VERSION);
915+
916+
if (RateLimitExceeded(dRespendCount, nLastRespendTime, nRespendLimit, nSize))
917+
{
918+
LogPrint("mempool", "Double-spend relay rejected by rate limiter\n");
919+
return false;
920+
}
921+
922+
LogPrint("mempool", "Rate limit dRespendCount: %g => %g\n", dRespendCount, dRespendCount+nSize);
923+
924+
// Clear the filter on average every MAX_DOUBLE_SPEND_BLOOM
925+
// insertions
926+
if (insecure_rand()%MAX_DOUBLESPEND_BLOOM == 0)
927+
filter.clear();
928+
929+
filter.insert(outPoint);
930+
931+
return true;
932+
}
933+
904934
bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree,
905935
bool* pfMissingInputs, bool fRejectInsaneFee)
906936
{
@@ -929,6 +959,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
929959
return false;
930960

931961
// Check for conflicts with in-memory transactions
962+
bool relayableRespend = false;
932963
{
933964
LOCK(pool.cs); // protect pool.mapNextTx
934965
for (unsigned int i = 0; i < tx.vin.size(); i++)
@@ -937,8 +968,9 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
937968
// Does tx conflict with a member of the pool, and is it not equivalent to that member?
938969
if (pool.mapNextTx.count(outpoint) && !tx.IsEquivalentTo(*pool.mapNextTx[outpoint].ptx))
939970
{
940-
g_signals.DetectedDoubleSpend(outpoint, tx, false);
941-
return false;
971+
relayableRespend = RelayableRespend(outpoint, tx, false, doubleSpendFilter);
972+
if (!relayableRespend)
973+
return false;
942974
}
943975
}
944976
}
@@ -1031,55 +1063,21 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
10311063
{
10321064
return error("AcceptToMemoryPool: : ConnectInputs failed %s", hash.ToString());
10331065
}
1034-
// Store transaction in memory
1035-
pool.addUnchecked(hash, entry);
1036-
}
1037-
1038-
g_signals.SyncTransaction(tx, NULL);
1039-
1040-
return true;
1041-
}
1042-
1043-
static void RelayDoubleSpend(const COutPoint& outPoint, const CTransaction& doubleSpend, bool fInBlock, CBloomFilter& filter)
1044-
{
1045-
// Relaying double-spend attempts to our peers lets them detect when
1046-
// somebody might be trying to cheat them. However, blindly relaying
1047-
// every double-spend across the entire network gives attackers
1048-
// a denial-of-service attack: just generate a stream of double-spends
1049-
// re-spending the same (limited) set of outpoints owned by the attacker.
1050-
// So, we use a bloom filter and only relay (at most) the first double
1051-
// spend for each outpoint. False-positives ("we have already relayed")
1052-
// are OK, because if the peer doesn't hear about the double-spend
1053-
// from us they are very likely to hear about it from another peer, since
1054-
// each peer uses a different, randomized bloom filter.
1055-
1056-
if (fInBlock || filter.contains(outPoint)) return;
1057-
1058-
// Apply an independent rate limit to double-spend relays
1059-
static double dRespendCount;
1060-
static int64_t nLastRespendTime;
1061-
static int64_t nRespendLimit = GetArg("-limitrespendrelay", 100);
1062-
unsigned int nSize = ::GetSerializeSize(doubleSpend, SER_NETWORK, PROTOCOL_VERSION);
10631066

1064-
if (RateLimitExceeded(dRespendCount, nLastRespendTime, nRespendLimit, nSize))
1065-
{
1066-
LogPrint("mempool", "Double-spend relay rejected by rate limiter\n");
1067-
return;
1067+
if (relayableRespend)
1068+
{
1069+
RelayTransaction(tx);
1070+
}
1071+
else
1072+
{
1073+
// Store transaction in memory
1074+
pool.addUnchecked(hash, entry);
1075+
}
10681076
}
10691077

1070-
LogPrint("mempool", "Rate limit dRespendCount: %g => %g\n", dRespendCount, dRespendCount+nSize);
1071-
1072-
// Clear the filter on average every MAX_DOUBLE_SPEND_BLOOM
1073-
// insertions
1074-
if (insecure_rand()%MAX_DOUBLESPEND_BLOOM == 0)
1075-
filter.clear();
1076-
1077-
filter.insert(outPoint);
1078-
1079-
RelayTransaction(doubleSpend);
1078+
g_signals.SyncTransaction(tx, NULL);
10801079

1081-
// Share conflict with wallet
1082-
g_signals.SyncTransaction(doubleSpend, NULL);
1080+
return !relayableRespend;
10831081
}
10841082

10851083

src/main.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,8 +109,8 @@ struct CNodeStateStats;
109109

110110
struct CBlockTemplate;
111111

112-
/** Set up internal signal handlers **/
113-
void RegisterInternalSignals();
112+
/** Initialize respend bloom filter **/
113+
void InitRespendFilter();
114114

115115
/** Register a wallet to receive updates from core */
116116
void RegisterWallet(CWalletInterface* pwalletIn);

0 commit comments

Comments
 (0)