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 
128  acquireInfo_->SQLiteDB = std::make_unique<DatabaseCon>(
129  setup,
133  acquireInfo_->SQLiteDB->setupCheckpointing(
134  &app_.getJobQueue(), app_.logs());
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  {
186  // All ledgers have been acquired, shard is complete
187  acquireInfo_.reset();
188  backendComplete_ = true;
189  }
190  }
191  }
192  else
193  {
194  // A finalized shard or has all ledgers stored in the backend
196  if (backend_->fetch(finalKey.data(), &nObj) != Status::ok)
197  {
198  legacy_ = true;
199  return fail("incompatible, missing backend final key");
200  }
201 
202  // Check final key's value
203  SerialIter sIt(nObj->getData().data(), nObj->getData().size());
204  if (sIt.get32() != version)
205  return fail("invalid version");
206 
207  if (sIt.get32() != firstSeq_ || sIt.get32() != lastSeq_)
208  return fail("out of range ledger sequences");
209 
210  if (sIt.get256().isZero())
211  return fail("invalid last ledger hash");
212 
213  if (exists(dir_ / LgrDBName) && exists(dir_ / TxDBName))
214  final_ = true;
215 
216  backendComplete_ = true;
217  }
218  }
219  catch (std::exception const& e)
220  {
221  return fail(
222  std::string("exception ") + e.what() + " in function " + __func__);
223  }
224 
225  setBackendCache(lock);
226  if (!initSQLite(lock))
227  return fail({});
228 
229  setFileStats(lock);
230  return true;
231 }
232 
233 boost::optional<std::uint32_t>
235 {
236  std::lock_guard lock(mutex_);
237  assert(backend_);
238 
239  if (backendComplete_)
240  {
241  JLOG(j_.warn()) << "shard " << index_
242  << " prepare called when shard is complete";
243  return {};
244  }
245 
246  assert(acquireInfo_);
247  auto const& storedSeqs{acquireInfo_->storedSeqs};
248  if (storedSeqs.empty())
249  return lastSeq_;
250  return prevMissing(storedSeqs, 1 + lastSeq_, firstSeq_);
251 }
252 
253 bool
255 {
256  auto const seq{ledger->info().seq};
257  if (seq < firstSeq_ || seq > lastSeq_)
258  {
259  JLOG(j_.error()) << "shard " << index_ << " invalid ledger sequence "
260  << seq;
261  return false;
262  }
263 
264  std::lock_guard lock(mutex_);
265  assert(backend_);
266 
267  if (backendComplete_)
268  {
269  JLOG(j_.debug()) << "shard " << index_ << " ledger sequence " << seq
270  << " already stored";
271  return true;
272  }
273 
274  assert(acquireInfo_);
275  auto& storedSeqs{acquireInfo_->storedSeqs};
276  if (boost::icl::contains(storedSeqs, seq))
277  {
278  JLOG(j_.debug()) << "shard " << index_ << " ledger sequence " << seq
279  << " already stored";
280  return true;
281  }
282  // storeSQLite looks at storedSeqs so insert before the call
283  storedSeqs.insert(seq);
284 
285  if (!storeSQLite(ledger, lock))
286  return false;
287 
288  if (boost::icl::length(storedSeqs) >= maxLedgers_)
289  {
290  if (!initSQLite(lock))
291  return false;
292 
293  acquireInfo_.reset();
294  backendComplete_ = true;
295  setBackendCache(lock);
296  }
297 
298  JLOG(j_.debug()) << "shard " << index_ << " stored ledger sequence " << seq
299  << (backendComplete_ ? " . All ledgers stored" : "");
300 
301  setFileStats(lock);
302  return true;
303 }
304 
305 bool
307 {
308  if (seq < firstSeq_ || seq > lastSeq_)
309  return false;
310 
311  std::lock_guard lock(mutex_);
312  if (backendComplete_)
313  return true;
314 
315  assert(acquireInfo_);
316  return boost::icl::contains(acquireInfo_->storedSeqs, seq);
317 }
318 
319 void
321 {
322  std::lock_guard lock(mutex_);
323  assert(pCache_ && nCache_);
324 
325  pCache_->sweep();
326  nCache_->sweep();
327 }
328 
329 std::tuple<
334 {
335  std::lock_guard lock(mutex_);
336  assert(backend_);
337 
338  return {backend_, pCache_, nCache_};
339 }
340 
343 {
344  std::lock_guard lock(mutex_);
345  assert(backend_);
346 
347  return backend_;
348 }
349 
350 bool
352 {
353  std::lock_guard lock(mutex_);
354  return backendComplete_;
355 }
356 
359 {
360  std::lock_guard lock(mutex_);
361  assert(pCache_);
362 
363  return pCache_;
364 }
365 
368 {
369  std::lock_guard lock(mutex_);
370  assert(nCache_);
371 
372  return nCache_;
373 }
374 
377 {
378  std::lock_guard lock(mutex_);
379  return {fileSz_, fdRequired_};
380 }
381 
382 bool
384 {
385  std::lock_guard lock(mutex_);
386  return final_;
387 }
388 
389 bool
391 {
392  std::lock_guard lock(mutex_);
393  return legacy_;
394 }
395 
396 bool
398  bool const writeSQLite,
399  boost::optional<uint256> const& expectedHash)
400 {
401  assert(backend_);
402 
403  if (stop_)
404  return false;
405 
406  uint256 hash{0};
407  std::uint32_t seq{0};
408  auto fail =
409  [j = j_, index = index_, &hash, &seq](std::string const& msg) {
410  JLOG(j.fatal())
411  << "shard " << index << ". " << msg
412  << (hash.isZero() ? "" : ". Ledger hash " + to_string(hash))
413  << (seq == 0 ? "" : ". Ledger sequence " + std::to_string(seq));
414  return false;
415  };
416 
417  try
418  {
419  std::unique_lock lock(mutex_);
420  if (!backendComplete_)
421  return fail("incomplete");
422 
423  /*
424  TODO MP
425  A lock is required when calling the NuDB verify function. Because
426  this can be a time consuming process, the server may desync.
427  Until this function is modified to work on an open database, we
428  are unable to use it from rippled.
429 
430  // Verify backend integrity
431  backend_->verify();
432  */
433 
434  // Check if a final key has been stored
435  lock.unlock();
437  backend_->fetch(finalKey.data(), &nObj) == Status::ok)
438  {
439  // Check final key's value
440  SerialIter sIt(nObj->getData().data(), nObj->getData().size());
441  if (sIt.get32() != version)
442  return fail("invalid version");
443 
444  if (sIt.get32() != firstSeq_ || sIt.get32() != lastSeq_)
445  return fail("out of range ledger sequences");
446 
447  if (hash = sIt.get256(); hash.isZero())
448  return fail("invalid last ledger hash");
449  }
450  else
451  {
452  // In the absence of a final key, an acquire SQLite database
453  // must be present in order to validate the shard
454  lock.lock();
455  if (!acquireInfo_)
456  return fail("missing acquire SQLite database");
457 
458  auto& session{acquireInfo_->SQLiteDB->getSession()};
459  boost::optional<std::uint32_t> index;
460  boost::optional<std::string> sHash;
461  soci::blob sociBlob(session);
462  soci::indicator blobPresent;
463  session << "SELECT ShardIndex, LastLedgerHash, StoredLedgerSeqs "
464  "FROM Shard "
465  "WHERE ShardIndex = :index;",
466  soci::into(index), soci::into(sHash),
467  soci::into(sociBlob, blobPresent), soci::use(index_);
468 
469  lock.unlock();
470  if (!index || index != index_)
471  return fail("missing or invalid ShardIndex");
472 
473  if (!sHash)
474  return fail("missing LastLedgerHash");
475 
476  if (hash.SetHexExact(*sHash); hash.isZero())
477  return fail("invalid LastLedgerHash");
478 
479  if (blobPresent != soci::i_ok)
480  return fail("missing StoredLedgerSeqs");
481 
482  std::string s;
483  convert(sociBlob, s);
484 
485  lock.lock();
486 
487  auto& storedSeqs{acquireInfo_->storedSeqs};
488  if (!from_string(storedSeqs, s) ||
489  boost::icl::first(storedSeqs) != firstSeq_ ||
490  boost::icl::last(storedSeqs) != lastSeq_ ||
491  storedSeqs.size() != maxLedgers_)
492  {
493  return fail("invalid StoredLedgerSeqs");
494  }
495  }
496  }
497  catch (std::exception const& e)
498  {
499  return fail(
500  std::string("exception ") + e.what() + " in function " + __func__);
501  }
502 
503  // Validate the last ledger hash of a downloaded shard
504  // using a ledger hash obtained from the peer network
505  if (expectedHash && *expectedHash != hash)
506  return fail("invalid last ledger hash");
507 
508  // Validate every ledger stored in the backend
511  auto const lastLedgerHash{hash};
512 
513  // Start with the last ledger in the shard and walk backwards from
514  // child to parent until we reach the first ledger
515  seq = lastSeq_;
516  while (seq >= firstSeq_)
517  {
518  if (stop_)
519  return false;
520 
521  auto nObj = valFetch(hash);
522  if (!nObj)
523  return fail("invalid ledger");
524 
525  ledger = std::make_shared<Ledger>(
526  deserializePrefixedHeader(makeSlice(nObj->getData())),
527  app_.config(),
528  *app_.shardFamily());
529  if (ledger->info().seq != seq)
530  return fail("invalid ledger sequence");
531  if (ledger->info().hash != hash)
532  return fail("invalid ledger hash");
533 
534  ledger->stateMap().setLedgerSeq(seq);
535  ledger->txMap().setLedgerSeq(seq);
536  ledger->setImmutable(app_.config());
537  if (!ledger->stateMap().fetchRoot(
538  SHAMapHash{ledger->info().accountHash}, nullptr))
539  {
540  return fail("missing root STATE node");
541  }
542  if (ledger->info().txHash.isNonZero() &&
543  !ledger->txMap().fetchRoot(
544  SHAMapHash{ledger->info().txHash}, nullptr))
545  {
546  return fail("missing root TXN node");
547  }
548 
549  if (!valLedger(ledger, next))
550  return fail("failed to validate ledger");
551 
552  if (writeSQLite)
553  {
554  std::lock_guard lock(mutex_);
555  if (!storeSQLite(ledger, lock))
556  return fail("failed storing to SQLite databases");
557  }
558 
559  hash = ledger->info().parentHash;
560  --seq;
561  next = ledger;
562  }
563 
564  JLOG(j_.debug()) << "shard " << index_ << " is valid";
565 
566  /*
567  TODO MP
568  SQLite VACUUM blocks all database access while processing.
569  Depending on the file size, that can take a while. Until we find
570  a non-blocking way of doing this, we cannot enable vacuum as
571  it can desync a server.
572 
573  try
574  {
575  // VACUUM the SQLite databases
576  auto const tmpDir {dir_ / "tmp_vacuum"};
577  create_directory(tmpDir);
578 
579  auto vacuum = [&tmpDir](std::unique_ptr<DatabaseCon>& sqliteDB)
580  {
581  auto& session {sqliteDB->getSession()};
582  session << "PRAGMA synchronous=OFF;";
583  session << "PRAGMA journal_mode=OFF;";
584  session << "PRAGMA temp_store_directory='" <<
585  tmpDir.string() << "';";
586  session << "VACUUM;";
587  };
588  vacuum(lgrSQLiteDB_);
589  vacuum(txSQLiteDB_);
590  remove_all(tmpDir);
591  }
592  catch (std::exception const& e)
593  {
594  return fail(std::string("exception ") +
595  e.what() + " in function " + __func__);
596  }
597  */
598 
599  // Store final key's value, may already be stored
600  Serializer s;
601  s.add32(version);
602  s.add32(firstSeq_);
603  s.add32(lastSeq_);
604  s.addBitString(lastLedgerHash);
605  auto nObj{
607  try
608  {
609  backend_->store(nObj);
610 
611  std::lock_guard lock(mutex_);
612  final_ = true;
613 
614  // Remove the acquire SQLite database if present
615  if (acquireInfo_)
616  acquireInfo_.reset();
617  remove_all(dir_ / AcquireShardDBName);
618 
619  if (!initSQLite(lock))
620  return fail("failed to initialize SQLite databases");
621 
622  setFileStats(lock);
623  }
624  catch (std::exception const& e)
625  {
626  return fail(
627  std::string("exception ") + e.what() + " in function " + __func__);
628  }
629 
630  return true;
631 }
632 
633 void
635 {
636  // Complete shards use the smallest cache and
637  // fastest expiration to reduce memory consumption.
638  // An incomplete shard is set according to configuration.
639 
640  Config const& config{app_.config()};
641  if (!pCache_)
642  {
643  auto const name{"shard " + std::to_string(index_)};
644  auto const sz{config.getValueFor(
646  backendComplete_ ? boost::optional<std::size_t>(0) : boost::none)};
647  auto const age{std::chrono::seconds{config.getValueFor(
649  backendComplete_ ? boost::optional<std::size_t>(0) : boost::none)}};
650 
651  pCache_ = std::make_shared<PCache>(name, sz, age, stopwatch(), j_);
652  nCache_ = std::make_shared<NCache>(name, stopwatch(), sz, age);
653  }
654  else
655  {
656  auto const sz{config.getValueFor(SizedItem::nodeCacheSize, 0)};
657  pCache_->setTargetSize(sz);
658  nCache_->setTargetSize(sz);
659 
660  auto const age{std::chrono::seconds{
661  config.getValueFor(SizedItem::nodeCacheAge, 0)}};
662  pCache_->setTargetAge(age);
663  nCache_->setTargetAge(age);
664  }
665 }
666 
667 bool
669 {
670  Config const& config{app_.config()};
671  DatabaseCon::Setup setup;
672  setup.startUp = config.START_UP;
673  setup.standAlone = config.standalone();
674  setup.dataDir = dir_;
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, LgrDBName, LgrDBPragma, LgrDBInit);
705  lgrSQLiteDB_->getSession() << boost::str(
706  boost::format("PRAGMA cache_size=-%d;") %
707  kilobytes(config.getValueFor(SizedItem::lgrDBCache)));
708  lgrSQLiteDB_->setupCheckpointing(&app_.getJobQueue(), app_.logs());
709 
710  txSQLiteDB_ = std::make_unique<DatabaseCon>(
711  setup, TxDBName, TxDBPragma, TxDBInit);
712  txSQLiteDB_->getSession() << boost::str(
713  boost::format("PRAGMA cache_size=-%d;") %
714  kilobytes(config.getValueFor(SizedItem::txnDBCache)));
715  txSQLiteDB_->setupCheckpointing(&app_.getJobQueue(), app_.logs());
716  }
717  }
718  catch (std::exception const& e)
719  {
720  JLOG(j_.fatal()) << "shard " << index_ << " exception " << e.what()
721  << " in function " << __func__;
722  return false;
723  }
724  return true;
725 }
726 
727 bool
729  std::shared_ptr<Ledger const> const& ledger,
731 {
732  if (stop_)
733  return false;
734 
735  auto const seq{ledger->info().seq};
736 
737  try
738  {
739  // Update the transactions database
740  {
741  auto& session{txSQLiteDB_->getSession()};
742  soci::transaction tr(session);
743 
744  session << "DELETE FROM Transactions "
745  "WHERE LedgerSeq = :seq;",
746  soci::use(seq);
747  session << "DELETE FROM AccountTransactions "
748  "WHERE LedgerSeq = :seq;",
749  soci::use(seq);
750 
751  if (ledger->info().txHash.isNonZero())
752  {
753  auto const sSeq{std::to_string(seq)};
754  if (!ledger->txMap().isValid())
755  {
756  JLOG(j_.error()) << "shard " << index_
757  << " has an invalid transaction map"
758  << " on sequence " << sSeq;
759  return false;
760  }
761 
762  for (auto const& item : ledger->txs)
763  {
764  if (stop_)
765  return false;
766 
767  auto const txID{item.first->getTransactionID()};
768  auto const sTxID{to_string(txID)};
769  auto const txMeta{std::make_shared<TxMeta>(
770  txID, ledger->seq(), *item.second)};
771 
772  session << "DELETE FROM AccountTransactions "
773  "WHERE TransID = :txID;",
774  soci::use(sTxID);
775 
776  auto const& accounts = txMeta->getAffectedAccounts(j_);
777  if (!accounts.empty())
778  {
779  auto const sTxnSeq{std::to_string(txMeta->getIndex())};
780  auto const s{boost::str(
781  boost::format("('%s','%s',%s,%s)") % sTxID % "%s" %
782  sSeq % sTxnSeq)};
783  std::string sql;
784  sql.reserve((accounts.size() + 1) * 128);
785  sql =
786  "INSERT INTO AccountTransactions "
787  "(TransID, Account, LedgerSeq, TxnSeq) VALUES ";
788  sql += boost::algorithm::join(
789  accounts |
790  boost::adaptors::transformed(
791  [&](AccountID const& accountID) {
792  return boost::str(
793  boost::format(s) %
794  ripple::toBase58(accountID));
795  }),
796  ",");
797  sql += ';';
798  session << sql;
799 
800  JLOG(j_.trace()) << "shard " << index_
801  << " account transaction: " << sql;
802  }
803  else
804  {
805  JLOG(j_.warn())
806  << "shard " << index_ << " transaction in ledger "
807  << sSeq << " affects no accounts";
808  }
809 
810  Serializer s;
811  item.second->add(s);
812  session
814  item.first->getMetaSQL(
815  seq, sqlEscape(s.modData())) +
816  ';');
817  }
818  }
819 
820  tr.commit();
821  }
822 
823  auto const sHash{to_string(ledger->info().hash)};
824 
825  // Update the ledger database
826  {
827  auto& session{lgrSQLiteDB_->getSession()};
828  soci::transaction tr(session);
829 
830  auto const sParentHash{to_string(ledger->info().parentHash)};
831  auto const sDrops{to_string(ledger->info().drops)};
832  auto const sAccountHash{to_string(ledger->info().accountHash)};
833  auto const sTxHash{to_string(ledger->info().txHash)};
834 
835  session << "DELETE FROM Ledgers "
836  "WHERE LedgerSeq = :seq;",
837  soci::use(seq);
838  session
839  << "INSERT OR REPLACE INTO Ledgers ("
840  "LedgerHash, LedgerSeq, PrevHash, TotalCoins, ClosingTime,"
841  "PrevClosingTime, CloseTimeRes, CloseFlags, AccountSetHash,"
842  "TransSetHash)"
843  "VALUES ("
844  ":ledgerHash, :ledgerSeq, :prevHash, :totalCoins,"
845  ":closingTime, :prevClosingTime, :closeTimeRes,"
846  ":closeFlags, :accountSetHash, :transSetHash);",
847  soci::use(sHash), soci::use(seq), soci::use(sParentHash),
848  soci::use(sDrops),
849  soci::use(ledger->info().closeTime.time_since_epoch().count()),
850  soci::use(
851  ledger->info().parentCloseTime.time_since_epoch().count()),
852  soci::use(ledger->info().closeTimeResolution.count()),
853  soci::use(ledger->info().closeFlags), soci::use(sAccountHash),
854  soci::use(sTxHash);
855 
856  tr.commit();
857  }
858 
859  // Update the acquire database if present
860  if (acquireInfo_)
861  {
862  auto& session{acquireInfo_->SQLiteDB->getSession()};
863  soci::blob sociBlob(session);
864 
865  if (!acquireInfo_->storedSeqs.empty())
866  convert(to_string(acquireInfo_->storedSeqs), sociBlob);
867 
868  if (ledger->info().seq == lastSeq_)
869  {
870  // Store shard's last ledger hash
871  session << "UPDATE Shard "
872  "SET LastLedgerHash = :lastLedgerHash,"
873  "StoredLedgerSeqs = :storedLedgerSeqs "
874  "WHERE ShardIndex = :shardIndex;",
875  soci::use(sHash), soci::use(sociBlob), soci::use(index_);
876  }
877  else
878  {
879  session << "UPDATE Shard "
880  "SET StoredLedgerSeqs = :storedLedgerSeqs "
881  "WHERE ShardIndex = :shardIndex;",
882  soci::use(sociBlob), soci::use(index_);
883  }
884  }
885  }
886  catch (std::exception const& e)
887  {
888  JLOG(j_.fatal()) << "shard " << index_ << " exception " << e.what()
889  << " in function " << __func__;
890  return false;
891  }
892  return true;
893 }
894 
895 void
897 {
898  fileSz_ = 0;
899  fdRequired_ = 0;
900  try
901  {
902  using namespace boost::filesystem;
903  for (auto const& d : directory_iterator(dir_))
904  {
905  if (is_regular_file(d))
906  {
907  fileSz_ += file_size(d);
908  ++fdRequired_;
909  }
910  }
911  }
912  catch (std::exception const& e)
913  {
914  JLOG(j_.error()) << "shard " << index_ << " exception " << e.what()
915  << " in function " << __func__;
916  }
917 }
918 
919 bool
921  std::shared_ptr<Ledger const> const& ledger,
922  std::shared_ptr<Ledger const> const& next) const
923 {
924  auto fail = [j = j_, index = index_, &ledger](std::string const& msg) {
925  JLOG(j.fatal()) << "shard " << index << ". " << msg
926  << (ledger->info().hash.isZero() ? ""
927  : ". Ledger hash " +
928  to_string(ledger->info().hash))
929  << (ledger->info().seq == 0 ? ""
930  : ". Ledger sequence " +
931  std::to_string(ledger->info().seq));
932  return false;
933  };
934 
935  if (ledger->info().hash.isZero())
936  return fail("Invalid ledger hash");
937  if (ledger->info().accountHash.isZero())
938  return fail("Invalid ledger account hash");
939 
940  bool error{false};
941  auto visit = [this, &error](SHAMapAbstractNode& node) {
942  if (stop_)
943  return false;
944  if (!valFetch(node.getNodeHash().as_uint256()))
945  error = true;
946  return !error;
947  };
948 
949  // Validate the state map
950  if (ledger->stateMap().getHash().isNonZero())
951  {
952  if (!ledger->stateMap().isValid())
953  return fail("Invalid state map");
954 
955  try
956  {
957  if (next && next->info().parentHash == ledger->info().hash)
958  ledger->stateMap().visitDifferences(&next->stateMap(), visit);
959  else
960  ledger->stateMap().visitNodes(visit);
961  }
962  catch (std::exception const& e)
963  {
964  return fail(
965  std::string("exception ") + e.what() + " in function " +
966  __func__);
967  }
968  if (stop_)
969  return false;
970  if (error)
971  return fail("Invalid state map");
972  }
973 
974  // Validate the transaction map
975  if (ledger->info().txHash.isNonZero())
976  {
977  if (!ledger->txMap().isValid())
978  return fail("Invalid transaction map");
979 
980  try
981  {
982  ledger->txMap().visitNodes(visit);
983  }
984  catch (std::exception const& e)
985  {
986  return fail(
987  std::string("exception ") + e.what() + " in function " +
988  __func__);
989  }
990  if (stop_)
991  return false;
992  if (error)
993  return fail("Invalid transaction map");
994  }
995 
996  return true;
997 }
998 
1000 Shard::valFetch(uint256 const& hash) const
1001 {
1003  auto fail = [j = j_, index = index_, &hash, &nObj](std::string const& msg) {
1004  JLOG(j.fatal()) << "shard " << index << ". " << msg
1005  << ". Node object hash " << to_string(hash);
1006  nObj.reset();
1007  return nObj;
1008  };
1009 
1010  try
1011  {
1012  switch (backend_->fetch(hash.data(), &nObj))
1013  {
1014  case ok:
1015  // This verifies that the hash of node object matches the
1016  // payload
1017  if (nObj->getHash() != sha512Half(makeSlice(nObj->getData())))
1018  return fail("Node object hash does not match payload");
1019  return nObj;
1020  case notFound:
1021  return fail("Missing node object");
1022  case dataCorrupt:
1023  return fail("Corrupt node object");
1024  default:
1025  return fail("Unknown error");
1026  }
1027  }
1028  catch (std::exception const& e)
1029  {
1030  return fail(
1031  std::string("exception ") + e.what() + " in function " + __func__);
1032  }
1033 }
1034 
1035 } // namespace NodeStore
1036 } // namespace ripple
beast::Journal::fatal
Stream fatal() const
Definition: Journal.h:339
ripple::NodeStore::Shard::getBackend
std::shared_ptr< Backend > getBackend() const
Definition: Shard.cpp:342
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:116
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:194
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:634
std::exception
STL class.
ripple::DatabaseCon::Setup
Definition: DatabaseCon.h:85
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:920
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:254
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:155
ripple::DatabaseCon::Setup::startUp
Config::StartUpType startUp
Definition: DatabaseCon.h:89
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:668
ripple::toBase58
std::string toBase58(AccountID const &v)
Convert AccountID to base58 checked string.
Definition: AccountID.cpp:29
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:91
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:41
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
ripple::AcquireShardDBPragma
constexpr std::array< char const *, 3 > AcquireShardDBPragma
Definition: DBInit.h:118
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:37
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:390
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:288
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:62
ripple::base_uint< 256 >
ripple::NodeStore::Shard::pCache
std::shared_ptr< PCache > pCache() const
Definition: Shard.cpp:358
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:397
ripple::NodeStore::Shard::fileSz_
std::uint64_t fileSz_
Definition: Shard.h:196
ripple::Application::shardFamily
virtual Family * shardFamily()=0
ripple::NodeStore::Shard::valFetch
std::shared_ptr< NodeObject > valFetch(uint256 const &hash) const
Definition: Shard.cpp:1000
ripple::DatabaseCon::Setup::standAlone
bool standAlone
Definition: DatabaseCon.h:90
ripple::LgrDBPragma
constexpr std::array< char const *, 3 > LgrDBPragma
Definition: DBInit.h:32
ripple::base_uint::isZero
bool isZero() const
Definition: base_uint.h:475
ripple::NodeStore::Shard::sweep
void sweep()
Definition: Shard.cpp:320
ripple::SerialIter::get256
uint256 get256()
Definition: Serializer.h:374
ripple::Config
Definition: Config.h:66
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:383
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:351
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::NodeStore::Shard::prepare
boost::optional< std::uint32_t > prepare()
Definition: Shard.cpp:234
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::NodeStore::Shard::setFileStats
void setFileStats(std::lock_guard< std::recursive_mutex > const &lock)
Definition: Shard.cpp:896
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:376
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:133
ripple::TxDBInit
constexpr std::array< char const *, 8 > TxDBInit
Definition: DBInit.h:82
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:306
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:367
ripple::TxDBPragma
constexpr std::array< char const *, 5 > TxDBPragma
Definition: DBInit.h:71
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:333
ripple::NodeStore::Shard::storeSQLite
bool storeSQLite(std::shared_ptr< Ledger const > const &ledger, std::lock_guard< std::recursive_mutex > const &lock)
Definition: Shard.cpp:728
ripple::LgrDBName
constexpr auto LgrDBName
Definition: DBInit.h:30