xrpld
Loading...
Searching...
No Matches
tests/libxrpl/shamap/SHAMap.cpp
1#include <xrpl/shamap/SHAMap.h>
2
3#include <xrpl/basics/Blob.h>
4#include <xrpl/basics/Buffer.h>
5#include <xrpl/basics/SHAMapHash.h>
6#include <xrpl/basics/base_uint.h>
7#include <xrpl/beast/utility/Journal.h>
8#include <xrpl/beast/utility/Zero.h>
9#include <xrpl/shamap/SHAMapInnerNode.h>
10#include <xrpl/shamap/SHAMapItem.h>
11#include <xrpl/shamap/SHAMapLeafNode.h>
12#include <xrpl/shamap/SHAMapMissingNode.h>
13#include <xrpl/shamap/SHAMapTreeNode.h>
14
15#include <gtest/gtest.h>
16#include <helpers/TestSink.h>
17#include <shamap/common.h>
18
19#include <array>
20#include <cstddef>
21#include <cstdint>
22#include <memory>
23#include <string>
24#include <string_view>
25#include <type_traits>
26#include <utility>
27#include <vector>
28
29namespace xrpl::tests {
30
31#ifndef __INTELLISENSE__
32static_assert(std::is_nothrow_destructible<SHAMap>{});
33static_assert(!std::is_default_constructible<SHAMap>{});
34static_assert(!std::is_copy_constructible<SHAMap>{});
35static_assert(!std::is_copy_assignable<SHAMap>{});
36static_assert(!std::is_move_constructible<SHAMap>{});
37static_assert(!std::is_move_assignable<SHAMap>{});
38
39static_assert(std::is_nothrow_destructible<SHAMap::ConstIterator>{});
40static_assert(std::is_copy_constructible<SHAMap::ConstIterator>{});
41static_assert(std::is_copy_assignable<SHAMap::ConstIterator>{});
42static_assert(std::is_move_constructible<SHAMap::ConstIterator>{});
43static_assert(std::is_move_assignable<SHAMap::ConstIterator>{});
44
45static_assert(std::is_nothrow_destructible<SHAMapItem>{});
46static_assert(!std::is_default_constructible<SHAMapItem>{});
47static_assert(!std::is_copy_constructible<SHAMapItem>{});
48
49static_assert(std::is_nothrow_destructible<SHAMapNodeID>{});
50static_assert(std::is_default_constructible<SHAMapNodeID>{});
51static_assert(std::is_copy_constructible<SHAMapNodeID>{});
52static_assert(std::is_copy_assignable<SHAMapNodeID>{});
53static_assert(std::is_move_constructible<SHAMapNodeID>{});
54static_assert(std::is_move_assignable<SHAMapNodeID>{});
55
56static_assert(std::is_nothrow_destructible<SHAMapHash>{});
57static_assert(std::is_default_constructible<SHAMapHash>{});
58static_assert(std::is_copy_constructible<SHAMapHash>{});
59static_assert(std::is_copy_assignable<SHAMapHash>{});
60static_assert(std::is_move_constructible<SHAMapHash>{});
61static_assert(std::is_move_assignable<SHAMapHash>{});
62
63static_assert(std::is_nothrow_destructible<SHAMapTreeNode>{});
64static_assert(!std::is_default_constructible<SHAMapTreeNode>{});
65static_assert(!std::is_copy_constructible<SHAMapTreeNode>{});
66static_assert(!std::is_copy_assignable<SHAMapTreeNode>{});
67static_assert(!std::is_move_constructible<SHAMapTreeNode>{});
68static_assert(!std::is_move_assignable<SHAMapTreeNode>{});
69
70static_assert(std::is_nothrow_destructible<SHAMapInnerNode>{});
71static_assert(!std::is_default_constructible<SHAMapInnerNode>{});
72static_assert(!std::is_copy_constructible<SHAMapInnerNode>{});
73static_assert(!std::is_copy_assignable<SHAMapInnerNode>{});
74static_assert(!std::is_move_constructible<SHAMapInnerNode>{});
75static_assert(!std::is_move_assignable<SHAMapInnerNode>{});
76
77static_assert(std::is_nothrow_destructible<SHAMapLeafNode>{});
78static_assert(!std::is_default_constructible<SHAMapLeafNode>{});
79static_assert(!std::is_copy_constructible<SHAMapLeafNode>{});
80static_assert(!std::is_copy_assignable<SHAMapLeafNode>{});
81static_assert(!std::is_move_constructible<SHAMapLeafNode>{});
82static_assert(!std::is_move_assignable<SHAMapLeafNode>{});
83#endif
84
85inline bool
86operator!=(SHAMapItem const& a, SHAMapItem const& b)
87{
88 return a.key() != b.key();
89}
90
96
97constexpr SHAMapBackingMode kBackedMode{.backed = true, .testName = "backed"};
98constexpr SHAMapBackingMode kUnbackedMode{.backed = false, .testName = "unbacked"};
99
101shamapBackingModeName(::testing::TestParamInfo<SHAMapBackingMode> const& info)
102{
103 return std::string{info.param.testName};
104}
105
106class SHAMapTest : public ::testing::TestWithParam<SHAMapBackingMode>
107{
108protected:
110
111 static Buffer
113 {
114 Buffer vuc{32};
115 vuc.fill(v);
116 return vuc;
117 }
118};
119
120TEST_P(SHAMapTest, add_traverse_snapshot_build_tear_and_iterate)
121{
122 auto const testMode = GetParam();
124
125 // kH3 and kH4 differ only in the leaf, same terminal node (level 19)
126 constexpr uint256 kH1("092891fe4ef6cee585fdc6fda0e09eb4d386363158ec3321b8123e5a772c6ca7");
127 constexpr uint256 kH2("436ccbac3347baa1f1e53baeef1f43334da88f1f6d70d963b833afd6dfa289fe");
128 constexpr uint256 kH3("b92891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e5a772c6ca8");
129 constexpr uint256 kH4("b92891fe4ef6cee585fdc6fda2e09eb4d386363158ec3321b8123e5a772c6ca8");
130
131 SHAMap sMap{SHAMapType::FREE, f};
132 sMap.invariants();
133 if (!testMode.backed)
134 sMap.setUnbacked();
135
136 auto i1 = makeShamapitem(kH1, intToVuc(1));
137 auto i2 = makeShamapitem(kH2, intToVuc(2));
138 auto i3 = makeShamapitem(kH3, intToVuc(3));
139 auto i4 = makeShamapitem(kH4, intToVuc(4));
140
141 EXPECT_TRUE(sMap.addItem(SHAMapNodeType::TnTransactionNm, makeShamapitem(*i2))) << "no add";
142 sMap.invariants();
143 EXPECT_TRUE(sMap.addItem(SHAMapNodeType::TnTransactionNm, makeShamapitem(*i1))) << "no add";
144 sMap.invariants();
145
146 auto i = sMap.begin();
147 auto e = sMap.end();
148 EXPECT_FALSE(i == e || (*i != *i1)) << "bad traverse";
149 ++i;
150 EXPECT_FALSE(i == e || (*i != *i2)) << "bad traverse";
151 ++i;
152 EXPECT_EQ(i, e) << "bad traverse";
154 sMap.invariants();
155 sMap.delItem(i2->key());
156 sMap.invariants();
158 sMap.invariants();
159 i = sMap.begin();
160 e = sMap.end();
161 EXPECT_FALSE(i == e || (*i != *i1)) << "bad traverse";
162 ++i;
163 EXPECT_FALSE(i == e || (*i != *i3)) << "bad traverse";
164 ++i;
165 EXPECT_FALSE(i == e || (*i != *i4)) << "bad traverse";
166 ++i;
167 EXPECT_EQ(i, e) << "bad traverse";
168
169 SHAMapHash const mapHash = sMap.getHash();
170 std::shared_ptr<SHAMap> const map2 = sMap.snapShot(false);
171 map2->invariants();
172 EXPECT_EQ(sMap.getHash(), mapHash) << "bad snapshot";
173 EXPECT_EQ(map2->getHash(), mapHash) << "bad snapshot";
174
175 SHAMap::Delta delta;
176 ASSERT_TRUE(sMap.compare(*map2, delta, 100));
177 EXPECT_TRUE(delta.empty());
178
179 EXPECT_TRUE(sMap.delItem(sMap.begin()->key())) << "bad mod";
180 sMap.invariants();
181 EXPECT_NE(sMap.getHash(), mapHash) << "bad snapshot";
182 EXPECT_EQ(map2->getHash(), mapHash) << "bad snapshot";
183
184 ASSERT_TRUE(sMap.compare(*map2, delta, 100));
185 ASSERT_EQ(delta.size(), 1);
186 EXPECT_EQ(delta.begin()->first, kH1);
187 EXPECT_EQ(delta.begin()->second.first, nullptr);
188 ASSERT_NE(delta.begin()->second.second, nullptr);
189 EXPECT_EQ(delta.begin()->second.second->key(), kH1);
190
191 sMap.dump();
192 {
193 constexpr std::array kKeys{
194 uint256{"b92891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e5a772c6ca8"},
195 uint256{"b92881fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e5a772c6ca8"},
196 uint256{"b92691fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e5a772c6ca8"},
197 uint256{"b92791fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e5a772c6ca8"},
198 uint256{"b91891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e5a772c6ca8"},
199 uint256{"b99891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e5a772c6ca8"},
200 uint256{"f22891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e5a772c6ca8"},
201 uint256{"292891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e5a772c6ca8"},
202 };
203
204 constexpr std::array kHashes{
205 uint256{"B7387CFEA0465759ADC718E8C42B52D2309D179B326E239EB5075C64B6281F7F"},
206 uint256{"FBC195A9592A54AB44010274163CB6BA95F497EC5BA0A8831845467FB2ECE266"},
207 uint256{"4E7D2684B65DFD48937FFB775E20175C43AF0C94066F7D5679F51AE756795B75"},
208 uint256{"7A2F312EB203695FFD164E038E281839EEF06A1B99BFC263F3CECC6C74F93E07"},
209 uint256{"395A6691A372387A703FB0F2C6D2C405DAF307D0817F8F0E207596462B0E3A3E"},
210 uint256{"D044C0A696DE3169CC70AE216A1564D69DE96582865796142CE7D98A84D9DDE4"},
211 uint256{"76DCC77C4027309B5A91AD164083264D70B77B5E43E08AEDA5EBF94361143615"},
212 uint256{"DF4220E93ADC6F5569063A01B4DC79F8DB9553B6A3222ADE23DEA02BBE7230E5"},
213 };
214
215 SHAMap map{SHAMapType::FREE, f};
216 if (!testMode.backed)
217 map.setUnbacked();
218
219 EXPECT_EQ(map.getHash(), beast::kZero);
220 for (std::size_t k = 0; k < kKeys.size(); ++k)
221 {
222 EXPECT_TRUE(map.addItem(
224 makeShamapitem(kKeys[k], intToVuc(static_cast<std::uint8_t>(k)))));
225 EXPECT_EQ(map.getHash().asUInt256(), kHashes[k]);
226 map.invariants();
227 }
228 for (std::size_t k = kKeys.size(); k-- > 0;)
229 {
230 EXPECT_EQ(map.getHash().asUInt256(), kHashes[k]);
231 EXPECT_TRUE(map.delItem(kKeys[k]));
232 map.invariants();
233 }
234 EXPECT_EQ(map.getHash(), beast::kZero);
235 }
236
237 {
238 constexpr std::array kKeys{
239 uint256{"f22891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e5a772c6ca8"},
240 uint256{"b99891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e5a772c6ca8"},
241 uint256{"b92891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e5a772c6ca8"},
242 uint256{"b92881fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e5a772c6ca8"},
243 uint256{"b92791fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e5a772c6ca8"},
244 uint256{"b92691fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e5a772c6ca8"},
245 uint256{"b91891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e5a772c6ca8"},
246 uint256{"292891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e5a772c6ca8"},
247 };
248
250 SHAMap map{SHAMapType::FREE, tf};
251 if (!testMode.backed)
252 map.setUnbacked();
253 for (auto const& k : kKeys)
254 {
256 map.invariants();
257 }
258
259 auto keyIndex = kKeys.size();
260 for (auto const& k : map)
261 EXPECT_EQ(k.key(), kKeys[--keyIndex]);
262 }
263}
264
266 BackingMode,
268 ::testing::Values(kBackedMode, kUnbackedMode),
270
271class SHAMapPathProof : public ::testing::Test
272{
273protected:
275};
276
277TEST_F(SHAMapPathProof, verify_proof_path)
278{
280 SHAMap map{SHAMapType::FREE, tf};
281 map.setUnbacked();
282
283 uint256 key;
284 uint256 rootHash;
285 std::vector<Blob> goodPath;
286
287 static constexpr unsigned char kFirstKey = 1;
288 static constexpr unsigned char kKeyCount = 100;
289 static constexpr unsigned char kLastKey = kKeyCount - 1;
290
291 for (unsigned char c = kFirstKey; c < kKeyCount; ++c)
292 {
293 uint256 k(c);
295 map.invariants();
296
297 auto root = map.getHash().asUInt256();
298 auto path = map.getProofPath(k);
299 if (!path)
300 {
301 ADD_FAILURE() << "Missing proof path";
302 return;
303 }
304 auto& proofPath = *path;
305
306 EXPECT_TRUE(map.verifyProofPath(root, k, proofPath));
307 if (c == kFirstKey)
308 {
309 // extra node
310 proofPath.insert(proofPath.begin(), proofPath.front());
311 EXPECT_FALSE(map.verifyProofPath(root, k, proofPath));
312 // wrong key
313 uint256 const wrongKey(c + 1);
314 EXPECT_FALSE(map.getProofPath(wrongKey));
315 }
316 if (c == kLastKey)
317 {
318 key = k;
319 rootHash = root;
320 goodPath = std::move(proofPath);
321 }
322 }
323
324 // still good
325 EXPECT_TRUE(map.verifyProofPath(rootHash, key, goodPath));
326 // empty path
327 std::vector<Blob> badPath;
328 EXPECT_FALSE(map.verifyProofPath(rootHash, key, badPath));
329 // too long
330 badPath = goodPath;
331 badPath.push_back(goodPath.back());
332 EXPECT_FALSE(map.verifyProofPath(rootHash, key, badPath));
333 // bad node
334 badPath.clear();
335 badPath.emplace_back(100, 100);
336 EXPECT_FALSE(map.verifyProofPath(rootHash, key, badPath));
337 // bad node type
338 badPath.clear();
339 badPath.push_back(goodPath.front());
340 badPath.front().back()--; // change node type
341 EXPECT_FALSE(map.verifyProofPath(rootHash, key, badPath));
342 // all inner
343 badPath.clear();
344 badPath = goodPath;
345 badPath.erase(badPath.begin());
346 EXPECT_FALSE(map.verifyProofPath(rootHash, key, badPath));
347}
348
349} // namespace xrpl::tests
T back(T... args)
T begin(T... args)
A generic endpoint for log messages.
Definition Journal.h:44
pointer data()
Definition base_uint.h:117
static constexpr std::size_t size()
Definition base_uint.h:548
Like std::vector<char> but better.
Definition Buffer.h:19
void fill(std::uint8_t value) noexcept
Set every byte in the buffer to the given value.
Definition Buffer.h:168
uint256 const & asUInt256() const
Definition SHAMapHash.h:26
uint256 const & key() const
Definition SHAMapItem.h:74
bool addItem(SHAMapNodeType type, boost::intrusive_ptr< SHAMapItem const > item)
static bool verifyProofPath(uint256 const &rootHash, uint256 const &key, std::vector< Blob > const &path)
Verify the proof path.
std::optional< std::vector< Blob > > getProofPath(uint256 const &key) const
Get the proof path of the key.
std::map< uint256, DeltaItem > Delta
Definition SHAMap.h:148
void setUnbacked()
Definition SHAMap.h:678
void dump(bool withHashes=false) const
bool compare(SHAMap const &otherMap, Delta &differences, int maxCount) const
std::shared_ptr< SHAMap > snapShot(bool isMutable) const
ConstIterator end() const
Definition SHAMap.h:799
SHAMapHash getHash() const
ConstIterator begin() const
Definition SHAMap.h:793
bool delItem(uint256 const &id)
An immutable linear range of bytes.
Definition Slice.h:28
static TestSink & instance()
Definition TestSink.h:12
static Buffer intToVuc(std::uint8_t v)
T clear(T... args)
T emplace_back(T... args)
T empty(T... args)
T erase(T... args)
T front(T... args)
constexpr Zero kZero
Definition Zero.h:30
constexpr SHAMapBackingMode kUnbackedMode
constexpr SHAMapBackingMode kBackedMode
TEST_P(SHAMapTest, add_traverse_snapshot_build_tear_and_iterate)
TEST_F(SHAMapPathProof, verify_proof_path)
bool operator!=(SHAMapItem const &a, SHAMapItem const &b)
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)
boost::intrusive_ptr< SHAMapItem > makeShamapitem(uint256 const &tag, Slice data)
Definition SHAMapItem.h:148
BaseUInt< 256 > uint256
Definition base_uint.h:580
T push_back(T... args)
T size(T... args)