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