20#include <xrpld/app/ledger/LedgerHistory.h>
21#include <xrpld/app/ledger/LedgerToJson.h>
22#include <xrpl/basics/Log.h>
23#include <xrpl/basics/chrono.h>
24#include <xrpl/basics/contract.h>
25#include <xrpl/json/to_string.h>
35 , collector_(collector)
36 , mismatch_counter_(collector->make_counter(
"ledger.history",
"mismatch"))
42 app_.journal(
"TaggedCache"))
43 , m_consensus_validated(
46 std::chrono::minutes{5},
48 app_.journal(
"TaggedCache"))
49 , j_(app.journal(
"LedgerHistory"))
58 if (!ledger->isImmutable())
62 ledger->stateMap().getHash().isNonZero(),
63 "ripple::LedgerHistory::insert : nonzero hash");
68 ledger->info().hash, ledger);
105 ret->info().seq == index,
106 "ripple::LedgerHistory::getLedgerBySeq : result sequence match");
114 "ripple::LedgerHistory::getLedgerBySeq : immutable result ledger");
117 return (ret->info().seq == index) ? ret :
nullptr;
130 "ripple::LedgerHistory::getLedgerByHash : immutable fetched "
133 ret->info().hash == hash,
134 "ripple::LedgerHistory::getLedgerByHash : fetched ledger hash "
146 "ripple::LedgerHistory::getLedgerByHash : immutable loaded ledger");
148 ret->info().hash == hash,
149 "ripple::LedgerHistory::getLedgerByHash : loaded ledger hash match");
152 ret->info().hash == hash,
153 "ripple::LedgerHistory::getLedgerByHash : result hash match");
167 if (metaData !=
nullptr)
169 JLOG(j.
debug()) <<
"MISMATCH on TX " << tx <<
": " << msg
170 <<
" is missing this transaction:\n"
175 JLOG(j.
debug()) <<
"MISMATCH on TX " << tx <<
": " << msg
176 <<
" is missing this transaction.";
194 auto validMetaData = getMeta(validLedger, tx);
195 auto builtMetaData = getMeta(builtLedger, tx);
198 validMetaData || builtMetaData,
199 "ripple::log_metadata_difference : some metadata present");
201 if (validMetaData && builtMetaData)
203 auto const& validNodes = validMetaData->getNodes();
204 auto const& builtNodes = builtMetaData->getNodes();
206 bool const result_diff =
207 validMetaData->getResultTER() != builtMetaData->getResultTER();
209 bool const index_diff =
210 validMetaData->getIndex() != builtMetaData->getIndex();
212 bool const nodes_diff = validNodes != builtNodes;
214 if (!result_diff && !index_diff && !nodes_diff)
216 JLOG(j.
error()) <<
"MISMATCH on TX " << tx
217 <<
": No apparent mismatches detected!";
223 if (result_diff && index_diff)
225 JLOG(j.
debug()) <<
"MISMATCH on TX " << tx
226 <<
": Different result and index!";
228 <<
" Built:" <<
" Result: " << builtMetaData->getResult()
229 <<
" Index: " << builtMetaData->getIndex();
231 <<
" Valid:" <<
" Result: " << validMetaData->getResult()
232 <<
" Index: " << validMetaData->getIndex();
234 else if (result_diff)
237 <<
"MISMATCH on TX " << tx <<
": Different result!";
239 <<
" Built:" <<
" Result: " << builtMetaData->getResult();
241 <<
" Valid:" <<
" Result: " << validMetaData->getResult();
246 <<
"MISMATCH on TX " << tx <<
": Different index!";
248 <<
" Built:" <<
" Index: " << builtMetaData->getIndex();
250 <<
" Valid:" <<
" Index: " << validMetaData->getIndex();
255 if (result_diff && index_diff)
257 JLOG(j.
debug()) <<
"MISMATCH on TX " << tx
258 <<
": Different result, index and nodes!";
259 JLOG(j.
debug()) <<
" Built:\n"
261 JLOG(j.
debug()) <<
" Valid:\n"
264 else if (result_diff)
266 JLOG(j.
debug()) <<
"MISMATCH on TX " << tx
267 <<
": Different result and nodes!";
269 <<
" Built:" <<
" Result: " << builtMetaData->getResult()
273 <<
" Valid:" <<
" Result: " << validMetaData->getResult()
279 JLOG(j.
debug()) <<
"MISMATCH on TX " << tx
280 <<
": Different index and nodes!";
282 <<
" Built:" <<
" Index: " << builtMetaData->getIndex()
286 <<
" Valid:" <<
" Index: " << validMetaData->getIndex()
293 <<
"MISMATCH on TX " << tx <<
": Different nodes!";
294 JLOG(j.
debug()) <<
" Built:" <<
" Nodes:\n"
296 JLOG(j.
debug()) <<
" Valid:" <<
" Nodes:\n"
306 JLOG(j.
error()) <<
"MISMATCH on TX " << tx
307 <<
": Metadata Difference. Valid=\n"
313 JLOG(j.
error()) <<
"MISMATCH on TX " << tx
314 <<
": Metadata Difference. Built=\n"
326 for (
auto const& item : sm)
330 return lhs->key() < rhs->key();
345 "ripple::LedgerHistory::handleMismatch : unequal hashes");
353 JLOG(
j_.
error()) <<
"MISMATCH cannot be analyzed:" <<
" builtLedger: "
361 builtLedger->info().seq == validLedger->info().seq,
362 "ripple::LedgerHistory::handleMismatch : sequence match");
367 stream <<
"Valid: " <<
getJson({*validLedger, {}});
368 stream <<
"Consensus: " << consensus;
375 if (
builtLedger->info().parentHash != validLedger->info().parentHash)
377 JLOG(
j_.
error()) <<
"MISMATCH on prior ledger";
382 if (
builtLedger->info().closeTime != validLedger->info().closeTime)
384 JLOG(
j_.
error()) <<
"MISMATCH on close time";
388 if (builtConsensusHash && validatedConsensusHash)
390 if (builtConsensusHash != validatedConsensusHash)
392 <<
"MISMATCH on consensus transaction set "
393 <<
" built: " <<
to_string(*builtConsensusHash)
394 <<
" validated: " <<
to_string(*validatedConsensusHash);
396 JLOG(
j_.
error()) <<
"MISMATCH with same consensus transaction set: "
402 auto const validTx =
leaves(validLedger->txMap());
404 if (builtTx == validTx)
405 JLOG(
j_.
error()) <<
"MISMATCH with same " << builtTx.size()
408 JLOG(
j_.
error()) <<
"MISMATCH with " << builtTx.size() <<
" built and "
409 << validTx.size() <<
" valid transactions.";
415 auto b = builtTx.
begin();
416 auto v = validTx.begin();
417 while (b != builtTx.end() && v != validTx.end())
419 if ((*b)->key() < (*v)->key())
424 else if ((*b)->key() > (*v)->key())
426 log_one(*validLedger, (*v)->key(),
"built",
j_);
431 if ((*b)->slice() != (*v)->slice())
441 for (; b != builtTx.end(); ++b)
443 for (; v != validTx.end(); ++v)
444 log_one(*validLedger, (*v)->key(),
"built",
j_);
456 !hash.
isZero(),
"ripple::LedgerHistory::builtLedger : nonzero hash");
460 auto entry = std::make_shared<cv_entry>();
463 if (entry->validated && !entry->built)
465 if (entry->validated.value() != hash)
467 JLOG(
j_.
error()) <<
"MISMATCH: seq=" << index
468 <<
" validated:" << entry->validated.value()
472 entry->validated.value(),
474 entry->validatedConsensusHash,
480 JLOG(
j_.
debug()) <<
"MATCH: seq=" << index <<
" late";
484 entry->built.emplace(hash);
485 entry->builtConsensusHash.emplace(consensusHash);
486 entry->consensus.emplace(std::move(consensus));
498 "ripple::LedgerHistory::validatedLedger : nonzero hash");
502 auto entry = std::make_shared<cv_entry>();
505 if (entry->built && !entry->validated)
507 if (entry->built.value() != hash)
510 <<
"MISMATCH: seq=" << index
511 <<
" built:" << entry->built.value() <<
" then:" << hash;
513 entry->built.value(),
515 entry->builtConsensusHash,
517 entry->consensus.
value());
522 JLOG(
j_.
debug()) <<
"MATCH: seq=" << index;
526 entry->validated.emplace(hash);
527 entry->validatedConsensusHash = consensusHash;
540 it->second = ledgerHash;
552 if (!ledger || ledger->info().seq < seq)
const_iterator begin() const
A generic endpoint for log messages.
LedgerHistory(beast::insight::Collector::ptr const &collector, Application &app)
std::map< LedgerIndex, LedgerHash > mLedgersByIndex
void builtLedger(std::shared_ptr< Ledger const > const &, uint256 const &consensusHash, Json::Value)
Report that we have locally built a particular ledger.
LedgersByHash m_ledgers_by_hash
ConsensusValidated m_consensus_validated
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.
LedgerHash getLedgerHash(LedgerIndex ledgerIndex)
Get a ledger's hash given its sequence number.
void clearLedgerCachePrior(LedgerIndex seq)
std::shared_ptr< Ledger const > getLedgerBySeq(LedgerIndex ledgerIndex)
Get a ledger given its sequence number.
beast::insight::Counter mismatch_counter_
bool insert(std::shared_ptr< Ledger const > const &ledger, bool validated)
Track a ledger.
bool fixIndex(LedgerIndex ledgerIndex, LedgerHash const &ledgerHash)
Repair a hash to index mapping.
void validatedLedger(std::shared_ptr< Ledger const > const &, std::optional< uint256 > const &consensusHash)
Report that we have validated a particular ledger.
std::shared_ptr< Ledger const > getLedgerByHash(LedgerHash const &ledgerHash)
Retrieve a ledger given its hash.
LedgerIndex seq() const
Returns the sequence number of the base ledger.
virtual tx_type txRead(key_type const &key) const =0
Read a transaction from the tx map.
A SHAMap is both a radix tree with a fan-out of 16 and a Merkle tree.
std::shared_ptr< T > fetch(const key_type &key)
bool canonicalize_replace_cache(const key_type &key, std::shared_ptr< T > const &data)
bool del(const key_type &key, bool valid)
bool canonicalize_replace_client(const key_type &key, std::shared_ptr< T > &data)
std::vector< key_type > getKeys() const
TER valid(PreclaimContext const &ctx, AccountID const &src)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
std::shared_ptr< Ledger > loadByIndex(std::uint32_t ledgerIndex, Application &app, bool acquire)
static void log_one(ReadView const &ledger, uint256 const &tx, char const *msg, beast::Journal &j)
static std::vector< SHAMapItem const * > leaves(SHAMap const &sm)
std::shared_ptr< Ledger > loadByHash(uint256 const &ledgerHash, Application &app, bool acquire)
Stopwatch & stopwatch()
Returns an instance of a wall clock.
std::string to_string(base_uint< Bits, Tag > const &a)
static void log_metadata_difference(ReadView const &builtLedger, ReadView const &validLedger, uint256 const &tx, beast::Journal j)
Json::Value getJson(LedgerFill const &fill)
Return a new Json::Value representing the ledger with given options.
void LogicError(std::string const &how) noexcept
Called when faulty logic causes a broken invariant.