@@ -123,9 +123,14 @@ namespace {
123
123
124
124
} // anon namespace
125
125
126
- // Forward reference functions defined here:
126
+ // Bloom filter to limit respend relays to one
127
127
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
+
129
134
130
135
// ////////////////////////////////////////////////////////////////////////////
131
136
//
@@ -149,24 +154,10 @@ struct CMainSignals {
149
154
boost::signals2::signal<void (const uint256 &)> Inventory;
150
155
// Tells listeners to broadcast their data.
151
156
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;
158
157
} g_signals;
159
158
160
159
} // anon namespace
161
160
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
-
170
161
171
162
void RegisterWallet (CWalletInterface* pwalletIn) {
172
163
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
901
892
return false ;
902
893
}
903
894
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
+
904
934
bool AcceptToMemoryPool (CTxMemPool& pool, CValidationState &state, const CTransaction &tx, bool fLimitFree ,
905
935
bool * pfMissingInputs, bool fRejectInsaneFee )
906
936
{
@@ -929,6 +959,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
929
959
return false ;
930
960
931
961
// Check for conflicts with in-memory transactions
962
+ bool relayableRespend = false ;
932
963
{
933
964
LOCK (pool.cs ); // protect pool.mapNextTx
934
965
for (unsigned int i = 0 ; i < tx.vin .size (); i++)
@@ -937,8 +968,9 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
937
968
// Does tx conflict with a member of the pool, and is it not equivalent to that member?
938
969
if (pool.mapNextTx .count (outpoint) && !tx.IsEquivalentTo (*pool.mapNextTx [outpoint].ptx ))
939
970
{
940
- g_signals.DetectedDoubleSpend (outpoint, tx, false );
941
- return false ;
971
+ relayableRespend = RelayableRespend (outpoint, tx, false , doubleSpendFilter);
972
+ if (!relayableRespend)
973
+ return false ;
942
974
}
943
975
}
944
976
}
@@ -1031,55 +1063,21 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
1031
1063
{
1032
1064
return error (" AcceptToMemoryPool: : ConnectInputs failed %s" , hash.ToString ());
1033
1065
}
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);
1063
1066
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
+ }
1068
1076
}
1069
1077
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 );
1080
1079
1081
- // Share conflict with wallet
1082
- g_signals.SyncTransaction (doubleSpend, NULL );
1080
+ return !relayableRespend;
1083
1081
}
1084
1082
1085
1083
0 commit comments