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>
29 #include <boost/algorithm/string.hpp>
30 #include <boost/range/adaptor/transformed.hpp>
44 , firstSeq_(db.firstLedgerSeq(index))
45 , lastSeq_(
std::max(firstSeq_, db.lastLedgerSeq(index)))
47 index == db.earliestShardIndex() ? lastSeq_ - firstSeq_ + 1
48 : db.ledgersPerShard())
53 Throw<std::runtime_error>(
"Shard: Invalid index");
67 boost::filesystem::remove_all(
dir_);
72 <<
" in function " << __func__;
86 std::string const type{get<std::string>(section,
"type",
"nudb")};
91 <<
" failed to create backend type " << type;
95 section.set(
"path",
dir_.string());
100 using namespace boost::filesystem;
101 auto preexist{
false};
102 auto fail = [
this, &preexist](
std::string const& msg) {
120 auto createAcquireInfo = [
this, &config]() {
124 setup.
startUp = config.START_UP;
140 preexist = exists(
dir_);
148 <<
"INSERT INTO Shard (ShardIndex) "
149 "VALUES (:shardIndex);",
158 boost::optional<std::uint32_t>
index;
159 soci::blob sociBlob(session);
160 soci::indicator blobPresent;
162 session <<
"SELECT ShardIndex, StoredLedgerSeqs "
164 "WHERE ShardIndex = :index;",
165 soci::into(
index), soci::into(sociBlob, blobPresent),
169 return fail(
"invalid acquire SQLite database");
171 if (blobPresent == soci::i_ok)
176 return fail(
"invalid StoredLedgerSeqs");
178 if (boost::icl::first(storedSeqs) <
firstSeq_ ||
179 boost::icl::last(storedSeqs) >
lastSeq_)
181 return fail(
"invalid StoredLedgerSeqs");
196 return fail(
"incompatible, missing backend final key");
200 SerialIter sIt(nObj->getData().data(), nObj->getData().size());
202 return fail(
"invalid version");
205 return fail(
"out of range ledger sequences");
208 return fail(
"invalid last ledger hash");
230 boost::optional<std::uint32_t>
239 <<
" prepare called when shard backend is complete";
245 if (storedSeqs.empty())
253 auto const seq{ledger->info().seq};
254 if (seq < firstSeq_ || seq >
lastSeq_)
256 JLOG(
j_.
error()) <<
"shard " <<
index_ <<
" invalid ledger sequence "
266 JLOG(
j_.
debug()) <<
"shard " <<
index_ <<
" ledger sequence " << seq
267 <<
" already stored";
273 if (boost::icl::contains(storedSeqs, seq))
275 JLOG(
j_.
debug()) <<
"shard " <<
index_ <<
" ledger sequence " << seq
276 <<
" already stored";
280 storedSeqs.insert(seq);
294 JLOG(
j_.
debug()) <<
"shard " <<
index_ <<
" stored ledger sequence " << seq
304 if (seq < firstSeq_ || seq >
lastSeq_)
312 return boost::icl::contains(
acquireInfo_->storedSeqs, seq);
394 bool const writeSQLite,
395 boost::optional<uint256>
const& expectedHash)
407 <<
"shard " <<
index <<
". " << msg
408 << (hash.isZero() ?
"" :
". Ledger hash " +
to_string(hash))
417 return fail(
"backend incomplete");
436 SerialIter sIt(nObj->getData().data(), nObj->getData().size());
438 return fail(
"invalid version");
441 return fail(
"out of range ledger sequences");
444 return fail(
"invalid last ledger hash");
452 return fail(
"missing acquire SQLite database");
455 boost::optional<std::uint32_t>
index;
456 boost::optional<std::string> sHash;
457 soci::blob sociBlob(session);
458 soci::indicator blobPresent;
459 session <<
"SELECT ShardIndex, LastLedgerHash, StoredLedgerSeqs "
461 "WHERE ShardIndex = :index;",
462 soci::into(
index), soci::into(sHash),
463 soci::into(sociBlob, blobPresent), soci::use(
index_);
467 return fail(
"missing or invalid ShardIndex");
470 return fail(
"missing LastLedgerHash");
472 if (hash.SetHexExact(*sHash); hash.isZero())
473 return fail(
"invalid LastLedgerHash");
475 if (blobPresent != soci::i_ok)
476 return fail(
"missing StoredLedgerSeqs");
485 boost::icl::first(storedSeqs) !=
firstSeq_ ||
486 boost::icl::last(storedSeqs) !=
lastSeq_ ||
489 return fail(
"invalid StoredLedgerSeqs");
501 if (expectedHash && *expectedHash != hash)
502 return fail(
"invalid last ledger hash");
507 auto const lastLedgerHash{hash};
519 return fail(
"invalid ledger");
521 ledger = std::make_shared<Ledger>(
525 if (ledger->info().seq != seq)
526 return fail(
"invalid ledger sequence");
527 if (ledger->info().hash != hash)
528 return fail(
"invalid ledger hash");
530 ledger->stateMap().setLedgerSeq(seq);
531 ledger->txMap().setLedgerSeq(seq);
533 if (!ledger->stateMap().fetchRoot(
534 SHAMapHash{ledger->info().accountHash},
nullptr))
536 return fail(
"missing root STATE node");
538 if (ledger->info().txHash.isNonZero() &&
539 !ledger->txMap().fetchRoot(
542 return fail(
"missing root TXN node");
546 return fail(
"failed to validate ledger");
552 return fail(
"failed storing to SQLite databases");
555 hash = ledger->info().parentHash;
556 next = std::move(ledger);
616 return fail(
"failed to initialize SQLite databases");
640 auto const sz{config.getValueFor(
669 result.
startUp = config.START_UP;
689 boost::format(
"PRAGMA cache_size=-%d;") %
696 boost::format(
"PRAGMA cache_size=-%d;") %
711 boost::format(
"PRAGMA cache_size=-%d;") %
722 boost::format(
"PRAGMA cache_size=-%d;") %
729 <<
" in function " << __func__;
743 auto const seq{ledger->info().seq};
750 soci::transaction tr(session);
752 session <<
"DELETE FROM Transactions "
753 "WHERE LedgerSeq = :seq;",
755 session <<
"DELETE FROM AccountTransactions "
756 "WHERE LedgerSeq = :seq;",
759 if (ledger->info().txHash.isNonZero())
762 if (!ledger->txMap().isValid())
765 <<
" has an invalid transaction map"
766 <<
" on sequence " << sSeq;
770 for (
auto const& item : ledger->txs)
775 auto const txID{item.first->getTransactionID()};
777 auto const txMeta{std::make_shared<TxMeta>(
778 txID, ledger->seq(), *item.second)};
780 session <<
"DELETE FROM AccountTransactions "
781 "WHERE TransID = :txID;",
784 auto const& accounts = txMeta->getAffectedAccounts(
j_);
785 if (!accounts.empty())
788 auto const s{boost::str(
789 boost::format(
"('%s','%s',%s,%s)") % sTxID %
"%s" %
792 sql.
reserve((accounts.size() + 1) * 128);
794 "INSERT INTO AccountTransactions "
795 "(TransID, Account, LedgerSeq, TxnSeq) VALUES ";
796 sql += boost::algorithm::join(
798 boost::adaptors::transformed(
809 <<
" account transaction: " << sql;
814 <<
"shard " <<
index_ <<
" transaction in ledger "
815 << sSeq <<
" affects no accounts";
822 item.first->getMetaSQL(
831 auto const sHash{
to_string(ledger->info().hash)};
836 soci::transaction tr(session);
838 auto const sParentHash{
to_string(ledger->info().parentHash)};
839 auto const sDrops{
to_string(ledger->info().drops)};
840 auto const sAccountHash{
to_string(ledger->info().accountHash)};
841 auto const sTxHash{
to_string(ledger->info().txHash)};
843 session <<
"DELETE FROM Ledgers "
844 "WHERE LedgerSeq = :seq;",
847 <<
"INSERT OR REPLACE INTO Ledgers ("
848 "LedgerHash, LedgerSeq, PrevHash, TotalCoins, ClosingTime,"
849 "PrevClosingTime, CloseTimeRes, CloseFlags, AccountSetHash,"
852 ":ledgerHash, :ledgerSeq, :prevHash, :totalCoins,"
853 ":closingTime, :prevClosingTime, :closeTimeRes,"
854 ":closeFlags, :accountSetHash, :transSetHash);",
855 soci::use(sHash), soci::use(seq), soci::use(sParentHash),
857 soci::use(ledger->info().closeTime.time_since_epoch().count()),
859 ledger->info().parentCloseTime.time_since_epoch().count()),
860 soci::use(ledger->info().closeTimeResolution.count()),
861 soci::use(ledger->info().closeFlags), soci::use(sAccountHash),
871 soci::blob sociBlob(session);
879 session <<
"UPDATE Shard "
880 "SET LastLedgerHash = :lastLedgerHash,"
881 "StoredLedgerSeqs = :storedLedgerSeqs "
882 "WHERE ShardIndex = :shardIndex;",
883 soci::use(sHash), soci::use(sociBlob), soci::use(
index_);
887 session <<
"UPDATE Shard "
888 "SET StoredLedgerSeqs = :storedLedgerSeqs "
889 "WHERE ShardIndex = :shardIndex;",
890 soci::use(sociBlob), soci::use(
index_);
897 <<
" in function " << __func__;
910 using namespace boost::filesystem;
911 for (
auto const& d : directory_iterator(
dir_))
913 if (is_regular_file(d))
923 <<
" in function " << __func__;
933 JLOG(j.fatal()) <<
"shard " <<
index <<
". " << msg
934 << (ledger->info().hash.isZero() ?
""
937 << (ledger->info().seq == 0 ?
""
938 :
". Ledger sequence " +
943 if (ledger->info().hash.isZero())
944 return fail(
"Invalid ledger hash");
945 if (ledger->info().accountHash.isZero())
946 return fail(
"Invalid ledger account hash");
952 if (!
valFetch(node.getNodeHash().as_uint256()))
958 if (ledger->stateMap().getHash().isNonZero())
960 if (!ledger->stateMap().isValid())
961 return fail(
"Invalid state map");
965 if (next && next->info().parentHash == ledger->info().hash)
966 ledger->stateMap().visitDifferences(&next->stateMap(), visit);
968 ledger->stateMap().visitNodes(visit);
979 return fail(
"Invalid state map");
983 if (ledger->info().txHash.isNonZero())
985 if (!ledger->txMap().isValid())
986 return fail(
"Invalid transaction map");
990 ledger->txMap().visitNodes(visit);
1001 return fail(
"Invalid transaction map");
1012 JLOG(j.fatal()) <<
"shard " <<
index <<
". " << msg
1013 <<
". Node object hash " <<
to_string(hash);
1020 switch (
backend_->fetch(hash.data(), &nObj))
1026 return fail(
"Node object hash does not match payload");
1029 return fail(
"Missing node object");
1031 return fail(
"Corrupt node object");
1033 return fail(
"Unknown error");