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>
45 , firstSeq_(db.firstLedgerSeq(index))
46 , lastSeq_(
std::max(firstSeq_, db.lastLedgerSeq(index)))
48 index == db.earliestShardIndex() ? lastSeq_ - firstSeq_ + 1
49 : db.ledgersPerShard())
54 Throw<std::runtime_error>(
"Shard: Invalid index");
68 boost::filesystem::remove_all(
dir_);
73 <<
" in function " << __func__;
87 std::string const type{get<std::string>(section,
"type",
"nudb")};
92 <<
" failed to create backend type " << type;
96 section.set(
"path",
dir_.string());
101 using namespace boost::filesystem;
102 auto preexist{
false};
103 auto fail = [
this, &preexist](
std::string const& msg) {
121 auto createAcquireInfo = [
this, &config]() {
125 setup.
startUp = config.START_UP;
142 preexist = exists(
dir_);
150 <<
"INSERT INTO Shard (ShardIndex) "
151 "VALUES (:shardIndex);",
160 boost::optional<std::uint32_t>
index;
161 soci::blob sociBlob(session);
162 soci::indicator blobPresent;
164 session <<
"SELECT ShardIndex, StoredLedgerSeqs "
166 "WHERE ShardIndex = :index;",
167 soci::into(
index), soci::into(sociBlob, blobPresent),
171 return fail(
"invalid acquire SQLite database");
173 if (blobPresent == soci::i_ok)
178 return fail(
"invalid StoredLedgerSeqs");
180 if (boost::icl::first(storedSeqs) <
firstSeq_ ||
181 boost::icl::last(storedSeqs) >
lastSeq_)
183 return fail(
"invalid StoredLedgerSeqs");
198 return fail(
"incompatible, missing backend final key");
202 SerialIter sIt(nObj->getData().data(), nObj->getData().size());
204 return fail(
"invalid version");
207 return fail(
"out of range ledger sequences");
210 return fail(
"invalid last ledger hash");
232 boost::optional<std::uint32_t>
241 <<
" prepare called when shard backend is complete";
247 if (storedSeqs.empty())
255 auto const seq{ledger->info().seq};
256 if (seq < firstSeq_ || seq >
lastSeq_)
258 JLOG(
j_.
error()) <<
"shard " <<
index_ <<
" invalid ledger sequence "
268 JLOG(
j_.
debug()) <<
"shard " <<
index_ <<
" ledger sequence " << seq
269 <<
" already stored";
275 if (boost::icl::contains(storedSeqs, seq))
277 JLOG(
j_.
debug()) <<
"shard " <<
index_ <<
" ledger sequence " << seq
278 <<
" already stored";
282 storedSeqs.insert(seq);
296 JLOG(
j_.
debug()) <<
"shard " <<
index_ <<
" stored ledger sequence " << seq
306 if (seq < firstSeq_ || seq >
lastSeq_)
314 return boost::icl::contains(
acquireInfo_->storedSeqs, seq);
396 bool const writeSQLite,
397 boost::optional<uint256>
const& expectedHash,
398 const bool writeDeterministicShard)
410 <<
"shard " <<
index <<
". " << msg
411 << (hash.isZero() ?
"" :
". Ledger hash " +
to_string(hash))
420 return fail(
"backend incomplete");
439 SerialIter sIt(nObj->getData().data(), nObj->getData().size());
441 return fail(
"invalid version");
444 return fail(
"out of range ledger sequences");
447 return fail(
"invalid last ledger hash");
455 return fail(
"missing acquire SQLite database");
458 boost::optional<std::uint32_t>
index;
459 boost::optional<std::string> sHash;
460 soci::blob sociBlob(session);
461 soci::indicator blobPresent;
462 session <<
"SELECT ShardIndex, LastLedgerHash, StoredLedgerSeqs "
464 "WHERE ShardIndex = :index;",
465 soci::into(
index), soci::into(sHash),
466 soci::into(sociBlob, blobPresent), soci::use(
index_);
470 return fail(
"missing or invalid ShardIndex");
473 return fail(
"missing LastLedgerHash");
475 if (hash.SetHexExact(*sHash); hash.isZero())
476 return fail(
"invalid LastLedgerHash");
478 if (blobPresent != soci::i_ok)
479 return fail(
"missing StoredLedgerSeqs");
488 boost::icl::first(storedSeqs) !=
firstSeq_ ||
489 boost::icl::last(storedSeqs) !=
lastSeq_ ||
492 return fail(
"invalid StoredLedgerSeqs");
504 if (expectedHash && *expectedHash != hash)
505 return fail(
"invalid last ledger hash");
510 auto const lastLedgerHash{hash};
513 if (writeDeterministicShard)
515 dsh = std::make_shared<DeterministicShard>(
519 return fail(
"can't create deterministic shard");
533 return fail(
"invalid ledger");
535 ledger = std::make_shared<Ledger>(
539 if (ledger->info().seq != seq)
540 return fail(
"invalid ledger sequence");
541 if (ledger->info().hash != hash)
542 return fail(
"invalid ledger hash");
544 ledger->stateMap().setLedgerSeq(seq);
545 ledger->txMap().setLedgerSeq(seq);
547 if (!ledger->stateMap().fetchRoot(
548 SHAMapHash{ledger->info().accountHash},
nullptr))
550 return fail(
"missing root STATE node");
552 if (ledger->info().txHash.isNonZero() &&
553 !ledger->txMap().fetchRoot(
556 return fail(
"missing root TXN node");
563 return fail(
"verification check failed");
569 return fail(
"failed storing to SQLite databases");
572 hash = ledger->info().parentHash;
573 next = std::move(ledger);
639 return fail(
"failed to initialize SQLite databases");
663 return finalize(
false, expectedHash,
false);
680 auto const sz{config.getValueFor(
709 result.
startUp = config.START_UP;
729 boost::format(
"PRAGMA cache_size=-%d;") %
736 boost::format(
"PRAGMA cache_size=-%d;") %
746 boost::format(
"PRAGMA cache_size=-%d;") %
753 boost::format(
"PRAGMA cache_size=-%d;") %
761 <<
" in function " << __func__;
775 auto const seq{ledger->info().seq};
782 soci::transaction tr(session);
784 session <<
"DELETE FROM Transactions "
785 "WHERE LedgerSeq = :seq;",
787 session <<
"DELETE FROM AccountTransactions "
788 "WHERE LedgerSeq = :seq;",
791 if (ledger->info().txHash.isNonZero())
794 if (!ledger->txMap().isValid())
797 <<
" has an invalid transaction map"
798 <<
" on sequence " << sSeq;
802 for (
auto const& item : ledger->txs)
807 auto const txID{item.first->getTransactionID()};
809 auto const txMeta{std::make_shared<TxMeta>(
810 txID, ledger->seq(), *item.second)};
812 session <<
"DELETE FROM AccountTransactions "
813 "WHERE TransID = :txID;",
816 auto const& accounts = txMeta->getAffectedAccounts(
j_);
817 if (!accounts.empty())
820 auto const s{boost::str(
821 boost::format(
"('%s','%s',%s,%s)") % sTxID %
"%s" %
824 sql.
reserve((accounts.size() + 1) * 128);
826 "INSERT INTO AccountTransactions "
827 "(TransID, Account, LedgerSeq, TxnSeq) VALUES ";
828 sql += boost::algorithm::join(
830 boost::adaptors::transformed(
841 <<
" account transaction: " << sql;
846 <<
"shard " <<
index_ <<
" transaction in ledger "
847 << sSeq <<
" affects no accounts";
854 item.first->getMetaSQL(
863 auto const sHash{
to_string(ledger->info().hash)};
868 soci::transaction tr(session);
870 auto const sParentHash{
to_string(ledger->info().parentHash)};
871 auto const sDrops{
to_string(ledger->info().drops)};
872 auto const sAccountHash{
to_string(ledger->info().accountHash)};
873 auto const sTxHash{
to_string(ledger->info().txHash)};
875 session <<
"DELETE FROM Ledgers "
876 "WHERE LedgerSeq = :seq;",
879 <<
"INSERT OR REPLACE INTO Ledgers ("
880 "LedgerHash, LedgerSeq, PrevHash, TotalCoins, ClosingTime,"
881 "PrevClosingTime, CloseTimeRes, CloseFlags, AccountSetHash,"
884 ":ledgerHash, :ledgerSeq, :prevHash, :totalCoins,"
885 ":closingTime, :prevClosingTime, :closeTimeRes,"
886 ":closeFlags, :accountSetHash, :transSetHash);",
887 soci::use(sHash), soci::use(seq), soci::use(sParentHash),
889 soci::use(ledger->info().closeTime.time_since_epoch().count()),
891 ledger->info().parentCloseTime.time_since_epoch().count()),
892 soci::use(ledger->info().closeTimeResolution.count()),
893 soci::use(ledger->info().closeFlags), soci::use(sAccountHash),
903 soci::blob sociBlob(session);
911 session <<
"UPDATE Shard "
912 "SET LastLedgerHash = :lastLedgerHash,"
913 "StoredLedgerSeqs = :storedLedgerSeqs "
914 "WHERE ShardIndex = :shardIndex;",
915 soci::use(sHash), soci::use(sociBlob), soci::use(
index_);
919 session <<
"UPDATE Shard "
920 "SET StoredLedgerSeqs = :storedLedgerSeqs "
921 "WHERE ShardIndex = :shardIndex;",
922 soci::use(sociBlob), soci::use(
index_);
929 <<
" in function " << __func__;
942 using namespace boost::filesystem;
943 for (
auto const& d : directory_iterator(
dir_))
945 if (is_regular_file(d))
955 <<
" in function " << __func__;
966 JLOG(j.fatal()) <<
"shard " <<
index <<
". " << msg
967 << (ledger->info().hash.isZero() ?
""
970 << (ledger->info().seq == 0 ?
""
971 :
". Ledger sequence " +
976 if (ledger->info().hash.isZero())
977 return fail(
"Invalid ledger hash");
978 if (ledger->info().accountHash.isZero())
979 return fail(
"Invalid ledger account hash");
985 auto nObj =
valFetch(node.getNodeHash().as_uint256());
994 if (ledger->stateMap().getHash().isNonZero())
996 if (!ledger->stateMap().isValid())
997 return fail(
"Invalid state map");
1001 if (next && next->info().parentHash == ledger->info().hash)
1002 ledger->stateMap().visitDifferences(&next->stateMap(), visit);
1004 ledger->stateMap().visitNodes(visit);
1015 return fail(
"Invalid state map");
1019 if (ledger->info().txHash.isNonZero())
1021 if (!ledger->txMap().isValid())
1022 return fail(
"Invalid transaction map");
1026 ledger->txMap().visitNodes(visit);
1037 return fail(
"Invalid transaction map");
1048 JLOG(j.fatal()) <<
"shard " <<
index <<
". " << msg
1049 <<
". Node object hash " <<
to_string(hash);
1056 switch (
backend_->fetch(hash.data(), &nObj))
1062 return fail(
"Node object hash does not match payload");
1065 return fail(
"Missing node object");
1067 return fail(
"Corrupt node object");
1069 return fail(
"Unknown error");