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 "
445 <<
"shard " <<
index_ <<
" missing acquire SQLite database";
448 if (boost::icl::contains(
acquireInfo_->storedSeqs, ledgerSeq))
451 JLOG(
j_.
debug()) <<
"shard " <<
index_ <<
" ledger sequence "
452 << ledgerSeq <<
" already stored";
468 soci::blob sociBlob(*session);
473 auto const sHash{
to_string(ledger->info().hash)};
474 *session <<
"UPDATE Shard "
475 "SET LastLedgerHash = :lastLedgerHash,"
476 "StoredLedgerSeqs = :storedLedgerSeqs "
477 "WHERE ShardIndex = :shardIndex;",
478 soci::use(sHash), soci::use(sociBlob), soci::use(
index_);
482 *session <<
"UPDATE Shard "
483 "SET StoredLedgerSeqs = :storedLedgerSeqs "
484 "WHERE ShardIndex = :shardIndex;",
485 soci::use(sociBlob), soci::use(
index_);
491 <<
". Exception caught in function " << __func__
492 <<
". Error: " << e.
what();
501 JLOG(
j_.
trace()) <<
"shard " <<
index_ <<
" stored ledger sequence "
509 if (ledgerSeq < firstSeq_ || ledgerSeq >
lastSeq_)
518 <<
" missing acquire SQLite database";
521 return boost::icl::contains(
acquireInfo_->storedSeqs, ledgerSeq);
530 std::chrono::steady_clock::time_point
562 bool const writeSQLite,
563 boost::optional<uint256>
const& expectedHash)
569 << (hash.isZero() ?
""
571 << (ledgerSeq == 0 ?
""
572 :
". Ledger sequence " +
603 nodeObject->getData().data(), nodeObject->getData().size());
605 return fail(
"invalid version");
608 return fail(
"out of range ledger sequences");
611 return fail(
"invalid last ledger hash");
618 return fail(
"missing acquire SQLite database");
621 boost::optional<std::uint32_t>
index;
622 boost::optional<std::string> sHash;
623 soci::blob sociBlob(*session);
624 soci::indicator blobPresent;
625 *session <<
"SELECT ShardIndex, LastLedgerHash, StoredLedgerSeqs "
627 "WHERE ShardIndex = :index;",
628 soci::into(
index), soci::into(sHash),
629 soci::into(sociBlob, blobPresent), soci::use(
index_);
632 return fail(
"missing or invalid ShardIndex");
635 return fail(
"missing LastLedgerHash");
637 if (!hash.parseHex(*sHash) || hash.isZero())
638 return fail(
"invalid LastLedgerHash");
640 if (blobPresent != soci::i_ok)
641 return fail(
"missing StoredLedgerSeqs");
648 boost::icl::first(storedSeqs) !=
firstSeq_ ||
649 boost::icl::last(storedSeqs) !=
lastSeq_ ||
652 return fail(
"invalid StoredLedgerSeqs");
659 std::string(
". Exception caught in function ") + __func__ +
660 ". Error: " + e.
what());
665 if (expectedHash && *expectedHash != hash)
666 return fail(
"invalid last ledger hash");
672 auto const lastLedgerHash{hash};
675 auto const treeNodeCache{shardFamily.getTreeNodeCache(
lastSeq_)};
678 fullBelowCache->reset();
679 treeNodeCache->reset();
691 return fail(
"invalid ledger");
693 ledger = std::make_shared<Ledger>(
697 if (ledger->info().seq != ledgerSeq)
698 return fail(
"invalid ledger sequence");
699 if (ledger->info().hash != hash)
700 return fail(
"invalid ledger hash");
702 ledger->stateMap().setLedgerSeq(ledgerSeq);
703 ledger->txMap().setLedgerSeq(ledgerSeq);
704 ledger->setImmutable(config);
705 if (!ledger->stateMap().fetchRoot(
706 SHAMapHash{ledger->info().accountHash},
nullptr))
708 return fail(
"missing root STATE node");
710 if (ledger->info().txHash.isNonZero() &&
711 !ledger->txMap().fetchRoot(
714 return fail(
"missing root TXN node");
718 return fail(
"failed to validate ledger");
721 return fail(
"failed storing to SQLite databases");
723 hash = ledger->info().parentHash;
724 next = std::move(ledger);
727 fullBelowCache->reset();
728 treeNodeCache->reset();
792 return fail(
"failed to initialize SQLite databases");
799 std::string(
". Exception caught in function ") + __func__ +
800 ". Error: " + e.
what());
809 using namespace boost::filesystem;
811 auto preexist{
false};
812 auto fail = [
this, &preexist](
std::string const& msg) {
829 auto createAcquireInfo = [
this, &config]() {
833 setup.
startUp = config.standalone() ? config.LOAD : config.START_UP;
850 preexist = exists(
dir_);
858 <<
"INSERT INTO Shard (ShardIndex) "
859 "VALUES (:shardIndex);",
868 boost::optional<std::uint32_t>
index;
869 soci::blob sociBlob(session);
870 soci::indicator blobPresent;
872 session <<
"SELECT ShardIndex, StoredLedgerSeqs "
874 "WHERE ShardIndex = :index;",
875 soci::into(
index), soci::into(sociBlob, blobPresent),
879 return fail(
"invalid acquire SQLite database");
881 if (blobPresent == soci::i_ok)
886 return fail(
"invalid StoredLedgerSeqs");
888 if (boost::icl::first(storedSeqs) <
firstSeq_ ||
889 boost::icl::last(storedSeqs) >
lastSeq_)
891 return fail(
"invalid StoredLedgerSeqs");
907 return fail(
"incompatible, missing backend final key");
912 nodeObject->getData().data(), nodeObject->getData().size());
914 return fail(
"invalid version");
917 return fail(
"out of range ledger sequences");
920 return fail(
"invalid last ledger hash");
934 std::string(
". Exception caught in function ") + __func__ +
935 ". Error: " + e.
what());
951 setup.
startUp = config.standalone() ? config.LOAD : config.START_UP;
971 boost::format(
"PRAGMA cache_size=-%d;") %
978 boost::format(
"PRAGMA cache_size=-%d;") %
993 boost::format(
"PRAGMA cache_size=-%d;") %
1004 boost::format(
"PRAGMA cache_size=-%d;") %
1011 <<
". Exception caught in function " << __func__
1012 <<
". Error: " << e.
what();
1025 auto const ledgerSeq{ledger->info().seq};
1032 soci::transaction tr(*session);
1034 *session <<
"DELETE FROM Transactions "
1035 "WHERE LedgerSeq = :seq;",
1036 soci::use(ledgerSeq);
1037 *session <<
"DELETE FROM AccountTransactions "
1038 "WHERE LedgerSeq = :seq;",
1039 soci::use(ledgerSeq);
1041 if (ledger->info().txHash.isNonZero())
1044 if (!ledger->txMap().isValid())
1047 <<
" has an invalid transaction map"
1048 <<
" on sequence " << sSeq;
1052 for (
auto const& item : ledger->txs)
1057 auto const txID{item.first->getTransactionID()};
1059 auto const txMeta{std::make_shared<TxMeta>(
1060 txID, ledger->seq(), *item.second)};
1062 *session <<
"DELETE FROM AccountTransactions "
1063 "WHERE TransID = :txID;",
1066 auto const& accounts = txMeta->getAffectedAccounts(
j_);
1067 if (!accounts.empty())
1070 auto const s{boost::str(
1071 boost::format(
"('%s','%s',%s,%s)") % sTxID %
"%s" %
1074 sql.
reserve((accounts.size() + 1) * 128);
1076 "INSERT INTO AccountTransactions "
1077 "(TransID, Account, LedgerSeq, TxnSeq) VALUES ";
1078 sql += boost::algorithm::join(
1080 boost::adaptors::transformed(
1091 <<
" account transaction: " << sql;
1096 <<
"shard " <<
index_ <<
" transaction in ledger "
1097 << sSeq <<
" affects no accounts";
1101 item.second->add(s);
1104 item.first->getMetaSQL(
1115 auto const sParentHash{
to_string(ledger->info().parentHash)};
1116 auto const sDrops{
to_string(ledger->info().drops)};
1117 auto const sAccountHash{
to_string(ledger->info().accountHash)};
1118 auto const sTxHash{
to_string(ledger->info().txHash)};
1119 auto const sHash{
to_string(ledger->info().hash)};
1122 soci::transaction tr(*session);
1124 *session <<
"DELETE FROM Ledgers "
1125 "WHERE LedgerSeq = :seq;",
1126 soci::use(ledgerSeq);
1128 <<
"INSERT OR REPLACE INTO Ledgers ("
1129 "LedgerHash, LedgerSeq, PrevHash, TotalCoins, ClosingTime,"
1130 "PrevClosingTime, CloseTimeRes, CloseFlags, AccountSetHash,"
1133 ":ledgerHash, :ledgerSeq, :prevHash, :totalCoins,"
1134 ":closingTime, :prevClosingTime, :closeTimeRes,"
1135 ":closeFlags, :accountSetHash, :transSetHash);",
1136 soci::use(sHash), soci::use(ledgerSeq), soci::use(sParentHash),
1138 soci::use(ledger->info().closeTime.time_since_epoch().count()),
1140 ledger->info().parentCloseTime.time_since_epoch().count()),
1141 soci::use(ledger->info().closeTimeResolution.count()),
1142 soci::use(ledger->info().closeFlags), soci::use(sAccountHash),
1151 <<
". Exception caught in function " << __func__
1152 <<
". Error: " << e.
what();
1166 using namespace boost::filesystem;
1167 for (
auto const& d : directory_iterator(
dir_))
1169 if (is_regular_file(d))
1179 <<
". Exception caught in function " << __func__
1180 <<
". Error: " << e.
what();
1190 JLOG(j.error()) <<
"shard " <<
index <<
". " << msg
1191 << (ledger->info().hash.isZero() ?
""
1192 :
". Ledger hash " +
1194 << (ledger->info().seq == 0 ?
""
1195 :
". Ledger sequence " +
1200 if (ledger->info().hash.isZero())
1201 return fail(
"Invalid ledger hash");
1202 if (ledger->info().accountHash.isZero())
1203 return fail(
"Invalid ledger account hash");
1215 if (ledger->stateMap().getHash().isNonZero())
1217 if (!ledger->stateMap().isValid())
1218 return fail(
"Invalid state map");
1222 if (next && next->info().parentHash == ledger->info().hash)
1223 ledger->stateMap().visitDifferences(&next->stateMap(), visit);
1225 ledger->stateMap().visitNodes(visit);
1230 std::string(
". Exception caught in function ") + __func__ +
1231 ". Error: " + e.
what());
1237 return fail(
"Invalid state map");
1241 if (ledger->info().txHash.isNonZero())
1243 if (!ledger->txMap().isValid())
1244 return fail(
"Invalid transaction map");
1248 ledger->txMap().visitNodes(visit);
1253 std::string(
". Exception caught in function ") + __func__ +
1254 ". Error: " + e.
what());
1259 return fail(
"Invalid transaction map");
1271 JLOG(j.error()) <<
"shard " <<
index <<
". " << msg
1272 <<
". Node object hash " <<
to_string(hash);
1279 switch (
backend_->fetch(hash.data(), &nodeObject))
1283 if (nodeObject->getHash() !=
1285 return fail(
"Node object hash does not match payload");
1288 return fail(
"Missing node object");
1290 return fail(
"Corrupt node object");
1292 return fail(
"Unknown error");
1298 std::string(
". Exception caught in function ") + __func__ +
1299 ". Error: " + e.
what());
1312 JLOG(
j_.
error()) <<
"shard " <<
index_ <<
" not initialized";
1320 else if (
state_ ==
final)