rippled
Loading...
Searching...
No Matches
ValidatorRPC_test.cpp
1#include <test/jtx.h>
2#include <test/jtx/TrustedPublisherServer.h>
3
4#include <xrpld/app/main/BasicApp.h>
5#include <xrpld/app/misc/ValidatorSite.h>
6#include <xrpld/core/ConfigSections.h>
7
8#include <xrpl/beast/unit_test.h>
9#include <xrpl/json/json_value.h>
10#include <xrpl/protocol/jss.h>
11
12#include <set>
13
14namespace xrpl {
15
16namespace test {
17
19{
21
22public:
23 void
25 {
26 using namespace test::jtx;
27
28 for (bool const isAdmin : {true, false})
29 {
30 for (std::string const cmd : {"validators", "validator_list_sites"})
31 {
32 Env env{*this, isAdmin ? envconfig() : envconfig(no_admin)};
33 env.set_retries(isAdmin ? 5 : 0);
34 auto const jrr = env.rpc(cmd)[jss::result];
35 if (isAdmin)
36 {
37 BEAST_EXPECT(!jrr.isMember(jss::error));
38 BEAST_EXPECT(jrr[jss::status] == "success");
39 }
40 else
41 {
42 // The current HTTP/S ServerHandler returns an HTTP 403
43 // error code here rather than a noPermission JSON error.
44 // The JSONRPCClient just eats that error and returns null
45 // result.
46 BEAST_EXPECT(jrr.isNull());
47 }
48 }
49
50 {
51 Env env{*this, isAdmin ? envconfig() : envconfig(no_admin)};
52 auto const jrr = env.rpc("server_info")[jss::result];
53 BEAST_EXPECT(jrr[jss::status] == "success");
54 BEAST_EXPECT(jrr[jss::info].isMember(jss::validator_list) == isAdmin);
55 }
56
57 {
58 Env env{*this, isAdmin ? envconfig() : envconfig(no_admin)};
59 auto const jrr = env.rpc("server_state")[jss::result];
60 BEAST_EXPECT(jrr[jss::status] == "success");
61 BEAST_EXPECT(jrr[jss::state].isMember(jss::validator_list_expires) == isAdmin);
62 }
63 }
64 }
65
66 void
68 {
69 using namespace test::jtx;
70
71 std::set<std::string> const keys = {
72 "n949f75evCHwgyP4fPVgaHqNHxUVN15PsJEZ3B3HnXPcPjcZAoy7",
73 "n9MD5h24qrQqiyBC8aeqqCWvpiBiYQ3jxSr91uiDvmrkyHRdYLUj"};
74 Env env{
75 *this,
77 for (auto const& key : keys)
78 cfg->section(SECTION_VALIDATORS).append(key);
79 return cfg;
80 }),
81 };
82
83 // Server info reports maximum expiration since not dynamic
84 {
85 auto const jrr = env.rpc("server_info")[jss::result];
86 BEAST_EXPECT(jrr[jss::info][jss::validator_list][jss::expiration] == "never");
87 }
88 {
89 auto const jrr = env.rpc("server_state")[jss::result];
90 BEAST_EXPECT(
91 jrr[jss::state][jss::validator_list_expires].asUInt() ==
92 NetClock::time_point::max().time_since_epoch().count());
93 }
94 // All our keys are in the response
95 {
96 auto const jrr = env.rpc("validators")[jss::result];
97 BEAST_EXPECT(jrr[jss::validator_list][jss::expiration] == "never");
98 BEAST_EXPECT(jrr[jss::validation_quorum].asUInt() == keys.size());
99 BEAST_EXPECT(jrr[jss::trusted_validator_keys].size() == keys.size());
100 BEAST_EXPECT(jrr[jss::publisher_lists].size() == 0);
101 BEAST_EXPECT(jrr[jss::local_static_keys].size() == keys.size());
102 for (auto const& jKey : jrr[jss::local_static_keys])
103 {
104 BEAST_EXPECT(keys.count(jKey.asString()) == 1);
105 }
106 BEAST_EXPECT(jrr[jss::signing_keys].size() == 0);
107 }
108 // No validator sites configured
109 {
110 auto const jrr = env.rpc("validator_list_sites")[jss::result];
111 BEAST_EXPECT(jrr[jss::validator_sites].size() == 0);
112 }
113 // Negative UNL empty
114 {
115 auto const jrr = env.rpc("validators")[jss::result];
116 BEAST_EXPECT(jrr[jss::NegativeUNL].isNull());
117 }
118 // Negative UNL update
119 {
120 hash_set<PublicKey> disabledKeys;
121 auto k1 = randomKeyPair(KeyType::ed25519).first;
122 auto k2 = randomKeyPair(KeyType::ed25519).first;
123 disabledKeys.insert(k1);
124 disabledKeys.insert(k2);
125 env.app().getValidators().setNegativeUNL(disabledKeys);
126
127 auto const jrr = env.rpc("validators")[jss::result];
128 auto& jrrnUnl = jrr[jss::NegativeUNL];
129 auto jrrnUnlSize = jrrnUnl.size();
130 BEAST_EXPECT(jrrnUnlSize == 2);
131 for (std::uint32_t x = 0; x < jrrnUnlSize; ++x)
132 {
133 auto parsedKey =
134 parseBase58<PublicKey>(TokenType::NodePublic, jrrnUnl[x].asString());
135 BEAST_EXPECT(parsedKey);
136 if (parsedKey)
137 BEAST_EXPECT(disabledKeys.contains(*parsedKey));
138 }
139
140 disabledKeys.clear();
141 env.app().getValidators().setNegativeUNL(disabledKeys);
142 auto const jrrUpdated = env.rpc("validators")[jss::result];
143 BEAST_EXPECT(jrrUpdated[jss::NegativeUNL].isNull());
144 }
145 }
146
147 void
149 {
150 using namespace test::jtx;
151
152 auto toStr = [](PublicKey const& publicKey) {
153 return toBase58(TokenType::NodePublic, publicKey);
154 };
155
156 // Validator keys that will be in the published list
157 std::vector<Validator> const validators = {
159 std::set<std::string> expectedKeys;
160 for (auto const& val : validators)
161 expectedKeys.insert(toStr(val.masterPublic));
162
163 // Manage single-thread io_context for server.
164 BasicApp worker{1};
165 using namespace std::chrono_literals;
166 NetClock::time_point const validUntil{3600s};
167 NetClock::time_point const validFrom2{validUntil - 60s};
168 NetClock::time_point const validUntil2{validFrom2 + 3600s};
169 auto server = make_TrustedPublisherServer(
170 worker.get_io_context(),
171 validators,
172 validUntil,
173 {{validFrom2, validUntil2}},
174 false,
175 1,
176 false);
177
178 //----------------------------------------------------------------------
179 // Publisher list site unavailable v1
180 {
181 // Publisher site information
182 using namespace std::string_literals;
183 std::string siteURI = "http://"s + getEnvLocalhostAddr() + ":1234/validators";
184
185 Env env{
186 *this,
188 cfg->section(SECTION_VALIDATOR_LIST_SITES).append(siteURI);
189 cfg->section(SECTION_VALIDATOR_LIST_KEYS)
190 .append(strHex(server->publisherPublic()));
191 return cfg;
192 }),
193 };
194
195 env.app().getValidatorSites().start();
196 env.app().getValidatorSites().join();
197
198 {
199 auto const jrr = env.rpc("server_info")[jss::result];
200 BEAST_EXPECT(jrr[jss::info][jss::validator_list][jss::expiration] == "unknown");
201 }
202 {
203 auto const jrr = env.rpc("server_state")[jss::result];
204 BEAST_EXPECT(jrr[jss::state][jss::validator_list_expires].asInt() == 0);
205 }
206 {
207 auto const jrr = env.rpc("validators")[jss::result];
208 BEAST_EXPECT(
209 jrr[jss::validation_quorum].asUInt() ==
211 BEAST_EXPECT(jrr[jss::local_static_keys].size() == 0);
212 BEAST_EXPECT(jrr[jss::trusted_validator_keys].size() == 0);
213 BEAST_EXPECT(jrr[jss::validator_list][jss::expiration] == "unknown");
214
215 if (BEAST_EXPECT(jrr[jss::publisher_lists].size() == 1))
216 {
217 auto jp = jrr[jss::publisher_lists][0u];
218 BEAST_EXPECT(jp[jss::available] == false);
219 BEAST_EXPECT(jp[jss::list].size() == 0);
220 BEAST_EXPECT(!jp.isMember(jss::seq));
221 BEAST_EXPECT(!jp.isMember(jss::expiration));
222 BEAST_EXPECT(!jp.isMember(jss::version));
223 BEAST_EXPECT(jp[jss::pubkey_publisher] == strHex(server->publisherPublic()));
224 }
225 BEAST_EXPECT(jrr[jss::signing_keys].size() == 0);
226 }
227 {
228 auto const jrr = env.rpc("validator_list_sites")[jss::result];
229 if (BEAST_EXPECT(jrr[jss::validator_sites].size() == 1))
230 {
231 auto js = jrr[jss::validator_sites][0u];
232 BEAST_EXPECT(js[jss::refresh_interval_min].asUInt() == 5);
233 BEAST_EXPECT(js[jss::uri] == siteURI);
234 BEAST_EXPECT(js.isMember(jss::last_refresh_time));
235 BEAST_EXPECT(js[jss::last_refresh_status] == "invalid");
236 }
237 }
238 }
239 // Publisher list site unavailable v2
240 {
241 // Publisher site information
242 using namespace std::string_literals;
243 std::string siteURI = "http://"s + getEnvLocalhostAddr() + ":1234/validators2";
244
245 Env env{
246 *this,
248 cfg->section(SECTION_VALIDATOR_LIST_SITES).append(siteURI);
249 cfg->section(SECTION_VALIDATOR_LIST_KEYS)
250 .append(strHex(server->publisherPublic()));
251 return cfg;
252 }),
253 };
254
255 env.app().getValidatorSites().start();
256 env.app().getValidatorSites().join();
257
258 {
259 auto const jrr = env.rpc("server_info")[jss::result];
260 BEAST_EXPECT(jrr[jss::info][jss::validator_list][jss::expiration] == "unknown");
261 }
262 {
263 auto const jrr = env.rpc("server_state")[jss::result];
264 BEAST_EXPECT(jrr[jss::state][jss::validator_list_expires].asInt() == 0);
265 }
266 {
267 auto const jrr = env.rpc("validators")[jss::result];
268 BEAST_EXPECT(
269 jrr[jss::validation_quorum].asUInt() ==
271 BEAST_EXPECT(jrr[jss::local_static_keys].size() == 0);
272 BEAST_EXPECT(jrr[jss::trusted_validator_keys].size() == 0);
273 BEAST_EXPECT(jrr[jss::validator_list][jss::expiration] == "unknown");
274
275 if (BEAST_EXPECT(jrr[jss::publisher_lists].size() == 1))
276 {
277 auto jp = jrr[jss::publisher_lists][0u];
278 BEAST_EXPECT(jp[jss::available] == false);
279 BEAST_EXPECT(jp[jss::list].size() == 0);
280 BEAST_EXPECT(!jp.isMember(jss::seq));
281 BEAST_EXPECT(!jp.isMember(jss::expiration));
282 BEAST_EXPECT(!jp.isMember(jss::version));
283 BEAST_EXPECT(jp[jss::pubkey_publisher] == strHex(server->publisherPublic()));
284 }
285 BEAST_EXPECT(jrr[jss::signing_keys].size() == 0);
286 }
287 {
288 auto const jrr = env.rpc("validator_list_sites")[jss::result];
289 if (BEAST_EXPECT(jrr[jss::validator_sites].size() == 1))
290 {
291 auto js = jrr[jss::validator_sites][0u];
292 BEAST_EXPECT(js[jss::refresh_interval_min].asUInt() == 5);
293 BEAST_EXPECT(js[jss::uri] == siteURI);
294 BEAST_EXPECT(js.isMember(jss::last_refresh_time));
295 BEAST_EXPECT(js[jss::last_refresh_status] == "invalid");
296 }
297 }
298 }
299 //----------------------------------------------------------------------
300 // Publisher list site available
301 server->start();
302 // Publisher list site available v1
303 {
305 uri << "http://" << server->local_endpoint() << "/validators";
306 auto siteURI = uri.str();
307
308 Env env{
309 *this,
311 cfg->section(SECTION_VALIDATOR_LIST_SITES).append(siteURI);
312 cfg->section(SECTION_VALIDATOR_LIST_KEYS)
313 .append(strHex(server->publisherPublic()));
314 return cfg;
315 }),
316 };
317
318 env.app().getValidatorSites().start();
319 env.app().getValidatorSites().join();
320 hash_set<NodeID> startKeys;
321 for (auto const& val : validators)
322 startKeys.insert(calcNodeID(val.masterPublic));
323
324 env.app().getValidators().updateTrusted(
325 startKeys,
326 env.timeKeeper().now(),
327 env.app().getOPs(),
328 env.app().getOverlay(),
329 env.app().getHashRouter());
330
331 {
332 auto const jrr = env.rpc("server_info")[jss::result];
333 BEAST_EXPECT(
334 jrr[jss::info][jss::validator_list][jss::expiration] == to_string(validUntil));
335 }
336 {
337 auto const jrr = env.rpc("server_state")[jss::result];
338 BEAST_EXPECT(
339 jrr[jss::state][jss::validator_list_expires].asUInt() ==
340 validUntil.time_since_epoch().count());
341 }
342 {
343 auto const jrr = env.rpc("validators")[jss::result];
344 BEAST_EXPECT(jrr[jss::validation_quorum].asUInt() == 2);
345 BEAST_EXPECT(jrr[jss::validator_list][jss::expiration] == to_string(validUntil));
346 BEAST_EXPECT(jrr[jss::local_static_keys].size() == 0);
347
348 BEAST_EXPECT(jrr[jss::trusted_validator_keys].size() == expectedKeys.size());
349 for (auto const& jKey : jrr[jss::trusted_validator_keys])
350 {
351 BEAST_EXPECT(expectedKeys.count(jKey.asString()) == 1);
352 }
353
354 if (BEAST_EXPECT(jrr[jss::publisher_lists].size() == 1))
355 {
356 auto jp = jrr[jss::publisher_lists][0u];
357 BEAST_EXPECT(jp[jss::available] == true);
358 if (BEAST_EXPECT(jp[jss::list].size() == 2))
359 {
360 // check entries
361 std::set<std::string> foundKeys;
362 for (auto const& k : jp[jss::list])
363 {
364 foundKeys.insert(k.asString());
365 }
366 BEAST_EXPECT(foundKeys == expectedKeys);
367 }
368 BEAST_EXPECT(jp[jss::seq].asUInt() == 1);
369 BEAST_EXPECT(jp[jss::pubkey_publisher] == strHex(server->publisherPublic()));
370 BEAST_EXPECT(jp[jss::expiration] == to_string(validUntil));
371 BEAST_EXPECT(jp[jss::version] == 1);
372 }
373 auto jsk = jrr[jss::signing_keys];
374 BEAST_EXPECT(jsk.size() == 2);
375 for (auto const& val : validators)
376 {
377 BEAST_EXPECT(jsk.isMember(toStr(val.masterPublic)));
378 BEAST_EXPECT(jsk[toStr(val.masterPublic)] == toStr(val.signingPublic));
379 }
380 }
381 {
382 auto const jrr = env.rpc("validator_list_sites")[jss::result];
383 if (BEAST_EXPECT(jrr[jss::validator_sites].size() == 1))
384 {
385 auto js = jrr[jss::validator_sites][0u];
386 BEAST_EXPECT(js[jss::refresh_interval_min].asUInt() == 5);
387 BEAST_EXPECT(js[jss::uri] == siteURI);
388 BEAST_EXPECT(js[jss::last_refresh_status] == "accepted");
389 // The actual time of the update will vary run to run, so
390 // just verify the time is there
391 BEAST_EXPECT(js.isMember(jss::last_refresh_time));
392 }
393 }
394 }
395 // Publisher list site available v2
396 {
398 uri << "http://" << server->local_endpoint() << "/validators2";
399 auto siteURI = uri.str();
400
401 Env env{
402 *this,
404 cfg->section(SECTION_VALIDATOR_LIST_SITES).append(siteURI);
405 cfg->section(SECTION_VALIDATOR_LIST_KEYS)
406 .append(strHex(server->publisherPublic()));
407 return cfg;
408 }),
409 };
410
411 env.app().getValidatorSites().start();
412 env.app().getValidatorSites().join();
413 hash_set<NodeID> startKeys;
414 for (auto const& val : validators)
415 startKeys.insert(calcNodeID(val.masterPublic));
416
417 env.app().getValidators().updateTrusted(
418 startKeys,
419 env.timeKeeper().now(),
420 env.app().getOPs(),
421 env.app().getOverlay(),
422 env.app().getHashRouter());
423
424 {
425 auto const jrr = env.rpc("server_info")[jss::result];
426 BEAST_EXPECT(
427 jrr[jss::info][jss::validator_list][jss::expiration] == to_string(validUntil2));
428 }
429 {
430 auto const jrr = env.rpc("server_state")[jss::result];
431 BEAST_EXPECT(
432 jrr[jss::state][jss::validator_list_expires].asUInt() ==
433 validUntil2.time_since_epoch().count());
434 }
435 {
436 auto const jrr = env.rpc("validators")[jss::result];
437 BEAST_EXPECT(jrr[jss::validation_quorum].asUInt() == 2);
438 BEAST_EXPECT(jrr[jss::validator_list][jss::expiration] == to_string(validUntil2));
439 BEAST_EXPECT(jrr[jss::local_static_keys].size() == 0);
440
441 BEAST_EXPECT(jrr[jss::trusted_validator_keys].size() == expectedKeys.size());
442 for (auto const& jKey : jrr[jss::trusted_validator_keys])
443 {
444 BEAST_EXPECT(expectedKeys.count(jKey.asString()) == 1);
445 }
446
447 if (BEAST_EXPECT(jrr[jss::publisher_lists].size() == 1))
448 {
449 auto jp = jrr[jss::publisher_lists][0u];
450 BEAST_EXPECT(jp[jss::available] == true);
451 if (BEAST_EXPECT(jp[jss::list].size() == 2))
452 {
453 // check entries
454 std::set<std::string> foundKeys;
455 for (auto const& k : jp[jss::list])
456 {
457 foundKeys.insert(k.asString());
458 }
459 BEAST_EXPECT(foundKeys == expectedKeys);
460 }
461 BEAST_EXPECT(jp[jss::seq].asUInt() == 1);
462 BEAST_EXPECT(jp[jss::pubkey_publisher] == strHex(server->publisherPublic()));
463 BEAST_EXPECT(jp[jss::expiration] == to_string(validUntil));
464 BEAST_EXPECT(jp[jss::version] == 2);
465 if (BEAST_EXPECT(jp.isMember(jss::remaining)) &&
466 BEAST_EXPECT(jp[jss::remaining].isArray()) &&
467 BEAST_EXPECT(jp[jss::remaining].size() == 1))
468 {
469 auto const& r = jp[jss::remaining][0u];
470 if (BEAST_EXPECT(r[jss::list].size() == 2))
471 {
472 // check entries
473 std::set<std::string> foundKeys;
474 for (auto const& k : r[jss::list])
475 {
476 foundKeys.insert(k.asString());
477 }
478 BEAST_EXPECT(foundKeys == expectedKeys);
479 }
480 BEAST_EXPECT(r[jss::seq].asUInt() == 2);
481 BEAST_EXPECT(r[jss::effective] == to_string(validFrom2));
482 BEAST_EXPECT(r[jss::expiration] == to_string(validUntil2));
483 }
484 }
485 auto jsk = jrr[jss::signing_keys];
486 BEAST_EXPECT(jsk.size() == 2);
487 for (auto const& val : validators)
488 {
489 BEAST_EXPECT(jsk.isMember(toStr(val.masterPublic)));
490 BEAST_EXPECT(jsk[toStr(val.masterPublic)] == toStr(val.signingPublic));
491 }
492 }
493 {
494 auto const jrr = env.rpc("validator_list_sites")[jss::result];
495 if (BEAST_EXPECT(jrr[jss::validator_sites].size() == 1))
496 {
497 auto js = jrr[jss::validator_sites][0u];
498 BEAST_EXPECT(js[jss::refresh_interval_min].asUInt() == 5);
499 BEAST_EXPECT(js[jss::uri] == siteURI);
500 BEAST_EXPECT(js[jss::last_refresh_status] == "accepted");
501 // The actual time of the update will vary run to run, so
502 // just verify the time is there
503 BEAST_EXPECT(js.isMember(jss::last_refresh_time));
504 }
505 }
506 }
507 }
508
509 void
511 {
512 using namespace test::jtx;
513 Env env{*this};
514 auto result = env.rpc("validation_create");
515 BEAST_EXPECT(result.isMember(jss::result) && result[jss::result][jss::status] == "success");
516 result =
517 env.rpc("validation_create", "BAWL MAN JADE MOON DOVE GEM SON NOW HAD ADEN GLOW TIRE");
518 BEAST_EXPECT(result.isMember(jss::result) && result[jss::result][jss::status] == "success");
519 }
520
521 void
522 run() override
523 {
524 testPrivileges();
525 testStaticUNL();
526 testDynamicUNL();
527 test_validation_create();
528 }
529};
530
531BEAST_DEFINE_TESTSUITE(ValidatorRPC, rpc, xrpl);
532
533} // namespace test
534} // namespace xrpl
A testsuite class.
Definition suite.h:51
A public key.
Definition PublicKey.h:42
virtual ValidatorSite & getValidatorSites()=0
void start()
Start fetching lists from sites.
void run() override
Runs the suite.
A transaction testing environment.
Definition Env.h:122
Application & app()
Definition Env.h:259
Json::Value rpc(unsigned apiVersion, std::unordered_map< std::string, std::string > const &headers, std::string const &cmd, Args &&... args)
Execute an RPC command.
Definition Env.h:847
Set the expected result code for a JTx The test will fail if the code doesn't match.
Definition rpc.h:15
T clear(T... args)
T contains(T... args)
T count(T... args)
T insert(T... args)
std::uint32_t asUInt(AnyValue const &v)
Definition Oracle.cpp:380
std::unique_ptr< Config > envconfig()
creates and initializes a default configuration for jtx::Env
Definition envconfig.h:34
std::unique_ptr< Config > no_admin(std::unique_ptr< Config >)
adjust config so no admin ports are enabled
Definition envconfig.cpp:57
char const * getEnvLocalhostAddr()
Definition envconfig.h:16
std::shared_ptr< TrustedPublisherServer > make_TrustedPublisherServer(boost::asio::io_context &ioc, std::vector< TrustedPublisherServer::Validator > const &validators, NetClock::time_point validUntil, std::vector< std::pair< NetClock::time_point, NetClock::time_point > > const &futures, bool useSSL=false, int version=1, bool immediateStart=true, int sequence=1)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
std::pair< PublicKey, SecretKey > randomKeyPair(KeyType type)
Create a key pair using secure random numbers.
std::string to_string(base_uint< Bits, Tag > const &a)
Definition base_uint.h:602
std::string strHex(FwdIt begin, FwdIt end)
Definition strHex.h:10
std::string toBase58(AccountID const &v)
Convert AccountID to base58 checked string.
Definition AccountID.cpp:92
bool isAdmin(Port const &port, Json::Value const &params, beast::IP::Address const &remoteIp)
Definition Role.cpp:64
NodeID calcNodeID(PublicKey const &)
Calculate the 160-bit node ID from a node public key.
T size(T... args)
T str(T... args)