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>
66 []() constexpr->bool {
67 std::underlying_type_t<SizedItem> idx = 0;
69 for (auto const& i : sizedItems)
71 if (static_cast<std::underlying_type_t<SizedItem>>(i.first) != idx)
79 "Mismatch between sized item enum & array indices");
85 #define SECTION_DEFAULT_NAME ""
95 boost::algorithm::replace_all(strData,
"\r\n",
"\n");
98 boost::algorithm::replace_all(strData,
"\r",
"\n");
100 boost::algorithm::split(vLines, strData, boost::algorithm::is_any_of(
"\n"));
106 secResult[strSection] = IniFileSections::mapped_type();
109 for (
auto& strValue : vLines)
112 boost::algorithm::trim(strValue);
114 if (strValue.empty() || strValue[0] ==
'#')
118 else if (strValue[0] ==
'[' && strValue[strValue.length() - 1] ==
']')
121 strSection = strValue.
substr(1, strValue.length() - 2);
122 secResult.
emplace(strSection, IniFileSections::mapped_type{});
127 if (!strValue.empty())
128 secResult[strSection].push_back(strValue);
135 IniFileSections::mapped_type*
138 IniFileSections::iterator it;
139 IniFileSections::mapped_type* smtResult;
140 it = secSource.
find(strSection);
141 if (it == secSource.
end())
144 smtResult = &(it->second);
155 IniFileSections::mapped_type* pmtEntries =
157 bool bSingle = pmtEntries && 1 == pmtEntries->size();
161 strValue = (*pmtEntries)[0];
165 JLOG(j.
warn()) << boost::str(
166 boost::format(
"Section [%s]: requires 1 line not %d lines.") %
167 strSection % pmtEntries->
size());
179 char const*
const Config::configFileName =
"rippled.cfg";
180 char const*
const Config::databaseDirName =
"db";
181 char const*
const Config::validatorsFileName =
"validators.txt";
188 auto const v = getenv(name);
196 constexpr FeeUnit32 Config::TRANSACTION_FEE_BASE;
199 Config::setupControl(
bool bQuiet,
bool bSilent,
bool bStandalone)
201 QUIET = bQuiet || bSilent;
203 RUN_STANDALONE = bStandalone;
213 boost::filesystem::path dataDir;
222 setupControl(bQuiet, bSilent, bStandalone);
224 strDbPath = databaseDirName;
226 if (!strConf.
empty())
227 strConfFile = strConf;
229 strConfFile = configFileName;
231 if (!strConf.
empty())
234 CONFIG_FILE = strConfFile;
235 CONFIG_DIR = boost::filesystem::absolute(CONFIG_FILE);
236 CONFIG_DIR.remove_filename();
237 dataDir = CONFIG_DIR / strDbPath;
241 CONFIG_DIR = boost::filesystem::current_path();
242 CONFIG_FILE = CONFIG_DIR / strConfFile;
243 dataDir = CONFIG_DIR / strDbPath;
251 if (boost::filesystem::exists(CONFIG_FILE)
253 || (strHome.
empty() &&
254 (strXdgConfigHome.
empty() || strXdgDataHome.
empty())))
260 if (strXdgConfigHome.
empty())
263 strXdgConfigHome = strHome +
"/.config";
266 if (strXdgDataHome.
empty())
269 strXdgDataHome = strHome +
"/.local/share";
272 CONFIG_DIR = strXdgConfigHome +
"/" + systemName();
273 CONFIG_FILE = CONFIG_DIR / strConfFile;
274 dataDir = strXdgDataHome +
"/" + systemName();
276 if (!boost::filesystem::exists(CONFIG_FILE))
278 CONFIG_DIR =
"/etc/opt/" + systemName();
279 CONFIG_FILE = CONFIG_DIR / strConfFile;
280 dataDir =
"/var/opt/" + systemName();
287 if (exists(
"reporting"))
289 RUN_REPORTING =
true;
290 RUN_STANDALONE =
true;
296 dataDir = boost::filesystem::path(dbPath);
297 else if (RUN_STANDALONE)
301 if (!dataDir.empty())
303 boost::system::error_code ec;
304 boost::filesystem::create_directories(dataDir, ec);
307 Throw<std::runtime_error>(
308 boost::str(boost::format(
"Can not create %s") % dataDir));
310 legacy(
"database_path", boost::filesystem::absolute(dataDir).
string());
313 HTTPClient::initializeSSLContext(*
this, j_);
319 Section ledgerTxTablesSection = section(
"ledger_tx_tables");
320 get_if_exists(ledgerTxTablesSection,
"use_tx_tables", USE_TX_TABLES);
330 std::cerr <<
"Loading: " << CONFIG_FILE <<
"\n";
332 boost::system::error_code ec;
337 std::cerr <<
"Failed to read '" << CONFIG_FILE <<
"'." << ec.value()
342 loadFromString(fileContents);
365 boost::filesystem::path p(dbPath);
366 legacy(
"database_path", boost::filesystem::absolute(p).
string());
373 PEER_PRIVATE = beast::lexicalCastThrow<bool>(strTemp);
377 PEERS_MAX = beast::lexicalCastThrow<std::size_t>(strTemp);
384 peers_in_max = beast::lexicalCastThrow<std::size_t>(strTemp);
385 if (*peers_in_max > 1000)
386 Throw<std::runtime_error>(
387 "Invalid value specified in [" SECTION_PEERS_IN_MAX
388 "] section; the value must be less or equal than 1000");
394 peers_out_max = beast::lexicalCastThrow<std::size_t>(strTemp);
395 if (*peers_out_max < 10 || *peers_out_max > 1000)
396 Throw<std::runtime_error>(
397 "Invalid value specified in [" SECTION_PEERS_OUT_MAX
398 "] section; the value must be in range 10-1000");
402 if ((peers_in_max && !peers_out_max) ||
403 (peers_out_max && !peers_in_max))
404 Throw<std::runtime_error>(
"Both sections [" SECTION_PEERS_IN_MAX
406 "and [" SECTION_PEERS_OUT_MAX
407 "] must be configured");
409 if (peers_in_max && peers_out_max)
411 PEERS_IN_MAX = *peers_in_max;
412 PEERS_OUT_MAX = *peers_out_max;
418 if (boost::iequals(strTemp,
"tiny"))
420 else if (boost::iequals(strTemp,
"small"))
422 else if (boost::iequals(strTemp,
"medium"))
424 else if (boost::iequals(strTemp,
"large"))
426 else if (boost::iequals(strTemp,
"huge"))
429 NODE_SIZE = std::min<std::size_t>(
430 4, beast::lexicalCastThrow<std::size_t>(strTemp));
434 signingEnabled_ = beast::lexicalCastThrow<bool>(strTemp);
437 ELB_SUPPORT = beast::lexicalCastThrow<bool>(strTemp);
440 WEBSOCKET_PING_FREQ =
447 SSL_VERIFY = beast::lexicalCastThrow<bool>(strTemp);
451 if (boost::iequals(strTemp,
"all"))
452 RELAY_UNTRUSTED_VALIDATIONS =
true;
453 else if (boost::iequals(strTemp,
"trusted"))
454 RELAY_UNTRUSTED_VALIDATIONS =
false;
456 Throw<std::runtime_error>(
457 "Invalid value specified in [" SECTION_RELAY_VALIDATIONS
463 if (boost::iequals(strTemp,
"all"))
464 RELAY_UNTRUSTED_PROPOSALS =
true;
465 else if (boost::iequals(strTemp,
"trusted"))
466 RELAY_UNTRUSTED_PROPOSALS =
false;
468 Throw<std::runtime_error>(
469 "Invalid value specified in [" SECTION_RELAY_PROPOSALS
473 if (exists(SECTION_VALIDATION_SEED) && exists(SECTION_VALIDATOR_TOKEN))
474 Throw<std::runtime_error>(
"Cannot have both [" SECTION_VALIDATION_SEED
475 "] and [" SECTION_VALIDATOR_TOKEN
476 "] config sections");
479 NETWORK_QUORUM = beast::lexicalCastThrow<std::size_t>(strTemp);
482 FEE_ACCOUNT_RESERVE = beast::lexicalCastThrow<std::uint64_t>(strTemp);
485 FEE_OWNER_RESERVE = beast::lexicalCastThrow<std::uint64_t>(strTemp);
488 FEE_DEFAULT = beast::lexicalCastThrow<std::uint64_t>(strTemp);
492 if (boost::iequals(strTemp,
"full"))
495 else if (boost::iequals(strTemp,
"none"))
498 LEDGER_HISTORY = beast::lexicalCastThrow<std::uint32_t>(strTemp);
503 if (boost::iequals(strTemp,
"none"))
505 else if (boost::iequals(strTemp,
"full"))
508 FETCH_DEPTH = beast::lexicalCastThrow<std::uint32_t>(strTemp);
510 if (FETCH_DEPTH < 10)
515 PATH_SEARCH_OLD = beast::lexicalCastThrow<int>(strTemp);
517 PATH_SEARCH = beast::lexicalCastThrow<int>(strTemp);
519 PATH_SEARCH_FAST = beast::lexicalCastThrow<int>(strTemp);
521 PATH_SEARCH_MAX = beast::lexicalCastThrow<int>(strTemp);
524 DEBUG_LOGFILE = strTemp;
527 WORKERS = beast::lexicalCastThrow<std::size_t>(strTemp);
530 COMPRESSION = beast::lexicalCastThrow<bool>(strTemp);
533 LEDGER_REPLAY = beast::lexicalCastThrow<bool>(strTemp);
535 if (exists(SECTION_REDUCE_RELAY))
537 auto sec = section(SECTION_REDUCE_RELAY);
538 VP_REDUCE_RELAY_ENABLE = sec.value_or(
"vp_enable",
false);
539 VP_REDUCE_RELAY_SQUELCH = sec.value_or(
"vp_squelch",
false);
545 beast::lexicalCastThrow<int>(strTemp),
554 Throw<std::runtime_error>(
555 "Invalid " SECTION_SERVER_DOMAIN
556 ": the domain name does not appear to meet the requirements.");
559 SERVER_DOMAIN = strTemp;
562 if (exists(SECTION_OVERLAY))
564 auto const sec = section(SECTION_OVERLAY);
570 if (
auto val = sec.get<
std::string>(
"max_unknown_time"))
572 seconds{beast::lexicalCastThrow<std::uint32_t>(*val)};
576 Throw<std::runtime_error>(
577 "Invalid value 'max_unknown_time' in " SECTION_OVERLAY
578 ": must be of the form '<number>' representing seconds.");
581 if (MAX_UNKNOWN_TIME <
seconds{300} || MAX_UNKNOWN_TIME >
seconds{1800})
582 Throw<std::runtime_error>(
583 "Invalid value 'max_unknown_time' in " SECTION_OVERLAY
584 ": the time must be between 300 and 1800 seconds, inclusive.");
588 if (
auto val = sec.get<
std::string>(
"max_diverged_time"))
590 seconds{beast::lexicalCastThrow<std::uint32_t>(*val)};
594 Throw<std::runtime_error>(
595 "Invalid value 'max_diverged_time' in " SECTION_OVERLAY
596 ": must be of the form '<number>' representing seconds.");
599 if (MAX_DIVERGED_TIME <
seconds{60} || MAX_DIVERGED_TIME >
seconds{900})
601 Throw<std::runtime_error>(
602 "Invalid value 'max_diverged_time' in " SECTION_OVERLAY
603 ": the time must be between 60 and 900 seconds, inclusive.");
608 secConfig, SECTION_AMENDMENT_MAJORITY_TIME, strTemp, j_))
611 boost::regex
const re(
612 "^\\s*(\\d+)\\s*(minutes|hours|days|weeks)\\s*(\\s+.*)?$");
614 if (!boost::regex_match(strTemp, match, re))
615 Throw<std::runtime_error>(
616 "Invalid " SECTION_AMENDMENT_MAJORITY_TIME
617 ", must be: [0-9]+ [minutes|hours|days|weeks]");
620 beast::lexicalCastThrow<std::uint32_t>(match[1].str());
622 if (boost::iequals(match[2],
"minutes"))
624 else if (boost::iequals(match[2],
"hours"))
626 else if (boost::iequals(match[2],
"days"))
628 else if (boost::iequals(match[2],
"weeks"))
631 if (AMENDMENT_MAJORITY_TIME <
minutes(15))
632 Throw<std::runtime_error>(
633 "Invalid " SECTION_AMENDMENT_MAJORITY_TIME
634 ", the minimum amount of time an amendment must hold a "
635 "majority is 15 minutes");
649 boost::filesystem::path validatorsFile;
653 validatorsFile = strTemp;
655 if (validatorsFile.empty())
656 Throw<std::runtime_error>(
657 "Invalid path specified in [" SECTION_VALIDATORS_FILE
"]");
659 if (!validatorsFile.is_absolute() && !CONFIG_DIR.empty())
660 validatorsFile = CONFIG_DIR / validatorsFile;
662 if (!boost::filesystem::exists(validatorsFile))
663 Throw<std::runtime_error>(
664 "The file specified in [" SECTION_VALIDATORS_FILE
667 validatorsFile.string());
670 !boost::filesystem::is_regular_file(validatorsFile) &&
671 !boost::filesystem::is_symlink(validatorsFile))
672 Throw<std::runtime_error>(
673 "Invalid file specified in [" SECTION_VALIDATORS_FILE
675 validatorsFile.string());
677 else if (!CONFIG_DIR.empty())
679 validatorsFile = CONFIG_DIR / validatorsFileName;
681 if (!validatorsFile.empty())
683 if (!boost::filesystem::exists(validatorsFile))
684 validatorsFile.clear();
686 !boost::filesystem::is_regular_file(validatorsFile) &&
687 !boost::filesystem::is_symlink(validatorsFile))
688 validatorsFile.clear();
692 if (!validatorsFile.empty() &&
693 boost::filesystem::exists(validatorsFile) &&
694 (boost::filesystem::is_regular_file(validatorsFile) ||
695 boost::filesystem::is_symlink(validatorsFile)))
697 boost::system::error_code ec;
701 Throw<std::runtime_error>(
702 "Failed to read '" + validatorsFile.string() +
"'." +
711 section(SECTION_VALIDATORS).append(*entries);
717 section(SECTION_VALIDATOR_KEYS).append(*valKeyEntries);
719 auto valSiteEntries =
723 section(SECTION_VALIDATOR_LIST_SITES).append(*valSiteEntries);
729 section(SECTION_VALIDATOR_LIST_KEYS).append(*valListKeys);
731 if (!entries && !valKeyEntries && !valListKeys)
732 Throw<std::runtime_error>(
733 "The file specified in [" SECTION_VALIDATORS_FILE
735 "does not contain a [" SECTION_VALIDATORS
737 "[" SECTION_VALIDATOR_KEYS
739 "[" SECTION_VALIDATOR_LIST_KEYS
742 validatorsFile.string());
746 section(SECTION_VALIDATORS)
747 .append(section(SECTION_VALIDATOR_KEYS).lines());
749 if (!section(SECTION_VALIDATOR_LIST_SITES).lines().empty() &&
750 section(SECTION_VALIDATOR_LIST_KEYS).lines().empty())
752 Throw<std::runtime_error>(
754 "] config section is missing");
759 auto const part = section(
"features");
760 for (
auto const& s : part.values())
765 Throw<std::runtime_error>(
766 "Unknown feature: " + s +
" in config file.");
780 if (NETWORK_QUORUM > pm)
782 Throw<std::runtime_error>(
783 "The minimum number of required peers (network_quorum) exceeds "
784 "the maximum number of allowed peers (peers_max)");
789 boost::filesystem::path
790 Config::getDebugLogFile()
const
792 auto log_file = DEBUG_LOGFILE;
794 if (!log_file.empty() && !log_file.is_absolute())
798 log_file = boost::filesystem::absolute(log_file, CONFIG_DIR);
801 if (!log_file.empty())
803 auto log_dir = log_file.parent_path();
805 if (!boost::filesystem::is_directory(log_dir))
807 boost::system::error_code ec;
808 boost::filesystem::create_directories(log_dir, ec);
814 std::cerr <<
"Unable to create log file path " << log_dir
815 <<
": " << ec.message() <<
'\n';
828 assert(!node || *node <= 4);