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>
35 #include <test/jtx/CaptureLogs.h>
36 #include <test/nodestore/TestBase.h>
38 #include <boost/algorithm/hex.hpp>
43 #include <openssl/ripemd.h>
58 template <
class IntType =
int>
82 :
A(params.
A),
B(params.
B)
86 template <
class Generator>
93 template <
class Generator>
97 return rnd(g, params.
A, params.
B);
125 template <
class Generator>
134 Generator::min() == 0,
"If non-zero we have handle the offset");
136 assert(Generator::max() >=
range);
141 while (n <= rejectLim);
146 template <
class Engine,
class Integral>
148 randInt(Engine& engine, Integral min, Integral max)
158 template <
class Engine,
class Integral>
162 return randInt(engine, Integral(0), max);
226 for (
int j = 0; j < p; ++j)
233 }
while (from == to);
243 for (
int j = 0; j < 8; ++j)
264 using namespace test::jtx;
289 if (ledger->info().seq != i)
318 using namespace test::jtx;
323 for (
auto const& sles : ledger->sles)
329 const auto id = sles->getAccountID(
sfAccount);
331 for (
int i = 0; i < data.accounts_.size(); ++i)
333 if (
id == data.accounts_[i].id())
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)))
341 for (
int k = 0; k < data.payAccounts_[j].size();
343 if (data.payAccounts_[j][k].first == i)
354 reqsq = data.nAccounts_[seq] + 1;
357 BEAST_EXPECT(sq == reqsq);
362 BEAST_EXPECT(rootCount == 1);
363 BEAST_EXPECT(accCount == data.nAccounts_[seq]);
364 BEAST_EXPECT(sothCount == 3);
370 for (
auto const& tx : ledger->txs)
375 tx.first->getFieldAmount(
sfAmount).xrp().decimalXRP();
381 BEAST_EXPECT(xrpAmount == data.xrpAmount_[seq]);
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);
409 std::move(s.modData()),
417 node.serializeWithPrefix(s);
422 node.getHash().as_uint256(),
431 if (next && next->info().parentHash == ledger.
info().
hash)
433 auto have = next->stateMap().snapShot(
false);
442 auto visitTx = [&](SHAMapTreeNode& node) {
444 node.serializeWithPrefix(s);
448 std::move(s.modData()),
449 node.getHash().as_uint256(),
468 if (!BEAST_EXPECT(fetched))
489 node.serializeWithPrefix(s);
494 node.getHash().as_uint256())};
495 if (!BEAST_EXPECT(nSrc))
499 node.getHash().as_uint256(), ledger.
info().
seq);
500 if (!BEAST_EXPECT(nDst))
503 BEAST_EXPECT(
isSame(nSrc, nDst));
512 node.serializeWithPrefix(s);
517 node.getHash().as_uint256())};
518 if (!BEAST_EXPECT(nSrc))
522 node.getHash().as_uint256(), ledger.
info().
seq);
523 if (!BEAST_EXPECT(nDst))
526 BEAST_EXPECT(
isSame(nSrc, nDst));
544 if (bitmask & (1ll << i))
563 using namespace test::jtx;
570 "max_historical_shards",
606 !boost::icl::contains(
621 int maxShardIndex = 1,
630 if (!BEAST_EXPECT(ledgerSeq != std::nullopt))
639 BEAST_EXPECT(
saveLedger(shardStore, *data.ledgers_[arrInd]));
647 s.
addRaw(data.ledgers_[arrInd]->info().hash.data(), 256 / 8);
651 shardStore.
setStored(data.ledgers_[arrInd]);
654 return waitShard(shardStore, shardIndex);
660 testcase(
"Standalone");
662 using namespace test::jtx;
671 BEAST_EXPECT(shardStore);
672 BEAST_EXPECT(shardStore->init());
679 shardStore->earliestShardIndex() ==
683 BEAST_EXPECT(shardStore->getRootDir().string() == shardDir.
path());
691 env.app().config().overwrite(
693 BEAST_EXPECT(!shardStore->init());
700 env.app().config().overwrite(
704 BEAST_EXPECT(!shardStore->init());
710 testcase(
"Create shard");
712 using namespace test::jtx;
720 if (!BEAST_EXPECT(data.makeLedgers(env)))
733 testcase(
"Reopen shard store");
735 using namespace test::jtx;
744 if (!BEAST_EXPECT(data.makeLedgers(env)))
747 for (
auto i = 0; i < 2; ++i)
759 if (!BEAST_EXPECT(data.makeLedgers(env)))
773 testcase(
"Get final shards");
775 using namespace test::jtx;
783 if (!BEAST_EXPECT(data.makeLedgers(env)))
792 shardIndex && *shardIndex >= 1 &&
798 BEAST_EXPECT(boost::icl::contains(
806 testcase(
"Prepare shards");
808 using namespace test::jtx;
816 if (!BEAST_EXPECT(data.makeLedgers(env)))
827 if (bitMask & (1ll << shardIndex))
830 bitMask &= ~(1ll << shardIndex);
835 bitMask |= 1ll << shardIndex;
857 shardIndex && *shardIndex >= 1 &&
863 BEAST_EXPECT(boost::icl::contains(
866 bitMask2 |= 1ll << *shardIndex;
867 BEAST_EXPECT((bitMask & bitMask2) == 0);
868 if ((bitMask | bitMask2) == ((1ll <<
nTestShards) - 1) << 1)
881 testcase(
"Import shard");
883 using namespace test::jtx;
893 if (!BEAST_EXPECT(data.makeLedgers(env)))
902 data.ledgers_.clear();
905 boost::filesystem::path importPath(importDir.
path());
914 if (!BEAST_EXPECT(data.makeLedgers(env)))
917 BEAST_EXPECT(!db->
importShard(1, importPath /
"not_exist"));
921 using namespace boost::filesystem;
931 if (!BEAST_EXPECT(n && *n == 1))
942 testcase(
"Corrupted shard store");
944 using namespace test::jtx;
954 if (!BEAST_EXPECT(data.makeLedgers(env)))
957 for (
auto i = 0; i < 2; ++i)
964 boost::filesystem::path path = shardDir.
path();
968 FILE* f = fopen(path.string().c_str(),
"r+b");
969 if (!BEAST_EXPECT(f))
973 BEAST_EXPECT(fwrite(buf, 1, 256, f) == 256);
982 if (!BEAST_EXPECT(data.makeLedgers(env)))
985 for (
std::uint32_t shardIndex = 1; shardIndex <= 1; ++shardIndex)
988 BEAST_EXPECT(boost::icl::contains(db->
getShardInfo()->finalized(), 1));
997 testcase(
"Illegal finalKey");
999 using namespace test::jtx;
1001 for (
int i = 0; i < 5; ++i)
1010 if (!BEAST_EXPECT(data.makeLedgers(env)))
1016 auto const ledgerSeq{
1018 if (!BEAST_EXPECT(ledgerSeq != std::nullopt))
1024 BEAST_EXPECT(
saveLedger(*db, *data.ledgers_[arrInd]));
1033 data.ledgers_[arrInd - (i == 4)]
1049 BEAST_EXPECT(boost::icl::contains(
1054 boost::filesystem::path path(shardDir.
path());
1056 boost::system::error_code ec;
1060 boost::filesystem::exists(path, ec))
1075 if (!BEAST_EXPECT(data.makeLedgers(env)))
1081 BEAST_EXPECT(boost::icl::contains(
1097 std::ifstream input(filename, std::ios::in | std::ios::binary);
1101 while (input.
read(buf, 4096), input.
gcount() > 0)
1105 const auto charDigest = binResult.
data();
1107 boost::algorithm::hex(
1109 charDigest +
sizeof(binResult),
1118 testcase(
"Deterministic shards");
1120 using namespace test::jtx;
1122 for (
int i = 0; i < 2; i++)
1131 if (!BEAST_EXPECT(data.makeLedgers(env)))
1134 if (!BEAST_EXPECT(
createShard(data, *db) != std::nullopt))
1138 boost::filesystem::path path(shardDir.
path());
1141 auto static const ripemd160Key =
1143 auto static const ripemd160Dat =
1152 if (!BEAST_EXPECT(data.makeLedgers(env)))
1155 if (!BEAST_EXPECT(
waitShard(*db, 1) != std::nullopt))
1163 ripemd160File((path /
"nudb.key").
string()) == ripemd160Key);
1165 ripemd160File((path /
"nudb.dat").
string()) == ripemd160Dat);
1172 testcase(
"Import node store");
1174 using namespace test::jtx;
1181 Database& ndb = env.app().getNodeStore();
1185 if (!BEAST_EXPECT(data.makeLedgers(env)))
1189 BEAST_EXPECT(
saveLedger(ndb, *data.ledgers_[i]));
1196 auto const finalShards{db->
getShardInfo()->finalized()};
1198 BEAST_EXPECT(boost::icl::contains(finalShards, shardIndex));
1206 if (!BEAST_EXPECT(data.makeLedgers(env)))
1212 auto const finalShards{db->
getShardInfo()->finalized()};
1214 BEAST_EXPECT(boost::icl::contains(finalShards, shardIndex));
1224 testcase(
"Import node store with online delete");
1226 using namespace test::jtx;
1236 section.set(
"online_delete",
"550");
1237 section.set(
"advisory_delete",
"1");
1240 c->section(SECTION_RPC_STARTUP)
1242 "{ \"command\": \"log_level\", \"severity\": \"trace\" "
1246 Env env{*
this, std::move(c), std::move(logs)};
1249 Database& ndb = env.app().getNodeStore();
1253 auto const shardCount = 5;
1254 TestData data(seedValue, 4, shardCount);
1255 if (!BEAST_EXPECT(data.makeLedgers(env)))
1258 auto& store = env.app().getSHAMapStore();
1259 auto lastRotated = store.getLastRotated();
1273 auto pauseVerifier =
std::thread([lastRotated, &store, db,
this] {
1279 if (store.getLastRotated() != lastRotated)
1284 auto const ledgerSeq = db->getDatabaseImportSequence();
1285 BEAST_EXPECT(!ledgerSeq || ledgerSeq >= lastRotated);
1294 if (!BEAST_EXPECT(data.makeLedgers(env, shardCount)))
1296 pauseVerifier.join();
1300 pauseVerifier.join();
1301 BEAST_EXPECT(store.getLastRotated() != lastRotated);
1306 auto const expectedLogMessage =
1307 "rotation would interfere with ShardStore import";
1309 capturedLogs.
find(expectedLogMessage) != std::string::npos);
1315 testcase(
"Import with historical paths");
1317 using namespace test::jtx;
1326 historicalDirs.
begin(),
1327 historicalDirs.
end(),
1328 historicalPaths.
begin(),
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()});
1341 Env env{*
this, std::move(c)};
1343 Database& ndb = env.app().getNodeStore();
1346 auto const shardCount = 4;
1348 TestData data(seedValue, 4, shardCount);
1349 if (!BEAST_EXPECT(data.makeLedgers(env)))
1353 BEAST_EXPECT(
saveLedger(ndb, *data.ledgers_[i]));
1363 BEAST_EXPECT(boost::icl::contains(
final, shardIndex));
1366 boost::filesystem::directory_iterator(shardDir.
path()),
1367 boost::filesystem::directory_iterator());
1371 BEAST_EXPECT(mainPathCount == 2);
1374 historicalPaths.
begin(),
1375 historicalPaths.
end(),
1377 [](
int const sum, boost::filesystem::path
const& path) {
1380 boost::filesystem::directory_iterator(path),
1381 boost::filesystem::directory_iterator());
1386 BEAST_EXPECT(historicalPathCount == shardCount - 2);
1397 auto& historyPaths = c->section(SECTION_HISTORICAL_SHARD_PATHS);
1398 historyPaths.append({historicalDir.
path()});
1400 Env env{*
this, std::move(c)};
1402 Database& ndb = env.app().getNodeStore();
1405 auto const shardCount = 4;
1407 TestData data(seedValue * 2, 4, shardCount);
1408 if (!BEAST_EXPECT(data.makeLedgers(env)))
1412 BEAST_EXPECT(
saveLedger(ndb, *data.ledgers_[i]));
1420 auto const finalShards{db->
getShardInfo()->finalized()};
1422 BEAST_EXPECT(boost::icl::contains(finalShards, shardIndex));
1425 boost::filesystem::directory_iterator(shardDir.
path()),
1426 boost::filesystem::directory_iterator());
1430 BEAST_EXPECT(mainPathCount == 2);
1433 boost::filesystem::directory_iterator(historicalDir.
path()),
1434 boost::filesystem::directory_iterator());
1438 BEAST_EXPECT(historicalPathCount == shardCount - 2);
1445 testcase(
"Prepare with historical paths");
1447 using namespace test::jtx;
1456 auto& paths{config->section(SECTION_HISTORICAL_SHARD_PATHS)};
1457 for (
auto const& dir : historicalDirs)
1458 paths.append(dir.path());
1461 Env env{*
this, std::move(config)};
1465 TestData data(seedValue, 4, numShards);
1466 if (!BEAST_EXPECT(data.makeLedgers(env)))
1469 auto shardStore{env.app().getShardStore()};
1470 BEAST_EXPECT(shardStore);
1472 for (
auto i = 0; i < numShards; ++i)
1474 auto const shardIndex{
createShard(data, *shardStore, numShards)};
1476 shardIndex && *shardIndex >= 1 && *shardIndex <= numShards))
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);
1490 using namespace boost::filesystem;
1494 for (
auto const& it : directory_iterator(dir.
path()))
1495 if (boost::filesystem::path(it).stem() == path)
1499 auto const historicalDirsContains = [&](
std::uint32_t shardIndex) {
1500 for (
auto const& dir : historicalDirs)
1501 if (dirContains(dir, shardIndex))
1507 for (
auto const shardIndex : {numShards - 1, numShards})
1509 BEAST_EXPECT(dirContains(primaryDir, shardIndex));
1510 BEAST_EXPECT(!historicalDirsContains(shardIndex));
1514 for (
auto shardIndex = 1; shardIndex < numShards - 1; ++shardIndex)
1516 BEAST_EXPECT(!dirContains(primaryDir, shardIndex));
1517 BEAST_EXPECT(historicalDirsContains(shardIndex));
1521 data =
TestData(seedValue * 2, 4, numShards);
1522 if (!BEAST_EXPECT(data.makeLedgers(env, numShards)))
1525 for (
auto i = 0; i < numShards; ++i)
1527 auto const shardIndex{
1528 createShard(data, *shardStore, numShards * 2, numShards)};
1530 shardIndex && *shardIndex >= numShards + 1 &&
1531 *shardIndex <= numShards * 2))
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);
1546 for (
auto const shardIndex : {numShards * 2 - 1, numShards * 2})
1548 BEAST_EXPECT(dirContains(primaryDir, shardIndex));
1549 BEAST_EXPECT(!historicalDirsContains(shardIndex));
1553 for (
auto shardIndex = 1; shardIndex < numShards * 2 - 1; ++shardIndex)
1555 BEAST_EXPECT(!dirContains(primaryDir, shardIndex));
1556 BEAST_EXPECT(historicalDirsContains(shardIndex));
1563 testcase(
"Open shard management");
1565 using namespace test::jtx;
1570 auto shardStore{env.app().getShardStore()};
1571 BEAST_EXPECT(shardStore);
1578 TestData data(seedValue, 2, numShards);
1579 if (!BEAST_EXPECT(data.makeLedgers(env)))
1582 BEAST_EXPECT(shardStore->getShardInfo()->finalized().empty());
1584 int oldestShardIndex{-1};
1585 for (
auto i = 0; i < numShards; ++i)
1587 auto shardIndex{
createShard(data, *shardStore, numShards)};
1589 shardIndex && *shardIndex >= 1 && *shardIndex <= numShards))
1594 BEAST_EXPECT(boost::icl::contains(
1595 shardStore->getShardInfo()->finalized(), *shardIndex));
1597 if (oldestShardIndex == -1)
1598 oldestShardIndex = *shardIndex;
1603 shardStore->sweep();
1606 auto const ledgerSeq{shardStore->lastLedgerSeq(oldestShardIndex)};
1608 BEAST_EXPECT(shardStore->fetchNodeObject(
1609 data.ledgers_[index]->info().hash, ledgerSeq));
1615 testcase(
"Shard info");
1617 using namespace test::jtx;
1621 auto shardStore{env.app().getShardStore()};
1622 BEAST_EXPECT(shardStore);
1626 auto const shardInfo{shardStore->getShardInfo()};
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());
1637 if (!BEAST_EXPECT(data.makeLedgers(env)))
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");
1656 if (!BEAST_EXPECT(shardIndex && *shardIndex == 1))
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());
1668 BEAST_EXPECT(
shardInfo->setFinalizedFromString(
"2"));
1669 BEAST_EXPECT(
shardInfo->finalizedToString() ==
"2");
1670 BEAST_EXPECT(boost::icl::contains(
shardInfo->finalized(), 2));
1680 auto const ledgerSeq{
1682 if (!BEAST_EXPECT(ledgerSeq != std::nullopt))
1686 if (!BEAST_EXPECT(
saveLedger(*shardStore, *data.ledgers_[arrInd])))
1689 shardStore->setStored(data.ledgers_[arrInd]);
1692 auto const shardInfo{shardStore->getShardInfo()};
1693 BEAST_EXPECT(
shardInfo->incompleteToString() ==
"2:10");
1697 auto const timeStamp{env.app().timeKeeper().now()};
1699 BEAST_EXPECT(timeStamp ==
shardInfo->msgTimestamp());
1702 auto const msg{
shardInfo->makeMessage(env.app())};
1706 BEAST_EXPECT(msg.timestamp() != 0);
1707 s.
add32(msg.timestamp());
1711 BEAST_EXPECT(msg.incomplete_size() == 1);
1713 auto const& incomplete{msg.incomplete(0)};
1714 BEAST_EXPECT(incomplete.shardindex() == 2);
1715 s.
add32(incomplete.shardindex());
1718 static_cast<ShardState>(incomplete.state()) ==
1720 s.
add32(incomplete.state());
1722 BEAST_EXPECT(incomplete.has_progress());
1723 BEAST_EXPECT(incomplete.progress() == 10);
1724 s.
add32(incomplete.progress());
1728 BEAST_EXPECT(msg.has_finalized());
1729 BEAST_EXPECT(msg.finalized() ==
"1");
1730 s.
addRaw(msg.finalized().data(), msg.finalized().size());
1740 BEAST_EXPECT(msg.peerchain_size() == 0);
1746 testcase(
"Relational DB Interface SQLite");
1748 using namespace test::jtx;
1753 auto shardStore{env.app().getShardStore()};
1754 BEAST_EXPECT(shardStore);
1756 auto const shardCount = 3;
1757 TestData data(seedValue, 3, shardCount);
1758 if (!BEAST_EXPECT(data.makeLedgers(env)))
1761 BEAST_EXPECT(shardStore->getShardInfo()->finalized().empty());
1762 BEAST_EXPECT(shardStore->getShardInfo()->incompleteToString().empty());
1765 &env.app().getRelationalDBInterface());
1773 auto n =
createShard(data, *shardStore, shardCount);
1774 if (!BEAST_EXPECT(n && *n >= 1 && *n <= shardCount))
1780 rdb->closeLedgerDB();
1781 rdb->closeTransactionDB();
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;
1793 for (
auto const& ledger : data.ledgers_)
1798 if (shardStore->seqToShardIndex(ledger->
seq()) <
1799 shardStore->earliestShardIndex() ||
1800 ledger->
info().
seq < shardStore->earliestLedgerSeq())
1803 auto info = rdb->getLedgerInfoByHash(ledger->
info().
hash);
1806 BEAST_EXPECT(infoCmp(*info, ledger->
info()));
1808 for (
auto const& transaction : ledger->
txs)
1816 auto reference = rdb->getTransaction(
1817 transaction.first->getTransactionID(), {}, error);
1820 if (!BEAST_EXPECT(reference.index() == 0))
1823 auto txn = std::get<0>(reference).first->getSTransaction();
1826 transaction.first->getFullText() == txn->getFullText());
1833 data =
TestData(seedValue * 2, 4, 1);
1834 if (!BEAST_EXPECT(data.makeLedgers(env, shardCount)))
1846 auto seedValue = [] {