Skip to content

Commit

Permalink
DoS fix for mapOrphanTransactions
Browse files Browse the repository at this point in the history
  • Loading branch information
gavinandresen committed Feb 29, 2012
1 parent 722d938 commit 142e604
Show file tree
Hide file tree
Showing 3 changed files with 104 additions and 1 deletion.
25 changes: 24 additions & 1 deletion src/main.cpp
Expand Up @@ -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));
Expand Down Expand Up @@ -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;
}






Expand Down Expand Up @@ -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);
} }
Expand Down
1 change: 1 addition & 0 deletions src/main.h
Expand Up @@ -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;
Expand Down
79 changes: 79 additions & 0 deletions src/test/DoS_tests.cpp
Expand Up @@ -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;
Expand Down Expand Up @@ -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()

0 comments on commit 142e604

Please sign in to comment.