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/beast/hash/hash_append.h>
23 #include <ripple/beast/utility/temp_dir.h>
24 #include <ripple/core/ConfigSections.h>
25 #include <ripple/nodestore/DatabaseShard.h>
26 #include <ripple/nodestore/DummyScheduler.h>
27 #include <ripple/nodestore/impl/DecodedBlob.h>
28 #include <ripple/nodestore/impl/Shard.h>
29 #include <ripple/protocol/digest.h>
30 #include <boost/algorithm/hex.hpp>
31 #include <chrono>
32 #include <fstream>
33 #include <iostream>
34 #include <numeric>
35 #include <openssl/ripemd.h>
36 #include <test/jtx.h>
37 #include <test/nodestore/TestBase.h>
38 
39 namespace ripple {
40 namespace NodeStore {
41 
52 template <class IntType = int>
54 {
55  using resultType = IntType;
56 
57  const resultType A, B;
58 
59  struct paramType
60  {
61  const resultType A, B;
62 
63  paramType(resultType aa, resultType bb) : A(aa), B(bb)
64  {
65  }
66  };
67 
69  const resultType a = 0,
71  : A(a), B(b)
72  {
73  }
74 
75  explicit uniformIntDistribution(const paramType& params)
76  : A(params.A), B(params.B)
77  {
78  }
79 
80  template <class Generator>
82  operator()(Generator& g) const
83  {
84  return rnd(g, A, B);
85  }
86 
87  template <class Generator>
89  operator()(Generator& g, const paramType& params) const
90  {
91  return rnd(g, params.A, params.B);
92  }
93 
95  a() const
96  {
97  return A;
98  }
99 
100  resultType
101  b() const
102  {
103  return B;
104  }
105 
106  resultType
107  min() const
108  {
109  return A;
110  }
111 
112  resultType
113  max() const
114  {
115  return B;
116  }
117 
118 private:
119  template <class Generator>
120  resultType
121  rnd(Generator& g, const resultType a, const resultType b) const
122  {
123  static_assert(
125  value,
126  "Ups...");
127  static_assert(
128  Generator::min() == 0, "If non-zero we have handle the offset");
129  const resultType range = b - a + 1;
130  assert(Generator::max() >= range); // Just for safety
131  const resultType rejectLim = g.max() % range;
132  resultType n;
133  do
134  n = g();
135  while (n <= rejectLim);
136  return (n % range) + a;
137  }
138 };
139 
140 template <class Engine, class Integral>
141 Integral
142 randInt(Engine& engine, Integral min, Integral max)
143 {
144  assert(max > min);
145 
146  // This should have no state and constructing it should
147  // be very cheap. If that turns out not to be the case
148  // it could be hand-optimized.
149  return uniformIntDistribution<Integral>(min, max)(engine);
150 }
151 
152 template <class Engine, class Integral>
153 Integral
154 randInt(Engine& engine, Integral max)
155 {
156  return randInt(engine, Integral(0), max);
157 }
158 
159 // Tests DatabaseShard class
160 //
162 {
163  static constexpr std::uint32_t maxSizeGb = 10;
164  static constexpr std::uint32_t maxHistoricalShards = 100;
165  static constexpr std::uint32_t ledgersPerShard = 256;
166  static constexpr std::uint32_t earliestSeq = ledgersPerShard + 1;
167  static constexpr std::uint32_t dataSizeMax = 4;
168  static constexpr std::uint32_t iniAmount = 1000000;
169  static constexpr std::uint32_t nTestShards = 4;
174 
175  struct TestData
176  {
177  /* ring used to generate pseudo-random sequence */
179  /* number of shards to generate */
180  int nShards_;
181  /* vector of accounts used to send test transactions */
183  /* nAccounts_[i] is the number of these accounts existed before i-th
184  * ledger */
186  /* payAccounts_[i][j] = {from, to} is the pair which consists of two
187  * number of accounts: source and destinations, which participate in
188  * j-th payment on i-th ledger */
190  /* xrpAmount_[i] is the amount for all payments on i-th ledger */
192  /* ledgers_[i] is the i-th ledger which contains the above described
193  * accounts and payments */
195 
197  std::uint64_t const seedValue,
198  int dataSize = dataSizeMax,
199  int nShards = 1)
200  : rng_(seedValue), nShards_(nShards)
201  {
202  std::uint32_t n = 0;
203  std::uint32_t nLedgers = ledgersPerShard * nShards;
204 
205  nAccounts_.reserve(nLedgers);
206  payAccounts_.reserve(nLedgers);
207  xrpAmount_.reserve(nLedgers);
208 
209  for (std::uint32_t i = 0; i < nLedgers; ++i)
210  {
211  int p;
212  if (n >= 2)
213  p = randInt(rng_, 2 * dataSize);
214  else
215  p = 0;
216 
218  pay.reserve(p);
219 
220  for (int j = 0; j < p; ++j)
221  {
222  int from, to;
223  do
224  {
225  from = randInt(rng_, n - 1);
226  to = randInt(rng_, n - 1);
227  } while (from == to);
228 
229  pay.push_back(std::make_pair(from, to));
230  }
231 
232  n += !randInt(rng_, nLedgers / dataSize);
233 
234  if (n > accounts_.size())
235  {
236  char str[9];
237  for (int j = 0; j < 8; ++j)
238  str[j] = 'a' + randInt(rng_, 'z' - 'a');
239  str[8] = 0;
240  accounts_.emplace_back(str);
241  }
242 
244  payAccounts_.push_back(std::move(pay));
245  xrpAmount_.push_back(randInt(rng_, 90) + 10);
246  }
247  }
248 
249  bool
250  isNewAccounts(int seq)
251  {
252  return nAccounts_[seq] > (seq ? nAccounts_[seq - 1] : 0);
253  }
254 
255  void
257  {
258  using namespace test::jtx;
259 
260  if (isNewAccounts(seq))
261  env_.fund(XRP(iniAmount), accounts_[nAccounts_[seq] - 1]);
262 
263  for (std::uint32_t i = 0; i < payAccounts_[seq].size(); ++i)
264  {
265  env_(
266  pay(accounts_[payAccounts_[seq][i].first],
267  accounts_[payAccounts_[seq][i].second],
268  XRP(xrpAmount_[seq])));
269  }
270  }
271 
272  bool
273  makeLedgers(test::jtx::Env& env_, std::uint32_t startIndex = 0)
274  {
275  if (startIndex == 0)
276  {
277  for (std::uint32_t i = 3; i <= ledgersPerShard; ++i)
278  {
279  if (!env_.close())
280  return false;
283  if (ledger->info().seq != i)
284  return false;
285  }
286  }
287 
288  for (std::uint32_t i = 0; i < ledgersPerShard * nShards_; ++i)
289  {
290  auto const index = i + (startIndex * ledgersPerShard);
291 
292  makeLedgerData(env_, i);
293  if (!env_.close())
294  return false;
297  if (ledger->info().seq != index + ledgersPerShard + 1)
298  return false;
299  ledgers_.push_back(ledger);
300  }
301 
302  return true;
303  }
304  };
305 
306  void
308  TestData& data,
310  std::uint32_t seq)
311  {
312  using namespace test::jtx;
313 
314  auto rootCount{0};
315  auto accCount{0};
316  auto sothCount{0};
317  for (auto const& sles : ledger->sles)
318  {
319  if (sles->getType() == ltACCOUNT_ROOT)
320  {
321  int sq = sles->getFieldU32(sfSequence);
322  int reqsq = -1;
323  const auto id = sles->getAccountID(sfAccount);
324 
325  for (int i = 0; i < data.accounts_.size(); ++i)
326  {
327  if (id == data.accounts_[i].id())
328  {
329  reqsq = ledgersPerShard + 1;
330  for (int j = 0; j <= seq; ++j)
331  if (data.nAccounts_[j] > i + 1 ||
332  (data.nAccounts_[j] == i + 1 &&
333  !data.isNewAccounts(j)))
334  {
335  for (int k = 0; k < data.payAccounts_[j].size();
336  ++k)
337  if (data.payAccounts_[j][k].first == i)
338  reqsq++;
339  }
340  else
341  reqsq++;
342  ++accCount;
343  break;
344  }
345  }
346  if (reqsq == -1)
347  {
348  reqsq = data.nAccounts_[seq] + 1;
349  ++rootCount;
350  }
351  BEAST_EXPECT(sq == reqsq);
352  }
353  else
354  ++sothCount;
355  }
356  BEAST_EXPECT(rootCount == 1);
357  BEAST_EXPECT(accCount == data.nAccounts_[seq]);
358  BEAST_EXPECT(sothCount == 3);
359 
360  auto iniCount{0};
361  auto setCount{0};
362  auto payCount{0};
363  auto tothCount{0};
364  for (auto const& tx : ledger->txs)
365  {
366  if (tx.first->getTxnType() == ttPAYMENT)
367  {
368  std::int64_t xrpAmount =
369  tx.first->getFieldAmount(sfAmount).xrp().decimalXRP();
370  if (xrpAmount == iniAmount)
371  ++iniCount;
372  else
373  {
374  ++payCount;
375  BEAST_EXPECT(xrpAmount == data.xrpAmount_[seq]);
376  }
377  }
378  else if (tx.first->getTxnType() == ttACCOUNT_SET)
379  ++setCount;
380  else
381  ++tothCount;
382  }
383  int newacc = data.isNewAccounts(seq) ? 1 : 0;
384  BEAST_EXPECT(iniCount == newacc);
385  BEAST_EXPECT(setCount == newacc);
386  BEAST_EXPECT(payCount == data.payAccounts_[seq].size());
387  BEAST_EXPECT(tothCount == !seq);
388  }
389 
390  bool
392  Database& db,
393  Ledger const& ledger,
394  std::shared_ptr<Ledger const> const& next = {})
395  {
396  // Store header
397  {
398  Serializer s(sizeof(std::uint32_t) + sizeof(LedgerInfo));
399  s.add32(HashPrefix::ledgerMaster);
400  addRaw(ledger.info(), s);
401  db.store(
402  hotLEDGER,
403  std::move(s.modData()),
404  ledger.info().hash,
405  ledger.info().seq);
406  }
407 
408  // Store the state map
409  auto visitAcc = [&](SHAMapTreeNode const& node) {
410  Serializer s;
411  node.serializeWithPrefix(s);
412  db.store(
413  node.getType() == SHAMapNodeType::tnINNER ? hotUNKNOWN
414  : hotACCOUNT_NODE,
415  std::move(s.modData()),
416  node.getHash().as_uint256(),
417  ledger.info().seq);
418  return true;
419  };
420 
421  if (ledger.stateMap().getHash().isNonZero())
422  {
423  if (!ledger.stateMap().isValid())
424  return false;
425  if (next && next->info().parentHash == ledger.info().hash)
426  {
427  auto have = next->stateMap().snapShot(false);
428  ledger.stateMap().snapShot(false)->visitDifferences(
429  &(*have), visitAcc);
430  }
431  else
432  ledger.stateMap().snapShot(false)->visitNodes(visitAcc);
433  }
434 
435  // Store the transaction map
436  auto visitTx = [&](SHAMapTreeNode& node) {
437  Serializer s;
438  node.serializeWithPrefix(s);
439  db.store(
440  node.getType() == SHAMapNodeType::tnINNER ? hotUNKNOWN
442  std::move(s.modData()),
443  node.getHash().as_uint256(),
444  ledger.info().seq);
445  return true;
446  };
447 
448  if (ledger.info().txHash.isNonZero())
449  {
450  if (!ledger.txMap().isValid())
451  return false;
452  ledger.txMap().snapShot(false)->visitNodes(visitTx);
453  }
454 
455  return true;
456  }
457 
458  void
459  checkLedger(TestData& data, DatabaseShard& db, Ledger const& ledger)
460  {
461  auto fetched = db.fetchLedger(ledger.info().hash, ledger.info().seq);
462  if (!BEAST_EXPECT(fetched))
463  return;
464 
465  testLedgerData(data, fetched, ledger.info().seq - ledgersPerShard - 1);
466 
467  // verify the metadata/header info by serializing to json
468  BEAST_EXPECT(
470  ledger, nullptr, LedgerFill::full | LedgerFill::expand}) ==
472  *fetched, nullptr, LedgerFill::full | LedgerFill::expand}));
473 
474  BEAST_EXPECT(
476  ledger, nullptr, LedgerFill::full | LedgerFill::binary}) ==
478  *fetched, nullptr, LedgerFill::full | LedgerFill::binary}));
479 
480  // walk shamap and validate each node
481  auto fcompAcc = [&](SHAMapTreeNode& node) -> bool {
482  Serializer s;
483  node.serializeWithPrefix(s);
484  auto nSrc{NodeObject::createObject(
485  node.getType() == SHAMapNodeType::tnINNER ? hotUNKNOWN
486  : hotACCOUNT_NODE,
487  std::move(s.modData()),
488  node.getHash().as_uint256())};
489  if (!BEAST_EXPECT(nSrc))
490  return false;
491 
492  auto nDst = db.fetchNodeObject(
493  node.getHash().as_uint256(), ledger.info().seq);
494  if (!BEAST_EXPECT(nDst))
495  return false;
496 
497  BEAST_EXPECT(isSame(nSrc, nDst));
498 
499  return true;
500  };
501  if (ledger.stateMap().getHash().isNonZero())
502  ledger.stateMap().snapShot(false)->visitNodes(fcompAcc);
503 
504  auto fcompTx = [&](SHAMapTreeNode& node) -> bool {
505  Serializer s;
506  node.serializeWithPrefix(s);
507  auto nSrc{NodeObject::createObject(
508  node.getType() == SHAMapNodeType::tnINNER ? hotUNKNOWN
510  std::move(s.modData()),
511  node.getHash().as_uint256())};
512  if (!BEAST_EXPECT(nSrc))
513  return false;
514 
515  auto nDst = db.fetchNodeObject(
516  node.getHash().as_uint256(), ledger.info().seq);
517  if (!BEAST_EXPECT(nDst))
518  return false;
519 
520  BEAST_EXPECT(isSame(nSrc, nDst));
521 
522  return true;
523  };
524  if (ledger.info().txHash.isNonZero())
525  ledger.txMap().snapShot(false)->visitNodes(fcompTx);
526  }
527 
530  {
532  if (!bitmask)
533  return set;
534  bool empty = true;
535 
536  for (std::uint32_t i = 0; i < 64 && bitmask; i++)
537  {
538  if (bitmask & (1ll << i))
539  {
540  if (!empty)
541  set += ",";
542  set += std::to_string(i);
543  empty = false;
544  }
545  }
546 
548  from_string(rs, set);
549  return to_string(rs);
550  }
551 
554  std::string const& shardDir,
555  std::string const& nodeDir = std::string())
556  {
557  using namespace test::jtx;
558 
559  return envconfig([&](std::unique_ptr<Config> cfg) {
560  // Shard store configuration
561  cfg->overwrite(ConfigSection::shardDatabase(), "path", shardDir);
562  cfg->overwrite(
564  "max_historical_shards",
566  cfg->overwrite(
568  "ledgers_per_shard",
570  cfg->overwrite(
572  "earliest_seq",
574 
575  // Node store configuration
576  cfg->overwrite(
578  "earliest_seq",
580  cfg->overwrite(
582  "path",
583  nodeDir.empty() ? defNodeDir.path() : nodeDir);
584  return cfg;
585  });
586  }
587 
590  DatabaseShard& db,
591  int shardIndex,
593  {
595  auto start = std::chrono::system_clock::now();
596  auto end = start + timeout;
597  while (!from_string(rs, db.getCompleteShards()) ||
598  !boost::icl::contains(rs, shardIndex))
599  {
600  if (!BEAST_EXPECT(std::chrono::system_clock::now() < end))
601  return {};
603  }
604 
605  return shardIndex;
606  }
607 
610  TestData& data,
611  DatabaseShard& db,
612  int maxShardNumber = 1,
613  int ledgerOffset = 0)
614  {
615  int shardIndex{-1};
616 
617  for (std::uint32_t i = 0; i < ledgersPerShard; ++i)
618  {
619  auto const ledgerSeq{
620  db.prepareLedger((maxShardNumber + 1) * ledgersPerShard)};
621  if (!BEAST_EXPECT(ledgerSeq != std::nullopt))
622  return {};
623 
624  shardIndex = db.seqToShardIndex(*ledgerSeq);
625 
626  int const arrInd = *ledgerSeq - (ledgersPerShard * ledgerOffset) -
627  ledgersPerShard - 1;
628  BEAST_EXPECT(
629  arrInd >= 0 && arrInd < maxShardNumber * ledgersPerShard);
630  BEAST_EXPECT(saveLedger(db, *data.ledgers_[arrInd]));
631  if (arrInd % ledgersPerShard == (ledgersPerShard - 1))
632  {
633  uint256 const finalKey_{0};
634  Serializer s;
636  s.add32(db.firstLedgerSeq(shardIndex));
637  s.add32(db.lastLedgerSeq(shardIndex));
638  s.addRaw(data.ledgers_[arrInd]->info().hash.data(), 256 / 8);
639  db.store(
640  hotUNKNOWN, std::move(s.modData()), finalKey_, *ledgerSeq);
641  }
642  db.setStored(data.ledgers_[arrInd]);
643  }
644 
645  return waitShard(db, shardIndex);
646  }
647 
648  void
650  {
651  testcase("Standalone");
652 
653  using namespace test::jtx;
654 
655  beast::temp_dir shardDir;
656  Env env{*this, testConfig(shardDir.path())};
657  DummyScheduler scheduler;
658  RootStoppable parent("TestRootStoppable");
659 
661  make_ShardStore(env.app(), parent, scheduler, 2, journal_);
662 
663  BEAST_EXPECT(db);
664  BEAST_EXPECT(db->ledgersPerShard() == db->ledgersPerShardDefault);
665  BEAST_EXPECT(db->init());
666  BEAST_EXPECT(db->ledgersPerShard() == ledgersPerShard);
667  BEAST_EXPECT(db->seqToShardIndex(ledgersPerShard + 1) == 1);
668  BEAST_EXPECT(db->seqToShardIndex(2 * ledgersPerShard) == 1);
669  BEAST_EXPECT(db->seqToShardIndex(2 * ledgersPerShard + 1) == 2);
670  BEAST_EXPECT(
671  db->earliestShardIndex() == (earliestSeq - 1) / ledgersPerShard);
672  BEAST_EXPECT(db->firstLedgerSeq(1) == ledgersPerShard + 1);
673  BEAST_EXPECT(db->lastLedgerSeq(1) == 2 * ledgersPerShard);
674  BEAST_EXPECT(db->getRootDir().string() == shardDir.path());
675  }
676 
677  void
679  {
680  testcase("Create shard");
681 
682  using namespace test::jtx;
683 
684  beast::temp_dir shardDir;
685  Env env{*this, testConfig(shardDir.path())};
686  DatabaseShard* db = env.app().getShardStore();
687  BEAST_EXPECT(db);
688 
689  TestData data(seedValue);
690  if (!BEAST_EXPECT(data.makeLedgers(env)))
691  return;
692 
693  if (!createShard(data, *db, 1))
694  return;
695 
696  for (std::uint32_t i = 0; i < ledgersPerShard; ++i)
697  checkLedger(data, *db, *data.ledgers_[i]);
698  }
699 
700  void
702  {
703  testcase("Reopen shard store");
704 
705  using namespace test::jtx;
706 
707  beast::temp_dir shardDir;
708  {
709  Env env{*this, testConfig(shardDir.path())};
710  DatabaseShard* db = env.app().getShardStore();
711  BEAST_EXPECT(db);
712 
713  TestData data(seedValue, 4, 2);
714  if (!BEAST_EXPECT(data.makeLedgers(env)))
715  return;
716 
717  for (std::uint32_t i = 0; i < 2; ++i)
718  if (!createShard(data, *db, 2))
719  return;
720  }
721  {
722  Env env{*this, testConfig(shardDir.path())};
723  DatabaseShard* db = env.app().getShardStore();
724  BEAST_EXPECT(db);
725 
726  TestData data(seedValue, 4, 2);
727  if (!BEAST_EXPECT(data.makeLedgers(env)))
728  return;
729 
730  for (std::uint32_t i = 1; i <= 2; ++i)
731  waitShard(*db, i);
732 
733  for (std::uint32_t i = 0; i < 2 * ledgersPerShard; ++i)
734  checkLedger(data, *db, *data.ledgers_[i]);
735  }
736  }
737 
738  void
740  {
741  testcase("Get complete shards");
742 
743  using namespace test::jtx;
744 
745  beast::temp_dir shardDir;
746  Env env{*this, testConfig(shardDir.path())};
747  DatabaseShard* db = env.app().getShardStore();
748  BEAST_EXPECT(db);
749 
750  TestData data(seedValue, 2, nTestShards);
751  if (!BEAST_EXPECT(data.makeLedgers(env)))
752  return;
753 
754  BEAST_EXPECT(db->getCompleteShards() == "");
755 
756  std::uint64_t bitMask = 0;
757 
758  for (std::uint32_t i = 0; i < nTestShards; ++i)
759  {
760  auto n = createShard(data, *db, nTestShards);
761  if (!BEAST_EXPECT(n && *n >= 1 && *n <= nTestShards))
762  return;
763  bitMask |= 1ll << *n;
764  BEAST_EXPECT(db->getCompleteShards() == bitmask2Rangeset(bitMask));
765  }
766  }
767 
768  void
770  {
771  testcase("Prepare shards");
772 
773  using namespace test::jtx;
774 
775  beast::temp_dir shardDir;
776  Env env{*this, testConfig(shardDir.path())};
777  DatabaseShard* db = env.app().getShardStore();
778  BEAST_EXPECT(db);
779 
780  TestData data(seedValue, 1, nTestShards);
781  if (!BEAST_EXPECT(data.makeLedgers(env)))
782  return;
783 
784  std::uint64_t bitMask = 0;
785  BEAST_EXPECT(db->getPreShards() == "");
786 
787  for (std::uint32_t i = 0; i < nTestShards * 2; ++i)
788  {
789  std::uint32_t n = randInt(data.rng_, nTestShards - 1) + 1;
790  if (bitMask & (1ll << n))
791  {
792  db->removePreShard(n);
793  bitMask &= ~(1ll << n);
794  }
795  else
796  {
797  db->prepareShards({n});
798  bitMask |= 1ll << n;
799  }
800  BEAST_EXPECT(db->getPreShards() == bitmask2Rangeset(bitMask));
801  }
802 
803  // test illegal cases
804  // adding shards with too large number
805  db->prepareShards({0});
806  BEAST_EXPECT(db->getPreShards() == bitmask2Rangeset(bitMask));
807  db->prepareShards({nTestShards + 1});
808  BEAST_EXPECT(db->getPreShards() == bitmask2Rangeset(bitMask));
809  db->prepareShards({nTestShards + 2});
810  BEAST_EXPECT(db->getPreShards() == bitmask2Rangeset(bitMask));
811 
812  // create shards which are not prepared for import
813  BEAST_EXPECT(db->getCompleteShards() == "");
814 
815  std::uint64_t bitMask2 = 0;
816 
817  for (std::uint32_t i = 0; i < nTestShards; ++i)
818  {
819  auto n = createShard(data, *db, nTestShards);
820  if (!BEAST_EXPECT(n && *n >= 1 && *n <= nTestShards))
821  return;
822  bitMask2 |= 1ll << *n;
823  BEAST_EXPECT(db->getPreShards() == bitmask2Rangeset(bitMask));
824  BEAST_EXPECT(db->getCompleteShards() == bitmask2Rangeset(bitMask2));
825  BEAST_EXPECT((bitMask & bitMask2) == 0);
826  if ((bitMask | bitMask2) == ((1ll << nTestShards) - 1) << 1)
827  break;
828  }
829 
830  // try to create another shard
831  BEAST_EXPECT(
833  std::nullopt);
834  }
835 
836  void
838  {
839  testcase("Import shard");
840 
841  using namespace test::jtx;
842 
843  beast::temp_dir importDir;
844  TestData data(seedValue, 2);
845 
846  {
847  Env env{*this, testConfig(importDir.path())};
848  DatabaseShard* db = env.app().getShardStore();
849  BEAST_EXPECT(db);
850 
851  if (!BEAST_EXPECT(data.makeLedgers(env)))
852  return;
853 
854  if (!createShard(data, *db, 1))
855  return;
856 
857  for (std::uint32_t i = 0; i < ledgersPerShard; ++i)
858  checkLedger(data, *db, *data.ledgers_[i]);
859 
860  data.ledgers_.clear();
861  }
862 
863  boost::filesystem::path importPath(importDir.path());
864  importPath /= "1";
865 
866  {
867  beast::temp_dir shardDir;
868  Env env{*this, testConfig(shardDir.path())};
869  DatabaseShard* db = env.app().getShardStore();
870  BEAST_EXPECT(db);
871 
872  if (!BEAST_EXPECT(data.makeLedgers(env)))
873  return;
874 
875  db->prepareShards({1});
876  BEAST_EXPECT(db->getPreShards() == bitmask2Rangeset(2));
877 
878  using namespace boost::filesystem;
879  remove_all(importPath / LgrDBName);
880  remove_all(importPath / TxDBName);
881 
882  if (!BEAST_EXPECT(db->importShard(1, importPath)))
883  return;
884 
885  BEAST_EXPECT(db->getPreShards() == "");
886 
887  auto n = waitShard(*db, 1);
888  if (!BEAST_EXPECT(n && *n == 1))
889  return;
890 
891  for (std::uint32_t i = 0; i < ledgersPerShard; ++i)
892  checkLedger(data, *db, *data.ledgers_[i]);
893  }
894  }
895 
896  void
898  {
899  testcase("Corrupted shard store");
900 
901  using namespace test::jtx;
902 
903  beast::temp_dir shardDir;
904  {
905  TestData data(seedValue, 4, 2);
906  {
907  Env env{*this, testConfig(shardDir.path())};
908  DatabaseShard* db = env.app().getShardStore();
909  BEAST_EXPECT(db);
910 
911  if (!BEAST_EXPECT(data.makeLedgers(env)))
912  return;
913 
914  for (std::uint32_t i = 0; i < 2; ++i)
915  if (!BEAST_EXPECT(createShard(data, *db, 2)))
916  return;
917  }
918 
919  boost::filesystem::path path = shardDir.path();
920  path /= std::string("2");
921  path /= "nudb.dat";
922 
923  FILE* f = fopen(path.string().c_str(), "r+b");
924  if (!BEAST_EXPECT(f))
925  return;
926  char buf[256];
927  beast::rngfill(buf, sizeof(buf), data.rng_);
928  BEAST_EXPECT(fwrite(buf, 1, 256, f) == 256);
929  fclose(f);
930  }
931 
932  Env env{*this, testConfig(shardDir.path())};
933  DatabaseShard* db = env.app().getShardStore();
934  BEAST_EXPECT(db);
935 
936  TestData data(seedValue, 4, 2);
937  if (!BEAST_EXPECT(data.makeLedgers(env)))
938  return;
939 
940  for (std::uint32_t i = 1; i <= 1; ++i)
941  waitShard(*db, i);
942 
943  BEAST_EXPECT(db->getCompleteShards() == bitmask2Rangeset(0x2));
944 
945  for (std::uint32_t i = 0; i < 1 * ledgersPerShard; ++i)
946  checkLedger(data, *db, *data.ledgers_[i]);
947  }
948 
949  void
951  {
952  testcase("Illegal finalKey");
953 
954  using namespace test::jtx;
955 
956  for (int i = 0; i < 5; ++i)
957  {
958  beast::temp_dir shardDir;
959  {
960  Env env{*this, testConfig(shardDir.path())};
961  DatabaseShard* db = env.app().getShardStore();
962  BEAST_EXPECT(db);
963 
964  TestData data(seedValue + i, 2);
965  if (!BEAST_EXPECT(data.makeLedgers(env)))
966  return;
967 
968  int shardIndex{-1};
969  for (std::uint32_t j = 0; j < ledgersPerShard; ++j)
970  {
971  auto const ledgerSeq{
972  db->prepareLedger(2 * ledgersPerShard)};
973  if (!BEAST_EXPECT(ledgerSeq != std::nullopt))
974  return;
975 
976  shardIndex = db->seqToShardIndex(*ledgerSeq);
977  int arrInd = *ledgerSeq - ledgersPerShard - 1;
978  BEAST_EXPECT(arrInd >= 0 && arrInd < ledgersPerShard);
979  BEAST_EXPECT(saveLedger(*db, *data.ledgers_[arrInd]));
980  if (arrInd % ledgersPerShard == (ledgersPerShard - 1))
981  {
982  uint256 const finalKey_{0};
983  Serializer s;
984  s.add32(Shard::version + (i == 0));
985  s.add32(db->firstLedgerSeq(shardIndex) + (i == 1));
986  s.add32(db->lastLedgerSeq(shardIndex) - (i == 3));
987  s.addRaw(
988  data.ledgers_[arrInd - (i == 4)]
989  ->info()
990  .hash.data(),
991  256 / 8);
992  db->store(
993  hotUNKNOWN,
994  std::move(s.modData()),
995  finalKey_,
996  *ledgerSeq);
997  }
998  db->setStored(data.ledgers_[arrInd]);
999  }
1000 
1001  if (i == 2)
1002  waitShard(*db, shardIndex);
1003  else
1004  {
1005  boost::filesystem::path path(shardDir.path());
1006  path /= "1";
1007  boost::system::error_code ec;
1008  auto start = std::chrono::system_clock::now();
1009  auto end = start + shardStoreTimeout;
1010  while (std::chrono::system_clock::now() < end &&
1011  boost::filesystem::exists(path, ec))
1012  {
1014  }
1015  }
1016 
1017  BEAST_EXPECT(
1018  db->getCompleteShards() ==
1019  bitmask2Rangeset(i == 2 ? 2 : 0));
1020  }
1021 
1022  {
1023  Env env{*this, testConfig(shardDir.path())};
1024  DatabaseShard* db = env.app().getShardStore();
1025  BEAST_EXPECT(db);
1026 
1027  TestData data(seedValue + i, 2);
1028  if (!BEAST_EXPECT(data.makeLedgers(env)))
1029  return;
1030 
1031  if (i == 2)
1032  waitShard(*db, 1);
1033 
1034  BEAST_EXPECT(
1035  db->getCompleteShards() ==
1036  bitmask2Rangeset(i == 2 ? 2 : 0));
1037 
1038  if (i == 2)
1039  {
1040  for (std::uint32_t j = 0; j < ledgersPerShard; ++j)
1041  checkLedger(data, *db, *data.ledgers_[j]);
1042  }
1043  }
1044  }
1045  }
1046 
1047  void
1048  testImport(std::uint64_t const seedValue)
1049  {
1050  testcase("Import node store");
1051 
1052  using namespace test::jtx;
1053 
1054  beast::temp_dir shardDir;
1055  {
1056  beast::temp_dir nodeDir;
1057  Env env{*this, testConfig(shardDir.path(), nodeDir.path())};
1058  DatabaseShard* db = env.app().getShardStore();
1059  Database& ndb = env.app().getNodeStore();
1060  BEAST_EXPECT(db);
1061 
1062  TestData data(seedValue, 4, 2);
1063  if (!BEAST_EXPECT(data.makeLedgers(env)))
1064  return;
1065 
1066  for (std::uint32_t i = 0; i < 2 * ledgersPerShard; ++i)
1067  BEAST_EXPECT(saveLedger(ndb, *data.ledgers_[i]));
1068 
1069  BEAST_EXPECT(db->getCompleteShards() == bitmask2Rangeset(0));
1070  db->import(ndb);
1071  for (std::uint32_t i = 1; i <= 2; ++i)
1072  waitShard(*db, i);
1073  BEAST_EXPECT(db->getCompleteShards() == bitmask2Rangeset(0x6));
1074  }
1075  {
1076  Env env{*this, testConfig(shardDir.path())};
1077  DatabaseShard* db = env.app().getShardStore();
1078  BEAST_EXPECT(db);
1079 
1080  TestData data(seedValue, 4, 2);
1081  if (!BEAST_EXPECT(data.makeLedgers(env)))
1082  return;
1083 
1084  for (std::uint32_t i = 1; i <= 2; ++i)
1085  waitShard(*db, i);
1086 
1087  BEAST_EXPECT(db->getCompleteShards() == bitmask2Rangeset(0x6));
1088 
1089  for (std::uint32_t i = 0; i < 2 * ledgersPerShard; ++i)
1090  checkLedger(data, *db, *data.ledgers_[i]);
1091  }
1092  }
1093 
1094  std::string
1096  {
1097  using beast::hash_append;
1098  std::ifstream input(filename, std::ios::in | std::ios::binary);
1099  char buf[4096];
1100  ripemd160_hasher h;
1101 
1102  while (input.read(buf, 4096), input.gcount() > 0)
1103  hash_append(h, buf, input.gcount());
1104 
1105  auto const binResult = static_cast<ripemd160_hasher::result_type>(h);
1106  const auto charDigest = binResult.data();
1107  std::string result;
1108  boost::algorithm::hex(
1109  charDigest,
1110  charDigest + sizeof(binResult),
1111  std::back_inserter(result));
1112 
1113  return result;
1114  }
1115 
1116  void
1118  {
1119  testcase("Deterministic shards");
1120 
1121  using namespace test::jtx;
1122 
1123  std::string ripemd160Key("B2F9DB61F714A82889966F097CD615C36DB2B01D"),
1124  ripemd160Dat("6DB1D02CD019F09198FE80DB5A7D707F0C6BFF4C");
1125 
1126  for (int i = 0; i < 2; i++)
1127  {
1128  beast::temp_dir shardDir;
1129  {
1130  Env env{*this, testConfig(shardDir.path())};
1131  DatabaseShard* db = env.app().getShardStore();
1132  BEAST_EXPECT(db);
1133 
1134  TestData data(seedValue, 4);
1135  if (!BEAST_EXPECT(data.makeLedgers(env)))
1136  return;
1137 
1138  if (createShard(data, *db) < 0)
1139  return;
1140  }
1141  {
1142  Env env{*this, testConfig(shardDir.path())};
1143  DatabaseShard* db = env.app().getShardStore();
1144  BEAST_EXPECT(db);
1145 
1146  TestData data(seedValue, 4);
1147  if (!BEAST_EXPECT(data.makeLedgers(env)))
1148  return;
1149 
1150  waitShard(*db, 1);
1151 
1152  for (std::uint32_t j = 0; j < ledgersPerShard; ++j)
1153  checkLedger(data, *db, *data.ledgers_[j]);
1154  }
1155 
1156  boost::filesystem::path path(shardDir.path());
1157  path /= "1";
1158  boost::filesystem::path keypath = path / "nudb.key";
1159  std::string key = ripemd160File(keypath.string());
1160  boost::filesystem::path datpath = path / "nudb.dat";
1161  std::string dat = ripemd160File(datpath.string());
1162 
1163  std::cerr << "Iteration " << i << ": RIPEMD160[nudb.key] = " << key
1164  << std::endl;
1165  std::cerr << "Iteration " << i << ": RIPEMD160[nudb.dat] = " << dat
1166  << std::endl;
1167 
1168  BEAST_EXPECT(key == ripemd160Key);
1169  BEAST_EXPECT(dat == ripemd160Dat);
1170  }
1171  }
1172 
1173  void
1175  {
1176  testcase("Import with historical paths");
1177 
1178  using namespace test::jtx;
1179 
1180  // Test importing with multiple historical
1181  // paths
1182  {
1183  beast::temp_dir shardDir;
1184  std::array<beast::temp_dir, 4> historicalDirs;
1186 
1188  historicalDirs.begin(),
1189  historicalDirs.end(),
1190  historicalPaths.begin(),
1191  [](const beast::temp_dir& dir) { return dir.path(); });
1192 
1193  beast::temp_dir nodeDir;
1194  auto c = testConfig(shardDir.path(), nodeDir.path());
1195 
1196  auto& historyPaths = c->section(SECTION_HISTORICAL_SHARD_PATHS);
1197  historyPaths.append(
1198  {historicalPaths[0].string(),
1199  historicalPaths[1].string(),
1200  historicalPaths[2].string(),
1201  historicalPaths[3].string()});
1202 
1203  Env env{*this, std::move(c)};
1204  DatabaseShard* db = env.app().getShardStore();
1205  Database& ndb = env.app().getNodeStore();
1206  BEAST_EXPECT(db);
1207 
1208  auto const ledgerCount = 4;
1209 
1210  TestData data(seedValue, 4, ledgerCount);
1211  if (!BEAST_EXPECT(data.makeLedgers(env)))
1212  return;
1213 
1214  for (std::uint32_t i = 0; i < ledgerCount * ledgersPerShard; ++i)
1215  BEAST_EXPECT(saveLedger(ndb, *data.ledgers_[i]));
1216 
1217  BEAST_EXPECT(db->getCompleteShards() == bitmask2Rangeset(0));
1218 
1219  db->import(ndb);
1220  for (std::uint32_t i = 1; i <= ledgerCount; ++i)
1221  waitShard(*db, i);
1222 
1223  BEAST_EXPECT(db->getCompleteShards() == bitmask2Rangeset(0b11110));
1224 
1225  auto const mainPathCount = std::distance(
1226  boost::filesystem::directory_iterator(shardDir.path()),
1227  boost::filesystem::directory_iterator());
1228 
1229  // Only the two most recent shards
1230  // should be stored at the main path
1231  BEAST_EXPECT(mainPathCount == 2);
1232 
1233  auto const historicalPathCount = std::accumulate(
1234  historicalPaths.begin(),
1235  historicalPaths.end(),
1236  0,
1237  [](int const sum, boost::filesystem::path const& path) {
1238  return sum +
1239  std::distance(
1240  boost::filesystem::directory_iterator(path),
1241  boost::filesystem::directory_iterator());
1242  });
1243 
1244  // All historical shards should be stored
1245  // at historical paths
1246  BEAST_EXPECT(historicalPathCount == ledgerCount - 2);
1247  }
1248 
1249  // Test importing with a single historical
1250  // path
1251  {
1252  beast::temp_dir shardDir;
1253  beast::temp_dir historicalDir;
1254  beast::temp_dir nodeDir;
1255 
1256  auto c = testConfig(shardDir.path(), nodeDir.path());
1257 
1258  auto& historyPaths = c->section(SECTION_HISTORICAL_SHARD_PATHS);
1259  historyPaths.append({historicalDir.path()});
1260 
1261  Env env{*this, std::move(c)};
1262  DatabaseShard* db = env.app().getShardStore();
1263  Database& ndb = env.app().getNodeStore();
1264  BEAST_EXPECT(db);
1265 
1266  auto const ledgerCount = 4;
1267 
1268  TestData data(seedValue * 2, 4, ledgerCount);
1269  if (!BEAST_EXPECT(data.makeLedgers(env)))
1270  return;
1271 
1272  for (std::uint32_t i = 0; i < ledgerCount * ledgersPerShard; ++i)
1273  BEAST_EXPECT(saveLedger(ndb, *data.ledgers_[i]));
1274 
1275  BEAST_EXPECT(db->getCompleteShards() == bitmask2Rangeset(0));
1276 
1277  db->import(ndb);
1278  for (std::uint32_t i = 1; i <= ledgerCount; ++i)
1279  waitShard(*db, i);
1280 
1281  BEAST_EXPECT(db->getCompleteShards() == bitmask2Rangeset(0b11110));
1282 
1283  auto const mainPathCount = std::distance(
1284  boost::filesystem::directory_iterator(shardDir.path()),
1285  boost::filesystem::directory_iterator());
1286 
1287  // Only the two most recent shards
1288  // should be stored at the main path
1289  BEAST_EXPECT(mainPathCount == 2);
1290 
1291  auto const historicalPathCount = std::distance(
1292  boost::filesystem::directory_iterator(historicalDir.path()),
1293  boost::filesystem::directory_iterator());
1294 
1295  // All historical shards should be stored
1296  // at historical paths
1297  BEAST_EXPECT(historicalPathCount == ledgerCount - 2);
1298  }
1299  }
1300 
1301  void
1303  {
1304  testcase("Prepare with historical paths");
1305 
1306  using namespace test::jtx;
1307 
1308  // Test importing with multiple historical
1309  // paths
1310  {
1311  beast::temp_dir shardDir;
1312  std::array<beast::temp_dir, 4> historicalDirs;
1314 
1316  historicalDirs.begin(),
1317  historicalDirs.end(),
1318  historicalPaths.begin(),
1319  [](const beast::temp_dir& dir) { return dir.path(); });
1320 
1321  beast::temp_dir nodeDir;
1322  auto c = testConfig(shardDir.path());
1323 
1324  auto& historyPaths = c->section(SECTION_HISTORICAL_SHARD_PATHS);
1325  historyPaths.append(
1326  {historicalPaths[0].string(),
1327  historicalPaths[1].string(),
1328  historicalPaths[2].string(),
1329  historicalPaths[3].string()});
1330 
1331  Env env{*this, std::move(c)};
1332  DatabaseShard* db = env.app().getShardStore();
1333  BEAST_EXPECT(db);
1334 
1335  auto const ledgerCount = 4;
1336 
1337  TestData data(seedValue, 4, ledgerCount);
1338  if (!BEAST_EXPECT(data.makeLedgers(env)))
1339  return;
1340 
1341  BEAST_EXPECT(db->getCompleteShards() == "");
1342  std::uint64_t bitMask = 0;
1343 
1344  // Add ten shards to the Shard Database
1345  for (std::uint32_t i = 0; i < ledgerCount; ++i)
1346  {
1347  auto n = createShard(data, *db, ledgerCount);
1348  if (!BEAST_EXPECT(n && *n >= 1 && *n <= ledgerCount))
1349  return;
1350  bitMask |= 1ll << *n;
1351  BEAST_EXPECT(
1352  db->getCompleteShards() == bitmask2Rangeset(bitMask));
1353  }
1354 
1355  auto mainPathCount = std::distance(
1356  boost::filesystem::directory_iterator(shardDir.path()),
1357  boost::filesystem::directory_iterator());
1358 
1359  // Only the two most recent shards
1360  // should be stored at the main path
1361  BEAST_EXPECT(mainPathCount == 2);
1362 
1363  // Confirm recent shard locations
1364  std::set<boost::filesystem::path> mainPathShards{
1365  shardDir.path() / boost::filesystem::path("3"),
1366  shardDir.path() / boost::filesystem::path("4")};
1368  boost::filesystem::directory_iterator(shardDir.path()),
1369  boost::filesystem::directory_iterator());
1370 
1371  BEAST_EXPECT(mainPathShards == actual);
1372 
1373  const auto generateHistoricalStems = [&historicalPaths, &actual] {
1374  for (auto const& path : historicalPaths)
1375  {
1376  for (auto const& shard :
1377  boost::filesystem::directory_iterator(path))
1378  {
1379  actual.insert(boost::filesystem::path(shard).stem());
1380  }
1381  }
1382  };
1383 
1384  // Confirm historical shard locations
1385  std::set<boost::filesystem::path> historicalPathShards;
1387  std::inserter(
1388  historicalPathShards, historicalPathShards.begin()),
1389  2,
1390  [n = 1]() mutable { return std::to_string(n++); });
1391  actual.clear();
1392  generateHistoricalStems();
1393 
1394  BEAST_EXPECT(historicalPathShards == actual);
1395 
1396  auto historicalPathCount = std::accumulate(
1397  historicalPaths.begin(),
1398  historicalPaths.end(),
1399  0,
1400  [](int const sum, boost::filesystem::path const& path) {
1401  return sum +
1402  std::distance(
1403  boost::filesystem::directory_iterator(path),
1404  boost::filesystem::directory_iterator());
1405  });
1406 
1407  // All historical shards should be stored
1408  // at historical paths
1409  BEAST_EXPECT(historicalPathCount == ledgerCount - 2);
1410 
1411  data = TestData(seedValue * 2, 4, ledgerCount);
1412  if (!BEAST_EXPECT(data.makeLedgers(env, ledgerCount)))
1413  return;
1414 
1415  // Add ten more shards to the Shard Database
1416  // to exercise recent shard rotation
1417  for (std::uint32_t i = 0; i < ledgerCount; ++i)
1418  {
1419  auto n = createShard(data, *db, ledgerCount * 2, ledgerCount);
1420  if (!BEAST_EXPECT(
1421  n && *n >= 1 + ledgerCount && *n <= ledgerCount * 2))
1422  return;
1423  bitMask |= 1ll << *n;
1424  BEAST_EXPECT(
1425  db->getCompleteShards() == bitmask2Rangeset(bitMask));
1426  }
1427 
1428  mainPathCount = std::distance(
1429  boost::filesystem::directory_iterator(shardDir.path()),
1430  boost::filesystem::directory_iterator());
1431 
1432  // Only the two most recent shards
1433  // should be stored at the main path
1434  BEAST_EXPECT(mainPathCount == 2);
1435 
1436  // Confirm recent shard locations
1437  mainPathShards = {
1438  shardDir.path() / boost::filesystem::path("7"),
1439  shardDir.path() / boost::filesystem::path("8")};
1440  actual = {
1441  boost::filesystem::directory_iterator(shardDir.path()),
1442  boost::filesystem::directory_iterator()};
1443 
1444  BEAST_EXPECT(mainPathShards == actual);
1445 
1446  // Confirm historical shard locations
1447  historicalPathShards.clear();
1449  std::inserter(
1450  historicalPathShards, historicalPathShards.begin()),
1451  6,
1452  [n = 1]() mutable { return std::to_string(n++); });
1453  actual.clear();
1454  generateHistoricalStems();
1455 
1456  BEAST_EXPECT(historicalPathShards == actual);
1457 
1458  historicalPathCount = std::accumulate(
1459  historicalPaths.begin(),
1460  historicalPaths.end(),
1461  0,
1462  [](int const sum, boost::filesystem::path const& path) {
1463  return sum +
1464  std::distance(
1465  boost::filesystem::directory_iterator(path),
1466  boost::filesystem::directory_iterator());
1467  });
1468 
1469  // All historical shards should be stored
1470  // at historical paths
1471  BEAST_EXPECT(historicalPathCount == (ledgerCount * 2) - 2);
1472  }
1473  }
1474 
1475  void
1477  {
1478  testcase("Open shard management");
1479 
1480  using namespace test::jtx;
1481 
1482  beast::temp_dir shardDir;
1483  Env env{*this, testConfig(shardDir.path())};
1484 
1485  auto shardStore{env.app().getShardStore()};
1486  BEAST_EXPECT(shardStore);
1487 
1488  // Create one shard more than the open final limit
1489  auto const openFinalLimit{env.app().config().getValueFor(
1490  SizedItem::openFinalLimit, std::nullopt)};
1491  auto const numShards{openFinalLimit + 1};
1492 
1493  TestData data(seedValue, 2, numShards);
1494  if (!BEAST_EXPECT(data.makeLedgers(env)))
1495  return;
1496 
1497  BEAST_EXPECT(shardStore->getCompleteShards().empty());
1498 
1499  int oldestShardIndex{-1};
1500  std::uint64_t bitMask{0};
1501  for (auto i = 0; i < numShards; ++i)
1502  {
1503  auto shardIndex{createShard(data, *shardStore, numShards)};
1504  if (!BEAST_EXPECT(
1505  shardIndex && *shardIndex >= 1 && *shardIndex <= numShards))
1506  return;
1507 
1508  bitMask |= (1ll << *shardIndex);
1509 
1510  if (oldestShardIndex == -1)
1511  oldestShardIndex = *shardIndex;
1512  }
1513 
1514  // The number of open shards exceeds the open limit by one.
1515  // A sweep will close enough shards to be within the limit.
1516  shardStore->sweep();
1517 
1518  // Read from the closed shard and automatically open it
1519  auto const ledgerSeq{shardStore->lastLedgerSeq(oldestShardIndex)};
1520  auto const index{ledgerSeq - ledgersPerShard - 1};
1521  BEAST_EXPECT(shardStore->fetchNodeObject(
1522  data.ledgers_[index]->info().hash, ledgerSeq));
1523  }
1524 
1525 public:
1526  DatabaseShard_test() : journal_("DatabaseShard_test", *this)
1527  {
1528  }
1529 
1530  void
1531  run() override
1532  {
1533  std::uint64_t const seedValue = 51;
1534 
1535  testStandalone();
1536  testCreateShard(seedValue);
1537  testReopenDatabase(seedValue + 10);
1538  testGetCompleteShards(seedValue + 20);
1539  testPrepareShards(seedValue + 30);
1540  testImportShard(seedValue + 40);
1541  testCorruptedDatabase(seedValue + 50);
1542  testIllegalFinalKey(seedValue + 60);
1543  testImport(seedValue + 70);
1544  testDeterministicShard(seedValue + 80);
1545  testImportWithHistoricalPaths(seedValue + 90);
1546  testPrepareWithHistoricalPaths(seedValue + 100);
1547  testOpenShardManagement(seedValue + 110);
1548  }
1549 };
1550 
1551 BEAST_DEFINE_TESTSUITE_MANUAL(DatabaseShard, NodeStore, ripple);
1552 
1553 } // namespace NodeStore
1554 } // namespace ripple
ripple::NodeStore::make_ShardStore
std::unique_ptr< DatabaseShard > make_ShardStore(Application &app, Stoppable &parent, Scheduler &scheduler, int readThreads, beast::Journal j)
Definition: DatabaseShardImp.cpp:2048
ripple::NodeStore::uniformIntDistribution::paramType::A
const resultType A
Definition: DatabaseShard_test.cpp:61
ripple::NodeStore::DummyScheduler
Simple NodeStore Scheduler that just peforms the tasks synchronously.
Definition: DummyScheduler.h:29
ripple::SizedItem::openFinalLimit
@ openFinalLimit
ripple::NodeStore::DatabaseShard_test::testIllegalFinalKey
void testIllegalFinalKey(std::uint64_t const seedValue)
Definition: DatabaseShard_test.cpp:950
ripple::NodeStore::DatabaseShard_test::testCreateShard
void testCreateShard(std::uint64_t const seedValue)
Definition: DatabaseShard_test.cpp:678
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:580
std::generate_n
T generate_n(T... args)
ripple::NodeStore::randInt
Integral randInt(Engine &engine, Integral min, Integral max)
Definition: DatabaseShard_test.cpp:142
ripple::NodeStore::DatabaseShard_test::testLedgerData
void testLedgerData(TestData &data, std::shared_ptr< Ledger > ledger, std::uint32_t seq)
Definition: DatabaseShard_test.cpp:307
ripple::NodeStore::DatabaseShard_test::testReopenDatabase
void testReopenDatabase(std::uint64_t const seedValue)
Definition: DatabaseShard_test.cpp:701
ripple::NodeStore::DatabaseShard_test::TestData::nAccounts_
std::vector< int > nAccounts_
Definition: DatabaseShard_test.cpp:185
fstream
ripple::NodeStore::Database
Persistency layer for NodeObject.
Definition: Database.h:50
ripple::NodeStore::DatabaseShard_test::ledgersPerShard
static constexpr std::uint32_t ledgersPerShard
Definition: DatabaseShard_test.cpp:165
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:783
ripple::SHAMapNodeType::tnINNER
@ tnINNER
ripple::NodeStore::uniformIntDistribution::paramType::B
const resultType B
Definition: DatabaseShard_test.cpp:61
ripple::base_uint::isNonZero
bool isNonZero() const
Definition: base_uint.h:513
ripple::NodeStore::DatabaseShard::seqToShardIndex
virtual std::uint32_t seqToShardIndex(std::uint32_t seq) const =0
Calculates the shard index for a given ledger sequence.
ripple::NodeStore::uniformIntDistribution::resultType
IntType resultType
Definition: DatabaseShard_test.cpp:55
ripple::NodeStore::DatabaseShard_test::testStandalone
void testStandalone()
Definition: DatabaseShard_test.cpp:649
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:194
std::vector::reserve
T reserve(T... args)
ripple::LedgerInfo::hash
uint256 hash
Definition: ReadView.h:100
ripple::NodeStore::DatabaseShard_test::journal_
test::SuiteJournal journal_
Definition: DatabaseShard_test.cpp:172
ripple::sfSequence
const SF_UINT32 sfSequence
ripple::NodeStore::DatabaseShard_test::TestData::accounts_
std::vector< test::jtx::Account > accounts_
Definition: DatabaseShard_test.cpp:182
ripple::addRaw
void addRaw(LedgerInfo const &info, Serializer &s, bool includeHash)
Definition: View.cpp:44
ripple::hotACCOUNT_NODE
@ hotACCOUNT_NODE
Definition: NodeObject.h:35
std::vector
STL class.
ripple::ConfigSection::shardDatabase
static std::string shardDatabase()
Definition: ConfigSections.h:38
std::vector::size
T size(T... args)
ripple::NodeStore::DatabaseShard_test::waitShard
std::optional< int > waitShard(DatabaseShard &db, int shardIndex, std::chrono::seconds timeout=shardStoreTimeout)
Definition: DatabaseShard_test.cpp:589
ripple::NodeStore::uniformIntDistribution::B
const resultType B
Definition: DatabaseShard_test.cpp:57
ripple::NodeStore::uniformIntDistribution::b
resultType b() const
Definition: DatabaseShard_test.cpp:101
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:1117
ripple::NodeStore::uniformIntDistribution::operator()
resultType operator()(Generator &g, const paramType &params) const
Definition: DatabaseShard_test.cpp:89
std::chrono::seconds
ripple::NodeStore::DatabaseShard_test::TestData::TestData
TestData(std::uint64_t const seedValue, int dataSize=dataSizeMax, int nShards=1)
Definition: DatabaseShard_test.cpp:196
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:189
ripple::NodeStore::DatabaseShard::lastLedgerSeq
virtual std::uint32_t lastLedgerSeq(std::uint32_t shardIndex) const =0
Calculates the last ledger sequence for a given shard index.
std::distance
T distance(T... args)
ripple::NodeStore::DatabaseShard_test::testPrepareWithHistoricalPaths
void testPrepareWithHistoricalPaths(std::uint64_t const seedValue)
Definition: DatabaseShard_test.cpp:1302
ripple::NodeStore::DatabaseShard_test::checkLedger
void checkLedger(TestData &data, DatabaseShard &db, Ledger const &ledger)
Definition: DatabaseShard_test.cpp:459
ripple::NodeStore::uniformIntDistribution::rnd
resultType rnd(Generator &g, const resultType a, const resultType b) const
Definition: DatabaseShard_test.cpp:121
std::cerr
ripple::NodeStore::DatabaseShard_test::maxHistoricalShards
static constexpr std::uint32_t maxHistoricalShards
Definition: DatabaseShard_test.cpp:164
ripple::from_string
bool from_string(RangeSet< T > &rs, std::string const &s)
Convert the given styled string to a RangeSet.
Definition: RangeSet.h:126
ripple::NodeStore::DatabaseShard_test::earliestSeq
static constexpr std::uint32_t earliestSeq
Definition: DatabaseShard_test.cpp:166
ripple::test::jtx::Env::app
Application & app()
Definition: Env.h:240
ripple::LedgerInfo::seq
LedgerIndex seq
Definition: ReadView.h:92
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: SHAMapTreeNode.h:74
ripple::NodeStore::DatabaseShard_test::TestData
Definition: DatabaseShard_test.cpp:175
ripple::hotTRANSACTION_NODE
@ hotTRANSACTION_NODE
Definition: NodeObject.h:36
ripple::NodeStore::DatabaseShard_test::nTestShards
static constexpr std::uint32_t nTestShards
Definition: DatabaseShard_test.cpp:169
ripple::NodeStore::DatabaseShard_test::run
void run() override
Definition: DatabaseShard_test.cpp:1531
ripple::SHAMap::snapShot
std::shared_ptr< SHAMap > snapShot(bool isMutable) const
Definition: SHAMap.cpp:70
ripple::NodeStore::DatabaseShard_test::iniAmount
static constexpr std::uint32_t iniAmount
Definition: DatabaseShard_test.cpp:168
iostream
ripple::NodeStore::DatabaseShard::getCompleteShards
virtual std::string getCompleteShards()=0
Query which complete shards are stored.
ripple::NodeStore::DatabaseShard_test::TestData::nShards_
int nShards_
Definition: DatabaseShard_test.cpp:180
ripple::LedgerFill::expand
@ expand
Definition: LedgerToJson.h:48
ripple::LedgerInfo::txHash
uint256 txHash
Definition: ReadView.h:101
ripple::NodeStore::uniformIntDistribution::min
resultType min() const
Definition: DatabaseShard_test.cpp:107
ripple::ttPAYMENT
@ ttPAYMENT
Definition: TxFormats.h:36
std::set::clear
T clear(T... args)
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::NodeStore::Shard::version
static constexpr std::uint32_t version
Definition: Shard.h:225
std::is_convertible
ripple::NodeStore::DatabaseShard_test::ripemd160File
std::string ripemd160File(std::string filename)
Definition: DatabaseShard_test.cpp:1095
ripple::NodeStore::DatabaseShard_test::createShard
std::optional< int > createShard(TestData &data, DatabaseShard &db, int maxShardNumber=1, int ledgerOffset=0)
Definition: DatabaseShard_test.cpp:609
std::vector::push_back
T push_back(T... args)
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:273
ripple::NodeStore::DatabaseShard_test::DatabaseShard_test
DatabaseShard_test()
Definition: DatabaseShard_test.cpp:1526
ripple::Ledger::info
LedgerInfo const & info() const override
Returns information about the ledger.
Definition: Ledger.h:148
ripple::NodeStore::DatabaseShard_test::shardStoreTimeout
static constexpr std::chrono::seconds shardStoreTimeout
Definition: DatabaseShard_test.cpp:170
ripple::NodeStore::DatabaseShard_test::testImportShard
void testImportShard(std::uint64_t const seedValue)
Definition: DatabaseShard_test.cpp:837
ripple::NodeStore::DatabaseShard_test::testConfig
std::unique_ptr< Config > testConfig(std::string const &shardDir, std::string const &nodeDir=std::string())
Definition: DatabaseShard_test.cpp:553
ripple::RootStoppable
Definition: Stoppable.h:354
ripple::Ledger
Holds a ledger.
Definition: Ledger.h:76
ripple::Application::getLedgerMaster
virtual LedgerMaster & getLedgerMaster()=0
chrono
ripple::NodeStore::DatabaseShard_test::defNodeDir
beast::temp_dir defNodeDir
Definition: DatabaseShard_test.cpp:173
ripple::Ledger::stateMap
SHAMap const & stateMap() const
Definition: Ledger.h:307
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:134
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:276
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:897
ripple::NodeStore::uniformIntDistribution
std::uniform_int_distribution is platform dependent.
Definition: DatabaseShard_test.cpp:53
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:178
ripple::NodeStore::DatabaseShard_test
Definition: DatabaseShard_test.cpp:161
std::accumulate
T accumulate(T... args)
std::uint32_t
ripple::NodeStore::uniformIntDistribution::uniformIntDistribution
uniformIntDistribution(const paramType &params)
Definition: DatabaseShard_test.cpp:75
ripple::NodeStore::DatabaseShard::firstLedgerSeq
virtual std::uint32_t firstLedgerSeq(std::uint32_t shardIndex) const =0
Calculates the first ledger sequence for a given shard index.
ripple::NodeStore::Database::fetchNodeObject
std::shared_ptr< NodeObject > fetchNodeObject(uint256 const &hash, std::uint32_t ledgerSeq=0, FetchType fetchType=FetchType::synchronous)
Fetch a node object.
Definition: Database.cpp:145
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:59
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::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:291
ripple::Ledger::txMap
SHAMap const & txMap() const
Definition: Ledger.h:319
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:105
ripple::NodeStore::uniformIntDistribution::max
resultType max() const
Definition: DatabaseShard_test.cpp:113
ripple::NodeStore::DatabaseShard::importShard
virtual bool importShard(std::uint32_t shardIndex, boost::filesystem::path const &srcDir)=0
Import a shard 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::testImportWithHistoricalPaths
void testImportWithHistoricalPaths(std::uint64_t const seedValue)
Definition: DatabaseShard_test.cpp:1174
std::endl
T endl(T... args)
ripple::NodeStore::DatabaseShard_test::testImport
void testImport(std::uint64_t const seedValue)
Definition: DatabaseShard_test.cpp:1048
ripple::ttACCOUNT_SET
@ ttACCOUNT_SET
Definition: TxFormats.h:39
std::array::begin
T begin(T... args)
ripple::NodeStore::DatabaseShard_test::saveLedger
bool saveLedger(Database &db, Ledger const &ledger, std::shared_ptr< Ledger const > const &next={})
Definition: DatabaseShard_test.cpp:391
beast::rngfill
void rngfill(void *buffer, std::size_t bytes, Generator &g)
Definition: rngfill.h:32
std::set::insert
T insert(T... args)
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:68
ripple::test::jtx::Env::fund
void fund(bool setDefaultRipple, STAmount const &amount, Account const &account)
Definition: Env.cpp:219
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:250
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:167
std::string::empty
T empty(T... args)
std::optional< int >
ripple::NodeStore::DatabaseShard_test::testGetCompleteShards
void testGetCompleteShards(std::uint64_t const seedValue)
Definition: DatabaseShard_test.cpp:739
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:40
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::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:84
ripple::NodeStore::uniformIntDistribution::A
const resultType A
Definition: DatabaseShard_test.cpp:57
std::array::end
T end(T... args)
ripple::NodeStore::uniformIntDistribution::a
resultType a() const
Definition: DatabaseShard_test.cpp:95
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:256
ripple::NodeStore::uniformIntDistribution::paramType::paramType
paramType(resultType aa, resultType bb)
Definition: DatabaseShard_test.cpp:63
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
std::inserter
T inserter(T... args)
ripple::NodeStore::DatabaseShard_test::testOpenShardManagement
void testOpenShardManagement(std::uint64_t const seedValue)
Definition: DatabaseShard_test.cpp:1476
ripple::ltACCOUNT_ROOT
@ ltACCOUNT_ROOT
Definition: LedgerFormats.h:53
std::unique_ptr
STL class.
ripple::sum
static auto sum(TCollection const &col)
Definition: BookStep.cpp:711
ripple::NodeStore::DatabaseShard_test::testPrepareShards
void testPrepareShards(std::uint64_t const seedValue)
Definition: DatabaseShard_test.cpp:769
ripple::NodeStore::Database::import
virtual void import(Database &source)=0
Import objects from another database.
ripple::LedgerFill
Definition: LedgerToJson.h:33
std::numeric_limits
ripple::NodeStore::DatabaseShard_test::bitmask2Rangeset
std::string bitmask2Rangeset(std::uint64_t bitmask)
Definition: DatabaseShard_test.cpp:529
std::array::data
T data(T... args)
std::set
STL class.
ripple::NodeStore::uniformIntDistribution::operator()
resultType operator()(Generator &g) const
Definition: DatabaseShard_test.cpp:82
ripple::test::jtx::Env
A transaction testing environment.
Definition: Env.h:115
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::TestData::xrpAmount_
std::vector< int > xrpAmount_
Definition: DatabaseShard_test.cpp:191
ripple::NodeStore::DatabaseShard_test::maxSizeGb
static constexpr std::uint32_t maxSizeGb
Definition: DatabaseShard_test.cpp:163
std::ifstream
STL class.
ripple::LgrDBName
constexpr auto LgrDBName
Definition: DBInit.h:43
std::chrono::system_clock::now
T now(T... args)