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