rippled
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 <ripple/basics/Blob.h>
21 #include <ripple/basics/Buffer.h>
22 #include <ripple/beast/unit_test.h>
23 #include <ripple/beast/utility/Journal.h>
24 #include <ripple/shamap/SHAMap.h>
25 #include <algorithm>
26 #include <test/shamap/common.h>
27 #include <test/unit_test/SuiteJournal.h>
28 
29 namespace ripple {
30 namespace tests {
31 
32 #ifndef __INTELLISENSE__
33 static_assert(std::is_nothrow_destructible<SHAMap>{}, "");
34 static_assert(!std::is_default_constructible<SHAMap>{}, "");
35 static_assert(!std::is_copy_constructible<SHAMap>{}, "");
36 static_assert(!std::is_copy_assignable<SHAMap>{}, "");
37 static_assert(!std::is_move_constructible<SHAMap>{}, "");
38 static_assert(!std::is_move_assignable<SHAMap>{}, "");
39 
45 
46 static_assert(std::is_nothrow_destructible<SHAMapItem>{}, "");
47 static_assert(!std::is_default_constructible<SHAMapItem>{}, "");
48 static_assert(std::is_copy_constructible<SHAMapItem>{}, "");
49 static_assert(std::is_copy_assignable<SHAMapItem>{}, "");
50 static_assert(std::is_move_constructible<SHAMapItem>{}, "");
51 static_assert(std::is_move_assignable<SHAMapItem>{}, "");
52 
55 static_assert(std::is_copy_constructible<SHAMapNodeID>{}, "");
56 static_assert(std::is_copy_assignable<SHAMapNodeID>{}, "");
57 static_assert(std::is_move_constructible<SHAMapNodeID>{}, "");
58 static_assert(std::is_move_assignable<SHAMapNodeID>{}, "");
59 
60 static_assert(std::is_nothrow_destructible<SHAMapHash>{}, "");
62 static_assert(std::is_copy_constructible<SHAMapHash>{}, "");
63 static_assert(std::is_copy_assignable<SHAMapHash>{}, "");
64 static_assert(std::is_move_constructible<SHAMapHash>{}, "");
65 static_assert(std::is_move_assignable<SHAMapHash>{}, "");
66 
69 static_assert(!std::is_copy_constructible<SHAMapTreeNode>{}, "");
70 static_assert(!std::is_copy_assignable<SHAMapTreeNode>{}, "");
71 static_assert(!std::is_move_constructible<SHAMapTreeNode>{}, "");
72 static_assert(!std::is_move_assignable<SHAMapTreeNode>{}, "");
73 
77 static_assert(!std::is_copy_assignable<SHAMapInnerNode>{}, "");
79 static_assert(!std::is_move_assignable<SHAMapInnerNode>{}, "");
80 
83 static_assert(!std::is_copy_constructible<SHAMapLeafNode>{}, "");
84 static_assert(!std::is_copy_assignable<SHAMapLeafNode>{}, "");
85 static_assert(!std::is_move_constructible<SHAMapLeafNode>{}, "");
86 static_assert(!std::is_move_assignable<SHAMapLeafNode>{}, "");
87 #endif
88 
89 inline bool
90 operator==(SHAMapItem const& a, SHAMapItem const& b)
91 {
92  return a.key() == b.key();
93 }
94 inline bool
95 operator!=(SHAMapItem const& a, SHAMapItem const& b)
96 {
97  return a.key() != b.key();
98 }
99 inline bool
100 operator==(SHAMapItem const& a, uint256 const& b)
101 {
102  return a.key() == b;
103 }
104 inline bool
105 operator!=(SHAMapItem const& a, uint256 const& b)
106 {
107  return a.key() != b;
108 }
109 
110 class SHAMap_test : public beast::unit_test::suite
111 {
112 public:
113  static Buffer
114  IntToVUC(int v)
115  {
116  Buffer vuc(32);
117  std::fill_n(vuc.data(), vuc.size(), static_cast<std::uint8_t>(v));
118  return vuc;
119  }
120 
121  void
122  run() override
123  {
124  using namespace beast::severities;
125  test::SuiteJournal journal("SHAMap_test", *this);
126 
127  run(true, journal);
128  run(false, journal);
129  }
130 
131  void
132  run(bool backed, beast::Journal const& journal)
133  {
134  if (backed)
135  testcase("add/traverse backed");
136  else
137  testcase("add/traverse unbacked");
138 
139  tests::TestNodeFamily f(journal);
140 
141  // h3 and h4 differ only in the leaf, same terminal node (level 19)
142  constexpr uint256 h1(
143  "092891fe4ef6cee585fdc6fda0e09eb4d386363158ec3321b8123e5a772c6ca7");
144  constexpr uint256 h2(
145  "436ccbac3347baa1f1e53baeef1f43334da88f1f6d70d963b833afd6dfa289fe");
146  constexpr uint256 h3(
147  "b92891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e5a772c6ca8");
148  constexpr uint256 h4(
149  "b92891fe4ef6cee585fdc6fda2e09eb4d386363158ec3321b8123e5a772c6ca8");
150  constexpr uint256 h5(
151  "a92891fe4ef6cee585fdc6fda0e09eb4d386363158ec3321b8123e5a772c6ca7");
152 
153  SHAMap sMap(SHAMapType::FREE, f);
154  sMap.invariants();
155  if (!backed)
156  sMap.setUnbacked();
157 
158  SHAMapItem i1(h1, IntToVUC(1)), i2(h2, IntToVUC(2)),
159  i3(h3, IntToVUC(3)), i4(h4, IntToVUC(4)), i5(h5, IntToVUC(5));
160  unexpected(
162  "no add");
163  sMap.invariants();
164  unexpected(
166  "no add");
167  sMap.invariants();
168 
169  auto i = sMap.begin();
170  auto e = sMap.end();
171  unexpected(i == e || (*i != i1), "bad traverse");
172  ++i;
173  unexpected(i == e || (*i != i2), "bad traverse");
174  ++i;
175  unexpected(i != e, "bad traverse");
177  sMap.invariants();
178  sMap.delItem(i2.key());
179  sMap.invariants();
181  sMap.invariants();
182  i = sMap.begin();
183  e = sMap.end();
184  unexpected(i == e || (*i != i1), "bad traverse");
185  ++i;
186  unexpected(i == e || (*i != i3), "bad traverse");
187  ++i;
188  unexpected(i == e || (*i != i4), "bad traverse");
189  ++i;
190  unexpected(i != e, "bad traverse");
191 
192  if (backed)
193  testcase("snapshot backed");
194  else
195  testcase("snapshot unbacked");
196 
197  SHAMapHash mapHash = sMap.getHash();
198  std::shared_ptr<SHAMap> map2 = sMap.snapShot(false);
199  map2->invariants();
200  unexpected(sMap.getHash() != mapHash, "bad snapshot");
201  unexpected(map2->getHash() != mapHash, "bad snapshot");
202 
203  SHAMap::Delta delta;
204  BEAST_EXPECT(sMap.compare(*map2, delta, 100));
205  BEAST_EXPECT(delta.empty());
206 
207  unexpected(!sMap.delItem(sMap.begin()->key()), "bad mod");
208  sMap.invariants();
209  unexpected(sMap.getHash() == mapHash, "bad snapshot");
210  unexpected(map2->getHash() != mapHash, "bad snapshot");
211 
212  BEAST_EXPECT(sMap.compare(*map2, delta, 100));
213  BEAST_EXPECT(delta.size() == 1);
214  BEAST_EXPECT(delta.begin()->first == h1);
215  BEAST_EXPECT(delta.begin()->second.first == nullptr);
216  BEAST_EXPECT(delta.begin()->second.second->key() == h1);
217 
218  sMap.dump();
219 
220  if (backed)
221  testcase("build/tear backed");
222  else
223  testcase("build/tear unbacked");
224  {
225  constexpr std::array keys{
226  uint256("b92891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
227  "5a772c6ca8"),
228  uint256("b92881fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
229  "5a772c6ca8"),
230  uint256("b92691fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
231  "5a772c6ca8"),
232  uint256("b92791fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
233  "5a772c6ca8"),
234  uint256("b91891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
235  "5a772c6ca8"),
236  uint256("b99891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
237  "5a772c6ca8"),
238  uint256("f22891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
239  "5a772c6ca8"),
240  uint256("292891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
241  "5a772c6ca8")};
242 
243  constexpr std::array hashes{
244  uint256("B7387CFEA0465759ADC718E8C42B52D2309D179B326E239EB5075C"
245  "64B6281F7F"),
246  uint256("FBC195A9592A54AB44010274163CB6BA95F497EC5BA0A883184546"
247  "7FB2ECE266"),
248  uint256("4E7D2684B65DFD48937FFB775E20175C43AF0C94066F7D5679F51A"
249  "E756795B75"),
250  uint256("7A2F312EB203695FFD164E038E281839EEF06A1B99BFC263F3CECC"
251  "6C74F93E07"),
252  uint256("395A6691A372387A703FB0F2C6D2C405DAF307D0817F8F0E207596"
253  "462B0E3A3E"),
254  uint256("D044C0A696DE3169CC70AE216A1564D69DE96582865796142CE7D9"
255  "8A84D9DDE4"),
256  uint256("76DCC77C4027309B5A91AD164083264D70B77B5E43E08AEDA5EBF9"
257  "4361143615"),
258  uint256("DF4220E93ADC6F5569063A01B4DC79F8DB9553B6A3222ADE23DEA0"
259  "2BBE7230E5")};
260 
261  SHAMap map(SHAMapType::FREE, f);
262  if (!backed)
263  map.setUnbacked();
264 
265  BEAST_EXPECT(map.getHash() == beast::zero);
266  for (int k = 0; k < keys.size(); ++k)
267  {
268  SHAMapItem item(keys[k], IntToVUC(k));
269  BEAST_EXPECT(map.addItem(
270  SHAMapNodeType::tnTRANSACTION_NM, std::move(item)));
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  testcase("iterate backed");
285  else
286  testcase("iterate unbacked");
287 
288  {
289  constexpr std::array keys{
290  uint256("f22891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
291  "5a772c6ca8"),
292  uint256("b99891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
293  "5a772c6ca8"),
294  uint256("b92891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
295  "5a772c6ca8"),
296  uint256("b92881fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
297  "5a772c6ca8"),
298  uint256("b92791fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
299  "5a772c6ca8"),
300  uint256("b92691fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
301  "5a772c6ca8"),
302  uint256("b91891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
303  "5a772c6ca8"),
304  uint256("292891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e"
305  "5a772c6ca8")};
306 
307  tests::TestNodeFamily tf{journal};
308  SHAMap map{SHAMapType::FREE, tf};
309  if (!backed)
310  map.setUnbacked();
311  for (auto const& k : keys)
312  {
313  map.addItem(
315  SHAMapItem{k, IntToVUC(0)});
316  map.invariants();
317  }
318 
319  int h = 7;
320  for (auto const& k : map)
321  {
322  BEAST_EXPECT(k.key() == keys[h]);
323  --h;
324  }
325  }
326  }
327 };
328 
329 class SHAMapPathProof_test : public beast::unit_test::suite
330 {
331  void
332  run() override
333  {
334  test::SuiteJournal journal("SHAMapPathProof_test", *this);
335 
336  tests::TestNodeFamily tf{journal};
337  SHAMap map{SHAMapType::FREE, tf};
338  map.setUnbacked();
339 
340  uint256 key;
341  uint256 rootHash;
342  std::vector<Blob> goodPath;
343 
344  for (unsigned char c = 1; c < 100; ++c)
345  {
346  uint256 k(c);
347  map.addItem(
349  SHAMapItem{k, Slice{k.data(), k.size()}});
350  map.invariants();
351 
352  auto root = map.getHash().as_uint256();
353  auto path = map.getProofPath(k);
354  BEAST_EXPECT(path);
355  if (!path)
356  break;
357  BEAST_EXPECT(map.verifyProofPath(root, k, *path));
358  if (c == 1)
359  {
360  // extra node
361  path->insert(path->begin(), path->front());
362  BEAST_EXPECT(!map.verifyProofPath(root, k, *path));
363  // wrong key
364  uint256 wrongKey(c + 1);
365  BEAST_EXPECT(!map.getProofPath(wrongKey));
366  }
367  if (c == 99)
368  {
369  key = k;
370  rootHash = root;
371  goodPath = std::move(*path);
372  }
373  }
374 
375  // still good
376  BEAST_EXPECT(map.verifyProofPath(rootHash, key, goodPath));
377  // empty path
378  std::vector<Blob> badPath;
379  BEAST_EXPECT(!map.verifyProofPath(rootHash, key, badPath));
380  // too long
381  badPath = goodPath;
382  badPath.push_back(goodPath.back());
383  BEAST_EXPECT(!map.verifyProofPath(rootHash, key, badPath));
384  // bad node
385  badPath.clear();
386  badPath.emplace_back(100, 100);
387  BEAST_EXPECT(!map.verifyProofPath(rootHash, key, badPath));
388  // bad node type
389  badPath.clear();
390  badPath.push_back(goodPath.front());
391  badPath.front().back()--; // change node type
392  BEAST_EXPECT(!map.verifyProofPath(rootHash, key, badPath));
393  // all inner
394  badPath.clear();
395  badPath = goodPath;
396  badPath.erase(badPath.begin());
397  BEAST_EXPECT(!map.verifyProofPath(rootHash, key, badPath));
398  }
399 };
400 
401 BEAST_DEFINE_TESTSUITE(SHAMap, ripple_app, ripple);
402 BEAST_DEFINE_TESTSUITE(SHAMapPathProof, ripple_app, ripple);
403 } // namespace tests
404 } // namespace ripple
ripple::tests::SHAMap_test
Definition: SHAMap_test.cpp:110
ripple::SHAMap::invariants
void invariants() const
Definition: SHAMap.cpp:1188
std::shared_ptr
STL class.
ripple::SHAMap::getHash
SHAMapHash getHash() const
Definition: SHAMap.cpp:852
std::is_nothrow_destructible
ripple::SHAMap::addItem
bool addItem(SHAMapNodeType type, SHAMapItem &&i)
Definition: SHAMap.cpp:846
ripple::Slice
An immutable linear range of bytes.
Definition: Slice.h:44
ripple::SHAMapNodeType::tnACCOUNT_STATE
@ tnACCOUNT_STATE
ripple::tests::SHAMap_test::run
void run(bool backed, beast::Journal const &journal)
Definition: SHAMap_test.cpp:132
ripple::SHAMap::dump
void dump(bool withHashes=false) const
Definition: SHAMap.cpp:1124
std::vector
STL class.
std::map::size
T size(T... args)
ripple::tests::TestNodeFamily
Definition: common.h:32
std::is_default_constructible
beast::severities
A namespace for easy access to logging severity values.
Definition: Journal.h:29
ripple::Buffer
Like std::vector<char> but better.
Definition: Buffer.h:35
ripple::SHAMap::begin
const_iterator begin() const
Definition: SHAMap.h:738
std::vector::back
T back(T... args)
ripple::tests::BEAST_DEFINE_TESTSUITE
BEAST_DEFINE_TESTSUITE(cluster, overlay, ripple)
ripple::SHAMap::snapShot
std::shared_ptr< SHAMap > snapShot(bool isMutable) const
Definition: SHAMap.cpp:70
ripple::tests::SHAMapPathProof_test::run
void run() override
Definition: SHAMap_test.cpp:332
ripple::tests::operator!=
bool operator!=(SHAMapItem const &a, SHAMapItem const &b)
Definition: SHAMap_test.cpp:95
std::vector::front
T front(T... args)
std::is_move_constructible
ripple::base_uint::data
pointer data()
Definition: base_uint.h:121
ripple::SHAMapHash
Definition: SHAMapHash.h:32
algorithm
ripple::tests::SHAMapPathProof_test
Definition: SHAMap_test.cpp:329
ripple::SHAMapNodeType::tnTRANSACTION_NM
@ tnTRANSACTION_NM
std::vector::clear
T clear(T... args)
ripple::base_uint::size
constexpr static std::size_t size()
Definition: base_uint.h:518
ripple::SHAMapItem::key
uint256 const & key() const
Definition: SHAMapItem.h:45
ripple::uint256
base_uint< 256 > uint256
Definition: base_uint.h:549
std::vector::push_back
T push_back(T... args)
ripple::base_uint
Integers of any length that is a multiple of 32-bits.
Definition: base_uint.h:81
ripple::SHAMapItem
Definition: SHAMapItem.h:31
ripple::SHAMap
A SHAMap is both a radix tree with a fan-out of 16 and a Merkle tree.
Definition: SHAMap.h:95
ripple::tests::SHAMap_test::IntToVUC
static Buffer IntToVUC(int v)
Definition: SHAMap_test.cpp:114
std::array
STL class.
ripple::Buffer::size
std::size_t size() const noexcept
Returns the number of bytes in the buffer.
Definition: Buffer.h:126
std::vector::erase
T erase(T... args)
ripple::Buffer::data
std::uint8_t const * data() const noexcept
Return a pointer to beginning of the storage.
Definition: Buffer.h:150
beast::Journal
A generic endpoint for log messages.
Definition: Journal.h:58
std::uint8_t
std::map
STL class.
ripple::tests::operator==
bool operator==(SHAMapItem const &a, SHAMapItem const &b)
Definition: SHAMap_test.cpp:90
ripple::test::SuiteJournal
Definition: SuiteJournal.h:88
std::is_copy_constructible
std::is_move_assignable
std::vector::emplace_back
T emplace_back(T... args)
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
ripple::SHAMap::setUnbacked
void setUnbacked()
Definition: SHAMap.h:623
std::map::begin
T begin(T... args)
ripple::SHAMap::end
const_iterator end() const
Definition: SHAMap.h:744
ripple::tests::SHAMap_test::run
void run() override
Definition: SHAMap_test.cpp:122
std::map::empty
T empty(T... args)
ripple::SHAMap::compare
bool compare(SHAMap const &otherMap, Delta &differences, int maxCount) const
Definition: SHAMapDelta.cpp:128
ripple::SHAMapType::FREE
@ FREE
std::is_copy_assignable
ripple::SHAMapHash::as_uint256
uint256 const & as_uint256() const
Definition: SHAMapHash.h:43
std::fill_n
T fill_n(T... args)
ripple::SHAMap::delItem
bool delItem(uint256 const &id)
Definition: SHAMap.cpp:700
ripple::root
Number root(Number f, unsigned d)
Definition: Number.cpp:624