1#include <xrpl/shamap/SHAMap.h>
3#include <xrpl/basics/Blob.h>
4#include <xrpl/basics/Buffer.h>
5#include <xrpl/basics/SHAMapHash.h>
6#include <xrpl/basics/Slice.h>
7#include <xrpl/basics/base_uint.h>
8#include <xrpl/beast/utility/Journal.h>
9#include <xrpl/beast/utility/Zero.h>
10#include <xrpl/protocol/Serializer.h>
11#include <xrpl/shamap/SHAMapInnerNode.h>
12#include <xrpl/shamap/SHAMapItem.h>
13#include <xrpl/shamap/SHAMapLeafNode.h>
14#include <xrpl/shamap/SHAMapMissingNode.h>
15#include <xrpl/shamap/SHAMapNodeID.h>
16#include <xrpl/shamap/SHAMapTreeNode.h>
18#include <gtest/gtest.h>
19#include <helpers/TestSink.h>
20#include <shamap/common.h>
35#ifndef __INTELLISENSE__
36static_assert(std::is_nothrow_destructible<SHAMap>{});
37static_assert(!std::is_default_constructible<SHAMap>{});
38static_assert(!std::is_copy_constructible<SHAMap>{});
39static_assert(!std::is_copy_assignable<SHAMap>{});
40static_assert(!std::is_move_constructible<SHAMap>{});
41static_assert(!std::is_move_assignable<SHAMap>{});
43static_assert(std::is_nothrow_destructible<SHAMap::ConstIterator>{});
44static_assert(std::is_copy_constructible<SHAMap::ConstIterator>{});
45static_assert(std::is_copy_assignable<SHAMap::ConstIterator>{});
46static_assert(std::is_move_constructible<SHAMap::ConstIterator>{});
47static_assert(std::is_move_assignable<SHAMap::ConstIterator>{});
49static_assert(std::is_nothrow_destructible<SHAMapItem>{});
50static_assert(!std::is_default_constructible<SHAMapItem>{});
51static_assert(!std::is_copy_constructible<SHAMapItem>{});
53static_assert(std::is_nothrow_destructible<SHAMapNodeID>{});
54static_assert(std::is_default_constructible<SHAMapNodeID>{});
55static_assert(std::is_copy_constructible<SHAMapNodeID>{});
56static_assert(std::is_copy_assignable<SHAMapNodeID>{});
57static_assert(std::is_move_constructible<SHAMapNodeID>{});
58static_assert(std::is_move_assignable<SHAMapNodeID>{});
60static_assert(std::is_nothrow_destructible<SHAMapHash>{});
61static_assert(std::is_default_constructible<SHAMapHash>{});
62static_assert(std::is_copy_constructible<SHAMapHash>{});
63static_assert(std::is_copy_assignable<SHAMapHash>{});
64static_assert(std::is_move_constructible<SHAMapHash>{});
65static_assert(std::is_move_assignable<SHAMapHash>{});
67static_assert(std::is_nothrow_destructible<SHAMapTreeNode>{});
68static_assert(!std::is_default_constructible<SHAMapTreeNode>{});
69static_assert(!std::is_copy_constructible<SHAMapTreeNode>{});
70static_assert(!std::is_copy_assignable<SHAMapTreeNode>{});
71static_assert(!std::is_move_constructible<SHAMapTreeNode>{});
72static_assert(!std::is_move_assignable<SHAMapTreeNode>{});
74static_assert(std::is_nothrow_destructible<SHAMapInnerNode>{});
75static_assert(!std::is_default_constructible<SHAMapInnerNode>{});
76static_assert(!std::is_copy_constructible<SHAMapInnerNode>{});
77static_assert(!std::is_copy_assignable<SHAMapInnerNode>{});
78static_assert(!std::is_move_constructible<SHAMapInnerNode>{});
79static_assert(!std::is_move_assignable<SHAMapInnerNode>{});
81static_assert(std::is_nothrow_destructible<SHAMapLeafNode>{});
82static_assert(!std::is_default_constructible<SHAMapLeafNode>{});
83static_assert(!std::is_copy_constructible<SHAMapLeafNode>{});
84static_assert(!std::is_copy_assignable<SHAMapLeafNode>{});
85static_assert(!std::is_move_constructible<SHAMapLeafNode>{});
86static_assert(!std::is_move_assignable<SHAMapLeafNode>{});
110class SHAMapTest :
public ::testing::TestWithParam<SHAMapBackingMode>
126 auto const testMode = GetParam();
130 constexpr UInt256 kH1(
"092891fe4ef6cee585fdc6fda0e09eb4d386363158ec3321b8123e5a772c6ca7");
131 constexpr UInt256 kH2(
"436ccbac3347baa1f1e53baeef1f43334da88f1f6d70d963b833afd6dfa289fe");
132 constexpr UInt256 kH3(
"b92891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e5a772c6ca8");
133 constexpr UInt256 kH4(
"b92891fe4ef6cee585fdc6fda2e09eb4d386363158ec3321b8123e5a772c6ca8");
137 if (!testMode.backed)
150 auto i = sMap.
begin();
152 EXPECT_FALSE(i == e || (*i != *i1)) <<
"bad traverse";
154 EXPECT_FALSE(i == e || (*i != *i2)) <<
"bad traverse";
156 EXPECT_EQ(i, e) <<
"bad traverse";
165 EXPECT_FALSE(i == e || (*i != *i1)) <<
"bad traverse";
167 EXPECT_FALSE(i == e || (*i != *i3)) <<
"bad traverse";
169 EXPECT_FALSE(i == e || (*i != *i4)) <<
"bad traverse";
171 EXPECT_EQ(i, e) <<
"bad traverse";
176 EXPECT_EQ(sMap.
getHash(), mapHash) <<
"bad snapshot";
177 EXPECT_EQ(map2->getHash(), mapHash) <<
"bad snapshot";
180 ASSERT_TRUE(sMap.
compare(*map2, delta, 100));
181 EXPECT_TRUE(delta.
empty());
183 EXPECT_TRUE(sMap.
delItem(sMap.
begin()->key())) <<
"bad mod";
185 EXPECT_NE(sMap.
getHash(), mapHash) <<
"bad snapshot";
186 EXPECT_EQ(map2->getHash(), mapHash) <<
"bad snapshot";
188 ASSERT_TRUE(sMap.
compare(*map2, delta, 100));
189 ASSERT_EQ(delta.
size(), 1);
190 EXPECT_EQ(delta.
begin()->first, kH1);
191 EXPECT_EQ(delta.
begin()->second.first,
nullptr);
192 ASSERT_NE(delta.
begin()->second.second,
nullptr);
193 EXPECT_EQ(delta.
begin()->second.second->key(), kH1);
198 UInt256{
"b92891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e5a772c6ca8"},
199 UInt256{
"b92881fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e5a772c6ca8"},
200 UInt256{
"b92691fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e5a772c6ca8"},
201 UInt256{
"b92791fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e5a772c6ca8"},
202 UInt256{
"b91891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e5a772c6ca8"},
203 UInt256{
"b99891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e5a772c6ca8"},
204 UInt256{
"f22891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e5a772c6ca8"},
205 UInt256{
"292891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e5a772c6ca8"},
209 UInt256{
"B7387CFEA0465759ADC718E8C42B52D2309D179B326E239EB5075C64B6281F7F"},
210 UInt256{
"FBC195A9592A54AB44010274163CB6BA95F497EC5BA0A8831845467FB2ECE266"},
211 UInt256{
"4E7D2684B65DFD48937FFB775E20175C43AF0C94066F7D5679F51AE756795B75"},
212 UInt256{
"7A2F312EB203695FFD164E038E281839EEF06A1B99BFC263F3CECC6C74F93E07"},
213 UInt256{
"395A6691A372387A703FB0F2C6D2C405DAF307D0817F8F0E207596462B0E3A3E"},
214 UInt256{
"D044C0A696DE3169CC70AE216A1564D69DE96582865796142CE7D98A84D9DDE4"},
215 UInt256{
"76DCC77C4027309B5A91AD164083264D70B77B5E43E08AEDA5EBF94361143615"},
216 UInt256{
"DF4220E93ADC6F5569063A01B4DC79F8DB9553B6A3222ADE23DEA02BBE7230E5"},
220 if (!testMode.backed)
235 EXPECT_TRUE(map.
delItem(kKeys[k]));
243 UInt256{
"f22891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e5a772c6ca8"},
244 UInt256{
"b99891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e5a772c6ca8"},
245 UInt256{
"b92891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e5a772c6ca8"},
246 UInt256{
"b92881fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e5a772c6ca8"},
247 UInt256{
"b92791fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e5a772c6ca8"},
248 UInt256{
"b92691fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e5a772c6ca8"},
249 UInt256{
"b91891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e5a772c6ca8"},
250 UInt256{
"292891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e5a772c6ca8"},
255 if (!testMode.backed)
257 for (
auto const& k : kKeys)
263 auto keyIndex = kKeys.
size();
264 for (
auto const& k : map)
265 EXPECT_EQ(k.key(), kKeys[--keyIndex]);
292 auto text =
std::string(
"abcde") +
"0123456789abcdef"[branch];
293 text.
append(64 - text.size(),
'7');
309 auto text =
std::string(63,
'a') +
"0123456789abcdef"[branch];
319 for (
auto const& k : keys)
333 auto keys = deepFanOutKeys();
339 for (
auto const& item : map)
342 EXPECT_EQ(visited, keys);
348 auto keys = deepFanOutKeys();
358 ASSERT_NE(it, map.
end()) <<
"no successor for key " << k;
359 EXPECT_EQ(it->key(), keys[k + 1]) <<
"wrong successor for key " << k;
367 auto keys = deepFanOutKeys();
377 ASSERT_NE(it, map.
end()) <<
"no predecessor for key " << k;
378 EXPECT_EQ(it->key(), keys[k - 1]) <<
"wrong predecessor for key " << k;
386 auto keys = deepFanOutKeys();
392 for (
unsigned char const c : {0x00, 0x40, 0x80, 0xc0, 0xff})
399 if (expectedUpper == keys.end())
401 EXPECT_EQ(upper, map.
end()) <<
"probe " <<
static_cast<unsigned>(c);
405 ASSERT_NE(upper, map.
end()) <<
"probe " <<
static_cast<unsigned>(c);
406 EXPECT_EQ(upper->key(), *expectedUpper) <<
"probe " <<
static_cast<unsigned>(c);
413 EXPECT_EQ(lower, map.
end()) <<
"probe " <<
static_cast<unsigned>(c);
417 ASSERT_NE(lower, map.
end()) <<
"probe " <<
static_cast<unsigned>(c);
418 EXPECT_EQ(lower->key(), keys[lowerCount - 1]) <<
"probe " <<
static_cast<unsigned>(c);
426 for (
char const nibble : {
'0',
'f'})
429 text.
append(64 - text.size(), nibble);
434 if (expectedUpper == keys.end())
436 EXPECT_EQ(upper, map.
end()) <<
"nibble " << nibble;
440 ASSERT_NE(upper, map.
end()) <<
"nibble " << nibble;
441 EXPECT_EQ(upper->key(), *expectedUpper) <<
"nibble " << nibble;
448 EXPECT_EQ(lower, map.
end()) <<
"nibble " << nibble;
452 ASSERT_NE(lower, map.
end()) <<
"nibble " << nibble;
453 EXPECT_EQ(lower->key(), keys[lowerCount - 1]) <<
"nibble " << nibble;
481 auto const key = deepFanOutKeys().front();
492 ASSERT_NE(upper, map.
end());
493 EXPECT_EQ(upper->key(), key);
498 ASSERT_NE(lower, map.
end());
499 EXPECT_EQ(lower->key(), key);
507 auto keys = deepFanOutKeys();
518 ASSERT_TRUE(map.
delItem(keys[k]));
527 for (
auto const& item : map)
529 EXPECT_EQ(visited, expected);
534 ASSERT_NE(it, map.
end());
535 EXPECT_EQ(it->key(), expected[k + 1]);
548 auto fanOutKeys = deepFanOutKeysAtLeafDepth();
549 fillMap(map, fanOutKeys);
565 for (
std::size_t k = 0; k + 1 < fanOutKeys.size(); ++k)
567 ASSERT_TRUE(map.
delItem(fanOutKeys[k]));
573 for (
auto const& item : map)
575 EXPECT_EQ(visited, expected);
578 ASSERT_NE(it, map.
end());
579 EXPECT_EQ(it->key(), fanOutKeys.back());
592 auto keys = deepFanOutKeysAtLeafDepth();
598 for (
auto const& item : map)
601 EXPECT_EQ(visited, keys);
607 auto keys = deepFanOutKeysAtLeafDepth();
617 ASSERT_NE(it, map.
end()) <<
"no successor for key " << k;
618 EXPECT_EQ(it->key(), keys[k + 1]) <<
"wrong successor for key " << k;
626 auto keys = deepFanOutKeysAtLeafDepth();
636 ASSERT_NE(it, map.
end()) <<
"no predecessor for key " << k;
637 EXPECT_EQ(it->key(), keys[k - 1]) <<
"wrong predecessor for key " << k;
645 auto keys = deepFanOutKeysAtLeafDepth();
654 for (
unsigned int const divergeAt : {0u, 31u, 61u, 62u})
658 for (
char const nibble : {
'9',
'b'})
661 text.
append(64 - text.size(),
'0');
666 if (expectedUpper == keys.end())
668 EXPECT_EQ(upper, map.
end()) <<
"divergeAt " << divergeAt <<
" nibble " << nibble;
672 ASSERT_NE(upper, map.
end()) <<
"divergeAt " << divergeAt <<
" nibble " << nibble;
673 EXPECT_EQ(upper->key(), *expectedUpper)
674 <<
"divergeAt " << divergeAt <<
" nibble " << nibble;
681 EXPECT_EQ(lower, map.
end()) <<
"divergeAt " << divergeAt <<
" nibble " << nibble;
685 ASSERT_NE(lower, map.
end()) <<
"divergeAt " << divergeAt <<
" nibble " << nibble;
686 EXPECT_EQ(lower->key(), keys[lowerCount - 1])
687 <<
"divergeAt " << divergeAt <<
" nibble " << nibble;
696 auto keys = deepFanOutKeysAtLeafDepth();
706 ASSERT_TRUE(map.
delItem(keys[k]));
715 for (
auto const& item : map)
717 EXPECT_EQ(visited, expected);
722 ASSERT_NE(it, map.
end());
723 EXPECT_EQ(it->key(), expected[k + 1]);
743 static constexpr unsigned char kFirstKey = 1;
744 static constexpr unsigned char kKeyCount = 100;
745 static constexpr unsigned char kLastKey = kKeyCount - 1;
747 for (
unsigned char c = kFirstKey; c < kKeyCount; ++c)
757 ADD_FAILURE() <<
"Missing proof path";
760 auto& proofPath = *
path;
766 proofPath.insert(proofPath.begin(), proofPath.front());
776 goodPath = std::move(proofPath);
796 badPath.
front().back()--;
817 for (
auto const& k : {kA, kB})
826 ASSERT_TRUE(pathA.has_value());
828 EXPECT_EQ(pathA->size(), 65u);
833 ASSERT_TRUE(pathB.has_value());
835 EXPECT_EQ(pathB->size(), 65u);
845 constexpr UInt256 kTestKey(
"b92891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e5a772c6ca8");
866 childHash = node->getHash();
869 ASSERT_EQ(
path.size(), 65u);
888 if (!leaf || !leaf->isLeaf())
916 constexpr UInt256 kKey(
"1c8cec8e5e9b0e5e0e0f5b3e2c9f7a1d6b4e8c2a0d7f3b9e5c1a8d4f2b6e0c93");
917 constexpr UInt256 kOtherKey(
"e3f1a7d5b9c2e8f406a1d3b5c7e9f2a4d6b8c0e2f4a6d8b0c2e4f6a8d0b2c4e6");
919 for (
auto const& k : {kKey, kOtherKey})
928 ASSERT_TRUE(ownPath.has_value());
929 ASSERT_TRUE(otherPath.has_value());
933 auto const& ownLeaf = ownPath->front();
934 auto const& otherLeaf = otherPath->front();
940 ASSERT_EQ(goodPath.size(), 2u);
946 ASSERT_EQ(badPath.size(), 2u);
A generic endpoint for log messages.
static constexpr std::size_t size()
Like std::vector<char> but better.
std::size_t size() const noexcept
Returns the number of bytes in the buffer.
std::uint8_t const * data() const noexcept
Return a pointer to beginning of the storage.
void fill(std::uint8_t value) noexcept
Set every byte in the buffer to the given value.
UInt256 const & asUInt256() const
UInt256 const & key() const
static SHAMapNodeID createID(unsigned int depth, UInt256 const &key)
Create a SHAMapNodeID of a node with the depth of the node and the key of a leaf.
static SHAMapTreeNodePtr makeFromWire(Slice rawNode)
bool addItem(SHAMapNodeType type, boost::intrusive_ptr< SHAMapItem const > item)
ConstIterator upperBound(UInt256 const &id) const
Find the first item after the given item.
static constexpr unsigned int kLeafDepth
The depth of the hash map: data is only present in the leaves.
static constexpr unsigned int kBranchFactor
Number of children each non-leaf node has (the 'radix tree' part of the map).
void dump(bool withHashes=false) const
std::map< UInt256, DeltaItem > Delta
bool compare(SHAMap const &otherMap, Delta &differences, int maxCount) const
std::shared_ptr< SHAMap > snapShot(bool isMutable) const
bool delItem(UInt256 const &id)
ConstIterator end() const
ConstIterator lowerBound(UInt256 const &id) const
Find the object with the greatest object id smaller than the input id.
SHAMapHash getHash() const
ConstIterator begin() const
std::optional< std::vector< Blob > > getProofPath(UInt256 const &key) const
Get the proof path of the key.
static bool verifyProofPath(UInt256 const &rootHash, UInt256 const &key, std::vector< Blob > const &path)
Verify the proof path.
Blob const & peekData() const
int addBitString(BaseUInt< Bits, Tag > const &v)
int add8(unsigned char byteValue)
An immutable linear range of bytes.
static TestSink & instance()
static Buffer intToVuc(std::uint8_t v)
static std::vector< UInt256 > deepFanOutKeysAtLeafDepth()
static std::vector< UInt256 > deepFanOutKeys()
static void fillMap(SHAMap &map, std::vector< UInt256 > const &keys)
T emplace_back(T... args)
TEST_F(SHAMapTraversal, forward_iteration_visits_every_key_in_order)
constexpr SHAMapBackingMode kUnbackedMode
constexpr SHAMapBackingMode kBackedMode
TEST_P(SHAMapTest, add_traverse_snapshot_build_tear_and_iterate)
constexpr UInt256 kTestKey("b92891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e5a772c6ca8")
bool operator!=(SHAMapItem const &a, SHAMapItem const &b)
static std::pair< std::vector< Blob >, UInt256 > forgeRootOverLeaf(Blob const &leafBlob, UInt256 const &key)
Wrap a leaf blob in a forged root inner node whose branch for key carries that leaf's hash.
std::string shamapBackingModeName(::testing::TestParamInfo< SHAMapBackingMode > const &info)
INSTANTIATE_TEST_SUITE_P(BackingMode, SHAMapTest, ::testing::Values(kBackedMode, kUnbackedMode), shamapBackingModeName)
Number root(Number f, unsigned d)
Slice makeSlice(std::array< T, N > const &a)
boost::intrusive_ptr< SHAMapItem > makeShamapitem(UInt256 const &tag, Slice data)
unsigned int selectBranch(SHAMapNodeID const &id, UInt256 const &hash)
Returns the branch that would contain the given hash.
std::vector< unsigned char > Blob
Storage for linear binary data.
static constexpr unsigned char const kWireTypeInner
std::string_view testName