xrpld
Loading...
Searching...
No Matches
LedgerMaster.cpp
1#include <xrpld/app/ledger/LedgerMaster.h>
2
3#include <xrpld/app/consensus/RCLValidations.h>
4#include <xrpld/app/ledger/InboundLedger.h>
5#include <xrpld/app/ledger/InboundLedgers.h>
6#include <xrpld/app/ledger/LedgerPersistence.h>
7#include <xrpld/app/ledger/LedgerReplay.h>
8#include <xrpld/app/ledger/LedgerReplayer.h>
9#include <xrpld/app/ledger/OpenLedger.h>
10#include <xrpld/app/main/Application.h>
11#include <xrpld/app/misc/SHAMapStore.h>
12#include <xrpld/app/misc/Transaction.h>
13#include <xrpld/app/misc/TxQ.h>
14#include <xrpld/app/misc/ValidatorList.h>
15#include <xrpld/core/Config.h>
16#include <xrpld/core/TimeKeeper.h>
17#include <xrpld/overlay/Overlay.h>
18#include <xrpld/overlay/Peer.h>
19#include <xrpld/rpc/detail/PathRequestManager.h>
20
21#include <xrpl/basics/Log.h>
22#include <xrpl/basics/MathUtilities.h>
23#include <xrpl/basics/RangeSet.h>
24#include <xrpl/basics/Slice.h>
25#include <xrpl/basics/UnorderedContainers.h>
26#include <xrpl/basics/UptimeClock.h>
27#include <xrpl/basics/base_uint.h>
28#include <xrpl/basics/chrono.h>
29#include <xrpl/basics/contract.h>
30#include <xrpl/basics/safe_cast.h>
31#include <xrpl/basics/scope.h>
32#include <xrpl/beast/insight/Collector.h>
33#include <xrpl/beast/utility/Journal.h>
34#include <xrpl/beast/utility/Zero.h>
35#include <xrpl/beast/utility/instrumentation.h>
36#include <xrpl/core/Job.h>
37#include <xrpl/json/json_value.h>
38#include <xrpl/ledger/AmendmentTable.h>
39#include <xrpl/ledger/Ledger.h>
40#include <xrpl/ledger/OrderBookDB.h>
41#include <xrpl/ledger/PendingSaves.h>
42#include <xrpl/ledger/View.h>
43#include <xrpl/nodestore/Database.h>
44#include <xrpl/protocol/BuildInfo.h>
45#include <xrpl/protocol/HashPrefix.h>
46#include <xrpl/protocol/LedgerHeader.h>
47#include <xrpl/protocol/Protocol.h>
48#include <xrpl/protocol/RippleLedgerHash.h>
49#include <xrpl/protocol/SField.h>
50#include <xrpl/protocol/Serializer.h>
51#include <xrpl/protocol/digest.h>
52#include <xrpl/rdb/RelationalDatabase.h>
53#include <xrpl/resource/Fees.h>
54#include <xrpl/server/LoadFeeTrack.h>
55#include <xrpl/server/NetworkOPs.h>
56#include <xrpl/shamap/SHAMap.h>
57#include <xrpl/shamap/SHAMapMissingNode.h>
58#include <xrpl/shamap/SHAMapTreeNode.h>
59
60#include <boost/icl/concept/interval_associator.hpp>
61#include <boost/icl/concept/interval_set.hpp>
62
63#include <xrpl.pb.h>
64
65#include <algorithm>
66#include <atomic>
67#include <chrono>
68#include <cstdint>
69#include <cstdlib>
70#include <exception>
71#include <functional>
72#include <iostream>
73#include <iterator>
74#include <map>
75#include <memory>
76#include <mutex>
77#include <optional>
78#include <ostream>
79#include <sstream>
80#include <utility>
81#include <vector>
82
83namespace xrpl {
84
92static constexpr int kMaxLedgerGap{100};
93
99
105static constexpr int kMaxWriteLoadAcquire{8192};
106
120static bool
122 std::uint32_t const currentLedger,
123 std::uint32_t const ledgerHistory,
124 std::optional<LedgerIndex> const minimumOnline,
125 std::uint32_t const candidateLedger,
127{
128 bool const ret = [&]() {
129 // Fetch ledger if it may be the current ledger
130 if (candidateLedger >= currentLedger)
131 return true;
132
133 // Or if it is within our configured history range:
134 if (currentLedger - candidateLedger <= ledgerHistory)
135 return true;
136
137 // Or if greater than or equal to a specific minimum ledger.
138 // Do nothing if the minimum ledger to keep online is unknown.
139 return minimumOnline.has_value() && candidateLedger >= *minimumOnline;
140 }();
141
142 JLOG(j.trace()) << "Missing ledger " << candidateLedger << (ret ? " should" : " should NOT")
143 << " be acquired";
144 return ret;
145}
146
148 Application& app,
150 beast::insight::Collector::Ptr const& collector,
151 beast::Journal journal)
152 : app_(app)
153 , journal_(journal)
154 , ledgerHistory_(collector, app)
155 , standalone_(app_.config().standalone())
156 , fetchDepth_(app_.getSHAMapStore().clampFetchDepth(app_.config().fetchDepth))
157 , ledgerHistorySize_(app_.config().ledgerHistory)
158 , ledgerFetchSize_(app_.config().getValueFor(SizedItem::LedgerFetch))
159 , fetchPacks_(
160 "FetchPack",
161 65536,
162 std::chrono::seconds{45},
163 stopwatch,
164 app_.getJournal("TaggedCache"))
165 , stats_([this] { collectMetrics(); }, collector)
166{
167}
168
171{
172 return app_.getOpenLedger().current()->header().seq;
173}
174
180
181bool
183{
184 auto validLedger = getValidatedLedger();
185
186 if (validLedger && !areCompatible(*validLedger, view, s, reason))
187 {
188 return false;
189 }
190
191 {
192 std::scoped_lock const sl(mutex_);
193
194 if ((lastValidLedger_.second != 0) &&
195 !areCompatible(lastValidLedger_.first, lastValidLedger_.second, view, s, reason))
196 {
197 return false;
198 }
199 }
200
201 return true;
202}
203
206{
207 using namespace std::chrono_literals;
208 std::chrono::seconds const pubClose{pubLedgerClose_.load()};
209 if (pubClose == 0s)
210 {
211 JLOG(journal_.debug()) << "No published ledger";
212 return Weeks{2};
213 }
214
215 std::chrono::seconds ret = app_.getTimeKeeper().closeTime().time_since_epoch();
216 ret -= pubClose;
217 ret = (ret > 0s) ? ret : 0s;
218 static std::chrono::seconds kLastRet = -1s;
219
220 if (ret != kLastRet)
221 {
222 JLOG(journal_.trace()) << "Published ledger age is " << ret.count();
223 kLastRet = ret;
224 }
225 return ret;
226}
227
230{
231 using namespace std::chrono_literals;
232
233 std::chrono::seconds const valClose{validLedgerSign_.load()};
234 if (valClose == 0s)
235 {
236 JLOG(journal_.debug()) << "No validated ledger";
237 return Weeks{2};
238 }
239
240 std::chrono::seconds ret = app_.getTimeKeeper().closeTime().time_since_epoch();
241 ret -= valClose;
242 ret = (ret > 0s) ? ret : 0s;
243 static std::chrono::seconds kLastRet = -1s;
244
245 if (ret != kLastRet)
246 {
247 JLOG(journal_.trace()) << "Validated ledger age is " << ret.count();
248 kLastRet = ret;
249 }
250 return ret;
251}
252
253bool
255{
256 using namespace std::chrono_literals;
257
258 if (getPublishedLedgerAge() > 3min)
259 {
260 reason = "No recently-published ledger";
261 return false;
262 }
263 std::uint32_t const validClose = validLedgerSign_.load();
264 std::uint32_t const pubClose = pubLedgerClose_.load();
265 if ((validClose == 0u) || (pubClose == 0u))
266 {
267 reason = "No published ledger";
268 return false;
269 }
270 if (validClose > (pubClose + 90))
271 {
272 reason = "Published ledger lags validated ledger";
273 return false;
274 }
275 return true;
276}
277
278void
280{
282 std::optional<UInt256> consensusHash;
283
284 if (!standalone_)
285 {
286 auto validations = app_.getValidators().negativeUNLFilter(
287 app_.getValidations().getTrustedForLedger(l->header().hash, l->header().seq));
288 times.reserve(validations.size());
289 for (auto const& val : validations)
290 times.push_back(val->getSignTime());
291
292 if (!validations.empty())
293 consensusHash = validations.front()->getConsensusHash();
294 }
295
296 NetClock::time_point signTime;
297
298 if (!times.empty() && times.size() >= app_.getValidators().quorum())
299 {
300 // Calculate the sample median
301 std::ranges::sort(times);
302 auto const t0 = times[(times.size() - 1) / 2];
303 auto const t1 = times[times.size() / 2];
304 signTime = t0 + (t1 - t0) / 2;
305 }
306 else
307 {
308 signTime = l->header().closeTime;
309 }
310
311 validLedger_.set(l);
312 validLedgerSign_ = signTime.time_since_epoch().count();
313 XRPL_ASSERT(
314 validLedgerSeq_ || !app_.getMaxDisallowedLedger() ||
315 l->header().seq + maxLedgerDifference_ > app_.getMaxDisallowedLedger(),
316 "xrpl::LedgerMaster::setValidLedger : valid ledger sequence");
318 validLedgerSeq_ = l->header().seq;
319
320 app_.getOPs().updateLocalTx(*l);
321 app_.getSHAMapStore().onLedgerClosed(getValidatedLedger());
322 ledgerHistory_.validatedLedger(l, consensusHash);
323 app_.getAmendmentTable().doValidatedLedger(l);
324 if (!app_.getOPs().isBlocked())
325 {
326 if (app_.getAmendmentTable().hasUnsupportedEnabled())
327 {
328 JLOG(journal_.error()) << "One or more unsupported amendments "
329 "activated: server blocked.";
330 app_.getOPs().setAmendmentBlocked();
331 }
332 else if (!app_.getOPs().isAmendmentWarned() || l->isFlagLedger())
333 {
334 // Amendments can lose majority, so re-check periodically (every
335 // flag ledger), and clear the flag if appropriate. If an unknown
336 // amendment gains majority log a warning as soon as it's
337 // discovered, then again every flag ledger until the operator
338 // upgrades, the amendment loses majority, or the amendment goes
339 // live and the node gets blocked. Unlike being amendment blocked,
340 // this message may be logged more than once per session, because
341 // the node will otherwise function normally, and this gives
342 // operators an opportunity to see and resolve the warning.
343 if (auto const first = app_.getAmendmentTable().firstUnsupportedExpected())
344 {
345 JLOG(journal_.error()) << "One or more unsupported amendments "
346 "reached majority. Upgrade before "
347 << to_string(*first)
348 << " to prevent your server from "
349 "becoming amendment blocked.";
350 app_.getOPs().setAmendmentWarned();
351 }
352 else
353 {
354 app_.getOPs().clearAmendmentWarned();
355 }
356 }
357 }
358}
359
360void
362{
363 pubLedger_ = l;
364 pubLedgerClose_ = l->header().closeTime.time_since_epoch().count();
365 pubLedgerSeq_ = l->header().seq;
366}
367
368void
370{
371 std::scoped_lock const ml(mutex_);
372 heldTransactions_.insert(transaction->getSTransaction());
373}
374
375// Validate a ledger's close time and sequence number if we're considering
376// jumping to that ledger. This helps defend against some rare hostile or
377// diverged majority scenarios.
378bool
380{
381 XRPL_ASSERT(ledger, "xrpl::LedgerMaster::canBeCurrent : non-null input");
382
383 // Never jump to a candidate ledger that precedes our
384 // last validated ledger
385
386 auto validLedger = getValidatedLedger();
387 if (validLedger && (ledger->header().seq < validLedger->header().seq))
388 {
389 JLOG(journal_.trace()) << "Candidate for current ledger has low seq "
390 << ledger->header().seq << " < " << validLedger->header().seq;
391 return false;
392 }
393
394 // Ensure this ledger's parent close time is within five minutes of
395 // our current time. If we already have a known fully-valid ledger
396 // we perform this check. Otherwise, we only do it if we've built a
397 // few ledgers as our clock can be off when we first start up
398
399 auto closeTime = app_.getTimeKeeper().closeTime();
400 auto ledgerClose = ledger->header().parentCloseTime;
401
402 using namespace std::chrono_literals;
403 if ((validLedger || (ledger->header().seq > 10)) &&
404 ((std::max(closeTime, ledgerClose) - std::min(closeTime, ledgerClose)) > 5min))
405 {
406 JLOG(journal_.warn()) << "Candidate for current ledger has close time "
407 << to_string(ledgerClose) << " at network time "
408 << to_string(closeTime) << " seq " << ledger->header().seq;
409 return false;
410 }
411
412 if (validLedger)
413 {
414 // Sequence number must not be too high. We allow ten ledgers
415 // for time inaccuracies plus a maximum run rate of one ledger
416 // every two seconds. The goal is to prevent a malicious ledger
417 // from increasing our sequence unreasonably high
418
419 LedgerIndex maxSeq = validLedger->header().seq + 10;
420
421 if (closeTime > validLedger->header().parentCloseTime)
422 {
424 closeTime - validLedger->header().parentCloseTime)
425 .count() /
426 2;
427 }
428
429 if (ledger->header().seq > maxSeq)
430 {
431 JLOG(journal_.warn()) << "Candidate for current ledger has high seq "
432 << ledger->header().seq << " > " << maxSeq;
433 return false;
434 }
435
436 JLOG(journal_.trace()) << "Acceptable seq range: " << validLedger->header().seq
437 << " <= " << ledger->header().seq << " <= " << maxSeq;
438 }
439
440 return true;
441}
442
443void
445{
446 XRPL_ASSERT(lastClosed, "xrpl::LedgerMaster::switchLCL : non-null input");
447 if (!lastClosed->isImmutable())
448 logicError("mutable ledger in switchLCL");
449
450 if (lastClosed->open())
451 logicError("The new last closed ledger is open!");
452
453 {
454 std::scoped_lock const ml(mutex_);
455 closedLedger_.set(lastClosed);
456 }
457
458 if (standalone_)
459 {
460 setFullLedger(lastClosed, true, false);
461 tryAdvance();
462 }
463 else
464 {
465 checkAccept(lastClosed);
466 }
467}
468
469bool
470LedgerMaster::fixIndex(LedgerIndex ledgerIndex, LedgerHash const& ledgerHash)
471{
472 return ledgerHistory_.fixIndex(ledgerIndex, ledgerHash);
473}
474
475bool
477{
478 bool const validated = ledger->header().validated;
479 // Returns true if we already had the ledger
480 return ledgerHistory_.insert(ledger, validated);
481}
482
489void
491{
492 CanonicalTXSet const set = [this]() {
493 std::scoped_lock const sl(mutex_);
494 // VFALCO NOTE The hash for an open ledger is undefined so we use
495 // something that is a reasonable substitute.
496 CanonicalTXSet set(app_.getOpenLedger().current()->header().parentHash);
498 return set;
499 }();
500
501 if (!set.empty())
502 app_.getOPs().processTransactionSet(set);
503}
504
507{
508 std::scoped_lock const sl(mutex_);
509
510 return heldTransactions_.popAcctTransaction(tx);
511}
512
513void
518
519bool
521{
523 return boost::icl::contains(completeLedgers_, seq);
524}
525
526void
532
533bool
535{
536 if (ledger.open())
537 return false;
538
539 if (ledger.header().validated)
540 return true;
541
542 auto const seq = ledger.header().seq;
543 try
544 {
545 // Use the skip list in the last validated ledger to see if ledger
546 // comes before the last validated ledger (and thus has been
547 // validated).
548 auto const hash = walkHashBySeq(seq, InboundLedger::Reason::GENERIC);
549
550 if (!hash || ledger.header().hash != *hash)
551 {
552 // This ledger's hash is not the hash of the validated ledger
553 if (hash)
554 {
555 XRPL_ASSERT(hash->isNonZero(), "xrpl::LedgerMaster::isValidated : nonzero hash");
556 UInt256 const valHash = app_.getRelationalDatabase().getHashByIndex(seq);
557 if (valHash == ledger.header().hash)
558 {
559 // SQL database doesn't match ledger chain
560 clearLedger(seq);
561 }
562 }
563 return false;
564 }
565 }
566 catch (SHAMapMissingNode const& mn)
567 {
568 JLOG(journal_.warn()) << "Ledger #" << seq << ": " << mn.what();
569 return false;
570 }
571
572 // Mark ledger as validated to save time if we see it again.
573 ledger.header().validated = true;
574 return true;
575}
576
577// returns Ledgers we have all the nodes for
578bool
580{
581 // Validated ledger is likely not stored in the DB yet so we use the
582 // published ledger which is.
583 maxVal = pubLedgerSeq_.load();
584
585 if (maxVal == 0u)
586 return false;
587
589 {
591 maybeMin = prevMissing(completeLedgers_, maxVal);
592 }
593
594 if (maybeMin == std::nullopt)
595 {
596 minVal = maxVal;
597 }
598 else
599 {
600 minVal = 1 + *maybeMin;
601 }
602
603 return true;
604}
605
606// Returns Ledgers we have all the nodes for and are indexed
607bool
609{
610 if (!getFullValidatedRange(minVal, maxVal))
611 return false;
612
613 // Remove from the validated range any ledger sequences that may not be
614 // fully updated in the database yet
615
616 auto const pendingSaves = app_.getPendingSaves().getSnapshot();
617
618 if (!pendingSaves.empty() && ((minVal != 0) || (maxVal != 0)))
619 {
620 // Ensure we shrink the tips as much as possible. If we have 7-9 and
621 // 8,9 are invalid, we don't want to see the 8 and shrink to just 9
622 // because then we'll have nothing when we could have 7.
623 while (pendingSaves.contains(maxVal))
624 --maxVal;
625 while (pendingSaves.contains(minVal))
626 ++minVal;
627
628 // Best effort for remaining exclusions
629 for (auto v : pendingSaves)
630 {
631 if ((v.first >= minVal) && (v.first <= maxVal))
632 {
633 if (v.first > ((minVal + maxVal) / 2))
634 {
635 maxVal = v.first - 1;
636 }
637 else
638 {
639 minVal = v.first + 1;
640 }
641 }
642 }
643
644 if (minVal > maxVal)
645 minVal = maxVal = 0;
646 }
647
648 return true;
649}
650
651// Get the earliest ledger we will let peers fetch
654{
655 // The earliest ledger we will let people fetch is ledger zero,
656 // unless that creates a larger range than allowed
657 std::uint32_t e = getClosedLedger()->header().seq;
658
659 if (e > fetchDepth_)
660 {
661 e -= fetchDepth_;
662 }
663 else
664 {
665 e = 0;
666 }
667 return e;
668}
669
670void
672{
673 std::uint32_t seq = ledger->header().seq;
674 UInt256 prevHash = ledger->header().parentHash;
675
677
678 std::uint32_t minHas = seq;
679 std::uint32_t maxHas = seq;
680
681 node_store::Database& nodeStore{app_.getNodeStore()};
682 while (!app_.getJobQueue().isStopping() && seq > 0)
683 {
684 {
685 std::scoped_lock const ml(mutex_);
686 minHas = seq;
687 --seq;
688
689 if (haveLedger(seq))
690 break;
691 }
692
693 auto it(ledgerHashes.find(seq));
694
695 if (it == ledgerHashes.end())
696 {
697 if (app_.isStopping())
698 return;
699
700 {
702 completeLedgers_.insert(range(minHas, maxHas));
703 }
704 maxHas = minHas;
705 ledgerHashes =
706 app_.getRelationalDatabase().getHashesByIndex((seq < 500) ? 0 : (seq - 499), seq);
707 it = ledgerHashes.find(seq);
708
709 if (it == ledgerHashes.end())
710 break;
711
712 if (!nodeStore.fetchNodeObject(
713 ledgerHashes.begin()->second.ledgerHash, ledgerHashes.begin()->first))
714 {
715 // The ledger is not backed by the node store
716 JLOG(journal_.warn())
717 << "SQL DB ledger sequence " << seq << " mismatches node store";
718 break;
719 }
720 }
721
722 if (it->second.ledgerHash != prevHash)
723 break;
724
725 prevHash = it->second.parentHash;
726 }
727
728 {
730 completeLedgers_.insert(range(minHas, maxHas));
731 }
732 {
733 std::scoped_lock const ml(mutex_);
734 fillInProgress_ = 0;
735 tryAdvance();
736 }
737}
738
742void
744{
745 LedgerIndex const ledgerIndex = missing + 1;
746
747 auto const haveHash{getLedgerHashForHistory(ledgerIndex, reason)};
748 if (!haveHash || haveHash->isZero())
749 {
750 JLOG(journal_.error()) << "No hash for fetch pack. Missing Index " << missing;
751 return;
752 }
753
754 // Select target Peer based on highest score. The score is randomized
755 // but biased in favor of Peers with low latency.
757 {
758 int maxScore = 0;
759 auto peerList = app_.getOverlay().getActivePeers();
760 for (auto const& peer : peerList)
761 {
762 if (peer->hasRange(missing, missing + 1))
763 {
764 int const score = peer->getScore(true);
765 if (!target || (score > maxScore))
766 {
767 target = peer;
768 maxScore = score;
769 }
770 }
771 }
772 }
773
774 if (target)
775 {
776 protocol::TMGetObjectByHash tmBH;
777 tmBH.set_query(true);
778 tmBH.set_type(protocol::TMGetObjectByHash::otFETCH_PACK);
779 tmBH.set_ledgerhash(haveHash->begin(), 32);
780 auto packet = std::make_shared<Message>(tmBH, protocol::mtGET_OBJECTS);
781
782 target->send(packet);
783 JLOG(journal_.trace()) << "Requested fetch pack for " << missing;
784 }
785 else
786 {
787 JLOG(journal_.debug()) << "No peer for fetch pack";
788 }
789}
790
791void
793{
794 int invalidate = 0;
796
797 for (std::uint32_t lSeq = ledger.header().seq - 1; lSeq > 0; --lSeq)
798 {
799 if (haveLedger(lSeq))
800 {
801 try
802 {
803 hash = hashOfSeq(ledger, lSeq, journal_);
804 }
805 catch (std::exception const& ex)
806 {
807 JLOG(journal_.warn())
808 << "fixMismatch encounters partial ledger. Exception: " << ex.what();
809 clearLedger(lSeq);
810 return;
811 }
812
813 if (hash)
814 {
815 // try to close the seam
816 auto otherLedger = getLedgerBySeq(lSeq);
817
818 if (otherLedger && (otherLedger->header().hash == *hash))
819 {
820 // we closed the seam
821 if (invalidate != 0)
822 {
823 JLOG(journal_.warn()) << "Match at " << lSeq << ", " << invalidate
824 << " prior ledgers invalidated";
825 }
826
827 return;
828 }
829 }
830
831 clearLedger(lSeq);
832 ++invalidate;
833 }
834 }
835
836 // all prior ledgers invalidated
837 if (invalidate != 0)
838 {
839 JLOG(journal_.warn()) << "All " << invalidate << " prior ledgers invalidated";
840 }
841}
842
843void
845 std::shared_ptr<Ledger const> const& ledger,
846 bool isSynchronous,
847 bool isCurrent)
848{
849 // A new ledger has been accepted as part of the trusted chain
850 JLOG(journal_.debug()) << "Ledger " << ledger->header().seq
851 << " accepted :" << ledger->header().hash;
852 XRPL_ASSERT(
853 ledger->stateMap().getHash().isNonZero(),
854 "xrpl::LedgerMaster::setFullLedger : nonzero ledger state hash");
855
856 ledger->setValidated();
857 ledger->setFull();
858
859 if (isCurrent)
860 ledgerHistory_.insert(ledger, true);
861
862 {
863 // Check the SQL database's entry for the sequence before this
864 // ledger, if it's not this ledger's parent, invalidate it
865 UInt256 const prevHash =
866 app_.getRelationalDatabase().getHashByIndex(ledger->header().seq - 1);
867 if (prevHash.isNonZero() && prevHash != ledger->header().parentHash)
868 clearLedger(ledger->header().seq - 1);
869 }
870
871 pendSaveValidated(app_, ledger, isSynchronous, isCurrent);
872
873 {
875 completeLedgers_.insert(ledger->header().seq);
876 }
877
878 {
879 std::scoped_lock const ml(mutex_);
880
881 if (ledger->header().seq > validLedgerSeq_)
882 setValidLedger(ledger);
883 if (!pubLedger_)
884 {
885 setPubLedger(ledger);
886 app_.getOrderBookDB().setup(ledger);
887 }
888
889 if (ledger->header().seq != 0 && haveLedger(ledger->header().seq - 1))
890 {
891 // we think we have the previous ledger, double check
892 auto prevLedger = getLedgerBySeq(ledger->header().seq - 1);
893
894 if (!prevLedger || (prevLedger->header().hash != ledger->header().parentHash))
895 {
896 JLOG(journal_.warn()) << "Acquired ledger invalidates previous ledger: "
897 << (prevLedger ? "hashMismatch" : "missingLedger");
898 fixMismatch(*ledger);
899 }
900 }
901 }
902}
903
904void
906{
907 clearLedger(seq);
908 app_.getInboundLedgers().acquire(hash, seq, InboundLedger::Reason::GENERIC);
909}
910
911// Check if the specified ledger can become the new last fully-validated
912// ledger.
913void
915{
916 std::size_t valCount = 0;
917
918 if (seq != 0)
919 {
920 // Ledger is too old
921 if (seq < validLedgerSeq_)
922 return;
923
924 auto validations = app_.getValidators().negativeUNLFilter(
925 app_.getValidations().getTrustedForLedger(hash, seq));
926 valCount = validations.size();
927 if (valCount >= app_.getValidators().quorum())
928 {
929 std::scoped_lock const ml(mutex_);
930 if (seq > lastValidLedger_.second)
931 lastValidLedger_ = std::make_pair(hash, seq);
932 }
933
934 if (seq == validLedgerSeq_)
935 return;
936
937 // Ledger could match the ledger we're already building
938 if (seq == buildingLedgerSeq_)
939 return;
940 }
941
942 auto ledger = ledgerHistory_.getLedgerByHash(hash);
943
944 if (!ledger)
945 {
946 if ((seq != 0) && (getValidLedgerIndex() == 0))
947 {
948 // Set peers converged early if we can
949 if (valCount >= app_.getValidators().quorum())
950 app_.getOverlay().checkTracking(seq);
951 }
952
953 // FIXME: We may not want to fetch a ledger with just one
954 // trusted validation
955 ledger = app_.getInboundLedgers().acquire(hash, seq, InboundLedger::Reason::GENERIC);
956 }
957
958 if (ledger)
959 checkAccept(ledger);
960}
961
969{
970 return standalone_ ? 0 : app_.getValidators().quorum();
971}
972
973void
975{
976 // Can we accept this ledger as our new last fully-validated ledger
977
978 if (!canBeCurrent(ledger))
979 return;
980
981 // Can we advance the last fully-validated ledger? If so, can we
982 // publish?
983 std::scoped_lock const ml(mutex_);
984
985 if (ledger->header().seq <= validLedgerSeq_)
986 return;
987
988 auto const minVal = getNeededValidations();
989 auto validations = app_.getValidators().negativeUNLFilter(
990 app_.getValidations().getTrustedForLedger(ledger->header().hash, ledger->header().seq));
991 auto const tvc = validations.size();
992 if (tvc < minVal) // nothing we can do
993 {
994 JLOG(journal_.trace()) << "Only " << tvc << " validations for " << ledger->header().hash;
995 return;
996 }
997
998 JLOG(journal_.info()) << "Advancing accepted ledger to " << ledger->header().seq
999 << " with >= " << minVal << " validations";
1000
1001 ledger->setValidated();
1002 ledger->setFull();
1003 setValidLedger(ledger);
1004 if (!pubLedger_)
1005 {
1006 pendSaveValidated(app_, ledger, true, true);
1007 setPubLedger(ledger);
1008 app_.getOrderBookDB().setup(ledger);
1009 }
1010
1011 std::uint32_t const base = app_.getFeeTrack().getLoadBase();
1012 auto fees = app_.getValidations().fees(ledger->header().hash, base);
1013 {
1014 auto fees2 = app_.getValidations().fees(ledger->header().parentHash, base);
1015 fees.reserve(fees.size() + fees2.size());
1017 }
1018 std::uint32_t fee = 0;
1019 if (!fees.empty())
1020 {
1021 std::ranges::sort(fees);
1022 if (auto stream = journal_.debug())
1023 {
1025 s << "Received fees from validations: (" << fees.size() << ") ";
1026 for (auto const fee1 : fees)
1027 {
1028 s << " " << fee1;
1029 }
1030 stream << s.str();
1031 }
1032 fee = fees[fees.size() / 2]; // median
1033 }
1034 else
1035 {
1036 fee = base;
1037 }
1038
1039 app_.getFeeTrack().setRemoteFee(fee);
1040
1041 tryAdvance();
1042
1043 if (ledger->seq() % 256 == 0)
1044 {
1045 // Check if the majority of validators run a higher version xrpld
1046 // software. If so print a warning.
1047 //
1048 // Validators include their xrpld software version in the validation
1049 // messages of every (flag - 1) ledger. We wait for one ledger time
1050 // before checking the version information to accumulate more validation
1051 // messages.
1052
1053 auto currentTime = app_.getTimeKeeper().now();
1054 bool needPrint = false;
1055
1056 // The variable upgradeWarningPrevTime_ will be set when and only when
1057 // the warning is printed.
1059 {
1060 // Have not printed the warning before, check if need to print.
1061 auto const vals = app_.getValidations().getTrustedForLedger(
1062 ledger->header().parentHash, ledger->header().seq - 1);
1063 std::size_t higherVersionCount = 0;
1064 std::size_t xrpldCount = 0;
1065 for (auto const& v : vals)
1066 {
1067 if (v->isFieldPresent(sfServerVersion))
1068 {
1069 auto version = v->getFieldU64(sfServerVersion);
1070 higherVersionCount += build_info::isNewerVersion(version) ? 1 : 0;
1071 xrpldCount += build_info::isXrpldVersion(version) ? 1 : 0;
1072 }
1073 }
1074 // We report only if (1) we have accumulated validation messages
1075 // from 90% validators from the UNL, (2) 60% of validators
1076 // running the xrpld implementation have higher version numbers,
1077 // and (3) the calculation won't cause divide-by-zero.
1078 if (higherVersionCount > 0 && xrpldCount > 0)
1079 {
1080 static constexpr std::size_t kReportingPercent = 90;
1081 static constexpr std::size_t kCutoffPercent = 60;
1082 auto const unlSize{app_.getValidators().getQuorumKeys().second.size()};
1083 needPrint = unlSize > 0 &&
1084 calculatePercent(vals.size(), unlSize) >= kReportingPercent &&
1085 calculatePercent(higherVersionCount, xrpldCount) >= kCutoffPercent;
1086 }
1087 }
1088 // To throttle the warning messages, instead of printing a warning
1089 // every flag ledger, we print every week.
1090 else if (currentTime - upgradeWarningPrevTime_ >= Weeks{1})
1091 {
1092 // Printed the warning before, and assuming most validators
1093 // do not downgrade, we keep printing the warning
1094 // until the local server is restarted.
1095 needPrint = true;
1096 }
1097
1098 if (needPrint)
1099 {
1100 upgradeWarningPrevTime_ = currentTime;
1101 auto const upgradeMsg =
1102 "Check for upgrade: "
1103 "A majority of trusted validators are "
1104 "running a newer version.";
1105 std::cerr << upgradeMsg << std::endl;
1106 JLOG(journal_.error()) << upgradeMsg;
1107 }
1108 }
1109}
1110
1114void
1116 std::shared_ptr<Ledger const> const& ledger,
1117 UInt256 const& consensusHash,
1118 json::Value consensus)
1119{
1120 // Because we just built a ledger, we are no longer building one
1122
1123 // No need to process validations in standalone mode
1124 if (standalone_)
1125 return;
1126
1127 ledgerHistory_.builtLedger(ledger, consensusHash, std::move(consensus));
1128
1129 if (ledger->header().seq <= validLedgerSeq_)
1130 {
1131 auto stream = app_.getJournal("LedgerConsensus").info();
1132 JLOG(stream) << "Consensus built old ledger: " << ledger->header().seq
1133 << " <= " << validLedgerSeq_;
1134 return;
1135 }
1136
1137 // See if this ledger can be the new fully-validated ledger
1138 checkAccept(ledger);
1139
1140 if (ledger->header().seq <= validLedgerSeq_)
1141 {
1142 auto stream = app_.getJournal("LedgerConsensus").debug();
1143 JLOG(stream) << "Consensus ledger fully validated";
1144 return;
1145 }
1146
1147 // This ledger cannot be the new fully-validated ledger, but
1148 // maybe we saved up validations for some other ledger that can be
1149
1150 auto validations =
1151 app_.getValidators().negativeUNLFilter(app_.getValidations().currentTrusted());
1152
1164 class ValSeq
1165 {
1166 public:
1167 ValSeq() = default;
1168
1175 void
1176 mergeValidation(LedgerIndex seq)
1177 {
1178 valCount++;
1179
1180 // If we didn't already know the sequence, now we do
1181 if (ledgerSeq == 0)
1182 ledgerSeq = seq;
1183 }
1184
1188 std::size_t valCount{0};
1189
1193 LedgerIndex ledgerSeq{0};
1194 };
1196
1197 // Count the number of current, trusted validations
1199 for (auto const& v : validations)
1200 {
1201 ValSeq& vs = count[v->getLedgerHash()];
1202 vs.mergeValidation(v->getFieldU32(sfLedgerSequence));
1203 }
1204
1205 auto const neededValidations = getNeededValidations();
1206 auto maxSeq = validLedgerSeq_.load();
1207 auto maxLedger = ledger->header().hash;
1208
1209 // Of the ledgers with sufficient validations,
1210 // find the one with the highest sequence
1211 for (auto& v : count)
1212 {
1213 if (v.second.valCount > neededValidations)
1214 {
1215 // If we still don't know the sequence, get it
1216 if (v.second.ledgerSeq == 0)
1217 {
1218 if (auto l = getLedgerByHash(v.first))
1219 v.second.ledgerSeq = l->header().seq;
1220 }
1221
1222 if (v.second.ledgerSeq > maxSeq)
1223 {
1224 maxSeq = v.second.ledgerSeq;
1225 maxLedger = v.first;
1226 }
1227 }
1228 }
1229
1230 if (maxSeq > validLedgerSeq_)
1231 {
1232 auto stream = app_.getJournal("LedgerConsensus").debug();
1233 JLOG(stream) << "Consensus triggered check of ledger";
1234 checkAccept(maxLedger, maxSeq);
1235 }
1236}
1237
1240{
1241 // Try to get the hash of a ledger we need to fetch for history
1243 auto const& l{histLedger_};
1244
1245 if (l && l->header().seq >= index)
1246 {
1247 ret = hashOfSeq(*l, index, journal_);
1248 if (!ret)
1249 ret = walkHashBySeq(index, l, reason);
1250 }
1251
1252 if (!ret)
1253 ret = walkHashBySeq(index, reason);
1254
1255 return ret;
1256}
1257
1260{
1262
1263 JLOG(journal_.trace()) << "findNewLedgersToPublish<";
1264
1265 // No valid ledger, nothing to do
1266 if (validLedger_.empty())
1267 {
1268 JLOG(journal_.trace()) << "No valid journal, nothing to publish.";
1269 return {};
1270 }
1271
1272 if (!pubLedger_)
1273 {
1274 JLOG(journal_.info()) << "First published ledger will be " << validLedgerSeq_;
1275 return {validLedger_.get()};
1276 }
1277
1279 {
1280 JLOG(journal_.warn()) << "Gap in validated ledger stream " << pubLedgerSeq_ << " - "
1281 << validLedgerSeq_ - 1;
1282
1283 auto valLedger = validLedger_.get();
1284 ret.push_back(valLedger);
1285 setPubLedger(valLedger);
1286 app_.getOrderBookDB().setup(valLedger);
1287
1288 return {valLedger};
1289 }
1290
1292 {
1293 JLOG(journal_.trace()) << "No valid journal, nothing to publish.";
1294 return {};
1295 }
1296
1297 int acqCount = 0;
1298
1299 auto pubSeq = pubLedgerSeq_ + 1; // Next sequence to publish
1300 auto valLedger = validLedger_.get();
1301 std::uint32_t const valSeq = valLedger->header().seq;
1302
1303 ScopeUnlock const sul{sl};
1304 try
1305 {
1306 for (std::uint32_t seq = pubSeq; seq <= valSeq; ++seq)
1307 {
1308 JLOG(journal_.trace()) << "Trying to fetch/publish valid ledger " << seq;
1309
1311 // This can throw
1312 auto hash = hashOfSeq(*valLedger, seq, journal_);
1313 // VFALCO TODO Restructure this code so that zero is not
1314 // used.
1315 if (!hash)
1316 hash = beast::kZero; // kludge
1317 if (seq == valSeq)
1318 {
1319 // We need to publish the ledger we just fully validated
1320 ledger = valLedger;
1321 }
1322 else if (hash->isZero())
1323 {
1324 // LCOV_EXCL_START
1325 JLOG(journal_.fatal()) << "Ledger: " << valSeq << " does not have hash for " << seq;
1326 UNREACHABLE(
1327 "xrpl::LedgerMaster::findNewLedgersToPublish : ledger "
1328 "not found");
1329 // LCOV_EXCL_STOP
1330 }
1331 else
1332 {
1333 ledger = ledgerHistory_.getLedgerByHash(*hash);
1334 }
1335
1336 if (!app_.config().ledgerReplay)
1337 {
1338 // Can we try to acquire the ledger we need?
1339 if (!ledger && (++acqCount < ledgerFetchSize_))
1340 {
1341 ledger = app_.getInboundLedgers().acquire(
1342 *hash, seq, InboundLedger::Reason::GENERIC);
1343 }
1344 }
1345
1346 // Did we acquire the next ledger we need to publish?
1347 if (ledger && (ledger->header().seq == pubSeq))
1348 {
1349 ledger->setValidated();
1350 ret.push_back(ledger);
1351 ++pubSeq;
1352 }
1353 }
1354
1355 JLOG(journal_.trace()) << "ready to publish " << ret.size() << " ledgers.";
1356 }
1357 catch (std::exception const& ex)
1358 {
1359 JLOG(journal_.error()) << "Exception while trying to find ledgers to publish: "
1360 << ex.what();
1361 }
1362
1363 if (app_.config().ledgerReplay)
1364 {
1365 /* Narrow down the gap of ledgers, and try to replay them.
1366 * When replaying a ledger gap, if the local node has
1367 * the start ledger, it saves an expensive InboundLedger
1368 * acquire. If the local node has the finish ledger, it
1369 * saves a skip list acquire.
1370 */
1371 auto const& startLedger = ret.empty() ? pubLedger_ : ret.back();
1372 auto finishLedger = valLedger;
1373 while (startLedger->seq() + 1 < finishLedger->seq())
1374 {
1375 if (auto const parent =
1376 ledgerHistory_.getLedgerByHash(finishLedger->header().parentHash);
1377 parent)
1378 {
1379 finishLedger = parent;
1380 }
1381 else
1382 {
1383 auto numberLedgers = finishLedger->seq() - startLedger->seq() + 1;
1384 JLOG(journal_.debug())
1385 << "Publish LedgerReplays " << numberLedgers
1386 << " ledgers, from seq=" << startLedger->header().seq << ", "
1387 << startLedger->header().hash << " to seq=" << finishLedger->header().seq
1388 << ", " << finishLedger->header().hash;
1389 app_.getLedgerReplayer().replay(
1390 InboundLedger::Reason::GENERIC, finishLedger->header().hash, numberLedgers);
1391 break;
1392 }
1393 }
1394 }
1395
1396 return ret;
1397}
1398
1399void
1401{
1402 std::scoped_lock const ml(mutex_);
1403
1404 // Can't advance without at least one fully-valid ledger
1405 advanceWork_ = true;
1406 if (!advanceThread_ && !validLedger_.empty())
1407 {
1408 advanceThread_ = true;
1409 app_.getJobQueue().addJob(JtAdvance, "AdvanceLedger", [this]() {
1411
1412 XRPL_ASSERT(
1413 !validLedger_.empty() && advanceThread_,
1414 "xrpl::LedgerMaster::tryAdvance : has valid ledger");
1415
1416 JLOG(journal_.trace()) << "advanceThread<";
1417
1418 try
1419 {
1420 doAdvance(sl);
1421 }
1422 catch (std::exception const& ex)
1423 {
1424 JLOG(journal_.fatal()) << "doAdvance throws: " << ex.what();
1425 }
1426
1427 advanceThread_ = false;
1428 JLOG(journal_.trace()) << "advanceThread>";
1429 });
1430 }
1431}
1432
1433void
1435{
1436 {
1437 std::scoped_lock const ml(mutex_);
1438 if (app_.getOPs().isNeedNetworkLedger())
1439 {
1441 pathLedger_.reset();
1442 JLOG(journal_.debug()) << "Need network ledger for updating paths";
1443 return;
1444 }
1445 }
1446
1447 while (!app_.getJobQueue().isStopping())
1448 {
1449 JLOG(journal_.debug()) << "updatePaths running";
1451 {
1452 std::scoped_lock const ml(mutex_);
1453
1454 if (!validLedger_.empty() &&
1455 (!pathLedger_ || (pathLedger_->header().seq != validLedgerSeq_)))
1456 { // We have a new valid ledger since the last full pathfinding
1457 pathLedger_ = validLedger_.get();
1458 lastLedger = pathLedger_;
1459 }
1460 else if (pathFindNewRequest_)
1461 { // We have a new request but no new ledger
1462 lastLedger = app_.getOpenLedger().current();
1463 }
1464 else
1465 { // Nothing to do
1467 pathLedger_.reset();
1468 JLOG(journal_.debug()) << "Nothing to do for updating paths";
1469 return;
1470 }
1471 }
1472
1473 if (!standalone_)
1474 { // don't pathfind with a ledger that's more than 60 seconds old
1475 using namespace std::chrono;
1476 auto age = time_point_cast<seconds>(app_.getTimeKeeper().closeTime()) -
1477 lastLedger->header().closeTime;
1478 if (age > 1min)
1479 {
1480 JLOG(journal_.debug()) << "Published ledger too old for updating paths";
1481 std::scoped_lock const ml(mutex_);
1483 pathLedger_.reset();
1484 return;
1485 }
1486 }
1487
1488 try
1489 {
1490 auto& pathRequests = app_.getPathRequestManager();
1491 {
1492 std::scoped_lock const ml(mutex_);
1493 if (!pathRequests.requestsPending())
1494 {
1496 pathLedger_.reset();
1497 JLOG(journal_.debug()) << "No path requests found. Nothing to do for updating "
1498 "paths. "
1499 << pathFindThread_ << " jobs remaining";
1500 return;
1501 }
1502 }
1503 JLOG(journal_.debug()) << "Updating paths";
1504 pathRequests.updateAll(lastLedger);
1505
1506 std::scoped_lock const ml(mutex_);
1507 if (!pathRequests.requestsPending())
1508 {
1509 JLOG(journal_.debug()) << "No path requests left. No need for further updating "
1510 "paths";
1512 pathLedger_.reset();
1513 return;
1514 }
1515 }
1516 catch (SHAMapMissingNode const& mn)
1517 {
1518 JLOG(journal_.info()) << "During pathfinding: " << mn.what();
1519 if (lastLedger->open())
1520 {
1521 // our parent is the problem
1522 app_.getInboundLedgers().acquire(
1523 lastLedger->header().parentHash,
1524 lastLedger->header().seq - 1,
1526 }
1527 else
1528 {
1529 // this ledger is the problem
1530 app_.getInboundLedgers().acquire(
1531 lastLedger->header().hash,
1532 lastLedger->header().seq,
1534 }
1535 }
1536 }
1537}
1538
1539bool
1541{
1543 pathFindNewRequest_ = newPFWork("PthFindNewReq", ml);
1544 return pathFindNewRequest_;
1545}
1546
1547bool
1549{
1550 std::scoped_lock const ml(mutex_);
1551 bool const ret = pathFindNewRequest_;
1552 pathFindNewRequest_ = false;
1553 return ret;
1554}
1555
1556// If the order book is radically updated, we need to reprocess all
1557// pathfinding requests.
1558bool
1560{
1562 pathLedger_.reset();
1563
1564 return newPFWork("PthFindOBDB", ml);
1565}
1566
1570bool
1572{
1573 if (!app_.isStopping() && pathFindThread_ < 2 && app_.getPathRequestManager().requestsPending())
1574 {
1575 JLOG(journal_.debug()) << "newPFWork: Creating job. path find threads: " << pathFindThread_;
1576 if (app_.getJobQueue().addJob(JtUpdatePf, name, [this]() { updatePaths(); }))
1577 {
1579 }
1580 }
1581 // If we're stopping don't give callers the expectation that their
1582 // request will be fulfilled, even if it may be serviced.
1583 return pathFindThread_ > 0 && !app_.isStopping();
1584}
1585
1588{
1589 return mutex_;
1590}
1591
1592// The current ledger is the ledger we believe new transactions should go in
1595{
1596 return app_.getOpenLedger().current();
1597}
1598
1601{
1602 return validLedger_.get();
1603}
1604
1605Rules
1607{
1608 // Once we have a guarantee that there's always a last validated
1609 // ledger then we can dispense with the if.
1610
1611 // Return the Rules from the last validated ledger.
1612 if (auto const ledger = getValidatedLedger())
1613 return ledger->rules();
1614
1615 return Rules(app_.config().features);
1616}
1617
1618// This is the last ledger we published to clients and can lag the validated
1619// ledger.
1626
1633
1636{
1637 if (first > last)
1638 {
1639 // In expected usage, this will never happen because "first" is generally initialized to
1640 // "last", "last" is guaranteed to grow monotonically, and "first" either doesn't change
1641 // or grows more slowly.
1642 // LCOV_EXCL_START
1643 UNREACHABLE("xrpl::LedgerMaster::missingFromCompleteLedgerRange : invalid parameters");
1644 return 0;
1645 // LCOV_EXCL_STOP
1646 }
1647
1648 RangeSet<LedgerIndex> const target{range(first, last)};
1649
1650 auto const missing = [&target, this] {
1652 return target - completeLedgers_;
1653 }();
1654
1655 return boost::icl::size(missing);
1656}
1657
1660{
1661 UInt256 const hash = getHashBySeq(ledgerIndex);
1662 return hash.isNonZero() ? getCloseTimeByHash(hash, ledgerIndex) : std::nullopt;
1663}
1664
1667{
1668 auto nodeObject = app_.getNodeStore().fetchNodeObject(ledgerHash, index);
1669 if (nodeObject && (nodeObject->getData().size() >= 120))
1670 {
1671 SerialIter it(nodeObject->getData().data(), nodeObject->getData().size());
1673 {
1674 it.skip(
1675 4 + 8 + 32 + // seq drops parentHash
1676 32 + 32 + 4); // txHash acctHash parentClose
1678 }
1679 }
1680
1681 return std::nullopt;
1682}
1683
1684UInt256
1686{
1687 UInt256 hash = ledgerHistory_.getLedgerHash(index);
1688
1689 if (hash.isNonZero())
1690 return hash;
1691
1692 return app_.getRelationalDatabase().getHashByIndex(index);
1693}
1694
1697{
1698 std::optional<LedgerHash> ledgerHash;
1699
1700 if (auto referenceLedger = validLedger_.get())
1701 ledgerHash = walkHashBySeq(index, referenceLedger, reason);
1702
1703 return ledgerHash;
1704}
1705
1708 std::uint32_t index,
1709 std::shared_ptr<ReadView const> const& referenceLedger,
1710 InboundLedger::Reason reason)
1711{
1712 if (!referenceLedger || (referenceLedger->header().seq < index))
1713 {
1714 // Nothing we can do. No validated ledger.
1715 return std::nullopt;
1716 }
1717
1718 // See if the hash for the ledger we need is in the reference ledger
1719 auto ledgerHash = hashOfSeq(*referenceLedger, index, journal_);
1720 if (ledgerHash)
1721 return ledgerHash;
1722
1723 // The hash is not in the reference ledger. Get another ledger which can
1724 // be located easily and should contain the hash.
1725 LedgerIndex const refIndex = getCandidateLedger(index);
1726 auto const refHash = hashOfSeq(*referenceLedger, refIndex, journal_);
1727 XRPL_ASSERT(refHash, "xrpl::LedgerMaster::walkHashBySeq : found ledger");
1728 if (refHash)
1729 {
1730 // Try the hash and sequence of a better reference ledger just found
1731 auto ledger = ledgerHistory_.getLedgerByHash(*refHash);
1732
1733 if (ledger)
1734 {
1735 try
1736 {
1737 ledgerHash = hashOfSeq(*ledger, index, journal_);
1738 }
1739 catch (SHAMapMissingNode const&)
1740 {
1741 ledger.reset();
1742 }
1743 }
1744
1745 // Try to acquire the complete ledger
1746 if (!ledger)
1747 {
1748 if (auto const l = app_.getInboundLedgers().acquire(*refHash, refIndex, reason))
1749 {
1750 ledgerHash = hashOfSeq(*l, index, journal_);
1751 XRPL_ASSERT(
1752 ledgerHash,
1753 "xrpl::LedgerMaster::walkHashBySeq : has complete "
1754 "ledger");
1755 }
1756 }
1757 }
1758 return ledgerHash;
1759}
1760
1763{
1764 if (index <= validLedgerSeq_)
1765 {
1766 // Always prefer a validated ledger
1767 if (auto valid = validLedger_.get())
1768 {
1769 if (valid->header().seq == index)
1770 return valid;
1771
1772 try
1773 {
1774 auto const hash = hashOfSeq(*valid, index, journal_);
1775
1776 if (hash)
1777 return ledgerHistory_.getLedgerByHash(*hash);
1778 }
1779 catch (std::exception const&) // NOLINT(bugprone-empty-catch)
1780 {
1781 // Missing nodes are already handled
1782 }
1783 }
1784 }
1785
1786 if (auto ret = ledgerHistory_.getLedgerBySeq(index))
1787 return ret;
1788
1789 auto ret = closedLedger_.get();
1790 if (ret && (ret->header().seq == index))
1791 return ret;
1792
1793 clearLedger(index);
1794 return {};
1795}
1796
1799{
1800 if (auto ret = ledgerHistory_.getLedgerByHash(hash))
1801 return ret;
1802
1803 auto ret = closedLedger_.get();
1804 if (ret && (ret->header().hash == hash))
1805 return ret;
1806
1807 return {};
1808}
1809
1810void
1816
1817void
1819{
1820 ledgerHistory_.sweep();
1821 fetchPacks_.sweep();
1822}
1823
1824float
1826{
1827 return ledgerHistory_.getCacheHitRate();
1828}
1829
1830void
1832{
1834 if (seq > 0)
1835 completeLedgers_.erase(range(0u, seq - 1));
1836}
1837
1838void
1840{
1841 ledgerHistory_.clearLedgerCachePrior(seq);
1842}
1843
1844void
1846{
1847 replayData_ = std::move(replay);
1848}
1849
1852{
1853 return std::move(replayData_);
1854}
1855
1856void
1858 std::uint32_t missing,
1859 bool& progress,
1860 InboundLedger::Reason reason,
1862{
1863 ScopeUnlock const sul{sl};
1864 if (auto hash = getLedgerHashForHistory(missing, reason))
1865 {
1866 XRPL_ASSERT(hash->isNonZero(), "xrpl::LedgerMaster::fetchForHistory : found ledger");
1867 auto ledger = getLedgerByHash(*hash);
1868 if (!ledger)
1869 {
1870 if (!app_.getInboundLedgers().isFailure(*hash))
1871 {
1872 ledger = app_.getInboundLedgers().acquire(*hash, missing, reason);
1873 if (!ledger && missing != fetchSeq_ &&
1874 missing > app_.getNodeStore().earliestLedgerSeq())
1875 {
1876 JLOG(journal_.trace()) << "fetchForHistory want fetch pack " << missing;
1877 fetchSeq_ = missing;
1878 getFetchPack(missing, reason);
1879 }
1880 else
1881 {
1882 JLOG(journal_.trace()) << "fetchForHistory no fetch pack for " << missing;
1883 }
1884 }
1885 else
1886 {
1887 JLOG(journal_.debug()) << "fetchForHistory found failed acquire";
1888 }
1889 }
1890 if (ledger)
1891 {
1892 auto seq = ledger->header().seq;
1893 XRPL_ASSERT(seq == missing, "xrpl::LedgerMaster::fetchForHistory : sequence match");
1894 JLOG(journal_.trace()) << "fetchForHistory acquired " << seq;
1895 setFullLedger(ledger, false, false);
1896 int fillInProgress = 0;
1897 {
1899 histLedger_ = ledger;
1900 fillInProgress = fillInProgress_;
1901 }
1902 if (fillInProgress == 0 &&
1903 app_.getRelationalDatabase().getHashByIndex(seq - 1) == ledger->header().parentHash)
1904 {
1905 {
1906 // Previous ledger is in DB
1908 fillInProgress_ = seq;
1909 }
1910 app_.getJobQueue().addJob(
1911 JtAdvance, "TryFill", [this, ledger]() { tryFill(ledger); });
1912 }
1913 progress = true;
1914 }
1915 else
1916 {
1917 std::uint32_t fetchSz = 0;
1918 // Do not fetch ledger sequences lower
1919 // than the earliest ledger sequence
1920 fetchSz = app_.getNodeStore().earliestLedgerSeq();
1921 fetchSz = missing >= fetchSz ? std::min(ledgerFetchSize_, (missing - fetchSz) + 1) : 0;
1922 try
1923 {
1924 for (std::uint32_t i = 0; i < fetchSz; ++i)
1925 {
1926 std::uint32_t const seq = missing - i;
1927 if (auto h = getLedgerHashForHistory(seq, reason))
1928 {
1929 XRPL_ASSERT(
1930 h->isNonZero(),
1931 "xrpl::LedgerMaster::fetchForHistory : "
1932 "prefetched ledger");
1933 app_.getInboundLedgers().acquire(*h, seq, reason);
1934 }
1935 }
1936 }
1937 catch (std::exception const& ex)
1938 {
1939 JLOG(journal_.warn()) << "Threw while prefetching: " << ex.what();
1940 }
1941 }
1942 }
1943 else
1944 {
1945 JLOG(journal_.fatal()) << "Can't find ledger following prevMissing " << missing;
1946 JLOG(journal_.fatal()) << "Pub:" << pubLedgerSeq_ << " Val:" << validLedgerSeq_;
1947 JLOG(journal_.fatal()) << "Ledgers: " << app_.getLedgerMaster().getCompleteLedgers();
1948 JLOG(journal_.fatal()) << "Acquire reason: "
1949 << (reason == InboundLedger::Reason::HISTORY ? "HISTORY"
1950 : "NOT HISTORY");
1951 clearLedger(missing + 1);
1952 progress = true;
1953 }
1954}
1955
1956// Try to publish ledgers, acquire missing ledgers
1957void
1959{
1960 do
1961 {
1962 advanceWork_ = false; // If there's work to do, we'll make progress
1963 bool progress = false;
1964
1965 auto const pubLedgers = findNewLedgersToPublish(sl);
1966 if (pubLedgers.empty())
1967 {
1968 if (!standalone_ && !app_.getFeeTrack().isLoadedLocal() &&
1969 (app_.getJobQueue().getJobCount(JtPuboldledger) < 10) &&
1972 (app_.getNodeStore().getWriteLoad() < kMaxWriteLoadAcquire))
1973 {
1974 // We are in sync, so can acquire
1977 {
1979 missing = prevMissing(
1981 pubLedger_->header().seq,
1982 app_.getNodeStore().earliestLedgerSeq());
1983 }
1984 if (missing)
1985 {
1986 JLOG(journal_.trace()) << "tryAdvance discovered missing " << *missing;
1987 if ((fillInProgress_ == 0 || *missing > fillInProgress_) &&
1991 app_.getSHAMapStore().minimumOnline(),
1992 *missing,
1993 journal_))
1994 {
1995 JLOG(journal_.trace()) << "advanceThread should acquire";
1996 }
1997 else
1998 {
1999 missing = std::nullopt;
2000 }
2001 }
2002 if (missing)
2003 {
2004 fetchForHistory(*missing, progress, reason, sl);
2006 {
2007 JLOG(journal_.debug()) << "tryAdvance found last valid changed";
2008 progress = true;
2009 }
2010 }
2011 }
2012 else
2013 {
2014 histLedger_.reset();
2015 JLOG(journal_.trace()) << "tryAdvance not fetching history";
2016 }
2017 }
2018 else
2019 {
2020 JLOG(journal_.trace())
2021 << "tryAdvance found " << pubLedgers.size() << " ledgers to publish";
2022 for (auto const& ledger : pubLedgers)
2023 {
2024 {
2025 ScopeUnlock const sul{sl};
2026 JLOG(journal_.debug()) << "tryAdvance publishing seq " << ledger->header().seq;
2027 setFullLedger(ledger, true, true);
2028 }
2029
2030 setPubLedger(ledger);
2031
2032 {
2033 ScopeUnlock const sul{sl};
2034 app_.getOPs().pubLedger(ledger);
2035 }
2036 }
2037
2038 app_.getOPs().clearNeedNetworkLedger();
2039 progress = newPFWork("PthFindNewLed", sl);
2040 }
2041 if (progress)
2042 advanceWork_ = true;
2043 } while (advanceWork_);
2044}
2045
2046void
2048{
2049 fetchPacks_.canonicalizeReplaceClient(hash, data);
2050}
2051
2054{
2055 Blob data;
2056 if (fetchPacks_.retrieve(hash, data))
2057 {
2058 fetchPacks_.del(hash, false);
2059 if (hash == sha512Half(makeSlice(data)))
2060 return data;
2061 }
2062 return std::nullopt;
2063}
2064
2065void
2067{
2068 if (!gotFetchPackThread_.test_and_set(std::memory_order_acquire))
2069 {
2070 app_.getJobQueue().addJob(JtLedgerData, "GotFetchPack", [&]() {
2071 app_.getInboundLedgers().gotFetchPack();
2072 gotFetchPackThread_.clear(std::memory_order_release);
2073 });
2074 }
2075}
2076
2103static void
2105 SHAMap const& want,
2106 SHAMap const* have,
2107 std::uint32_t cnt,
2108 protocol::TMGetObjectByHash* into,
2109 std::uint32_t seq,
2110 bool withLeaves = true)
2111{
2112 XRPL_ASSERT(cnt, "xrpl::populateFetchPack : nonzero count input");
2113
2114 Serializer s(1024);
2115
2116 want.visitDifferences(have, [&s, withLeaves, &cnt, into, seq](SHAMapTreeNode const& n) -> bool {
2117 if (!withLeaves && n.isLeaf())
2118 return true;
2119
2120 s.erase();
2122
2123 auto const& hash = n.getHash().asUInt256();
2124
2125 protocol::TMIndexedObject* obj = into->add_objects();
2126 obj->set_ledgerseq(seq);
2127 obj->set_hash(hash.data(), hash.size());
2128 obj->set_data(s.getDataPtr(), s.getLength());
2129
2130 return --cnt != 0;
2131 });
2132}
2133
2134void
2136 std::weak_ptr<Peer> const& wPeer,
2138 UInt256 haveLedgerHash,
2140{
2141 using namespace std::chrono_literals;
2142 if (UptimeClock::now() > uptime + 1s)
2143 {
2144 JLOG(journal_.info()) << "Fetch pack request got stale";
2145 return;
2146 }
2147
2148 if (app_.getFeeTrack().isLoadedLocal() || (getValidatedLedgerAge() > 40s))
2149 {
2150 JLOG(journal_.info()) << "Too busy to make fetch pack";
2151 return;
2152 }
2153
2154 auto peer = wPeer.lock();
2155
2156 if (!peer)
2157 return;
2158
2159 auto have = getLedgerByHash(haveLedgerHash);
2160
2161 if (!have)
2162 {
2163 JLOG(journal_.info()) << "Peer requests fetch pack for ledger we don't have: " << have;
2164 peer->charge(resource::kFeeRequestNoReply, "get_object ledger");
2165 return;
2166 }
2167
2168 if (have->open())
2169 {
2170 JLOG(journal_.warn()) << "Peer requests fetch pack from open ledger: " << have;
2171 peer->charge(resource::kFeeMalformedRequest, "get_object ledger open");
2172 return;
2173 }
2174
2175 if (have->header().seq < getEarliestFetch())
2176 {
2177 JLOG(journal_.debug()) << "Peer requests fetch pack that is too early";
2178 peer->charge(resource::kFeeMalformedRequest, "get_object ledger early");
2179 return;
2180 }
2181
2182 auto want = getLedgerByHash(have->header().parentHash);
2183
2184 if (!want)
2185 {
2186 JLOG(journal_.info()) << "Peer requests fetch pack for ledger whose predecessor we "
2187 << "don't have: " << have;
2188 peer->charge(resource::kFeeRequestNoReply, "get_object ledger no parent");
2189 return;
2190 }
2191
2192 try
2193 {
2194 Serializer hdr(128);
2195
2196 protocol::TMGetObjectByHash reply;
2197 reply.set_query(false);
2198
2199 reply.set_ledgerhash(request->ledgerhash());
2200 reply.set_type(protocol::TMGetObjectByHash::otFETCH_PACK);
2201
2202 // Building a fetch pack:
2203 // 1. Add the header for the requested ledger.
2204 // 2. Add the nodes for the AccountStateMap of that ledger.
2205 // 3. If there are transactions, add the nodes for the
2206 // transactions of the ledger.
2207 // 4. If the FetchPack now contains at least 512 entries then stop.
2208 // 5. If not very much time has elapsed, then loop back and repeat
2209 // the same process adding the previous ledger to the FetchPack.
2210 do
2211 {
2212 std::uint32_t const lSeq = want->header().seq;
2213
2214 {
2215 // Serialize the ledger header:
2216 hdr.erase();
2217
2219 addRaw(want->header(), hdr);
2220
2221 // Add the data
2222 protocol::TMIndexedObject* obj = reply.add_objects();
2223 obj->set_hash(want->header().hash.data(), want->header().hash.size());
2224 obj->set_data(hdr.getDataPtr(), hdr.getLength());
2225 obj->set_ledgerseq(lSeq);
2226 }
2227
2228 populateFetchPack(want->stateMap(), &have->stateMap(), 16384, &reply, lSeq);
2229
2230 // We use nullptr here because transaction maps are per ledger
2231 // and so the requestor is unlikely to already have it.
2232 if (want->header().txHash.isNonZero())
2233 populateFetchPack(want->txMap(), nullptr, 512, &reply, lSeq);
2234
2235 if (reply.objects().size() >= 512)
2236 break;
2237
2238 have = std::move(want);
2239 want = getLedgerByHash(have->header().parentHash);
2240 } while (want && UptimeClock::now() <= uptime + 1s);
2241
2242 auto msg = std::make_shared<Message>(reply, protocol::mtGET_OBJECTS);
2243
2244 JLOG(journal_.info()) << "Built fetch pack with " << reply.objects().size() << " nodes ("
2245 << msg->getBufferSize() << " bytes)";
2246
2247 peer->send(msg);
2248 }
2249 catch (std::exception const& ex)
2250 {
2251 JLOG(journal_.warn()) << "Exception building fetch pack. Exception: " << ex.what();
2252 }
2253}
2254
2257{
2258 return fetchPacks_.getCacheSize();
2259}
2260
2261// Returns the minimum ledger sequence in SQL database, if any.
2264{
2265 return app_.getRelationalDatabase().getMinLedgerSeq();
2266}
2267
2270{
2271 uint32_t first = 0, last = 0;
2272
2273 if (!getValidatedRange(first, last) || last < ledgerSeq)
2274 return {};
2275
2276 auto const lgr = getLedgerBySeq(ledgerSeq);
2277 if (!lgr || lgr->txs.empty())
2278 return {};
2279
2280 for (auto it = lgr->txs.begin(); it != lgr->txs.end(); ++it)
2281 {
2282 if (it->first && it->second && it->second->isFieldPresent(sfTransactionIndex) &&
2283 it->second->getFieldU32(sfTransactionIndex) == txnIndex)
2284 return it->first->getTransactionID();
2285 }
2286
2287 return {};
2288}
2289
2290} // namespace xrpl
T back(T... args)
T back_inserter(T... args)
T begin(T... args)
NetClock::time_point time_point
Provide a light-weight way to check active() before string formatting.
Definition Journal.h:199
A generic endpoint for log messages.
Definition Journal.h:44
Stream trace() const
Severity stream access functions.
Definition Journal.h:338
std::shared_ptr< Collector > Ptr
Definition Collector.h:29
Represents a JSON value.
Definition json_value.h:117
bool isNonZero() const
Definition base_uint.h:567
Holds transactions which were deferred to the next pass of consensus.
std::optional< LedgerIndex > minSqlSeq()
Returns the minimum ledger sequence in SQL database, if any.
LedgerIndex const maxLedgerDifference_
Try to keep a validator from switching from test to live network without first wiping the database.
std::atomic_flag gotFetchPackThread_
Set while a GotFetchPack job is outstanding, so only one ever is.
std::pair< UInt256, LedgerIndex > lastValidLedger_
Fully validated ledger, whether or not we have the ledger resident.
std::size_t getNeededValidations()
Determines how many validations are needed to fully validate a ledger.
bool isCompatible(ReadView const &, beast::Journal::Stream, char const *reason)
Tests whether a view can belong to the same chain as what we validated.
std::shared_ptr< STTx const > popAcctTransaction(std::shared_ptr< STTx const > const &tx)
Get the next transaction held for a particular account if any.
void setValidLedger(std::shared_ptr< Ledger const > const &l)
Adopts a ledger as the last validated one and tells the rest of the server, including the amendment-s...
void switchLCL(std::shared_ptr< Ledger const > const &lastClosed)
Adopts a ledger as the last closed ledger, then either accepts it directly (standalone) or tests it f...
std::recursive_mutex & peekMutex()
Exposes mutex_.
std::chrono::seconds getValidatedLedgerAge()
Stats stats_
The gauges and the collector hook that samples them.
TimeKeeper::time_point upgradeWarningPrevTime_
Time that the previous upgrade warning was issued.
bool pathFindNewRequest_
A pathfinding request arrived and has not been picked up yet.
std::uint32_t const ledgerFetchSize_
Cap on ledgers acquired in one publication or prefetch pass.
std::atomic< std::uint32_t > pubLedgerClose_
Close time of pubLedger_, in seconds since the network epoch; 0 if none.
LedgerIndex getCurrentLedgerIndex()
bool fixIndex(LedgerIndex ledgerIndex, LedgerHash const &ledgerHash)
Corrects the cached sequence-to-hash mapping for one ledger.
bool getValidatedRange(std::uint32_t &minVal, std::uint32_t &maxVal)
Returns Ledgers we have all the nodes for and are indexed: a contiguous part of getFullValidatedRange...
void applyHeldTransactions()
Apply held transactions to the open ledger This is normally called as we close the ledger.
bool storeLedger(std::shared_ptr< Ledger const > ledger)
Adds a ledger to the history cache.
void gotFetchPack(bool progress, std::uint32_t seq)
Signals that fetch-pack data arrived, so waiting acquires can use it.
beast::Journal journal_
Log sink for this component.
void tryFill(std::shared_ptr< Ledger const > ledger)
Walks back from a ledger through the SQL index, marking each ancestor resident until the chain breaks...
std::shared_ptr< Ledger const > getLedgerBySeq(std::uint32_t index)
Finds a ledger by sequence, preferring the validated chain.
void setPubLedger(std::shared_ptr< Ledger const > const &l)
Records how far publication has reached.
bool newPFWork(char const *name, std::unique_lock< std::recursive_mutex > &)
A thread needs to be dispatched to handle pathfinding work of some kind.
void setFullLedger(std::shared_ptr< Ledger const > const &ledger, bool isSynchronous, bool isCurrent)
A new ledger has been accepted as part of the trusted chain: mark it validated and full,...
std::atomic< LedgerIndex > pubLedgerSeq_
Sequence of pubLedger_; 0 if nothing has been published.
void clearPriorLedgers(LedgerIndex seq)
Forgets that we hold any ledger below a sequence, without touching the cached ledgers themselves.
void setBuildingLedger(LedgerIndex index)
Records which ledger consensus is currently building, so validations for it are not chased separately...
std::uint32_t fetchSeq_
Sequence of the last fetch-pack attempt, so the same one is not tried twice in a row.
void consensusBuilt(std::shared_ptr< Ledger const > const &ledger, UInt256 const &consensusHash, json::Value consensus)
Report that the consensus process built a particular ledger.
UInt256 getHashBySeq(std::uint32_t index)
Get a ledger's hash by sequence number using the cache.
bool isCaughtUp(std::string &reason)
Decides whether the node is close enough to the network to serve clients.
std::size_t getFetchPackCacheSize() const
std::vector< std::shared_ptr< Ledger const > > findNewLedgersToPublish(std::unique_lock< std::recursive_mutex > &)
Collects the next run of validated ledgers to publish, acquiring or replaying the ones that are missi...
std::atomic< LedgerIndex > buildingLedgerSeq_
Sequence consensus is building; 0 once it is built.
std::optional< NetClock::time_point > getCloseTimeByHash(LedgerHash const &ledgerHash, LedgerIndex ledgerIndex)
Reads a close time straight out of the serialized header in the node store.
void clearLedger(std::uint32_t seq)
Marks a ledger no longer resident.
void clearLedgerCachePrior(LedgerIndex seq)
Drops cached ledgers below a sequence.
TaggedCache< UInt256, Blob > fetchPacks_
Fetch-pack nodes keyed by node hash.
void sweep()
Expires stale entries from the ledger history and fetch-pack caches.
bool newPathRequest()
Notes a new pathfinding request and dispatches a worker if one is due.
std::unique_ptr< LedgerReplay > replayData_
A set of transactions to replay during the next close.
LedgerMaster(Application &app, Stopwatch &stopwatch, beast::insight::Collector::Ptr const &collector, beast::Journal journal)
Constructs the tracker; reads history and fetch limits from the config.
std::optional< UInt256 > txnIdFromIndex(uint32_t ledgerSeq, uint32_t txnIndex)
Iff a txn exists at the specified ledger and offset then return its txnid.
std::atomic< LedgerIndex > validLedgerSeq_
Sequence of validLedger_; 0 if nothing is validated.
std::shared_ptr< Ledger const > getClosedLedger()
The finalized ledger is the last closed/accepted ledger.
std::shared_ptr< Ledger const > getLedgerByHash(UInt256 const &hash)
void setLedgerRangePresent(std::uint32_t minV, std::uint32_t maxV)
Records a closed range of sequences as resident.
std::optional< NetClock::time_point > getCloseTimeBySeq(LedgerIndex ledgerIndex)
std::shared_ptr< Ledger const > getValidatedLedger()
The validated ledger is the last fully validated ledger.
void fetchForHistory(std::uint32_t missing, bool &progress, InboundLedger::Reason reason, std::unique_lock< std::recursive_mutex > &)
Starts acquiring one missing historical ledger.
bool isValidated(ReadView const &ledger)
Tests whether a ledger is on the validated chain, using the skip list when the ledger does not alread...
void fixMismatch(ReadView const &ledger)
Walks back from a ledger, dropping every resident ledger it cannot confirm against that ledger's skip...
LedgerIndex getValidLedgerIndex()
LedgerHolder validLedger_
The highest-sequence ledger we have fully accepted.
CanonicalTXSet heldTransactions_
Transactions deferred to the next open ledger, in canonical order.
bool const standalone_
The server is in standalone mode.
std::shared_ptr< Ledger const > pathLedger_
The last ledger we did pathfinding against.
std::shared_ptr< Ledger const > pubLedger_
The last ledger we have published.
std::shared_ptr< ReadView const > getPublishedLedger()
This is the last ledger we published to clients and can lag the validated ledger.
bool newOrderBookDB()
If the order book is radically updated, we need to reprocess all pathfinding requests.
std::uint32_t const fetchDepth_
How many ledgers before the closed ledger do we allow peers to request?
std::recursive_mutex mutex_
Guards pubLedger_, pathLedger_, lastValidLedger_, the held transactions and the job flags other than ...
void collectMetrics()
Samples both ledger ages into the gauges.
Rules getValidatedRules()
The Rules are in the last fully validated ledger if there is one.
bool advanceWork_
Publish thread has work to do.
LedgerHistory ledgerHistory_
Cache of ledgers by hash and of validated sequence-to-hash mappings.
std::optional< LedgerHash > walkHashBySeq(std::uint32_t index, InboundLedger::Reason reason)
Walk to a ledger's hash using the skip list.
std::chrono::seconds getPublishedLedgerAge()
bool canBeCurrent(std::shared_ptr< Ledger const > const &ledger)
Check the sequence number and parent close time of a ledger against our clock and last validated ledg...
LedgerHolder closedLedger_
The ledger that most recently closed.
bool getFullValidatedRange(std::uint32_t &minVal, std::uint32_t &maxVal)
Returns Ledgers we have all the nodes for: the contiguous resident range ending at the published ledg...
std::optional< LedgerHash > getLedgerHashForHistory(LedgerIndex index, InboundLedger::Reason reason)
Finds the hash of a ledger the history back-fill wants, preferring the last back-filled ledger as the...
void tryAdvance()
Signals that the ledger stream may be able to make progress.
int fillInProgress_
Sequence a tryFill() job is currently walking back from, or 0 when no fill is running.
int pathFindThread_
Pathfinding jobs queued or running.
RangeSet< std::uint32_t > completeLedgers_
Sequences of the ledgers this node believes it holds in full.
void checkAccept(std::shared_ptr< Ledger const > const &ledger)
If the ledger passes canBeCurrent(), is newer than the validated ledger and has a quorum,...
std::optional< Blob > getFetchPack(UInt256 const &hash) override
Consumes one cached fetch-pack node.
std::uint32_t const ledgerHistorySize_
How much history do we want to keep.
std::atomic< std::uint32_t > validLedgerSign_
Sign time of validLedger_, in seconds since the network epoch: the median of its trusted validations,...
void doAdvance(std::unique_lock< std::recursive_mutex > &)
Try to publish ledgers, acquire missing ledgers.
void addHeldTransaction(std::shared_ptr< Transaction > const &trans)
Defers a transaction to the next open ledger.
std::shared_ptr< ReadView const > getCurrentLedger()
The current ledger is the ledger we believe new transactions should go in.
std::recursive_mutex completeLock_
Guards completeLedgers_ only.
std::string getCompleteLedgers() const
void takeReplay(std::unique_ptr< LedgerReplay > replay)
Stores a transaction set to replay when the next ledger closes.
void updatePaths()
Runs one pathfinding pass per new validated ledger or new request, and exits when neither is new,...
std::unique_ptr< LedgerReplay > releaseReplay()
std::size_t missingFromCompleteLedgerRange(LedgerIndex first, LedgerIndex last) const
Counts gaps in a closed sequence interval.
bool advanceThread_
An AdvanceLedger job is queued or running.
bool haveLedger(std::uint32_t seq) const
std::uint32_t getEarliestFetch()
Get the earliest ledger we will let peers fetch.
void makeFetchPack(std::weak_ptr< Peer > const &wPeer, std::shared_ptr< protocol::TMGetObjectByHash > const &request, UInt256 haveLedgerHash, UptimeClock::time_point uptime)
Builds a fetch pack of the ledgers preceding the one a peer says it has, and sends it.
std::shared_ptr< Ledger const > histLedger_
The last ledger we handled fetching history.
void addFetchPack(UInt256 const &hash, std::shared_ptr< Blob > data)
Caches one fetch-pack node received from a peer.
Application & app_
Owning application, the route to every other subsystem.
void failedSave(std::uint32_t seq, UInt256 const &hash)
Marks a ledger no longer resident after its save failed, and starts fetching it again.
std::chrono::time_point< NetClock > time_point
Definition chrono.h:48
std::chrono::duration< rep, period > duration
Definition chrono.h:47
A view into a ledger.
Definition ReadView.h:41
virtual LedgerHeader const & header() const =0
Returns information about the ledger.
virtual bool open() const =0
Returns true if this reflects an open ledger.
Rules controlling protocol behavior.
Definition Rules.h:40
UInt256 const & asUInt256() const
Definition SHAMapHash.h:26
SHAMapHash const & getHash() const
Return the hash of this node.
virtual void serializeWithPrefix(Serializer &) const =0
Serialize the node in a format appropriate for hashing.
virtual bool isLeaf() const =0
Determines if this is a leaf node.
void visitDifferences(SHAMap const *have, std::function< bool(SHAMapTreeNode const &)> const &) const
Visit every node in this SHAMap that is not present in the specified SHAMap.
Automatically unlocks and re-locks a unique_lock object.
Definition scope.h:197
void const * getDataPtr() const
Definition Serializer.h:294
int getLength() const
Definition Serializer.h:304
std::chrono::time_point< UptimeClock > time_point
Definition UptimeClock.h:24
static time_point now()
Persistency layer for NodeObject.
Definition Database.h:45
std::shared_ptr< NodeObject > fetchNodeObject(UInt256 const &hash, std::uint32_t ledgerSeq=0, FetchType fetchType=FetchType::Synchronous, bool duplicate=false)
Fetch a node object.
T copy(T... args)
T count(T... args)
T data(T... args)
T duration_cast(T... args)
T empty(T... args)
T end(T... args)
T endl(T... args)
T find(T... args)
T lock(T... args)
T make_pair(T... args)
T make_shared(T... args)
T max(T... args)
T min(T... args)
constexpr Zero kZero
Definition Zero.h:30
STL namespace.
bool isXrpldVersion(std::uint64_t version)
Check if the encoded software version is an xrpld software version.
bool isNewerVersion(std::uint64_t version)
Check if the version is newer than the local node's xrpld software version.
TER valid(STTx const &tx, ReadView const &view, AccountID const &src, beast::Journal j)
Charge const kFeeRequestNoReply
Charge const kFeeMalformedRequest
Schedule of fees charged for imposing load on the server.
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
static constexpr int kMaxLedgerGap
Don't catch up more than 100 ledgers (cannot exceed 256).
bool set(T &target, std::string const &name, Section const &section)
Set a value from a configuration Section If the named value is not found or doesn't parse as a T,...
std::optional< UInt256 > hashOfSeq(ReadView const &ledger, LedgerIndex seq, beast::Journal journal)
Return the hash of a ledger by sequence.
Definition View.cpp:323
beast::AbstractClock< std::chrono::steady_clock > Stopwatch
A clock for measuring elapsed time.
Definition chrono.h:90
bool isCurrent(ValidationParms const &p, NetClock::time_point now, NetClock::time_point signTime, NetClock::time_point seenTime)
Whether a validation is still current.
std::optional< T > prevMissing(RangeSet< T > const &rs, T t, T minVal=0)
Find the largest value not in the set that is less than a given value.
Definition RangeSet.h:181
bool pendSaveValidated(ServiceRegistry &registry, std::shared_ptr< Ledger const > const &ledger, bool isSynchronous, bool isCurrent)
Save, or arrange to save, a fully-validated ledger.
std::uint32_t LedgerIndex
A ledger index.
Definition Protocol.h:382
Stopwatch & stopwatch()
Returns an instance of a wall clock.
Definition chrono.h:101
ClosedInterval< T > range(T low, T high)
Create a closed range interval.
Definition RangeSet.h:37
constexpr std::size_t calculatePercent(std::size_t count, std::size_t total)
Calculate one number divided by another number in percentage.
UInt256 LedgerHash
static void populateFetchPack(SHAMap const &want, SHAMap const *have, std::uint32_t cnt, protocol::TMGetObjectByHash *into, std::uint32_t seq, bool withLeaves=true)
Populate a fetch pack with data from the map the recipient wants.
constexpr Dest safeCast(Src s) noexcept
Definition safe_cast.h:21
std::string to_string(BaseUInt< Bits, Tag > const &a)
Definition base_uint.h:657
void logicError(std::string const &how) noexcept
Called when faulty logic causes a broken invariant.
bool areCompatible(ReadView const &validLedger, ReadView const &testLedger, beast::Journal::Stream &s, char const *reason)
Return false if the test ledger is provably incompatible with the valid ledger, that is,...
Definition View.cpp:213
LedgerIndex getCandidateLedger(LedgerIndex requested)
Find a ledger index from which we could easily get the requested ledger.
Definition View.h:152
Slice makeSlice(std::array< T, N > const &a)
Definition Slice.h:228
BaseUInt< 256 > UInt256
Definition base_uint.h:580
@ JtPuboldledger
Definition Job.h:30
@ JtLedgerData
Definition Job.h:52
@ JtAdvance
Definition Job.h:53
@ JtUpdatePf
Definition Job.h:42
void addRaw(LedgerHeader const &, Serializer &, bool includeHash=false)
boost::icl::interval_set< T, std::less, ClosedInterval< T > > RangeSet
A set of closed intervals over the domain T.
Definition RangeSet.h:54
@ LedgerMaster
ledger master data for signing
Definition HashPrefix.h:59
std::unordered_map< Key, Value, Hash, Pred, Allocator > HashMap
std::chrono::duration< int, std::ratio_multiply< Days::period, std::ratio< 7 > > > Weeks
Definition chrono.h:22
std::vector< unsigned char > Blob
Storage for linear binary data.
Definition Blob.h:11
static constexpr int kMaxWriteLoadAcquire
Don't acquire history if write load is too high.
static constexpr std::chrono::minutes kMaxLedgerAgeAcquire
Don't acquire history if ledger is too old.
Sha512HalfHasher::result_type sha512Half(Args const &... args)
Returns the SHA512-Half of a series of objects.
Definition digest.h:215
static bool shouldAcquire(std::uint32_t const currentLedger, std::uint32_t const ledgerHistory, std::optional< LedgerIndex > const minimumOnline, std::uint32_t const candidateLedger, beast::Journal j)
Helper function for LedgerMaster::doAdvance() Return true if candidateLedger should be fetched from t...
T has_value(T... args)
T push_back(T... args)
T reserve(T... args)
T size(T... args)
T sort(T... args)
T str(T... args)
T swap(T... args)
T time_point_cast(T... args)
T time_since_epoch(T... args)
T what(T... args)