rippled
LedgerHistory.cpp
1 //------------------------------------------------------------------------------
2 /*
3  This file is part of rippled: https://github.com/ripple/rippled
4  Copyright (c) 2012, 2013 Ripple Labs Inc.
5 
6  Permission to use, copy, modify, and/or distribute this software for any
7  purpose with or without fee is hereby granted, provided that the above
8  copyright notice and this permission notice appear in all copies.
9 
10  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18 //==============================================================================
19 
20 #include <ripple/app/ledger/LedgerHistory.h>
21 #include <ripple/app/ledger/LedgerToJson.h>
22 #include <ripple/basics/Log.h>
23 #include <ripple/basics/chrono.h>
24 #include <ripple/basics/contract.h>
25 #include <ripple/json/to_string.h>
26 
27 namespace ripple {
28 
29 // VFALCO TODO replace macros
30 
31 #ifndef CACHED_LEDGER_NUM
32 #define CACHED_LEDGER_NUM 96
33 #endif
34 
36 
37 // FIXME: Need to clean up ledgers by index at some point
38 
40  beast::insight::Collector::ptr const& collector,
41  Application& app)
42  : app_ (app)
43  , collector_ (collector)
44  , mismatch_counter_ (collector->make_counter ("ledger.history", "mismatch"))
45  , m_ledgers_by_hash ("LedgerCache", CACHED_LEDGER_NUM, CachedLedgerAge,
46  stopwatch(), app_.journal("TaggedCache"))
47  , m_consensus_validated ("ConsensusValidated", 64, std::chrono::minutes {5},
48  stopwatch(), app_.journal("TaggedCache"))
49  , j_ (app.journal ("LedgerHistory"))
50 {
51 }
52 
53 bool
56  bool validated)
57 {
58  if(! ledger->isImmutable())
59  LogicError("mutable Ledger in insert");
60 
61  assert (ledger->stateMap().getHash ().isNonZero ());
62 
64 
65  const bool alreadyHad = m_ledgers_by_hash.canonicalize_replace_cache(
66  ledger->info().hash, ledger);
67  if (validated)
68  mLedgersByIndex[ledger->info().seq] = ledger->info().hash;
69 
70  return alreadyHad;
71 }
72 
74 {
76  auto it = mLedgersByIndex.find (index);
77 
78  if (it != mLedgersByIndex.end ())
79  return it->second;
80 
81  return uint256 ();
82 }
83 
86 {
87  {
89  auto it = mLedgersByIndex.find (index);
90 
91  if (it != mLedgersByIndex.end ())
92  {
93  uint256 hash = it->second;
94  sl.unlock ();
95  return getLedgerByHash (hash);
96  }
97  }
98 
100 
101  if (!ret)
102  return ret;
103 
104  assert (ret->info().seq == index);
105 
106  {
107  // Add this ledger to the local tracking by index
109 
110  assert (ret->isImmutable ());
111  m_ledgers_by_hash.canonicalize_replace_client(ret->info().hash, ret);
112  mLedgersByIndex[ret->info().seq] = ret->info().hash;
113  return (ret->info().seq == index) ? ret : nullptr;
114  }
115 }
116 
119 {
120  auto ret = m_ledgers_by_hash.fetch (hash);
121 
122  if (ret)
123  {
124  assert (ret->isImmutable ());
125  assert (ret->info().hash == hash);
126  return ret;
127  }
128 
129  ret = loadByHash (hash, app_);
130 
131  if (!ret)
132  return ret;
133 
134  assert (ret->isImmutable ());
135  assert (ret->info().hash == hash);
136  m_ledgers_by_hash.canonicalize_replace_client(ret->info().hash, ret);
137  assert (ret->info().hash == hash);
138 
139  return ret;
140 }
141 
142 static
143 void
145  ReadView const& ledger,
146  uint256 const& tx,
147  char const* msg,
148  beast::Journal& j)
149 {
150  auto metaData = ledger.txRead(tx).second;
151 
152  if (metaData != nullptr)
153  {
154  JLOG (j.debug()) << "MISMATCH on TX " << tx <<
155  ": " << msg << " is missing this transaction:\n" <<
156  metaData->getJson (JsonOptions::none);
157  }
158  else
159  {
160  JLOG (j.debug()) << "MISMATCH on TX " << tx <<
161  ": " << msg << " is missing this transaction.";
162  }
163 }
164 
165 static
166 void
168  ReadView const& builtLedger,
169  ReadView const& validLedger,
170  uint256 const& tx,
171  beast::Journal j)
172 {
173  auto getMeta = [](ReadView const& ledger,
174  uint256 const& txID) -> std::shared_ptr<TxMeta>
175  {
176  auto meta = ledger.txRead(txID).second;
177  if (!meta)
178  return {};
179  return std::make_shared<TxMeta> (txID, ledger.seq(), *meta);
180  };
181 
182  auto validMetaData = getMeta (validLedger, tx);
183  auto builtMetaData = getMeta (builtLedger, tx);
184  assert(validMetaData != nullptr || builtMetaData != nullptr);
185 
186  if (validMetaData != nullptr && builtMetaData != nullptr)
187  {
188  auto const& validNodes = validMetaData->getNodes ();
189  auto const& builtNodes = builtMetaData->getNodes ();
190 
191  bool const result_diff =
192  validMetaData->getResultTER () != builtMetaData->getResultTER ();
193 
194  bool const index_diff =
195  validMetaData->getIndex() != builtMetaData->getIndex ();
196 
197  bool const nodes_diff = validNodes != builtNodes;
198 
199  if (!result_diff && !index_diff && !nodes_diff)
200  {
201  JLOG (j.error()) << "MISMATCH on TX " << tx <<
202  ": No apparent mismatches detected!";
203  return;
204  }
205 
206  if (!nodes_diff)
207  {
208  if (result_diff && index_diff)
209  {
210  JLOG (j.debug()) << "MISMATCH on TX " << tx <<
211  ": Different result and index!";
212  JLOG (j.debug()) << " Built:" <<
213  " Result: " << builtMetaData->getResult () <<
214  " Index: " << builtMetaData->getIndex ();
215  JLOG (j.debug()) << " Valid:" <<
216  " Result: " << validMetaData->getResult () <<
217  " Index: " << validMetaData->getIndex ();
218  }
219  else if (result_diff)
220  {
221  JLOG (j.debug()) << "MISMATCH on TX " << tx <<
222  ": Different result!";
223  JLOG (j.debug()) << " Built:" <<
224  " Result: " << builtMetaData->getResult ();
225  JLOG (j.debug()) << " Valid:" <<
226  " Result: " << validMetaData->getResult ();
227  }
228  else if (index_diff)
229  {
230  JLOG (j.debug()) << "MISMATCH on TX " << tx <<
231  ": Different index!";
232  JLOG (j.debug()) << " Built:" <<
233  " Index: " << builtMetaData->getIndex ();
234  JLOG (j.debug()) << " Valid:" <<
235  " Index: " << validMetaData->getIndex ();
236  }
237  }
238  else
239  {
240  if (result_diff && index_diff)
241  {
242  JLOG (j.debug()) << "MISMATCH on TX " << tx <<
243  ": Different result, index and nodes!";
244  JLOG (j.debug()) << " Built:\n" <<
245  builtMetaData->getJson (JsonOptions::none);
246  JLOG (j.debug()) << " Valid:\n" <<
247  validMetaData->getJson (JsonOptions::none);
248  }
249  else if (result_diff)
250  {
251  JLOG (j.debug()) << "MISMATCH on TX " << tx <<
252  ": Different result and nodes!";
253  JLOG (j.debug()) << " Built:" <<
254  " Result: " << builtMetaData->getResult () <<
255  " Nodes:\n" << builtNodes.getJson (JsonOptions::none);
256  JLOG (j.debug()) << " Valid:" <<
257  " Result: " << validMetaData->getResult () <<
258  " Nodes:\n" << validNodes.getJson (JsonOptions::none);
259  }
260  else if (index_diff)
261  {
262  JLOG (j.debug()) << "MISMATCH on TX " << tx <<
263  ": Different index and nodes!";
264  JLOG (j.debug()) << " Built:" <<
265  " Index: " << builtMetaData->getIndex () <<
266  " Nodes:\n" << builtNodes.getJson (JsonOptions::none);
267  JLOG (j.debug()) << " Valid:" <<
268  " Index: " << validMetaData->getIndex () <<
269  " Nodes:\n" << validNodes.getJson (JsonOptions::none);
270  }
271  else // nodes_diff
272  {
273  JLOG (j.debug()) << "MISMATCH on TX " << tx <<
274  ": Different nodes!";
275  JLOG (j.debug()) << " Built:" <<
276  " Nodes:\n" << builtNodes.getJson (JsonOptions::none);
277  JLOG (j.debug()) << " Valid:" <<
278  " Nodes:\n" << validNodes.getJson (JsonOptions::none);
279  }
280  }
281  }
282  else if (validMetaData != nullptr)
283  {
284  JLOG (j.error()) << "MISMATCH on TX " << tx <<
285  ": Metadata Difference (built has none)\n" <<
286  validMetaData->getJson (JsonOptions::none);
287  }
288  else // builtMetaData != nullptr
289  {
290  JLOG (j.error()) << "MISMATCH on TX " << tx <<
291  ": Metadata Difference (valid has none)\n" <<
292  builtMetaData->getJson (JsonOptions::none);
293  }
294 }
295 
296 //------------------------------------------------------------------------------
297 
298 // Return list of leaves sorted by key
299 static
301 leaves (SHAMap const& sm)
302 {
304  for (auto const& item : sm)
305  v.push_back(&item);
306  std::sort(v.begin(), v.end(),
307  [](SHAMapItem const* lhs, SHAMapItem const* rhs)
308  { return lhs->key() < rhs->key(); });
309  return v;
310 }
311 
312 void
314  LedgerHash const& built,
315  LedgerHash const& valid,
316  boost::optional<uint256> const& builtConsensusHash,
317  boost::optional<uint256> const& validatedConsensusHash,
318  Json::Value const& consensus)
319 {
320  assert (built != valid);
322 
323  auto builtLedger = getLedgerByHash (built);
324  auto validLedger = getLedgerByHash (valid);
325 
326  if (!builtLedger || !validLedger)
327  {
328  JLOG (j_.error()) << "MISMATCH cannot be analyzed:" <<
329  " builtLedger: " << to_string (built) << " -> " << builtLedger <<
330  " validLedger: " << to_string (valid) << " -> " << validLedger;
331  return;
332  }
333 
334  assert (builtLedger->info().seq == validLedger->info().seq);
335 
336  if (auto stream = j_.debug())
337  {
338  stream << "Built: " << getJson (*builtLedger);
339  stream << "Valid: " << getJson (*validLedger);
340  stream << "Consensus: " << consensus;
341  }
342 
343  // Determine the mismatch reason, distinguishing Byzantine
344  // failure from transaction processing difference
345 
346  // Disagreement over prior ledger indicates sync issue
347  if (builtLedger->info().parentHash != validLedger->info().parentHash)
348  {
349  JLOG (j_.error()) << "MISMATCH on prior ledger";
350  return;
351  }
352 
353  // Disagreement over close time indicates Byzantine failure
354  if (builtLedger->info().closeTime != validLedger->info().closeTime)
355  {
356  JLOG (j_.error()) << "MISMATCH on close time";
357  return;
358  }
359 
360  if (builtConsensusHash && validatedConsensusHash)
361  {
362  if (builtConsensusHash != validatedConsensusHash)
363  JLOG(j_.error())
364  << "MISMATCH on consensus transaction set "
365  << " built: " << to_string(*builtConsensusHash)
366  << " validated: " << to_string(*validatedConsensusHash);
367  else
368  JLOG(j_.error()) << "MISMATCH with same consensus transaction set: "
369  << to_string(*builtConsensusHash);
370  }
371 
372  // Find differences between built and valid ledgers
373  auto const builtTx = leaves(builtLedger->txMap());
374  auto const validTx = leaves(validLedger->txMap());
375 
376  if (builtTx == validTx)
377  JLOG (j_.error()) <<
378  "MISMATCH with same " << builtTx.size() <<
379  " transactions";
380  else
381  JLOG (j_.error()) << "MISMATCH with " <<
382  builtTx.size() << " built and " <<
383  validTx.size() << " valid transactions.";
384 
385  JLOG (j_.error()) << "built\n" << getJson(*builtLedger);
386  JLOG (j_.error()) << "valid\n" << getJson(*validLedger);
387 
388  // Log all differences between built and valid ledgers
389  auto b = builtTx.begin();
390  auto v = validTx.begin();
391  while(b != builtTx.end() && v != validTx.end())
392  {
393  if ((*b)->key() < (*v)->key())
394  {
395  log_one (*builtLedger, (*b)->key(), "valid", j_);
396  ++b;
397  }
398  else if ((*b)->key() > (*v)->key())
399  {
400  log_one(*validLedger, (*v)->key(), "built", j_);
401  ++v;
402  }
403  else
404  {
405  if ((*b)->peekData() != (*v)->peekData())
406  {
407  // Same transaction with different metadata
409  *builtLedger,
410  *validLedger, (*b)->key(), j_);
411  }
412  ++b;
413  ++v;
414  }
415  }
416  for (; b != builtTx.end(); ++b)
417  log_one (*builtLedger, (*b)->key(), "valid", j_);
418  for (; v != validTx.end(); ++v)
419  log_one (*validLedger, (*v)->key(), "built", j_);
420 }
421 
423  std::shared_ptr<Ledger const> const& ledger,
424  uint256 const& consensusHash,
425  Json::Value consensus)
426 {
427  LedgerIndex index = ledger->info().seq;
428  LedgerHash hash = ledger->info().hash;
429  assert (!hash.isZero());
430 
431  std::unique_lock sl (
433 
434  auto entry = std::make_shared<cv_entry>();
436 
437  if (entry->validated && ! entry->built)
438  {
439  if (entry->validated.get() != hash)
440  {
441  JLOG (j_.error()) << "MISMATCH: seq=" << index
442  << " validated:" << entry->validated.get()
443  << " then:" << hash;
445  hash,
446  entry->validated.get(),
447  consensusHash,
448  entry->validatedConsensusHash,
449  consensus);
450  }
451  else
452  {
453  // We validated a ledger and then built it locally
454  JLOG (j_.debug()) << "MATCH: seq=" << index << " late";
455  }
456  }
457 
458  entry->built.emplace (hash);
459  entry->builtConsensusHash.emplace(consensusHash);
460  entry->consensus.emplace (std::move (consensus));
461 }
462 
464  std::shared_ptr<Ledger const> const& ledger,
465  boost::optional<uint256> const& consensusHash)
466 {
467  LedgerIndex index = ledger->info().seq;
468  LedgerHash hash = ledger->info().hash;
469  assert (!hash.isZero());
470 
471  std::unique_lock sl (
473 
474  auto entry = std::make_shared<cv_entry>();
476 
477  if (entry->built && ! entry->validated)
478  {
479  if (entry->built.get() != hash)
480  {
481  JLOG (j_.error()) << "MISMATCH: seq=" << index
482  << " built:" << entry->built.get()
483  << " then:" << hash;
485  entry->built.get(),
486  hash,
487  entry->builtConsensusHash,
488  consensusHash,
489  entry->consensus.get());
490  }
491  else
492  {
493  // We built a ledger locally and then validated it
494  JLOG (j_.debug()) << "MATCH: seq=" << index;
495  }
496  }
497 
498  entry->validated.emplace (hash);
499  entry->validatedConsensusHash = consensusHash;
500 }
501 
505  LedgerIndex ledgerIndex, LedgerHash const& ledgerHash)
506 {
508  auto it = mLedgersByIndex.find (ledgerIndex);
509 
510  if ((it != mLedgersByIndex.end ()) && (it->second != ledgerHash) )
511  {
512  it->second = ledgerHash;
513  return false;
514  }
515  return true;
516 }
517 
519 {
522 }
523 
525 {
527  {
528  auto const ledger = getLedgerByHash (it);
529  if (!ledger || ledger->info().seq < seq)
530  m_ledgers_by_hash.del (it, false);
531  }
532 }
533 
534 } // ripple
ripple::Application
Definition: Application.h:85
ripple::TaggedCache::del
bool del(const key_type &key, bool valid)
Definition: TaggedCache.h:251
std::shared_ptr< Collector >
ripple::loadByIndex
std::shared_ptr< Ledger > loadByIndex(std::uint32_t ledgerIndex, Application &app, bool acquire)
Definition: Ledger.cpp:1159
ripple::CachedLedgerAge
constexpr std::chrono::seconds CachedLedgerAge
Definition: LedgerHistory.cpp:35
ripple::leaves
static std::vector< SHAMapItem const * > leaves(SHAMap const &sm)
Definition: LedgerHistory.cpp:301
std::pair::second
T second
ripple::log_metadata_difference
static void log_metadata_difference(ReadView const &builtLedger, ReadView const &validLedger, uint256 const &tx, beast::Journal j)
Definition: LedgerHistory.cpp:167
ripple::LedgerHistory::fixIndex
bool fixIndex(LedgerIndex ledgerIndex, LedgerHash const &ledgerHash)
Repair a hash to index mapping.
Definition: LedgerHistory.cpp:504
std::vector
STL class.
ripple::LedgerHistory::mLedgersByIndex
std::map< LedgerIndex, LedgerHash > mLedgersByIndex
Definition: LedgerHistory.h:150
ripple::LedgerHistory::j_
beast::Journal j_
Definition: LedgerHistory.h:152
std::chrono::seconds
ripple::LedgerHistory::tune
void tune(int size, std::chrono::seconds age)
Set the history cache's parameters.
Definition: LedgerHistory.cpp:518
ripple::TaggedCache::fetch
std::shared_ptr< T > fetch(const key_type &key)
Definition: TaggedCache.h:370
ripple::stopwatch
Stopwatch & stopwatch()
Returns an instance of a wall clock.
Definition: chrono.h:87
ripple::to_string
std::string to_string(ListDisposition disposition)
Definition: ValidatorList.cpp:41
ripple::TaggedCache::canonicalize_replace_cache
bool canonicalize_replace_cache(const key_type &key, std::shared_ptr< T > const &data)
Definition: TaggedCache.h:360
ripple::LedgerHistory::getLedgerHash
LedgerHash getLedgerHash(LedgerIndex ledgerIndex)
Get a ledger's hash given its sequence number.
Definition: LedgerHistory.cpp:73
std::sort
T sort(T... args)
ripple::LedgerHistory::builtLedger
void builtLedger(std::shared_ptr< Ledger const > const &, uint256 const &consensusHash, Json::Value)
Report that we have locally built a particular ledger.
Definition: LedgerHistory.cpp:422
std::unique_lock::unlock
T unlock(T... args)
ripple::uint256
base_uint< 256 > uint256
Definition: base_uint.h:436
std::vector::push_back
T push_back(T... args)
ripple::LedgerHistory::m_consensus_validated
ConsensusValidated m_consensus_validated
Definition: LedgerHistory.h:146
ripple::base_uint< 256 >
ripple::loadByHash
std::shared_ptr< Ledger > loadByHash(uint256 const &ledgerHash, Application &app, bool acquire)
Definition: Ledger.cpp:1176
ripple::LedgerHistory::insert
bool insert(std::shared_ptr< Ledger const > ledger, bool validated)
Track a ledger.
Definition: LedgerHistory.cpp:54
ripple::base_uint::isZero
bool isZero() const
Definition: base_uint.h:429
ripple::LedgerHistory::app_
Application & app_
Definition: LedgerHistory.h:122
ripple::SHAMapItem
Definition: SHAMapItem.h:34
ripple::LedgerHistory::getLedgerByHash
std::shared_ptr< Ledger const > getLedgerByHash(LedgerHash const &ledgerHash)
Retrieve a ledger given its hash.
Definition: LedgerHistory.cpp:118
ripple::JsonOptions::none
@ none
ripple::TaggedCache::getKeys
std::vector< key_type > getKeys() const
Definition: TaggedCache.h:491
ripple::TaggedCache::peekMutex
mutex_type & peekMutex()
Definition: TaggedCache.h:486
std::unique_lock
STL class.
ripple::ReadView::txRead
virtual tx_type txRead(key_type const &key) const =0
Read a transaction from the tx map.
ripple::SHAMap
A SHAMap is both a radix tree with a fan-out of 16 and a Merkle tree.
Definition: SHAMap.h:79
ripple::LedgerHistory::handleMismatch
void handleMismatch(LedgerHash const &built, LedgerHash const &valid, boost::optional< uint256 > const &builtConsensusHash, boost::optional< uint256 > const &validatedConsensusHash, Json::Value const &consensus)
Log details in the case where we build one ledger but validate a different one.
Definition: LedgerHistory.cpp:313
beast::Journal::error
Stream error() const
Definition: Journal.h:307
ripple::LedgerHistory::validatedLedger
void validatedLedger(std::shared_ptr< Ledger const > const &, boost::optional< uint256 > const &consensusHash)
Report that we have validated a particular ledger.
Definition: LedgerHistory.cpp:463
beast::Journal
A generic endpoint for log messages.
Definition: Journal.h:60
std::uint32_t
ripple::LedgerHistory::getLedgerBySeq
std::shared_ptr< Ledger const > getLedgerBySeq(LedgerIndex ledgerIndex)
Get a ledger given its sequence number.
Definition: LedgerHistory.cpp:85
ripple::log_one
static void log_one(ReadView const &ledger, uint256 const &tx, char const *msg, beast::Journal &j)
Definition: LedgerHistory.cpp:144
ripple::getJson
Json::Value getJson(LedgerFill const &fill)
Return a new Json::Value representing the ledger with given options.
Definition: LedgerToJson.cpp:272
ripple::TaggedCache::setTargetAge
void setTargetAge(clock_type::duration s)
Definition: TaggedCache.h:115
ripple::ReadView
A view into a ledger.
Definition: ReadView.h:186
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
ripple::TaggedCache::setTargetSize
void setTargetSize(int s)
Definition: TaggedCache.h:97
ripple::ReadView::seq
LedgerIndex seq() const
Returns the sequence number of the base ledger.
Definition: ReadView.h:258
std::vector::begin
T begin(T... args)
std
STL namespace.
ripple::LogicError
void LogicError(std::string const &how) noexcept
Called when faulty logic causes a broken invariant.
Definition: contract.cpp:50
ripple::LedgerHistory::clearLedgerCachePrior
void clearLedgerCachePrior(LedgerIndex seq)
Definition: LedgerHistory.cpp:524
beast::Journal::debug
Stream debug() const
Definition: Journal.h:292
std::vector::end
T end(T... args)
ripple::TaggedCache::canonicalize_replace_client
bool canonicalize_replace_client(const key_type &key, std::shared_ptr< T > &data)
Definition: TaggedCache.h:365
ripple::LedgerHistory::mismatch_counter_
beast::insight::Counter mismatch_counter_
Definition: LedgerHistory.h:124
ripple::LedgerHistory::LedgerHistory
LedgerHistory(beast::insight::Collector::ptr const &collector, Application &app)
Definition: LedgerHistory.cpp:39
ripple::LedgerHistory::m_ledgers_by_hash
LedgersByHash m_ledgers_by_hash
Definition: LedgerHistory.h:128
Json::Value
Represents a JSON value.
Definition: json_value.h:141