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/StringUtilities.h>
22 #include <ripple/beast/unit_test.h>
23 #include <ripple/beast/utility/Journal.h>
24 #include <ripple/shamap/SHAMap.h>
25 #include <test/shamap/common.h>
26 #include <test/unit_test/SuiteJournal.h>
27 
28 namespace ripple {
29 namespace tests {
30 
31 #ifndef __INTELLISENSE__
32 static_assert(std::is_nothrow_destructible<SHAMap>{}, "");
33 static_assert(!std::is_default_constructible<SHAMap>{}, "");
34 static_assert(!std::is_copy_constructible<SHAMap>{}, "");
35 static_assert(!std::is_copy_assignable<SHAMap>{}, "");
36 static_assert(!std::is_move_constructible<SHAMap>{}, "");
37 static_assert(!std::is_move_assignable<SHAMap>{}, "");
38 
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 
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<SHAMapTreeNode>{}, "");
84 static_assert(!std::is_copy_assignable<SHAMapTreeNode>{}, "");
85 static_assert(!std::is_move_constructible<SHAMapTreeNode>{}, "");
86 static_assert(!std::is_move_assignable<SHAMapTreeNode>{}, "");
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 Blob
114  IntToVUC(int v)
115  {
116  Blob vuc;
117 
118  for (int i = 0; i < 32; ++i)
119  vuc.push_back(static_cast<unsigned char>(v));
120 
121  return vuc;
122  }
123 
124  void
125  run() override
126  {
127  using namespace beast::severities;
128  test::SuiteJournal journal("SHAMap_test", *this);
129 
130  run(true, journal);
131  run(false, journal);
132  }
133 
134  void
135  run(bool backed, beast::Journal const& journal)
136  {
137  if (backed)
138  testcase("add/traverse backed");
139  else
140  testcase("add/traverse unbacked");
141 
142  tests::TestNodeFamily f(journal);
143 
144  // h3 and h4 differ only in the leaf, same terminal node (level 19)
145  uint256 h1, h2, h3, h4, h5;
146  h1.SetHex(
147  "092891fe4ef6cee585fdc6fda0e09eb4d386363158ec3321b8123e5a772c6ca7");
148  h2.SetHex(
149  "436ccbac3347baa1f1e53baeef1f43334da88f1f6d70d963b833afd6dfa289fe");
150  h3.SetHex(
151  "b92891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e5a772c6ca8");
152  h4.SetHex(
153  "b92891fe4ef6cee585fdc6fda2e09eb4d386363158ec3321b8123e5a772c6ca8");
154  h5.SetHex(
155  "a92891fe4ef6cee585fdc6fda0e09eb4d386363158ec3321b8123e5a772c6ca7");
156 
157  SHAMap sMap(SHAMapType::FREE, f);
158  sMap.invariants();
159  if (!backed)
160  sMap.setUnbacked();
161 
162  SHAMapItem i1(h1, IntToVUC(1)), i2(h2, IntToVUC(2)),
163  i3(h3, IntToVUC(3)), i4(h4, IntToVUC(4)), i5(h5, IntToVUC(5));
164  unexpected(!sMap.addItem(SHAMapItem{i2}, true, false), "no add");
165  sMap.invariants();
166  unexpected(!sMap.addItem(SHAMapItem{i1}, true, false), "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");
176  sMap.addItem(SHAMapItem{i4}, true, false);
177  sMap.invariants();
178  sMap.delItem(i2.key());
179  sMap.invariants();
180  sMap.addItem(SHAMapItem{i3}, true, false);
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  std::vector<uint256> keys(8);
226  keys[0].SetHex(
227  "b92891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e5a772c6c"
228  "a8");
229  keys[1].SetHex(
230  "b92881fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e5a772c6c"
231  "a8");
232  keys[2].SetHex(
233  "b92691fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e5a772c6c"
234  "a8");
235  keys[3].SetHex(
236  "b92791fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e5a772c6c"
237  "a8");
238  keys[4].SetHex(
239  "b91891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e5a772c6c"
240  "a8");
241  keys[5].SetHex(
242  "b99891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e5a772c6c"
243  "a8");
244  keys[6].SetHex(
245  "f22891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e5a772c6c"
246  "a8");
247  keys[7].SetHex(
248  "292891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e5a772c6c"
249  "a8");
250 
251  std::vector<uint256> hashes(8);
252  hashes[0].SetHex(
253  "B7387CFEA0465759ADC718E8C42B52D2309D179B326E239EB5075C64B6281F"
254  "7F");
255  hashes[1].SetHex(
256  "FBC195A9592A54AB44010274163CB6BA95F497EC5BA0A8831845467FB2ECE2"
257  "66");
258  hashes[2].SetHex(
259  "4E7D2684B65DFD48937FFB775E20175C43AF0C94066F7D5679F51AE756795B"
260  "75");
261  hashes[3].SetHex(
262  "7A2F312EB203695FFD164E038E281839EEF06A1B99BFC263F3CECC6C74F93E"
263  "07");
264  hashes[4].SetHex(
265  "395A6691A372387A703FB0F2C6D2C405DAF307D0817F8F0E207596462B0E3A"
266  "3E");
267  hashes[5].SetHex(
268  "D044C0A696DE3169CC70AE216A1564D69DE96582865796142CE7D98A84D9DD"
269  "E4");
270  hashes[6].SetHex(
271  "76DCC77C4027309B5A91AD164083264D70B77B5E43E08AEDA5EBF943611436"
272  "15");
273  hashes[7].SetHex(
274  "DF4220E93ADC6F5569063A01B4DC79F8DB9553B6A3222ADE23DEA02BBE7230"
275  "E5");
276 
277  SHAMap map(SHAMapType::FREE, f);
278  if (!backed)
279  map.setUnbacked();
280 
281  BEAST_EXPECT(map.getHash() == beast::zero);
282  for (int k = 0; k < keys.size(); ++k)
283  {
284  SHAMapItem item(keys[k], IntToVUC(k));
285  BEAST_EXPECT(map.addItem(std::move(item), true, false));
286  BEAST_EXPECT(map.getHash().as_uint256() == hashes[k]);
287  map.invariants();
288  }
289  for (int k = keys.size() - 1; k >= 0; --k)
290  {
291  BEAST_EXPECT(map.getHash().as_uint256() == hashes[k]);
292  BEAST_EXPECT(map.delItem(keys[k]));
293  map.invariants();
294  }
295  BEAST_EXPECT(map.getHash() == beast::zero);
296  }
297 
298  if (backed)
299  testcase("iterate backed");
300  else
301  testcase("iterate unbacked");
302 
303  {
304  std::vector<uint256> keys(8);
305  keys[0].SetHex(
306  "f22891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e5a772c6c"
307  "a8");
308  keys[1].SetHex(
309  "b99891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e5a772c6c"
310  "a8");
311  keys[2].SetHex(
312  "b92891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e5a772c6c"
313  "a8");
314  keys[3].SetHex(
315  "b92881fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e5a772c6c"
316  "a8");
317  keys[4].SetHex(
318  "b92791fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e5a772c6c"
319  "a8");
320  keys[5].SetHex(
321  "b92691fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e5a772c6c"
322  "a8");
323  keys[6].SetHex(
324  "b91891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e5a772c6c"
325  "a8");
326  keys[7].SetHex(
327  "292891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e5a772c6c"
328  "a8");
329 
330  tests::TestNodeFamily tf{journal};
331  SHAMap map{SHAMapType::FREE, tf};
332  if (!backed)
333  map.setUnbacked();
334  for (auto const& k : keys)
335  {
336  map.addItem(SHAMapItem{k, IntToVUC(0)}, true, false);
337  map.invariants();
338  }
339 
340  int h = 7;
341  for (auto const& k : map)
342  {
343  BEAST_EXPECT(k.key() == keys[h]);
344  --h;
345  }
346  }
347  }
348 };
349 
350 BEAST_DEFINE_TESTSUITE(SHAMap, ripple_app, ripple);
351 
352 } // namespace tests
353 } // namespace ripple
ripple::tests::SHAMap_test
Definition: SHAMap_test.cpp:110
ripple::SHAMap::invariants
void invariants() const
Definition: SHAMap.cpp:1139
std::shared_ptr
STL class.
ripple::SHAMap::getHash
SHAMapHash getHash() const
Definition: SHAMap.cpp:795
std::is_nothrow_destructible
ripple::tests::SHAMap_test::run
void run(bool backed, beast::Journal const &journal)
Definition: SHAMap_test.cpp:135
ripple::SHAMap::dump
void dump(bool withHashes=false) const
Definition: SHAMap.cpp:1075
std::vector< unsigned char >
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::SHAMap::begin
const_iterator begin() const
Definition: SHAMap.h:638
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:55
ripple::tests::operator!=
bool operator!=(SHAMapItem const &a, SHAMapItem const &b)
Definition: SHAMap_test.cpp:95
std::is_move_constructible
ripple::SHAMapHash
Definition: SHAMapTreeNode.h:43
std::vector::push_back
T push_back(T... args)
ripple::base_uint
Definition: base_uint.h:63
ripple::SHAMapItem::key
uint256 const & key() const
Definition: SHAMapItem.h:81
ripple::SHAMapItem
Definition: SHAMapItem.h:34
ripple::SHAMap
A SHAMap is both a radix tree with a fan-out of 16 and a Merkle tree.
Definition: SHAMap.h:81
ripple::base_uint::SetHex
bool SetHex(const char *psz, bool bStrict=false)
Parse a hex string into a base_uint The input can be:
Definition: base_uint.h:406
beast::Journal
A generic endpoint for log messages.
Definition: Journal.h:58
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
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:528
std::map::begin
T begin(T... args)
ripple::SHAMap::end
const_iterator end() const
Definition: SHAMap.h:644
ripple::tests::SHAMap_test::run
void run() override
Definition: SHAMap_test.cpp:125
std::map::empty
T empty(T... args)
ripple::SHAMap::compare
bool compare(SHAMap const &otherMap, Delta &differences, int maxCount) const
Definition: SHAMapDelta.cpp:124
ripple::SHAMap::addItem
bool addItem(SHAMapItem &&i, bool isTransaction, bool hasMeta)
Definition: SHAMap.cpp:786
ripple::SHAMapType::FREE
@ FREE
std::is_copy_assignable
ripple::SHAMapHash::as_uint256
uint256 const & as_uint256() const
Definition: SHAMapTreeNode.h:54
ripple::SHAMap::delItem
bool delItem(uint256 const &id)
Definition: SHAMap.cpp:625
ripple::tests::SHAMap_test::IntToVUC
static Blob IntToVUC(int v)
Definition: SHAMap_test.cpp:114