rippled
Loading...
Searching...
No Matches
Config_test.cpp
1#include <test/jtx/TestSuite.h>
2#include <test/unit_test/FileDirGuard.h>
3
4#include <xrpld/core/Config.h>
5#include <xrpld/core/ConfigSections.h>
6
7#include <xrpl/beast/unit_test/suite.h>
8#include <xrpl/beast/utility/temp_dir.h>
9#include <xrpl/server/Port.h>
10
11#include <boost/filesystem.hpp>
12#include <boost/format.hpp>
13
14#include <fstream>
15#include <regex>
16
17namespace xrpl {
18namespace detail {
20configContents(std::string const& dbPath, std::string const& validatorsFile)
21{
22 static boost::format configContentsTemplate(R"xrpldConfig(
23[server]
24port_rpc
25port_peer
26port_wss_admin
27
28[port_rpc]
29port = 5005
30ip = 127.0.0.1
31admin = 127.0.0.1, ::1
32protocol = https
33
34[port_peer]
35port = 51235
36ip = 0.0.0.0
37protocol = peer
38
39[port_wss_admin]
40port = 6006
41ip = 127.0.0.1
42admin = 127.0.0.1
43protocol = wss
44
45#[port_ws_public]
46#port = 5005
47#ip = 127.0.0.1
48#protocol = wss
49
50#-------------------------------------------------------------------------------
51
52[node_size]
53medium
54
55# This is primary persistent datastore for xrpld. This includes transaction
56# metadata, account states, and ledger headers. Helpful information can be
57# found on https://xrpl.org/capacity-planning.html#node-db-type
58# delete old ledgers while maintaining at least 2000. Do not require an
59# external administrative command to initiate deletion.
60[node_db]
61type=memory
62path=/Users/dummy/xrpld/config/db/rocksdb
63open_files=2000
64filter_bits=12
65cache_mb=256
66file_size_mb=8
67file_size_mult=2
68
69%1%
70
71%2%
72
73# This needs to be an absolute directory reference, not a relative one.
74# Modify this value as required.
75[debug_logfile]
76/Users/dummy/xrpld/config/log/debug.log
77
78[sntp_servers]
79time.windows.com
80time.apple.com
81time.nist.gov
82pool.ntp.org
83
84# Where to find some other servers speaking the Ripple protocol.
85#
86[ips]
87r.ripple.com 51235
88
89# Turn down default logging to save disk space in the long run.
90# Valid values here are trace, debug, info, warning, error, and fatal
91[rpc_startup]
92{ "command": "log_level", "severity": "warning" }
93
94# Defaults to 1 ("yes") so that certificates will be validated. To allow the use
95# of self-signed certificates for development or internal use, set to 0 ("no").
96[ssl_verify]
970
98
99[sqdb]
100backend=sqlite
101)xrpldConfig");
102
103 std::string dbPathSection = dbPath.empty() ? "" : "[database_path]\n" + dbPath;
104 std::string valFileSection =
105 validatorsFile.empty() ? "" : "[validators_file]\n" + validatorsFile;
106 return boost::str(configContentsTemplate % dbPathSection % valFileSection);
107}
108
113{
114private:
116
117 bool rmDataDir_{false};
118
120
121public:
124 path subDir,
125 path const& dbPath,
126 path const& configFile,
127 path const& validatorsFile,
128 bool useCounter = true,
129 std::string confContents = "")
130 : FileDirGuard(
131 test,
132 std::move(subDir),
134 confContents.empty() ? configContents(dbPath.string(), validatorsFile.string())
135 : confContents,
136 useCounter)
137 , dataDir_(dbPath)
138 {
139 if (dbPath.empty())
141
142 rmDataDir_ = !exists(dataDir_);
144 file_.string(),
145 /* bQuiet */ true,
146 /* bSilent */ false,
147 /* bStandalone */ false);
148 }
149
150 Config const&
151 config() const
152 {
153 return config_;
154 }
155
158 {
159 return file().string();
160 }
161
162 bool
164 {
165 return boost::filesystem::is_directory(dataDir_);
166 }
167
168 bool
170 {
171 return fileExists();
172 }
173
175 {
176 try
177 {
178 using namespace boost::filesystem;
179 if (rmDataDir_)
181 }
182 catch (std::exception& e)
183 {
184 // if we throw here, just let it die.
185 test_.log << "Error in ~FileCfgGuard: " << e.what() << std::endl;
186 };
187 }
188};
189
192{
193 std::string configContents(R"xrpldConfig(
194[validators]
195n949f75evCHwgyP4fPVgaHqNHxUVN15PsJEZ3B3HnXPcPjcZAoy7
196n9MD5h24qrQqiyBC8aeqqCWvpiBiYQ3jxSr91uiDvmrkyHRdYLUj
197n9L81uNCaPgtUJfaHh89gmdvXKAmSt5Gdsw2g1iPWaPkAHW5Nm4C
198n9KiYM9CgngLvtRCQHZwgC2gjpdaZcCcbt3VboxiNFcKuwFVujzS
199n9LdgEtkmGB9E2h3K4Vp7iGUaKuq23Zr32ehxiU8FWY7xoxbWTSA
200
201[validator_keys]
202nHUhG1PgAG8H8myUENypM35JgfqXAKNQvRVVAFDRzJrny5eZN8d5
203nHBu9PTL9dn2GuZtdW4U2WzBwffyX9qsQCd9CNU4Z5YG3PQfViM8
204nHUPDdcdb2Y5DZAJne4c2iabFuAP3F34xZUgYQT2NH7qfkdapgnz
205
206[validator_list_sites]
207recommended-xrpl-validators.com
208more-xrpl-validators.net
209
210[validator_list_keys]
21103E74EE14CB525AFBB9F1B7D86CD58ECC4B91452294B42AB4E78F260BD905C091D
212030775A669685BD6ABCEBD80385921C7851783D991A8055FD21D2F3966C96F1B56
213
214[validator_list_threshold]
2152
216)xrpldConfig");
217 return configContents;
218}
219
224{
225public:
228 path subDir,
229 path const& validatorsFileName,
230 bool useCounter = true)
231 : FileDirGuard(
232 test,
233 std::move(subDir),
234 path(validatorsFileName.empty() ? Config::validatorsFileName : validatorsFileName),
236 useCounter)
237 {
238 }
239
240 bool
242 {
243 return fileExists();
244 }
245
248 {
249 return absolute(file()).string();
250 }
251
253 {
254 }
255};
256} // namespace detail
257
258class Config_test final : public TestSuite
259{
260private:
261 using path = boost::filesystem::path;
262
263public:
264 void
266 {
267 testcase("legacy");
268
269 Config c;
270
271 std::string const toLoad(R"xrpldConfig(
272[server]
273port_rpc
274port_peer
275port_wss_admin
276
277[ssl_verify]
2780
279)xrpldConfig");
280
281 c.loadFromString(toLoad);
282
283 BEAST_EXPECT(c.legacy("ssl_verify") == "0");
284 expectException([&c] { c.legacy("server"); }); // not a single line
285
286 // set a legacy value
287 BEAST_EXPECT(c.legacy("not_in_file").empty());
288 c.legacy("not_in_file", "new_value");
289 BEAST_EXPECT(c.legacy("not_in_file") == "new_value");
290 }
291 void
293 {
294 testcase("config_file");
295
296 using namespace boost::filesystem;
297 auto const cwd = current_path();
298
299 // Test both config file names.
301
302 // Config file in current directory.
303 for (auto const& configFile : configFiles)
304 {
305 // Use a temporary directory for testing.
306 beast::temp_dir const td;
307 current_path(td.path());
308 path const f = td.file(std::string{configFile});
309 std::ofstream o(f.string());
310 o << detail::configContents("", "");
311 o.close();
312
313 // Load the config file from the current directory and verify it.
314 Config c;
315 c.setup("", true, false, true);
316 BEAST_EXPECT(c.section(SECTION_DEBUG_LOGFILE).values().size() == 1);
317 BEAST_EXPECT(
318 c.section(SECTION_DEBUG_LOGFILE).values()[0] ==
319 "/Users/dummy/xrpld/config/log/debug.log");
320 }
321
322 // Config file in HOME or XDG_CONFIG_HOME directory.
323#if BOOST_OS_LINUX || BOOST_OS_MACOS
324 for (auto const& configFile : configFiles)
325 {
326 // Point the current working directory to a temporary directory, so
327 // we don't pick up an actual config file from the repository root.
328 beast::temp_dir const td;
329 current_path(td.path());
330
331 // The XDG config directory is set: the config file must be in a
332 // subdirectory named after the system.
333 {
334 beast::temp_dir const tc;
335
336 // Set the HOME and XDG_CONFIG_HOME environment variables. The
337 // HOME variable is not used when XDG_CONFIG_HOME is set, but
338 // must be set.
339 char const* h = getenv("HOME");
340 setenv("HOME", tc.path().c_str(), 1);
341 char const* x = getenv("XDG_CONFIG_HOME");
342 setenv("XDG_CONFIG_HOME", tc.path().c_str(), 1);
343
344 // Create the config file in '${XDG_CONFIG_HOME}/[systemName]'.
345 path p = tc.file(systemName());
346 create_directory(p);
347 p = tc.file(systemName() + "/" + std::string{configFile});
348 std::ofstream o(p.string());
349 o << detail::configContents("", "");
350 o.close();
351
352 // Load the config file from the config directory and verify it.
353 Config c;
354 c.setup("", true, false, true);
355 BEAST_EXPECT(c.section(SECTION_DEBUG_LOGFILE).values().size() == 1);
356 BEAST_EXPECT(
357 c.section(SECTION_DEBUG_LOGFILE).values()[0] ==
358 "/Users/dummy/xrpld/config/log/debug.log");
359
360 // Restore the environment variables.
361 (h != nullptr) ? setenv("HOME", h, 1) : unsetenv("HOME");
362 (x != nullptr) ? setenv("XDG_CONFIG_HOME", x, 1) : unsetenv("XDG_CONFIG_HOME");
363 }
364
365 // The XDG config directory is not set: the config file must be in a
366 // subdirectory named .config followed by the system name.
367 {
368 beast::temp_dir const tc;
369
370 // Set only the HOME environment variable.
371 char const* h = getenv("HOME");
372 setenv("HOME", tc.path().c_str(), 1);
373 char const* x = getenv("XDG_CONFIG_HOME");
374 unsetenv("XDG_CONFIG_HOME");
375
376 // Create the config file in '${HOME}/.config/[systemName]'.
377 std::string s = ".config";
378 path p = tc.file(s);
379 create_directory(p);
380 s += "/" + systemName();
381 p = tc.file(s);
382 create_directory(p);
383 p = tc.file(s + "/" + std::string{configFile});
384 std::ofstream o(p.string());
385 o << detail::configContents("", "");
386 o.close();
387
388 // Load the config file from the config directory and verify it.
389 Config c;
390 c.setup("", true, false, true);
391 BEAST_EXPECT(c.section(SECTION_DEBUG_LOGFILE).values().size() == 1);
392 BEAST_EXPECT(
393 c.section(SECTION_DEBUG_LOGFILE).values()[0] ==
394 "/Users/dummy/xrpld/config/log/debug.log");
395
396 // Restore the environment variables.
397 (h != nullptr) ? setenv("HOME", h, 1) : unsetenv("HOME");
398 if (x != nullptr)
399 setenv("XDG_CONFIG_HOME", x, 1);
400 }
401 }
402#endif
403
404 // Restore the current working directory.
405 current_path(cwd);
406 }
407 void
409 {
410 testcase("database_path");
411
412 using namespace boost::filesystem;
413 {
414 boost::format cc("[database_path]\n%1%\n");
415
416 auto const cwd = current_path();
417 path const dataDirRel("test_data_dir");
418 path const dataDirAbs(cwd / dataDirRel);
419 {
420 // Dummy test - do we get back what we put in
421 Config c;
422 c.loadFromString(boost::str(cc % dataDirAbs.string()));
423 BEAST_EXPECT(c.legacy("database_path") == dataDirAbs.string());
424 }
425 {
426 // Rel paths should convert to abs paths
427 Config c;
428 c.loadFromString(boost::str(cc % dataDirRel.string()));
429 BEAST_EXPECT(c.legacy("database_path") == dataDirAbs.string());
430 }
431 {
432 // No db section.
433 // N.B. Config::setup will give database_path a default,
434 // load will not.
435 Config c;
436 c.loadFromString("");
437 BEAST_EXPECT(c.legacy("database_path").empty());
438 }
439 }
440 {
441 // read from file absolute path
442 auto const cwd = current_path();
443 detail::DirGuard const g0(*this, "test_db");
444 path const dataDirRel("test_data_dir");
445 path const dataDirAbs(cwd / g0.subdir() / dataDirRel);
446 detail::FileCfgGuard const g(
447 *this, g0.subdir(), dataDirAbs, Config::configFileName, "", false);
448 auto const& c(g.config());
449 BEAST_EXPECT(g.dataDirExists());
450 BEAST_EXPECT(g.configFileExists());
451 BEAST_EXPECT(c.legacy("database_path") == dataDirAbs.string());
452 }
453 {
454 // read from file relative path
455 std::string const dbPath("my_db");
456 detail::FileCfgGuard const g(*this, "test_db", dbPath, Config::configFileName, "");
457 auto const& c(g.config());
458 std::string const nativeDbPath = absolute(path(dbPath)).string();
459 BEAST_EXPECT(g.dataDirExists());
460 BEAST_EXPECT(g.configFileExists());
461 BEAST_EXPECT(c.legacy("database_path") == nativeDbPath);
462 }
463 {
464 // read from file no path
465 detail::FileCfgGuard const g(*this, "test_db", "", Config::configFileName, "");
466 auto const& c(g.config());
467 std::string const nativeDbPath =
468 absolute(g.subdir() / path(Config::databaseDirName)).string();
469 BEAST_EXPECT(g.dataDirExists());
470 BEAST_EXPECT(g.configFileExists());
471 BEAST_EXPECT(c.legacy("database_path") == nativeDbPath);
472 }
473 }
474
475 void
477 {
478 testcase("validator keys");
479
480 std::string const validationSeed = "spA4sh1qTvwq92X715tYyGQKmAKfa";
481
482 auto const token =
483 "eyJ2YWxpZGF0aW9uX3ByaXZhdGVfa2V5IjoiOWVkNDVmODY2MjQxY2MxOGEyNzQ3Yj"
484 "U0Mzg3YzA2MjU5MDc5NzJmNGU3MTkwMjMxZmFhOTM3NDU3ZmE5ZGFmNiIsIm1hbmlm"
485 "ZXN0IjoiSkFBQUFBRnhJZTFGdHdtaW12R3RIMmlDY01KcUM5Z1ZGS2lsR2Z3MS92Q3"
486 "hIWFhMcGxjMkduTWhBa0UxYWdxWHhCd0R3RGJJRDZPTVNZdU0wRkRBbHBBZ05rOFNL"
487 "Rm43TU8yZmRrY3dSUUloQU9uZ3U5c0FLcVhZb3VKK2wyVjBXK3NBT2tWQitaUlM2UF"
488 "NobEpBZlVzWGZBaUJzVkpHZXNhYWRPSmMvYUFab2tTMXZ5bUdtVnJsSFBLV1gzWXl3"
489 "dTZpbjhIQVNRS1B1Z0JENjdrTWFSRkd2bXBBVEhsR0tKZHZERmxXUFl5NUFxRGVkRn"
490 "Y1VEphMncwaTIxZXEzTVl5d0xWSlpuRk9yN0Mwa3cyQWlUelNDakl6ZGl0UTg9In0"
491 "=";
492
493 {
494 Config c;
495 static boost::format configTemplate(R"xrpldConfig(
496[validation_seed]
497%1%
498
499[validator_token]
500%2%
501)xrpldConfig");
502 std::string error;
503 auto const expectedError =
504 "Cannot have both [validation_seed] "
505 "and [validator_token] config sections";
506 try
507 {
508 c.loadFromString(boost::str(configTemplate % validationSeed % token));
509 }
510 catch (std::runtime_error& e)
511 {
512 error = e.what();
513 }
514 BEAST_EXPECT(error == expectedError);
515 }
516 }
517
518 void
520 {
521 testcase("network id");
522 std::string error;
523 Config c;
524 try
525 {
526 c.loadFromString(R"xrpldConfig(
527[network_id]
528main
529)xrpldConfig");
530 }
531 catch (std::runtime_error& e)
532 {
533 error = e.what();
534 }
535
536 BEAST_EXPECT(error.empty());
537 BEAST_EXPECT(c.NETWORK_ID == 0);
538
539 try
540 {
541 c.loadFromString(R"xrpldConfig(
542)xrpldConfig");
543 }
544 catch (std::runtime_error& e)
545 {
546 error = e.what();
547 }
548
549 BEAST_EXPECT(error.empty());
550 BEAST_EXPECT(c.NETWORK_ID == 0);
551
552 try
553 {
554 c.loadFromString(R"xrpldConfig(
555[network_id]
556255
557)xrpldConfig");
558 }
559 catch (std::runtime_error& e)
560 {
561 error = e.what();
562 }
563
564 BEAST_EXPECT(error.empty());
565 BEAST_EXPECT(c.NETWORK_ID == 255);
566
567 try
568 {
569 c.loadFromString(R"xrpldConfig(
570[network_id]
57110000
572)xrpldConfig");
573 }
574 catch (std::runtime_error& e)
575 {
576 error = e.what();
577 }
578
579 BEAST_EXPECT(error.empty());
580 BEAST_EXPECT(c.NETWORK_ID == 10000);
581 }
582
583 void
585 {
586 testcase("validators_file");
587
588 using namespace boost::filesystem;
589 {
590 // load should throw for missing specified validators file
591 boost::format cc("[validators_file]\n%1%\n");
592 std::string error;
593 std::string const missingPath = "/no/way/this/path/exists";
594 auto const expectedError =
595 "The file specified in [validators_file] does not exist: " + missingPath;
596 try
597 {
598 Config c;
599 c.loadFromString(boost::str(cc % missingPath));
600 }
601 catch (std::runtime_error& e)
602 {
603 error = e.what();
604 }
605 BEAST_EXPECT(error == expectedError);
606 }
607 {
608 // load should throw for invalid [validators_file]
609 detail::ValidatorsTxtGuard const vtg(*this, "test_cfg", "validators.cfg");
610 path const invalidFile = current_path() / vtg.subdir();
611 boost::format cc("[validators_file]\n%1%\n");
612 std::string error;
613 auto const expectedError =
614 "Invalid file specified in [validators_file]: " + invalidFile.string();
615 try
616 {
617 Config c;
618 c.loadFromString(boost::str(cc % invalidFile.string()));
619 }
620 catch (std::runtime_error& e)
621 {
622 error = e.what();
623 }
624 BEAST_EXPECT(error == expectedError);
625 }
626 {
627 // load validators from config into single section
628 Config c;
629 std::string const toLoad(R"xrpldConfig(
630[validators]
631n949f75evCHwgyP4fPVgaHqNHxUVN15PsJEZ3B3HnXPcPjcZAoy7
632n9MD5h24qrQqiyBC8aeqqCWvpiBiYQ3jxSr91uiDvmrkyHRdYLUj
633n9L81uNCaPgtUJfaHh89gmdvXKAmSt5Gdsw2g1iPWaPkAHW5Nm4C
634
635[validator_keys]
636nHUhG1PgAG8H8myUENypM35JgfqXAKNQvRVVAFDRzJrny5eZN8d5
637nHBu9PTL9dn2GuZtdW4U2WzBwffyX9qsQCd9CNU4Z5YG3PQfViM8
638)xrpldConfig");
639 c.loadFromString(toLoad);
640 BEAST_EXPECT(c.legacy("validators_file").empty());
641 BEAST_EXPECT(c.section(SECTION_VALIDATORS).values().size() == 5);
642 BEAST_EXPECT(c.VALIDATOR_LIST_THRESHOLD == std::nullopt);
643 }
644 {
645 // load validator list sites and keys from config
646 Config c;
647 std::string const toLoad(R"xrpldConfig(
648[validator_list_sites]
649xrpl-validators.com
650trust-these-validators.gov
651
652[validator_list_keys]
653021A99A537FDEBC34E4FCA03B39BEADD04299BB19E85097EC92B15A3518801E566
654
655[validator_list_threshold]
6561
657)xrpldConfig");
658 c.loadFromString(toLoad);
659 BEAST_EXPECT(c.section(SECTION_VALIDATOR_LIST_SITES).values().size() == 2);
660 BEAST_EXPECT(
661 c.section(SECTION_VALIDATOR_LIST_SITES).values()[0] == "xrpl-validators.com");
662 BEAST_EXPECT(
663 c.section(SECTION_VALIDATOR_LIST_SITES).values()[1] ==
664 "trust-these-validators.gov");
665 BEAST_EXPECT(c.section(SECTION_VALIDATOR_LIST_KEYS).values().size() == 1);
666 BEAST_EXPECT(
667 c.section(SECTION_VALIDATOR_LIST_KEYS).values()[0] ==
668 "021A99A537FDEBC34E4FCA03B39BEADD04299BB19E85097EC92B15A3518801"
669 "E566");
670 BEAST_EXPECT(c.section(SECTION_VALIDATOR_LIST_THRESHOLD).values().size() == 1);
671 BEAST_EXPECT(c.section(SECTION_VALIDATOR_LIST_THRESHOLD).values()[0] == "1");
672 BEAST_EXPECT(c.VALIDATOR_LIST_THRESHOLD == std::size_t(1));
673 }
674 {
675 // load validator list sites and keys from config
676 Config c;
677 std::string const toLoad(R"xrpldConfig(
678[validator_list_sites]
679xrpl-validators.com
680trust-these-validators.gov
681
682[validator_list_keys]
683021A99A537FDEBC34E4FCA03B39BEADD04299BB19E85097EC92B15A3518801E566
684
685[validator_list_threshold]
6860
687)xrpldConfig");
688 c.loadFromString(toLoad);
689 BEAST_EXPECT(c.section(SECTION_VALIDATOR_LIST_SITES).values().size() == 2);
690 BEAST_EXPECT(
691 c.section(SECTION_VALIDATOR_LIST_SITES).values()[0] == "xrpl-validators.com");
692 BEAST_EXPECT(
693 c.section(SECTION_VALIDATOR_LIST_SITES).values()[1] ==
694 "trust-these-validators.gov");
695 BEAST_EXPECT(c.section(SECTION_VALIDATOR_LIST_KEYS).values().size() == 1);
696 BEAST_EXPECT(
697 c.section(SECTION_VALIDATOR_LIST_KEYS).values()[0] ==
698 "021A99A537FDEBC34E4FCA03B39BEADD04299BB19E85097EC92B15A3518801"
699 "E566");
700 BEAST_EXPECT(c.section(SECTION_VALIDATOR_LIST_THRESHOLD).values().size() == 1);
701 BEAST_EXPECT(c.section(SECTION_VALIDATOR_LIST_THRESHOLD).values()[0] == "0");
702 BEAST_EXPECT(c.VALIDATOR_LIST_THRESHOLD == std::nullopt);
703 }
704 {
705 // load should throw if [validator_list_threshold] is greater than
706 // the number of [validator_list_keys]
707 Config c;
708 std::string const toLoad(R"xrpldConfig(
709[validator_list_sites]
710xrpl-validators.com
711trust-these-validators.gov
712
713[validator_list_keys]
714021A99A537FDEBC34E4FCA03B39BEADD04299BB19E85097EC92B15A3518801E566
715
716[validator_list_threshold]
7172
718)xrpldConfig");
719 std::string error;
720 auto const expectedError =
721 "Value in config section [validator_list_threshold] exceeds "
722 "the number of configured list keys";
723 try
724 {
725 c.loadFromString(toLoad);
726 fail();
727 }
728 catch (std::runtime_error& e)
729 {
730 error = e.what();
731 }
732 BEAST_EXPECT(error == expectedError);
733 }
734 {
735 // load should throw if [validator_list_threshold] is malformed
736 Config c;
737 std::string const toLoad(R"xrpldConfig(
738[validator_list_sites]
739xrpl-validators.com
740trust-these-validators.gov
741
742[validator_list_keys]
743021A99A537FDEBC34E4FCA03B39BEADD04299BB19E85097EC92B15A3518801E566
744
745[validator_list_threshold]
746value = 2
747)xrpldConfig");
748 std::string error;
749 auto const expectedError =
750 "Config section [validator_list_threshold] should contain "
751 "single value only";
752 try
753 {
754 c.loadFromString(toLoad);
755 fail();
756 }
757 catch (std::runtime_error& e)
758 {
759 error = e.what();
760 }
761 BEAST_EXPECT(error == expectedError);
762 }
763 {
764 // load should throw if [validator_list_threshold] is negative
765 Config c;
766 std::string const toLoad(R"xrpldConfig(
767[validator_list_sites]
768xrpl-validators.com
769trust-these-validators.gov
770
771[validator_list_keys]
772021A99A537FDEBC34E4FCA03B39BEADD04299BB19E85097EC92B15A3518801E566
773
774[validator_list_threshold]
775-1
776)xrpldConfig");
777 bool error = false;
778 try
779 {
780 c.loadFromString(toLoad);
781 fail();
782 }
783 catch (std::bad_cast& e)
784 {
785 error = true;
786 }
787 BEAST_EXPECT(error);
788 }
789 {
790 // load should throw if [validator_list_sites] is configured but
791 // [validator_list_keys] is not
792 Config c;
793 std::string const toLoad(R"xrpldConfig(
794[validator_list_sites]
795xrpl-validators.com
796trust-these-validators.gov
797)xrpldConfig");
798 std::string error;
799 auto const expectedError = "[validator_list_keys] config section is missing";
800 try
801 {
802 c.loadFromString(toLoad);
803 fail();
804 }
805 catch (std::runtime_error& e)
806 {
807 error = e.what();
808 }
809 BEAST_EXPECT(error == expectedError);
810 }
811 {
812 // load from specified [validators_file] absolute path
813 detail::ValidatorsTxtGuard const vtg(*this, "test_cfg", "validators.cfg");
814 BEAST_EXPECT(vtg.validatorsFileExists());
815 Config c;
816 boost::format cc("[validators_file]\n%1%\n");
817 c.loadFromString(boost::str(cc % vtg.validatorsFile()));
818 BEAST_EXPECT(c.legacy("validators_file") == vtg.validatorsFile());
819 BEAST_EXPECT(c.section(SECTION_VALIDATORS).values().size() == 8);
820 BEAST_EXPECT(c.section(SECTION_VALIDATOR_LIST_SITES).values().size() == 2);
821 BEAST_EXPECT(c.section(SECTION_VALIDATOR_LIST_KEYS).values().size() == 2);
822 BEAST_EXPECT(c.section(SECTION_VALIDATOR_LIST_THRESHOLD).values().size() == 1);
823 BEAST_EXPECT(c.VALIDATOR_LIST_THRESHOLD == 2);
824 }
825 {
826 // load from specified [validators_file] file name
827 // in config directory
828 std::string const valFileName = "validators.txt";
829 detail::ValidatorsTxtGuard const vtg(*this, "test_cfg", valFileName);
830 detail::FileCfgGuard const rcg(
831 *this, vtg.subdir(), "", Config::configFileName, valFileName, false);
832 BEAST_EXPECT(vtg.validatorsFileExists());
833 BEAST_EXPECT(rcg.configFileExists());
834 auto const& c(rcg.config());
835 BEAST_EXPECT(c.legacy("validators_file") == valFileName);
836 BEAST_EXPECT(c.section(SECTION_VALIDATORS).values().size() == 8);
837 BEAST_EXPECT(c.section(SECTION_VALIDATOR_LIST_SITES).values().size() == 2);
838 BEAST_EXPECT(c.section(SECTION_VALIDATOR_LIST_KEYS).values().size() == 2);
839 BEAST_EXPECT(c.section(SECTION_VALIDATOR_LIST_THRESHOLD).values().size() == 1);
840 BEAST_EXPECT(c.VALIDATOR_LIST_THRESHOLD == 2);
841 }
842 {
843 // load from specified [validators_file] relative path
844 // to config directory
845 detail::ValidatorsTxtGuard const vtg(*this, "test_cfg", "validators.txt");
846 auto const valFilePath = ".." / vtg.subdir() / "validators.txt";
847 detail::FileCfgGuard const rcg(
848 *this, vtg.subdir(), "", Config::configFileName, valFilePath, false);
849 BEAST_EXPECT(vtg.validatorsFileExists());
850 BEAST_EXPECT(rcg.configFileExists());
851 auto const& c(rcg.config());
852 BEAST_EXPECT(c.legacy("validators_file") == valFilePath);
853 BEAST_EXPECT(c.section(SECTION_VALIDATORS).values().size() == 8);
854 BEAST_EXPECT(c.section(SECTION_VALIDATOR_LIST_SITES).values().size() == 2);
855 BEAST_EXPECT(c.section(SECTION_VALIDATOR_LIST_KEYS).values().size() == 2);
856 BEAST_EXPECT(c.section(SECTION_VALIDATOR_LIST_THRESHOLD).values().size() == 1);
857 BEAST_EXPECT(c.VALIDATOR_LIST_THRESHOLD == 2);
858 }
859 {
860 // load from validators file in default location
861 detail::ValidatorsTxtGuard const vtg(*this, "test_cfg", "validators.txt");
862 detail::FileCfgGuard const rcg(
863 *this, vtg.subdir(), "", Config::configFileName, "", false);
864 BEAST_EXPECT(vtg.validatorsFileExists());
865 BEAST_EXPECT(rcg.configFileExists());
866 auto const& c(rcg.config());
867 BEAST_EXPECT(c.legacy("validators_file").empty());
868 BEAST_EXPECT(c.section(SECTION_VALIDATORS).values().size() == 8);
869 BEAST_EXPECT(c.section(SECTION_VALIDATOR_LIST_SITES).values().size() == 2);
870 BEAST_EXPECT(c.section(SECTION_VALIDATOR_LIST_KEYS).values().size() == 2);
871 BEAST_EXPECT(c.section(SECTION_VALIDATOR_LIST_THRESHOLD).values().size() == 1);
872 BEAST_EXPECT(c.VALIDATOR_LIST_THRESHOLD == 2);
873 }
874 {
875 // load from specified [validators_file] instead
876 // of default location
877 detail::ValidatorsTxtGuard const vtg(*this, "test_cfg", "validators.cfg");
878 BEAST_EXPECT(vtg.validatorsFileExists());
879 detail::ValidatorsTxtGuard const vtgDefault(
880 *this, vtg.subdir(), "validators.txt", false);
881 BEAST_EXPECT(vtgDefault.validatorsFileExists());
882 detail::FileCfgGuard const rcg(
883 *this, vtg.subdir(), "", Config::configFileName, vtg.validatorsFile(), false);
884 BEAST_EXPECT(rcg.configFileExists());
885 auto const& c(rcg.config());
886 BEAST_EXPECT(c.legacy("validators_file") == vtg.validatorsFile());
887 BEAST_EXPECT(c.section(SECTION_VALIDATORS).values().size() == 8);
888 BEAST_EXPECT(c.section(SECTION_VALIDATOR_LIST_SITES).values().size() == 2);
889 BEAST_EXPECT(c.section(SECTION_VALIDATOR_LIST_KEYS).values().size() == 2);
890 BEAST_EXPECT(c.section(SECTION_VALIDATOR_LIST_THRESHOLD).values().size() == 1);
891 BEAST_EXPECT(c.VALIDATOR_LIST_THRESHOLD == 2);
892 }
893
894 {
895 // load validators from both config and validators file
896 boost::format cc(R"xrpldConfig(
897[validators_file]
898%1%
899
900[validators]
901n949f75evCHwgyP4fPVgaHqNHxUVN15PsJEZ3B3HnXPcPjcZAoy7
902n9MD5h24qrQqiyBC8aeqqCWvpiBiYQ3jxSr91uiDvmrkyHRdYLUj
903n9L81uNCaPgtUJfaHh89gmdvXKAmSt5Gdsw2g1iPWaPkAHW5Nm4C
904n9KiYM9CgngLvtRCQHZwgC2gjpdaZcCcbt3VboxiNFcKuwFVujzS
905n9LdgEtkmGB9E2h3K4Vp7iGUaKuq23Zr32ehxiU8FWY7xoxbWTSA
906
907[validator_keys]
908nHB1X37qrniVugfQcuBTAjswphC1drx7QjFFojJPZwKHHnt8kU7v
909nHUkAWDR4cB8AgPg7VXMX6et8xRTQb2KJfgv1aBEXozwrawRKgMB
910
911[validator_list_sites]
912xrpl-validators.com
913trust-these-validators.gov
914
915[validator_list_keys]
916021A99A537FDEBC34E4FCA03B39BEADD04299BB19E85097EC92B15A3518801E566
917)xrpldConfig");
918 detail::ValidatorsTxtGuard const vtg(*this, "test_cfg", "validators.cfg");
919 BEAST_EXPECT(vtg.validatorsFileExists());
920 Config c;
921 c.loadFromString(boost::str(cc % vtg.validatorsFile()));
922 BEAST_EXPECT(c.legacy("validators_file") == vtg.validatorsFile());
923 BEAST_EXPECT(c.section(SECTION_VALIDATORS).values().size() == 15);
924 BEAST_EXPECT(c.section(SECTION_VALIDATOR_LIST_SITES).values().size() == 4);
925 BEAST_EXPECT(c.section(SECTION_VALIDATOR_LIST_KEYS).values().size() == 3);
926 BEAST_EXPECT(c.section(SECTION_VALIDATOR_LIST_THRESHOLD).values().size() == 1);
927 BEAST_EXPECT(c.VALIDATOR_LIST_THRESHOLD == 2);
928 }
929 {
930 // load should throw if [validator_list_threshold] is present both
931 // in xrpld.cfg and validators file
932 boost::format cc(R"xrpldConfig(
933[validators_file]
934%1%
935
936[validator_list_threshold]
9371
938)xrpldConfig");
939 std::string error;
940 detail::ValidatorsTxtGuard const vtg(*this, "test_cfg", "validators.cfg");
941 BEAST_EXPECT(vtg.validatorsFileExists());
942 auto const expectedError =
943 "Config section [validator_list_threshold] should contain "
944 "single value only";
945 try
946 {
947 Config c;
948 c.loadFromString(boost::str(cc % vtg.validatorsFile()));
949 fail();
950 }
951 catch (std::runtime_error& e)
952 {
953 error = e.what();
954 }
955 BEAST_EXPECT(error == expectedError);
956 }
957 {
958 // load should throw if [validators], [validator_keys] and
959 // [validator_list_keys] are missing from xrpld.cfg and
960 // validators file
961 Config const c;
962 boost::format cc("[validators_file]\n%1%\n");
963 std::string error;
964 detail::ValidatorsTxtGuard const vtg(*this, "test_cfg", "validators.cfg");
965 BEAST_EXPECT(vtg.validatorsFileExists());
966 auto const expectedError =
967 "The file specified in [validators_file] does not contain a "
968 "[validators], [validator_keys] or [validator_list_keys] "
969 "section: " +
970 vtg.validatorsFile();
971 std::ofstream const o(vtg.validatorsFile());
972 try
973 {
974 Config c2;
975 c2.loadFromString(boost::str(cc % vtg.validatorsFile()));
976 }
977 catch (std::runtime_error& e)
978 {
979 error = e.what();
980 }
981 BEAST_EXPECT(error == expectedError);
982 }
983 }
984
985 void
986 testSetup(bool explicitPath)
987 {
988 detail::FileCfgGuard const cfg(
989 *this, "testSetup", explicitPath ? "test_db" : "", Config::configFileName, "");
990 /* FileCfgGuard has a Config object that gets loaded on
991 construction, but Config::setup is not reentrant, so we
992 need a fresh config for every test case, so ignore it.
993 */
994 {
995 Config config;
996 config.setup(
997 cfg.configFile(),
998 /*bQuiet*/ false,
999 /* bSilent */ false,
1000 /* bStandalone */ false);
1001 BEAST_EXPECT(!config.quiet());
1002 BEAST_EXPECT(!config.silent());
1003 BEAST_EXPECT(!config.standalone());
1004 BEAST_EXPECT(config.LEDGER_HISTORY == 256);
1005 BEAST_EXPECT(!config.legacy("database_path").empty());
1006 }
1007 {
1008 Config config;
1009 config.setup(
1010 cfg.configFile(),
1011 /*bQuiet*/ true,
1012 /* bSilent */ false,
1013 /* bStandalone */ false);
1014 BEAST_EXPECT(config.quiet());
1015 BEAST_EXPECT(!config.silent());
1016 BEAST_EXPECT(!config.standalone());
1017 BEAST_EXPECT(config.LEDGER_HISTORY == 256);
1018 BEAST_EXPECT(!config.legacy("database_path").empty());
1019 }
1020 {
1021 Config config;
1022 config.setup(
1023 cfg.configFile(),
1024 /*bQuiet*/ false,
1025 /* bSilent */ true,
1026 /* bStandalone */ false);
1027 BEAST_EXPECT(config.quiet());
1028 BEAST_EXPECT(config.silent());
1029 BEAST_EXPECT(!config.standalone());
1030 BEAST_EXPECT(config.LEDGER_HISTORY == 256);
1031 BEAST_EXPECT(!config.legacy("database_path").empty());
1032 }
1033 {
1034 Config config;
1035 config.setup(
1036 cfg.configFile(),
1037 /*bQuiet*/ true,
1038 /* bSilent */ true,
1039 /* bStandalone */ false);
1040 BEAST_EXPECT(config.quiet());
1041 BEAST_EXPECT(config.silent());
1042 BEAST_EXPECT(!config.standalone());
1043 BEAST_EXPECT(config.LEDGER_HISTORY == 256);
1044 BEAST_EXPECT(!config.legacy("database_path").empty());
1045 }
1046 {
1047 Config config;
1048 config.setup(
1049 cfg.configFile(),
1050 /*bQuiet*/ false,
1051 /* bSilent */ false,
1052 /* bStandalone */ true);
1053 BEAST_EXPECT(!config.quiet());
1054 BEAST_EXPECT(!config.silent());
1055 BEAST_EXPECT(config.standalone());
1056 BEAST_EXPECT(config.LEDGER_HISTORY == 0);
1057 BEAST_EXPECT(config.legacy("database_path").empty() == !explicitPath);
1058 }
1059 {
1060 Config config;
1061 config.setup(
1062 cfg.configFile(),
1063 /*bQuiet*/ true,
1064 /* bSilent */ false,
1065 /* bStandalone */ true);
1066 BEAST_EXPECT(config.quiet());
1067 BEAST_EXPECT(!config.silent());
1068 BEAST_EXPECT(config.standalone());
1069 BEAST_EXPECT(config.LEDGER_HISTORY == 0);
1070 BEAST_EXPECT(config.legacy("database_path").empty() == !explicitPath);
1071 }
1072 {
1073 Config config;
1074 config.setup(
1075 cfg.configFile(),
1076 /*bQuiet*/ false,
1077 /* bSilent */ true,
1078 /* bStandalone */ true);
1079 BEAST_EXPECT(config.quiet());
1080 BEAST_EXPECT(config.silent());
1081 BEAST_EXPECT(config.standalone());
1082 BEAST_EXPECT(config.LEDGER_HISTORY == 0);
1083 BEAST_EXPECT(config.legacy("database_path").empty() == !explicitPath);
1084 }
1085 {
1086 Config config;
1087 config.setup(
1088 cfg.configFile(),
1089 /*bQuiet*/ true,
1090 /* bSilent */ true,
1091 /* bStandalone */ true);
1092 BEAST_EXPECT(config.quiet());
1093 BEAST_EXPECT(config.silent());
1094 BEAST_EXPECT(config.standalone());
1095 BEAST_EXPECT(config.LEDGER_HISTORY == 0);
1096 BEAST_EXPECT(config.legacy("database_path").empty() == !explicitPath);
1097 }
1098 }
1099
1100 void
1102 {
1103 detail::FileCfgGuard const cfg(*this, "testPort", "", Config::configFileName, "");
1104 auto const& conf = cfg.config();
1105 if (!BEAST_EXPECT(conf.exists("port_rpc")))
1106 return;
1107 if (!BEAST_EXPECT(conf.exists("port_wss_admin")))
1108 return;
1109 ParsedPort rpc;
1110 if (!unexcept([&]() { parse_Port(rpc, conf["port_rpc"], log); }))
1111 return;
1112 BEAST_EXPECT(rpc.admin_nets_v4.size() + rpc.admin_nets_v6.size() == 2);
1113 ParsedPort wss;
1114 if (!unexcept([&]() { parse_Port(wss, conf["port_wss_admin"], log); }))
1115 return;
1116 BEAST_EXPECT(wss.admin_nets_v4.size() + wss.admin_nets_v6.size() == 1);
1117 }
1118
1119 void
1121 {
1122 auto const contents = std::regex_replace(
1123 detail::configContents("", ""), std::regex("port\\s*=\\s*\\d+"), "port = 0");
1124
1125 try
1126 {
1127 detail::FileCfgGuard const cfg(
1128 *this, "testPort", "", Config::configFileName, "", true, contents);
1129 BEAST_EXPECT(false);
1130 }
1131 catch (std::exception const& ex)
1132 {
1133 BEAST_EXPECT(
1134 std::string_view(ex.what()).starts_with("Invalid value '0' for key 'port'"));
1135 }
1136 }
1137
1138 void
1140 {
1141 Config cfg;
1142 /* NOTE: this string includes some explicit
1143 * space chars in order to verify proper trimming */
1144 std::string const toLoad(
1145 R"(
1146[port_rpc])"
1147 "\x20"
1148 R"(
1149# comment
1150 # indented comment
1151)"
1152 "\x20\x20"
1153 R"(
1154[ips])"
1155 "\x20"
1156 R"(
1157r.ripple.com 51235
1158
1159 [ips_fixed])"
1160 "\x20\x20"
1161 R"(
1162 # COMMENT
1163 s1.ripple.com 51235
1164 s2.ripple.com 51235
1165
1166)");
1167 cfg.loadFromString(toLoad);
1168 BEAST_EXPECT(
1169 cfg.exists("port_rpc") && cfg.section("port_rpc").lines().empty() &&
1170 cfg.section("port_rpc").values().empty());
1171 BEAST_EXPECT(
1172 cfg.exists(SECTION_IPS) && cfg.section(SECTION_IPS).lines().size() == 1 &&
1173 cfg.section(SECTION_IPS).values().size() == 1);
1174 BEAST_EXPECT(
1175 cfg.exists(SECTION_IPS_FIXED) && cfg.section(SECTION_IPS_FIXED).lines().size() == 2 &&
1176 cfg.section(SECTION_IPS_FIXED).values().size() == 2);
1177 }
1178
1179 void
1180 testColons()
1181 {
1182 Config cfg;
1183 /* NOTE: this string includes some explicit
1184 * space chars in order to verify proper trimming */
1185 std::string const toLoad(
1186 R"(
1187[port_rpc])"
1188 "\x20"
1189 R"(
1190# comment
1191 # indented comment
1192)"
1193 "\x20\x20"
1194 R"(
1195[ips])"
1196 "\x20"
1197 R"(
1198r.ripple.com:51235
1199
1200 [ips_fixed])"
1201 "\x20\x20"
1202 R"(
1203 # COMMENT
1204 s1.ripple.com:51235
1205 s2.ripple.com 51235
1206 anotherserversansport
1207 anotherserverwithport:12
1208 1.1.1.1:1
1209 1.1.1.1 1
1210 12.34.12.123:12345
1211 12.34.12.123 12345
1212 ::
1213 2001:db8::
1214 ::1
1215 ::1:12345
1216 [::1]:12345
1217 2001:db8:3333:4444:5555:6666:7777:8888:12345
1218 [2001:db8:3333:4444:5555:6666:7777:8888]:1
1219
1220
1221)");
1222 cfg.loadFromString(toLoad);
1223 BEAST_EXPECT(
1224 cfg.exists("port_rpc") && cfg.section("port_rpc").lines().empty() &&
1225 cfg.section("port_rpc").values().empty());
1226 BEAST_EXPECT(
1227 cfg.exists(SECTION_IPS) && cfg.section(SECTION_IPS).lines().size() == 1 &&
1228 cfg.section(SECTION_IPS).values().size() == 1);
1229 BEAST_EXPECT(
1230 cfg.exists(SECTION_IPS_FIXED) && cfg.section(SECTION_IPS_FIXED).lines().size() == 15 &&
1231 cfg.section(SECTION_IPS_FIXED).values().size() == 15);
1232 BEAST_EXPECT(cfg.IPS[0] == "r.ripple.com 51235");
1233
1234 BEAST_EXPECT(cfg.IPS_FIXED[0] == "s1.ripple.com 51235");
1235 BEAST_EXPECT(cfg.IPS_FIXED[1] == "s2.ripple.com 51235");
1236 BEAST_EXPECT(cfg.IPS_FIXED[2] == "anotherserversansport");
1237 BEAST_EXPECT(cfg.IPS_FIXED[3] == "anotherserverwithport 12");
1238 BEAST_EXPECT(cfg.IPS_FIXED[4] == "1.1.1.1 1");
1239 BEAST_EXPECT(cfg.IPS_FIXED[5] == "1.1.1.1 1");
1240 BEAST_EXPECT(cfg.IPS_FIXED[6] == "12.34.12.123 12345");
1241 BEAST_EXPECT(cfg.IPS_FIXED[7] == "12.34.12.123 12345");
1242
1243 // all ipv6 should be ignored by colon replacer, howsoever formatted
1244 BEAST_EXPECT(cfg.IPS_FIXED[8] == "::");
1245 BEAST_EXPECT(cfg.IPS_FIXED[9] == "2001:db8::");
1246 BEAST_EXPECT(cfg.IPS_FIXED[10] == "::1");
1247 BEAST_EXPECT(cfg.IPS_FIXED[11] == "::1:12345");
1248 BEAST_EXPECT(cfg.IPS_FIXED[12] == "[::1]:12345");
1249 BEAST_EXPECT(cfg.IPS_FIXED[13] == "2001:db8:3333:4444:5555:6666:7777:8888:12345");
1250 BEAST_EXPECT(cfg.IPS_FIXED[14] == "[2001:db8:3333:4444:5555:6666:7777:8888]:1");
1251 }
1252
1253 void
1254 testComments()
1255 {
1256 struct TestCommentData
1257 {
1258 std::string_view line;
1259 std::string_view field;
1260 std::string_view expect;
1261 bool had_comment;
1262 };
1263
1264 std::array<TestCommentData, 13> const tests = {
1265 {{"password = aaaa\\#bbbb", "password", "aaaa#bbbb", false},
1266 {"password = aaaa#bbbb", "password", "aaaa", true},
1267 {"password = aaaa #bbbb", "password", "aaaa", true},
1268 // since the value is all comment, this doesn't parse as k=v :
1269 {"password = #aaaa #bbbb", "", "password =", true},
1270 {"password = aaaa\\# #bbbb", "password", "aaaa#", true},
1271 {"password = aaaa\\##bbbb", "password", "aaaa#", true},
1272 {"aaaa#bbbb", "", "aaaa", true},
1273 {"aaaa\\#bbbb", "", "aaaa#bbbb", false},
1274 {"aaaa\\##bbbb", "", "aaaa#", true},
1275 {"aaaa #bbbb", "", "aaaa", true},
1276 {"1 #comment", "", "1", true},
1277 {"#whole thing is comment", "", "", false},
1278 {" #whole comment with space", "", "", false}}};
1279
1280 for (auto const& t : tests)
1281 {
1282 Section s;
1283 s.append(std::string(t.line));
1284 BEAST_EXPECT(s.had_trailing_comments() == t.had_comment);
1285 if (t.field.empty())
1286 {
1287 BEAST_EXPECTS(s.legacy() == t.expect, s.legacy());
1288 }
1289 else
1290 {
1292 BEAST_EXPECTS(set(field, std::string(t.field), s), t.line);
1293 BEAST_EXPECTS(field == t.expect, t.line);
1294 }
1295 }
1296
1297 {
1298 Section s;
1299 s.append("online_delete = 3000");
1300 std::uint32_t od = 0;
1301 BEAST_EXPECT(set(od, "online_delete", s));
1302 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
1303 BEAST_EXPECTS(od == 3000, *(s.get<std::string>("online_delete")));
1304 }
1305
1306 {
1307 Section s;
1308 s.append("online_delete = 2000 #my comment on this");
1309 std::uint32_t od = 0;
1310 BEAST_EXPECT(set(od, "online_delete", s));
1311 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
1312 BEAST_EXPECTS(od == 2000, *(s.get<std::string>("online_delete")));
1313 }
1314 }
1315
1316 void
1317 testGetters()
1318 {
1319 using namespace std::string_literals;
1320 Section s{"MySection"};
1321 s.append("a_string = mystring");
1322 s.append("positive_int = 2");
1323 s.append("negative_int = -3");
1324 s.append("bool_ish = 1");
1325
1326 {
1327 auto val_1 = "value 1"s;
1328 BEAST_EXPECT(set(val_1, "a_string", s));
1329 BEAST_EXPECT(val_1 == "mystring");
1330
1331 auto val_2 = "value 2"s;
1332 BEAST_EXPECT(!set(val_2, "not_a_key", s));
1333 BEAST_EXPECT(val_2 == "value 2");
1334 BEAST_EXPECT(!set(val_2, "default"s, "not_a_key", s));
1335 BEAST_EXPECT(val_2 == "default");
1336
1337 auto val_3 = get<std::string>(s, "a_string");
1338 BEAST_EXPECT(val_3 == "mystring");
1339 auto val_4 = get<std::string>(s, "not_a_key");
1340 BEAST_EXPECT(val_4.empty());
1341 auto val_5 = get<std::string>(s, "not_a_key", "default");
1342 BEAST_EXPECT(val_5 == "default");
1343
1344 auto val_6 = "value 6"s;
1345 BEAST_EXPECT(get_if_exists(s, "a_string", val_6));
1346 BEAST_EXPECT(val_6 == "mystring");
1347
1348 auto val_7 = "value 7"s;
1349 BEAST_EXPECT(!get_if_exists(s, "not_a_key", val_7));
1350 BEAST_EXPECT(val_7 == "value 7");
1351 }
1352
1353 {
1354 int val_1 = 1;
1355 BEAST_EXPECT(set(val_1, "positive_int", s));
1356 BEAST_EXPECT(val_1 == 2);
1357
1358 int val_2 = 2;
1359 BEAST_EXPECT(set(val_2, "negative_int", s));
1360 BEAST_EXPECT(val_2 == -3);
1361
1362 int val_3 = 3;
1363 BEAST_EXPECT(!set(val_3, "a_string", s));
1364 BEAST_EXPECT(val_3 == 3);
1365
1366 auto val_4 = get<int>(s, "positive_int");
1367 BEAST_EXPECT(val_4 == 2);
1368 auto val_5 = get<int>(s, "not_a_key");
1369 BEAST_EXPECT(val_5 == 0);
1370 auto val_6 = get<int>(s, "not_a_key", 5);
1371 BEAST_EXPECT(val_6 == 5);
1372 auto val_7 = get<int>(s, "a_string", 6);
1373 BEAST_EXPECT(val_7 == 6);
1374
1375 int val_8 = 8;
1376 BEAST_EXPECT(get_if_exists(s, "positive_int", val_8));
1377 BEAST_EXPECT(val_8 == 2);
1378
1379 auto val_9 = 9;
1380 BEAST_EXPECT(!get_if_exists(s, "not_a_key", val_9));
1381 BEAST_EXPECT(val_9 == 9);
1382
1383 auto val_10 = 10;
1384 BEAST_EXPECT(!get_if_exists(s, "a_string", val_10));
1385 BEAST_EXPECT(val_10 == 10);
1386
1387 BEAST_EXPECT(s.get<int>("not_a_key") == std::nullopt);
1388 try
1389 {
1390 s.get<int>("a_string");
1391 fail();
1392 }
1393 catch (boost::bad_lexical_cast&)
1394 {
1395 pass();
1396 }
1397 }
1398
1399 {
1400 bool flag_1 = false;
1401 BEAST_EXPECT(get_if_exists(s, "bool_ish", flag_1));
1402 BEAST_EXPECT(flag_1 == true);
1403
1404 bool flag_2 = false;
1405 BEAST_EXPECT(!get_if_exists(s, "not_a_key", flag_2));
1406 BEAST_EXPECT(flag_2 == false);
1407 }
1408 }
1409
1410 void
1412 {
1413 testcase("amendment");
1414 struct ConfigUnit
1415 {
1416 std::string unit;
1417 std::uint32_t numSeconds;
1418 std::uint32_t configVal;
1419 bool shouldPass;
1420 };
1421
1422 std::vector<ConfigUnit> const units = {
1423 {"seconds", 1, 15 * 60, false},
1424 {"minutes", 60, 14, false},
1425 {"minutes", 60, 15, true},
1426 {"hours", 3600, 10, true},
1427 {"days", 86400, 10, true},
1428 {"weeks", 604800, 2, true},
1429 {"months", 2592000, 1, false},
1430 {"years", 31536000, 1, false}};
1431
1433 for (auto& [unit, sec, val, shouldPass] : units)
1434 {
1435 Config c;
1436 std::string toLoad(R"xrpldConfig(
1437[amendment_majority_time]
1438)xrpldConfig");
1439 toLoad += std::to_string(val) + space + unit;
1440 space = space.empty() ? " " : "";
1441
1442 try
1443 {
1444 c.loadFromString(toLoad);
1445 if (shouldPass)
1446 {
1447 BEAST_EXPECT(c.AMENDMENT_MAJORITY_TIME.count() == val * sec);
1448 }
1449 else
1450 {
1451 fail();
1452 }
1453 }
1454 catch (std::runtime_error&)
1455 {
1456 if (!shouldPass)
1457 {
1458 pass();
1459 }
1460 else
1461 {
1462 fail();
1463 }
1464 }
1465 }
1466 }
1467
1468 void
1469 testOverlay()
1470 {
1471 testcase("overlay: unknown time");
1472
1473 auto testUnknown = [](std::string value) -> std::optional<std::chrono::seconds> {
1474 try
1475 {
1476 Config c;
1477 c.loadFromString("[overlay]\nmax_unknown_time=" + value);
1478 return c.MAX_UNKNOWN_TIME;
1479 }
1480 catch (std::runtime_error&)
1481 {
1482 return {};
1483 }
1484 };
1485
1486 // Failures
1487 BEAST_EXPECT(!testUnknown("none"));
1488 BEAST_EXPECT(!testUnknown("0.5"));
1489 BEAST_EXPECT(!testUnknown("180 seconds"));
1490 BEAST_EXPECT(!testUnknown("9 minutes"));
1491
1492 // Below lower bound
1493 BEAST_EXPECT(!testUnknown("299"));
1494
1495 // In bounds
1496 BEAST_EXPECT(testUnknown("300") == std::chrono::seconds{300});
1497 BEAST_EXPECT(testUnknown("301") == std::chrono::seconds{301});
1498 BEAST_EXPECT(testUnknown("1799") == std::chrono::seconds{1799});
1499 BEAST_EXPECT(testUnknown("1800") == std::chrono::seconds{1800});
1500
1501 // Above upper bound
1502 BEAST_EXPECT(!testUnknown("1801"));
1503
1504 testcase("overlay: diverged time");
1505
1506 // In bounds:
1507 auto testDiverged = [](std::string value) -> std::optional<std::chrono::seconds> {
1508 try
1509 {
1510 Config c;
1511 c.loadFromString("[overlay]\nmax_diverged_time=" + value);
1512 return c.MAX_DIVERGED_TIME;
1513 }
1514 catch (std::runtime_error&)
1515 {
1516 return {};
1517 }
1518 };
1519
1520 // Failures
1521 BEAST_EXPECT(!testDiverged("none"));
1522 BEAST_EXPECT(!testDiverged("0.5"));
1523 BEAST_EXPECT(!testDiverged("180 seconds"));
1524 BEAST_EXPECT(!testDiverged("9 minutes"));
1525
1526 // Below lower bound
1527 BEAST_EXPECT(!testDiverged("0"));
1528 BEAST_EXPECT(!testDiverged("59"));
1529
1530 // In bounds
1531 BEAST_EXPECT(testDiverged("60") == std::chrono::seconds{60});
1532 BEAST_EXPECT(testDiverged("61") == std::chrono::seconds{61});
1533 BEAST_EXPECT(testDiverged("899") == std::chrono::seconds{899});
1534 BEAST_EXPECT(testDiverged("900") == std::chrono::seconds{900});
1535
1536 // Above upper bound
1537 BEAST_EXPECT(!testDiverged("901"));
1538 }
1539
1540 void
1541 run() override
1542 {
1543 testLegacy();
1545 testDbPath();
1548 testSetup(false);
1549 testSetup(true);
1550 testPort();
1551 testZeroPort();
1553 testColons();
1554 testComments();
1555 testGetters();
1556 testAmendment();
1557 testOverlay();
1559 }
1560};
1561
1562BEAST_DEFINE_TESTSUITE(Config, core, xrpl);
1563
1564} // namespace xrpl
T c_str(T... args)
RAII temporary directory.
Definition temp_dir.h:15
std::string path() const
Get the native path for the temporary directory.
Definition temp_dir.h:47
std::string file(std::string const &name) const
Get the native path for the a file.
Definition temp_dir.h:57
A testsuite class.
Definition suite.h:51
log_os< char > log
Logging output stream.
Definition suite.h:147
void pass()
Record a successful test condition.
Definition suite.h:497
testcase_t testcase
Memberspace for declaring test cases.
Definition suite.h:150
bool unexcept(F &&f, String const &reason)
Definition suite.h:468
void fail(String const &reason, char const *file, int line)
Record a failure.
Definition suite.h:519
bool exists(std::string const &name) const
Returns true if a section with the given name exists.
void legacy(std::string const &section, std::string value)
Set a value that is not a key/value pair.
Section & section(std::string const &name)
Returns the section with the given name.
void testSetup(bool explicitPath)
void run() override
Runs the suite.
boost::filesystem::path path
uint32_t NETWORK_ID
Definition Config.h:146
static char const *const configLegacyName
Definition Config.h:80
std::optional< std::size_t > VALIDATOR_LIST_THRESHOLD
Definition Config.h:284
bool silent() const
Definition Config.h:311
static char const *const configFileName
Definition Config.h:79
bool quiet() const
Definition Config.h:306
std::chrono::seconds MAX_DIVERGED_TIME
Definition Config.h:269
std::vector< std::string > IPS
Definition Config.h:132
bool standalone() const
Definition Config.h:316
void setup(std::string const &strConf, bool bQuiet, bool bSilent, bool bStandalone)
Definition Config.cpp:285
void loadFromString(std::string const &fileContents)
Load the config from the contents of the string.
Definition Config.cpp:452
std::uint32_t LEDGER_HISTORY
Definition Config.h:192
std::vector< std::string > IPS_FIXED
Definition Config.h:135
static char const *const databaseDirName
Definition Config.h:81
std::vector< std::string > const & values() const
Returns all the values in the section.
Definition BasicConfig.h:58
std::vector< std::string > const & lines() const
Returns all the lines in the section.
Definition BasicConfig.h:49
bool expectException(Functor f, std::string const &message="")
Definition TestSuite.h:75
Create a directory and remove it when it's done.
beast::unit_test::suite & test_
path const & subdir() const
auto rmDir(path const &toRm)
boost::filesystem::path path
Write a xrpld config file and remove when done.
Config const & config() const
std::string configFile() const
FileCfgGuard(beast::unit_test::suite &test, path subDir, path const &dbPath, path const &configFile, path const &validatorsFile, bool useCounter=true, std::string confContents="")
Write a file in a directory and remove when done.
path const & file() const
Write a validators.txt file and remove when done.
std::string validatorsFile() const
ValidatorsTxtGuard(beast::unit_test::suite &test, path subDir, path const &validatorsFileName, bool useCounter=true)
T close(T... args)
T empty(T... args)
T endl(T... args)
T is_same_v
field_t< CharT, Traits, Allocator > field(std::basic_string< CharT, Traits, Allocator > const &text, int width=8, int pad=0, bool right=false)
Definition iosformat.h:133
STL namespace.
std::string valFileContents()
std::string configContents(std::string const &dbPath, std::string const &validatorsFile)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
void parse_Port(ParsedPort &port, Section const &section, std::ostream &log)
Definition Port.cpp:194
bool set(T &target, std::string const &name, Section const &section)
Set a value from a configuration Section If the named value is not found or doesn't parse as a T,...
static std::string const & systemName()
bool get_if_exists(Section const &section, std::string const &name, T &v)
static std::optional< Seed > validationSeed(Json::Value const &params)
T regex_replace(T... args)
T size(T... args)
T starts_with(T... args)
std::vector< boost::asio::ip::network_v4 > admin_nets_v4
Definition Port.h:97
std::vector< boost::asio::ip::network_v6 > admin_nets_v6
Definition Port.h:98
T to_string(T... args)
T what(T... args)