rippled
Loading...
Searching...
No Matches
SHAMap_test.cpp
1//------------------------------------------------------------------------------
2/*
3 This file is part of rippled: https://github.com/ripple/rippled
4 Copyright (c) 2012, 2013 Ripple Labs Inc.
5
6 Permission to use, copy, modify, and/or distribute this software for any
7 purpose with or without fee is hereby granted, provided that the above
8 copyright notice and this permission notice appear in all copies.
9
10 THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17*/
18//==============================================================================
19
20#include <test/shamap/common.h>
21#include <test/unit_test/SuiteJournal.h>
22
23#include <xrpl/basics/Blob.h>
24#include <xrpl/basics/Buffer.h>
25#include <xrpl/beast/unit_test.h>
26#include <xrpl/beast/utility/Journal.h>
27#include <xrpl/shamap/SHAMap.h>
28
29namespace ripple {
30namespace tests {
31
32#ifndef __INTELLISENSE__
33static_assert(std::is_nothrow_destructible<SHAMap>{}, "");
34static_assert(!std::is_default_constructible<SHAMap>{}, "");
35static_assert(!std::is_copy_constructible<SHAMap>{}, "");
36static_assert(!std::is_copy_assignable<SHAMap>{}, "");
37static_assert(!std::is_move_constructible<SHAMap>{}, "");
38static_assert(!std::is_move_assignable<SHAMap>{}, "");
39
45
48static_assert(!std::is_copy_constructible<SHAMapItem>{}, "");
49
53static_assert(std::is_copy_assignable<SHAMapNodeID>{}, "");
55static_assert(std::is_move_assignable<SHAMapNodeID>{}, "");
56
60static_assert(std::is_copy_assignable<SHAMapHash>{}, "");
62static_assert(std::is_move_assignable<SHAMapHash>{}, "");
63
67static_assert(!std::is_copy_assignable<SHAMapTreeNode>{}, "");
69static_assert(!std::is_move_assignable<SHAMapTreeNode>{}, "");
70
77
81static_assert(!std::is_copy_assignable<SHAMapLeafNode>{}, "");
83static_assert(!std::is_move_assignable<SHAMapLeafNode>{}, "");
84#endif
85
86inline bool
87operator==(SHAMapItem const& a, SHAMapItem const& b)
88{
89 return a.key() == b.key();
90}
91inline bool
92operator!=(SHAMapItem const& a, SHAMapItem const& b)
93{
94 return a.key() != b.key();
95}
96inline bool
97operator==(SHAMapItem const& a, uint256 const& b)
98{
99 return a.key() == b;
100}
101inline bool
102operator!=(SHAMapItem const& a, uint256 const& b)
103{
104 return a.key() != b;
105}
106
108{
109public:
110 static Buffer
111 IntToVUC(int v)
112 {
113 Buffer vuc(32);
114 std::fill_n(vuc.data(), vuc.size(), static_cast<std::uint8_t>(v));
115 return vuc;
116 }
117
118 void
119 run() override
120 {
121 using namespace beast::severities;
122 test::SuiteJournal journal("SHAMap_test", *this);
123
124 run(true, journal);
125 run(false, journal);
126 }
127
128 void
129 run(bool backed, beast::Journal const& journal)
130 {
131 if (backed)
132 testcase("add/traverse backed");
133 else
134 testcase("add/traverse unbacked");
135
136 tests::TestNodeFamily f(journal);
137
138 // h3 and h4 differ only in the leaf, same terminal node (level 19)
139 constexpr uint256 h1(
140 "092891fe4ef6cee585fdc6fda0e09eb4d386363158ec3321b8123e5a772c6ca7");
141 constexpr uint256 h2(
142 "436ccbac3347baa1f1e53baeef1f43334da88f1f6d70d963b833afd6dfa289fe");
143 constexpr uint256 h3(
144 "b92891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e5a772c6ca8");
145 constexpr uint256 h4(
146 "b92891fe4ef6cee585fdc6fda2e09eb4d386363158ec3321b8123e5a772c6ca8");
147 constexpr uint256 h5(
148 "a92891fe4ef6cee585fdc6fda0e09eb4d386363158ec3321b8123e5a772c6ca7");
149
150 SHAMap sMap(SHAMapType::FREE, f);
151 sMap.invariants();
152 if (!backed)
153 sMap.setUnbacked();
154
155 auto i1 = make_shamapitem(h1, IntToVUC(1));
156 auto i2 = make_shamapitem(h2, IntToVUC(2));
157 auto i3 = make_shamapitem(h3, IntToVUC(3));
158 auto i4 = make_shamapitem(h4, IntToVUC(4));
159 auto i5 = make_shamapitem(h5, IntToVUC(5));
160
162 !sMap.addItem(
164 "no add");
165 sMap.invariants();
167 !sMap.addItem(
169 "no add");
170 sMap.invariants();
171
172 auto i = sMap.begin();
173 auto e = sMap.end();
174 unexpected(i == e || (*i != *i1), "bad traverse");
175 ++i;
176 unexpected(i == e || (*i != *i2), "bad traverse");
177 ++i;
178 unexpected(i != e, "bad traverse");
180 sMap.invariants();
181 sMap.delItem(i2->key());
182 sMap.invariants();
184 sMap.invariants();
185 i = sMap.begin();
186 e = sMap.end();
187 unexpected(i == e || (*i != *i1), "bad traverse");
188 ++i;
189 unexpected(i == e || (*i != *i3), "bad traverse");
190 ++i;
191 unexpected(i == e || (*i != *i4), "bad traverse");
192 ++i;
193 unexpected(i != e, "bad traverse");
194
195 if (backed)
196 testcase("snapshot backed");
197 else
198 testcase("snapshot unbacked");
199
200 SHAMapHash mapHash = sMap.getHash();
201 std::shared_ptr<SHAMap> map2 = sMap.snapShot(false);
202 map2->invariants();
203 unexpected(sMap.getHash() != mapHash, "bad snapshot");
204 unexpected(map2->getHash() != mapHash, "bad snapshot");
205
206 SHAMap::Delta delta;
207 BEAST_EXPECT(sMap.compare(*map2, delta, 100));
208 BEAST_EXPECT(delta.empty());
209
210 unexpected(!sMap.delItem(sMap.begin()->key()), "bad mod");
211 sMap.invariants();
212 unexpected(sMap.getHash() == mapHash, "bad snapshot");
213 unexpected(map2->getHash() != mapHash, "bad snapshot");
214
215 BEAST_EXPECT(sMap.compare(*map2, delta, 100));
216 BEAST_EXPECT(delta.size() == 1);
217 BEAST_EXPECT(delta.begin()->first == h1);
218 BEAST_EXPECT(delta.begin()->second.first == nullptr);
219 BEAST_EXPECT(delta.begin()->second.second->key() == h1);
220
221 sMap.dump();
222
223 if (backed)
224 testcase("build/tear backed");
225 else
226 testcase("build/tear unbacked");
227 {
228 constexpr std::array keys{
229 uint256("b92891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
230 "5a772c6ca8"),
231 uint256("b92881fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
232 "5a772c6ca8"),
233 uint256("b92691fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
234 "5a772c6ca8"),
235 uint256("b92791fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
236 "5a772c6ca8"),
237 uint256("b91891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
238 "5a772c6ca8"),
239 uint256("b99891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
240 "5a772c6ca8"),
241 uint256("f22891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
242 "5a772c6ca8"),
243 uint256("292891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
244 "5a772c6ca8")};
245
246 constexpr std::array hashes{
247 uint256("B7387CFEA0465759ADC718E8C42B52D2309D179B326E239EB5075C"
248 "64B6281F7F"),
249 uint256("FBC195A9592A54AB44010274163CB6BA95F497EC5BA0A883184546"
250 "7FB2ECE266"),
251 uint256("4E7D2684B65DFD48937FFB775E20175C43AF0C94066F7D5679F51A"
252 "E756795B75"),
253 uint256("7A2F312EB203695FFD164E038E281839EEF06A1B99BFC263F3CECC"
254 "6C74F93E07"),
255 uint256("395A6691A372387A703FB0F2C6D2C405DAF307D0817F8F0E207596"
256 "462B0E3A3E"),
257 uint256("D044C0A696DE3169CC70AE216A1564D69DE96582865796142CE7D9"
258 "8A84D9DDE4"),
259 uint256("76DCC77C4027309B5A91AD164083264D70B77B5E43E08AEDA5EBF9"
260 "4361143615"),
261 uint256("DF4220E93ADC6F5569063A01B4DC79F8DB9553B6A3222ADE23DEA0"
262 "2BBE7230E5")};
263
264 SHAMap map(SHAMapType::FREE, f);
265 if (!backed)
266 map.setUnbacked();
267
268 BEAST_EXPECT(map.getHash() == beast::zero);
269 for (int k = 0; k < keys.size(); ++k)
270 {
271 BEAST_EXPECT(map.addItem(
273 make_shamapitem(keys[k], IntToVUC(k))));
274 BEAST_EXPECT(map.getHash().as_uint256() == hashes[k]);
275 map.invariants();
276 }
277 for (int k = keys.size() - 1; k >= 0; --k)
278 {
279 BEAST_EXPECT(map.getHash().as_uint256() == hashes[k]);
280 BEAST_EXPECT(map.delItem(keys[k]));
281 map.invariants();
282 }
283 BEAST_EXPECT(map.getHash() == beast::zero);
284 }
285
286 if (backed)
287 testcase("iterate backed");
288 else
289 testcase("iterate unbacked");
290
291 {
292 constexpr std::array keys{
293 uint256("f22891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
294 "5a772c6ca8"),
295 uint256("b99891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
296 "5a772c6ca8"),
297 uint256("b92891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
298 "5a772c6ca8"),
299 uint256("b92881fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
300 "5a772c6ca8"),
301 uint256("b92791fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
302 "5a772c6ca8"),
303 uint256("b92691fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
304 "5a772c6ca8"),
305 uint256("b91891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
306 "5a772c6ca8"),
307 uint256("292891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
308 "5a772c6ca8")};
309
310 tests::TestNodeFamily tf{journal};
311 SHAMap map{SHAMapType::FREE, tf};
312 if (!backed)
313 map.setUnbacked();
314 for (auto const& k : keys)
315 {
316 map.addItem(
319 map.invariants();
320 }
321
322 int h = 7;
323 for (auto const& k : map)
324 {
325 BEAST_EXPECT(k.key() == keys[h]);
326 --h;
327 }
328 }
329 }
330};
331
333{
334 void
335 run() override
336 {
337 test::SuiteJournal journal("SHAMapPathProof_test", *this);
338
339 tests::TestNodeFamily tf{journal};
340 SHAMap map{SHAMapType::FREE, tf};
341 map.setUnbacked();
342
343 uint256 key;
344 uint256 rootHash;
345 std::vector<Blob> goodPath;
346
347 for (unsigned char c = 1; c < 100; ++c)
348 {
349 uint256 k(c);
350 map.addItem(
352 make_shamapitem(k, Slice{k.data(), k.size()}));
353 map.invariants();
354
355 auto root = map.getHash().as_uint256();
356 auto path = map.getProofPath(k);
357 BEAST_EXPECT(path);
358 if (!path)
359 break;
360 BEAST_EXPECT(map.verifyProofPath(root, k, *path));
361 if (c == 1)
362 {
363 // extra node
364 path->insert(path->begin(), path->front());
365 BEAST_EXPECT(!map.verifyProofPath(root, k, *path));
366 // wrong key
367 uint256 wrongKey(c + 1);
368 BEAST_EXPECT(!map.getProofPath(wrongKey));
369 }
370 if (c == 99)
371 {
372 key = k;
373 rootHash = root;
374 goodPath = std::move(*path);
375 }
376 }
377
378 // still good
379 BEAST_EXPECT(map.verifyProofPath(rootHash, key, goodPath));
380 // empty path
381 std::vector<Blob> badPath;
382 BEAST_EXPECT(!map.verifyProofPath(rootHash, key, badPath));
383 // too long
384 badPath = goodPath;
385 badPath.push_back(goodPath.back());
386 BEAST_EXPECT(!map.verifyProofPath(rootHash, key, badPath));
387 // bad node
388 badPath.clear();
389 badPath.emplace_back(100, 100);
390 BEAST_EXPECT(!map.verifyProofPath(rootHash, key, badPath));
391 // bad node type
392 badPath.clear();
393 badPath.push_back(goodPath.front());
394 badPath.front().back()--; // change node type
395 BEAST_EXPECT(!map.verifyProofPath(rootHash, key, badPath));
396 // all inner
397 badPath.clear();
398 badPath = goodPath;
399 badPath.erase(badPath.begin());
400 BEAST_EXPECT(!map.verifyProofPath(rootHash, key, badPath));
401 }
402};
403
404BEAST_DEFINE_TESTSUITE(SHAMap, shamap, ripple);
405BEAST_DEFINE_TESTSUITE(SHAMapPathProof, shamap, ripple);
406} // namespace tests
407} // namespace ripple
T back(T... args)
T begin(T... args)
A generic endpoint for log messages.
Definition Journal.h:60
A testsuite class.
Definition suite.h:55
bool unexpected(Condition shouldBeFalse, String const &reason)
Definition suite.h:499
testcase_t testcase
Memberspace for declaring test cases.
Definition suite.h:155
Like std::vector<char> but better.
Definition Buffer.h:36
std::size_t size() const noexcept
Returns the number of bytes in the buffer.
Definition Buffer.h:127
std::uint8_t const * data() const noexcept
Return a pointer to beginning of the storage.
Definition Buffer.h:151
uint256 const & as_uint256() const
Definition SHAMapHash.h:44
uint256 const & key() const
Definition SHAMapItem.h:88
A SHAMap is both a radix tree with a fan-out of 16 and a Merkle tree.
Definition SHAMap.h:97
bool compare(SHAMap const &otherMap, Delta &differences, int maxCount) const
void dump(bool withHashes=false) const
Definition SHAMap.cpp:1178
void setUnbacked()
Definition SHAMap.h:635
const_iterator end() const
Definition SHAMap.h:761
void invariants() const
Definition SHAMap.cpp:1248
bool addItem(SHAMapNodeType type, boost::intrusive_ptr< SHAMapItem const > item)
Definition SHAMap.cpp:882
SHAMapHash getHash() const
Definition SHAMap.cpp:890
const_iterator begin() const
Definition SHAMap.h:755
bool delItem(uint256 const &id)
Definition SHAMap.cpp:721
std::shared_ptr< SHAMap > snapShot(bool isMutable) const
Definition SHAMap.cpp:89
An immutable linear range of bytes.
Definition Slice.h:46
static constexpr std::size_t size()
Definition base_uint.h:526
void run() override
Runs the suite.
void run() override
Runs the suite.
void run(bool backed, beast::Journal const &journal)
static Buffer IntToVUC(int v)
T clear(T... args)
T emplace_back(T... args)
T empty(T... args)
T erase(T... args)
T fill_n(T... args)
T front(T... args)
A namespace for easy access to logging severity values.
Definition Journal.h:30
bool operator!=(SHAMapItem const &a, SHAMapItem const &b)
bool operator==(SHAMapItem const &a, SHAMapItem const &b)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:25
base_uint< 256 > uint256
Definition base_uint.h:558
boost::intrusive_ptr< SHAMapItem > make_shamapitem(uint256 const &tag, Slice data)
Definition SHAMapItem.h:161
Number root(Number f, unsigned d)
Definition Number.cpp:636
T push_back(T... args)
T size(T... args)