cea4577a3b9ab4b40df3570a45549ea2abc3967b
[novacoin.git] / src / test / DoS_tests.cpp
1 //
2 // Unit tests for denial-of-service detection/prevention code
3 //
4 #include <boost/assign/list_of.hpp> // for 'map_list_of()'
5 #include <boost/test/unit_test.hpp>
6 #include <boost/foreach.hpp>
7
8 #include "main.h"
9 #include "wallet.h"
10 #include "net.h"
11 #include "util.h"
12
13 #include <stdint.h>
14
15 // Tests this internal-to-main.cpp method:
16 extern bool AddOrphanTx(const CDataStream& vMsg);
17 extern unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans);
18 extern std::map<uint256, CDataStream*> mapOrphanTransactions;
19 extern std::map<uint256, std::map<uint256, CDataStream*> > mapOrphanTransactionsByPrev;
20
21 CService ip(uint32_t i)
22 {
23     struct in_addr s;
24     s.s_addr = i;
25     return CService(CNetAddr(s), GetDefaultPort());
26 }
27
28 BOOST_AUTO_TEST_SUITE(DoS_tests)
29
30 BOOST_AUTO_TEST_CASE(DoS_banning)
31 {
32     CNode::ClearBanned();
33     CAddress addr1(ip(0xa0b0c001));
34     CNode dummyNode1(INVALID_SOCKET, addr1, true);
35     dummyNode1.Misbehaving(100); // Should get banned
36     BOOST_CHECK(CNode::IsBanned(addr1));
37     BOOST_CHECK(!CNode::IsBanned(ip(0xa0b0c001|0x0000ff00))); // Different ip, not banned
38
39     CAddress addr2(ip(0xa0b0c002));
40     CNode dummyNode2(INVALID_SOCKET, addr2, true);
41     dummyNode2.Misbehaving(50);
42     BOOST_CHECK(!CNode::IsBanned(addr2)); // 2 not banned yet...
43     BOOST_CHECK(CNode::IsBanned(addr1));  // ... but 1 still should be
44     dummyNode2.Misbehaving(50);
45     BOOST_CHECK(CNode::IsBanned(addr2));
46 }    
47
48 BOOST_AUTO_TEST_CASE(DoS_banscore)
49 {
50     CNode::ClearBanned();
51     mapArgs["-banscore"] = "111"; // because 11 is my favorite number
52     CAddress addr1(ip(0xa0b0c001));
53     CNode dummyNode1(INVALID_SOCKET, addr1, true);
54     dummyNode1.Misbehaving(100);
55     BOOST_CHECK(!CNode::IsBanned(addr1));
56     dummyNode1.Misbehaving(10);
57     BOOST_CHECK(!CNode::IsBanned(addr1));
58     dummyNode1.Misbehaving(1);
59     BOOST_CHECK(CNode::IsBanned(addr1));
60     mapArgs["-banscore"] = "100";
61 }
62
63 BOOST_AUTO_TEST_CASE(DoS_bantime)
64 {
65     CNode::ClearBanned();
66     int64 nStartTime = GetTime();
67     SetMockTime(nStartTime); // Overrides future calls to GetTime()
68
69     CAddress addr(ip(0xa0b0c001));
70     CNode dummyNode(INVALID_SOCKET, addr, true);
71
72     dummyNode.Misbehaving(100);
73     BOOST_CHECK(CNode::IsBanned(addr));
74
75     SetMockTime(nStartTime+60*60);
76     BOOST_CHECK(CNode::IsBanned(addr));
77
78     SetMockTime(nStartTime+60*60*24+1);
79     BOOST_CHECK(!CNode::IsBanned(addr));
80 }
81
82 static bool CheckNBits(unsigned int nbits1, int64 time1, unsigned int nbits2, int64 time2)\
83 {
84     if (time1 > time2)
85         return CheckNBits(nbits2, time2, nbits1, time1);
86     int64 deltaTime = time2-time1;
87
88     CBigNum required;
89     required.SetCompact(ComputeMinWork(nbits1, deltaTime));
90     CBigNum have;
91     have.SetCompact(nbits2);
92     return (have <= required);
93 }
94
95 BOOST_AUTO_TEST_CASE(DoS_checknbits)
96 {
97     using namespace boost::assign; // for 'map_list_of()'
98
99     // Timestamps,nBits from the bitcoin blockchain.
100     // These are the block-chain checkpoint blocks
101     typedef std::map<int64, unsigned int> BlockData;
102     BlockData chainData =
103         map_list_of(1239852051,486604799)(1262749024,486594666)
104         (1279305360,469854461)(1280200847,469830746)(1281678674,469809688)
105         (1296207707,453179945)(1302624061,453036989)(1309640330,437004818)
106         (1313172719,436789733);
107
108     // Make sure CheckNBits considers every combination of block-chain-lock-in-points
109     // "sane":
110     BOOST_FOREACH(const BlockData::value_type& i, chainData)
111     {
112         BOOST_FOREACH(const BlockData::value_type& j, chainData)
113         {
114             BOOST_CHECK(CheckNBits(i.second, i.first, j.second, j.first));
115         }
116     }
117
118     // Test a couple of insane combinations:
119     BlockData::value_type firstcheck = *(chainData.begin());
120     BlockData::value_type lastcheck = *(chainData.rbegin());
121
122     // First checkpoint difficulty at or a while after the last checkpoint time should fail when
123     // compared to last checkpoint
124     BOOST_CHECK(!CheckNBits(firstcheck.second, lastcheck.first+60*10, lastcheck.second, lastcheck.first));
125     BOOST_CHECK(!CheckNBits(firstcheck.second, lastcheck.first+60*60*24*14, lastcheck.second, lastcheck.first));
126
127     // ... but OK if enough time passed for difficulty to adjust downward:
128     BOOST_CHECK(CheckNBits(firstcheck.second, lastcheck.first+60*60*24*365*4, lastcheck.second, lastcheck.first));
129     
130 }
131
132 static uint256 RandomHash()
133 {
134     std::vector<unsigned char> randbytes(32);
135     RAND_bytes(&randbytes[0], 32);
136     uint256 randomhash(randbytes);
137     return randomhash;
138 }
139
140 CTransaction RandomOrphan()
141 {
142     std::map<uint256, CDataStream*>::iterator it;
143     it = mapOrphanTransactions.lower_bound(RandomHash());
144     if (it == mapOrphanTransactions.end())
145         it = mapOrphanTransactions.begin();
146     const CDataStream* pvMsg = it->second;
147     CTransaction tx;
148     CDataStream(*pvMsg) >> tx;
149     return tx;
150 }
151
152 BOOST_AUTO_TEST_CASE(DoS_mapOrphans)
153 {
154     CKey key;
155     key.MakeNewKey(true);
156     CBasicKeyStore keystore;
157     keystore.AddKey(key);
158
159     // 50 orphan transactions:
160     for (int i = 0; i < 50; i++)
161     {
162         CTransaction tx;
163         tx.vin.resize(1);
164         tx.vin[0].prevout.n = 0;
165         tx.vin[0].prevout.hash = RandomHash();
166         tx.vin[0].scriptSig << OP_1;
167         tx.vout.resize(1);
168         tx.vout[0].nValue = 1*CENT;
169         tx.vout[0].scriptPubKey.SetBitcoinAddress(key.GetPubKey());
170
171         CDataStream ds(SER_DISK, CLIENT_VERSION);
172         ds << tx;
173         AddOrphanTx(ds);
174     }
175
176     // ... and 50 that depend on other orphans:
177     for (int i = 0; i < 50; i++)
178     {
179         CTransaction txPrev = RandomOrphan();
180
181         CTransaction tx;
182         tx.vin.resize(1);
183         tx.vin[0].prevout.n = 0;
184         tx.vin[0].prevout.hash = txPrev.GetHash();
185         tx.vout.resize(1);
186         tx.vout[0].nValue = 1*CENT;
187         tx.vout[0].scriptPubKey.SetBitcoinAddress(key.GetPubKey());
188         SignSignature(keystore, txPrev, tx, 0);
189
190         CDataStream ds(SER_DISK, CLIENT_VERSION);
191         ds << tx;
192         AddOrphanTx(ds);
193     }
194
195     // This really-big orphan should be ignored:
196     for (int i = 0; i < 10; i++)
197     {
198         CTransaction txPrev = RandomOrphan();
199
200         CTransaction tx;
201         tx.vout.resize(1);
202         tx.vout[0].nValue = 1*CENT;
203         tx.vout[0].scriptPubKey.SetBitcoinAddress(key.GetPubKey());
204         tx.vin.resize(500);
205         for (int j = 0; j < tx.vin.size(); j++)
206         {
207             tx.vin[j].prevout.n = j;
208             tx.vin[j].prevout.hash = txPrev.GetHash();
209         }
210         SignSignature(keystore, txPrev, tx, 0);
211         // Re-use same signature for other inputs
212         // (they don't have to be valid for this test)
213         for (int j = 1; j < tx.vin.size(); j++)
214             tx.vin[j].scriptSig = tx.vin[0].scriptSig;
215
216         CDataStream ds(SER_DISK, CLIENT_VERSION);
217         ds << tx;
218         BOOST_CHECK(!AddOrphanTx(ds));
219     }
220
221     // Test LimitOrphanTxSize() function:
222     LimitOrphanTxSize(40);
223     BOOST_CHECK(mapOrphanTransactions.size() <= 40);
224     LimitOrphanTxSize(10);
225     BOOST_CHECK(mapOrphanTransactions.size() <= 10);
226     LimitOrphanTxSize(0);
227     BOOST_CHECK(mapOrphanTransactions.empty());
228     BOOST_CHECK(mapOrphanTransactionsByPrev.empty());
229 }
230
231 BOOST_AUTO_TEST_SUITE_END()