rippled
DatabaseShard_test.cpp
1 //------------------------------------------------------------------------------
2 /*
3  This file is part of rippled: https://github.com/ripple/rippled
4  Copyright (c) 2020 Ripple Labs Inc.
5 
6  Permission to use, copy, modify, and/or distribute this software for any
7  purpose with or without fee is hereby granted, provided that the above
8  copyright notice and this permission notice appear in all copies.
9 
10  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18 //==============================================================================
19 
20 #include <ripple/app/ledger/LedgerMaster.h>
21 #include <ripple/app/ledger/LedgerToJson.h>
22 #include <ripple/app/misc/LoadFeeTrack.h>
23 #include <ripple/app/misc/SHAMapStore.h>
24 #include <ripple/app/rdb/backend/SQLiteDatabase.h>
25 #include <ripple/basics/Slice.h>
26 #include <ripple/basics/random.h>
27 #include <ripple/beast/hash/hash_append.h>
28 #include <ripple/beast/utility/temp_dir.h>
29 #include <ripple/core/ConfigSections.h>
30 #include <ripple/nodestore/DatabaseShard.h>
31 #include <ripple/nodestore/DummyScheduler.h>
32 #include <ripple/nodestore/impl/DecodedBlob.h>
33 #include <ripple/nodestore/impl/Shard.h>
34 #include <ripple/protocol/digest.h>
35 #include <test/jtx.h>
36 #include <test/jtx/CaptureLogs.h>
37 #include <test/nodestore/TestBase.h>
38 
39 #include <boost/algorithm/hex.hpp>
40 #include <chrono>
41 #include <fstream>
42 #include <iostream>
43 #include <numeric>
44 #include <openssl/ripemd.h>
45 
46 namespace ripple {
47 namespace NodeStore {
48 
59 template <class IntType = int>
61 {
62  using resultType = IntType;
63 
64  const resultType A, B;
65 
66  struct paramType
67  {
68  const resultType A, B;
69 
70  paramType(resultType aa, resultType bb) : A(aa), B(bb)
71  {
72  }
73  };
74 
76  const resultType a = 0,
78  : A(a), B(b)
79  {
80  }
81 
82  explicit uniformIntDistribution(const paramType& params)
83  : A(params.A), B(params.B)
84  {
85  }
86 
87  template <class Generator>
89  operator()(Generator& g) const
90  {
91  return rnd(g, A, B);
92  }
93 
94  template <class Generator>
96  operator()(Generator& g, const paramType& params) const
97  {
98  return rnd(g, params.A, params.B);
99  }
100 
101  resultType
102  a() const
103  {
104  return A;
105  }
106 
107  resultType
108  b() const
109  {
110  return B;
111  }
112 
113  resultType
114  min() const
115  {
116  return A;
117  }
118 
119  resultType
120  max() const
121  {
122  return B;
123  }
124 
125 private:
126  template <class Generator>
127  resultType
128  rnd(Generator& g, const resultType a, const resultType b) const
129  {
130  static_assert(
132  value,
133  "Ups...");
134  static_assert(
135  Generator::min() == 0, "If non-zero we have handle the offset");
136  const resultType range = b - a + 1;
137  assert(Generator::max() >= range); // Just for safety
138  const resultType rejectLim = g.max() % range;
139  resultType n;
140  do
141  n = g();
142  while (n <= rejectLim);
143  return (n % range) + a;
144  }
145 };
146 
147 template <class Engine, class Integral>
148 Integral
149 randInt(Engine& engine, Integral min, Integral max)
150 {
151  assert(max > min);
152 
153  // This should have no state and constructing it should
154  // be very cheap. If that turns out not to be the case
155  // it could be hand-optimized.
156  return uniformIntDistribution<Integral>(min, max)(engine);
157 }
158 
159 template <class Engine, class Integral>
160 Integral
161 randInt(Engine& engine, Integral max)
162 {
163  return randInt(engine, Integral(0), max);
164 }
165 
166 // Tests DatabaseShard class
167 //
169 {
170  static constexpr std::uint32_t maxSizeGb = 10;
171  static constexpr std::uint32_t maxHistoricalShards = 100;
172  static constexpr std::uint32_t ledgersPerShard = 256;
173  static constexpr std::uint32_t earliestSeq = ledgersPerShard + 1;
174  static constexpr std::uint32_t dataSizeMax = 4;
175  static constexpr std::uint32_t iniAmount = 1000000;
176  static constexpr std::uint32_t nTestShards = 4;
181 
182  struct TestData
183  {
184  /* ring used to generate pseudo-random sequence */
186  /* number of shards to generate */
188  /* vector of accounts used to send test transactions */
190  /* nAccounts_[i] is the number of these accounts existed before i-th
191  * ledger */
193  /* payAccounts_[i][j] = {from, to} is the pair which consists of two
194  * number of accounts: source and destinations, which participate in
195  * j-th payment on i-th ledger */
197  /* xrpAmount_[i] is the amount for all payments on i-th ledger */
199  /* ledgers_[i] is the i-th ledger which contains the above described
200  * accounts and payments */
202 
204  std::uint64_t const seedValue,
205  int dataSize = dataSizeMax,
206  int numShards = 1)
207  : rng_(seedValue), numShards_(numShards)
208  {
209  std::uint32_t n = 0;
210  std::uint32_t nLedgers = ledgersPerShard * numShards;
211 
212  nAccounts_.reserve(nLedgers);
213  payAccounts_.reserve(nLedgers);
214  xrpAmount_.reserve(nLedgers);
215 
216  for (std::uint32_t i = 0; i < nLedgers; ++i)
217  {
218  int p;
219  if (n >= 2)
220  p = randInt(rng_, 2 * dataSize);
221  else
222  p = 0;
223 
225  pay.reserve(p);
226 
227  for (int j = 0; j < p; ++j)
228  {
229  int from, to;
230  do
231  {
232  from = randInt(rng_, n - 1);
233  to = randInt(rng_, n - 1);
234  } while (from == to);
235 
236  pay.push_back(std::make_pair(from, to));
237  }
238 
239  n += !randInt(rng_, nLedgers / dataSize);
240 
241  if (n > accounts_.size())
242  {
243  char str[9];
244  for (int j = 0; j < 8; ++j)
245  str[j] = 'a' + randInt(rng_, 'z' - 'a');
246  str[8] = 0;
247  accounts_.emplace_back(str);
248  }
249 
251  payAccounts_.push_back(std::move(pay));
252  xrpAmount_.push_back(randInt(rng_, 90) + 10);
253  }
254  }
255 
256  bool
257  isNewAccounts(int seq)
258  {
259  return nAccounts_[seq] > (seq ? nAccounts_[seq - 1] : 0);
260  }
261 
262  void
264  {
265  using namespace test::jtx;
266 
267  // The local fee may go up, especially in the online delete tests
268  while (env_.app().getFeeTrack().lowerLocalFee())
270 
271  if (isNewAccounts(seq))
272  env_.fund(XRP(iniAmount), accounts_[nAccounts_[seq] - 1]);
273 
274  for (std::uint32_t i = 0; i < payAccounts_[seq].size(); ++i)
275  {
276  env_(
277  pay(accounts_[payAccounts_[seq][i].first],
278  accounts_[payAccounts_[seq][i].second],
279  XRP(xrpAmount_[seq])));
280  }
281  }
282 
283  bool
284  makeLedgers(test::jtx::Env& env_, std::uint32_t startIndex = 0)
285  {
286  if (startIndex == 0)
287  {
288  for (std::uint32_t i = 3; i <= ledgersPerShard; ++i)
289  {
290  if (!env_.close())
291  return false;
294  if (ledger->info().seq != i)
295  return false;
296  }
297  }
298 
299  for (std::uint32_t i = 0; i < ledgersPerShard * numShards_; ++i)
300  {
301  auto const index = i + (startIndex * ledgersPerShard);
302 
303  makeLedgerData(env_, i);
304  if (!env_.close())
305  return false;
308  if (ledger->info().seq != index + ledgersPerShard + 1)
309  return false;
310  ledgers_.push_back(ledger);
311  }
312 
313  return true;
314  }
315  };
316 
317  void
319  TestData& data,
321  std::uint32_t seq)
322  {
323  using namespace test::jtx;
324 
325  auto rootCount{0};
326  auto accCount{0};
327  auto sothCount{0};
328  for (auto const& sles : ledger->sles)
329  {
330  if (sles->getType() == ltACCOUNT_ROOT)
331  {
332  int sq = sles->getFieldU32(sfSequence);
333  int reqsq = -1;
334  const auto id = sles->getAccountID(sfAccount);
335 
336  for (int i = 0; i < data.accounts_.size(); ++i)
337  {
338  if (id == data.accounts_[i].id())
339  {
340  reqsq = ledgersPerShard + 1;
341  for (int j = 0; j <= seq; ++j)
342  if (data.nAccounts_[j] > i + 1 ||
343  (data.nAccounts_[j] == i + 1 &&
344  !data.isNewAccounts(j)))
345  {
346  for (int k = 0; k < data.payAccounts_[j].size();
347  ++k)
348  if (data.payAccounts_[j][k].first == i)
349  reqsq++;
350  }
351  else
352  reqsq++;
353  ++accCount;
354  break;
355  }
356  }
357  if (reqsq == -1)
358  {
359  reqsq = data.nAccounts_[seq] + 1;
360  ++rootCount;
361  }
362  BEAST_EXPECT(sq == reqsq);
363  }
364  else
365  ++sothCount;
366  }
367  BEAST_EXPECT(rootCount == 1);
368  BEAST_EXPECT(accCount == data.nAccounts_[seq]);
369  BEAST_EXPECT(sothCount == 3);
370 
371  auto iniCount{0};
372  auto setCount{0};
373  auto payCount{0};
374  auto tothCount{0};
375  for (auto const& tx : ledger->txs)
376  {
377  if (tx.first->getTxnType() == ttPAYMENT)
378  {
379  std::int64_t xrpAmount =
380  tx.first->getFieldAmount(sfAmount).xrp().decimalXRP();
381  if (xrpAmount == iniAmount)
382  ++iniCount;
383  else
384  {
385  ++payCount;
386  BEAST_EXPECT(xrpAmount == data.xrpAmount_[seq]);
387  }
388  }
389  else if (tx.first->getTxnType() == ttACCOUNT_SET)
390  ++setCount;
391  else
392  ++tothCount;
393  }
394  int newacc = data.isNewAccounts(seq) ? 1 : 0;
395  BEAST_EXPECT(iniCount == newacc);
396  BEAST_EXPECT(setCount == newacc);
397  BEAST_EXPECT(payCount == data.payAccounts_[seq].size());
398  BEAST_EXPECT(tothCount == !seq);
399  }
400 
401  bool
403  Database& db,
404  Ledger const& ledger,
405  std::shared_ptr<Ledger const> const& next = {})
406  {
407  // Store header
408  {
409  Serializer s(sizeof(std::uint32_t) + sizeof(LedgerInfo));
410  s.add32(HashPrefix::ledgerMaster);
411  addRaw(ledger.info(), s);
412  db.store(
413  hotLEDGER,
414  std::move(s.modData()),
415  ledger.info().hash,
416  ledger.info().seq);
417  }
418 
419  // Store the state map
420  auto visitAcc = [&](SHAMapTreeNode const& node) {
421  Serializer s;
422  node.serializeWithPrefix(s);
423  db.store(
424  node.getType() == SHAMapNodeType::tnINNER ? hotUNKNOWN
425  : hotACCOUNT_NODE,
426  std::move(s.modData()),
427  node.getHash().as_uint256(),
428  ledger.info().seq);
429  return true;
430  };
431 
432  if (ledger.stateMap().getHash().isNonZero())
433  {
434  if (!ledger.stateMap().isValid())
435  return false;
436  if (next && next->info().parentHash == ledger.info().hash)
437  {
438  auto have = next->stateMap().snapShot(false);
439  ledger.stateMap().snapShot(false)->visitDifferences(
440  &(*have), visitAcc);
441  }
442  else
443  ledger.stateMap().snapShot(false)->visitNodes(visitAcc);
444  }
445 
446  // Store the transaction map
447  auto visitTx = [&](SHAMapTreeNode& node) {
448  Serializer s;
449  node.serializeWithPrefix(s);
450  db.store(
451  node.getType() == SHAMapNodeType::tnINNER ? hotUNKNOWN
453  std::move(s.modData()),
454  node.getHash().as_uint256(),
455  ledger.info().seq);
456  return true;
457  };
458 
459  if (ledger.info().txHash.isNonZero())
460  {
461  if (!ledger.txMap().isValid())
462  return false;
463  ledger.txMap().snapShot(false)->visitNodes(visitTx);
464  }
465 
466  return true;
467  }
468 
469  void
470  checkLedger(TestData& data, DatabaseShard& db, Ledger const& ledger)
471  {
472  auto fetched = db.fetchLedger(ledger.info().hash, ledger.info().seq);
473  if (!BEAST_EXPECT(fetched))
474  return;
475 
476  testLedgerData(data, fetched, ledger.info().seq - ledgersPerShard - 1);
477 
478  // verify the metadata/header info by serializing to json
479  BEAST_EXPECT(
481  ledger, nullptr, LedgerFill::full | LedgerFill::expand}) ==
483  *fetched, nullptr, LedgerFill::full | LedgerFill::expand}));
484 
485  BEAST_EXPECT(
487  ledger, nullptr, LedgerFill::full | LedgerFill::binary}) ==
489  *fetched, nullptr, LedgerFill::full | LedgerFill::binary}));
490 
491  // walk shamap and validate each node
492  auto fcompAcc = [&](SHAMapTreeNode& node) -> bool {
493  Serializer s;
494  node.serializeWithPrefix(s);
495  auto nSrc{NodeObject::createObject(
496  node.getType() == SHAMapNodeType::tnINNER ? hotUNKNOWN
497  : hotACCOUNT_NODE,
498  std::move(s.modData()),
499  node.getHash().as_uint256())};
500  if (!BEAST_EXPECT(nSrc))
501  return false;
502 
503  auto nDst = db.fetchNodeObject(
504  node.getHash().as_uint256(), ledger.info().seq);
505  if (!BEAST_EXPECT(nDst))
506  return false;
507 
508  BEAST_EXPECT(isSame(nSrc, nDst));
509 
510  return true;
511  };
512  if (ledger.stateMap().getHash().isNonZero())
513  ledger.stateMap().snapShot(false)->visitNodes(fcompAcc);
514 
515  auto fcompTx = [&](SHAMapTreeNode& node) -> bool {
516  Serializer s;
517  node.serializeWithPrefix(s);
518  auto nSrc{NodeObject::createObject(
519  node.getType() == SHAMapNodeType::tnINNER ? hotUNKNOWN
521  std::move(s.modData()),
522  node.getHash().as_uint256())};
523  if (!BEAST_EXPECT(nSrc))
524  return false;
525 
526  auto nDst = db.fetchNodeObject(
527  node.getHash().as_uint256(), ledger.info().seq);
528  if (!BEAST_EXPECT(nDst))
529  return false;
530 
531  BEAST_EXPECT(isSame(nSrc, nDst));
532 
533  return true;
534  };
535  if (ledger.info().txHash.isNonZero())
536  ledger.txMap().snapShot(false)->visitNodes(fcompTx);
537  }
538 
541  {
543  if (!bitmask)
544  return set;
545  bool empty = true;
546 
547  for (std::uint32_t i = 0; i < 64 && bitmask; i++)
548  {
549  if (bitmask & (1ll << i))
550  {
551  if (!empty)
552  set += ",";
553  set += std::to_string(i);
554  empty = false;
555  }
556  }
557 
559  BEAST_EXPECT(from_string(rs, set));
560  return ripple::to_string(rs);
561  }
562 
565  std::string const& shardDir,
566  std::string const& nodeDir = std::string())
567  {
568  using namespace test::jtx;
569 
570  return envconfig([&](std::unique_ptr<Config> cfg) {
571  // Shard store configuration
572  cfg->overwrite(ConfigSection::shardDatabase(), "path", shardDir);
573  cfg->overwrite(
575  "max_historical_shards",
577  cfg->overwrite(
579  "ledgers_per_shard",
581  cfg->overwrite(
583  "earliest_seq",
585 
586  // Node store configuration
587  cfg->overwrite(
589  "path",
590  nodeDir.empty() ? defNodeDir.path() : nodeDir);
591  cfg->overwrite(
593  "ledgers_per_shard",
595  cfg->overwrite(
597  "earliest_seq",
599  return cfg;
600  });
601  }
602 
605  DatabaseShard& shardStore,
606  std::uint32_t shardIndex,
608  {
609  auto const end{std::chrono::system_clock::now() + timeout};
610  while (shardStore.getNumTasks() ||
611  !boost::icl::contains(
612  shardStore.getShardInfo()->finalized(), shardIndex))
613  {
614  if (!BEAST_EXPECT(std::chrono::system_clock::now() < end))
615  return std::nullopt;
617  }
618 
619  return shardIndex;
620  }
621 
624  TestData& data,
625  DatabaseShard& shardStore,
626  int maxShardIndex = 1,
627  int shardOffset = 0)
628  {
629  int shardIndex{-1};
630 
631  for (std::uint32_t i = 0; i < ledgersPerShard; ++i)
632  {
633  auto const ledgerSeq{shardStore.prepareLedger(
634  (maxShardIndex + 1) * ledgersPerShard)};
635  if (!BEAST_EXPECT(ledgerSeq != std::nullopt))
636  return std::nullopt;
637 
638  shardIndex = shardStore.seqToShardIndex(*ledgerSeq);
639 
640  int const arrInd = *ledgerSeq - (ledgersPerShard * shardOffset) -
641  ledgersPerShard - 1;
642  BEAST_EXPECT(
643  arrInd >= 0 && arrInd < maxShardIndex * ledgersPerShard);
644  BEAST_EXPECT(saveLedger(shardStore, *data.ledgers_[arrInd]));
645  if (arrInd % ledgersPerShard == (ledgersPerShard - 1))
646  {
647  uint256 const finalKey_{0};
648  Serializer s;
650  s.add32(shardStore.firstLedgerSeq(shardIndex));
651  s.add32(shardStore.lastLedgerSeq(shardIndex));
652  s.addRaw(data.ledgers_[arrInd]->info().hash.data(), 256 / 8);
653  shardStore.store(
654  hotUNKNOWN, std::move(s.modData()), finalKey_, *ledgerSeq);
655  }
656  shardStore.setStored(data.ledgers_[arrInd]);
657  }
658 
659  return waitShard(shardStore, shardIndex);
660  }
661 
662  void
664  {
665  testcase("Standalone");
666 
667  using namespace test::jtx;
668 
669  beast::temp_dir shardDir;
670  DummyScheduler scheduler;
671  {
672  Env env{*this, testConfig(shardDir.path())};
674  make_ShardStore(env.app(), scheduler, 2, journal_)};
675 
676  BEAST_EXPECT(shardStore);
677  BEAST_EXPECT(shardStore->init());
678  BEAST_EXPECT(shardStore->ledgersPerShard() == ledgersPerShard);
679  BEAST_EXPECT(shardStore->seqToShardIndex(ledgersPerShard + 1) == 1);
680  BEAST_EXPECT(shardStore->seqToShardIndex(2 * ledgersPerShard) == 1);
681  BEAST_EXPECT(
682  shardStore->seqToShardIndex(2 * ledgersPerShard + 1) == 2);
683  BEAST_EXPECT(
684  shardStore->earliestShardIndex() ==
685  (earliestSeq - 1) / ledgersPerShard);
686  BEAST_EXPECT(shardStore->firstLedgerSeq(1) == ledgersPerShard + 1);
687  BEAST_EXPECT(shardStore->lastLedgerSeq(1) == 2 * ledgersPerShard);
688  BEAST_EXPECT(shardStore->getRootDir().string() == shardDir.path());
689  }
690 
691  {
692  Env env{*this, testConfig(shardDir.path())};
694  make_ShardStore(env.app(), scheduler, 2, journal_)};
695 
696  env.app().config().overwrite(
697  ConfigSection::shardDatabase(), "ledgers_per_shard", "512");
698  BEAST_EXPECT(!shardStore->init());
699  }
700 
701  Env env{*this, testConfig(shardDir.path())};
703  make_ShardStore(env.app(), scheduler, 2, journal_)};
704 
705  env.app().config().overwrite(
707  "earliest_seq",
709  BEAST_EXPECT(!shardStore->init());
710  }
711 
712  void
714  {
715  testcase("Create shard");
716 
717  using namespace test::jtx;
718 
719  beast::temp_dir shardDir;
720  Env env{*this, testConfig(shardDir.path())};
721  DatabaseShard* db = env.app().getShardStore();
722  BEAST_EXPECT(db);
723 
724  TestData data(seedValue);
725  if (!BEAST_EXPECT(data.makeLedgers(env)))
726  return;
727 
728  if (!createShard(data, *db, 1))
729  return;
730 
731  for (std::uint32_t i = 0; i < ledgersPerShard; ++i)
732  checkLedger(data, *db, *data.ledgers_[i]);
733  }
734 
735  void
737  {
738  testcase("Reopen shard store");
739 
740  using namespace test::jtx;
741 
742  beast::temp_dir shardDir;
743  {
744  Env env{*this, testConfig(shardDir.path())};
745  DatabaseShard* db = env.app().getShardStore();
746  BEAST_EXPECT(db);
747 
748  TestData data(seedValue, 4, 2);
749  if (!BEAST_EXPECT(data.makeLedgers(env)))
750  return;
751 
752  for (auto i = 0; i < 2; ++i)
753  {
754  if (!createShard(data, *db, 2))
755  return;
756  }
757  }
758  {
759  Env env{*this, testConfig(shardDir.path())};
760  DatabaseShard* db = env.app().getShardStore();
761  BEAST_EXPECT(db);
762 
763  TestData data(seedValue, 4, 2);
764  if (!BEAST_EXPECT(data.makeLedgers(env)))
765  return;
766 
767  for (std::uint32_t i = 1; i <= 2; ++i)
768  waitShard(*db, i);
769 
770  for (std::uint32_t i = 0; i < 2 * ledgersPerShard; ++i)
771  checkLedger(data, *db, *data.ledgers_[i]);
772  }
773  }
774 
775  void
777  {
778  testcase("Get final shards");
779 
780  using namespace test::jtx;
781 
782  beast::temp_dir shardDir;
783  Env env{*this, testConfig(shardDir.path())};
784  DatabaseShard* db = env.app().getShardStore();
785  BEAST_EXPECT(db);
786 
787  TestData data(seedValue, 2, nTestShards);
788  if (!BEAST_EXPECT(data.makeLedgers(env)))
789  return;
790 
791  BEAST_EXPECT(db->getShardInfo()->finalized().empty());
792 
793  for (auto i = 0; i < nTestShards; ++i)
794  {
795  auto const shardIndex{createShard(data, *db, nTestShards)};
796  if (!BEAST_EXPECT(
797  shardIndex && *shardIndex >= 1 &&
798  *shardIndex <= nTestShards))
799  {
800  return;
801  }
802 
803  BEAST_EXPECT(boost::icl::contains(
804  db->getShardInfo()->finalized(), *shardIndex));
805  }
806  }
807 
808  void
810  {
811  testcase("Prepare shards");
812 
813  using namespace test::jtx;
814 
815  beast::temp_dir shardDir;
816  Env env{*this, testConfig(shardDir.path())};
817  DatabaseShard* db = env.app().getShardStore();
818  BEAST_EXPECT(db);
819 
820  TestData data(seedValue, 1, nTestShards);
821  if (!BEAST_EXPECT(data.makeLedgers(env)))
822  return;
823 
824  BEAST_EXPECT(db->getPreShards() == "");
825  BEAST_EXPECT(!db->prepareShards({}));
826 
827  std::uint64_t bitMask = 0;
828  for (std::uint32_t i = 0; i < nTestShards * 2; ++i)
829  {
830  std::uint32_t const shardIndex{
831  randInt(data.rng_, nTestShards - 1) + 1};
832  if (bitMask & (1ll << shardIndex))
833  {
834  db->removePreShard(shardIndex);
835  bitMask &= ~(1ll << shardIndex);
836  }
837  else
838  {
839  BEAST_EXPECT(db->prepareShards({shardIndex}));
840  bitMask |= 1ll << shardIndex;
841  }
842  BEAST_EXPECT(db->getPreShards() == bitmask2Rangeset(bitMask));
843  }
844 
845  // test illegal cases
846  // adding shards with too large number
847  BEAST_EXPECT(!db->prepareShards({0}));
848  BEAST_EXPECT(db->getPreShards() == bitmask2Rangeset(bitMask));
849  BEAST_EXPECT(!db->prepareShards({nTestShards + 1}));
850  BEAST_EXPECT(db->getPreShards() == bitmask2Rangeset(bitMask));
851  BEAST_EXPECT(!db->prepareShards({nTestShards + 2}));
852  BEAST_EXPECT(db->getPreShards() == bitmask2Rangeset(bitMask));
853 
854  // create shards which are not prepared for import
855  BEAST_EXPECT(db->getShardInfo()->finalized().empty());
856 
857  std::uint64_t bitMask2 = 0;
858  for (auto i = 0; i < nTestShards; ++i)
859  {
860  auto const shardIndex{createShard(data, *db, nTestShards)};
861  if (!BEAST_EXPECT(
862  shardIndex && *shardIndex >= 1 &&
863  *shardIndex <= nTestShards))
864  {
865  return;
866  }
867 
868  BEAST_EXPECT(boost::icl::contains(
869  db->getShardInfo()->finalized(), *shardIndex));
870 
871  bitMask2 |= 1ll << *shardIndex;
872  BEAST_EXPECT((bitMask & bitMask2) == 0);
873  if ((bitMask | bitMask2) == ((1ll << nTestShards) - 1) << 1)
874  break;
875  }
876 
877  // try to create another shard
878  BEAST_EXPECT(
880  std::nullopt);
881  }
882 
883  void
885  {
886  testcase("Import shard");
887 
888  using namespace test::jtx;
889 
890  beast::temp_dir importDir;
891  TestData data(seedValue, 2);
892 
893  {
894  Env env{*this, testConfig(importDir.path())};
895  DatabaseShard* db = env.app().getShardStore();
896  BEAST_EXPECT(db);
897 
898  if (!BEAST_EXPECT(data.makeLedgers(env)))
899  return;
900 
901  if (!createShard(data, *db, 1))
902  return;
903 
904  for (std::uint32_t i = 0; i < ledgersPerShard; ++i)
905  checkLedger(data, *db, *data.ledgers_[i]);
906 
907  data.ledgers_.clear();
908  }
909 
910  boost::filesystem::path importPath(importDir.path());
911  importPath /= "1";
912 
913  {
914  beast::temp_dir shardDir;
915  Env env{*this, testConfig(shardDir.path())};
916  DatabaseShard* db = env.app().getShardStore();
917  BEAST_EXPECT(db);
918 
919  if (!BEAST_EXPECT(data.makeLedgers(env)))
920  return;
921 
922  BEAST_EXPECT(!db->importShard(1, importPath / "not_exist"));
923  BEAST_EXPECT(db->prepareShards({1}));
924  BEAST_EXPECT(db->getPreShards() == "1");
925 
926  using namespace boost::filesystem;
927  remove_all(importPath / LgrDBName);
928  remove_all(importPath / TxDBName);
929 
930  if (!BEAST_EXPECT(db->importShard(1, importPath)))
931  return;
932 
933  BEAST_EXPECT(db->getPreShards() == "");
934 
935  auto n = waitShard(*db, 1);
936  if (!BEAST_EXPECT(n && *n == 1))
937  return;
938 
939  for (std::uint32_t i = 0; i < ledgersPerShard; ++i)
940  checkLedger(data, *db, *data.ledgers_[i]);
941  }
942  }
943 
944  void
946  {
947  testcase("Corrupted shard store");
948 
949  using namespace test::jtx;
950 
951  beast::temp_dir shardDir;
952  {
953  TestData data(seedValue, 4, 2);
954  {
955  Env env{*this, testConfig(shardDir.path())};
956  DatabaseShard* db = env.app().getShardStore();
957  BEAST_EXPECT(db);
958 
959  if (!BEAST_EXPECT(data.makeLedgers(env)))
960  return;
961 
962  for (auto i = 0; i < 2; ++i)
963  {
964  if (!BEAST_EXPECT(createShard(data, *db, 2)))
965  return;
966  }
967  }
968 
969  boost::filesystem::path path = shardDir.path();
970  path /= std::string("2");
971  path /= "nudb.dat";
972 
973  FILE* f = fopen(path.string().c_str(), "r+b");
974  if (!BEAST_EXPECT(f))
975  return;
976  char buf[256];
977  beast::rngfill(buf, sizeof(buf), data.rng_);
978  BEAST_EXPECT(fwrite(buf, 1, 256, f) == 256);
979  fclose(f);
980  }
981 
982  Env env{*this, testConfig(shardDir.path())};
983  DatabaseShard* db = env.app().getShardStore();
984  BEAST_EXPECT(db);
985 
986  TestData data(seedValue, 4, 2);
987  if (!BEAST_EXPECT(data.makeLedgers(env)))
988  return;
989 
990  for (std::uint32_t shardIndex = 1; shardIndex <= 1; ++shardIndex)
991  waitShard(*db, shardIndex);
992 
993  BEAST_EXPECT(boost::icl::contains(db->getShardInfo()->finalized(), 1));
994 
995  for (std::uint32_t i = 0; i < 1 * ledgersPerShard; ++i)
996  checkLedger(data, *db, *data.ledgers_[i]);
997  }
998 
999  void
1001  {
1002  testcase("Illegal finalKey");
1003 
1004  using namespace test::jtx;
1005 
1006  for (int i = 0; i < 5; ++i)
1007  {
1008  beast::temp_dir shardDir;
1009  {
1010  Env env{*this, testConfig(shardDir.path())};
1011  DatabaseShard* db = env.app().getShardStore();
1012  BEAST_EXPECT(db);
1013 
1014  TestData data(seedValue + i, 2);
1015  if (!BEAST_EXPECT(data.makeLedgers(env)))
1016  return;
1017 
1018  int shardIndex{-1};
1019  for (std::uint32_t j = 0; j < ledgersPerShard; ++j)
1020  {
1021  auto const ledgerSeq{
1022  db->prepareLedger(2 * ledgersPerShard)};
1023  if (!BEAST_EXPECT(ledgerSeq != std::nullopt))
1024  return;
1025 
1026  shardIndex = db->seqToShardIndex(*ledgerSeq);
1027  int arrInd = *ledgerSeq - ledgersPerShard - 1;
1028  BEAST_EXPECT(arrInd >= 0 && arrInd < ledgersPerShard);
1029  BEAST_EXPECT(saveLedger(*db, *data.ledgers_[arrInd]));
1030  if (arrInd % ledgersPerShard == (ledgersPerShard - 1))
1031  {
1032  uint256 const finalKey_{0};
1033  Serializer s;
1034  s.add32(Shard::version + (i == 0));
1035  s.add32(db->firstLedgerSeq(shardIndex) + (i == 1));
1036  s.add32(db->lastLedgerSeq(shardIndex) - (i == 3));
1037  s.addRaw(
1038  data.ledgers_[arrInd - (i == 4)]
1039  ->info()
1040  .hash.data(),
1041  256 / 8);
1042  db->store(
1043  hotUNKNOWN,
1044  std::move(s.modData()),
1045  finalKey_,
1046  *ledgerSeq);
1047  }
1048  db->setStored(data.ledgers_[arrInd]);
1049  }
1050 
1051  if (i == 2)
1052  {
1053  waitShard(*db, shardIndex);
1054  BEAST_EXPECT(boost::icl::contains(
1055  db->getShardInfo()->finalized(), 1));
1056  }
1057  else
1058  {
1059  boost::filesystem::path path(shardDir.path());
1060  path /= "1";
1061  boost::system::error_code ec;
1062  auto start = std::chrono::system_clock::now();
1063  auto end = start + shardStoreTimeout;
1064  while (std::chrono::system_clock::now() < end &&
1065  boost::filesystem::exists(path, ec))
1066  {
1068  }
1069 
1070  BEAST_EXPECT(db->getShardInfo()->finalized().empty());
1071  }
1072  }
1073 
1074  {
1075  Env env{*this, testConfig(shardDir.path())};
1076  DatabaseShard* db = env.app().getShardStore();
1077  BEAST_EXPECT(db);
1078 
1079  TestData data(seedValue + i, 2);
1080  if (!BEAST_EXPECT(data.makeLedgers(env)))
1081  return;
1082 
1083  if (i == 2)
1084  {
1085  waitShard(*db, 1);
1086  BEAST_EXPECT(boost::icl::contains(
1087  db->getShardInfo()->finalized(), 1));
1088 
1089  for (std::uint32_t j = 0; j < ledgersPerShard; ++j)
1090  checkLedger(data, *db, *data.ledgers_[j]);
1091  }
1092  else
1093  BEAST_EXPECT(db->getShardInfo()->finalized().empty());
1094  }
1095  }
1096  }
1097 
1098  std::string
1100  {
1101  using beast::hash_append;
1102  std::ifstream input(filename, std::ios::in | std::ios::binary);
1103  char buf[4096];
1104  ripemd160_hasher h;
1105 
1106  while (input.read(buf, 4096), input.gcount() > 0)
1107  hash_append(h, buf, input.gcount());
1108 
1109  auto const binResult = static_cast<ripemd160_hasher::result_type>(h);
1110  const auto charDigest = binResult.data();
1111  std::string result;
1112  boost::algorithm::hex(
1113  charDigest,
1114  charDigest + sizeof(binResult),
1115  std::back_inserter(result));
1116 
1117  return result;
1118  }
1119 
1120  void
1122  {
1123  testcase("Deterministic shards");
1124 
1125  using namespace test::jtx;
1126 
1127  for (int i = 0; i < 2; i++)
1128  {
1129  beast::temp_dir shardDir;
1130  {
1131  Env env{*this, testConfig(shardDir.path())};
1132  DatabaseShard* db = env.app().getShardStore();
1133  BEAST_EXPECT(db);
1134 
1135  TestData data(seedValue, 4);
1136  if (!BEAST_EXPECT(data.makeLedgers(env)))
1137  return;
1138 
1139  if (!BEAST_EXPECT(createShard(data, *db) != std::nullopt))
1140  return;
1141  }
1142 
1143  boost::filesystem::path path(shardDir.path());
1144  path /= "1";
1145 
1146  auto static const ripemd160Key =
1147  ripemd160File((path / "nudb.key").string());
1148  auto static const ripemd160Dat =
1149  ripemd160File((path / "nudb.dat").string());
1150 
1151  {
1152  Env env{*this, testConfig(shardDir.path())};
1153  DatabaseShard* db = env.app().getShardStore();
1154  BEAST_EXPECT(db);
1155 
1156  TestData data(seedValue, 4);
1157  if (!BEAST_EXPECT(data.makeLedgers(env)))
1158  return;
1159 
1160  if (!BEAST_EXPECT(waitShard(*db, 1) != std::nullopt))
1161  return;
1162 
1163  for (std::uint32_t j = 0; j < ledgersPerShard; ++j)
1164  checkLedger(data, *db, *data.ledgers_[j]);
1165  }
1166 
1167  BEAST_EXPECT(
1168  ripemd160File((path / "nudb.key").string()) == ripemd160Key);
1169  BEAST_EXPECT(
1170  ripemd160File((path / "nudb.dat").string()) == ripemd160Dat);
1171  }
1172  }
1173 
1174  void
1176  {
1177  testcase("Import node store");
1178 
1179  using namespace test::jtx;
1180 
1181  beast::temp_dir shardDir;
1182  {
1183  beast::temp_dir nodeDir;
1184  Env env{*this, testConfig(shardDir.path(), nodeDir.path())};
1185  DatabaseShard* db = env.app().getShardStore();
1186  Database& ndb = env.app().getNodeStore();
1187  BEAST_EXPECT(db);
1188 
1189  TestData data(seedValue, 4, 2);
1190  if (!BEAST_EXPECT(data.makeLedgers(env)))
1191  return;
1192 
1193  for (std::uint32_t i = 0; i < 2 * ledgersPerShard; ++i)
1194  BEAST_EXPECT(saveLedger(ndb, *data.ledgers_[i]));
1195 
1196  BEAST_EXPECT(db->getShardInfo()->finalized().empty());
1197  db->importDatabase(ndb);
1198  for (std::uint32_t i = 1; i <= 2; ++i)
1199  waitShard(*db, i);
1200 
1201  auto const finalShards{db->getShardInfo()->finalized()};
1202  for (std::uint32_t shardIndex : {1, 2})
1203  BEAST_EXPECT(boost::icl::contains(finalShards, shardIndex));
1204  }
1205  {
1206  Env env{*this, testConfig(shardDir.path())};
1207  DatabaseShard* db = env.app().getShardStore();
1208  BEAST_EXPECT(db);
1209 
1210  TestData data(seedValue, 4, 2);
1211  if (!BEAST_EXPECT(data.makeLedgers(env)))
1212  return;
1213 
1214  for (std::uint32_t i = 1; i <= 2; ++i)
1215  waitShard(*db, i);
1216 
1217  auto const finalShards{db->getShardInfo()->finalized()};
1218  for (std::uint32_t shardIndex : {1, 2})
1219  BEAST_EXPECT(boost::icl::contains(finalShards, shardIndex));
1220 
1221  for (std::uint32_t i = 0; i < 2 * ledgersPerShard; ++i)
1222  checkLedger(data, *db, *data.ledgers_[i]);
1223  }
1224  }
1225 
1226  void
1228  {
1229  testcase("Import node store with online delete");
1230 
1231  using namespace test::jtx;
1232  using test::CaptureLogs;
1233 
1234  beast::temp_dir shardDir;
1235  beast::temp_dir nodeDir;
1236  std::string capturedLogs;
1237 
1238  {
1239  auto c = testConfig(shardDir.path(), nodeDir.path());
1240  auto& section = c->section(ConfigSection::nodeDatabase());
1241  section.set("online_delete", "550");
1242  section.set("advisory_delete", "1");
1243 
1244  // Adjust the log level to capture relevant output
1245  c->section(SECTION_RPC_STARTUP)
1246  .append(
1247  "{ \"command\": \"log_level\", \"severity\": \"trace\" "
1248  "}");
1249 
1250  std::unique_ptr<Logs> logs(new CaptureLogs(&capturedLogs));
1251  Env env{*this, std::move(c), std::move(logs)};
1252 
1253  DatabaseShard* db = env.app().getShardStore();
1254  Database& ndb = env.app().getNodeStore();
1255  BEAST_EXPECT(db);
1256 
1257  auto& store = env.app().getSHAMapStore();
1258 
1259  // Allow online delete to delete the startup ledgers
1260  // so that it will take some time for the import to
1261  // catch up to the point of the next rotation
1262  store.setCanDelete(10);
1263 
1264  // Create some ledgers for the shard store to import
1265  auto const shardCount = 5;
1266  TestData data(seedValue, 4, shardCount);
1267  if (!BEAST_EXPECT(data.makeLedgers(env)))
1268  return;
1269 
1270  store.rendezvous();
1271  auto const lastRotated = store.getLastRotated();
1272  BEAST_EXPECT(lastRotated >= 553 && lastRotated < 1103);
1273 
1274  // Start the import
1275  db->importDatabase(ndb);
1276 
1277  while (!db->getDatabaseImportSequence())
1278  {
1279  // Wait until the import starts
1281  }
1282 
1283  // Enable unimpeded online deletion now that the import has started
1284  store.setCanDelete(std::numeric_limits<std::uint32_t>::max());
1285 
1286  auto pauseVerifier = std::thread([lastRotated, &store, db, this] {
1287  // The import should still be running when this thread starts
1288  BEAST_EXPECT(db->getDatabaseImportSequence());
1289  auto rotationProgress = lastRotated;
1290  while (auto const ledgerSeq = db->getDatabaseImportSequence())
1291  {
1292  // Make sure database rotations dont interfere
1293  // with the import
1294 
1295  auto const last = store.getLastRotated();
1296  if (last != rotationProgress)
1297  {
1298  // A rotation occurred during shard import. Not
1299  // necessarily an error
1300 
1301  BEAST_EXPECT(
1302  !ledgerSeq || ledgerSeq >= rotationProgress);
1303  rotationProgress = last;
1304  }
1305  }
1306  });
1307 
1308  auto join = [&pauseVerifier]() {
1309  if (pauseVerifier.joinable())
1310  pauseVerifier.join();
1311  };
1312 
1313  // Create more ledgers to trigger online deletion
1314  data = TestData(seedValue * 2);
1315  if (!BEAST_EXPECT(data.makeLedgers(env, shardCount)))
1316  {
1317  join();
1318  return;
1319  }
1320 
1321  join();
1322  BEAST_EXPECT(store.getLastRotated() != lastRotated);
1323  }
1324 
1325  // Database rotation should have been postponed at some
1326  // point during the import
1327  auto const expectedLogMessage =
1328  "rotation would interfere with ShardStore import";
1329  BEAST_EXPECT(
1330  capturedLogs.find(expectedLogMessage) != std::string::npos);
1331  }
1332 
1333  void
1335  {
1336  testcase("Import with historical paths");
1337 
1338  using namespace test::jtx;
1339 
1340  // Test importing with multiple historical paths
1341  {
1342  beast::temp_dir shardDir;
1343  std::array<beast::temp_dir, 4> historicalDirs;
1345 
1347  historicalDirs.begin(),
1348  historicalDirs.end(),
1349  historicalPaths.begin(),
1350  [](const beast::temp_dir& dir) { return dir.path(); });
1351 
1352  beast::temp_dir nodeDir;
1353  auto c = testConfig(shardDir.path(), nodeDir.path());
1354 
1355  auto& historyPaths = c->section(SECTION_HISTORICAL_SHARD_PATHS);
1356  historyPaths.append(
1357  {historicalPaths[0].string(),
1358  historicalPaths[1].string(),
1359  historicalPaths[2].string(),
1360  historicalPaths[3].string()});
1361 
1362  Env env{*this, std::move(c)};
1363  DatabaseShard* db = env.app().getShardStore();
1364  Database& ndb = env.app().getNodeStore();
1365  BEAST_EXPECT(db);
1366 
1367  auto const shardCount = 4;
1368 
1369  TestData data(seedValue, 4, shardCount);
1370  if (!BEAST_EXPECT(data.makeLedgers(env)))
1371  return;
1372 
1373  for (std::uint32_t i = 0; i < shardCount * ledgersPerShard; ++i)
1374  BEAST_EXPECT(saveLedger(ndb, *data.ledgers_[i]));
1375 
1376  BEAST_EXPECT(db->getShardInfo()->finalized().empty());
1377 
1378  db->importDatabase(ndb);
1379  for (std::uint32_t i = 1; i <= shardCount; ++i)
1380  waitShard(*db, i);
1381 
1382  auto const final{db->getShardInfo()->finalized()};
1383  for (std::uint32_t shardIndex : {1, 2, 3, 4})
1384  BEAST_EXPECT(boost::icl::contains(final, shardIndex));
1385 
1386  auto const mainPathCount = std::distance(
1387  boost::filesystem::directory_iterator(shardDir.path()),
1388  boost::filesystem::directory_iterator());
1389 
1390  // Only the two most recent shards
1391  // should be stored at the main path
1392  BEAST_EXPECT(mainPathCount == 2);
1393 
1394  auto const historicalPathCount = std::accumulate(
1395  historicalPaths.begin(),
1396  historicalPaths.end(),
1397  0,
1398  [](int const sum, boost::filesystem::path const& path) {
1399  return sum +
1400  std::distance(
1401  boost::filesystem::directory_iterator(path),
1402  boost::filesystem::directory_iterator());
1403  });
1404 
1405  // All historical shards should be stored
1406  // at historical paths
1407  BEAST_EXPECT(historicalPathCount == shardCount - 2);
1408  }
1409 
1410  // Test importing with a single historical path
1411  {
1412  beast::temp_dir shardDir;
1413  beast::temp_dir historicalDir;
1414  beast::temp_dir nodeDir;
1415 
1416  auto c = testConfig(shardDir.path(), nodeDir.path());
1417 
1418  auto& historyPaths = c->section(SECTION_HISTORICAL_SHARD_PATHS);
1419  historyPaths.append({historicalDir.path()});
1420 
1421  Env env{*this, std::move(c)};
1422  DatabaseShard* db = env.app().getShardStore();
1423  Database& ndb = env.app().getNodeStore();
1424  BEAST_EXPECT(db);
1425 
1426  auto const shardCount = 4;
1427 
1428  TestData data(seedValue * 2, 4, shardCount);
1429  if (!BEAST_EXPECT(data.makeLedgers(env)))
1430  return;
1431 
1432  for (std::uint32_t i = 0; i < shardCount * ledgersPerShard; ++i)
1433  BEAST_EXPECT(saveLedger(ndb, *data.ledgers_[i]));
1434 
1435  BEAST_EXPECT(db->getShardInfo()->finalized().empty());
1436 
1437  db->importDatabase(ndb);
1438  for (std::uint32_t i = 1; i <= shardCount; ++i)
1439  waitShard(*db, i);
1440 
1441  auto const finalShards{db->getShardInfo()->finalized()};
1442  for (std::uint32_t shardIndex : {1, 2, 3, 4})
1443  BEAST_EXPECT(boost::icl::contains(finalShards, shardIndex));
1444 
1445  auto const mainPathCount = std::distance(
1446  boost::filesystem::directory_iterator(shardDir.path()),
1447  boost::filesystem::directory_iterator());
1448 
1449  // Only the two most recent shards
1450  // should be stored at the main path
1451  BEAST_EXPECT(mainPathCount == 2);
1452 
1453  auto const historicalPathCount = std::distance(
1454  boost::filesystem::directory_iterator(historicalDir.path()),
1455  boost::filesystem::directory_iterator());
1456 
1457  // All historical shards should be stored
1458  // at historical paths
1459  BEAST_EXPECT(historicalPathCount == shardCount - 2);
1460  }
1461  }
1462 
1463  void
1465  {
1466  testcase("Prepare with historical paths");
1467 
1468  using namespace test::jtx;
1469 
1470  // Create the primary shard directory
1471  beast::temp_dir primaryDir;
1472  auto config{testConfig(primaryDir.path())};
1473 
1474  // Create four historical directories
1475  std::array<beast::temp_dir, 4> historicalDirs;
1476  {
1477  auto& paths{config->section(SECTION_HISTORICAL_SHARD_PATHS)};
1478  for (auto const& dir : historicalDirs)
1479  paths.append(dir.path());
1480  }
1481 
1482  Env env{*this, std::move(config)};
1483 
1484  // Create some shards
1485  std::uint32_t constexpr numShards{4};
1486  TestData data(seedValue, 4, numShards);
1487  if (!BEAST_EXPECT(data.makeLedgers(env)))
1488  return;
1489 
1490  auto shardStore{env.app().getShardStore()};
1491  BEAST_EXPECT(shardStore);
1492 
1493  for (auto i = 0; i < numShards; ++i)
1494  {
1495  auto const shardIndex{createShard(data, *shardStore, numShards)};
1496  if (!BEAST_EXPECT(
1497  shardIndex && *shardIndex >= 1 && *shardIndex <= numShards))
1498  {
1499  return;
1500  }
1501  }
1502 
1503  {
1504  // Confirm finalized shards are in the shard store
1505  auto const finalized{shardStore->getShardInfo()->finalized()};
1506  BEAST_EXPECT(boost::icl::length(finalized) == numShards);
1507  BEAST_EXPECT(boost::icl::first(finalized) == 1);
1508  BEAST_EXPECT(boost::icl::last(finalized) == numShards);
1509  }
1510 
1511  using namespace boost::filesystem;
1512  auto const dirContains = [](beast::temp_dir const& dir,
1513  std::uint32_t shardIndex) {
1514  boost::filesystem::path const path(std::to_string(shardIndex));
1515  for (auto const& it : directory_iterator(dir.path()))
1516  if (boost::filesystem::path(it).stem() == path)
1517  return true;
1518  return false;
1519  };
1520  auto const historicalDirsContains = [&](std::uint32_t shardIndex) {
1521  for (auto const& dir : historicalDirs)
1522  if (dirContains(dir, shardIndex))
1523  return true;
1524  return false;
1525  };
1526 
1527  // Confirm two most recent shards are in the primary shard directory
1528  for (auto const shardIndex : {numShards - 1, numShards})
1529  {
1530  BEAST_EXPECT(dirContains(primaryDir, shardIndex));
1531  BEAST_EXPECT(!historicalDirsContains(shardIndex));
1532  }
1533 
1534  // Confirm remaining shards are in the historical shard directories
1535  for (auto shardIndex = 1; shardIndex < numShards - 1; ++shardIndex)
1536  {
1537  BEAST_EXPECT(!dirContains(primaryDir, shardIndex));
1538  BEAST_EXPECT(historicalDirsContains(shardIndex));
1539  }
1540 
1541  // Create some more shards to exercise recent shard rotation
1542  data = TestData(seedValue * 2, 4, numShards);
1543  if (!BEAST_EXPECT(data.makeLedgers(env, numShards)))
1544  return;
1545 
1546  for (auto i = 0; i < numShards; ++i)
1547  {
1548  auto const shardIndex{
1549  createShard(data, *shardStore, numShards * 2, numShards)};
1550  if (!BEAST_EXPECT(
1551  shardIndex && *shardIndex >= numShards + 1 &&
1552  *shardIndex <= numShards * 2))
1553  {
1554  return;
1555  }
1556  }
1557 
1558  {
1559  // Confirm finalized shards are in the shard store
1560  auto const finalized{shardStore->getShardInfo()->finalized()};
1561  BEAST_EXPECT(boost::icl::length(finalized) == numShards * 2);
1562  BEAST_EXPECT(boost::icl::first(finalized) == 1);
1563  BEAST_EXPECT(boost::icl::last(finalized) == numShards * 2);
1564  }
1565 
1566  // Confirm two most recent shards are in the primary shard directory
1567  for (auto const shardIndex : {numShards * 2 - 1, numShards * 2})
1568  {
1569  BEAST_EXPECT(dirContains(primaryDir, shardIndex));
1570  BEAST_EXPECT(!historicalDirsContains(shardIndex));
1571  }
1572 
1573  // Confirm remaining shards are in the historical shard directories
1574  for (auto shardIndex = 1; shardIndex < numShards * 2 - 1; ++shardIndex)
1575  {
1576  BEAST_EXPECT(!dirContains(primaryDir, shardIndex));
1577  BEAST_EXPECT(historicalDirsContains(shardIndex));
1578  }
1579  }
1580 
1581  void
1583  {
1584  testcase("Open shard management");
1585 
1586  using namespace test::jtx;
1587 
1588  beast::temp_dir shardDir;
1589  Env env{*this, testConfig(shardDir.path())};
1590 
1591  auto shardStore{env.app().getShardStore()};
1592  BEAST_EXPECT(shardStore);
1593 
1594  // Create one shard more than the open final limit
1595  auto const openFinalLimit{env.app().config().getValueFor(
1596  SizedItem::openFinalLimit, std::nullopt)};
1597  auto const numShards{openFinalLimit + 1};
1598 
1599  TestData data(seedValue, 2, numShards);
1600  if (!BEAST_EXPECT(data.makeLedgers(env)))
1601  return;
1602 
1603  BEAST_EXPECT(shardStore->getShardInfo()->finalized().empty());
1604 
1605  int oldestShardIndex{-1};
1606  for (auto i = 0; i < numShards; ++i)
1607  {
1608  auto shardIndex{createShard(data, *shardStore, numShards)};
1609  if (!BEAST_EXPECT(
1610  shardIndex && *shardIndex >= 1 && *shardIndex <= numShards))
1611  {
1612  return;
1613  }
1614 
1615  BEAST_EXPECT(boost::icl::contains(
1616  shardStore->getShardInfo()->finalized(), *shardIndex));
1617 
1618  if (oldestShardIndex == -1)
1619  oldestShardIndex = *shardIndex;
1620  }
1621 
1622  // The number of open shards exceeds the open limit by one.
1623  // A sweep will close enough shards to be within the limit.
1624  shardStore->sweep();
1625 
1626  // Read from the closed shard and automatically open it
1627  auto const ledgerSeq{shardStore->lastLedgerSeq(oldestShardIndex)};
1628  auto const index{ledgerSeq - ledgersPerShard - 1};
1629  BEAST_EXPECT(shardStore->fetchNodeObject(
1630  data.ledgers_[index]->info().hash, ledgerSeq));
1631  }
1632 
1633  void
1634  testShardInfo(std::uint64_t const seedValue)
1635  {
1636  testcase("Shard info");
1637 
1638  using namespace test::jtx;
1639  beast::temp_dir shardDir;
1640  Env env{*this, testConfig(shardDir.path())};
1641 
1642  auto shardStore{env.app().getShardStore()};
1643  BEAST_EXPECT(shardStore);
1644 
1645  // Check shard store is empty
1646  {
1647  auto const shardInfo{shardStore->getShardInfo()};
1648  BEAST_EXPECT(
1649  shardInfo->msgTimestamp().time_since_epoch().count() == 0);
1650  BEAST_EXPECT(shardInfo->finalizedToString().empty());
1651  BEAST_EXPECT(shardInfo->finalized().empty());
1652  BEAST_EXPECT(shardInfo->incompleteToString().empty());
1653  BEAST_EXPECT(shardInfo->incomplete().empty());
1654  }
1655 
1656  // Create an incomplete shard with index 1
1657  TestData data(seedValue, dataSizeMax, 2);
1658  if (!BEAST_EXPECT(data.makeLedgers(env)))
1659  return;
1660  if (!BEAST_EXPECT(shardStore->prepareLedger(2 * ledgersPerShard)))
1661  return;
1662 
1663  // Check shard is incomplete
1664  {
1665  auto const shardInfo{shardStore->getShardInfo()};
1666  BEAST_EXPECT(shardInfo->finalizedToString().empty());
1667  BEAST_EXPECT(shardInfo->finalized().empty());
1668  BEAST_EXPECT(shardInfo->incompleteToString() == "1:0");
1669  BEAST_EXPECT(
1670  shardInfo->incomplete().find(1) !=
1671  shardInfo->incomplete().end());
1672  }
1673 
1674  // Finalize the shard
1675  {
1676  auto shardIndex{createShard(data, *shardStore)};
1677  if (!BEAST_EXPECT(shardIndex && *shardIndex == 1))
1678  return;
1679  }
1680 
1681  // Check shard is finalized
1682  {
1683  auto const shardInfo{shardStore->getShardInfo()};
1684  BEAST_EXPECT(shardInfo->finalizedToString() == "1");
1685  BEAST_EXPECT(boost::icl::contains(shardInfo->finalized(), 1));
1686  BEAST_EXPECT(shardInfo->incompleteToString().empty());
1687  BEAST_EXPECT(shardInfo->incomplete().empty());
1688  BEAST_EXPECT(!shardInfo->update(1, ShardState::finalized, 0));
1689  BEAST_EXPECT(shardInfo->setFinalizedFromString("2"));
1690  BEAST_EXPECT(shardInfo->finalizedToString() == "2");
1691  BEAST_EXPECT(boost::icl::contains(shardInfo->finalized(), 2));
1692  }
1693 
1694  // Create an incomplete shard with index 2
1695  if (!BEAST_EXPECT(shardStore->prepareLedger(3 * ledgersPerShard)))
1696  return;
1697 
1698  // Store 10 percent of the ledgers
1699  for (std::uint32_t i = 0; i < (ledgersPerShard / 10); ++i)
1700  {
1701  auto const ledgerSeq{
1702  shardStore->prepareLedger(3 * ledgersPerShard)};
1703  if (!BEAST_EXPECT(ledgerSeq != std::nullopt))
1704  return;
1705 
1706  auto const arrInd{*ledgerSeq - ledgersPerShard - 1};
1707  if (!BEAST_EXPECT(saveLedger(*shardStore, *data.ledgers_[arrInd])))
1708  return;
1709 
1710  shardStore->setStored(data.ledgers_[arrInd]);
1711  }
1712 
1713  auto const shardInfo{shardStore->getShardInfo()};
1714  BEAST_EXPECT(shardInfo->incompleteToString() == "2:10");
1715  BEAST_EXPECT(
1716  shardInfo->incomplete().find(2) != shardInfo->incomplete().end());
1717 
1718  auto const timeStamp{env.app().timeKeeper().now()};
1719  shardInfo->setMsgTimestamp(timeStamp);
1720  BEAST_EXPECT(timeStamp == shardInfo->msgTimestamp());
1721 
1722  // Check message
1723  auto const msg{shardInfo->makeMessage(env.app())};
1724  Serializer s;
1726 
1727  BEAST_EXPECT(msg.timestamp() != 0);
1728  s.add32(msg.timestamp());
1729 
1730  // Verify incomplete shard
1731  {
1732  BEAST_EXPECT(msg.incomplete_size() == 1);
1733 
1734  auto const& incomplete{msg.incomplete(0)};
1735  BEAST_EXPECT(incomplete.shardindex() == 2);
1736  s.add32(incomplete.shardindex());
1737 
1738  BEAST_EXPECT(
1739  static_cast<ShardState>(incomplete.state()) ==
1741  s.add32(incomplete.state());
1742 
1743  BEAST_EXPECT(incomplete.has_progress());
1744  BEAST_EXPECT(incomplete.progress() == 10);
1745  s.add32(incomplete.progress());
1746  }
1747 
1748  // Verify finalized shard
1749  BEAST_EXPECT(msg.has_finalized());
1750  BEAST_EXPECT(msg.finalized() == "1");
1751  s.addRaw(msg.finalized().data(), msg.finalized().size());
1752 
1753  // Verify public key
1754  auto slice{makeSlice(msg.publickey())};
1755  BEAST_EXPECT(publicKeyType(slice));
1756 
1757  // Verify signature
1758  BEAST_EXPECT(verify(
1759  PublicKey(slice), s.slice(), makeSlice(msg.signature()), false));
1760 
1761  BEAST_EXPECT(msg.peerchain_size() == 0);
1762  }
1763 
1764  void
1766  {
1767  testcase("SQLite Database");
1768 
1769  using namespace test::jtx;
1770 
1771  beast::temp_dir shardDir;
1772  Env env{*this, testConfig(shardDir.path())};
1773 
1774  auto shardStore{env.app().getShardStore()};
1775  BEAST_EXPECT(shardStore);
1776 
1777  auto const shardCount = 3;
1778  TestData data(seedValue, 3, shardCount);
1779  if (!BEAST_EXPECT(data.makeLedgers(env)))
1780  return;
1781 
1782  BEAST_EXPECT(shardStore->getShardInfo()->finalized().empty());
1783  BEAST_EXPECT(shardStore->getShardInfo()->incompleteToString().empty());
1784 
1785  auto rdb =
1786  dynamic_cast<SQLiteDatabase*>(&env.app().getRelationalDatabase());
1787 
1788  BEAST_EXPECT(rdb);
1789 
1790  for (std::uint32_t i = 0; i < shardCount; ++i)
1791  {
1792  // Populate the shard store
1793 
1794  auto n = createShard(data, *shardStore, shardCount);
1795  if (!BEAST_EXPECT(n && *n >= 1 && *n <= shardCount))
1796  return;
1797  }
1798 
1799  // Close these databases to force the SQLiteDatabase
1800  // to use the shard databases and lookup tables.
1801  rdb->closeLedgerDB();
1802  rdb->closeTransactionDB();
1803 
1804  // Lambda for comparing Ledger objects
1805  auto infoCmp = [](auto const& a, auto const& b) {
1806  return a.hash == b.hash && a.txHash == b.txHash &&
1807  a.accountHash == b.accountHash &&
1808  a.parentHash == b.parentHash && a.drops == b.drops &&
1809  a.accepted == b.accepted && a.closeFlags == b.closeFlags &&
1810  a.closeTimeResolution == b.closeTimeResolution &&
1811  a.closeTime == b.closeTime;
1812  };
1813 
1814  for (auto const& ledger : data.ledgers_)
1815  {
1816  // Compare each test ledger to the data retrieved
1817  // from the SQLiteDatabase class
1818 
1819  if (shardStore->seqToShardIndex(ledger->seq()) <
1820  shardStore->earliestShardIndex() ||
1821  ledger->info().seq < shardStore->earliestLedgerSeq())
1822  continue;
1823 
1824  auto info = rdb->getLedgerInfoByHash(ledger->info().hash);
1825 
1826  BEAST_EXPECT(info);
1827  BEAST_EXPECT(infoCmp(*info, ledger->info()));
1828 
1829  for (auto const& transaction : ledger->txs)
1830  {
1831  // Compare each test transaction to the data
1832  // retrieved from the SQLiteDatabase class
1833 
1834  error_code_i error{rpcSUCCESS};
1835 
1836  auto reference = rdb->getTransaction(
1837  transaction.first->getTransactionID(), {}, error);
1838 
1839  BEAST_EXPECT(error == rpcSUCCESS);
1840  if (!BEAST_EXPECT(reference.index() == 0))
1841  continue;
1842 
1843  auto txn = std::get<0>(reference).first->getSTransaction();
1844 
1845  BEAST_EXPECT(
1846  transaction.first->getFullText() == txn->getFullText());
1847  }
1848  }
1849 
1850  // Create additional ledgers to test a pathway in
1851  // 'ripple::saveLedgerMeta' wherein fetching the
1852  // accepted ledger fails
1853  data = TestData(seedValue * 2, 4, 1);
1854  if (!BEAST_EXPECT(data.makeLedgers(env, shardCount)))
1855  return;
1856  }
1857 
1858 public:
1859  DatabaseShard_test() : journal_("DatabaseShard_test", *this)
1860  {
1861  }
1862 
1863  void
1864  run() override
1865  {
1866  auto seedValue = [] {
1867  static std::uint64_t seedValue = 41;
1868  seedValue += 10;
1869  return seedValue;
1870  };
1871 
1872  testStandalone();
1873  testCreateShard(seedValue());
1874  testReopenDatabase(seedValue());
1875  testGetFinalShards(seedValue());
1876  testPrepareShards(seedValue());
1877  testImportShard(seedValue());
1878  testCorruptedDatabase(seedValue());
1879  testIllegalFinalKey(seedValue());
1880  testDeterministicShard(seedValue());
1881  testImportNodeStore(seedValue());
1882  testImportWithOnlineDelete(seedValue());
1883  testImportWithHistoricalPaths(seedValue());
1884  testPrepareWithHistoricalPaths(seedValue());
1885  testOpenShardManagement(seedValue());
1886  testShardInfo(seedValue());
1887  testSQLiteDatabase(seedValue());
1888  }
1889 };
1890 
1891 BEAST_DEFINE_TESTSUITE_MANUAL(DatabaseShard, NodeStore, ripple);
1892 
1893 } // namespace NodeStore
1894 } // namespace ripple
ripple::SQLiteDatabase
Definition: SQLiteDatabase.h:27
ripple::NodeStore::uniformIntDistribution::paramType::A
const resultType A
Definition: DatabaseShard_test.cpp:68
ripple::NodeStore::DummyScheduler
Simple NodeStore Scheduler that just peforms the tasks synchronously.
Definition: DummyScheduler.h:29
ripple::SizedItem::openFinalLimit
@ openFinalLimit
ripple::NodeStore::Database::lastLedgerSeq
std::uint32_t lastLedgerSeq(std::uint32_t shardIndex) const noexcept
Calculates the last ledger sequence for a given shard index.
Definition: Database.h:271
ripple::NodeStore::DatabaseShard_test::testIllegalFinalKey
void testIllegalFinalKey(std::uint64_t const seedValue)
Definition: DatabaseShard_test.cpp:1000
ripple::NodeStore::DatabaseShard_test::testCreateShard
void testCreateShard(std::uint64_t const seedValue)
Definition: DatabaseShard_test.cpp:713
ripple::hotUNKNOWN
@ hotUNKNOWN
Definition: NodeObject.h:33
ripple::HashPrefix::ledgerMaster
@ ledgerMaster
ledger master data for signing
ripple::SHAMap::isValid
bool isValid() const
Definition: SHAMap.h:625
std::this_thread::sleep_for
T sleep_for(T... args)
ripple::NodeStore::make_ShardStore
std::unique_ptr< DatabaseShard > make_ShardStore(Application &app, Scheduler &scheduler, int readThreads, beast::Journal j)
Definition: DatabaseShardImp.cpp:2236
ripple::makeSlice
std::enable_if_t< std::is_same< T, char >::value||std::is_same< T, unsigned char >::value, Slice > makeSlice(std::array< T, N > const &a)
Definition: Slice.h:241
ripple::NodeStore::randInt
Integral randInt(Engine &engine, Integral min, Integral max)
Definition: DatabaseShard_test.cpp:149
ripple::NodeStore::DatabaseShard::getNumTasks
virtual size_t getNumTasks() const =0
Returns the number of queued tasks.
ripple::NodeStore::DatabaseShard_test::testLedgerData
void testLedgerData(TestData &data, std::shared_ptr< Ledger > ledger, std::uint32_t seq)
Definition: DatabaseShard_test.cpp:318
ripple::NodeStore::DatabaseShard_test::testReopenDatabase
void testReopenDatabase(std::uint64_t const seedValue)
Definition: DatabaseShard_test.cpp:736
ripple::NodeStore::DatabaseShard_test::TestData::nAccounts_
std::vector< int > nAccounts_
Definition: DatabaseShard_test.cpp:192
fstream
ripple::NodeStore::Database
Persistency layer for NodeObject.
Definition: Database.h:51
ripple::NodeStore::DatabaseShard_test::ledgersPerShard
static constexpr std::uint32_t ledgersPerShard
Definition: DatabaseShard_test.cpp:172
ripple::NodeStore::TestBase
Definition: TestBase.h:68
std::string
STL class.
std::shared_ptr
STL class.
ripple::SHAMap::getHash
SHAMapHash getHash() const
Definition: SHAMap.cpp:852
ripple::SHAMapNodeType::tnINNER
@ tnINNER
ripple::NodeStore::uniformIntDistribution::paramType::B
const resultType B
Definition: DatabaseShard_test.cpp:68
ripple::base_uint::isNonZero
bool isNonZero() const
Definition: base_uint.h:537
ripple::NodeStore::uniformIntDistribution::resultType
IntType resultType
Definition: DatabaseShard_test.cpp:62
ripple::NodeStore::DatabaseShard_test::testStandalone
void testStandalone()
Definition: DatabaseShard_test.cpp:663
ripple::sfAmount
const SF_AMOUNT sfAmount
ripple::Serializer::modData
Blob & modData()
Definition: Serializer.h:178
ripple::NodeStore::DatabaseShard_test::TestData::ledgers_
std::vector< std::shared_ptr< const Ledger > > ledgers_
Definition: DatabaseShard_test.cpp:201
std::vector::reserve
T reserve(T... args)
ripple::LedgerInfo::hash
uint256 hash
Definition: ReadView.h:91
ripple::NodeStore::DatabaseShard_test::journal_
test::SuiteJournal journal_
Definition: DatabaseShard_test.cpp:179
ripple::join
Stream & join(Stream &s, Iter iter, Iter end, std::string const &delimiter)
Definition: join.h:28
ripple::sfSequence
const SF_UINT32 sfSequence
ripple::NodeStore::DatabaseShard_test::TestData::accounts_
std::vector< test::jtx::Account > accounts_
Definition: DatabaseShard_test.cpp:189
ripple::NodeStore::DatabaseShard_test::testSQLiteDatabase
void testSQLiteDatabase(std::uint64_t const seedValue)
Definition: DatabaseShard_test.cpp:1765
ripple::addRaw
void addRaw(LedgerInfo const &info, Serializer &s, bool includeHash)
Definition: View.cpp:162
ripple::hotACCOUNT_NODE
@ hotACCOUNT_NODE
Definition: NodeObject.h:35
ripple::verify
bool verify(PublicKey const &publicKey, Slice const &m, Slice const &sig, bool mustBeFullyCanonical) noexcept
Verify a signature on a message.
Definition: PublicKey.cpp:272
std::vector
STL class.
std::string::find
T find(T... args)
ripple::ConfigSection::shardDatabase
static std::string shardDatabase()
Definition: ConfigSections.h:38
std::vector::size
T size(T... args)
ripple::NodeStore::DatabaseShard_test::createShard
std::optional< std::uint32_t > createShard(TestData &data, DatabaseShard &shardStore, int maxShardIndex=1, int shardOffset=0)
Definition: DatabaseShard_test.cpp:623
ripple::NodeStore::uniformIntDistribution::B
const resultType B
Definition: DatabaseShard_test.cpp:64
ripple::NodeStore::uniformIntDistribution::b
resultType b() const
Definition: DatabaseShard_test.cpp:108
std::back_inserter
T back_inserter(T... args)
ripple::NodeObject::createObject
static std::shared_ptr< NodeObject > createObject(NodeObjectType type, Blob &&data, uint256 const &hash)
Create an object from fields.
Definition: NodeObject.cpp:37
ripple::NodeStore::DatabaseShard_test::testDeterministicShard
void testDeterministicShard(std::uint64_t const seedValue)
Definition: DatabaseShard_test.cpp:1121
ripple::NodeStore::uniformIntDistribution::operator()
resultType operator()(Generator &g, const paramType &params) const
Definition: DatabaseShard_test.cpp:96
std::chrono::seconds
std::ifstream::gcount
T gcount(T... args)
ripple::NodeStore::DatabaseShard_test::TestData::payAccounts_
std::vector< std::vector< std::pair< int, int > > > payAccounts_
Definition: DatabaseShard_test.cpp:196
std::distance
T distance(T... args)
ripple::NodeStore::DatabaseShard_test::testPrepareWithHistoricalPaths
void testPrepareWithHistoricalPaths(std::uint64_t const seedValue)
Definition: DatabaseShard_test.cpp:1464
ripple::NodeStore::DatabaseShard_test::checkLedger
void checkLedger(TestData &data, DatabaseShard &db, Ledger const &ledger)
Definition: DatabaseShard_test.cpp:470
ripple::NodeStore::uniformIntDistribution::rnd
resultType rnd(Generator &g, const resultType a, const resultType b) const
Definition: DatabaseShard_test.cpp:128
ripple::NodeStore::DatabaseShard_test::maxHistoricalShards
static constexpr std::uint32_t maxHistoricalShards
Definition: DatabaseShard_test.cpp:171
ripple::from_string
bool from_string(RangeSet< T > &rs, std::string const &s)
Convert the given styled string to a RangeSet.
Definition: RangeSet.h:123
ripple::NodeStore::DatabaseShard_test::earliestSeq
static constexpr std::uint32_t earliestSeq
Definition: DatabaseShard_test.cpp:173
ripple::test::jtx::Env::app
Application & app()
Definition: Env.h:241
ripple::LedgerInfo::seq
LedgerIndex seq
Definition: ReadView.h:83
ripple::NodeStore::Database::store
virtual void store(NodeObjectType type, Blob &&data, uint256 const &hash, std::uint32_t ledgerSeq)=0
Store the object.
std::ifstream::read
T read(T... args)
ripple::SHAMapHash::isNonZero
bool isNonZero() const
Definition: SHAMapHash.h:58
ripple::NodeStore::DatabaseShard_test::TestData
Definition: DatabaseShard_test.cpp:182
ripple::hotTRANSACTION_NODE
@ hotTRANSACTION_NODE
Definition: NodeObject.h:36
ripple::ShardState
ShardState
Shard states.
Definition: nodestore/Types.h:60
ripple::NodeStore::DatabaseShard_test::nTestShards
static constexpr std::uint32_t nTestShards
Definition: DatabaseShard_test.cpp:176
ripple::NodeStore::DatabaseShard_test::run
void run() override
Definition: DatabaseShard_test.cpp:1864
ripple::SHAMap::snapShot
std::shared_ptr< SHAMap > snapShot(bool isMutable) const
Definition: SHAMap.cpp:88
ripple::NodeStore::DatabaseShard_test::iniAmount
static constexpr std::uint32_t iniAmount
Definition: DatabaseShard_test.cpp:175
ripple::NodeStore::DatabaseShard_test::TestData::TestData
TestData(std::uint64_t const seedValue, int dataSize=dataSizeMax, int numShards=1)
Definition: DatabaseShard_test.cpp:203
iostream
ripple::NodeStore::DatabaseShard_test::testImportNodeStore
void testImportNodeStore(std::uint64_t const seedValue)
Definition: DatabaseShard_test.cpp:1175
ripple::LedgerFill::expand
@ expand
Definition: LedgerToJson.h:48
ripple::LedgerInfo::txHash
uint256 txHash
Definition: ReadView.h:92
ripple::error_code_i
error_code_i
Definition: ErrorCodes.h:40
ripple::NodeStore::uniformIntDistribution::min
resultType min() const
Definition: DatabaseShard_test.cpp:114
ripple::NodeStore::DatabaseShard::prepareLedger
virtual std::optional< std::uint32_t > prepareLedger(std::uint32_t validLedgerSeq)=0
Prepare to store a new ledger in the shard being acquired.
ripple::Application::getFeeTrack
virtual LoadFeeTrack & getFeeTrack()=0
ripple::NodeStore::Shard::version
static constexpr std::uint32_t version
Definition: nodestore/impl/Shard.h:246
std::is_convertible
ripple::NodeStore::DatabaseShard_test::ripemd160File
std::string ripemd160File(std::string filename)
Definition: DatabaseShard_test.cpp:1099
std::vector::push_back
T push_back(T... args)
ripple::ttPAYMENT
@ ttPAYMENT
This transaction type executes a payment.
Definition: TxFormats.h:59
ripple::publicKeyType
std::optional< KeyType > publicKeyType(Slice const &slice)
Returns the type of public key.
Definition: PublicKey.cpp:207
ripple::NodeStore::DatabaseShard::getDatabaseImportSequence
virtual std::optional< std::uint32_t > getDatabaseImportSequence() const =0
Returns the first ledger sequence of the shard currently being imported from the NodeStore.
ripple::TxDBName
constexpr auto TxDBName
Definition: DBInit.h:73
ripple::base_uint< 256 >
ripple::NodeStore::DatabaseShard_test::TestData::makeLedgers
bool makeLedgers(test::jtx::Env &env_, std::uint32_t startIndex=0)
Definition: DatabaseShard_test.cpp:284
ripple::NodeStore::DatabaseShard_test::DatabaseShard_test
DatabaseShard_test()
Definition: DatabaseShard_test.cpp:1859
ripple::Ledger::info
LedgerInfo const & info() const override
Returns information about the ledger.
Definition: Ledger.h:152
ripple::NodeStore::DatabaseShard_test::shardStoreTimeout
static constexpr std::chrono::seconds shardStoreTimeout
Definition: DatabaseShard_test.cpp:177
ripple::rpcSUCCESS
@ rpcSUCCESS
Definition: ErrorCodes.h:44
ripple::NodeStore::Database::firstLedgerSeq
std::uint32_t firstLedgerSeq(std::uint32_t shardIndex) const noexcept
Calculates the first ledger sequence for a given shard index.
Definition: Database.h:257
ripple::NodeStore::DatabaseShard_test::testImportShard
void testImportShard(std::uint64_t const seedValue)
Definition: DatabaseShard_test.cpp:884
ripple::NodeStore::DatabaseShard_test::testConfig
std::unique_ptr< Config > testConfig(std::string const &shardDir, std::string const &nodeDir=std::string())
Definition: DatabaseShard_test.cpp:564
std::thread
STL class.
ripple::Ledger
Holds a ledger.
Definition: Ledger.h:76
ripple::Application::getLedgerMaster
virtual LedgerMaster & getLedgerMaster()=0
ripple::PublicKey
A public key.
Definition: PublicKey.h:59
chrono
ripple::NodeStore::DatabaseShard_test::defNodeDir
beast::temp_dir defNodeDir
Definition: DatabaseShard_test.cpp:180
ripple::Ledger::stateMap
SHAMap const & stateMap() const
Definition: Ledger.h:310
ripple::LedgerFill::full
@ full
Definition: LedgerToJson.h:49
ripple::NodeStore::DatabaseShard
A collection of historical shards.
Definition: DatabaseShard.h:37
ripple::Serializer::addRaw
int addRaw(Blob const &vector)
Definition: Serializer.cpp:100
ripple::SHAMapTreeNode
Definition: SHAMapTreeNode.h:53
std::to_string
T to_string(T... args)
ripple::set
bool set(T &target, std::string const &name, Section const &section)
Set a value from a configuration Section If the named value is not found or doesn't parse as a T,...
Definition: BasicConfig.h:313
std::array
STL class.
ripple::test::jtx::Env::close
bool close(NetClock::time_point closeTime, std::optional< std::chrono::milliseconds > consensusDelay=std::nullopt)
Close and advance the ledger.
Definition: Env.cpp:121
ripple::NodeStore::DatabaseShard_test::testCorruptedDatabase
void testCorruptedDatabase(std::uint64_t const seedValue)
Definition: DatabaseShard_test.cpp:945
ripple::NodeStore::uniformIntDistribution
std::uniform_int_distribution is platform dependent.
Definition: DatabaseShard_test.cpp:60
ripple::Serializer::slice
Slice slice() const noexcept
Definition: Serializer.h:63
ripple::NodeStore::BEAST_DEFINE_TESTSUITE_MANUAL
BEAST_DEFINE_TESTSUITE_MANUAL(DatabaseShard, NodeStore, ripple)
ripple::NodeStore::DatabaseShard_test::TestData::rng_
beast::xor_shift_engine rng_
Definition: DatabaseShard_test.cpp:185
ripple::ShardState::finalized
@ finalized
ripple::NodeStore::DatabaseShard_test
Definition: DatabaseShard_test.cpp:168
std::accumulate
T accumulate(T... args)
std::uint32_t
ripple::NodeStore::uniformIntDistribution::uniformIntDistribution
uniformIntDistribution(const paramType &params)
Definition: DatabaseShard_test.cpp:82
ripple::range
ClosedInterval< T > range(T low, T high)
Create a closed range interval.
Definition: RangeSet.h:53
ripple::NodeStore::DatabaseShard::prepareShards
virtual bool prepareShards(std::vector< std::uint32_t > const &shardIndexes)=0
Prepare one or more shard indexes to be imported into the database.
std::transform
T transform(T... args)
ripple::NodeStore::uniformIntDistribution::paramType
Definition: DatabaseShard_test.cpp:66
ripple::test::SuiteJournal
Definition: SuiteJournal.h:88
beast::temp_dir::path
std::string path() const
Get the native path for the temporary directory.
Definition: temp_dir.h:66
ripple::ttACCOUNT_SET
@ ttACCOUNT_SET
This transaction type adjusts various account settings.
Definition: TxFormats.h:68
ripple::Serializer
Definition: Serializer.h:39
ripple::LedgerFill::binary
@ binary
Definition: LedgerToJson.h:50
ripple::getJson
Json::Value getJson(LedgerFill const &fill)
Return a new Json::Value representing the ledger with given options.
Definition: LedgerToJson.cpp:296
ripple::Ledger::txMap
SHAMap const & txMap() const
Definition: Ledger.h:322
ripple::NodeStore::DatabaseShard::fetchLedger
virtual std::shared_ptr< Ledger > fetchLedger(uint256 const &hash, std::uint32_t seq)=0
Fetch a ledger from the shard store.
ripple::LedgerMaster::getClosedLedger
std::shared_ptr< Ledger const > getClosedLedger()
Definition: LedgerMaster.h:98
ripple::test::CaptureLogs
Log manager for CaptureSinks.
Definition: CaptureLogs.h:31
ripple::NodeStore::uniformIntDistribution::max
resultType max() const
Definition: DatabaseShard_test.cpp:120
ripple::NodeStore::DatabaseShard::importShard
virtual bool importShard(std::uint32_t shardIndex, boost::filesystem::path const &srcDir)=0
Import a shard from the shard archive handler into the shard database.
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
ripple::NodeStore::DatabaseShard_test::testImportWithOnlineDelete
void testImportWithOnlineDelete(std::uint64_t const seedValue)
Definition: DatabaseShard_test.cpp:1227
ripple::NodeStore::DatabaseShard_test::testImportWithHistoricalPaths
void testImportWithHistoricalPaths(std::uint64_t const seedValue)
Definition: DatabaseShard_test.cpp:1334
ripple::ShardState::acquire
@ acquire
ripple::ReadView::seq
LedgerIndex seq() const
Returns the sequence number of the base ledger.
Definition: ReadView.h:193
std::array::begin
T begin(T... args)
ripple::NodeStore::Database::seqToShardIndex
std::uint32_t seqToShardIndex(std::uint32_t ledgerSeq) const noexcept
Calculates the shard index for a given ledger sequence.
Definition: Database.h:283
ripple::NodeStore::DatabaseShard_test::saveLedger
bool saveLedger(Database &db, Ledger const &ledger, std::shared_ptr< Ledger const > const &next={})
Definition: DatabaseShard_test.cpp:402
beast::rngfill
void rngfill(void *buffer, std::size_t bytes, Generator &g)
Definition: rngfill.h:33
ripple::ltACCOUNT_ROOT
@ ltACCOUNT_ROOT
A ledger object which describes an account.
Definition: LedgerFormats.h:59
ripple::NodeStore::DatabaseShard_test::testGetFinalShards
void testGetFinalShards(std::uint64_t const seedValue)
Definition: DatabaseShard_test.cpp:776
beast::hash_append
std::enable_if_t< is_contiguously_hashable< T, Hasher >::value > hash_append(Hasher &h, T const &t) noexcept
Logically concatenate input data to a Hasher.
Definition: hash_append.h:236
ripple::NodeStore::uniformIntDistribution::uniformIntDistribution
uniformIntDistribution(const resultType a=0, const resultType b=std::numeric_limits< resultType >::max())
Definition: DatabaseShard_test.cpp:75
ripple::test::jtx::Env::fund
void fund(bool setDefaultRipple, STAmount const &amount, Account const &account)
Definition: Env.cpp:228
ripple::NodeStore::DatabaseShard_test::TestData::numShards_
int numShards_
Definition: DatabaseShard_test.cpp:187
ripple::NodeStore::isSame
bool isSame(std::shared_ptr< NodeObject > const &lhs, std::shared_ptr< NodeObject > const &rhs)
Returns true if objects are identical.
Definition: TestBase.h:57
ripple::NodeStore::DatabaseShard_test::TestData::isNewAccounts
bool isNewAccounts(int seq)
Definition: DatabaseShard_test.cpp:257
ripple::NodeStore::DatabaseShard::getPreShards
virtual std::string getPreShards()=0
Get shard indexes being imported.
ripple::NodeStore::DatabaseShard::setStored
virtual void setStored(std::shared_ptr< Ledger const > const &ledger)=0
Notifies the database that the given ledger has been fully acquired and stored.
ripple::NodeStore::DatabaseShard_test::dataSizeMax
static constexpr std::uint32_t dataSizeMax
Definition: DatabaseShard_test.cpp:174
std::string::empty
T empty(T... args)
std::optional< std::uint32_t >
ripple::to_string
std::string to_string(Manifest const &m)
Format the specified manifest to a string for debugging purposes.
Definition: app/misc/impl/Manifest.cpp:41
ripple::hotLEDGER
@ hotLEDGER
Definition: NodeObject.h:34
std::make_pair
T make_pair(T... args)
ripple::sfAccount
const SF_ACCOUNT sfAccount
beast::detail::xor_shift_engine
Definition: xor_shift_engine.h:32
ripple::openssl_ripemd160_hasher
Message digest functions used in the codebase.
Definition: digest.h:46
ripple::NodeStore::DatabaseShard_test::testShardInfo
void testShardInfo(std::uint64_t const seedValue)
Definition: DatabaseShard_test.cpp:1634
ripple::Serializer::add32
int add32(std::uint32_t i)
Definition: Serializer.cpp:38
ripple::LedgerInfo
Information about the notional ledger backing the view.
Definition: ReadView.h:75
ripple::NodeStore::uniformIntDistribution::A
const resultType A
Definition: DatabaseShard_test.cpp:64
std::array::end
T end(T... args)
ripple::NodeStore::Database::fetchNodeObject
std::shared_ptr< NodeObject > fetchNodeObject(uint256 const &hash, std::uint32_t ledgerSeq=0, FetchType fetchType=FetchType::synchronous, bool duplicate=false)
Fetch a node object.
Definition: Database.cpp:252
ripple::NodeStore::uniformIntDistribution::a
resultType a() const
Definition: DatabaseShard_test.cpp:102
ripple::RangeSet
boost::icl::interval_set< T, std::less, ClosedInterval< T > > RangeSet
A set of closed intervals over the domain T.
Definition: RangeSet.h:69
ripple::NodeStore::DatabaseShard_test::TestData::makeLedgerData
void makeLedgerData(test::jtx::Env &env_, std::uint32_t seq)
Definition: DatabaseShard_test.cpp:263
ripple::NodeStore::uniformIntDistribution::paramType::paramType
paramType(resultType aa, resultType bb)
Definition: DatabaseShard_test.cpp:70
numeric
ripple::NodeStore::DatabaseShard::removePreShard
virtual void removePreShard(std::uint32_t shardIndex)=0
Remove a previously prepared shard index for import.
ripple::hash_append
void hash_append(Hasher &h, ValidatorBlobInfo const &blobInfo)
Definition: ValidatorList.h:897
ripple::LoadFeeTrack::lowerLocalFee
bool lowerLocalFee()
Definition: LoadFeeTrack.cpp:65
ripple::NodeStore::DatabaseShard_test::testOpenShardManagement
void testOpenShardManagement(std::uint64_t const seedValue)
Definition: DatabaseShard_test.cpp:1582
std::unique_ptr
STL class.
ripple::sum
static auto sum(TCollection const &col)
Definition: BookStep.cpp:710
ripple::NodeStore::DatabaseShard_test::testPrepareShards
void testPrepareShards(std::uint64_t const seedValue)
Definition: DatabaseShard_test.cpp:809
ripple::LedgerFill
Definition: LedgerToJson.h:33
std::numeric_limits
ripple::NodeStore::DatabaseShard::getShardInfo
virtual std::unique_ptr< ShardInfo > getShardInfo() const =0
Query information about shards held.
ripple::NodeStore::DatabaseShard_test::bitmask2Rangeset
std::string bitmask2Rangeset(std::uint64_t bitmask)
Definition: DatabaseShard_test.cpp:540
ripple::NodeStore::Database::importDatabase
virtual void importDatabase(Database &source)=0
Import objects from another database.
std::array::data
T data(T... args)
ripple::NodeStore::uniformIntDistribution::operator()
resultType operator()(Generator &g) const
Definition: DatabaseShard_test.cpp:89
ripple::test::jtx::Env
A transaction testing environment.
Definition: Env.h:116
ripple::ConfigSection::nodeDatabase
static std::string nodeDatabase()
Definition: ConfigSections.h:33
std::this_thread::yield
T yield(T... args)
beast::temp_dir
RAII temporary directory.
Definition: temp_dir.h:33
ripple::NodeStore::DatabaseShard_test::waitShard
std::optional< std::uint32_t > waitShard(DatabaseShard &shardStore, std::uint32_t shardIndex, std::chrono::seconds timeout=shardStoreTimeout)
Definition: DatabaseShard_test.cpp:604
ripple::ReadView::txs
txs_type txs
Definition: ReadView.h:323
ripple::NodeStore::DatabaseShard_test::TestData::xrpAmount_
std::vector< int > xrpAmount_
Definition: DatabaseShard_test.cpp:198
ripple::HashPrefix::shardInfo
@ shardInfo
shard info for signing
ripple::NodeStore::DatabaseShard_test::maxSizeGb
static constexpr std::uint32_t maxSizeGb
Definition: DatabaseShard_test.cpp:170
std::ifstream
STL class.
ripple::LgrDBName
constexpr auto LgrDBName
Definition: DBInit.h:43
std::chrono::system_clock::now
T now(T... args)