1 // Copyright (c) 2011-2015 The Bitcoin Core developers
2 // Distributed under the MIT software license, see the accompanying
3 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4
5 // Unit tests for denial-of-service detection/prevention code
6
7 #include "chainparams.h"
8 #include "keystore.h"
9 #include "main.h"
10 #include "net.h"
11 #include "pow.h"
12 #include "script/sign.h"
13 #include "serialize.h"
14 #include "util.h"
15
16 #include "test/test_bitcoin.h"
17
18 #include <stdint.h>
19
20 #include <boost/assign/list_of.hpp> // for 'map_list_of()'
21 #include <boost/date_time/posix_time/posix_time_types.hpp>
22 #include <boost/foreach.hpp>
23 #include <boost/test/unit_test.hpp>
24
25 // Tests this internal-to-main.cpp method:
26 extern bool AddOrphanTx(const CTransaction& tx, NodeId peer);
27 extern void EraseOrphansFor(NodeId peer);
28 extern unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans);
29 struct COrphanTx {
30 CTransaction tx;
31 NodeId fromPeer;
32 };
33 extern std::map<uint256, COrphanTx> mapOrphanTransactions;
34 extern std::map<uint256, std::set<uint256> > mapOrphanTransactionsByPrev;
35
ip(uint32_t i)36 CService ip(uint32_t i)
37 {
38 struct in_addr s;
39 s.s_addr = i;
40 return CService(CNetAddr(s), Params().GetDefaultPort());
41 }
42
BOOST_FIXTURE_TEST_SUITE(DoS_tests,TestingSetup)43 BOOST_FIXTURE_TEST_SUITE(DoS_tests, TestingSetup)
44
45 BOOST_AUTO_TEST_CASE(DoS_banning)
46 {
47 CNode::ClearBanned();
48 CAddress addr1(ip(0xa0b0c001), NODE_NONE);
49 CNode dummyNode1(INVALID_SOCKET, addr1, "", true);
50 dummyNode1.nVersion = 1;
51 Misbehaving(dummyNode1.GetId(), 100); // Should get banned
52 SendMessages(&dummyNode1);
53 BOOST_CHECK(CNode::IsBanned(addr1));
54 BOOST_CHECK(!CNode::IsBanned(ip(0xa0b0c001|0x0000ff00))); // Different IP, not banned
55
56 CAddress addr2(ip(0xa0b0c002), NODE_NONE);
57 CNode dummyNode2(INVALID_SOCKET, addr2, "", true);
58 dummyNode2.nVersion = 1;
59 Misbehaving(dummyNode2.GetId(), 50);
60 SendMessages(&dummyNode2);
61 BOOST_CHECK(!CNode::IsBanned(addr2)); // 2 not banned yet...
62 BOOST_CHECK(CNode::IsBanned(addr1)); // ... but 1 still should be
63 Misbehaving(dummyNode2.GetId(), 50);
64 SendMessages(&dummyNode2);
65 BOOST_CHECK(CNode::IsBanned(addr2));
66 }
67
BOOST_AUTO_TEST_CASE(DoS_banscore)68 BOOST_AUTO_TEST_CASE(DoS_banscore)
69 {
70 CNode::ClearBanned();
71 mapArgs["-banscore"] = "111"; // because 11 is my favorite number
72 CAddress addr1(ip(0xa0b0c001), NODE_NONE);
73 CNode dummyNode1(INVALID_SOCKET, addr1, "", true);
74 dummyNode1.nVersion = 1;
75 Misbehaving(dummyNode1.GetId(), 100);
76 SendMessages(&dummyNode1);
77 BOOST_CHECK(!CNode::IsBanned(addr1));
78 Misbehaving(dummyNode1.GetId(), 10);
79 SendMessages(&dummyNode1);
80 BOOST_CHECK(!CNode::IsBanned(addr1));
81 Misbehaving(dummyNode1.GetId(), 1);
82 SendMessages(&dummyNode1);
83 BOOST_CHECK(CNode::IsBanned(addr1));
84 mapArgs.erase("-banscore");
85 }
86
BOOST_AUTO_TEST_CASE(DoS_bantime)87 BOOST_AUTO_TEST_CASE(DoS_bantime)
88 {
89 CNode::ClearBanned();
90 int64_t nStartTime = GetTime();
91 SetMockTime(nStartTime); // Overrides future calls to GetTime()
92
93 CAddress addr(ip(0xa0b0c001), NODE_NONE);
94 CNode dummyNode(INVALID_SOCKET, addr, "", true);
95 dummyNode.nVersion = 1;
96
97 Misbehaving(dummyNode.GetId(), 100);
98 SendMessages(&dummyNode);
99 BOOST_CHECK(CNode::IsBanned(addr));
100
101 SetMockTime(nStartTime+60*60);
102 BOOST_CHECK(CNode::IsBanned(addr));
103
104 SetMockTime(nStartTime+60*60*24+1);
105 BOOST_CHECK(!CNode::IsBanned(addr));
106 }
107
RandomOrphan()108 CTransaction RandomOrphan()
109 {
110 std::map<uint256, COrphanTx>::iterator it;
111 it = mapOrphanTransactions.lower_bound(GetRandHash());
112 if (it == mapOrphanTransactions.end())
113 it = mapOrphanTransactions.begin();
114 return it->second.tx;
115 }
116
BOOST_AUTO_TEST_CASE(DoS_mapOrphans)117 BOOST_AUTO_TEST_CASE(DoS_mapOrphans)
118 {
119 CKey key;
120 key.MakeNewKey(true);
121 CBasicKeyStore keystore;
122 keystore.AddKey(key);
123
124 // 50 orphan transactions:
125 for (int i = 0; i < 50; i++)
126 {
127 CMutableTransaction tx;
128 tx.vin.resize(1);
129 tx.vin[0].prevout.n = 0;
130 tx.vin[0].prevout.hash = GetRandHash();
131 tx.vin[0].scriptSig << OP_1;
132 tx.vout.resize(1);
133 tx.vout[0].nValue = 1*CENT;
134 tx.vout[0].scriptPubKey = GetScriptForDestination(key.GetPubKey().GetID());
135
136 AddOrphanTx(tx, i);
137 }
138
139 // ... and 50 that depend on other orphans:
140 for (int i = 0; i < 50; i++)
141 {
142 CTransaction txPrev = RandomOrphan();
143
144 CMutableTransaction tx;
145 tx.vin.resize(1);
146 tx.vin[0].prevout.n = 0;
147 tx.vin[0].prevout.hash = txPrev.GetHash();
148 tx.vout.resize(1);
149 tx.vout[0].nValue = 1*CENT;
150 tx.vout[0].scriptPubKey = GetScriptForDestination(key.GetPubKey().GetID());
151 SignSignature(keystore, txPrev, tx, 0, SIGHASH_ALL);
152
153 AddOrphanTx(tx, i);
154 }
155
156 // This really-big orphan should be ignored:
157 for (int i = 0; i < 10; i++)
158 {
159 CTransaction txPrev = RandomOrphan();
160
161 CMutableTransaction tx;
162 tx.vout.resize(1);
163 tx.vout[0].nValue = 1*CENT;
164 tx.vout[0].scriptPubKey = GetScriptForDestination(key.GetPubKey().GetID());
165 tx.vin.resize(2777);
166 for (unsigned int j = 0; j < tx.vin.size(); j++)
167 {
168 tx.vin[j].prevout.n = j;
169 tx.vin[j].prevout.hash = txPrev.GetHash();
170 }
171 SignSignature(keystore, txPrev, tx, 0, SIGHASH_ALL);
172 // Re-use same signature for other inputs
173 // (they don't have to be valid for this test)
174 for (unsigned int j = 1; j < tx.vin.size(); j++)
175 tx.vin[j].scriptSig = tx.vin[0].scriptSig;
176
177 BOOST_CHECK(!AddOrphanTx(tx, i));
178 }
179
180 // Test EraseOrphansFor:
181 for (NodeId i = 0; i < 3; i++)
182 {
183 size_t sizeBefore = mapOrphanTransactions.size();
184 EraseOrphansFor(i);
185 BOOST_CHECK(mapOrphanTransactions.size() < sizeBefore);
186 }
187
188 // Test LimitOrphanTxSize() function:
189 LimitOrphanTxSize(40);
190 BOOST_CHECK(mapOrphanTransactions.size() <= 40);
191 LimitOrphanTxSize(10);
192 BOOST_CHECK(mapOrphanTransactions.size() <= 10);
193 LimitOrphanTxSize(0);
194 BOOST_CHECK(mapOrphanTransactions.empty());
195 BOOST_CHECK(mapOrphanTransactionsByPrev.empty());
196 }
197
198 BOOST_AUTO_TEST_SUITE_END()
199