DoS fix for mapOrphanTransactions
This commit is contained in:
parent
722d9387be
commit
142e604184
3 changed files with 104 additions and 1 deletions
25
src/main.cpp
25
src/main.cpp
|
@ -167,13 +167,14 @@ void static ResendWalletTransactions()
|
||||||
// mapOrphanTransactions
|
// mapOrphanTransactions
|
||||||
//
|
//
|
||||||
|
|
||||||
void static AddOrphanTx(const CDataStream& vMsg)
|
void AddOrphanTx(const CDataStream& vMsg)
|
||||||
{
|
{
|
||||||
CTransaction tx;
|
CTransaction tx;
|
||||||
CDataStream(vMsg) >> tx;
|
CDataStream(vMsg) >> tx;
|
||||||
uint256 hash = tx.GetHash();
|
uint256 hash = tx.GetHash();
|
||||||
if (mapOrphanTransactions.count(hash))
|
if (mapOrphanTransactions.count(hash))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
CDataStream* pvMsg = mapOrphanTransactions[hash] = new CDataStream(vMsg);
|
CDataStream* pvMsg = mapOrphanTransactions[hash] = new CDataStream(vMsg);
|
||||||
BOOST_FOREACH(const CTxIn& txin, tx.vin)
|
BOOST_FOREACH(const CTxIn& txin, tx.vin)
|
||||||
mapOrphanTransactionsByPrev.insert(make_pair(txin.prevout.hash, pvMsg));
|
mapOrphanTransactionsByPrev.insert(make_pair(txin.prevout.hash, pvMsg));
|
||||||
|
@ -201,6 +202,23 @@ void static EraseOrphanTx(uint256 hash)
|
||||||
mapOrphanTransactions.erase(hash);
|
mapOrphanTransactions.erase(hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int LimitOrphanTxSize(int nMaxOrphans)
|
||||||
|
{
|
||||||
|
int nEvicted = 0;
|
||||||
|
while (mapOrphanTransactions.size() > nMaxOrphans)
|
||||||
|
{
|
||||||
|
// Evict a random orphan:
|
||||||
|
std::vector<unsigned char> randbytes(32);
|
||||||
|
RAND_bytes(&randbytes[0], 32);
|
||||||
|
uint256 randomhash(randbytes);
|
||||||
|
map<uint256, CDataStream*>::iterator it = mapOrphanTransactions.lower_bound(randomhash);
|
||||||
|
if (it == mapOrphanTransactions.end())
|
||||||
|
it = mapOrphanTransactions.begin();
|
||||||
|
EraseOrphanTx(it->first);
|
||||||
|
++nEvicted;
|
||||||
|
}
|
||||||
|
return nEvicted;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -2473,6 +2491,11 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
|
||||||
{
|
{
|
||||||
printf("storing orphan tx %s\n", inv.hash.ToString().substr(0,10).c_str());
|
printf("storing orphan tx %s\n", inv.hash.ToString().substr(0,10).c_str());
|
||||||
AddOrphanTx(vMsg);
|
AddOrphanTx(vMsg);
|
||||||
|
|
||||||
|
// DoS prevention: do not allow mapOrphanTransactions to grow unbounded
|
||||||
|
int nEvicted = LimitOrphanTxSize(MAX_ORPHAN_TRANSACTIONS);
|
||||||
|
if (nEvicted > 0)
|
||||||
|
printf("mapOrphan overflow, removed %d tx\n", nEvicted);
|
||||||
}
|
}
|
||||||
if (tx.nDoS) pfrom->Misbehaving(tx.nDoS);
|
if (tx.nDoS) pfrom->Misbehaving(tx.nDoS);
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,6 +33,7 @@ extern const std::string CLIENT_NAME;
|
||||||
static const unsigned int MAX_BLOCK_SIZE = 1000000;
|
static const unsigned int MAX_BLOCK_SIZE = 1000000;
|
||||||
static const unsigned int MAX_BLOCK_SIZE_GEN = MAX_BLOCK_SIZE/2;
|
static const unsigned int MAX_BLOCK_SIZE_GEN = MAX_BLOCK_SIZE/2;
|
||||||
static const int MAX_BLOCK_SIGOPS = MAX_BLOCK_SIZE/50;
|
static const int MAX_BLOCK_SIGOPS = MAX_BLOCK_SIZE/50;
|
||||||
|
static const int MAX_ORPHAN_TRANSACTIONS = MAX_BLOCK_SIZE/100;
|
||||||
static const int64 COIN = 100000000;
|
static const int64 COIN = 100000000;
|
||||||
static const int64 CENT = 1000000;
|
static const int64 CENT = 1000000;
|
||||||
static const int64 MIN_TX_FEE = 50000;
|
static const int64 MIN_TX_FEE = 50000;
|
||||||
|
|
|
@ -12,6 +12,12 @@
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
|
// Tests this internal-to-main.cpp method:
|
||||||
|
extern void AddOrphanTx(const CDataStream& vMsg);
|
||||||
|
extern int LimitOrphanTxSize(int nMaxOrphans);
|
||||||
|
extern std::map<uint256, CDataStream*> mapOrphanTransactions;
|
||||||
|
extern std::multimap<uint256, CDataStream*> mapOrphanTransactionsByPrev;
|
||||||
|
|
||||||
CService ip(uint32_t i)
|
CService ip(uint32_t i)
|
||||||
{
|
{
|
||||||
struct in_addr s;
|
struct in_addr s;
|
||||||
|
@ -123,4 +129,77 @@ BOOST_AUTO_TEST_CASE(DoS_checknbits)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static uint256 RandomHash()
|
||||||
|
{
|
||||||
|
std::vector<unsigned char> randbytes(32);
|
||||||
|
RAND_bytes(&randbytes[0], 32);
|
||||||
|
uint256 randomhash(randbytes);
|
||||||
|
return randomhash;
|
||||||
|
}
|
||||||
|
|
||||||
|
CTransaction RandomOrphan()
|
||||||
|
{
|
||||||
|
std::map<uint256, CDataStream*>::iterator it;
|
||||||
|
it = mapOrphanTransactions.lower_bound(RandomHash());
|
||||||
|
if (it == mapOrphanTransactions.end())
|
||||||
|
it = mapOrphanTransactions.begin();
|
||||||
|
const CDataStream* pvMsg = it->second;
|
||||||
|
CTransaction tx;
|
||||||
|
CDataStream(*pvMsg) >> tx;
|
||||||
|
return tx;
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(DoS_mapOrphans)
|
||||||
|
{
|
||||||
|
CKey key;
|
||||||
|
key.MakeNewKey(true);
|
||||||
|
CBasicKeyStore keystore;
|
||||||
|
keystore.AddKey(key);
|
||||||
|
|
||||||
|
// 50 orphan transactions:
|
||||||
|
for (int i = 0; i < 50; i++)
|
||||||
|
{
|
||||||
|
CTransaction tx;
|
||||||
|
tx.vin.resize(1);
|
||||||
|
tx.vin[0].prevout.n = 0;
|
||||||
|
tx.vin[0].prevout.hash = RandomHash();
|
||||||
|
tx.vin[0].scriptSig << OP_1;
|
||||||
|
tx.vout.resize(1);
|
||||||
|
tx.vout[0].nValue = 1*CENT;
|
||||||
|
tx.vout[0].scriptPubKey.SetBitcoinAddress(key.GetPubKey());
|
||||||
|
|
||||||
|
CDataStream ds;
|
||||||
|
ds << tx;
|
||||||
|
AddOrphanTx(ds);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ... and 50 that depend on other orphans:
|
||||||
|
for (int i = 0; i < 50; i++)
|
||||||
|
{
|
||||||
|
CTransaction txPrev = RandomOrphan();
|
||||||
|
|
||||||
|
CTransaction tx;
|
||||||
|
tx.vin.resize(1);
|
||||||
|
tx.vin[0].prevout.n = 0;
|
||||||
|
tx.vin[0].prevout.hash = txPrev.GetHash();
|
||||||
|
tx.vout.resize(1);
|
||||||
|
tx.vout[0].nValue = 1*CENT;
|
||||||
|
tx.vout[0].scriptPubKey.SetBitcoinAddress(key.GetPubKey());
|
||||||
|
SignSignature(keystore, txPrev, tx, 0);
|
||||||
|
|
||||||
|
CDataStream ds;
|
||||||
|
ds << tx;
|
||||||
|
AddOrphanTx(ds);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test LimitOrphanTxSize() function:
|
||||||
|
LimitOrphanTxSize(40);
|
||||||
|
BOOST_CHECK(mapOrphanTransactions.size() <= 40);
|
||||||
|
LimitOrphanTxSize(10);
|
||||||
|
BOOST_CHECK(mapOrphanTransactions.size() <= 10);
|
||||||
|
LimitOrphanTxSize(0);
|
||||||
|
BOOST_CHECK(mapOrphanTransactions.empty());
|
||||||
|
BOOST_CHECK(mapOrphanTransactionsByPrev.empty());
|
||||||
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_SUITE_END()
|
BOOST_AUTO_TEST_SUITE_END()
|
||||||
|
|
Loading…
Reference in a new issue