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>
68 []() constexpr->bool {
69 std::underlying_type_t<SizedItem> idx = 0;
71 for (auto const& i : sizedItems)
73 if (static_cast<std::underlying_type_t<SizedItem>>(i.first) != idx)
81 "Mismatch between sized item enum & array indices");
87 #define SECTION_DEFAULT_NAME ""
97 boost::algorithm::replace_all(strData,
"\r\n",
"\n");
100 boost::algorithm::replace_all(strData,
"\r",
"\n");
102 boost::algorithm::split(vLines, strData, boost::algorithm::is_any_of(
"\n"));
108 secResult[strSection] = IniFileSections::mapped_type();
111 for (
auto& strValue : vLines)
114 boost::algorithm::trim(strValue);
116 if (strValue.empty() || strValue[0] ==
'#')
120 else if (strValue[0] ==
'[' && strValue[strValue.length() - 1] ==
']')
123 strSection = strValue.
substr(1, strValue.length() - 2);
124 secResult.
emplace(strSection, IniFileSections::mapped_type{});
129 if (!strValue.empty())
130 secResult[strSection].push_back(strValue);
137 IniFileSections::mapped_type*
140 IniFileSections::iterator it;
141 IniFileSections::mapped_type* smtResult;
142 it = secSource.
find(strSection);
143 if (it == secSource.
end())
146 smtResult = &(it->second);
157 IniFileSections::mapped_type* pmtEntries =
159 bool bSingle = pmtEntries && 1 == pmtEntries->size();
163 strValue = (*pmtEntries)[0];
167 JLOG(j.
warn()) << boost::str(
168 boost::format(
"Section [%s]: requires 1 line not %d lines.") %
169 strSection % pmtEntries->
size());
181 char const*
const Config::configFileName =
"rippled.cfg";
182 char const*
const Config::databaseDirName =
"db";
183 char const*
const Config::validatorsFileName =
"validators.txt";
190 auto const v = getenv(name);
198 constexpr FeeUnit32 Config::TRANSACTION_FEE_BASE;
201 Config::setupControl(
bool bQuiet,
bool bSilent,
bool bStandalone)
203 QUIET = bQuiet || bSilent;
205 RUN_STANDALONE = bStandalone;
215 boost::filesystem::path dataDir;
224 setupControl(bQuiet, bSilent, bStandalone);
226 strDbPath = databaseDirName;
228 if (!strConf.
empty())
229 strConfFile = strConf;
231 strConfFile = configFileName;
233 if (!strConf.
empty())
236 CONFIG_FILE = strConfFile;
237 CONFIG_DIR = boost::filesystem::absolute(CONFIG_FILE);
238 CONFIG_DIR.remove_filename();
239 dataDir = CONFIG_DIR / strDbPath;
243 CONFIG_DIR = boost::filesystem::current_path();
244 CONFIG_FILE = CONFIG_DIR / strConfFile;
245 dataDir = CONFIG_DIR / strDbPath;
253 if (boost::filesystem::exists(CONFIG_FILE)
255 || (strHome.
empty() &&
256 (strXdgConfigHome.
empty() || strXdgDataHome.
empty())))
262 if (strXdgConfigHome.
empty())
265 strXdgConfigHome = strHome +
"/.config";
268 if (strXdgDataHome.
empty())
271 strXdgDataHome = strHome +
"/.local/share";
274 CONFIG_DIR = strXdgConfigHome +
"/" + systemName();
275 CONFIG_FILE = CONFIG_DIR / strConfFile;
276 dataDir = strXdgDataHome +
"/" + systemName();
278 if (!boost::filesystem::exists(CONFIG_FILE))
280 CONFIG_DIR =
"/etc/opt/" + systemName();
281 CONFIG_FILE = CONFIG_DIR / strConfFile;
282 dataDir =
"/var/opt/" + systemName();
293 dataDir = boost::filesystem::path(dbPath);
294 else if (RUN_STANDALONE)
298 if (!dataDir.empty())
300 boost::system::error_code ec;
301 boost::filesystem::create_directories(dataDir, ec);
304 Throw<std::runtime_error>(
305 boost::str(boost::format(
"Can not create %s") % dataDir));
307 legacy(
"database_path", boost::filesystem::absolute(dataDir).
string());
310 HTTPClient::initializeSSLContext(*
this, j_);
323 std::cerr <<
"Loading: " << CONFIG_FILE <<
"\n";
325 boost::system::error_code ec;
330 std::cerr <<
"Failed to read '" << CONFIG_FILE <<
"'." << ec.value()
335 loadFromString(fileContents);
358 boost::filesystem::path p(dbPath);
359 legacy(
"database_path", boost::filesystem::absolute(p).
string());
366 PEER_PRIVATE = beast::lexicalCastThrow<bool>(strTemp);
370 PEERS_MAX = beast::lexicalCastThrow<std::size_t>(strTemp);
377 peers_in_max = beast::lexicalCastThrow<std::size_t>(strTemp);
378 if (*peers_in_max > 1000)
379 Throw<std::runtime_error>(
380 "Invalid value specified in [" SECTION_PEERS_IN_MAX
381 "] section; the value must be less or equal than 1000");
387 peers_out_max = beast::lexicalCastThrow<std::size_t>(strTemp);
388 if (*peers_out_max < 10 || *peers_out_max > 1000)
389 Throw<std::runtime_error>(
390 "Invalid value specified in [" SECTION_PEERS_OUT_MAX
391 "] section; the value must be in range 10-1000");
395 if ((peers_in_max && !peers_out_max) ||
396 (peers_out_max && !peers_in_max))
397 Throw<std::runtime_error>(
"Both sections [" SECTION_PEERS_IN_MAX
399 "and [" SECTION_PEERS_OUT_MAX
400 "] must be configured");
402 if (peers_in_max && peers_out_max)
404 PEERS_IN_MAX = *peers_in_max;
405 PEERS_OUT_MAX = *peers_out_max;
411 if (boost::iequals(strTemp,
"tiny"))
413 else if (boost::iequals(strTemp,
"small"))
415 else if (boost::iequals(strTemp,
"medium"))
417 else if (boost::iequals(strTemp,
"large"))
419 else if (boost::iequals(strTemp,
"huge"))
422 NODE_SIZE = std::min<std::size_t>(
423 4, beast::lexicalCastThrow<std::size_t>(strTemp));
427 signingEnabled_ = beast::lexicalCastThrow<bool>(strTemp);
430 ELB_SUPPORT = beast::lexicalCastThrow<bool>(strTemp);
433 WEBSOCKET_PING_FREQ =
440 SSL_VERIFY = beast::lexicalCastThrow<bool>(strTemp);
444 if (boost::iequals(strTemp,
"all"))
445 RELAY_UNTRUSTED_VALIDATIONS =
true;
446 else if (boost::iequals(strTemp,
"trusted"))
447 RELAY_UNTRUSTED_VALIDATIONS =
false;
449 Throw<std::runtime_error>(
450 "Invalid value specified in [" SECTION_RELAY_VALIDATIONS
456 if (boost::iequals(strTemp,
"all"))
457 RELAY_UNTRUSTED_PROPOSALS =
true;
458 else if (boost::iequals(strTemp,
"trusted"))
459 RELAY_UNTRUSTED_PROPOSALS =
false;
461 Throw<std::runtime_error>(
462 "Invalid value specified in [" SECTION_RELAY_PROPOSALS
466 if (exists(SECTION_VALIDATION_SEED) && exists(SECTION_VALIDATOR_TOKEN))
467 Throw<std::runtime_error>(
"Cannot have both [" SECTION_VALIDATION_SEED
468 "] and [" SECTION_VALIDATOR_TOKEN
469 "] config sections");
472 NETWORK_QUORUM = beast::lexicalCastThrow<std::size_t>(strTemp);
475 FEE_ACCOUNT_RESERVE = beast::lexicalCastThrow<std::uint64_t>(strTemp);
478 FEE_OWNER_RESERVE = beast::lexicalCastThrow<std::uint64_t>(strTemp);
481 FEE_DEFAULT = beast::lexicalCastThrow<std::uint64_t>(strTemp);
485 if (boost::iequals(strTemp,
"full"))
488 else if (boost::iequals(strTemp,
"none"))
491 LEDGER_HISTORY = beast::lexicalCastThrow<std::uint32_t>(strTemp);
496 if (boost::iequals(strTemp,
"none"))
498 else if (boost::iequals(strTemp,
"full"))
501 FETCH_DEPTH = beast::lexicalCastThrow<std::uint32_t>(strTemp);
503 if (FETCH_DEPTH < 10)
508 PATH_SEARCH_OLD = beast::lexicalCastThrow<int>(strTemp);
510 PATH_SEARCH = beast::lexicalCastThrow<int>(strTemp);
512 PATH_SEARCH_FAST = beast::lexicalCastThrow<int>(strTemp);
514 PATH_SEARCH_MAX = beast::lexicalCastThrow<int>(strTemp);
517 DEBUG_LOGFILE = strTemp;
520 WORKERS = beast::lexicalCastThrow<std::size_t>(strTemp);
523 COMPRESSION = beast::lexicalCastThrow<bool>(strTemp);
525 if (exists(SECTION_REDUCE_RELAY))
527 auto sec = section(SECTION_REDUCE_RELAY);
528 REDUCE_RELAY_ENABLE = sec.value_or(
"enable",
false);
529 REDUCE_RELAY_SQUELCH = sec.value_or(
"squelch",
false);
535 beast::lexicalCastThrow<int>(strTemp),
544 Throw<std::runtime_error>(
545 "Invalid " SECTION_SERVER_DOMAIN
546 ": the domain name does not appear to meet the requirements.");
549 SERVER_DOMAIN = strTemp;
552 if (exists(SECTION_OVERLAY))
554 auto const sec = section(SECTION_OVERLAY);
560 if (
auto val = sec.get<
std::string>(
"max_unknown_time"))
562 seconds{beast::lexicalCastThrow<std::uint32_t>(*val)};
566 Throw<std::runtime_error>(
567 "Invalid value 'max_unknown_time' in " SECTION_OVERLAY
568 ": must be of the form '<number>' representing seconds.");
571 if (MAX_UNKNOWN_TIME <
seconds{300} || MAX_UNKNOWN_TIME >
seconds{1800})
572 Throw<std::runtime_error>(
573 "Invalid value 'max_unknown_time' in " SECTION_OVERLAY
574 ": the time must be between 300 and 1800 seconds, inclusive.");
578 if (
auto val = sec.get<
std::string>(
"max_diverged_time"))
580 seconds{beast::lexicalCastThrow<std::uint32_t>(*val)};
584 Throw<std::runtime_error>(
585 "Invalid value 'max_diverged_time' in " SECTION_OVERLAY
586 ": must be of the form '<number>' representing seconds.");
589 if (MAX_DIVERGED_TIME <
seconds{60} || MAX_DIVERGED_TIME >
seconds{900})
591 Throw<std::runtime_error>(
592 "Invalid value 'max_diverged_time' in " SECTION_OVERLAY
593 ": the time must be between 60 and 900 seconds, inclusive.");
598 secConfig, SECTION_AMENDMENT_MAJORITY_TIME, strTemp, j_))
601 boost::regex
const re(
602 "^\\s*(\\d+)\\s*(minutes|hours|days|weeks)\\s*(\\s+.*)?$");
604 if (!boost::regex_match(strTemp, match, re))
605 Throw<std::runtime_error>(
606 "Invalid " SECTION_AMENDMENT_MAJORITY_TIME
607 ", must be: [0-9]+ [minutes|hours|days|weeks]");
610 beast::lexicalCastThrow<std::uint32_t>(match[1].str());
612 if (boost::iequals(match[2],
"minutes"))
614 else if (boost::iequals(match[2],
"hours"))
616 else if (boost::iequals(match[2],
"days"))
618 else if (boost::iequals(match[2],
"weeks"))
621 if (AMENDMENT_MAJORITY_TIME <
minutes(15))
622 Throw<std::runtime_error>(
623 "Invalid " SECTION_AMENDMENT_MAJORITY_TIME
624 ", the minimum amount of time an amendment must hold a "
625 "majority is 15 minutes");
639 boost::filesystem::path validatorsFile;
643 validatorsFile = strTemp;
645 if (validatorsFile.empty())
646 Throw<std::runtime_error>(
647 "Invalid path specified in [" SECTION_VALIDATORS_FILE
"]");
649 if (!validatorsFile.is_absolute() && !CONFIG_DIR.empty())
650 validatorsFile = CONFIG_DIR / validatorsFile;
652 if (!boost::filesystem::exists(validatorsFile))
653 Throw<std::runtime_error>(
654 "The file specified in [" SECTION_VALIDATORS_FILE
657 validatorsFile.string());
660 !boost::filesystem::is_regular_file(validatorsFile) &&
661 !boost::filesystem::is_symlink(validatorsFile))
662 Throw<std::runtime_error>(
663 "Invalid file specified in [" SECTION_VALIDATORS_FILE
665 validatorsFile.string());
667 else if (!CONFIG_DIR.empty())
669 validatorsFile = CONFIG_DIR / validatorsFileName;
671 if (!validatorsFile.empty())
673 if (!boost::filesystem::exists(validatorsFile))
674 validatorsFile.clear();
676 !boost::filesystem::is_regular_file(validatorsFile) &&
677 !boost::filesystem::is_symlink(validatorsFile))
678 validatorsFile.clear();
682 if (!validatorsFile.empty() &&
683 boost::filesystem::exists(validatorsFile) &&
684 (boost::filesystem::is_regular_file(validatorsFile) ||
685 boost::filesystem::is_symlink(validatorsFile)))
687 boost::system::error_code ec;
691 Throw<std::runtime_error>(
692 "Failed to read '" + validatorsFile.string() +
"'." +
701 section(SECTION_VALIDATORS).append(*entries);
707 section(SECTION_VALIDATOR_KEYS).append(*valKeyEntries);
709 auto valSiteEntries =
713 section(SECTION_VALIDATOR_LIST_SITES).append(*valSiteEntries);
719 section(SECTION_VALIDATOR_LIST_KEYS).append(*valListKeys);
721 if (!entries && !valKeyEntries && !valListKeys)
722 Throw<std::runtime_error>(
723 "The file specified in [" SECTION_VALIDATORS_FILE
725 "does not contain a [" SECTION_VALIDATORS
727 "[" SECTION_VALIDATOR_KEYS
729 "[" SECTION_VALIDATOR_LIST_KEYS
732 validatorsFile.string());
736 section(SECTION_VALIDATORS)
737 .append(section(SECTION_VALIDATOR_KEYS).lines());
739 if (!section(SECTION_VALIDATOR_LIST_SITES).lines().empty() &&
740 section(SECTION_VALIDATOR_LIST_KEYS).lines().empty())
742 Throw<std::runtime_error>(
744 "] config section is missing");
749 auto const part = section(
"features");
750 for (
auto const& s : part.values())
755 Throw<std::runtime_error>(
756 "Unknown feature: " + s +
" in config file.");
770 if (NETWORK_QUORUM > pm)
772 Throw<std::runtime_error>(
773 "The minimum number of required peers (network_quorum) exceeds "
774 "the maximum number of allowed peers (peers_max)");
779 boost::filesystem::path
780 Config::getDebugLogFile()
const
782 auto log_file = DEBUG_LOGFILE;
784 if (!log_file.empty() && !log_file.is_absolute())
788 log_file = boost::filesystem::absolute(log_file, CONFIG_DIR);
791 if (!log_file.empty())
793 auto log_dir = log_file.parent_path();
795 if (!boost::filesystem::is_directory(log_dir))
797 boost::system::error_code ec;
798 boost::filesystem::create_directories(log_dir, ec);
804 std::cerr <<
"Unable to create log file path " << log_dir
805 <<
": " << ec.message() <<
'\n';
814 Config::getValueFor(
SizedItem item, boost::optional<std::size_t> node)
const
818 assert(!node || *node <= 4);
819 return sizedItems.at(index).second.at(node.value_or(NODE_SIZE));