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