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