rippled
Loading...
Searching...
No Matches
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 <xrpld/core/Config.h>
21#include <xrpld/core/ConfigSections.h>
22#include <xrpld/net/HTTPClient.h>
23
24#include <xrpl/basics/FileUtilities.h>
25#include <xrpl/basics/Log.h>
26#include <xrpl/basics/StringUtilities.h>
27#include <xrpl/basics/contract.h>
28#include <xrpl/beast/core/LexicalCast.h>
29#include <xrpl/json/json_reader.h>
30#include <xrpl/protocol/Feature.h>
31#include <xrpl/protocol/SystemParameters.h>
32
33#include <boost/algorithm/string.hpp>
34#include <boost/format.hpp>
35#include <boost/predef.h>
36#include <boost/regex.hpp>
37
38#include <algorithm>
39#include <cstdlib>
40#include <iostream>
41#include <iterator>
42#include <regex>
43#include <thread>
44
45#if BOOST_OS_WINDOWS
46#include <sysinfoapi.h>
47
48namespace ripple {
49namespace detail {
50
51[[nodiscard]] std::uint64_t
52getMemorySize()
53{
54 if (MEMORYSTATUSEX msx{sizeof(MEMORYSTATUSEX)}; GlobalMemoryStatusEx(&msx))
55 return static_cast<std::uint64_t>(msx.ullTotalPhys);
56
57 return 0;
58}
59
60} // namespace detail
61} // namespace ripple
62#endif
63
64#if BOOST_OS_LINUX
65#include <sys/sysinfo.h>
66
67namespace ripple {
68namespace detail {
69
70[[nodiscard]] std::uint64_t
71getMemorySize()
72{
73 if (struct sysinfo si; sysinfo(&si) == 0)
74 return static_cast<std::uint64_t>(si.totalram) * si.mem_unit;
75
76 return 0;
77}
78
79} // namespace detail
80} // namespace ripple
81
82#endif
83
84#if BOOST_OS_MACOS
85#include <sys/sysctl.h>
86#include <sys/types.h>
87
88namespace ripple {
89namespace detail {
90
91[[nodiscard]] std::uint64_t
92getMemorySize()
93{
94 int mib[] = {CTL_HW, HW_MEMSIZE};
95 std::int64_t ram = 0;
96 size_t size = sizeof(ram);
97
98 if (sysctl(mib, 2, &ram, &size, NULL, 0) == 0)
99 return static_cast<std::uint64_t>(ram);
100
101 return 0;
102}
103
104} // namespace detail
105} // namespace ripple
106#endif
107
108namespace ripple {
109
110// clang-format off
111// The configurable node sizes are "tiny", "small", "medium", "large", "huge"
114{{
115 // FIXME: We should document each of these items, explaining exactly
116 // what they control and whether there exists an explicit
117 // config option that can be used to override the default.
118
119 // tiny small medium large huge
120 {SizedItem::sweepInterval, {{ 10, 30, 60, 90, 120 }}},
121 {SizedItem::treeCacheSize, {{ 262144, 524288, 2097152, 4194304, 8388608 }}},
122 {SizedItem::treeCacheAge, {{ 30, 60, 90, 120, 900 }}},
123 {SizedItem::ledgerSize, {{ 32, 32, 64, 256, 384 }}},
124 {SizedItem::ledgerAge, {{ 30, 60, 180, 300, 600 }}},
125 {SizedItem::ledgerFetch, {{ 2, 3, 4, 5, 8 }}},
126 {SizedItem::hashNodeDBCache, {{ 4, 12, 24, 64, 128 }}},
127 {SizedItem::txnDBCache, {{ 4, 12, 24, 64, 128 }}},
128 {SizedItem::lgrDBCache, {{ 4, 8, 16, 32, 128 }}},
129 {SizedItem::openFinalLimit, {{ 8, 16, 32, 64, 128 }}},
130 {SizedItem::burstSize, {{ 4, 8, 16, 32, 48 }}},
131 {SizedItem::ramSizeGB, {{ 6, 8, 12, 24, 0 }}},
132 {SizedItem::accountIdCacheSize, {{ 20047, 50053, 77081, 150061, 300007 }}}
133}};
134
135// Ensure that the order of entries in the table corresponds to the
136// order of entries in the enum:
137static_assert(
138 []() constexpr->bool {
140
141 for (auto const& i : sizedItems)
142 {
143 if (static_cast<std::underlying_type_t<SizedItem>>(i.first) != idx)
144 return false;
145
146 ++idx;
147 }
148
149 return true;
150 }(),
151 "Mismatch between sized item enum & array indices");
152// clang-format on
153
154//
155// TODO: Check permissions on config file before using it.
156//
157
158#define SECTION_DEFAULT_NAME ""
159
161parseIniFile(std::string const& strInput, bool const bTrim)
162{
163 std::string strData(strInput);
165 IniFileSections secResult;
166
167 // Convert DOS format to unix.
168 boost::algorithm::replace_all(strData, "\r\n", "\n");
169
170 // Convert MacOS format to unix.
171 boost::algorithm::replace_all(strData, "\r", "\n");
172
173 boost::algorithm::split(vLines, strData, boost::algorithm::is_any_of("\n"));
174
175 // Set the default Section name.
176 std::string strSection = SECTION_DEFAULT_NAME;
177
178 // Initialize the default Section.
179 secResult[strSection] = IniFileSections::mapped_type();
180
181 // Parse each line.
182 for (auto& strValue : vLines)
183 {
184 if (bTrim)
185 boost::algorithm::trim(strValue);
186
187 if (strValue.empty() || strValue[0] == '#')
188 {
189 // Blank line or comment, do nothing.
190 }
191 else if (strValue[0] == '[' && strValue[strValue.length() - 1] == ']')
192 {
193 // New Section.
194 strSection = strValue.substr(1, strValue.length() - 2);
195 secResult.emplace(strSection, IniFileSections::mapped_type{});
196 }
197 else
198 {
199 // Another line for Section.
200 if (!strValue.empty())
201 secResult[strSection].push_back(strValue);
202 }
203 }
204
205 return secResult;
206}
207
208IniFileSections::mapped_type*
209getIniFileSection(IniFileSections& secSource, std::string const& strSection)
210{
211 if (auto it = secSource.find(strSection); it != secSource.end())
212 return &(it->second);
213
214 return nullptr;
215}
216
217bool
219 IniFileSections& secSource,
220 std::string const& strSection,
221 std::string& strValue,
223{
224 auto const pmtEntries = getIniFileSection(secSource, strSection);
225
226 if (pmtEntries && pmtEntries->size() == 1)
227 {
228 strValue = (*pmtEntries)[0];
229 return true;
230 }
231
232 if (pmtEntries)
233 {
234 JLOG(j.warn()) << "Section '" << strSection << "': requires 1 line not "
235 << pmtEntries->size() << " lines.";
236 }
237
238 return false;
239}
240
241//------------------------------------------------------------------------------
242//
243// Config (DEPRECATED)
244//
245//------------------------------------------------------------------------------
246
247char const* const Config::configFileName = "rippled.cfg";
248char const* const Config::databaseDirName = "db";
249char const* const Config::validatorsFileName = "validators.txt";
250
251[[nodiscard]] static std::string
252getEnvVar(char const* name)
253{
254 std::string value;
255
256 if (auto const v = std::getenv(name); v != nullptr)
257 value = v;
258
259 return value;
260}
261
262Config::Config()
263 : j_(beast::Journal::getNullSink())
264 , ramSize_(detail::getMemorySize() / (1024 * 1024 * 1024))
265{
266}
267
268void
269Config::setupControl(bool bQuiet, bool bSilent, bool bStandalone)
270{
271 XRPL_ASSERT(
272 NODE_SIZE == 0, "ripple::Config::setupControl : node size not set");
273
274 QUIET = bQuiet || bSilent;
275 SILENT = bSilent;
276 RUN_STANDALONE = bStandalone;
277
278 // We try to autodetect the appropriate node size by checking available
279 // RAM and CPU resources. We default to "tiny" for standalone mode.
280 if (!bStandalone)
281 {
282 // First, check against 'minimum' RAM requirements per node size:
283 auto const& threshold =
285
286 auto ns = std::find_if(
287 threshold.second.begin(),
288 threshold.second.end(),
289 [this](std::size_t limit) {
290 return (limit == 0) || (ramSize_ < limit);
291 });
292
293 XRPL_ASSERT(
294 ns != threshold.second.end(),
295 "ripple::Config::setupControl : valid node size");
296
297 if (ns != threshold.second.end())
298 NODE_SIZE = std::distance(threshold.second.begin(), ns);
299
300 // Adjust the size based on the number of hardware threads of
301 // execution available to us:
302 if (auto const hc = std::thread::hardware_concurrency(); hc != 0)
303 NODE_SIZE = std::min<std::size_t>(hc / 2, NODE_SIZE);
304 }
305
306 XRPL_ASSERT(
307 NODE_SIZE <= 4, "ripple::Config::setupControl : node size is set");
308}
309
310void
312 std::string const& strConf,
313 bool bQuiet,
314 bool bSilent,
315 bool bStandalone)
316{
317 boost::filesystem::path dataDir;
318 std::string strDbPath, strConfFile;
319
320 // Determine the config and data directories.
321 // If the config file is found in the current working
322 // directory, use the current working directory as the
323 // config directory and that with "db" as the data
324 // directory.
325
326 setupControl(bQuiet, bSilent, bStandalone);
327
328 strDbPath = databaseDirName;
329
330 if (!strConf.empty())
331 strConfFile = strConf;
332 else
333 strConfFile = configFileName;
334
335 if (!strConf.empty())
336 {
337 // --conf=<path> : everything is relative that file.
338 CONFIG_FILE = strConfFile;
339 CONFIG_DIR = boost::filesystem::absolute(CONFIG_FILE);
340 CONFIG_DIR.remove_filename();
341 dataDir = CONFIG_DIR / strDbPath;
342 }
343 else
344 {
345 CONFIG_DIR = boost::filesystem::current_path();
346 CONFIG_FILE = CONFIG_DIR / strConfFile;
347 dataDir = CONFIG_DIR / strDbPath;
348
349 // Construct XDG config and data home.
350 // http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
351 auto const strHome = getEnvVar("HOME");
352 auto strXdgConfigHome = getEnvVar("XDG_CONFIG_HOME");
353 auto strXdgDataHome = getEnvVar("XDG_DATA_HOME");
354
355 if (boost::filesystem::exists(CONFIG_FILE)
356 // Can we figure out XDG dirs?
357 || (strHome.empty() &&
358 (strXdgConfigHome.empty() || strXdgDataHome.empty())))
359 {
360 // Current working directory is fine, put dbs in a subdir.
361 }
362 else
363 {
364 if (strXdgConfigHome.empty())
365 {
366 // $XDG_CONFIG_HOME was not set, use default based on $HOME.
367 strXdgConfigHome = strHome + "/.config";
368 }
369
370 if (strXdgDataHome.empty())
371 {
372 // $XDG_DATA_HOME was not set, use default based on $HOME.
373 strXdgDataHome = strHome + "/.local/share";
374 }
375
376 CONFIG_DIR = strXdgConfigHome + "/" + systemName();
377 CONFIG_FILE = CONFIG_DIR / strConfFile;
378 dataDir = strXdgDataHome + "/" + systemName();
379
380 if (!boost::filesystem::exists(CONFIG_FILE))
381 {
382 CONFIG_DIR = "/etc/opt/" + systemName();
383 CONFIG_FILE = CONFIG_DIR / strConfFile;
384 dataDir = "/var/opt/" + systemName();
385 }
386 }
387 }
388
389 // Update default values
390 load();
391 {
392 // load() may have set a new value for the dataDir
393 std::string const dbPath(legacy("database_path"));
394 if (!dbPath.empty())
395 dataDir = boost::filesystem::path(dbPath);
396 else if (RUN_STANDALONE)
397 dataDir.clear();
398 }
399
400 if (!dataDir.empty())
401 {
402 boost::system::error_code ec;
403 boost::filesystem::create_directories(dataDir, ec);
404
405 if (ec)
406 Throw<std::runtime_error>(
407 boost::str(boost::format("Can not create %s") % dataDir));
408
409 legacy("database_path", boost::filesystem::absolute(dataDir).string());
410 }
411
413
414 if (RUN_STANDALONE)
415 LEDGER_HISTORY = 0;
416
417 std::string ledgerTxDbType;
418 Section ledgerTxTablesSection = section("ledger_tx_tables");
419 get_if_exists(ledgerTxTablesSection, "use_tx_tables", USE_TX_TABLES);
420
422 get_if_exists(nodeDbSection, "fast_load", FAST_LOAD);
423}
424
425// 0 ports are allowed for unit tests, but still not allowed to be present in
426// config file
427static void
428checkZeroPorts(Config const& config)
429{
430 if (!config.exists("server"))
431 return;
432
433 for (auto const& name : config.section("server").values())
434 {
435 if (!config.exists(name))
436 return;
437
438 auto const& section = config[name];
439 auto const optResult = section.get("port");
440 if (optResult)
441 {
442 auto const port = beast::lexicalCast<std::uint16_t>(*optResult);
443 if (!port)
444 {
446 ss << "Invalid value '" << *optResult << "' for key 'port' in ["
447 << name << "]";
448 Throw<std::runtime_error>(ss.str());
449 }
450 }
451 }
452}
453
454void
456{
457 // NOTE: this writes to cerr because we want cout to be reserved
458 // for the writing of the json response (so that stdout can be part of a
459 // pipeline, for instance)
460 if (!QUIET)
461 std::cerr << "Loading: " << CONFIG_FILE << "\n";
462
463 boost::system::error_code ec;
464 auto const fileContents = getFileContents(ec, CONFIG_FILE);
465
466 if (ec)
467 {
468 std::cerr << "Failed to read '" << CONFIG_FILE << "'." << ec.value()
469 << ": " << ec.message() << std::endl;
470 return;
471 }
472
473 loadFromString(fileContents);
474 checkZeroPorts(*this);
475}
476
477void
479{
480 IniFileSections secConfig = parseIniFile(fileContents, true);
481
482 build(secConfig);
483
484 if (auto s = getIniFileSection(secConfig, SECTION_IPS))
485 IPS = *s;
486
487 if (auto s = getIniFileSection(secConfig, SECTION_IPS_FIXED))
488 IPS_FIXED = *s;
489
490 // if the user has specified ip:port then replace : with a space.
491 {
492 auto replaceColons = [](std::vector<std::string>& strVec) {
493 static std::regex const e(":([0-9]+)$");
494 for (auto& line : strVec)
495 {
496 // skip anything that might be an ipv6 address
497 if (std::count(line.begin(), line.end(), ':') != 1)
498 continue;
499
500 std::string result = std::regex_replace(line, e, " $1");
501 // sanity check the result of the replace, should be same length
502 // as input
503 if (result.size() == line.size())
504 line = result;
505 }
506 };
507
508 replaceColons(IPS_FIXED);
509 replaceColons(IPS);
510 }
511
512 {
513 std::string dbPath;
514 if (getSingleSection(secConfig, "database_path", dbPath, j_))
515 {
516 boost::filesystem::path p(dbPath);
517 legacy("database_path", boost::filesystem::absolute(p).string());
518 }
519 }
520
521 std::string strTemp;
522
523 if (getSingleSection(secConfig, SECTION_NETWORK_ID, strTemp, j_))
524 {
525 if (strTemp == "main")
526 NETWORK_ID = 0;
527 else if (strTemp == "testnet")
528 NETWORK_ID = 1;
529 else if (strTemp == "devnet")
530 NETWORK_ID = 2;
531 else
532 NETWORK_ID = beast::lexicalCastThrow<uint32_t>(strTemp);
533 }
534
535 if (getSingleSection(secConfig, SECTION_PEER_PRIVATE, strTemp, j_))
536 PEER_PRIVATE = beast::lexicalCastThrow<bool>(strTemp);
537
538 if (getSingleSection(secConfig, SECTION_PEERS_MAX, strTemp, j_))
539 {
540 PEERS_MAX = beast::lexicalCastThrow<std::size_t>(strTemp);
541 }
542 else
543 {
544 std::optional<std::size_t> peers_in_max{};
545 if (getSingleSection(secConfig, SECTION_PEERS_IN_MAX, strTemp, j_))
546 {
547 peers_in_max = beast::lexicalCastThrow<std::size_t>(strTemp);
548 if (*peers_in_max > 1000)
549 Throw<std::runtime_error>(
550 "Invalid value specified in [" SECTION_PEERS_IN_MAX
551 "] section; the value must be less or equal than 1000");
552 }
553
554 std::optional<std::size_t> peers_out_max{};
555 if (getSingleSection(secConfig, SECTION_PEERS_OUT_MAX, strTemp, j_))
556 {
557 peers_out_max = beast::lexicalCastThrow<std::size_t>(strTemp);
558 if (*peers_out_max < 10 || *peers_out_max > 1000)
559 Throw<std::runtime_error>(
560 "Invalid value specified in [" SECTION_PEERS_OUT_MAX
561 "] section; the value must be in range 10-1000");
562 }
563
564 // if one section is configured then the other must be configured too
565 if ((peers_in_max && !peers_out_max) ||
566 (peers_out_max && !peers_in_max))
567 Throw<std::runtime_error>("Both sections [" SECTION_PEERS_IN_MAX
568 "]"
569 "and [" SECTION_PEERS_OUT_MAX
570 "] must be configured");
571
572 if (peers_in_max && peers_out_max)
573 {
574 PEERS_IN_MAX = *peers_in_max;
575 PEERS_OUT_MAX = *peers_out_max;
576 }
577 }
578
579 if (getSingleSection(secConfig, SECTION_NODE_SIZE, strTemp, j_))
580 {
581 if (boost::iequals(strTemp, "tiny"))
582 NODE_SIZE = 0;
583 else if (boost::iequals(strTemp, "small"))
584 NODE_SIZE = 1;
585 else if (boost::iequals(strTemp, "medium"))
586 NODE_SIZE = 2;
587 else if (boost::iequals(strTemp, "large"))
588 NODE_SIZE = 3;
589 else if (boost::iequals(strTemp, "huge"))
590 NODE_SIZE = 4;
591 else
592 NODE_SIZE = std::min<std::size_t>(
593 4, beast::lexicalCastThrow<std::size_t>(strTemp));
594 }
595
596 if (getSingleSection(secConfig, SECTION_SIGNING_SUPPORT, strTemp, j_))
597 signingEnabled_ = beast::lexicalCastThrow<bool>(strTemp);
598
599 if (getSingleSection(secConfig, SECTION_ELB_SUPPORT, strTemp, j_))
600 ELB_SUPPORT = beast::lexicalCastThrow<bool>(strTemp);
601
602 getSingleSection(secConfig, SECTION_SSL_VERIFY_FILE, SSL_VERIFY_FILE, j_);
603 getSingleSection(secConfig, SECTION_SSL_VERIFY_DIR, SSL_VERIFY_DIR, j_);
604
605 if (getSingleSection(secConfig, SECTION_SSL_VERIFY, strTemp, j_))
606 SSL_VERIFY = beast::lexicalCastThrow<bool>(strTemp);
607
608 if (getSingleSection(secConfig, SECTION_RELAY_VALIDATIONS, strTemp, j_))
609 {
610 if (boost::iequals(strTemp, "all"))
612 else if (boost::iequals(strTemp, "trusted"))
614 else if (boost::iequals(strTemp, "drop_untrusted"))
616 else
617 Throw<std::runtime_error>(
618 "Invalid value specified in [" SECTION_RELAY_VALIDATIONS
619 "] section");
620 }
621
622 if (getSingleSection(secConfig, SECTION_RELAY_PROPOSALS, strTemp, j_))
623 {
624 if (boost::iequals(strTemp, "all"))
626 else if (boost::iequals(strTemp, "trusted"))
628 else if (boost::iequals(strTemp, "drop_untrusted"))
630 else
631 Throw<std::runtime_error>(
632 "Invalid value specified in [" SECTION_RELAY_PROPOSALS
633 "] section");
634 }
635
636 if (exists(SECTION_VALIDATION_SEED) && exists(SECTION_VALIDATOR_TOKEN))
637 Throw<std::runtime_error>("Cannot have both [" SECTION_VALIDATION_SEED
638 "] and [" SECTION_VALIDATOR_TOKEN
639 "] config sections");
640
641 if (getSingleSection(secConfig, SECTION_NETWORK_QUORUM, strTemp, j_))
642 NETWORK_QUORUM = beast::lexicalCastThrow<std::size_t>(strTemp);
643
644 FEES = setup_FeeVote(section("voting"));
645 /* [fee_default] is documented in the example config files as useful for
646 * things like offline transaction signing. Until that's completely
647 * deprecated, allow it to override the [voting] section. */
648 if (getSingleSection(secConfig, SECTION_FEE_DEFAULT, strTemp, j_))
649 FEES.reference_fee = beast::lexicalCastThrow<std::uint64_t>(strTemp);
650
651 if (getSingleSection(secConfig, SECTION_LEDGER_HISTORY, strTemp, j_))
652 {
653 if (boost::iequals(strTemp, "full"))
655 std::numeric_limits<decltype(LEDGER_HISTORY)>::max();
656 else if (boost::iequals(strTemp, "none"))
657 LEDGER_HISTORY = 0;
658 else
659 LEDGER_HISTORY = beast::lexicalCastThrow<std::uint32_t>(strTemp);
660 }
661
662 if (getSingleSection(secConfig, SECTION_FETCH_DEPTH, strTemp, j_))
663 {
664 if (boost::iequals(strTemp, "none"))
665 FETCH_DEPTH = 0;
666 else if (boost::iequals(strTemp, "full"))
667 FETCH_DEPTH = std::numeric_limits<decltype(FETCH_DEPTH)>::max();
668 else
669 FETCH_DEPTH = beast::lexicalCastThrow<std::uint32_t>(strTemp);
670
671 if (FETCH_DEPTH < 10)
672 FETCH_DEPTH = 10;
673 }
674
675 // By default, validators don't have pathfinding enabled, unless it is
676 // explicitly requested by the server's admin.
677 if (exists(SECTION_VALIDATION_SEED) || exists(SECTION_VALIDATOR_TOKEN))
678 PATH_SEARCH_MAX = 0;
679
680 if (getSingleSection(secConfig, SECTION_PATH_SEARCH_OLD, strTemp, j_))
681 PATH_SEARCH_OLD = beast::lexicalCastThrow<int>(strTemp);
682 if (getSingleSection(secConfig, SECTION_PATH_SEARCH, strTemp, j_))
683 PATH_SEARCH = beast::lexicalCastThrow<int>(strTemp);
684 if (getSingleSection(secConfig, SECTION_PATH_SEARCH_FAST, strTemp, j_))
685 PATH_SEARCH_FAST = beast::lexicalCastThrow<int>(strTemp);
686 if (getSingleSection(secConfig, SECTION_PATH_SEARCH_MAX, strTemp, j_))
687 PATH_SEARCH_MAX = beast::lexicalCastThrow<int>(strTemp);
688
689 if (getSingleSection(secConfig, SECTION_DEBUG_LOGFILE, strTemp, j_))
690 DEBUG_LOGFILE = strTemp;
691
692 if (getSingleSection(secConfig, SECTION_SWEEP_INTERVAL, strTemp, j_))
693 {
694 SWEEP_INTERVAL = beast::lexicalCastThrow<std::size_t>(strTemp);
695
696 if (SWEEP_INTERVAL < 10 || SWEEP_INTERVAL > 600)
697 Throw<std::runtime_error>("Invalid " SECTION_SWEEP_INTERVAL
698 ": must be between 10 and 600 inclusive");
699 }
700
701 if (getSingleSection(secConfig, SECTION_WORKERS, strTemp, j_))
702 {
703 WORKERS = beast::lexicalCastThrow<int>(strTemp);
704
705 if (WORKERS < 1 || WORKERS > 1024)
706 Throw<std::runtime_error>(
707 "Invalid " SECTION_WORKERS
708 ": must be between 1 and 1024 inclusive.");
709 }
710
711 if (getSingleSection(secConfig, SECTION_IO_WORKERS, strTemp, j_))
712 {
713 IO_WORKERS = beast::lexicalCastThrow<int>(strTemp);
714
715 if (IO_WORKERS < 1 || IO_WORKERS > 1024)
716 Throw<std::runtime_error>(
717 "Invalid " SECTION_IO_WORKERS
718 ": must be between 1 and 1024 inclusive.");
719 }
720
721 if (getSingleSection(secConfig, SECTION_PREFETCH_WORKERS, strTemp, j_))
722 {
723 PREFETCH_WORKERS = beast::lexicalCastThrow<int>(strTemp);
724
725 if (PREFETCH_WORKERS < 1 || PREFETCH_WORKERS > 1024)
726 Throw<std::runtime_error>(
727 "Invalid " SECTION_PREFETCH_WORKERS
728 ": must be between 1 and 1024 inclusive.");
729 }
730
731 if (getSingleSection(secConfig, SECTION_COMPRESSION, strTemp, j_))
732 COMPRESSION = beast::lexicalCastThrow<bool>(strTemp);
733
734 if (getSingleSection(secConfig, SECTION_LEDGER_REPLAY, strTemp, j_))
735 LEDGER_REPLAY = beast::lexicalCastThrow<bool>(strTemp);
736
737 if (exists(SECTION_REDUCE_RELAY))
738 {
739 auto sec = section(SECTION_REDUCE_RELAY);
740
742 // vp_enable config option is deprecated by vp_base_squelch_enable //
743 // This option is kept for backwards compatibility. When squelching //
744 // is the default algorithm, it must be replaced with: //
745 // VP_REDUCE_RELAY_BASE_SQUELCH_ENABLE = //
746 // sec.value_or("vp_base_squelch_enable", true); //
747 if (sec.exists("vp_base_squelch_enable") && sec.exists("vp_enable"))
748 Throw<std::runtime_error>(
749 "Invalid " SECTION_REDUCE_RELAY
750 " cannot specify both vp_base_squelch_enable and vp_enable "
751 "options. "
752 "vp_enable was deprecated and replaced by "
753 "vp_base_squelch_enable");
754
755 if (sec.exists("vp_base_squelch_enable"))
757 sec.value_or("vp_base_squelch_enable", false);
758 else if (sec.exists("vp_enable"))
760 sec.value_or("vp_enable", false);
761 else
764
766 // Temporary squelching config for the peers selected as a source of //
767 // validator messages. The config must be removed once squelching is //
768 // made the default routing algorithm. //
770 sec.value_or("vp_base_squelch_max_selected_peers", 5);
772 Throw<std::runtime_error>(
773 "Invalid " SECTION_REDUCE_RELAY
774 " vp_base_squelch_max_selected_peers must be "
775 "greater than or equal to 3");
777
778 TX_REDUCE_RELAY_ENABLE = sec.value_or("tx_enable", false);
779 TX_REDUCE_RELAY_METRICS = sec.value_or("tx_metrics", false);
780 TX_REDUCE_RELAY_MIN_PEERS = sec.value_or("tx_min_peers", 20);
781 TX_RELAY_PERCENTAGE = sec.value_or("tx_relay_percentage", 25);
782 if (TX_RELAY_PERCENTAGE < 10 || TX_RELAY_PERCENTAGE > 100 ||
784 Throw<std::runtime_error>(
785 "Invalid " SECTION_REDUCE_RELAY
786 ", tx_min_peers must be greater than or equal to 10"
787 ", tx_relay_percentage must be greater than or equal to 10 "
788 "and less than or equal to 100");
789 }
790
791 if (getSingleSection(secConfig, SECTION_MAX_TRANSACTIONS, strTemp, j_))
792 {
794 beast::lexicalCastThrow<int>(strTemp),
797 }
798
799 if (getSingleSection(secConfig, SECTION_SERVER_DOMAIN, strTemp, j_))
800 {
801 if (!isProperlyFormedTomlDomain(strTemp))
802 {
803 Throw<std::runtime_error>(
804 "Invalid " SECTION_SERVER_DOMAIN
805 ": the domain name does not appear to meet the requirements.");
806 }
807
808 SERVER_DOMAIN = strTemp;
809 }
810
811 if (exists(SECTION_OVERLAY))
812 {
813 auto const sec = section(SECTION_OVERLAY);
814
815 using namespace std::chrono;
816
817 try
818 {
819 if (auto val = sec.get("max_unknown_time"))
821 seconds{beast::lexicalCastThrow<std::uint32_t>(*val)};
822 }
823 catch (...)
824 {
825 Throw<std::runtime_error>(
826 "Invalid value 'max_unknown_time' in " SECTION_OVERLAY
827 ": must be of the form '<number>' representing seconds.");
828 }
829
830 if (MAX_UNKNOWN_TIME < seconds{300} || MAX_UNKNOWN_TIME > seconds{1800})
831 Throw<std::runtime_error>(
832 "Invalid value 'max_unknown_time' in " SECTION_OVERLAY
833 ": the time must be between 300 and 1800 seconds, inclusive.");
834
835 try
836 {
837 if (auto val = sec.get("max_diverged_time"))
839 seconds{beast::lexicalCastThrow<std::uint32_t>(*val)};
840 }
841 catch (...)
842 {
843 Throw<std::runtime_error>(
844 "Invalid value 'max_diverged_time' in " SECTION_OVERLAY
845 ": must be of the form '<number>' representing seconds.");
846 }
847
849 {
850 Throw<std::runtime_error>(
851 "Invalid value 'max_diverged_time' in " SECTION_OVERLAY
852 ": the time must be between 60 and 900 seconds, inclusive.");
853 }
854 }
855
857 secConfig, SECTION_AMENDMENT_MAJORITY_TIME, strTemp, j_))
858 {
859 using namespace std::chrono;
860 boost::regex const re(
861 "^\\s*(\\d+)\\s*(minutes|hours|days|weeks)\\s*(\\s+.*)?$");
862 boost::smatch match;
863 if (!boost::regex_match(strTemp, match, re))
864 Throw<std::runtime_error>(
865 "Invalid " SECTION_AMENDMENT_MAJORITY_TIME
866 ", must be: [0-9]+ [minutes|hours|days|weeks]");
867
869 beast::lexicalCastThrow<std::uint32_t>(match[1].str());
870
871 if (boost::iequals(match[2], "minutes"))
873 else if (boost::iequals(match[2], "hours"))
875 else if (boost::iequals(match[2], "days"))
877 else if (boost::iequals(match[2], "weeks"))
879
881 Throw<std::runtime_error>(
882 "Invalid " SECTION_AMENDMENT_MAJORITY_TIME
883 ", the minimum amount of time an amendment must hold a "
884 "majority is 15 minutes");
885 }
886
887 if (getSingleSection(secConfig, SECTION_BETA_RPC_API, strTemp, j_))
888 BETA_RPC_API = beast::lexicalCastThrow<bool>(strTemp);
889
890 // Do not load trusted validator configuration for standalone mode
891 if (!RUN_STANDALONE)
892 {
893 // If a file was explicitly specified, then throw if the
894 // path is malformed or if the file does not exist or is
895 // not a file.
896 // If the specified file is not an absolute path, then look
897 // for it in the same directory as the config file.
898 // If no path was specified, then look for validators.txt
899 // in the same directory as the config file, but don't complain
900 // if we can't find it.
901 boost::filesystem::path validatorsFile;
902
903 if (getSingleSection(secConfig, SECTION_VALIDATORS_FILE, strTemp, j_))
904 {
905 validatorsFile = strTemp;
906
907 if (validatorsFile.empty())
908 Throw<std::runtime_error>(
909 "Invalid path specified in [" SECTION_VALIDATORS_FILE "]");
910
911 if (!validatorsFile.is_absolute() && !CONFIG_DIR.empty())
912 validatorsFile = CONFIG_DIR / validatorsFile;
913
914 if (!boost::filesystem::exists(validatorsFile))
915 Throw<std::runtime_error>(
916 "The file specified in [" SECTION_VALIDATORS_FILE
917 "] "
918 "does not exist: " +
919 validatorsFile.string());
920
921 else if (
922 !boost::filesystem::is_regular_file(validatorsFile) &&
923 !boost::filesystem::is_symlink(validatorsFile))
924 Throw<std::runtime_error>(
925 "Invalid file specified in [" SECTION_VALIDATORS_FILE
926 "]: " +
927 validatorsFile.string());
928 }
929 else if (!CONFIG_DIR.empty())
930 {
931 validatorsFile = CONFIG_DIR / validatorsFileName;
932
933 if (!validatorsFile.empty())
934 {
935 if (!boost::filesystem::exists(validatorsFile))
936 validatorsFile.clear();
937 else if (
938 !boost::filesystem::is_regular_file(validatorsFile) &&
939 !boost::filesystem::is_symlink(validatorsFile))
940 validatorsFile.clear();
941 }
942 }
943
944 if (!validatorsFile.empty() &&
945 boost::filesystem::exists(validatorsFile) &&
946 (boost::filesystem::is_regular_file(validatorsFile) ||
947 boost::filesystem::is_symlink(validatorsFile)))
948 {
949 boost::system::error_code ec;
950 auto const data = getFileContents(ec, validatorsFile);
951 if (ec)
952 {
953 Throw<std::runtime_error>(
954 "Failed to read '" + validatorsFile.string() + "'." +
955 std::to_string(ec.value()) + ": " + ec.message());
956 }
957
958 auto iniFile = parseIniFile(data, true);
959
960 auto entries = getIniFileSection(iniFile, SECTION_VALIDATORS);
961
962 if (entries)
963 section(SECTION_VALIDATORS).append(*entries);
964
965 auto valKeyEntries =
966 getIniFileSection(iniFile, SECTION_VALIDATOR_KEYS);
967
968 if (valKeyEntries)
969 section(SECTION_VALIDATOR_KEYS).append(*valKeyEntries);
970
971 auto valSiteEntries =
972 getIniFileSection(iniFile, SECTION_VALIDATOR_LIST_SITES);
973
974 if (valSiteEntries)
975 section(SECTION_VALIDATOR_LIST_SITES).append(*valSiteEntries);
976
977 auto valListKeys =
978 getIniFileSection(iniFile, SECTION_VALIDATOR_LIST_KEYS);
979
980 if (valListKeys)
981 section(SECTION_VALIDATOR_LIST_KEYS).append(*valListKeys);
982
983 auto valListThreshold =
984 getIniFileSection(iniFile, SECTION_VALIDATOR_LIST_THRESHOLD);
985
986 if (valListThreshold)
987 section(SECTION_VALIDATOR_LIST_THRESHOLD)
988 .append(*valListThreshold);
989
990 if (!entries && !valKeyEntries && !valListKeys)
991 Throw<std::runtime_error>(
992 "The file specified in [" SECTION_VALIDATORS_FILE
993 "] "
994 "does not contain a [" SECTION_VALIDATORS
995 "], "
996 "[" SECTION_VALIDATOR_KEYS
997 "] or "
998 "[" SECTION_VALIDATOR_LIST_KEYS
999 "]"
1000 " section: " +
1001 validatorsFile.string());
1002 }
1003
1005 auto const& listThreshold =
1006 section(SECTION_VALIDATOR_LIST_THRESHOLD);
1007 if (listThreshold.lines().empty())
1008 return std::nullopt;
1009 else if (listThreshold.values().size() == 1)
1010 {
1011 auto strTemp = listThreshold.values()[0];
1012 auto const listThreshold =
1013 beast::lexicalCastThrow<std::size_t>(strTemp);
1014 if (listThreshold == 0)
1015 return std::nullopt; // NOTE: Explicitly ask for computed
1016 else if (
1017 listThreshold >
1018 section(SECTION_VALIDATOR_LIST_KEYS).values().size())
1019 {
1020 Throw<std::runtime_error>(
1021 "Value in config section "
1022 "[" SECTION_VALIDATOR_LIST_THRESHOLD
1023 "] exceeds the number of configured list keys");
1024 }
1025 return listThreshold;
1026 }
1027 else
1028 {
1029 Throw<std::runtime_error>(
1030 "Config section "
1031 "[" SECTION_VALIDATOR_LIST_THRESHOLD
1032 "] should contain single value only");
1033 }
1034 }();
1035
1036 // Consolidate [validator_keys] and [validators]
1037 section(SECTION_VALIDATORS)
1038 .append(section(SECTION_VALIDATOR_KEYS).lines());
1039
1040 if (!section(SECTION_VALIDATOR_LIST_SITES).lines().empty() &&
1041 section(SECTION_VALIDATOR_LIST_KEYS).lines().empty())
1042 {
1043 Throw<std::runtime_error>(
1044 "[" + std::string(SECTION_VALIDATOR_LIST_KEYS) +
1045 "] config section is missing");
1046 }
1047 }
1048
1049 {
1050 auto const part = section("features");
1051 for (auto const& s : part.values())
1052 {
1053 if (auto const f = getRegisteredFeature(s))
1054 features.insert(*f);
1055 else
1056 Throw<std::runtime_error>(
1057 "Unknown feature: " + s + " in config file.");
1058 }
1059 }
1060
1061 // This doesn't properly belong here, but check to make sure that the
1062 // value specified for network_quorum is achievable:
1063 {
1064 auto pm = PEERS_MAX;
1065
1066 // FIXME this apparently magic value is actually defined as a constant
1067 // elsewhere (see defaultMaxPeers) but we handle this check here.
1068 if (pm == 0)
1069 pm = 21;
1070
1071 if (NETWORK_QUORUM > pm)
1072 {
1073 Throw<std::runtime_error>(
1074 "The minimum number of required peers (network_quorum) exceeds "
1075 "the maximum number of allowed peers (peers_max)");
1076 }
1077 }
1078}
1079
1080boost::filesystem::path
1082{
1083 auto log_file = DEBUG_LOGFILE;
1084
1085 if (!log_file.empty() && !log_file.is_absolute())
1086 {
1087 // Unless an absolute path for the log file is specified, the
1088 // path is relative to the config file directory.
1089 log_file = boost::filesystem::absolute(log_file, CONFIG_DIR);
1090 }
1091
1092 if (!log_file.empty())
1093 {
1094 auto log_dir = log_file.parent_path();
1095
1096 if (!boost::filesystem::is_directory(log_dir))
1097 {
1098 boost::system::error_code ec;
1099 boost::filesystem::create_directories(log_dir, ec);
1100
1101 // If we fail, we warn but continue so that the calling code can
1102 // decide how to handle this situation.
1103 if (ec)
1104 {
1105 std::cerr << "Unable to create log file path " << log_dir
1106 << ": " << ec.message() << '\n';
1107 }
1108 }
1109 }
1110
1111 return log_file;
1112}
1113
1114int
1116{
1117 auto const index = static_cast<std::underlying_type_t<SizedItem>>(item);
1118 XRPL_ASSERT(
1119 index < sizedItems.size(),
1120 "ripple::Config::getValueFor : valid index input");
1121 XRPL_ASSERT(
1122 !node || *node <= 4,
1123 "ripple::Config::getValueFor : unset or valid node");
1124 return sizedItems.at(index).second.at(node.value_or(NODE_SIZE));
1125}
1126
1128setup_FeeVote(Section const& section)
1129{
1130 FeeSetup setup;
1131 {
1132 std::uint64_t temp;
1133 if (set(temp, "reference_fee", section) &&
1135 setup.reference_fee = temp;
1136 }
1137 {
1138 std::uint32_t temp;
1139 if (set(temp, "account_reserve", section))
1140 setup.account_reserve = temp;
1141 if (set(temp, "owner_reserve", section))
1142 setup.owner_reserve = temp;
1143 }
1144 return setup;
1145}
1146
1147} // namespace ripple
T clamp(T... args)
A generic endpoint for log messages.
Definition: Journal.h:60
Stream warn() const
Definition: Journal.h:340
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 &section, std::string value)
Set a value that is not a key/value pair.
uint32_t NETWORK_ID
Definition: Config.h:156
int PATH_SEARCH
Definition: Config.h:196
bool ELB_SUPPORT
Definition: Config.h:138
static char const *const databaseDirName
Definition: Config.h:90
std::optional< int > SWEEP_INTERVAL
Definition: Config.h:243
std::uint32_t LEDGER_HISTORY
Definition: Config.h:207
int PATH_SEARCH_OLD
Definition: Config.h:195
std::uint32_t FETCH_DEPTH
Definition: Config.h:208
int IO_WORKERS
Definition: Config.h:235
std::size_t NETWORK_QUORUM
Definition: Config.h:164
std::vector< std::string > IPS_FIXED
Definition: Config.h:144
boost::filesystem::path CONFIG_DIR
Definition: Config.h:101
void setup(std::string const &strConf, bool bQuiet, bool bSilent, bool bStandalone)
Definition: Config.cpp:311
int PREFETCH_WORKERS
Definition: Config.h:236
static char const *const configFileName
Definition: Config.h:89
bool SILENT
Definition: Config.h:111
std::vector< std::string > IPS
Definition: Config.h:141
std::size_t PEERS_IN_MAX
Definition: Config.h:181
bool PEER_PRIVATE
Definition: Config.h:173
std::size_t TX_REDUCE_RELAY_MIN_PEERS
Definition: Config.h:268
bool VP_REDUCE_RELAY_BASE_SQUELCH_ENABLE
Definition: Config.h:248
bool BETA_RPC_API
Definition: Config.h:287
beast::Journal const j_
Definition: Config.h:108
bool LEDGER_REPLAY
Definition: Config.h:223
std::optional< std::size_t > VALIDATOR_LIST_THRESHOLD
Definition: Config.h:300
int PATH_SEARCH_MAX
Definition: Config.h:198
int PATH_SEARCH_FAST
Definition: Config.h:197
int RELAY_UNTRUSTED_PROPOSALS
Definition: Config.h:170
bool TX_REDUCE_RELAY_ENABLE
Definition: Config.h:258
boost::filesystem::path getDebugLogFile() const
Returns the full path and filename of the debug log file.
Definition: Config.cpp:1081
void load()
Definition: Config.cpp:455
bool FAST_LOAD
Definition: Config.h:290
bool TX_REDUCE_RELAY_METRICS
Definition: Config.h:265
static constexpr int MIN_JOB_QUEUE_TX
Definition: Config.h:228
bool RUN_STANDALONE
Operate in stand-alone mode.
Definition: Config.h:121
std::size_t TX_RELAY_PERCENTAGE
Definition: Config.h:271
std::string SERVER_DOMAIN
Definition: Config.h:278
static constexpr int MAX_JOB_QUEUE_TX
Definition: Config.h:227
int MAX_TRANSACTIONS
Definition: Config.h:226
std::size_t NODE_SIZE
Definition: Config.h:213
std::chrono::seconds MAX_DIVERGED_TIME
Definition: Config.h:284
bool SSL_VERIFY
Definition: Config.h:215
boost::filesystem::path CONFIG_FILE
Definition: Config.h:98
bool USE_TX_TABLES
Definition: Config.h:123
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.
Definition: Config.cpp:1115
FeeSetup FEES
Definition: Config.h:204
bool signingEnabled_
Determines if the server will sign a tx, given an account's secret seed.
Definition: Config.h:131
boost::filesystem::path DEBUG_LOGFILE
Definition: Config.h:104
std::string SSL_VERIFY_FILE
Definition: Config.h:216
void setupControl(bool bQuiet, bool bSilent, bool bStandalone)
Definition: Config.cpp:269
void loadFromString(std::string const &fileContents)
Load the config from the contents of the string.
Definition: Config.cpp:478
std::unordered_set< uint256, beast::uhash<> > features
Definition: Config.h:276
std::chrono::seconds AMENDMENT_MAJORITY_TIME
Definition: Config.h:231
bool COMPRESSION
Definition: Config.h:220
bool QUIET
Definition: Config.h:110
static char const *const validatorsFileName
Definition: Config.h:91
std::chrono::seconds MAX_UNKNOWN_TIME
Definition: Config.h:281
std::size_t VP_REDUCE_RELAY_SQUELCH_MAX_SELECTED_PEERS
Definition: Config.h:254
std::size_t PEERS_OUT_MAX
Definition: Config.h:180
int RELAY_UNTRUSTED_VALIDATIONS
Definition: Config.h:169
std::string SSL_VERIFY_DIR
Definition: Config.h:217
std::size_t PEERS_MAX
Definition: Config.h:179
static void initializeSSLContext(Config const &config, beast::Journal j)
Definition: HTTPClient.cpp:39
Holds a collection of configuration values.
Definition: BasicConfig.h:45
void append(std::vector< std::string > const &lines)
Append a set of lines to this section.
Definition: BasicConfig.cpp:47
std::vector< std::string > const & values() const
Returns all the values in the section.
Definition: BasicConfig.h:79
T count(T... args)
T distance(T... args)
T emplace(T... args)
T empty(T... args)
T end(T... args)
T endl(T... args)
T find(T... args)
T getenv(T... args)
T hardware_concurrency(T... args)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: algorithm.h:26
bool getSingleSection(IniFileSections &secSource, std::string const &strSection, std::string &strValue, beast::Journal j)
Definition: Config.cpp:218
IniFileSections::mapped_type * getIniFileSection(IniFileSections &secSource, std::string const &strSection)
Definition: Config.cpp:209
static std::string const & systemName()
SizedItem
Definition: Config.h:44
static void checkZeroPorts(Config const &config)
Definition: Config.cpp:428
IniFileSections parseIniFile(std::string const &strInput, bool const bTrim)
Definition: Config.cpp:161
std::chrono::duration< int, std::ratio_multiply< std::chrono::hours::period, std::ratio< 24 > > > days
Definition: chrono.h:40
bool get_if_exists(Section const &section, std::string const &name, T &v)
Definition: BasicConfig.h:386
std::chrono::duration< int, std::ratio_multiply< days::period, std::ratio< 7 > > > weeks
Definition: chrono.h:43
static std::string getEnvVar(char const *name)
Definition: Config.cpp:252
std::optional< uint256 > getRegisteredFeature(std::string const &name)
Definition: Feature.cpp:382
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 &section)
Definition: Config.cpp:1128
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
Definition: BasicConfig.h:37
constexpr std::array< std::pair< SizedItem, std::array< int, 5 > >, 13 > sizedItems
Definition: Config.cpp:114
T regex_replace(T... args)
T size(T... args)
T str(T... args)
static std::string nodeDatabase()
Fee schedule for startup / standalone, and to vote for.
Definition: Config.h:66
XRPAmount reference_fee
The cost of a reference transaction in drops.
Definition: Config.h:68
XRPAmount owner_reserve
The per-owned item reserve requirement in drops.
Definition: Config.h:74
XRPAmount account_reserve
The account reserve requirement in drops.
Definition: Config.h:71
T substr(T... args)
T to_string(T... args)
T value_or(T... args)