rippled
Loading...
Searching...
No Matches
SHAMap_test.cpp
1#include <test/shamap/common.h>
2#include <test/unit_test/SuiteJournal.h>
3
4#include <xrpl/basics/Blob.h>
5#include <xrpl/basics/Buffer.h>
6#include <xrpl/beast/unit_test.h>
7#include <xrpl/beast/utility/Journal.h>
8#include <xrpl/shamap/SHAMap.h>
9
10namespace xrpl {
11namespace tests {
12
13#ifndef __INTELLISENSE__
14static_assert(std::is_nothrow_destructible<SHAMap>{}, "");
15static_assert(!std::is_default_constructible<SHAMap>{}, "");
16static_assert(!std::is_copy_constructible<SHAMap>{}, "");
17static_assert(!std::is_copy_assignable<SHAMap>{}, "");
18static_assert(!std::is_move_constructible<SHAMap>{}, "");
19static_assert(!std::is_move_assignable<SHAMap>{}, "");
20
26
29static_assert(!std::is_copy_constructible<SHAMapItem>{}, "");
30
34static_assert(std::is_copy_assignable<SHAMapNodeID>{}, "");
36static_assert(std::is_move_assignable<SHAMapNodeID>{}, "");
37
41static_assert(std::is_copy_assignable<SHAMapHash>{}, "");
43static_assert(std::is_move_assignable<SHAMapHash>{}, "");
44
48static_assert(!std::is_copy_assignable<SHAMapTreeNode>{}, "");
50static_assert(!std::is_move_assignable<SHAMapTreeNode>{}, "");
51
58
62static_assert(!std::is_copy_assignable<SHAMapLeafNode>{}, "");
64static_assert(!std::is_move_assignable<SHAMapLeafNode>{}, "");
65#endif
66
67inline bool
68operator==(SHAMapItem const& a, SHAMapItem const& b)
69{
70 return a.key() == b.key();
71}
72inline bool
73operator!=(SHAMapItem const& a, SHAMapItem const& b)
74{
75 return a.key() != b.key();
76}
77inline bool
78operator==(SHAMapItem const& a, uint256 const& b)
79{
80 return a.key() == b;
81}
82inline bool
83operator!=(SHAMapItem const& a, uint256 const& b)
84{
85 return a.key() != b;
86}
87
89{
90public:
91 static Buffer
92 IntToVUC(int v)
93 {
94 Buffer vuc(32);
95 std::fill_n(vuc.data(), vuc.size(), static_cast<std::uint8_t>(v));
96 return vuc;
97 }
98
99 void
100 run() override
101 {
102 using namespace beast::severities;
103 test::SuiteJournal journal("SHAMap_test", *this);
104
105 run(true, journal);
106 run(false, journal);
107 }
108
109 void
110 run(bool backed, beast::Journal const& journal)
111 {
112 if (backed)
113 {
114 testcase("add/traverse backed");
115 }
116 else
117 {
118 testcase("add/traverse unbacked");
119 }
120
121 tests::TestNodeFamily f(journal);
122
123 // h3 and h4 differ only in the leaf, same terminal node (level 19)
124 constexpr uint256 h1("092891fe4ef6cee585fdc6fda0e09eb4d386363158ec3321b8123e5a772c6ca7");
125 constexpr uint256 h2("436ccbac3347baa1f1e53baeef1f43334da88f1f6d70d963b833afd6dfa289fe");
126 constexpr uint256 h3("b92891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e5a772c6ca8");
127 constexpr uint256 h4("b92891fe4ef6cee585fdc6fda2e09eb4d386363158ec3321b8123e5a772c6ca8");
128 constexpr uint256 h5("a92891fe4ef6cee585fdc6fda0e09eb4d386363158ec3321b8123e5a772c6ca7");
129
130 SHAMap sMap(SHAMapType::FREE, f);
131 sMap.invariants();
132 if (!backed)
133 sMap.setUnbacked();
134
135 auto i1 = make_shamapitem(h1, IntToVUC(1));
136 auto i2 = make_shamapitem(h2, IntToVUC(2));
137 auto i3 = make_shamapitem(h3, IntToVUC(3));
138 auto i4 = make_shamapitem(h4, IntToVUC(4));
139 auto i5 = make_shamapitem(h5, IntToVUC(5));
140
142 sMap.invariants();
144 sMap.invariants();
145
146 auto i = sMap.begin();
147 auto e = sMap.end();
148 unexpected(i == e || (*i != *i1), "bad traverse");
149 ++i;
150 unexpected(i == e || (*i != *i2), "bad traverse");
151 ++i;
152 unexpected(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 unexpected(i == e || (*i != *i1), "bad traverse");
162 ++i;
163 unexpected(i == e || (*i != *i3), "bad traverse");
164 ++i;
165 unexpected(i == e || (*i != *i4), "bad traverse");
166 ++i;
167 unexpected(i != e, "bad traverse");
168
169 if (backed)
170 {
171 testcase("snapshot backed");
172 }
173 else
174 {
175 testcase("snapshot unbacked");
176 }
177
178 SHAMapHash const mapHash = sMap.getHash();
179 std::shared_ptr<SHAMap> const map2 = sMap.snapShot(false);
180 map2->invariants();
181 unexpected(sMap.getHash() != mapHash, "bad snapshot");
182 unexpected(map2->getHash() != mapHash, "bad snapshot");
183
184 SHAMap::Delta delta;
185 BEAST_EXPECT(sMap.compare(*map2, delta, 100));
186 BEAST_EXPECT(delta.empty());
187
188 unexpected(!sMap.delItem(sMap.begin()->key()), "bad mod");
189 sMap.invariants();
190 unexpected(sMap.getHash() == mapHash, "bad snapshot");
191 unexpected(map2->getHash() != mapHash, "bad snapshot");
192
193 BEAST_EXPECT(sMap.compare(*map2, delta, 100));
194 BEAST_EXPECT(delta.size() == 1);
195 BEAST_EXPECT(delta.begin()->first == h1);
196 BEAST_EXPECT(delta.begin()->second.first == nullptr);
197 BEAST_EXPECT(delta.begin()->second.second->key() == h1);
198
199 sMap.dump();
200
201 if (backed)
202 {
203 testcase("build/tear backed");
204 }
205 else
206 {
207 testcase("build/tear unbacked");
208 }
209 {
210 constexpr std::array keys{
211 uint256(
212 "b92891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
213 "5a772c6ca8"),
214 uint256(
215 "b92881fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
216 "5a772c6ca8"),
217 uint256(
218 "b92691fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
219 "5a772c6ca8"),
220 uint256(
221 "b92791fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
222 "5a772c6ca8"),
223 uint256(
224 "b91891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
225 "5a772c6ca8"),
226 uint256(
227 "b99891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
228 "5a772c6ca8"),
229 uint256(
230 "f22891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
231 "5a772c6ca8"),
232 uint256(
233 "292891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
234 "5a772c6ca8")};
235
236 constexpr std::array hashes{
237 uint256(
238 "B7387CFEA0465759ADC718E8C42B52D2309D179B326E239EB5075C"
239 "64B6281F7F"),
240 uint256(
241 "FBC195A9592A54AB44010274163CB6BA95F497EC5BA0A883184546"
242 "7FB2ECE266"),
243 uint256(
244 "4E7D2684B65DFD48937FFB775E20175C43AF0C94066F7D5679F51A"
245 "E756795B75"),
246 uint256(
247 "7A2F312EB203695FFD164E038E281839EEF06A1B99BFC263F3CECC"
248 "6C74F93E07"),
249 uint256(
250 "395A6691A372387A703FB0F2C6D2C405DAF307D0817F8F0E207596"
251 "462B0E3A3E"),
252 uint256(
253 "D044C0A696DE3169CC70AE216A1564D69DE96582865796142CE7D9"
254 "8A84D9DDE4"),
255 uint256(
256 "76DCC77C4027309B5A91AD164083264D70B77B5E43E08AEDA5EBF9"
257 "4361143615"),
258 uint256(
259 "DF4220E93ADC6F5569063A01B4DC79F8DB9553B6A3222ADE23DEA0"
260 "2BBE7230E5")};
261
262 SHAMap map(SHAMapType::FREE, f);
263 if (!backed)
264 map.setUnbacked();
265
266 BEAST_EXPECT(map.getHash() == beast::zero);
267 for (int k = 0; k < keys.size(); ++k)
268 {
269 BEAST_EXPECT(map.addItem(
271 BEAST_EXPECT(map.getHash().as_uint256() == hashes[k]);
272 map.invariants();
273 }
274 for (int k = keys.size() - 1; k >= 0; --k)
275 {
276 BEAST_EXPECT(map.getHash().as_uint256() == hashes[k]);
277 BEAST_EXPECT(map.delItem(keys[k]));
278 map.invariants();
279 }
280 BEAST_EXPECT(map.getHash() == beast::zero);
281 }
282
283 if (backed)
284 {
285 testcase("iterate backed");
286 }
287 else
288 {
289 testcase("iterate unbacked");
290 }
291
292 {
293 constexpr std::array keys{
294 uint256(
295 "f22891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
296 "5a772c6ca8"),
297 uint256(
298 "b99891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
299 "5a772c6ca8"),
300 uint256(
301 "b92891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
302 "5a772c6ca8"),
303 uint256(
304 "b92881fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
305 "5a772c6ca8"),
306 uint256(
307 "b92791fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
308 "5a772c6ca8"),
309 uint256(
310 "b92691fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
311 "5a772c6ca8"),
312 uint256(
313 "b91891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
314 "5a772c6ca8"),
315 uint256(
316 "292891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
317 "5a772c6ca8")};
318
319 tests::TestNodeFamily tf{journal};
320 SHAMap map{SHAMapType::FREE, tf};
321 if (!backed)
322 map.setUnbacked();
323 for (auto const& k : keys)
324 {
326 map.invariants();
327 }
328
329 int h = 7;
330 for (auto const& k : map)
331 {
332 BEAST_EXPECT(k.key() == keys[h]);
333 --h;
334 }
335 }
336 }
337};
338
340{
341 void
342 run() override
343 {
344 test::SuiteJournal journal("SHAMapPathProof_test", *this);
345
346 tests::TestNodeFamily tf{journal};
347 SHAMap map{SHAMapType::FREE, tf};
348 map.setUnbacked();
349
350 uint256 key;
351 uint256 rootHash;
352 std::vector<Blob> goodPath;
353
354 for (unsigned char c = 1; c < 100; ++c)
355 {
356 uint256 k(c);
357 map.addItem(
359 map.invariants();
360
361 auto root = map.getHash().as_uint256();
362 auto path = map.getProofPath(k);
363 BEAST_EXPECT(path);
364 if (!path)
365 break;
366 BEAST_EXPECT(map.verifyProofPath(root, k, *path));
367 if (c == 1)
368 {
369 // extra node
370 path->insert(path->begin(), path->front());
371 BEAST_EXPECT(!map.verifyProofPath(root, k, *path));
372 // wrong key
373 uint256 const wrongKey(c + 1);
374 BEAST_EXPECT(!map.getProofPath(wrongKey));
375 }
376 if (c == 99)
377 {
378 key = k;
379 rootHash = root;
380 goodPath = std::move(*path);
381 }
382 }
383
384 // still good
385 BEAST_EXPECT(map.verifyProofPath(rootHash, key, goodPath));
386 // empty path
387 std::vector<Blob> badPath;
388 BEAST_EXPECT(!map.verifyProofPath(rootHash, key, badPath));
389 // too long
390 badPath = goodPath;
391 badPath.push_back(goodPath.back());
392 BEAST_EXPECT(!map.verifyProofPath(rootHash, key, badPath));
393 // bad node
394 badPath.clear();
395 badPath.emplace_back(100, 100);
396 BEAST_EXPECT(!map.verifyProofPath(rootHash, key, badPath));
397 // bad node type
398 badPath.clear();
399 badPath.push_back(goodPath.front());
400 badPath.front().back()--; // change node type
401 BEAST_EXPECT(!map.verifyProofPath(rootHash, key, badPath));
402 // all inner
403 badPath.clear();
404 badPath = goodPath;
405 badPath.erase(badPath.begin());
406 BEAST_EXPECT(!map.verifyProofPath(rootHash, key, badPath));
407 }
408};
409
410BEAST_DEFINE_TESTSUITE(SHAMap, shamap, xrpl);
411BEAST_DEFINE_TESTSUITE(SHAMapPathProof, shamap, xrpl);
412} // namespace tests
413} // namespace xrpl
T back(T... args)
T begin(T... args)
A generic endpoint for log messages.
Definition Journal.h:40
A testsuite class.
Definition suite.h:51
bool unexpected(Condition shouldBeFalse, String const &reason)
Definition suite.h:485
testcase_t testcase
Memberspace for declaring test cases.
Definition suite.h:150
Like std::vector<char> but better.
Definition Buffer.h:16
std::size_t size() const noexcept
Returns the number of bytes in the buffer.
Definition Buffer.h:104
std::uint8_t const * data() const noexcept
Return a pointer to beginning of the storage.
Definition Buffer.h:128
uint256 const & as_uint256() const
Definition SHAMapHash.h:24
uint256 const & key() const
Definition SHAMapItem.h:66
A SHAMap is both a radix tree with a fan-out of 16 and a Merkle tree.
Definition SHAMap.h:77
bool addItem(SHAMapNodeType type, boost::intrusive_ptr< SHAMapItem const > item)
Definition SHAMap.cpp:807
const_iterator end() const
Definition SHAMap.h:698
const_iterator begin() const
Definition SHAMap.h:692
void setUnbacked()
Definition SHAMap.h:575
void dump(bool withHashes=false) const
Definition SHAMap.cpp:1082
bool compare(SHAMap const &otherMap, Delta &differences, int maxCount) const
std::shared_ptr< SHAMap > snapShot(bool isMutable) const
Definition SHAMap.cpp:64
void invariants() const
Definition SHAMap.cpp:1147
SHAMapHash getHash() const
Definition SHAMap.cpp:813
bool delItem(uint256 const &id)
Definition SHAMap.cpp:653
An immutable linear range of bytes.
Definition Slice.h:26
pointer data()
Definition base_uint.h:101
static constexpr std::size_t size()
Definition base_uint.h:499
void run() override
Runs the suite.
static Buffer IntToVUC(int v)
void run() override
Runs the suite.
void run(bool backed, beast::Journal const &journal)
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:10
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:5
boost::intrusive_ptr< SHAMapItem > make_shamapitem(uint256 const &tag, Slice data)
Definition SHAMapItem.h:139
Number root(Number f, unsigned d)
Definition Number.cpp:958
base_uint< 256 > uint256
Definition base_uint.h:531
T push_back(T... args)
T size(T... args)