Merge branch 'optimize'

This commit is contained in:
Gavin Andresen
2012-05-22 11:05:46 -04:00
6 changed files with 544 additions and 286 deletions

View File

@@ -1,7 +1,10 @@
//
// Unit tests for denial-of-service detection/prevention code
//
#include <algorithm>
#include <boost/assign/list_of.hpp> // for 'map_list_of()'
#include <boost/date_time/posix_time/posix_time_types.hpp>
#include <boost/test/unit_test.hpp>
#include <boost/foreach.hpp>
@@ -13,10 +16,10 @@
#include <stdint.h>
// Tests this internal-to-main.cpp method:
extern void AddOrphanTx(const CDataStream& vMsg);
extern bool AddOrphanTx(const CDataStream& vMsg);
extern unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans);
extern std::map<uint256, CDataStream*> mapOrphanTransactions;
extern std::multimap<uint256, CDataStream*> mapOrphanTransactionsByPrev;
extern std::map<uint256, std::map<uint256, CDataStream*> > mapOrphanTransactionsByPrev;
CService ip(uint32_t i)
{
@@ -57,7 +60,7 @@ BOOST_AUTO_TEST_CASE(DoS_banscore)
BOOST_CHECK(!CNode::IsBanned(addr1));
dummyNode1.Misbehaving(1);
BOOST_CHECK(CNode::IsBanned(addr1));
mapArgs["-banscore"] = "100";
mapArgs.erase("-banscore");
}
BOOST_AUTO_TEST_CASE(DoS_bantime)
@@ -129,18 +132,10 @@ 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());
it = mapOrphanTransactions.lower_bound(GetRandHash());
if (it == mapOrphanTransactions.end())
it = mapOrphanTransactions.begin();
const CDataStream* pvMsg = it->second;
@@ -162,7 +157,7 @@ BOOST_AUTO_TEST_CASE(DoS_mapOrphans)
CTransaction tx;
tx.vin.resize(1);
tx.vin[0].prevout.n = 0;
tx.vin[0].prevout.hash = RandomHash();
tx.vin[0].prevout.hash = GetRandHash();
tx.vin[0].scriptSig << OP_1;
tx.vout.resize(1);
tx.vout[0].nValue = 1*CENT;
@@ -192,6 +187,32 @@ BOOST_AUTO_TEST_CASE(DoS_mapOrphans)
AddOrphanTx(ds);
}
// This really-big orphan should be ignored:
for (int i = 0; i < 10; i++)
{
CTransaction txPrev = RandomOrphan();
CTransaction tx;
tx.vout.resize(1);
tx.vout[0].nValue = 1*CENT;
tx.vout[0].scriptPubKey.SetBitcoinAddress(key.GetPubKey());
tx.vin.resize(500);
for (int j = 0; j < tx.vin.size(); j++)
{
tx.vin[j].prevout.n = j;
tx.vin[j].prevout.hash = txPrev.GetHash();
}
SignSignature(keystore, txPrev, tx, 0);
// Re-use same signature for other inputs
// (they don't have to be valid for this test)
for (int j = 1; j < tx.vin.size(); j++)
tx.vin[j].scriptSig = tx.vin[0].scriptSig;
CDataStream ds(SER_DISK, CLIENT_VERSION);
ds << tx;
BOOST_CHECK(!AddOrphanTx(ds));
}
// Test LimitOrphanTxSize() function:
LimitOrphanTxSize(40);
BOOST_CHECK(mapOrphanTransactions.size() <= 40);
@@ -202,4 +223,92 @@ BOOST_AUTO_TEST_CASE(DoS_mapOrphans)
BOOST_CHECK(mapOrphanTransactionsByPrev.empty());
}
BOOST_AUTO_TEST_CASE(DoS_checkSig)
{
// Test signature caching code (see key.cpp Verify() methods)
CKey key;
key.MakeNewKey(true);
CBasicKeyStore keystore;
keystore.AddKey(key);
// 100 orphan transactions:
static const int NPREV=100;
CTransaction orphans[NPREV];
for (int i = 0; i < NPREV; i++)
{
CTransaction& tx = orphans[i];
tx.vin.resize(1);
tx.vin[0].prevout.n = 0;
tx.vin[0].prevout.hash = GetRandHash();
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(SER_DISK, CLIENT_VERSION);
ds << tx;
AddOrphanTx(ds);
}
// Create a transaction that depends on orphans:
CTransaction tx;
tx.vout.resize(1);
tx.vout[0].nValue = 1*CENT;
tx.vout[0].scriptPubKey.SetBitcoinAddress(key.GetPubKey());
tx.vin.resize(NPREV);
for (int j = 0; j < tx.vin.size(); j++)
{
tx.vin[j].prevout.n = 0;
tx.vin[j].prevout.hash = orphans[j].GetHash();
}
// Creating signatures primes the cache:
boost::posix_time::ptime mst1 = boost::posix_time::microsec_clock::local_time();
for (int j = 0; j < tx.vin.size(); j++)
BOOST_CHECK(SignSignature(keystore, orphans[j], tx, j));
boost::posix_time::ptime mst2 = boost::posix_time::microsec_clock::local_time();
boost::posix_time::time_duration msdiff = mst2 - mst1;
long nOneValidate = msdiff.total_milliseconds();
if (fDebug) printf("DoS_Checksig sign: %ld\n", nOneValidate);
// ... now validating repeatedly should be quick:
// 2.8GHz machine, -g build: Sign takes ~760ms,
// uncached Verify takes ~250ms, cached Verify takes ~50ms
// (for 100 single-signature inputs)
mst1 = boost::posix_time::microsec_clock::local_time();
for (int i = 0; i < 5; i++)
for (int j = 0; j < tx.vin.size(); j++)
BOOST_CHECK(VerifySignature(orphans[j], tx, j, true, SIGHASH_ALL));
mst2 = boost::posix_time::microsec_clock::local_time();
msdiff = mst2 - mst1;
long nManyValidate = msdiff.total_milliseconds();
if (fDebug) printf("DoS_Checksig five: %ld\n", nManyValidate);
BOOST_CHECK_MESSAGE(nManyValidate < nOneValidate, "Signature cache timing failed");
// Empty a signature, validation should fail:
CScript save = tx.vin[0].scriptSig;
tx.vin[0].scriptSig = CScript();
BOOST_CHECK(!VerifySignature(orphans[0], tx, 0, true, SIGHASH_ALL));
tx.vin[0].scriptSig = save;
// Swap signatures, validation should fail:
std::swap(tx.vin[0].scriptSig, tx.vin[1].scriptSig);
BOOST_CHECK(!VerifySignature(orphans[0], tx, 0, true, SIGHASH_ALL));
BOOST_CHECK(!VerifySignature(orphans[1], tx, 1, true, SIGHASH_ALL));
std::swap(tx.vin[0].scriptSig, tx.vin[1].scriptSig);
// Exercise -maxsigcachesize code:
mapArgs["-maxsigcachesize"] = "10";
// Generate a new, different signature for vin[0] to trigger cache clear:
CScript oldSig = tx.vin[0].scriptSig;
BOOST_CHECK(SignSignature(keystore, orphans[0], tx, 0));
BOOST_CHECK(tx.vin[0].scriptSig != oldSig);
for (int j = 0; j < tx.vin.size(); j++)
BOOST_CHECK(VerifySignature(orphans[j], tx, j, true, SIGHASH_ALL));
mapArgs.erase("-maxsigcachesize");
LimitOrphanTxSize(0);
}
BOOST_AUTO_TEST_SUITE_END()