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