2 // Unit tests for denial-of-service detection/prevention code
6 #include <boost/assign/list_of.hpp> // for 'map_list_of()'
7 #include <boost/date_time/posix_time/posix_time_types.hpp>
8 #include <boost/test/unit_test.hpp>
9 #include <boost/foreach.hpp>
18 // Tests this internal-to-main.cpp method:
19 extern bool AddOrphanTx(const CDataStream& vMsg);
20 extern unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans);
21 extern std::map<uint256, CDataStream*> mapOrphanTransactions;
22 extern std::map<uint256, std::map<uint256, CDataStream*> > mapOrphanTransactionsByPrev;
24 CService ip(uint32_t i)
28 return CService(CNetAddr(s), GetDefaultPort());
31 BOOST_AUTO_TEST_SUITE(DoS_tests)
33 BOOST_AUTO_TEST_CASE(DoS_banning)
36 CAddress addr1(ip(0xa0b0c001));
37 CNode dummyNode1(INVALID_SOCKET, addr1, true);
38 dummyNode1.Misbehaving(100); // Should get banned
39 BOOST_CHECK(CNode::IsBanned(addr1));
40 BOOST_CHECK(!CNode::IsBanned(ip(0xa0b0c001|0x0000ff00))); // Different ip, not banned
42 CAddress addr2(ip(0xa0b0c002));
43 CNode dummyNode2(INVALID_SOCKET, addr2, true);
44 dummyNode2.Misbehaving(50);
45 BOOST_CHECK(!CNode::IsBanned(addr2)); // 2 not banned yet...
46 BOOST_CHECK(CNode::IsBanned(addr1)); // ... but 1 still should be
47 dummyNode2.Misbehaving(50);
48 BOOST_CHECK(CNode::IsBanned(addr2));
51 BOOST_AUTO_TEST_CASE(DoS_banscore)
54 mapArgs["-banscore"] = "111"; // because 11 is my favorite number
55 CAddress addr1(ip(0xa0b0c001));
56 CNode dummyNode1(INVALID_SOCKET, addr1, true);
57 dummyNode1.Misbehaving(100);
58 BOOST_CHECK(!CNode::IsBanned(addr1));
59 dummyNode1.Misbehaving(10);
60 BOOST_CHECK(!CNode::IsBanned(addr1));
61 dummyNode1.Misbehaving(1);
62 BOOST_CHECK(CNode::IsBanned(addr1));
63 mapArgs.erase("-banscore");
66 BOOST_AUTO_TEST_CASE(DoS_bantime)
69 int64 nStartTime = GetTime();
70 SetMockTime(nStartTime); // Overrides future calls to GetTime()
72 CAddress addr(ip(0xa0b0c001));
73 CNode dummyNode(INVALID_SOCKET, addr, true);
75 dummyNode.Misbehaving(100);
76 BOOST_CHECK(CNode::IsBanned(addr));
78 SetMockTime(nStartTime+60*60);
79 BOOST_CHECK(CNode::IsBanned(addr));
81 SetMockTime(nStartTime+60*60*24+1);
82 BOOST_CHECK(!CNode::IsBanned(addr));
85 static bool CheckNBits(unsigned int nbits1, int64 time1, unsigned int nbits2, int64 time2)\
88 return CheckNBits(nbits2, time2, nbits1, time1);
89 int64 deltaTime = time2-time1;
92 required.SetCompact(ComputeMinWork(nbits1, deltaTime));
94 have.SetCompact(nbits2);
95 return (have <= required);
98 BOOST_AUTO_TEST_CASE(DoS_checknbits)
100 using namespace boost::assign; // for 'map_list_of()'
102 // Timestamps,nBits from the bitcoin blockchain.
103 // These are the block-chain checkpoint blocks
104 typedef std::map<int64, unsigned int> BlockData;
105 BlockData chainData =
106 map_list_of(1239852051,486604799)(1262749024,486594666)
107 (1279305360,469854461)(1280200847,469830746)(1281678674,469809688)
108 (1296207707,453179945)(1302624061,453036989)(1309640330,437004818)
109 (1313172719,436789733);
111 // Make sure CheckNBits considers every combination of block-chain-lock-in-points
113 BOOST_FOREACH(const BlockData::value_type& i, chainData)
115 BOOST_FOREACH(const BlockData::value_type& j, chainData)
117 BOOST_CHECK(CheckNBits(i.second, i.first, j.second, j.first));
121 // Test a couple of insane combinations:
122 BlockData::value_type firstcheck = *(chainData.begin());
123 BlockData::value_type lastcheck = *(chainData.rbegin());
125 // First checkpoint difficulty at or a while after the last checkpoint time should fail when
126 // compared to last checkpoint
127 BOOST_CHECK(!CheckNBits(firstcheck.second, lastcheck.first+60*10, lastcheck.second, lastcheck.first));
128 BOOST_CHECK(!CheckNBits(firstcheck.second, lastcheck.first+60*60*24*14, lastcheck.second, lastcheck.first));
130 // ... but OK if enough time passed for difficulty to adjust downward:
131 BOOST_CHECK(CheckNBits(firstcheck.second, lastcheck.first+60*60*24*365*4, lastcheck.second, lastcheck.first));
135 CTransaction RandomOrphan()
137 std::map<uint256, CDataStream*>::iterator it;
138 it = mapOrphanTransactions.lower_bound(GetRandHash());
139 if (it == mapOrphanTransactions.end())
140 it = mapOrphanTransactions.begin();
141 const CDataStream* pvMsg = it->second;
143 CDataStream(*pvMsg) >> tx;
147 BOOST_AUTO_TEST_CASE(DoS_mapOrphans)
150 key.MakeNewKey(true);
151 CBasicKeyStore keystore;
152 keystore.AddKey(key);
154 // 50 orphan transactions:
155 for (int i = 0; i < 50; i++)
159 tx.vin[0].prevout.n = 0;
160 tx.vin[0].prevout.hash = GetRandHash();
161 tx.vin[0].scriptSig << OP_1;
163 tx.vout[0].nValue = 1*CENT;
164 tx.vout[0].scriptPubKey.SetBitcoinAddress(key.GetPubKey());
166 CDataStream ds(SER_DISK, CLIENT_VERSION);
171 // ... and 50 that depend on other orphans:
172 for (int i = 0; i < 50; i++)
174 CTransaction txPrev = RandomOrphan();
178 tx.vin[0].prevout.n = 0;
179 tx.vin[0].prevout.hash = txPrev.GetHash();
181 tx.vout[0].nValue = 1*CENT;
182 tx.vout[0].scriptPubKey.SetBitcoinAddress(key.GetPubKey());
183 SignSignature(keystore, txPrev, tx, 0);
185 CDataStream ds(SER_DISK, CLIENT_VERSION);
190 // This really-big orphan should be ignored:
191 for (int i = 0; i < 10; i++)
193 CTransaction txPrev = RandomOrphan();
197 tx.vout[0].nValue = 1*CENT;
198 tx.vout[0].scriptPubKey.SetBitcoinAddress(key.GetPubKey());
200 for (int j = 0; j < tx.vin.size(); j++)
202 tx.vin[j].prevout.n = j;
203 tx.vin[j].prevout.hash = txPrev.GetHash();
205 SignSignature(keystore, txPrev, tx, 0);
206 // Re-use same signature for other inputs
207 // (they don't have to be valid for this test)
208 for (int j = 1; j < tx.vin.size(); j++)
209 tx.vin[j].scriptSig = tx.vin[0].scriptSig;
211 CDataStream ds(SER_DISK, CLIENT_VERSION);
213 BOOST_CHECK(!AddOrphanTx(ds));
216 // Test LimitOrphanTxSize() function:
217 LimitOrphanTxSize(40);
218 BOOST_CHECK(mapOrphanTransactions.size() <= 40);
219 LimitOrphanTxSize(10);
220 BOOST_CHECK(mapOrphanTransactions.size() <= 10);
221 LimitOrphanTxSize(0);
222 BOOST_CHECK(mapOrphanTransactions.empty());
223 BOOST_CHECK(mapOrphanTransactionsByPrev.empty());
226 BOOST_AUTO_TEST_CASE(DoS_checkSig)
228 // Test signature caching code (see key.cpp Verify() methods)
231 key.MakeNewKey(true);
232 CBasicKeyStore keystore;
233 keystore.AddKey(key);
235 // 100 orphan transactions:
236 static const int NPREV=100;
237 CTransaction orphans[NPREV];
238 for (int i = 0; i < NPREV; i++)
240 CTransaction& tx = orphans[i];
242 tx.vin[0].prevout.n = 0;
243 tx.vin[0].prevout.hash = GetRandHash();
244 tx.vin[0].scriptSig << OP_1;
246 tx.vout[0].nValue = 1*CENT;
247 tx.vout[0].scriptPubKey.SetBitcoinAddress(key.GetPubKey());
249 CDataStream ds(SER_DISK, CLIENT_VERSION);
254 // Create a transaction that depends on orphans:
257 tx.vout[0].nValue = 1*CENT;
258 tx.vout[0].scriptPubKey.SetBitcoinAddress(key.GetPubKey());
259 tx.vin.resize(NPREV);
260 for (int j = 0; j < tx.vin.size(); j++)
262 tx.vin[j].prevout.n = 0;
263 tx.vin[j].prevout.hash = orphans[j].GetHash();
265 // Creating signatures primes the cache:
266 boost::posix_time::ptime mst1 = boost::posix_time::microsec_clock::local_time();
267 for (int j = 0; j < tx.vin.size(); j++)
268 BOOST_CHECK(SignSignature(keystore, orphans[j], tx, j));
269 boost::posix_time::ptime mst2 = boost::posix_time::microsec_clock::local_time();
270 boost::posix_time::time_duration msdiff = mst2 - mst1;
271 long nOneValidate = msdiff.total_milliseconds();
272 if (fDebug) printf("DoS_Checksig sign: %ld\n", nOneValidate);
274 // ... now validating repeatedly should be quick:
275 // 2.8GHz machine, -g build: Sign takes ~760ms,
276 // uncached Verify takes ~250ms, cached Verify takes ~50ms
277 // (for 100 single-signature inputs)
278 mst1 = boost::posix_time::microsec_clock::local_time();
279 for (int i = 0; i < 5; i++)
280 for (int j = 0; j < tx.vin.size(); j++)
281 BOOST_CHECK(VerifySignature(orphans[j], tx, j, true, SIGHASH_ALL));
282 mst2 = boost::posix_time::microsec_clock::local_time();
283 msdiff = mst2 - mst1;
284 long nManyValidate = msdiff.total_milliseconds();
285 if (fDebug) printf("DoS_Checksig five: %ld\n", nManyValidate);
287 BOOST_CHECK_MESSAGE(nManyValidate < nOneValidate, "Signature cache timing failed");
289 // Empty a signature, validation should fail:
290 CScript save = tx.vin[0].scriptSig;
291 tx.vin[0].scriptSig = CScript();
292 BOOST_CHECK(!VerifySignature(orphans[0], tx, 0, true, SIGHASH_ALL));
293 tx.vin[0].scriptSig = save;
295 // Swap signatures, validation should fail:
296 std::swap(tx.vin[0].scriptSig, tx.vin[1].scriptSig);
297 BOOST_CHECK(!VerifySignature(orphans[0], tx, 0, true, SIGHASH_ALL));
298 BOOST_CHECK(!VerifySignature(orphans[1], tx, 1, true, SIGHASH_ALL));
299 std::swap(tx.vin[0].scriptSig, tx.vin[1].scriptSig);
301 // Exercise -maxsigcachesize code:
302 mapArgs["-maxsigcachesize"] = "10";
303 // Generate a new, different signature for vin[0] to trigger cache clear:
304 CScript oldSig = tx.vin[0].scriptSig;
305 BOOST_CHECK(SignSignature(keystore, orphans[0], tx, 0));
306 BOOST_CHECK(tx.vin[0].scriptSig != oldSig);
307 for (int j = 0; j < tx.vin.size(); j++)
308 BOOST_CHECK(VerifySignature(orphans[j], tx, j, true, SIGHASH_ALL));
309 mapArgs.erase("-maxsigcachesize");
311 LimitOrphanTxSize(0);
314 BOOST_AUTO_TEST_SUITE_END()