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 <xrpld/shamap/SHAMap.h>
24
25#include <xrpl/basics/Blob.h>
26#include <xrpl/basics/Buffer.h>
27#include <xrpl/beast/unit_test.h>
28#include <xrpl/beast/utility/Journal.h>
29
30namespace ripple {
31namespace tests {
32
33#ifndef __INTELLISENSE__
34static_assert(std::is_nothrow_destructible<SHAMap>{}, "");
35static_assert(!std::is_default_constructible<SHAMap>{}, "");
36static_assert(!std::is_copy_constructible<SHAMap>{}, "");
37static_assert(!std::is_copy_assignable<SHAMap>{}, "");
38static_assert(!std::is_move_constructible<SHAMap>{}, "");
39static_assert(!std::is_move_assignable<SHAMap>{}, "");
40
46
49static_assert(!std::is_copy_constructible<SHAMapItem>{}, "");
50
54static_assert(std::is_copy_assignable<SHAMapNodeID>{}, "");
56static_assert(std::is_move_assignable<SHAMapNodeID>{}, "");
57
61static_assert(std::is_copy_assignable<SHAMapHash>{}, "");
63static_assert(std::is_move_assignable<SHAMapHash>{}, "");
64
68static_assert(!std::is_copy_assignable<SHAMapTreeNode>{}, "");
70static_assert(!std::is_move_assignable<SHAMapTreeNode>{}, "");
71
78
82static_assert(!std::is_copy_assignable<SHAMapLeafNode>{}, "");
84static_assert(!std::is_move_assignable<SHAMapLeafNode>{}, "");
85#endif
86
87inline bool
88operator==(SHAMapItem const& a, SHAMapItem const& b)
89{
90 return a.key() == b.key();
91}
92inline bool
93operator!=(SHAMapItem const& a, SHAMapItem const& b)
94{
95 return a.key() != b.key();
96}
97inline bool
98operator==(SHAMapItem const& a, uint256 const& b)
99{
100 return a.key() == b;
101}
102inline bool
103operator!=(SHAMapItem const& a, uint256 const& b)
104{
105 return a.key() != b;
106}
107
109{
110public:
111 static Buffer
112 IntToVUC(int v)
113 {
114 Buffer vuc(32);
115 std::fill_n(vuc.data(), vuc.size(), static_cast<std::uint8_t>(v));
116 return vuc;
117 }
118
119 void
120 run() override
121 {
122 using namespace beast::severities;
123 test::SuiteJournal journal("SHAMap_test", *this);
124
125 run(true, journal);
126 run(false, journal);
127 }
128
129 void
130 run(bool backed, beast::Journal const& journal)
131 {
132 if (backed)
133 testcase("add/traverse backed");
134 else
135 testcase("add/traverse unbacked");
136
137 tests::TestNodeFamily f(journal);
138
139 // h3 and h4 differ only in the leaf, same terminal node (level 19)
140 constexpr uint256 h1(
141 "092891fe4ef6cee585fdc6fda0e09eb4d386363158ec3321b8123e5a772c6ca7");
142 constexpr uint256 h2(
143 "436ccbac3347baa1f1e53baeef1f43334da88f1f6d70d963b833afd6dfa289fe");
144 constexpr uint256 h3(
145 "b92891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e5a772c6ca8");
146 constexpr uint256 h4(
147 "b92891fe4ef6cee585fdc6fda2e09eb4d386363158ec3321b8123e5a772c6ca8");
148 constexpr uint256 h5(
149 "a92891fe4ef6cee585fdc6fda0e09eb4d386363158ec3321b8123e5a772c6ca7");
150
151 SHAMap sMap(SHAMapType::FREE, f);
152 sMap.invariants();
153 if (!backed)
154 sMap.setUnbacked();
155
156 auto i1 = make_shamapitem(h1, IntToVUC(1));
157 auto i2 = make_shamapitem(h2, IntToVUC(2));
158 auto i3 = make_shamapitem(h3, IntToVUC(3));
159 auto i4 = make_shamapitem(h4, IntToVUC(4));
160 auto i5 = make_shamapitem(h5, IntToVUC(5));
161
163 !sMap.addItem(
165 "no add");
166 sMap.invariants();
168 !sMap.addItem(
170 "no add");
171 sMap.invariants();
172
173 auto i = sMap.begin();
174 auto e = sMap.end();
175 unexpected(i == e || (*i != *i1), "bad traverse");
176 ++i;
177 unexpected(i == e || (*i != *i2), "bad traverse");
178 ++i;
179 unexpected(i != e, "bad traverse");
181 sMap.invariants();
182 sMap.delItem(i2->key());
183 sMap.invariants();
185 sMap.invariants();
186 i = sMap.begin();
187 e = sMap.end();
188 unexpected(i == e || (*i != *i1), "bad traverse");
189 ++i;
190 unexpected(i == e || (*i != *i3), "bad traverse");
191 ++i;
192 unexpected(i == e || (*i != *i4), "bad traverse");
193 ++i;
194 unexpected(i != e, "bad traverse");
195
196 if (backed)
197 testcase("snapshot backed");
198 else
199 testcase("snapshot unbacked");
200
201 SHAMapHash mapHash = sMap.getHash();
202 std::shared_ptr<SHAMap> map2 = sMap.snapShot(false);
203 map2->invariants();
204 unexpected(sMap.getHash() != mapHash, "bad snapshot");
205 unexpected(map2->getHash() != mapHash, "bad snapshot");
206
207 SHAMap::Delta delta;
208 BEAST_EXPECT(sMap.compare(*map2, delta, 100));
209 BEAST_EXPECT(delta.empty());
210
211 unexpected(!sMap.delItem(sMap.begin()->key()), "bad mod");
212 sMap.invariants();
213 unexpected(sMap.getHash() == mapHash, "bad snapshot");
214 unexpected(map2->getHash() != mapHash, "bad snapshot");
215
216 BEAST_EXPECT(sMap.compare(*map2, delta, 100));
217 BEAST_EXPECT(delta.size() == 1);
218 BEAST_EXPECT(delta.begin()->first == h1);
219 BEAST_EXPECT(delta.begin()->second.first == nullptr);
220 BEAST_EXPECT(delta.begin()->second.second->key() == h1);
221
222 sMap.dump();
223
224 if (backed)
225 testcase("build/tear backed");
226 else
227 testcase("build/tear unbacked");
228 {
229 constexpr std::array keys{
230 uint256("b92891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
231 "5a772c6ca8"),
232 uint256("b92881fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
233 "5a772c6ca8"),
234 uint256("b92691fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
235 "5a772c6ca8"),
236 uint256("b92791fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
237 "5a772c6ca8"),
238 uint256("b91891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
239 "5a772c6ca8"),
240 uint256("b99891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
241 "5a772c6ca8"),
242 uint256("f22891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
243 "5a772c6ca8"),
244 uint256("292891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
245 "5a772c6ca8")};
246
247 constexpr std::array hashes{
248 uint256("B7387CFEA0465759ADC718E8C42B52D2309D179B326E239EB5075C"
249 "64B6281F7F"),
250 uint256("FBC195A9592A54AB44010274163CB6BA95F497EC5BA0A883184546"
251 "7FB2ECE266"),
252 uint256("4E7D2684B65DFD48937FFB775E20175C43AF0C94066F7D5679F51A"
253 "E756795B75"),
254 uint256("7A2F312EB203695FFD164E038E281839EEF06A1B99BFC263F3CECC"
255 "6C74F93E07"),
256 uint256("395A6691A372387A703FB0F2C6D2C405DAF307D0817F8F0E207596"
257 "462B0E3A3E"),
258 uint256("D044C0A696DE3169CC70AE216A1564D69DE96582865796142CE7D9"
259 "8A84D9DDE4"),
260 uint256("76DCC77C4027309B5A91AD164083264D70B77B5E43E08AEDA5EBF9"
261 "4361143615"),
262 uint256("DF4220E93ADC6F5569063A01B4DC79F8DB9553B6A3222ADE23DEA0"
263 "2BBE7230E5")};
264
265 SHAMap map(SHAMapType::FREE, f);
266 if (!backed)
267 map.setUnbacked();
268
269 BEAST_EXPECT(map.getHash() == beast::zero);
270 for (int k = 0; k < keys.size(); ++k)
271 {
272 BEAST_EXPECT(map.addItem(
274 make_shamapitem(keys[k], IntToVUC(k))));
275 BEAST_EXPECT(map.getHash().as_uint256() == hashes[k]);
276 map.invariants();
277 }
278 for (int k = keys.size() - 1; k >= 0; --k)
279 {
280 BEAST_EXPECT(map.getHash().as_uint256() == hashes[k]);
281 BEAST_EXPECT(map.delItem(keys[k]));
282 map.invariants();
283 }
284 BEAST_EXPECT(map.getHash() == beast::zero);
285 }
286
287 if (backed)
288 testcase("iterate backed");
289 else
290 testcase("iterate unbacked");
291
292 {
293 constexpr std::array keys{
294 uint256("f22891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
295 "5a772c6ca8"),
296 uint256("b99891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
297 "5a772c6ca8"),
298 uint256("b92891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
299 "5a772c6ca8"),
300 uint256("b92881fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
301 "5a772c6ca8"),
302 uint256("b92791fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
303 "5a772c6ca8"),
304 uint256("b92691fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
305 "5a772c6ca8"),
306 uint256("b91891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
307 "5a772c6ca8"),
308 uint256("292891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
309 "5a772c6ca8")};
310
311 tests::TestNodeFamily tf{journal};
312 SHAMap map{SHAMapType::FREE, tf};
313 if (!backed)
314 map.setUnbacked();
315 for (auto const& k : keys)
316 {
317 map.addItem(
320 map.invariants();
321 }
322
323 int h = 7;
324 for (auto const& k : map)
325 {
326 BEAST_EXPECT(k.key() == keys[h]);
327 --h;
328 }
329 }
330 }
331};
332
334{
335 void
336 run() override
337 {
338 test::SuiteJournal journal("SHAMapPathProof_test", *this);
339
340 tests::TestNodeFamily tf{journal};
341 SHAMap map{SHAMapType::FREE, tf};
342 map.setUnbacked();
343
344 uint256 key;
345 uint256 rootHash;
346 std::vector<Blob> goodPath;
347
348 for (unsigned char c = 1; c < 100; ++c)
349 {
350 uint256 k(c);
351 map.addItem(
353 make_shamapitem(k, Slice{k.data(), k.size()}));
354 map.invariants();
355
356 auto root = map.getHash().as_uint256();
357 auto path = map.getProofPath(k);
358 BEAST_EXPECT(path);
359 if (!path)
360 break;
361 BEAST_EXPECT(map.verifyProofPath(root, k, *path));
362 if (c == 1)
363 {
364 // extra node
365 path->insert(path->begin(), path->front());
366 BEAST_EXPECT(!map.verifyProofPath(root, k, *path));
367 // wrong key
368 uint256 wrongKey(c + 1);
369 BEAST_EXPECT(!map.getProofPath(wrongKey));
370 }
371 if (c == 99)
372 {
373 key = k;
374 rootHash = root;
375 goodPath = std::move(*path);
376 }
377 }
378
379 // still good
380 BEAST_EXPECT(map.verifyProofPath(rootHash, key, goodPath));
381 // empty path
382 std::vector<Blob> badPath;
383 BEAST_EXPECT(!map.verifyProofPath(rootHash, key, badPath));
384 // too long
385 badPath = goodPath;
386 badPath.push_back(goodPath.back());
387 BEAST_EXPECT(!map.verifyProofPath(rootHash, key, badPath));
388 // bad node
389 badPath.clear();
390 badPath.emplace_back(100, 100);
391 BEAST_EXPECT(!map.verifyProofPath(rootHash, key, badPath));
392 // bad node type
393 badPath.clear();
394 badPath.push_back(goodPath.front());
395 badPath.front().back()--; // change node type
396 BEAST_EXPECT(!map.verifyProofPath(rootHash, key, badPath));
397 // all inner
398 badPath.clear();
399 badPath = goodPath;
400 badPath.erase(badPath.begin());
401 BEAST_EXPECT(!map.verifyProofPath(rootHash, key, badPath));
402 }
403};
404
405BEAST_DEFINE_TESTSUITE(SHAMap, shamap, ripple);
406BEAST_DEFINE_TESTSUITE(SHAMapPathProof, shamap, ripple);
407} // namespace tests
408} // 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:99
bool compare(SHAMap const &otherMap, Delta &differences, int maxCount) const
void dump(bool withHashes=false) const
Definition SHAMap.cpp:1175
void setUnbacked()
Definition SHAMap.h:637
const_iterator end() const
Definition SHAMap.h:763
void invariants() const
Definition SHAMap.cpp:1245
bool addItem(SHAMapNodeType type, boost::intrusive_ptr< SHAMapItem const > item)
Definition SHAMap.cpp:881
SHAMapHash getHash() const
Definition SHAMap.cpp:889
const_iterator begin() const
Definition SHAMap.h:757
bool delItem(uint256 const &id)
Definition SHAMap.cpp:720
std::shared_ptr< SHAMap > snapShot(bool isMutable) const
Definition SHAMap.cpp:90
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)