rippled
Loading...
Searching...
No Matches
PeerFinder_test.cpp
1//------------------------------------------------------------------------------
2/*
3 This file is part of rippled: https://github.com/ripple/rippled
4 Copyright (c) 2012, 2013 Ripple Labs Inc.
5
6 Permission to use, copy, modify, and/or distribute this software for any
7 purpose with or without fee is hereby granted, provided that the above
8 copyright notice and this permission notice appear in all copies.
9
10 THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17*/
18//==============================================================================
19
20#include <test/unit_test/SuiteJournal.h>
21
22#include <xrpld/core/Config.h>
23#include <xrpld/peerfinder/detail/Logic.h>
24
25#include <xrpl/basics/chrono.h>
26#include <xrpl/beast/unit_test/suite.h>
27#include <xrpl/protocol/PublicKey.h>
28#include <xrpl/protocol/SecretKey.h>
29
30namespace ripple {
31namespace PeerFinder {
32
34{
36
37public:
38 PeerFinder_test() : journal_("PeerFinder_test", *this)
39 {
40 }
41
43 {
45 load(load_callback const& cb) override
46 {
47 return 0;
48 }
49
50 void
51 save(std::vector<Entry> const&) override
52 {
53 }
54 };
55
57 {
58 void
60 {
61 }
62
63 void
65 {
66 }
67
68 template <class Handler>
69 void
70 async_connect(beast::IP::Endpoint const& ep, Handler&& handler)
71 {
72 boost::system::error_code ec;
73 handler(ep, ep, ec);
74 }
75 };
76
77 void
79 {
80 auto const seconds = 10000;
81 testcase("backoff 1");
82 TestStore store;
83 TestChecker checker;
84 TestStopwatch clock;
85 Logic<TestChecker> logic(clock, store, checker, journal_);
86 logic.addFixedPeer(
87 "test", beast::IP::Endpoint::from_string("65.0.0.1:5"));
88 {
89 Config c;
90 c.autoConnect = false;
91 c.listeningPort = 1024;
92 logic.config(c);
93 }
94 std::size_t n = 0;
95 for (std::size_t i = 0; i < seconds; ++i)
96 {
97 auto const list = logic.autoconnect();
98 if (!list.empty())
99 {
100 BEAST_EXPECT(list.size() == 1);
101 auto const slot = logic.new_outbound_slot(list.front());
102 BEAST_EXPECT(logic.onConnected(
103 slot, beast::IP::Endpoint::from_string("65.0.0.2:5")));
104 logic.on_closed(slot);
105 ++n;
106 }
107 clock.advance(std::chrono::seconds(1));
108 logic.once_per_second();
109 }
110 // Less than 20 attempts
111 BEAST_EXPECT(n < 20);
112 }
113
114 // with activate
115 void
117 {
118 auto const seconds = 10000;
119 testcase("backoff 2");
120 TestStore store;
121 TestChecker checker;
122 TestStopwatch clock;
123 Logic<TestChecker> logic(clock, store, checker, journal_);
124 logic.addFixedPeer(
125 "test", beast::IP::Endpoint::from_string("65.0.0.1:5"));
126 {
127 Config c;
128 c.autoConnect = false;
129 c.listeningPort = 1024;
130 logic.config(c);
131 }
132
134 std::size_t n = 0;
135
136 for (std::size_t i = 0; i < seconds; ++i)
137 {
138 auto const list = logic.autoconnect();
139 if (!list.empty())
140 {
141 BEAST_EXPECT(list.size() == 1);
142 auto const slot = logic.new_outbound_slot(list.front());
143 if (!BEAST_EXPECT(logic.onConnected(
144 slot, beast::IP::Endpoint::from_string("65.0.0.2:5"))))
145 return;
146 std::string s = ".";
147 if (!BEAST_EXPECT(
148 logic.activate(slot, pk, false) ==
149 PeerFinder::Result::success))
150 return;
151 logic.on_closed(slot);
152 ++n;
153 }
154 clock.advance(std::chrono::seconds(1));
155 logic.once_per_second();
156 }
157 // No more often than once per minute
158 BEAST_EXPECT(n <= (seconds + 59) / 60);
159 }
160
161 void
163 {
164 testcase("duplicate out/in");
165 TestStore store;
166 TestChecker checker;
167 TestStopwatch clock;
168 Logic<TestChecker> logic(clock, store, checker, journal_);
169 logic.addFixedPeer(
170 "test", beast::IP::Endpoint::from_string("65.0.0.1:5"));
171 {
172 Config c;
173 c.autoConnect = false;
174 c.listeningPort = 1024;
175 c.ipLimit = 2;
176 logic.config(c);
177 }
178
179 auto const list = logic.autoconnect();
180 if (BEAST_EXPECT(!list.empty()))
181 {
182 BEAST_EXPECT(list.size() == 1);
183 auto const remote = list.front();
184 auto const slot1 = logic.new_outbound_slot(remote);
185 if (BEAST_EXPECT(slot1 != nullptr))
186 {
187 BEAST_EXPECT(
188 logic.connectedAddresses_.count(remote.address()) == 1);
189 auto const local =
190 beast::IP::Endpoint::from_string("65.0.0.2:1024");
191 auto const slot2 = logic.new_inbound_slot(local, remote);
192 BEAST_EXPECT(
193 logic.connectedAddresses_.count(remote.address()) == 1);
194 if (!BEAST_EXPECT(slot2 == nullptr))
195 logic.on_closed(slot2);
196 logic.on_closed(slot1);
197 }
198 }
199 }
200
201 void
203 {
204 testcase("duplicate in/out");
205 TestStore store;
206 TestChecker checker;
207 TestStopwatch clock;
208 Logic<TestChecker> logic(clock, store, checker, journal_);
209 logic.addFixedPeer(
210 "test", beast::IP::Endpoint::from_string("65.0.0.1:5"));
211 {
212 Config c;
213 c.autoConnect = false;
214 c.listeningPort = 1024;
215 c.ipLimit = 2;
216 logic.config(c);
217 }
218
219 auto const list = logic.autoconnect();
220 if (BEAST_EXPECT(!list.empty()))
221 {
222 BEAST_EXPECT(list.size() == 1);
223 auto const remote = list.front();
224 auto const local =
225 beast::IP::Endpoint::from_string("65.0.0.2:1024");
226 auto const slot1 = logic.new_inbound_slot(local, remote);
227 if (BEAST_EXPECT(slot1 != nullptr))
228 {
229 BEAST_EXPECT(
230 logic.connectedAddresses_.count(remote.address()) == 1);
231 auto const slot2 = logic.new_outbound_slot(remote);
232 BEAST_EXPECT(
233 logic.connectedAddresses_.count(remote.address()) == 1);
234 if (!BEAST_EXPECT(slot2 == nullptr))
235 logic.on_closed(slot2);
236 logic.on_closed(slot1);
237 }
238 }
239 }
240
241 void
243 {
244 // if peers_max is configured then peers_in_max and peers_out_max are
245 // ignored
246 auto run = [&](std::string const& test,
250 std::uint16_t port,
251 std::uint16_t expectOut,
252 std::uint16_t expectIn,
253 std::uint16_t expectIpLimit) {
255
256 testcase(test);
257
258 std::string toLoad = "";
259 int max = 0;
260 if (maxPeers)
261 {
262 max = maxPeers.value();
263 toLoad += "[peers_max]\n" + std::to_string(max) + "\n" +
264 "[peers_in_max]\n" + std::to_string(maxIn.value_or(0)) +
265 "\n" + "[peers_out_max]\n" +
266 std::to_string(maxOut.value_or(0)) + "\n";
267 }
268 else if (maxIn && maxOut)
269 {
270 toLoad += "[peers_in_max]\n" + std::to_string(*maxIn) + "\n" +
271 "[peers_out_max]\n" + std::to_string(*maxOut) + "\n";
272 }
273
274 c.loadFromString(toLoad);
275 BEAST_EXPECT(
276 (c.PEERS_MAX == max && c.PEERS_IN_MAX == 0 &&
277 c.PEERS_OUT_MAX == 0) ||
278 (c.PEERS_IN_MAX == *maxIn && c.PEERS_OUT_MAX == *maxOut));
279
280 Config config = Config::makeConfig(c, port, false, 0);
281
282 Counts counts;
283 counts.onConfig(config);
284 BEAST_EXPECT(
285 counts.out_max() == expectOut &&
286 counts.inboundSlots() == expectIn &&
287 config.ipLimit == expectIpLimit);
288 };
289
290 // if max_peers == 0 => maxPeers = 21,
291 // else if max_peers < 10 => maxPeers = 10 else maxPeers = max_peers
292 // expectOut => if legacy => max(0.15 * maxPeers, 10),
293 // if legacy && !wantIncoming => maxPeers else max_out_peers
294 // expectIn => if legacy && wantIncoming => maxPeers - outPeers
295 // else if !wantIncoming => 0 else max_in_peers
296 // ipLimit => if expectIn <= 21 => 2 else 2 + min(5, expectIn/21)
297 // ipLimit = max(1, min(ipLimit, expectIn/2))
298
299 // legacy test with max_peers
300 run("legacy no config", {}, {}, {}, 4000, 10, 11, 2);
301 run("legacy max_peers 0", 0, 100, 10, 4000, 10, 11, 2);
302 run("legacy max_peers 5", 5, 100, 10, 4000, 10, 0, 1);
303 run("legacy max_peers 20", 20, 100, 10, 4000, 10, 10, 2);
304 run("legacy max_peers 100", 100, 100, 10, 4000, 15, 85, 6);
305 run("legacy max_peers 20, private", 20, 100, 10, 0, 20, 0, 1);
306
307 // test with max_in_peers and max_out_peers
308 run("new in 100/out 10", {}, 100, 10, 4000, 10, 100, 6);
309 run("new in 0/out 10", {}, 0, 10, 4000, 10, 0, 1);
310 run("new in 100/out 10, private", {}, 100, 10, 0, 10, 0, 6);
311 }
312
313 void
315 {
316 testcase("invalid config");
317
318 auto run = [&](std::string const& toLoad) {
320 try
321 {
322 c.loadFromString(toLoad);
323 fail();
324 }
325 catch (...)
326 {
327 pass();
328 }
329 };
330 run(R"rippleConfig(
331[peers_in_max]
332100
333)rippleConfig");
334 run(R"rippleConfig(
335[peers_out_max]
336100
337)rippleConfig");
338 run(R"rippleConfig(
339[peers_in_max]
340100
341[peers_out_max]
3425
343)rippleConfig");
344 run(R"rippleConfig(
345[peers_in_max]
3461001
347[peers_out_max]
34810
349)rippleConfig");
350 run(R"rippleConfig(
351[peers_in_max]
35210
353[peers_out_max]
3541001
355)rippleConfig");
356 }
357
358 void
359 run() override
360 {
365 test_config();
367 }
368};
369
370BEAST_DEFINE_TESTSUITE(PeerFinder, PeerFinder, ripple);
371
372} // namespace PeerFinder
373} // namespace ripple
A version-independent IP address and port combination.
Definition: IPEndpoint.h:39
static Endpoint from_string(std::string const &s)
Definition: IPEndpoint.cpp:59
A testsuite class.
Definition: suite.h:55
void pass()
Record a successful test condition.
Definition: suite.h:511
testcase_t testcase
Memberspace for declaring test cases.
Definition: suite.h:155
void fail(String const &reason, char const *file, int line)
Record a failure.
Definition: suite.h:533
std::size_t PEERS_IN_MAX
Definition: Config.h:181
void loadFromString(std::string const &fileContents)
Load the config from the contents of the string.
Definition: Config.cpp:478
std::size_t PEERS_OUT_MAX
Definition: Config.h:180
std::size_t PEERS_MAX
Definition: Config.h:179
Manages the count of available connections for the various slots.
Definition: Counts.h:34
int inboundSlots() const
Returns the total number of inbound slots.
Definition: Counts.h:166
int out_max() const
Returns the total number of outbound slots.
Definition: Counts.h:104
void onConfig(Config const &config)
Called when the config is set or changed.
Definition: Counts.h:136
The Logic for maintaining the list of Slot addresses.
bool onConnected(SlotImp::ptr const &slot, beast::IP::Endpoint const &local_endpoint)
void on_closed(SlotImp::ptr const &slot)
SlotImp::ptr new_outbound_slot(beast::IP::Endpoint const &remote_endpoint)
Result activate(SlotImp::ptr const &slot, PublicKey const &key, bool reserved)
std::multiset< beast::IP::Address > connectedAddresses_
std::vector< beast::IP::Endpoint > autoconnect()
Create new outbound connection attempts as needed.
SlotImp::ptr new_inbound_slot(beast::IP::Endpoint const &local_endpoint, beast::IP::Endpoint const &remote_endpoint)
void addFixedPeer(std::string const &name, beast::IP::Endpoint const &ep)
void run() override
Runs the suite.
Abstract persistence for PeerFinder data.
Definition: Store.h:29
A public key.
Definition: PublicKey.h:62
T count(T... args)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: algorithm.h:26
std::pair< PublicKey, SecretKey > randomKeyPair(KeyType type)
Create a key pair using secure random numbers.
Definition: SecretKey.cpp:386
PeerFinder configuration settings.
bool autoConnect
true if we want to establish connections automatically
int ipLimit
Limit how many incoming connections we allow per IP.
static Config makeConfig(ripple::Config const &config, std::uint16_t port, bool validationPublicKey, int ipLimit)
Make PeerFinder::Config from configuration parameters.
std::uint16_t listeningPort
The listening port number.
void async_connect(beast::IP::Endpoint const &ep, Handler &&handler)
void save(std::vector< Entry > const &) override
std::size_t load(load_callback const &cb) override
T to_string(T... args)