123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315 |
- //
- // 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>
-
- #include "chainparams.h"
- #include "main.h"
- #include "wallet.h"
- #include "net.h"
- #include "util.h"
-
- #include <stdint.h>
-
- // Tests this internal-to-main.cpp method:
- extern bool AddOrphanTx(const CDataStream& vMsg);
- extern unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans);
- extern std::map<uint256, CDataStream*> mapOrphanTransactions;
- extern std::map<uint256, std::map<uint256, CDataStream*> > mapOrphanTransactionsByPrev;
-
- CService ip(uint32_t i)
- {
- struct in_addr s;
- s.s_addr = i;
- return CService(CNetAddr(s), Params().GetDefaultPort());
- }
-
- BOOST_AUTO_TEST_SUITE(DoS_tests)
-
- BOOST_AUTO_TEST_CASE(DoS_banning)
- {
- CNode::ClearBanned();
- CAddress addr1(ip(0xa0b0c001));
- CNode dummyNode1(INVALID_SOCKET, addr1, "", true);
- dummyNode1.Misbehaving(100); // Should get banned
- BOOST_CHECK(CNode::IsBanned(addr1));
- BOOST_CHECK(!CNode::IsBanned(ip(0xa0b0c001|0x0000ff00))); // Different IP, not banned
-
- CAddress addr2(ip(0xa0b0c002));
- CNode dummyNode2(INVALID_SOCKET, addr2, "", true);
- dummyNode2.Misbehaving(50);
- BOOST_CHECK(!CNode::IsBanned(addr2)); // 2 not banned yet...
- BOOST_CHECK(CNode::IsBanned(addr1)); // ... but 1 still should be
- dummyNode2.Misbehaving(50);
- BOOST_CHECK(CNode::IsBanned(addr2));
- }
-
- BOOST_AUTO_TEST_CASE(DoS_banscore)
- {
- CNode::ClearBanned();
- mapArgs["-banscore"] = "111"; // because 11 is my favorite number
- CAddress addr1(ip(0xa0b0c001));
- CNode dummyNode1(INVALID_SOCKET, addr1, "", true);
- dummyNode1.Misbehaving(100);
- BOOST_CHECK(!CNode::IsBanned(addr1));
- dummyNode1.Misbehaving(10);
- BOOST_CHECK(!CNode::IsBanned(addr1));
- dummyNode1.Misbehaving(1);
- BOOST_CHECK(CNode::IsBanned(addr1));
- mapArgs.erase("-banscore");
- }
-
- BOOST_AUTO_TEST_CASE(DoS_bantime)
- {
- CNode::ClearBanned();
- int64 nStartTime = GetTime();
- SetMockTime(nStartTime); // Overrides future calls to GetTime()
-
- CAddress addr(ip(0xa0b0c001));
- CNode dummyNode(INVALID_SOCKET, addr, "", true);
-
- dummyNode.Misbehaving(100);
- BOOST_CHECK(CNode::IsBanned(addr));
-
- SetMockTime(nStartTime+60*60);
- BOOST_CHECK(CNode::IsBanned(addr));
-
- SetMockTime(nStartTime+60*60*24+1);
- BOOST_CHECK(!CNode::IsBanned(addr));
- }
-
- static bool CheckNBits(unsigned int nbits1, int64 time1, unsigned int nbits2, int64 time2)\
- {
- if (time1 > time2)
- return CheckNBits(nbits2, time2, nbits1, time1);
- int64 deltaTime = time2-time1;
-
- CBigNum required;
- required.SetCompact(ComputeMinWork(nbits1, deltaTime));
- CBigNum have;
- have.SetCompact(nbits2);
- return (have <= required);
- }
-
- BOOST_AUTO_TEST_CASE(DoS_checknbits)
- {
- using namespace boost::assign; // for 'map_list_of()'
-
- // Timestamps,nBits from the bitcoin block chain.
- // These are the block-chain checkpoint blocks
- typedef std::map<int64, unsigned int> BlockData;
- BlockData chainData =
- map_list_of(1239852051,486604799)(1262749024,486594666)
- (1279305360,469854461)(1280200847,469830746)(1281678674,469809688)
- (1296207707,453179945)(1302624061,453036989)(1309640330,437004818)
- (1313172719,436789733);
-
- // Make sure CheckNBits considers every combination of block-chain-lock-in-points
- // "sane":
- BOOST_FOREACH(const BlockData::value_type& i, chainData)
- {
- BOOST_FOREACH(const BlockData::value_type& j, chainData)
- {
- BOOST_CHECK(CheckNBits(i.second, i.first, j.second, j.first));
- }
- }
-
- // Test a couple of insane combinations:
- BlockData::value_type firstcheck = *(chainData.begin());
- BlockData::value_type lastcheck = *(chainData.rbegin());
-
- // First checkpoint difficulty at or a while after the last checkpoint time should fail when
- // compared to last checkpoint
- BOOST_CHECK(!CheckNBits(firstcheck.second, lastcheck.first+60*10, lastcheck.second, lastcheck.first));
- BOOST_CHECK(!CheckNBits(firstcheck.second, lastcheck.first+60*60*24*14, lastcheck.second, lastcheck.first));
-
- // ... but OK if enough time passed for difficulty to adjust downward:
- BOOST_CHECK(CheckNBits(firstcheck.second, lastcheck.first+60*60*24*365*4, lastcheck.second, lastcheck.first));
- }
-
- CTransaction RandomOrphan()
- {
- std::map<uint256, CDataStream*>::iterator it;
- it = mapOrphanTransactions.lower_bound(GetRandHash());
- 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 = GetRandHash();
- tx.vin[0].scriptSig << OP_1;
- tx.vout.resize(1);
- tx.vout[0].nValue = 1*CENT;
- tx.vout[0].scriptPubKey.SetDestination(key.GetPubKey().GetID());
-
- CDataStream ds(SER_DISK, CLIENT_VERSION);
- 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.SetDestination(key.GetPubKey().GetID());
- SignSignature(keystore, txPrev, tx, 0);
-
- CDataStream ds(SER_DISK, CLIENT_VERSION);
- ds << tx;
- 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.SetDestination(key.GetPubKey().GetID());
- tx.vin.resize(500);
- for (unsigned 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 (unsigned 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);
- LimitOrphanTxSize(10);
- BOOST_CHECK(mapOrphanTransactions.size() <= 10);
- LimitOrphanTxSize(0);
- BOOST_CHECK(mapOrphanTransactions.empty());
- 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);
- unsigned int flags = SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC;
-
- // 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.SetDestination(key.GetPubKey().GetID());
-
- 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.SetDestination(key.GetPubKey().GetID());
- tx.vin.resize(NPREV);
- for (unsigned 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 (unsigned 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 (unsigned int i = 0; i < 5; i++)
- for (unsigned int j = 0; j < tx.vin.size(); j++)
- BOOST_CHECK(VerifySignature(CCoins(orphans[j], MEMPOOL_HEIGHT), tx, j, flags, 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(CCoins(orphans[0], MEMPOOL_HEIGHT), tx, 0, flags, 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(CCoins(orphans[0], MEMPOOL_HEIGHT), tx, 0, flags, SIGHASH_ALL));
- BOOST_CHECK(!VerifySignature(CCoins(orphans[1], MEMPOOL_HEIGHT), tx, 1, flags, 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 (unsigned int j = 0; j < tx.vin.size(); j++)
- BOOST_CHECK(VerifySignature(CCoins(orphans[j], MEMPOOL_HEIGHT), tx, j, flags, SIGHASH_ALL));
- mapArgs.erase("-maxsigcachesize");
-
- LimitOrphanTxSize(0);
- }
-
- BOOST_AUTO_TEST_SUITE_END()
|