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