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
423void
425{
426 // NOTE: this writes to cerr because we want cout to be reserved
427 // for the writing of the json response (so that stdout can be part of a
428 // pipeline, for instance)
429 if (!QUIET)
430 std::cerr << "Loading: " << CONFIG_FILE << "\n";
431
432 boost::system::error_code ec;
433 auto const fileContents = getFileContents(ec, CONFIG_FILE);
434
435 if (ec)
436 {
437 std::cerr << "Failed to read '" << CONFIG_FILE << "'." << ec.value()
438 << ": " << ec.message() << std::endl;
439 return;
440 }
441
442 loadFromString(fileContents);
443}
444
445void
447{
448 IniFileSections secConfig = parseIniFile(fileContents, true);
449
450 build(secConfig);
451
452 if (auto s = getIniFileSection(secConfig, SECTION_IPS))
453 IPS = *s;
454
455 if (auto s = getIniFileSection(secConfig, SECTION_IPS_FIXED))
456 IPS_FIXED = *s;
457
458 // if the user has specified ip:port then replace : with a space.
459 {
460 auto replaceColons = [](std::vector<std::string>& strVec) {
461 const static std::regex e(":([0-9]+)$");
462 for (auto& line : strVec)
463 {
464 // skip anything that might be an ipv6 address
465 if (std::count(line.begin(), line.end(), ':') != 1)
466 continue;
467
468 std::string result = std::regex_replace(line, e, " $1");
469 // sanity check the result of the replace, should be same length
470 // as input
471 if (result.size() == line.size())
472 line = result;
473 }
474 };
475
476 replaceColons(IPS_FIXED);
477 replaceColons(IPS);
478 }
479
480 {
481 std::string dbPath;
482 if (getSingleSection(secConfig, "database_path", dbPath, j_))
483 {
484 boost::filesystem::path p(dbPath);
485 legacy("database_path", boost::filesystem::absolute(p).string());
486 }
487 }
488
489 std::string strTemp;
490
491 if (getSingleSection(secConfig, SECTION_NETWORK_ID, strTemp, j_))
492 {
493 if (strTemp == "main")
494 NETWORK_ID = 0;
495 else if (strTemp == "testnet")
496 NETWORK_ID = 1;
497 else if (strTemp == "devnet")
498 NETWORK_ID = 2;
499 else
500 NETWORK_ID = beast::lexicalCastThrow<uint32_t>(strTemp);
501 }
502
503 if (getSingleSection(secConfig, SECTION_PEER_PRIVATE, strTemp, j_))
504 PEER_PRIVATE = beast::lexicalCastThrow<bool>(strTemp);
505
506 if (getSingleSection(secConfig, SECTION_PEERS_MAX, strTemp, j_))
507 {
508 PEERS_MAX = beast::lexicalCastThrow<std::size_t>(strTemp);
509 }
510 else
511 {
512 std::optional<std::size_t> peers_in_max{};
513 if (getSingleSection(secConfig, SECTION_PEERS_IN_MAX, strTemp, j_))
514 {
515 peers_in_max = beast::lexicalCastThrow<std::size_t>(strTemp);
516 if (*peers_in_max > 1000)
517 Throw<std::runtime_error>(
518 "Invalid value specified in [" SECTION_PEERS_IN_MAX
519 "] section; the value must be less or equal than 1000");
520 }
521
522 std::optional<std::size_t> peers_out_max{};
523 if (getSingleSection(secConfig, SECTION_PEERS_OUT_MAX, strTemp, j_))
524 {
525 peers_out_max = beast::lexicalCastThrow<std::size_t>(strTemp);
526 if (*peers_out_max < 10 || *peers_out_max > 1000)
527 Throw<std::runtime_error>(
528 "Invalid value specified in [" SECTION_PEERS_OUT_MAX
529 "] section; the value must be in range 10-1000");
530 }
531
532 // if one section is configured then the other must be configured too
533 if ((peers_in_max && !peers_out_max) ||
534 (peers_out_max && !peers_in_max))
535 Throw<std::runtime_error>("Both sections [" SECTION_PEERS_IN_MAX
536 "]"
537 "and [" SECTION_PEERS_OUT_MAX
538 "] must be configured");
539
540 if (peers_in_max && peers_out_max)
541 {
542 PEERS_IN_MAX = *peers_in_max;
543 PEERS_OUT_MAX = *peers_out_max;
544 }
545 }
546
547 if (getSingleSection(secConfig, SECTION_NODE_SIZE, strTemp, j_))
548 {
549 if (boost::iequals(strTemp, "tiny"))
550 NODE_SIZE = 0;
551 else if (boost::iequals(strTemp, "small"))
552 NODE_SIZE = 1;
553 else if (boost::iequals(strTemp, "medium"))
554 NODE_SIZE = 2;
555 else if (boost::iequals(strTemp, "large"))
556 NODE_SIZE = 3;
557 else if (boost::iequals(strTemp, "huge"))
558 NODE_SIZE = 4;
559 else
560 NODE_SIZE = std::min<std::size_t>(
561 4, beast::lexicalCastThrow<std::size_t>(strTemp));
562 }
563
564 if (getSingleSection(secConfig, SECTION_SIGNING_SUPPORT, strTemp, j_))
565 signingEnabled_ = beast::lexicalCastThrow<bool>(strTemp);
566
567 if (getSingleSection(secConfig, SECTION_ELB_SUPPORT, strTemp, j_))
568 ELB_SUPPORT = beast::lexicalCastThrow<bool>(strTemp);
569
570 getSingleSection(secConfig, SECTION_SSL_VERIFY_FILE, SSL_VERIFY_FILE, j_);
571 getSingleSection(secConfig, SECTION_SSL_VERIFY_DIR, SSL_VERIFY_DIR, j_);
572
573 if (getSingleSection(secConfig, SECTION_SSL_VERIFY, strTemp, j_))
574 SSL_VERIFY = beast::lexicalCastThrow<bool>(strTemp);
575
576 if (getSingleSection(secConfig, SECTION_RELAY_VALIDATIONS, strTemp, j_))
577 {
578 if (boost::iequals(strTemp, "all"))
580 else if (boost::iequals(strTemp, "trusted"))
582 else if (boost::iequals(strTemp, "drop_untrusted"))
584 else
585 Throw<std::runtime_error>(
586 "Invalid value specified in [" SECTION_RELAY_VALIDATIONS
587 "] section");
588 }
589
590 if (getSingleSection(secConfig, SECTION_RELAY_PROPOSALS, strTemp, j_))
591 {
592 if (boost::iequals(strTemp, "all"))
594 else if (boost::iequals(strTemp, "trusted"))
596 else if (boost::iequals(strTemp, "drop_untrusted"))
598 else
599 Throw<std::runtime_error>(
600 "Invalid value specified in [" SECTION_RELAY_PROPOSALS
601 "] section");
602 }
603
604 if (exists(SECTION_VALIDATION_SEED) && exists(SECTION_VALIDATOR_TOKEN))
605 Throw<std::runtime_error>("Cannot have both [" SECTION_VALIDATION_SEED
606 "] and [" SECTION_VALIDATOR_TOKEN
607 "] config sections");
608
609 if (getSingleSection(secConfig, SECTION_NETWORK_QUORUM, strTemp, j_))
610 NETWORK_QUORUM = beast::lexicalCastThrow<std::size_t>(strTemp);
611
612 FEES = setup_FeeVote(section("voting"));
613 /* [fee_default] is documented in the example config files as useful for
614 * things like offline transaction signing. Until that's completely
615 * deprecated, allow it to override the [voting] section. */
616 if (getSingleSection(secConfig, SECTION_FEE_DEFAULT, strTemp, j_))
617 FEES.reference_fee = beast::lexicalCastThrow<std::uint64_t>(strTemp);
618
619 if (getSingleSection(secConfig, SECTION_LEDGER_HISTORY, strTemp, j_))
620 {
621 if (boost::iequals(strTemp, "full"))
623 std::numeric_limits<decltype(LEDGER_HISTORY)>::max();
624 else if (boost::iequals(strTemp, "none"))
625 LEDGER_HISTORY = 0;
626 else
627 LEDGER_HISTORY = beast::lexicalCastThrow<std::uint32_t>(strTemp);
628 }
629
630 if (getSingleSection(secConfig, SECTION_FETCH_DEPTH, strTemp, j_))
631 {
632 if (boost::iequals(strTemp, "none"))
633 FETCH_DEPTH = 0;
634 else if (boost::iequals(strTemp, "full"))
635 FETCH_DEPTH = std::numeric_limits<decltype(FETCH_DEPTH)>::max();
636 else
637 FETCH_DEPTH = beast::lexicalCastThrow<std::uint32_t>(strTemp);
638
639 if (FETCH_DEPTH < 10)
640 FETCH_DEPTH = 10;
641 }
642
643 // By default, validators don't have pathfinding enabled, unless it is
644 // explicitly requested by the server's admin.
645 if (exists(SECTION_VALIDATION_SEED) || exists(SECTION_VALIDATOR_TOKEN))
646 PATH_SEARCH_MAX = 0;
647
648 if (getSingleSection(secConfig, SECTION_PATH_SEARCH_OLD, strTemp, j_))
649 PATH_SEARCH_OLD = beast::lexicalCastThrow<int>(strTemp);
650 if (getSingleSection(secConfig, SECTION_PATH_SEARCH, strTemp, j_))
651 PATH_SEARCH = beast::lexicalCastThrow<int>(strTemp);
652 if (getSingleSection(secConfig, SECTION_PATH_SEARCH_FAST, strTemp, j_))
653 PATH_SEARCH_FAST = beast::lexicalCastThrow<int>(strTemp);
654 if (getSingleSection(secConfig, SECTION_PATH_SEARCH_MAX, strTemp, j_))
655 PATH_SEARCH_MAX = beast::lexicalCastThrow<int>(strTemp);
656
657 if (getSingleSection(secConfig, SECTION_DEBUG_LOGFILE, strTemp, j_))
658 DEBUG_LOGFILE = strTemp;
659
660 if (getSingleSection(secConfig, SECTION_SWEEP_INTERVAL, strTemp, j_))
661 {
662 SWEEP_INTERVAL = beast::lexicalCastThrow<std::size_t>(strTemp);
663
664 if (SWEEP_INTERVAL < 10 || SWEEP_INTERVAL > 600)
665 Throw<std::runtime_error>("Invalid " SECTION_SWEEP_INTERVAL
666 ": must be between 10 and 600 inclusive");
667 }
668
669 if (getSingleSection(secConfig, SECTION_WORKERS, strTemp, j_))
670 {
671 WORKERS = beast::lexicalCastThrow<int>(strTemp);
672
673 if (WORKERS < 1 || WORKERS > 1024)
674 Throw<std::runtime_error>(
675 "Invalid " SECTION_WORKERS
676 ": must be between 1 and 1024 inclusive.");
677 }
678
679 if (getSingleSection(secConfig, SECTION_IO_WORKERS, strTemp, j_))
680 {
681 IO_WORKERS = beast::lexicalCastThrow<int>(strTemp);
682
683 if (IO_WORKERS < 1 || IO_WORKERS > 1024)
684 Throw<std::runtime_error>(
685 "Invalid " SECTION_IO_WORKERS
686 ": must be between 1 and 1024 inclusive.");
687 }
688
689 if (getSingleSection(secConfig, SECTION_PREFETCH_WORKERS, strTemp, j_))
690 {
691 PREFETCH_WORKERS = beast::lexicalCastThrow<int>(strTemp);
692
693 if (PREFETCH_WORKERS < 1 || PREFETCH_WORKERS > 1024)
694 Throw<std::runtime_error>(
695 "Invalid " SECTION_PREFETCH_WORKERS
696 ": must be between 1 and 1024 inclusive.");
697 }
698
699 if (getSingleSection(secConfig, SECTION_COMPRESSION, strTemp, j_))
700 COMPRESSION = beast::lexicalCastThrow<bool>(strTemp);
701
702 if (getSingleSection(secConfig, SECTION_LEDGER_REPLAY, strTemp, j_))
703 LEDGER_REPLAY = beast::lexicalCastThrow<bool>(strTemp);
704
705 if (exists(SECTION_REDUCE_RELAY))
706 {
707 auto sec = section(SECTION_REDUCE_RELAY);
708 VP_REDUCE_RELAY_ENABLE = sec.value_or("vp_enable", false);
709 VP_REDUCE_RELAY_SQUELCH = sec.value_or("vp_squelch", false);
710 TX_REDUCE_RELAY_ENABLE = sec.value_or("tx_enable", false);
711 TX_REDUCE_RELAY_METRICS = sec.value_or("tx_metrics", false);
712 TX_REDUCE_RELAY_MIN_PEERS = sec.value_or("tx_min_peers", 20);
713 TX_RELAY_PERCENTAGE = sec.value_or("tx_relay_percentage", 25);
714 if (TX_RELAY_PERCENTAGE < 10 || TX_RELAY_PERCENTAGE > 100 ||
716 Throw<std::runtime_error>(
717 "Invalid " SECTION_REDUCE_RELAY
718 ", tx_min_peers must be greater or equal to 10"
719 ", tx_relay_percentage must be greater or equal to 10 "
720 "and less or equal to 100");
721 }
722
723 if (getSingleSection(secConfig, SECTION_MAX_TRANSACTIONS, strTemp, j_))
724 {
726 beast::lexicalCastThrow<int>(strTemp),
729 }
730
731 if (getSingleSection(secConfig, SECTION_SERVER_DOMAIN, strTemp, j_))
732 {
733 if (!isProperlyFormedTomlDomain(strTemp))
734 {
735 Throw<std::runtime_error>(
736 "Invalid " SECTION_SERVER_DOMAIN
737 ": the domain name does not appear to meet the requirements.");
738 }
739
740 SERVER_DOMAIN = strTemp;
741 }
742
743 if (exists(SECTION_OVERLAY))
744 {
745 auto const sec = section(SECTION_OVERLAY);
746
747 using namespace std::chrono;
748
749 try
750 {
751 if (auto val = sec.get("max_unknown_time"))
753 seconds{beast::lexicalCastThrow<std::uint32_t>(*val)};
754 }
755 catch (...)
756 {
757 Throw<std::runtime_error>(
758 "Invalid value 'max_unknown_time' in " SECTION_OVERLAY
759 ": must be of the form '<number>' representing seconds.");
760 }
761
762 if (MAX_UNKNOWN_TIME < seconds{300} || MAX_UNKNOWN_TIME > seconds{1800})
763 Throw<std::runtime_error>(
764 "Invalid value 'max_unknown_time' in " SECTION_OVERLAY
765 ": the time must be between 300 and 1800 seconds, inclusive.");
766
767 try
768 {
769 if (auto val = sec.get("max_diverged_time"))
771 seconds{beast::lexicalCastThrow<std::uint32_t>(*val)};
772 }
773 catch (...)
774 {
775 Throw<std::runtime_error>(
776 "Invalid value 'max_diverged_time' in " SECTION_OVERLAY
777 ": must be of the form '<number>' representing seconds.");
778 }
779
781 {
782 Throw<std::runtime_error>(
783 "Invalid value 'max_diverged_time' in " SECTION_OVERLAY
784 ": the time must be between 60 and 900 seconds, inclusive.");
785 }
786 }
787
789 secConfig, SECTION_AMENDMENT_MAJORITY_TIME, strTemp, j_))
790 {
791 using namespace std::chrono;
792 boost::regex const re(
793 "^\\s*(\\d+)\\s*(minutes|hours|days|weeks)\\s*(\\s+.*)?$");
794 boost::smatch match;
795 if (!boost::regex_match(strTemp, match, re))
796 Throw<std::runtime_error>(
797 "Invalid " SECTION_AMENDMENT_MAJORITY_TIME
798 ", must be: [0-9]+ [minutes|hours|days|weeks]");
799
801 beast::lexicalCastThrow<std::uint32_t>(match[1].str());
802
803 if (boost::iequals(match[2], "minutes"))
805 else if (boost::iequals(match[2], "hours"))
807 else if (boost::iequals(match[2], "days"))
809 else if (boost::iequals(match[2], "weeks"))
811
813 Throw<std::runtime_error>(
814 "Invalid " SECTION_AMENDMENT_MAJORITY_TIME
815 ", the minimum amount of time an amendment must hold a "
816 "majority is 15 minutes");
817 }
818
819 if (getSingleSection(secConfig, SECTION_BETA_RPC_API, strTemp, j_))
820 BETA_RPC_API = beast::lexicalCastThrow<bool>(strTemp);
821
822 // Do not load trusted validator configuration for standalone mode
823 if (!RUN_STANDALONE)
824 {
825 // If a file was explicitly specified, then throw if the
826 // path is malformed or if the file does not exist or is
827 // not a file.
828 // If the specified file is not an absolute path, then look
829 // for it in the same directory as the config file.
830 // If no path was specified, then look for validators.txt
831 // in the same directory as the config file, but don't complain
832 // if we can't find it.
833 boost::filesystem::path validatorsFile;
834
835 if (getSingleSection(secConfig, SECTION_VALIDATORS_FILE, strTemp, j_))
836 {
837 validatorsFile = strTemp;
838
839 if (validatorsFile.empty())
840 Throw<std::runtime_error>(
841 "Invalid path specified in [" SECTION_VALIDATORS_FILE "]");
842
843 if (!validatorsFile.is_absolute() && !CONFIG_DIR.empty())
844 validatorsFile = CONFIG_DIR / validatorsFile;
845
846 if (!boost::filesystem::exists(validatorsFile))
847 Throw<std::runtime_error>(
848 "The file specified in [" SECTION_VALIDATORS_FILE
849 "] "
850 "does not exist: " +
851 validatorsFile.string());
852
853 else if (
854 !boost::filesystem::is_regular_file(validatorsFile) &&
855 !boost::filesystem::is_symlink(validatorsFile))
856 Throw<std::runtime_error>(
857 "Invalid file specified in [" SECTION_VALIDATORS_FILE
858 "]: " +
859 validatorsFile.string());
860 }
861 else if (!CONFIG_DIR.empty())
862 {
863 validatorsFile = CONFIG_DIR / validatorsFileName;
864
865 if (!validatorsFile.empty())
866 {
867 if (!boost::filesystem::exists(validatorsFile))
868 validatorsFile.clear();
869 else if (
870 !boost::filesystem::is_regular_file(validatorsFile) &&
871 !boost::filesystem::is_symlink(validatorsFile))
872 validatorsFile.clear();
873 }
874 }
875
876 if (!validatorsFile.empty() &&
877 boost::filesystem::exists(validatorsFile) &&
878 (boost::filesystem::is_regular_file(validatorsFile) ||
879 boost::filesystem::is_symlink(validatorsFile)))
880 {
881 boost::system::error_code ec;
882 auto const data = getFileContents(ec, validatorsFile);
883 if (ec)
884 {
885 Throw<std::runtime_error>(
886 "Failed to read '" + validatorsFile.string() + "'." +
887 std::to_string(ec.value()) + ": " + ec.message());
888 }
889
890 auto iniFile = parseIniFile(data, true);
891
892 auto entries = getIniFileSection(iniFile, SECTION_VALIDATORS);
893
894 if (entries)
895 section(SECTION_VALIDATORS).append(*entries);
896
897 auto valKeyEntries =
898 getIniFileSection(iniFile, SECTION_VALIDATOR_KEYS);
899
900 if (valKeyEntries)
901 section(SECTION_VALIDATOR_KEYS).append(*valKeyEntries);
902
903 auto valSiteEntries =
904 getIniFileSection(iniFile, SECTION_VALIDATOR_LIST_SITES);
905
906 if (valSiteEntries)
907 section(SECTION_VALIDATOR_LIST_SITES).append(*valSiteEntries);
908
909 auto valListKeys =
910 getIniFileSection(iniFile, SECTION_VALIDATOR_LIST_KEYS);
911
912 if (valListKeys)
913 section(SECTION_VALIDATOR_LIST_KEYS).append(*valListKeys);
914
915 auto valListThreshold =
916 getIniFileSection(iniFile, SECTION_VALIDATOR_LIST_THRESHOLD);
917
918 if (valListThreshold)
919 section(SECTION_VALIDATOR_LIST_THRESHOLD)
920 .append(*valListThreshold);
921
922 if (!entries && !valKeyEntries && !valListKeys)
923 Throw<std::runtime_error>(
924 "The file specified in [" SECTION_VALIDATORS_FILE
925 "] "
926 "does not contain a [" SECTION_VALIDATORS
927 "], "
928 "[" SECTION_VALIDATOR_KEYS
929 "] or "
930 "[" SECTION_VALIDATOR_LIST_KEYS
931 "]"
932 " section: " +
933 validatorsFile.string());
934 }
935
937 auto const& listThreshold =
938 section(SECTION_VALIDATOR_LIST_THRESHOLD);
939 if (listThreshold.lines().empty())
940 return std::nullopt;
941 else if (listThreshold.values().size() == 1)
942 {
943 auto strTemp = listThreshold.values()[0];
944 auto const listThreshold =
945 beast::lexicalCastThrow<std::size_t>(strTemp);
946 if (listThreshold == 0)
947 return std::nullopt; // NOTE: Explicitly ask for computed
948 else if (
949 listThreshold >
950 section(SECTION_VALIDATOR_LIST_KEYS).values().size())
951 {
952 Throw<std::runtime_error>(
953 "Value in config section "
954 "[" SECTION_VALIDATOR_LIST_THRESHOLD
955 "] exceeds the number of configured list keys");
956 }
957 return listThreshold;
958 }
959 else
960 {
961 Throw<std::runtime_error>(
962 "Config section "
963 "[" SECTION_VALIDATOR_LIST_THRESHOLD
964 "] should contain single value only");
965 }
966 }();
967
968 // Consolidate [validator_keys] and [validators]
969 section(SECTION_VALIDATORS)
970 .append(section(SECTION_VALIDATOR_KEYS).lines());
971
972 if (!section(SECTION_VALIDATOR_LIST_SITES).lines().empty() &&
973 section(SECTION_VALIDATOR_LIST_KEYS).lines().empty())
974 {
975 Throw<std::runtime_error>(
976 "[" + std::string(SECTION_VALIDATOR_LIST_KEYS) +
977 "] config section is missing");
978 }
979 }
980
981 {
982 auto const part = section("features");
983 for (auto const& s : part.values())
984 {
985 if (auto const f = getRegisteredFeature(s))
986 features.insert(*f);
987 else
988 Throw<std::runtime_error>(
989 "Unknown feature: " + s + " in config file.");
990 }
991 }
992
993 // This doesn't properly belong here, but check to make sure that the
994 // value specified for network_quorum is achievable:
995 {
996 auto pm = PEERS_MAX;
997
998 // FIXME this apparently magic value is actually defined as a constant
999 // elsewhere (see defaultMaxPeers) but we handle this check here.
1000 if (pm == 0)
1001 pm = 21;
1002
1003 if (NETWORK_QUORUM > pm)
1004 {
1005 Throw<std::runtime_error>(
1006 "The minimum number of required peers (network_quorum) exceeds "
1007 "the maximum number of allowed peers (peers_max)");
1008 }
1009 }
1010}
1011
1012boost::filesystem::path
1014{
1015 auto log_file = DEBUG_LOGFILE;
1016
1017 if (!log_file.empty() && !log_file.is_absolute())
1018 {
1019 // Unless an absolute path for the log file is specified, the
1020 // path is relative to the config file directory.
1021 log_file = boost::filesystem::absolute(log_file, CONFIG_DIR);
1022 }
1023
1024 if (!log_file.empty())
1025 {
1026 auto log_dir = log_file.parent_path();
1027
1028 if (!boost::filesystem::is_directory(log_dir))
1029 {
1030 boost::system::error_code ec;
1031 boost::filesystem::create_directories(log_dir, ec);
1032
1033 // If we fail, we warn but continue so that the calling code can
1034 // decide how to handle this situation.
1035 if (ec)
1036 {
1037 std::cerr << "Unable to create log file path " << log_dir
1038 << ": " << ec.message() << '\n';
1039 }
1040 }
1041 }
1042
1043 return log_file;
1044}
1045
1046int
1048{
1049 auto const index = static_cast<std::underlying_type_t<SizedItem>>(item);
1050 XRPL_ASSERT(
1051 index < sizedItems.size(),
1052 "ripple::Config::getValueFor : valid index input");
1053 XRPL_ASSERT(
1054 !node || *node <= 4,
1055 "ripple::Config::getValueFor : unset or valid node");
1056 return sizedItems.at(index).second.at(node.value_or(NODE_SIZE));
1057}
1058
1060setup_FeeVote(Section const& section)
1061{
1062 FeeSetup setup;
1063 {
1064 std::uint64_t temp;
1065 if (set(temp, "reference_fee", section) &&
1067 setup.reference_fee = temp;
1068 }
1069 {
1070 std::uint32_t temp;
1071 if (set(temp, "account_reserve", section))
1072 setup.account_reserve = temp;
1073 if (set(temp, "owner_reserve", section))
1074 setup.owner_reserve = temp;
1075 }
1076 return setup;
1077}
1078
1079} // 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:1013
void load()
Definition: Config.cpp:424
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:1047
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:446
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:43
void append(std::vector< std::string > const &lines)
Append a set of lines to this section.
Definition: BasicConfig.cpp:38
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
std::map< std::string, std::vector< std::string > > IniFileSections
Definition: BasicConfig.h:35
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
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:384
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:1060
bool isProperlyFormedTomlDomain(std::string_view domain)
Determines if the given string looks like a TOML-file hosting domain.
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)
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)