mirror of
https://github.com/kvazar-network/kevacoin.git
synced 2025-02-03 02:34:14 +00:00
Merge pull request #4885
aa3c697 Store fewer orphan tx by default, add -maxorphantx option (Gavin Andresen) c74332c Stricter handling of orphan transactions (Gavin Andresen)
This commit is contained in:
commit
3fa1c81b94
@ -226,6 +226,7 @@ std::string HelpMessage(HelpMessageMode mode)
|
|||||||
strUsage += " -dbcache=<n> " + strprintf(_("Set database cache size in megabytes (%d to %d, default: %d)"), nMinDbCache, nMaxDbCache, nDefaultDbCache) + "\n";
|
strUsage += " -dbcache=<n> " + strprintf(_("Set database cache size in megabytes (%d to %d, default: %d)"), nMinDbCache, nMaxDbCache, nDefaultDbCache) + "\n";
|
||||||
strUsage += " -loadblock=<file> " + _("Imports blocks from external blk000??.dat file") + " " + _("on startup") + "\n";
|
strUsage += " -loadblock=<file> " + _("Imports blocks from external blk000??.dat file") + " " + _("on startup") + "\n";
|
||||||
strUsage += " -maxorphanblocks=<n> " + strprintf(_("Keep at most <n> unconnectable blocks in memory (default: %u)"), DEFAULT_MAX_ORPHAN_BLOCKS) + "\n";
|
strUsage += " -maxorphanblocks=<n> " + strprintf(_("Keep at most <n> unconnectable blocks in memory (default: %u)"), DEFAULT_MAX_ORPHAN_BLOCKS) + "\n";
|
||||||
|
strUsage += " -maxorphantx=<n> " + strprintf(_("Keep at most <n> unconnectable transactions in memory (default: %u)"), DEFAULT_MAX_ORPHAN_TRANSACTIONS) + "\n";
|
||||||
strUsage += " -par=<n> " + strprintf(_("Set the number of script verification threads (%u to %d, 0 = auto, <0 = leave that many cores free, default: %d)"), -(int)boost::thread::hardware_concurrency(), MAX_SCRIPTCHECK_THREADS, DEFAULT_SCRIPTCHECK_THREADS) + "\n";
|
strUsage += " -par=<n> " + strprintf(_("Set the number of script verification threads (%u to %d, 0 = auto, <0 = leave that many cores free, default: %d)"), -(int)boost::thread::hardware_concurrency(), MAX_SCRIPTCHECK_THREADS, DEFAULT_SCRIPTCHECK_THREADS) + "\n";
|
||||||
strUsage += " -pid=<file> " + _("Specify pid file (default: bitcoind.pid)") + "\n";
|
strUsage += " -pid=<file> " + _("Specify pid file (default: bitcoind.pid)") + "\n";
|
||||||
strUsage += " -reindex " + _("Rebuild block chain index from current blk000??.dat files") + " " + _("on startup") + "\n";
|
strUsage += " -reindex " + _("Rebuild block chain index from current blk000??.dat files") + " " + _("on startup") + "\n";
|
||||||
|
68
src/main.cpp
68
src/main.cpp
@ -63,8 +63,13 @@ struct COrphanBlock {
|
|||||||
map<uint256, COrphanBlock*> mapOrphanBlocks;
|
map<uint256, COrphanBlock*> mapOrphanBlocks;
|
||||||
multimap<uint256, COrphanBlock*> mapOrphanBlocksByPrev;
|
multimap<uint256, COrphanBlock*> mapOrphanBlocksByPrev;
|
||||||
|
|
||||||
map<uint256, CTransaction> mapOrphanTransactions;
|
struct COrphanTx {
|
||||||
|
CTransaction tx;
|
||||||
|
NodeId fromPeer;
|
||||||
|
};
|
||||||
|
map<uint256, COrphanTx> mapOrphanTransactions;
|
||||||
map<uint256, set<uint256> > mapOrphanTransactionsByPrev;
|
map<uint256, set<uint256> > mapOrphanTransactionsByPrev;
|
||||||
|
void EraseOrphansFor(NodeId peer);
|
||||||
|
|
||||||
// Constant stuff for coinbase transactions we create:
|
// Constant stuff for coinbase transactions we create:
|
||||||
CScript COINBASE_FLAGS;
|
CScript COINBASE_FLAGS;
|
||||||
@ -264,6 +269,7 @@ void FinalizeNode(NodeId nodeid) {
|
|||||||
mapBlocksInFlight.erase(entry.hash);
|
mapBlocksInFlight.erase(entry.hash);
|
||||||
BOOST_FOREACH(const uint256& hash, state->vBlocksToDownload)
|
BOOST_FOREACH(const uint256& hash, state->vBlocksToDownload)
|
||||||
mapBlocksToDownload.erase(hash);
|
mapBlocksToDownload.erase(hash);
|
||||||
|
EraseOrphansFor(nodeid);
|
||||||
|
|
||||||
mapNodeState.erase(nodeid);
|
mapNodeState.erase(nodeid);
|
||||||
}
|
}
|
||||||
@ -461,7 +467,7 @@ CBlockTreeDB *pblocktree = NULL;
|
|||||||
// mapOrphanTransactions
|
// mapOrphanTransactions
|
||||||
//
|
//
|
||||||
|
|
||||||
bool AddOrphanTx(const CTransaction& tx)
|
bool AddOrphanTx(const CTransaction& tx, NodeId peer)
|
||||||
{
|
{
|
||||||
uint256 hash = tx.GetHash();
|
uint256 hash = tx.GetHash();
|
||||||
if (mapOrphanTransactions.count(hash))
|
if (mapOrphanTransactions.count(hash))
|
||||||
@ -481,21 +487,22 @@ bool AddOrphanTx(const CTransaction& tx)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
mapOrphanTransactions[hash] = tx;
|
mapOrphanTransactions[hash].tx = tx;
|
||||||
|
mapOrphanTransactions[hash].fromPeer = peer;
|
||||||
BOOST_FOREACH(const CTxIn& txin, tx.vin)
|
BOOST_FOREACH(const CTxIn& txin, tx.vin)
|
||||||
mapOrphanTransactionsByPrev[txin.prevout.hash].insert(hash);
|
mapOrphanTransactionsByPrev[txin.prevout.hash].insert(hash);
|
||||||
|
|
||||||
LogPrint("mempool", "stored orphan tx %s (mapsz %u)\n", hash.ToString(),
|
LogPrint("mempool", "stored orphan tx %s (mapsz %u prevsz %u)\n", hash.ToString(),
|
||||||
mapOrphanTransactions.size());
|
mapOrphanTransactions.size(), mapOrphanTransactionsByPrev.size());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void static EraseOrphanTx(uint256 hash)
|
void static EraseOrphanTx(uint256 hash)
|
||||||
{
|
{
|
||||||
map<uint256, CTransaction>::iterator it = mapOrphanTransactions.find(hash);
|
map<uint256, COrphanTx>::iterator it = mapOrphanTransactions.find(hash);
|
||||||
if (it == mapOrphanTransactions.end())
|
if (it == mapOrphanTransactions.end())
|
||||||
return;
|
return;
|
||||||
BOOST_FOREACH(const CTxIn& txin, it->second.vin)
|
BOOST_FOREACH(const CTxIn& txin, it->second.tx.vin)
|
||||||
{
|
{
|
||||||
map<uint256, set<uint256> >::iterator itPrev = mapOrphanTransactionsByPrev.find(txin.prevout.hash);
|
map<uint256, set<uint256> >::iterator itPrev = mapOrphanTransactionsByPrev.find(txin.prevout.hash);
|
||||||
if (itPrev == mapOrphanTransactionsByPrev.end())
|
if (itPrev == mapOrphanTransactionsByPrev.end())
|
||||||
@ -507,6 +514,23 @@ void static EraseOrphanTx(uint256 hash)
|
|||||||
mapOrphanTransactions.erase(it);
|
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)
|
unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans)
|
||||||
{
|
{
|
||||||
unsigned int nEvicted = 0;
|
unsigned int nEvicted = 0;
|
||||||
@ -514,7 +538,7 @@ unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans)
|
|||||||
{
|
{
|
||||||
// Evict a random orphan:
|
// Evict a random orphan:
|
||||||
uint256 randomhash = GetRandHash();
|
uint256 randomhash = GetRandHash();
|
||||||
map<uint256, CTransaction>::iterator it = mapOrphanTransactions.lower_bound(randomhash);
|
map<uint256, COrphanTx>::iterator it = mapOrphanTransactions.lower_bound(randomhash);
|
||||||
if (it == mapOrphanTransactions.end())
|
if (it == mapOrphanTransactions.end())
|
||||||
it = mapOrphanTransactions.begin();
|
it = mapOrphanTransactions.begin();
|
||||||
EraseOrphanTx(it->first);
|
EraseOrphanTx(it->first);
|
||||||
@ -3777,6 +3801,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
|
|||||||
mempool.mapTx.size());
|
mempool.mapTx.size());
|
||||||
|
|
||||||
// Recursively process any orphan transactions that depended on this one
|
// Recursively process any orphan transactions that depended on this one
|
||||||
|
set<NodeId> setMisbehaving;
|
||||||
for (unsigned int i = 0; i < vWorkQueue.size(); i++)
|
for (unsigned int i = 0; i < vWorkQueue.size(); i++)
|
||||||
{
|
{
|
||||||
map<uint256, set<uint256> >::iterator itByPrev = mapOrphanTransactionsByPrev.find(vWorkQueue[i]);
|
map<uint256, set<uint256> >::iterator itByPrev = mapOrphanTransactionsByPrev.find(vWorkQueue[i]);
|
||||||
@ -3787,25 +3812,36 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
|
|||||||
++mi)
|
++mi)
|
||||||
{
|
{
|
||||||
const uint256& orphanHash = *mi;
|
const uint256& orphanHash = *mi;
|
||||||
const CTransaction& orphanTx = mapOrphanTransactions[orphanHash];
|
const CTransaction& orphanTx = mapOrphanTransactions[orphanHash].tx;
|
||||||
|
NodeId fromPeer = mapOrphanTransactions[orphanHash].fromPeer;
|
||||||
bool fMissingInputs2 = false;
|
bool fMissingInputs2 = false;
|
||||||
// Use a dummy CValidationState so someone can't setup nodes to counter-DoS based on orphan
|
// 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
|
// resolution (that is, feeding people an invalid transaction based on LegitTxX in order to get
|
||||||
// anyone relaying LegitTxX banned)
|
// anyone relaying LegitTxX banned)
|
||||||
CValidationState stateDummy;
|
CValidationState stateDummy;
|
||||||
|
|
||||||
|
vEraseQueue.push_back(orphanHash);
|
||||||
|
|
||||||
|
if (setMisbehaving.count(fromPeer))
|
||||||
|
continue;
|
||||||
if (AcceptToMemoryPool(mempool, stateDummy, orphanTx, true, &fMissingInputs2))
|
if (AcceptToMemoryPool(mempool, stateDummy, orphanTx, true, &fMissingInputs2))
|
||||||
{
|
{
|
||||||
LogPrint("mempool", " accepted orphan tx %s\n", orphanHash.ToString());
|
LogPrint("mempool", " accepted orphan tx %s\n", orphanHash.ToString());
|
||||||
RelayTransaction(orphanTx);
|
RelayTransaction(orphanTx);
|
||||||
mapAlreadyAskedFor.erase(CInv(MSG_TX, orphanHash));
|
mapAlreadyAskedFor.erase(CInv(MSG_TX, orphanHash));
|
||||||
vWorkQueue.push_back(orphanHash);
|
vWorkQueue.push_back(orphanHash);
|
||||||
vEraseQueue.push_back(orphanHash);
|
|
||||||
}
|
}
|
||||||
else if (!fMissingInputs2)
|
else if (!fMissingInputs2)
|
||||||
{
|
{
|
||||||
// invalid or too-little-fee orphan
|
int nDos = 0;
|
||||||
vEraseQueue.push_back(orphanHash);
|
if (stateDummy.IsInvalid(nDos) && nDos > 0)
|
||||||
|
{
|
||||||
|
// Punish peer that gave us an invalid orphan tx
|
||||||
|
Misbehaving(fromPeer, nDos);
|
||||||
|
setMisbehaving.insert(fromPeer);
|
||||||
|
LogPrint("mempool", " invalid orphan tx %s\n", orphanHash.ToString());
|
||||||
|
}
|
||||||
|
// too-little-fee orphan
|
||||||
LogPrint("mempool", " removed orphan tx %s\n", orphanHash.ToString());
|
LogPrint("mempool", " removed orphan tx %s\n", orphanHash.ToString());
|
||||||
}
|
}
|
||||||
mempool.check(pcoinsTip);
|
mempool.check(pcoinsTip);
|
||||||
@ -3817,10 +3853,11 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
|
|||||||
}
|
}
|
||||||
else if (fMissingInputs)
|
else if (fMissingInputs)
|
||||||
{
|
{
|
||||||
AddOrphanTx(tx);
|
AddOrphanTx(tx, pfrom->GetId());
|
||||||
|
|
||||||
// DoS prevention: do not allow mapOrphanTransactions to grow unbounded
|
// DoS prevention: do not allow mapOrphanTransactions to grow unbounded
|
||||||
unsigned int nEvicted = LimitOrphanTxSize(MAX_ORPHAN_TRANSACTIONS);
|
unsigned int nMaxOrphanTx = (unsigned int)std::max((int64_t)0, GetArg("-maxorphantx", DEFAULT_MAX_ORPHAN_TRANSACTIONS));
|
||||||
|
unsigned int nEvicted = LimitOrphanTxSize(nMaxOrphanTx);
|
||||||
if (nEvicted > 0)
|
if (nEvicted > 0)
|
||||||
LogPrint("mempool", "mapOrphan overflow, removed %u tx\n", nEvicted);
|
LogPrint("mempool", "mapOrphan overflow, removed %u tx\n", nEvicted);
|
||||||
} else if (pfrom->fWhitelisted) {
|
} else if (pfrom->fWhitelisted) {
|
||||||
@ -4324,7 +4361,9 @@ bool SendMessages(CNode* pto, bool fSendTrickle)
|
|||||||
if (pto->addr.IsLocal())
|
if (pto->addr.IsLocal())
|
||||||
LogPrintf("Warning: not banning local peer %s!\n", pto->addr.ToString());
|
LogPrintf("Warning: not banning local peer %s!\n", pto->addr.ToString());
|
||||||
else
|
else
|
||||||
|
{
|
||||||
CNode::Ban(pto->addr);
|
CNode::Ban(pto->addr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
state.fShouldBan = false;
|
state.fShouldBan = false;
|
||||||
}
|
}
|
||||||
@ -4538,5 +4577,6 @@ public:
|
|||||||
|
|
||||||
// orphan transactions
|
// orphan transactions
|
||||||
mapOrphanTransactions.clear();
|
mapOrphanTransactions.clear();
|
||||||
|
mapOrphanTransactionsByPrev.clear();
|
||||||
}
|
}
|
||||||
} instance_of_cmaincleanup;
|
} instance_of_cmaincleanup;
|
||||||
|
@ -51,8 +51,8 @@ static const unsigned int MAX_BLOCK_SIGOPS = MAX_BLOCK_SIZE/50;
|
|||||||
static const unsigned int MAX_P2SH_SIGOPS = 15;
|
static const unsigned int MAX_P2SH_SIGOPS = 15;
|
||||||
/** The maximum number of sigops we're willing to relay/mine in a single tx */
|
/** The maximum number of sigops we're willing to relay/mine in a single tx */
|
||||||
static const unsigned int MAX_TX_SIGOPS = MAX_BLOCK_SIGOPS/5;
|
static const unsigned int MAX_TX_SIGOPS = MAX_BLOCK_SIGOPS/5;
|
||||||
/** The maximum number of orphan transactions kept in memory */
|
/** Default for -maxorphantx, maximum number of orphan transactions kept in memory */
|
||||||
static const unsigned int MAX_ORPHAN_TRANSACTIONS = MAX_BLOCK_SIZE/100;
|
static const unsigned int DEFAULT_MAX_ORPHAN_TRANSACTIONS = 100;
|
||||||
/** Default for -maxorphanblocks, maximum number of orphan blocks kept in memory */
|
/** Default for -maxorphanblocks, maximum number of orphan blocks kept in memory */
|
||||||
static const unsigned int DEFAULT_MAX_ORPHAN_BLOCKS = 750;
|
static const unsigned int DEFAULT_MAX_ORPHAN_BLOCKS = 750;
|
||||||
/** The maximum size of a blk?????.dat file (since 0.8) */
|
/** The maximum size of a blk?????.dat file (since 0.8) */
|
||||||
|
@ -24,7 +24,8 @@
|
|||||||
#include <boost/test/unit_test.hpp>
|
#include <boost/test/unit_test.hpp>
|
||||||
|
|
||||||
// Tests this internal-to-main.cpp method:
|
// Tests this internal-to-main.cpp method:
|
||||||
extern bool AddOrphanTx(const CTransaction& tx);
|
extern bool AddOrphanTx(const CTransaction& tx, NodeId peer);
|
||||||
|
extern void EraseOrphansFor(NodeId peer);
|
||||||
extern unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans);
|
extern unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans);
|
||||||
extern std::map<uint256, CTransaction> mapOrphanTransactions;
|
extern std::map<uint256, CTransaction> mapOrphanTransactions;
|
||||||
extern std::map<uint256, std::set<uint256> > mapOrphanTransactionsByPrev;
|
extern std::map<uint256, std::set<uint256> > mapOrphanTransactionsByPrev;
|
||||||
@ -174,7 +175,7 @@ BOOST_AUTO_TEST_CASE(DoS_mapOrphans)
|
|||||||
tx.vout[0].nValue = 1*CENT;
|
tx.vout[0].nValue = 1*CENT;
|
||||||
tx.vout[0].scriptPubKey.SetDestination(key.GetPubKey().GetID());
|
tx.vout[0].scriptPubKey.SetDestination(key.GetPubKey().GetID());
|
||||||
|
|
||||||
AddOrphanTx(tx);
|
AddOrphanTx(tx, i);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ... and 50 that depend on other orphans:
|
// ... and 50 that depend on other orphans:
|
||||||
@ -191,7 +192,7 @@ BOOST_AUTO_TEST_CASE(DoS_mapOrphans)
|
|||||||
tx.vout[0].scriptPubKey.SetDestination(key.GetPubKey().GetID());
|
tx.vout[0].scriptPubKey.SetDestination(key.GetPubKey().GetID());
|
||||||
SignSignature(keystore, txPrev, tx, 0);
|
SignSignature(keystore, txPrev, tx, 0);
|
||||||
|
|
||||||
AddOrphanTx(tx);
|
AddOrphanTx(tx, i);
|
||||||
}
|
}
|
||||||
|
|
||||||
// This really-big orphan should be ignored:
|
// This really-big orphan should be ignored:
|
||||||
@ -215,7 +216,15 @@ BOOST_AUTO_TEST_CASE(DoS_mapOrphans)
|
|||||||
for (unsigned int j = 1; j < tx.vin.size(); j++)
|
for (unsigned int j = 1; j < tx.vin.size(); j++)
|
||||||
tx.vin[j].scriptSig = tx.vin[0].scriptSig;
|
tx.vin[j].scriptSig = tx.vin[0].scriptSig;
|
||||||
|
|
||||||
BOOST_CHECK(!AddOrphanTx(tx));
|
BOOST_CHECK(!AddOrphanTx(tx, i));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test EraseOrphansFor:
|
||||||
|
for (NodeId i = 0; i < 3; i++)
|
||||||
|
{
|
||||||
|
size_t sizeBefore = mapOrphanTransactions.size();
|
||||||
|
EraseOrphansFor(i);
|
||||||
|
BOOST_CHECK(mapOrphanTransactions.size() < sizeBefore);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test LimitOrphanTxSize() function:
|
// Test LimitOrphanTxSize() function:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user