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