rippled
Shard.cpp
1 //------------------------------------------------------------------------------
2 /*
3  This file is part of rippled: https://github.com/ripple/rippled
4  Copyright (c) 2012, 2017 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/InboundLedger.h>
21 #include <ripple/app/main/DBInit.h>
22 #include <ripple/basics/StringUtilities.h>
23 #include <ripple/core/ConfigSections.h>
24 #include <ripple/nodestore/Manager.h>
25 #include <ripple/nodestore/impl/DatabaseShardImp.h>
26 #include <ripple/nodestore/impl/Shard.h>
27 #include <ripple/protocol/digest.h>
28 
29 #include <boost/algorithm/string.hpp>
30 #include <boost/range/adaptor/transformed.hpp>
31 
32 namespace ripple {
33 namespace NodeStore {
34 
35 uint256 const Shard::finalKey{0};
36 
38  Application& app,
39  DatabaseShard const& db,
40  std::uint32_t index,
42  : app_(app)
43  , index_(index)
44  , firstSeq_(db.firstLedgerSeq(index))
45  , lastSeq_(std::max(firstSeq_, db.lastLedgerSeq(index)))
46  , maxLedgers_(
47  index == db.earliestShardIndex() ? lastSeq_ - firstSeq_ + 1
48  : db.ledgersPerShard())
49  , dir_(db.getRootDir() / std::to_string(index_))
50  , j_(j)
51 {
52  if (index_ < db.earliestShardIndex())
53  Throw<std::runtime_error>("Shard: Invalid index");
54 }
55 
57 {
58  if (removeOnDestroy_)
59  {
60  backend_.reset();
61  lgrSQLiteDB_.reset();
62  txSQLiteDB_.reset();
63  acquireInfo_.reset();
64 
65  try
66  {
67  boost::filesystem::remove_all(dir_);
68  }
69  catch (std::exception const& e)
70  {
71  JLOG(j_.error()) << "shard " << index_ << " exception " << e.what()
72  << " in function " << __func__;
73  }
74  }
75 }
76 
77 bool
78 Shard::open(Scheduler& scheduler, nudb::context& ctx)
79 {
80  std::lock_guard lock{mutex_};
81  assert(!backend_);
82 
83  Config const& config{app_.config()};
84  {
85  Section section{config.section(ConfigSection::shardDatabase())};
86  std::string const type{get<std::string>(section, "type", "nudb")};
87  auto factory{Manager::instance().find(type)};
88  if (!factory)
89  {
90  JLOG(j_.error()) << "shard " << index_
91  << " failed to create backend type " << type;
92  return false;
93  }
94 
95  section.set("path", dir_.string());
96  backend_ = factory->createInstance(
97  NodeObject::keyBytes, section, scheduler, ctx, j_);
98  }
99 
100  using namespace boost::filesystem;
101  auto preexist{false};
102  auto fail = [this, &preexist](std::string const& msg) {
103  pCache_.reset();
104  nCache_.reset();
105  backend_.reset();
106  lgrSQLiteDB_.reset();
107  txSQLiteDB_.reset();
108  acquireInfo_.reset();
109 
110  if (!preexist)
111  remove_all(dir_);
112 
113  if (!msg.empty())
114  {
115  JLOG(j_.fatal()) << "shard " << index_ << " " << msg;
116  }
117  return false;
118  };
119 
120  auto createAcquireInfo = [this, &config]() {
121  acquireInfo_ = std::make_unique<AcquireInfo>();
122 
123  DatabaseCon::Setup setup;
124  setup.startUp = config.START_UP;
125  setup.standAlone = config.standalone();
126  setup.dataDir = dir_;
127  setup.useGlobalPragma = true;
128 
129  acquireInfo_->SQLiteDB = std::make_unique<DatabaseCon>(
130  setup,
135  };
136 
137  try
138  {
139  // Open or create the NuDB key/value store
140  preexist = exists(dir_);
141  backend_->open(!preexist);
142 
143  if (!preexist)
144  {
145  // A new shard
146  createAcquireInfo();
147  acquireInfo_->SQLiteDB->getSession()
148  << "INSERT INTO Shard (ShardIndex) "
149  "VALUES (:shardIndex);",
150  soci::use(index_);
151  }
152  else if (exists(dir_ / AcquireShardDBName))
153  {
154  // An incomplete shard, being acquired
155  createAcquireInfo();
156 
157  auto& session{acquireInfo_->SQLiteDB->getSession()};
158  boost::optional<std::uint32_t> index;
159  soci::blob sociBlob(session);
160  soci::indicator blobPresent;
161 
162  session << "SELECT ShardIndex, StoredLedgerSeqs "
163  "FROM Shard "
164  "WHERE ShardIndex = :index;",
165  soci::into(index), soci::into(sociBlob, blobPresent),
166  soci::use(index_);
167 
168  if (!index || index != index_)
169  return fail("invalid acquire SQLite database");
170 
171  if (blobPresent == soci::i_ok)
172  {
173  std::string s;
174  auto& storedSeqs{acquireInfo_->storedSeqs};
175  if (convert(sociBlob, s); !from_string(storedSeqs, s))
176  return fail("invalid StoredLedgerSeqs");
177 
178  if (boost::icl::first(storedSeqs) < firstSeq_ ||
179  boost::icl::last(storedSeqs) > lastSeq_)
180  {
181  return fail("invalid StoredLedgerSeqs");
182  }
183 
184  if (boost::icl::length(storedSeqs) == maxLedgers_)
185  // All ledgers have been acquired, shard backend is complete
186  backendComplete_ = true;
187  }
188  }
189  else
190  {
191  // A finalized shard or has all ledgers stored in the backend
193  if (backend_->fetch(finalKey.data(), &nObj) != Status::ok)
194  {
195  legacy_ = true;
196  return fail("incompatible, missing backend final key");
197  }
198 
199  // Check final key's value
200  SerialIter sIt(nObj->getData().data(), nObj->getData().size());
201  if (sIt.get32() != version)
202  return fail("invalid version");
203 
204  if (sIt.get32() != firstSeq_ || sIt.get32() != lastSeq_)
205  return fail("out of range ledger sequences");
206 
207  if (sIt.get256().isZero())
208  return fail("invalid last ledger hash");
209 
210  if (exists(dir_ / LgrDBName) && exists(dir_ / TxDBName))
211  final_ = true;
212 
213  backendComplete_ = true;
214  }
215  }
216  catch (std::exception const& e)
217  {
218  return fail(
219  std::string("exception ") + e.what() + " in function " + __func__);
220  }
221 
222  setBackendCache(lock);
223  if (!initSQLite(lock))
224  return fail({});
225 
226  setFileStats(lock);
227  return true;
228 }
229 
230 boost::optional<std::uint32_t>
232 {
233  std::lock_guard lock(mutex_);
234  assert(backend_);
235 
236  if (backendComplete_)
237  {
238  JLOG(j_.warn()) << "shard " << index_
239  << " prepare called when shard backend is complete";
240  return {};
241  }
242 
243  assert(acquireInfo_);
244  auto const& storedSeqs{acquireInfo_->storedSeqs};
245  if (storedSeqs.empty())
246  return lastSeq_;
247  return prevMissing(storedSeqs, 1 + lastSeq_, firstSeq_);
248 }
249 
250 bool
252 {
253  auto const seq{ledger->info().seq};
254  if (seq < firstSeq_ || seq > lastSeq_)
255  {
256  JLOG(j_.error()) << "shard " << index_ << " invalid ledger sequence "
257  << seq;
258  return false;
259  }
260 
261  std::lock_guard lock(mutex_);
262  assert(backend_);
263 
264  if (backendComplete_)
265  {
266  JLOG(j_.debug()) << "shard " << index_ << " ledger sequence " << seq
267  << " already stored";
268  return true;
269  }
270 
271  assert(acquireInfo_);
272  auto& storedSeqs{acquireInfo_->storedSeqs};
273  if (boost::icl::contains(storedSeqs, seq))
274  {
275  JLOG(j_.debug()) << "shard " << index_ << " ledger sequence " << seq
276  << " already stored";
277  return true;
278  }
279  // storeSQLite looks at storedSeqs so insert before the call
280  storedSeqs.insert(seq);
281 
282  if (!storeSQLite(ledger, lock))
283  return false;
284 
285  if (boost::icl::length(storedSeqs) >= maxLedgers_)
286  {
287  if (!initSQLite(lock))
288  return false;
289 
290  backendComplete_ = true;
291  setBackendCache(lock);
292  }
293 
294  JLOG(j_.debug()) << "shard " << index_ << " stored ledger sequence " << seq
295  << (backendComplete_ ? " . All ledgers stored" : "");
296 
297  setFileStats(lock);
298  return true;
299 }
300 
301 bool
303 {
304  if (seq < firstSeq_ || seq > lastSeq_)
305  return false;
306 
307  std::lock_guard lock(mutex_);
308  if (backendComplete_)
309  return true;
310 
311  assert(acquireInfo_);
312  return boost::icl::contains(acquireInfo_->storedSeqs, seq);
313 }
314 
315 void
317 {
318  std::lock_guard lock(mutex_);
319  assert(pCache_ && nCache_);
320 
321  pCache_->sweep();
322  nCache_->sweep();
323 }
324 
325 std::tuple<
330 {
331  std::lock_guard lock(mutex_);
332  assert(backend_);
333 
334  return {backend_, pCache_, nCache_};
335 }
336 
339 {
340  std::lock_guard lock(mutex_);
341  assert(backend_);
342 
343  return backend_;
344 }
345 
346 bool
348 {
349  std::lock_guard lock(mutex_);
350  return backendComplete_;
351 }
352 
355 {
356  std::lock_guard lock(mutex_);
357  assert(pCache_);
358 
359  return pCache_;
360 }
361 
364 {
365  std::lock_guard lock(mutex_);
366  assert(nCache_);
367 
368  return nCache_;
369 }
370 
373 {
374  std::lock_guard lock(mutex_);
375  return {fileSz_, fdRequired_};
376 }
377 
378 bool
380 {
381  std::lock_guard lock(mutex_);
382  return final_;
383 }
384 
385 bool
387 {
388  std::lock_guard lock(mutex_);
389  return legacy_;
390 }
391 
392 bool
394  bool const writeSQLite,
395  boost::optional<uint256> const& expectedHash)
396 {
397  assert(backend_);
398 
399  if (stop_)
400  return false;
401 
402  uint256 hash{0};
403  std::uint32_t seq{0};
404  auto fail =
405  [j = j_, index = index_, &hash, &seq](std::string const& msg) {
406  JLOG(j.fatal())
407  << "shard " << index << ". " << msg
408  << (hash.isZero() ? "" : ". Ledger hash " + to_string(hash))
409  << (seq == 0 ? "" : ". Ledger sequence " + std::to_string(seq));
410  return false;
411  };
412 
413  try
414  {
415  std::unique_lock lock(mutex_);
416  if (!backendComplete_)
417  return fail("backend incomplete");
418 
419  /*
420  TODO MP
421  A lock is required when calling the NuDB verify function. Because
422  this can be a time consuming process, the server may desync.
423  Until this function is modified to work on an open database, we
424  are unable to use it from rippled.
425 
426  // Verify backend integrity
427  backend_->verify();
428  */
429 
430  // Check if a final key has been stored
431  lock.unlock();
433  backend_->fetch(finalKey.data(), &nObj) == Status::ok)
434  {
435  // Check final key's value
436  SerialIter sIt(nObj->getData().data(), nObj->getData().size());
437  if (sIt.get32() != version)
438  return fail("invalid version");
439 
440  if (sIt.get32() != firstSeq_ || sIt.get32() != lastSeq_)
441  return fail("out of range ledger sequences");
442 
443  if (hash = sIt.get256(); hash.isZero())
444  return fail("invalid last ledger hash");
445  }
446  else
447  {
448  // In the absence of a final key, an acquire SQLite database
449  // must be present in order to validate the shard
450  lock.lock();
451  if (!acquireInfo_)
452  return fail("missing acquire SQLite database");
453 
454  auto& session{acquireInfo_->SQLiteDB->getSession()};
455  boost::optional<std::uint32_t> index;
456  boost::optional<std::string> sHash;
457  soci::blob sociBlob(session);
458  soci::indicator blobPresent;
459  session << "SELECT ShardIndex, LastLedgerHash, StoredLedgerSeqs "
460  "FROM Shard "
461  "WHERE ShardIndex = :index;",
462  soci::into(index), soci::into(sHash),
463  soci::into(sociBlob, blobPresent), soci::use(index_);
464 
465  lock.unlock();
466  if (!index || index != index_)
467  return fail("missing or invalid ShardIndex");
468 
469  if (!sHash)
470  return fail("missing LastLedgerHash");
471 
472  if (hash.SetHexExact(*sHash); hash.isZero())
473  return fail("invalid LastLedgerHash");
474 
475  if (blobPresent != soci::i_ok)
476  return fail("missing StoredLedgerSeqs");
477 
478  std::string s;
479  convert(sociBlob, s);
480 
481  lock.lock();
482 
483  auto& storedSeqs{acquireInfo_->storedSeqs};
484  if (!from_string(storedSeqs, s) ||
485  boost::icl::first(storedSeqs) != firstSeq_ ||
486  boost::icl::last(storedSeqs) != lastSeq_ ||
487  storedSeqs.size() != maxLedgers_)
488  {
489  return fail("invalid StoredLedgerSeqs");
490  }
491  }
492  }
493  catch (std::exception const& e)
494  {
495  return fail(
496  std::string("exception ") + e.what() + " in function " + __func__);
497  }
498 
499  // Validate the last ledger hash of a downloaded shard
500  // using a ledger hash obtained from the peer network
501  if (expectedHash && *expectedHash != hash)
502  return fail("invalid last ledger hash");
503 
504  // Validate every ledger stored in the backend
507  auto const lastLedgerHash{hash};
508 
509  // Start with the last ledger in the shard and walk backwards from
510  // child to parent until we reach the first ledger
511  seq = lastSeq_;
512  while (seq >= firstSeq_)
513  {
514  if (stop_)
515  return false;
516 
517  auto nObj = valFetch(hash);
518  if (!nObj)
519  return fail("invalid ledger");
520 
521  ledger = std::make_shared<Ledger>(
522  deserializePrefixedHeader(makeSlice(nObj->getData())),
523  app_.config(),
524  *app_.getShardFamily());
525  if (ledger->info().seq != seq)
526  return fail("invalid ledger sequence");
527  if (ledger->info().hash != hash)
528  return fail("invalid ledger hash");
529 
530  ledger->stateMap().setLedgerSeq(seq);
531  ledger->txMap().setLedgerSeq(seq);
532  ledger->setImmutable(app_.config());
533  if (!ledger->stateMap().fetchRoot(
534  SHAMapHash{ledger->info().accountHash}, nullptr))
535  {
536  return fail("missing root STATE node");
537  }
538  if (ledger->info().txHash.isNonZero() &&
539  !ledger->txMap().fetchRoot(
540  SHAMapHash{ledger->info().txHash}, nullptr))
541  {
542  return fail("missing root TXN node");
543  }
544 
545  if (!valLedger(ledger, next))
546  return fail("failed to validate ledger");
547 
548  if (writeSQLite)
549  {
550  std::lock_guard lock(mutex_);
551  if (!storeSQLite(ledger, lock))
552  return fail("failed storing to SQLite databases");
553  }
554 
555  hash = ledger->info().parentHash;
556  next = std::move(ledger);
557  --seq;
558  }
559 
560  JLOG(j_.debug()) << "shard " << index_ << " is valid";
561 
562  /*
563  TODO MP
564  SQLite VACUUM blocks all database access while processing.
565  Depending on the file size, that can take a while. Until we find
566  a non-blocking way of doing this, we cannot enable vacuum as
567  it can desync a server.
568 
569  try
570  {
571  // VACUUM the SQLite databases
572  auto const tmpDir {dir_ / "tmp_vacuum"};
573  create_directory(tmpDir);
574 
575  auto vacuum = [&tmpDir](std::unique_ptr<DatabaseCon>& sqliteDB)
576  {
577  auto& session {sqliteDB->getSession()};
578  session << "PRAGMA synchronous=OFF;";
579  session << "PRAGMA journal_mode=OFF;";
580  session << "PRAGMA temp_store_directory='" <<
581  tmpDir.string() << "';";
582  session << "VACUUM;";
583  };
584  vacuum(lgrSQLiteDB_);
585  vacuum(txSQLiteDB_);
586  remove_all(tmpDir);
587  }
588  catch (std::exception const& e)
589  {
590  return fail(std::string("exception ") +
591  e.what() + " in function " + __func__);
592  }
593  */
594 
595  // Store final key's value, may already be stored
596  Serializer s;
597  s.add32(version);
598  s.add32(firstSeq_);
599  s.add32(lastSeq_);
600  s.addBitString(lastLedgerHash);
601  auto nObj{
603  try
604  {
605  backend_->store(nObj);
606 
607  std::lock_guard lock(mutex_);
608  final_ = true;
609 
610  // Remove the acquire SQLite database if present
611  if (acquireInfo_)
612  acquireInfo_.reset();
613  remove_all(dir_ / AcquireShardDBName);
614 
615  if (!initSQLite(lock))
616  return fail("failed to initialize SQLite databases");
617 
618  setFileStats(lock);
619  }
620  catch (std::exception const& e)
621  {
622  return fail(
623  std::string("exception ") + e.what() + " in function " + __func__);
624  }
625 
626  return true;
627 }
628 
629 void
631 {
632  // Complete shards use the smallest cache and
633  // fastest expiration to reduce memory consumption.
634  // An incomplete shard is set according to configuration.
635 
636  Config const& config{app_.config()};
637  if (!pCache_)
638  {
639  auto const name{"shard " + std::to_string(index_)};
640  auto const sz{config.getValueFor(
642  backendComplete_ ? boost::optional<std::size_t>(0) : boost::none)};
643  auto const age{std::chrono::seconds{config.getValueFor(
645  backendComplete_ ? boost::optional<std::size_t>(0) : boost::none)}};
646 
647  pCache_ = std::make_shared<PCache>(name, sz, age, stopwatch(), j_);
648  nCache_ = std::make_shared<NCache>(name, stopwatch(), sz, age);
649  }
650  else
651  {
652  auto const sz{config.getValueFor(SizedItem::nodeCacheSize, 0)};
653  pCache_->setTargetSize(sz);
654  nCache_->setTargetSize(sz);
655 
656  auto const age{std::chrono::seconds{
657  config.getValueFor(SizedItem::nodeCacheAge, 0)}};
658  pCache_->setTargetAge(age);
659  nCache_->setTargetAge(age);
660  }
661 }
662 
663 bool
665 {
666  Config const& config{app_.config()};
667  DatabaseCon::Setup const setup = [&]() {
668  DatabaseCon::Setup result;
669  result.startUp = config.START_UP;
670  result.standAlone = config.standalone();
671  result.dataDir = dir_;
673  return result;
674  }();
675 
676  try
677  {
678  if (lgrSQLiteDB_)
679  lgrSQLiteDB_.reset();
680 
681  if (txSQLiteDB_)
682  txSQLiteDB_.reset();
683 
684  if (backendComplete_)
685  {
686  lgrSQLiteDB_ = std::make_unique<DatabaseCon>(
688  lgrSQLiteDB_->getSession() << boost::str(
689  boost::format("PRAGMA cache_size=-%d;") %
690  kilobytes(
691  config.getValueFor(SizedItem::lgrDBCache, boost::none)));
692 
693  txSQLiteDB_ = std::make_unique<DatabaseCon>(
695  txSQLiteDB_->getSession() << boost::str(
696  boost::format("PRAGMA cache_size=-%d;") %
697  kilobytes(
698  config.getValueFor(SizedItem::txnDBCache, boost::none)));
699  }
700  else
701  {
702  // The incomplete shard uses a Write Ahead Log for performance
703  lgrSQLiteDB_ = std::make_unique<DatabaseCon>(
704  setup,
705  LgrDBName,
706  LgrDBPragma,
707  LgrDBInit,
709  &app_.getJobQueue(), &app_.logs()});
710  lgrSQLiteDB_->getSession() << boost::str(
711  boost::format("PRAGMA cache_size=-%d;") %
712  kilobytes(config.getValueFor(SizedItem::lgrDBCache)));
713 
714  txSQLiteDB_ = std::make_unique<DatabaseCon>(
715  setup,
716  TxDBName,
717  TxDBPragma,
718  TxDBInit,
720  &app_.getJobQueue(), &app_.logs()});
721  txSQLiteDB_->getSession() << boost::str(
722  boost::format("PRAGMA cache_size=-%d;") %
723  kilobytes(config.getValueFor(SizedItem::txnDBCache)));
724  }
725  }
726  catch (std::exception const& e)
727  {
728  JLOG(j_.fatal()) << "shard " << index_ << " exception " << e.what()
729  << " in function " << __func__;
730  return false;
731  }
732  return true;
733 }
734 
735 bool
737  std::shared_ptr<Ledger const> const& ledger,
739 {
740  if (stop_)
741  return false;
742 
743  auto const seq{ledger->info().seq};
744 
745  try
746  {
747  // Update the transactions database
748  {
749  auto& session{txSQLiteDB_->getSession()};
750  soci::transaction tr(session);
751 
752  session << "DELETE FROM Transactions "
753  "WHERE LedgerSeq = :seq;",
754  soci::use(seq);
755  session << "DELETE FROM AccountTransactions "
756  "WHERE LedgerSeq = :seq;",
757  soci::use(seq);
758 
759  if (ledger->info().txHash.isNonZero())
760  {
761  auto const sSeq{std::to_string(seq)};
762  if (!ledger->txMap().isValid())
763  {
764  JLOG(j_.error()) << "shard " << index_
765  << " has an invalid transaction map"
766  << " on sequence " << sSeq;
767  return false;
768  }
769 
770  for (auto const& item : ledger->txs)
771  {
772  if (stop_)
773  return false;
774 
775  auto const txID{item.first->getTransactionID()};
776  auto const sTxID{to_string(txID)};
777  auto const txMeta{std::make_shared<TxMeta>(
778  txID, ledger->seq(), *item.second)};
779 
780  session << "DELETE FROM AccountTransactions "
781  "WHERE TransID = :txID;",
782  soci::use(sTxID);
783 
784  auto const& accounts = txMeta->getAffectedAccounts(j_);
785  if (!accounts.empty())
786  {
787  auto const sTxnSeq{std::to_string(txMeta->getIndex())};
788  auto const s{boost::str(
789  boost::format("('%s','%s',%s,%s)") % sTxID % "%s" %
790  sSeq % sTxnSeq)};
791  std::string sql;
792  sql.reserve((accounts.size() + 1) * 128);
793  sql =
794  "INSERT INTO AccountTransactions "
795  "(TransID, Account, LedgerSeq, TxnSeq) VALUES ";
796  sql += boost::algorithm::join(
797  accounts |
798  boost::adaptors::transformed(
799  [&](AccountID const& accountID) {
800  return boost::str(
801  boost::format(s) %
802  ripple::toBase58(accountID));
803  }),
804  ",");
805  sql += ';';
806  session << sql;
807 
808  JLOG(j_.trace()) << "shard " << index_
809  << " account transaction: " << sql;
810  }
811  else
812  {
813  JLOG(j_.warn())
814  << "shard " << index_ << " transaction in ledger "
815  << sSeq << " affects no accounts";
816  }
817 
818  Serializer s;
819  item.second->add(s);
820  session
822  item.first->getMetaSQL(
823  seq, sqlEscape(s.modData())) +
824  ';');
825  }
826  }
827 
828  tr.commit();
829  }
830 
831  auto const sHash{to_string(ledger->info().hash)};
832 
833  // Update the ledger database
834  {
835  auto& session{lgrSQLiteDB_->getSession()};
836  soci::transaction tr(session);
837 
838  auto const sParentHash{to_string(ledger->info().parentHash)};
839  auto const sDrops{to_string(ledger->info().drops)};
840  auto const sAccountHash{to_string(ledger->info().accountHash)};
841  auto const sTxHash{to_string(ledger->info().txHash)};
842 
843  session << "DELETE FROM Ledgers "
844  "WHERE LedgerSeq = :seq;",
845  soci::use(seq);
846  session
847  << "INSERT OR REPLACE INTO Ledgers ("
848  "LedgerHash, LedgerSeq, PrevHash, TotalCoins, ClosingTime,"
849  "PrevClosingTime, CloseTimeRes, CloseFlags, AccountSetHash,"
850  "TransSetHash)"
851  "VALUES ("
852  ":ledgerHash, :ledgerSeq, :prevHash, :totalCoins,"
853  ":closingTime, :prevClosingTime, :closeTimeRes,"
854  ":closeFlags, :accountSetHash, :transSetHash);",
855  soci::use(sHash), soci::use(seq), soci::use(sParentHash),
856  soci::use(sDrops),
857  soci::use(ledger->info().closeTime.time_since_epoch().count()),
858  soci::use(
859  ledger->info().parentCloseTime.time_since_epoch().count()),
860  soci::use(ledger->info().closeTimeResolution.count()),
861  soci::use(ledger->info().closeFlags), soci::use(sAccountHash),
862  soci::use(sTxHash);
863 
864  tr.commit();
865  }
866 
867  // Update the acquire database if present
868  if (acquireInfo_)
869  {
870  auto& session{acquireInfo_->SQLiteDB->getSession()};
871  soci::blob sociBlob(session);
872 
873  if (!acquireInfo_->storedSeqs.empty())
874  convert(to_string(acquireInfo_->storedSeqs), sociBlob);
875 
876  if (ledger->info().seq == lastSeq_)
877  {
878  // Store shard's last ledger hash
879  session << "UPDATE Shard "
880  "SET LastLedgerHash = :lastLedgerHash,"
881  "StoredLedgerSeqs = :storedLedgerSeqs "
882  "WHERE ShardIndex = :shardIndex;",
883  soci::use(sHash), soci::use(sociBlob), soci::use(index_);
884  }
885  else
886  {
887  session << "UPDATE Shard "
888  "SET StoredLedgerSeqs = :storedLedgerSeqs "
889  "WHERE ShardIndex = :shardIndex;",
890  soci::use(sociBlob), soci::use(index_);
891  }
892  }
893  }
894  catch (std::exception const& e)
895  {
896  JLOG(j_.fatal()) << "shard " << index_ << " exception " << e.what()
897  << " in function " << __func__;
898  return false;
899  }
900  return true;
901 }
902 
903 void
905 {
906  fileSz_ = 0;
907  fdRequired_ = 0;
908  try
909  {
910  using namespace boost::filesystem;
911  for (auto const& d : directory_iterator(dir_))
912  {
913  if (is_regular_file(d))
914  {
915  fileSz_ += file_size(d);
916  ++fdRequired_;
917  }
918  }
919  }
920  catch (std::exception const& e)
921  {
922  JLOG(j_.error()) << "shard " << index_ << " exception " << e.what()
923  << " in function " << __func__;
924  }
925 }
926 
927 bool
929  std::shared_ptr<Ledger const> const& ledger,
930  std::shared_ptr<Ledger const> const& next) const
931 {
932  auto fail = [j = j_, index = index_, &ledger](std::string const& msg) {
933  JLOG(j.fatal()) << "shard " << index << ". " << msg
934  << (ledger->info().hash.isZero() ? ""
935  : ". Ledger hash " +
936  to_string(ledger->info().hash))
937  << (ledger->info().seq == 0 ? ""
938  : ". Ledger sequence " +
939  std::to_string(ledger->info().seq));
940  return false;
941  };
942 
943  if (ledger->info().hash.isZero())
944  return fail("Invalid ledger hash");
945  if (ledger->info().accountHash.isZero())
946  return fail("Invalid ledger account hash");
947 
948  bool error{false};
949  auto visit = [this, &error](SHAMapAbstractNode& node) {
950  if (stop_)
951  return false;
952  if (!valFetch(node.getNodeHash().as_uint256()))
953  error = true;
954  return !error;
955  };
956 
957  // Validate the state map
958  if (ledger->stateMap().getHash().isNonZero())
959  {
960  if (!ledger->stateMap().isValid())
961  return fail("Invalid state map");
962 
963  try
964  {
965  if (next && next->info().parentHash == ledger->info().hash)
966  ledger->stateMap().visitDifferences(&next->stateMap(), visit);
967  else
968  ledger->stateMap().visitNodes(visit);
969  }
970  catch (std::exception const& e)
971  {
972  return fail(
973  std::string("exception ") + e.what() + " in function " +
974  __func__);
975  }
976  if (stop_)
977  return false;
978  if (error)
979  return fail("Invalid state map");
980  }
981 
982  // Validate the transaction map
983  if (ledger->info().txHash.isNonZero())
984  {
985  if (!ledger->txMap().isValid())
986  return fail("Invalid transaction map");
987 
988  try
989  {
990  ledger->txMap().visitNodes(visit);
991  }
992  catch (std::exception const& e)
993  {
994  return fail(
995  std::string("exception ") + e.what() + " in function " +
996  __func__);
997  }
998  if (stop_)
999  return false;
1000  if (error)
1001  return fail("Invalid transaction map");
1002  }
1003 
1004  return true;
1005 }
1006 
1008 Shard::valFetch(uint256 const& hash) const
1009 {
1011  auto fail = [j = j_, index = index_, &hash, &nObj](std::string const& msg) {
1012  JLOG(j.fatal()) << "shard " << index << ". " << msg
1013  << ". Node object hash " << to_string(hash);
1014  nObj.reset();
1015  return nObj;
1016  };
1017 
1018  try
1019  {
1020  switch (backend_->fetch(hash.data(), &nObj))
1021  {
1022  case ok:
1023  // This verifies that the hash of node object matches the
1024  // payload
1025  if (nObj->getHash() != sha512Half(makeSlice(nObj->getData())))
1026  return fail("Node object hash does not match payload");
1027  return nObj;
1028  case notFound:
1029  return fail("Missing node object");
1030  case dataCorrupt:
1031  return fail("Corrupt node object");
1032  default:
1033  return fail("Unknown error");
1034  }
1035  }
1036  catch (std::exception const& e)
1037  {
1038  return fail(
1039  std::string("exception ") + e.what() + " in function " + __func__);
1040  }
1041 }
1042 
1043 } // namespace NodeStore
1044 } // namespace ripple
beast::Journal::fatal
Stream fatal() const
Definition: Journal.h:339
ripple::AcquireShardDBPragma
constexpr std::array< char const *, 1 > AcquireShardDBPragma
Definition: DBInit.h:120
ripple::NodeStore::Shard::getBackend
std::shared_ptr< Backend > getBackend() const
Definition: Shard.cpp:338
ripple::Section
Holds a collection of configuration values.
Definition: BasicConfig.h:43
ripple::NodeStore::Shard::dir_
const boost::filesystem::path dir_
Definition: Shard.h:193
ripple::Application
Definition: Application.h:97
ripple::AcquireShardDBName
constexpr auto AcquireShardDBName
Definition: DBInit.h:118
ripple::hotUNKNOWN
@ hotUNKNOWN
Definition: NodeObject.h:33
ripple::makeSlice
std::enable_if_t< std::is_same< T, char >::value||std::is_same< T, unsigned char >::value, Slice > makeSlice(std::array< T, N > const &a)
Definition: Slice.h:240
ripple::NodeStore::DatabaseShard::earliestShardIndex
virtual std::uint32_t earliestShardIndex() const =0
std::string
STL class.
std::shared_ptr< NodeObject >
ripple::NodeStore::Shard::lgrSQLiteDB_
std::unique_ptr< DatabaseCon > lgrSQLiteDB_
Definition: Shard.h:205
ripple::NodeStore::Shard::removeOnDestroy_
std::atomic< bool > removeOnDestroy_
Definition: Shard.h:230
ripple::NodeStore::Shard::setBackendCache
void setBackendCache(std::lock_guard< std::recursive_mutex > const &lock)
Definition: Shard.cpp:630
std::exception
STL class.
ripple::DatabaseCon::Setup
Definition: DatabaseCon.h:84
beast::Journal::trace
Stream trace() const
Severity stream access functions.
Definition: Journal.h:309
ripple::SizedItem::nodeCacheSize
@ nodeCacheSize
ripple::NodeStore::ok
@ ok
Definition: nodestore/Types.h:45
ripple::NodeStore::Shard::valLedger
bool valLedger(std::shared_ptr< Ledger const > const &ledger, std::shared_ptr< Ledger const > const &next) const
Definition: Shard.cpp:928
ripple::Serializer::modData
Blob & modData()
Definition: Serializer.h:176
std::pair
std::string::reserve
T reserve(T... args)
ripple::NodeStore::Shard::store
bool store(std::shared_ptr< Ledger const > const &ledger)
Definition: Shard.cpp:251
ripple::NodeStore::Shard::backendComplete_
bool backendComplete_
Definition: Shard.h:217
ripple::convert
void convert(soci::blob &from, std::vector< std::uint8_t > &to)
Definition: SociDB.cpp:156
ripple::DatabaseCon::Setup::startUp
Config::StartUpType startUp
Definition: DatabaseCon.h:88
ripple::ConfigSection::shardDatabase
static std::string shardDatabase()
Definition: ConfigSections.h:38
ripple::NodeStore::Shard::final_
bool final_
Definition: Shard.h:224
ripple::NodeObject::createObject
static std::shared_ptr< NodeObject > createObject(NodeObjectType type, Blob &&data, uint256 const &hash)
Create an object from fields.
Definition: NodeObject.cpp:37
std::chrono::seconds
ripple::NodeStore::Shard::initSQLite
bool initSQLite(std::lock_guard< std::recursive_mutex > const &lock)
Definition: Shard.cpp:664
ripple::toBase58
std::string toBase58(AccountID const &v)
Convert AccountID to base58 checked string.
Definition: AccountID.cpp:29
ripple::DatabaseCon::CheckpointerSetup
Definition: DatabaseCon.h:106
beast::Journal::warn
Stream warn() const
Definition: Journal.h:327
std::lock_guard
STL class.
ripple::kilobytes
constexpr auto kilobytes(T value) noexcept
Definition: ByteUtilities.h:27
ripple::AcquireShardDBInit
constexpr std::array< char const *, 1 > AcquireShardDBInit
Definition: DBInit.h:123
std::tuple
ripple::DatabaseCon::Setup::dataDir
boost::filesystem::path dataDir
Definition: DatabaseCon.h:90
ripple::STTx::getMetaSQLInsertReplaceHeader
static std::string const & getMetaSQLInsertReplaceHeader()
Definition: STTx.cpp:216
ripple::from_string
bool from_string(RangeSet< T > &rs, std::string const &s)
Convert the given styled string to a RangeSet.
Definition: RangeSet.h:126
ripple::stopwatch
Stopwatch & stopwatch()
Returns an instance of a wall clock.
Definition: chrono.h:86
ripple::SizedItem::nodeCacheAge
@ nodeCacheAge
ripple::sqlEscape
static std::string sqlEscape(std::string const &strSrc)
Definition: StringUtilities.h:34
ripple::to_string
std::string to_string(ListDisposition disposition)
Definition: ValidatorList.cpp:42
ripple::NodeStore::Shard::finalKey
static const uint256 finalKey
Definition: Shard.h:158
ripple::NodeStore::Shard::lastSeq_
const std::uint32_t lastSeq_
Definition: Shard.h:180
std::shared_ptr::reset
T reset(T... args)
ripple::base_uint::data
pointer data()
Definition: base_uint.h:103
ripple::LgrDBInit
constexpr std::array< char const *, 5 > LgrDBInit
Definition: DBInit.h:48
ripple::SHAMapHash
Definition: SHAMapTreeNode.h:43
ripple::NodeStore::Shard::isLegacy
bool isLegacy() const
Returns true if the shard is older, without final key data.
Definition: Shard.cpp:386
ripple::uint256
base_uint< 256 > uint256
Definition: base_uint.h:493
ripple::deserializePrefixedHeader
LedgerInfo deserializePrefixedHeader(Slice data)
Deserialize a ledger header (prefixed with 4 bytes) from a byte array.
Definition: InboundLedger.cpp:292
ripple::NodeStore::Shard::version
static constexpr std::uint32_t version
Definition: Shard.h:153
ripple::NodeStore::notFound
@ notFound
Definition: nodestore/Types.h:46
ripple::TxDBName
constexpr auto TxDBName
Definition: DBInit.h:73
ripple::base_uint< 256 >
ripple::DatabaseCon::Setup::useGlobalPragma
bool useGlobalPragma
Definition: DatabaseCon.h:93
ripple::NodeStore::Shard::pCache
std::shared_ptr< PCache > pCache() const
Definition: Shard.cpp:354
ripple::NodeStore::Shard::j_
const beast::Journal j_
Definition: Shard.h:214
ripple::NodeStore::Shard::finalize
bool finalize(bool const writeSQLite, boost::optional< uint256 > const &referenceHash)
Finalize shard by walking its ledgers and verifying each Merkle tree.
Definition: Shard.cpp:393
ripple::NodeStore::Shard::fileSz_
std::uint64_t fileSz_
Definition: Shard.h:196
ripple::NodeStore::Shard::valFetch
std::shared_ptr< NodeObject > valFetch(uint256 const &hash) const
Definition: Shard.cpp:1008
ripple::DatabaseCon::Setup::standAlone
bool standAlone
Definition: DatabaseCon.h:89
ripple::base_uint::isZero
bool isZero() const
Definition: base_uint.h:475
ripple::LgrDBPragma
constexpr std::array< char const *, 1 > LgrDBPragma
Definition: DBInit.h:45
ripple::NodeStore::Shard::sweep
void sweep()
Definition: Shard.cpp:316
ripple::SerialIter::get256
uint256 get256()
Definition: Serializer.h:374
ripple::Config
Definition: Config.h:67
ripple::Application::config
virtual Config & config()=0
ripple::prevMissing
boost::optional< T > prevMissing(RangeSet< T > const &rs, T t, T minVal=0)
Find the largest value not in the set that is less than a given value.
Definition: RangeSet.h:184
std::unique_lock
STL class.
ripple::NodeStore::DatabaseShard
A collection of historical shards.
Definition: DatabaseShard.h:37
ripple::NodeStore::Shard::nCache_
std::shared_ptr< NCache > nCache_
Definition: Shard.h:190
std::to_string
T to_string(T... args)
ripple::Application::getJobQueue
virtual JobQueue & getJobQueue()=0
ripple::NodeStore::Shard::backend_
std::shared_ptr< Backend > backend_
Definition: Shard.h:202
ripple::NodeStore::Shard::firstSeq_
const std::uint32_t firstSeq_
Definition: Shard.h:177
ripple::NodeStore::Shard::pCache_
std::shared_ptr< PCache > pCache_
Definition: Shard.h:187
ripple::NodeStore::Shard::isFinal
bool isFinal() const
Returns true if the shard is complete, validated, and immutable.
Definition: Shard.cpp:379
beast::Journal::error
Stream error() const
Definition: Journal.h:333
ripple::Application::logs
virtual Logs & logs()=0
ripple::NodeStore::Shard::maxLedgers_
const std::uint32_t maxLedgers_
Definition: Shard.h:184
ripple::SerialIter
Definition: Serializer.h:308
ripple::SizedItem::lgrDBCache
@ lgrDBCache
ripple::NodeStore::Shard::~Shard
~Shard()
Definition: Shard.cpp:56
beast::Journal
A generic endpoint for log messages.
Definition: Journal.h:58
ripple::NodeStore::dataCorrupt
@ dataCorrupt
Definition: nodestore/Types.h:47
std::uint32_t
ripple::NodeStore::Scheduler
Scheduling for asynchronous backend activity.
Definition: ripple/nodestore/Scheduler.h:57
ripple::SizedItem::txnDBCache
@ txnDBCache
ripple::NodeStore::Shard::acquireInfo_
std::unique_ptr< AcquireInfo > acquireInfo_
Definition: Shard.h:212
ripple::NodeStore::Shard::app_
Application & app_
Definition: Shard.h:170
ripple::NodeStore::Shard::isBackendComplete
bool isBackendComplete() const
Returns true if all shard ledgers have been stored in the backend.
Definition: Shard.cpp:347
ripple::NodeStore::Shard::mutex_
std::recursive_mutex mutex_
Definition: Shard.h:171
ripple::Serializer
Definition: Serializer.h:39
ripple::NodeStore::Manager::find
virtual Factory * find(std::string const &name)=0
Return a pointer to the matching factory if it exists.
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
ripple::Serializer::addBitString
int addBitString(base_uint< Bits, Tag > const &v)
Definition: Serializer.h:97
ripple::NodeObject::keyBytes
static constexpr std::size_t keyBytes
Definition: NodeObject.h:57
ripple::Application::getShardFamily
virtual Family * getShardFamily()=0
ripple::NodeStore::Shard::prepare
boost::optional< std::uint32_t > prepare()
Definition: Shard.cpp:231
std
STL namespace.
ripple::NodeStore::Shard::Shard
Shard(Application &app, DatabaseShard const &db, std::uint32_t index, beast::Journal j)
Definition: Shard.cpp:37
ripple::sha512Half
sha512_half_hasher::result_type sha512Half(Args const &... args)
Returns the SHA512-Half of a series of objects.
Definition: digest.h:227
ripple::TxDBPragma
constexpr std::array TxDBPragma
Definition: DBInit.h:76
ripple::NodeStore::Shard::setFileStats
void setFileStats(std::lock_guard< std::recursive_mutex > const &lock)
Definition: Shard.cpp:904
ripple::SHAMapAbstractNode
Definition: SHAMapTreeNode.h:122
ripple::NodeStore::Shard::legacy_
bool legacy_
Definition: Shard.h:221
ripple::NodeStore::Shard::fdRequired_
std::uint32_t fdRequired_
Definition: Shard.h:199
ripple::NodeStore::Shard::index
std::uint32_t index() const
Definition: Shard.h:78
beast::Journal::debug
Stream debug() const
Definition: Journal.h:315
ripple::Serializer::add32
int add32(std::uint32_t i)
Definition: Serializer.cpp:38
ripple::NodeStore::Shard::fileInfo
std::pair< std::uint64_t, std::uint32_t > fileInfo() const
Returns a pair where the first item describes the storage space utilized and the second item is the n...
Definition: Shard.cpp:372
ripple::NodeStore::Shard::open
bool open(Scheduler &scheduler, nudb::context &ctx)
Definition: Shard.cpp:78
ripple::CompleteShardDBPragma
constexpr std::array< char const *, 2 > CompleteShardDBPragma
Definition: DBInit.h:134
ripple::TxDBInit
constexpr std::array< char const *, 8 > TxDBInit
Definition: DBInit.h:84
ripple::NodeStore::Manager::instance
static Manager & instance()
Returns the instance of the manager singleton.
Definition: ManagerImp.cpp:117
ripple::SerialIter::get32
std::uint32_t get32()
Definition: Serializer.cpp:378
ripple::NodeStore::Shard::txSQLiteDB_
std::unique_ptr< DatabaseCon > txSQLiteDB_
Definition: Shard.h:208
ripple::NodeStore::Shard::containsLedger
bool containsLedger(std::uint32_t seq) const
Definition: Shard.cpp:302
ripple::NodeStore::Shard::index_
const std::uint32_t index_
Definition: Shard.h:174
ripple::NodeStore::Shard::stop_
std::atomic< bool > stop_
Definition: Shard.h:227
ripple::NodeStore::Shard::nCache
std::shared_ptr< NCache > nCache() const
Definition: Shard.cpp:363
std::exception::what
T what(T... args)
ripple::NodeStore::Shard::getBackendAll
std::tuple< std::shared_ptr< Backend >, std::shared_ptr< PCache >, std::shared_ptr< NCache > > getBackendAll() const
Definition: Shard.cpp:329
ripple::NodeStore::Shard::storeSQLite
bool storeSQLite(std::shared_ptr< Ledger const > const &ledger, std::lock_guard< std::recursive_mutex > const &lock)
Definition: Shard.cpp:736
ripple::LgrDBName
constexpr auto LgrDBName
Definition: DBInit.h:43