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  TX_REDUCE_RELAY_ENABLE = sec.value_or("tx_enable", false);
645  TX_REDUCE_RELAY_METRICS = sec.value_or("tx_metrics", false);
646  TX_REDUCE_RELAY_MIN_PEERS = sec.value_or("tx_min_peers", 20);
647  TX_RELAY_PERCENTAGE = sec.value_or("tx_relay_percentage", 25);
648  if (TX_RELAY_PERCENTAGE < 10 || TX_RELAY_PERCENTAGE > 100 ||
650  Throw<std::runtime_error>(
651  "Invalid " SECTION_REDUCE_RELAY
652  ", tx_min_peers must be greater or equal to 10"
653  ", tx_relay_percentage must be greater or equal to 10 "
654  "and less or equal to 100");
655  }
656 
657  if (getSingleSection(secConfig, SECTION_MAX_TRANSACTIONS, strTemp, j_))
658  {
660  beast::lexicalCastThrow<int>(strTemp),
663  }
664 
665  if (getSingleSection(secConfig, SECTION_SERVER_DOMAIN, strTemp, j_))
666  {
667  if (!isProperlyFormedTomlDomain(strTemp))
668  {
669  Throw<std::runtime_error>(
670  "Invalid " SECTION_SERVER_DOMAIN
671  ": the domain name does not appear to meet the requirements.");
672  }
673 
674  SERVER_DOMAIN = strTemp;
675  }
676 
677  if (exists(SECTION_OVERLAY))
678  {
679  auto const sec = section(SECTION_OVERLAY);
680 
681  using namespace std::chrono;
682 
683  try
684  {
685  if (auto val = sec.get("max_unknown_time"))
687  seconds{beast::lexicalCastThrow<std::uint32_t>(*val)};
688  }
689  catch (...)
690  {
691  Throw<std::runtime_error>(
692  "Invalid value 'max_unknown_time' in " SECTION_OVERLAY
693  ": must be of the form '<number>' representing seconds.");
694  }
695 
696  if (MAX_UNKNOWN_TIME < seconds{300} || MAX_UNKNOWN_TIME > seconds{1800})
697  Throw<std::runtime_error>(
698  "Invalid value 'max_unknown_time' in " SECTION_OVERLAY
699  ": the time must be between 300 and 1800 seconds, inclusive.");
700 
701  try
702  {
703  if (auto val = sec.get("max_diverged_time"))
705  seconds{beast::lexicalCastThrow<std::uint32_t>(*val)};
706  }
707  catch (...)
708  {
709  Throw<std::runtime_error>(
710  "Invalid value 'max_diverged_time' in " SECTION_OVERLAY
711  ": must be of the form '<number>' representing seconds.");
712  }
713 
715  {
716  Throw<std::runtime_error>(
717  "Invalid value 'max_diverged_time' in " SECTION_OVERLAY
718  ": the time must be between 60 and 900 seconds, inclusive.");
719  }
720  }
721 
722  if (getSingleSection(
723  secConfig, SECTION_AMENDMENT_MAJORITY_TIME, strTemp, j_))
724  {
725  using namespace std::chrono;
726  boost::regex const re(
727  "^\\s*(\\d+)\\s*(minutes|hours|days|weeks)\\s*(\\s+.*)?$");
728  boost::smatch match;
729  if (!boost::regex_match(strTemp, match, re))
730  Throw<std::runtime_error>(
731  "Invalid " SECTION_AMENDMENT_MAJORITY_TIME
732  ", must be: [0-9]+ [minutes|hours|days|weeks]");
733 
735  beast::lexicalCastThrow<std::uint32_t>(match[1].str());
736 
737  if (boost::iequals(match[2], "minutes"))
739  else if (boost::iequals(match[2], "hours"))
741  else if (boost::iequals(match[2], "days"))
743  else if (boost::iequals(match[2], "weeks"))
745 
747  Throw<std::runtime_error>(
748  "Invalid " SECTION_AMENDMENT_MAJORITY_TIME
749  ", the minimum amount of time an amendment must hold a "
750  "majority is 15 minutes");
751  }
752 
753  if (getSingleSection(secConfig, SECTION_BETA_RPC_API, strTemp, j_))
754  BETA_RPC_API = beast::lexicalCastThrow<bool>(strTemp);
755 
756  // Do not load trusted validator configuration for standalone mode
757  if (!RUN_STANDALONE)
758  {
759  // If a file was explicitly specified, then throw if the
760  // path is malformed or if the file does not exist or is
761  // not a file.
762  // If the specified file is not an absolute path, then look
763  // for it in the same directory as the config file.
764  // If no path was specified, then look for validators.txt
765  // in the same directory as the config file, but don't complain
766  // if we can't find it.
767  boost::filesystem::path validatorsFile;
768 
769  if (getSingleSection(secConfig, SECTION_VALIDATORS_FILE, strTemp, j_))
770  {
771  validatorsFile = strTemp;
772 
773  if (validatorsFile.empty())
774  Throw<std::runtime_error>(
775  "Invalid path specified in [" SECTION_VALIDATORS_FILE "]");
776 
777  if (!validatorsFile.is_absolute() && !CONFIG_DIR.empty())
778  validatorsFile = CONFIG_DIR / validatorsFile;
779 
780  if (!boost::filesystem::exists(validatorsFile))
781  Throw<std::runtime_error>(
782  "The file specified in [" SECTION_VALIDATORS_FILE
783  "] "
784  "does not exist: " +
785  validatorsFile.string());
786 
787  else if (
788  !boost::filesystem::is_regular_file(validatorsFile) &&
789  !boost::filesystem::is_symlink(validatorsFile))
790  Throw<std::runtime_error>(
791  "Invalid file specified in [" SECTION_VALIDATORS_FILE
792  "]: " +
793  validatorsFile.string());
794  }
795  else if (!CONFIG_DIR.empty())
796  {
797  validatorsFile = CONFIG_DIR / validatorsFileName;
798 
799  if (!validatorsFile.empty())
800  {
801  if (!boost::filesystem::exists(validatorsFile))
802  validatorsFile.clear();
803  else if (
804  !boost::filesystem::is_regular_file(validatorsFile) &&
805  !boost::filesystem::is_symlink(validatorsFile))
806  validatorsFile.clear();
807  }
808  }
809 
810  if (!validatorsFile.empty() &&
811  boost::filesystem::exists(validatorsFile) &&
812  (boost::filesystem::is_regular_file(validatorsFile) ||
813  boost::filesystem::is_symlink(validatorsFile)))
814  {
815  boost::system::error_code ec;
816  auto const data = getFileContents(ec, validatorsFile);
817  if (ec)
818  {
819  Throw<std::runtime_error>(
820  "Failed to read '" + validatorsFile.string() + "'." +
821  std::to_string(ec.value()) + ": " + ec.message());
822  }
823 
824  auto iniFile = parseIniFile(data, true);
825 
826  auto entries = getIniFileSection(iniFile, SECTION_VALIDATORS);
827 
828  if (entries)
829  section(SECTION_VALIDATORS).append(*entries);
830 
831  auto valKeyEntries =
832  getIniFileSection(iniFile, SECTION_VALIDATOR_KEYS);
833 
834  if (valKeyEntries)
835  section(SECTION_VALIDATOR_KEYS).append(*valKeyEntries);
836 
837  auto valSiteEntries =
838  getIniFileSection(iniFile, SECTION_VALIDATOR_LIST_SITES);
839 
840  if (valSiteEntries)
841  section(SECTION_VALIDATOR_LIST_SITES).append(*valSiteEntries);
842 
843  auto valListKeys =
844  getIniFileSection(iniFile, SECTION_VALIDATOR_LIST_KEYS);
845 
846  if (valListKeys)
847  section(SECTION_VALIDATOR_LIST_KEYS).append(*valListKeys);
848 
849  if (!entries && !valKeyEntries && !valListKeys)
850  Throw<std::runtime_error>(
851  "The file specified in [" SECTION_VALIDATORS_FILE
852  "] "
853  "does not contain a [" SECTION_VALIDATORS
854  "], "
855  "[" SECTION_VALIDATOR_KEYS
856  "] or "
857  "[" SECTION_VALIDATOR_LIST_KEYS
858  "]"
859  " section: " +
860  validatorsFile.string());
861  }
862 
863  // Consolidate [validator_keys] and [validators]
864  section(SECTION_VALIDATORS)
865  .append(section(SECTION_VALIDATOR_KEYS).lines());
866 
867  if (!section(SECTION_VALIDATOR_LIST_SITES).lines().empty() &&
868  section(SECTION_VALIDATOR_LIST_KEYS).lines().empty())
869  {
870  Throw<std::runtime_error>(
871  "[" + std::string(SECTION_VALIDATOR_LIST_KEYS) +
872  "] config section is missing");
873  }
874  }
875 
876  {
877  auto const part = section("features");
878  for (auto const& s : part.values())
879  {
880  if (auto const f = getRegisteredFeature(s))
881  features.insert(*f);
882  else
883  Throw<std::runtime_error>(
884  "Unknown feature: " + s + " in config file.");
885  }
886  }
887 
888  // This doesn't properly belong here, but check to make sure that the
889  // value specified for network_quorum is achievable:
890  {
891  auto pm = PEERS_MAX;
892 
893  // FIXME this apparently magic value is actually defined as a constant
894  // elsewhere (see defaultMaxPeers) but we handle this check here.
895  if (pm == 0)
896  pm = 21;
897 
898  if (NETWORK_QUORUM > pm)
899  {
900  Throw<std::runtime_error>(
901  "The minimum number of required peers (network_quorum) exceeds "
902  "the maximum number of allowed peers (peers_max)");
903  }
904  }
905 }
906 
907 boost::filesystem::path
909 {
910  auto log_file = DEBUG_LOGFILE;
911 
912  if (!log_file.empty() && !log_file.is_absolute())
913  {
914  // Unless an absolute path for the log file is specified, the
915  // path is relative to the config file directory.
916  log_file = boost::filesystem::absolute(log_file, CONFIG_DIR);
917  }
918 
919  if (!log_file.empty())
920  {
921  auto log_dir = log_file.parent_path();
922 
923  if (!boost::filesystem::is_directory(log_dir))
924  {
925  boost::system::error_code ec;
926  boost::filesystem::create_directories(log_dir, ec);
927 
928  // If we fail, we warn but continue so that the calling code can
929  // decide how to handle this situation.
930  if (ec)
931  {
932  std::cerr << "Unable to create log file path " << log_dir
933  << ": " << ec.message() << '\n';
934  }
935  }
936  }
937 
938  return log_file;
939 }
940 
941 int
943 {
944  auto const index = static_cast<std::underlying_type_t<SizedItem>>(item);
945  assert(index < sizedItems.size());
946  assert(!node || *node <= 4);
947  return sizedItems.at(index).second.at(node.value_or(NODE_SIZE));
948 }
949 
950 } // namespace ripple
ripple::SizedItem::openFinalLimit
@ openFinalLimit
ripple::Section
Holds a collection of configuration values.
Definition: BasicConfig.h:42
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:384
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: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:942
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:176
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:250
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: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:239
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::TX_REDUCE_RELAY_MIN_PEERS
std::size_t TX_REDUCE_RELAY_MIN_PEERS
Definition: Config.h:231
ripple::Config::SERVER_DOMAIN
std::string SERVER_DOMAIN
Definition: Config.h:241
ripple::Config::TX_REDUCE_RELAY_METRICS
bool TX_REDUCE_RELAY_METRICS
Definition: Config.h:228
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.
ripple::Config::TX_RELAY_PERCENTAGE
std::size_t TX_RELAY_PERCENTAGE
Definition: Config.h:234
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::Config::TX_REDUCE_RELAY_ENABLE
bool TX_REDUCE_RELAY_ENABLE
Definition: Config.h:221
ripple::getRegisteredFeature
std::optional< uint256 > getRegisteredFeature(std::string const &name)
Definition: Feature.cpp:336
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:908
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:121
ripple::SizedItem::ledgerAge
@ ledgerAge
ripple::Config::MAX_UNKNOWN_TIME
std::chrono::seconds MAX_UNKNOWN_TIME
Definition: Config.h:244
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:247
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:106
beast
Definition: base_uint.h:657
std::chrono