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 }};
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  Section& nodeDbSection{section(ConfigSection::nodeDatabase())};
431  get_if_exists(nodeDbSection, "fast_load", FAST_LOAD);
432 }
433 
434 void
436 {
437  // NOTE: this writes to cerr because we want cout to be reserved
438  // for the writing of the json response (so that stdout can be part of a
439  // pipeline, for instance)
440  if (!QUIET)
441  std::cerr << "Loading: " << CONFIG_FILE << "\n";
442 
443  boost::system::error_code ec;
444  auto const fileContents = getFileContents(ec, CONFIG_FILE);
445 
446  if (ec)
447  {
448  std::cerr << "Failed to read '" << CONFIG_FILE << "'." << ec.value()
449  << ": " << ec.message() << std::endl;
450  return;
451  }
452 
453  loadFromString(fileContents);
454 }
455 
456 void
458 {
459  IniFileSections secConfig = parseIniFile(fileContents, true);
460 
461  build(secConfig);
462 
463  if (auto s = getIniFileSection(secConfig, SECTION_IPS))
464  IPS = *s;
465 
466  if (auto s = getIniFileSection(secConfig, SECTION_IPS_FIXED))
467  IPS_FIXED = *s;
468 
469  if (auto s = getIniFileSection(secConfig, SECTION_SNTP))
470  SNTP_SERVERS = *s;
471 
472  {
473  std::string dbPath;
474  if (getSingleSection(secConfig, "database_path", dbPath, j_))
475  {
476  boost::filesystem::path p(dbPath);
477  legacy("database_path", boost::filesystem::absolute(p).string());
478  }
479  }
480 
481  std::string strTemp;
482 
483  if (getSingleSection(secConfig, SECTION_PEER_PRIVATE, strTemp, j_))
484  PEER_PRIVATE = beast::lexicalCastThrow<bool>(strTemp);
485 
486  if (getSingleSection(secConfig, SECTION_PEERS_MAX, strTemp, j_))
487  {
488  PEERS_MAX = beast::lexicalCastThrow<std::size_t>(strTemp);
489  }
490  else
491  {
492  std::optional<std::size_t> peers_in_max{};
493  if (getSingleSection(secConfig, SECTION_PEERS_IN_MAX, strTemp, j_))
494  {
495  peers_in_max = beast::lexicalCastThrow<std::size_t>(strTemp);
496  if (*peers_in_max > 1000)
497  Throw<std::runtime_error>(
498  "Invalid value specified in [" SECTION_PEERS_IN_MAX
499  "] section; the value must be less or equal than 1000");
500  }
501 
502  std::optional<std::size_t> peers_out_max{};
503  if (getSingleSection(secConfig, SECTION_PEERS_OUT_MAX, strTemp, j_))
504  {
505  peers_out_max = beast::lexicalCastThrow<std::size_t>(strTemp);
506  if (*peers_out_max < 10 || *peers_out_max > 1000)
507  Throw<std::runtime_error>(
508  "Invalid value specified in [" SECTION_PEERS_OUT_MAX
509  "] section; the value must be in range 10-1000");
510  }
511 
512  // if one section is configured then the other must be configured too
513  if ((peers_in_max && !peers_out_max) ||
514  (peers_out_max && !peers_in_max))
515  Throw<std::runtime_error>("Both sections [" SECTION_PEERS_IN_MAX
516  "]"
517  "and [" SECTION_PEERS_OUT_MAX
518  "] must be configured");
519 
520  if (peers_in_max && peers_out_max)
521  {
522  PEERS_IN_MAX = *peers_in_max;
523  PEERS_OUT_MAX = *peers_out_max;
524  }
525  }
526 
527  if (getSingleSection(secConfig, SECTION_NODE_SIZE, strTemp, j_))
528  {
529  if (boost::iequals(strTemp, "tiny"))
530  NODE_SIZE = 0;
531  else if (boost::iequals(strTemp, "small"))
532  NODE_SIZE = 1;
533  else if (boost::iequals(strTemp, "medium"))
534  NODE_SIZE = 2;
535  else if (boost::iequals(strTemp, "large"))
536  NODE_SIZE = 3;
537  else if (boost::iequals(strTemp, "huge"))
538  NODE_SIZE = 4;
539  else
540  NODE_SIZE = std::min<std::size_t>(
541  4, beast::lexicalCastThrow<std::size_t>(strTemp));
542  }
543 
544  if (getSingleSection(secConfig, SECTION_SIGNING_SUPPORT, strTemp, j_))
545  signingEnabled_ = beast::lexicalCastThrow<bool>(strTemp);
546 
547  if (getSingleSection(secConfig, SECTION_ELB_SUPPORT, strTemp, j_))
548  ELB_SUPPORT = beast::lexicalCastThrow<bool>(strTemp);
549 
550  getSingleSection(secConfig, SECTION_SSL_VERIFY_FILE, SSL_VERIFY_FILE, j_);
551  getSingleSection(secConfig, SECTION_SSL_VERIFY_DIR, SSL_VERIFY_DIR, j_);
552 
553  if (getSingleSection(secConfig, SECTION_SSL_VERIFY, strTemp, j_))
554  SSL_VERIFY = beast::lexicalCastThrow<bool>(strTemp);
555 
556  if (getSingleSection(secConfig, SECTION_RELAY_VALIDATIONS, strTemp, j_))
557  {
558  if (boost::iequals(strTemp, "all"))
560  else if (boost::iequals(strTemp, "trusted"))
562  else if (boost::iequals(strTemp, "drop_untrusted"))
564  else
565  Throw<std::runtime_error>(
566  "Invalid value specified in [" SECTION_RELAY_VALIDATIONS
567  "] section");
568  }
569 
570  if (getSingleSection(secConfig, SECTION_RELAY_PROPOSALS, strTemp, j_))
571  {
572  if (boost::iequals(strTemp, "all"))
574  else if (boost::iequals(strTemp, "trusted"))
576  else if (boost::iequals(strTemp, "drop_untrusted"))
578  else
579  Throw<std::runtime_error>(
580  "Invalid value specified in [" SECTION_RELAY_PROPOSALS
581  "] section");
582  }
583 
584  if (exists(SECTION_VALIDATION_SEED) && exists(SECTION_VALIDATOR_TOKEN))
585  Throw<std::runtime_error>("Cannot have both [" SECTION_VALIDATION_SEED
586  "] and [" SECTION_VALIDATOR_TOKEN
587  "] config sections");
588 
589  if (getSingleSection(secConfig, SECTION_NETWORK_QUORUM, strTemp, j_))
590  NETWORK_QUORUM = beast::lexicalCastThrow<std::size_t>(strTemp);
591 
592  if (getSingleSection(secConfig, SECTION_FEE_ACCOUNT_RESERVE, strTemp, j_))
593  FEE_ACCOUNT_RESERVE = beast::lexicalCastThrow<std::uint64_t>(strTemp);
594 
595  if (getSingleSection(secConfig, SECTION_FEE_OWNER_RESERVE, strTemp, j_))
596  FEE_OWNER_RESERVE = beast::lexicalCastThrow<std::uint64_t>(strTemp);
597 
598  if (getSingleSection(secConfig, SECTION_FEE_DEFAULT, strTemp, j_))
599  FEE_DEFAULT = beast::lexicalCastThrow<std::uint64_t>(strTemp);
600 
601  if (getSingleSection(secConfig, SECTION_LEDGER_HISTORY, strTemp, j_))
602  {
603  if (boost::iequals(strTemp, "full"))
605  std::numeric_limits<decltype(LEDGER_HISTORY)>::max();
606  else if (boost::iequals(strTemp, "none"))
607  LEDGER_HISTORY = 0;
608  else
609  LEDGER_HISTORY = beast::lexicalCastThrow<std::uint32_t>(strTemp);
610  }
611 
612  if (getSingleSection(secConfig, SECTION_FETCH_DEPTH, strTemp, j_))
613  {
614  if (boost::iequals(strTemp, "none"))
615  FETCH_DEPTH = 0;
616  else if (boost::iequals(strTemp, "full"))
617  FETCH_DEPTH = std::numeric_limits<decltype(FETCH_DEPTH)>::max();
618  else
619  FETCH_DEPTH = beast::lexicalCastThrow<std::uint32_t>(strTemp);
620 
621  if (FETCH_DEPTH < 10)
622  FETCH_DEPTH = 10;
623  }
624 
625  // By default, validators don't have pathfinding enabled, unless it is
626  // explicitly requested by the server's admin.
627  if (exists(SECTION_VALIDATION_SEED) || exists(SECTION_VALIDATOR_TOKEN))
628  PATH_SEARCH_MAX = 0;
629 
630  if (getSingleSection(secConfig, SECTION_PATH_SEARCH_OLD, strTemp, j_))
631  PATH_SEARCH_OLD = beast::lexicalCastThrow<int>(strTemp);
632  if (getSingleSection(secConfig, SECTION_PATH_SEARCH, strTemp, j_))
633  PATH_SEARCH = beast::lexicalCastThrow<int>(strTemp);
634  if (getSingleSection(secConfig, SECTION_PATH_SEARCH_FAST, strTemp, j_))
635  PATH_SEARCH_FAST = beast::lexicalCastThrow<int>(strTemp);
636  if (getSingleSection(secConfig, SECTION_PATH_SEARCH_MAX, strTemp, j_))
637  PATH_SEARCH_MAX = beast::lexicalCastThrow<int>(strTemp);
638 
639  if (getSingleSection(secConfig, SECTION_DEBUG_LOGFILE, strTemp, j_))
640  DEBUG_LOGFILE = strTemp;
641 
642  if (getSingleSection(secConfig, SECTION_SWEEP_INTERVAL, strTemp, j_))
643  {
644  SWEEP_INTERVAL = beast::lexicalCastThrow<std::size_t>(strTemp);
645 
646  if (SWEEP_INTERVAL < 10 || SWEEP_INTERVAL > 600)
647  Throw<std::runtime_error>("Invalid " SECTION_SWEEP_INTERVAL
648  ": must be between 10 and 600 inclusive");
649  }
650 
651  if (getSingleSection(secConfig, SECTION_WORKERS, strTemp, j_))
652  {
653  WORKERS = beast::lexicalCastThrow<int>(strTemp);
654 
655  if (WORKERS < 1 || WORKERS > 1024)
656  Throw<std::runtime_error>(
657  "Invalid " SECTION_WORKERS
658  ": must be between 1 and 1024 inclusive.");
659  }
660 
661  if (getSingleSection(secConfig, SECTION_IO_WORKERS, strTemp, j_))
662  {
663  IO_WORKERS = beast::lexicalCastThrow<int>(strTemp);
664 
665  if (IO_WORKERS < 1 || IO_WORKERS > 1024)
666  Throw<std::runtime_error>(
667  "Invalid " SECTION_IO_WORKERS
668  ": must be between 1 and 1024 inclusive.");
669  }
670 
671  if (getSingleSection(secConfig, SECTION_PREFETCH_WORKERS, strTemp, j_))
672  {
673  PREFETCH_WORKERS = beast::lexicalCastThrow<int>(strTemp);
674 
675  if (PREFETCH_WORKERS < 1 || PREFETCH_WORKERS > 1024)
676  Throw<std::runtime_error>(
677  "Invalid " SECTION_PREFETCH_WORKERS
678  ": must be between 1 and 1024 inclusive.");
679  }
680 
681  if (getSingleSection(secConfig, SECTION_COMPRESSION, strTemp, j_))
682  COMPRESSION = beast::lexicalCastThrow<bool>(strTemp);
683 
684  if (getSingleSection(secConfig, SECTION_LEDGER_REPLAY, strTemp, j_))
685  LEDGER_REPLAY = beast::lexicalCastThrow<bool>(strTemp);
686 
687  if (exists(SECTION_REDUCE_RELAY))
688  {
689  auto sec = section(SECTION_REDUCE_RELAY);
690  VP_REDUCE_RELAY_ENABLE = sec.value_or("vp_enable", false);
691  VP_REDUCE_RELAY_SQUELCH = sec.value_or("vp_squelch", false);
692  TX_REDUCE_RELAY_ENABLE = sec.value_or("tx_enable", false);
693  TX_REDUCE_RELAY_METRICS = sec.value_or("tx_metrics", false);
694  TX_REDUCE_RELAY_MIN_PEERS = sec.value_or("tx_min_peers", 20);
695  TX_RELAY_PERCENTAGE = sec.value_or("tx_relay_percentage", 25);
696  if (TX_RELAY_PERCENTAGE < 10 || TX_RELAY_PERCENTAGE > 100 ||
698  Throw<std::runtime_error>(
699  "Invalid " SECTION_REDUCE_RELAY
700  ", tx_min_peers must be greater or equal to 10"
701  ", tx_relay_percentage must be greater or equal to 10 "
702  "and less or equal to 100");
703  }
704 
705  if (getSingleSection(secConfig, SECTION_MAX_TRANSACTIONS, strTemp, j_))
706  {
708  beast::lexicalCastThrow<int>(strTemp),
711  }
712 
713  if (getSingleSection(secConfig, SECTION_SERVER_DOMAIN, strTemp, j_))
714  {
715  if (!isProperlyFormedTomlDomain(strTemp))
716  {
717  Throw<std::runtime_error>(
718  "Invalid " SECTION_SERVER_DOMAIN
719  ": the domain name does not appear to meet the requirements.");
720  }
721 
722  SERVER_DOMAIN = strTemp;
723  }
724 
725  if (exists(SECTION_OVERLAY))
726  {
727  auto const sec = section(SECTION_OVERLAY);
728 
729  using namespace std::chrono;
730 
731  try
732  {
733  if (auto val = sec.get("max_unknown_time"))
735  seconds{beast::lexicalCastThrow<std::uint32_t>(*val)};
736  }
737  catch (...)
738  {
739  Throw<std::runtime_error>(
740  "Invalid value 'max_unknown_time' in " SECTION_OVERLAY
741  ": must be of the form '<number>' representing seconds.");
742  }
743 
744  if (MAX_UNKNOWN_TIME < seconds{300} || MAX_UNKNOWN_TIME > seconds{1800})
745  Throw<std::runtime_error>(
746  "Invalid value 'max_unknown_time' in " SECTION_OVERLAY
747  ": the time must be between 300 and 1800 seconds, inclusive.");
748 
749  try
750  {
751  if (auto val = sec.get("max_diverged_time"))
753  seconds{beast::lexicalCastThrow<std::uint32_t>(*val)};
754  }
755  catch (...)
756  {
757  Throw<std::runtime_error>(
758  "Invalid value 'max_diverged_time' in " SECTION_OVERLAY
759  ": must be of the form '<number>' representing seconds.");
760  }
761 
763  {
764  Throw<std::runtime_error>(
765  "Invalid value 'max_diverged_time' in " SECTION_OVERLAY
766  ": the time must be between 60 and 900 seconds, inclusive.");
767  }
768  }
769 
770  if (getSingleSection(
771  secConfig, SECTION_AMENDMENT_MAJORITY_TIME, strTemp, j_))
772  {
773  using namespace std::chrono;
774  boost::regex const re(
775  "^\\s*(\\d+)\\s*(minutes|hours|days|weeks)\\s*(\\s+.*)?$");
776  boost::smatch match;
777  if (!boost::regex_match(strTemp, match, re))
778  Throw<std::runtime_error>(
779  "Invalid " SECTION_AMENDMENT_MAJORITY_TIME
780  ", must be: [0-9]+ [minutes|hours|days|weeks]");
781 
783  beast::lexicalCastThrow<std::uint32_t>(match[1].str());
784 
785  if (boost::iequals(match[2], "minutes"))
787  else if (boost::iequals(match[2], "hours"))
789  else if (boost::iequals(match[2], "days"))
791  else if (boost::iequals(match[2], "weeks"))
793 
795  Throw<std::runtime_error>(
796  "Invalid " SECTION_AMENDMENT_MAJORITY_TIME
797  ", the minimum amount of time an amendment must hold a "
798  "majority is 15 minutes");
799  }
800 
801  if (getSingleSection(secConfig, SECTION_BETA_RPC_API, strTemp, j_))
802  BETA_RPC_API = beast::lexicalCastThrow<bool>(strTemp);
803 
804  // Do not load trusted validator configuration for standalone mode
805  if (!RUN_STANDALONE)
806  {
807  // If a file was explicitly specified, then throw if the
808  // path is malformed or if the file does not exist or is
809  // not a file.
810  // If the specified file is not an absolute path, then look
811  // for it in the same directory as the config file.
812  // If no path was specified, then look for validators.txt
813  // in the same directory as the config file, but don't complain
814  // if we can't find it.
815  boost::filesystem::path validatorsFile;
816 
817  if (getSingleSection(secConfig, SECTION_VALIDATORS_FILE, strTemp, j_))
818  {
819  validatorsFile = strTemp;
820 
821  if (validatorsFile.empty())
822  Throw<std::runtime_error>(
823  "Invalid path specified in [" SECTION_VALIDATORS_FILE "]");
824 
825  if (!validatorsFile.is_absolute() && !CONFIG_DIR.empty())
826  validatorsFile = CONFIG_DIR / validatorsFile;
827 
828  if (!boost::filesystem::exists(validatorsFile))
829  Throw<std::runtime_error>(
830  "The file specified in [" SECTION_VALIDATORS_FILE
831  "] "
832  "does not exist: " +
833  validatorsFile.string());
834 
835  else if (
836  !boost::filesystem::is_regular_file(validatorsFile) &&
837  !boost::filesystem::is_symlink(validatorsFile))
838  Throw<std::runtime_error>(
839  "Invalid file specified in [" SECTION_VALIDATORS_FILE
840  "]: " +
841  validatorsFile.string());
842  }
843  else if (!CONFIG_DIR.empty())
844  {
845  validatorsFile = CONFIG_DIR / validatorsFileName;
846 
847  if (!validatorsFile.empty())
848  {
849  if (!boost::filesystem::exists(validatorsFile))
850  validatorsFile.clear();
851  else if (
852  !boost::filesystem::is_regular_file(validatorsFile) &&
853  !boost::filesystem::is_symlink(validatorsFile))
854  validatorsFile.clear();
855  }
856  }
857 
858  if (!validatorsFile.empty() &&
859  boost::filesystem::exists(validatorsFile) &&
860  (boost::filesystem::is_regular_file(validatorsFile) ||
861  boost::filesystem::is_symlink(validatorsFile)))
862  {
863  boost::system::error_code ec;
864  auto const data = getFileContents(ec, validatorsFile);
865  if (ec)
866  {
867  Throw<std::runtime_error>(
868  "Failed to read '" + validatorsFile.string() + "'." +
869  std::to_string(ec.value()) + ": " + ec.message());
870  }
871 
872  auto iniFile = parseIniFile(data, true);
873 
874  auto entries = getIniFileSection(iniFile, SECTION_VALIDATORS);
875 
876  if (entries)
877  section(SECTION_VALIDATORS).append(*entries);
878 
879  auto valKeyEntries =
880  getIniFileSection(iniFile, SECTION_VALIDATOR_KEYS);
881 
882  if (valKeyEntries)
883  section(SECTION_VALIDATOR_KEYS).append(*valKeyEntries);
884 
885  auto valSiteEntries =
886  getIniFileSection(iniFile, SECTION_VALIDATOR_LIST_SITES);
887 
888  if (valSiteEntries)
889  section(SECTION_VALIDATOR_LIST_SITES).append(*valSiteEntries);
890 
891  auto valListKeys =
892  getIniFileSection(iniFile, SECTION_VALIDATOR_LIST_KEYS);
893 
894  if (valListKeys)
895  section(SECTION_VALIDATOR_LIST_KEYS).append(*valListKeys);
896 
897  if (!entries && !valKeyEntries && !valListKeys)
898  Throw<std::runtime_error>(
899  "The file specified in [" SECTION_VALIDATORS_FILE
900  "] "
901  "does not contain a [" SECTION_VALIDATORS
902  "], "
903  "[" SECTION_VALIDATOR_KEYS
904  "] or "
905  "[" SECTION_VALIDATOR_LIST_KEYS
906  "]"
907  " section: " +
908  validatorsFile.string());
909  }
910 
911  // Consolidate [validator_keys] and [validators]
912  section(SECTION_VALIDATORS)
913  .append(section(SECTION_VALIDATOR_KEYS).lines());
914 
915  if (!section(SECTION_VALIDATOR_LIST_SITES).lines().empty() &&
916  section(SECTION_VALIDATOR_LIST_KEYS).lines().empty())
917  {
918  Throw<std::runtime_error>(
919  "[" + std::string(SECTION_VALIDATOR_LIST_KEYS) +
920  "] config section is missing");
921  }
922  }
923 
924  {
925  auto const part = section("features");
926  for (auto const& s : part.values())
927  {
928  if (auto const f = getRegisteredFeature(s))
929  features.insert(*f);
930  else
931  Throw<std::runtime_error>(
932  "Unknown feature: " + s + " in config file.");
933  }
934  }
935 
936  // This doesn't properly belong here, but check to make sure that the
937  // value specified for network_quorum is achievable:
938  {
939  auto pm = PEERS_MAX;
940 
941  // FIXME this apparently magic value is actually defined as a constant
942  // elsewhere (see defaultMaxPeers) but we handle this check here.
943  if (pm == 0)
944  pm = 21;
945 
946  if (NETWORK_QUORUM > pm)
947  {
948  Throw<std::runtime_error>(
949  "The minimum number of required peers (network_quorum) exceeds "
950  "the maximum number of allowed peers (peers_max)");
951  }
952  }
953 }
954 
955 boost::filesystem::path
957 {
958  auto log_file = DEBUG_LOGFILE;
959 
960  if (!log_file.empty() && !log_file.is_absolute())
961  {
962  // Unless an absolute path for the log file is specified, the
963  // path is relative to the config file directory.
964  log_file = boost::filesystem::absolute(log_file, CONFIG_DIR);
965  }
966 
967  if (!log_file.empty())
968  {
969  auto log_dir = log_file.parent_path();
970 
971  if (!boost::filesystem::is_directory(log_dir))
972  {
973  boost::system::error_code ec;
974  boost::filesystem::create_directories(log_dir, ec);
975 
976  // If we fail, we warn but continue so that the calling code can
977  // decide how to handle this situation.
978  if (ec)
979  {
980  std::cerr << "Unable to create log file path " << log_dir
981  << ": " << ec.message() << '\n';
982  }
983  }
984  }
985 
986  return log_file;
987 }
988 
989 int
991 {
992  auto const index = static_cast<std::underlying_type_t<SizedItem>>(item);
993  assert(index < sizedItems.size());
994  assert(!node || *node <= 4);
995  return sizedItems.at(index).second.at(node.value_or(NODE_SIZE));
996 }
997 
998 } // 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:227
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:178
ripple::Config::LEDGER_REPLAY
bool LEDGER_REPLAY
Definition: Config.h:207
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:201
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:197
iterator
ripple::Config::PEER_PRIVATE
bool PEER_PRIVATE
Definition: Config.h:155
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:212
iostream
ripple::SizedItem::ledgerFetch
@ ledgerFetch
ripple::Config::MAX_TRANSACTIONS
int MAX_TRANSACTIONS
Definition: Config.h:210
ripple::Config::FETCH_DEPTH
std::uint32_t FETCH_DEPTH
Definition: Config.h:192
algorithm
ripple::Config::PATH_SEARCH_MAX
int PATH_SEARCH_MAX
Definition: Config.h:180
ripple::Config::VP_REDUCE_RELAY_SQUELCH
bool VP_REDUCE_RELAY_SQUELCH
Definition: Config.h:241
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:219
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:435
ripple::Config::j_
const beast::Journal j_
Definition: Config.h:91
ripple::Config::RELAY_UNTRUSTED_PROPOSALS
int RELAY_UNTRUSTED_PROPOSALS
Definition: Config.h:152
ripple::Config::SSL_VERIFY_FILE
std::string SSL_VERIFY_FILE
Definition: Config.h:200
ripple::Config::loadFromString
void loadFromString(std::string const &fileContents)
Load the config from the contents of the string.
Definition: Config.cpp:457
ripple::Config::getValueFor
int getValueFor(SizedItem item, std::optional< std::size_t > node=std::nullopt) const
Retrieve the default value for the item at the specified node size.
Definition: Config.cpp:990
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:275
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:188
ripple::Config::SSL_VERIFY
bool SSL_VERIFY
Definition: Config.h:199
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
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:187
std::array
STL class.
ripple::Config::BETA_RPC_API
bool BETA_RPC_API
Definition: Config.h:272
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:162
ripple::Config::AMENDMENT_MAJORITY_TIME
std::chrono::seconds AMENDMENT_MAJORITY_TIME
Definition: Config.h:215
ripple::SizedItem::lgrDBCache
@ lgrDBCache
ripple::Config::MAX_JOB_QUEUE_TX
static constexpr int MAX_JOB_QUEUE_TX
Definition: Config.h:211
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:218
ripple::Config::PEERS_IN_MAX
std::size_t PEERS_IN_MAX
Definition: Config.h:163
ripple::Config::databaseDirName
static char const *const databaseDirName
Definition: Config.h:73
std::map
STL class.
ripple::SizedItem::txnDBCache
@ txnDBCache
ripple::Config::PREFETCH_WORKERS
int PREFETCH_WORKERS
Definition: Config.h:220
ripple::Config::PATH_SEARCH_OLD
int PATH_SEARCH_OLD
Definition: Config.h:177
ripple::Config::LEDGER_HISTORY
std::uint32_t LEDGER_HISTORY
Definition: Config.h:191
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:232
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:161
ripple::Config::FEE_DEFAULT
XRPAmount FEE_DEFAULT
Definition: Config.h:186
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:261
ripple::Config::PATH_SEARCH_FAST
int PATH_SEARCH_FAST
Definition: Config.h:179
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:253
ripple::Config::SERVER_DOMAIN
std::string SERVER_DOMAIN
Definition: Config.h:263
ripple::Config::TX_REDUCE_RELAY_METRICS
bool TX_REDUCE_RELAY_METRICS
Definition: Config.h:250
ripple::Config::RELAY_UNTRUSTED_VALIDATIONS
int RELAY_UNTRUSTED_VALIDATIONS
Definition: Config.h:151
ripple::SizedItem::sweepInterval
@ sweepInterval
ripple::Config::COMPRESSION
bool COMPRESSION
Definition: Config.h:204
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: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:256
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:243
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:956
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::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: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:266
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:269
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:671
std::chrono