20#include <xrpld/core/Config.h>
21#include <xrpld/core/ConfigSections.h>
23#include <xrpl/basics/FileUtilities.h>
24#include <xrpl/basics/Log.h>
25#include <xrpl/basics/StringUtilities.h>
26#include <xrpl/basics/contract.h>
27#include <xrpl/beast/core/LexicalCast.h>
28#include <xrpl/json/json_reader.h>
29#include <xrpl/net/HTTPClient.h>
30#include <xrpl/protocol/Feature.h>
31#include <xrpl/protocol/SystemParameters.h>
33#include <boost/algorithm/string.hpp>
34#include <boost/format.hpp>
35#include <boost/predef.h>
36#include <boost/regex.hpp>
46#include <sysinfoapi.h>
54 if (MEMORYSTATUSEX msx{
sizeof(MEMORYSTATUSEX)}; GlobalMemoryStatusEx(&msx))
65#include <sys/sysinfo.h>
73 if (
struct sysinfo si; sysinfo(&si) == 0)
85#include <sys/sysctl.h>
94 int mib[] = {CTL_HW, HW_MEMSIZE};
96 size_t size =
sizeof(ram);
98 if (sysctl(mib, 2, &ram, &size, NULL, 0) == 0)
138 []() constexpr->bool {
151 "Mismatch between sized item enum & array indices");
158#define SECTION_DEFAULT_NAME ""
168 boost::algorithm::replace_all(strData,
"\r\n",
"\n");
171 boost::algorithm::replace_all(strData,
"\r",
"\n");
173 boost::algorithm::split(vLines, strData, boost::algorithm::is_any_of(
"\n"));
179 secResult[strSection] = IniFileSections::mapped_type();
182 for (
auto& strValue : vLines)
185 boost::algorithm::trim(strValue);
187 if (strValue.empty() || strValue[0] ==
'#')
191 else if (strValue[0] ==
'[' && strValue[strValue.length() - 1] ==
']')
194 strSection = strValue.
substr(1, strValue.length() - 2);
195 secResult.
emplace(strSection, IniFileSections::mapped_type{});
200 if (!strValue.empty())
201 secResult[strSection].push_back(strValue);
208IniFileSections::mapped_type*
211 if (
auto it = secSource.
find(strSection); it != secSource.
end())
212 return &(it->second);
226 if (pmtEntries && pmtEntries->size() == 1)
228 strValue = (*pmtEntries)[0];
234 JLOG(j.
warn()) <<
"Section '" << strSection <<
"': requires 1 line not "
235 << pmtEntries->
size() <<
" lines.";
247char const*
const Config::configFileName =
"rippled.cfg";
248char const*
const Config::databaseDirName =
"db";
249char const*
const Config::validatorsFileName =
"validators.txt";
256 if (
auto const v =
std::getenv(name); v !=
nullptr)
263 : j_(
beast::Journal::getNullSink())
264 , ramSize_(detail::getMemorySize() / (1024 * 1024 * 1024))
272 NODE_SIZE == 0,
"ripple::Config::setupControl : node size not set");
274 QUIET = bQuiet || bSilent;
283 auto const& threshold =
287 threshold.second.begin(),
288 threshold.second.end(),
290 return (limit == 0) || (ramSize_ < limit);
294 ns != threshold.second.end(),
295 "ripple::Config::setupControl : valid node size");
297 if (ns != threshold.second.end())
307 NODE_SIZE <= 4,
"ripple::Config::setupControl : node size is set");
317 boost::filesystem::path dataDir;
330 if (!strConf.
empty())
331 strConfFile = strConf;
335 if (!strConf.
empty())
345 CONFIG_DIR = boost::filesystem::current_path();
352 auto strXdgConfigHome =
getEnvVar(
"XDG_CONFIG_HOME");
353 auto strXdgDataHome =
getEnvVar(
"XDG_DATA_HOME");
357 || (strHome.empty() &&
358 (strXdgConfigHome.empty() || strXdgDataHome.empty())))
364 if (strXdgConfigHome.empty())
367 strXdgConfigHome = strHome +
"/.config";
370 if (strXdgDataHome.empty())
373 strXdgDataHome = strHome +
"/.local/share";
378 dataDir = strXdgDataHome +
"/" +
systemName();
395 dataDir = boost::filesystem::path(dbPath);
400 if (!dataDir.empty())
402 boost::system::error_code ec;
403 boost::filesystem::create_directories(dataDir, ec);
406 Throw<std::runtime_error>(
407 boost::str(boost::format(
"Can not create %s") % dataDir));
409 legacy(
"database_path", boost::filesystem::absolute(dataDir).
string());
431 if (!config.
exists(
"server"))
439 auto const& section = config[name];
440 auto const optResult = section.get(
"port");
443 auto const port = beast::lexicalCast<std::uint16_t>(*optResult);
447 ss <<
"Invalid value '" << *optResult <<
"' for key 'port' in ["
449 Throw<std::runtime_error>(ss.
str());
464 boost::system::error_code ec;
495 for (
auto& line : strVec)
498 if (
std::count(line.begin(), line.end(),
':') != 1)
504 if (result.
size() == line.size())
517 boost::filesystem::path p(dbPath);
518 legacy(
"database_path", boost::filesystem::absolute(p).
string());
526 if (strTemp ==
"main")
528 else if (strTemp ==
"testnet")
530 else if (strTemp ==
"devnet")
533 NETWORK_ID = beast::lexicalCastThrow<uint32_t>(strTemp);
541 PEERS_MAX = beast::lexicalCastThrow<std::size_t>(strTemp);
548 peers_in_max = beast::lexicalCastThrow<std::size_t>(strTemp);
549 if (*peers_in_max > 1000)
550 Throw<std::runtime_error>(
551 "Invalid value specified in [" SECTION_PEERS_IN_MAX
552 "] section; the value must be less or equal than 1000");
558 peers_out_max = beast::lexicalCastThrow<std::size_t>(strTemp);
559 if (*peers_out_max < 10 || *peers_out_max > 1000)
560 Throw<std::runtime_error>(
561 "Invalid value specified in [" SECTION_PEERS_OUT_MAX
562 "] section; the value must be in range 10-1000");
566 if ((peers_in_max && !peers_out_max) ||
567 (peers_out_max && !peers_in_max))
568 Throw<std::runtime_error>(
"Both sections [" SECTION_PEERS_IN_MAX
570 "and [" SECTION_PEERS_OUT_MAX
571 "] must be configured");
573 if (peers_in_max && peers_out_max)
582 if (boost::iequals(strTemp,
"tiny"))
584 else if (boost::iequals(strTemp,
"small"))
586 else if (boost::iequals(strTemp,
"medium"))
588 else if (boost::iequals(strTemp,
"large"))
590 else if (boost::iequals(strTemp,
"huge"))
594 4, beast::lexicalCastThrow<std::size_t>(strTemp));
601 ELB_SUPPORT = beast::lexicalCastThrow<bool>(strTemp);
607 SSL_VERIFY = beast::lexicalCastThrow<bool>(strTemp);
611 if (boost::iequals(strTemp,
"all"))
613 else if (boost::iequals(strTemp,
"trusted"))
615 else if (boost::iequals(strTemp,
"drop_untrusted"))
618 Throw<std::runtime_error>(
619 "Invalid value specified in [" SECTION_RELAY_VALIDATIONS
625 if (boost::iequals(strTemp,
"all"))
627 else if (boost::iequals(strTemp,
"trusted"))
629 else if (boost::iequals(strTemp,
"drop_untrusted"))
632 Throw<std::runtime_error>(
633 "Invalid value specified in [" SECTION_RELAY_PROPOSALS
637 if (
exists(SECTION_VALIDATION_SEED) &&
exists(SECTION_VALIDATOR_TOKEN))
638 Throw<std::runtime_error>(
"Cannot have both [" SECTION_VALIDATION_SEED
639 "] and [" SECTION_VALIDATOR_TOKEN
640 "] config sections");
654 if (boost::iequals(strTemp,
"full"))
657 else if (boost::iequals(strTemp,
"none"))
665 if (boost::iequals(strTemp,
"none"))
667 else if (boost::iequals(strTemp,
"full"))
670 FETCH_DEPTH = beast::lexicalCastThrow<std::uint32_t>(strTemp);
678 if (
exists(SECTION_VALIDATION_SEED) ||
exists(SECTION_VALIDATOR_TOKEN))
684 PATH_SEARCH = beast::lexicalCastThrow<int>(strTemp);
697 if (SWEEP_INTERVAL < 10 || SWEEP_INTERVAL > 600)
698 Throw<std::runtime_error>(
"Invalid " SECTION_SWEEP_INTERVAL
699 ": must be between 10 and 600 inclusive");
704 WORKERS = beast::lexicalCastThrow<int>(strTemp);
706 if (WORKERS < 1 || WORKERS > 1024)
707 Throw<std::runtime_error>(
708 "Invalid " SECTION_WORKERS
709 ": must be between 1 and 1024 inclusive.");
714 IO_WORKERS = beast::lexicalCastThrow<int>(strTemp);
716 if (IO_WORKERS < 1 || IO_WORKERS > 1024)
717 Throw<std::runtime_error>(
718 "Invalid " SECTION_IO_WORKERS
719 ": must be between 1 and 1024 inclusive.");
726 if (PREFETCH_WORKERS < 1 || PREFETCH_WORKERS > 1024)
727 Throw<std::runtime_error>(
728 "Invalid " SECTION_PREFETCH_WORKERS
729 ": must be between 1 and 1024 inclusive.");
733 COMPRESSION = beast::lexicalCastThrow<bool>(strTemp);
738 if (
exists(SECTION_REDUCE_RELAY))
740 auto sec =
section(SECTION_REDUCE_RELAY);
748 if (sec.exists(
"vp_base_squelch_enable") && sec.exists(
"vp_enable"))
749 Throw<std::runtime_error>(
750 "Invalid " SECTION_REDUCE_RELAY
751 " cannot specify both vp_base_squelch_enable and vp_enable "
753 "vp_enable was deprecated and replaced by "
754 "vp_base_squelch_enable");
756 if (sec.exists(
"vp_base_squelch_enable"))
758 sec.value_or(
"vp_base_squelch_enable",
false);
759 else if (sec.exists(
"vp_enable"))
761 sec.value_or(
"vp_enable",
false);
771 sec.value_or(
"vp_base_squelch_max_selected_peers", 5);
773 Throw<std::runtime_error>(
774 "Invalid " SECTION_REDUCE_RELAY
775 " vp_base_squelch_max_selected_peers must be "
776 "greater than or equal to 3");
783 if (TX_RELAY_PERCENTAGE < 10 || TX_RELAY_PERCENTAGE > 100 ||
785 Throw<std::runtime_error>(
786 "Invalid " SECTION_REDUCE_RELAY
787 ", tx_min_peers must be greater than or equal to 10"
788 ", tx_relay_percentage must be greater than or equal to 10 "
789 "and less than or equal to 100");
795 beast::lexicalCastThrow<int>(strTemp),
804 Throw<std::runtime_error>(
805 "Invalid " SECTION_SERVER_DOMAIN
806 ": the domain name does not appear to meet the requirements.");
812 if (
exists(SECTION_OVERLAY))
814 auto const sec =
section(SECTION_OVERLAY);
820 if (
auto val = sec.get(
"max_unknown_time"))
822 seconds{beast::lexicalCastThrow<std::uint32_t>(*val)};
826 Throw<std::runtime_error>(
827 "Invalid value 'max_unknown_time' in " SECTION_OVERLAY
828 ": must be of the form '<number>' representing seconds.");
832 Throw<std::runtime_error>(
833 "Invalid value 'max_unknown_time' in " SECTION_OVERLAY
834 ": the time must be between 300 and 1800 seconds, inclusive.");
838 if (
auto val = sec.get(
"max_diverged_time"))
840 seconds{beast::lexicalCastThrow<std::uint32_t>(*val)};
844 Throw<std::runtime_error>(
845 "Invalid value 'max_diverged_time' in " SECTION_OVERLAY
846 ": must be of the form '<number>' representing seconds.");
851 Throw<std::runtime_error>(
852 "Invalid value 'max_diverged_time' in " SECTION_OVERLAY
853 ": the time must be between 60 and 900 seconds, inclusive.");
858 secConfig, SECTION_AMENDMENT_MAJORITY_TIME, strTemp,
j_))
861 boost::regex
const re(
862 "^\\s*(\\d+)\\s*(minutes|hours|days|weeks)\\s*(\\s+.*)?$");
864 if (!boost::regex_match(strTemp,
match, re))
865 Throw<std::runtime_error>(
866 "Invalid " SECTION_AMENDMENT_MAJORITY_TIME
867 ", must be: [0-9]+ [minutes|hours|days|weeks]");
870 beast::lexicalCastThrow<std::uint32_t>(
match[1].str());
872 if (boost::iequals(
match[2],
"minutes"))
874 else if (boost::iequals(
match[2],
"hours"))
876 else if (boost::iequals(
match[2],
"days"))
878 else if (boost::iequals(
match[2],
"weeks"))
882 Throw<std::runtime_error>(
883 "Invalid " SECTION_AMENDMENT_MAJORITY_TIME
884 ", the minimum amount of time an amendment must hold a "
885 "majority is 15 minutes");
902 boost::filesystem::path validatorsFile;
906 validatorsFile = strTemp;
908 if (validatorsFile.empty())
909 Throw<std::runtime_error>(
910 "Invalid path specified in [" SECTION_VALIDATORS_FILE
"]");
912 if (!validatorsFile.is_absolute() && !
CONFIG_DIR.empty())
915 if (!boost::filesystem::exists(validatorsFile))
916 Throw<std::runtime_error>(
917 "The file specified in [" SECTION_VALIDATORS_FILE
920 validatorsFile.string());
923 !boost::filesystem::is_regular_file(validatorsFile) &&
924 !boost::filesystem::is_symlink(validatorsFile))
925 Throw<std::runtime_error>(
926 "Invalid file specified in [" SECTION_VALIDATORS_FILE
928 validatorsFile.string());
934 if (!validatorsFile.empty())
936 if (!boost::filesystem::exists(validatorsFile))
937 validatorsFile.clear();
939 !boost::filesystem::is_regular_file(validatorsFile) &&
940 !boost::filesystem::is_symlink(validatorsFile))
941 validatorsFile.clear();
945 if (!validatorsFile.empty() &&
946 boost::filesystem::exists(validatorsFile) &&
947 (boost::filesystem::is_regular_file(validatorsFile) ||
948 boost::filesystem::is_symlink(validatorsFile)))
950 boost::system::error_code ec;
954 Throw<std::runtime_error>(
955 "Failed to read '" + validatorsFile.string() +
"'." +
972 auto valSiteEntries =
976 section(SECTION_VALIDATOR_LIST_SITES).
append(*valSiteEntries);
984 auto valListThreshold =
987 if (valListThreshold)
988 section(SECTION_VALIDATOR_LIST_THRESHOLD)
989 .
append(*valListThreshold);
991 if (!entries && !valKeyEntries && !valListKeys)
992 Throw<std::runtime_error>(
993 "The file specified in [" SECTION_VALIDATORS_FILE
995 "does not contain a [" SECTION_VALIDATORS
997 "[" SECTION_VALIDATOR_KEYS
999 "[" SECTION_VALIDATOR_LIST_KEYS
1002 validatorsFile.string());
1006 auto const& listThreshold =
1007 section(SECTION_VALIDATOR_LIST_THRESHOLD);
1008 if (listThreshold.lines().empty())
1009 return std::nullopt;
1010 else if (listThreshold.values().size() == 1)
1012 auto strTemp = listThreshold.values()[0];
1013 auto const listThreshold =
1014 beast::lexicalCastThrow<std::size_t>(strTemp);
1015 if (listThreshold == 0)
1016 return std::nullopt;
1019 section(SECTION_VALIDATOR_LIST_KEYS).values().size())
1021 Throw<std::runtime_error>(
1022 "Value in config section "
1023 "[" SECTION_VALIDATOR_LIST_THRESHOLD
1024 "] exceeds the number of configured list keys");
1026 return listThreshold;
1030 Throw<std::runtime_error>(
1032 "[" SECTION_VALIDATOR_LIST_THRESHOLD
1033 "] should contain single value only");
1041 if (!
section(SECTION_VALIDATOR_LIST_SITES).lines().empty() &&
1042 section(SECTION_VALIDATOR_LIST_KEYS).lines().empty())
1044 Throw<std::runtime_error>(
1046 "] config section is missing");
1051 auto const part =
section(
"features");
1052 for (
auto const& s : part.values())
1057 Throw<std::runtime_error>(
1058 "Unknown feature: " + s +
" in config file.");
1074 Throw<std::runtime_error>(
1075 "The minimum number of required peers (network_quorum) exceeds "
1076 "the maximum number of allowed peers (peers_max)");
1081boost::filesystem::path
1086 if (!log_file.empty() && !log_file.is_absolute())
1090 log_file = boost::filesystem::absolute(log_file,
CONFIG_DIR);
1093 if (!log_file.empty())
1095 auto log_dir = log_file.parent_path();
1097 if (!boost::filesystem::is_directory(log_dir))
1099 boost::system::error_code ec;
1100 boost::filesystem::create_directories(log_dir, ec);
1106 std::cerr <<
"Unable to create log file path " << log_dir
1107 <<
": " << ec.message() <<
'\n';
1121 "ripple::Config::getValueFor : valid index input");
1123 !node || *node <= 4,
1124 "ripple::Config::getValueFor : unset or valid node");
1134 if (
set(temp,
"reference_fee", section) &&
1140 if (
set(temp,
"account_reserve", section))
1142 if (
set(temp,
"owner_reserve", section))
A generic endpoint for log messages.
bool exists(std::string const &name) const
Returns true if a section with the given name exists.
Section & section(std::string const &name)
Returns the section with the given name.
void build(IniFileSections const &ifs)
void legacy(std::string const §ion, std::string value)
Set a value that is not a key/value pair.
static char const *const databaseDirName
std::optional< int > SWEEP_INTERVAL
std::uint32_t LEDGER_HISTORY
std::uint32_t FETCH_DEPTH
std::size_t NETWORK_QUORUM
std::vector< std::string > IPS_FIXED
boost::filesystem::path CONFIG_DIR
void setup(std::string const &strConf, bool bQuiet, bool bSilent, bool bStandalone)
static char const *const configFileName
std::vector< std::string > IPS
std::size_t TX_REDUCE_RELAY_MIN_PEERS
bool VP_REDUCE_RELAY_BASE_SQUELCH_ENABLE
std::optional< std::size_t > VALIDATOR_LIST_THRESHOLD
int RELAY_UNTRUSTED_PROPOSALS
bool TX_REDUCE_RELAY_ENABLE
boost::filesystem::path getDebugLogFile() const
Returns the full path and filename of the debug log file.
bool TX_REDUCE_RELAY_METRICS
static constexpr int MIN_JOB_QUEUE_TX
bool RUN_STANDALONE
Operate in stand-alone mode.
std::size_t TX_RELAY_PERCENTAGE
std::string SERVER_DOMAIN
static constexpr int MAX_JOB_QUEUE_TX
std::chrono::seconds MAX_DIVERGED_TIME
boost::filesystem::path CONFIG_FILE
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.
bool signingEnabled_
Determines if the server will sign a tx, given an account's secret seed.
boost::filesystem::path DEBUG_LOGFILE
std::string SSL_VERIFY_FILE
void setupControl(bool bQuiet, bool bSilent, bool bStandalone)
void loadFromString(std::string const &fileContents)
Load the config from the contents of the string.
std::unordered_set< uint256, beast::uhash<> > features
std::chrono::seconds AMENDMENT_MAJORITY_TIME
static char const *const validatorsFileName
std::chrono::seconds MAX_UNKNOWN_TIME
std::size_t VP_REDUCE_RELAY_SQUELCH_MAX_SELECTED_PEERS
std::size_t PEERS_OUT_MAX
int RELAY_UNTRUSTED_VALIDATIONS
std::string SSL_VERIFY_DIR
static void initializeSSLContext(std::string const &sslVerifyDir, std::string const &sslVerifyFile, bool sslVerify, beast::Journal j)
Holds a collection of configuration values.
void append(std::vector< std::string > const &lines)
Append a set of lines to this section.
std::vector< std::string > const & values() const
Returns all the values in the section.
T hardware_concurrency(T... args)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
bool getSingleSection(IniFileSections &secSource, std::string const &strSection, std::string &strValue, beast::Journal j)
IniFileSections::mapped_type * getIniFileSection(IniFileSections &secSource, std::string const &strSection)
static std::string const & systemName()
static void checkZeroPorts(Config const &config)
IniFileSections parseIniFile(std::string const &strInput, bool const bTrim)
std::chrono::duration< int, std::ratio_multiply< std::chrono::hours::period, std::ratio< 24 > > > days
bool get_if_exists(Section const §ion, std::string const &name, T &v)
std::chrono::duration< int, std::ratio_multiply< days::period, std::ratio< 7 > > > weeks
static std::string getEnvVar(char const *name)
std::optional< uint256 > getRegisteredFeature(std::string const &name)
std::string getFileContents(boost::system::error_code &ec, boost::filesystem::path const &sourcePath, std::optional< std::size_t > maxSize=std::nullopt)
FeeSetup setup_FeeVote(Section const §ion)
bool isProperlyFormedTomlDomain(std::string_view domain)
Determines if the given string looks like a TOML-file hosting domain.
std::unordered_map< std::string, std::vector< std::string > > IniFileSections
constexpr std::array< std::pair< SizedItem, std::array< int, 5 > >, 13 > sizedItems
T regex_replace(T... args)
static std::string nodeDatabase()
Fee schedule for startup / standalone, and to vote for.
XRPAmount reference_fee
The cost of a reference transaction in drops.
XRPAmount owner_reserve
The per-owned item reserve requirement in drops.
XRPAmount account_reserve
The account reserve requirement in drops.