Line data Source code
1 : // Copyright (c) 2011-2014 The Bitcoin Core developers
2 : // Copyright (c) 2019-2022 The PIVX Core developers
3 : // Distributed under the MIT/X11 software license, see the accompanying
4 : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
5 :
6 : //
7 : // Unit tests for denial-of-service detection/prevention code
8 : //
9 :
10 : #include "test/test_pivx.h"
11 :
12 : #include "arith_uint256.h"
13 : #include "keystore.h"
14 : #include "net_processing.h"
15 : #include "net.h"
16 : #include "pubkey.h"
17 : #include "pow.h"
18 : #include "script/sign.h"
19 : #include "serialize.h"
20 : #include "util/system.h"
21 : #include "validation.h"
22 :
23 : #include <stdint.h>
24 :
25 : #include <boost/test/unit_test.hpp>
26 :
27 : // Tests this internal-to-validation.cpp method:
28 : extern bool AddOrphanTx(const CTransactionRef& tx, NodeId peer);
29 : extern void EraseOrphansFor(NodeId peer);
30 : extern unsigned int LimitOrphanTxSize(unsigned int nMaxOrphans);
31 : struct COrphanTx {
32 : CTransactionRef tx;
33 : NodeId fromPeer;
34 : int64_t nTimeExpire;
35 : };
36 : extern RecursiveMutex g_cs_orphans;
37 : extern std::map<uint256, COrphanTx> mapOrphanTransactions GUARDED_BY(g_cs_orphans);
38 :
39 5 : CService ip(uint32_t i)
40 : {
41 5 : struct in_addr s;
42 5 : s.s_addr = i;
43 10 : return CService(CNetAddr(s), Params().GetDefaultPort());
44 : }
45 :
46 : static NodeId id = 0;
47 :
48 : BOOST_FIXTURE_TEST_SUITE(DoS_tests, TestingSetup)
49 :
50 7 : void misbehave(NodeId id, int value) {
51 7 : LOCK(cs_main);
52 14 : Misbehaving(id, value); // Should get banned
53 7 : }
54 :
55 2 : BOOST_AUTO_TEST_CASE(DoS_banning)
56 : {
57 1 : std::atomic<bool> interruptDummy(false);
58 :
59 1 : connman->ClearBanned();
60 2 : CAddress addr1(ip(0xa0b0c001), NODE_NONE);
61 2 : CNode dummyNode1(id++, NODE_NETWORK, 0, INVALID_SOCKET, addr1, 0, 0, "", true);
62 1 : dummyNode1.SetSendVersion(PROTOCOL_VERSION);
63 1 : peerLogic->InitializeNode(&dummyNode1);
64 1 : dummyNode1.nVersion = 1;
65 1 : dummyNode1.fSuccessfullyConnected = true;
66 1 : misbehave(dummyNode1.GetId(), 100); // Should get banned
67 1 : {
68 2 : LOCK2(cs_main, dummyNode1.cs_sendProcessing);
69 1 : peerLogic->SendMessages(&dummyNode1, interruptDummy);
70 : }
71 2 : BOOST_CHECK(connman->IsBanned(addr1));
72 2 : BOOST_CHECK(!connman->IsBanned(ip(0xa0b0c001|0x0000ff00))); // Different IP, not banned
73 :
74 3 : CAddress addr2(ip(0xa0b0c002), NODE_NONE);
75 2 : CNode dummyNode2(id++, NODE_NETWORK, 0, INVALID_SOCKET, addr2, 1, 1, "", true);
76 1 : dummyNode2.SetSendVersion(PROTOCOL_VERSION);
77 1 : peerLogic->InitializeNode(&dummyNode2);
78 1 : dummyNode2.nVersion = 1;
79 1 : dummyNode2.fSuccessfullyConnected = true;
80 1 : misbehave(dummyNode2.GetId(), 50);
81 1 : {
82 2 : LOCK2(cs_main, dummyNode2.cs_sendProcessing);
83 1 : peerLogic->SendMessages(&dummyNode2, interruptDummy);
84 : }
85 2 : BOOST_CHECK(!connman->IsBanned(addr2)); // 2 not banned yet...
86 2 : BOOST_CHECK(connman->IsBanned(addr1)); // ... but 1 still should be
87 1 : misbehave(dummyNode2.GetId(), 50);
88 1 : {
89 2 : LOCK2(cs_main, dummyNode2.cs_sendProcessing);
90 1 : peerLogic->SendMessages(&dummyNode2, interruptDummy);
91 : }
92 2 : BOOST_CHECK(connman->IsBanned(addr2));
93 1 : }
94 :
95 2 : BOOST_AUTO_TEST_CASE(DoS_banscore)
96 : {
97 1 : std::atomic<bool> interruptDummy(false);
98 :
99 1 : connman->ClearBanned();
100 2 : gArgs.ForceSetArg("-banscore", "111"); // because 11 is my favorite number
101 2 : CAddress addr1(ip(0xa0b0c001), NODE_NONE);
102 2 : CNode dummyNode1(id++, NODE_NETWORK, 0, INVALID_SOCKET, addr1, 3, 1, "", true);
103 1 : dummyNode1.SetSendVersion(PROTOCOL_VERSION);
104 1 : peerLogic->InitializeNode(&dummyNode1);
105 1 : dummyNode1.nVersion = 1;
106 1 : dummyNode1.fSuccessfullyConnected = true;
107 1 : misbehave(dummyNode1.GetId(), 100);
108 1 : {
109 2 : LOCK2(cs_main, dummyNode1.cs_sendProcessing);
110 1 : peerLogic->SendMessages(&dummyNode1, interruptDummy);
111 : }
112 2 : BOOST_CHECK(!connman->IsBanned(addr1));
113 1 : misbehave(dummyNode1.GetId(), 10);
114 1 : {
115 2 : LOCK2(cs_main, dummyNode1.cs_sendProcessing);
116 1 : peerLogic->SendMessages(&dummyNode1, interruptDummy);
117 : }
118 2 : BOOST_CHECK(!connman->IsBanned(addr1));
119 1 : misbehave(dummyNode1.GetId(), 1);
120 1 : {
121 2 : LOCK2(cs_main, dummyNode1.cs_sendProcessing);
122 1 : peerLogic->SendMessages(&dummyNode1, interruptDummy);
123 : }
124 2 : BOOST_CHECK(connman->IsBanned(addr1));
125 2 : gArgs.ForceSetArg("-banscore", std::to_string(DEFAULT_BANSCORE_THRESHOLD));
126 1 : }
127 :
128 2 : BOOST_AUTO_TEST_CASE(DoS_bantime)
129 : {
130 1 : std::atomic<bool> interruptDummy(false);
131 :
132 1 : connman->ClearBanned();
133 1 : int64_t nStartTime = GetTime();
134 1 : SetMockTime(nStartTime); // Overrides future calls to GetTime()
135 :
136 2 : CAddress addr(ip(0xa0b0c001), NODE_NONE);
137 3 : CNode dummyNode(id++, NODE_NETWORK, 0, INVALID_SOCKET, addr, 4, 4, "", true);
138 1 : dummyNode.SetSendVersion(PROTOCOL_VERSION);
139 1 : peerLogic->InitializeNode(&dummyNode);
140 1 : dummyNode.nVersion = 1;
141 1 : dummyNode.fSuccessfullyConnected = true;
142 :
143 1 : misbehave(dummyNode.GetId(), 100);
144 1 : {
145 2 : LOCK2(cs_main, dummyNode.cs_sendProcessing);
146 1 : peerLogic->SendMessages(&dummyNode, interruptDummy);
147 : }
148 2 : BOOST_CHECK(connman->IsBanned(addr));
149 :
150 1 : SetMockTime(nStartTime+60*60);
151 2 : BOOST_CHECK(connman->IsBanned(addr));
152 :
153 1 : SetMockTime(nStartTime+60*60*24+1);
154 2 : BOOST_CHECK(!connman->IsBanned(addr));
155 1 : }
156 :
157 60 : CTransactionRef RandomOrphan()
158 : {
159 60 : std::map<uint256, COrphanTx>::iterator it;
160 120 : LOCK2(cs_main, g_cs_orphans);
161 60 : it = mapOrphanTransactions.lower_bound(InsecureRand256());
162 60 : if (it == mapOrphanTransactions.end())
163 0 : it = mapOrphanTransactions.begin();
164 120 : return it->second.tx;
165 : }
166 :
167 1 : static void MakeNewKeyWithFastRandomContext(CKey& key)
168 : {
169 1 : std::vector<unsigned char> keydata;
170 1 : keydata = g_insecure_rand_ctx.randbytes(32);
171 1 : key.Set(keydata.data(), keydata.data() + keydata.size(), /*fCompressedIn*/ true);
172 1 : assert(key.IsValid());
173 1 : }
174 :
175 2 : BOOST_AUTO_TEST_CASE(DoS_mapOrphans)
176 : {
177 : // This test had non-deterministic coverage due to
178 : // randomly selected seeds.
179 : // This seed is chosen so that all branches of the function
180 : // ecdsa_signature_parse_der_lax are executed during this test.
181 : // Specifically branches that run only when an ECDSA
182 : // signature's R and S values have leading zeros.
183 2 : g_insecure_rand_ctx = FastRandomContext(ArithToUint256(arith_uint256(33)));
184 :
185 1 : CKey key;
186 1 : MakeNewKeyWithFastRandomContext(key);
187 2 : CBasicKeyStore keystore;
188 1 : keystore.AddKey(key);
189 :
190 : // 50 orphan transactions:
191 51 : for (int i = 0; i < 50; i++)
192 : {
193 50 : CMutableTransaction tx;
194 50 : tx.vin.resize(1);
195 50 : tx.vin[0].prevout.n = 0;
196 50 : tx.vin[0].prevout.hash = InsecureRand256();
197 50 : tx.vin[0].scriptSig << OP_1;
198 50 : tx.vout.resize(1);
199 50 : tx.vout[0].nValue = 1*CENT;
200 50 : tx.vout[0].scriptPubKey = GetScriptForDestination(key.GetPubKey().GetID());
201 :
202 100 : AddOrphanTx(MakeTransactionRef(tx), i);
203 : }
204 :
205 : // ... and 50 that depend on other orphans:
206 51 : for (int i = 0; i < 50; i++)
207 : {
208 100 : CTransactionRef txPrev = RandomOrphan();
209 :
210 100 : CMutableTransaction tx;
211 50 : tx.vin.resize(1);
212 50 : tx.vin[0].prevout.n = 0;
213 50 : tx.vin[0].prevout.hash = txPrev->GetHash();
214 50 : tx.vout.resize(1);
215 50 : tx.vout[0].nValue = 1*CENT;
216 50 : tx.vout[0].scriptPubKey = GetScriptForDestination(key.GetPubKey().GetID());
217 50 : SignSignature(keystore, *txPrev, tx, 0, SIGHASH_ALL);
218 :
219 100 : AddOrphanTx(MakeTransactionRef(tx), i);
220 : }
221 :
222 : // This really-big orphan should be ignored:
223 11 : for (int i = 0; i < 10; i++)
224 : {
225 20 : CTransactionRef txPrev = RandomOrphan();
226 :
227 20 : CMutableTransaction tx;
228 10 : tx.vout.resize(1);
229 10 : tx.vout[0].nValue = 1*CENT;
230 10 : tx.vout[0].scriptPubKey = GetScriptForDestination(key.GetPubKey().GetID());
231 10 : tx.vin.resize(2777);
232 27780 : for (unsigned int j = 0; j < tx.vin.size(); j++)
233 : {
234 27770 : tx.vin[j].prevout.n = j;
235 27770 : tx.vin[j].prevout.hash = txPrev->GetHash();
236 : }
237 10 : SignSignature(keystore, *txPrev, tx, 0, SIGHASH_ALL);
238 : // Re-use same signature for other inputs
239 : // (they don't have to be valid for this test)
240 27770 : for (unsigned int j = 1; j < tx.vin.size(); j++)
241 27760 : tx.vin[j].scriptSig = tx.vin[0].scriptSig;
242 :
243 30 : BOOST_CHECK(!AddOrphanTx(MakeTransactionRef(tx), i));
244 : }
245 :
246 3 : LOCK2(cs_main, g_cs_orphans);
247 : // Test EraseOrphansFor:
248 4 : for (NodeId i = 0; i < 3; i++)
249 : {
250 3 : size_t sizeBefore = mapOrphanTransactions.size();
251 3 : EraseOrphansFor(i);
252 6 : BOOST_CHECK(mapOrphanTransactions.size() < sizeBefore);
253 : }
254 :
255 : // Test LimitOrphanTxSize() function:
256 1 : LimitOrphanTxSize(40);
257 2 : BOOST_CHECK(mapOrphanTransactions.size() <= 40);
258 1 : LimitOrphanTxSize(10);
259 2 : BOOST_CHECK(mapOrphanTransactions.size() <= 10);
260 1 : LimitOrphanTxSize(0);
261 2 : BOOST_CHECK(mapOrphanTransactions.empty());
262 1 : }
263 :
264 : BOOST_AUTO_TEST_SUITE_END()
|