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