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