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#include <xrpld/shamap/SHAMap.h>
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
28namespace ripple {
29namespace 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
44
47static_assert(!std::is_copy_constructible<SHAMapItem>{}, "");
48
52static_assert(std::is_copy_assignable<SHAMapNodeID>{}, "");
54static_assert(std::is_move_assignable<SHAMapNodeID>{}, "");
55
59static_assert(std::is_copy_assignable<SHAMapHash>{}, "");
61static_assert(std::is_move_assignable<SHAMapHash>{}, "");
62
66static_assert(!std::is_copy_assignable<SHAMapTreeNode>{}, "");
68static_assert(!std::is_move_assignable<SHAMapTreeNode>{}, "");
69
76
80static_assert(!std::is_copy_assignable<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}
90inline bool
91operator!=(SHAMapItem const& a, SHAMapItem const& b)
92{
93 return a.key() != b.key();
94}
95inline bool
96operator==(SHAMapItem const& a, uint256 const& b)
97{
98 return a.key() == b;
99}
100inline bool
101operator!=(SHAMapItem const& a, uint256 const& b)
102{
103 return a.key() != b;
104}
105
107{
108public:
109 static Buffer
110 IntToVUC(int v)
111 {
112 Buffer vuc(32);
113 std::fill_n(vuc.data(), vuc.size(), static_cast<std::uint8_t>(v));
114 return vuc;
115 }
116
117 void
118 run() override
119 {
120 using namespace beast::severities;
121 test::SuiteJournal journal("SHAMap_test", *this);
122
123 run(true, journal);
124 run(false, journal);
125 }
126
127 void
128 run(bool backed, beast::Journal const& journal)
129 {
130 if (backed)
131 testcase("add/traverse backed");
132 else
133 testcase("add/traverse unbacked");
134
135 tests::TestNodeFamily f(journal);
136
137 // h3 and h4 differ only in the leaf, same terminal node (level 19)
138 constexpr uint256 h1(
139 "092891fe4ef6cee585fdc6fda0e09eb4d386363158ec3321b8123e5a772c6ca7");
140 constexpr uint256 h2(
141 "436ccbac3347baa1f1e53baeef1f43334da88f1f6d70d963b833afd6dfa289fe");
142 constexpr uint256 h3(
143 "b92891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e5a772c6ca8");
144 constexpr uint256 h4(
145 "b92891fe4ef6cee585fdc6fda2e09eb4d386363158ec3321b8123e5a772c6ca8");
146 constexpr uint256 h5(
147 "a92891fe4ef6cee585fdc6fda0e09eb4d386363158ec3321b8123e5a772c6ca7");
148
149 SHAMap sMap(SHAMapType::FREE, f);
150 sMap.invariants();
151 if (!backed)
152 sMap.setUnbacked();
153
154 auto i1 = make_shamapitem(h1, IntToVUC(1));
155 auto i2 = make_shamapitem(h2, IntToVUC(2));
156 auto i3 = make_shamapitem(h3, IntToVUC(3));
157 auto i4 = make_shamapitem(h4, IntToVUC(4));
158 auto i5 = make_shamapitem(h5, IntToVUC(5));
159
161 !sMap.addItem(
163 "no add");
164 sMap.invariants();
166 !sMap.addItem(
168 "no add");
169 sMap.invariants();
170
171 auto i = sMap.begin();
172 auto e = sMap.end();
173 unexpected(i == e || (*i != *i1), "bad traverse");
174 ++i;
175 unexpected(i == e || (*i != *i2), "bad traverse");
176 ++i;
177 unexpected(i != e, "bad traverse");
179 sMap.invariants();
180 sMap.delItem(i2->key());
181 sMap.invariants();
183 sMap.invariants();
184 i = sMap.begin();
185 e = sMap.end();
186 unexpected(i == e || (*i != *i1), "bad traverse");
187 ++i;
188 unexpected(i == e || (*i != *i3), "bad traverse");
189 ++i;
190 unexpected(i == e || (*i != *i4), "bad traverse");
191 ++i;
192 unexpected(i != e, "bad traverse");
193
194 if (backed)
195 testcase("snapshot backed");
196 else
197 testcase("snapshot unbacked");
198
199 SHAMapHash mapHash = sMap.getHash();
200 std::shared_ptr<SHAMap> map2 = sMap.snapShot(false);
201 map2->invariants();
202 unexpected(sMap.getHash() != mapHash, "bad snapshot");
203 unexpected(map2->getHash() != mapHash, "bad snapshot");
204
205 SHAMap::Delta delta;
206 BEAST_EXPECT(sMap.compare(*map2, delta, 100));
207 BEAST_EXPECT(delta.empty());
208
209 unexpected(!sMap.delItem(sMap.begin()->key()), "bad mod");
210 sMap.invariants();
211 unexpected(sMap.getHash() == mapHash, "bad snapshot");
212 unexpected(map2->getHash() != mapHash, "bad snapshot");
213
214 BEAST_EXPECT(sMap.compare(*map2, delta, 100));
215 BEAST_EXPECT(delta.size() == 1);
216 BEAST_EXPECT(delta.begin()->first == h1);
217 BEAST_EXPECT(delta.begin()->second.first == nullptr);
218 BEAST_EXPECT(delta.begin()->second.second->key() == h1);
219
220 sMap.dump();
221
222 if (backed)
223 testcase("build/tear backed");
224 else
225 testcase("build/tear unbacked");
226 {
227 constexpr std::array keys{
228 uint256("b92891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
229 "5a772c6ca8"),
230 uint256("b92881fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
231 "5a772c6ca8"),
232 uint256("b92691fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
233 "5a772c6ca8"),
234 uint256("b92791fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
235 "5a772c6ca8"),
236 uint256("b91891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
237 "5a772c6ca8"),
238 uint256("b99891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
239 "5a772c6ca8"),
240 uint256("f22891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
241 "5a772c6ca8"),
242 uint256("292891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
243 "5a772c6ca8")};
244
245 constexpr std::array hashes{
246 uint256("B7387CFEA0465759ADC718E8C42B52D2309D179B326E239EB5075C"
247 "64B6281F7F"),
248 uint256("FBC195A9592A54AB44010274163CB6BA95F497EC5BA0A883184546"
249 "7FB2ECE266"),
250 uint256("4E7D2684B65DFD48937FFB775E20175C43AF0C94066F7D5679F51A"
251 "E756795B75"),
252 uint256("7A2F312EB203695FFD164E038E281839EEF06A1B99BFC263F3CECC"
253 "6C74F93E07"),
254 uint256("395A6691A372387A703FB0F2C6D2C405DAF307D0817F8F0E207596"
255 "462B0E3A3E"),
256 uint256("D044C0A696DE3169CC70AE216A1564D69DE96582865796142CE7D9"
257 "8A84D9DDE4"),
258 uint256("76DCC77C4027309B5A91AD164083264D70B77B5E43E08AEDA5EBF9"
259 "4361143615"),
260 uint256("DF4220E93ADC6F5569063A01B4DC79F8DB9553B6A3222ADE23DEA0"
261 "2BBE7230E5")};
262
263 SHAMap map(SHAMapType::FREE, f);
264 if (!backed)
265 map.setUnbacked();
266
267 BEAST_EXPECT(map.getHash() == beast::zero);
268 for (int k = 0; k < keys.size(); ++k)
269 {
270 BEAST_EXPECT(map.addItem(
272 make_shamapitem(keys[k], IntToVUC(k))));
273 BEAST_EXPECT(map.getHash().as_uint256() == hashes[k]);
274 map.invariants();
275 }
276 for (int k = keys.size() - 1; k >= 0; --k)
277 {
278 BEAST_EXPECT(map.getHash().as_uint256() == hashes[k]);
279 BEAST_EXPECT(map.delItem(keys[k]));
280 map.invariants();
281 }
282 BEAST_EXPECT(map.getHash() == beast::zero);
283 }
284
285 if (backed)
286 testcase("iterate backed");
287 else
288 testcase("iterate unbacked");
289
290 {
291 constexpr std::array keys{
292 uint256("f22891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
293 "5a772c6ca8"),
294 uint256("b99891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
295 "5a772c6ca8"),
296 uint256("b92891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
297 "5a772c6ca8"),
298 uint256("b92881fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
299 "5a772c6ca8"),
300 uint256("b92791fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
301 "5a772c6ca8"),
302 uint256("b92691fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
303 "5a772c6ca8"),
304 uint256("b91891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
305 "5a772c6ca8"),
306 uint256("292891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
307 "5a772c6ca8")};
308
309 tests::TestNodeFamily tf{journal};
310 SHAMap map{SHAMapType::FREE, tf};
311 if (!backed)
312 map.setUnbacked();
313 for (auto const& k : keys)
314 {
315 map.addItem(
318 map.invariants();
319 }
320
321 int h = 7;
322 for (auto const& k : map)
323 {
324 BEAST_EXPECT(k.key() == keys[h]);
325 --h;
326 }
327 }
328 }
329};
330
332{
333 void
334 run() override
335 {
336 test::SuiteJournal journal("SHAMapPathProof_test", *this);
337
338 tests::TestNodeFamily tf{journal};
339 SHAMap map{SHAMapType::FREE, tf};
340 map.setUnbacked();
341
342 uint256 key;
343 uint256 rootHash;
344 std::vector<Blob> goodPath;
345
346 for (unsigned char c = 1; c < 100; ++c)
347 {
348 uint256 k(c);
349 map.addItem(
351 make_shamapitem(k, Slice{k.data(), k.size()}));
352 map.invariants();
353
354 auto root = map.getHash().as_uint256();
355 auto path = map.getProofPath(k);
356 BEAST_EXPECT(path);
357 if (!path)
358 break;
359 BEAST_EXPECT(map.verifyProofPath(root, k, *path));
360 if (c == 1)
361 {
362 // extra node
363 path->insert(path->begin(), path->front());
364 BEAST_EXPECT(!map.verifyProofPath(root, k, *path));
365 // wrong key
366 uint256 wrongKey(c + 1);
367 BEAST_EXPECT(!map.getProofPath(wrongKey));
368 }
369 if (c == 99)
370 {
371 key = k;
372 rootHash = root;
373 goodPath = std::move(*path);
374 }
375 }
376
377 // still good
378 BEAST_EXPECT(map.verifyProofPath(rootHash, key, goodPath));
379 // empty path
380 std::vector<Blob> badPath;
381 BEAST_EXPECT(!map.verifyProofPath(rootHash, key, badPath));
382 // too long
383 badPath = goodPath;
384 badPath.push_back(goodPath.back());
385 BEAST_EXPECT(!map.verifyProofPath(rootHash, key, badPath));
386 // bad node
387 badPath.clear();
388 badPath.emplace_back(100, 100);
389 BEAST_EXPECT(!map.verifyProofPath(rootHash, key, badPath));
390 // bad node type
391 badPath.clear();
392 badPath.push_back(goodPath.front());
393 badPath.front().back()--; // change node type
394 BEAST_EXPECT(!map.verifyProofPath(rootHash, key, badPath));
395 // all inner
396 badPath.clear();
397 badPath = goodPath;
398 badPath.erase(badPath.begin());
399 BEAST_EXPECT(!map.verifyProofPath(rootHash, key, badPath));
400 }
401};
402
403BEAST_DEFINE_TESTSUITE(SHAMap, ripple_app, ripple);
404BEAST_DEFINE_TESTSUITE(SHAMapPathProof, ripple_app, ripple);
405} // namespace tests
406} // namespace ripple
T back(T... args)
T begin(T... args)
A generic endpoint for log messages.
Definition: Journal.h:59
A testsuite class.
Definition: suite.h:53
bool unexpected(Condition shouldBeFalse, String const &reason)
Definition: suite.h:497
testcase_t testcase
Memberspace for declaring test cases.
Definition: suite.h:153
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:87
A SHAMap is both a radix tree with a fan-out of 16 and a Merkle tree.
Definition: SHAMap.h:96
bool compare(SHAMap const &otherMap, Delta &differences, int maxCount) const
void dump(bool withHashes=false) const
Definition: SHAMap.cpp:1172
void setUnbacked()
Definition: SHAMap.h:633
const_iterator end() const
Definition: SHAMap.h:759
void invariants() const
Definition: SHAMap.cpp:1242
bool addItem(SHAMapNodeType type, boost::intrusive_ptr< SHAMapItem const > item)
Definition: SHAMap.cpp:880
SHAMapHash getHash() const
Definition: SHAMap.cpp:888
const_iterator begin() const
Definition: SHAMap.h:753
bool delItem(uint256 const &id)
Definition: SHAMap.cpp:721
std::shared_ptr< SHAMap > snapShot(bool isMutable) const
Definition: SHAMap.cpp:88
An immutable linear range of bytes.
Definition: Slice.h:45
pointer data()
Definition: base_uint.h:124
static constexpr std::size_t size()
Definition: base_uint.h:525
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:29
bool operator!=(SHAMapItem const &a, SHAMapItem const &b)
Definition: SHAMap_test.cpp:91
bool operator==(SHAMapItem const &a, SHAMapItem const &b)
Definition: SHAMap_test.cpp:86
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: algorithm.h:26
base_uint< 256 > uint256
Definition: base_uint.h:557
boost::intrusive_ptr< SHAMapItem > make_shamapitem(uint256 const &tag, Slice data)
Definition: SHAMapItem.h:160
Number root(Number f, unsigned d)
Definition: Number.cpp:630
T push_back(T... args)
T size(T... args)