Feathercoin  0.5.0
P2P Digital Currency
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros
DoS_tests.cpp
Go to the documentation of this file.
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 CTransaction& tx);
20 extern unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans);
21 extern std::map<uint256, CTransaction> mapOrphanTransactions;
22 extern std::map<uint256, std::set<uint256> > mapOrphanTransactionsByPrev;
23 
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 
34 {
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 {
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 
67 {
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 block chain.
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 
135 {
136  std::map<uint256, CTransaction>::iterator it;
137  it = mapOrphanTransactions.lower_bound(GetRandHash());
138  if (it == mapOrphanTransactions.end())
139  it = mapOrphanTransactions.begin();
140  return it->second;
141 }
142 
143 BOOST_AUTO_TEST_CASE(DoS_mapOrphans)
144 {
145  CKey key;
146  key.MakeNewKey(true);
147  CBasicKeyStore keystore;
148  keystore.AddKey(key);
149 
150  // 50 orphan transactions:
151  for (int i = 0; i < 50; i++)
152  {
153  CTransaction tx;
154  tx.vin.resize(1);
155  tx.vin[0].prevout.n = 0;
156  tx.vin[0].prevout.hash = GetRandHash();
157  tx.vin[0].scriptSig << OP_1;
158  tx.vout.resize(1);
159  tx.vout[0].nValue = 1*CENT;
160  tx.vout[0].scriptPubKey.SetDestination(key.GetPubKey().GetID());
161 
162  AddOrphanTx(tx);
163  }
164 
165  // ... and 50 that depend on other orphans:
166  for (int i = 0; i < 50; i++)
167  {
168  CTransaction txPrev = RandomOrphan();
169 
170  CTransaction tx;
171  tx.vin.resize(1);
172  tx.vin[0].prevout.n = 0;
173  tx.vin[0].prevout.hash = txPrev.GetHash();
174  tx.vout.resize(1);
175  tx.vout[0].nValue = 1*CENT;
176  tx.vout[0].scriptPubKey.SetDestination(key.GetPubKey().GetID());
177  SignSignature(keystore, txPrev, tx, 0);
178 
179  AddOrphanTx(tx);
180  }
181 
182  // This really-big orphan should be ignored:
183  for (int i = 0; i < 10; i++)
184  {
185  CTransaction txPrev = RandomOrphan();
186 
187  CTransaction tx;
188  tx.vout.resize(1);
189  tx.vout[0].nValue = 1*CENT;
190  tx.vout[0].scriptPubKey.SetDestination(key.GetPubKey().GetID());
191  tx.vin.resize(500);
192  for (unsigned int j = 0; j < tx.vin.size(); j++)
193  {
194  tx.vin[j].prevout.n = j;
195  tx.vin[j].prevout.hash = txPrev.GetHash();
196  }
197  SignSignature(keystore, txPrev, tx, 0);
198  // Re-use same signature for other inputs
199  // (they don't have to be valid for this test)
200  for (unsigned int j = 1; j < tx.vin.size(); j++)
201  tx.vin[j].scriptSig = tx.vin[0].scriptSig;
202 
203  BOOST_CHECK(!AddOrphanTx(tx));
204  }
205 
206  // Test LimitOrphanTxSize() function:
207  LimitOrphanTxSize(40);
208  BOOST_CHECK(mapOrphanTransactions.size() <= 40);
209  LimitOrphanTxSize(10);
210  BOOST_CHECK(mapOrphanTransactions.size() <= 10);
212  BOOST_CHECK(mapOrphanTransactions.empty());
213  BOOST_CHECK(mapOrphanTransactionsByPrev.empty());
214 }
215 
216 BOOST_AUTO_TEST_CASE(DoS_checkSig)
217 {
218  // Test signature caching code (see key.cpp Verify() methods)
219 
220  CKey key;
221  key.MakeNewKey(true);
222  CBasicKeyStore keystore;
223  keystore.AddKey(key);
224  unsigned int flags = SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC;
225 
226  // 100 orphan transactions:
227  static const int NPREV=100;
228  CTransaction orphans[NPREV];
229  for (int i = 0; i < NPREV; i++)
230  {
231  CTransaction& tx = orphans[i];
232  tx.vin.resize(1);
233  tx.vin[0].prevout.n = 0;
234  tx.vin[0].prevout.hash = GetRandHash();
235  tx.vin[0].scriptSig << OP_1;
236  tx.vout.resize(1);
237  tx.vout[0].nValue = 1*CENT;
238  tx.vout[0].scriptPubKey.SetDestination(key.GetPubKey().GetID());
239 
240  AddOrphanTx(tx);
241  }
242 
243  // Create a transaction that depends on orphans:
244  CTransaction tx;
245  tx.vout.resize(1);
246  tx.vout[0].nValue = 1*CENT;
247  tx.vout[0].scriptPubKey.SetDestination(key.GetPubKey().GetID());
248  tx.vin.resize(NPREV);
249  for (unsigned int j = 0; j < tx.vin.size(); j++)
250  {
251  tx.vin[j].prevout.n = 0;
252  tx.vin[j].prevout.hash = orphans[j].GetHash();
253  }
254  // Creating signatures primes the cache:
255  boost::posix_time::ptime mst1 = boost::posix_time::microsec_clock::local_time();
256  for (unsigned int j = 0; j < tx.vin.size(); j++)
257  BOOST_CHECK(SignSignature(keystore, orphans[j], tx, j));
258  boost::posix_time::ptime mst2 = boost::posix_time::microsec_clock::local_time();
259  boost::posix_time::time_duration msdiff = mst2 - mst1;
260  long nOneValidate = msdiff.total_milliseconds();
261  if (fDebug) printf("DoS_Checksig sign: %ld\n", nOneValidate);
262 
263  // ... now validating repeatedly should be quick:
264  // 2.8GHz machine, -g build: Sign takes ~760ms,
265  // uncached Verify takes ~250ms, cached Verify takes ~50ms
266  // (for 100 single-signature inputs)
267  mst1 = boost::posix_time::microsec_clock::local_time();
268  for (unsigned int i = 0; i < 5; i++)
269  for (unsigned int j = 0; j < tx.vin.size(); j++)
270  BOOST_CHECK(VerifySignature(CCoins(orphans[j], MEMPOOL_HEIGHT), tx, j, flags, SIGHASH_ALL));
271  mst2 = boost::posix_time::microsec_clock::local_time();
272  msdiff = mst2 - mst1;
273  long nManyValidate = msdiff.total_milliseconds();
274  if (fDebug) printf("DoS_Checksig five: %ld\n", nManyValidate);
275 
276  BOOST_CHECK_MESSAGE(nManyValidate < nOneValidate, "Signature cache timing failed");
277 
278  // Empty a signature, validation should fail:
279  CScript save = tx.vin[0].scriptSig;
280  tx.vin[0].scriptSig = CScript();
281  BOOST_CHECK(!VerifySignature(CCoins(orphans[0], MEMPOOL_HEIGHT), tx, 0, flags, SIGHASH_ALL));
282  tx.vin[0].scriptSig = save;
283 
284  // Swap signatures, validation should fail:
285  std::swap(tx.vin[0].scriptSig, tx.vin[1].scriptSig);
286  BOOST_CHECK(!VerifySignature(CCoins(orphans[0], MEMPOOL_HEIGHT), tx, 0, flags, SIGHASH_ALL));
287  BOOST_CHECK(!VerifySignature(CCoins(orphans[1], MEMPOOL_HEIGHT), tx, 1, flags, SIGHASH_ALL));
288  std::swap(tx.vin[0].scriptSig, tx.vin[1].scriptSig);
289 
290  // Exercise -maxsigcachesize code:
291  mapArgs["-maxsigcachesize"] = "10";
292  // Generate a new, different signature for vin[0] to trigger cache clear:
293  CScript oldSig = tx.vin[0].scriptSig;
294  BOOST_CHECK(SignSignature(keystore, orphans[0], tx, 0));
295  BOOST_CHECK(tx.vin[0].scriptSig != oldSig);
296  for (unsigned int j = 0; j < tx.vin.size(); j++)
297  BOOST_CHECK(VerifySignature(CCoins(orphans[j], MEMPOOL_HEIGHT), tx, j, flags, SIGHASH_ALL));
298  mapArgs.erase("-maxsigcachesize");
299 
301 }
302 
303 BOOST_AUTO_TEST_SUITE_END()
std::map< uint256, std::set< uint256 > > mapOrphanTransactionsByPrev
Definition: main.cpp:73
bool fDebug
Definition: util.cpp:73
static void ClearBanned()
Definition: net.cpp:559
bool AddOrphanTx(const CTransaction &tx)
Definition: main.cpp:297
bool SignSignature(const CKeyStore &keystore, const CScript &fromPubKey, CTransaction &txTo, unsigned int nIn, int nHashType)
Definition: script.cpp:1519
uint256 GetHash() const
Definition: main.h:515
pruned version of CTransaction: only retains metadata and unspent transaction outputs ...
Definition: main.h:896
#define INVALID_SOCKET
Definition: compat.h:45
unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans)
Definition: main.cpp:340
std::map< uint256, CTransaction > mapOrphanTransactions
Definition: main.cpp:72
int64 GetTime()
Definition: util.cpp:1298
bool VerifySignature(const CCoins &txFrom, const CTransaction &txTo, unsigned int nIn, unsigned int flags, int nHashType)
Verify a signature.
Definition: main.cpp:1470
void SetMockTime(int64 nMockTimeIn)
Definition: util.cpp:1305
CBigNum & SetCompact(unsigned int nCompact)
Definition: bignum.h:289
#define printf
Definition: rpcdump.cpp:12
unsigned int uint32_t
Definition: stdint.h:21
CPubKey GetPubKey() const
Definition: key.cpp:312
std::vector< CTxOut > vout
Definition: main.h:485
A combination of a network address (CNetAddr) and a (TCP) port.
Definition: netbase.h:90
std::vector< CTxIn > vin
Definition: main.h:484
Definition: script.h:77
void MakeNewKey(bool fCompressed)
Definition: key.cpp:285
C++ wrapper for BIGNUM (OpenSSL bignum)
Definition: bignum.h:51
A CService with information about it as peer.
Definition: protocol.h:76
virtual bool AddKey(const CKey &key)
Definition: keystore.cpp:18
unsigned int ComputeMinWork(unsigned int nBase, int64 nTime)
Calculate the minimum amount of work a received block needs, without knowing its direct parent...
IP address (IPv6, or IPv4 using mapped IPv6 range (::FFFF:0:0/96))
Definition: netbase.h:34
Serialized script, used inside transaction inputs and outputs.
Definition: script.h:244
static bool IsBanned(CNetAddr ip)
Definition: net.cpp:564
BOOST_AUTO_TEST_CASE(DoS_banning)
Definition: DoS_tests.cpp:33
bool Misbehaving(int howmuch)
Definition: net.cpp:580
An encapsulated private key.
Definition: key.h:172
CTransaction RandomOrphan()
Definition: DoS_tests.cpp:134
The basic transaction that is broadcasted on the network and contained in blocks. ...
Definition: main.h:477
Information about a peer.
Definition: net.h:154
uint256 GetRandHash()
Definition: util.cpp:195
CKeyID GetID() const
Definition: key.h:129
CService ip(uint32_t i)
Definition: DoS_tests.cpp:24
Basic key store, that keeps keys in an address->secret map.
Definition: keystore.h:43
map< string, string > mapArgs
Definition: util.cpp:71
long long int64
Definition: serialize.h:25