rippled
SHAMapDelta.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/contract.h>
21 #include <ripple/shamap/SHAMap.h>
22 
23 namespace ripple {
24 
25 // This code is used to compare another node's transaction tree
26 // to our own. It returns a map containing all items that are different
27 // between two SHA maps. It is optimized not to descend down tree
28 // branches with the same branch hash. A limit can be passed so
29 // that we will abort early if a node sends a map to us that
30 // makes no sense at all. (And our sync algorithm will avoid
31 // synchronizing matching branches too.)
32 
33 bool
35  SHAMapTreeNode* node,
36  std::shared_ptr<SHAMapItem const> const& otherMapItem,
37  bool isFirstMap,
38  Delta& differences,
39  int& maxCount) const
40 {
41  // Walk a branch of a SHAMap that's matched by an empty branch or single
42  // item in the other map
44  nodeStack.push(node);
45 
46  bool emptyBranch = !otherMapItem;
47 
48  while (!nodeStack.empty())
49  {
50  node = nodeStack.top();
51  nodeStack.pop();
52 
53  if (node->isInner())
54  {
55  // This is an inner node, add all non-empty branches
56  auto inner = static_cast<SHAMapInnerNode*>(node);
57  for (int i = 0; i < 16; ++i)
58  if (!inner->isEmptyBranch(i))
59  nodeStack.push({descendThrow(inner, i)});
60  }
61  else
62  {
63  // This is a leaf node, process its item
64  auto item = static_cast<SHAMapLeafNode*>(node)->peekItem();
65 
66  if (emptyBranch || (item->key() != otherMapItem->key()))
67  {
68  // unmatched
69  if (isFirstMap)
70  differences.insert(std::make_pair(
71  item->key(),
73  else
74  differences.insert(std::make_pair(
75  item->key(),
77 
78  if (--maxCount <= 0)
79  return false;
80  }
81  else if (item->slice() != otherMapItem->slice())
82  {
83  // non-matching items with same tag
84  if (isFirstMap)
85  differences.insert(std::make_pair(
86  item->key(), DeltaRef(item, otherMapItem)));
87  else
88  differences.insert(std::make_pair(
89  item->key(), DeltaRef(otherMapItem, item)));
90 
91  if (--maxCount <= 0)
92  return false;
93 
94  emptyBranch = true;
95  }
96  else
97  {
98  // exact match
99  emptyBranch = true;
100  }
101  }
102  }
103 
104  if (!emptyBranch)
105  {
106  // otherMapItem was unmatched, must add
107  if (isFirstMap) // this is first map, so other item is from second
108  differences.insert(std::make_pair(
109  otherMapItem->key(),
110  DeltaRef(std::shared_ptr<SHAMapItem const>(), otherMapItem)));
111  else
112  differences.insert(std::make_pair(
113  otherMapItem->key(),
114  DeltaRef(otherMapItem, std::shared_ptr<SHAMapItem const>())));
115 
116  if (--maxCount <= 0)
117  return false;
118  }
119 
120  return true;
121 }
122 
123 bool
124 SHAMap::compare(SHAMap const& otherMap, Delta& differences, int maxCount) const
125 {
126  // compare two hash trees, add up to maxCount differences to the difference
127  // table return value: true=complete table of differences given, false=too
128  // many differences throws on corrupt tables or missing nodes CAUTION:
129  // otherMap is not locked and must be immutable
130 
131  assert(isValid() && otherMap.isValid());
132 
133  if (getHash() == otherMap.getHash())
134  return true;
135 
138  nodeStack; // track nodes we've pushed
139 
140  nodeStack.push({root_.get(), otherMap.root_.get()});
141  while (!nodeStack.empty())
142  {
143  auto [ourNode, otherNode] = nodeStack.top();
144  nodeStack.pop();
145 
146  if (!ourNode || !otherNode)
147  {
148  assert(false);
149  Throw<SHAMapMissingNode>(type_, uint256());
150  }
151 
152  if (ourNode->isLeaf() && otherNode->isLeaf())
153  {
154  // two leaves
155  auto ours = static_cast<SHAMapLeafNode*>(ourNode);
156  auto other = static_cast<SHAMapLeafNode*>(otherNode);
157  if (ours->peekItem()->key() == other->peekItem()->key())
158  {
159  if (ours->peekItem()->slice() != other->peekItem()->slice())
160  {
161  differences.insert(std::make_pair(
162  ours->peekItem()->key(),
163  DeltaRef(ours->peekItem(), other->peekItem())));
164  if (--maxCount <= 0)
165  return false;
166  }
167  }
168  else
169  {
170  differences.insert(std::make_pair(
171  ours->peekItem()->key(),
172  DeltaRef(
173  ours->peekItem(),
175  if (--maxCount <= 0)
176  return false;
177 
178  differences.insert(std::make_pair(
179  other->peekItem()->key(),
180  DeltaRef(
182  other->peekItem())));
183  if (--maxCount <= 0)
184  return false;
185  }
186  }
187  else if (ourNode->isInner() && otherNode->isLeaf())
188  {
189  auto ours = static_cast<SHAMapInnerNode*>(ourNode);
190  auto other = static_cast<SHAMapLeafNode*>(otherNode);
191  if (!walkBranch(
192  ours, other->peekItem(), true, differences, maxCount))
193  return false;
194  }
195  else if (ourNode->isLeaf() && otherNode->isInner())
196  {
197  auto ours = static_cast<SHAMapLeafNode*>(ourNode);
198  auto other = static_cast<SHAMapInnerNode*>(otherNode);
199  if (!otherMap.walkBranch(
200  other, ours->peekItem(), false, differences, maxCount))
201  return false;
202  }
203  else if (ourNode->isInner() && otherNode->isInner())
204  {
205  auto ours = static_cast<SHAMapInnerNode*>(ourNode);
206  auto other = static_cast<SHAMapInnerNode*>(otherNode);
207  for (int i = 0; i < 16; ++i)
208  if (ours->getChildHash(i) != other->getChildHash(i))
209  {
210  if (other->isEmptyBranch(i))
211  {
212  // We have a branch, the other tree does not
213  SHAMapTreeNode* iNode = descendThrow(ours, i);
214  if (!walkBranch(
215  iNode,
217  true,
218  differences,
219  maxCount))
220  return false;
221  }
222  else if (ours->isEmptyBranch(i))
223  {
224  // The other tree has a branch, we do not
225  SHAMapTreeNode* iNode = otherMap.descendThrow(other, i);
226  if (!otherMap.walkBranch(
227  iNode,
229  false,
230  differences,
231  maxCount))
232  return false;
233  }
234  else // The two trees have different non-empty branches
235  nodeStack.push(
236  {descendThrow(ours, i),
237  otherMap.descendThrow(other, i)});
238  }
239  }
240  else
241  assert(false);
242  }
243 
244  return true;
245 }
246 
247 void
248 SHAMap::walkMap(std::vector<SHAMapMissingNode>& missingNodes, int maxMissing)
249  const
250 {
251  if (!root_->isInner()) // root_ is only node, and we have it
252  return;
253 
254  using StackEntry = std::shared_ptr<SHAMapInnerNode>;
256 
257  nodeStack.push(std::static_pointer_cast<SHAMapInnerNode>(root_));
258 
259  while (!nodeStack.empty())
260  {
261  std::shared_ptr<SHAMapInnerNode> node = std::move(nodeStack.top());
262  nodeStack.pop();
263 
264  for (int i = 0; i < 16; ++i)
265  {
266  if (!node->isEmptyBranch(i))
267  {
269  descendNoStore(node, i);
270 
271  if (nextNode)
272  {
273  if (nextNode->isInner())
274  nodeStack.push(
275  std::static_pointer_cast<SHAMapInnerNode>(
276  nextNode));
277  }
278  else
279  {
280  missingNodes.emplace_back(type_, node->getChildHash(i));
281  if (--maxMissing <= 0)
282  return;
283  }
284  }
285  }
286  }
287 }
288 
289 } // namespace ripple
ripple::SHAMap::isValid
bool isValid() const
Definition: SHAMap.h:580
std::shared_ptr
STL class.
ripple::SHAMap::getHash
SHAMapHash getHash() const
Definition: SHAMap.cpp:783
std::pair
std::vector
STL class.
std::stack
STL class.
ripple::SHAMap::peekItem
std::shared_ptr< SHAMapItem const > const & peekItem(uint256 const &id) const
Definition: SHAMap.cpp:561
ripple::SHAMapTreeNode::isInner
virtual bool isInner() const =0
Determines if this is an inner node.
ripple::SHAMapLeafNode
Definition: SHAMapLeafNode.h:32
ripple::base_uint< 256 >
ripple::SHAMap::DeltaRef
std::pair< std::shared_ptr< SHAMapItem const > const &, std::shared_ptr< SHAMapItem const > const & > DeltaRef
Definition: SHAMap.h:347
ripple::SHAMapInnerNode
Definition: SHAMapInnerNode.h:39
ripple::SHAMap
A SHAMap is both a radix tree with a fan-out of 16 and a Merkle tree.
Definition: SHAMap.h:95
std::stack::pop
T pop(T... args)
std::stack::top
T top(T... args)
ripple::SHAMapTreeNode
Definition: SHAMapTreeNode.h:53
ripple::SHAMap::descendThrow
SHAMapTreeNode * descendThrow(SHAMapInnerNode *, int branch) const
Definition: SHAMap.cpp:283
std::map
STL class.
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
std::map::insert
T insert(T... args)
std::stack::empty
T empty(T... args)
std::stack::push
T push(T... args)
std::make_pair
T make_pair(T... args)
ripple::SHAMap::walkBranch
bool walkBranch(SHAMapTreeNode *node, std::shared_ptr< SHAMapItem const > const &otherMapItem, bool isFirstMap, Delta &differences, int &maxCount) const
Definition: SHAMapDelta.cpp:34
ripple::SHAMap::root_
std::shared_ptr< SHAMapTreeNode > root_
Definition: SHAMap.h:107