mirror of
https://github.com/XRPLF/rippled.git
synced 2025-12-06 17:27:55 +00:00
389 lines
14 KiB
C++
389 lines
14 KiB
C++
#include <test/shamap/common.h>
|
|
#include <test/unit_test/SuiteJournal.h>
|
|
|
|
#include <xrpl/basics/Blob.h>
|
|
#include <xrpl/basics/Buffer.h>
|
|
#include <xrpl/beast/unit_test.h>
|
|
#include <xrpl/beast/utility/Journal.h>
|
|
#include <xrpl/shamap/SHAMap.h>
|
|
|
|
namespace ripple {
|
|
namespace tests {
|
|
|
|
#ifndef __INTELLISENSE__
|
|
static_assert(std::is_nothrow_destructible<SHAMap>{}, "");
|
|
static_assert(!std::is_default_constructible<SHAMap>{}, "");
|
|
static_assert(!std::is_copy_constructible<SHAMap>{}, "");
|
|
static_assert(!std::is_copy_assignable<SHAMap>{}, "");
|
|
static_assert(!std::is_move_constructible<SHAMap>{}, "");
|
|
static_assert(!std::is_move_assignable<SHAMap>{}, "");
|
|
|
|
static_assert(std::is_nothrow_destructible<SHAMap::const_iterator>{}, "");
|
|
static_assert(std::is_copy_constructible<SHAMap::const_iterator>{}, "");
|
|
static_assert(std::is_copy_assignable<SHAMap::const_iterator>{}, "");
|
|
static_assert(std::is_move_constructible<SHAMap::const_iterator>{}, "");
|
|
static_assert(std::is_move_assignable<SHAMap::const_iterator>{}, "");
|
|
|
|
static_assert(std::is_nothrow_destructible<SHAMapItem>{}, "");
|
|
static_assert(!std::is_default_constructible<SHAMapItem>{}, "");
|
|
static_assert(!std::is_copy_constructible<SHAMapItem>{}, "");
|
|
|
|
static_assert(std::is_nothrow_destructible<SHAMapNodeID>{}, "");
|
|
static_assert(std::is_default_constructible<SHAMapNodeID>{}, "");
|
|
static_assert(std::is_copy_constructible<SHAMapNodeID>{}, "");
|
|
static_assert(std::is_copy_assignable<SHAMapNodeID>{}, "");
|
|
static_assert(std::is_move_constructible<SHAMapNodeID>{}, "");
|
|
static_assert(std::is_move_assignable<SHAMapNodeID>{}, "");
|
|
|
|
static_assert(std::is_nothrow_destructible<SHAMapHash>{}, "");
|
|
static_assert(std::is_default_constructible<SHAMapHash>{}, "");
|
|
static_assert(std::is_copy_constructible<SHAMapHash>{}, "");
|
|
static_assert(std::is_copy_assignable<SHAMapHash>{}, "");
|
|
static_assert(std::is_move_constructible<SHAMapHash>{}, "");
|
|
static_assert(std::is_move_assignable<SHAMapHash>{}, "");
|
|
|
|
static_assert(std::is_nothrow_destructible<SHAMapTreeNode>{}, "");
|
|
static_assert(!std::is_default_constructible<SHAMapTreeNode>{}, "");
|
|
static_assert(!std::is_copy_constructible<SHAMapTreeNode>{}, "");
|
|
static_assert(!std::is_copy_assignable<SHAMapTreeNode>{}, "");
|
|
static_assert(!std::is_move_constructible<SHAMapTreeNode>{}, "");
|
|
static_assert(!std::is_move_assignable<SHAMapTreeNode>{}, "");
|
|
|
|
static_assert(std::is_nothrow_destructible<SHAMapInnerNode>{}, "");
|
|
static_assert(!std::is_default_constructible<SHAMapInnerNode>{}, "");
|
|
static_assert(!std::is_copy_constructible<SHAMapInnerNode>{}, "");
|
|
static_assert(!std::is_copy_assignable<SHAMapInnerNode>{}, "");
|
|
static_assert(!std::is_move_constructible<SHAMapInnerNode>{}, "");
|
|
static_assert(!std::is_move_assignable<SHAMapInnerNode>{}, "");
|
|
|
|
static_assert(std::is_nothrow_destructible<SHAMapLeafNode>{}, "");
|
|
static_assert(!std::is_default_constructible<SHAMapLeafNode>{}, "");
|
|
static_assert(!std::is_copy_constructible<SHAMapLeafNode>{}, "");
|
|
static_assert(!std::is_copy_assignable<SHAMapLeafNode>{}, "");
|
|
static_assert(!std::is_move_constructible<SHAMapLeafNode>{}, "");
|
|
static_assert(!std::is_move_assignable<SHAMapLeafNode>{}, "");
|
|
#endif
|
|
|
|
inline bool
|
|
operator==(SHAMapItem const& a, SHAMapItem const& b)
|
|
{
|
|
return a.key() == b.key();
|
|
}
|
|
inline bool
|
|
operator!=(SHAMapItem const& a, SHAMapItem const& b)
|
|
{
|
|
return a.key() != b.key();
|
|
}
|
|
inline bool
|
|
operator==(SHAMapItem const& a, uint256 const& b)
|
|
{
|
|
return a.key() == b;
|
|
}
|
|
inline bool
|
|
operator!=(SHAMapItem const& a, uint256 const& b)
|
|
{
|
|
return a.key() != b;
|
|
}
|
|
|
|
class SHAMap_test : public beast::unit_test::suite
|
|
{
|
|
public:
|
|
static Buffer
|
|
IntToVUC(int v)
|
|
{
|
|
Buffer vuc(32);
|
|
std::fill_n(vuc.data(), vuc.size(), static_cast<std::uint8_t>(v));
|
|
return vuc;
|
|
}
|
|
|
|
void
|
|
run() override
|
|
{
|
|
using namespace beast::severities;
|
|
test::SuiteJournal journal("SHAMap_test", *this);
|
|
|
|
run(true, journal);
|
|
run(false, journal);
|
|
}
|
|
|
|
void
|
|
run(bool backed, beast::Journal const& journal)
|
|
{
|
|
if (backed)
|
|
testcase("add/traverse backed");
|
|
else
|
|
testcase("add/traverse unbacked");
|
|
|
|
tests::TestNodeFamily f(journal);
|
|
|
|
// h3 and h4 differ only in the leaf, same terminal node (level 19)
|
|
constexpr uint256 h1(
|
|
"092891fe4ef6cee585fdc6fda0e09eb4d386363158ec3321b8123e5a772c6ca7");
|
|
constexpr uint256 h2(
|
|
"436ccbac3347baa1f1e53baeef1f43334da88f1f6d70d963b833afd6dfa289fe");
|
|
constexpr uint256 h3(
|
|
"b92891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e5a772c6ca8");
|
|
constexpr uint256 h4(
|
|
"b92891fe4ef6cee585fdc6fda2e09eb4d386363158ec3321b8123e5a772c6ca8");
|
|
constexpr uint256 h5(
|
|
"a92891fe4ef6cee585fdc6fda0e09eb4d386363158ec3321b8123e5a772c6ca7");
|
|
|
|
SHAMap sMap(SHAMapType::FREE, f);
|
|
sMap.invariants();
|
|
if (!backed)
|
|
sMap.setUnbacked();
|
|
|
|
auto i1 = make_shamapitem(h1, IntToVUC(1));
|
|
auto i2 = make_shamapitem(h2, IntToVUC(2));
|
|
auto i3 = make_shamapitem(h3, IntToVUC(3));
|
|
auto i4 = make_shamapitem(h4, IntToVUC(4));
|
|
auto i5 = make_shamapitem(h5, IntToVUC(5));
|
|
|
|
unexpected(
|
|
!sMap.addItem(
|
|
SHAMapNodeType::tnTRANSACTION_NM, make_shamapitem(*i2)),
|
|
"no add");
|
|
sMap.invariants();
|
|
unexpected(
|
|
!sMap.addItem(
|
|
SHAMapNodeType::tnTRANSACTION_NM, make_shamapitem(*i1)),
|
|
"no add");
|
|
sMap.invariants();
|
|
|
|
auto i = sMap.begin();
|
|
auto e = sMap.end();
|
|
unexpected(i == e || (*i != *i1), "bad traverse");
|
|
++i;
|
|
unexpected(i == e || (*i != *i2), "bad traverse");
|
|
++i;
|
|
unexpected(i != e, "bad traverse");
|
|
sMap.addItem(SHAMapNodeType::tnTRANSACTION_NM, make_shamapitem(*i4));
|
|
sMap.invariants();
|
|
sMap.delItem(i2->key());
|
|
sMap.invariants();
|
|
sMap.addItem(SHAMapNodeType::tnTRANSACTION_NM, make_shamapitem(*i3));
|
|
sMap.invariants();
|
|
i = sMap.begin();
|
|
e = sMap.end();
|
|
unexpected(i == e || (*i != *i1), "bad traverse");
|
|
++i;
|
|
unexpected(i == e || (*i != *i3), "bad traverse");
|
|
++i;
|
|
unexpected(i == e || (*i != *i4), "bad traverse");
|
|
++i;
|
|
unexpected(i != e, "bad traverse");
|
|
|
|
if (backed)
|
|
testcase("snapshot backed");
|
|
else
|
|
testcase("snapshot unbacked");
|
|
|
|
SHAMapHash mapHash = sMap.getHash();
|
|
std::shared_ptr<SHAMap> map2 = sMap.snapShot(false);
|
|
map2->invariants();
|
|
unexpected(sMap.getHash() != mapHash, "bad snapshot");
|
|
unexpected(map2->getHash() != mapHash, "bad snapshot");
|
|
|
|
SHAMap::Delta delta;
|
|
BEAST_EXPECT(sMap.compare(*map2, delta, 100));
|
|
BEAST_EXPECT(delta.empty());
|
|
|
|
unexpected(!sMap.delItem(sMap.begin()->key()), "bad mod");
|
|
sMap.invariants();
|
|
unexpected(sMap.getHash() == mapHash, "bad snapshot");
|
|
unexpected(map2->getHash() != mapHash, "bad snapshot");
|
|
|
|
BEAST_EXPECT(sMap.compare(*map2, delta, 100));
|
|
BEAST_EXPECT(delta.size() == 1);
|
|
BEAST_EXPECT(delta.begin()->first == h1);
|
|
BEAST_EXPECT(delta.begin()->second.first == nullptr);
|
|
BEAST_EXPECT(delta.begin()->second.second->key() == h1);
|
|
|
|
sMap.dump();
|
|
|
|
if (backed)
|
|
testcase("build/tear backed");
|
|
else
|
|
testcase("build/tear unbacked");
|
|
{
|
|
constexpr std::array keys{
|
|
uint256("b92891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
|
|
"5a772c6ca8"),
|
|
uint256("b92881fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
|
|
"5a772c6ca8"),
|
|
uint256("b92691fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
|
|
"5a772c6ca8"),
|
|
uint256("b92791fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
|
|
"5a772c6ca8"),
|
|
uint256("b91891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
|
|
"5a772c6ca8"),
|
|
uint256("b99891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
|
|
"5a772c6ca8"),
|
|
uint256("f22891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
|
|
"5a772c6ca8"),
|
|
uint256("292891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
|
|
"5a772c6ca8")};
|
|
|
|
constexpr std::array hashes{
|
|
uint256("B7387CFEA0465759ADC718E8C42B52D2309D179B326E239EB5075C"
|
|
"64B6281F7F"),
|
|
uint256("FBC195A9592A54AB44010274163CB6BA95F497EC5BA0A883184546"
|
|
"7FB2ECE266"),
|
|
uint256("4E7D2684B65DFD48937FFB775E20175C43AF0C94066F7D5679F51A"
|
|
"E756795B75"),
|
|
uint256("7A2F312EB203695FFD164E038E281839EEF06A1B99BFC263F3CECC"
|
|
"6C74F93E07"),
|
|
uint256("395A6691A372387A703FB0F2C6D2C405DAF307D0817F8F0E207596"
|
|
"462B0E3A3E"),
|
|
uint256("D044C0A696DE3169CC70AE216A1564D69DE96582865796142CE7D9"
|
|
"8A84D9DDE4"),
|
|
uint256("76DCC77C4027309B5A91AD164083264D70B77B5E43E08AEDA5EBF9"
|
|
"4361143615"),
|
|
uint256("DF4220E93ADC6F5569063A01B4DC79F8DB9553B6A3222ADE23DEA0"
|
|
"2BBE7230E5")};
|
|
|
|
SHAMap map(SHAMapType::FREE, f);
|
|
if (!backed)
|
|
map.setUnbacked();
|
|
|
|
BEAST_EXPECT(map.getHash() == beast::zero);
|
|
for (int k = 0; k < keys.size(); ++k)
|
|
{
|
|
BEAST_EXPECT(map.addItem(
|
|
SHAMapNodeType::tnTRANSACTION_NM,
|
|
make_shamapitem(keys[k], IntToVUC(k))));
|
|
BEAST_EXPECT(map.getHash().as_uint256() == hashes[k]);
|
|
map.invariants();
|
|
}
|
|
for (int k = keys.size() - 1; k >= 0; --k)
|
|
{
|
|
BEAST_EXPECT(map.getHash().as_uint256() == hashes[k]);
|
|
BEAST_EXPECT(map.delItem(keys[k]));
|
|
map.invariants();
|
|
}
|
|
BEAST_EXPECT(map.getHash() == beast::zero);
|
|
}
|
|
|
|
if (backed)
|
|
testcase("iterate backed");
|
|
else
|
|
testcase("iterate unbacked");
|
|
|
|
{
|
|
constexpr std::array keys{
|
|
uint256("f22891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
|
|
"5a772c6ca8"),
|
|
uint256("b99891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
|
|
"5a772c6ca8"),
|
|
uint256("b92891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
|
|
"5a772c6ca8"),
|
|
uint256("b92881fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
|
|
"5a772c6ca8"),
|
|
uint256("b92791fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
|
|
"5a772c6ca8"),
|
|
uint256("b92691fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
|
|
"5a772c6ca8"),
|
|
uint256("b91891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
|
|
"5a772c6ca8"),
|
|
uint256("292891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
|
|
"5a772c6ca8")};
|
|
|
|
tests::TestNodeFamily tf{journal};
|
|
SHAMap map{SHAMapType::FREE, tf};
|
|
if (!backed)
|
|
map.setUnbacked();
|
|
for (auto const& k : keys)
|
|
{
|
|
map.addItem(
|
|
SHAMapNodeType::tnTRANSACTION_NM,
|
|
make_shamapitem(k, IntToVUC(0)));
|
|
map.invariants();
|
|
}
|
|
|
|
int h = 7;
|
|
for (auto const& k : map)
|
|
{
|
|
BEAST_EXPECT(k.key() == keys[h]);
|
|
--h;
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
class SHAMapPathProof_test : public beast::unit_test::suite
|
|
{
|
|
void
|
|
run() override
|
|
{
|
|
test::SuiteJournal journal("SHAMapPathProof_test", *this);
|
|
|
|
tests::TestNodeFamily tf{journal};
|
|
SHAMap map{SHAMapType::FREE, tf};
|
|
map.setUnbacked();
|
|
|
|
uint256 key;
|
|
uint256 rootHash;
|
|
std::vector<Blob> goodPath;
|
|
|
|
for (unsigned char c = 1; c < 100; ++c)
|
|
{
|
|
uint256 k(c);
|
|
map.addItem(
|
|
SHAMapNodeType::tnACCOUNT_STATE,
|
|
make_shamapitem(k, Slice{k.data(), k.size()}));
|
|
map.invariants();
|
|
|
|
auto root = map.getHash().as_uint256();
|
|
auto path = map.getProofPath(k);
|
|
BEAST_EXPECT(path);
|
|
if (!path)
|
|
break;
|
|
BEAST_EXPECT(map.verifyProofPath(root, k, *path));
|
|
if (c == 1)
|
|
{
|
|
// extra node
|
|
path->insert(path->begin(), path->front());
|
|
BEAST_EXPECT(!map.verifyProofPath(root, k, *path));
|
|
// wrong key
|
|
uint256 wrongKey(c + 1);
|
|
BEAST_EXPECT(!map.getProofPath(wrongKey));
|
|
}
|
|
if (c == 99)
|
|
{
|
|
key = k;
|
|
rootHash = root;
|
|
goodPath = std::move(*path);
|
|
}
|
|
}
|
|
|
|
// still good
|
|
BEAST_EXPECT(map.verifyProofPath(rootHash, key, goodPath));
|
|
// empty path
|
|
std::vector<Blob> badPath;
|
|
BEAST_EXPECT(!map.verifyProofPath(rootHash, key, badPath));
|
|
// too long
|
|
badPath = goodPath;
|
|
badPath.push_back(goodPath.back());
|
|
BEAST_EXPECT(!map.verifyProofPath(rootHash, key, badPath));
|
|
// bad node
|
|
badPath.clear();
|
|
badPath.emplace_back(100, 100);
|
|
BEAST_EXPECT(!map.verifyProofPath(rootHash, key, badPath));
|
|
// bad node type
|
|
badPath.clear();
|
|
badPath.push_back(goodPath.front());
|
|
badPath.front().back()--; // change node type
|
|
BEAST_EXPECT(!map.verifyProofPath(rootHash, key, badPath));
|
|
// all inner
|
|
badPath.clear();
|
|
badPath = goodPath;
|
|
badPath.erase(badPath.begin());
|
|
BEAST_EXPECT(!map.verifyProofPath(rootHash, key, badPath));
|
|
}
|
|
};
|
|
|
|
BEAST_DEFINE_TESTSUITE(SHAMap, shamap, ripple);
|
|
BEAST_DEFINE_TESTSUITE(SHAMapPathProof, shamap, ripple);
|
|
} // namespace tests
|
|
} // namespace ripple
|