rippled
ShardArchiveHandler.cpp
1 //------------------------------------------------------------------------------
2 /*
3  This file is part of rippled: https://github.com/ripple/rippled
4  Copyright (c) 2012-2014 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/misc/NetworkOPs.h>
21 #include <ripple/basics/Archive.h>
22 #include <ripple/basics/BasicConfig.h>
23 #include <ripple/core/ConfigSections.h>
24 #include <ripple/nodestore/DatabaseShard.h>
25 #include <ripple/rpc/ShardArchiveHandler.h>
26 #include <ripple/rpc/impl/Handler.h>
27 
28 #include <memory>
29 
30 namespace ripple {
31 namespace RPC {
32 
33 using namespace boost::filesystem;
34 using namespace std::chrono_literals;
35 
36 boost::filesystem::path
38 {
39  return get(config.section(ConfigSection::shardDatabase()),
40  "download_path",
42  "path",
43  "")) /
44  "download";
45 }
46 
49  Application& app,
50  Stoppable& parent)
51 {
52  return std::make_unique<ShardArchiveHandler>(app, parent);
53 }
54 
57 {
58  auto const downloadDir(getDownloadDirectory(app.config()));
59 
60  // Create the handler iff the database
61  // is present.
62  if (exists(downloadDir / stateDBName) &&
63  is_regular_file(downloadDir / stateDBName))
64  {
65  return std::make_unique<RecoveryHandler>(app, parent);
66  }
67 
68  return nullptr;
69 }
70 
72  : Stoppable("ShardArchiveHandler", parent)
73  , process_(false)
74  , app_(app)
75  , j_(app.journal("ShardArchiveHandler"))
76  , downloadDir_(getDownloadDirectory(app.config()))
77  , timer_(app_.getIOService())
78  , verificationScheduler_(
79  std::chrono::seconds(get<std::uint32_t>(
80  app.config().section(ConfigSection::shardDatabase()),
81  "shard_verification_retry_interval")),
82 
83  get<std::uint32_t>(
84  app.config().section(ConfigSection::shardDatabase()),
85  "shard_verification_max_attempts"))
86 {
87  assert(app_.getShardStore());
88 }
89 
90 bool
92 {
93  std::lock_guard lock(m_);
94 
95  if (process_ || downloader_ != nullptr || sqliteDB_ != nullptr)
96  {
97  JLOG(j_.warn()) << "Archives already being processed";
98  return false;
99  }
100 
101  // Initialize from pre-existing database
102  if (exists(downloadDir_ / stateDBName) &&
103  is_regular_file(downloadDir_ / stateDBName))
104  {
105  downloader_.reset(
107 
108  return initFromDB(lock);
109  }
110 
111  // Fresh initialization
112  else
113  {
114  try
115  {
116  create_directories(downloadDir_);
117 
118  sqliteDB_ = std::make_unique<DatabaseCon>(
119  downloadDir_,
120  stateDBName,
123  }
124  catch (std::exception const& e)
125  {
126  JLOG(j_.error())
127  << "exception: " << e.what() << " in function: " << __func__;
128 
129  return false;
130  }
131  }
132 
133  return true;
134 }
135 
136 bool
138 {
139  try
140  {
141  using namespace boost::filesystem;
142 
143  assert(
144  exists(downloadDir_ / stateDBName) &&
145  is_regular_file(downloadDir_ / stateDBName));
146 
147  sqliteDB_ = std::make_unique<DatabaseCon>(
148  downloadDir_,
149  stateDBName,
152 
153  auto& session{sqliteDB_->getSession()};
154 
155  soci::rowset<soci::row> rs =
156  (session.prepare << "SELECT * FROM State;");
157 
158  for (auto it = rs.begin(); it != rs.end(); ++it)
159  {
160  parsedURL url;
161 
162  if (!parseUrl(url, it->get<std::string>(1)))
163  {
164  JLOG(j_.error())
165  << "Failed to parse url: " << it->get<std::string>(1);
166 
167  continue;
168  }
169 
170  add(it->get<int>(0), std::move(url), lock);
171  }
172 
173  // Failed to load anything
174  // from the state database.
175  if (archives_.empty())
176  {
177  release();
178  return false;
179  }
180  }
181  catch (std::exception const& e)
182  {
183  JLOG(j_.error()) << "exception: " << e.what()
184  << " in function: " << __func__;
185 
186  return false;
187  }
188 
189  return true;
190 }
191 
192 void
194 {
195  {
197 
198  if (downloader_)
199  {
200  downloader_->onStop();
201  downloader_.reset();
202  }
203 
204  timer_.cancel();
205  }
206 
208  "ShardArchiveHandler", std::chrono::milliseconds(2000), j_);
209 
211  "ShardArchiveHandler", std::chrono::milliseconds(2000), j_);
212 
213  stopped();
214 }
215 
216 bool
218  std::uint32_t shardIndex,
220 {
222 
223  if (!add(shardIndex, std::forward<parsedURL>(url.first), lock))
224  return false;
225 
226  auto& session{sqliteDB_->getSession()};
227 
228  session << "INSERT INTO State VALUES (:index, :url);",
229  soci::use(shardIndex), soci::use(url.second);
230 
231  return true;
232 }
233 
234 bool
236  std::uint32_t shardIndex,
237  parsedURL&& url,
239 {
240  if (process_)
241  {
242  JLOG(j_.error()) << "Download and import already in progress";
243  return false;
244  }
245 
246  auto const it{archives_.find(shardIndex)};
247  if (it != archives_.end())
248  return url == it->second;
249 
250  if (!app_.getShardStore()->prepareShard(shardIndex))
251  return false;
252 
253  archives_.emplace(shardIndex, std::move(url));
254 
255  return true;
256 }
257 
258 bool
260 {
261  std::lock_guard lock(m_);
262  if (!app_.getShardStore())
263  {
264  JLOG(j_.error()) << "No shard store available";
265  return false;
266  }
267  if (process_)
268  {
269  JLOG(j_.warn()) << "Archives already being processed";
270  return false;
271  }
272  if (archives_.empty())
273  {
274  JLOG(j_.warn()) << "No archives to process";
275  return false;
276  }
277 
278  try
279  {
280  // Create temp root download directory
281  create_directories(downloadDir_);
282 
283  if (!downloader_)
284  {
285  // will throw if can't initialize ssl context
286  downloader_ = std::make_unique<DatabaseDownloader>(
287  app_.getIOService(), j_, app_.config());
288  }
289  }
290  catch (std::exception const& e)
291  {
292  JLOG(j_.error()) << "exception: " << e.what();
293  return false;
294  }
295 
296  process_ = true;
297  return next(lock);
298 }
299 
300 void
302 {
304  doRelease(lock);
305 }
306 
307 bool
309 {
310  if (archives_.empty())
311  {
312  doRelease(l);
313  return false;
314  }
315 
316  if (isStopping())
317  return false;
318 
319  auto const shardIndex{archives_.begin()->first};
320 
321  // We use the sequence of the last validated ledger
322  // to determine whether or not we have stored a ledger
323  // that comes after the last ledger in this shard. A
324  // later ledger must be present in order to reliably
325  // retrieve the hash of the shard's last ledger.
326  boost::optional<uint256> expectedHash;
327  bool shouldHaveHash = false;
328  if (auto const seq = app_.getShardStore()->lastLedgerSeq(shardIndex);
329  (shouldHaveHash = app_.getLedgerMaster().getValidLedgerIndex() > seq))
330  {
331  expectedHash = app_.getLedgerMaster().walkHashBySeq(seq);
332  }
333 
334  if (!expectedHash)
335  {
336  auto wrapper =
337  timerCounter_.wrap([this](boost::system::error_code const& ec) {
338  if (ec != boost::asio::error::operation_aborted)
339  {
340  std::lock_guard lock(m_);
341  this->next(lock);
342  }
343  });
344 
345  if (!wrapper)
346  return onClosureFailed(
347  "failed to wrap closure for last ledger confirmation timer", l);
348 
349  if (!verificationScheduler_.retry(app_, shouldHaveHash, *wrapper))
350  {
351  JLOG(j_.error()) << "failed to find last ledger hash for shard "
352  << shardIndex << ", maximum attempts reached";
353 
354  return removeAndProceed(l);
355  }
356 
357  return true;
358  }
359 
360  // Create a temp archive directory at the root
361  auto const dstDir{downloadDir_ / std::to_string(shardIndex)};
362  try
363  {
364  create_directory(dstDir);
365  }
366  catch (std::exception const& e)
367  {
368  JLOG(j_.error()) << "exception: " << e.what();
369  return removeAndProceed(l);
370  }
371 
372  // Download the archive. Process in another thread
373  // to prevent holding up the lock if the downloader
374  // sleeps.
375  auto const& url{archives_.begin()->second};
376  auto wrapper = jobCounter_.wrap([this, url, dstDir](Job&) {
377  if (!downloader_->download(
378  url.domain,
379  std::to_string(url.port.get_value_or(443)),
380  url.path,
381  11,
382  dstDir / "archive.tar.lz4",
383  [this](path dstPath) { complete(dstPath); }))
384  {
385  std::lock_guard<std::mutex> l(m_);
386  removeAndProceed(l);
387  }
388  });
389 
390  if (!wrapper)
391  return onClosureFailed(
392  "failed to wrap closure for starting download", l);
393 
394  app_.getJobQueue().addJob(jtCLIENT, "ShardArchiveHandler", *wrapper);
395 
396  return true;
397 }
398 
399 void
401 {
402  if (isStopping())
403  return;
404 
405  {
406  std::lock_guard lock(m_);
407  try
408  {
409  if (!is_regular_file(dstPath))
410  {
411  auto ar{archives_.begin()};
412  JLOG(j_.error())
413  << "Downloading shard id " << ar->first << " from URL "
414  << ar->second.domain << ar->second.path;
415  removeAndProceed(lock);
416  return;
417  }
418  }
419  catch (std::exception const& e)
420  {
421  JLOG(j_.error()) << "exception: " << e.what();
422  removeAndProceed(lock);
423  return;
424  }
425  }
426 
427  // Make lambdas mutable captured vars can be moved from
428  auto wrapper =
429  jobCounter_.wrap([=, dstPath = std::move(dstPath)](Job&) mutable {
430  if (isStopping())
431  return;
432 
433  // If not synced then defer and retry
434  auto const mode{app_.getOPs().getOperatingMode()};
435  if (mode != OperatingMode::FULL)
436  {
437  std::lock_guard lock(m_);
438  timer_.expires_from_now(static_cast<std::chrono::seconds>(
439  (static_cast<std::size_t>(OperatingMode::FULL) -
440  static_cast<std::size_t>(mode)) *
441  10));
442 
443  auto wrapper = timerCounter_.wrap(
444  [=, dstPath = std::move(dstPath)](
445  boost::system::error_code const& ec) mutable {
446  if (ec != boost::asio::error::operation_aborted)
447  complete(std::move(dstPath));
448  });
449 
450  if (!wrapper)
452  "failed to wrap closure for operating mode timer",
453  lock);
454  else
455  timer_.async_wait(*wrapper);
456  }
457  else
458  {
459  process(dstPath);
460  std::lock_guard lock(m_);
461  removeAndProceed(lock);
462  }
463  });
464 
465  if (!wrapper)
466  {
467  if (isStopping())
468  return;
469 
470  JLOG(j_.error()) << "failed to wrap closure for process()";
471 
472  std::lock_guard lock(m_);
473  removeAndProceed(lock);
474  }
475 
476  // Process in another thread to not hold up the IO service
477  app_.getJobQueue().addJob(jtCLIENT, "ShardArchiveHandler", *wrapper);
478 }
479 
480 void
481 ShardArchiveHandler::process(path const& dstPath)
482 {
483  std::uint32_t shardIndex;
484  {
485  std::lock_guard lock(m_);
486  shardIndex = archives_.begin()->first;
487  }
488 
489  auto const shardDir{dstPath.parent_path() / std::to_string(shardIndex)};
490  try
491  {
492  // Extract the downloaded archive
493  extractTarLz4(dstPath, dstPath.parent_path());
494 
495  // The extracted root directory name must match the shard index
496  if (!is_directory(shardDir))
497  {
498  JLOG(j_.error()) << "Shard " << shardIndex
499  << " mismatches archive shard directory";
500  return;
501  }
502  }
503  catch (std::exception const& e)
504  {
505  JLOG(j_.error()) << "exception: " << e.what();
506  return;
507  }
508 
509  // Import the shard into the shard store
510  if (!app_.getShardStore()->importShard(shardIndex, shardDir))
511  {
512  JLOG(j_.error()) << "Importing shard " << shardIndex;
513  return;
514  }
515 
516  JLOG(j_.debug()) << "Shard " << shardIndex << " downloaded and imported";
517 }
518 
519 void
521 {
523 
524  auto const shardIndex{archives_.begin()->first};
525  app_.getShardStore()->removePreShard(shardIndex);
526  archives_.erase(shardIndex);
527 
528  auto& session{sqliteDB_->getSession()};
529 
530  session << "DELETE FROM State WHERE ShardIndex = :index;",
531  soci::use(shardIndex);
532 
533  auto const dstDir{downloadDir_ / std::to_string(shardIndex)};
534  try
535  {
536  remove_all(dstDir);
537  }
538  catch (std::exception const& e)
539  {
540  JLOG(j_.error()) << "exception: " << e.what();
541  }
542 }
543 
544 void
546 {
547  timer_.cancel();
548  for (auto const& ar : archives_)
549  app_.getShardStore()->removePreShard(ar.first);
550  archives_.clear();
551 
552  {
553  auto& session{sqliteDB_->getSession()};
554 
555  session << "DROP TABLE State;";
556  }
557 
558  sqliteDB_.reset();
559 
560  // Remove temp root download directory
561  try
562  {
563  remove_all(downloadDir_);
564  }
565  catch (std::exception const& e)
566  {
567  JLOG(j_.error()) << "exception: " << e.what()
568  << " in function: " << __func__;
569  }
570 
571  downloader_.reset();
572  process_ = false;
573 }
574 
575 bool
577  std::string const& errorMsg,
578  std::lock_guard<std::mutex> const& lock)
579 {
580  if (isStopping())
581  return false;
582 
583  JLOG(j_.error()) << errorMsg;
584 
585  return removeAndProceed(lock);
586 }
587 
588 bool
590 {
591  remove(lock);
592  return next(lock);
593 }
594 
596  : ShardArchiveHandler(app, parent)
597 {
598 }
599 
600 } // namespace RPC
601 } // namespace ripple
ripple::Application
Definition: Application.h:97
ripple::RPC::ShardArchiveHandler::getDownloadDirectory
static boost::filesystem::path getDownloadDirectory(Config const &config)
Definition: ShardArchiveHandler.cpp:37
ripple::RPC::ShardArchiveHandler::downloadDir_
const boost::filesystem::path downloadDir_
Definition: ShardArchiveHandler.h:141
ripple::RPC::ShardArchiveHandler::remove
void remove(std::lock_guard< std::mutex > const &)
Definition: ShardArchiveHandler.cpp:520
ripple::RPC::ShardArchiveHandler::timerCounter_
TimerOpCounter timerCounter_
Definition: ShardArchiveHandler.h:144
ripple::RPC::ShardArchiveHandler::verificationScheduler_
ShardVerificationScheduler verificationScheduler_
Definition: ShardArchiveHandler.h:145
std::string
STL class.
ripple::RPC::ShardArchiveHandler::removeAndProceed
bool removeAndProceed(std::lock_guard< std::mutex > const &lock)
Definition: ShardArchiveHandler.cpp:589
ripple::RPC::ShardArchiveHandler::start
bool start()
Starts downloading and importing archives.
Definition: ShardArchiveHandler.cpp:259
ripple::jtCLIENT
@ jtCLIENT
Definition: Job.h:48
std::exception
STL class.
ripple::RPC::ShardArchiveHandler::add
bool add(std::uint32_t shardIndex, std::pair< parsedURL, std::string > &&url)
Definition: ShardArchiveHandler.cpp:217
ripple::Stoppable::stopped
void stopped()
Called by derived classes to indicate that the stoppable has stopped.
Definition: Stoppable.cpp:72
ripple::parsedURL
Definition: StringUtilities.h:123
std::pair
ripple::RPC::ShardVerificationScheduler::reset
void reset()
Definition: ShardVerificationScheduler.cpp:62
ripple::LedgerMaster::getValidLedgerIndex
LedgerIndex getValidLedgerIndex()
Definition: LedgerMaster.cpp:209
ripple::ConfigSection::shardDatabase
static std::string shardDatabase()
Definition: ConfigSections.h:38
ripple::RPC::ShardArchiveHandler::onStop
void onStop() override
Override called when the stop notification is issued.
Definition: ShardArchiveHandler.cpp:193
std::chrono::milliseconds
beast::Journal::warn
Stream warn() const
Definition: Journal.h:327
ripple::NodeStore::DatabaseShard::lastLedgerSeq
virtual std::uint32_t lastLedgerSeq(std::uint32_t shardIndex) const =0
Calculates the last ledger sequence for a given shard index.
ripple::RPC::ShardArchiveHandler::doRelease
void doRelease(std::lock_guard< std::mutex > const &)
Definition: ShardArchiveHandler.cpp:545
std::lock_guard
STL class.
ripple::Application::getShardStore
virtual NodeStore::DatabaseShard * getShardStore()=0
ripple::ShardArchiveHandlerDBInit
static constexpr std::array< char const *, 3 > ShardArchiveHandlerDBInit
Definition: DBInit.h:178
ripple::JobQueue::addJob
bool addJob(JobType type, std::string const &name, JobHandler &&jobHandler)
Adds a job to the JobQueue.
Definition: JobQueue.h:166
ripple::RPC::ShardArchiveHandler::initFromDB
bool initFromDB(std::lock_guard< std::mutex > const &)
Definition: ShardArchiveHandler.cpp:137
ripple::RPC::ShardArchiveHandler::onClosureFailed
bool onClosureFailed(std::string const &errorMsg, std::lock_guard< std::mutex > const &lock)
Definition: ShardArchiveHandler.cpp:576
ripple::extractTarLz4
void extractTarLz4(boost::filesystem::path const &src, boost::filesystem::path const &dst)
Extract a tar archive compressed with lz4.
Definition: Archive.cpp:29
ripple::Application::getOPs
virtual NetworkOPs & getOPs()=0
ripple::DownloaderDBPragma
static constexpr std::array< char const *, 2 > DownloaderDBPragma
Definition: DBInit.h:175
ripple::RPC::ShardArchiveHandler::timer_
boost::asio::basic_waitable_timer< std::chrono::steady_clock > timer_
Definition: ShardArchiveHandler.h:142
ripple::Stoppable
Provides an interface for starting and stopping.
Definition: Stoppable.h:200
ripple::RPC::ShardArchiveHandler::release
void release()
Definition: ShardArchiveHandler.cpp:301
ripple::RPC::ShardArchiveHandler::process
void process(boost::filesystem::path const &dstPath)
Definition: ShardArchiveHandler.cpp:481
ripple::Application::getLedgerMaster
virtual LedgerMaster & getLedgerMaster()=0
ripple::Config
Definition: Config.h:66
ripple::Application::config
virtual Config & config()=0
std::to_string
T to_string(T... args)
ripple::Application::getJobQueue
virtual JobQueue & getJobQueue()=0
ripple::parseUrl
bool parseUrl(parsedURL &pUrl, std::string const &strUrl)
Definition: StringUtilities.cpp:55
beast::Journal::error
Stream error() const
Definition: Journal.h:333
ripple::RPC::ShardArchiveHandler::sqliteDB_
std::unique_ptr< DatabaseCon > sqliteDB_
Definition: ShardArchiveHandler.h:136
ripple::Job
Definition: Job.h:82
ripple::ConfigSection
Definition: ConfigSections.h:28
ripple::RPC::ShardArchiveHandler::complete
void complete(boost::filesystem::path dstPath)
Definition: ShardArchiveHandler.cpp:400
std::uint32_t
ripple::RPC::ShardVerificationScheduler::retry
bool retry(Application &app, bool shouldHaveHash, retryFunction f)
Definition: ShardVerificationScheduler.cpp:37
ripple::RPC::ShardArchiveHandler::downloader_
std::unique_ptr< DatabaseDownloader > downloader_
Definition: ShardArchiveHandler.h:133
ripple::ClosureCounter::wrap
boost::optional< Wrapper< Closure > > wrap(Closure &&closure)
Wrap the passed closure with a reference counter.
Definition: ClosureCounter.h:178
ripple::RPC::ShardArchiveHandler::makeShardArchiveHandler
static std::unique_ptr< ShardArchiveHandler > makeShardArchiveHandler(Application &app, Stoppable &parent)
Definition: ShardArchiveHandler.cpp:48
ripple::RPC::ShardArchiveHandler::j_
const beast::Journal j_
Definition: ShardArchiveHandler.h:140
memory
ripple::RPC::RecoveryHandler::RecoveryHandler
RecoveryHandler(Application &app, Stoppable &parent)
Definition: ShardArchiveHandler.cpp:595
ripple::Application::getIOService
virtual boost::asio::io_service & getIOService()=0
ripple::RPC::ShardArchiveHandler::tryMakeRecoveryHandler
static std::unique_ptr< ShardArchiveHandler > tryMakeRecoveryHandler(Application &app, Stoppable &parent)
Definition: ShardArchiveHandler.cpp:56
ripple::stateDBName
static constexpr auto stateDBName
Definition: DBInit.h:173
ripple::NodeStore::DatabaseShard::importShard
virtual bool importShard(std::uint32_t shardIndex, boost::filesystem::path const &srcDir)=0
Import a shard into the shard database.
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
ripple::RPC::ShardArchiveHandler::next
bool next(std::lock_guard< std::mutex > const &l)
Definition: ShardArchiveHandler.cpp:308
ripple::RPC::ShardArchiveHandler::archives_
std::map< std::uint32_t, parsedURL > archives_
Definition: ShardArchiveHandler.h:134
std
STL namespace.
ripple::NodeStore::DatabaseShard::prepareShard
virtual bool prepareShard(std::uint32_t shardIndex)=0
Prepare a shard index to be imported into the database.
ripple::RPC::ShardArchiveHandler::app_
Application & app_
Definition: ShardArchiveHandler.h:139
ripple::RPC::ShardArchiveHandler
Handles the download and import of one or more shard archives.
Definition: ShardArchiveHandler.h:39
ripple::RPC::ShardArchiveHandler::ShardArchiveHandler
ShardArchiveHandler()=delete
ripple::ClosureCounter::join
void join(char const *name, std::chrono::milliseconds wait, beast::Journal j)
Returns once all counted in-flight closures are destroyed.
Definition: ClosureCounter.h:152
beast::Journal::debug
Stream debug() const
Definition: Journal.h:315
std::size_t
ripple::RPC::ShardArchiveHandler::m_
std::mutex m_
Definition: ShardArchiveHandler.h:132
ripple::DatabaseDownloader
Definition: DatabaseDownloader.h:28
ripple::RPC::ShardArchiveHandler::jobCounter_
JobCounter jobCounter_
Definition: ShardArchiveHandler.h:143
ripple::NodeStore::DatabaseShard::removePreShard
virtual void removePreShard(std::uint32_t shardIndex)=0
Remove a previously prepared shard index for import.
std::unique_ptr
STL class.
ripple::RPC::ShardArchiveHandler::process_
bool process_
Definition: ShardArchiveHandler.h:135
ripple::RPC::ShardArchiveHandler::init
bool init()
Definition: ShardArchiveHandler.cpp:91
ripple::LedgerMaster::walkHashBySeq
boost::optional< LedgerHash > walkHashBySeq(std::uint32_t index)
Walk to a ledger's hash using the skip list.
Definition: LedgerMaster.cpp:1550
std::exception::what
T what(T... args)
ripple::get
T & get(EitherAmount &amt)
Definition: AmountSpec.h:116
ripple::BasicConfig::section
Section & section(std::string const &name)
Returns the section with the given name.
Definition: BasicConfig.cpp:138
ripple::Stoppable::isStopping
bool isStopping() const
Returns true if the stoppable should stop.
Definition: Stoppable.cpp:54
ripple::OperatingMode::FULL
@ FULL
we have the ledger and can even validate