1#include <xrpld/app/ledger/LedgerHistory.h>
2#include <xrpld/app/ledger/LedgerPersistence.h>
3#include <xrpld/app/ledger/LedgerToJson.h>
4#include <xrpld/app/main/Application.h>
6#include <xrpl/basics/chrono.h>
7#include <xrpl/basics/contract.h>
8#include <xrpl/json/to_string.h>
16 , collector_(collector)
17 , mismatch_counter_(collector->make_counter(
"ledger.history",
"mismatch"))
23 app_.getJournal(
"TaggedCache"))
24 , m_consensus_validated(
27 std::chrono::minutes{5},
29 app_.getJournal(
"TaggedCache"))
30 , j_(app.getJournal(
"LedgerHistory"))
37 if (!ledger->isImmutable())
41 ledger->stateMap().getHash().isNonZero(),
"xrpl::LedgerHistory::insert : nonzero hash");
45 bool const alreadyHad =
71 uint256 const hash = it->second;
85 ret->header().seq == index,
"xrpl::LedgerHistory::getLedgerBySeq : result sequence match");
92 ret->isImmutable(),
"xrpl::LedgerHistory::getLedgerBySeq : immutable result ledger");
95 return (ret->header().seq == index) ? ret :
nullptr;
108 "xrpl::LedgerHistory::getLedgerByHash : immutable fetched "
111 ret->header().hash == hash,
112 "xrpl::LedgerHistory::getLedgerByHash : fetched ledger hash "
125 ret->isImmutable(),
"xrpl::LedgerHistory::getLedgerByHash : immutable loaded ledger");
127 ret->header().hash == hash,
128 "xrpl::LedgerHistory::getLedgerByHash : loaded ledger hash match");
131 ret->header().hash == hash,
"xrpl::LedgerHistory::getLedgerByHash : result hash match");
141 if (metaData !=
nullptr)
143 JLOG(j.
debug()) <<
"MISMATCH on TX " << tx <<
": " << msg
144 <<
" is missing this transaction:\n"
149 JLOG(j.
debug()) <<
"MISMATCH on TX " << tx <<
": " << msg
150 <<
" is missing this transaction.";
168 auto validMetaData = getMeta(validLedger, tx);
169 auto builtMetaData = getMeta(builtLedger, tx);
172 validMetaData || builtMetaData,
"xrpl::log_metadata_difference : some metadata present");
174 if (validMetaData && builtMetaData)
176 auto const& validNodes = validMetaData->getNodes();
177 auto const& builtNodes = builtMetaData->getNodes();
179 bool const result_diff = validMetaData->getResultTER() != builtMetaData->getResultTER();
181 bool const index_diff = validMetaData->getIndex() != builtMetaData->getIndex();
183 bool const nodes_diff = validNodes != builtNodes;
185 if (!result_diff && !index_diff && !nodes_diff)
187 JLOG(j.
error()) <<
"MISMATCH on TX " << tx <<
": No apparent mismatches detected!";
193 if (result_diff && index_diff)
195 JLOG(j.
debug()) <<
"MISMATCH on TX " << tx <<
": Different result and index!";
196 JLOG(j.
debug()) <<
" Built:"
197 <<
" Result: " << builtMetaData->getResult()
198 <<
" Index: " << builtMetaData->getIndex();
199 JLOG(j.
debug()) <<
" Valid:"
200 <<
" Result: " << validMetaData->getResult()
201 <<
" Index: " << validMetaData->getIndex();
203 else if (result_diff)
205 JLOG(j.
debug()) <<
"MISMATCH on TX " << tx <<
": Different result!";
206 JLOG(j.
debug()) <<
" Built:"
207 <<
" Result: " << builtMetaData->getResult();
208 JLOG(j.
debug()) <<
" Valid:"
209 <<
" Result: " << validMetaData->getResult();
213 JLOG(j.
debug()) <<
"MISMATCH on TX " << tx <<
": Different index!";
214 JLOG(j.
debug()) <<
" Built:"
215 <<
" Index: " << builtMetaData->getIndex();
216 JLOG(j.
debug()) <<
" Valid:"
217 <<
" Index: " << validMetaData->getIndex();
222 if (result_diff && index_diff)
224 JLOG(j.
debug()) <<
"MISMATCH on TX " << tx
225 <<
": Different result, index and nodes!";
229 else if (result_diff)
231 JLOG(j.
debug()) <<
"MISMATCH on TX " << tx <<
": Different result and nodes!";
232 JLOG(j.
debug()) <<
" Built:"
233 <<
" Result: " << builtMetaData->getResult() <<
" Nodes:\n"
235 JLOG(j.
debug()) <<
" Valid:"
236 <<
" Result: " << validMetaData->getResult() <<
" Nodes:\n"
241 JLOG(j.
debug()) <<
"MISMATCH on TX " << tx <<
": Different index and nodes!";
242 JLOG(j.
debug()) <<
" Built:"
243 <<
" Index: " << builtMetaData->getIndex() <<
" Nodes:\n"
245 JLOG(j.
debug()) <<
" Valid:"
246 <<
" Index: " << validMetaData->getIndex() <<
" Nodes:\n"
251 JLOG(j.
debug()) <<
"MISMATCH on TX " << tx <<
": Different nodes!";
252 JLOG(j.
debug()) <<
" Built:"
255 JLOG(j.
debug()) <<
" Valid:"
266 JLOG(j.
error()) <<
"MISMATCH on TX " << tx <<
": Metadata Difference. Valid=\n"
272 JLOG(j.
error()) <<
"MISMATCH on TX " << tx <<
": Metadata Difference. Built=\n"
284 for (
auto const& item : sm)
287 return lhs->key() < rhs->key();
300 XRPL_ASSERT(built !=
valid,
"xrpl::LedgerHistory::handleMismatch : unequal hashes");
308 JLOG(
j_.
error()) <<
"MISMATCH cannot be analyzed:"
315 builtLedger->header().seq == validLedger->header().seq,
316 "xrpl::LedgerHistory::handleMismatch : sequence match");
321 stream <<
"Valid: " <<
getJson({*validLedger, {}});
322 stream <<
"Consensus: " << consensus;
329 if (
builtLedger->header().parentHash != validLedger->header().parentHash)
331 JLOG(
j_.
error()) <<
"MISMATCH on prior ledger";
336 if (
builtLedger->header().closeTime != validLedger->header().closeTime)
338 JLOG(
j_.
error()) <<
"MISMATCH on close time";
342 if (builtConsensusHash && validatedConsensusHash)
344 if (builtConsensusHash != validatedConsensusHash)
346 JLOG(
j_.
error()) <<
"MISMATCH on consensus transaction set "
347 <<
" built: " <<
to_string(*builtConsensusHash)
348 <<
" validated: " <<
to_string(*validatedConsensusHash);
351 JLOG(
j_.
error()) <<
"MISMATCH with same consensus transaction set: "
357 auto const validTx =
leaves(validLedger->txMap());
359 if (builtTx == validTx)
361 JLOG(
j_.
error()) <<
"MISMATCH with same " << builtTx.size() <<
" transactions";
364 JLOG(
j_.
error()) <<
"MISMATCH with " << builtTx.size() <<
" built and " << validTx.size()
365 <<
" valid transactions.";
371 auto b = builtTx.
begin();
372 auto v = validTx.begin();
373 while (b != builtTx.end() && v != validTx.end())
375 if ((*b)->key() < (*v)->key())
380 else if ((*b)->key() > (*v)->key())
382 log_one(*validLedger, (*v)->key(),
"built",
j_);
387 if ((*b)->slice() != (*v)->slice())
396 for (; b != builtTx.end(); ++b)
398 for (; v != validTx.end(); ++v)
399 log_one(*validLedger, (*v)->key(),
"built",
j_);
409 LedgerHash const hash = ledger->header().hash;
410 XRPL_ASSERT(!hash.
isZero(),
"xrpl::LedgerHistory::builtLedger : nonzero hash");
417 if (entry->validated && !entry->built)
419 if (entry->validated.value() != hash)
421 JLOG(
j_.
error()) <<
"MISMATCH: seq=" << index
422 <<
" validated:" << entry->validated.value() <<
" then:" << hash;
425 entry->validated.value(),
427 entry->validatedConsensusHash,
433 JLOG(
j_.
debug()) <<
"MATCH: seq=" << index <<
" late";
437 entry->built.emplace(hash);
438 entry->builtConsensusHash.emplace(consensusHash);
439 entry->consensus.emplace(std::move(consensus));
448 LedgerHash const hash = ledger->header().hash;
449 XRPL_ASSERT(!hash.
isZero(),
"xrpl::LedgerHistory::validatedLedger : nonzero hash");
456 if (entry->built && !entry->validated)
458 if (entry->built.value() != hash)
460 JLOG(
j_.
error()) <<
"MISMATCH: seq=" << index <<
" built:" << entry->built.value()
463 entry->built.value(),
465 entry->builtConsensusHash,
467 entry->consensus.value());
472 JLOG(
j_.
debug()) <<
"MATCH: seq=" << index;
476 entry->validated.emplace(hash);
477 entry->validatedConsensusHash = consensusHash;
490 it->second = ledgerHash;
502 if (!ledger || ledger->header().seq < seq)
const_iterator begin() const
A generic endpoint for log messages.
virtual Config & config()=0
std::unordered_set< uint256, beast::uhash<> > features
std::map< LedgerIndex, LedgerHash > mLedgersByIndex
void validatedLedger(std::shared_ptr< Ledger const > const &, std::optional< uint256 > const &consensusHash)
Report that we have validated a particular ledger.
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.
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.
ConsensusValidated m_consensus_validated
LedgerHistory(beast::insight::Collector::ptr const &collector, Application &app)
LedgersByHash m_ledgers_by_hash
void builtLedger(std::shared_ptr< Ledger const > const &, uint256 const &consensusHash, Json::Value)
Report that we have locally built a particular ledger.
beast::insight::Counter mismatch_counter_
std::shared_ptr< Ledger const > getLedgerBySeq(LedgerIndex ledgerIndex)
Get a ledger given its sequence number.
void clearLedgerCachePrior(LedgerIndex seq)
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.
A SHAMap is both a radix tree with a fan-out of 16 and a Merkle tree.
std::vector< key_type > getKeys() const
bool del(key_type const &key, bool valid)
bool canonicalize_replace_cache(key_type const &key, SharedPointerType const &data)
bool canonicalize_replace_client(key_type const &key, SharedPointerType &data)
SharedPointerType fetch(key_type const &key)
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.
void LogicError(std::string const &how) noexcept
Called when faulty logic causes a broken invariant.
Json::Value getJson(LedgerFill const &fill)
Return a new Json::Value representing the ledger with given options.
Stopwatch & stopwatch()
Returns an instance of a wall clock.
std::string to_string(base_uint< Bits, Tag > const &a)
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.
static void log_metadata_difference(ReadView const &builtLedger, ReadView const &validLedger, uint256 const &tx, beast::Journal j)
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, Rules const &rules, Fees const &fees, ServiceRegistry ®istry, bool acquire)
Load a ledger by its hash.
Fees toFees() const
Convert to a Fees object for use with Ledger construction.
Reflects the fee settings for a particular ledger.