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,
53 , firstSeq_(db.firstLedgerSeq(index))
54 , lastSeq_(
std::max(firstSeq_, db.lastLedgerSeq(index)))
56 index == db.earliestShardIndex() ? lastSeq_ - firstSeq_ + 1
57 : db.ledgersPerShard())
58 , dir_((dir.empty() ? db.getRootDir() : dir) /
std::
to_string(index_))
62 Throw<std::runtime_error>(
"Shard: Invalid index");
76 boost::filesystem::remove_all(
dir_);
81 <<
" in function " << __func__;
95 std::string const type{get<std::string>(section,
"type",
"nudb")};
100 <<
" failed to create backend type " << type;
104 section.set(
"path",
dir_.string());
109 using namespace boost::filesystem;
110 auto preexist{
false};
111 auto fail = [
this, &preexist](
std::string const& msg) {
129 auto createAcquireInfo = [
this, &config]() {
133 setup.
startUp = config.START_UP;
149 preexist = exists(
dir_);
157 <<
"INSERT INTO Shard (ShardIndex) "
158 "VALUES (:shardIndex);",
167 boost::optional<std::uint32_t>
index;
168 soci::blob sociBlob(session);
169 soci::indicator blobPresent;
171 session <<
"SELECT ShardIndex, StoredLedgerSeqs "
173 "WHERE ShardIndex = :index;",
174 soci::into(
index), soci::into(sociBlob, blobPresent),
178 return fail(
"invalid acquire SQLite database");
180 if (blobPresent == soci::i_ok)
185 return fail(
"invalid StoredLedgerSeqs");
187 if (boost::icl::first(storedSeqs) <
firstSeq_ ||
188 boost::icl::last(storedSeqs) >
lastSeq_)
190 return fail(
"invalid StoredLedgerSeqs");
205 return fail(
"incompatible, missing backend final key");
209 SerialIter sIt(nObj->getData().data(), nObj->getData().size());
211 return fail(
"invalid version");
214 return fail(
"out of range ledger sequences");
217 return fail(
"invalid last ledger hash");
258 boost::optional<std::uint32_t>
267 <<
" prepare called when shard backend is complete";
273 if (storedSeqs.empty())
281 auto const seq{ledger->info().seq};
282 if (seq < firstSeq_ || seq >
lastSeq_)
284 JLOG(
j_.
error()) <<
"shard " <<
index_ <<
" invalid ledger sequence "
294 JLOG(
j_.
debug()) <<
"shard " <<
index_ <<
" ledger sequence " << seq
295 <<
" already stored";
301 if (boost::icl::contains(storedSeqs, seq))
303 JLOG(
j_.
debug()) <<
"shard " <<
index_ <<
" ledger sequence " << seq
304 <<
" already stored";
308 storedSeqs.insert(seq);
321 JLOG(
j_.
debug()) <<
"shard " <<
index_ <<
" stored ledger sequence " << seq
331 if (seq < firstSeq_ || seq >
lastSeq_)
339 return boost::icl::contains(
acquireInfo_->storedSeqs, seq);
421 bool const writeSQLite,
422 boost::optional<uint256>
const& expectedHash)
434 <<
"shard " <<
index <<
". " << msg
435 << (hash.isZero() ?
"" :
". Ledger hash " +
to_string(hash))
444 return fail(
"backend incomplete");
463 SerialIter sIt(nObj->getData().data(), nObj->getData().size());
465 return fail(
"invalid version");
468 return fail(
"out of range ledger sequences");
471 return fail(
"invalid last ledger hash");
479 return fail(
"missing acquire SQLite database");
482 boost::optional<std::uint32_t>
index;
483 boost::optional<std::string> sHash;
484 soci::blob sociBlob(session);
485 soci::indicator blobPresent;
486 session <<
"SELECT ShardIndex, LastLedgerHash, StoredLedgerSeqs "
488 "WHERE ShardIndex = :index;",
489 soci::into(
index), soci::into(sHash),
490 soci::into(sociBlob, blobPresent), soci::use(
index_);
494 return fail(
"missing or invalid ShardIndex");
497 return fail(
"missing LastLedgerHash");
499 if (hash.SetHexExact(*sHash); hash.isZero())
500 return fail(
"invalid LastLedgerHash");
502 if (blobPresent != soci::i_ok)
503 return fail(
"missing StoredLedgerSeqs");
512 boost::icl::first(storedSeqs) !=
firstSeq_ ||
513 boost::icl::last(storedSeqs) !=
lastSeq_ ||
516 return fail(
"invalid StoredLedgerSeqs");
528 if (expectedHash && *expectedHash != hash)
529 return fail(
"invalid last ledger hash");
534 auto const lastLedgerHash{hash};
537 auto const treeNodeCache{shardFamily.getTreeNodeCache(
lastSeq_)};
542 fullBelowCache->reset();
543 treeNodeCache->reset();
555 return fail(
"invalid ledger");
557 ledger = std::make_shared<Ledger>(
561 if (ledger->info().seq != seq)
562 return fail(
"invalid ledger sequence");
563 if (ledger->info().hash != hash)
564 return fail(
"invalid ledger hash");
566 ledger->stateMap().setLedgerSeq(seq);
567 ledger->txMap().setLedgerSeq(seq);
569 if (!ledger->stateMap().fetchRoot(
570 SHAMapHash{ledger->info().accountHash},
nullptr))
572 return fail(
"missing root STATE node");
574 if (ledger->info().txHash.isNonZero() &&
575 !ledger->txMap().fetchRoot(
578 return fail(
"missing root TXN node");
582 return fail(
"failed to validate ledger");
588 return fail(
"failed storing to SQLite databases");
591 hash = ledger->info().parentHash;
592 next = std::move(ledger);
597 fullBelowCache->reset();
598 treeNodeCache->reset();
655 return fail(
"failed to initialize SQLite databases");
675 result.
startUp = config.START_UP;
695 boost::format(
"PRAGMA cache_size=-%d;") %
702 boost::format(
"PRAGMA cache_size=-%d;") %
717 boost::format(
"PRAGMA cache_size=-%d;") %
728 boost::format(
"PRAGMA cache_size=-%d;") %
735 <<
" in function " << __func__;
749 auto const seq{ledger->info().seq};
756 soci::transaction tr(session);
758 session <<
"DELETE FROM Transactions "
759 "WHERE LedgerSeq = :seq;",
761 session <<
"DELETE FROM AccountTransactions "
762 "WHERE LedgerSeq = :seq;",
765 if (ledger->info().txHash.isNonZero())
768 if (!ledger->txMap().isValid())
771 <<
" has an invalid transaction map"
772 <<
" on sequence " << sSeq;
776 for (
auto const& item : ledger->txs)
781 auto const txID{item.first->getTransactionID()};
783 auto const txMeta{std::make_shared<TxMeta>(
784 txID, ledger->seq(), *item.second)};
786 session <<
"DELETE FROM AccountTransactions "
787 "WHERE TransID = :txID;",
790 auto const& accounts = txMeta->getAffectedAccounts(
j_);
791 if (!accounts.empty())
794 auto const s{boost::str(
795 boost::format(
"('%s','%s',%s,%s)") % sTxID %
"%s" %
798 sql.
reserve((accounts.size() + 1) * 128);
800 "INSERT INTO AccountTransactions "
801 "(TransID, Account, LedgerSeq, TxnSeq) VALUES ";
802 sql += boost::algorithm::join(
804 boost::adaptors::transformed(
815 <<
" account transaction: " << sql;
820 <<
"shard " <<
index_ <<
" transaction in ledger "
821 << sSeq <<
" affects no accounts";
828 item.first->getMetaSQL(
837 auto const sHash{
to_string(ledger->info().hash)};
842 soci::transaction tr(session);
844 auto const sParentHash{
to_string(ledger->info().parentHash)};
845 auto const sDrops{
to_string(ledger->info().drops)};
846 auto const sAccountHash{
to_string(ledger->info().accountHash)};
847 auto const sTxHash{
to_string(ledger->info().txHash)};
849 session <<
"DELETE FROM Ledgers "
850 "WHERE LedgerSeq = :seq;",
853 <<
"INSERT OR REPLACE INTO Ledgers ("
854 "LedgerHash, LedgerSeq, PrevHash, TotalCoins, ClosingTime,"
855 "PrevClosingTime, CloseTimeRes, CloseFlags, AccountSetHash,"
858 ":ledgerHash, :ledgerSeq, :prevHash, :totalCoins,"
859 ":closingTime, :prevClosingTime, :closeTimeRes,"
860 ":closeFlags, :accountSetHash, :transSetHash);",
861 soci::use(sHash), soci::use(seq), soci::use(sParentHash),
863 soci::use(ledger->info().closeTime.time_since_epoch().count()),
865 ledger->info().parentCloseTime.time_since_epoch().count()),
866 soci::use(ledger->info().closeTimeResolution.count()),
867 soci::use(ledger->info().closeFlags), soci::use(sAccountHash),
877 soci::blob sociBlob(session);
885 session <<
"UPDATE Shard "
886 "SET LastLedgerHash = :lastLedgerHash,"
887 "StoredLedgerSeqs = :storedLedgerSeqs "
888 "WHERE ShardIndex = :shardIndex;",
889 soci::use(sHash), soci::use(sociBlob), soci::use(
index_);
893 session <<
"UPDATE Shard "
894 "SET StoredLedgerSeqs = :storedLedgerSeqs "
895 "WHERE ShardIndex = :shardIndex;",
896 soci::use(sociBlob), soci::use(
index_);
903 <<
" in function " << __func__;
916 using namespace boost::filesystem;
917 for (
auto const& d : directory_iterator(
dir_))
919 if (is_regular_file(d))
929 <<
" in function " << __func__;
939 JLOG(j.fatal()) <<
"shard " <<
index <<
". " << msg
940 << (ledger->info().hash.isZero() ?
""
943 << (ledger->info().seq == 0 ?
""
944 :
". Ledger sequence " +
949 if (ledger->info().hash.isZero())
950 return fail(
"Invalid ledger hash");
951 if (ledger->info().accountHash.isZero())
952 return fail(
"Invalid ledger account hash");
958 if (!
valFetch(node.getNodeHash().as_uint256()))
964 if (ledger->stateMap().getHash().isNonZero())
966 if (!ledger->stateMap().isValid())
967 return fail(
"Invalid state map");
971 if (next && next->info().parentHash == ledger->info().hash)
972 ledger->stateMap().visitDifferences(&next->stateMap(), visit);
974 ledger->stateMap().visitNodes(visit);
985 return fail(
"Invalid state map");
989 if (ledger->info().txHash.isNonZero())
991 if (!ledger->txMap().isValid())
992 return fail(
"Invalid transaction map");
996 ledger->txMap().visitNodes(visit);
1007 return fail(
"Invalid transaction map");
1018 JLOG(j.fatal()) <<
"shard " <<
index <<
". " << msg
1019 <<
". Node object hash " <<
to_string(hash);
1026 switch (
backend_->fetch(hash.data(), &nObj))
1032 return fail(
"Node object hash does not match payload");
1035 return fail(
"Missing node object");
1037 return fail(
"Corrupt node object");
1039 return fail(
"Unknown error");