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