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