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