rippled
LedgerCleaner.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/InboundLedgers.h>
21 #include <ripple/app/ledger/LedgerCleaner.h>
22 #include <ripple/app/ledger/LedgerMaster.h>
23 #include <ripple/app/misc/LoadFeeTrack.h>
24 #include <ripple/beast/core/CurrentThreadName.h>
25 #include <ripple/protocol/jss.h>
26 
27 namespace ripple {
28 namespace detail {
29 
30 /*
31 
32 LedgerCleaner
33 
34 Cleans up the ledger. Specifically, resolves these issues:
35 
36 1. Older versions could leave the SQLite account and transaction databases in
37  an inconsistent state. The cleaner identifies these inconsistencies and
38  resolves them.
39 
40 2. Upon request, checks for missing nodes in a ledger and triggers a fetch.
41 
42 */
43 
45 {
48  mutable std::mutex mutex_;
49 
51 
53 
54  enum class State : char { readyToClean = 0, startCleaning, cleaning };
56  bool shouldExit_ = false;
57 
58  // The lowest ledger in the range we're checking.
60 
61  // The highest ledger in the range we're checking
63 
64  // Check all state/transaction nodes
65  bool checkNodes_ = false;
66 
67  // Rewrite SQL databases
68  bool fixTxns_ = false;
69 
70  // Number of errors encountered since last success
71  int failures_ = 0;
72 
73  //--------------------------------------------------------------------------
74 public:
76  Application& app,
77  Stoppable& stoppable,
78  beast::Journal journal)
79  : LedgerCleaner(stoppable), app_(app), j_(journal)
80  {
81  }
82 
83  ~LedgerCleanerImp() override
84  {
85  if (thread_.joinable())
86  LogicError("LedgerCleanerImp::onStop not called.");
87  }
88 
89  //--------------------------------------------------------------------------
90  //
91  // Stoppable
92  //
93  //--------------------------------------------------------------------------
94 
95  void
96  onPrepare() override
97  {
98  }
99 
100  void
101  onStart() override
102  {
104  }
105 
106  void
107  onStop() override
108  {
109  JLOG(j_.info()) << "Stopping";
110  {
111  std::lock_guard lock(mutex_);
112  shouldExit_ = true;
114  }
115  thread_.join();
116  }
117 
118  //--------------------------------------------------------------------------
119  //
120  // PropertyStream
121  //
122  //--------------------------------------------------------------------------
123 
124  void
126  {
127  std::lock_guard lock(mutex_);
128 
129  if (maxRange_ == 0)
130  map["status"] = "idle";
131  else
132  {
133  map["status"] = "running";
134  map["min_ledger"] = minRange_;
135  map["max_ledger"] = maxRange_;
136  map["check_nodes"] = checkNodes_ ? "true" : "false";
137  map["fix_txns"] = fixTxns_ ? "true" : "false";
138  if (failures_ > 0)
139  map["fail_counts"] = failures_;
140  }
141  }
142 
143  //--------------------------------------------------------------------------
144  //
145  // LedgerCleaner
146  //
147  //--------------------------------------------------------------------------
148 
149  void
150  doClean(Json::Value const& params) override
151  {
152  LedgerIndex minRange = 0;
153  LedgerIndex maxRange = 0;
154  app_.getLedgerMaster().getFullValidatedRange(minRange, maxRange);
155 
156  {
157  std::lock_guard lock(mutex_);
158 
159  maxRange_ = maxRange;
160  minRange_ = minRange;
161  checkNodes_ = false;
162  fixTxns_ = false;
163  failures_ = 0;
164 
165  /*
166  JSON Parameters:
167 
168  All parameters are optional. By default the cleaner cleans
169  things it thinks are necessary. This behavior can be modified
170  using the following options supplied via JSON RPC:
171 
172  "ledger"
173  A single unsigned integer representing an individual
174  ledger to clean.
175 
176  "min_ledger", "max_ledger"
177  Unsigned integers representing the starting and ending
178  ledger numbers to clean. If unspecified, clean all ledgers.
179 
180  "full"
181  A boolean. When true, means clean everything possible.
182 
183  "fix_txns"
184  A boolean value indicating whether or not to fix the
185  transactions in the database as well.
186 
187  "check_nodes"
188  A boolean, when set to true means check the nodes.
189 
190  "stop"
191  A boolean, when true informs the cleaner to gracefully
192  stop its current activities if any cleaning is taking place.
193  */
194 
195  // Quick way to fix a single ledger
196  if (params.isMember(jss::ledger))
197  {
198  maxRange_ = params[jss::ledger].asUInt();
199  minRange_ = params[jss::ledger].asUInt();
200  fixTxns_ = true;
201  checkNodes_ = true;
202  }
203 
204  if (params.isMember(jss::max_ledger))
205  maxRange_ = params[jss::max_ledger].asUInt();
206 
207  if (params.isMember(jss::min_ledger))
208  minRange_ = params[jss::min_ledger].asUInt();
209 
210  if (params.isMember(jss::full))
211  fixTxns_ = checkNodes_ = params[jss::full].asBool();
212 
213  if (params.isMember(jss::fix_txns))
214  fixTxns_ = params[jss::fix_txns].asBool();
215 
216  if (params.isMember(jss::check_nodes))
217  checkNodes_ = params[jss::check_nodes].asBool();
218 
219  if (params.isMember(jss::stop) && params[jss::stop].asBool())
220  minRange_ = maxRange_ = 0;
221 
223  {
226  }
227  }
228  }
229 
230  //--------------------------------------------------------------------------
231  //
232  // LedgerCleanerImp
233  //
234  //--------------------------------------------------------------------------
235 private:
236  void
238  {
239  JLOG(j_.debug()) << "Initializing";
240  }
241 
242  void
243  run()
244  {
245  beast::setCurrentThreadName("LedgerCleaner");
246  JLOG(j_.debug()) << "Started";
247 
248  init();
249 
250  while (true)
251  {
252  {
254  wakeup_.wait(lock, [this]() {
255  return (shouldExit_ || state_ == State::startCleaning);
256  });
257  if (shouldExit_)
258  break;
259 
261  }
262  doLedgerCleaner();
263  }
264 
265  stopped();
266  }
267 
268  // VFALCO TODO This should return boost::optional<uint256>
269  LedgerHash
271  {
272  boost::optional<LedgerHash> hash;
273  try
274  {
275  hash = hashOfSeq(*ledger, index, j_);
276  }
277  catch (SHAMapMissingNode const& mn)
278  {
279  JLOG(j_.warn())
280  << "Ledger #" << ledger->info().seq << ": " << mn.what();
282  ledger->info().hash,
283  ledger->info().seq,
285  }
286  return hash ? *hash : beast::zero; // kludge
287  }
288 
296  bool
298  LedgerIndex const& ledgerIndex,
299  LedgerHash const& ledgerHash,
300  bool doNodes,
301  bool doTxns)
302  {
303  auto nodeLedger = app_.getInboundLedgers().acquire(
304  ledgerHash, ledgerIndex, InboundLedger::Reason::GENERIC);
305  if (!nodeLedger)
306  {
307  JLOG(j_.debug()) << "Ledger " << ledgerIndex << " not available";
308  app_.getLedgerMaster().clearLedger(ledgerIndex);
310  ledgerHash, ledgerIndex, InboundLedger::Reason::GENERIC);
311  return false;
312  }
313 
314  auto dbLedger = loadByIndex(ledgerIndex, app_);
315  if (!dbLedger || (dbLedger->info().hash != ledgerHash) ||
316  (dbLedger->info().parentHash != nodeLedger->info().parentHash))
317  {
318  // Ideally we'd also check for more than one ledger with that index
319  JLOG(j_.debug())
320  << "Ledger " << ledgerIndex << " mismatches SQL DB";
321  doTxns = true;
322  }
323 
324  if (!app_.getLedgerMaster().fixIndex(ledgerIndex, ledgerHash))
325  {
326  JLOG(j_.debug())
327  << "ledger " << ledgerIndex << " had wrong entry in history";
328  doTxns = true;
329  }
330 
331  if (doNodes && !nodeLedger->walkLedger(app_.journal("Ledger")))
332  {
333  JLOG(j_.debug()) << "Ledger " << ledgerIndex << " is missing nodes";
334  app_.getLedgerMaster().clearLedger(ledgerIndex);
336  ledgerHash, ledgerIndex, InboundLedger::Reason::GENERIC);
337  return false;
338  }
339 
340  if (doTxns && !pendSaveValidated(app_, nodeLedger, true, false))
341  {
342  JLOG(j_.debug()) << "Failed to save ledger " << ledgerIndex;
343  return false;
344  }
345 
346  return true;
347  }
348 
354  LedgerHash
356  LedgerIndex const& ledgerIndex,
357  std::shared_ptr<ReadView const>& referenceLedger)
358  {
359  LedgerHash ledgerHash;
360 
361  if (!referenceLedger || (referenceLedger->info().seq < ledgerIndex))
362  {
363  referenceLedger = app_.getLedgerMaster().getValidatedLedger();
364  if (!referenceLedger)
365  {
366  JLOG(j_.warn()) << "No validated ledger";
367  return ledgerHash; // Nothing we can do. No validated ledger.
368  }
369  }
370 
371  if (referenceLedger->info().seq >= ledgerIndex)
372  {
373  // See if the hash for the ledger we need is in the reference ledger
374  ledgerHash = getLedgerHash(referenceLedger, ledgerIndex);
375  if (ledgerHash.isZero())
376  {
377  // No. Try to get another ledger that might have the hash we
378  // need: compute the index and hash of a ledger that will have
379  // the hash we need.
380  LedgerIndex refIndex = getCandidateLedger(ledgerIndex);
381  LedgerHash refHash = getLedgerHash(referenceLedger, refIndex);
382 
383  bool const nonzero(refHash.isNonZero());
384  assert(nonzero);
385  if (nonzero)
386  {
387  // We found the hash and sequence of a better reference
388  // ledger.
389  referenceLedger = app_.getInboundLedgers().acquire(
390  refHash, refIndex, InboundLedger::Reason::GENERIC);
391  if (referenceLedger)
392  ledgerHash =
393  getLedgerHash(referenceLedger, ledgerIndex);
394  }
395  }
396  }
397  else
398  JLOG(j_.warn()) << "Validated ledger is prior to target ledger";
399 
400  return ledgerHash;
401  }
402 
404  void
406  {
407  auto shouldExit = [this]() {
408  std::lock_guard lock(mutex_);
409  return shouldExit_;
410  };
411 
413 
414  while (!shouldExit())
415  {
416  LedgerIndex ledgerIndex;
417  LedgerHash ledgerHash;
418  bool doNodes;
419  bool doTxns;
420 
421  while (app_.getFeeTrack().isLoadedLocal())
422  {
423  JLOG(j_.debug()) << "Waiting for load to subside";
425  if (shouldExit())
426  return;
427  }
428 
429  {
430  std::lock_guard lock(mutex_);
431  if ((minRange_ > maxRange_) || (maxRange_ == 0) ||
432  (minRange_ == 0))
433  {
434  minRange_ = maxRange_ = 0;
436  return;
437  }
438  ledgerIndex = maxRange_;
439  doNodes = checkNodes_;
440  doTxns = fixTxns_;
441  }
442 
443  ledgerHash = getHash(ledgerIndex, goodLedger);
444 
445  bool fail = false;
446  if (ledgerHash.isZero())
447  {
448  JLOG(j_.info())
449  << "Unable to get hash for ledger " << ledgerIndex;
450  fail = true;
451  }
452  else if (!doLedger(ledgerIndex, ledgerHash, doNodes, doTxns))
453  {
454  JLOG(j_.info()) << "Failed to process ledger " << ledgerIndex;
455  fail = true;
456  }
457 
458  if (fail)
459  {
460  {
461  std::lock_guard lock(mutex_);
462  ++failures_;
463  }
464  // Wait for acquiring to catch up to us
466  }
467  else
468  {
469  {
470  std::lock_guard lock(mutex_);
471  if (ledgerIndex == minRange_)
472  ++minRange_;
473  if (ledgerIndex == maxRange_)
474  --maxRange_;
475  failures_ = 0;
476  }
477  // Reduce I/O pressure and wait for acquiring to catch up to us
479  }
480  }
481  }
482 };
483 
484 //------------------------------------------------------------------------------
485 
487  : Stoppable("LedgerCleaner", parent)
488  , beast::PropertyStream::Source("ledgercleaner")
489 {
490 }
491 
493 
496 {
497  return std::make_unique<LedgerCleanerImp>(app, parent, journal);
498 }
499 
500 } // namespace detail
501 } // namespace ripple
ripple::Application
Definition: Application.h:94
std::this_thread::sleep_for
T sleep_for(T... args)
ripple::detail::LedgerCleaner::LedgerCleaner
LedgerCleaner(Stoppable &parent)
Definition: LedgerCleaner.cpp:486
ripple::detail::LedgerCleanerImp
Definition: LedgerCleaner.cpp:44
ripple::detail::LedgerCleanerImp::mutex_
std::mutex mutex_
Definition: LedgerCleaner.cpp:48
std::shared_ptr
STL class.
ripple::detail::LedgerCleanerImp::app_
Application & app_
Definition: LedgerCleaner.cpp:46
ripple::loadByIndex
std::shared_ptr< Ledger > loadByIndex(std::uint32_t ledgerIndex, Application &app, bool acquire)
Definition: Ledger.cpp:1102
ripple::detail::LedgerCleanerImp::doClean
void doClean(Json::Value const &params) override
Start a long running task to clean the ledger.
Definition: LedgerCleaner.cpp:150
ripple::detail::LedgerCleanerImp::State
State
Definition: LedgerCleaner.cpp:54
ripple::base_uint::isNonZero
bool isNonZero() const
Definition: base_uint.h:480
ripple::Stoppable::stopped
void stopped()
Called by derived classes to indicate that the stoppable has stopped.
Definition: Stoppable.cpp:72
beast::PropertyStream::Map
Definition: PropertyStream.h:236
ripple::detail::LedgerCleanerImp::j_
const beast::Journal j_
Definition: LedgerCleaner.cpp:47
ripple::InboundLedger::Reason::GENERIC
@ GENERIC
std::chrono::seconds
beast::Journal::warn
Stream warn() const
Definition: Journal.h:327
std::lock_guard
STL class.
ripple::detail::LedgerCleanerImp::State::readyToClean
@ readyToClean
ripple::detail::LedgerCleanerImp::State::startCleaning
@ startCleaning
ripple::detail::LedgerCleanerImp::wakeup_
std::condition_variable wakeup_
Definition: LedgerCleaner.cpp:50
ripple::detail::LedgerCleanerImp::onStop
void onStop() override
Override called when the stop notification is issued.
Definition: LedgerCleaner.cpp:107
ripple::Application::getInboundLedgers
virtual InboundLedgers & getInboundLedgers()=0
ripple::Application::getFeeTrack
virtual LoadFeeTrack & getFeeTrack()=0
ripple::detail::LedgerCleanerImp::onWrite
void onWrite(beast::PropertyStream::Map &map) override
Subclass override.
Definition: LedgerCleaner.cpp:125
ripple::detail::LedgerCleanerImp::shouldExit_
bool shouldExit_
Definition: LedgerCleaner.cpp:56
Json::Value::asBool
bool asBool() const
Definition: json_value.cpp:619
ripple::SHAMapMissingNode
Definition: SHAMapMissingNode.h:55
ripple::detail::LedgerCleanerImp::minRange_
LedgerIndex minRange_
Definition: LedgerCleaner.cpp:59
ripple::base_uint< 256 >
ripple::LoadFeeTrack::isLoadedLocal
bool isLoadedLocal() const
Definition: LoadFeeTrack.h:123
std::thread::joinable
T joinable(T... args)
ripple::Stoppable
Provides an interface for starting and stopping.
Definition: Stoppable.h:200
ripple::detail::make_LedgerCleaner
std::unique_ptr< LedgerCleaner > make_LedgerCleaner(Application &app, Stoppable &parent, beast::Journal journal)
Definition: LedgerCleaner.cpp:495
ripple::base_uint::isZero
bool isZero() const
Definition: base_uint.h:475
std::thread
STL class.
ripple::detail::LedgerCleanerImp::~LedgerCleanerImp
~LedgerCleanerImp() override
Definition: LedgerCleaner.cpp:83
ripple::detail::LedgerCleaner
Check the ledger/transaction databases to make sure they have continuity.
Definition: LedgerCleaner.h:34
ripple::Application::getLedgerMaster
virtual LedgerMaster & getLedgerMaster()=0
ripple::InboundLedgers::acquire
virtual std::shared_ptr< Ledger const > acquire(uint256 const &hash, std::uint32_t seq, InboundLedger::Reason)=0
ripple::detail::LedgerCleanerImp::getHash
LedgerHash getHash(LedgerIndex const &ledgerIndex, std::shared_ptr< ReadView const > &referenceLedger)
Returns the hash of the specified ledger.
Definition: LedgerCleaner.cpp:355
ripple::LedgerMaster::getFullValidatedRange
bool getFullValidatedRange(std::uint32_t &minVal, std::uint32_t &maxVal)
Definition: LedgerMaster.cpp:600
ripple::LedgerMaster::fixIndex
bool fixIndex(LedgerIndex ledgerIndex, LedgerHash const &ledgerHash)
Definition: LedgerMaster.cpp:519
ripple::detail::LedgerCleanerImp::getLedgerHash
LedgerHash getLedgerHash(std::shared_ptr< ReadView const > &ledger, LedgerIndex index)
Definition: LedgerCleaner.cpp:270
ripple::detail::LedgerCleanerImp::failures_
int failures_
Definition: LedgerCleaner.cpp:71
std::unique_lock
STL class.
ripple::LedgerMaster::getValidatedLedger
std::shared_ptr< Ledger const > getValidatedLedger()
Definition: LedgerMaster.h:90
ripple::detail::LedgerCleanerImp::maxRange_
LedgerIndex maxRange_
Definition: LedgerCleaner.cpp:62
beast::Journal::info
Stream info() const
Definition: Journal.h:321
Json::Value::isMember
bool isMember(const char *key) const
Return true if the object has a member named key.
Definition: json_value.cpp:932
beast::Journal
A generic endpoint for log messages.
Definition: Journal.h:58
std::uint32_t
ripple::detail::LedgerCleanerImp::state_
State state_
Definition: LedgerCleaner.cpp:55
std::condition_variable::wait
T wait(T... args)
ripple::detail::LedgerCleaner::~LedgerCleaner
virtual ~LedgerCleaner()=0
Destroy the object.
ripple::detail::LedgerCleanerImp::onStart
void onStart() override
Override called during start.
Definition: LedgerCleaner.cpp:101
ripple::detail::LedgerCleanerImp::checkNodes_
bool checkNodes_
Definition: LedgerCleaner.cpp:65
ripple::detail::LedgerCleanerImp::init
void init()
Definition: LedgerCleaner.cpp:237
std::condition_variable::notify_one
T notify_one(T... args)
ripple::hashOfSeq
boost::optional< uint256 > hashOfSeq(ReadView const &ledger, LedgerIndex seq, beast::Journal journal)
Return the hash of a ledger by sequence.
Definition: View.cpp:573
beast::setCurrentThreadName
void setCurrentThreadName(std::string_view name)
Changes the name of the caller thread.
Definition: CurrentThreadName.cpp:119
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
ripple::Application::journal
virtual beast::Journal journal(std::string const &name)=0
ripple::detail::LedgerCleanerImp::doLedger
bool doLedger(LedgerIndex const &ledgerIndex, LedgerHash const &ledgerHash, bool doNodes, bool doTxns)
Process a single ledger.
Definition: LedgerCleaner.cpp:297
ripple::detail::LedgerCleanerImp::thread_
std::thread thread_
Definition: LedgerCleaner.cpp:52
ripple::detail::LedgerCleanerImp::State::cleaning
@ cleaning
ripple::LedgerMaster::clearLedger
void clearLedger(std::uint32_t seq)
Definition: LedgerMaster.cpp:592
ripple::LogicError
void LogicError(std::string const &how) noexcept
Called when faulty logic causes a broken invariant.
Definition: contract.cpp:48
Json::Value::asUInt
UInt asUInt() const
Definition: json_value.cpp:545
std::condition_variable
std::mutex
STL class.
beast::Journal::debug
Stream debug() const
Definition: Journal.h:315
ripple::detail::LedgerCleanerImp::fixTxns_
bool fixTxns_
Definition: LedgerCleaner.cpp:68
ripple::detail::LedgerCleanerImp::doLedgerCleaner
void doLedgerCleaner()
Run the ledger cleaner.
Definition: LedgerCleaner.cpp:405
ripple::getCandidateLedger
LedgerIndex getCandidateLedger(LedgerIndex requested)
Find a ledger index from which we could easily get the requested ledger.
Definition: View.h:187
ripple::pendSaveValidated
bool pendSaveValidated(Application &app, std::shared_ptr< Ledger const > const &ledger, bool isSynchronous, bool isCurrent)
Save, or arrange to save, a fully-validated ledger Returns false on error.
Definition: Ledger.cpp:936
std::unique_ptr
STL class.
ripple::detail::LedgerCleanerImp::LedgerCleanerImp
LedgerCleanerImp(Application &app, Stoppable &stoppable, beast::Journal journal)
Definition: LedgerCleaner.cpp:75
ripple::detail::LedgerCleanerImp::onPrepare
void onPrepare() override
Override called during preparation.
Definition: LedgerCleaner.cpp:96
std::thread::join
T join(T... args)
std::runtime_error::what
T what(T... args)
Json::Value
Represents a JSON value.
Definition: json_value.h:145
ripple::detail::LedgerCleanerImp::run
void run()
Definition: LedgerCleaner.cpp:243
beast
Definition: base_uint.h:646