Skip to content

Commit

Permalink
Move orphans handling to one class
Browse files Browse the repository at this point in the history
This is part of the "make it object oriented again" movement.
  • Loading branch information
zander committed Feb 9, 2017
1 parent 82b7745 commit 2a45e3c
Show file tree
Hide file tree
Showing 6 changed files with 400 additions and 188 deletions.
2 changes: 2 additions & 0 deletions src/Makefile.am
Expand Up @@ -161,6 +161,7 @@ BITCOIN_CORE_H = \
torcontrol.h \
txdb.h \
txmempool.h \
txorphancache.h \
ui_interface.h \
uint256.h \
undo.h \
Expand Down Expand Up @@ -204,6 +205,7 @@ libbitcoin_server_a_SOURCES = \
init.cpp \
dbwrapper.cpp \
main.cpp \
txorphancache.cpp \
merkleblock.cpp \
miner.cpp \
net.cpp \
Expand Down
3 changes: 3 additions & 0 deletions src/init.cpp
Expand Up @@ -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"
Expand Down Expand Up @@ -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")) {
Expand Down
187 changes: 34 additions & 153 deletions src/main.cpp
Expand Up @@ -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"
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -3774,8 +3682,7 @@ void UnloadBlockIndex()
pindexBestInvalid = NULL;
pindexBestHeader = NULL;
mempool.clear();
mapOrphanTransactions.clear();
mapOrphanTransactionsByPrev.clear();
CTxOrphanCache::clear();
nSyncStarted = 0;
mapBlocksUnlinked.clear();
vinfoBlockFile.clear();
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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);
Expand All @@ -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);
Expand Down Expand Up @@ -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))
{
Expand All @@ -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
Expand All @@ -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 {
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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");
Expand All @@ -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;
Expand Down Expand Up @@ -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);
}


Expand Down Expand Up @@ -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);
Expand All @@ -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);
Expand Down Expand Up @@ -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;

0 comments on commit 2a45e3c

Please sign in to comment.