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