Permalink
Browse files

Move orphans handling to one class

This is part of the "make it object oriented again" movement.
  • Loading branch information...
zander committed Feb 8, 2017
1 parent 82b7745 commit 2a45e3ceb9461466a937a8812264331a49be5a64
Showing with 400 additions and 188 deletions.
  1. +2 −0 src/Makefile.am
  2. +3 −0 src/init.cpp
  3. +34 −153 src/main.cpp
  4. +99 −35 src/test/DoS_tests.cpp
  5. +205 −0 src/txorphancache.cpp
  6. +57 −0 src/txorphancache.h
View
@@ -161,6 +161,7 @@ BITCOIN_CORE_H = \
torcontrol.h \
txdb.h \
txmempool.h \
+ txorphancache.h \
ui_interface.h \
uint256.h \
undo.h \
@@ -204,6 +205,7 @@ libbitcoin_server_a_SOURCES = \
init.cpp \
dbwrapper.cpp \
main.cpp \
+ txorphancache.cpp \
merkleblock.cpp \
miner.cpp \
net.cpp \
View
@@ -35,6 +35,7 @@
#include "util.h"
#include "utilmoneystr.h"
#include "utilstrencodings.h"
+#include "txorphancache.h"
#include "validationinterface.h"
#ifdef ENABLE_WALLET
#include "wallet/db.h"
@@ -1105,6 +1106,8 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
#endif // ENABLE_WALLET
// ********************************************************* Step 6: network initialization
+ CTxOrphanCache::instance()->setLimit((unsigned int)std::max((int64_t)0, GetArg("-maxorphantx", DEFAULT_MAX_ORPHAN_TRANSACTIONS)));
+
RegisterNodeSignals(GetNodeSignals());
if (mapArgs.count("-onlynet")) {
View
@@ -28,6 +28,7 @@
#include "tinyformat.h"
#include "txdb.h"
#include "txmempool.h"
+#include "txorphancache.h"
#include "ui_interface.h"
#include "undo.h"
#include "util.h"
@@ -82,14 +83,6 @@ CFeeRate minRelayTxFee = CFeeRate(DEFAULT_MIN_RELAY_TX_FEE);
CTxMemPool mempool(::minRelayTxFee);
-struct COrphanTx {
- CTransaction tx;
- NodeId fromPeer;
-};
-map<uint256, COrphanTx> mapOrphanTransactions GUARDED_BY(cs_main);;
-map<uint256, set<uint256> > mapOrphanTransactionsByPrev GUARDED_BY(cs_main);;
-void EraseOrphansFor(NodeId peer) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
-
/**
* Returns true if there are nRequired or more blocks of minVersion or above
* in the last Consensus::Params::nMajorityWindow blocks, starting at pstart and going backwards.
@@ -591,91 +584,6 @@ CBlockIndex* FindForkInGlobalIndex(const CChain& chain, const CBlockLocator& loc
CCoinsViewCache *pcoinsTip = NULL;
CBlockTreeDB *pblocktree = NULL;
-//////////////////////////////////////////////////////////////////////////////
-//
-// mapOrphanTransactions
-//
-
-bool AddOrphanTx(const CTransaction& tx, NodeId peer) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
-{
- uint256 hash = tx.GetHash();
- if (mapOrphanTransactions.count(hash))
- return false;
-
- // Ignore big transactions, to avoid a
- // send-big-orphans memory exhaustion attack. If a peer has a legitimate
- // large transaction with a missing parent then we assume
- // it will rebroadcast it later, after the parent transaction(s)
- // have been mined or received.
- // 10,000 orphans, each of which is at most 5,000 bytes big is
- // at most 500 megabytes of orphans:
- unsigned int sz = tx.GetSerializeSize(SER_NETWORK, CTransaction::CURRENT_VERSION);
- if (sz > 5000)
- {
- LogPrint("mempool", "ignoring large orphan tx (size: %u, hash: %s)\n", sz, hash.ToString());
- return false;
- }
-
- mapOrphanTransactions[hash].tx = tx;
- mapOrphanTransactions[hash].fromPeer = peer;
- BOOST_FOREACH(const CTxIn& txin, tx.vin)
- mapOrphanTransactionsByPrev[txin.prevout.hash].insert(hash);
-
- LogPrint("mempool", "stored orphan tx %s (mapsz %u prevsz %u)\n", hash.ToString(),
- mapOrphanTransactions.size(), mapOrphanTransactionsByPrev.size());
- return true;
-}
-
-void static EraseOrphanTx(uint256 hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
-{
- map<uint256, COrphanTx>::iterator it = mapOrphanTransactions.find(hash);
- if (it == mapOrphanTransactions.end())
- return;
- BOOST_FOREACH(const CTxIn& txin, it->second.tx.vin)
- {
- map<uint256, set<uint256> >::iterator itPrev = mapOrphanTransactionsByPrev.find(txin.prevout.hash);
- if (itPrev == mapOrphanTransactionsByPrev.end())
- continue;
- itPrev->second.erase(hash);
- if (itPrev->second.empty())
- mapOrphanTransactionsByPrev.erase(itPrev);
- }
- mapOrphanTransactions.erase(it);
-}
-
-void EraseOrphansFor(NodeId peer)
-{
- int nErased = 0;
- map<uint256, COrphanTx>::iterator iter = mapOrphanTransactions.begin();
- while (iter != mapOrphanTransactions.end())
- {
- map<uint256, COrphanTx>::iterator maybeErase = iter++; // increment to avoid iterator becoming invalid
- if (maybeErase->second.fromPeer == peer)
- {
- EraseOrphanTx(maybeErase->second.tx.GetHash());
- ++nErased;
- }
- }
- if (nErased > 0) LogPrint("mempool", "Erased %d orphan tx from peer %d\n", nErased, peer);
-}
-
-
-unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
-{
- unsigned int nEvicted = 0;
- while (mapOrphanTransactions.size() > nMaxOrphans)
- {
- // Evict a random orphan:
- uint256 randomhash = GetRandHash();
- map<uint256, COrphanTx>::iterator it = mapOrphanTransactions.lower_bound(randomhash);
- if (it == mapOrphanTransactions.end())
- it = mapOrphanTransactions.begin();
- EraseOrphanTx(it->first);
- ++nEvicted;
- }
- return nEvicted;
-}
-
bool IsFinalTx(const CTransaction &tx, int nBlockHeight, int64_t nBlockTime)
{
if (tx.nLockTime == 0)
@@ -3774,8 +3682,7 @@ void UnloadBlockIndex()
pindexBestInvalid = NULL;
pindexBestHeader = NULL;
mempool.clear();
- mapOrphanTransactions.clear();
- mapOrphanTransactionsByPrev.clear();
+ CTxOrphanCache::clear();
nSyncStarted = 0;
mapBlocksUnlinked.clear();
vinfoBlockFile.clear();
@@ -4210,7 +4117,7 @@ bool static AlreadyHave(const CInv& inv) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
return recentRejects->contains(inv.hash) ||
mempool.exists(inv.hash) ||
- mapOrphanTransactions.count(inv.hash) ||
+ CTxOrphanCache::contains(inv.hash) ||
pcoinsTip->HaveCoins(inv.hash);
}
case MSG_BLOCK:
@@ -4685,10 +4592,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
if (pfrom->mapThinBlocksInFlight.size() < 1 && pfrom->ThinBlockCapable()) { // We can only send one thinblock per peer at a time
pfrom->mapThinBlocksInFlight[inv2.hash] = GetTime();
inv2.type = MSG_XTHINBLOCK;
- std::vector<uint256> vOrphanHashes;
- for (map<uint256, COrphanTx>::iterator mi = mapOrphanTransactions.begin(); mi != mapOrphanTransactions.end(); ++mi)
- vOrphanHashes.push_back((*mi).first);
- CBloomFilter filterMemPool = createSeededBloomFilter(vOrphanHashes);
+ CBloomFilter filterMemPool = createSeededBloomFilter(CTxOrphanCache::instance()->fetchTransactionIds());
ss << inv2;
ss << filterMemPool;
pfrom->PushMessage(NetMsgType::GET_XTHIN, ss);
@@ -4701,10 +4605,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
if (pfrom->mapThinBlocksInFlight.size() < 1 && pfrom->ThinBlockCapable()) { // We can only send one thinblock per peer at a time
pfrom->mapThinBlocksInFlight[inv2.hash] = GetTime();
inv2.type = MSG_XTHINBLOCK;
- std::vector<uint256> vOrphanHashes;
- for (map<uint256, COrphanTx>::iterator mi = mapOrphanTransactions.begin(); mi != mapOrphanTransactions.end(); ++mi)
- vOrphanHashes.push_back((*mi).first);
- CBloomFilter filterMemPool = createSeededBloomFilter(vOrphanHashes);
+ CBloomFilter filterMemPool = createSeededBloomFilter(CTxOrphanCache::instance()->fetchTransactionIds());
ss << inv2;
ss << filterMemPool;
pfrom->PushMessage(NetMsgType::GET_XTHIN, ss);
@@ -4904,24 +4805,17 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
set<NodeId> setMisbehaving;
for (unsigned int i = 0; i < vWorkQueue.size(); i++)
{
- map<uint256, set<uint256> >::iterator itByPrev = mapOrphanTransactionsByPrev.find(vWorkQueue[i]);
- if (itByPrev == mapOrphanTransactionsByPrev.end())
- continue;
- for (set<uint256>::iterator mi = itByPrev->second.begin();
- mi != itByPrev->second.end();
- ++mi)
- {
- const uint256& orphanHash = *mi;
- const CTransaction& orphanTx = mapOrphanTransactions[orphanHash].tx;
- NodeId fromPeer = mapOrphanTransactions[orphanHash].fromPeer;
+ auto orphans = CTxOrphanCache::instance()->fetchTransactionsByPrev(vWorkQueue[i]);
+ for (auto mi = orphans.begin(); mi != orphans.end(); ++mi) {
+ const CTransaction& orphanTx = mi->tx;
+ const uint256 orphanHash = orphanTx.GetHash();
bool fMissingInputs2 = false;
// Use a dummy CValidationState so someone can't setup nodes to counter-DoS based on orphan
// resolution (that is, feeding people an invalid transaction based on LegitTxX in order to get
// anyone relaying LegitTxX banned)
CValidationState stateDummy;
-
- if (setMisbehaving.count(fromPeer))
+ if (setMisbehaving.count(mi->fromPeer))
continue;
if (AcceptToMemoryPool(mempool, stateDummy, orphanTx, true, &fMissingInputs2))
{
@@ -4936,8 +4830,8 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
if (stateDummy.IsInvalid(nDos) && nDos > 0)
{
// Punish peer that gave us an invalid orphan tx
- Misbehaving(fromPeer, nDos);
- setMisbehaving.insert(fromPeer);
+ Misbehaving(mi->fromPeer, nDos);
+ setMisbehaving.insert(mi->fromPeer);
LogPrint("mempool", " invalid orphan tx %s\n", orphanHash.ToString());
}
// Has inputs but not accepted to mempool
@@ -4951,16 +4845,15 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
}
}
- BOOST_FOREACH(uint256 hash, vEraseQueue)
- EraseOrphanTx(hash);
+ CTxOrphanCache::instance()->EraseOrphans(vEraseQueue);
+ CTxOrphanCache::instance()->EraseOrphansByTime();
}
else if (fMissingInputs)
{
- AddOrphanTx(tx, pfrom->GetId());
-
- // DoS prevention: do not allow mapOrphanTransactions to grow unbounded
- unsigned int nMaxOrphanTx = (unsigned int)std::max((int64_t)0, GetArg("-maxorphantx", DEFAULT_MAX_ORPHAN_TRANSACTIONS));
- unsigned int nEvicted = LimitOrphanTxSize(nMaxOrphanTx);
+ CTxOrphanCache *cache = CTxOrphanCache::instance();
+ // DoS prevention: do not allow CTxOrphanCache to grow unbounded
+ cache->AddOrphanTx(tx, pfrom->GetId());
+ std::uint32_t nEvicted = cache->LimitOrphanTxSize();
if (nEvicted > 0)
LogPrint("mempool", "mapOrphan overflow, removed %u tx\n", nEvicted);
} else {
@@ -5157,12 +5050,6 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
collision = true;
mapPartialTxHash[cheapHash] = memPoolHashes[i];
}
- for (map<uint256, COrphanTx>::iterator mi = mapOrphanTransactions.begin(); mi != mapOrphanTransactions.end(); ++mi) {
- uint64_t cheapHash = (*mi).first.GetCheapHash();
- if(mapPartialTxHash.count(cheapHash)) //Check for collisions
- collision = true;
- mapPartialTxHash[cheapHash] = (*mi).first;
- }
for (map<uint256, CTransaction>::iterator mi = mapMissingTx.begin(); mi != mapMissingTx.end(); ++mi) {
uint64_t cheapHash = (*mi).first.GetCheapHash();
// Check for cheap hash collision. Only mark as collision if the full hash is not the same,
@@ -5203,13 +5090,12 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
{
bool inMemPool = mempool.lookup(hash, tx);
bool inMissingTx = mapMissingTx.count(hash) > 0;
- bool inOrphanCache = mapOrphanTransactions.count(hash) > 0;
+ bool inOrphanCache = false;
if ((inMemPool && inMissingTx) || (inOrphanCache && inMissingTx))
unnecessaryCount++;
if (inOrphanCache) {
- tx = mapOrphanTransactions[hash].tx;
setUnVerifiedOrphanTxHash.insert(hash);
}
else if (inMemPool && fXVal)
@@ -5240,8 +5126,6 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
#endif
HandleBlockMessage(pfrom, strCommand, pfrom->thinBlock, inv); // clears the thin block
- BOOST_FOREACH(uint64_t &cheapHash, thinBlock.vTxHashes)
- EraseOrphanTx(mapPartialTxHash[cheapHash]);
}
else if (pfrom->thinBlockWaitingForTxns > 0) {
// This marks the end of the transactions we've received. If we get this and we have NOT been able to
@@ -5304,10 +5188,14 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
);
#endif
- std::vector<CTransaction> vTx = pfrom->thinBlock.vtx;
+ // For correctness sake, assume all came from the orphans cache
+ std::vector<uint256> orphans;
+ orphans.reserve(pfrom->thinBlock.vtx.size());
+ for (unsigned int i = 0; i < pfrom->thinBlock.vtx.size(); i++) {
+ orphans.push_back(pfrom->thinBlock.vtx[i].GetHash());
+ }
HandleBlockMessage(pfrom, strCommand, pfrom->thinBlock, inv);
- for (unsigned int i = 0; i < vTx.size(); i++)
- EraseOrphanTx(vTx[i].GetHash());
+ CTxOrphanCache::instance()->EraseOrphans(orphans);
}
else {
LogPrint("thin", "Failed to retrieve all transactions for block - DOS Banned\n");
@@ -5316,7 +5204,6 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
}
}
-
else if (strCommand == NetMsgType::GET_XBLOCKTX && !fImporting && !fReindex) // return Re-requested xthinblock transactions
{
CXRequestThinBlockTx thinRequestBlockTx;
@@ -5386,8 +5273,12 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
// BUIP010 Extreme Thinblocks: Handle Block Message
HandleBlockMessage(pfrom, strCommand, block, inv);
- for (unsigned int i = 0; i < block.vtx.size(); i++)
- EraseOrphanTx(block.vtx[i].GetHash());
+ std::vector<uint256> orphans;
+ orphans.reserve(block.vtx.size());
+ for (unsigned int i = 0; i < block.vtx.size(); i++) {
+ orphans.push_back(block.vtx[i].GetHash());
+ }
+ CTxOrphanCache::instance()->EraseOrphans(orphans);
}
@@ -6050,10 +5941,7 @@ bool SendMessages(CNode* pto)
// Must download a block from a ThinBlock peer
if (pto->mapThinBlocksInFlight.size() < 1 && pto->ThinBlockCapable()) { // We can only send one thinblock per peer at a time
pto->mapThinBlocksInFlight[pindex->GetBlockHash()] = GetTime();
- std::vector<uint256> vOrphanHashes;
- for (map<uint256, COrphanTx>::iterator mi = mapOrphanTransactions.begin(); mi != mapOrphanTransactions.end(); ++mi)
- vOrphanHashes.push_back((*mi).first);
- CBloomFilter filterMemPool = createSeededBloomFilter(vOrphanHashes);
+ CBloomFilter filterMemPool = createSeededBloomFilter(CTxOrphanCache::instance()->fetchTransactionIds());
ss << CInv(MSG_XTHINBLOCK, pindex->GetBlockHash());
ss << filterMemPool;
pto->PushMessage(NetMsgType::GET_XTHIN, ss);
@@ -6066,10 +5954,7 @@ bool SendMessages(CNode* pto)
// Try to download a thinblock if possible otherwise just download a regular block
if (pto->mapThinBlocksInFlight.size() < 1 && pto->ThinBlockCapable()) { // We can only send one thinblock per peer at a time
pto->mapThinBlocksInFlight[pindex->GetBlockHash()] = GetTime();
- std::vector<uint256> vOrphanHashes;
- for (map<uint256, COrphanTx>::iterator mi = mapOrphanTransactions.begin(); mi != mapOrphanTransactions.end(); ++mi)
- vOrphanHashes.push_back((*mi).first);
- CBloomFilter filterMemPool = createSeededBloomFilter(vOrphanHashes);
+ CBloomFilter filterMemPool = createSeededBloomFilter(CTxOrphanCache::instance()->fetchTransactionIds());
ss << CInv(MSG_XTHINBLOCK, pindex->GetBlockHash());
ss << filterMemPool;
pto->PushMessage(NetMsgType::GET_XTHIN, ss);
@@ -6149,9 +6034,5 @@ class CMainCleanup
for (; it1 != mapBlockIndex.end(); it1++)
delete (*it1).second;
mapBlockIndex.clear();
-
- // orphan transactions
- mapOrphanTransactions.clear();
- mapOrphanTransactionsByPrev.clear();
}
} instance_of_cmaincleanup;
Oops, something went wrong.

0 comments on commit 2a45e3c

Please sign in to comment.