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