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