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 <thread>
41 
42 #if BOOST_OS_WINDOWS
43 #include <sysinfoapi.h>
44 
45 namespace ripple {
46 namespace detail {
47 
48 [[nodiscard]] std::uint64_t
49 getMemorySize()
50 {
51  if (MEMORYSTATUSEX msx{sizeof(MEMORYSTATUSEX)}; GlobalMemoryStatusEx(&msx))
52  return static_cast<std::uint64_t>(msx.ullTotalPhys);
53 
54  return 0;
55 }
56 
57 } // namespace detail
58 } // namespace ripple
59 #endif
60 
61 #if BOOST_OS_LINUX
62 #include <sys/sysinfo.h>
63 
64 namespace ripple {
65 namespace detail {
66 
67 [[nodiscard]] std::uint64_t
68 getMemorySize()
69 {
70  struct sysinfo si;
71 
72  if (sysinfo(&si) == 0)
73  return static_cast<std::uint64_t>(si.totalram);
74 
75  return 0;
76 }
77 
78 } // namespace detail
79 } // namespace ripple
80 
81 #endif
82 
83 #if BOOST_OS_MACOS
84 #include <sys/sysctl.h>
85 #include <sys/types.h>
86 
87 namespace ripple {
88 namespace detail {
89 
90 [[nodiscard]] std::uint64_t
91 getMemorySize()
92 {
93  int mib[] = {CTL_HW, HW_MEMSIZE};
94  std::int64_t ram = 0;
95  size_t size = sizeof(ram);
96 
97  if (sysctl(mib, 2, &ram, &size, NULL, 0) == 0)
98  return static_cast<std::uint64_t>(ram);
99 
100  return 0;
101 }
102 
103 } // namespace detail
104 } // namespace ripple
105 #endif
106 
107 namespace ripple {
108 
109 // clang-format off
110 // The configurable node sizes are "tiny", "small", "medium", "large", "huge"
113 {{
114  // FIXME: We should document each of these items, explaining exactly
115  // what they control and whether there exists an explicit
116  // config option that can be used to override the default.
117 
118  // tiny small medium large huge
119  {SizedItem::sweepInterval, {{ 10, 30, 60, 90, 120 }}},
120  {SizedItem::treeCacheSize, {{ 128000, 256000, 512000, 768000, 2048000 }}},
121  {SizedItem::treeCacheAge, {{ 30, 60, 90, 120, 900 }}},
122  {SizedItem::ledgerSize, {{ 32, 128, 256, 384, 768 }}},
123  {SizedItem::ledgerAge, {{ 30, 90, 180, 240, 900 }}},
124  {SizedItem::ledgerFetch, {{ 2, 3, 4, 5, 8 }}},
125  {SizedItem::hashNodeDBCache, {{ 4, 12, 24, 64, 128 }}},
126  {SizedItem::txnDBCache, {{ 4, 12, 24, 64, 128 }}},
127  {SizedItem::lgrDBCache, {{ 4, 8, 16, 32, 128 }}},
128  {SizedItem::openFinalLimit, {{ 8, 16, 32, 64, 128 }}},
129  {SizedItem::burstSize, {{ 4, 8, 16, 32, 48 }}},
130  {SizedItem::ramSizeGB, {{ 8, 12, 16, 24, 32 }}},
131 }};
132 
133 // Ensure that the order of entries in the table corresponds to the
134 // order of entries in the enum:
135 static_assert(
136  []() constexpr->bool {
137  std::underlying_type_t<SizedItem> idx = 0;
138 
139  for (auto const& i : sizedItems)
140  {
141  if (static_cast<std::underlying_type_t<SizedItem>>(i.first) != idx)
142  return false;
143 
144  ++idx;
145  }
146 
147  return true;
148  }(),
149  "Mismatch between sized item enum & array indices");
150 // clang-format on
151 
152 //
153 // TODO: Check permissions on config file before using it.
154 //
155 
156 #define SECTION_DEFAULT_NAME ""
157 
159 parseIniFile(std::string const& strInput, const bool bTrim)
160 {
161  std::string strData(strInput);
163  IniFileSections secResult;
164 
165  // Convert DOS format to unix.
166  boost::algorithm::replace_all(strData, "\r\n", "\n");
167 
168  // Convert MacOS format to unix.
169  boost::algorithm::replace_all(strData, "\r", "\n");
170 
171  boost::algorithm::split(vLines, strData, boost::algorithm::is_any_of("\n"));
172 
173  // Set the default Section name.
174  std::string strSection = SECTION_DEFAULT_NAME;
175 
176  // Initialize the default Section.
177  secResult[strSection] = IniFileSections::mapped_type();
178 
179  // Parse each line.
180  for (auto& strValue : vLines)
181  {
182  if (bTrim)
183  boost::algorithm::trim(strValue);
184 
185  if (strValue.empty() || strValue[0] == '#')
186  {
187  // Blank line or comment, do nothing.
188  }
189  else if (strValue[0] == '[' && strValue[strValue.length() - 1] == ']')
190  {
191  // New Section.
192  strSection = strValue.substr(1, strValue.length() - 2);
193  secResult.emplace(strSection, IniFileSections::mapped_type{});
194  }
195  else
196  {
197  // Another line for Section.
198  if (!strValue.empty())
199  secResult[strSection].push_back(strValue);
200  }
201  }
202 
203  return secResult;
204 }
205 
206 IniFileSections::mapped_type*
207 getIniFileSection(IniFileSections& secSource, std::string const& strSection)
208 {
209  IniFileSections::iterator it;
210  IniFileSections::mapped_type* smtResult;
211  it = secSource.find(strSection);
212  if (it == secSource.end())
213  smtResult = nullptr;
214  else
215  smtResult = &(it->second);
216  return smtResult;
217 }
218 
219 bool
221  IniFileSections& secSource,
222  std::string const& strSection,
223  std::string& strValue,
224  beast::Journal j)
225 {
226  IniFileSections::mapped_type* pmtEntries =
227  getIniFileSection(secSource, strSection);
228  bool bSingle = pmtEntries && 1 == pmtEntries->size();
229 
230  if (bSingle)
231  {
232  strValue = (*pmtEntries)[0];
233  }
234  else if (pmtEntries)
235  {
236  JLOG(j.warn()) << boost::str(
237  boost::format("Section [%s]: requires 1 line not %d lines.") %
238  strSection % pmtEntries->size());
239  }
240 
241  return bSingle;
242 }
243 
244 //------------------------------------------------------------------------------
245 //
246 // Config (DEPRECATED)
247 //
248 //------------------------------------------------------------------------------
249 
250 char const* const Config::configFileName = "rippled.cfg";
251 char const* const Config::databaseDirName = "db";
252 char const* const Config::validatorsFileName = "validators.txt";
253 
254 [[nodiscard]] static std::string
255 getEnvVar(char const* name)
256 {
257  std::string value;
258 
259  if (auto const v = std::getenv(name); v != nullptr)
260  value = v;
261 
262  return value;
263 }
264 
265 constexpr FeeUnit32 Config::TRANSACTION_FEE_BASE;
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 
431 void
433 {
434  // NOTE: this writes to cerr because we want cout to be reserved
435  // for the writing of the json response (so that stdout can be part of a
436  // pipeline, for instance)
437  if (!QUIET)
438  std::cerr << "Loading: " << CONFIG_FILE << "\n";
439 
440  boost::system::error_code ec;
441  auto const fileContents = getFileContents(ec, CONFIG_FILE);
442 
443  if (ec)
444  {
445  std::cerr << "Failed to read '" << CONFIG_FILE << "'." << ec.value()
446  << ": " << ec.message() << std::endl;
447  return;
448  }
449 
450  loadFromString(fileContents);
451 }
452 
453 void
455 {
456  IniFileSections secConfig = parseIniFile(fileContents, true);
457 
458  build(secConfig);
459 
460  if (auto s = getIniFileSection(secConfig, SECTION_IPS))
461  IPS = *s;
462 
463  if (auto s = getIniFileSection(secConfig, SECTION_IPS_FIXED))
464  IPS_FIXED = *s;
465 
466  if (auto s = getIniFileSection(secConfig, SECTION_SNTP))
467  SNTP_SERVERS = *s;
468 
469  {
470  std::string dbPath;
471  if (getSingleSection(secConfig, "database_path", dbPath, j_))
472  {
473  boost::filesystem::path p(dbPath);
474  legacy("database_path", boost::filesystem::absolute(p).string());
475  }
476  }
477 
478  std::string strTemp;
479 
480  if (getSingleSection(secConfig, SECTION_PEER_PRIVATE, strTemp, j_))
481  PEER_PRIVATE = beast::lexicalCastThrow<bool>(strTemp);
482 
483  if (getSingleSection(secConfig, SECTION_PEERS_MAX, strTemp, j_))
484  {
485  PEERS_MAX = beast::lexicalCastThrow<std::size_t>(strTemp);
486  }
487  else
488  {
489  std::optional<std::size_t> peers_in_max{};
490  if (getSingleSection(secConfig, SECTION_PEERS_IN_MAX, strTemp, j_))
491  {
492  peers_in_max = beast::lexicalCastThrow<std::size_t>(strTemp);
493  if (*peers_in_max > 1000)
494  Throw<std::runtime_error>(
495  "Invalid value specified in [" SECTION_PEERS_IN_MAX
496  "] section; the value must be less or equal than 1000");
497  }
498 
499  std::optional<std::size_t> peers_out_max{};
500  if (getSingleSection(secConfig, SECTION_PEERS_OUT_MAX, strTemp, j_))
501  {
502  peers_out_max = beast::lexicalCastThrow<std::size_t>(strTemp);
503  if (*peers_out_max < 10 || *peers_out_max > 1000)
504  Throw<std::runtime_error>(
505  "Invalid value specified in [" SECTION_PEERS_OUT_MAX
506  "] section; the value must be in range 10-1000");
507  }
508 
509  // if one section is configured then the other must be configured too
510  if ((peers_in_max && !peers_out_max) ||
511  (peers_out_max && !peers_in_max))
512  Throw<std::runtime_error>("Both sections [" SECTION_PEERS_IN_MAX
513  "]"
514  "and [" SECTION_PEERS_OUT_MAX
515  "] must be configured");
516 
517  if (peers_in_max && peers_out_max)
518  {
519  PEERS_IN_MAX = *peers_in_max;
520  PEERS_OUT_MAX = *peers_out_max;
521  }
522  }
523 
524  if (getSingleSection(secConfig, SECTION_NODE_SIZE, strTemp, j_))
525  {
526  if (boost::iequals(strTemp, "tiny"))
527  NODE_SIZE = 0;
528  else if (boost::iequals(strTemp, "small"))
529  NODE_SIZE = 1;
530  else if (boost::iequals(strTemp, "medium"))
531  NODE_SIZE = 2;
532  else if (boost::iequals(strTemp, "large"))
533  NODE_SIZE = 3;
534  else if (boost::iequals(strTemp, "huge"))
535  NODE_SIZE = 4;
536  else
537  NODE_SIZE = std::min<std::size_t>(
538  4, beast::lexicalCastThrow<std::size_t>(strTemp));
539  }
540 
541  if (getSingleSection(secConfig, SECTION_SIGNING_SUPPORT, strTemp, j_))
542  signingEnabled_ = beast::lexicalCastThrow<bool>(strTemp);
543 
544  if (getSingleSection(secConfig, SECTION_ELB_SUPPORT, strTemp, j_))
545  ELB_SUPPORT = beast::lexicalCastThrow<bool>(strTemp);
546 
547  getSingleSection(secConfig, SECTION_SSL_VERIFY_FILE, SSL_VERIFY_FILE, j_);
548  getSingleSection(secConfig, SECTION_SSL_VERIFY_DIR, SSL_VERIFY_DIR, j_);
549 
550  if (getSingleSection(secConfig, SECTION_SSL_VERIFY, strTemp, j_))
551  SSL_VERIFY = beast::lexicalCastThrow<bool>(strTemp);
552 
553  if (getSingleSection(secConfig, SECTION_RELAY_VALIDATIONS, strTemp, j_))
554  {
555  if (boost::iequals(strTemp, "all"))
557  else if (boost::iequals(strTemp, "trusted"))
559  else
560  Throw<std::runtime_error>(
561  "Invalid value specified in [" SECTION_RELAY_VALIDATIONS
562  "] section");
563  }
564 
565  if (getSingleSection(secConfig, SECTION_RELAY_PROPOSALS, strTemp, j_))
566  {
567  if (boost::iequals(strTemp, "all"))
569  else if (boost::iequals(strTemp, "trusted"))
571  else
572  Throw<std::runtime_error>(
573  "Invalid value specified in [" SECTION_RELAY_PROPOSALS
574  "] section");
575  }
576 
577  if (exists(SECTION_VALIDATION_SEED) && exists(SECTION_VALIDATOR_TOKEN))
578  Throw<std::runtime_error>("Cannot have both [" SECTION_VALIDATION_SEED
579  "] and [" SECTION_VALIDATOR_TOKEN
580  "] config sections");
581 
582  if (getSingleSection(secConfig, SECTION_NETWORK_QUORUM, strTemp, j_))
583  NETWORK_QUORUM = beast::lexicalCastThrow<std::size_t>(strTemp);
584 
585  if (getSingleSection(secConfig, SECTION_FEE_ACCOUNT_RESERVE, strTemp, j_))
586  FEE_ACCOUNT_RESERVE = beast::lexicalCastThrow<std::uint64_t>(strTemp);
587 
588  if (getSingleSection(secConfig, SECTION_FEE_OWNER_RESERVE, strTemp, j_))
589  FEE_OWNER_RESERVE = beast::lexicalCastThrow<std::uint64_t>(strTemp);
590 
591  if (getSingleSection(secConfig, SECTION_FEE_DEFAULT, strTemp, j_))
592  FEE_DEFAULT = beast::lexicalCastThrow<std::uint64_t>(strTemp);
593 
594  if (getSingleSection(secConfig, SECTION_LEDGER_HISTORY, strTemp, j_))
595  {
596  if (boost::iequals(strTemp, "full"))
598  std::numeric_limits<decltype(LEDGER_HISTORY)>::max();
599  else if (boost::iequals(strTemp, "none"))
600  LEDGER_HISTORY = 0;
601  else
602  LEDGER_HISTORY = beast::lexicalCastThrow<std::uint32_t>(strTemp);
603  }
604 
605  if (getSingleSection(secConfig, SECTION_FETCH_DEPTH, strTemp, j_))
606  {
607  if (boost::iequals(strTemp, "none"))
608  FETCH_DEPTH = 0;
609  else if (boost::iequals(strTemp, "full"))
610  FETCH_DEPTH = std::numeric_limits<decltype(FETCH_DEPTH)>::max();
611  else
612  FETCH_DEPTH = beast::lexicalCastThrow<std::uint32_t>(strTemp);
613 
614  if (FETCH_DEPTH < 10)
615  FETCH_DEPTH = 10;
616  }
617 
618  if (getSingleSection(secConfig, SECTION_PATH_SEARCH_OLD, strTemp, j_))
619  PATH_SEARCH_OLD = beast::lexicalCastThrow<int>(strTemp);
620  if (getSingleSection(secConfig, SECTION_PATH_SEARCH, strTemp, j_))
621  PATH_SEARCH = beast::lexicalCastThrow<int>(strTemp);
622  if (getSingleSection(secConfig, SECTION_PATH_SEARCH_FAST, strTemp, j_))
623  PATH_SEARCH_FAST = beast::lexicalCastThrow<int>(strTemp);
624  if (getSingleSection(secConfig, SECTION_PATH_SEARCH_MAX, strTemp, j_))
625  PATH_SEARCH_MAX = beast::lexicalCastThrow<int>(strTemp);
626 
627  if (getSingleSection(secConfig, SECTION_DEBUG_LOGFILE, strTemp, j_))
628  DEBUG_LOGFILE = strTemp;
629 
630  if (getSingleSection(secConfig, SECTION_WORKERS, strTemp, j_))
631  WORKERS = beast::lexicalCastThrow<std::size_t>(strTemp);
632 
633  if (getSingleSection(secConfig, SECTION_COMPRESSION, strTemp, j_))
634  COMPRESSION = beast::lexicalCastThrow<bool>(strTemp);
635 
636  if (getSingleSection(secConfig, SECTION_LEDGER_REPLAY, strTemp, j_))
637  LEDGER_REPLAY = beast::lexicalCastThrow<bool>(strTemp);
638 
639  if (exists(SECTION_REDUCE_RELAY))
640  {
641  auto sec = section(SECTION_REDUCE_RELAY);
642  VP_REDUCE_RELAY_ENABLE = sec.value_or("vp_enable", false);
643  VP_REDUCE_RELAY_SQUELCH = sec.value_or("vp_squelch", false);
644  }
645 
646  if (getSingleSection(secConfig, SECTION_MAX_TRANSACTIONS, strTemp, j_))
647  {
649  beast::lexicalCastThrow<int>(strTemp),
652  }
653 
654  if (getSingleSection(secConfig, SECTION_SERVER_DOMAIN, strTemp, j_))
655  {
656  if (!isProperlyFormedTomlDomain(strTemp))
657  {
658  Throw<std::runtime_error>(
659  "Invalid " SECTION_SERVER_DOMAIN
660  ": the domain name does not appear to meet the requirements.");
661  }
662 
663  SERVER_DOMAIN = strTemp;
664  }
665 
666  if (exists(SECTION_OVERLAY))
667  {
668  auto const sec = section(SECTION_OVERLAY);
669 
670  using namespace std::chrono;
671 
672  try
673  {
674  if (auto val = sec.get<std::string>("max_unknown_time"))
676  seconds{beast::lexicalCastThrow<std::uint32_t>(*val)};
677  }
678  catch (...)
679  {
680  Throw<std::runtime_error>(
681  "Invalid value 'max_unknown_time' in " SECTION_OVERLAY
682  ": must be of the form '<number>' representing seconds.");
683  }
684 
685  if (MAX_UNKNOWN_TIME < seconds{300} || MAX_UNKNOWN_TIME > seconds{1800})
686  Throw<std::runtime_error>(
687  "Invalid value 'max_unknown_time' in " SECTION_OVERLAY
688  ": the time must be between 300 and 1800 seconds, inclusive.");
689 
690  try
691  {
692  if (auto val = sec.get<std::string>("max_diverged_time"))
694  seconds{beast::lexicalCastThrow<std::uint32_t>(*val)};
695  }
696  catch (...)
697  {
698  Throw<std::runtime_error>(
699  "Invalid value 'max_diverged_time' in " SECTION_OVERLAY
700  ": must be of the form '<number>' representing seconds.");
701  }
702 
704  {
705  Throw<std::runtime_error>(
706  "Invalid value 'max_diverged_time' in " SECTION_OVERLAY
707  ": the time must be between 60 and 900 seconds, inclusive.");
708  }
709  }
710 
711  if (getSingleSection(
712  secConfig, SECTION_AMENDMENT_MAJORITY_TIME, strTemp, j_))
713  {
714  using namespace std::chrono;
715  boost::regex const re(
716  "^\\s*(\\d+)\\s*(minutes|hours|days|weeks)\\s*(\\s+.*)?$");
717  boost::smatch match;
718  if (!boost::regex_match(strTemp, match, re))
719  Throw<std::runtime_error>(
720  "Invalid " SECTION_AMENDMENT_MAJORITY_TIME
721  ", must be: [0-9]+ [minutes|hours|days|weeks]");
722 
724  beast::lexicalCastThrow<std::uint32_t>(match[1].str());
725 
726  if (boost::iequals(match[2], "minutes"))
728  else if (boost::iequals(match[2], "hours"))
730  else if (boost::iequals(match[2], "days"))
732  else if (boost::iequals(match[2], "weeks"))
734 
736  Throw<std::runtime_error>(
737  "Invalid " SECTION_AMENDMENT_MAJORITY_TIME
738  ", the minimum amount of time an amendment must hold a "
739  "majority is 15 minutes");
740  }
741 
742  if (getSingleSection(secConfig, SECTION_BETA_RPC_API, strTemp, j_))
743  BETA_RPC_API = beast::lexicalCastThrow<bool>(strTemp);
744 
745  // Do not load trusted validator configuration for standalone mode
746  if (!RUN_STANDALONE)
747  {
748  // If a file was explicitly specified, then throw if the
749  // path is malformed or if the file does not exist or is
750  // not a file.
751  // If the specified file is not an absolute path, then look
752  // for it in the same directory as the config file.
753  // If no path was specified, then look for validators.txt
754  // in the same directory as the config file, but don't complain
755  // if we can't find it.
756  boost::filesystem::path validatorsFile;
757 
758  if (getSingleSection(secConfig, SECTION_VALIDATORS_FILE, strTemp, j_))
759  {
760  validatorsFile = strTemp;
761 
762  if (validatorsFile.empty())
763  Throw<std::runtime_error>(
764  "Invalid path specified in [" SECTION_VALIDATORS_FILE "]");
765 
766  if (!validatorsFile.is_absolute() && !CONFIG_DIR.empty())
767  validatorsFile = CONFIG_DIR / validatorsFile;
768 
769  if (!boost::filesystem::exists(validatorsFile))
770  Throw<std::runtime_error>(
771  "The file specified in [" SECTION_VALIDATORS_FILE
772  "] "
773  "does not exist: " +
774  validatorsFile.string());
775 
776  else if (
777  !boost::filesystem::is_regular_file(validatorsFile) &&
778  !boost::filesystem::is_symlink(validatorsFile))
779  Throw<std::runtime_error>(
780  "Invalid file specified in [" SECTION_VALIDATORS_FILE
781  "]: " +
782  validatorsFile.string());
783  }
784  else if (!CONFIG_DIR.empty())
785  {
786  validatorsFile = CONFIG_DIR / validatorsFileName;
787 
788  if (!validatorsFile.empty())
789  {
790  if (!boost::filesystem::exists(validatorsFile))
791  validatorsFile.clear();
792  else if (
793  !boost::filesystem::is_regular_file(validatorsFile) &&
794  !boost::filesystem::is_symlink(validatorsFile))
795  validatorsFile.clear();
796  }
797  }
798 
799  if (!validatorsFile.empty() &&
800  boost::filesystem::exists(validatorsFile) &&
801  (boost::filesystem::is_regular_file(validatorsFile) ||
802  boost::filesystem::is_symlink(validatorsFile)))
803  {
804  boost::system::error_code ec;
805  auto const data = getFileContents(ec, validatorsFile);
806  if (ec)
807  {
808  Throw<std::runtime_error>(
809  "Failed to read '" + validatorsFile.string() + "'." +
810  std::to_string(ec.value()) + ": " + ec.message());
811  }
812 
813  auto iniFile = parseIniFile(data, true);
814 
815  auto entries = getIniFileSection(iniFile, SECTION_VALIDATORS);
816 
817  if (entries)
818  section(SECTION_VALIDATORS).append(*entries);
819 
820  auto valKeyEntries =
821  getIniFileSection(iniFile, SECTION_VALIDATOR_KEYS);
822 
823  if (valKeyEntries)
824  section(SECTION_VALIDATOR_KEYS).append(*valKeyEntries);
825 
826  auto valSiteEntries =
827  getIniFileSection(iniFile, SECTION_VALIDATOR_LIST_SITES);
828 
829  if (valSiteEntries)
830  section(SECTION_VALIDATOR_LIST_SITES).append(*valSiteEntries);
831 
832  auto valListKeys =
833  getIniFileSection(iniFile, SECTION_VALIDATOR_LIST_KEYS);
834 
835  if (valListKeys)
836  section(SECTION_VALIDATOR_LIST_KEYS).append(*valListKeys);
837 
838  if (!entries && !valKeyEntries && !valListKeys)
839  Throw<std::runtime_error>(
840  "The file specified in [" SECTION_VALIDATORS_FILE
841  "] "
842  "does not contain a [" SECTION_VALIDATORS
843  "], "
844  "[" SECTION_VALIDATOR_KEYS
845  "] or "
846  "[" SECTION_VALIDATOR_LIST_KEYS
847  "]"
848  " section: " +
849  validatorsFile.string());
850  }
851 
852  // Consolidate [validator_keys] and [validators]
853  section(SECTION_VALIDATORS)
854  .append(section(SECTION_VALIDATOR_KEYS).lines());
855 
856  if (!section(SECTION_VALIDATOR_LIST_SITES).lines().empty() &&
857  section(SECTION_VALIDATOR_LIST_KEYS).lines().empty())
858  {
859  Throw<std::runtime_error>(
860  "[" + std::string(SECTION_VALIDATOR_LIST_KEYS) +
861  "] config section is missing");
862  }
863  }
864 
865  {
866  auto const part = section("features");
867  for (auto const& s : part.values())
868  {
869  if (auto const f = getRegisteredFeature(s))
870  features.insert(*f);
871  else
872  Throw<std::runtime_error>(
873  "Unknown feature: " + s + " in config file.");
874  }
875  }
876 
877  // This doesn't properly belong here, but check to make sure that the
878  // value specified for network_quorum is achievable:
879  {
880  auto pm = PEERS_MAX;
881 
882  // FIXME this apparently magic value is actually defined as a constant
883  // elsewhere (see defaultMaxPeers) but we handle this check here.
884  if (pm == 0)
885  pm = 21;
886 
887  if (NETWORK_QUORUM > pm)
888  {
889  Throw<std::runtime_error>(
890  "The minimum number of required peers (network_quorum) exceeds "
891  "the maximum number of allowed peers (peers_max)");
892  }
893  }
894 }
895 
896 boost::filesystem::path
898 {
899  auto log_file = DEBUG_LOGFILE;
900 
901  if (!log_file.empty() && !log_file.is_absolute())
902  {
903  // Unless an absolute path for the log file is specified, the
904  // path is relative to the config file directory.
905  log_file = boost::filesystem::absolute(log_file, CONFIG_DIR);
906  }
907 
908  if (!log_file.empty())
909  {
910  auto log_dir = log_file.parent_path();
911 
912  if (!boost::filesystem::is_directory(log_dir))
913  {
914  boost::system::error_code ec;
915  boost::filesystem::create_directories(log_dir, ec);
916 
917  // If we fail, we warn but continue so that the calling code can
918  // decide how to handle this situation.
919  if (ec)
920  {
921  std::cerr << "Unable to create log file path " << log_dir
922  << ": " << ec.message() << '\n';
923  }
924  }
925  }
926 
927  return log_file;
928 }
929 
930 int
932 {
933  auto const index = static_cast<std::underlying_type_t<SizedItem>>(item);
934  assert(index < sizedItems.size());
935  assert(!node || *node <= 4);
936  return sizedItems.at(index).second.at(node.value_or(NODE_SIZE));
937 }
938 
939 } // namespace ripple
ripple::SizedItem::openFinalLimit
@ openFinalLimit
ripple::Section
Holds a collection of configuration values.
Definition: BasicConfig.h:43
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
std::string
STL class.
ripple::SizedItem
SizedItem
Definition: Config.h:48
ripple::Config::PATH_SEARCH
int PATH_SEARCH
Definition: Config.h:165
ripple::Config::LEDGER_REPLAY
bool LEDGER_REPLAY
Definition: Config.h:194
ripple::Config::validatorsFileName
static char const *const validatorsFileName
Definition: Config.h:74
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:188
std::optional::value_or
T value_or(T... args)
ripple::Config::CONFIG_FILE
boost::filesystem::path CONFIG_FILE
Definition: Config.h:81
std::chrono::seconds
ripple::Config::NODE_SIZE
std::size_t NODE_SIZE
Definition: Config.h:184
iterator
ripple::Config::PEER_PRIVATE
bool PEER_PRIVATE
Definition: Config.h:153
ripple::Config::DEBUG_LOGFILE
boost::filesystem::path DEBUG_LOGFILE
Definition: Config.h:87
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:199
iostream
ripple::SizedItem::ledgerFetch
@ ledgerFetch
ripple::Config::MAX_TRANSACTIONS
int MAX_TRANSACTIONS
Definition: Config.h:197
ripple::Config::FETCH_DEPTH
std::uint32_t FETCH_DEPTH
Definition: Config.h:179
algorithm
ripple::Config::PATH_SEARCH_MAX
int PATH_SEARCH_MAX
Definition: Config.h:167
ripple::Config::VP_REDUCE_RELAY_SQUELCH
bool VP_REDUCE_RELAY_SQUELCH
Definition: Config.h:219
ripple::get_if_exists
bool get_if_exists(Section const &section, std::string const &name, T &v)
Definition: BasicConfig.h:347
ripple::Section::append
void append(std::vector< std::string > const &lines)
Append a set of lines to this section.
Definition: BasicConfig.cpp:40
std::underlying_type_t
ripple::Config::RUN_STANDALONE
bool RUN_STANDALONE
Operate in stand-alone mode.
Definition: Config.h:104
ripple::SizedItem::ramSizeGB
@ ramSizeGB
ripple::Config::load
void load()
Definition: Config.cpp:432
ripple::Config::j_
const beast::Journal j_
Definition: Config.h:91
ripple::Config::SSL_VERIFY_FILE
std::string SSL_VERIFY_FILE
Definition: Config.h:187
ripple::Config::loadFromString
void loadFromString(std::string const &fileContents)
Load the config from the contents of the string.
Definition: Config.cpp:454
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:931
ripple::weeks
std::chrono::duration< int, std::ratio_multiply< days::period, std::ratio< 7 > >> weeks
Definition: chrono.h:41
thread
std::thread::hardware_concurrency
T hardware_concurrency(T... args)
ripple::Config::SNTP_SERVERS
std::vector< std::string > SNTP_SERVERS
Definition: Config.h:130
ripple::Config::IPS_FIXED
std::vector< std::string > IPS_FIXED
Definition: Config.h:129
ripple::Config::FEE_OWNER_RESERVE
XRPAmount FEE_OWNER_RESERVE
Definition: Config.h:175
ripple::Config::SSL_VERIFY
bool SSL_VERIFY
Definition: Config.h:186
ripple::BasicConfig::build
void build(IniFileSections const &ifs)
Definition: BasicConfig.cpp:187
ripple::Config::USE_TX_TABLES
bool USE_TX_TABLES
Definition: Config.h:110
ripple::Config::RELAY_UNTRUSTED_PROPOSALS
bool RELAY_UNTRUSTED_PROPOSALS
Definition: Config.h:150
std::getenv
T getenv(T... args)
ripple::sizedItems
constexpr std::array< std::pair< SizedItem, std::array< int, 5 > >, 12 > sizedItems
Definition: Config.cpp:113
std::to_string
T to_string(T... args)
ripple::Config::FEE_ACCOUNT_RESERVE
XRPAmount FEE_ACCOUNT_RESERVE
Definition: Config.h:174
std::array
STL class.
ripple::Config::BETA_RPC_API
bool BETA_RPC_API
Definition: Config.h:235
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:175
ripple::Config::PEERS_OUT_MAX
std::size_t PEERS_OUT_MAX
Definition: Config.h:160
ripple::Config::AMENDMENT_MAJORITY_TIME
std::chrono::seconds AMENDMENT_MAJORITY_TIME
Definition: Config.h:202
ripple::SizedItem::lgrDBCache
@ lgrDBCache
ripple::Config::MAX_JOB_QUEUE_TX
static constexpr int MAX_JOB_QUEUE_TX
Definition: Config.h:198
beast::Journal
A generic endpoint for log messages.
Definition: Journal.h:58
ripple::SizedItem::burstSize
@ burstSize
std::uint64_t
ripple::Config::PEERS_IN_MAX
std::size_t PEERS_IN_MAX
Definition: Config.h:161
ripple::Config::databaseDirName
static char const *const databaseDirName
Definition: Config.h:73
std::map
STL class.
ripple::SizedItem::txnDBCache
@ txnDBCache
ripple::Config::WORKERS
std::size_t WORKERS
Definition: Config.h:205
ripple::Config::PATH_SEARCH_OLD
int PATH_SEARCH_OLD
Definition: Config.h:164
ripple::Config::LEDGER_HISTORY
std::uint32_t LEDGER_HISTORY
Definition: Config.h:178
ripple::parseIniFile
IniFileSections parseIniFile(std::string const &strInput, const bool bTrim)
Definition: Config.cpp:159
ripple::Config::NETWORK_QUORUM
std::size_t NETWORK_QUORUM
Definition: Config.h:146
ripple::Config::VP_REDUCE_RELAY_ENABLE
bool VP_REDUCE_RELAY_ENABLE
Definition: Config.h:210
ripple::Config::CONFIG_DIR
boost::filesystem::path CONFIG_DIR
Definition: Config.h:84
std::string::substr
T substr(T... args)
ripple::Config::PEERS_MAX
std::size_t PEERS_MAX
Definition: Config.h:159
ripple::Config::FEE_DEFAULT
XRPAmount FEE_DEFAULT
Definition: Config.h:173
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:224
ripple::Config::PATH_SEARCH_FAST
int PATH_SEARCH_FAST
Definition: Config.h:166
ripple::getSingleSection
bool getSingleSection(IniFileSections &secSource, std::string const &strSection, std::string &strValue, beast::Journal j)
Definition: Config.cpp:220
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:255
ripple::SizedItem::treeCacheSize
@ treeCacheSize
ripple::Config::SERVER_DOMAIN
std::string SERVER_DOMAIN
Definition: Config.h:226
ripple::SizedItem::sweepInterval
@ sweepInterval
ripple::Config::COMPRESSION
bool COMPRESSION
Definition: Config.h:191
std::string::empty
T empty(T... args)
ripple::Config::RELAY_UNTRUSTED_VALIDATIONS
bool RELAY_UNTRUSTED_VALIDATIONS
Definition: Config.h:149
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:128
ripple::Config::configFileName
static char const *const configFileName
Definition: Config.h:72
std::size_t
ripple::isProperlyFormedTomlDomain
bool isProperlyFormedTomlDomain(std::string const &domain)
Determines if the given string looks like a TOML-file hosting domain.
std::map::end
T end(T... args)
ripple::Config::QUIET
bool QUIET
Definition: Config.h:93
ripple::days
std::chrono::duration< int, std::ratio_multiply< std::chrono::hours::period, std::ratio< 24 > >> days
Definition: chrono.h:38
ripple::getRegisteredFeature
std::optional< uint256 > getRegisteredFeature(std::string const &name)
Definition: Feature.cpp:146
ripple::Config::SILENT
bool SILENT
Definition: Config.h:94
ripple::Config::getDebugLogFile
boost::filesystem::path getDebugLogFile() const
Returns the full path and filename of the debug log file.
Definition: Config.cpp:897
ripple::Config::signingEnabled_
bool signingEnabled_
Determines if the server will sign a tx, given an account's secret seed.
Definition: Config.h:118
std::numeric_limits
ripple::Config::ELB_SUPPORT
bool ELB_SUPPORT
Definition: Config.h:126
ripple::HTTPClient::initializeSSLContext
static void initializeSSLContext(Config const &config, beast::Journal j)
Definition: HTTPClient.cpp:38
ripple::getIniFileSection
IniFileSections::mapped_type * getIniFileSection(IniFileSections &secSource, std::string const &strSection)
Definition: Config.cpp:207
ripple::BasicConfig::exists
bool exists(std::string const &name) const
Returns true if a section with the given name exists.
Definition: BasicConfig.cpp:132
ripple::SizedItem::ledgerAge
@ ledgerAge
ripple::Config::MAX_UNKNOWN_TIME
std::chrono::seconds MAX_UNKNOWN_TIME
Definition: Config.h:229
ripple::BasicConfig::section
Section & section(std::string const &name)
Returns the section with the given name.
Definition: BasicConfig.cpp:138
ripple::Config::MAX_DIVERGED_TIME
std::chrono::seconds MAX_DIVERGED_TIME
Definition: Config.h:232
ripple::IniFileSections
std::map< std::string, std::vector< std::string > > IniFileSections
Definition: BasicConfig.h:36
ripple::Config::RUN_REPORTING
bool RUN_REPORTING
Definition: Config.h:106
beast
Definition: base_uint.h:654
std::chrono