rippled
Config_test.cpp
1 //------------------------------------------------------------------------------
2 /*
3  This file is part of rippled: https://github.com/ripple/rippled
4  Copyright (c) 2012-2015 Ripple Labs Inc.
5 
6  Permission to use, copy, modify, and/or distribute this software for any
7  purpose with or without fee is hereby granted, provided that the above
8  copyright notice and this permission notice appear in all copies.
9 
10  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18 //==============================================================================
19 
20 #include <ripple/basics/contract.h>
21 #include <ripple/core/Config.h>
22 #include <ripple/core/ConfigSections.h>
23 #include <ripple/server/Port.h>
24 #include <boost/filesystem.hpp>
25 #include <boost/format.hpp>
26 #include <fstream>
27 #include <iostream>
28 #include <test/jtx/TestSuite.h>
29 #include <test/unit_test/FileDirGuard.h>
30 
31 namespace ripple {
32 namespace detail {
34 configContents(std::string const& dbPath, std::string const& validatorsFile)
35 {
36  static boost::format configContentsTemplate(R"rippleConfig(
37 [server]
38 port_rpc
39 port_peer
40 port_wss_admin
41 
42 [port_rpc]
43 port = 5005
44 ip = 127.0.0.1
45 admin = 127.0.0.1, ::1
46 protocol = https
47 
48 [port_peer]
49 port = 51235
50 ip = 0.0.0.0
51 protocol = peer
52 
53 [port_wss_admin]
54 port = 6006
55 ip = 127.0.0.1
56 admin = 127.0.0.1
57 protocol = wss
58 
59 #[port_ws_public]
60 #port = 5005
61 #ip = 127.0.0.1
62 #protocol = wss
63 
64 #-------------------------------------------------------------------------------
65 
66 [node_size]
67 medium
68 
69 # This is primary persistent datastore for rippled. This includes transaction
70 # metadata, account states, and ledger headers. Helpful information can be
71 # found on https://xrpl.org/capacity-planning.html#node-db-type
72 # delete old ledgers while maintaining at least 2000. Do not require an
73 # external administrative command to initiate deletion.
74 [node_db]
75 type=memory
76 path=/Users/dummy/ripple/config/db/rocksdb
77 open_files=2000
78 filter_bits=12
79 cache_mb=256
80 file_size_mb=8
81 file_size_mult=2
82 
83 %1%
84 
85 %2%
86 
87 # This needs to be an absolute directory reference, not a relative one.
88 # Modify this value as required.
89 [debug_logfile]
90 /Users/dummy/ripple/config/log/debug.log
91 
92 [sntp_servers]
93 time.windows.com
94 time.apple.com
95 time.nist.gov
96 pool.ntp.org
97 
98 # Where to find some other servers speaking the Ripple protocol.
99 #
100 [ips]
101 r.ripple.com 51235
102 
103 # Turn down default logging to save disk space in the long run.
104 # Valid values here are trace, debug, info, warning, error, and fatal
105 [rpc_startup]
106 { "command": "log_level", "severity": "warning" }
107 
108 # Defaults to 1 ("yes") so that certificates will be validated. To allow the use
109 # of self-signed certificates for development or internal use, set to 0 ("no").
110 [ssl_verify]
111 0
112 
113 [sqdb]
114 backend=sqlite
115 )rippleConfig");
116 
117  std::string dbPathSection =
118  dbPath.empty() ? "" : "[database_path]\n" + dbPath;
119  std::string valFileSection =
120  validatorsFile.empty() ? "" : "[validators_file]\n" + validatorsFile;
121  return boost::str(configContentsTemplate % dbPathSection % valFileSection);
122 }
123 
128 {
129 private:
131 
132  bool rmDataDir_{false};
133 
135 
136 public:
138  beast::unit_test::suite& test,
139  path subDir,
140  path const& dbPath,
141  path const& validatorsFile,
142  bool useCounter = true)
143  : FileDirGuard(
144  test,
145  std::move(subDir),
146  path(Config::configFileName),
147  configContents(dbPath.string(), validatorsFile.string()),
148  useCounter)
149  , dataDir_(dbPath)
150  {
151  if (dbPath.empty())
153 
154  rmDataDir_ = !exists(dataDir_);
155  config_.setup(
156  file_.string(),
157  /*bQuiet*/ true,
158  /* bSilent */ false,
159  /* bStandalone */ false);
160  }
161 
162  Config const&
163  config() const
164  {
165  return config_;
166  }
167 
169  configFile() const
170  {
171  return file().string();
172  }
173 
174  bool
176  {
177  return boost::filesystem::is_directory(dataDir_);
178  }
179 
180  bool
182  {
183  return fileExists();
184  }
185 
187  {
188  try
189  {
190  using namespace boost::filesystem;
191  if (rmDataDir_)
192  rmDir(dataDir_);
193  else
194  test_.log << "Skipping rm dir: " << dataDir_.string()
195  << std::endl;
196  }
197  catch (std::exception& e)
198  {
199  // if we throw here, just let it die.
200  test_.log << "Error in ~RippledCfgGuard: " << e.what() << std::endl;
201  };
202  }
203 };
204 
207 {
208  std::string configContents(R"rippleConfig(
209 [validators]
210 n949f75evCHwgyP4fPVgaHqNHxUVN15PsJEZ3B3HnXPcPjcZAoy7
211 n9MD5h24qrQqiyBC8aeqqCWvpiBiYQ3jxSr91uiDvmrkyHRdYLUj
212 n9L81uNCaPgtUJfaHh89gmdvXKAmSt5Gdsw2g1iPWaPkAHW5Nm4C
213 n9KiYM9CgngLvtRCQHZwgC2gjpdaZcCcbt3VboxiNFcKuwFVujzS
214 n9LdgEtkmGB9E2h3K4Vp7iGUaKuq23Zr32ehxiU8FWY7xoxbWTSA
215 
216 [validator_keys]
217 nHUhG1PgAG8H8myUENypM35JgfqXAKNQvRVVAFDRzJrny5eZN8d5
218 nHBu9PTL9dn2GuZtdW4U2WzBwffyX9qsQCd9CNU4Z5YG3PQfViM8
219 nHUPDdcdb2Y5DZAJne4c2iabFuAP3F34xZUgYQT2NH7qfkdapgnz
220 
221 [validator_list_sites]
222 recommendedripplevalidators.com
223 moreripplevalidators.net
224 
225 [validator_list_keys]
226 03E74EE14CB525AFBB9F1B7D86CD58ECC4B91452294B42AB4E78F260BD905C091D
227 030775A669685BD6ABCEBD80385921C7851783D991A8055FD21D2F3966C96F1B56
228 )rippleConfig");
229  return configContents;
230 }
231 
236 {
237 public:
239  beast::unit_test::suite& test,
240  path subDir,
241  path const& validatorsFileName,
242  bool useCounter = true)
243  : FileDirGuard(
244  test,
245  std::move(subDir),
246  path(
247  validatorsFileName.empty() ? Config::validatorsFileName
248  : validatorsFileName),
249  valFileContents(),
250  useCounter)
251  {
252  }
253 
254  bool
256  {
257  return fileExists();
258  }
259 
262  {
263  return absolute(file()).string();
264  }
265 
267  {
268  }
269 };
270 } // namespace detail
271 
272 class Config_test final : public TestSuite
273 {
274 private:
275  using path = boost::filesystem::path;
276 
277 public:
278  void
280  {
281  testcase("legacy");
282 
283  Config c;
284 
285  std::string toLoad(R"rippleConfig(
286 [server]
287 port_rpc
288 port_peer
289 port_wss_admin
290 
291 [ssl_verify]
292 0
293 )rippleConfig");
294 
295  c.loadFromString(toLoad);
296 
297  BEAST_EXPECT(c.legacy("ssl_verify") == "0");
298  expectException([&c] { c.legacy("server"); }); // not a single line
299 
300  // set a legacy value
301  BEAST_EXPECT(c.legacy("not_in_file") == "");
302  c.legacy("not_in_file", "new_value");
303  BEAST_EXPECT(c.legacy("not_in_file") == "new_value");
304  }
305  void
307  {
308  testcase("database_path");
309 
310  using namespace boost::filesystem;
311  {
312  boost::format cc("[database_path]\n%1%\n");
313 
314  auto const cwd = current_path();
315  path const dataDirRel("test_data_dir");
316  path const dataDirAbs(cwd / dataDirRel);
317  {
318  // Dummy test - do we get back what we put in
319  Config c;
320  c.loadFromString(boost::str(cc % dataDirAbs.string()));
321  BEAST_EXPECT(c.legacy("database_path") == dataDirAbs.string());
322  }
323  {
324  // Rel paths should convert to abs paths
325  Config c;
326  c.loadFromString(boost::str(cc % dataDirRel.string()));
327  BEAST_EXPECT(c.legacy("database_path") == dataDirAbs.string());
328  }
329  {
330  // No db section.
331  // N.B. Config::setup will give database_path a default,
332  // load will not.
333  Config c;
334  c.loadFromString("");
335  BEAST_EXPECT(c.legacy("database_path") == "");
336  }
337  }
338  {
339  // read from file absolute path
340  auto const cwd = current_path();
341  ripple::test::detail::DirGuard const g0(*this, "test_db");
342  path const dataDirRel("test_data_dir");
343  path const dataDirAbs(cwd / g0.subdir() / dataDirRel);
344  detail::RippledCfgGuard const g(
345  *this, g0.subdir(), dataDirAbs, "", false);
346  auto const& c(g.config());
347  BEAST_EXPECT(g.dataDirExists());
348  BEAST_EXPECT(g.configFileExists());
349  BEAST_EXPECT(c.legacy("database_path") == dataDirAbs.string());
350  }
351  {
352  // read from file relative path
353  std::string const dbPath("my_db");
354  detail::RippledCfgGuard const g(*this, "test_db", dbPath, "");
355  auto const& c(g.config());
356  std::string const nativeDbPath = absolute(path(dbPath)).string();
357  BEAST_EXPECT(g.dataDirExists());
358  BEAST_EXPECT(g.configFileExists());
359  BEAST_EXPECT(c.legacy("database_path") == nativeDbPath);
360  }
361  {
362  // read from file no path
363  detail::RippledCfgGuard const g(*this, "test_db", "", "");
364  auto const& c(g.config());
365  std::string const nativeDbPath =
366  absolute(g.subdir() / path(Config::databaseDirName)).string();
367  BEAST_EXPECT(g.dataDirExists());
368  BEAST_EXPECT(g.configFileExists());
369  BEAST_EXPECT(c.legacy("database_path") == nativeDbPath);
370  }
371  }
372 
373  void
375  {
376  testcase("validator keys");
377 
378  std::string const validationSeed = "spA4sh1qTvwq92X715tYyGQKmAKfa";
379 
380  auto const token =
381  "eyJ2YWxpZGF0aW9uX3ByaXZhdGVfa2V5IjoiOWVkNDVmODY2MjQxY2MxOGEyNzQ3Yj"
382  "U0Mzg3YzA2MjU5MDc5NzJmNGU3MTkwMjMxZmFhOTM3NDU3ZmE5ZGFmNiIsIm1hbmlm"
383  "ZXN0IjoiSkFBQUFBRnhJZTFGdHdtaW12R3RIMmlDY01KcUM5Z1ZGS2lsR2Z3MS92Q3"
384  "hIWFhMcGxjMkduTWhBa0UxYWdxWHhCd0R3RGJJRDZPTVNZdU0wRkRBbHBBZ05rOFNL"
385  "Rm43TU8yZmRrY3dSUUloQU9uZ3U5c0FLcVhZb3VKK2wyVjBXK3NBT2tWQitaUlM2UF"
386  "NobEpBZlVzWGZBaUJzVkpHZXNhYWRPSmMvYUFab2tTMXZ5bUdtVnJsSFBLV1gzWXl3"
387  "dTZpbjhIQVNRS1B1Z0JENjdrTWFSRkd2bXBBVEhsR0tKZHZERmxXUFl5NUFxRGVkRn"
388  "Y1VEphMncwaTIxZXEzTVl5d0xWSlpuRk9yN0Mwa3cyQWlUelNDakl6ZGl0UTg9In0"
389  "=";
390 
391  {
392  Config c;
393  static boost::format configTemplate(R"rippleConfig(
394 [validation_seed]
395 %1%
396 
397 [validator_token]
398 %2%
399 )rippleConfig");
400  std::string error;
401  auto const expectedError =
402  "Cannot have both [validation_seed] "
403  "and [validator_token] config sections";
404  try
405  {
406  c.loadFromString(
407  boost::str(configTemplate % validationSeed % token));
408  }
409  catch (std::runtime_error& e)
410  {
411  error = e.what();
412  }
413  BEAST_EXPECT(error == expectedError);
414  }
415  }
416 
417  void
419  {
420  testcase("validators_file");
421 
422  using namespace boost::filesystem;
423  {
424  // load should throw for missing specified validators file
425  boost::format cc("[validators_file]\n%1%\n");
426  std::string error;
427  std::string const missingPath = "/no/way/this/path/exists";
428  auto const expectedError =
429  "The file specified in [validators_file] does not exist: " +
430  missingPath;
431  try
432  {
433  Config c;
434  c.loadFromString(boost::str(cc % missingPath));
435  }
436  catch (std::runtime_error& e)
437  {
438  error = e.what();
439  }
440  BEAST_EXPECT(error == expectedError);
441  }
442  {
443  // load should throw for invalid [validators_file]
444  detail::ValidatorsTxtGuard const vtg(
445  *this, "test_cfg", "validators.cfg");
446  path const invalidFile = current_path() / vtg.subdir();
447  boost::format cc("[validators_file]\n%1%\n");
448  std::string error;
449  auto const expectedError =
450  "Invalid file specified in [validators_file]: " +
451  invalidFile.string();
452  try
453  {
454  Config c;
455  c.loadFromString(boost::str(cc % invalidFile.string()));
456  }
457  catch (std::runtime_error& e)
458  {
459  error = e.what();
460  }
461  BEAST_EXPECT(error == expectedError);
462  }
463  {
464  // load validators from config into single section
465  Config c;
466  std::string toLoad(R"rippleConfig(
467 [validators]
468 n949f75evCHwgyP4fPVgaHqNHxUVN15PsJEZ3B3HnXPcPjcZAoy7
469 n9MD5h24qrQqiyBC8aeqqCWvpiBiYQ3jxSr91uiDvmrkyHRdYLUj
470 n9L81uNCaPgtUJfaHh89gmdvXKAmSt5Gdsw2g1iPWaPkAHW5Nm4C
471 
472 [validator_keys]
473 nHUhG1PgAG8H8myUENypM35JgfqXAKNQvRVVAFDRzJrny5eZN8d5
474 nHBu9PTL9dn2GuZtdW4U2WzBwffyX9qsQCd9CNU4Z5YG3PQfViM8
475 )rippleConfig");
476  c.loadFromString(toLoad);
477  BEAST_EXPECT(c.legacy("validators_file").empty());
478  BEAST_EXPECT(c.section(SECTION_VALIDATORS).values().size() == 5);
479  }
480  {
481  // load validator list sites and keys from config
482  Config c;
483  std::string toLoad(R"rippleConfig(
484 [validator_list_sites]
485 ripplevalidators.com
486 trustthesevalidators.gov
487 
488 [validator_list_keys]
489 021A99A537FDEBC34E4FCA03B39BEADD04299BB19E85097EC92B15A3518801E566
490 )rippleConfig");
491  c.loadFromString(toLoad);
492  BEAST_EXPECT(
493  c.section(SECTION_VALIDATOR_LIST_SITES).values().size() == 2);
494  BEAST_EXPECT(
495  c.section(SECTION_VALIDATOR_LIST_SITES).values()[0] ==
496  "ripplevalidators.com");
497  BEAST_EXPECT(
498  c.section(SECTION_VALIDATOR_LIST_SITES).values()[1] ==
499  "trustthesevalidators.gov");
500  BEAST_EXPECT(
501  c.section(SECTION_VALIDATOR_LIST_KEYS).values().size() == 1);
502  BEAST_EXPECT(
503  c.section(SECTION_VALIDATOR_LIST_KEYS).values()[0] ==
504  "021A99A537FDEBC34E4FCA03B39BEADD04299BB19E85097EC92B15A3518801"
505  "E566");
506  }
507  {
508  // load should throw if [validator_list_sites] is configured but
509  // [validator_list_keys] is not
510  Config c;
511  std::string toLoad(R"rippleConfig(
512 [validator_list_sites]
513 ripplevalidators.com
514 trustthesevalidators.gov
515 )rippleConfig");
516  std::string error;
517  auto const expectedError =
518  "[validator_list_keys] config section is missing";
519  try
520  {
521  c.loadFromString(toLoad);
522  }
523  catch (std::runtime_error& e)
524  {
525  error = e.what();
526  }
527  BEAST_EXPECT(error == expectedError);
528  }
529  {
530  // load from specified [validators_file] absolute path
531  detail::ValidatorsTxtGuard const vtg(
532  *this, "test_cfg", "validators.cfg");
533  BEAST_EXPECT(vtg.validatorsFileExists());
534  Config c;
535  boost::format cc("[validators_file]\n%1%\n");
536  c.loadFromString(boost::str(cc % vtg.validatorsFile()));
537  BEAST_EXPECT(c.legacy("validators_file") == vtg.validatorsFile());
538  BEAST_EXPECT(c.section(SECTION_VALIDATORS).values().size() == 8);
539  BEAST_EXPECT(
540  c.section(SECTION_VALIDATOR_LIST_SITES).values().size() == 2);
541  BEAST_EXPECT(
542  c.section(SECTION_VALIDATOR_LIST_KEYS).values().size() == 2);
543  }
544  {
545  // load from specified [validators_file] file name
546  // in config directory
547  std::string const valFileName = "validators.txt";
548  detail::ValidatorsTxtGuard const vtg(
549  *this, "test_cfg", valFileName);
550  detail::RippledCfgGuard const rcg(
551  *this, vtg.subdir(), "", valFileName, false);
552  BEAST_EXPECT(vtg.validatorsFileExists());
553  BEAST_EXPECT(rcg.configFileExists());
554  auto const& c(rcg.config());
555  BEAST_EXPECT(c.legacy("validators_file") == valFileName);
556  BEAST_EXPECT(c.section(SECTION_VALIDATORS).values().size() == 8);
557  BEAST_EXPECT(
558  c.section(SECTION_VALIDATOR_LIST_SITES).values().size() == 2);
559  BEAST_EXPECT(
560  c.section(SECTION_VALIDATOR_LIST_KEYS).values().size() == 2);
561  }
562  {
563  // load from specified [validators_file] relative path
564  // to config directory
565  detail::ValidatorsTxtGuard const vtg(
566  *this, "test_cfg", "validators.txt");
567  auto const valFilePath = ".." / vtg.subdir() / "validators.txt";
568  detail::RippledCfgGuard const rcg(
569  *this, vtg.subdir(), "", valFilePath, false);
570  BEAST_EXPECT(vtg.validatorsFileExists());
571  BEAST_EXPECT(rcg.configFileExists());
572  auto const& c(rcg.config());
573  BEAST_EXPECT(c.legacy("validators_file") == valFilePath);
574  BEAST_EXPECT(c.section(SECTION_VALIDATORS).values().size() == 8);
575  BEAST_EXPECT(
576  c.section(SECTION_VALIDATOR_LIST_SITES).values().size() == 2);
577  BEAST_EXPECT(
578  c.section(SECTION_VALIDATOR_LIST_KEYS).values().size() == 2);
579  }
580  {
581  // load from validators file in default location
582  detail::ValidatorsTxtGuard const vtg(
583  *this, "test_cfg", "validators.txt");
584  detail::RippledCfgGuard const rcg(
585  *this, vtg.subdir(), "", "", false);
586  BEAST_EXPECT(vtg.validatorsFileExists());
587  BEAST_EXPECT(rcg.configFileExists());
588  auto const& c(rcg.config());
589  BEAST_EXPECT(c.legacy("validators_file").empty());
590  BEAST_EXPECT(c.section(SECTION_VALIDATORS).values().size() == 8);
591  BEAST_EXPECT(
592  c.section(SECTION_VALIDATOR_LIST_SITES).values().size() == 2);
593  BEAST_EXPECT(
594  c.section(SECTION_VALIDATOR_LIST_KEYS).values().size() == 2);
595  }
596  {
597  // load from specified [validators_file] instead
598  // of default location
599  detail::ValidatorsTxtGuard const vtg(
600  *this, "test_cfg", "validators.cfg");
601  BEAST_EXPECT(vtg.validatorsFileExists());
602  detail::ValidatorsTxtGuard const vtgDefault(
603  *this, vtg.subdir(), "validators.txt", false);
604  BEAST_EXPECT(vtgDefault.validatorsFileExists());
605  detail::RippledCfgGuard const rcg(
606  *this, vtg.subdir(), "", vtg.validatorsFile(), false);
607  BEAST_EXPECT(rcg.configFileExists());
608  auto const& c(rcg.config());
609  BEAST_EXPECT(c.legacy("validators_file") == vtg.validatorsFile());
610  BEAST_EXPECT(c.section(SECTION_VALIDATORS).values().size() == 8);
611  BEAST_EXPECT(
612  c.section(SECTION_VALIDATOR_LIST_SITES).values().size() == 2);
613  BEAST_EXPECT(
614  c.section(SECTION_VALIDATOR_LIST_KEYS).values().size() == 2);
615  }
616 
617  {
618  // load validators from both config and validators file
619  boost::format cc(R"rippleConfig(
620 [validators_file]
621 %1%
622 
623 [validators]
624 n949f75evCHwgyP4fPVgaHqNHxUVN15PsJEZ3B3HnXPcPjcZAoy7
625 n9MD5h24qrQqiyBC8aeqqCWvpiBiYQ3jxSr91uiDvmrkyHRdYLUj
626 n9L81uNCaPgtUJfaHh89gmdvXKAmSt5Gdsw2g1iPWaPkAHW5Nm4C
627 n9KiYM9CgngLvtRCQHZwgC2gjpdaZcCcbt3VboxiNFcKuwFVujzS
628 n9LdgEtkmGB9E2h3K4Vp7iGUaKuq23Zr32ehxiU8FWY7xoxbWTSA
629 
630 [validator_keys]
631 nHB1X37qrniVugfQcuBTAjswphC1drx7QjFFojJPZwKHHnt8kU7v
632 nHUkAWDR4cB8AgPg7VXMX6et8xRTQb2KJfgv1aBEXozwrawRKgMB
633 
634 [validator_list_sites]
635 ripplevalidators.com
636 trustthesevalidators.gov
637 
638 [validator_list_keys]
639 021A99A537FDEBC34E4FCA03B39BEADD04299BB19E85097EC92B15A3518801E566
640 )rippleConfig");
641  detail::ValidatorsTxtGuard const vtg(
642  *this, "test_cfg", "validators.cfg");
643  BEAST_EXPECT(vtg.validatorsFileExists());
644  Config c;
645  c.loadFromString(boost::str(cc % vtg.validatorsFile()));
646  BEAST_EXPECT(c.legacy("validators_file") == vtg.validatorsFile());
647  BEAST_EXPECT(c.section(SECTION_VALIDATORS).values().size() == 15);
648  BEAST_EXPECT(
649  c.section(SECTION_VALIDATOR_LIST_SITES).values().size() == 4);
650  BEAST_EXPECT(
651  c.section(SECTION_VALIDATOR_LIST_KEYS).values().size() == 3);
652  }
653  {
654  // load should throw if [validators], [validator_keys] and
655  // [validator_list_keys] are missing from rippled cfg and
656  // validators file
657  Config c;
658  boost::format cc("[validators_file]\n%1%\n");
659  std::string error;
660  detail::ValidatorsTxtGuard const vtg(
661  *this, "test_cfg", "validators.cfg");
662  BEAST_EXPECT(vtg.validatorsFileExists());
663  auto const expectedError =
664  "The file specified in [validators_file] does not contain a "
665  "[validators], [validator_keys] or [validator_list_keys] "
666  "section: " +
667  vtg.validatorsFile();
668  std::ofstream o(vtg.validatorsFile());
669  try
670  {
671  Config c2;
672  c2.loadFromString(boost::str(cc % vtg.validatorsFile()));
673  }
674  catch (std::runtime_error& e)
675  {
676  error = e.what();
677  }
678  BEAST_EXPECT(error == expectedError);
679  }
680  }
681 
682  void
683  testSetup(bool explicitPath)
684  {
685  detail::RippledCfgGuard const cfg(
686  *this, "testSetup", explicitPath ? "test_db" : "", "");
687  /* RippledCfgGuard has a Config object that gets loaded on
688  construction, but Config::setup is not reentrant, so we
689  need a fresh config for every test case, so ignore it.
690  */
691  {
692  Config config;
693  config.setup(
694  cfg.configFile(),
695  /*bQuiet*/ false,
696  /* bSilent */ false,
697  /* bStandalone */ false);
698  BEAST_EXPECT(!config.quiet());
699  BEAST_EXPECT(!config.silent());
700  BEAST_EXPECT(!config.standalone());
701  BEAST_EXPECT(config.LEDGER_HISTORY == 256);
702  BEAST_EXPECT(!config.legacy("database_path").empty());
703  }
704  {
705  Config config;
706  config.setup(
707  cfg.configFile(),
708  /*bQuiet*/ true,
709  /* bSilent */ false,
710  /* bStandalone */ false);
711  BEAST_EXPECT(config.quiet());
712  BEAST_EXPECT(!config.silent());
713  BEAST_EXPECT(!config.standalone());
714  BEAST_EXPECT(config.LEDGER_HISTORY == 256);
715  BEAST_EXPECT(!config.legacy("database_path").empty());
716  }
717  {
718  Config config;
719  config.setup(
720  cfg.configFile(),
721  /*bQuiet*/ false,
722  /* bSilent */ true,
723  /* bStandalone */ false);
724  BEAST_EXPECT(config.quiet());
725  BEAST_EXPECT(config.silent());
726  BEAST_EXPECT(!config.standalone());
727  BEAST_EXPECT(config.LEDGER_HISTORY == 256);
728  BEAST_EXPECT(!config.legacy("database_path").empty());
729  }
730  {
731  Config config;
732  config.setup(
733  cfg.configFile(),
734  /*bQuiet*/ true,
735  /* bSilent */ true,
736  /* bStandalone */ false);
737  BEAST_EXPECT(config.quiet());
738  BEAST_EXPECT(config.silent());
739  BEAST_EXPECT(!config.standalone());
740  BEAST_EXPECT(config.LEDGER_HISTORY == 256);
741  BEAST_EXPECT(!config.legacy("database_path").empty());
742  }
743  {
744  Config config;
745  config.setup(
746  cfg.configFile(),
747  /*bQuiet*/ false,
748  /* bSilent */ false,
749  /* bStandalone */ true);
750  BEAST_EXPECT(!config.quiet());
751  BEAST_EXPECT(!config.silent());
752  BEAST_EXPECT(config.standalone());
753  BEAST_EXPECT(config.LEDGER_HISTORY == 0);
754  BEAST_EXPECT(
755  config.legacy("database_path").empty() == !explicitPath);
756  }
757  {
758  Config config;
759  config.setup(
760  cfg.configFile(),
761  /*bQuiet*/ true,
762  /* bSilent */ false,
763  /* bStandalone */ true);
764  BEAST_EXPECT(config.quiet());
765  BEAST_EXPECT(!config.silent());
766  BEAST_EXPECT(config.standalone());
767  BEAST_EXPECT(config.LEDGER_HISTORY == 0);
768  BEAST_EXPECT(
769  config.legacy("database_path").empty() == !explicitPath);
770  }
771  {
772  Config config;
773  config.setup(
774  cfg.configFile(),
775  /*bQuiet*/ false,
776  /* bSilent */ true,
777  /* bStandalone */ true);
778  BEAST_EXPECT(config.quiet());
779  BEAST_EXPECT(config.silent());
780  BEAST_EXPECT(config.standalone());
781  BEAST_EXPECT(config.LEDGER_HISTORY == 0);
782  BEAST_EXPECT(
783  config.legacy("database_path").empty() == !explicitPath);
784  }
785  {
786  Config config;
787  config.setup(
788  cfg.configFile(),
789  /*bQuiet*/ true,
790  /* bSilent */ true,
791  /* bStandalone */ true);
792  BEAST_EXPECT(config.quiet());
793  BEAST_EXPECT(config.silent());
794  BEAST_EXPECT(config.standalone());
795  BEAST_EXPECT(config.LEDGER_HISTORY == 0);
796  BEAST_EXPECT(
797  config.legacy("database_path").empty() == !explicitPath);
798  }
799  }
800 
801  void
803  {
804  detail::RippledCfgGuard const cfg(*this, "testPort", "", "");
805  auto const& conf = cfg.config();
806  if (!BEAST_EXPECT(conf.exists("port_rpc")))
807  return;
808  if (!BEAST_EXPECT(conf.exists("port_wss_admin")))
809  return;
810  ParsedPort rpc;
811  if (!unexcept([&]() { parse_Port(rpc, conf["port_rpc"], log); }))
812  return;
813  BEAST_EXPECT(rpc.admin_ip && (rpc.admin_ip.value().size() == 2));
814  ParsedPort wss;
815  if (!unexcept([&]() { parse_Port(wss, conf["port_wss_admin"], log); }))
816  return;
817  BEAST_EXPECT(wss.admin_ip && (wss.admin_ip.value().size() == 1));
818  }
819 
820  void
822  {
823  Config cfg;
824  /* NOTE: this string includes some explicit
825  * space chars in order to verify proper trimming */
826  std::string toLoad(R"(
827 [port_rpc])"
828  "\x20"
829  R"(
830 # comment
831  # indented comment
832 )"
833  "\x20\x20"
834  R"(
835 [ips])"
836  "\x20"
837  R"(
838 r.ripple.com 51235
839 
840  [ips_fixed])"
841  "\x20\x20"
842  R"(
843  # COMMENT
844  s1.ripple.com 51235
845  s2.ripple.com 51235
846 
847 )");
848  cfg.loadFromString(toLoad);
849  BEAST_EXPECT(
850  cfg.exists("port_rpc") && cfg.section("port_rpc").lines().empty() &&
851  cfg.section("port_rpc").values().empty());
852  BEAST_EXPECT(
853  cfg.exists(SECTION_IPS) &&
854  cfg.section(SECTION_IPS).lines().size() == 1 &&
855  cfg.section(SECTION_IPS).values().size() == 1);
856  BEAST_EXPECT(
857  cfg.exists(SECTION_IPS_FIXED) &&
858  cfg.section(SECTION_IPS_FIXED).lines().size() == 2 &&
859  cfg.section(SECTION_IPS_FIXED).values().size() == 2);
860  }
861 
862  void
863  testComments()
864  {
865  struct TestCommentData
866  {
867  std::string_view line;
868  std::string_view field;
869  std::string_view expect;
870  bool had_comment;
871  };
872 
874  {{"password = aaaa\\#bbbb", "password", "aaaa#bbbb", false},
875  {"password = aaaa#bbbb", "password", "aaaa", true},
876  {"password = aaaa #bbbb", "password", "aaaa", true},
877  // since the value is all comment, this doesn't parse as k=v :
878  {"password = #aaaa #bbbb", "", "password =", true},
879  {"password = aaaa\\# #bbbb", "password", "aaaa#", true},
880  {"password = aaaa\\##bbbb", "password", "aaaa#", true},
881  {"aaaa#bbbb", "", "aaaa", true},
882  {"aaaa\\#bbbb", "", "aaaa#bbbb", false},
883  {"aaaa\\##bbbb", "", "aaaa#", true},
884  {"aaaa #bbbb", "", "aaaa", true},
885  {"1 #comment", "", "1", true},
886  {"#whole thing is comment", "", "", false},
887  {" #whole comment with space", "", "", false}}};
888 
889  for (auto const& t : tests)
890  {
891  Section s;
892  s.append(t.line.data());
893  BEAST_EXPECT(s.had_trailing_comments() == t.had_comment);
894  if (t.field.empty())
895  {
896  BEAST_EXPECTS(s.legacy() == t.expect, s.legacy());
897  }
898  else
899  {
901  BEAST_EXPECTS(set(field, t.field.data(), s), t.line);
902  BEAST_EXPECTS(field == t.expect, t.line);
903  }
904  }
905 
906  {
907  Section s;
908  s.append("online_delete = 3000");
909  std::uint32_t od = 0;
910  BEAST_EXPECT(set(od, "online_delete", s));
911  BEAST_EXPECTS(od == 3000, *(s.get<std::string>("online_delete")));
912  }
913 
914  {
915  Section s;
916  s.append("online_delete = 2000 #my comment on this");
917  std::uint32_t od = 0;
918  BEAST_EXPECT(set(od, "online_delete", s));
919  BEAST_EXPECTS(od == 2000, *(s.get<std::string>("online_delete")));
920  }
921  }
922 
923  void
924  testGetters()
925  {
926  using namespace std::string_literals;
927  Section s{"MySection"};
928  s.append("a_string = mystring");
929  s.append("positive_int = 2");
930  s.append("negative_int = -3");
931  s.append("bool_ish = 1");
932 
933  {
934  auto val_1 = "value 1"s;
935  BEAST_EXPECT(set(val_1, "a_string", s));
936  BEAST_EXPECT(val_1 == "mystring");
937 
938  auto val_2 = "value 2"s;
939  BEAST_EXPECT(!set(val_2, "not_a_key", s));
940  BEAST_EXPECT(val_2 == "value 2");
941  BEAST_EXPECT(!set(val_2, "default"s, "not_a_key", s));
942  BEAST_EXPECT(val_2 == "default");
943 
944  auto val_3 = get<std::string>(s, "a_string");
945  BEAST_EXPECT(val_3 == "mystring");
946  auto val_4 = get<std::string>(s, "not_a_key");
947  BEAST_EXPECT(val_4 == "");
948  auto val_5 = get<std::string>(s, "not_a_key", "default");
949  BEAST_EXPECT(val_5 == "default");
950 
951  auto val_6 = "value 6"s;
952  BEAST_EXPECT(get_if_exists(s, "a_string", val_6));
953  BEAST_EXPECT(val_6 == "mystring");
954 
955  auto val_7 = "value 7"s;
956  BEAST_EXPECT(!get_if_exists(s, "not_a_key", val_7));
957  BEAST_EXPECT(val_7 == "value 7");
958  }
959 
960  {
961  int val_1 = 1;
962  BEAST_EXPECT(set(val_1, "positive_int", s));
963  BEAST_EXPECT(val_1 == 2);
964 
965  int val_2 = 2;
966  BEAST_EXPECT(set(val_2, "negative_int", s));
967  BEAST_EXPECT(val_2 == -3);
968 
969  int val_3 = 3;
970  BEAST_EXPECT(!set(val_3, "a_string", s));
971  BEAST_EXPECT(val_3 == 3);
972 
973  auto val_4 = get<int>(s, "positive_int");
974  BEAST_EXPECT(val_4 == 2);
975  auto val_5 = get<int>(s, "not_a_key");
976  BEAST_EXPECT(val_5 == 0);
977  auto val_6 = get<int>(s, "not_a_key", 5);
978  BEAST_EXPECT(val_6 == 5);
979  auto val_7 = get<int>(s, "a_string", 6);
980  BEAST_EXPECT(val_7 == 6);
981 
982  int val_8 = 8;
983  BEAST_EXPECT(get_if_exists(s, "positive_int", val_8));
984  BEAST_EXPECT(val_8 == 2);
985 
986  auto val_9 = 9;
987  BEAST_EXPECT(!get_if_exists(s, "not_a_key", val_9));
988  BEAST_EXPECT(val_9 == 9);
989 
990  auto val_10 = 10;
991  BEAST_EXPECT(!get_if_exists(s, "a_string", val_10));
992  BEAST_EXPECT(val_10 == 10);
993 
994  BEAST_EXPECT(s.get<int>("not_a_key") == std::nullopt);
995  try
996  {
997  s.get<int>("a_string");
998  fail();
999  }
1000  catch (boost::bad_lexical_cast&)
1001  {
1002  pass();
1003  }
1004  }
1005 
1006  {
1007  bool flag_1 = false;
1008  BEAST_EXPECT(get_if_exists(s, "bool_ish", flag_1));
1009  BEAST_EXPECT(flag_1 == true);
1010 
1011  bool flag_2 = false;
1012  BEAST_EXPECT(!get_if_exists(s, "not_a_key", flag_2));
1013  BEAST_EXPECT(flag_2 == false);
1014  }
1015  }
1016 
1017  void
1018  testAmendment()
1019  {
1020  testcase("amendment");
1021  struct ConfigUnit
1022  {
1023  std::string unit;
1024  std::uint32_t numSeconds;
1025  std::uint32_t configVal;
1026  bool shouldPass;
1027  };
1028 
1029  std::vector<ConfigUnit> units = {
1030  {"seconds", 1, 15 * 60, false},
1031  {"minutes", 60, 14, false},
1032  {"minutes", 60, 15, true},
1033  {"hours", 3600, 10, true},
1034  {"days", 86400, 10, true},
1035  {"weeks", 604800, 2, true},
1036  {"months", 2592000, 1, false},
1037  {"years", 31536000, 1, false}};
1038 
1039  std::string space = "";
1040  for (auto& [unit, sec, val, shouldPass] : units)
1041  {
1042  Config c;
1043  std::string toLoad(R"rippleConfig(
1044 [amendment_majority_time]
1045 )rippleConfig");
1046  toLoad += std::to_string(val) + space + unit;
1047  space = space == "" ? " " : "";
1048 
1049  try
1050  {
1051  c.loadFromString(toLoad);
1052  if (shouldPass)
1053  BEAST_EXPECT(
1054  c.AMENDMENT_MAJORITY_TIME.count() == val * sec);
1055  else
1056  fail();
1057  }
1058  catch (std::runtime_error&)
1059  {
1060  if (!shouldPass)
1061  pass();
1062  else
1063  fail();
1064  }
1065  }
1066  }
1067 
1068  void
1069  testOverlay()
1070  {
1071  testcase("overlay: unknown time");
1072 
1073  auto testUnknown =
1075  try
1076  {
1077  Config c;
1078  c.loadFromString("[overlay]\nmax_unknown_time=" + value);
1079  return c.MAX_UNKNOWN_TIME;
1080  }
1081  catch (std::runtime_error&)
1082  {
1083  return {};
1084  }
1085  };
1086 
1087  // Failures
1088  BEAST_EXPECT(!testUnknown("none"));
1089  BEAST_EXPECT(!testUnknown("0.5"));
1090  BEAST_EXPECT(!testUnknown("180 seconds"));
1091  BEAST_EXPECT(!testUnknown("9 minutes"));
1092 
1093  // Below lower bound
1094  BEAST_EXPECT(!testUnknown("299"));
1095 
1096  // In bounds
1097  BEAST_EXPECT(testUnknown("300") == std::chrono::seconds{300});
1098  BEAST_EXPECT(testUnknown("301") == std::chrono::seconds{301});
1099  BEAST_EXPECT(testUnknown("1799") == std::chrono::seconds{1799});
1100  BEAST_EXPECT(testUnknown("1800") == std::chrono::seconds{1800});
1101 
1102  // Above upper bound
1103  BEAST_EXPECT(!testUnknown("1801"));
1104 
1105  testcase("overlay: diverged time");
1106 
1107  // In bounds:
1108  auto testDiverged =
1110  try
1111  {
1112  Config c;
1113  c.loadFromString("[overlay]\nmax_diverged_time=" + value);
1114  return c.MAX_DIVERGED_TIME;
1115  }
1116  catch (std::runtime_error&)
1117  {
1118  return {};
1119  }
1120  };
1121 
1122  // Failures
1123  BEAST_EXPECT(!testDiverged("none"));
1124  BEAST_EXPECT(!testDiverged("0.5"));
1125  BEAST_EXPECT(!testDiverged("180 seconds"));
1126  BEAST_EXPECT(!testDiverged("9 minutes"));
1127 
1128  // Below lower bound
1129  BEAST_EXPECT(!testDiverged("0"));
1130  BEAST_EXPECT(!testDiverged("59"));
1132  // In bounds
1133  BEAST_EXPECT(testDiverged("60") == std::chrono::seconds{60});
1134  BEAST_EXPECT(testDiverged("61") == std::chrono::seconds{61});
1135  BEAST_EXPECT(testDiverged("899") == std::chrono::seconds{899});
1136  BEAST_EXPECT(testDiverged("900") == std::chrono::seconds{900});
1137 
1138  // Above upper bound
1139  BEAST_EXPECT(!testDiverged("901"));
1140  }
1141 
1142  void
1143  run() override
1144  {
1145  testLegacy();
1146  testDbPath();
1147  testValidatorKeys();
1148  testValidatorsFile();
1149  testSetup(false);
1150  testSetup(true);
1151  testPort();
1152  testWhitespace();
1153  testComments();
1154  testGetters();
1155  testAmendment();
1156  testOverlay();
1157  }
1158 };
1159 
1160 BEAST_DEFINE_TESTSUITE(Config, core, ripple);
1161 
1162 } // namespace ripple
ripple::Config_test::testWhitespace
void testWhitespace()
Definition: Config_test.cpp:821
ripple::Section
Holds a collection of configuration values.
Definition: BasicConfig.h:42
ripple::Config_test
Definition: Config_test.cpp:272
ripple::ParsedPort::admin_ip
std::optional< std::vector< beast::IP::Address > > admin_ip
Definition: Port.h:111
ripple::detail::RippledCfgGuard::RippledCfgGuard
RippledCfgGuard(beast::unit_test::suite &test, path subDir, path const &dbPath, path const &validatorsFile, bool useCounter=true)
Definition: Config_test.cpp:137
fstream
std::string
STL class.
ripple::BEAST_DEFINE_TESTSUITE
BEAST_DEFINE_TESTSUITE(AccountTxPaging, app, ripple)
ripple::detail::ValidatorsTxtGuard::ValidatorsTxtGuard
ValidatorsTxtGuard(beast::unit_test::suite &test, path subDir, path const &validatorsFileName, bool useCounter=true)
Definition: Config_test.cpp:238
std::exception
STL class.
std::string_view
STL class.
ripple::test::detail::DirGuard
Create a directory and remove it when it's done.
Definition: FileDirGuard.h:34
ripple::Config_test::testSetup
void testSetup(bool explicitPath)
Definition: Config_test.cpp:683
std::vector
STL class.
std::vector::size
T size(T... args)
ripple::detail::RippledCfgGuard::config_
Config config_
Definition: Config_test.cpp:134
std::chrono::seconds
ripple::Config::silent
bool silent() const
Definition: Config.h:306
ripple::detail::ValidatorsTxtGuard::validatorsFile
std::string validatorsFile() const
Definition: Config_test.cpp:261
ripple::Config_test::testDbPath
void testDbPath()
Definition: Config_test.cpp:306
ripple::parse_Port
void parse_Port(ParsedPort &port, Section const &section, std::ostream &log)
Definition: Port.cpp:139
ripple::Config_test::path
boost::filesystem::path path
Definition: Config_test.cpp:275
ripple::ParsedPort
Definition: Port.h:91
iostream
ripple::Config::quiet
bool quiet() const
Definition: Config.h:301
ripple::test::detail::DirGuard::subdir
path const & subdir() const
Definition: FileDirGuard.h:101
ripple::Section::values
std::vector< std::string > const & values() const
Returns all the values in the section.
Definition: BasicConfig.h:77
ripple::detail::RippledCfgGuard::~RippledCfgGuard
~RippledCfgGuard()
Definition: Config_test.cpp:186
ripple::get_if_exists
bool get_if_exists(Section const &section, std::string const &name, T &v)
Definition: BasicConfig.h:384
ripple::Section::append
void append(std::vector< std::string > const &lines)
Append a set of lines to this section.
Definition: BasicConfig.cpp:38
ripple::Config_test::testLegacy
void testLegacy()
Definition: Config_test.cpp:279
ripple::test::detail::FileDirGuard::file
path const & file() const
Definition: FileDirGuard.h:167
ripple::detail::ValidatorsTxtGuard::validatorsFileExists
bool validatorsFileExists() const
Definition: Config_test.cpp:255
ripple::Config::loadFromString
void loadFromString(std::string const &fileContents)
Load the config from the contents of the string.
Definition: Config.cpp:457
ripple::detail::RippledCfgGuard::configFile
std::string configFile() const
Definition: Config_test.cpp:169
ripple::Config
Definition: Config.h:68
std::ofstream
STL class.
ripple::Config_test::testPort
void testPort()
Definition: Config_test.cpp:802
ripple::Config::standalone
bool standalone() const
Definition: Config.h:311
std::to_string
T to_string(T... args)
ripple::set
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,...
Definition: BasicConfig.h:313
std::array
STL class.
ripple::validationSeed
static std::optional< Seed > validationSeed(Json::Value const &params)
Definition: ValidationCreate.cpp:30
ripple::BasicConfig::legacy
void legacy(std::string const &section, std::string value)
Set a value that is not a key/value pair.
Definition: BasicConfig.cpp:164
ripple::Section::lines
std::vector< std::string > const & lines() const
Returns all the lines in the section.
Definition: BasicConfig.h:68
ripple::detail::valFileContents
std::string valFileContents()
Definition: Config_test.cpp:206
std::runtime_error
STL class.
ripple::test::detail::DirGuard::test_
beast::unit_test::suite & test_
Definition: FileDirGuard.h:44
std::uint32_t
ripple::Config::databaseDirName
static char const *const databaseDirName
Definition: Config.h:73
ripple::detail::RippledCfgGuard::configFileExists
bool configFileExists() const
Definition: Config_test.cpp:181
ripple::detail::RippledCfgGuard
Write a rippled config file and remove when done.
Definition: Config_test.cpp:127
ripple::Config::LEDGER_HISTORY
std::uint32_t LEDGER_HISTORY
Definition: Config.h:191
ripple::TestSuite
Definition: TestSuite.h:28
std::experimental::filesystem::space
T space(T... args)
std::optional::value
T value(T... args)
beast::field
field_t< CharT, Traits, Allocator > field(std::basic_string< CharT, Traits, Allocator > const &text, int width=8, int pad=0, bool right=false)
Definition: iosformat.h:162
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
ripple::test::detail::FileDirGuard::FileDirGuard
FileDirGuard(beast::unit_test::suite &test, path subDir, path file, std::string const &contents, bool useCounter=true, bool create=true)
Definition: FileDirGuard.h:117
ripple::detail::RippledCfgGuard::rmDataDir_
bool rmDataDir_
Definition: Config_test.cpp:132
ripple::Config::setup
void setup(std::string const &strConf, bool bQuiet, bool bSilent, bool bStandalone)
Definition: Config.cpp:315
std::endl
T endl(T... args)
ripple::Config_test::testValidatorsFile
void testValidatorsFile()
Definition: Config_test.cpp:418
std
STL namespace.
ripple::Section::get
std::optional< T > get(std::string const &name) const
Definition: BasicConfig.h:138
std::string::empty
T empty(T... args)
std::optional< std::chrono::seconds >
ripple::test::detail::FileDirGuard::fileExists
bool fileExists() const
Definition: FileDirGuard.h:173
ripple::test::detail::FileDirGuard
Write a file in a directory and remove when done.
Definition: FileDirGuard.h:110
ripple::test::detail::DirGuard::rmDir
auto rmDir(path const &toRm)
Definition: FileDirGuard.h:47
ripple::Config_test::testValidatorKeys
void testValidatorKeys()
Definition: Config_test.cpp:374
ripple::test::detail::FileDirGuard::file_
const path file_
Definition: FileDirGuard.h:113
ripple::detail::RippledCfgGuard::dataDirExists
bool dataDirExists() const
Definition: Config_test.cpp:175
ripple::detail::ValidatorsTxtGuard
Write a validators.txt file and remove when done.
Definition: Config_test.cpp:235
ripple::detail::configContents
std::string configContents(std::string const &dbPath, std::string const &validatorsFile)
Definition: Config_test.cpp:34
ripple::detail::RippledCfgGuard::dataDir_
path dataDir_
Definition: Config_test.cpp:130
ripple::test::detail::DirGuard::path
boost::filesystem::path path
Definition: FileDirGuard.h:37
ripple::detail::ValidatorsTxtGuard::~ValidatorsTxtGuard
~ValidatorsTxtGuard()
Definition: Config_test.cpp:266
std::exception::what
T what(T... args)
ripple::BasicConfig::exists
bool exists(std::string const &name) const
Returns true if a section with the given name exists.
Definition: BasicConfig.cpp:121
ripple::BasicConfig::section
Section & section(std::string const &name)
Returns the section with the given name.
Definition: BasicConfig.cpp:127
ripple::detail::RippledCfgGuard::config
Config const & config() const
Definition: Config_test.cpp:163