1#include <xrpld/app/ledger/LedgerHistory.h>
3#include <xrpld/app/ledger/LedgerPersistence.h>
4#include <xrpld/app/ledger/LedgerToJson.h>
5#include <xrpld/app/main/Application.h>
6#include <xrpld/core/Config.h>
8#include <xrpl/basics/Log.h>
9#include <xrpl/basics/base_uint.h>
10#include <xrpl/basics/chrono.h>
11#include <xrpl/basics/contract.h>
12#include <xrpl/beast/insight/Collector.h>
13#include <xrpl/beast/utility/Journal.h>
14#include <xrpl/beast/utility/instrumentation.h>
15#include <xrpl/json/json_value.h>
16#include <xrpl/json/to_string.h>
17#include <xrpl/ledger/ReadView.h>
18#include <xrpl/protocol/Protocol.h>
19#include <xrpl/protocol/RippleLedgerHash.h>
20#include <xrpl/protocol/Rules.h>
21#include <xrpl/protocol/TxMeta.h>
22#include <xrpl/shamap/SHAMap.h>
23#include <xrpl/shamap/SHAMapItem.h>
45 app_.getJournal(
"TaggedCache"))
49 std::chrono::minutes{5},
51 app_.getJournal(
"TaggedCache"))
52 ,
j_(app.getJournal(
"LedgerHistory"))
59 if (!ledger->isImmutable())
63 ledger->stateMap().getHash().isNonZero(),
"xrpl::LedgerHistory::insert : nonzero hash");
67 bool const alreadyHad =
ledgersByHash_.canonicalizeReplaceCache(ledger->header().hash, ledger);
92 uint256 const hash = it->second;
98 Rules const rules{
app_.config().features};
99 Fees const fees =
app_.config().fees.toFees();
106 ret->header().seq == index,
"xrpl::LedgerHistory::getLedgerBySeq : result sequence match");
113 ret->isImmutable(),
"xrpl::LedgerHistory::getLedgerBySeq : immutable result ledger");
114 ledgersByHash_.canonicalizeReplaceClient(ret->header().hash, ret);
116 return (ret->header().seq == index) ? ret :
nullptr;
129 "xrpl::LedgerHistory::getLedgerByHash : immutable fetched "
132 ret->header().hash == hash,
133 "xrpl::LedgerHistory::getLedgerByHash : fetched ledger hash "
138 Rules const rules{
app_.config().features};
139 Fees const fees =
app_.config().fees.toFees();
146 ret->isImmutable(),
"xrpl::LedgerHistory::getLedgerByHash : immutable loaded ledger");
148 ret->header().hash == hash,
149 "xrpl::LedgerHistory::getLedgerByHash : loaded ledger hash match");
150 ledgersByHash_.canonicalizeReplaceClient(ret->header().hash, ret);
152 ret->header().hash == hash,
"xrpl::LedgerHistory::getLedgerByHash : result hash match");
162 if (metaData !=
nullptr)
164 JLOG(j.
debug()) <<
"MISMATCH on TX " << tx <<
": " << msg
165 <<
" is missing this transaction:\n"
170 JLOG(j.
debug()) <<
"MISMATCH on TX " << tx <<
": " << msg
171 <<
" is missing this transaction.";
189 auto validMetaData = getMeta(validLedger, tx);
190 auto builtMetaData = getMeta(builtLedger, tx);
193 validMetaData || builtMetaData,
"xrpl::log_metadata_difference : some metadata present");
195 if (validMetaData && builtMetaData)
197 auto const& validNodes = validMetaData->getNodes();
198 auto const& builtNodes = builtMetaData->getNodes();
200 bool const resultDiff = validMetaData->getResultTER() != builtMetaData->getResultTER();
202 bool const indexDiff = validMetaData->getIndex() != builtMetaData->getIndex();
204 bool const nodesDiff = validNodes != builtNodes;
206 if (!resultDiff && !indexDiff && !nodesDiff)
208 JLOG(j.
error()) <<
"MISMATCH on TX " << tx <<
": No apparent mismatches detected!";
214 if (resultDiff && indexDiff)
216 JLOG(j.
debug()) <<
"MISMATCH on TX " << tx <<
": Different result and index!";
217 JLOG(j.
debug()) <<
" Built:"
218 <<
" Result: " << builtMetaData->getResult()
219 <<
" Index: " << builtMetaData->getIndex();
220 JLOG(j.
debug()) <<
" Valid:"
221 <<
" Result: " << validMetaData->getResult()
222 <<
" Index: " << validMetaData->getIndex();
226 JLOG(j.
debug()) <<
"MISMATCH on TX " << tx <<
": Different result!";
227 JLOG(j.
debug()) <<
" Built:"
228 <<
" Result: " << builtMetaData->getResult();
229 JLOG(j.
debug()) <<
" Valid:"
230 <<
" Result: " << validMetaData->getResult();
234 JLOG(j.
debug()) <<
"MISMATCH on TX " << tx <<
": Different index!";
235 JLOG(j.
debug()) <<
" Built:"
236 <<
" Index: " << builtMetaData->getIndex();
237 JLOG(j.
debug()) <<
" Valid:"
238 <<
" Index: " << validMetaData->getIndex();
243 if (resultDiff && indexDiff)
245 JLOG(j.
debug()) <<
"MISMATCH on TX " << tx
246 <<
": Different result, index and nodes!";
252 JLOG(j.
debug()) <<
"MISMATCH on TX " << tx <<
": Different result and nodes!";
253 JLOG(j.
debug()) <<
" Built:"
254 <<
" Result: " << builtMetaData->getResult() <<
" Nodes:\n"
256 JLOG(j.
debug()) <<
" Valid:"
257 <<
" Result: " << validMetaData->getResult() <<
" Nodes:\n"
262 JLOG(j.
debug()) <<
"MISMATCH on TX " << tx <<
": Different index and nodes!";
263 JLOG(j.
debug()) <<
" Built:"
264 <<
" Index: " << builtMetaData->getIndex() <<
" Nodes:\n"
266 JLOG(j.
debug()) <<
" Valid:"
267 <<
" Index: " << validMetaData->getIndex() <<
" Nodes:\n"
272 JLOG(j.
debug()) <<
"MISMATCH on TX " << tx <<
": Different nodes!";
273 JLOG(j.
debug()) <<
" Built:"
276 JLOG(j.
debug()) <<
" Valid:"
287 JLOG(j.
error()) <<
"MISMATCH on TX " << tx <<
": Metadata Difference. Valid=\n"
293 JLOG(j.
error()) <<
"MISMATCH on TX " << tx <<
": Metadata Difference. Built=\n"
305 for (
auto const& item : sm)
320 XRPL_ASSERT(built !=
valid,
"xrpl::LedgerHistory::handleMismatch : unequal hashes");
328 JLOG(
j_.error()) <<
"MISMATCH cannot be analyzed:"
335 builtLedger->header().seq == validLedger->header().seq,
336 "xrpl::LedgerHistory::handleMismatch : sequence match");
338 if (
auto stream =
j_.debug())
341 stream <<
"Valid: " <<
getJson({*validLedger, {}});
342 stream <<
"Consensus: " << consensus;
349 if (
builtLedger->header().parentHash != validLedger->header().parentHash)
351 JLOG(
j_.error()) <<
"MISMATCH on prior ledger";
356 if (
builtLedger->header().closeTime != validLedger->header().closeTime)
358 JLOG(
j_.error()) <<
"MISMATCH on close time";
362 if (builtConsensusHash && validatedConsensusHash)
364 if (builtConsensusHash != validatedConsensusHash)
366 JLOG(
j_.error()) <<
"MISMATCH on consensus transaction set "
367 <<
" built: " <<
to_string(*builtConsensusHash)
368 <<
" validated: " <<
to_string(*validatedConsensusHash);
372 JLOG(
j_.error()) <<
"MISMATCH with same consensus transaction set: "
379 auto const validTx =
leaves(validLedger->txMap());
381 if (builtTx == validTx)
383 JLOG(
j_.error()) <<
"MISMATCH with same " << builtTx.size() <<
" transactions";
387 JLOG(
j_.error()) <<
"MISMATCH with " << builtTx.size() <<
" built and " << validTx.size()
388 <<
" valid transactions.";
392 JLOG(
j_.error()) <<
"valid\n" <<
getJson({*validLedger, {}});
395 auto b = builtTx.
begin();
396 auto v = validTx.begin();
397 while (b != builtTx.end() && v != validTx.end())
399 if ((*b)->key() < (*v)->key())
404 else if ((*b)->key() > (*v)->key())
406 logOne(*validLedger, (*v)->key(),
"built",
j_);
411 if ((*b)->slice() != (*v)->slice())
420 for (; b != builtTx.end(); ++b)
422 for (; v != validTx.end(); ++v)
423 logOne(*validLedger, (*v)->key(),
"built",
j_);
433 LedgerHash const hash = ledger->header().hash;
434 XRPL_ASSERT(!hash.
isZero(),
"xrpl::LedgerHistory::builtLedger : nonzero hash");
441 if (entry->validated && !entry->built)
443 if (entry->validated.value() != hash)
445 JLOG(
j_.error()) <<
"MISMATCH: seq=" << index
446 <<
" validated:" << entry->validated.value() <<
" then:" << hash;
449 entry->validated.value(),
451 entry->validatedConsensusHash,
457 JLOG(
j_.debug()) <<
"MATCH: seq=" << index <<
" late";
461 entry->built.emplace(hash);
462 entry->builtConsensusHash.emplace(consensusHash);
463 entry->consensus.emplace(std::move(consensus));
472 LedgerHash const hash = ledger->header().hash;
473 XRPL_ASSERT(!hash.
isZero(),
"xrpl::LedgerHistory::validatedLedger : nonzero hash");
480 if (entry->built && !entry->validated)
482 if (entry->built.value() != hash)
484 JLOG(
j_.error()) <<
"MISMATCH: seq=" << index <<
" built:" << entry->built.value()
487 entry->built.value(),
489 entry->builtConsensusHash,
491 entry->consensus.value());
497 JLOG(
j_.debug()) <<
"MATCH: seq=" << index;
501 entry->validated.emplace(hash);
502 entry->validatedConsensusHash = consensusHash;
515 it->second = ledgerHash;
527 if (!ledger || ledger->header().seq < seq)
A generic endpoint for log messages.
std::shared_ptr< Collector > ptr
const_iterator begin() const
void validatedLedger(std::shared_ptr< Ledger const > const &, std::optional< uint256 > const &consensusHash)
Report that we have validated a particular ledger.
ConsensusValidated consensusValidated_
beast::insight::Collector::ptr collector_
bool insert(std::shared_ptr< Ledger const > const &ledger, bool validated)
Track a ledger.
LedgerHash getLedgerHash(LedgerIndex ledgerIndex)
Get a ledger's hash given its sequence number.
bool fixIndex(LedgerIndex ledgerIndex, LedgerHash const &ledgerHash)
Repair a hash to index mapping.
std::shared_ptr< Ledger const > getLedgerByHash(LedgerHash const &ledgerHash)
Retrieve a ledger given its hash.
void builtLedger(std::shared_ptr< Ledger const > const &, uint256 const &consensusHash, json::Value)
Report that we have locally built a particular ledger.
LedgerHistory(beast::insight::Collector::ptr const &collector, Application &app)
void handleMismatch(LedgerHash const &built, LedgerHash const &valid, std::optional< uint256 > const &builtConsensusHash, std::optional< uint256 > const &validatedConsensusHash, json::Value const &consensus)
Log details in the case where we build one ledger but validate a different one.
beast::insight::Counter mismatchCounter_
std::shared_ptr< Ledger const > getLedgerBySeq(LedgerIndex ledgerIndex)
Get a ledger given its sequence number.
LedgersByHash ledgersByHash_
void clearLedgerCachePrior(LedgerIndex seq)
std::map< LedgerIndex, LedgerHash > ledgersByIndex_
virtual tx_type txRead(key_type const &key) const =0
Read a transaction from the tx map.
LedgerIndex seq() const
Returns the sequence number of the base ledger.
Rules controlling protocol behavior.
uint256 const & key() const
A SHAMap is both a radix tree with a fan-out of 16 and a Merkle tree.
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.
std::uint32_t LedgerIndex
A ledger index.
Stopwatch & stopwatch()
Returns an instance of a wall clock.
static void logMetadataDifference(ReadView const &builtLedger, ReadView const &validLedger, uint256 const &tx, beast::Journal j)
std::shared_ptr< Ledger > loadByIndex(std::uint32_t ledgerIndex, Rules const &rules, Fees const &fees, ServiceRegistry ®istry, bool acquire)
Load a ledger by its sequence number.
std::string to_string(BaseUInt< Bits, Tag > const &a)
void logicError(std::string const &how) noexcept
Called when faulty logic causes a broken invariant.
static std::vector< SHAMapItem const * > leaves(SHAMap const &sm)
static void logOne(ReadView const &ledger, uint256 const &tx, char const *msg, beast::Journal &j)
json::Value getJson(LedgerFill const &fill)
Return a new json::Value representing the ledger with given options.
std::shared_ptr< Ledger > loadByHash(uint256 const &ledgerHash, Rules const &rules, Fees const &fees, ServiceRegistry ®istry, bool acquire)
Load a ledger by its hash.
Reflects the fee settings for a particular ledger.