-
Notifications
You must be signed in to change notification settings - Fork 37.7k
Keep track of recently rejected transactions with a rolling bloom filter (cont'd) #6498
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
bbe4108
a3d65fe
d2d7ee0
d741371
0847d9c
a8d0407
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -162,6 +162,29 @@ namespace { | |
*/ | ||
map<uint256, NodeId> mapBlockSource; | ||
|
||
/** | ||
* Filter for transactions that were recently rejected by | ||
* AcceptToMemoryPool. These are not rerequested until the chain tip | ||
* changes, at which point the entire filter is reset. Protected by | ||
* cs_main. | ||
* | ||
* Without this filter we'd be re-requesting txs from each of our peers, | ||
* increasing bandwidth consumption considerably. For instance, with 100 | ||
* peers, half of which relay a tx we don't accept, that might be a 50x | ||
* bandwidth increase. A flooding attacker attempting to roll-over the | ||
* filter using minimum-sized, 60byte, transactions might manage to send | ||
* 1000/sec if we have fast peers, so we pick 120,000 to give our peers a | ||
* two minute window to send invs to us. | ||
* | ||
* Decreasing the false positive rate is fairly cheap, so we pick one in a | ||
* million to make it highly unlikely for users to have issues with this | ||
* filter. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Decreasing the false positive rate [for a valid transaction?] is fairly cheap, so we pick one in [a] million [1/1e6, what is this unit?] to make it highly unlikely for users [legitimate transactions?] to have issues with this [passing this?] filter. Could this be clarified a little? edit: if I can get clarifications to the above to cement my own understanding, I'll happily make the change/PR |
||
* | ||
* Memory used: 1.7MB | ||
*/ | ||
boost::scoped_ptr<CRollingBloomFilter> recentRejects; | ||
uint256 hashRecentRejectsChainTip; | ||
|
||
/** Blocks that are in flight, and that are in the queue to be downloaded. Protected by cs_main. */ | ||
struct QueuedBlock { | ||
uint256 hash; | ||
|
@@ -3267,6 +3290,7 @@ void UnloadBlockIndex() | |
setDirtyBlockIndex.clear(); | ||
setDirtyFileInfo.clear(); | ||
mapNodeState.clear(); | ||
recentRejects.reset(NULL); | ||
|
||
BOOST_FOREACH(BlockMap::value_type& entry, mapBlockIndex) { | ||
delete entry.second; | ||
|
@@ -3287,6 +3311,10 @@ bool LoadBlockIndex() | |
bool InitBlockIndex() { | ||
const CChainParams& chainparams = Params(); | ||
LOCK(cs_main); | ||
|
||
// Initialize global variables that cannot be constructed at startup. | ||
recentRejects.reset(new CRollingBloomFilter(120000, 0.000001)); | ||
|
||
// Check whether we're already initialized | ||
if (chainActive.Genesis() != NULL) | ||
return true; | ||
|
@@ -3689,10 +3717,21 @@ bool static AlreadyHave(const CInv& inv) EXCLUSIVE_LOCKS_REQUIRED(cs_main) | |
{ | ||
case MSG_TX: | ||
{ | ||
bool txInMap = false; | ||
txInMap = mempool.exists(inv.hash); | ||
return txInMap || mapOrphanTransactions.count(inv.hash) || | ||
pcoinsTip->HaveCoins(inv.hash); | ||
assert(recentRejects); | ||
if (chainActive.Tip()->GetBlockHash() != hashRecentRejectsChainTip) | ||
{ | ||
// If the chain tip has changed previously rejected transactions | ||
// might be now valid, e.g. due to a nLockTime'd tx becoming valid, | ||
// or a double-spend. Reset the rejects filter and give those | ||
// txs a second chance. | ||
hashRecentRejectsChainTip = chainActive.Tip()->GetBlockHash(); | ||
recentRejects->reset(); | ||
} | ||
|
||
return recentRejects->contains(inv.hash) || | ||
mempool.exists(inv.hash) || | ||
mapOrphanTransactions.count(inv.hash) || | ||
pcoinsTip->HaveCoins(inv.hash); | ||
} | ||
case MSG_BLOCK: | ||
return mapBlockIndex.count(inv.hash); | ||
|
@@ -4292,6 +4331,8 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, | |
// Probably non-standard or insufficient fee/priority | ||
LogPrint("mempool", " removed orphan tx %s\n", orphanHash.ToString()); | ||
vEraseQueue.push_back(orphanHash); | ||
assert(recentRejects); | ||
recentRejects->insert(orphanHash); | ||
} | ||
mempool.check(pcoinsTip); | ||
} | ||
|
@@ -4309,11 +4350,24 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, | |
unsigned int nEvicted = LimitOrphanTxSize(nMaxOrphanTx); | ||
if (nEvicted > 0) | ||
LogPrint("mempool", "mapOrphan overflow, removed %u tx\n", nEvicted); | ||
} else if (pfrom->fWhitelisted) { | ||
// Always relay transactions received from whitelisted peers, even | ||
// if they are already in the mempool (allowing the node to function | ||
// as a gateway for nodes hidden behind it). | ||
RelayTransaction(tx); | ||
} else { | ||
// AcceptToMemoryPool() returned false, possibly because the tx is | ||
// already in the mempool; if the tx isn't in the mempool that | ||
// means it was rejected and we shouldn't ask for it again. | ||
if (!mempool.exists(tx.GetHash())) { | ||
assert(recentRejects); | ||
recentRejects->insert(tx.GetHash()); | ||
} | ||
if (pfrom->fWhitelisted) { | ||
// Always relay transactions received from whitelisted peers, even | ||
// if they were rejected from the mempool, allowing the node to | ||
// function as a gateway for nodes hidden behind it. | ||
// | ||
// FIXME: This includes invalid transactions, which means a | ||
// whitelisted peer could get us banned! We may want to change | ||
// that. | ||
RelayTransaction(tx); | ||
} | ||
} | ||
int nDoS = 0; | ||
if (state.IsInvalid(nDoS)) | ||
|
@@ -4812,7 +4866,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle) | |
{ | ||
// Periodically clear addrKnown to allow refresh broadcasts | ||
if (nLastRebroadcast) | ||
pnode->addrKnown.clear(); | ||
pnode->addrKnown.reset(); | ||
|
||
// Rebroadcast our address | ||
AdvertizeLocal(pnode); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pick
120,000
what?