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