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
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>
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)
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 this->SSL_VERIFY_DIR, this->SSL_VERIFY_FILE, this->SSL_VERIFY, j_);
414
415 if (RUN_STANDALONE)
416 LEDGER_HISTORY = 0;
417
418 std::string ledgerTxDbType;
419 Section ledgerTxTablesSection = section("ledger_tx_tables");
420 get_if_exists(ledgerTxTablesSection, "use_tx_tables", USE_TX_TABLES);
421
423 get_if_exists(nodeDbSection, "fast_load", FAST_LOAD);
424}
425
426// 0 ports are allowed for unit tests, but still not allowed to be present in
427// config file
428static void
429checkZeroPorts(Config const& config)
430{
431 if (!config.exists("server"))
432 return;
433
434 for (auto const& name : config.section("server").values())
435 {
436 if (!config.exists(name))
437 return;
438
439 auto const& section = config[name];
440 auto const optResult = section.get("port");
441 if (optResult)
442 {
443 auto const port = beast::lexicalCast<std::uint16_t>(*optResult);
444 if (!port)
445 {
447 ss << "Invalid value '" << *optResult << "' for key 'port' in ["
448 << name << "]";
449 Throw<std::runtime_error>(ss.str());
450 }
451 }
452 }
453}
454
455void
457{
458 // NOTE: this writes to cerr because we want cout to be reserved
459 // for the writing of the json response (so that stdout can be part of a
460 // pipeline, for instance)
461 if (!QUIET)
462 std::cerr << "Loading: " << CONFIG_FILE << "\n";
463
464 boost::system::error_code ec;
465 auto const fileContents = getFileContents(ec, CONFIG_FILE);
466
467 if (ec)
468 {
469 std::cerr << "Failed to read '" << CONFIG_FILE << "'." << ec.value()
470 << ": " << ec.message() << std::endl;
471 return;
472 }
473
474 loadFromString(fileContents);
475 checkZeroPorts(*this);
476}
477
478void
480{
481 IniFileSections secConfig = parseIniFile(fileContents, true);
482
483 build(secConfig);
484
485 if (auto s = getIniFileSection(secConfig, SECTION_IPS))
486 IPS = *s;
487
488 if (auto s = getIniFileSection(secConfig, SECTION_IPS_FIXED))
489 IPS_FIXED = *s;
490
491 // if the user has specified ip:port then replace : with a space.
492 {
493 auto replaceColons = [](std::vector<std::string>& strVec) {
494 static std::regex const e(":([0-9]+)$");
495 for (auto& line : strVec)
496 {
497 // skip anything that might be an ipv6 address
498 if (std::count(line.begin(), line.end(), ':') != 1)
499 continue;
500
501 std::string result = std::regex_replace(line, e, " $1");
502 // sanity check the result of the replace, should be same length
503 // as input
504 if (result.size() == line.size())
505 line = result;
506 }
507 };
508
509 replaceColons(IPS_FIXED);
510 replaceColons(IPS);
511 }
512
513 {
514 std::string dbPath;
515 if (getSingleSection(secConfig, "database_path", dbPath, j_))
516 {
517 boost::filesystem::path p(dbPath);
518 legacy("database_path", boost::filesystem::absolute(p).string());
519 }
520 }
521
522 std::string strTemp;
523
524 if (getSingleSection(secConfig, SECTION_NETWORK_ID, strTemp, j_))
525 {
526 if (strTemp == "main")
527 NETWORK_ID = 0;
528 else if (strTemp == "testnet")
529 NETWORK_ID = 1;
530 else if (strTemp == "devnet")
531 NETWORK_ID = 2;
532 else
533 NETWORK_ID = beast::lexicalCastThrow<uint32_t>(strTemp);
534 }
535
536 if (getSingleSection(secConfig, SECTION_PEER_PRIVATE, strTemp, j_))
537 PEER_PRIVATE = beast::lexicalCastThrow<bool>(strTemp);
538
539 if (getSingleSection(secConfig, SECTION_PEERS_MAX, strTemp, j_))
540 {
541 PEERS_MAX = beast::lexicalCastThrow<std::size_t>(strTemp);
542 }
543 else
544 {
545 std::optional<std::size_t> peers_in_max{};
546 if (getSingleSection(secConfig, SECTION_PEERS_IN_MAX, strTemp, j_))
547 {
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");
553 }
554
555 std::optional<std::size_t> peers_out_max{};
556 if (getSingleSection(secConfig, SECTION_PEERS_OUT_MAX, strTemp, j_))
557 {
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");
563 }
564
565 // if one section is configured then the other must be configured too
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
569 "]"
570 "and [" SECTION_PEERS_OUT_MAX
571 "] must be configured");
572
573 if (peers_in_max && peers_out_max)
574 {
575 PEERS_IN_MAX = *peers_in_max;
576 PEERS_OUT_MAX = *peers_out_max;
577 }
578 }
579
580 if (getSingleSection(secConfig, SECTION_NODE_SIZE, strTemp, j_))
581 {
582 if (boost::iequals(strTemp, "tiny"))
583 NODE_SIZE = 0;
584 else if (boost::iequals(strTemp, "small"))
585 NODE_SIZE = 1;
586 else if (boost::iequals(strTemp, "medium"))
587 NODE_SIZE = 2;
588 else if (boost::iequals(strTemp, "large"))
589 NODE_SIZE = 3;
590 else if (boost::iequals(strTemp, "huge"))
591 NODE_SIZE = 4;
592 else
594 4, beast::lexicalCastThrow<std::size_t>(strTemp));
595 }
596
597 if (getSingleSection(secConfig, SECTION_SIGNING_SUPPORT, strTemp, j_))
598 signingEnabled_ = beast::lexicalCastThrow<bool>(strTemp);
599
600 if (getSingleSection(secConfig, SECTION_ELB_SUPPORT, strTemp, j_))
601 ELB_SUPPORT = beast::lexicalCastThrow<bool>(strTemp);
602
603 getSingleSection(secConfig, SECTION_SSL_VERIFY_FILE, SSL_VERIFY_FILE, j_);
604 getSingleSection(secConfig, SECTION_SSL_VERIFY_DIR, SSL_VERIFY_DIR, j_);
605
606 if (getSingleSection(secConfig, SECTION_SSL_VERIFY, strTemp, j_))
607 SSL_VERIFY = beast::lexicalCastThrow<bool>(strTemp);
608
609 if (getSingleSection(secConfig, SECTION_RELAY_VALIDATIONS, strTemp, j_))
610 {
611 if (boost::iequals(strTemp, "all"))
613 else if (boost::iequals(strTemp, "trusted"))
615 else if (boost::iequals(strTemp, "drop_untrusted"))
617 else
618 Throw<std::runtime_error>(
619 "Invalid value specified in [" SECTION_RELAY_VALIDATIONS
620 "] section");
621 }
622
623 if (getSingleSection(secConfig, SECTION_RELAY_PROPOSALS, strTemp, j_))
624 {
625 if (boost::iequals(strTemp, "all"))
627 else if (boost::iequals(strTemp, "trusted"))
629 else if (boost::iequals(strTemp, "drop_untrusted"))
631 else
632 Throw<std::runtime_error>(
633 "Invalid value specified in [" SECTION_RELAY_PROPOSALS
634 "] section");
635 }
636
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");
641
642 if (getSingleSection(secConfig, SECTION_NETWORK_QUORUM, strTemp, j_))
643 NETWORK_QUORUM = beast::lexicalCastThrow<std::size_t>(strTemp);
644
645 FEES = setup_FeeVote(section("voting"));
646 /* [fee_default] is documented in the example config files as useful for
647 * things like offline transaction signing. Until that's completely
648 * deprecated, allow it to override the [voting] section. */
649 if (getSingleSection(secConfig, SECTION_FEE_DEFAULT, strTemp, j_))
650 FEES.reference_fee = beast::lexicalCastThrow<std::uint64_t>(strTemp);
651
652 if (getSingleSection(secConfig, SECTION_LEDGER_HISTORY, strTemp, j_))
653 {
654 if (boost::iequals(strTemp, "full"))
656 std::numeric_limits<decltype(LEDGER_HISTORY)>::max();
657 else if (boost::iequals(strTemp, "none"))
658 LEDGER_HISTORY = 0;
659 else
660 LEDGER_HISTORY = beast::lexicalCastThrow<std::uint32_t>(strTemp);
661 }
662
663 if (getSingleSection(secConfig, SECTION_FETCH_DEPTH, strTemp, j_))
664 {
665 if (boost::iequals(strTemp, "none"))
666 FETCH_DEPTH = 0;
667 else if (boost::iequals(strTemp, "full"))
668 FETCH_DEPTH = std::numeric_limits<decltype(FETCH_DEPTH)>::max();
669 else
670 FETCH_DEPTH = beast::lexicalCastThrow<std::uint32_t>(strTemp);
671
672 if (FETCH_DEPTH < 10)
673 FETCH_DEPTH = 10;
674 }
675
676 // By default, validators don't have pathfinding enabled, unless it is
677 // explicitly requested by the server's admin.
678 if (exists(SECTION_VALIDATION_SEED) || exists(SECTION_VALIDATOR_TOKEN))
679 PATH_SEARCH_MAX = 0;
680
681 if (getSingleSection(secConfig, SECTION_PATH_SEARCH_OLD, strTemp, j_))
682 PATH_SEARCH_OLD = beast::lexicalCastThrow<int>(strTemp);
683 if (getSingleSection(secConfig, SECTION_PATH_SEARCH, strTemp, j_))
684 PATH_SEARCH = beast::lexicalCastThrow<int>(strTemp);
685 if (getSingleSection(secConfig, SECTION_PATH_SEARCH_FAST, strTemp, j_))
686 PATH_SEARCH_FAST = beast::lexicalCastThrow<int>(strTemp);
687 if (getSingleSection(secConfig, SECTION_PATH_SEARCH_MAX, strTemp, j_))
688 PATH_SEARCH_MAX = beast::lexicalCastThrow<int>(strTemp);
689
690 if (getSingleSection(secConfig, SECTION_DEBUG_LOGFILE, strTemp, j_))
691 DEBUG_LOGFILE = strTemp;
692
693 if (getSingleSection(secConfig, SECTION_SWEEP_INTERVAL, strTemp, j_))
694 {
695 SWEEP_INTERVAL = beast::lexicalCastThrow<std::size_t>(strTemp);
696
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");
700 }
701
702 if (getSingleSection(secConfig, SECTION_WORKERS, strTemp, j_))
703 {
704 WORKERS = beast::lexicalCastThrow<int>(strTemp);
705
706 if (WORKERS < 1 || WORKERS > 1024)
707 Throw<std::runtime_error>(
708 "Invalid " SECTION_WORKERS
709 ": must be between 1 and 1024 inclusive.");
710 }
711
712 if (getSingleSection(secConfig, SECTION_IO_WORKERS, strTemp, j_))
713 {
714 IO_WORKERS = beast::lexicalCastThrow<int>(strTemp);
715
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.");
720 }
721
722 if (getSingleSection(secConfig, SECTION_PREFETCH_WORKERS, strTemp, j_))
723 {
724 PREFETCH_WORKERS = beast::lexicalCastThrow<int>(strTemp);
725
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.");
730 }
731
732 if (getSingleSection(secConfig, SECTION_COMPRESSION, strTemp, j_))
733 COMPRESSION = beast::lexicalCastThrow<bool>(strTemp);
734
735 if (getSingleSection(secConfig, SECTION_LEDGER_REPLAY, strTemp, j_))
736 LEDGER_REPLAY = beast::lexicalCastThrow<bool>(strTemp);
737
738 if (exists(SECTION_REDUCE_RELAY))
739 {
740 auto sec = section(SECTION_REDUCE_RELAY);
741
743 // vp_enable config option is deprecated by vp_base_squelch_enable //
744 // This option is kept for backwards compatibility. When squelching //
745 // is the default algorithm, it must be replaced with: //
746 // VP_REDUCE_RELAY_BASE_SQUELCH_ENABLE = //
747 // sec.value_or("vp_base_squelch_enable", true); //
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 "
752 "options. "
753 "vp_enable was deprecated and replaced by "
754 "vp_base_squelch_enable");
755
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);
762 else
765
767 // Temporary squelching config for the peers selected as a source of //
768 // validator messages. The config must be removed once squelching is //
769 // made the default routing algorithm. //
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");
778
779 TX_REDUCE_RELAY_ENABLE = sec.value_or("tx_enable", false);
780 TX_REDUCE_RELAY_METRICS = sec.value_or("tx_metrics", false);
781 TX_REDUCE_RELAY_MIN_PEERS = sec.value_or("tx_min_peers", 20);
782 TX_RELAY_PERCENTAGE = sec.value_or("tx_relay_percentage", 25);
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");
790 }
791
792 if (getSingleSection(secConfig, SECTION_MAX_TRANSACTIONS, strTemp, j_))
793 {
795 beast::lexicalCastThrow<int>(strTemp),
798 }
799
800 if (getSingleSection(secConfig, SECTION_SERVER_DOMAIN, strTemp, j_))
801 {
802 if (!isProperlyFormedTomlDomain(strTemp))
803 {
804 Throw<std::runtime_error>(
805 "Invalid " SECTION_SERVER_DOMAIN
806 ": the domain name does not appear to meet the requirements.");
807 }
808
809 SERVER_DOMAIN = strTemp;
810 }
811
812 if (exists(SECTION_OVERLAY))
813 {
814 auto const sec = section(SECTION_OVERLAY);
815
816 using namespace std::chrono;
817
818 try
819 {
820 if (auto val = sec.get("max_unknown_time"))
822 seconds{beast::lexicalCastThrow<std::uint32_t>(*val)};
823 }
824 catch (...)
825 {
826 Throw<std::runtime_error>(
827 "Invalid value 'max_unknown_time' in " SECTION_OVERLAY
828 ": must be of the form '<number>' representing seconds.");
829 }
830
831 if (MAX_UNKNOWN_TIME < seconds{300} || MAX_UNKNOWN_TIME > seconds{1800})
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.");
835
836 try
837 {
838 if (auto val = sec.get("max_diverged_time"))
840 seconds{beast::lexicalCastThrow<std::uint32_t>(*val)};
841 }
842 catch (...)
843 {
844 Throw<std::runtime_error>(
845 "Invalid value 'max_diverged_time' in " SECTION_OVERLAY
846 ": must be of the form '<number>' representing seconds.");
847 }
848
850 {
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.");
854 }
855 }
856
858 secConfig, SECTION_AMENDMENT_MAJORITY_TIME, strTemp, j_))
859 {
860 using namespace std::chrono;
861 boost::regex const re(
862 "^\\s*(\\d+)\\s*(minutes|hours|days|weeks)\\s*(\\s+.*)?$");
863 boost::smatch match;
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]");
868
870 beast::lexicalCastThrow<std::uint32_t>(match[1].str());
871
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"))
880
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");
886 }
887
888 if (getSingleSection(secConfig, SECTION_BETA_RPC_API, strTemp, j_))
889 BETA_RPC_API = beast::lexicalCastThrow<bool>(strTemp);
890
891 // Do not load trusted validator configuration for standalone mode
892 if (!RUN_STANDALONE)
893 {
894 // If a file was explicitly specified, then throw if the
895 // path is malformed or if the file does not exist or is
896 // not a file.
897 // If the specified file is not an absolute path, then look
898 // for it in the same directory as the config file.
899 // If no path was specified, then look for validators.txt
900 // in the same directory as the config file, but don't complain
901 // if we can't find it.
902 boost::filesystem::path validatorsFile;
903
904 if (getSingleSection(secConfig, SECTION_VALIDATORS_FILE, strTemp, j_))
905 {
906 validatorsFile = strTemp;
907
908 if (validatorsFile.empty())
909 Throw<std::runtime_error>(
910 "Invalid path specified in [" SECTION_VALIDATORS_FILE "]");
911
912 if (!validatorsFile.is_absolute() && !CONFIG_DIR.empty())
913 validatorsFile = CONFIG_DIR / validatorsFile;
914
915 if (!boost::filesystem::exists(validatorsFile))
916 Throw<std::runtime_error>(
917 "The file specified in [" SECTION_VALIDATORS_FILE
918 "] "
919 "does not exist: " +
920 validatorsFile.string());
921
922 else if (
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
927 "]: " +
928 validatorsFile.string());
929 }
930 else if (!CONFIG_DIR.empty())
931 {
932 validatorsFile = CONFIG_DIR / validatorsFileName;
933
934 if (!validatorsFile.empty())
935 {
936 if (!boost::filesystem::exists(validatorsFile))
937 validatorsFile.clear();
938 else if (
939 !boost::filesystem::is_regular_file(validatorsFile) &&
940 !boost::filesystem::is_symlink(validatorsFile))
941 validatorsFile.clear();
942 }
943 }
944
945 if (!validatorsFile.empty() &&
946 boost::filesystem::exists(validatorsFile) &&
947 (boost::filesystem::is_regular_file(validatorsFile) ||
948 boost::filesystem::is_symlink(validatorsFile)))
949 {
950 boost::system::error_code ec;
951 auto const data = getFileContents(ec, validatorsFile);
952 if (ec)
953 {
954 Throw<std::runtime_error>(
955 "Failed to read '" + validatorsFile.string() + "'." +
956 std::to_string(ec.value()) + ": " + ec.message());
957 }
958
959 auto iniFile = parseIniFile(data, true);
960
961 auto entries = getIniFileSection(iniFile, SECTION_VALIDATORS);
962
963 if (entries)
964 section(SECTION_VALIDATORS).append(*entries);
965
966 auto valKeyEntries =
967 getIniFileSection(iniFile, SECTION_VALIDATOR_KEYS);
968
969 if (valKeyEntries)
970 section(SECTION_VALIDATOR_KEYS).append(*valKeyEntries);
971
972 auto valSiteEntries =
973 getIniFileSection(iniFile, SECTION_VALIDATOR_LIST_SITES);
974
975 if (valSiteEntries)
976 section(SECTION_VALIDATOR_LIST_SITES).append(*valSiteEntries);
977
978 auto valListKeys =
979 getIniFileSection(iniFile, SECTION_VALIDATOR_LIST_KEYS);
980
981 if (valListKeys)
982 section(SECTION_VALIDATOR_LIST_KEYS).append(*valListKeys);
983
984 auto valListThreshold =
985 getIniFileSection(iniFile, SECTION_VALIDATOR_LIST_THRESHOLD);
986
987 if (valListThreshold)
988 section(SECTION_VALIDATOR_LIST_THRESHOLD)
989 .append(*valListThreshold);
990
991 if (!entries && !valKeyEntries && !valListKeys)
992 Throw<std::runtime_error>(
993 "The file specified in [" SECTION_VALIDATORS_FILE
994 "] "
995 "does not contain a [" SECTION_VALIDATORS
996 "], "
997 "[" SECTION_VALIDATOR_KEYS
998 "] or "
999 "[" SECTION_VALIDATOR_LIST_KEYS
1000 "]"
1001 " section: " +
1002 validatorsFile.string());
1003 }
1004
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)
1011 {
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; // NOTE: Explicitly ask for computed
1017 else if (
1018 listThreshold >
1019 section(SECTION_VALIDATOR_LIST_KEYS).values().size())
1020 {
1021 Throw<std::runtime_error>(
1022 "Value in config section "
1023 "[" SECTION_VALIDATOR_LIST_THRESHOLD
1024 "] exceeds the number of configured list keys");
1025 }
1026 return listThreshold;
1027 }
1028 else
1029 {
1030 Throw<std::runtime_error>(
1031 "Config section "
1032 "[" SECTION_VALIDATOR_LIST_THRESHOLD
1033 "] should contain single value only");
1034 }
1035 }();
1036
1037 // Consolidate [validator_keys] and [validators]
1038 section(SECTION_VALIDATORS)
1039 .append(section(SECTION_VALIDATOR_KEYS).lines());
1040
1041 if (!section(SECTION_VALIDATOR_LIST_SITES).lines().empty() &&
1042 section(SECTION_VALIDATOR_LIST_KEYS).lines().empty())
1043 {
1044 Throw<std::runtime_error>(
1045 "[" + std::string(SECTION_VALIDATOR_LIST_KEYS) +
1046 "] config section is missing");
1047 }
1048 }
1049
1050 {
1051 auto const part = section("features");
1052 for (auto const& s : part.values())
1053 {
1054 if (auto const f = getRegisteredFeature(s))
1055 features.insert(*f);
1056 else
1057 Throw<std::runtime_error>(
1058 "Unknown feature: " + s + " in config file.");
1059 }
1060 }
1061
1062 // This doesn't properly belong here, but check to make sure that the
1063 // value specified for network_quorum is achievable:
1064 {
1065 auto pm = PEERS_MAX;
1066
1067 // FIXME this apparently magic value is actually defined as a constant
1068 // elsewhere (see defaultMaxPeers) but we handle this check here.
1069 if (pm == 0)
1070 pm = 21;
1071
1072 if (NETWORK_QUORUM > pm)
1073 {
1074 Throw<std::runtime_error>(
1075 "The minimum number of required peers (network_quorum) exceeds "
1076 "the maximum number of allowed peers (peers_max)");
1077 }
1078 }
1079}
1080
1081boost::filesystem::path
1083{
1084 auto log_file = DEBUG_LOGFILE;
1085
1086 if (!log_file.empty() && !log_file.is_absolute())
1087 {
1088 // Unless an absolute path for the log file is specified, the
1089 // path is relative to the config file directory.
1090 log_file = boost::filesystem::absolute(log_file, CONFIG_DIR);
1091 }
1092
1093 if (!log_file.empty())
1094 {
1095 auto log_dir = log_file.parent_path();
1096
1097 if (!boost::filesystem::is_directory(log_dir))
1098 {
1099 boost::system::error_code ec;
1100 boost::filesystem::create_directories(log_dir, ec);
1101
1102 // If we fail, we warn but continue so that the calling code can
1103 // decide how to handle this situation.
1104 if (ec)
1105 {
1106 std::cerr << "Unable to create log file path " << log_dir
1107 << ": " << ec.message() << '\n';
1108 }
1109 }
1110 }
1111
1112 return log_file;
1113}
1114
1115int
1117{
1118 auto const index = static_cast<std::underlying_type_t<SizedItem>>(item);
1119 XRPL_ASSERT(
1120 index < sizedItems.size(),
1121 "ripple::Config::getValueFor : valid index input");
1122 XRPL_ASSERT(
1123 !node || *node <= 4,
1124 "ripple::Config::getValueFor : unset or valid node");
1125 return sizedItems.at(index).second.at(node.value_or(NODE_SIZE));
1126}
1127
1129setup_FeeVote(Section const& section)
1130{
1131 FeeSetup setup;
1132 {
1133 std::uint64_t temp;
1134 if (set(temp, "reference_fee", section) &&
1136 setup.reference_fee = temp;
1137 }
1138 {
1139 std::uint32_t temp;
1140 if (set(temp, "account_reserve", section))
1141 setup.account_reserve = temp;
1142 if (set(temp, "owner_reserve", section))
1143 setup.owner_reserve = temp;
1144 }
1145 return setup;
1146}
1147
1148} // 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
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
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:1082
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:1116
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:479
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
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(std::string const &sslVerifyDir, std::string const &sslVerifyFile, bool sslVerify, beast::Journal j)
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.
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)
T is_same_v
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:25
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:429
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)
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:1129
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)