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>
28 #include <boost/algorithm/string.hpp>
29 #include <boost/range/adaptor/transformed.hpp>
41 :
Shard(app, db, index,
"", j)
49 boost::filesystem::path
const& dir,
54 , firstSeq_(db.firstLedgerSeq(index))
55 , lastSeq_(
std::max(firstSeq_, db.lastLedgerSeq(index)))
57 index == db.earliestShardIndex() ? lastSeq_ - firstSeq_ + 1
58 : db.ledgersPerShard())
59 , dir_((dir.empty() ? db.getRootDir() : dir) /
std::
to_string(index_))
74 <<
" backend in use, unable to remove directory";
87 boost::filesystem::remove_all(
dir_);
92 <<
". Exception caught in function " << __func__
93 <<
". Error: " << e.
what();
101 std::string const type{get<std::string>(section,
"type",
"nudb")};
105 JLOG(
j_.
error()) <<
"shard " <<
index_ <<
" failed to find factory for "
109 section.set(
"path",
dir_.string());
114 JLOG(
j_.
error()) <<
"shard " <<
index_ <<
" already initialized";
134 JLOG(
j_.
error()) <<
"shard " <<
index_ <<
" not initialized";
156 JLOG(
j_.
error()) <<
"shard " <<
index_ <<
" not initialized";
169 <<
". Exception caught in function " << __func__
170 <<
". Error: " << e.
what();
185 boost::optional<std::uint32_t>
191 <<
" prepare called when not acquiring";
199 <<
" missing acquire SQLite database";
214 if (nodeObject->getHash() !=
finalKey)
233 <<
". Exception caught in function " << __func__
234 <<
". Error: " << e.
what();
259 <<
". Exception caught in function " << __func__
260 <<
". Error: " << e.
what();
271 <<
"shard " <<
index_ <<
". Corrupt node object at hash "
277 <<
"shard " <<
index_ <<
". Unknown status=" << status
278 <<
" fetching node object at hash " <<
to_string(hash);
303 JLOG(
j_.
trace()) <<
"shard " <<
index_ <<
". Ledger already stored";
308 JLOG(
j_.
error()) <<
"shard " <<
index_ <<
". Source ledger sequence "
309 << srcLedger->info().seq <<
". " << msg;
314 if (srcLedger->info().hash.isZero())
315 return fail(
"Invalid hash");
316 if (srcLedger->info().accountHash.isZero())
317 return fail(
"Invalid account hash");
319 auto& srcDB{
const_cast<Database&
>(srcLedger->stateMap().family().db())};
321 return fail(
"Source and destination databases are the same");
325 return fail(
"Failed to lock backend");
329 auto storeBatch = [&]() {
331 for (
auto const& nodeObject : batch)
332 sz += nodeObject->getData().size();
341 std::string(
". Exception caught in function ") + __func__ +
342 ". Error: " + e.
what());
346 result.
count += batch.size();
356 addRaw(srcLedger->info(), s);
366 if (
auto nodeObject = srcDB.fetchNodeObject(
367 node.getHash().as_uint256(), srcLedger->info().seq))
380 if (srcLedger->stateMap().getHash().isNonZero())
382 if (!srcLedger->stateMap().isValid())
383 return fail(
"Invalid state map");
385 if (next && next->info().parentHash == srcLedger->info().hash)
387 auto have = next->stateMap().snapShot(
false);
388 srcLedger->stateMap().snapShot(
false)->visitDifferences(
392 srcLedger->stateMap().snapShot(
false)->visitNodes(visit);
394 return fail(
"Failed to store state map");
398 if (srcLedger->info().txHash.isNonZero())
400 if (!srcLedger->txMap().isValid())
401 return fail(
"Invalid transaction map");
403 srcLedger->txMap().snapShot(
false)->visitNodes(visit);
405 return fail(
"Failed to store transaction map");
408 if (!batch.
empty() && !storeBatch())
409 return fail(
"Failed to store");
424 auto const ledgerSeq{ledger->info().seq};
425 if (ledgerSeq < firstSeq_ || ledgerSeq >
lastSeq_)
427 JLOG(
j_.
error()) <<
"shard " <<
index_ <<
" invalid ledger sequence "
436 <<
" missing acquire SQLite database";
439 if (boost::icl::contains(
acquireInfo_->storedSeqs, ledgerSeq))
442 JLOG(
j_.
debug()) <<
"shard " <<
index_ <<
" ledger sequence "
443 << ledgerSeq <<
" already stored";
460 JLOG(
j_.
debug()) <<
"shard " <<
index_ <<
" stored ledger sequence "
470 if (ledgerSeq < firstSeq_ || ledgerSeq >
lastSeq_)
479 <<
" missing acquire SQLite database";
482 return boost::icl::contains(
acquireInfo_->storedSeqs, ledgerSeq);
491 std::chrono::steady_clock::time_point
523 bool const writeSQLite,
524 boost::optional<uint256>
const& expectedHash)
531 <<
"shard " <<
index <<
". " << msg
532 << (hash.isZero() ?
"" :
". Ledger hash " +
to_string(hash))
564 nodeObject->getData().data(), nodeObject->getData().size());
566 return fail(
"invalid version");
569 return fail(
"out of range ledger sequences");
572 return fail(
"invalid last ledger hash");
579 return fail(
"missing acquire SQLite database");
582 boost::optional<std::uint32_t>
index;
583 boost::optional<std::string> sHash;
584 soci::blob sociBlob(session);
585 soci::indicator blobPresent;
586 session <<
"SELECT ShardIndex, LastLedgerHash, StoredLedgerSeqs "
588 "WHERE ShardIndex = :index;",
589 soci::into(
index), soci::into(sHash),
590 soci::into(sociBlob, blobPresent), soci::use(
index_);
593 return fail(
"missing or invalid ShardIndex");
596 return fail(
"missing LastLedgerHash");
598 if (!hash.parseHex(*sHash) || hash.isZero())
599 return fail(
"invalid LastLedgerHash");
601 if (blobPresent != soci::i_ok)
602 return fail(
"missing StoredLedgerSeqs");
609 boost::icl::first(storedSeqs) !=
firstSeq_ ||
610 boost::icl::last(storedSeqs) !=
lastSeq_ ||
613 return fail(
"invalid StoredLedgerSeqs");
620 std::string(
". Exception caught in function ") + __func__ +
621 ". Error: " + e.
what());
626 if (expectedHash && *expectedHash != hash)
627 return fail(
"invalid last ledger hash");
633 auto const lastLedgerHash{hash};
636 auto const treeNodeCache{shardFamily.getTreeNodeCache(
lastSeq_)};
639 fullBelowCache->reset();
640 treeNodeCache->reset();
652 return fail(
"invalid ledger");
654 ledger = std::make_shared<Ledger>(
658 if (ledger->info().seq != ledgerSeq)
659 return fail(
"invalid ledger sequence");
660 if (ledger->info().hash != hash)
661 return fail(
"invalid ledger hash");
663 ledger->stateMap().setLedgerSeq(ledgerSeq);
664 ledger->txMap().setLedgerSeq(ledgerSeq);
665 ledger->setImmutable(config);
666 if (!ledger->stateMap().fetchRoot(
667 SHAMapHash{ledger->info().accountHash},
nullptr))
669 return fail(
"missing root STATE node");
671 if (ledger->info().txHash.isNonZero() &&
672 !ledger->txMap().fetchRoot(
675 return fail(
"missing root TXN node");
679 return fail(
"failed to validate ledger");
685 return fail(
"failed storing to SQLite databases");
688 hash = ledger->info().parentHash;
689 next = std::move(ledger);
692 fullBelowCache->reset();
693 treeNodeCache->reset();
754 return fail(
"failed to initialize SQLite databases");
762 std::string(
". Exception caught in function ") + __func__ +
763 ". Error: " + e.
what());
773 using namespace boost::filesystem;
775 auto preexist{
false};
776 auto fail = [
this, &preexist](
std::string const& msg) {
793 auto createAcquireInfo = [
this, &config]() {
797 setup.
startUp = config.standalone() ? config.LOAD : config.START_UP;
814 preexist = exists(
dir_);
822 <<
"INSERT INTO Shard (ShardIndex) "
823 "VALUES (:shardIndex);",
832 boost::optional<std::uint32_t>
index;
833 soci::blob sociBlob(session);
834 soci::indicator blobPresent;
836 session <<
"SELECT ShardIndex, StoredLedgerSeqs "
838 "WHERE ShardIndex = :index;",
839 soci::into(
index), soci::into(sociBlob, blobPresent),
843 return fail(
"invalid acquire SQLite database");
845 if (blobPresent == soci::i_ok)
850 return fail(
"invalid StoredLedgerSeqs");
852 if (boost::icl::first(storedSeqs) <
firstSeq_ ||
853 boost::icl::last(storedSeqs) >
lastSeq_)
855 return fail(
"invalid StoredLedgerSeqs");
871 return fail(
"incompatible, missing backend final key");
876 nodeObject->getData().data(), nodeObject->getData().size());
878 return fail(
"invalid version");
881 return fail(
"out of range ledger sequences");
884 return fail(
"invalid last ledger hash");
898 std::string(
". Exception caught in function ") + __func__ +
899 ". Error: " + e.
what());
915 setup.
startUp = config.standalone() ? config.LOAD : config.START_UP;
935 boost::format(
"PRAGMA cache_size=-%d;") %
942 boost::format(
"PRAGMA cache_size=-%d;") %
957 boost::format(
"PRAGMA cache_size=-%d;") %
968 boost::format(
"PRAGMA cache_size=-%d;") %
975 <<
". Exception caught in function " << __func__
976 <<
". Error: " << e.
what();
991 auto const ledgerSeq{ledger->info().seq};
998 soci::transaction tr(session);
1000 session <<
"DELETE FROM Transactions "
1001 "WHERE LedgerSeq = :seq;",
1002 soci::use(ledgerSeq);
1003 session <<
"DELETE FROM AccountTransactions "
1004 "WHERE LedgerSeq = :seq;",
1005 soci::use(ledgerSeq);
1007 if (ledger->info().txHash.isNonZero())
1010 if (!ledger->txMap().isValid())
1013 <<
" has an invalid transaction map"
1014 <<
" on sequence " << sSeq;
1018 for (
auto const& item : ledger->txs)
1023 auto const txID{item.first->getTransactionID()};
1025 auto const txMeta{std::make_shared<TxMeta>(
1026 txID, ledger->seq(), *item.second)};
1028 session <<
"DELETE FROM AccountTransactions "
1029 "WHERE TransID = :txID;",
1032 auto const& accounts = txMeta->getAffectedAccounts(
j_);
1033 if (!accounts.empty())
1036 auto const s{boost::str(
1037 boost::format(
"('%s','%s',%s,%s)") % sTxID %
"%s" %
1040 sql.
reserve((accounts.size() + 1) * 128);
1042 "INSERT INTO AccountTransactions "
1043 "(TransID, Account, LedgerSeq, TxnSeq) VALUES ";
1044 sql += boost::algorithm::join(
1046 boost::adaptors::transformed(
1057 <<
" account transaction: " << sql;
1062 <<
"shard " <<
index_ <<
" transaction in ledger "
1063 << sSeq <<
" affects no accounts";
1067 item.second->add(s);
1070 item.first->getMetaSQL(
1079 auto const sHash{
to_string(ledger->info().hash)};
1084 soci::transaction tr(session);
1086 auto const sParentHash{
to_string(ledger->info().parentHash)};
1087 auto const sDrops{
to_string(ledger->info().drops)};
1088 auto const sAccountHash{
to_string(ledger->info().accountHash)};
1089 auto const sTxHash{
to_string(ledger->info().txHash)};
1091 session <<
"DELETE FROM Ledgers "
1092 "WHERE LedgerSeq = :seq;",
1093 soci::use(ledgerSeq);
1095 <<
"INSERT OR REPLACE INTO Ledgers ("
1096 "LedgerHash, LedgerSeq, PrevHash, TotalCoins, ClosingTime,"
1097 "PrevClosingTime, CloseTimeRes, CloseFlags, AccountSetHash,"
1100 ":ledgerHash, :ledgerSeq, :prevHash, :totalCoins,"
1101 ":closingTime, :prevClosingTime, :closeTimeRes,"
1102 ":closeFlags, :accountSetHash, :transSetHash);",
1103 soci::use(sHash), soci::use(ledgerSeq), soci::use(sParentHash),
1105 soci::use(ledger->info().closeTime.time_since_epoch().count()),
1107 ledger->info().parentCloseTime.time_since_epoch().count()),
1108 soci::use(ledger->info().closeTimeResolution.count()),
1109 soci::use(ledger->info().closeFlags), soci::use(sAccountHash),
1119 soci::blob sociBlob(session);
1124 if (ledger->info().seq ==
lastSeq_)
1127 session <<
"UPDATE Shard "
1128 "SET LastLedgerHash = :lastLedgerHash,"
1129 "StoredLedgerSeqs = :storedLedgerSeqs "
1130 "WHERE ShardIndex = :shardIndex;",
1131 soci::use(sHash), soci::use(sociBlob), soci::use(
index_);
1135 session <<
"UPDATE Shard "
1136 "SET StoredLedgerSeqs = :storedLedgerSeqs "
1137 "WHERE ShardIndex = :shardIndex;",
1138 soci::use(sociBlob), soci::use(
index_);
1145 <<
". Exception caught in function " << __func__
1146 <<
". Error: " << e.
what();
1160 using namespace boost::filesystem;
1161 for (
auto const& d : directory_iterator(
dir_))
1163 if (is_regular_file(d))
1173 <<
". Exception caught in function " << __func__
1174 <<
". Error: " << e.
what();
1184 JLOG(j.error()) <<
"shard " <<
index <<
". " << msg
1185 << (ledger->info().hash.isZero() ?
""
1186 :
". Ledger hash " +
1188 << (ledger->info().seq == 0 ?
""
1189 :
". Ledger sequence " +
1194 if (ledger->info().hash.isZero())
1195 return fail(
"Invalid ledger hash");
1196 if (ledger->info().accountHash.isZero())
1197 return fail(
"Invalid ledger account hash");
1209 if (ledger->stateMap().getHash().isNonZero())
1211 if (!ledger->stateMap().isValid())
1212 return fail(
"Invalid state map");
1216 if (next && next->info().parentHash == ledger->info().hash)
1217 ledger->stateMap().visitDifferences(&next->stateMap(), visit);
1219 ledger->stateMap().visitNodes(visit);
1224 std::string(
". Exception caught in function ") + __func__ +
1225 ". Error: " + e.
what());
1231 return fail(
"Invalid state map");
1235 if (ledger->info().txHash.isNonZero())
1237 if (!ledger->txMap().isValid())
1238 return fail(
"Invalid transaction map");
1242 ledger->txMap().visitNodes(visit);
1247 std::string(
". Exception caught in function ") + __func__ +
1248 ". Error: " + e.
what());
1253 return fail(
"Invalid transaction map");
1265 JLOG(j.error()) <<
"shard " <<
index <<
". " << msg
1266 <<
". Node object hash " <<
to_string(hash);
1273 switch (
backend_->fetch(hash.data(), &nodeObject))
1277 if (nodeObject->getHash() !=
1279 return fail(
"Node object hash does not match payload");
1282 return fail(
"Missing node object");
1284 return fail(
"Corrupt node object");
1286 return fail(
"Unknown error");
1292 std::string(
". Exception caught in function ") + __func__ +
1293 ". Error: " + e.
what());
1306 JLOG(
j_.
error()) <<
"shard " <<
index_ <<
" not initialized";
1314 else if (
state_ ==
final)