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  // By default, validators don't have pathfinding enabled, unless it is
619  // explicitly requested by the server's admin.
620  if (exists(SECTION_VALIDATION_SEED) || exists(SECTION_VALIDATOR_TOKEN))
621  PATH_SEARCH_MAX = 0;
622 
623  if (getSingleSection(secConfig, SECTION_PATH_SEARCH_OLD, strTemp, j_))
624  PATH_SEARCH_OLD = beast::lexicalCastThrow<int>(strTemp);
625  if (getSingleSection(secConfig, SECTION_PATH_SEARCH, strTemp, j_))
626  PATH_SEARCH = beast::lexicalCastThrow<int>(strTemp);
627  if (getSingleSection(secConfig, SECTION_PATH_SEARCH_FAST, strTemp, j_))
628  PATH_SEARCH_FAST = beast::lexicalCastThrow<int>(strTemp);
629  if (getSingleSection(secConfig, SECTION_PATH_SEARCH_MAX, strTemp, j_))
630  PATH_SEARCH_MAX = beast::lexicalCastThrow<int>(strTemp);
631 
632  if (getSingleSection(secConfig, SECTION_DEBUG_LOGFILE, strTemp, j_))
633  DEBUG_LOGFILE = strTemp;
634 
635  if (getSingleSection(secConfig, SECTION_SWEEP_INTERVAL, strTemp, j_))
636  {
637  SWEEP_INTERVAL = beast::lexicalCastThrow<std::size_t>(strTemp);
638 
639  if (SWEEP_INTERVAL < 10 || SWEEP_INTERVAL > 600)
640  Throw<std::runtime_error>("Invalid " SECTION_SWEEP_INTERVAL
641  ": must be between 10 and 600 inclusive");
642  }
643 
644  if (getSingleSection(secConfig, SECTION_WORKERS, strTemp, j_))
645  {
646  WORKERS = beast::lexicalCastThrow<int>(strTemp);
647 
648  if (WORKERS < 1 || WORKERS > 1024)
649  Throw<std::runtime_error>(
650  "Invalid " SECTION_WORKERS
651  ": must be between 1 and 1024 inclusive.");
652  }
653 
654  if (getSingleSection(secConfig, SECTION_COMPRESSION, strTemp, j_))
655  COMPRESSION = beast::lexicalCastThrow<bool>(strTemp);
656 
657  if (getSingleSection(secConfig, SECTION_LEDGER_REPLAY, strTemp, j_))
658  LEDGER_REPLAY = beast::lexicalCastThrow<bool>(strTemp);
659 
660  if (exists(SECTION_REDUCE_RELAY))
661  {
662  auto sec = section(SECTION_REDUCE_RELAY);
663  VP_REDUCE_RELAY_ENABLE = sec.value_or("vp_enable", false);
664  VP_REDUCE_RELAY_SQUELCH = sec.value_or("vp_squelch", false);
665  TX_REDUCE_RELAY_ENABLE = sec.value_or("tx_enable", false);
666  TX_REDUCE_RELAY_METRICS = sec.value_or("tx_metrics", false);
667  TX_REDUCE_RELAY_MIN_PEERS = sec.value_or("tx_min_peers", 20);
668  TX_RELAY_PERCENTAGE = sec.value_or("tx_relay_percentage", 25);
669  if (TX_RELAY_PERCENTAGE < 10 || TX_RELAY_PERCENTAGE > 100 ||
671  Throw<std::runtime_error>(
672  "Invalid " SECTION_REDUCE_RELAY
673  ", tx_min_peers must be greater or equal to 10"
674  ", tx_relay_percentage must be greater or equal to 10 "
675  "and less or equal to 100");
676  }
677 
678  if (getSingleSection(secConfig, SECTION_MAX_TRANSACTIONS, strTemp, j_))
679  {
681  beast::lexicalCastThrow<int>(strTemp),
684  }
685 
686  if (getSingleSection(secConfig, SECTION_SERVER_DOMAIN, strTemp, j_))
687  {
688  if (!isProperlyFormedTomlDomain(strTemp))
689  {
690  Throw<std::runtime_error>(
691  "Invalid " SECTION_SERVER_DOMAIN
692  ": the domain name does not appear to meet the requirements.");
693  }
694 
695  SERVER_DOMAIN = strTemp;
696  }
697 
698  if (exists(SECTION_OVERLAY))
699  {
700  auto const sec = section(SECTION_OVERLAY);
701 
702  using namespace std::chrono;
703 
704  try
705  {
706  if (auto val = sec.get("max_unknown_time"))
708  seconds{beast::lexicalCastThrow<std::uint32_t>(*val)};
709  }
710  catch (...)
711  {
712  Throw<std::runtime_error>(
713  "Invalid value 'max_unknown_time' in " SECTION_OVERLAY
714  ": must be of the form '<number>' representing seconds.");
715  }
716 
717  if (MAX_UNKNOWN_TIME < seconds{300} || MAX_UNKNOWN_TIME > seconds{1800})
718  Throw<std::runtime_error>(
719  "Invalid value 'max_unknown_time' in " SECTION_OVERLAY
720  ": the time must be between 300 and 1800 seconds, inclusive.");
721 
722  try
723  {
724  if (auto val = sec.get("max_diverged_time"))
726  seconds{beast::lexicalCastThrow<std::uint32_t>(*val)};
727  }
728  catch (...)
729  {
730  Throw<std::runtime_error>(
731  "Invalid value 'max_diverged_time' in " SECTION_OVERLAY
732  ": must be of the form '<number>' representing seconds.");
733  }
734 
736  {
737  Throw<std::runtime_error>(
738  "Invalid value 'max_diverged_time' in " SECTION_OVERLAY
739  ": the time must be between 60 and 900 seconds, inclusive.");
740  }
741  }
742 
743  if (getSingleSection(
744  secConfig, SECTION_AMENDMENT_MAJORITY_TIME, strTemp, j_))
745  {
746  using namespace std::chrono;
747  boost::regex const re(
748  "^\\s*(\\d+)\\s*(minutes|hours|days|weeks)\\s*(\\s+.*)?$");
749  boost::smatch match;
750  if (!boost::regex_match(strTemp, match, re))
751  Throw<std::runtime_error>(
752  "Invalid " SECTION_AMENDMENT_MAJORITY_TIME
753  ", must be: [0-9]+ [minutes|hours|days|weeks]");
754 
756  beast::lexicalCastThrow<std::uint32_t>(match[1].str());
757 
758  if (boost::iequals(match[2], "minutes"))
760  else if (boost::iequals(match[2], "hours"))
762  else if (boost::iequals(match[2], "days"))
764  else if (boost::iequals(match[2], "weeks"))
766 
768  Throw<std::runtime_error>(
769  "Invalid " SECTION_AMENDMENT_MAJORITY_TIME
770  ", the minimum amount of time an amendment must hold a "
771  "majority is 15 minutes");
772  }
773 
774  if (getSingleSection(secConfig, SECTION_BETA_RPC_API, strTemp, j_))
775  BETA_RPC_API = beast::lexicalCastThrow<bool>(strTemp);
776 
777  // Do not load trusted validator configuration for standalone mode
778  if (!RUN_STANDALONE)
779  {
780  // If a file was explicitly specified, then throw if the
781  // path is malformed or if the file does not exist or is
782  // not a file.
783  // If the specified file is not an absolute path, then look
784  // for it in the same directory as the config file.
785  // If no path was specified, then look for validators.txt
786  // in the same directory as the config file, but don't complain
787  // if we can't find it.
788  boost::filesystem::path validatorsFile;
789 
790  if (getSingleSection(secConfig, SECTION_VALIDATORS_FILE, strTemp, j_))
791  {
792  validatorsFile = strTemp;
793 
794  if (validatorsFile.empty())
795  Throw<std::runtime_error>(
796  "Invalid path specified in [" SECTION_VALIDATORS_FILE "]");
797 
798  if (!validatorsFile.is_absolute() && !CONFIG_DIR.empty())
799  validatorsFile = CONFIG_DIR / validatorsFile;
800 
801  if (!boost::filesystem::exists(validatorsFile))
802  Throw<std::runtime_error>(
803  "The file specified in [" SECTION_VALIDATORS_FILE
804  "] "
805  "does not exist: " +
806  validatorsFile.string());
807 
808  else if (
809  !boost::filesystem::is_regular_file(validatorsFile) &&
810  !boost::filesystem::is_symlink(validatorsFile))
811  Throw<std::runtime_error>(
812  "Invalid file specified in [" SECTION_VALIDATORS_FILE
813  "]: " +
814  validatorsFile.string());
815  }
816  else if (!CONFIG_DIR.empty())
817  {
818  validatorsFile = CONFIG_DIR / validatorsFileName;
819 
820  if (!validatorsFile.empty())
821  {
822  if (!boost::filesystem::exists(validatorsFile))
823  validatorsFile.clear();
824  else if (
825  !boost::filesystem::is_regular_file(validatorsFile) &&
826  !boost::filesystem::is_symlink(validatorsFile))
827  validatorsFile.clear();
828  }
829  }
830 
831  if (!validatorsFile.empty() &&
832  boost::filesystem::exists(validatorsFile) &&
833  (boost::filesystem::is_regular_file(validatorsFile) ||
834  boost::filesystem::is_symlink(validatorsFile)))
835  {
836  boost::system::error_code ec;
837  auto const data = getFileContents(ec, validatorsFile);
838  if (ec)
839  {
840  Throw<std::runtime_error>(
841  "Failed to read '" + validatorsFile.string() + "'." +
842  std::to_string(ec.value()) + ": " + ec.message());
843  }
844 
845  auto iniFile = parseIniFile(data, true);
846 
847  auto entries = getIniFileSection(iniFile, SECTION_VALIDATORS);
848 
849  if (entries)
850  section(SECTION_VALIDATORS).append(*entries);
851 
852  auto valKeyEntries =
853  getIniFileSection(iniFile, SECTION_VALIDATOR_KEYS);
854 
855  if (valKeyEntries)
856  section(SECTION_VALIDATOR_KEYS).append(*valKeyEntries);
857 
858  auto valSiteEntries =
859  getIniFileSection(iniFile, SECTION_VALIDATOR_LIST_SITES);
860 
861  if (valSiteEntries)
862  section(SECTION_VALIDATOR_LIST_SITES).append(*valSiteEntries);
863 
864  auto valListKeys =
865  getIniFileSection(iniFile, SECTION_VALIDATOR_LIST_KEYS);
866 
867  if (valListKeys)
868  section(SECTION_VALIDATOR_LIST_KEYS).append(*valListKeys);
869 
870  if (!entries && !valKeyEntries && !valListKeys)
871  Throw<std::runtime_error>(
872  "The file specified in [" SECTION_VALIDATORS_FILE
873  "] "
874  "does not contain a [" SECTION_VALIDATORS
875  "], "
876  "[" SECTION_VALIDATOR_KEYS
877  "] or "
878  "[" SECTION_VALIDATOR_LIST_KEYS
879  "]"
880  " section: " +
881  validatorsFile.string());
882  }
883 
884  // Consolidate [validator_keys] and [validators]
885  section(SECTION_VALIDATORS)
886  .append(section(SECTION_VALIDATOR_KEYS).lines());
887 
888  if (!section(SECTION_VALIDATOR_LIST_SITES).lines().empty() &&
889  section(SECTION_VALIDATOR_LIST_KEYS).lines().empty())
890  {
891  Throw<std::runtime_error>(
892  "[" + std::string(SECTION_VALIDATOR_LIST_KEYS) +
893  "] config section is missing");
894  }
895  }
896 
897  {
898  auto const part = section("features");
899  for (auto const& s : part.values())
900  {
901  if (auto const f = getRegisteredFeature(s))
902  features.insert(*f);
903  else
904  Throw<std::runtime_error>(
905  "Unknown feature: " + s + " in config file.");
906  }
907  }
908 
909  // This doesn't properly belong here, but check to make sure that the
910  // value specified for network_quorum is achievable:
911  {
912  auto pm = PEERS_MAX;
913 
914  // FIXME this apparently magic value is actually defined as a constant
915  // elsewhere (see defaultMaxPeers) but we handle this check here.
916  if (pm == 0)
917  pm = 21;
918 
919  if (NETWORK_QUORUM > pm)
920  {
921  Throw<std::runtime_error>(
922  "The minimum number of required peers (network_quorum) exceeds "
923  "the maximum number of allowed peers (peers_max)");
924  }
925  }
926 }
927 
928 boost::filesystem::path
930 {
931  auto log_file = DEBUG_LOGFILE;
932 
933  if (!log_file.empty() && !log_file.is_absolute())
934  {
935  // Unless an absolute path for the log file is specified, the
936  // path is relative to the config file directory.
937  log_file = boost::filesystem::absolute(log_file, CONFIG_DIR);
938  }
939 
940  if (!log_file.empty())
941  {
942  auto log_dir = log_file.parent_path();
943 
944  if (!boost::filesystem::is_directory(log_dir))
945  {
946  boost::system::error_code ec;
947  boost::filesystem::create_directories(log_dir, ec);
948 
949  // If we fail, we warn but continue so that the calling code can
950  // decide how to handle this situation.
951  if (ec)
952  {
953  std::cerr << "Unable to create log file path " << log_dir
954  << ": " << ec.message() << '\n';
955  }
956  }
957  }
958 
959  return log_file;
960 }
961 
962 int
964 {
965  auto const index = static_cast<std::underlying_type_t<SizedItem>>(item);
966  assert(index < sizedItems.size());
967  assert(!node || *node <= 4);
968  return sizedItems.at(index).second.at(node.value_or(NODE_SIZE));
969 }
970 
971 } // 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:222
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:176
ripple::Config::LEDGER_REPLAY
bool LEDGER_REPLAY
Definition: Config.h:205
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:199
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:195
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:210
iostream
ripple::SizedItem::ledgerFetch
@ ledgerFetch
ripple::Config::MAX_TRANSACTIONS
int MAX_TRANSACTIONS
Definition: Config.h:208
ripple::Config::FETCH_DEPTH
std::uint32_t FETCH_DEPTH
Definition: Config.h:190
algorithm
ripple::Config::PATH_SEARCH_MAX
int PATH_SEARCH_MAX
Definition: Config.h:178
ripple::Config::VP_REDUCE_RELAY_SQUELCH
bool VP_REDUCE_RELAY_SQUELCH
Definition: Config.h:236
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:198
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:963
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:186
ripple::Config::SSL_VERIFY
bool SSL_VERIFY
Definition: Config.h:197
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:185
std::array
STL class.
ripple::Config::BETA_RPC_API
bool BETA_RPC_API
Definition: Config.h:267
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:213
ripple::SizedItem::lgrDBCache
@ lgrDBCache
ripple::Config::MAX_JOB_QUEUE_TX
static constexpr int MAX_JOB_QUEUE_TX
Definition: Config.h:209
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:216
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::PATH_SEARCH_OLD
int PATH_SEARCH_OLD
Definition: Config.h:175
ripple::Config::LEDGER_HISTORY
std::uint32_t LEDGER_HISTORY
Definition: Config.h:189
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:227
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:184
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:256
ripple::Config::PATH_SEARCH_FAST
int PATH_SEARCH_FAST
Definition: Config.h:177
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:248
ripple::Config::SERVER_DOMAIN
std::string SERVER_DOMAIN
Definition: Config.h:258
ripple::Config::TX_REDUCE_RELAY_METRICS
bool TX_REDUCE_RELAY_METRICS
Definition: Config.h:245
ripple::SizedItem::sweepInterval
@ sweepInterval
ripple::Config::COMPRESSION
bool COMPRESSION
Definition: Config.h:202
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:251
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:238
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:929
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:261
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:264
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