20 #include <ripple/nodestore/impl/Shard.h>
21 #include <ripple/app/ledger/InboundLedger.h>
22 #include <ripple/app/main/DBInit.h>
23 #include <ripple/basics/StringUtilities.h>
24 #include <ripple/core/ConfigSections.h>
25 #include <ripple/nodestore/impl/DatabaseShardImp.h>
26 #include <ripple/nodestore/Manager.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)))
46 , maxLedgers_(index == db.earliestShardIndex() ?
47 lastSeq_ - firstSeq_ + 1 : db.ledgersPerShard())
52 Throw<std::runtime_error>(
"Shard: Invalid index");
66 boost::filesystem::remove_all(
dir_);
72 " exception " << e.
what() <<
73 " in function " << __func__;
87 std::string const type {get<std::string>(section,
"type",
"nudb")};
93 " failed to create backend type " << type;
97 section.set(
"path",
dir_.string());
102 using namespace boost::filesystem;
103 auto preexist {
false};
104 auto fail = [
this, &preexist](
std::string const& msg)
123 auto createAcquireInfo = [
this, &config]()
128 setup.
startUp = config.START_UP;
145 preexist = exists(
dir_);
153 "INSERT INTO Shard (ShardIndex) "
154 "VALUES (:shardIndex);"
163 boost::optional<std::uint32_t>
index;
164 soci::blob sociBlob(session);
165 soci::indicator blobPresent;
168 "SELECT ShardIndex, StoredLedgerSeqs "
170 "WHERE ShardIndex = :index;"
172 , soci::into(sociBlob, blobPresent)
176 return fail(
"invalid acquire SQLite database");
178 if (blobPresent == soci::i_ok)
183 return fail(
"invalid StoredLedgerSeqs");
185 if (boost::icl::first(storedSeqs) <
firstSeq_ ||
186 boost::icl::last(storedSeqs) >
lastSeq_)
188 return fail(
"invalid StoredLedgerSeqs");
206 return fail(
"incompatible, missing backend final key");
210 SerialIter sIt(nObj->getData().data(), nObj->getData().size());
212 return fail(
"invalid version");
215 return fail(
"out of range ledger sequences");
218 return fail(
"invalid last ledger hash");
229 e.
what() +
" in function " + __func__);
240 boost::optional<std::uint32_t>
250 " prepare called when shard is complete";
256 if (storedSeqs.empty())
264 auto const seq {ledger->info().seq};
265 if (seq < firstSeq_ || seq >
lastSeq_)
269 " invalid ledger sequence " << seq;
280 " ledger sequence " << seq <<
" already stored";
286 if (boost::icl::contains(storedSeqs, seq))
290 " ledger sequence " << seq <<
" already stored";
294 storedSeqs.insert(seq);
311 " stored ledger sequence " << seq <<
321 if (seq < firstSeq_ || seq >
lastSeq_)
329 return boost::icl::contains(
acquireInfo_->storedSeqs, seq);
422 "shard " <<
index <<
". " << msg <<
423 (hash.isZero() ?
"" :
". Ledger hash " +
to_string(hash)) <<
432 return fail(
"incomplete");
451 SerialIter sIt(nObj->getData().data(), nObj->getData().size());
453 return fail(
"invalid version");
456 return fail(
"out of range ledger sequences");
459 return fail(
"invalid last ledger hash");
467 return fail(
"missing acquire SQLite database");
470 boost::optional<std::uint32_t>
index;
471 boost::optional<std::string> sHash;
472 soci::blob sociBlob(session);
473 soci::indicator blobPresent;
475 "SELECT ShardIndex, LastLedgerHash, StoredLedgerSeqs "
477 "WHERE ShardIndex = :index;"
480 , soci::into(sociBlob, blobPresent)
485 return fail(
"missing or invalid ShardIndex");
488 return fail(
"missing LastLedgerHash");
490 if (hash.SetHexExact(*sHash); hash.isZero())
491 return fail(
"invalid LastLedgerHash");
493 if (blobPresent != soci::i_ok)
494 return fail(
"missing StoredLedgerSeqs");
503 boost::icl::first(storedSeqs) !=
firstSeq_ ||
504 boost::icl::last(storedSeqs) !=
lastSeq_ ||
507 return fail(
"invalid StoredLedgerSeqs");
514 e.
what() +
" in function " + __func__);
520 auto const lastLedgerHash {hash};
532 return fail(
"invalid ledger");
534 ledger = std::make_shared<Ledger>(
538 if (ledger->info().seq != seq)
539 return fail(
"invalid ledger sequence");
540 if (ledger->info().hash != hash)
541 return fail(
"invalid ledger hash");
543 ledger->stateMap().setLedgerSeq(seq);
544 ledger->txMap().setLedgerSeq(seq);
546 if (!ledger->stateMap().fetchRoot(
547 SHAMapHash {ledger->info().accountHash},
nullptr))
549 return fail(
"missing root STATE node");
551 if (ledger->info().txHash.isNonZero() &&
552 !ledger->txMap().fetchRoot(
556 return fail(
"missing root TXN node");
560 return fail(
"failed to validate ledger");
566 return fail(
"failed storing to SQLite databases");
569 hash = ledger->info().parentHash;
632 return fail(
"failed to initialize SQLite databases");
639 e.
what() +
" in function " + __func__);
656 auto const sz {config.getValueFor(
684 setup.
startUp = config.START_UP;
704 boost::str(boost::format(
"PRAGMA cache_size=-%d;") %
715 boost::str(boost::format(
"PRAGMA cache_size=-%d;") %
729 boost::str(boost::format(
"PRAGMA cache_size=-%d;") %
739 boost::str(boost::format(
"PRAGMA cache_size=-%d;") %
748 " exception " << e.
what() <<
749 " in function " << __func__;
763 auto const seq {ledger->info().seq};
770 soci::transaction tr(session);
773 "DELETE FROM Transactions "
774 "WHERE LedgerSeq = :seq;"
777 "DELETE FROM AccountTransactions "
778 "WHERE LedgerSeq = :seq;"
781 if (ledger->info().txHash.isNonZero())
784 if (!ledger->txMap().isValid())
788 " has an invalid transaction map" <<
789 " on sequence " << sSeq;
793 for (
auto const& item : ledger->txs)
798 auto const txID {item.first->getTransactionID()};
800 auto const txMeta {std::make_shared<TxMeta>(
801 txID, ledger->seq(), *item.second)};
804 "DELETE FROM AccountTransactions "
805 "WHERE TransID = :txID;"
808 auto const& accounts = txMeta->getAffectedAccounts(
j_);
809 if (!accounts.empty())
812 auto const s {boost::str(boost::format(
819 sql.
reserve((accounts.size() + 1) * 128);
820 sql =
"INSERT INTO AccountTransactions "
821 "(TransID, Account, LedgerSeq, TxnSeq) VALUES ";
822 sql += boost::algorithm::join(
823 accounts | boost::adaptors::transformed(
826 return boost::str(boost::format(s)
835 " account transaction: " << sql;
841 " transaction in ledger " << sSeq <<
842 " affects no accounts";
849 item.first->getMetaSQL(
859 auto const sHash {
to_string(ledger->info().hash)};
864 soci::transaction tr(session);
866 auto const sParentHash {
to_string(ledger->info().parentHash)};
867 auto const sDrops {
to_string(ledger->info().drops)};
868 auto const sAccountHash {
to_string(ledger->info().accountHash)};
869 auto const sTxHash {
to_string(ledger->info().txHash)};
872 "DELETE FROM Ledgers "
873 "WHERE LedgerSeq = :seq;"
876 "INSERT OR REPLACE INTO Ledgers ("
877 "LedgerHash, LedgerSeq, PrevHash, TotalCoins, ClosingTime,"
878 "PrevClosingTime, CloseTimeRes, CloseFlags, AccountSetHash,"
881 ":ledgerHash, :ledgerSeq, :prevHash, :totalCoins,"
882 ":closingTime, :prevClosingTime, :closeTimeRes,"
883 ":closeFlags, :accountSetHash, :transSetHash);",
886 soci::use(sParentHash),
888 soci::use(ledger->info().closeTime.time_since_epoch().count()),
890 ledger->info().parentCloseTime.time_since_epoch().count()),
891 soci::use(ledger->info().closeTimeResolution.count()),
892 soci::use(ledger->info().closeFlags),
893 soci::use(sAccountHash),
903 soci::blob sociBlob(session);
913 "SET LastLedgerHash = :lastLedgerHash,"
914 "StoredLedgerSeqs = :storedLedgerSeqs "
915 "WHERE ShardIndex = :shardIndex;"
917 , soci::use(sociBlob)
924 "SET StoredLedgerSeqs = :storedLedgerSeqs "
925 "WHERE ShardIndex = :shardIndex;"
926 , soci::use(sociBlob)
935 " exception " << e.
what() <<
936 " in function " << __func__;
949 using namespace boost::filesystem;
950 for (
auto const& d : directory_iterator(
dir_))
952 if (is_regular_file(d))
963 " exception " << e.
what() <<
964 " in function " << __func__;
976 "shard " <<
index <<
". " << msg <<
977 (ledger->info().hash.isZero() ?
978 "" :
". Ledger hash " +
980 (ledger->info().seq == 0 ?
981 "" :
". Ledger sequence " +
986 if (ledger->info().hash.isZero())
987 return fail(
"Invalid ledger hash");
988 if (ledger->info().accountHash.isZero())
989 return fail(
"Invalid ledger account hash");
996 if (!
valFetch(node.getNodeHash().as_uint256()))
1002 if (ledger->stateMap().getHash().isNonZero())
1004 if (!ledger->stateMap().isValid())
1005 return fail(
"Invalid state map");
1009 if (next && next->info().parentHash == ledger->info().hash)
1010 ledger->stateMap().visitDifferences(&next->stateMap(), visit);
1012 ledger->stateMap().visitNodes(visit);
1017 e.
what() +
" in function " + __func__);
1022 return fail(
"Invalid state map");
1026 if (ledger->info().txHash.isNonZero())
1028 if (!ledger->txMap().isValid())
1029 return fail(
"Invalid transaction map");
1033 ledger->txMap().visitNodes(visit);
1038 e.
what() +
" in function " + __func__);
1043 return fail(
"Invalid transaction map");
1056 "shard " <<
index <<
". " << msg <<
1057 ". Node object hash " <<
to_string(hash);
1064 switch (
backend_->fetch(hash.data(), &nObj))
1069 return fail(
"Node object hash does not match payload");
1072 return fail(
"Missing node object");
1074 return fail(
"Corrupt node object");
1076 return fail(
"Unknown error");
1082 e.
what() +
" in function " + __func__);