7f08dd554337e1b65846cb645b50102da1f6ec6c
[novacoin.git] / src / test / DoS_tests.cpp
1 //
2 // Unit tests for denial-of-service detection/prevention code
3 //
4 #include <algorithm>
5
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>
10
11 #include "main.h"
12 #include "wallet.h"
13 #include "net.h"
14 #include "util.h"
15
16 #include <stdint.h>
17
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;
23
24 CService ip(uint32_t i)
25 {
26     struct in_addr s;
27     s.s_addr = i;
28     return CService(CNetAddr(s), GetDefaultPort());
29 }
30
31 BOOST_AUTO_TEST_SUITE(DoS_tests)
32
33 BOOST_AUTO_TEST_CASE(DoS_banning)
34 {
35     CNode::ClearBanned();
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
41
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));
49 }    
50
51 BOOST_AUTO_TEST_CASE(DoS_banscore)
52 {
53     CNode::ClearBanned();
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");
64 }
65
66 BOOST_AUTO_TEST_CASE(DoS_bantime)
67 {
68     CNode::ClearBanned();
69     int64 nStartTime = GetTime();
70     SetMockTime(nStartTime); // Overrides future calls to GetTime()
71
72     CAddress addr(ip(0xa0b0c001));
73     CNode dummyNode(INVALID_SOCKET, addr, "", true);
74
75     dummyNode.Misbehaving(100);
76     BOOST_CHECK(CNode::IsBanned(addr));
77
78     SetMockTime(nStartTime+60*60);
79     BOOST_CHECK(CNode::IsBanned(addr));
80
81     SetMockTime(nStartTime+60*60*24+1);
82     BOOST_CHECK(!CNode::IsBanned(addr));
83 }
84
85 static bool CheckNBits(unsigned int nbits1, int64 time1, unsigned int nbits2, int64 time2)\
86 {
87     if (time1 > time2)
88         return CheckNBits(nbits2, time2, nbits1, time1);
89     int64 deltaTime = time2-time1;
90
91     CBigNum required;
92     required.SetCompact(ComputeMinWork(nbits1, deltaTime));
93     CBigNum have;
94     have.SetCompact(nbits2);
95     return (have <= required);
96 }
97
98 BOOST_AUTO_TEST_CASE(DoS_checknbits)
99 {
100     using namespace boost::assign; // for 'map_list_of()'
101
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);
110
111     // Make sure CheckNBits considers every combination of block-chain-lock-in-points
112     // "sane":
113     BOOST_FOREACH(const BlockData::value_type& i, chainData)
114     {
115         BOOST_FOREACH(const BlockData::value_type& j, chainData)
116         {
117             BOOST_CHECK(CheckNBits(i.second, i.first, j.second, j.first));
118         }
119     }
120
121     // Test a couple of insane combinations:
122     BlockData::value_type firstcheck = *(chainData.begin());
123     BlockData::value_type lastcheck = *(chainData.rbegin());
124
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));
129
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));
132     
133 }
134
135 CTransaction RandomOrphan()
136 {
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;
142     CTransaction tx;
143     CDataStream(*pvMsg) >> tx;
144     return tx;
145 }
146
147 BOOST_AUTO_TEST_CASE(DoS_mapOrphans)
148 {
149     CKey key;
150     key.MakeNewKey(true);
151     CBasicKeyStore keystore;
152     keystore.AddKey(key);
153
154     // 50 orphan transactions:
155     for (int i = 0; i < 50; i++)
156     {
157         CTransaction tx;
158         tx.vin.resize(1);
159         tx.vin[0].prevout.n = 0;
160         tx.vin[0].prevout.hash = GetRandHash();
161         tx.vin[0].scriptSig << OP_1;
162         tx.vout.resize(1);
163         tx.vout[0].nValue = 1*CENT;
164         tx.vout[0].scriptPubKey.SetDestination(key.GetPubKey().GetID());
165
166         CDataStream ds(SER_DISK, CLIENT_VERSION);
167         ds << tx;
168         AddOrphanTx(ds);
169     }
170
171     // ... and 50 that depend on other orphans:
172     for (int i = 0; i < 50; i++)
173     {
174         CTransaction txPrev = RandomOrphan();
175
176         CTransaction tx;
177         tx.vin.resize(1);
178         tx.vin[0].prevout.n = 0;
179         tx.vin[0].prevout.hash = txPrev.GetHash();
180         tx.vout.resize(1);
181         tx.vout[0].nValue = 1*CENT;
182         tx.vout[0].scriptPubKey.SetDestination(key.GetPubKey().GetID());
183         SignSignature(keystore, txPrev, tx, 0);
184
185         CDataStream ds(SER_DISK, CLIENT_VERSION);
186         ds << tx;
187         AddOrphanTx(ds);
188     }
189
190     // This really-big orphan should be ignored:
191     for (int i = 0; i < 10; i++)
192     {
193         CTransaction txPrev = RandomOrphan();
194
195         CTransaction tx;
196         tx.vout.resize(1);
197         tx.vout[0].nValue = 1*CENT;
198         tx.vout[0].scriptPubKey.SetDestination(key.GetPubKey().GetID());
199         tx.vin.resize(500);
200         for (unsigned int j = 0; j < tx.vin.size(); j++)
201         {
202             tx.vin[j].prevout.n = j;
203             tx.vin[j].prevout.hash = txPrev.GetHash();
204         }
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 (unsigned int j = 1; j < tx.vin.size(); j++)
209             tx.vin[j].scriptSig = tx.vin[0].scriptSig;
210
211         CDataStream ds(SER_DISK, CLIENT_VERSION);
212         ds << tx;
213         BOOST_CHECK(!AddOrphanTx(ds));
214     }
215
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());
224 }
225
226 BOOST_AUTO_TEST_CASE(DoS_checkSig)
227 {
228     // Test signature caching code (see key.cpp Verify() methods)
229
230     CKey key;
231     key.MakeNewKey(true);
232     CBasicKeyStore keystore;
233     keystore.AddKey(key);
234
235     // 100 orphan transactions:
236     static const int NPREV=100;
237     CTransaction orphans[NPREV];
238     for (int i = 0; i < NPREV; i++)
239     {
240         CTransaction& tx = orphans[i];
241         tx.vin.resize(1);
242         tx.vin[0].prevout.n = 0;
243         tx.vin[0].prevout.hash = GetRandHash();
244         tx.vin[0].scriptSig << OP_1;
245         tx.vout.resize(1);
246         tx.vout[0].nValue = 1*CENT;
247         tx.vout[0].scriptPubKey.SetDestination(key.GetPubKey().GetID());
248
249         CDataStream ds(SER_DISK, CLIENT_VERSION);
250         ds << tx;
251         AddOrphanTx(ds);
252     }
253
254     // Create a transaction that depends on orphans:
255     CTransaction tx;
256     tx.vout.resize(1);
257     tx.vout[0].nValue = 1*CENT;
258     tx.vout[0].scriptPubKey.SetDestination(key.GetPubKey().GetID());
259     tx.vin.resize(NPREV);
260     for (unsigned int j = 0; j < tx.vin.size(); j++)
261     {
262         tx.vin[j].prevout.n = 0;
263         tx.vin[j].prevout.hash = orphans[j].GetHash();
264     }
265     // Creating signatures primes the cache:
266     boost::posix_time::ptime mst1 = boost::posix_time::microsec_clock::local_time();
267     for (unsigned 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);
273
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 (unsigned int i = 0; i < 5; i++)
280         for (unsigned 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);
286
287     BOOST_CHECK_MESSAGE(nManyValidate < nOneValidate, "Signature cache timing failed");
288
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;
294
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);
300
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 (unsigned int j = 0; j < tx.vin.size(); j++)
308         BOOST_CHECK(VerifySignature(orphans[j], tx, j, true, SIGHASH_ALL));
309     mapArgs.erase("-maxsigcachesize");
310
311     LimitOrphanTxSize(0);
312 }
313
314 BOOST_AUTO_TEST_SUITE_END()