20 #include <ripple/app/ledger/LedgerMaster.h>
21 #include <ripple/app/ledger/LedgerToJson.h>
22 #include <ripple/app/misc/LoadFeeTrack.h>
23 #include <ripple/app/misc/SHAMapStore.h>
24 #include <ripple/app/rdb/backend/SQLiteDatabase.h>
25 #include <ripple/basics/Slice.h>
26 #include <ripple/basics/random.h>
27 #include <ripple/beast/hash/hash_append.h>
28 #include <ripple/beast/utility/temp_dir.h>
29 #include <ripple/core/ConfigSections.h>
30 #include <ripple/nodestore/DatabaseShard.h>
31 #include <ripple/nodestore/DummyScheduler.h>
32 #include <ripple/nodestore/impl/DecodedBlob.h>
33 #include <ripple/nodestore/impl/Shard.h>
34 #include <ripple/protocol/digest.h>
36 #include <test/jtx/CaptureLogs.h>
37 #include <test/nodestore/TestBase.h>
39 #include <boost/algorithm/hex.hpp>
44 #include <openssl/ripemd.h>
59 template <
class IntType =
int>
83 :
A(params.
A),
B(params.
B)
87 template <
class Generator>
94 template <
class Generator>
98 return rnd(g, params.
A, params.
B);
126 template <
class Generator>
135 Generator::min() == 0,
"If non-zero we have handle the offset");
137 assert(Generator::max() >=
range);
142 while (n <= rejectLim);
147 template <
class Engine,
class Integral>
149 randInt(Engine& engine, Integral min, Integral max)
159 template <
class Engine,
class Integral>
163 return randInt(engine, Integral(0), max);
227 for (
int j = 0; j < p; ++j)
234 }
while (from == to);
244 for (
int j = 0; j < 8; ++j)
265 using namespace test::jtx;
294 if (ledger->info().seq != i)
323 using namespace test::jtx;
328 for (
auto const& sles : ledger->sles)
334 const auto id = sles->getAccountID(
sfAccount);
336 for (
int i = 0; i < data.accounts_.size(); ++i)
338 if (
id == data.accounts_[i].id())
341 for (
int j = 0; j <= seq; ++j)
342 if (data.nAccounts_[j] > i + 1 ||
343 (data.nAccounts_[j] == i + 1 &&
344 !data.isNewAccounts(j)))
346 for (
int k = 0; k < data.payAccounts_[j].size();
348 if (data.payAccounts_[j][k].first == i)
359 reqsq = data.nAccounts_[seq] + 1;
362 BEAST_EXPECT(sq == reqsq);
367 BEAST_EXPECT(rootCount == 1);
368 BEAST_EXPECT(accCount == data.nAccounts_[seq]);
369 BEAST_EXPECT(sothCount == 3);
375 for (
auto const& tx : ledger->txs)
380 tx.first->getFieldAmount(
sfAmount).xrp().decimalXRP();
386 BEAST_EXPECT(xrpAmount == data.xrpAmount_[seq]);
394 int newacc = data.isNewAccounts(seq) ? 1 : 0;
395 BEAST_EXPECT(iniCount == newacc);
396 BEAST_EXPECT(setCount == newacc);
397 BEAST_EXPECT(payCount == data.payAccounts_[seq].size());
398 BEAST_EXPECT(tothCount == !seq);
414 std::move(s.modData()),
422 node.serializeWithPrefix(s);
427 node.getHash().as_uint256(),
436 if (next && next->info().parentHash == ledger.
info().
hash)
438 auto have = next->stateMap().snapShot(
false);
447 auto visitTx = [&](SHAMapTreeNode& node) {
449 node.serializeWithPrefix(s);
453 std::move(s.modData()),
454 node.getHash().as_uint256(),
473 if (!BEAST_EXPECT(fetched))
494 node.serializeWithPrefix(s);
499 node.getHash().as_uint256())};
500 if (!BEAST_EXPECT(nSrc))
504 node.getHash().as_uint256(), ledger.
info().
seq);
505 if (!BEAST_EXPECT(nDst))
508 BEAST_EXPECT(
isSame(nSrc, nDst));
517 node.serializeWithPrefix(s);
522 node.getHash().as_uint256())};
523 if (!BEAST_EXPECT(nSrc))
527 node.getHash().as_uint256(), ledger.
info().
seq);
528 if (!BEAST_EXPECT(nDst))
531 BEAST_EXPECT(
isSame(nSrc, nDst));
549 if (bitmask & (1ll << i))
568 using namespace test::jtx;
575 "max_historical_shards",
611 !boost::icl::contains(
626 int maxShardIndex = 1,
635 if (!BEAST_EXPECT(ledgerSeq != std::nullopt))
644 BEAST_EXPECT(
saveLedger(shardStore, *data.ledgers_[arrInd]));
652 s.
addRaw(data.ledgers_[arrInd]->info().hash.data(), 256 / 8);
656 shardStore.
setStored(data.ledgers_[arrInd]);
659 return waitShard(shardStore, shardIndex);
665 testcase(
"Standalone");
667 using namespace test::jtx;
676 BEAST_EXPECT(shardStore);
677 BEAST_EXPECT(shardStore->init());
684 shardStore->earliestShardIndex() ==
688 BEAST_EXPECT(shardStore->getRootDir().string() == shardDir.
path());
696 env.app().config().overwrite(
698 BEAST_EXPECT(!shardStore->init());
705 env.app().config().overwrite(
709 BEAST_EXPECT(!shardStore->init());
715 testcase(
"Create shard");
717 using namespace test::jtx;
725 if (!BEAST_EXPECT(data.makeLedgers(env)))
738 testcase(
"Reopen shard store");
740 using namespace test::jtx;
749 if (!BEAST_EXPECT(data.makeLedgers(env)))
752 for (
auto i = 0; i < 2; ++i)
764 if (!BEAST_EXPECT(data.makeLedgers(env)))
778 testcase(
"Get final shards");
780 using namespace test::jtx;
788 if (!BEAST_EXPECT(data.makeLedgers(env)))
797 shardIndex && *shardIndex >= 1 &&
803 BEAST_EXPECT(boost::icl::contains(
811 testcase(
"Prepare shards");
813 using namespace test::jtx;
821 if (!BEAST_EXPECT(data.makeLedgers(env)))
832 if (bitMask & (1ll << shardIndex))
835 bitMask &= ~(1ll << shardIndex);
840 bitMask |= 1ll << shardIndex;
862 shardIndex && *shardIndex >= 1 &&
868 BEAST_EXPECT(boost::icl::contains(
871 bitMask2 |= 1ll << *shardIndex;
872 BEAST_EXPECT((bitMask & bitMask2) == 0);
873 if ((bitMask | bitMask2) == ((1ll <<
nTestShards) - 1) << 1)
886 testcase(
"Import shard");
888 using namespace test::jtx;
898 if (!BEAST_EXPECT(data.makeLedgers(env)))
907 data.ledgers_.clear();
910 boost::filesystem::path importPath(importDir.
path());
919 if (!BEAST_EXPECT(data.makeLedgers(env)))
922 BEAST_EXPECT(!db->
importShard(1, importPath /
"not_exist"));
926 using namespace boost::filesystem;
936 if (!BEAST_EXPECT(n && *n == 1))
947 testcase(
"Corrupted shard store");
949 using namespace test::jtx;
959 if (!BEAST_EXPECT(data.makeLedgers(env)))
962 for (
auto i = 0; i < 2; ++i)
969 boost::filesystem::path path = shardDir.
path();
973 FILE* f = fopen(path.string().c_str(),
"r+b");
974 if (!BEAST_EXPECT(f))
978 BEAST_EXPECT(fwrite(buf, 1, 256, f) == 256);
987 if (!BEAST_EXPECT(data.makeLedgers(env)))
990 for (
std::uint32_t shardIndex = 1; shardIndex <= 1; ++shardIndex)
993 BEAST_EXPECT(boost::icl::contains(db->
getShardInfo()->finalized(), 1));
1002 testcase(
"Illegal finalKey");
1004 using namespace test::jtx;
1006 for (
int i = 0; i < 5; ++i)
1015 if (!BEAST_EXPECT(data.makeLedgers(env)))
1021 auto const ledgerSeq{
1023 if (!BEAST_EXPECT(ledgerSeq != std::nullopt))
1029 BEAST_EXPECT(
saveLedger(*db, *data.ledgers_[arrInd]));
1038 data.ledgers_[arrInd - (i == 4)]
1054 BEAST_EXPECT(boost::icl::contains(
1059 boost::filesystem::path path(shardDir.
path());
1061 boost::system::error_code ec;
1065 boost::filesystem::exists(path, ec))
1080 if (!BEAST_EXPECT(data.makeLedgers(env)))
1086 BEAST_EXPECT(boost::icl::contains(
1102 std::ifstream input(filename, std::ios::in | std::ios::binary);
1106 while (input.
read(buf, 4096), input.
gcount() > 0)
1110 const auto charDigest = binResult.
data();
1112 boost::algorithm::hex(
1114 charDigest +
sizeof(binResult),
1123 testcase(
"Deterministic shards");
1125 using namespace test::jtx;
1127 for (
int i = 0; i < 2; i++)
1136 if (!BEAST_EXPECT(data.makeLedgers(env)))
1139 if (!BEAST_EXPECT(
createShard(data, *db) != std::nullopt))
1143 boost::filesystem::path path(shardDir.
path());
1146 auto static const ripemd160Key =
1148 auto static const ripemd160Dat =
1157 if (!BEAST_EXPECT(data.makeLedgers(env)))
1160 if (!BEAST_EXPECT(
waitShard(*db, 1) != std::nullopt))
1168 ripemd160File((path /
"nudb.key").
string()) == ripemd160Key);
1170 ripemd160File((path /
"nudb.dat").
string()) == ripemd160Dat);
1177 testcase(
"Import node store");
1179 using namespace test::jtx;
1186 Database& ndb = env.app().getNodeStore();
1190 if (!BEAST_EXPECT(data.makeLedgers(env)))
1194 BEAST_EXPECT(
saveLedger(ndb, *data.ledgers_[i]));
1201 auto const finalShards{db->
getShardInfo()->finalized()};
1203 BEAST_EXPECT(boost::icl::contains(finalShards, shardIndex));
1211 if (!BEAST_EXPECT(data.makeLedgers(env)))
1217 auto const finalShards{db->
getShardInfo()->finalized()};
1219 BEAST_EXPECT(boost::icl::contains(finalShards, shardIndex));
1229 testcase(
"Import node store with online delete");
1231 using namespace test::jtx;
1241 section.set(
"online_delete",
"550");
1242 section.set(
"advisory_delete",
"1");
1245 c->section(SECTION_RPC_STARTUP)
1247 "{ \"command\": \"log_level\", \"severity\": \"trace\" "
1251 Env env{*
this, std::move(c), std::move(logs)};
1254 Database& ndb = env.app().getNodeStore();
1257 auto& store = env.app().getSHAMapStore();
1262 store.setCanDelete(10);
1265 auto const shardCount = 5;
1266 TestData data(seedValue, 4, shardCount);
1267 if (!BEAST_EXPECT(data.makeLedgers(env)))
1271 auto const lastRotated = store.getLastRotated();
1272 BEAST_EXPECT(lastRotated >= 553 && lastRotated < 1103);
1286 auto pauseVerifier =
std::thread([lastRotated, &store, db,
this] {
1289 auto rotationProgress = lastRotated;
1295 auto const last = store.getLastRotated();
1296 if (last != rotationProgress)
1302 !ledgerSeq || ledgerSeq >= rotationProgress);
1303 rotationProgress = last;
1308 auto join = [&pauseVerifier]() {
1309 if (pauseVerifier.joinable())
1310 pauseVerifier.join();
1315 if (!BEAST_EXPECT(data.makeLedgers(env, shardCount)))
1322 BEAST_EXPECT(store.getLastRotated() != lastRotated);
1327 auto const expectedLogMessage =
1328 "rotation would interfere with ShardStore import";
1330 capturedLogs.
find(expectedLogMessage) != std::string::npos);
1336 testcase(
"Import with historical paths");
1338 using namespace test::jtx;
1347 historicalDirs.
begin(),
1348 historicalDirs.
end(),
1349 historicalPaths.
begin(),
1355 auto& historyPaths = c->section(SECTION_HISTORICAL_SHARD_PATHS);
1356 historyPaths.append(
1357 {historicalPaths[0].string(),
1358 historicalPaths[1].
string(),
1359 historicalPaths[2].
string(),
1360 historicalPaths[3].
string()});
1362 Env env{*
this, std::move(c)};
1364 Database& ndb = env.app().getNodeStore();
1367 auto const shardCount = 4;
1369 TestData data(seedValue, 4, shardCount);
1370 if (!BEAST_EXPECT(data.makeLedgers(env)))
1374 BEAST_EXPECT(
saveLedger(ndb, *data.ledgers_[i]));
1384 BEAST_EXPECT(boost::icl::contains(
final, shardIndex));
1387 boost::filesystem::directory_iterator(shardDir.
path()),
1388 boost::filesystem::directory_iterator());
1392 BEAST_EXPECT(mainPathCount == 2);
1395 historicalPaths.
begin(),
1396 historicalPaths.
end(),
1398 [](
int const sum, boost::filesystem::path
const& path) {
1401 boost::filesystem::directory_iterator(path),
1402 boost::filesystem::directory_iterator());
1407 BEAST_EXPECT(historicalPathCount == shardCount - 2);
1418 auto& historyPaths = c->section(SECTION_HISTORICAL_SHARD_PATHS);
1419 historyPaths.append({historicalDir.
path()});
1421 Env env{*
this, std::move(c)};
1423 Database& ndb = env.app().getNodeStore();
1426 auto const shardCount = 4;
1428 TestData data(seedValue * 2, 4, shardCount);
1429 if (!BEAST_EXPECT(data.makeLedgers(env)))
1433 BEAST_EXPECT(
saveLedger(ndb, *data.ledgers_[i]));
1441 auto const finalShards{db->
getShardInfo()->finalized()};
1443 BEAST_EXPECT(boost::icl::contains(finalShards, shardIndex));
1446 boost::filesystem::directory_iterator(shardDir.
path()),
1447 boost::filesystem::directory_iterator());
1451 BEAST_EXPECT(mainPathCount == 2);
1454 boost::filesystem::directory_iterator(historicalDir.
path()),
1455 boost::filesystem::directory_iterator());
1459 BEAST_EXPECT(historicalPathCount == shardCount - 2);
1466 testcase(
"Prepare with historical paths");
1468 using namespace test::jtx;
1477 auto& paths{config->section(SECTION_HISTORICAL_SHARD_PATHS)};
1478 for (
auto const& dir : historicalDirs)
1479 paths.append(dir.path());
1482 Env env{*
this, std::move(config)};
1486 TestData data(seedValue, 4, numShards);
1487 if (!BEAST_EXPECT(data.makeLedgers(env)))
1490 auto shardStore{env.app().getShardStore()};
1491 BEAST_EXPECT(shardStore);
1493 for (
auto i = 0; i < numShards; ++i)
1495 auto const shardIndex{
createShard(data, *shardStore, numShards)};
1497 shardIndex && *shardIndex >= 1 && *shardIndex <= numShards))
1505 auto const finalized{shardStore->getShardInfo()->finalized()};
1506 BEAST_EXPECT(boost::icl::length(
finalized) == numShards);
1507 BEAST_EXPECT(boost::icl::first(
finalized) == 1);
1508 BEAST_EXPECT(boost::icl::last(
finalized) == numShards);
1511 using namespace boost::filesystem;
1515 for (
auto const& it : directory_iterator(dir.
path()))
1516 if (boost::filesystem::path(it).stem() == path)
1520 auto const historicalDirsContains = [&](
std::uint32_t shardIndex) {
1521 for (
auto const& dir : historicalDirs)
1522 if (dirContains(dir, shardIndex))
1528 for (
auto const shardIndex : {numShards - 1, numShards})
1530 BEAST_EXPECT(dirContains(primaryDir, shardIndex));
1531 BEAST_EXPECT(!historicalDirsContains(shardIndex));
1535 for (
auto shardIndex = 1; shardIndex < numShards - 1; ++shardIndex)
1537 BEAST_EXPECT(!dirContains(primaryDir, shardIndex));
1538 BEAST_EXPECT(historicalDirsContains(shardIndex));
1542 data =
TestData(seedValue * 2, 4, numShards);
1543 if (!BEAST_EXPECT(data.makeLedgers(env, numShards)))
1546 for (
auto i = 0; i < numShards; ++i)
1548 auto const shardIndex{
1549 createShard(data, *shardStore, numShards * 2, numShards)};
1551 shardIndex && *shardIndex >= numShards + 1 &&
1552 *shardIndex <= numShards * 2))
1560 auto const finalized{shardStore->getShardInfo()->finalized()};
1561 BEAST_EXPECT(boost::icl::length(
finalized) == numShards * 2);
1562 BEAST_EXPECT(boost::icl::first(
finalized) == 1);
1563 BEAST_EXPECT(boost::icl::last(
finalized) == numShards * 2);
1567 for (
auto const shardIndex : {numShards * 2 - 1, numShards * 2})
1569 BEAST_EXPECT(dirContains(primaryDir, shardIndex));
1570 BEAST_EXPECT(!historicalDirsContains(shardIndex));
1574 for (
auto shardIndex = 1; shardIndex < numShards * 2 - 1; ++shardIndex)
1576 BEAST_EXPECT(!dirContains(primaryDir, shardIndex));
1577 BEAST_EXPECT(historicalDirsContains(shardIndex));
1584 testcase(
"Open shard management");
1586 using namespace test::jtx;
1591 auto shardStore{env.app().getShardStore()};
1592 BEAST_EXPECT(shardStore);
1599 TestData data(seedValue, 2, numShards);
1600 if (!BEAST_EXPECT(data.makeLedgers(env)))
1603 BEAST_EXPECT(shardStore->getShardInfo()->finalized().empty());
1605 int oldestShardIndex{-1};
1606 for (
auto i = 0; i < numShards; ++i)
1608 auto shardIndex{
createShard(data, *shardStore, numShards)};
1610 shardIndex && *shardIndex >= 1 && *shardIndex <= numShards))
1615 BEAST_EXPECT(boost::icl::contains(
1616 shardStore->getShardInfo()->finalized(), *shardIndex));
1618 if (oldestShardIndex == -1)
1619 oldestShardIndex = *shardIndex;
1624 shardStore->sweep();
1627 auto const ledgerSeq{shardStore->lastLedgerSeq(oldestShardIndex)};
1629 BEAST_EXPECT(shardStore->fetchNodeObject(
1630 data.ledgers_[index]->info().hash, ledgerSeq));
1636 testcase(
"Shard info");
1638 using namespace test::jtx;
1642 auto shardStore{env.app().getShardStore()};
1643 BEAST_EXPECT(shardStore);
1647 auto const shardInfo{shardStore->getShardInfo()};
1649 shardInfo->msgTimestamp().time_since_epoch().count() == 0);
1650 BEAST_EXPECT(
shardInfo->finalizedToString().empty());
1651 BEAST_EXPECT(
shardInfo->finalized().empty());
1652 BEAST_EXPECT(
shardInfo->incompleteToString().empty());
1653 BEAST_EXPECT(
shardInfo->incomplete().empty());
1658 if (!BEAST_EXPECT(data.makeLedgers(env)))
1665 auto const shardInfo{shardStore->getShardInfo()};
1666 BEAST_EXPECT(
shardInfo->finalizedToString().empty());
1667 BEAST_EXPECT(
shardInfo->finalized().empty());
1668 BEAST_EXPECT(
shardInfo->incompleteToString() ==
"1:0");
1677 if (!BEAST_EXPECT(shardIndex && *shardIndex == 1))
1683 auto const shardInfo{shardStore->getShardInfo()};
1684 BEAST_EXPECT(
shardInfo->finalizedToString() ==
"1");
1685 BEAST_EXPECT(boost::icl::contains(
shardInfo->finalized(), 1));
1686 BEAST_EXPECT(
shardInfo->incompleteToString().empty());
1687 BEAST_EXPECT(
shardInfo->incomplete().empty());
1689 BEAST_EXPECT(
shardInfo->setFinalizedFromString(
"2"));
1690 BEAST_EXPECT(
shardInfo->finalizedToString() ==
"2");
1691 BEAST_EXPECT(boost::icl::contains(
shardInfo->finalized(), 2));
1701 auto const ledgerSeq{
1703 if (!BEAST_EXPECT(ledgerSeq != std::nullopt))
1707 if (!BEAST_EXPECT(
saveLedger(*shardStore, *data.ledgers_[arrInd])))
1710 shardStore->setStored(data.ledgers_[arrInd]);
1713 auto const shardInfo{shardStore->getShardInfo()};
1714 BEAST_EXPECT(
shardInfo->incompleteToString() ==
"2:10");
1718 auto const timeStamp{env.app().timeKeeper().now()};
1720 BEAST_EXPECT(timeStamp ==
shardInfo->msgTimestamp());
1723 auto const msg{
shardInfo->makeMessage(env.app())};
1727 BEAST_EXPECT(msg.timestamp() != 0);
1728 s.
add32(msg.timestamp());
1732 BEAST_EXPECT(msg.incomplete_size() == 1);
1734 auto const& incomplete{msg.incomplete(0)};
1735 BEAST_EXPECT(incomplete.shardindex() == 2);
1736 s.
add32(incomplete.shardindex());
1739 static_cast<ShardState>(incomplete.state()) ==
1741 s.
add32(incomplete.state());
1743 BEAST_EXPECT(incomplete.has_progress());
1744 BEAST_EXPECT(incomplete.progress() == 10);
1745 s.
add32(incomplete.progress());
1749 BEAST_EXPECT(msg.has_finalized());
1750 BEAST_EXPECT(msg.finalized() ==
"1");
1751 s.
addRaw(msg.finalized().data(), msg.finalized().size());
1761 BEAST_EXPECT(msg.peerchain_size() == 0);
1767 testcase(
"SQLite Database");
1769 using namespace test::jtx;
1774 auto shardStore{env.app().getShardStore()};
1775 BEAST_EXPECT(shardStore);
1777 auto const shardCount = 3;
1778 TestData data(seedValue, 3, shardCount);
1779 if (!BEAST_EXPECT(data.makeLedgers(env)))
1782 BEAST_EXPECT(shardStore->getShardInfo()->finalized().empty());
1783 BEAST_EXPECT(shardStore->getShardInfo()->incompleteToString().empty());
1786 dynamic_cast<SQLiteDatabase*
>(&env.app().getRelationalDatabase());
1794 auto n =
createShard(data, *shardStore, shardCount);
1795 if (!BEAST_EXPECT(n && *n >= 1 && *n <= shardCount))
1801 rdb->closeLedgerDB();
1802 rdb->closeTransactionDB();
1805 auto infoCmp = [](
auto const& a,
auto const& b) {
1806 return a.hash == b.hash && a.txHash == b.txHash &&
1807 a.accountHash == b.accountHash &&
1808 a.parentHash == b.parentHash && a.drops == b.drops &&
1809 a.accepted == b.accepted && a.closeFlags == b.closeFlags &&
1810 a.closeTimeResolution == b.closeTimeResolution &&
1811 a.closeTime == b.closeTime;
1814 for (
auto const& ledger : data.ledgers_)
1819 if (shardStore->seqToShardIndex(ledger->
seq()) <
1820 shardStore->earliestShardIndex() ||
1821 ledger->
info().
seq < shardStore->earliestLedgerSeq())
1824 auto info = rdb->getLedgerInfoByHash(ledger->
info().
hash);
1827 BEAST_EXPECT(infoCmp(*info, ledger->
info()));
1829 for (
auto const& transaction : ledger->
txs)
1836 auto reference = rdb->getTransaction(
1837 transaction.first->getTransactionID(), {}, error);
1840 if (!BEAST_EXPECT(reference.index() == 0))
1843 auto txn = std::get<0>(reference).first->getSTransaction();
1846 transaction.first->getFullText() == txn->getFullText());
1853 data =
TestData(seedValue * 2, 4, 1);
1854 if (!BEAST_EXPECT(data.makeLedgers(env, shardCount)))
1866 auto seedValue = [] {