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");
199 return fail(
"incompatible, missing backend final key");
203 SerialIter sIt(nObj->getData().data(), nObj->getData().size());
205 return fail(
"invalid version");
208 return fail(
"out of range ledger sequences");
211 return fail(
"invalid last ledger hash");
233 boost::optional<std::uint32_t>
242 <<
" prepare called when shard is complete";
248 if (storedSeqs.empty())
256 auto const seq{ledger->info().seq};
257 if (seq < firstSeq_ || seq >
lastSeq_)
259 JLOG(
j_.
error()) <<
"shard " <<
index_ <<
" invalid ledger sequence "
269 JLOG(
j_.
debug()) <<
"shard " <<
index_ <<
" ledger sequence " << seq
270 <<
" already stored";
276 if (boost::icl::contains(storedSeqs, seq))
278 JLOG(
j_.
debug()) <<
"shard " <<
index_ <<
" ledger sequence " << seq
279 <<
" already stored";
283 storedSeqs.insert(seq);
298 JLOG(
j_.
debug()) <<
"shard " <<
index_ <<
" stored ledger sequence " << seq
308 if (seq < firstSeq_ || seq >
lastSeq_)
316 return boost::icl::contains(
acquireInfo_->storedSeqs, seq);
398 bool const writeSQLite,
399 boost::optional<uint256>
const& expectedHash)
411 <<
"shard " <<
index <<
". " << msg
412 << (hash.isZero() ?
"" :
". Ledger hash " +
to_string(hash))
421 return fail(
"incomplete");
440 SerialIter sIt(nObj->getData().data(), nObj->getData().size());
442 return fail(
"invalid version");
445 return fail(
"out of range ledger sequences");
448 return fail(
"invalid last ledger hash");
456 return fail(
"missing acquire SQLite database");
459 boost::optional<std::uint32_t>
index;
460 boost::optional<std::string> sHash;
461 soci::blob sociBlob(session);
462 soci::indicator blobPresent;
463 session <<
"SELECT ShardIndex, LastLedgerHash, StoredLedgerSeqs "
465 "WHERE ShardIndex = :index;",
466 soci::into(
index), soci::into(sHash),
467 soci::into(sociBlob, blobPresent), soci::use(
index_);
471 return fail(
"missing or invalid ShardIndex");
474 return fail(
"missing LastLedgerHash");
476 if (hash.SetHexExact(*sHash); hash.isZero())
477 return fail(
"invalid LastLedgerHash");
479 if (blobPresent != soci::i_ok)
480 return fail(
"missing StoredLedgerSeqs");
489 boost::icl::first(storedSeqs) !=
firstSeq_ ||
490 boost::icl::last(storedSeqs) !=
lastSeq_ ||
493 return fail(
"invalid StoredLedgerSeqs");
505 if (expectedHash && *expectedHash != hash)
506 return fail(
"invalid last ledger hash");
511 auto const lastLedgerHash{hash};
523 return fail(
"invalid ledger");
525 ledger = std::make_shared<Ledger>(
529 if (ledger->info().seq != seq)
530 return fail(
"invalid ledger sequence");
531 if (ledger->info().hash != hash)
532 return fail(
"invalid ledger hash");
534 ledger->stateMap().setLedgerSeq(seq);
535 ledger->txMap().setLedgerSeq(seq);
537 if (!ledger->stateMap().fetchRoot(
538 SHAMapHash{ledger->info().accountHash},
nullptr))
540 return fail(
"missing root STATE node");
542 if (ledger->info().txHash.isNonZero() &&
543 !ledger->txMap().fetchRoot(
546 return fail(
"missing root TXN node");
550 return fail(
"failed to validate ledger");
556 return fail(
"failed storing to SQLite databases");
559 hash = ledger->info().parentHash;
620 return fail(
"failed to initialize SQLite databases");
644 auto const sz{config.getValueFor(
672 setup.
startUp = config.START_UP;
689 boost::format(
"PRAGMA cache_size=-%d;") %
696 boost::format(
"PRAGMA cache_size=-%d;") %
706 boost::format(
"PRAGMA cache_size=-%d;") %
713 boost::format(
"PRAGMA cache_size=-%d;") %
721 <<
" in function " << __func__;
735 auto const seq{ledger->info().seq};
742 soci::transaction tr(session);
744 session <<
"DELETE FROM Transactions "
745 "WHERE LedgerSeq = :seq;",
747 session <<
"DELETE FROM AccountTransactions "
748 "WHERE LedgerSeq = :seq;",
751 if (ledger->info().txHash.isNonZero())
754 if (!ledger->txMap().isValid())
757 <<
" has an invalid transaction map"
758 <<
" on sequence " << sSeq;
762 for (
auto const& item : ledger->txs)
767 auto const txID{item.first->getTransactionID()};
769 auto const txMeta{std::make_shared<TxMeta>(
770 txID, ledger->seq(), *item.second)};
772 session <<
"DELETE FROM AccountTransactions "
773 "WHERE TransID = :txID;",
776 auto const& accounts = txMeta->getAffectedAccounts(
j_);
777 if (!accounts.empty())
780 auto const s{boost::str(
781 boost::format(
"('%s','%s',%s,%s)") % sTxID %
"%s" %
784 sql.
reserve((accounts.size() + 1) * 128);
786 "INSERT INTO AccountTransactions "
787 "(TransID, Account, LedgerSeq, TxnSeq) VALUES ";
788 sql += boost::algorithm::join(
790 boost::adaptors::transformed(
801 <<
" account transaction: " << sql;
806 <<
"shard " <<
index_ <<
" transaction in ledger "
807 << sSeq <<
" affects no accounts";
814 item.first->getMetaSQL(
823 auto const sHash{
to_string(ledger->info().hash)};
828 soci::transaction tr(session);
830 auto const sParentHash{
to_string(ledger->info().parentHash)};
831 auto const sDrops{
to_string(ledger->info().drops)};
832 auto const sAccountHash{
to_string(ledger->info().accountHash)};
833 auto const sTxHash{
to_string(ledger->info().txHash)};
835 session <<
"DELETE FROM Ledgers "
836 "WHERE LedgerSeq = :seq;",
839 <<
"INSERT OR REPLACE INTO Ledgers ("
840 "LedgerHash, LedgerSeq, PrevHash, TotalCoins, ClosingTime,"
841 "PrevClosingTime, CloseTimeRes, CloseFlags, AccountSetHash,"
844 ":ledgerHash, :ledgerSeq, :prevHash, :totalCoins,"
845 ":closingTime, :prevClosingTime, :closeTimeRes,"
846 ":closeFlags, :accountSetHash, :transSetHash);",
847 soci::use(sHash), soci::use(seq), soci::use(sParentHash),
849 soci::use(ledger->info().closeTime.time_since_epoch().count()),
851 ledger->info().parentCloseTime.time_since_epoch().count()),
852 soci::use(ledger->info().closeTimeResolution.count()),
853 soci::use(ledger->info().closeFlags), soci::use(sAccountHash),
863 soci::blob sociBlob(session);
871 session <<
"UPDATE Shard "
872 "SET LastLedgerHash = :lastLedgerHash,"
873 "StoredLedgerSeqs = :storedLedgerSeqs "
874 "WHERE ShardIndex = :shardIndex;",
875 soci::use(sHash), soci::use(sociBlob), soci::use(
index_);
879 session <<
"UPDATE Shard "
880 "SET StoredLedgerSeqs = :storedLedgerSeqs "
881 "WHERE ShardIndex = :shardIndex;",
882 soci::use(sociBlob), soci::use(
index_);
889 <<
" in function " << __func__;
902 using namespace boost::filesystem;
903 for (
auto const& d : directory_iterator(
dir_))
905 if (is_regular_file(d))
915 <<
" in function " << __func__;
925 JLOG(j.fatal()) <<
"shard " <<
index <<
". " << msg
926 << (ledger->info().hash.isZero() ?
""
929 << (ledger->info().seq == 0 ?
""
930 :
". Ledger sequence " +
935 if (ledger->info().hash.isZero())
936 return fail(
"Invalid ledger hash");
937 if (ledger->info().accountHash.isZero())
938 return fail(
"Invalid ledger account hash");
944 if (!
valFetch(node.getNodeHash().as_uint256()))
950 if (ledger->stateMap().getHash().isNonZero())
952 if (!ledger->stateMap().isValid())
953 return fail(
"Invalid state map");
957 if (next && next->info().parentHash == ledger->info().hash)
958 ledger->stateMap().visitDifferences(&next->stateMap(), visit);
960 ledger->stateMap().visitNodes(visit);
971 return fail(
"Invalid state map");
975 if (ledger->info().txHash.isNonZero())
977 if (!ledger->txMap().isValid())
978 return fail(
"Invalid transaction map");
982 ledger->txMap().visitNodes(visit);
993 return fail(
"Invalid transaction map");
1004 JLOG(j.fatal()) <<
"shard " <<
index <<
". " << msg
1005 <<
". Node object hash " <<
to_string(hash);
1012 switch (
backend_->fetch(hash.data(), &nObj))
1018 return fail(
"Node object hash does not match payload");
1021 return fail(
"Missing node object");
1023 return fail(
"Corrupt node object");
1025 return fail(
"Unknown error");