rippled
Loading...
Searching...
No Matches
Application.cpp
1#include <xrpld/app/consensus/RCLValidations.h>
2#include <xrpld/app/ledger/InboundLedgers.h>
3#include <xrpld/app/ledger/InboundTransactions.h>
4#include <xrpld/app/ledger/LedgerCleaner.h>
5#include <xrpld/app/ledger/LedgerMaster.h>
6#include <xrpld/app/ledger/LedgerReplayer.h>
7#include <xrpld/app/ledger/LedgerToJson.h>
8#include <xrpld/app/ledger/OpenLedger.h>
9#include <xrpld/app/ledger/OrderBookDB.h>
10#include <xrpld/app/ledger/PendingSaves.h>
11#include <xrpld/app/ledger/TransactionMaster.h>
12#include <xrpld/app/main/Application.h>
13#include <xrpld/app/main/BasicApp.h>
14#include <xrpld/app/main/DBInit.h>
15#include <xrpld/app/main/GRPCServer.h>
16#include <xrpld/app/main/LoadManager.h>
17#include <xrpld/app/main/NodeIdentity.h>
18#include <xrpld/app/main/NodeStoreScheduler.h>
19#include <xrpld/app/misc/AmendmentTable.h>
20#include <xrpld/app/misc/HashRouter.h>
21#include <xrpld/app/misc/LoadFeeTrack.h>
22#include <xrpld/app/misc/NetworkOPs.h>
23#include <xrpld/app/misc/SHAMapStore.h>
24#include <xrpld/app/misc/TxQ.h>
25#include <xrpld/app/misc/ValidatorKeys.h>
26#include <xrpld/app/misc/ValidatorSite.h>
27#include <xrpld/app/paths/PathRequests.h>
28#include <xrpld/app/rdb/RelationalDatabase.h>
29#include <xrpld/app/rdb/Wallet.h>
30#include <xrpld/app/tx/apply.h>
31#include <xrpld/core/DatabaseCon.h>
32#include <xrpld/overlay/Cluster.h>
33#include <xrpld/overlay/PeerReservationTable.h>
34#include <xrpld/overlay/PeerSet.h>
35#include <xrpld/overlay/make_Overlay.h>
36#include <xrpld/shamap/NodeFamily.h>
37
38#include <xrpl/basics/ByteUtilities.h>
39#include <xrpl/basics/ResolverAsio.h>
40#include <xrpl/basics/random.h>
41#include <xrpl/beast/asio/io_latency_probe.h>
42#include <xrpl/beast/core/LexicalCast.h>
43#include <xrpl/core/PerfLog.h>
44#include <xrpl/crypto/csprng.h>
45#include <xrpl/json/json_reader.h>
46#include <xrpl/nodestore/DummyScheduler.h>
47#include <xrpl/protocol/ApiVersion.h>
48#include <xrpl/protocol/BuildInfo.h>
49#include <xrpl/protocol/Feature.h>
50#include <xrpl/protocol/Protocol.h>
51#include <xrpl/protocol/STParsedJSON.h>
52#include <xrpl/resource/Fees.h>
53
54#include <boost/algorithm/string/predicate.hpp>
55#include <boost/asio/steady_timer.hpp>
56#include <boost/system/error_code.hpp>
57
58#include <date/date.h>
59
60#include <chrono>
61#include <condition_variable>
62#include <cstring>
63#include <fstream>
64#include <limits>
65#include <mutex>
66#include <optional>
67#include <utility>
68
69namespace xrpl {
70
71static void
72fixConfigPorts(Config& config, Endpoints const& endpoints);
73
74// VFALCO TODO Move the function definitions into the class declaration
75class ApplicationImp : public Application, public BasicApp
76{
77private:
79 {
80 private:
85
86 public:
91 boost::asio::io_context& ios)
92 : m_event(ev), m_journal(journal), m_probe(interval, ios), lastSample_{}
93 {
94 }
95
96 void
98 {
99 m_probe.sample(std::ref(*this));
100 }
101
102 template <class Duration>
103 void
104 operator()(Duration const& elapsed)
105 {
106 using namespace std::chrono;
107 auto const lastSample = ceil<milliseconds>(elapsed);
108
109 lastSample_ = lastSample;
110
111 if (lastSample >= 10ms)
112 m_event.notify(lastSample);
113 if (lastSample >= 500ms)
114 {
115 JLOG(m_journal.warn()) << "io_context latency = " << lastSample.count();
116 }
117 }
118
120 get() const
121 {
122 return lastSample_.load();
123 }
124
125 void
127 {
128 m_probe.cancel();
129 }
130
131 void
133 {
135 }
136 };
137
138public:
142
144
148
149 // Required by the SHAMapStore
151
158
163
165
168 // VFALCO TODO Make OrderBookDB abstract
192 boost::asio::steady_timer sweepTimer_;
193 boost::asio::steady_timer entropyTimer_;
194
199
200 boost::asio::signal_set m_signals;
201
203
205
207
209
211
212 //--------------------------------------------------------------------------
213
214 static std::size_t
216 {
217#if XRPL_SINGLE_IO_SERVICE_THREAD
218 return 1;
219#else
220
221 if (config.IO_WORKERS > 0)
222 return config.IO_WORKERS;
223
224 auto const cores = std::thread::hardware_concurrency();
225
226 // Use a single thread when running on under-provisioned systems
227 // or if we are configured to use minimal resources.
228 if ((cores == 1) || ((config.NODE_SIZE == 0) && (cores == 2)))
229 return 1;
230
231 // Otherwise, prefer six threads.
232 return 6;
233#endif
234 }
235
236 //--------------------------------------------------------------------------
237
240 , config_(std::move(config))
241 , logs_(std::move(logs))
242 , timeKeeper_(std::move(timeKeeper))
244 , m_journal(logs_->journal("Application"))
245
246 // PerfLog must be started before any other threads are launched.
247 , perfLog_(perf::make_PerfLog(
248 perf::setup_PerfLog(config_->section("perf"), config_->CONFIG_DIR),
249 *this,
250 logs_->journal("PerfLog"),
251 [this] { signalStop("PerfLog"); }))
252
253 , m_txMaster(*this)
254
255 , m_collectorManager(make_CollectorManager(config_->section(SECTION_INSIGHT), logs_->journal("Collector")))
256
260 return 1;
261
262 if (config->WORKERS)
263 return config->WORKERS;
264
265 auto count = static_cast<int>(std::thread::hardware_concurrency());
266
267 // Be more aggressive about the number of threads to use
268 // for the job queue if the server is configured as
269 // "large" or "huge" if there are enough cores.
270 if (config->NODE_SIZE >= 4 && count >= 16)
271 count = 6 + std::min(count, 8);
272 else if (config->NODE_SIZE >= 3 && count >= 8)
273 count = 4 + std::min(count, 6);
274 else
275 count = 2 + std::min(count, 4);
276
277 return count;
278 }(config_),
279 m_collectorManager->group("jobq"),
280 logs_->journal("JobQueue"),
281 *logs_,
282 *perfLog_))
283
285
286 , m_shaMapStore(make_SHAMapStore(*this, m_nodeStoreScheduler, logs_->journal("SHAMapStore")))
287
288 , m_tempNodeCache("NodeCache", 16384, std::chrono::seconds{90}, stopwatch(), logs_->journal("TaggedCache"))
289
290 , cachedSLEs_("Cached SLEs", 0, std::chrono::minutes(1), stopwatch(), logs_->journal("CachedSLEs"))
291
293
294 , m_resourceManager(Resource::make_Manager(m_collectorManager->collector(), logs_->journal("Resource")))
295
296 , m_nodeStore(m_shaMapStore->makeNodeStore(config_->PREFETCH_WORKERS > 0 ? config_->PREFETCH_WORKERS : 4))
297
299
300 , m_orderBookDB(*this)
301
303 std::make_unique<PathRequests>(*this, logs_->journal("PathRequest"), m_collectorManager->collector()))
304
305 , m_ledgerMaster(std::make_unique<LedgerMaster>(
306 *this,
307 stopwatch(),
308 m_collectorManager->collector(),
309 logs_->journal("LedgerMaster")))
310
311 , ledgerCleaner_(make_LedgerCleaner(*this, logs_->journal("LedgerCleaner")))
312
313 // VFALCO NOTE must come before NetworkOPs to prevent a crash due
314 // to dependencies in the destructor.
315 //
317
319 *this,
320 m_collectorManager->collector(),
321 [this](std::shared_ptr<SHAMap> const& set, bool fromAcquire) { gotTXSet(set, fromAcquire); }))
322
324
326 "AcceptedLedger",
327 4,
329 stopwatch(),
330 logs_->journal("TaggedCache"))
331
333 *this,
334 stopwatch(),
335 config_->standalone(),
336 config_->NETWORK_QUORUM,
337 config_->START_VALID,
338 *m_jobQueue,
342 logs_->journal("NetworkOPs"),
343 m_collectorManager->collector()))
344
345 , cluster_(std::make_unique<Cluster>(logs_->journal("Overlay")))
346
347 , peerReservations_(std::make_unique<PeerReservationTable>(logs_->journal("PeerReservationTable")))
348
349 , validatorManifests_(std::make_unique<ManifestCache>(logs_->journal("ManifestCache")))
350
351 , publisherManifests_(std::make_unique<ManifestCache>(logs_->journal("ManifestCache")))
352
357 config_->legacy("database_path"),
358 logs_->journal("ValidatorList"),
359 config_->VALIDATION_QUORUM))
360
362
364 *this,
366 *m_jobQueue,
370
371 , mFeeTrack(std::make_unique<LoadFeeTrack>(logs_->journal("LoadManager")))
372
374
375 , mValidations(ValidationParms(), stopwatch(), *this, logs_->journal("Validations"))
376
377 , m_loadManager(make_LoadManager(*this, logs_->journal("LoadManager")))
378
379 , txQ_(std::make_unique<TxQ>(setup_TxQ(*config_), logs_->journal("TxQ")))
380
382
384
386
387 , checkSigs_(true)
388
389 , m_resolver(ResolverAsio::New(get_io_context(), logs_->journal("Resolver")))
390
392 m_collectorManager->collector()->make_event("ios_latency"),
393 logs_->journal("Application"),
397 {
399
400 add(m_resourceManager.get());
401
402 //
403 // VFALCO - READ THIS!
404 //
405 // Do not start threads, open sockets, or do any sort of "real work"
406 // inside the constructor. Put it in start instead. Or if you must,
407 // put it in setup (but everything in setup should be moved to start
408 // anyway.
409 //
410 // The reason is that the unit tests require an Application object to
411 // be created. But we don't actually start all the threads, sockets,
412 // and services when running the unit tests. Therefore anything which
413 // needs to be stopped will not get stopped correctly if it is
414 // started in this constructor.
415 //
416
417 add(ledgerCleaner_.get());
418 }
419
420 //--------------------------------------------------------------------------
421
422 bool
423 setup(boost::program_options::variables_map const& cmdline) override;
424 void
425 start(bool withTimers) override;
426 void
427 run() override;
428 void
429 signalStop(std::string msg) override;
430 bool
431 checkSigs() const override;
432 void
433 checkSigs(bool) override;
434 bool
435 isStopping() const override;
436 int
437 fdRequired() const override;
438
439 //--------------------------------------------------------------------------
440
442 instanceID() const override
443 {
444 return instanceCookie_;
445 }
446
447 Logs&
448 logs() override
449 {
450 return *logs_;
451 }
452
453 Config&
454 config() override
455 {
456 return *config_;
457 }
458
461 {
462 return *m_collectorManager;
463 }
464
465 Family&
466 getNodeFamily() override
467 {
468 return nodeFamily_;
469 }
470
472 timeKeeper() override
473 {
474 return *timeKeeper_;
475 }
476
477 JobQueue&
478 getJobQueue() override
479 {
480 return *m_jobQueue;
481 }
482
484 nodeIdentity() override
485 {
486 if (nodeIdentity_)
487 return *nodeIdentity_;
488
489 LogicError("Accessing Application::nodeIdentity() before it is initialized.");
490 }
491
493 getValidationPublicKey() const override
494 {
495 if (!validatorKeys_.keys)
496 return {};
497
498 return validatorKeys_.keys->publicKey;
499 }
500
502 getOPs() override
503 {
504 return *m_networkOPs;
505 }
506
507 virtual ServerHandler&
509 {
510 XRPL_ASSERT(
512 "xrpl::ApplicationImp::getServerHandler : non-null server "
513 "handle");
514 return *serverHandler_;
515 }
516
517 boost::asio::io_context&
518 getIOContext() override
519 {
520 return get_io_context();
521 }
522
524 getIOLatency() override
525 {
526 return m_io_latency_sampler.get();
527 }
528
531 {
532 return *m_ledgerMaster;
533 }
534
537 {
538 return *ledgerCleaner_;
539 }
540
543 {
544 return *m_ledgerReplayer;
545 }
546
549 {
550 return *m_inboundLedgers;
551 }
552
555 {
556 return *m_inboundTransactions;
557 }
558
561 {
563 }
564
565 void
566 gotTXSet(std::shared_ptr<SHAMap> const& set, bool fromAcquire)
567 {
568 if (set)
569 m_networkOPs->mapComplete(set, fromAcquire);
570 }
571
574 {
575 return m_txMaster;
576 }
577
579 getPerfLog() override
580 {
581 return *perfLog_;
582 }
583
584 NodeCache&
586 {
587 return m_tempNodeCache;
588 }
589
591 getNodeStore() override
592 {
593 return *m_nodeStore;
594 }
595
597 getMasterMutex() override
598 {
599 return m_masterMutex;
600 }
601
603 getLoadManager() override
604 {
605 return *m_loadManager;
606 }
607
610 {
611 return *m_resourceManager;
612 }
613
615 getOrderBookDB() override
616 {
617 return m_orderBookDB;
618 }
619
622 {
623 return *m_pathRequests;
624 }
625
627 cachedSLEs() override
628 {
629 return cachedSLEs_;
630 }
631
634 {
635 return *m_amendmentTable;
636 }
637
639 getFeeTrack() override
640 {
641 return *mFeeTrack;
642 }
643
645 getHashRouter() override
646 {
647 return *hashRouter_;
648 }
649
651 getValidations() override
652 {
653 return mValidations;
654 }
655
657 validators() override
658 {
659 return *validators_;
660 }
661
663 validatorSites() override
664 {
665 return *validatorSites_;
666 }
667
670 {
671 return *validatorManifests_;
672 }
673
676 {
677 return *publisherManifests_;
678 }
679
680 Cluster&
681 cluster() override
682 {
683 return *cluster_;
684 }
685
688 {
689 return *peerReservations_;
690 }
691
693 getSHAMapStore() override
694 {
695 return *m_shaMapStore;
696 }
697
699 pendingSaves() override
700 {
701 return pendingSaves_;
702 }
703
705 openLedger() override
706 {
707 return *openLedger_;
708 }
709
710 OpenLedger const&
711 openLedger() const override
712 {
713 return *openLedger_;
714 }
715
716 Overlay&
717 overlay() override
718 {
719 XRPL_ASSERT(overlay_, "xrpl::ApplicationImp::overlay : non-null overlay");
720 return *overlay_;
721 }
722
723 TxQ&
724 getTxQ() override
725 {
726 XRPL_ASSERT(txQ_, "xrpl::ApplicationImp::getTxQ : non-null transaction queue");
727 return *txQ_;
728 }
729
732 {
733 XRPL_ASSERT(
735 "xrpl::ApplicationImp::getRelationalDatabase : non-null "
736 "relational database");
737 return *mRelationalDatabase;
738 }
739
741 getWalletDB() override
742 {
743 XRPL_ASSERT(mWalletDB, "xrpl::ApplicationImp::getWalletDB : non-null wallet database");
744 return *mWalletDB;
745 }
746
747 bool
748 serverOkay(std::string& reason) override;
749
751 journal(std::string const& name) override;
752
753 //--------------------------------------------------------------------------
754
755 bool
757 {
758 XRPL_ASSERT(
759 mWalletDB.get() == nullptr,
760 "xrpl::ApplicationImp::initRelationalDatabase : null wallet "
761 "database");
762
763 try
764 {
766
767 // wallet database
769 setup.useGlobalPragma = false;
770
772 }
773 catch (std::exception const& e)
774 {
775 JLOG(m_journal.fatal()) << "Failed to initialize SQL databases: " << e.what();
776 return false;
777 }
778
779 return true;
780 }
781
782 bool
784 {
785 if (config_->doImport)
786 {
787 auto j = logs_->journal("NodeObject");
788 NodeStore::DummyScheduler dummyScheduler;
791 dummyScheduler,
792 0,
794 j);
795
796 JLOG(j.warn()) << "Starting node import from '" << source->getName() << "' to '" << m_nodeStore->getName()
797 << "'.";
798
799 using namespace std::chrono;
800 auto const start = steady_clock::now();
801
802 m_nodeStore->importDatabase(*source);
803
804 auto const elapsed = duration_cast<seconds>(steady_clock::now() - start);
805 JLOG(j.warn()) << "Node import from '" << source->getName() << "' took " << elapsed.count() << " seconds.";
806 }
807
808 return true;
809 }
810
811 //--------------------------------------------------------------------------
812 //
813 // PropertyStream
814 //
815
816 void
818 {
819 }
820
821 //--------------------------------------------------------------------------
822
823 void
825 {
826 // Only start the timer if waitHandlerCounter_ is not yet joined.
827 if (auto optionalCountedHandler = waitHandlerCounter_.wrap([this](boost::system::error_code const& e) {
828 if (e.value() == boost::system::errc::success)
829 {
830 m_jobQueue->addJob(jtSWEEP, "sweep", [this]() { doSweep(); });
831 }
832 // Recover as best we can if an unexpected error occurs.
833 if (e.value() != boost::system::errc::success && e.value() != boost::asio::error::operation_aborted)
834 {
835 // Try again later and hope for the best.
836 JLOG(m_journal.error()) << "Sweep timer got error '" << e.message() << "'. Restarting timer.";
837 setSweepTimer();
838 }
839 }))
840 {
841 using namespace std::chrono;
842 sweepTimer_.expires_after(
843 seconds{config_->SWEEP_INTERVAL.value_or(config_->getValueFor(SizedItem::sweepInterval))});
844 sweepTimer_.async_wait(std::move(*optionalCountedHandler));
845 }
846 }
847
848 void
850 {
851 // Only start the timer if waitHandlerCounter_ is not yet joined.
852 if (auto optionalCountedHandler = waitHandlerCounter_.wrap([this](boost::system::error_code const& e) {
853 if (e.value() == boost::system::errc::success)
854 {
855 crypto_prng().mix_entropy();
856 setEntropyTimer();
857 }
858 // Recover as best we can if an unexpected error occurs.
859 if (e.value() != boost::system::errc::success && e.value() != boost::asio::error::operation_aborted)
860 {
861 // Try again later and hope for the best.
862 JLOG(m_journal.error()) << "Entropy timer got error '" << e.message() << "'. Restarting timer.";
863 setEntropyTimer();
864 }
865 }))
866 {
867 using namespace std::chrono_literals;
868 entropyTimer_.expires_after(5min);
869 entropyTimer_.async_wait(std::move(*optionalCountedHandler));
870 }
871 }
872
873 void
875 {
876 if (!config_->standalone() && !getRelationalDatabase().transactionDbHasSpace(*config_))
877 {
878 signalStop("Out of transaction DB space");
879 }
880
881 // VFALCO NOTE Does the order of calls matter?
882 // VFALCO TODO fix the dependency inversion using an observer,
883 // have listeners register for "onSweep ()" notification.
884
885 {
886 std::shared_ptr<FullBelowCache const> const fullBelowCache = nodeFamily_.getFullBelowCache();
887
888 std::shared_ptr<TreeNodeCache const> const treeNodeCache = nodeFamily_.getTreeNodeCache();
889
890 std::size_t const oldFullBelowSize = fullBelowCache->size();
891 std::size_t const oldTreeNodeSize = treeNodeCache->size();
892
893 nodeFamily_.sweep();
894
895 JLOG(m_journal.debug()) << "NodeFamily::FullBelowCache sweep. Size before: " << oldFullBelowSize
896 << "; size after: " << fullBelowCache->size();
897
898 JLOG(m_journal.debug()) << "NodeFamily::TreeNodeCache sweep. Size before: " << oldTreeNodeSize
899 << "; size after: " << treeNodeCache->size();
900 }
901 {
902 TaggedCache<uint256, Transaction> const& masterTxCache = getMasterTransaction().getCache();
903
904 std::size_t const oldMasterTxSize = masterTxCache.size();
905
906 getMasterTransaction().sweep();
907
908 JLOG(m_journal.debug()) << "MasterTransaction sweep. Size before: " << oldMasterTxSize
909 << "; size after: " << masterTxCache.size();
910 }
911 {
912 std::size_t const oldLedgerMasterCacheSize = getLedgerMaster().getFetchPackCacheSize();
913
914 getLedgerMaster().sweep();
915
916 JLOG(m_journal.debug()) << "LedgerMaster sweep. Size before: " << oldLedgerMasterCacheSize
917 << "; size after: " << getLedgerMaster().getFetchPackCacheSize();
918 }
919 {
920 // NodeCache == TaggedCache<SHAMapHash, Blob>
921 std::size_t const oldTempNodeCacheSize = getTempNodeCache().size();
922
923 getTempNodeCache().sweep();
924
925 JLOG(m_journal.debug()) << "TempNodeCache sweep. Size before: " << oldTempNodeCacheSize
926 << "; size after: " << getTempNodeCache().size();
927 }
928 {
929 std::size_t const oldCurrentCacheSize = getValidations().sizeOfCurrentCache();
930 std::size_t const oldSizeSeqEnforcesSize = getValidations().sizeOfSeqEnforcersCache();
931 std::size_t const oldByLedgerSize = getValidations().sizeOfByLedgerCache();
932 std::size_t const oldBySequenceSize = getValidations().sizeOfBySequenceCache();
933
934 getValidations().expire(m_journal);
935
936 JLOG(m_journal.debug()) << "Validations Current expire. Size before: " << oldCurrentCacheSize
937 << "; size after: " << getValidations().sizeOfCurrentCache();
938
939 JLOG(m_journal.debug()) << "Validations SeqEnforcer expire. Size before: " << oldSizeSeqEnforcesSize
940 << "; size after: " << getValidations().sizeOfSeqEnforcersCache();
941
942 JLOG(m_journal.debug()) << "Validations ByLedger expire. Size before: " << oldByLedgerSize
943 << "; size after: " << getValidations().sizeOfByLedgerCache();
944
945 JLOG(m_journal.debug()) << "Validations BySequence expire. Size before: " << oldBySequenceSize
946 << "; size after: " << getValidations().sizeOfBySequenceCache();
947 }
948 {
949 std::size_t const oldInboundLedgersSize = getInboundLedgers().cacheSize();
950
951 getInboundLedgers().sweep();
952
953 JLOG(m_journal.debug()) << "InboundLedgers sweep. Size before: " << oldInboundLedgersSize
954 << "; size after: " << getInboundLedgers().cacheSize();
955 }
956 {
957 size_t const oldTasksSize = getLedgerReplayer().tasksSize();
958 size_t const oldDeltasSize = getLedgerReplayer().deltasSize();
959 size_t const oldSkipListsSize = getLedgerReplayer().skipListsSize();
960
961 getLedgerReplayer().sweep();
962
963 JLOG(m_journal.debug()) << "LedgerReplayer tasks sweep. Size before: " << oldTasksSize
964 << "; size after: " << getLedgerReplayer().tasksSize();
965
966 JLOG(m_journal.debug()) << "LedgerReplayer deltas sweep. Size before: " << oldDeltasSize
967 << "; size after: " << getLedgerReplayer().deltasSize();
968
969 JLOG(m_journal.debug()) << "LedgerReplayer skipLists sweep. Size before: " << oldSkipListsSize
970 << "; size after: " << getLedgerReplayer().skipListsSize();
971 }
972 {
973 std::size_t const oldAcceptedLedgerSize = m_acceptedLedgerCache.size();
974
975 m_acceptedLedgerCache.sweep();
976
977 JLOG(m_journal.debug()) << "AcceptedLedgerCache sweep. Size before: " << oldAcceptedLedgerSize
978 << "; size after: " << m_acceptedLedgerCache.size();
979 }
980 {
981 std::size_t const oldCachedSLEsSize = cachedSLEs_.size();
982
983 cachedSLEs_.sweep();
984
985 JLOG(m_journal.debug()) << "CachedSLEs sweep. Size before: " << oldCachedSLEsSize
986 << "; size after: " << cachedSLEs_.size();
987 }
988
989 // Set timer to do another sweep later.
990 setSweepTimer();
991 }
992
995 {
996 return maxDisallowedLedger_;
997 }
998
999 virtual std::optional<uint256> const&
1000 trapTxID() const override
1001 {
1002 return trapTxID_;
1003 }
1004
1005private:
1006 // For a newly-started validator, this is the greatest persisted ledger
1007 // and new validations must be greater than this.
1008 std::atomic<LedgerIndex> maxDisallowedLedger_{0};
1009
1010 void
1011 startGenesisLedger();
1012
1014 getLastFullLedger();
1015
1017 loadLedgerFromFile(std::string const& ledgerID);
1018
1019 bool
1020 loadOldLedger(std::string const& ledgerID, bool replay, bool isFilename, std::optional<uint256> trapTxID);
1021
1022 void
1023 setMaxDisallowedLedger();
1024};
1025
1026//------------------------------------------------------------------------------
1027
1028// TODO Break this up into smaller, more digestible initialization segments.
1029bool
1030ApplicationImp::setup(boost::program_options::variables_map const& cmdline)
1031{
1032 // We want to intercept CTRL-C and the standard termination signal SIGTERM
1033 // and terminate the process. This handler will NEVER be invoked twice.
1034 //
1035 // Note that async_wait is "one-shot": for each call, the handler will be
1036 // invoked exactly once, either when one of the registered signals in the
1037 // signal set occurs or the signal set is cancelled. Subsequent signals are
1038 // effectively ignored (technically, they are queued up, waiting for a call
1039 // to async_wait).
1040 m_signals.add(SIGINT);
1041 m_signals.add(SIGTERM);
1042 m_signals.async_wait([this](boost::system::error_code const& ec, int signum) {
1043 // Indicates the signal handler has been aborted; do nothing
1044 if (ec == boost::asio::error::operation_aborted)
1045 return;
1046
1047 JLOG(m_journal.info()) << "Received signal " << signum;
1048
1049 if (signum == SIGTERM || signum == SIGINT)
1050 signalStop("Signal: " + to_string(signum));
1051 });
1052
1053 auto debug_log = config_->getDebugLogFile();
1054
1055 if (!debug_log.empty())
1056 {
1057 // Let debug messages go to the file but only WARNING or higher to
1058 // regular output (unless verbose)
1059
1060 if (!logs_->open(debug_log))
1061 std::cerr << "Can't open log file " << debug_log << '\n';
1062
1063 using namespace beast::severities;
1064 if (logs_->threshold() > kDebug)
1065 logs_->threshold(kDebug);
1066 }
1067
1068 JLOG(m_journal.info()) << "Process starting: " << BuildInfo::getFullVersionString()
1069 << ", Instance Cookie: " << instanceCookie_;
1070
1071 if (numberOfThreads(*config_) < 2)
1072 {
1073 JLOG(m_journal.warn()) << "Limited to a single I/O service thread by "
1074 "system configuration.";
1075 }
1076
1077 // Optionally turn off logging to console.
1078 logs_->silent(config_->silent());
1079
1080 if (!initRelationalDatabase() || !initNodeStore())
1081 return false;
1082
1083 if (!peerReservations_->load(getWalletDB()))
1084 {
1085 JLOG(m_journal.fatal()) << "Cannot find peer reservations!";
1086 return false;
1087 }
1088
1089 if (validatorKeys_.keys)
1090 setMaxDisallowedLedger();
1091
1092 // Configure the amendments the server supports
1093 {
1094 auto const supported = []() {
1095 auto const& amendments = detail::supportedAmendments();
1097 supported.reserve(amendments.size());
1098 for (auto const& [a, vote] : amendments)
1099 {
1100 auto const f = xrpl::getRegisteredFeature(a);
1101 XRPL_ASSERT(f, "xrpl::ApplicationImp::setup : registered feature");
1102 if (f)
1103 supported.emplace_back(a, *f, vote);
1104 }
1105 return supported;
1106 }();
1107 Section const& downVoted = config_->section(SECTION_VETO_AMENDMENTS);
1108
1109 Section const& upVoted = config_->section(SECTION_AMENDMENTS);
1110
1111 m_amendmentTable = make_AmendmentTable(
1112 *this, config().AMENDMENT_MAJORITY_TIME, supported, upVoted, downVoted, logs_->journal("Amendments"));
1113 }
1114
1115 Pathfinder::initPathTable();
1116
1117 auto const startUp = config_->START_UP;
1118 JLOG(m_journal.debug()) << "startUp: " << startUp;
1119 if (startUp == Config::FRESH)
1120 {
1121 JLOG(m_journal.info()) << "Starting new Ledger";
1122
1123 startGenesisLedger();
1124 }
1125 else if (startUp == Config::LOAD || startUp == Config::LOAD_FILE || startUp == Config::REPLAY)
1126 {
1127 JLOG(m_journal.info()) << "Loading specified Ledger";
1128
1129 if (!loadOldLedger(
1130 config_->START_LEDGER, startUp == Config::REPLAY, startUp == Config::LOAD_FILE, config_->TRAP_TX_HASH))
1131 {
1132 JLOG(m_journal.error()) << "The specified ledger could not be loaded.";
1133 if (config_->FAST_LOAD)
1134 {
1135 // Fall back to syncing from the network, such as
1136 // when there's no existing data.
1137 startGenesisLedger();
1138 }
1139 else
1140 {
1141 return false;
1142 }
1143 }
1144 }
1145 else if (startUp == Config::NETWORK)
1146 {
1147 // This should probably become the default once we have a stable
1148 // network.
1149 if (!config_->standalone())
1150 m_networkOPs->setNeedNetworkLedger();
1151
1152 startGenesisLedger();
1153 }
1154 else
1155 {
1156 startGenesisLedger();
1157 }
1158
1159 if (auto const& forcedRange = config().FORCED_LEDGER_RANGE_PRESENT)
1160 {
1161 m_ledgerMaster->setLedgerRangePresent(forcedRange->first, forcedRange->second);
1162 }
1163
1164 m_orderBookDB.setup(getLedgerMaster().getCurrentLedger());
1165
1166 nodeIdentity_ = getNodeIdentity(*this, cmdline);
1167
1168 if (!cluster_->load(config().section(SECTION_CLUSTER_NODES)))
1169 {
1170 JLOG(m_journal.fatal()) << "Invalid entry in cluster configuration.";
1171 return false;
1172 }
1173
1174 {
1175 if (validatorKeys_.configInvalid())
1176 return false;
1177
1178 if (!validatorManifests_->load(
1179 getWalletDB(),
1180 "ValidatorManifests",
1181 validatorKeys_.manifest,
1182 config().section(SECTION_VALIDATOR_KEY_REVOCATION).values()))
1183 {
1184 JLOG(m_journal.fatal()) << "Invalid configured validator manifest.";
1185 return false;
1186 }
1187
1188 publisherManifests_->load(getWalletDB(), "PublisherManifests");
1189
1190 // It is possible to have a valid ValidatorKeys object without
1191 // setting the signingKey or masterKey. This occurs if the
1192 // configuration file does not have either
1193 // SECTION_VALIDATOR_TOKEN or SECTION_VALIDATION_SEED section.
1194
1195 // masterKey for the configuration-file specified validator keys
1196 std::optional<PublicKey> localSigningKey;
1197 if (validatorKeys_.keys)
1198 localSigningKey = validatorKeys_.keys->publicKey;
1199
1200 // Setup trusted validators
1201 if (!validators_->load(
1202 localSigningKey,
1203 config().section(SECTION_VALIDATORS).values(),
1204 config().section(SECTION_VALIDATOR_LIST_KEYS).values(),
1205 config().VALIDATOR_LIST_THRESHOLD))
1206 {
1207 JLOG(m_journal.fatal()) << "Invalid entry in validator configuration.";
1208 return false;
1209 }
1210 }
1211
1212 if (!validatorSites_->load(config().section(SECTION_VALIDATOR_LIST_SITES).values()))
1213 {
1214 JLOG(m_journal.fatal()) << "Invalid entry in [" << SECTION_VALIDATOR_LIST_SITES << "]";
1215 return false;
1216 }
1217
1218 // Tell the AmendmentTable who the trusted validators are.
1219 m_amendmentTable->trustChanged(validators_->getQuorumKeys().second);
1220
1221 //----------------------------------------------------------------------
1222 //
1223 // Server
1224 //
1225 //----------------------------------------------------------------------
1226
1227 // VFALCO NOTE Unfortunately, in stand-alone mode some code still
1228 // foolishly calls overlay(). When this is fixed we can
1229 // move the instantiation inside a conditional:
1230 //
1231 // if (!config_.standalone())
1232 overlay_ = make_Overlay(
1233 *this,
1234 setup_Overlay(*config_),
1235 *serverHandler_,
1236 *m_resourceManager,
1237 *m_resolver,
1238 get_io_context(),
1239 *config_,
1240 m_collectorManager->collector());
1241 add(*overlay_); // add to PropertyStream
1242
1243 // start first consensus round
1244 if (!m_networkOPs->beginConsensus(m_ledgerMaster->getClosedLedger()->header().hash, {}))
1245 {
1246 JLOG(m_journal.fatal()) << "Unable to start consensus";
1247 return false;
1248 }
1249
1250 {
1251 try
1252 {
1253 auto setup = setup_ServerHandler(*config_, beast::logstream{m_journal.error()});
1254 setup.makeContexts();
1255 serverHandler_->setup(setup, m_journal);
1256 fixConfigPorts(*config_, serverHandler_->endpoints());
1257 }
1258 catch (std::exception const& e)
1259 {
1260 if (auto stream = m_journal.fatal())
1261 {
1262 stream << "Unable to setup server handler";
1263 if (std::strlen(e.what()) > 0)
1264 stream << ": " << e.what();
1265 }
1266 return false;
1267 }
1268 }
1269
1270 // Begin connecting to network.
1271 if (!config_->standalone())
1272 {
1273 // Should this message be here, conceptually? In theory this sort
1274 // of message, if displayed, should be displayed from PeerFinder.
1275 if (config_->PEER_PRIVATE && config_->IPS_FIXED.empty())
1276 {
1277 JLOG(m_journal.warn()) << "No outbound peer connections will be made";
1278 }
1279
1280 // VFALCO NOTE the state timer resets the deadlock detector.
1281 //
1282 m_networkOPs->setStateTimer();
1283 }
1284 else
1285 {
1286 JLOG(m_journal.warn()) << "Running in standalone mode";
1287
1288 m_networkOPs->setStandAlone();
1289 }
1290
1291 if (config_->canSign())
1292 {
1293 JLOG(m_journal.warn()) << "*** The server is configured to allow the "
1294 "'sign' and 'sign_for'";
1295 JLOG(m_journal.warn()) << "*** commands. These commands have security "
1296 "implications and have";
1297 JLOG(m_journal.warn()) << "*** been deprecated. They will be removed "
1298 "in a future release of";
1299 JLOG(m_journal.warn()) << "*** rippled.";
1300 JLOG(m_journal.warn()) << "*** If you do not use them to sign "
1301 "transactions please edit your";
1302 JLOG(m_journal.warn()) << "*** configuration file and remove the [enable_signing] stanza.";
1303 JLOG(m_journal.warn()) << "*** If you do use them to sign transactions "
1304 "please migrate to a";
1305 JLOG(m_journal.warn()) << "*** standalone signing solution as soon as possible.";
1306 }
1307
1308 //
1309 // Execute start up rpc commands.
1310 //
1311 for (auto cmd : config_->section(SECTION_RPC_STARTUP).lines())
1312 {
1313 Json::Reader jrReader;
1314 Json::Value jvCommand;
1315
1316 if (!jrReader.parse(cmd, jvCommand))
1317 {
1318 JLOG(m_journal.fatal()) << "Couldn't parse entry in [" << SECTION_RPC_STARTUP << "]: '" << cmd;
1319 }
1320
1321 if (!config_->quiet())
1322 {
1323 JLOG(m_journal.fatal()) << "Startup RPC: " << jvCommand << std::endl;
1324 }
1325
1326 Resource::Charge loadType = Resource::feeReferenceRPC;
1328 RPC::JsonContext context{
1329 {journal("RPCHandler"),
1330 *this,
1331 loadType,
1332 getOPs(),
1333 getLedgerMaster(),
1334 c,
1335 Role::ADMIN,
1336 {},
1337 {},
1338 RPC::apiMaximumSupportedVersion},
1339 jvCommand};
1340
1341 Json::Value jvResult;
1342 RPC::doCommand(context, jvResult);
1343
1344 if (!config_->quiet())
1345 {
1346 JLOG(m_journal.fatal()) << "Result: " << jvResult << std::endl;
1347 }
1348 }
1349
1350 validatorSites_->start();
1351
1352 return true;
1353}
1354
1355void
1356ApplicationImp::start(bool withTimers)
1357{
1358 JLOG(m_journal.info()) << "Application starting. Version is " << BuildInfo::getVersionString();
1359
1360 if (withTimers)
1361 {
1362 setSweepTimer();
1363 setEntropyTimer();
1364 }
1365
1366 m_io_latency_sampler.start();
1367 m_resolver->start();
1368 m_loadManager->start();
1369 m_shaMapStore->start();
1370 if (overlay_)
1371 overlay_->start();
1372
1373 if (grpcServer_->start())
1374 fixConfigPorts(*config_, {{SECTION_PORT_GRPC, grpcServer_->getEndpoint()}});
1375
1376 ledgerCleaner_->start();
1377 perfLog_->start();
1378}
1379
1380void
1381ApplicationImp::run()
1382{
1383 if (!config_->standalone())
1384 {
1385 // VFALCO NOTE This seems unnecessary. If we properly refactor the load
1386 // manager then the stall detector can just always be
1387 // "armed"
1388 //
1389 getLoadManager().activateStallDetector();
1390 }
1391
1392 isTimeToStop.wait(false, std::memory_order_relaxed);
1393
1394 JLOG(m_journal.debug()) << "Application stopping";
1395
1396 m_io_latency_sampler.cancel_async();
1397
1398 // VFALCO Enormous hack, we have to force the probe to cancel
1399 // before we stop the io_context queue or else it never
1400 // unblocks in its destructor. The fix is to make all
1401 // io_objects gracefully handle exit so that we can
1402 // naturally return from io_context::run() instead of
1403 // forcing a call to io_context::stop()
1404 m_io_latency_sampler.cancel();
1405
1406 m_resolver->stop_async();
1407
1408 // NIKB This is a hack - we need to wait for the resolver to
1409 // stop. before we stop the io_server_queue or weird
1410 // things will happen.
1411 m_resolver->stop();
1412
1413 {
1414 try
1415 {
1416 sweepTimer_.cancel();
1417 }
1418 catch (boost::system::system_error const& e)
1419 {
1420 JLOG(m_journal.error()) << "Application: sweepTimer cancel error: " << e.what();
1421 }
1422
1423 try
1424 {
1425 entropyTimer_.cancel();
1426 }
1427 catch (boost::system::system_error const& e)
1428 {
1429 JLOG(m_journal.error()) << "Application: entropyTimer cancel error: " << e.what();
1430 }
1431 }
1432
1433 // Make sure that any waitHandlers pending in our timers are done
1434 // before we declare ourselves stopped.
1435 using namespace std::chrono_literals;
1436
1437 waitHandlerCounter_.join("Application", 1s, m_journal);
1438
1439 mValidations.flush();
1440
1441 validatorSites_->stop();
1442
1443 // TODO Store manifests in manifests.sqlite instead of wallet.db
1444 validatorManifests_->save(
1445 getWalletDB(), "ValidatorManifests", [this](PublicKey const& pubKey) { return validators().listed(pubKey); });
1446
1447 publisherManifests_->save(getWalletDB(), "PublisherManifests", [this](PublicKey const& pubKey) {
1448 return validators().trustedPublisher(pubKey);
1449 });
1450
1451 // The order of these stop calls is delicate.
1452 // Re-ordering them risks undefined behavior.
1453 m_loadManager->stop();
1454 m_shaMapStore->stop();
1455 m_jobQueue->stop();
1456 if (overlay_)
1457 overlay_->stop();
1458 grpcServer_->stop();
1459 m_networkOPs->stop();
1460 serverHandler_->stop();
1461 m_ledgerReplayer->stop();
1462 m_inboundTransactions->stop();
1463 m_inboundLedgers->stop();
1464 ledgerCleaner_->stop();
1465 m_nodeStore->stop();
1466 perfLog_->stop();
1467
1468 JLOG(m_journal.info()) << "Done.";
1469}
1470
1471void
1472ApplicationImp::signalStop(std::string msg)
1473{
1474 if (!isTimeToStop.test_and_set(std::memory_order_acquire))
1475 {
1476 if (msg.empty())
1477 JLOG(m_journal.warn()) << "Server stopping";
1478 else
1479 JLOG(m_journal.warn()) << "Server stopping: " << msg;
1480
1481 isTimeToStop.notify_all();
1482 }
1483}
1484
1485bool
1486ApplicationImp::checkSigs() const
1487{
1488 return checkSigs_;
1489}
1490
1491void
1492ApplicationImp::checkSigs(bool check)
1493{
1494 checkSigs_ = check;
1495}
1496
1497bool
1498ApplicationImp::isStopping() const
1499{
1500 return isTimeToStop.test(std::memory_order_relaxed);
1501}
1502
1503int
1504ApplicationImp::fdRequired() const
1505{
1506 // Standard handles, config file, misc I/O etc:
1507 int needed = 128;
1508
1509 // 2x the configured peer limit for peer connections:
1510 if (overlay_)
1511 needed += 2 * overlay_->limit();
1512
1513 // the number of fds needed by the backend (internally
1514 // doubled if online delete is enabled).
1515 needed += std::max(5, m_shaMapStore->fdRequired());
1516
1517 // One fd per incoming connection a port can accept, or
1518 // if no limit is set, assume it'll handle 256 clients.
1519 for (auto const& p : serverHandler_->setup().ports)
1520 needed += std::max(256, p.limit);
1521
1522 // The minimum number of file descriptors we need is 1024:
1523 return std::max(1024, needed);
1524}
1525
1526//------------------------------------------------------------------------------
1527
1528void
1529ApplicationImp::startGenesisLedger()
1530{
1531 std::vector<uint256> const initialAmendments =
1532 (config_->START_UP == Config::FRESH) ? m_amendmentTable->getDesired() : std::vector<uint256>{};
1533
1534 std::shared_ptr<Ledger> const genesis =
1535 std::make_shared<Ledger>(create_genesis, *config_, initialAmendments, nodeFamily_);
1536 m_ledgerMaster->storeLedger(genesis);
1537
1538 auto const next = std::make_shared<Ledger>(*genesis, timeKeeper().closeTime());
1539 next->updateSkipList();
1540 XRPL_ASSERT(
1541 next->header().seq < XRP_LEDGER_EARLIEST_FEES || next->read(keylet::fees()),
1542 "xrpl::ApplicationImp::startGenesisLedger : valid ledger fees");
1543 next->setImmutable();
1544 openLedger_.emplace(next, cachedSLEs_, logs_->journal("OpenLedger"));
1545 m_ledgerMaster->storeLedger(next);
1546 m_ledgerMaster->switchLCL(next);
1547}
1548
1550ApplicationImp::getLastFullLedger()
1551{
1552 auto j = journal("Ledger");
1553
1554 try
1555 {
1556 auto const [ledger, seq, hash] = getLatestLedger(*this);
1557
1558 if (!ledger)
1559 return ledger;
1560
1561 XRPL_ASSERT(
1562 ledger->header().seq < XRP_LEDGER_EARLIEST_FEES || ledger->read(keylet::fees()),
1563 "xrpl::ApplicationImp::getLastFullLedger : valid ledger fees");
1564 ledger->setImmutable();
1565
1566 if (getLedgerMaster().haveLedger(seq))
1567 ledger->setValidated();
1568
1569 if (ledger->header().hash == hash)
1570 {
1571 JLOG(j.trace()) << "Loaded ledger: " << hash;
1572 return ledger;
1573 }
1574
1575 if (auto stream = j.error())
1576 {
1577 stream << "Failed on ledger";
1578 Json::Value p;
1579 addJson(p, {*ledger, nullptr, LedgerFill::full});
1580 stream << p;
1581 }
1582
1583 return {};
1584 }
1585 catch (SHAMapMissingNode const& mn)
1586 {
1587 JLOG(j.warn()) << "Ledger in database: " << mn.what();
1588 return {};
1589 }
1590}
1591
1593ApplicationImp::loadLedgerFromFile(std::string const& name)
1594{
1595 try
1596 {
1597 std::ifstream ledgerFile(name, std::ios::in);
1598
1599 if (!ledgerFile)
1600 {
1601 JLOG(m_journal.fatal()) << "Unable to open file '" << name << "'";
1602 return nullptr;
1603 }
1604
1605 Json::Reader reader;
1606 Json::Value jLedger;
1607
1608 if (!reader.parse(ledgerFile, jLedger))
1609 {
1610 JLOG(m_journal.fatal()) << "Unable to parse ledger JSON";
1611 return nullptr;
1612 }
1613
1615
1616 // accept a wrapped ledger
1617 if (ledger.get().isMember("result"))
1618 ledger = ledger.get()["result"];
1619
1620 if (ledger.get().isMember("ledger"))
1621 ledger = ledger.get()["ledger"];
1622
1623 std::uint32_t seq = 1;
1624 auto closeTime = timeKeeper().closeTime();
1625 using namespace std::chrono_literals;
1626 auto closeTimeResolution = 30s;
1627 bool closeTimeEstimated = false;
1628 std::uint64_t totalDrops = 0;
1629
1630 if (ledger.get().isMember("accountState"))
1631 {
1632 if (ledger.get().isMember(jss::ledger_index))
1633 {
1634 seq = ledger.get()[jss::ledger_index].asUInt();
1635 }
1636
1637 if (ledger.get().isMember("close_time"))
1638 {
1639 using tp = NetClock::time_point;
1640 using d = tp::duration;
1641 closeTime = tp{d{ledger.get()["close_time"].asUInt()}};
1642 }
1643 if (ledger.get().isMember("close_time_resolution"))
1644 {
1645 using namespace std::chrono;
1646 closeTimeResolution = seconds{ledger.get()["close_time_resolution"].asUInt()};
1647 }
1648 if (ledger.get().isMember("close_time_estimated"))
1649 {
1650 closeTimeEstimated = ledger.get()["close_time_estimated"].asBool();
1651 }
1652 if (ledger.get().isMember("total_coins"))
1653 {
1654 totalDrops = beast::lexicalCastThrow<std::uint64_t>(ledger.get()["total_coins"].asString());
1655 }
1656
1657 ledger = ledger.get()["accountState"];
1658 }
1659
1660 if (!ledger.get().isArrayOrNull())
1661 {
1662 JLOG(m_journal.fatal()) << "State nodes must be an array";
1663 return nullptr;
1664 }
1665
1666 auto loadLedger = std::make_shared<Ledger>(seq, closeTime, *config_, nodeFamily_);
1667 loadLedger->setTotalDrops(totalDrops);
1668
1669 for (Json::UInt index = 0; index < ledger.get().size(); ++index)
1670 {
1671 Json::Value& entry = ledger.get()[index];
1672
1673 if (!entry.isObjectOrNull())
1674 {
1675 JLOG(m_journal.fatal()) << "Invalid entry in ledger";
1676 return nullptr;
1677 }
1678
1679 uint256 uIndex;
1680
1681 if (!uIndex.parseHex(entry[jss::index].asString()))
1682 {
1683 JLOG(m_journal.fatal()) << "Invalid entry in ledger";
1684 return nullptr;
1685 }
1686
1687 entry.removeMember(jss::index);
1688
1689 STParsedJSONObject stp("sle", ledger.get()[index]);
1690
1691 if (!stp.object || uIndex.isZero())
1692 {
1693 JLOG(m_journal.fatal()) << "Invalid entry in ledger";
1694 return nullptr;
1695 }
1696
1697 // VFALCO TODO This is the only place that
1698 // constructor is used, try to remove it
1699 STLedgerEntry sle(*stp.object, uIndex);
1700
1701 if (!loadLedger->addSLE(sle))
1702 {
1703 JLOG(m_journal.fatal()) << "Couldn't add serialized ledger: " << uIndex;
1704 return nullptr;
1705 }
1706 }
1707
1708 loadLedger->stateMap().flushDirty(hotACCOUNT_NODE);
1709
1710 XRPL_ASSERT(
1711 loadLedger->header().seq < XRP_LEDGER_EARLIEST_FEES || loadLedger->read(keylet::fees()),
1712 "xrpl::ApplicationImp::loadLedgerFromFile : valid ledger fees");
1713 loadLedger->setAccepted(closeTime, closeTimeResolution, !closeTimeEstimated);
1714
1715 return loadLedger;
1716 }
1717 catch (std::exception const& x)
1718 {
1719 JLOG(m_journal.fatal()) << "Ledger contains invalid data: " << x.what();
1720 return nullptr;
1721 }
1722}
1723
1724bool
1725ApplicationImp::loadOldLedger(
1726 std::string const& ledgerID,
1727 bool replay,
1728 bool isFileName,
1729 std::optional<uint256> trapTxID)
1730{
1731 try
1732 {
1733 std::shared_ptr<Ledger const> loadLedger, replayLedger;
1734
1735 if (isFileName)
1736 {
1737 if (!ledgerID.empty())
1738 loadLedger = loadLedgerFromFile(ledgerID);
1739 }
1740 else if (ledgerID.length() == 64)
1741 {
1742 uint256 hash;
1743
1744 if (hash.parseHex(ledgerID))
1745 {
1746 loadLedger = loadByHash(hash, *this);
1747
1748 if (!loadLedger)
1749 {
1750 // Try to build the ledger from the back end
1752 *this, hash, 0, InboundLedger::Reason::GENERIC, stopwatch(), make_DummyPeerSet(*this));
1753 if (il->checkLocal())
1754 loadLedger = il->getLedger();
1755 }
1756 }
1757 }
1758 else if (ledgerID.empty() || boost::iequals(ledgerID, "latest"))
1759 {
1760 loadLedger = getLastFullLedger();
1761 }
1762 else
1763 {
1764 // assume by sequence
1765 std::uint32_t index;
1766
1767 if (beast::lexicalCastChecked(index, ledgerID))
1768 loadLedger = loadByIndex(index, *this);
1769 }
1770
1771 if (!loadLedger)
1772 return false;
1773
1774 if (replay)
1775 {
1776 // Replay a ledger close with same prior ledger and transactions
1777
1778 // this ledger holds the transactions we want to replay
1779 replayLedger = loadLedger;
1780
1781 JLOG(m_journal.info()) << "Loading parent ledger";
1782
1783 loadLedger = loadByHash(replayLedger->header().parentHash, *this);
1784 if (!loadLedger)
1785 {
1786 JLOG(m_journal.info()) << "Loading parent ledger from node store";
1787
1788 // Try to build the ledger from the back end
1790 *this,
1791 replayLedger->header().parentHash,
1792 0,
1793 InboundLedger::Reason::GENERIC,
1794 stopwatch(),
1795 make_DummyPeerSet(*this));
1796
1797 if (il->checkLocal())
1798 loadLedger = il->getLedger();
1799
1800 if (!loadLedger)
1801 {
1802 // LCOV_EXCL_START
1803 JLOG(m_journal.fatal()) << "Replay ledger missing/damaged";
1804 UNREACHABLE(
1805 "xrpl::ApplicationImp::loadOldLedger : replay ledger "
1806 "missing/damaged");
1807 return false;
1808 // LCOV_EXCL_STOP
1809 }
1810 }
1811 }
1812 using namespace std::chrono_literals;
1813 using namespace date;
1814 static constexpr NetClock::time_point ledgerWarnTimePoint{
1815 sys_days{January / 1 / 2018} - sys_days{January / 1 / 2000}};
1816 if (loadLedger->header().closeTime < ledgerWarnTimePoint)
1817 {
1818 JLOG(m_journal.fatal()) << "\n\n*** WARNING ***\n"
1819 "You are replaying a ledger from before "
1820 << to_string(ledgerWarnTimePoint)
1821 << " UTC.\n"
1822 "This replay will not handle your ledger as it was "
1823 "originally "
1824 "handled.\nConsider running an earlier version of rippled "
1825 "to "
1826 "get the older rules.\n*** CONTINUING ***\n";
1827 }
1828
1829 JLOG(m_journal.info()) << "Loading ledger " << loadLedger->header().hash << " seq:" << loadLedger->header().seq;
1830
1831 if (loadLedger->header().accountHash.isZero())
1832 {
1833 // LCOV_EXCL_START
1834 JLOG(m_journal.fatal()) << "Ledger is empty.";
1835 UNREACHABLE("xrpl::ApplicationImp::loadOldLedger : ledger is empty");
1836 return false;
1837 // LCOV_EXCL_STOP
1838 }
1839
1840 if (!loadLedger->walkLedger(journal("Ledger"), true))
1841 {
1842 // LCOV_EXCL_START
1843 JLOG(m_journal.fatal()) << "Ledger is missing nodes.";
1844 UNREACHABLE(
1845 "xrpl::ApplicationImp::loadOldLedger : ledger is missing "
1846 "nodes");
1847 return false;
1848 // LCOV_EXCL_STOP
1849 }
1850
1851 if (!loadLedger->assertSensible(journal("Ledger")))
1852 {
1853 // LCOV_EXCL_START
1854 JLOG(m_journal.fatal()) << "Ledger is not sensible.";
1855 UNREACHABLE(
1856 "xrpl::ApplicationImp::loadOldLedger : ledger is not "
1857 "sensible");
1858 return false;
1859 // LCOV_EXCL_STOP
1860 }
1861
1862 m_ledgerMaster->setLedgerRangePresent(loadLedger->header().seq, loadLedger->header().seq);
1863
1864 m_ledgerMaster->switchLCL(loadLedger);
1865 loadLedger->setValidated();
1866 m_ledgerMaster->setFullLedger(loadLedger, true, false);
1867 openLedger_.emplace(loadLedger, cachedSLEs_, logs_->journal("OpenLedger"));
1868
1869 if (replay)
1870 {
1871 // inject transaction(s) from the replayLedger into our open ledger
1872 // and build replay structure
1873 auto replayData = std::make_unique<LedgerReplay>(loadLedger, replayLedger);
1874
1875 for (auto const& [_, tx] : replayData->orderedTxns())
1876 {
1877 (void)_;
1878 auto txID = tx->getTransactionID();
1879 if (trapTxID == txID)
1880 {
1881 trapTxID_ = txID;
1882 JLOG(m_journal.debug()) << "Trap transaction set: " << txID;
1883 }
1884
1886 tx->add(*s);
1887
1888 forceValidity(getHashRouter(), txID, Validity::SigGoodOnly);
1889
1890 openLedger_->modify([&txID, &s](OpenView& view, beast::Journal j) {
1891 view.rawTxInsert(txID, std::move(s), nullptr);
1892 return true;
1893 });
1894 }
1895
1896 m_ledgerMaster->takeReplay(std::move(replayData));
1897
1898 if (trapTxID && !trapTxID_)
1899 {
1900 JLOG(m_journal.fatal()) << "Ledger " << replayLedger->header().seq
1901 << " does not contain the transaction hash " << *trapTxID;
1902 return false;
1903 }
1904 }
1905 }
1906 catch (SHAMapMissingNode const& mn)
1907 {
1908 JLOG(m_journal.fatal()) << "While loading specified ledger: " << mn.what();
1909 return false;
1910 }
1911 catch (boost::bad_lexical_cast&)
1912 {
1913 JLOG(m_journal.fatal()) << "Ledger specified '" << ledgerID << "' is not valid";
1914 return false;
1915 }
1916
1917 return true;
1918}
1919
1920bool
1921ApplicationImp::serverOkay(std::string& reason)
1922{
1923 if (!config().ELB_SUPPORT)
1924 return true;
1925
1926 if (isStopping())
1927 {
1928 reason = "Server is shutting down";
1929 return false;
1930 }
1931
1932 if (getOPs().isNeedNetworkLedger())
1933 {
1934 reason = "Not synchronized with network yet";
1935 return false;
1936 }
1937
1938 if (getOPs().isAmendmentBlocked())
1939 {
1940 reason = "Server version too old";
1941 return false;
1942 }
1943
1944 if (getOPs().isUNLBlocked())
1945 {
1946 reason = "No valid validator list available";
1947 return false;
1948 }
1949
1950 if (getOPs().getOperatingMode() < OperatingMode::SYNCING)
1951 {
1952 reason = "Not synchronized with network";
1953 return false;
1954 }
1955
1956 if (!getLedgerMaster().isCaughtUp(reason))
1957 return false;
1958
1959 if (getFeeTrack().isLoadedLocal())
1960 {
1961 reason = "Too much load";
1962 return false;
1963 }
1964
1965 return true;
1966}
1967
1969ApplicationImp::journal(std::string const& name)
1970{
1971 return logs_->journal(name);
1972}
1973
1974void
1975ApplicationImp::setMaxDisallowedLedger()
1976{
1977 auto seq = getRelationalDatabase().getMaxLedgerSeq();
1978 if (seq)
1979 maxDisallowedLedger_ = *seq;
1980
1981 JLOG(m_journal.trace()) << "Max persisted ledger is " << maxDisallowedLedger_;
1982}
1983
1984//------------------------------------------------------------------------------
1985
1986Application::Application() : beast::PropertyStream::Source("app")
1987{
1988}
1989
1990//------------------------------------------------------------------------------
1991
1994{
1995 return std::make_unique<ApplicationImp>(std::move(config), std::move(logs), std::move(timeKeeper));
1996}
1997
1998void
1999fixConfigPorts(Config& config, Endpoints const& endpoints)
2000{
2001 for (auto const& [name, ep] : endpoints)
2002 {
2003 if (!config.exists(name))
2004 continue;
2005
2006 auto& section = config[name];
2007 auto const optPort = section.get("port");
2008 if (optPort)
2009 {
2010 std::uint16_t const port = beast::lexicalCast<std::uint16_t>(*optPort);
2011 if (!port)
2012 section.set("port", std::to_string(ep.port()));
2013 }
2014 }
2015}
2016
2017} // namespace xrpl
boost::asio::io_context & get_io_context()
Definition BasicApp.h:22
Unserialize a JSON document into a Value.
Definition json_reader.h:17
bool parse(std::string const &document, Value &root)
Read a Value from a JSON document.
Represents a JSON value.
Definition json_value.h:130
bool isObjectOrNull() const
Value removeMember(char const *key)
Remove and return the named member.
std::string asString() const
Returns the unquoted string value.
A generic endpoint for log messages.
Definition Journal.h:40
Stream fatal() const
Definition Journal.h:324
Stream warn() const
Definition Journal.h:312
std::string const & name() const
Returns the name of this source.
void add(Source &source)
Add a child source.
Abstract stream with RAII containers that produce a property tree.
A metric for reporting event timing.
Definition Event.h:21
void notify(std::chrono::duration< Rep, Period > const &value) const
Push an event notification.
Definition Event.h:44
Measures handler latency on an io_context queue.
void sample(Handler &&handler)
Initiate continuous i/o latency sampling.
void cancel()
Cancel all pending i/o.
The amendment table stores the list of enabled and potential amendments.
beast::io_latency_probe< std::chrono::steady_clock > m_probe
void operator()(Duration const &elapsed)
std::atomic< std::chrono::milliseconds > lastSample_
io_latency_sampler(beast::insight::Event ev, beast::Journal journal, std::chrono::milliseconds interval, boost::asio::io_context &ios)
std::chrono::milliseconds get() const
LedgerReplayer & getLedgerReplayer() override
Application::MutexType & getMasterMutex() override
std::optional< std::pair< PublicKey, SecretKey > > nodeIdentity_
InboundLedgers & getInboundLedgers() override
std::unique_ptr< LedgerCleaner > ledgerCleaner_
std::unique_ptr< LoadManager > m_loadManager
void start(bool withTimers) override
LoadFeeTrack & getFeeTrack() override
Cluster & cluster() override
std::unique_ptr< perf::PerfLog > perfLog_
std::unique_ptr< HashRouter > hashRouter_
std::optional< OpenLedger > openLedger_
RCLValidations & getValidations() override
void run() override
OpenLedger & openLedger() override
Resource::Manager & getResourceManager() override
NodeStoreScheduler m_nodeStoreScheduler
ClosureCounter< void, boost::system::error_code const & > waitHandlerCounter_
beast::Journal m_journal
RelationalDatabase & getRelationalDatabase() override
TransactionMaster & getMasterTransaction() override
std::chrono::milliseconds getIOLatency() override
std::unique_ptr< CollectorManager > m_collectorManager
boost::asio::io_context & getIOContext() override
std::optional< PublicKey const > getValidationPublicKey() const override
HashRouter & getHashRouter() override
LoadManager & getLoadManager() override
PendingSaves pendingSaves_
std::unique_ptr< RelationalDatabase > mRelationalDatabase
std::atomic< bool > checkSigs_
bool checkSigs() const override
bool serverOkay(std::string &reason) override
std::unique_ptr< SHAMapStore > m_shaMapStore
Application::MutexType m_masterMutex
InboundTransactions & getInboundTransactions() override
io_latency_sampler m_io_latency_sampler
std::unique_ptr< AmendmentTable > m_amendmentTable
std::unique_ptr< InboundTransactions > m_inboundTransactions
SHAMapStore & getSHAMapStore() override
boost::asio::steady_timer sweepTimer_
std::unique_ptr< NodeStore::Database > m_nodeStore
std::unique_ptr< LoadFeeTrack > mFeeTrack
boost::asio::steady_timer entropyTimer_
std::unique_ptr< Overlay > overlay_
bool setup(boost::program_options::variables_map const &cmdline) override
Overlay & overlay() override
std::unique_ptr< Config > config_
ManifestCache & publisherManifests() override
std::unique_ptr< ManifestCache > validatorManifests_
CollectorManager & getCollectorManager() override
std::optional< uint256 > trapTxID_
std::unique_ptr< ResolverAsio > m_resolver
NetworkOPs & getOPs() override
TimeKeeper & timeKeeper() override
std::unique_ptr< ValidatorList > validators_
std::unique_ptr< TxQ > txQ_
static std::size_t numberOfThreads(Config const &config)
OpenLedger const & openLedger() const override
std::unique_ptr< ManifestCache > publisherManifests_
LedgerIndex getMaxDisallowedLedger() override
Ensure that a newly-started validator does not sign proposals older than the last ledger it persisted...
NodeCache & getTempNodeCache() override
Logs & logs() override
OrderBookDB m_orderBookDB
ValidatorList & validators() override
PeerReservationTable & peerReservations() override
ApplicationImp(std::unique_ptr< Config > config, std::unique_ptr< Logs > logs, std::unique_ptr< TimeKeeper > timeKeeper)
std::unique_ptr< PeerReservationTable > peerReservations_
std::unique_ptr< Resource::Manager > m_resourceManager
std::pair< PublicKey, SecretKey > const & nodeIdentity() override
std::unique_ptr< Logs > logs_
std::unique_ptr< DatabaseCon > mWalletDB
CachedSLEs & cachedSLEs() override
ValidatorSite & validatorSites() override
virtual std::optional< uint256 > const & trapTxID() const override
std::atomic_flag isTimeToStop
std::unique_ptr< LedgerMaster > m_ledgerMaster
std::unique_ptr< GRPCServer > grpcServer_
std::unique_ptr< ServerHandler > serverHandler_
ValidatorKeys const validatorKeys_
std::uint64_t instanceID() const override
Returns a 64-bit instance identifier, generated at startup.
OrderBookDB & getOrderBookDB() override
Family & getNodeFamily() override
void gotTXSet(std::shared_ptr< SHAMap > const &set, bool fromAcquire)
Config & config() override
DatabaseCon & getWalletDB() override
Retrieve the "wallet database".
PathRequests & getPathRequests() override
bool isStopping() const override
std::unique_ptr< TimeKeeper > timeKeeper_
beast::Journal journal(std::string const &name) override
std::unique_ptr< ValidatorSite > validatorSites_
RCLValidations mValidations
void signalStop(std::string msg) override
std::uint64_t const instanceCookie_
LedgerCleaner & getLedgerCleaner() override
ManifestCache & validatorManifests() override
AmendmentTable & getAmendmentTable() override
int fdRequired() const override
TaggedCache< uint256, AcceptedLedger > m_acceptedLedgerCache
std::unique_ptr< PathRequests > m_pathRequests
std::unique_ptr< JobQueue > m_jobQueue
LedgerMaster & getLedgerMaster() override
NodeStore::Database & getNodeStore() override
TransactionMaster m_txMaster
std::unique_ptr< Cluster > cluster_
std::unique_ptr< NetworkOPs > m_networkOPs
virtual ServerHandler & getServerHandler() override
JobQueue & getJobQueue() override
PendingSaves & pendingSaves() override
void onWrite(beast::PropertyStream::Map &stream) override
Subclass override.
std::unique_ptr< InboundLedgers > m_inboundLedgers
TaggedCache< uint256, AcceptedLedger > & getAcceptedLedgerCache() override
perf::PerfLog & getPerfLog() override
TxQ & getTxQ() override
boost::asio::signal_set m_signals
std::unique_ptr< LedgerReplayer > m_ledgerReplayer
bool exists(std::string const &name) const
Returns true if a section with the given name exists.
The role of a ClosureCounter is to assist in shutdown by letting callers wait for the completion of c...
Provides the beast::insight::Collector service.
bool standalone() const
Definition Config.h:311
bool FORCE_MULTI_THREAD
Definition Config.h:219
std::size_t NODE_SIZE
Definition Config.h:193
int IO_WORKERS
Definition Config.h:215
int WORKERS
Definition Config.h:214
Routing table for objects identified by hash.
Definition HashRouter.h:77
Manages the lifetime of inbound ledgers.
Manages the acquisition and lifetime of transaction sets.
A pool of threads to perform work.
Definition JobQueue.h:37
Check the ledger/transaction databases to make sure they have continuity.
Manages the lifetime of ledger replay tasks.
Manages the current fee schedule.
Manages load sources.
Definition LoadManager.h:26
Manages partitions for logging.
Definition Log.h:32
Remembers manifests with the highest sequence number.
Definition Manifest.h:224
Provides server functionality for clients.
Definition NetworkOPs.h:69
A NodeStore::Scheduler which uses the JobQueue.
Persistency layer for NodeObject.
Definition Database.h:31
Simple NodeStore Scheduler that just performs the tasks synchronously.
static Manager & instance()
Returns the instance of the manager singleton.
virtual std::unique_ptr< Database > make_Database(std::size_t burstSize, Scheduler &scheduler, int readThreads, Section const &backendParameters, beast::Journal journal)=0
Construct a NodeStore database.
Represents the open ledger.
Definition OpenLedger.h:32
Writable ledger view that accumulates state and tx changes.
Definition OpenView.h:45
void rawTxInsert(key_type const &key, std::shared_ptr< Serializer const > const &txn, std::shared_ptr< Serializer const > const &metaData) override
Add a transaction to the tx map.
Definition OpenView.cpp:229
Manages the set of connected peers.
Definition Overlay.h:29
Keeps track of which ledgers haven't been fully saved.
A public key.
Definition PublicKey.h:42
static std::unique_ptr< RelationalDatabase > init(Application &app, Config const &config, JobQueue &jobQueue)
init Creates and returns an appropriate RelationalDatabase instance based on configuration.
static std::unique_ptr< ResolverAsio > New(boost::asio::io_context &, beast::Journal)
A consumption charge.
Definition Charge.h:10
An endpoint that consumes resources.
Definition Consumer.h:16
Tracks load and resource consumption.
class to create database, launch online delete thread, and related SQLite database
Definition SHAMapStore.h:18
Holds the serialized result of parsing an input JSON object.
std::optional< STObject > object
The STObject if the parse was successful.
Holds a collection of configuration values.
Definition BasicConfig.h:24
std::size_t size() const
Returns the number of items in the container.
Manages various times used by the server.
Definition TimeKeeper.h:12
Transaction Queue.
Definition TxQ.h:41
Validator keys and manifest as set in configuration file.
std::optional< Keys > keys
constexpr bool parseHex(std::string_view sv)
Parse a hex string into a base_uint.
Definition base_uint.h:471
bool isZero() const
Definition base_uint.h:508
Singleton class that maintains performance counters and optionally writes Json-formatted data to a di...
Definition PerfLog.h:31
T empty(T... args)
T endl(T... args)
T hardware_concurrency(T... args)
T is_same_v
T load(T... args)
T make_unique(T... args)
T max(T... args)
T min(T... args)
unsigned int UInt
A namespace for easy access to logging severity values.
Definition Journal.h:10
bool lexicalCastChecked(Out &out, In in)
Intelligently convert from one type to another.
STL namespace.
std::unique_ptr< Manager > make_Manager(beast::insight::Collector::ptr const &collector, beast::Journal journal)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
std::shared_ptr< Ledger > loadByHash(uint256 const &ledgerHash, Application &app, bool acquire)
Definition Ledger.cpp:1038
std::pair< PublicKey, SecretKey > getNodeIdentity(Application &app, boost::program_options::variables_map const &cmdline)
The cryptographic credentials identifying this server instance.
void LogicError(std::string const &how) noexcept
Called when faulty logic causes a broken invariant.
csprng_engine & crypto_prng()
The default cryptographically secure PRNG.
std::unique_ptr< LedgerCleaner > make_LedgerCleaner(Application &app, beast::Journal journal)
std::unique_ptr< LoadManager > make_LoadManager(Application &app, beast::Journal journal)
Stopwatch & stopwatch()
Returns an instance of a wall clock.
Definition chrono.h:93
TxQ::Setup setup_TxQ(Config const &config)
Build a TxQ::Setup object from application configuration.
Definition TxQ.cpp:1733
std::tuple< std::shared_ptr< Ledger >, std::uint32_t, uint256 > getLatestLedger(Application &app)
Definition Ledger.cpp:1017
create_genesis_t const create_genesis
Definition Ledger.cpp:32
std::unique_ptr< NetworkOPs > make_NetworkOPs(Application &app, NetworkOPs::clock_type &clock, bool standalone, std::size_t minPeerCount, bool startvalid, JobQueue &job_queue, LedgerMaster &ledgerMaster, ValidatorKeys const &validatorKeys, boost::asio::io_context &io_svc, beast::Journal journal, beast::insight::Collector::ptr const &collector)
std::enable_if_t< std::is_integral< Integral >::value, Integral > rand_int()
std::unique_ptr< SHAMapStore > make_SHAMapStore(Application &app, NodeStore::Scheduler &scheduler, beast::Journal journal)
std::unique_ptr< AmendmentTable > make_AmendmentTable(Application &app, std::chrono::seconds majorityTime, std::vector< AmendmentTable::FeatureInfo > const &supported, Section const &enabled, Section const &vetoed, beast::Journal journal)
std::unique_ptr< PeerSet > make_DummyPeerSet(Application &app)
Make a dummy PeerSet that does not do anything.
Definition PeerSet.cpp:164
@ hotACCOUNT_NODE
Definition NodeObject.h:15
std::unique_ptr< Application > make_Application(std::unique_ptr< Config > config, std::unique_ptr< Logs > logs, std::unique_ptr< TimeKeeper > timeKeeper)
ServerHandler::Setup setup_ServerHandler(Config const &config, std::ostream &&log)
std::shared_ptr< Ledger > loadByIndex(std::uint32_t ledgerIndex, Application &app, bool acquire)
Definition Ledger.cpp:1026
std::unique_ptr< CollectorManager > make_CollectorManager(Section const &params, beast::Journal journal)
std::unique_ptr< DatabaseCon > makeWalletDB(DatabaseCon::Setup const &setup, beast::Journal j)
makeWalletDB Opens the wallet database and returns it.
Definition Wallet.cpp:8
std::unique_ptr< InboundLedgers > make_InboundLedgers(Application &app, InboundLedgers::clock_type &clock, beast::insight::Collector::ptr const &collector)
std::unique_ptr< ServerHandler > make_ServerHandler(Application &app, boost::asio::io_context &io_context, JobQueue &jobQueue, NetworkOPs &networkOPs, Resource::Manager &resourceManager, CollectorManager &cm)
constexpr auto megabytes(T value) noexcept
static void fixConfigPorts(Config &config, Endpoints const &endpoints)
DatabaseCon::Setup setup_DatabaseCon(Config const &c, std::optional< beast::Journal > j=std::nullopt)
void initAccountIdCache(std::size_t count)
Initialize the global cache used to map AccountID to base58 conversions.
Definition AccountID.cpp:85
std::unique_ptr< InboundTransactions > make_InboundTransactions(Application &app, beast::insight::Collector::ptr const &collector, std::function< void(std::shared_ptr< SHAMap > const &, bool)> gotSet)
void addJson(Json::Value &json, LedgerFill const &fill)
Given a Ledger and options, fill a Json::Value with a description of the ledger.
Overlay::Setup setup_Overlay(BasicConfig const &config)
HashRouter::Setup setup_HashRouter(Config const &config)
void forceValidity(HashRouter &router, uint256 const &txid, Validity validity)
Sets the validity of a given transaction in the cache.
Definition apply.cpp:89
std::unordered_map< std::string, boost::asio::ip::tcp::endpoint > Endpoints
Definition ServerImpl.h:20
std::optional< uint256 > getRegisteredFeature(std::string const &name)
Definition Feature.cpp:336
std::unique_ptr< PeerSetBuilder > make_PeerSetBuilder(Application &app)
Definition PeerSet.cpp:121
std::unique_ptr< Overlay > make_Overlay(Application &app, Overlay::Setup const &setup, ServerHandler &serverHandler, Resource::Manager &resourceManager, Resolver &resolver, boost::asio::io_context &io_context, BasicConfig const &config, beast::insight::Collector::ptr const &collector)
Creates the implementation of Overlay.
T ref(T... args)
T length(T... args)
T strlen(T... args)
static std::string importNodeDatabase()
T to_string(T... args)
T what(T... args)