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/contract.h>
23 #include <ripple/beast/core/LexicalCast.h>
24 #include <ripple/core/Config.h>
25 #include <ripple/core/ConfigSections.h>
26 #include <ripple/json/json_reader.h>
27 #include <ripple/net/HTTPClient.h>
28 #include <ripple/protocol/Feature.h>
29 #include <ripple/protocol/SystemParameters.h>
30 #include <boost/algorithm/string.hpp>
31 #include <boost/beast/core/string.hpp>
32 #include <boost/format.hpp>
33 #include <boost/regex.hpp>
34 #include <boost/system/error_code.hpp>
35 #include <algorithm>
36 #include <fstream>
37 #include <iostream>
38 #include <iterator>
39 
40 namespace ripple {
41 
44  // FIXME: We should document each of these items, explaining exactly
45  // what
46  // they control and whether there exists an explicit config
47  // option that can be used to override the default.
48  {SizedItem::sweepInterval, {{10, 30, 60, 90, 120}}},
49  {SizedItem::treeCacheSize, {{128000, 256000, 512000, 768000, 2048000}}},
50  {SizedItem::treeCacheAge, {{30, 60, 90, 120, 900}}},
51  {SizedItem::ledgerSize, {{32, 128, 256, 384, 768}}},
52  {SizedItem::ledgerAge, {{30, 90, 180, 240, 900}}},
53  {SizedItem::ledgerFetch, {{2, 3, 4, 5, 8}}},
54  {SizedItem::nodeCacheSize, {{16384, 32768, 131072, 262144, 524288}}},
55  {SizedItem::nodeCacheAge, {{60, 90, 120, 900, 1800}}},
56  {SizedItem::hashNodeDBCache, {{4, 12, 24, 64, 128}}},
57  {SizedItem::txnDBCache, {{4, 12, 24, 64, 128}}},
58  {SizedItem::lgrDBCache, {{4, 8, 16, 32, 128}}},
59  }};
60 
61 // Ensure that the order of entries in the table corresponds to the
62 // order of entries in the enum:
63 static_assert(
64  []() constexpr->bool {
65  std::underlying_type_t<SizedItem> idx = 0;
66 
67  for (auto const& i : sizedItems)
68  {
69  if (static_cast<std::underlying_type_t<SizedItem>>(i.first) != idx)
70  return false;
71 
72  ++idx;
73  }
74 
75  return true;
76  }(),
77  "Mismatch between sized item enum & array indices");
78 
79 //
80 // TODO: Check permissions on config file before using it.
81 //
82 
83 #define SECTION_DEFAULT_NAME ""
84 
86 parseIniFile(std::string const& strInput, const bool bTrim)
87 {
88  std::string strData(strInput);
90  IniFileSections secResult;
91 
92  // Convert DOS format to unix.
93  boost::algorithm::replace_all(strData, "\r\n", "\n");
94 
95  // Convert MacOS format to unix.
96  boost::algorithm::replace_all(strData, "\r", "\n");
97 
98  boost::algorithm::split(vLines, strData, boost::algorithm::is_any_of("\n"));
99 
100  // Set the default Section name.
101  std::string strSection = SECTION_DEFAULT_NAME;
102 
103  // Initialize the default Section.
104  secResult[strSection] = IniFileSections::mapped_type();
105 
106  // Parse each line.
107  for (auto& strValue : vLines)
108  {
109  if (bTrim)
110  boost::algorithm::trim(strValue);
111 
112  if (strValue.empty() || strValue[0] == '#')
113  {
114  // Blank line or comment, do nothing.
115  }
116  else if (strValue[0] == '[' && strValue[strValue.length() - 1] == ']')
117  {
118  // New Section.
119  strSection = strValue.substr(1, strValue.length() - 2);
120  secResult.emplace(strSection, IniFileSections::mapped_type{});
121  }
122  else
123  {
124  // Another line for Section.
125  if (!strValue.empty())
126  secResult[strSection].push_back(strValue);
127  }
128  }
129 
130  return secResult;
131 }
132 
133 IniFileSections::mapped_type*
134 getIniFileSection(IniFileSections& secSource, std::string const& strSection)
135 {
136  IniFileSections::iterator it;
137  IniFileSections::mapped_type* smtResult;
138  it = secSource.find(strSection);
139  if (it == secSource.end())
140  smtResult = nullptr;
141  else
142  smtResult = &(it->second);
143  return smtResult;
144 }
145 
146 bool
148  IniFileSections& secSource,
149  std::string const& strSection,
150  std::string& strValue,
151  beast::Journal j)
152 {
153  IniFileSections::mapped_type* pmtEntries =
154  getIniFileSection(secSource, strSection);
155  bool bSingle = pmtEntries && 1 == pmtEntries->size();
156 
157  if (bSingle)
158  {
159  strValue = (*pmtEntries)[0];
160  }
161  else if (pmtEntries)
162  {
163  JLOG(j.warn()) << boost::str(
164  boost::format("Section [%s]: requires 1 line not %d lines.") %
165  strSection % pmtEntries->size());
166  }
167 
168  return bSingle;
169 }
170 
171 //------------------------------------------------------------------------------
172 //
173 // Config (DEPRECATED)
174 //
175 //------------------------------------------------------------------------------
176 
177 char const* const Config::configFileName = "rippled.cfg";
178 char const* const Config::databaseDirName = "db";
179 char const* const Config::validatorsFileName = "validators.txt";
180 
181 static std::string
182 getEnvVar(char const* name)
183 {
184  std::string value;
185 
186  auto const v = getenv(name);
187 
188  if (v != nullptr)
189  value = v;
190 
191  return value;
192 }
193 
194 constexpr FeeUnit32 Config::TRANSACTION_FEE_BASE;
195 
196 void
197 Config::setupControl(bool bQuiet, bool bSilent, bool bStandalone)
198 {
199  QUIET = bQuiet || bSilent;
200  SILENT = bSilent;
201  RUN_STANDALONE = bStandalone;
202 }
203 
204 void
205 Config::setup(
206  std::string const& strConf,
207  bool bQuiet,
208  bool bSilent,
209  bool bStandalone)
210 {
211  boost::filesystem::path dataDir;
212  std::string strDbPath, strConfFile;
213 
214  // Determine the config and data directories.
215  // If the config file is found in the current working
216  // directory, use the current working directory as the
217  // config directory and that with "db" as the data
218  // directory.
219 
220  setupControl(bQuiet, bSilent, bStandalone);
221 
222  strDbPath = databaseDirName;
223 
224  if (!strConf.empty())
225  strConfFile = strConf;
226  else
227  strConfFile = configFileName;
228 
229  if (!strConf.empty())
230  {
231  // --conf=<path> : everything is relative that file.
232  CONFIG_FILE = strConfFile;
233  CONFIG_DIR = boost::filesystem::absolute(CONFIG_FILE);
234  CONFIG_DIR.remove_filename();
235  dataDir = CONFIG_DIR / strDbPath;
236  }
237  else
238  {
239  CONFIG_DIR = boost::filesystem::current_path();
240  CONFIG_FILE = CONFIG_DIR / strConfFile;
241  dataDir = CONFIG_DIR / strDbPath;
242 
243  // Construct XDG config and data home.
244  // http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
245  std::string strHome = getEnvVar("HOME");
246  std::string strXdgConfigHome = getEnvVar("XDG_CONFIG_HOME");
247  std::string strXdgDataHome = getEnvVar("XDG_DATA_HOME");
248 
249  if (boost::filesystem::exists(CONFIG_FILE)
250  // Can we figure out XDG dirs?
251  || (strHome.empty() &&
252  (strXdgConfigHome.empty() || strXdgDataHome.empty())))
253  {
254  // Current working directory is fine, put dbs in a subdir.
255  }
256  else
257  {
258  if (strXdgConfigHome.empty())
259  {
260  // $XDG_CONFIG_HOME was not set, use default based on $HOME.
261  strXdgConfigHome = strHome + "/.config";
262  }
263 
264  if (strXdgDataHome.empty())
265  {
266  // $XDG_DATA_HOME was not set, use default based on $HOME.
267  strXdgDataHome = strHome + "/.local/share";
268  }
269 
270  CONFIG_DIR = strXdgConfigHome + "/" + systemName();
271  CONFIG_FILE = CONFIG_DIR / strConfFile;
272  dataDir = strXdgDataHome + "/" + systemName();
273 
274  if (!boost::filesystem::exists(CONFIG_FILE))
275  {
276  CONFIG_DIR = "/etc/opt/" + systemName();
277  CONFIG_FILE = CONFIG_DIR / strConfFile;
278  dataDir = "/var/opt/" + systemName();
279  }
280  }
281  }
282 
283  // Update default values
284  load();
285  {
286  // load() may have set a new value for the dataDir
287  std::string const dbPath(legacy("database_path"));
288  if (!dbPath.empty())
289  dataDir = boost::filesystem::path(dbPath);
290  else if (RUN_STANDALONE)
291  dataDir.clear();
292  }
293 
294  if (!dataDir.empty())
295  {
296  boost::system::error_code ec;
297  boost::filesystem::create_directories(dataDir, ec);
298 
299  if (ec)
300  Throw<std::runtime_error>(
301  boost::str(boost::format("Can not create %s") % dataDir));
302 
303  legacy("database_path", boost::filesystem::absolute(dataDir).string());
304  }
305 
306  HTTPClient::initializeSSLContext(*this, j_);
307 
308  if (RUN_STANDALONE)
309  LEDGER_HISTORY = 0;
310 }
311 
312 void
313 Config::load()
314 {
315  // NOTE: this writes to cerr because we want cout to be reserved
316  // for the writing of the json response (so that stdout can be part of a
317  // pipeline, for instance)
318  if (!QUIET)
319  std::cerr << "Loading: " << CONFIG_FILE << "\n";
320 
321  boost::system::error_code ec;
322  auto const fileContents = getFileContents(ec, CONFIG_FILE);
323 
324  if (ec)
325  {
326  std::cerr << "Failed to read '" << CONFIG_FILE << "'." << ec.value()
327  << ": " << ec.message() << std::endl;
328  return;
329  }
330 
331  loadFromString(fileContents);
332 }
333 
334 void
335 Config::loadFromString(std::string const& fileContents)
336 {
337  IniFileSections secConfig = parseIniFile(fileContents, true);
338 
339  build(secConfig);
340 
341  if (auto s = getIniFileSection(secConfig, SECTION_IPS))
342  IPS = *s;
343 
344  if (auto s = getIniFileSection(secConfig, SECTION_IPS_FIXED))
345  IPS_FIXED = *s;
346 
347  if (auto s = getIniFileSection(secConfig, SECTION_SNTP))
348  SNTP_SERVERS = *s;
349 
350  {
351  std::string dbPath;
352  if (getSingleSection(secConfig, "database_path", dbPath, j_))
353  {
354  boost::filesystem::path p(dbPath);
355  legacy("database_path", boost::filesystem::absolute(p).string());
356  }
357  }
358 
359  std::string strTemp;
360 
361  if (getSingleSection(secConfig, SECTION_PEER_PRIVATE, strTemp, j_))
362  PEER_PRIVATE = beast::lexicalCastThrow<bool>(strTemp);
363 
364  if (getSingleSection(secConfig, SECTION_PEERS_MAX, strTemp, j_))
365  PEERS_MAX = beast::lexicalCastThrow<std::size_t>(strTemp);
366 
367  if (getSingleSection(secConfig, SECTION_NODE_SIZE, strTemp, j_))
368  {
369  if (boost::iequals(strTemp, "tiny"))
370  NODE_SIZE = 0;
371  else if (boost::iequals(strTemp, "small"))
372  NODE_SIZE = 1;
373  else if (boost::iequals(strTemp, "medium"))
374  NODE_SIZE = 2;
375  else if (boost::iequals(strTemp, "large"))
376  NODE_SIZE = 3;
377  else if (boost::iequals(strTemp, "huge"))
378  NODE_SIZE = 4;
379  else
380  NODE_SIZE = std::min<std::size_t>(
381  4, beast::lexicalCastThrow<std::size_t>(strTemp));
382  }
383 
384  if (getSingleSection(secConfig, SECTION_SIGNING_SUPPORT, strTemp, j_))
385  signingEnabled_ = beast::lexicalCastThrow<bool>(strTemp);
386 
387  if (getSingleSection(secConfig, SECTION_ELB_SUPPORT, strTemp, j_))
388  ELB_SUPPORT = beast::lexicalCastThrow<bool>(strTemp);
389 
390  if (getSingleSection(secConfig, SECTION_WEBSOCKET_PING_FREQ, strTemp, j_))
391  WEBSOCKET_PING_FREQ =
392  std::chrono::seconds{beast::lexicalCastThrow<int>(strTemp)};
393 
394  getSingleSection(secConfig, SECTION_SSL_VERIFY_FILE, SSL_VERIFY_FILE, j_);
395  getSingleSection(secConfig, SECTION_SSL_VERIFY_DIR, SSL_VERIFY_DIR, j_);
396 
397  if (getSingleSection(secConfig, SECTION_SSL_VERIFY, strTemp, j_))
398  SSL_VERIFY = beast::lexicalCastThrow<bool>(strTemp);
399 
400  if (getSingleSection(secConfig, SECTION_RELAY_VALIDATIONS, strTemp, j_))
401  {
402  if (boost::iequals(strTemp, "all"))
403  RELAY_UNTRUSTED_VALIDATIONS = true;
404  else if (boost::iequals(strTemp, "trusted"))
405  RELAY_UNTRUSTED_VALIDATIONS = false;
406  else
407  Throw<std::runtime_error>(
408  "Invalid value specified in [" SECTION_RELAY_VALIDATIONS
409  "] section");
410  }
411 
412  if (getSingleSection(secConfig, SECTION_RELAY_PROPOSALS, strTemp, j_))
413  {
414  if (boost::iequals(strTemp, "all"))
415  RELAY_UNTRUSTED_PROPOSALS = true;
416  else if (boost::iequals(strTemp, "trusted"))
417  RELAY_UNTRUSTED_PROPOSALS = false;
418  else
419  Throw<std::runtime_error>(
420  "Invalid value specified in [" SECTION_RELAY_PROPOSALS
421  "] section");
422  }
423 
424  if (exists(SECTION_VALIDATION_SEED) && exists(SECTION_VALIDATOR_TOKEN))
425  Throw<std::runtime_error>("Cannot have both [" SECTION_VALIDATION_SEED
426  "] "
427  "and [" SECTION_VALIDATOR_TOKEN
428  "] config sections");
429 
430  if (getSingleSection(secConfig, SECTION_NETWORK_QUORUM, strTemp, j_))
431  NETWORK_QUORUM = beast::lexicalCastThrow<std::size_t>(strTemp);
432 
433  if (getSingleSection(secConfig, SECTION_FEE_ACCOUNT_RESERVE, strTemp, j_))
434  FEE_ACCOUNT_RESERVE = beast::lexicalCastThrow<std::uint64_t>(strTemp);
435 
436  if (getSingleSection(secConfig, SECTION_FEE_OWNER_RESERVE, strTemp, j_))
437  FEE_OWNER_RESERVE = beast::lexicalCastThrow<std::uint64_t>(strTemp);
438 
439  if (getSingleSection(secConfig, SECTION_FEE_DEFAULT, strTemp, j_))
440  FEE_DEFAULT = beast::lexicalCastThrow<std::uint64_t>(strTemp);
441 
442  if (getSingleSection(secConfig, SECTION_LEDGER_HISTORY, strTemp, j_))
443  {
444  if (boost::iequals(strTemp, "full"))
445  LEDGER_HISTORY =
446  std::numeric_limits<decltype(LEDGER_HISTORY)>::max();
447  else if (boost::iequals(strTemp, "none"))
448  LEDGER_HISTORY = 0;
449  else
450  LEDGER_HISTORY = beast::lexicalCastThrow<std::uint32_t>(strTemp);
451  }
452 
453  if (getSingleSection(secConfig, SECTION_FETCH_DEPTH, strTemp, j_))
454  {
455  if (boost::iequals(strTemp, "none"))
456  FETCH_DEPTH = 0;
457  else if (boost::iequals(strTemp, "full"))
458  FETCH_DEPTH = std::numeric_limits<decltype(FETCH_DEPTH)>::max();
459  else
460  FETCH_DEPTH = beast::lexicalCastThrow<std::uint32_t>(strTemp);
461 
462  if (FETCH_DEPTH < 10)
463  FETCH_DEPTH = 10;
464  }
465 
466  if (getSingleSection(secConfig, SECTION_PATH_SEARCH_OLD, strTemp, j_))
467  PATH_SEARCH_OLD = beast::lexicalCastThrow<int>(strTemp);
468  if (getSingleSection(secConfig, SECTION_PATH_SEARCH, strTemp, j_))
469  PATH_SEARCH = beast::lexicalCastThrow<int>(strTemp);
470  if (getSingleSection(secConfig, SECTION_PATH_SEARCH_FAST, strTemp, j_))
471  PATH_SEARCH_FAST = beast::lexicalCastThrow<int>(strTemp);
472  if (getSingleSection(secConfig, SECTION_PATH_SEARCH_MAX, strTemp, j_))
473  PATH_SEARCH_MAX = beast::lexicalCastThrow<int>(strTemp);
474 
475  if (getSingleSection(secConfig, SECTION_DEBUG_LOGFILE, strTemp, j_))
476  DEBUG_LOGFILE = strTemp;
477 
478  if (getSingleSection(secConfig, SECTION_WORKERS, strTemp, j_))
479  WORKERS = beast::lexicalCastThrow<std::size_t>(strTemp);
480 
481  if (getSingleSection(secConfig, SECTION_COMPRESSION, strTemp, j_))
482  COMPRESSION = beast::lexicalCastThrow<bool>(strTemp);
483 
484  if (exists(SECTION_REDUCE_RELAY))
485  {
486  auto sec = section(SECTION_REDUCE_RELAY);
487  REDUCE_RELAY_ENABLE = sec.value_or("enable", false);
488  REDUCE_RELAY_SQUELCH = sec.value_or("squelch", false);
489  }
490 
491  if (getSingleSection(secConfig, SECTION_MAX_TRANSACTIONS, strTemp, j_))
492  {
493  MAX_TRANSACTIONS = std::clamp(
494  beast::lexicalCastThrow<int>(strTemp),
495  MIN_JOB_QUEUE_TX,
496  MAX_JOB_QUEUE_TX);
497  }
498 
499  if (getSingleSection(
500  secConfig, SECTION_AMENDMENT_MAJORITY_TIME, strTemp, j_))
501  {
502  using namespace std::chrono;
503  boost::regex const re(
504  "^\\s*(\\d+)\\s*(minutes|hours|days|weeks)\\s*(\\s+.*)?$");
505  boost::smatch match;
506  if (!boost::regex_match(strTemp, match, re))
507  Throw<std::runtime_error>(
508  "Invalid " SECTION_AMENDMENT_MAJORITY_TIME
509  ", must be: [0-9]+ [minutes|hours|days|weeks]");
510 
512  beast::lexicalCastThrow<std::uint32_t>(match[1].str());
513 
514  if (boost::iequals(match[2], "minutes"))
515  AMENDMENT_MAJORITY_TIME = minutes(duration);
516  else if (boost::iequals(match[2], "hours"))
517  AMENDMENT_MAJORITY_TIME = hours(duration);
518  else if (boost::iequals(match[2], "days"))
519  AMENDMENT_MAJORITY_TIME = days(duration);
520  else if (boost::iequals(match[2], "weeks"))
521  AMENDMENT_MAJORITY_TIME = weeks(duration);
522 
523  if (AMENDMENT_MAJORITY_TIME < minutes(15))
524  Throw<std::runtime_error>(
525  "Invalid " SECTION_AMENDMENT_MAJORITY_TIME
526  ", the minimum amount of time an amendment must hold a "
527  "majority is 15 minutes");
528  }
529 
530  // Do not load trusted validator configuration for standalone mode
531  if (!RUN_STANDALONE)
532  {
533  // If a file was explicitly specified, then throw if the
534  // path is malformed or if the file does not exist or is
535  // not a file.
536  // If the specified file is not an absolute path, then look
537  // for it in the same directory as the config file.
538  // If no path was specified, then look for validators.txt
539  // in the same directory as the config file, but don't complain
540  // if we can't find it.
541  boost::filesystem::path validatorsFile;
542 
543  if (getSingleSection(secConfig, SECTION_VALIDATORS_FILE, strTemp, j_))
544  {
545  validatorsFile = strTemp;
546 
547  if (validatorsFile.empty())
548  Throw<std::runtime_error>(
549  "Invalid path specified in [" SECTION_VALIDATORS_FILE "]");
550 
551  if (!validatorsFile.is_absolute() && !CONFIG_DIR.empty())
552  validatorsFile = CONFIG_DIR / validatorsFile;
553 
554  if (!boost::filesystem::exists(validatorsFile))
555  Throw<std::runtime_error>(
556  "The file specified in [" SECTION_VALIDATORS_FILE
557  "] "
558  "does not exist: " +
559  validatorsFile.string());
560 
561  else if (
562  !boost::filesystem::is_regular_file(validatorsFile) &&
563  !boost::filesystem::is_symlink(validatorsFile))
564  Throw<std::runtime_error>(
565  "Invalid file specified in [" SECTION_VALIDATORS_FILE
566  "]: " +
567  validatorsFile.string());
568  }
569  else if (!CONFIG_DIR.empty())
570  {
571  validatorsFile = CONFIG_DIR / validatorsFileName;
572 
573  if (!validatorsFile.empty())
574  {
575  if (!boost::filesystem::exists(validatorsFile))
576  validatorsFile.clear();
577  else if (
578  !boost::filesystem::is_regular_file(validatorsFile) &&
579  !boost::filesystem::is_symlink(validatorsFile))
580  validatorsFile.clear();
581  }
582  }
583 
584  if (!validatorsFile.empty() &&
585  boost::filesystem::exists(validatorsFile) &&
586  (boost::filesystem::is_regular_file(validatorsFile) ||
587  boost::filesystem::is_symlink(validatorsFile)))
588  {
589  boost::system::error_code ec;
590  auto const data = getFileContents(ec, validatorsFile);
591  if (ec)
592  {
593  Throw<std::runtime_error>(
594  "Failed to read '" + validatorsFile.string() + "'." +
595  std::to_string(ec.value()) + ": " + ec.message());
596  }
597 
598  auto iniFile = parseIniFile(data, true);
599 
600  auto entries = getIniFileSection(iniFile, SECTION_VALIDATORS);
601 
602  if (entries)
603  section(SECTION_VALIDATORS).append(*entries);
604 
605  auto valKeyEntries =
606  getIniFileSection(iniFile, SECTION_VALIDATOR_KEYS);
607 
608  if (valKeyEntries)
609  section(SECTION_VALIDATOR_KEYS).append(*valKeyEntries);
610 
611  auto valSiteEntries =
612  getIniFileSection(iniFile, SECTION_VALIDATOR_LIST_SITES);
613 
614  if (valSiteEntries)
615  section(SECTION_VALIDATOR_LIST_SITES).append(*valSiteEntries);
616 
617  auto valListKeys =
618  getIniFileSection(iniFile, SECTION_VALIDATOR_LIST_KEYS);
619 
620  if (valListKeys)
621  section(SECTION_VALIDATOR_LIST_KEYS).append(*valListKeys);
622 
623  if (!entries && !valKeyEntries && !valListKeys)
624  Throw<std::runtime_error>(
625  "The file specified in [" SECTION_VALIDATORS_FILE
626  "] "
627  "does not contain a [" SECTION_VALIDATORS
628  "], "
629  "[" SECTION_VALIDATOR_KEYS
630  "] or "
631  "[" SECTION_VALIDATOR_LIST_KEYS
632  "]"
633  " section: " +
634  validatorsFile.string());
635  }
636 
637  // Consolidate [validator_keys] and [validators]
638  section(SECTION_VALIDATORS)
639  .append(section(SECTION_VALIDATOR_KEYS).lines());
640 
641  if (!section(SECTION_VALIDATOR_LIST_SITES).lines().empty() &&
642  section(SECTION_VALIDATOR_LIST_KEYS).lines().empty())
643  {
644  Throw<std::runtime_error>(
645  "[" + std::string(SECTION_VALIDATOR_LIST_KEYS) +
646  "] config section is missing");
647  }
648  }
649 
650  {
651  auto const part = section("features");
652  for (auto const& s : part.values())
653  {
654  if (auto const f = getRegisteredFeature(s))
655  features.insert(*f);
656  else
657  Throw<std::runtime_error>(
658  "Unknown feature: " + s + " in config file.");
659  }
660  }
661 
662  // This doesn't properly belong here, but check to make sure that the
663  // value specified for network_quorum is achievable:
664  {
665  auto pm = PEERS_MAX;
666 
667  // FIXME this apparently magic value is actually defined as a constant
668  // elsewhere (see defaultMaxPeers) but we handle this check here.
669  if (pm == 0)
670  pm = 21;
671 
672  if (NETWORK_QUORUM > pm)
673  {
674  Throw<std::runtime_error>(
675  "The minimum number of required peers (network_quorum) exceeds "
676  "the maximum number of allowed peers (peers_max)");
677  }
678  }
679 }
680 
681 boost::filesystem::path
682 Config::getDebugLogFile() const
683 {
684  auto log_file = DEBUG_LOGFILE;
685 
686  if (!log_file.empty() && !log_file.is_absolute())
687  {
688  // Unless an absolute path for the log file is specified, the
689  // path is relative to the config file directory.
690  log_file = boost::filesystem::absolute(log_file, CONFIG_DIR);
691  }
692 
693  if (!log_file.empty())
694  {
695  auto log_dir = log_file.parent_path();
696 
697  if (!boost::filesystem::is_directory(log_dir))
698  {
699  boost::system::error_code ec;
700  boost::filesystem::create_directories(log_dir, ec);
701 
702  // If we fail, we warn but continue so that the calling code can
703  // decide how to handle this situation.
704  if (ec)
705  {
706  std::cerr << "Unable to create log file path " << log_dir
707  << ": " << ec.message() << '\n';
708  }
709  }
710  }
711 
712  return log_file;
713 }
714 
715 int
716 Config::getValueFor(SizedItem item, boost::optional<std::size_t> node) const
717 {
718  auto const index = static_cast<std::underlying_type_t<SizedItem>>(item);
719  assert(index < sizedItems.size());
720  assert(!node || *node <= 4);
721  return sizedItems.at(index).second.at(node.value_or(NODE_SIZE));
722 }
723 
724 } // namespace ripple
ripple::sizedItems
constexpr std::array< std::pair< SizedItem, std::array< int, 5 > >, 11 > sizedItems
Definition: Config.cpp:43
fstream
std::string
STL class.
ripple::SizedItem
SizedItem
Definition: Config.h:48
ripple::SizedItem::nodeCacheSize
@ nodeCacheSize
std::vector< std::string >
std::map::find
T find(T... args)
std::string::size
T size(T... args)
std::chrono::seconds
iterator
std::map::emplace
T emplace(T... args)
ripple::SizedItem::treeCacheAge
@ treeCacheAge
beast::Journal::warn
Stream warn() const
Definition: Journal.h:327
std::cerr
ripple::SizedItem::nodeCacheAge
@ nodeCacheAge
ripple::SizedItem::hashNodeDBCache
@ hashNodeDBCache
iostream
ripple::SizedItem::ledgerFetch
@ ledgerFetch
algorithm
std::underlying_type_t
std::to_string
T to_string(T... args)
std::array
STL class.
ripple::SizedItem::lgrDBCache
@ lgrDBCache
beast::Journal
A generic endpoint for log messages.
Definition: Journal.h:58
std::uint32_t
std::map
STL class.
ripple::getFileContents
std::string getFileContents(boost::system::error_code &ec, boost::filesystem::path const &sourcePath, boost::optional< std::size_t > maxSize=boost::none)
Definition: FileUtilities.cpp:25
ripple::SizedItem::txnDBCache
@ txnDBCache
ripple::parseIniFile
IniFileSections parseIniFile(std::string const &strInput, const bool bTrim)
Definition: Config.cpp:86
std::string::substr
T substr(T... args)
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
ripple::getSingleSection
bool getSingleSection(IniFileSections &secSource, std::string const &strSection, std::string &strValue, beast::Journal j)
Definition: Config.cpp:147
std::endl
T endl(T... args)
std::clamp
T clamp(T... args)
ripple::getEnvVar
static std::string getEnvVar(char const *name)
Definition: Config.cpp:182
ripple::SizedItem::treeCacheSize
@ treeCacheSize
ripple::SizedItem::sweepInterval
@ sweepInterval
std::string::empty
T empty(T... args)
ripple::SizedItem::ledgerSize
@ ledgerSize
std::map::end
T end(T... args)
std::numeric_limits
ripple::getIniFileSection
IniFileSections::mapped_type * getIniFileSection(IniFileSections &secSource, std::string const &strSection)
Definition: Config.cpp:134
ripple::SizedItem::ledgerAge
@ ledgerAge
ripple::IniFileSections
std::map< std::string, std::vector< std::string > > IniFileSections
Definition: BasicConfig.h:36
ripple::getRegisteredFeature
boost::optional< uint256 > getRegisteredFeature(std::string const &name)
Definition: Feature.cpp:143
std::chrono