rippled
Config.cpp
1 //------------------------------------------------------------------------------
2 /*
3  This file is part of rippled: https://github.com/ripple/rippled
4  Copyright (c) 2012, 2013 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/FileUtilities.h>
21 #include <ripple/basics/Log.h>
22 #include <ripple/basics/StringUtilities.h>
23 #include <ripple/basics/contract.h>
24 #include <ripple/beast/core/LexicalCast.h>
25 #include <ripple/core/Config.h>
26 #include <ripple/core/ConfigSections.h>
27 #include <ripple/json/json_reader.h>
28 #include <ripple/net/HTTPClient.h>
29 #include <ripple/protocol/Feature.h>
30 #include <ripple/protocol/SystemParameters.h>
31 #include <boost/algorithm/string.hpp>
32 #include <boost/format.hpp>
33 #include <boost/predef.h>
34 #include <boost/regex.hpp>
35 #include <boost/system/error_code.hpp>
36 #include <algorithm>
37 #include <cstdlib>
38 #include <iostream>
39 #include <iterator>
40 #include <regex>
41 #include <thread>
42 
43 #if BOOST_OS_WINDOWS
44 #include <sysinfoapi.h>
45 
46 namespace ripple {
47 namespace detail {
48 
49 [[nodiscard]] std::uint64_t
50 getMemorySize()
51 {
52  if (MEMORYSTATUSEX msx{sizeof(MEMORYSTATUSEX)}; GlobalMemoryStatusEx(&msx))
53  return static_cast<std::uint64_t>(msx.ullTotalPhys);
54 
55  return 0;
56 }
57 
58 } // namespace detail
59 } // namespace ripple
60 #endif
61 
62 #if BOOST_OS_LINUX
63 #include <sys/sysinfo.h>
64 
65 namespace ripple {
66 namespace detail {
67 
68 [[nodiscard]] std::uint64_t
69 getMemorySize()
70 {
71  struct sysinfo si;
72 
73  if (sysinfo(&si) == 0)
74  return static_cast<std::uint64_t>(si.totalram);
75 
76  return 0;
77 }
78 
79 } // namespace detail
80 } // namespace ripple
81 
82 #endif
83 
84 #if BOOST_OS_MACOS
85 #include <sys/sysctl.h>
86 #include <sys/types.h>
87 
88 namespace ripple {
89 namespace detail {
90 
91 [[nodiscard]] std::uint64_t
92 getMemorySize()
93 {
94  int mib[] = {CTL_HW, HW_MEMSIZE};
95  std::int64_t ram = 0;
96  size_t size = sizeof(ram);
97 
98  if (sysctl(mib, 2, &ram, &size, NULL, 0) == 0)
99  return static_cast<std::uint64_t>(ram);
100 
101  return 0;
102 }
103 
104 } // namespace detail
105 } // namespace ripple
106 #endif
107 
108 namespace ripple {
109 
110 // clang-format off
111 // The configurable node sizes are "tiny", "small", "medium", "large", "huge"
114 {{
115  // FIXME: We should document each of these items, explaining exactly
116  // what they control and whether there exists an explicit
117  // config option that can be used to override the default.
118 
119  // tiny small medium large huge
120  {SizedItem::sweepInterval, {{ 10, 30, 60, 90, 120 }}},
121  {SizedItem::treeCacheSize, {{ 262144, 524288, 2097152, 4194304, 8388608 }}},
122  {SizedItem::treeCacheAge, {{ 30, 60, 90, 120, 900 }}},
123  {SizedItem::ledgerSize, {{ 32, 32, 64, 256, 384 }}},
124  {SizedItem::ledgerAge, {{ 30, 60, 180, 300, 600 }}},
125  {SizedItem::ledgerFetch, {{ 2, 3, 4, 5, 8 }}},
126  {SizedItem::hashNodeDBCache, {{ 4, 12, 24, 64, 128 }}},
127  {SizedItem::txnDBCache, {{ 4, 12, 24, 64, 128 }}},
128  {SizedItem::lgrDBCache, {{ 4, 8, 16, 32, 128 }}},
129  {SizedItem::openFinalLimit, {{ 8, 16, 32, 64, 128 }}},
130  {SizedItem::burstSize, {{ 4, 8, 16, 32, 48 }}},
131  {SizedItem::ramSizeGB, {{ 8, 12, 16, 24, 32 }}},
132  {SizedItem::accountIdCacheSize, {{ 20047, 50053, 77081, 150061, 300007 }}}
133 }};
134 
135 // Ensure that the order of entries in the table corresponds to the
136 // order of entries in the enum:
137 static_assert(
138  []() constexpr->bool {
139  std::underlying_type_t<SizedItem> idx = 0;
140 
141  for (auto const& i : sizedItems)
142  {
143  if (static_cast<std::underlying_type_t<SizedItem>>(i.first) != idx)
144  return false;
145 
146  ++idx;
147  }
148 
149  return true;
150  }(),
151  "Mismatch between sized item enum & array indices");
152 // clang-format on
153 
154 //
155 // TODO: Check permissions on config file before using it.
156 //
157 
158 #define SECTION_DEFAULT_NAME ""
159 
161 parseIniFile(std::string const& strInput, const bool bTrim)
162 {
163  std::string strData(strInput);
165  IniFileSections secResult;
166 
167  // Convert DOS format to unix.
168  boost::algorithm::replace_all(strData, "\r\n", "\n");
169 
170  // Convert MacOS format to unix.
171  boost::algorithm::replace_all(strData, "\r", "\n");
172 
173  boost::algorithm::split(vLines, strData, boost::algorithm::is_any_of("\n"));
174 
175  // Set the default Section name.
176  std::string strSection = SECTION_DEFAULT_NAME;
177 
178  // Initialize the default Section.
179  secResult[strSection] = IniFileSections::mapped_type();
180 
181  // Parse each line.
182  for (auto& strValue : vLines)
183  {
184  if (bTrim)
185  boost::algorithm::trim(strValue);
186 
187  if (strValue.empty() || strValue[0] == '#')
188  {
189  // Blank line or comment, do nothing.
190  }
191  else if (strValue[0] == '[' && strValue[strValue.length() - 1] == ']')
192  {
193  // New Section.
194  strSection = strValue.substr(1, strValue.length() - 2);
195  secResult.emplace(strSection, IniFileSections::mapped_type{});
196  }
197  else
198  {
199  // Another line for Section.
200  if (!strValue.empty())
201  secResult[strSection].push_back(strValue);
202  }
203  }
204 
205  return secResult;
206 }
207 
208 IniFileSections::mapped_type*
209 getIniFileSection(IniFileSections& secSource, std::string const& strSection)
210 {
211  IniFileSections::iterator it;
212  IniFileSections::mapped_type* smtResult;
213  it = secSource.find(strSection);
214  if (it == secSource.end())
215  smtResult = nullptr;
216  else
217  smtResult = &(it->second);
218  return smtResult;
219 }
220 
221 bool
223  IniFileSections& secSource,
224  std::string const& strSection,
225  std::string& strValue,
226  beast::Journal j)
227 {
228  IniFileSections::mapped_type* pmtEntries =
229  getIniFileSection(secSource, strSection);
230  bool bSingle = pmtEntries && 1 == pmtEntries->size();
231 
232  if (bSingle)
233  {
234  strValue = (*pmtEntries)[0];
235  }
236  else if (pmtEntries)
237  {
238  JLOG(j.warn()) << boost::str(
239  boost::format("Section [%s]: requires 1 line not %d lines.") %
240  strSection % pmtEntries->size());
241  }
242 
243  return bSingle;
244 }
245 
246 //------------------------------------------------------------------------------
247 //
248 // Config (DEPRECATED)
249 //
250 //------------------------------------------------------------------------------
251 
252 char const* const Config::configFileName = "rippled.cfg";
253 char const* const Config::databaseDirName = "db";
254 char const* const Config::validatorsFileName = "validators.txt";
255 
256 [[nodiscard]] static std::string
257 getEnvVar(char const* name)
258 {
259  std::string value;
260 
261  if (auto const v = std::getenv(name); v != nullptr)
262  value = v;
263 
264  return value;
265 }
266 
267 Config::Config()
268  : j_(beast::Journal::getNullSink()), ramSize_(detail::getMemorySize())
269 {
270 }
271 
272 void
273 Config::setupControl(bool bQuiet, bool bSilent, bool bStandalone)
274 {
275  assert(NODE_SIZE == 0);
276 
277  QUIET = bQuiet || bSilent;
278  SILENT = bSilent;
279  RUN_STANDALONE = bStandalone;
280 
281  // We try to autodetect the appropriate node size by checking available
282  // RAM and CPU resources. We default to "tiny" for standalone mode.
283  if (!bStandalone)
284  {
285  // First, check against 'minimum' RAM requirements per node size:
286  auto const& threshold =
288 
289  auto ns = std::find_if(
290  threshold.second.begin(),
291  threshold.second.end(),
292  [this](std::size_t limit) {
293  return (ramSize_ / (1024 * 1024 * 1024)) < limit;
294  });
295 
296  if (ns != threshold.second.end())
297  NODE_SIZE = std::distance(threshold.second.begin(), ns);
298 
299  // Adjust the size based on the number of hardware threads of
300  // execution available to us:
301  if (auto const hc = std::thread::hardware_concurrency())
302  {
303  if (hc == 1)
304  NODE_SIZE = 0;
305 
306  if (hc < 4)
307  NODE_SIZE = std::min<std::size_t>(NODE_SIZE, 1);
308  }
309  }
310 
311  assert(NODE_SIZE <= 4);
312 }
313 
314 void
316  std::string const& strConf,
317  bool bQuiet,
318  bool bSilent,
319  bool bStandalone)
320 {
321  boost::filesystem::path dataDir;
322  std::string strDbPath, strConfFile;
323 
324  // Determine the config and data directories.
325  // If the config file is found in the current working
326  // directory, use the current working directory as the
327  // config directory and that with "db" as the data
328  // directory.
329 
330  setupControl(bQuiet, bSilent, bStandalone);
331 
332  strDbPath = databaseDirName;
333 
334  if (!strConf.empty())
335  strConfFile = strConf;
336  else
337  strConfFile = configFileName;
338 
339  if (!strConf.empty())
340  {
341  // --conf=<path> : everything is relative that file.
342  CONFIG_FILE = strConfFile;
343  CONFIG_DIR = boost::filesystem::absolute(CONFIG_FILE);
344  CONFIG_DIR.remove_filename();
345  dataDir = CONFIG_DIR / strDbPath;
346  }
347  else
348  {
349  CONFIG_DIR = boost::filesystem::current_path();
350  CONFIG_FILE = CONFIG_DIR / strConfFile;
351  dataDir = CONFIG_DIR / strDbPath;
352 
353  // Construct XDG config and data home.
354  // http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
355  auto const strHome = getEnvVar("HOME");
356  auto strXdgConfigHome = getEnvVar("XDG_CONFIG_HOME");
357  auto strXdgDataHome = getEnvVar("XDG_DATA_HOME");
358 
359  if (boost::filesystem::exists(CONFIG_FILE)
360  // Can we figure out XDG dirs?
361  || (strHome.empty() &&
362  (strXdgConfigHome.empty() || strXdgDataHome.empty())))
363  {
364  // Current working directory is fine, put dbs in a subdir.
365  }
366  else
367  {
368  if (strXdgConfigHome.empty())
369  {
370  // $XDG_CONFIG_HOME was not set, use default based on $HOME.
371  strXdgConfigHome = strHome + "/.config";
372  }
373 
374  if (strXdgDataHome.empty())
375  {
376  // $XDG_DATA_HOME was not set, use default based on $HOME.
377  strXdgDataHome = strHome + "/.local/share";
378  }
379 
380  CONFIG_DIR = strXdgConfigHome + "/" + systemName();
381  CONFIG_FILE = CONFIG_DIR / strConfFile;
382  dataDir = strXdgDataHome + "/" + systemName();
383 
384  if (!boost::filesystem::exists(CONFIG_FILE))
385  {
386  CONFIG_DIR = "/etc/opt/" + systemName();
387  CONFIG_FILE = CONFIG_DIR / strConfFile;
388  dataDir = "/var/opt/" + systemName();
389  }
390  }
391  }
392 
393  // Update default values
394  load();
395  if (exists("reporting"))
396  {
397  RUN_REPORTING = true;
398  RUN_STANDALONE = true;
399  }
400  {
401  // load() may have set a new value for the dataDir
402  std::string const dbPath(legacy("database_path"));
403  if (!dbPath.empty())
404  dataDir = boost::filesystem::path(dbPath);
405  else if (RUN_STANDALONE)
406  dataDir.clear();
407  }
408 
409  if (!dataDir.empty())
410  {
411  boost::system::error_code ec;
412  boost::filesystem::create_directories(dataDir, ec);
413 
414  if (ec)
415  Throw<std::runtime_error>(
416  boost::str(boost::format("Can not create %s") % dataDir));
417 
418  legacy("database_path", boost::filesystem::absolute(dataDir).string());
419  }
420 
422 
423  if (RUN_STANDALONE)
424  LEDGER_HISTORY = 0;
425 
426  std::string ledgerTxDbType;
427  Section ledgerTxTablesSection = section("ledger_tx_tables");
428  get_if_exists(ledgerTxTablesSection, "use_tx_tables", USE_TX_TABLES);
429 
430  Section& nodeDbSection{section(ConfigSection::nodeDatabase())};
431  get_if_exists(nodeDbSection, "fast_load", FAST_LOAD);
432 }
433 
434 void
436 {
437  // NOTE: this writes to cerr because we want cout to be reserved
438  // for the writing of the json response (so that stdout can be part of a
439  // pipeline, for instance)
440  if (!QUIET)
441  std::cerr << "Loading: " << CONFIG_FILE << "\n";
442 
443  boost::system::error_code ec;
444  auto const fileContents = getFileContents(ec, CONFIG_FILE);
445 
446  if (ec)
447  {
448  std::cerr << "Failed to read '" << CONFIG_FILE << "'." << ec.value()
449  << ": " << ec.message() << std::endl;
450  return;
451  }
452 
453  loadFromString(fileContents);
454 }
455 
456 void
458 {
459  IniFileSections secConfig = parseIniFile(fileContents, true);
460 
461  build(secConfig);
462 
463  if (auto s = getIniFileSection(secConfig, SECTION_IPS))
464  IPS = *s;
465 
466  if (auto s = getIniFileSection(secConfig, SECTION_IPS_FIXED))
467  IPS_FIXED = *s;
468 
469  if (auto s = getIniFileSection(secConfig, SECTION_SNTP))
470  SNTP_SERVERS = *s;
471 
472  // if the user has specified ip:port then replace : with a space.
473  {
474  auto replaceColons = [](std::vector<std::string>& strVec) {
475  const static std::regex e(":([0-9]+)$");
476  for (auto& line : strVec)
477  {
478  // skip anything that might be an ipv6 address
479  if (std::count(line.begin(), line.end(), ':') != 1)
480  continue;
481 
482  std::string result = std::regex_replace(line, e, " $1");
483  // sanity check the result of the replace, should be same length
484  // as input
485  if (result.size() == line.size())
486  line = result;
487  }
488  };
489 
490  replaceColons(IPS_FIXED);
491  replaceColons(IPS);
492  }
493 
494  {
495  std::string dbPath;
496  if (getSingleSection(secConfig, "database_path", dbPath, j_))
497  {
498  boost::filesystem::path p(dbPath);
499  legacy("database_path", boost::filesystem::absolute(p).string());
500  }
501  }
502 
503  std::string strTemp;
504 
505  if (getSingleSection(secConfig, SECTION_PEER_PRIVATE, strTemp, j_))
506  PEER_PRIVATE = beast::lexicalCastThrow<bool>(strTemp);
507 
508  if (getSingleSection(secConfig, SECTION_PEERS_MAX, strTemp, j_))
509  {
510  PEERS_MAX = beast::lexicalCastThrow<std::size_t>(strTemp);
511  }
512  else
513  {
514  std::optional<std::size_t> peers_in_max{};
515  if (getSingleSection(secConfig, SECTION_PEERS_IN_MAX, strTemp, j_))
516  {
517  peers_in_max = beast::lexicalCastThrow<std::size_t>(strTemp);
518  if (*peers_in_max > 1000)
519  Throw<std::runtime_error>(
520  "Invalid value specified in [" SECTION_PEERS_IN_MAX
521  "] section; the value must be less or equal than 1000");
522  }
523 
524  std::optional<std::size_t> peers_out_max{};
525  if (getSingleSection(secConfig, SECTION_PEERS_OUT_MAX, strTemp, j_))
526  {
527  peers_out_max = beast::lexicalCastThrow<std::size_t>(strTemp);
528  if (*peers_out_max < 10 || *peers_out_max > 1000)
529  Throw<std::runtime_error>(
530  "Invalid value specified in [" SECTION_PEERS_OUT_MAX
531  "] section; the value must be in range 10-1000");
532  }
533 
534  // if one section is configured then the other must be configured too
535  if ((peers_in_max && !peers_out_max) ||
536  (peers_out_max && !peers_in_max))
537  Throw<std::runtime_error>("Both sections [" SECTION_PEERS_IN_MAX
538  "]"
539  "and [" SECTION_PEERS_OUT_MAX
540  "] must be configured");
541 
542  if (peers_in_max && peers_out_max)
543  {
544  PEERS_IN_MAX = *peers_in_max;
545  PEERS_OUT_MAX = *peers_out_max;
546  }
547  }
548 
549  if (getSingleSection(secConfig, SECTION_NODE_SIZE, strTemp, j_))
550  {
551  if (boost::iequals(strTemp, "tiny"))
552  NODE_SIZE = 0;
553  else if (boost::iequals(strTemp, "small"))
554  NODE_SIZE = 1;
555  else if (boost::iequals(strTemp, "medium"))
556  NODE_SIZE = 2;
557  else if (boost::iequals(strTemp, "large"))
558  NODE_SIZE = 3;
559  else if (boost::iequals(strTemp, "huge"))
560  NODE_SIZE = 4;
561  else
562  NODE_SIZE = std::min<std::size_t>(
563  4, beast::lexicalCastThrow<std::size_t>(strTemp));
564  }
565 
566  if (getSingleSection(secConfig, SECTION_SIGNING_SUPPORT, strTemp, j_))
567  signingEnabled_ = beast::lexicalCastThrow<bool>(strTemp);
568 
569  if (getSingleSection(secConfig, SECTION_ELB_SUPPORT, strTemp, j_))
570  ELB_SUPPORT = beast::lexicalCastThrow<bool>(strTemp);
571 
572  getSingleSection(secConfig, SECTION_SSL_VERIFY_FILE, SSL_VERIFY_FILE, j_);
573  getSingleSection(secConfig, SECTION_SSL_VERIFY_DIR, SSL_VERIFY_DIR, j_);
574 
575  if (getSingleSection(secConfig, SECTION_SSL_VERIFY, strTemp, j_))
576  SSL_VERIFY = beast::lexicalCastThrow<bool>(strTemp);
577 
578  if (getSingleSection(secConfig, SECTION_RELAY_VALIDATIONS, strTemp, j_))
579  {
580  if (boost::iequals(strTemp, "all"))
582  else if (boost::iequals(strTemp, "trusted"))
584  else if (boost::iequals(strTemp, "drop_untrusted"))
586  else
587  Throw<std::runtime_error>(
588  "Invalid value specified in [" SECTION_RELAY_VALIDATIONS
589  "] section");
590  }
591 
592  if (getSingleSection(secConfig, SECTION_RELAY_PROPOSALS, strTemp, j_))
593  {
594  if (boost::iequals(strTemp, "all"))
596  else if (boost::iequals(strTemp, "trusted"))
598  else if (boost::iequals(strTemp, "drop_untrusted"))
600  else
601  Throw<std::runtime_error>(
602  "Invalid value specified in [" SECTION_RELAY_PROPOSALS
603  "] section");
604  }
605 
606  if (exists(SECTION_VALIDATION_SEED) && exists(SECTION_VALIDATOR_TOKEN))
607  Throw<std::runtime_error>("Cannot have both [" SECTION_VALIDATION_SEED
608  "] and [" SECTION_VALIDATOR_TOKEN
609  "] config sections");
610 
611  if (getSingleSection(secConfig, SECTION_NETWORK_QUORUM, strTemp, j_))
612  NETWORK_QUORUM = beast::lexicalCastThrow<std::size_t>(strTemp);
613 
614  if (getSingleSection(secConfig, SECTION_FEE_ACCOUNT_RESERVE, strTemp, j_))
615  FEE_ACCOUNT_RESERVE = beast::lexicalCastThrow<std::uint64_t>(strTemp);
616 
617  if (getSingleSection(secConfig, SECTION_FEE_OWNER_RESERVE, strTemp, j_))
618  FEE_OWNER_RESERVE = beast::lexicalCastThrow<std::uint64_t>(strTemp);
619 
620  if (getSingleSection(secConfig, SECTION_FEE_DEFAULT, strTemp, j_))
621  FEE_DEFAULT = beast::lexicalCastThrow<std::uint64_t>(strTemp);
622 
623  if (getSingleSection(secConfig, SECTION_LEDGER_HISTORY, strTemp, j_))
624  {
625  if (boost::iequals(strTemp, "full"))
627  std::numeric_limits<decltype(LEDGER_HISTORY)>::max();
628  else if (boost::iequals(strTemp, "none"))
629  LEDGER_HISTORY = 0;
630  else
631  LEDGER_HISTORY = beast::lexicalCastThrow<std::uint32_t>(strTemp);
632  }
633 
634  if (getSingleSection(secConfig, SECTION_FETCH_DEPTH, strTemp, j_))
635  {
636  if (boost::iequals(strTemp, "none"))
637  FETCH_DEPTH = 0;
638  else if (boost::iequals(strTemp, "full"))
639  FETCH_DEPTH = std::numeric_limits<decltype(FETCH_DEPTH)>::max();
640  else
641  FETCH_DEPTH = beast::lexicalCastThrow<std::uint32_t>(strTemp);
642 
643  if (FETCH_DEPTH < 10)
644  FETCH_DEPTH = 10;
645  }
646 
647  // By default, validators don't have pathfinding enabled, unless it is
648  // explicitly requested by the server's admin.
649  if (exists(SECTION_VALIDATION_SEED) || exists(SECTION_VALIDATOR_TOKEN))
650  PATH_SEARCH_MAX = 0;
651 
652  if (getSingleSection(secConfig, SECTION_PATH_SEARCH_OLD, strTemp, j_))
653  PATH_SEARCH_OLD = beast::lexicalCastThrow<int>(strTemp);
654  if (getSingleSection(secConfig, SECTION_PATH_SEARCH, strTemp, j_))
655  PATH_SEARCH = beast::lexicalCastThrow<int>(strTemp);
656  if (getSingleSection(secConfig, SECTION_PATH_SEARCH_FAST, strTemp, j_))
657  PATH_SEARCH_FAST = beast::lexicalCastThrow<int>(strTemp);
658  if (getSingleSection(secConfig, SECTION_PATH_SEARCH_MAX, strTemp, j_))
659  PATH_SEARCH_MAX = beast::lexicalCastThrow<int>(strTemp);
660 
661  if (getSingleSection(secConfig, SECTION_DEBUG_LOGFILE, strTemp, j_))
662  DEBUG_LOGFILE = strTemp;
663 
664  if (getSingleSection(secConfig, SECTION_SWEEP_INTERVAL, strTemp, j_))
665  {
666  SWEEP_INTERVAL = beast::lexicalCastThrow<std::size_t>(strTemp);
667 
668  if (SWEEP_INTERVAL < 10 || SWEEP_INTERVAL > 600)
669  Throw<std::runtime_error>("Invalid " SECTION_SWEEP_INTERVAL
670  ": must be between 10 and 600 inclusive");
671  }
672 
673  if (getSingleSection(secConfig, SECTION_WORKERS, strTemp, j_))
674  {
675  WORKERS = beast::lexicalCastThrow<int>(strTemp);
676 
677  if (WORKERS < 1 || WORKERS > 1024)
678  Throw<std::runtime_error>(
679  "Invalid " SECTION_WORKERS
680  ": must be between 1 and 1024 inclusive.");
681  }
682 
683  if (getSingleSection(secConfig, SECTION_IO_WORKERS, strTemp, j_))
684  {
685  IO_WORKERS = beast::lexicalCastThrow<int>(strTemp);
686 
687  if (IO_WORKERS < 1 || IO_WORKERS > 1024)
688  Throw<std::runtime_error>(
689  "Invalid " SECTION_IO_WORKERS
690  ": must be between 1 and 1024 inclusive.");
691  }
692 
693  if (getSingleSection(secConfig, SECTION_PREFETCH_WORKERS, strTemp, j_))
694  {
695  PREFETCH_WORKERS = beast::lexicalCastThrow<int>(strTemp);
696 
697  if (PREFETCH_WORKERS < 1 || PREFETCH_WORKERS > 1024)
698  Throw<std::runtime_error>(
699  "Invalid " SECTION_PREFETCH_WORKERS
700  ": must be between 1 and 1024 inclusive.");
701  }
702 
703  if (getSingleSection(secConfig, SECTION_COMPRESSION, strTemp, j_))
704  COMPRESSION = beast::lexicalCastThrow<bool>(strTemp);
705 
706  if (getSingleSection(secConfig, SECTION_LEDGER_REPLAY, strTemp, j_))
707  LEDGER_REPLAY = beast::lexicalCastThrow<bool>(strTemp);
708 
709  if (exists(SECTION_REDUCE_RELAY))
710  {
711  auto sec = section(SECTION_REDUCE_RELAY);
712  VP_REDUCE_RELAY_ENABLE = sec.value_or("vp_enable", false);
713  VP_REDUCE_RELAY_SQUELCH = sec.value_or("vp_squelch", false);
714  TX_REDUCE_RELAY_ENABLE = sec.value_or("tx_enable", false);
715  TX_REDUCE_RELAY_METRICS = sec.value_or("tx_metrics", false);
716  TX_REDUCE_RELAY_MIN_PEERS = sec.value_or("tx_min_peers", 20);
717  TX_RELAY_PERCENTAGE = sec.value_or("tx_relay_percentage", 25);
718  if (TX_RELAY_PERCENTAGE < 10 || TX_RELAY_PERCENTAGE > 100 ||
720  Throw<std::runtime_error>(
721  "Invalid " SECTION_REDUCE_RELAY
722  ", tx_min_peers must be greater or equal to 10"
723  ", tx_relay_percentage must be greater or equal to 10 "
724  "and less or equal to 100");
725  }
726 
727  if (getSingleSection(secConfig, SECTION_MAX_TRANSACTIONS, strTemp, j_))
728  {
730  beast::lexicalCastThrow<int>(strTemp),
733  }
734 
735  if (getSingleSection(secConfig, SECTION_SERVER_DOMAIN, strTemp, j_))
736  {
737  if (!isProperlyFormedTomlDomain(strTemp))
738  {
739  Throw<std::runtime_error>(
740  "Invalid " SECTION_SERVER_DOMAIN
741  ": the domain name does not appear to meet the requirements.");
742  }
743 
744  SERVER_DOMAIN = strTemp;
745  }
746 
747  if (exists(SECTION_OVERLAY))
748  {
749  auto const sec = section(SECTION_OVERLAY);
750 
751  using namespace std::chrono;
752 
753  try
754  {
755  if (auto val = sec.get("max_unknown_time"))
757  seconds{beast::lexicalCastThrow<std::uint32_t>(*val)};
758  }
759  catch (...)
760  {
761  Throw<std::runtime_error>(
762  "Invalid value 'max_unknown_time' in " SECTION_OVERLAY
763  ": must be of the form '<number>' representing seconds.");
764  }
765 
766  if (MAX_UNKNOWN_TIME < seconds{300} || MAX_UNKNOWN_TIME > seconds{1800})
767  Throw<std::runtime_error>(
768  "Invalid value 'max_unknown_time' in " SECTION_OVERLAY
769  ": the time must be between 300 and 1800 seconds, inclusive.");
770 
771  try
772  {
773  if (auto val = sec.get("max_diverged_time"))
775  seconds{beast::lexicalCastThrow<std::uint32_t>(*val)};
776  }
777  catch (...)
778  {
779  Throw<std::runtime_error>(
780  "Invalid value 'max_diverged_time' in " SECTION_OVERLAY
781  ": must be of the form '<number>' representing seconds.");
782  }
783 
785  {
786  Throw<std::runtime_error>(
787  "Invalid value 'max_diverged_time' in " SECTION_OVERLAY
788  ": the time must be between 60 and 900 seconds, inclusive.");
789  }
790  }
791 
792  if (getSingleSection(
793  secConfig, SECTION_AMENDMENT_MAJORITY_TIME, strTemp, j_))
794  {
795  using namespace std::chrono;
796  boost::regex const re(
797  "^\\s*(\\d+)\\s*(minutes|hours|days|weeks)\\s*(\\s+.*)?$");
798  boost::smatch match;
799  if (!boost::regex_match(strTemp, match, re))
800  Throw<std::runtime_error>(
801  "Invalid " SECTION_AMENDMENT_MAJORITY_TIME
802  ", must be: [0-9]+ [minutes|hours|days|weeks]");
803 
805  beast::lexicalCastThrow<std::uint32_t>(match[1].str());
806 
807  if (boost::iequals(match[2], "minutes"))
809  else if (boost::iequals(match[2], "hours"))
811  else if (boost::iequals(match[2], "days"))
813  else if (boost::iequals(match[2], "weeks"))
815 
817  Throw<std::runtime_error>(
818  "Invalid " SECTION_AMENDMENT_MAJORITY_TIME
819  ", the minimum amount of time an amendment must hold a "
820  "majority is 15 minutes");
821  }
822 
823  if (getSingleSection(secConfig, SECTION_BETA_RPC_API, strTemp, j_))
824  BETA_RPC_API = beast::lexicalCastThrow<bool>(strTemp);
825 
826  // Do not load trusted validator configuration for standalone mode
827  if (!RUN_STANDALONE)
828  {
829  // If a file was explicitly specified, then throw if the
830  // path is malformed or if the file does not exist or is
831  // not a file.
832  // If the specified file is not an absolute path, then look
833  // for it in the same directory as the config file.
834  // If no path was specified, then look for validators.txt
835  // in the same directory as the config file, but don't complain
836  // if we can't find it.
837  boost::filesystem::path validatorsFile;
838 
839  if (getSingleSection(secConfig, SECTION_VALIDATORS_FILE, strTemp, j_))
840  {
841  validatorsFile = strTemp;
842 
843  if (validatorsFile.empty())
844  Throw<std::runtime_error>(
845  "Invalid path specified in [" SECTION_VALIDATORS_FILE "]");
846 
847  if (!validatorsFile.is_absolute() && !CONFIG_DIR.empty())
848  validatorsFile = CONFIG_DIR / validatorsFile;
849 
850  if (!boost::filesystem::exists(validatorsFile))
851  Throw<std::runtime_error>(
852  "The file specified in [" SECTION_VALIDATORS_FILE
853  "] "
854  "does not exist: " +
855  validatorsFile.string());
856 
857  else if (
858  !boost::filesystem::is_regular_file(validatorsFile) &&
859  !boost::filesystem::is_symlink(validatorsFile))
860  Throw<std::runtime_error>(
861  "Invalid file specified in [" SECTION_VALIDATORS_FILE
862  "]: " +
863  validatorsFile.string());
864  }
865  else if (!CONFIG_DIR.empty())
866  {
867  validatorsFile = CONFIG_DIR / validatorsFileName;
868 
869  if (!validatorsFile.empty())
870  {
871  if (!boost::filesystem::exists(validatorsFile))
872  validatorsFile.clear();
873  else if (
874  !boost::filesystem::is_regular_file(validatorsFile) &&
875  !boost::filesystem::is_symlink(validatorsFile))
876  validatorsFile.clear();
877  }
878  }
879 
880  if (!validatorsFile.empty() &&
881  boost::filesystem::exists(validatorsFile) &&
882  (boost::filesystem::is_regular_file(validatorsFile) ||
883  boost::filesystem::is_symlink(validatorsFile)))
884  {
885  boost::system::error_code ec;
886  auto const data = getFileContents(ec, validatorsFile);
887  if (ec)
888  {
889  Throw<std::runtime_error>(
890  "Failed to read '" + validatorsFile.string() + "'." +
891  std::to_string(ec.value()) + ": " + ec.message());
892  }
893 
894  auto iniFile = parseIniFile(data, true);
895 
896  auto entries = getIniFileSection(iniFile, SECTION_VALIDATORS);
897 
898  if (entries)
899  section(SECTION_VALIDATORS).append(*entries);
900 
901  auto valKeyEntries =
902  getIniFileSection(iniFile, SECTION_VALIDATOR_KEYS);
903 
904  if (valKeyEntries)
905  section(SECTION_VALIDATOR_KEYS).append(*valKeyEntries);
906 
907  auto valSiteEntries =
908  getIniFileSection(iniFile, SECTION_VALIDATOR_LIST_SITES);
909 
910  if (valSiteEntries)
911  section(SECTION_VALIDATOR_LIST_SITES).append(*valSiteEntries);
912 
913  auto valListKeys =
914  getIniFileSection(iniFile, SECTION_VALIDATOR_LIST_KEYS);
915 
916  if (valListKeys)
917  section(SECTION_VALIDATOR_LIST_KEYS).append(*valListKeys);
918 
919  if (!entries && !valKeyEntries && !valListKeys)
920  Throw<std::runtime_error>(
921  "The file specified in [" SECTION_VALIDATORS_FILE
922  "] "
923  "does not contain a [" SECTION_VALIDATORS
924  "], "
925  "[" SECTION_VALIDATOR_KEYS
926  "] or "
927  "[" SECTION_VALIDATOR_LIST_KEYS
928  "]"
929  " section: " +
930  validatorsFile.string());
931  }
932 
933  // Consolidate [validator_keys] and [validators]
934  section(SECTION_VALIDATORS)
935  .append(section(SECTION_VALIDATOR_KEYS).lines());
936 
937  if (!section(SECTION_VALIDATOR_LIST_SITES).lines().empty() &&
938  section(SECTION_VALIDATOR_LIST_KEYS).lines().empty())
939  {
940  Throw<std::runtime_error>(
941  "[" + std::string(SECTION_VALIDATOR_LIST_KEYS) +
942  "] config section is missing");
943  }
944  }
945 
946  {
947  auto const part = section("features");
948  for (auto const& s : part.values())
949  {
950  if (auto const f = getRegisteredFeature(s))
951  features.insert(*f);
952  else
953  Throw<std::runtime_error>(
954  "Unknown feature: " + s + " in config file.");
955  }
956  }
957 
958  // This doesn't properly belong here, but check to make sure that the
959  // value specified for network_quorum is achievable:
960  {
961  auto pm = PEERS_MAX;
962 
963  // FIXME this apparently magic value is actually defined as a constant
964  // elsewhere (see defaultMaxPeers) but we handle this check here.
965  if (pm == 0)
966  pm = 21;
967 
968  if (NETWORK_QUORUM > pm)
969  {
970  Throw<std::runtime_error>(
971  "The minimum number of required peers (network_quorum) exceeds "
972  "the maximum number of allowed peers (peers_max)");
973  }
974  }
975 }
976 
977 boost::filesystem::path
979 {
980  auto log_file = DEBUG_LOGFILE;
981 
982  if (!log_file.empty() && !log_file.is_absolute())
983  {
984  // Unless an absolute path for the log file is specified, the
985  // path is relative to the config file directory.
986  log_file = boost::filesystem::absolute(log_file, CONFIG_DIR);
987  }
988 
989  if (!log_file.empty())
990  {
991  auto log_dir = log_file.parent_path();
992 
993  if (!boost::filesystem::is_directory(log_dir))
994  {
995  boost::system::error_code ec;
996  boost::filesystem::create_directories(log_dir, ec);
997 
998  // If we fail, we warn but continue so that the calling code can
999  // decide how to handle this situation.
1000  if (ec)
1001  {
1002  std::cerr << "Unable to create log file path " << log_dir
1003  << ": " << ec.message() << '\n';
1004  }
1005  }
1006  }
1007 
1008  return log_file;
1009 }
1010 
1011 int
1013 {
1014  auto const index = static_cast<std::underlying_type_t<SizedItem>>(item);
1015  assert(index < sizedItems.size());
1016  assert(!node || *node <= 4);
1017  return sizedItems.at(index).second.at(node.value_or(NODE_SIZE));
1018 }
1019 
1020 } // namespace ripple
ripple::SizedItem::openFinalLimit
@ openFinalLimit
ripple::Section
Holds a collection of configuration values.
Definition: BasicConfig.h:42
ripple::Config::SWEEP_INTERVAL
std::optional< int > SWEEP_INTERVAL
Definition: Config.h:229
ripple::getFileContents
std::string getFileContents(boost::system::error_code &ec, boost::filesystem::path const &sourcePath, std::optional< std::size_t > maxSize=std::nullopt)
Definition: FileUtilities.cpp:25
regex
std::string
STL class.
ripple::SizedItem
SizedItem
Definition: Config.h:48
ripple::Config::PATH_SEARCH
int PATH_SEARCH
Definition: Config.h:180
ripple::Config::LEDGER_REPLAY
bool LEDGER_REPLAY
Definition: Config.h:209
ripple::SizedItem::accountIdCacheSize
@ accountIdCacheSize
ripple::Config::validatorsFileName
static char const *const validatorsFileName
Definition: Config.h:75
std::vector< std::string >
std::map::find
T find(T... args)
std::size
T size(T... args)
ripple::Config::SSL_VERIFY_DIR
std::string SSL_VERIFY_DIR
Definition: Config.h:203
std::optional::value_or
T value_or(T... args)
ripple::Config::CONFIG_FILE
boost::filesystem::path CONFIG_FILE
Definition: Config.h:82
std::chrono::seconds
ripple::Config::NODE_SIZE
std::size_t NODE_SIZE
Definition: Config.h:199
iterator
ripple::Config::PEER_PRIVATE
bool PEER_PRIVATE
Definition: Config.h:157
ripple::Config::DEBUG_LOGFILE
boost::filesystem::path DEBUG_LOGFILE
Definition: Config.h:88
std::map::emplace
T emplace(T... args)
ripple::Config::setupControl
void setupControl(bool bQuiet, bool bSilent, bool bStandalone)
Definition: Config.cpp:273
ripple::SizedItem::treeCacheAge
@ treeCacheAge
beast::Journal::warn
Stream warn() const
Definition: Journal.h:327
std::distance
T distance(T... args)
std::cerr
ripple::SizedItem::hashNodeDBCache
@ hashNodeDBCache
ripple::Config::MIN_JOB_QUEUE_TX
static constexpr int MIN_JOB_QUEUE_TX
Definition: Config.h:214
iostream
ripple::SizedItem::ledgerFetch
@ ledgerFetch
ripple::Config::MAX_TRANSACTIONS
int MAX_TRANSACTIONS
Definition: Config.h:212
ripple::Config::FETCH_DEPTH
std::uint32_t FETCH_DEPTH
Definition: Config.h:194
algorithm
ripple::Config::PATH_SEARCH_MAX
int PATH_SEARCH_MAX
Definition: Config.h:182
ripple::Config::VP_REDUCE_RELAY_SQUELCH
bool VP_REDUCE_RELAY_SQUELCH
Definition: Config.h:243
ripple::get_if_exists
bool get_if_exists(Section const &section, std::string const &name, T &v)
Definition: BasicConfig.h:384
ripple::Config::IO_WORKERS
int IO_WORKERS
Definition: Config.h:221
ripple::Section::append
void append(std::vector< std::string > const &lines)
Append a set of lines to this section.
Definition: BasicConfig.cpp:38
std::underlying_type_t
ripple::Config::RUN_STANDALONE
bool RUN_STANDALONE
Operate in stand-alone mode.
Definition: Config.h:105
ripple::SizedItem::ramSizeGB
@ ramSizeGB
ripple::Config::load
void load()
Definition: Config.cpp:435
ripple::Config::j_
const beast::Journal j_
Definition: Config.h:92
ripple::Config::RELAY_UNTRUSTED_PROPOSALS
int RELAY_UNTRUSTED_PROPOSALS
Definition: Config.h:154
ripple::Config::SSL_VERIFY_FILE
std::string SSL_VERIFY_FILE
Definition: Config.h:202
ripple::Config::loadFromString
void loadFromString(std::string const &fileContents)
Load the config from the contents of the string.
Definition: Config.cpp:457
ripple::Config::getValueFor
int getValueFor(SizedItem item, std::optional< std::size_t > node=std::nullopt) const
Retrieve the default value for the item at the specified node size.
Definition: Config.cpp:1012
ripple::weeks
std::chrono::duration< int, std::ratio_multiply< days::period, std::ratio< 7 > >> weeks
Definition: chrono.h:41
thread
ripple::Config::FAST_LOAD
bool FAST_LOAD
Definition: Config.h:277
std::thread::hardware_concurrency
T hardware_concurrency(T... args)
ripple::Config::SNTP_SERVERS
std::vector< std::string > SNTP_SERVERS
Definition: Config.h:131
ripple::Config::IPS_FIXED
std::vector< std::string > IPS_FIXED
Definition: Config.h:130
ripple::Config::FEE_OWNER_RESERVE
XRPAmount FEE_OWNER_RESERVE
Definition: Config.h:190
ripple::Config::SSL_VERIFY
bool SSL_VERIFY
Definition: Config.h:201
ripple::BasicConfig::build
void build(IniFileSections const &ifs)
Definition: BasicConfig.cpp:176
ripple::Config::USE_TX_TABLES
bool USE_TX_TABLES
Definition: Config.h:111
std::getenv
T getenv(T... args)
std::to_string
T to_string(T... args)
ripple::Config::FEE_ACCOUNT_RESERVE
XRPAmount FEE_ACCOUNT_RESERVE
Definition: Config.h:189
std::array
STL class.
ripple::Config::BETA_RPC_API
bool BETA_RPC_API
Definition: Config.h:274
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::Config::PEERS_OUT_MAX
std::size_t PEERS_OUT_MAX
Definition: Config.h:164
ripple::Config::AMENDMENT_MAJORITY_TIME
std::chrono::seconds AMENDMENT_MAJORITY_TIME
Definition: Config.h:217
ripple::SizedItem::lgrDBCache
@ lgrDBCache
ripple::Config::MAX_JOB_QUEUE_TX
static constexpr int MAX_JOB_QUEUE_TX
Definition: Config.h:213
beast::Journal
A generic endpoint for log messages.
Definition: Journal.h:58
ripple::SizedItem::burstSize
@ burstSize
std::uint64_t
ripple::Config::WORKERS
int WORKERS
Definition: Config.h:220
ripple::Config::PEERS_IN_MAX
std::size_t PEERS_IN_MAX
Definition: Config.h:165
ripple::Config::databaseDirName
static char const *const databaseDirName
Definition: Config.h:74
std::map
STL class.
std::regex
ripple::SizedItem::txnDBCache
@ txnDBCache
ripple::Config::PREFETCH_WORKERS
int PREFETCH_WORKERS
Definition: Config.h:222
ripple::Config::PATH_SEARCH_OLD
int PATH_SEARCH_OLD
Definition: Config.h:179
ripple::Config::LEDGER_HISTORY
std::uint32_t LEDGER_HISTORY
Definition: Config.h:193
ripple::parseIniFile
IniFileSections parseIniFile(std::string const &strInput, const bool bTrim)
Definition: Config.cpp:161
ripple::Config::NETWORK_QUORUM
std::size_t NETWORK_QUORUM
Definition: Config.h:148
ripple::sizedItems
constexpr std::array< std::pair< SizedItem, std::array< int, 5 > >, 13 > sizedItems
Definition: Config.cpp:114
ripple::Config::VP_REDUCE_RELAY_ENABLE
bool VP_REDUCE_RELAY_ENABLE
Definition: Config.h:234
ripple::Config::CONFIG_DIR
boost::filesystem::path CONFIG_DIR
Definition: Config.h:85
std::string::substr
T substr(T... args)
ripple::Config::PEERS_MAX
std::size_t PEERS_MAX
Definition: Config.h:163
ripple::Config::FEE_DEFAULT
XRPAmount FEE_DEFAULT
Definition: Config.h:188
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
ripple::Config::features
std::unordered_set< uint256, beast::uhash<> > features
Definition: Config.h:263
ripple::Config::PATH_SEARCH_FAST
int PATH_SEARCH_FAST
Definition: Config.h:181
ripple::getSingleSection
bool getSingleSection(IniFileSections &secSource, std::string const &strSection, std::string &strValue, beast::Journal j)
Definition: Config.cpp:222
ripple::Config::setup
void setup(std::string const &strConf, bool bQuiet, bool bSilent, bool bStandalone)
Definition: Config.cpp:315
cstdlib
std::endl
T endl(T... args)
std::clamp
T clamp(T... args)
ripple::getEnvVar
static std::string getEnvVar(char const *name)
Definition: Config.cpp:257
ripple::SizedItem::treeCacheSize
@ treeCacheSize
ripple::Config::TX_REDUCE_RELAY_MIN_PEERS
std::size_t TX_REDUCE_RELAY_MIN_PEERS
Definition: Config.h:255
ripple::Config::SERVER_DOMAIN
std::string SERVER_DOMAIN
Definition: Config.h:265
ripple::Config::TX_REDUCE_RELAY_METRICS
bool TX_REDUCE_RELAY_METRICS
Definition: Config.h:252
ripple::Config::RELAY_UNTRUSTED_VALIDATIONS
int RELAY_UNTRUSTED_VALIDATIONS
Definition: Config.h:153
ripple::SizedItem::sweepInterval
@ sweepInterval
ripple::Config::COMPRESSION
bool COMPRESSION
Definition: Config.h:206
std::count
T count(T... args)
std::string::empty
T empty(T... args)
ripple::systemName
static std::string const & systemName()
Definition: SystemParameters.h:34
ripple::SizedItem::ledgerSize
@ ledgerSize
std::optional< std::size_t >
ripple::Config::IPS
std::vector< std::string > IPS
Definition: Config.h:129
ripple::Config::configFileName
static char const *const configFileName
Definition: Config.h:73
std::size_t
ripple::isProperlyFormedTomlDomain
bool isProperlyFormedTomlDomain(std::string const &domain)
Determines if the given string looks like a TOML-file hosting domain.
ripple::Config::TX_RELAY_PERCENTAGE
std::size_t TX_RELAY_PERCENTAGE
Definition: Config.h:258
std::map::end
T end(T... args)
ripple::Config::QUIET
bool QUIET
Definition: Config.h:94
ripple::days
std::chrono::duration< int, std::ratio_multiply< std::chrono::hours::period, std::ratio< 24 > >> days
Definition: chrono.h:38
ripple::Config::TX_REDUCE_RELAY_ENABLE
bool TX_REDUCE_RELAY_ENABLE
Definition: Config.h:245
ripple::getRegisteredFeature
std::optional< uint256 > getRegisteredFeature(std::string const &name)
Definition: Feature.cpp:341
ripple::Config::SILENT
bool SILENT
Definition: Config.h:95
ripple::Config::getDebugLogFile
boost::filesystem::path getDebugLogFile() const
Returns the full path and filename of the debug log file.
Definition: Config.cpp:978
ripple::Config::signingEnabled_
bool signingEnabled_
Determines if the server will sign a tx, given an account's secret seed.
Definition: Config.h:119
std::numeric_limits
std::regex_replace
T regex_replace(T... args)
ripple::Config::ELB_SUPPORT
bool ELB_SUPPORT
Definition: Config.h:127
ripple::HTTPClient::initializeSSLContext
static void initializeSSLContext(Config const &config, beast::Journal j)
Definition: HTTPClient.cpp:38
ripple::ConfigSection::nodeDatabase
static std::string nodeDatabase()
Definition: ConfigSections.h:33
ripple::getIniFileSection
IniFileSections::mapped_type * getIniFileSection(IniFileSections &secSource, std::string const &strSection)
Definition: Config.cpp:209
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::SizedItem::ledgerAge
@ ledgerAge
ripple::Config::MAX_UNKNOWN_TIME
std::chrono::seconds MAX_UNKNOWN_TIME
Definition: Config.h:268
ripple::BasicConfig::section
Section & section(std::string const &name)
Returns the section with the given name.
Definition: BasicConfig.cpp:127
ripple::Config::MAX_DIVERGED_TIME
std::chrono::seconds MAX_DIVERGED_TIME
Definition: Config.h:271
ripple::IniFileSections
std::map< std::string, std::vector< std::string > > IniFileSections
Definition: BasicConfig.h:35
ripple::Config::RUN_REPORTING
bool RUN_REPORTING
Definition: Config.h:107
beast
Definition: base_uint.h:641
std::chrono