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