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_set.hpp>
61
62#include <xrpl.pb.h>
63
64#include <algorithm>
65#include <atomic>
66#include <chrono>
67#include <cstdint>
68#include <cstdlib>
69#include <exception>
70#include <functional>
71#include <iostream>
72#include <iterator>
73#include <map>
74#include <memory>
75#include <mutex>
76#include <optional>
77#include <ostream>
78#include <sstream>
79#include <utility>
80#include <vector>
81
82namespace xrpl {
83
84// Don't catch up more than 100 ledgers (cannot exceed 256)
85static constexpr int kMaxLedgerGap{100};
86
87// Don't acquire history if ledger is too old
89
90// Don't acquire history if write load is too high
91static constexpr int kMaxWriteLoadAcquire{8192};
92
93// Helper function for LedgerMaster::doAdvance()
94// Return true if candidateLedger should be fetched from the network.
95static bool
97 std::uint32_t const currentLedger,
98 std::uint32_t const ledgerHistory,
99 std::optional<LedgerIndex> const minimumOnline,
100 std::uint32_t const candidateLedger,
102{
103 bool const ret = [&]() {
104 // Fetch ledger if it may be the current ledger
105 if (candidateLedger >= currentLedger)
106 return true;
107
108 // Or if it is within our configured history range:
109 if (currentLedger - candidateLedger <= ledgerHistory)
110 return true;
111
112 // Or if greater than or equal to a specific minimum ledger.
113 // Do nothing if the minimum ledger to keep online is unknown.
114 return minimumOnline.has_value() && candidateLedger >= *minimumOnline;
115 }();
116
117 JLOG(j.trace()) << "Missing ledger " << candidateLedger << (ret ? " should" : " should NOT")
118 << " be acquired";
119 return ret;
120}
121
123 Application& app,
125 beast::insight::Collector::ptr const& collector,
126 beast::Journal journal)
127 : app_(app)
128 , journal_(journal)
129 , ledgerHistory_(collector, app)
130 , standalone_(app_.config().standalone())
131 , fetchDepth_(app_.getSHAMapStore().clampFetchDepth(app_.config().fetchDepth))
132 , ledgerHistorySize_(app_.config().ledgerHistory)
133 , ledgerFetchSize_(app_.config().getValueFor(SizedItem::LedgerFetch))
134 , fetchPacks_(
135 "FetchPack",
136 65536,
137 std::chrono::seconds{45},
138 stopwatch,
139 app_.getJournal("TaggedCache"))
140 , stats_([this] { collectMetrics(); }, collector)
141{
142}
143
146{
147 return app_.getOpenLedger().current()->header().seq;
148}
149
155
156bool
158{
159 auto validLedger = getValidatedLedger();
160
161 if (validLedger && !areCompatible(*validLedger, view, s, reason))
162 {
163 return false;
164 }
165
166 {
167 std::scoped_lock const sl(mutex_);
168
169 if ((lastValidLedger_.second != 0) &&
170 !areCompatible(lastValidLedger_.first, lastValidLedger_.second, view, s, reason))
171 {
172 return false;
173 }
174 }
175
176 return true;
177}
178
181{
182 using namespace std::chrono_literals;
183 std::chrono::seconds const pubClose{pubLedgerClose_.load()};
184 if (pubClose == 0s)
185 {
186 JLOG(journal_.debug()) << "No published ledger";
187 return weeks{2};
188 }
189
190 std::chrono::seconds ret = app_.getTimeKeeper().closeTime().time_since_epoch();
191 ret -= pubClose;
192 ret = (ret > 0s) ? ret : 0s;
193 static std::chrono::seconds kLastRet = -1s;
194
195 if (ret != kLastRet)
196 {
197 JLOG(journal_.trace()) << "Published ledger age is " << ret.count();
198 kLastRet = ret;
199 }
200 return ret;
201}
202
205{
206 using namespace std::chrono_literals;
207
208 std::chrono::seconds const valClose{validLedgerSign_.load()};
209 if (valClose == 0s)
210 {
211 JLOG(journal_.debug()) << "No validated ledger";
212 return weeks{2};
213 }
214
215 std::chrono::seconds ret = app_.getTimeKeeper().closeTime().time_since_epoch();
216 ret -= valClose;
217 ret = (ret > 0s) ? ret : 0s;
218 static std::chrono::seconds kLastRet = -1s;
219
220 if (ret != kLastRet)
221 {
222 JLOG(journal_.trace()) << "Validated ledger age is " << ret.count();
223 kLastRet = ret;
224 }
225 return ret;
226}
227
228bool
230{
231 using namespace std::chrono_literals;
232
233 if (getPublishedLedgerAge() > 3min)
234 {
235 reason = "No recently-published ledger";
236 return false;
237 }
238 std::uint32_t const validClose = validLedgerSign_.load();
239 std::uint32_t const pubClose = pubLedgerClose_.load();
240 if ((validClose == 0u) || (pubClose == 0u))
241 {
242 reason = "No published ledger";
243 return false;
244 }
245 if (validClose > (pubClose + 90))
246 {
247 reason = "Published ledger lags validated ledger";
248 return false;
249 }
250 return true;
251}
252
253void
255{
257 std::optional<uint256> consensusHash;
258
259 if (!standalone_)
260 {
261 auto validations = app_.getValidators().negativeUNLFilter(
262 app_.getValidations().getTrustedForLedger(l->header().hash, l->header().seq));
263 times.reserve(validations.size());
264 for (auto const& val : validations)
265 times.push_back(val->getSignTime());
266
267 if (!validations.empty())
268 consensusHash = validations.front()->getConsensusHash();
269 }
270
271 NetClock::time_point signTime;
272
273 if (!times.empty() && times.size() >= app_.getValidators().quorum())
274 {
275 // Calculate the sample median
276 std::ranges::sort(times);
277 auto const t0 = times[(times.size() - 1) / 2];
278 auto const t1 = times[times.size() / 2];
279 signTime = t0 + (t1 - t0) / 2;
280 }
281 else
282 {
283 signTime = l->header().closeTime;
284 }
285
286 validLedger_.set(l);
287 validLedgerSign_ = signTime.time_since_epoch().count();
288 XRPL_ASSERT(
289 validLedgerSeq_ || !app_.getMaxDisallowedLedger() ||
290 l->header().seq + maxLedgerDifference_ > app_.getMaxDisallowedLedger(),
291 "xrpl::LedgerMaster::setValidLedger : valid ledger sequence");
293 validLedgerSeq_ = l->header().seq;
294
295 app_.getOPs().updateLocalTx(*l);
296 app_.getSHAMapStore().onLedgerClosed(getValidatedLedger());
297 ledgerHistory_.validatedLedger(l, consensusHash);
298 app_.getAmendmentTable().doValidatedLedger(l);
299 if (!app_.getOPs().isBlocked())
300 {
301 if (app_.getAmendmentTable().hasUnsupportedEnabled())
302 {
303 JLOG(journal_.error()) << "One or more unsupported amendments "
304 "activated: server blocked.";
305 app_.getOPs().setAmendmentBlocked();
306 }
307 else if (!app_.getOPs().isAmendmentWarned() || l->isFlagLedger())
308 {
309 // Amendments can lose majority, so re-check periodically (every
310 // flag ledger), and clear the flag if appropriate. If an unknown
311 // amendment gains majority log a warning as soon as it's
312 // discovered, then again every flag ledger until the operator
313 // upgrades, the amendment loses majority, or the amendment goes
314 // live and the node gets blocked. Unlike being amendment blocked,
315 // this message may be logged more than once per session, because
316 // the node will otherwise function normally, and this gives
317 // operators an opportunity to see and resolve the warning.
318 if (auto const first = app_.getAmendmentTable().firstUnsupportedExpected())
319 {
320 JLOG(journal_.error()) << "One or more unsupported amendments "
321 "reached majority. Upgrade before "
322 << to_string(*first)
323 << " to prevent your server from "
324 "becoming amendment blocked.";
325 app_.getOPs().setAmendmentWarned();
326 }
327 else
328 {
329 app_.getOPs().clearAmendmentWarned();
330 }
331 }
332 }
333}
334
335void
337{
338 pubLedger_ = l;
339 pubLedgerClose_ = l->header().closeTime.time_since_epoch().count();
340 pubLedgerSeq_ = l->header().seq;
341}
342
343void
345{
346 std::scoped_lock const ml(mutex_);
347 heldTransactions_.insert(transaction->getSTransaction());
348}
349
350// Validate a ledger's close time and sequence number if we're considering
351// jumping to that ledger. This helps defend against some rare hostile or
352// diverged majority scenarios.
353bool
355{
356 XRPL_ASSERT(ledger, "xrpl::LedgerMaster::canBeCurrent : non-null input");
357
358 // Never jump to a candidate ledger that precedes our
359 // last validated ledger
360
361 auto validLedger = getValidatedLedger();
362 if (validLedger && (ledger->header().seq < validLedger->header().seq))
363 {
364 JLOG(journal_.trace()) << "Candidate for current ledger has low seq "
365 << ledger->header().seq << " < " << validLedger->header().seq;
366 return false;
367 }
368
369 // Ensure this ledger's parent close time is within five minutes of
370 // our current time. If we already have a known fully-valid ledger
371 // we perform this check. Otherwise, we only do it if we've built a
372 // few ledgers as our clock can be off when we first start up
373
374 auto closeTime = app_.getTimeKeeper().closeTime();
375 auto ledgerClose = ledger->header().parentCloseTime;
376
377 using namespace std::chrono_literals;
378 if ((validLedger || (ledger->header().seq > 10)) &&
379 ((std::max(closeTime, ledgerClose) - std::min(closeTime, ledgerClose)) > 5min))
380 {
381 JLOG(journal_.warn()) << "Candidate for current ledger has close time "
382 << to_string(ledgerClose) << " at network time "
383 << to_string(closeTime) << " seq " << ledger->header().seq;
384 return false;
385 }
386
387 if (validLedger)
388 {
389 // Sequence number must not be too high. We allow ten ledgers
390 // for time inaccuracies plus a maximum run rate of one ledger
391 // every two seconds. The goal is to prevent a malicious ledger
392 // from increasing our sequence unreasonably high
393
394 LedgerIndex maxSeq = validLedger->header().seq + 10;
395
396 if (closeTime > validLedger->header().parentCloseTime)
397 {
399 closeTime - validLedger->header().parentCloseTime)
400 .count() /
401 2;
402 }
403
404 if (ledger->header().seq > maxSeq)
405 {
406 JLOG(journal_.warn()) << "Candidate for current ledger has high seq "
407 << ledger->header().seq << " > " << maxSeq;
408 return false;
409 }
410
411 JLOG(journal_.trace()) << "Acceptable seq range: " << validLedger->header().seq
412 << " <= " << ledger->header().seq << " <= " << maxSeq;
413 }
414
415 return true;
416}
417
418void
420{
421 XRPL_ASSERT(lastClosed, "xrpl::LedgerMaster::switchLCL : non-null input");
422 if (!lastClosed->isImmutable())
423 logicError("mutable ledger in switchLCL");
424
425 if (lastClosed->open())
426 logicError("The new last closed ledger is open!");
427
428 {
429 std::scoped_lock const ml(mutex_);
430 closedLedger_.set(lastClosed);
431 }
432
433 if (standalone_)
434 {
435 setFullLedger(lastClosed, true, false);
436 tryAdvance();
437 }
438 else
439 {
440 checkAccept(lastClosed);
441 }
442}
443
444bool
445LedgerMaster::fixIndex(LedgerIndex ledgerIndex, LedgerHash const& ledgerHash)
446{
447 return ledgerHistory_.fixIndex(ledgerIndex, ledgerHash);
448}
449
450bool
452{
453 bool const validated = ledger->header().validated;
454 // Returns true if we already had the ledger
455 return ledgerHistory_.insert(ledger, validated);
456}
457
464void
466{
467 CanonicalTXSet const set = [this]() {
468 std::scoped_lock const sl(mutex_);
469 // VFALCO NOTE The hash for an open ledger is undefined so we use
470 // something that is a reasonable substitute.
471 CanonicalTXSet set(app_.getOpenLedger().current()->header().parentHash);
473 return set;
474 }();
475
476 if (!set.empty())
477 app_.getOPs().processTransactionSet(set);
478}
479
482{
483 std::scoped_lock const sl(mutex_);
484
485 return heldTransactions_.popAcctTransaction(tx);
486}
487
488void
493
494bool
496{
498 return boost::icl::contains(completeLedgers_, seq);
499}
500
501void
507
508bool
510{
511 if (ledger.open())
512 return false;
513
514 if (ledger.header().validated)
515 return true;
516
517 auto const seq = ledger.header().seq;
518 try
519 {
520 // Use the skip list in the last validated ledger to see if ledger
521 // comes before the last validated ledger (and thus has been
522 // validated).
523 auto const hash = walkHashBySeq(seq, InboundLedger::Reason::GENERIC);
524
525 if (!hash || ledger.header().hash != *hash)
526 {
527 // This ledger's hash is not the hash of the validated ledger
528 if (hash)
529 {
530 XRPL_ASSERT(hash->isNonZero(), "xrpl::LedgerMaster::isValidated : nonzero hash");
531 uint256 const valHash = app_.getRelationalDatabase().getHashByIndex(seq);
532 if (valHash == ledger.header().hash)
533 {
534 // SQL database doesn't match ledger chain
535 clearLedger(seq);
536 }
537 }
538 return false;
539 }
540 }
541 catch (SHAMapMissingNode const& mn)
542 {
543 JLOG(journal_.warn()) << "Ledger #" << seq << ": " << mn.what();
544 return false;
545 }
546
547 // Mark ledger as validated to save time if we see it again.
548 ledger.header().validated = true;
549 return true;
550}
551
552// returns Ledgers we have all the nodes for
553bool
555{
556 // Validated ledger is likely not stored in the DB yet so we use the
557 // published ledger which is.
558 maxVal = pubLedgerSeq_.load();
559
560 if (maxVal == 0u)
561 return false;
562
564 {
566 maybeMin = prevMissing(completeLedgers_, maxVal);
567 }
568
569 if (maybeMin == std::nullopt)
570 {
571 minVal = maxVal;
572 }
573 else
574 {
575 minVal = 1 + *maybeMin;
576 }
577
578 return true;
579}
580
581// Returns Ledgers we have all the nodes for and are indexed
582bool
584{
585 if (!getFullValidatedRange(minVal, maxVal))
586 return false;
587
588 // Remove from the validated range any ledger sequences that may not be
589 // fully updated in the database yet
590
591 auto const pendingSaves = app_.getPendingSaves().getSnapshot();
592
593 if (!pendingSaves.empty() && ((minVal != 0) || (maxVal != 0)))
594 {
595 // Ensure we shrink the tips as much as possible. If we have 7-9 and
596 // 8,9 are invalid, we don't want to see the 8 and shrink to just 9
597 // because then we'll have nothing when we could have 7.
598 while (pendingSaves.contains(maxVal))
599 --maxVal;
600 while (pendingSaves.contains(minVal))
601 ++minVal;
602
603 // Best effort for remaining exclusions
604 for (auto v : pendingSaves)
605 {
606 if ((v.first >= minVal) && (v.first <= maxVal))
607 {
608 if (v.first > ((minVal + maxVal) / 2))
609 {
610 maxVal = v.first - 1;
611 }
612 else
613 {
614 minVal = v.first + 1;
615 }
616 }
617 }
618
619 if (minVal > maxVal)
620 minVal = maxVal = 0;
621 }
622
623 return true;
624}
625
626// Get the earliest ledger we will let peers fetch
629{
630 // The earliest ledger we will let people fetch is ledger zero,
631 // unless that creates a larger range than allowed
632 std::uint32_t e = getClosedLedger()->header().seq;
633
634 if (e > fetchDepth_)
635 {
636 e -= fetchDepth_;
637 }
638 else
639 {
640 e = 0;
641 }
642 return e;
643}
644
645void
647{
648 std::uint32_t seq = ledger->header().seq;
649 uint256 prevHash = ledger->header().parentHash;
650
652
653 std::uint32_t minHas = seq;
654 std::uint32_t maxHas = seq;
655
656 node_store::Database& nodeStore{app_.getNodeStore()};
657 while (!app_.getJobQueue().isStopping() && seq > 0)
658 {
659 {
660 std::scoped_lock const ml(mutex_);
661 minHas = seq;
662 --seq;
663
664 if (haveLedger(seq))
665 break;
666 }
667
668 auto it(ledgerHashes.find(seq));
669
670 if (it == ledgerHashes.end())
671 {
672 if (app_.isStopping())
673 return;
674
675 {
677 completeLedgers_.insert(range(minHas, maxHas));
678 }
679 maxHas = minHas;
680 ledgerHashes =
681 app_.getRelationalDatabase().getHashesByIndex((seq < 500) ? 0 : (seq - 499), seq);
682 it = ledgerHashes.find(seq);
683
684 if (it == ledgerHashes.end())
685 break;
686
687 if (!nodeStore.fetchNodeObject(
688 ledgerHashes.begin()->second.ledgerHash, ledgerHashes.begin()->first))
689 {
690 // The ledger is not backed by the node store
691 JLOG(journal_.warn())
692 << "SQL DB ledger sequence " << seq << " mismatches node store";
693 break;
694 }
695 }
696
697 if (it->second.ledgerHash != prevHash)
698 break;
699
700 prevHash = it->second.parentHash;
701 }
702
703 {
705 completeLedgers_.insert(range(minHas, maxHas));
706 }
707 {
708 std::scoped_lock const ml(mutex_);
709 fillInProgress_ = 0;
710 tryAdvance();
711 }
712}
713
717void
719{
720 LedgerIndex const ledgerIndex = missing + 1;
721
722 auto const haveHash{getLedgerHashForHistory(ledgerIndex, reason)};
723 if (!haveHash || haveHash->isZero())
724 {
725 JLOG(journal_.error()) << "No hash for fetch pack. Missing Index " << missing;
726 return;
727 }
728
729 // Select target Peer based on highest score. The score is randomized
730 // but biased in favor of Peers with low latency.
732 {
733 int maxScore = 0;
734 auto peerList = app_.getOverlay().getActivePeers();
735 for (auto const& peer : peerList)
736 {
737 if (peer->hasRange(missing, missing + 1))
738 {
739 int const score = peer->getScore(true);
740 if (!target || (score > maxScore))
741 {
742 target = peer;
743 maxScore = score;
744 }
745 }
746 }
747 }
748
749 if (target)
750 {
751 protocol::TMGetObjectByHash tmBH;
752 tmBH.set_query(true);
753 tmBH.set_type(protocol::TMGetObjectByHash::otFETCH_PACK);
754 tmBH.set_ledgerhash(haveHash->begin(), 32);
755 auto packet = std::make_shared<Message>(tmBH, protocol::mtGET_OBJECTS);
756
757 target->send(packet);
758 JLOG(journal_.trace()) << "Requested fetch pack for " << missing;
759 }
760 else
761 {
762 JLOG(journal_.debug()) << "No peer for fetch pack";
763 }
764}
765
766void
768{
769 int invalidate = 0;
771
772 for (std::uint32_t lSeq = ledger.header().seq - 1; lSeq > 0; --lSeq)
773 {
774 if (haveLedger(lSeq))
775 {
776 try
777 {
778 hash = hashOfSeq(ledger, lSeq, journal_);
779 }
780 catch (std::exception const& ex)
781 {
782 JLOG(journal_.warn())
783 << "fixMismatch encounters partial ledger. Exception: " << ex.what();
784 clearLedger(lSeq);
785 return;
786 }
787
788 if (hash)
789 {
790 // try to close the seam
791 auto otherLedger = getLedgerBySeq(lSeq);
792
793 if (otherLedger && (otherLedger->header().hash == *hash))
794 {
795 // we closed the seam
796 if (invalidate != 0)
797 {
798 JLOG(journal_.warn()) << "Match at " << lSeq << ", " << invalidate
799 << " prior ledgers invalidated";
800 }
801
802 return;
803 }
804 }
805
806 clearLedger(lSeq);
807 ++invalidate;
808 }
809 }
810
811 // all prior ledgers invalidated
812 if (invalidate != 0)
813 {
814 JLOG(journal_.warn()) << "All " << invalidate << " prior ledgers invalidated";
815 }
816}
817
818void
820 std::shared_ptr<Ledger const> const& ledger,
821 bool isSynchronous,
822 bool isCurrent)
823{
824 // A new ledger has been accepted as part of the trusted chain
825 JLOG(journal_.debug()) << "Ledger " << ledger->header().seq
826 << " accepted :" << ledger->header().hash;
827 XRPL_ASSERT(
828 ledger->stateMap().getHash().isNonZero(),
829 "xrpl::LedgerMaster::setFullLedger : nonzero ledger state hash");
830
831 ledger->setValidated();
832 ledger->setFull();
833
834 if (isCurrent)
835 ledgerHistory_.insert(ledger, true);
836
837 {
838 // Check the SQL database's entry for the sequence before this
839 // ledger, if it's not this ledger's parent, invalidate it
840 uint256 const prevHash =
841 app_.getRelationalDatabase().getHashByIndex(ledger->header().seq - 1);
842 if (prevHash.isNonZero() && prevHash != ledger->header().parentHash)
843 clearLedger(ledger->header().seq - 1);
844 }
845
846 pendSaveValidated(app_, ledger, isSynchronous, isCurrent);
847
848 {
850 completeLedgers_.insert(ledger->header().seq);
851 }
852
853 {
854 std::scoped_lock const ml(mutex_);
855
856 if (ledger->header().seq > validLedgerSeq_)
857 setValidLedger(ledger);
858 if (!pubLedger_)
859 {
860 setPubLedger(ledger);
861 app_.getOrderBookDB().setup(ledger);
862 }
863
864 if (ledger->header().seq != 0 && haveLedger(ledger->header().seq - 1))
865 {
866 // we think we have the previous ledger, double check
867 auto prevLedger = getLedgerBySeq(ledger->header().seq - 1);
868
869 if (!prevLedger || (prevLedger->header().hash != ledger->header().parentHash))
870 {
871 JLOG(journal_.warn()) << "Acquired ledger invalidates previous ledger: "
872 << (prevLedger ? "hashMismatch" : "missingLedger");
873 fixMismatch(*ledger);
874 }
875 }
876 }
877}
878
879void
881{
882 clearLedger(seq);
883 app_.getInboundLedgers().acquire(hash, seq, InboundLedger::Reason::GENERIC);
884}
885
886// Check if the specified ledger can become the new last fully-validated
887// ledger.
888void
890{
891 std::size_t valCount = 0;
892
893 if (seq != 0)
894 {
895 // Ledger is too old
896 if (seq < validLedgerSeq_)
897 return;
898
899 auto validations = app_.getValidators().negativeUNLFilter(
900 app_.getValidations().getTrustedForLedger(hash, seq));
901 valCount = validations.size();
902 if (valCount >= app_.getValidators().quorum())
903 {
904 std::scoped_lock const ml(mutex_);
905 if (seq > lastValidLedger_.second)
906 lastValidLedger_ = std::make_pair(hash, seq);
907 }
908
909 if (seq == validLedgerSeq_)
910 return;
911
912 // Ledger could match the ledger we're already building
913 if (seq == buildingLedgerSeq_)
914 return;
915 }
916
917 auto ledger = ledgerHistory_.getLedgerByHash(hash);
918
919 if (!ledger)
920 {
921 if ((seq != 0) && (getValidLedgerIndex() == 0))
922 {
923 // Set peers converged early if we can
924 if (valCount >= app_.getValidators().quorum())
925 app_.getOverlay().checkTracking(seq);
926 }
927
928 // FIXME: We may not want to fetch a ledger with just one
929 // trusted validation
930 ledger = app_.getInboundLedgers().acquire(hash, seq, InboundLedger::Reason::GENERIC);
931 }
932
933 if (ledger)
934 checkAccept(ledger);
935}
936
944{
945 return standalone_ ? 0 : app_.getValidators().quorum();
946}
947
948void
950{
951 // Can we accept this ledger as our new last fully-validated ledger
952
953 if (!canBeCurrent(ledger))
954 return;
955
956 // Can we advance the last fully-validated ledger? If so, can we
957 // publish?
958 std::scoped_lock const ml(mutex_);
959
960 if (ledger->header().seq <= validLedgerSeq_)
961 return;
962
963 auto const minVal = getNeededValidations();
964 auto validations = app_.getValidators().negativeUNLFilter(
965 app_.getValidations().getTrustedForLedger(ledger->header().hash, ledger->header().seq));
966 auto const tvc = validations.size();
967 if (tvc < minVal) // nothing we can do
968 {
969 JLOG(journal_.trace()) << "Only " << tvc << " validations for " << ledger->header().hash;
970 return;
971 }
972
973 JLOG(journal_.info()) << "Advancing accepted ledger to " << ledger->header().seq
974 << " with >= " << minVal << " validations";
975
976 ledger->setValidated();
977 ledger->setFull();
978 setValidLedger(ledger);
979 if (!pubLedger_)
980 {
981 pendSaveValidated(app_, ledger, true, true);
982 setPubLedger(ledger);
983 app_.getOrderBookDB().setup(ledger);
984 }
985
986 std::uint32_t const base = app_.getFeeTrack().getLoadBase();
987 auto fees = app_.getValidations().fees(ledger->header().hash, base);
988 {
989 auto fees2 = app_.getValidations().fees(ledger->header().parentHash, base);
990 fees.reserve(fees.size() + fees2.size());
992 }
993 std::uint32_t fee = 0;
994 if (!fees.empty())
995 {
996 std::ranges::sort(fees);
997 if (auto stream = journal_.debug())
998 {
1000 s << "Received fees from validations: (" << fees.size() << ") ";
1001 for (auto const fee1 : fees)
1002 {
1003 s << " " << fee1;
1004 }
1005 stream << s.str();
1006 }
1007 fee = fees[fees.size() / 2]; // median
1008 }
1009 else
1010 {
1011 fee = base;
1012 }
1013
1014 app_.getFeeTrack().setRemoteFee(fee);
1015
1016 tryAdvance();
1017
1018 if (ledger->seq() % 256 == 0)
1019 {
1020 // Check if the majority of validators run a higher version xrpld
1021 // software. If so print a warning.
1022 //
1023 // Validators include their xrpld software version in the validation
1024 // messages of every (flag - 1) ledger. We wait for one ledger time
1025 // before checking the version information to accumulate more validation
1026 // messages.
1027
1028 auto currentTime = app_.getTimeKeeper().now();
1029 bool needPrint = false;
1030
1031 // The variable upgradeWarningPrevTime_ will be set when and only when
1032 // the warning is printed.
1034 {
1035 // Have not printed the warning before, check if need to print.
1036 auto const vals = app_.getValidations().getTrustedForLedger(
1037 ledger->header().parentHash, ledger->header().seq - 1);
1038 std::size_t higherVersionCount = 0;
1039 std::size_t xrpldCount = 0;
1040 for (auto const& v : vals)
1041 {
1042 if (v->isFieldPresent(sfServerVersion))
1043 {
1044 auto version = v->getFieldU64(sfServerVersion);
1045 higherVersionCount += build_info::isNewerVersion(version) ? 1 : 0;
1046 xrpldCount += build_info::isXrpldVersion(version) ? 1 : 0;
1047 }
1048 }
1049 // We report only if (1) we have accumulated validation messages
1050 // from 90% validators from the UNL, (2) 60% of validators
1051 // running the xrpld implementation have higher version numbers,
1052 // and (3) the calculation won't cause divide-by-zero.
1053 if (higherVersionCount > 0 && xrpldCount > 0)
1054 {
1055 static constexpr std::size_t kReportingPercent = 90;
1056 static constexpr std::size_t kCutoffPercent = 60;
1057 auto const unlSize{app_.getValidators().getQuorumKeys().second.size()};
1058 needPrint = unlSize > 0 &&
1059 calculatePercent(vals.size(), unlSize) >= kReportingPercent &&
1060 calculatePercent(higherVersionCount, xrpldCount) >= kCutoffPercent;
1061 }
1062 }
1063 // To throttle the warning messages, instead of printing a warning
1064 // every flag ledger, we print every week.
1065 else if (currentTime - upgradeWarningPrevTime_ >= weeks{1})
1066 {
1067 // Printed the warning before, and assuming most validators
1068 // do not downgrade, we keep printing the warning
1069 // until the local server is restarted.
1070 needPrint = true;
1071 }
1072
1073 if (needPrint)
1074 {
1075 upgradeWarningPrevTime_ = currentTime;
1076 auto const upgradeMsg =
1077 "Check for upgrade: "
1078 "A majority of trusted validators are "
1079 "running a newer version.";
1080 std::cerr << upgradeMsg << std::endl;
1081 JLOG(journal_.error()) << upgradeMsg;
1082 }
1083 }
1084}
1085
1089void
1091 std::shared_ptr<Ledger const> const& ledger,
1092 uint256 const& consensusHash,
1093 json::Value consensus)
1094{
1095 // Because we just built a ledger, we are no longer building one
1097
1098 // No need to process validations in standalone mode
1099 if (standalone_)
1100 return;
1101
1102 ledgerHistory_.builtLedger(ledger, consensusHash, std::move(consensus));
1103
1104 if (ledger->header().seq <= validLedgerSeq_)
1105 {
1106 auto stream = app_.getJournal("LedgerConsensus").info();
1107 JLOG(stream) << "Consensus built old ledger: " << ledger->header().seq
1108 << " <= " << validLedgerSeq_;
1109 return;
1110 }
1111
1112 // See if this ledger can be the new fully-validated ledger
1113 checkAccept(ledger);
1114
1115 if (ledger->header().seq <= validLedgerSeq_)
1116 {
1117 auto stream = app_.getJournal("LedgerConsensus").debug();
1118 JLOG(stream) << "Consensus ledger fully validated";
1119 return;
1120 }
1121
1122 // This ledger cannot be the new fully-validated ledger, but
1123 // maybe we saved up validations for some other ledger that can be
1124
1125 auto validations =
1126 app_.getValidators().negativeUNLFilter(app_.getValidations().currentTrusted());
1127
1128 // Track validation counts with sequence numbers
1129 class ValSeq
1130 {
1131 public:
1132 ValSeq() = default;
1133
1134 void
1135 mergeValidation(LedgerIndex seq)
1136 {
1137 valCount++;
1138
1139 // If we didn't already know the sequence, now we do
1140 if (ledgerSeq == 0)
1141 ledgerSeq = seq;
1142 }
1143
1144 std::size_t valCount{0};
1145 LedgerIndex ledgerSeq{0};
1146 };
1147
1148 // Count the number of current, trusted validations
1150 for (auto const& v : validations)
1151 {
1152 ValSeq& vs = count[v->getLedgerHash()];
1153 vs.mergeValidation(v->getFieldU32(sfLedgerSequence));
1154 }
1155
1156 auto const neededValidations = getNeededValidations();
1157 auto maxSeq = validLedgerSeq_.load();
1158 auto maxLedger = ledger->header().hash;
1159
1160 // Of the ledgers with sufficient validations,
1161 // find the one with the highest sequence
1162 for (auto& v : count)
1163 {
1164 if (v.second.valCount > neededValidations)
1165 {
1166 // If we still don't know the sequence, get it
1167 if (v.second.ledgerSeq == 0)
1168 {
1169 if (auto l = getLedgerByHash(v.first))
1170 v.second.ledgerSeq = l->header().seq;
1171 }
1172
1173 if (v.second.ledgerSeq > maxSeq)
1174 {
1175 maxSeq = v.second.ledgerSeq;
1176 maxLedger = v.first;
1177 }
1178 }
1179 }
1180
1181 if (maxSeq > validLedgerSeq_)
1182 {
1183 auto stream = app_.getJournal("LedgerConsensus").debug();
1184 JLOG(stream) << "Consensus triggered check of ledger";
1185 checkAccept(maxLedger, maxSeq);
1186 }
1187}
1188
1191{
1192 // Try to get the hash of a ledger we need to fetch for history
1194 auto const& l{histLedger_};
1195
1196 if (l && l->header().seq >= index)
1197 {
1198 ret = hashOfSeq(*l, index, journal_);
1199 if (!ret)
1200 ret = walkHashBySeq(index, l, reason);
1201 }
1202
1203 if (!ret)
1204 ret = walkHashBySeq(index, reason);
1205
1206 return ret;
1207}
1208
1211{
1213
1214 JLOG(journal_.trace()) << "findNewLedgersToPublish<";
1215
1216 // No valid ledger, nothing to do
1217 if (validLedger_.empty())
1218 {
1219 JLOG(journal_.trace()) << "No valid journal, nothing to publish.";
1220 return {};
1221 }
1222
1223 if (!pubLedger_)
1224 {
1225 JLOG(journal_.info()) << "First published ledger will be " << validLedgerSeq_;
1226 return {validLedger_.get()};
1227 }
1228
1230 {
1231 JLOG(journal_.warn()) << "Gap in validated ledger stream " << pubLedgerSeq_ << " - "
1232 << validLedgerSeq_ - 1;
1233
1234 auto valLedger = validLedger_.get();
1235 ret.push_back(valLedger);
1236 setPubLedger(valLedger);
1237 app_.getOrderBookDB().setup(valLedger);
1238
1239 return {valLedger};
1240 }
1241
1243 {
1244 JLOG(journal_.trace()) << "No valid journal, nothing to publish.";
1245 return {};
1246 }
1247
1248 int acqCount = 0;
1249
1250 auto pubSeq = pubLedgerSeq_ + 1; // Next sequence to publish
1251 auto valLedger = validLedger_.get();
1252 std::uint32_t const valSeq = valLedger->header().seq;
1253
1254 ScopeUnlock const sul{sl};
1255 try
1256 {
1257 for (std::uint32_t seq = pubSeq; seq <= valSeq; ++seq)
1258 {
1259 JLOG(journal_.trace()) << "Trying to fetch/publish valid ledger " << seq;
1260
1262 // This can throw
1263 auto hash = hashOfSeq(*valLedger, seq, journal_);
1264 // VFALCO TODO Restructure this code so that zero is not
1265 // used.
1266 if (!hash)
1267 hash = beast::kZero; // kludge
1268 if (seq == valSeq)
1269 {
1270 // We need to publish the ledger we just fully validated
1271 ledger = valLedger;
1272 }
1273 else if (hash->isZero())
1274 {
1275 // LCOV_EXCL_START
1276 JLOG(journal_.fatal()) << "Ledger: " << valSeq << " does not have hash for " << seq;
1277 UNREACHABLE(
1278 "xrpl::LedgerMaster::findNewLedgersToPublish : ledger "
1279 "not found");
1280 // LCOV_EXCL_STOP
1281 }
1282 else
1283 {
1284 ledger = ledgerHistory_.getLedgerByHash(*hash);
1285 }
1286
1287 if (!app_.config().ledgerReplay)
1288 {
1289 // Can we try to acquire the ledger we need?
1290 if (!ledger && (++acqCount < ledgerFetchSize_))
1291 {
1292 ledger = app_.getInboundLedgers().acquire(
1293 *hash, seq, InboundLedger::Reason::GENERIC);
1294 }
1295 }
1296
1297 // Did we acquire the next ledger we need to publish?
1298 if (ledger && (ledger->header().seq == pubSeq))
1299 {
1300 ledger->setValidated();
1301 ret.push_back(ledger);
1302 ++pubSeq;
1303 }
1304 }
1305
1306 JLOG(journal_.trace()) << "ready to publish " << ret.size() << " ledgers.";
1307 }
1308 catch (std::exception const& ex)
1309 {
1310 JLOG(journal_.error()) << "Exception while trying to find ledgers to publish: "
1311 << ex.what();
1312 }
1313
1314 if (app_.config().ledgerReplay)
1315 {
1316 /* Narrow down the gap of ledgers, and try to replay them.
1317 * When replaying a ledger gap, if the local node has
1318 * the start ledger, it saves an expensive InboundLedger
1319 * acquire. If the local node has the finish ledger, it
1320 * saves a skip list acquire.
1321 */
1322 auto const& startLedger = ret.empty() ? pubLedger_ : ret.back();
1323 auto finishLedger = valLedger;
1324 while (startLedger->seq() + 1 < finishLedger->seq())
1325 {
1326 if (auto const parent =
1327 ledgerHistory_.getLedgerByHash(finishLedger->header().parentHash);
1328 parent)
1329 {
1330 finishLedger = parent;
1331 }
1332 else
1333 {
1334 auto numberLedgers = finishLedger->seq() - startLedger->seq() + 1;
1335 JLOG(journal_.debug())
1336 << "Publish LedgerReplays " << numberLedgers
1337 << " ledgers, from seq=" << startLedger->header().seq << ", "
1338 << startLedger->header().hash << " to seq=" << finishLedger->header().seq
1339 << ", " << finishLedger->header().hash;
1340 app_.getLedgerReplayer().replay(
1341 InboundLedger::Reason::GENERIC, finishLedger->header().hash, numberLedgers);
1342 break;
1343 }
1344 }
1345 }
1346
1347 return ret;
1348}
1349
1350void
1352{
1353 std::scoped_lock const ml(mutex_);
1354
1355 // Can't advance without at least one fully-valid ledger
1356 advanceWork_ = true;
1357 if (!advanceThread_ && !validLedger_.empty())
1358 {
1359 advanceThread_ = true;
1360 app_.getJobQueue().addJob(JtAdvance, "AdvanceLedger", [this]() {
1362
1363 XRPL_ASSERT(
1364 !validLedger_.empty() && advanceThread_,
1365 "xrpl::LedgerMaster::tryAdvance : has valid ledger");
1366
1367 JLOG(journal_.trace()) << "advanceThread<";
1368
1369 try
1370 {
1371 doAdvance(sl);
1372 }
1373 catch (std::exception const& ex)
1374 {
1375 JLOG(journal_.fatal()) << "doAdvance throws: " << ex.what();
1376 }
1377
1378 advanceThread_ = false;
1379 JLOG(journal_.trace()) << "advanceThread>";
1380 });
1381 }
1382}
1383
1384void
1386{
1387 {
1388 std::scoped_lock const ml(mutex_);
1389 if (app_.getOPs().isNeedNetworkLedger())
1390 {
1392 pathLedger_.reset();
1393 JLOG(journal_.debug()) << "Need network ledger for updating paths";
1394 return;
1395 }
1396 }
1397
1398 while (!app_.getJobQueue().isStopping())
1399 {
1400 JLOG(journal_.debug()) << "updatePaths running";
1402 {
1403 std::scoped_lock const ml(mutex_);
1404
1405 if (!validLedger_.empty() &&
1406 (!pathLedger_ || (pathLedger_->header().seq != validLedgerSeq_)))
1407 { // We have a new valid ledger since the last full pathfinding
1408 pathLedger_ = validLedger_.get();
1409 lastLedger = pathLedger_;
1410 }
1411 else if (pathFindNewRequest_)
1412 { // We have a new request but no new ledger
1413 lastLedger = app_.getOpenLedger().current();
1414 }
1415 else
1416 { // Nothing to do
1418 pathLedger_.reset();
1419 JLOG(journal_.debug()) << "Nothing to do for updating paths";
1420 return;
1421 }
1422 }
1423
1424 if (!standalone_)
1425 { // don't pathfind with a ledger that's more than 60 seconds old
1426 using namespace std::chrono;
1427 auto age = time_point_cast<seconds>(app_.getTimeKeeper().closeTime()) -
1428 lastLedger->header().closeTime;
1429 if (age > 1min)
1430 {
1431 JLOG(journal_.debug()) << "Published ledger too old for updating paths";
1432 std::scoped_lock const ml(mutex_);
1434 pathLedger_.reset();
1435 return;
1436 }
1437 }
1438
1439 try
1440 {
1441 auto& pathRequests = app_.getPathRequestManager();
1442 {
1443 std::scoped_lock const ml(mutex_);
1444 if (!pathRequests.requestsPending())
1445 {
1447 pathLedger_.reset();
1448 JLOG(journal_.debug()) << "No path requests found. Nothing to do for updating "
1449 "paths. "
1450 << pathFindThread_ << " jobs remaining";
1451 return;
1452 }
1453 }
1454 JLOG(journal_.debug()) << "Updating paths";
1455 pathRequests.updateAll(lastLedger);
1456
1457 std::scoped_lock const ml(mutex_);
1458 if (!pathRequests.requestsPending())
1459 {
1460 JLOG(journal_.debug()) << "No path requests left. No need for further updating "
1461 "paths";
1463 pathLedger_.reset();
1464 return;
1465 }
1466 }
1467 catch (SHAMapMissingNode const& mn)
1468 {
1469 JLOG(journal_.info()) << "During pathfinding: " << mn.what();
1470 if (lastLedger->open())
1471 {
1472 // our parent is the problem
1473 app_.getInboundLedgers().acquire(
1474 lastLedger->header().parentHash,
1475 lastLedger->header().seq - 1,
1477 }
1478 else
1479 {
1480 // this ledger is the problem
1481 app_.getInboundLedgers().acquire(
1482 lastLedger->header().hash,
1483 lastLedger->header().seq,
1485 }
1486 }
1487 }
1488}
1489
1490bool
1492{
1494 pathFindNewRequest_ = newPFWork("PthFindNewReq", ml);
1495 return pathFindNewRequest_;
1496}
1497
1498bool
1500{
1501 std::scoped_lock const ml(mutex_);
1502 bool const ret = pathFindNewRequest_;
1503 pathFindNewRequest_ = false;
1504 return ret;
1505}
1506
1507// If the order book is radically updated, we need to reprocess all
1508// pathfinding requests.
1509bool
1511{
1513 pathLedger_.reset();
1514
1515 return newPFWork("PthFindOBDB", ml);
1516}
1517
1521bool
1523{
1524 if (!app_.isStopping() && pathFindThread_ < 2 && app_.getPathRequestManager().requestsPending())
1525 {
1526 JLOG(journal_.debug()) << "newPFWork: Creating job. path find threads: " << pathFindThread_;
1527 if (app_.getJobQueue().addJob(JtUpdatePf, name, [this]() { updatePaths(); }))
1528 {
1530 }
1531 }
1532 // If we're stopping don't give callers the expectation that their
1533 // request will be fulfilled, even if it may be serviced.
1534 return pathFindThread_ > 0 && !app_.isStopping();
1535}
1536
1539{
1540 return mutex_;
1541}
1542
1543// The current ledger is the ledger we believe new transactions should go in
1546{
1547 return app_.getOpenLedger().current();
1548}
1549
1552{
1553 return validLedger_.get();
1554}
1555
1556Rules
1558{
1559 // Once we have a guarantee that there's always a last validated
1560 // ledger then we can dispense with the if.
1561
1562 // Return the Rules from the last validated ledger.
1563 if (auto const ledger = getValidatedLedger())
1564 return ledger->rules();
1565
1566 return Rules(app_.config().features);
1567}
1568
1569// This is the last ledger we published to clients and can lag the validated
1570// ledger.
1577
1584
1587{
1588 uint256 const hash = getHashBySeq(ledgerIndex);
1589 return hash.isNonZero() ? getCloseTimeByHash(hash, ledgerIndex) : std::nullopt;
1590}
1591
1594{
1595 auto nodeObject = app_.getNodeStore().fetchNodeObject(ledgerHash, index);
1596 if (nodeObject && (nodeObject->getData().size() >= 120))
1597 {
1598 SerialIter it(nodeObject->getData().data(), nodeObject->getData().size());
1600 {
1601 it.skip(
1602 4 + 8 + 32 + // seq drops parentHash
1603 32 + 32 + 4); // txHash acctHash parentClose
1605 }
1606 }
1607
1608 return std::nullopt;
1609}
1610
1611uint256
1613{
1614 uint256 hash = ledgerHistory_.getLedgerHash(index);
1615
1616 if (hash.isNonZero())
1617 return hash;
1618
1619 return app_.getRelationalDatabase().getHashByIndex(index);
1620}
1621
1624{
1625 std::optional<LedgerHash> ledgerHash;
1626
1627 if (auto referenceLedger = validLedger_.get())
1628 ledgerHash = walkHashBySeq(index, referenceLedger, reason);
1629
1630 return ledgerHash;
1631}
1632
1635 std::uint32_t index,
1636 std::shared_ptr<ReadView const> const& referenceLedger,
1637 InboundLedger::Reason reason)
1638{
1639 if (!referenceLedger || (referenceLedger->header().seq < index))
1640 {
1641 // Nothing we can do. No validated ledger.
1642 return std::nullopt;
1643 }
1644
1645 // See if the hash for the ledger we need is in the reference ledger
1646 auto ledgerHash = hashOfSeq(*referenceLedger, index, journal_);
1647 if (ledgerHash)
1648 return ledgerHash;
1649
1650 // The hash is not in the reference ledger. Get another ledger which can
1651 // be located easily and should contain the hash.
1652 LedgerIndex const refIndex = getCandidateLedger(index);
1653 auto const refHash = hashOfSeq(*referenceLedger, refIndex, journal_);
1654 XRPL_ASSERT(refHash, "xrpl::LedgerMaster::walkHashBySeq : found ledger");
1655 if (refHash)
1656 {
1657 // Try the hash and sequence of a better reference ledger just found
1658 auto ledger = ledgerHistory_.getLedgerByHash(*refHash);
1659
1660 if (ledger)
1661 {
1662 try
1663 {
1664 ledgerHash = hashOfSeq(*ledger, index, journal_);
1665 }
1666 catch (SHAMapMissingNode const&)
1667 {
1668 ledger.reset();
1669 }
1670 }
1671
1672 // Try to acquire the complete ledger
1673 if (!ledger)
1674 {
1675 if (auto const l = app_.getInboundLedgers().acquire(*refHash, refIndex, reason))
1676 {
1677 ledgerHash = hashOfSeq(*l, index, journal_);
1678 XRPL_ASSERT(
1679 ledgerHash,
1680 "xrpl::LedgerMaster::walkHashBySeq : has complete "
1681 "ledger");
1682 }
1683 }
1684 }
1685 return ledgerHash;
1686}
1687
1690{
1691 if (index <= validLedgerSeq_)
1692 {
1693 // Always prefer a validated ledger
1694 if (auto valid = validLedger_.get())
1695 {
1696 if (valid->header().seq == index)
1697 return valid;
1698
1699 try
1700 {
1701 auto const hash = hashOfSeq(*valid, index, journal_);
1702
1703 if (hash)
1704 return ledgerHistory_.getLedgerByHash(*hash);
1705 }
1706 catch (std::exception const&) // NOLINT(bugprone-empty-catch)
1707 {
1708 // Missing nodes are already handled
1709 }
1710 }
1711 }
1712
1713 if (auto ret = ledgerHistory_.getLedgerBySeq(index))
1714 return ret;
1715
1716 auto ret = closedLedger_.get();
1717 if (ret && (ret->header().seq == index))
1718 return ret;
1719
1720 clearLedger(index);
1721 return {};
1722}
1723
1726{
1727 if (auto ret = ledgerHistory_.getLedgerByHash(hash))
1728 return ret;
1729
1730 auto ret = closedLedger_.get();
1731 if (ret && (ret->header().hash == hash))
1732 return ret;
1733
1734 return {};
1735}
1736
1737void
1743
1744void
1746{
1747 ledgerHistory_.sweep();
1748 fetchPacks_.sweep();
1749}
1750
1751float
1753{
1754 return ledgerHistory_.getCacheHitRate();
1755}
1756
1757void
1759{
1761 if (seq > 0)
1762 completeLedgers_.erase(range(0u, seq - 1));
1763}
1764
1765void
1767{
1768 ledgerHistory_.clearLedgerCachePrior(seq);
1769}
1770
1771void
1773{
1774 replayData_ = std::move(replay);
1775}
1776
1779{
1780 return std::move(replayData_);
1781}
1782
1783void
1785 std::uint32_t missing,
1786 bool& progress,
1787 InboundLedger::Reason reason,
1789{
1790 ScopeUnlock const sul{sl};
1791 if (auto hash = getLedgerHashForHistory(missing, reason))
1792 {
1793 XRPL_ASSERT(hash->isNonZero(), "xrpl::LedgerMaster::fetchForHistory : found ledger");
1794 auto ledger = getLedgerByHash(*hash);
1795 if (!ledger)
1796 {
1797 if (!app_.getInboundLedgers().isFailure(*hash))
1798 {
1799 ledger = app_.getInboundLedgers().acquire(*hash, missing, reason);
1800 if (!ledger && missing != fetchSeq_ &&
1801 missing > app_.getNodeStore().earliestLedgerSeq())
1802 {
1803 JLOG(journal_.trace()) << "fetchForHistory want fetch pack " << missing;
1804 fetchSeq_ = missing;
1805 getFetchPack(missing, reason);
1806 }
1807 else
1808 {
1809 JLOG(journal_.trace()) << "fetchForHistory no fetch pack for " << missing;
1810 }
1811 }
1812 else
1813 {
1814 JLOG(journal_.debug()) << "fetchForHistory found failed acquire";
1815 }
1816 }
1817 if (ledger)
1818 {
1819 auto seq = ledger->header().seq;
1820 XRPL_ASSERT(seq == missing, "xrpl::LedgerMaster::fetchForHistory : sequence match");
1821 JLOG(journal_.trace()) << "fetchForHistory acquired " << seq;
1822 setFullLedger(ledger, false, false);
1823 int fillInProgress = 0;
1824 {
1826 histLedger_ = ledger;
1827 fillInProgress = fillInProgress_;
1828 }
1829 if (fillInProgress == 0 &&
1830 app_.getRelationalDatabase().getHashByIndex(seq - 1) == ledger->header().parentHash)
1831 {
1832 {
1833 // Previous ledger is in DB
1835 fillInProgress_ = seq;
1836 }
1837 app_.getJobQueue().addJob(
1838 JtAdvance, "TryFill", [this, ledger]() { tryFill(ledger); });
1839 }
1840 progress = true;
1841 }
1842 else
1843 {
1844 std::uint32_t fetchSz = 0;
1845 // Do not fetch ledger sequences lower
1846 // than the earliest ledger sequence
1847 fetchSz = app_.getNodeStore().earliestLedgerSeq();
1848 fetchSz = missing >= fetchSz ? std::min(ledgerFetchSize_, (missing - fetchSz) + 1) : 0;
1849 try
1850 {
1851 for (std::uint32_t i = 0; i < fetchSz; ++i)
1852 {
1853 std::uint32_t const seq = missing - i;
1854 if (auto h = getLedgerHashForHistory(seq, reason))
1855 {
1856 XRPL_ASSERT(
1857 h->isNonZero(),
1858 "xrpl::LedgerMaster::fetchForHistory : "
1859 "prefetched ledger");
1860 app_.getInboundLedgers().acquire(*h, seq, reason);
1861 }
1862 }
1863 }
1864 catch (std::exception const& ex)
1865 {
1866 JLOG(journal_.warn()) << "Threw while prefetching: " << ex.what();
1867 }
1868 }
1869 }
1870 else
1871 {
1872 JLOG(journal_.fatal()) << "Can't find ledger following prevMissing " << missing;
1873 JLOG(journal_.fatal()) << "Pub:" << pubLedgerSeq_ << " Val:" << validLedgerSeq_;
1874 JLOG(journal_.fatal()) << "Ledgers: " << app_.getLedgerMaster().getCompleteLedgers();
1875 JLOG(journal_.fatal()) << "Acquire reason: "
1876 << (reason == InboundLedger::Reason::HISTORY ? "HISTORY"
1877 : "NOT HISTORY");
1878 clearLedger(missing + 1);
1879 progress = true;
1880 }
1881}
1882
1883// Try to publish ledgers, acquire missing ledgers
1884void
1886{
1887 do
1888 {
1889 advanceWork_ = false; // If there's work to do, we'll make progress
1890 bool progress = false;
1891
1892 auto const pubLedgers = findNewLedgersToPublish(sl);
1893 if (pubLedgers.empty())
1894 {
1895 if (!standalone_ && !app_.getFeeTrack().isLoadedLocal() &&
1896 (app_.getJobQueue().getJobCount(JtPuboldledger) < 10) &&
1899 (app_.getNodeStore().getWriteLoad() < kMaxWriteLoadAcquire))
1900 {
1901 // We are in sync, so can acquire
1904 {
1906 missing = prevMissing(
1908 pubLedger_->header().seq,
1909 app_.getNodeStore().earliestLedgerSeq());
1910 }
1911 if (missing)
1912 {
1913 JLOG(journal_.trace()) << "tryAdvance discovered missing " << *missing;
1914 if ((fillInProgress_ == 0 || *missing > fillInProgress_) &&
1918 app_.getSHAMapStore().minimumOnline(),
1919 *missing,
1920 journal_))
1921 {
1922 JLOG(journal_.trace()) << "advanceThread should acquire";
1923 }
1924 else
1925 {
1926 missing = std::nullopt;
1927 }
1928 }
1929 if (missing)
1930 {
1931 fetchForHistory(*missing, progress, reason, sl);
1933 {
1934 JLOG(journal_.debug()) << "tryAdvance found last valid changed";
1935 progress = true;
1936 }
1937 }
1938 }
1939 else
1940 {
1941 histLedger_.reset();
1942 JLOG(journal_.trace()) << "tryAdvance not fetching history";
1943 }
1944 }
1945 else
1946 {
1947 JLOG(journal_.trace())
1948 << "tryAdvance found " << pubLedgers.size() << " ledgers to publish";
1949 for (auto const& ledger : pubLedgers)
1950 {
1951 {
1952 ScopeUnlock const sul{sl};
1953 JLOG(journal_.debug()) << "tryAdvance publishing seq " << ledger->header().seq;
1954 setFullLedger(ledger, true, true);
1955 }
1956
1957 setPubLedger(ledger);
1958
1959 {
1960 ScopeUnlock const sul{sl};
1961 app_.getOPs().pubLedger(ledger);
1962 }
1963 }
1964
1965 app_.getOPs().clearNeedNetworkLedger();
1966 progress = newPFWork("PthFindNewLed", sl);
1967 }
1968 if (progress)
1969 advanceWork_ = true;
1970 } while (advanceWork_);
1971}
1972
1973void
1975{
1976 fetchPacks_.canonicalizeReplaceClient(hash, data);
1977}
1978
1981{
1982 Blob data;
1983 if (fetchPacks_.retrieve(hash, data))
1984 {
1985 fetchPacks_.del(hash, false);
1986 if (hash == sha512Half(makeSlice(data)))
1987 return data;
1988 }
1989 return std::nullopt;
1990}
1991
1992void
1994{
1995 if (!gotFetchPackThread_.test_and_set(std::memory_order_acquire))
1996 {
1997 app_.getJobQueue().addJob(JtLedgerData, "GotFetchPack", [&]() {
1998 app_.getInboundLedgers().gotFetchPack();
1999 gotFetchPackThread_.clear(std::memory_order_release);
2000 });
2001 }
2002}
2003
2030static void
2032 SHAMap const& want,
2033 SHAMap const* have,
2034 std::uint32_t cnt,
2035 protocol::TMGetObjectByHash* into,
2036 std::uint32_t seq,
2037 bool withLeaves = true)
2038{
2039 XRPL_ASSERT(cnt, "xrpl::populateFetchPack : nonzero count input");
2040
2041 Serializer s(1024);
2042
2043 want.visitDifferences(have, [&s, withLeaves, &cnt, into, seq](SHAMapTreeNode const& n) -> bool {
2044 if (!withLeaves && n.isLeaf())
2045 return true;
2046
2047 s.erase();
2049
2050 auto const& hash = n.getHash().asUInt256();
2051
2052 protocol::TMIndexedObject* obj = into->add_objects();
2053 obj->set_ledgerseq(seq);
2054 obj->set_hash(hash.data(), hash.size());
2055 obj->set_data(s.getDataPtr(), s.getLength());
2056
2057 return --cnt != 0;
2058 });
2059}
2060
2061void
2063 std::weak_ptr<Peer> const& wPeer,
2065 uint256 haveLedgerHash,
2067{
2068 using namespace std::chrono_literals;
2069 if (UptimeClock::now() > uptime + 1s)
2070 {
2071 JLOG(journal_.info()) << "Fetch pack request got stale";
2072 return;
2073 }
2074
2075 if (app_.getFeeTrack().isLoadedLocal() || (getValidatedLedgerAge() > 40s))
2076 {
2077 JLOG(journal_.info()) << "Too busy to make fetch pack";
2078 return;
2079 }
2080
2081 auto peer = wPeer.lock();
2082
2083 if (!peer)
2084 return;
2085
2086 auto have = getLedgerByHash(haveLedgerHash);
2087
2088 if (!have)
2089 {
2090 JLOG(journal_.info()) << "Peer requests fetch pack for ledger we don't have: " << have;
2091 peer->charge(resource::kFeeRequestNoReply, "get_object ledger");
2092 return;
2093 }
2094
2095 if (have->open())
2096 {
2097 JLOG(journal_.warn()) << "Peer requests fetch pack from open ledger: " << have;
2098 peer->charge(resource::kFeeMalformedRequest, "get_object ledger open");
2099 return;
2100 }
2101
2102 if (have->header().seq < getEarliestFetch())
2103 {
2104 JLOG(journal_.debug()) << "Peer requests fetch pack that is too early";
2105 peer->charge(resource::kFeeMalformedRequest, "get_object ledger early");
2106 return;
2107 }
2108
2109 auto want = getLedgerByHash(have->header().parentHash);
2110
2111 if (!want)
2112 {
2113 JLOG(journal_.info()) << "Peer requests fetch pack for ledger whose predecessor we "
2114 << "don't have: " << have;
2115 peer->charge(resource::kFeeRequestNoReply, "get_object ledger no parent");
2116 return;
2117 }
2118
2119 try
2120 {
2121 Serializer hdr(128);
2122
2123 protocol::TMGetObjectByHash reply;
2124 reply.set_query(false);
2125
2126 reply.set_ledgerhash(request->ledgerhash());
2127 reply.set_type(protocol::TMGetObjectByHash::otFETCH_PACK);
2128
2129 // Building a fetch pack:
2130 // 1. Add the header for the requested ledger.
2131 // 2. Add the nodes for the AccountStateMap of that ledger.
2132 // 3. If there are transactions, add the nodes for the
2133 // transactions of the ledger.
2134 // 4. If the FetchPack now contains at least 512 entries then stop.
2135 // 5. If not very much time has elapsed, then loop back and repeat
2136 // the same process adding the previous ledger to the FetchPack.
2137 do
2138 {
2139 std::uint32_t const lSeq = want->header().seq;
2140
2141 {
2142 // Serialize the ledger header:
2143 hdr.erase();
2144
2146 addRaw(want->header(), hdr);
2147
2148 // Add the data
2149 protocol::TMIndexedObject* obj = reply.add_objects();
2150 obj->set_hash(want->header().hash.data(), want->header().hash.size());
2151 obj->set_data(hdr.getDataPtr(), hdr.getLength());
2152 obj->set_ledgerseq(lSeq);
2153 }
2154
2155 populateFetchPack(want->stateMap(), &have->stateMap(), 16384, &reply, lSeq);
2156
2157 // We use nullptr here because transaction maps are per ledger
2158 // and so the requestor is unlikely to already have it.
2159 if (want->header().txHash.isNonZero())
2160 populateFetchPack(want->txMap(), nullptr, 512, &reply, lSeq);
2161
2162 if (reply.objects().size() >= 512)
2163 break;
2164
2165 have = std::move(want);
2166 want = getLedgerByHash(have->header().parentHash);
2167 } while (want && UptimeClock::now() <= uptime + 1s);
2168
2169 auto msg = std::make_shared<Message>(reply, protocol::mtGET_OBJECTS);
2170
2171 JLOG(journal_.info()) << "Built fetch pack with " << reply.objects().size() << " nodes ("
2172 << msg->getBufferSize() << " bytes)";
2173
2174 peer->send(msg);
2175 }
2176 catch (std::exception const& ex)
2177 {
2178 JLOG(journal_.warn()) << "Exception building fetch pack. Exception: " << ex.what();
2179 }
2180}
2181
2184{
2185 return fetchPacks_.getCacheSize();
2186}
2187
2188// Returns the minimum ledger sequence in SQL database, if any.
2191{
2192 return app_.getRelationalDatabase().getMinLedgerSeq();
2193}
2194
2197{
2198 uint32_t first = 0, last = 0;
2199
2200 if (!getValidatedRange(first, last) || last < ledgerSeq)
2201 return {};
2202
2203 auto const lgr = getLedgerBySeq(ledgerSeq);
2204 if (!lgr || lgr->txs.empty())
2205 return {};
2206
2207 for (auto it = lgr->txs.begin(); it != lgr->txs.end(); ++it)
2208 {
2209 if (it->first && it->second && it->second->isFieldPresent(sfTransactionIndex) &&
2210 it->second->getFieldU32(sfTransactionIndex) == txnIndex)
2211 return it->first->getTransactionID();
2212 }
2213
2214 return {};
2215}
2216
2217} // 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()
LedgerIndex const maxLedgerDifference_
std::atomic_flag gotFetchPackThread_
bool haveLedger(std::uint32_t seq)
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)
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)
void switchLCL(std::shared_ptr< Ledger const > const &lastClosed)
std::recursive_mutex & peekMutex()
std::chrono::seconds getValidatedLedgerAge()
TimeKeeper::time_point upgradeWarningPrevTime_
std::uint32_t const ledgerFetchSize_
std::atomic< std::uint32_t > pubLedgerClose_
LedgerIndex getCurrentLedgerIndex()
bool fixIndex(LedgerIndex ledgerIndex, LedgerHash const &ledgerHash)
bool getValidatedRange(std::uint32_t &minVal, std::uint32_t &maxVal)
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)
void gotFetchPack(bool progress, std::uint32_t seq)
beast::Journal journal_
void tryFill(std::shared_ptr< Ledger const > ledger)
std::shared_ptr< Ledger const > getLedgerBySeq(std::uint32_t index)
void setPubLedger(std::shared_ptr< Ledger const > const &l)
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)
std::atomic< LedgerIndex > pubLedgerSeq_
void clearPriorLedgers(LedgerIndex seq)
void setBuildingLedger(LedgerIndex index)
std::uint32_t fetchSeq_
bool isCaughtUp(std::string &reason)
std::size_t getFetchPackCacheSize() const
std::optional< Blob > getFetchPack(uint256 const &hash) override
std::vector< std::shared_ptr< Ledger const > > findNewLedgersToPublish(std::unique_lock< std::recursive_mutex > &)
std::atomic< LedgerIndex > buildingLedgerSeq_
std::optional< NetClock::time_point > getCloseTimeByHash(LedgerHash const &ledgerHash, LedgerIndex ledgerIndex)
void clearLedger(std::uint32_t seq)
void clearLedgerCachePrior(LedgerIndex seq)
uint256 getHashBySeq(std::uint32_t index)
Get a ledger's hash by sequence number using the cache.
std::unique_ptr< LedgerReplay > replayData_
void consensusBuilt(std::shared_ptr< Ledger const > const &ledger, uint256 const &consensusHash, json::Value consensus)
Report that the consensus process built a particular ledger.
std::atomic< LedgerIndex > validLedgerSeq_
std::shared_ptr< Ledger const > getClosedLedger()
void setLedgerRangePresent(std::uint32_t minV, std::uint32_t maxV)
std::optional< NetClock::time_point > getCloseTimeBySeq(LedgerIndex ledgerIndex)
std::string getCompleteLedgers()
std::shared_ptr< Ledger const > getValidatedLedger()
void fetchForHistory(std::uint32_t missing, bool &progress, InboundLedger::Reason reason, std::unique_lock< std::recursive_mutex > &)
bool isValidated(ReadView const &ledger)
void fixMismatch(ReadView const &ledger)
void makeFetchPack(std::weak_ptr< Peer > const &wPeer, std::shared_ptr< protocol::TMGetObjectByHash > const &request, uint256 haveLedgerHash, UptimeClock::time_point uptime)
LedgerIndex getValidLedgerIndex()
LedgerHolder validLedger_
CanonicalTXSet heldTransactions_
bool const standalone_
std::shared_ptr< Ledger const > pathLedger_
std::shared_ptr< Ledger const > pubLedger_
std::shared_ptr< ReadView const > getPublishedLedger()
std::uint32_t const fetchDepth_
std::recursive_mutex mutex_
std::pair< uint256, LedgerIndex > lastValidLedger_
LedgerHistory ledgerHistory_
std::optional< LedgerHash > walkHashBySeq(std::uint32_t index, InboundLedger::Reason reason)
Walk to a ledger's hash using the skip list.
LedgerMaster(Application &app, Stopwatch &stopwatch, beast::insight::Collector::ptr const &collector, beast::Journal journal)
std::chrono::seconds getPublishedLedgerAge()
std::optional< uint256 > txnIdFromIndex(uint32_t ledgerSeq, uint32_t txnIndex)
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_
void addFetchPack(uint256 const &hash, std::shared_ptr< Blob > data)
bool getFullValidatedRange(std::uint32_t &minVal, std::uint32_t &maxVal)
std::optional< LedgerHash > getLedgerHashForHistory(LedgerIndex index, InboundLedger::Reason reason)
RangeSet< std::uint32_t > completeLedgers_
void checkAccept(std::shared_ptr< Ledger const > const &ledger)
std::uint32_t const ledgerHistorySize_
std::atomic< std::uint32_t > validLedgerSign_
void doAdvance(std::unique_lock< std::recursive_mutex > &)
void addHeldTransaction(std::shared_ptr< Transaction > const &trans)
std::shared_ptr< ReadView const > getCurrentLedger()
std::recursive_mutex completeLock_
void takeReplay(std::unique_ptr< LedgerReplay > replay)
std::unique_ptr< LedgerReplay > releaseReplay()
std::shared_ptr< Ledger const > getLedgerByHash(uint256 const &hash)
std::uint32_t getEarliestFetch()
std::shared_ptr< Ledger const > histLedger_
TaggedCache< uint256, Blob > fetchPacks_
void failedSave(std::uint32_t seq, uint256 const &hash)
Application & app_
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:198
int getLength() const
Definition Serializer.h:208
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
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,...
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.
sha512_half_hasher::result_type sha512Half(Args const &... args)
Returns the SHA512-Half of a series of objects.
Definition digest.h:215
std::uint32_t LedgerIndex
A ledger index.
Definition Protocol.h:370
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.
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:651
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:142
LedgerIndex getCandidateLedger(LedgerIndex requested)
Find a ledger index from which we could easily get the requested ledger.
Definition View.h:124
Slice makeSlice(std::array< T, N > const &a)
Definition Slice.h:228
std::chrono::duration< int, std::ratio_multiply< days::period, std::ratio< 7 > > > weeks
Definition chrono.h:22
std::optional< uint256 > hashOfSeq(ReadView const &ledger, LedgerIndex seq, beast::Journal journal)
Return the hash of a ledger by sequence.
Definition View.cpp:279
@ JtPuboldledger
Definition Job.h:30
@ JtLedgerData
Definition Job.h:52
@ JtAdvance
Definition Job.h:53
@ JtUpdatePf
Definition Job.h:42
uint256 LedgerHash
void addRaw(LedgerHeader const &, Serializer &, bool includeHash=false)
std::unordered_map< Key, Value, Hash, Pred, Allocator > hash_map
@ LedgerMaster
ledger master data for signing
Definition HashPrefix.h:59
std::vector< unsigned char > Blob
Storage for linear binary data.
Definition Blob.h:11
static constexpr int kMaxWriteLoadAcquire
BaseUInt< 256 > uint256
Definition base_uint.h:580
static constexpr std::chrono::minutes kMaxLedgerAgeAcquire
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)
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)