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  SHAMapAbstractNode* 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<SHAMapTreeNode*>(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->peekData() != otherMapItem->peekData())
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<SHAMapTreeNode*>(ourNode);
156  auto other = static_cast<SHAMapTreeNode*>(otherNode);
157  if (ours->peekItem()->key() == other->peekItem()->key())
158  {
159  if (ours->peekItem()->peekData() !=
160  other->peekItem()->peekData())
161  {
162  differences.insert(std::make_pair(
163  ours->peekItem()->key(),
164  DeltaRef(ours->peekItem(), other->peekItem())));
165  if (--maxCount <= 0)
166  return false;
167  }
168  }
169  else
170  {
171  differences.insert(std::make_pair(
172  ours->peekItem()->key(),
173  DeltaRef(
174  ours->peekItem(),
176  if (--maxCount <= 0)
177  return false;
178 
179  differences.insert(std::make_pair(
180  other->peekItem()->key(),
181  DeltaRef(
183  other->peekItem())));
184  if (--maxCount <= 0)
185  return false;
186  }
187  }
188  else if (ourNode->isInner() && otherNode->isLeaf())
189  {
190  auto ours = static_cast<SHAMapInnerNode*>(ourNode);
191  auto other = static_cast<SHAMapTreeNode*>(otherNode);
192  if (!walkBranch(
193  ours, other->peekItem(), true, differences, maxCount))
194  return false;
195  }
196  else if (ourNode->isLeaf() && otherNode->isInner())
197  {
198  auto ours = static_cast<SHAMapTreeNode*>(ourNode);
199  auto other = static_cast<SHAMapInnerNode*>(otherNode);
200  if (!otherMap.walkBranch(
201  other, ours->peekItem(), false, differences, maxCount))
202  return false;
203  }
204  else if (ourNode->isInner() && otherNode->isInner())
205  {
206  auto ours = static_cast<SHAMapInnerNode*>(ourNode);
207  auto other = static_cast<SHAMapInnerNode*>(otherNode);
208  for (int i = 0; i < 16; ++i)
209  if (ours->getChildHash(i) != other->getChildHash(i))
210  {
211  if (other->isEmptyBranch(i))
212  {
213  // We have a branch, the other tree does not
214  SHAMapAbstractNode* iNode = descendThrow(ours, i);
215  if (!walkBranch(
216  iNode,
218  true,
219  differences,
220  maxCount))
221  return false;
222  }
223  else if (ours->isEmptyBranch(i))
224  {
225  // The other tree has a branch, we do not
226  SHAMapAbstractNode* iNode =
227  otherMap.descendThrow(other, i);
228  if (!otherMap.walkBranch(
229  iNode,
231  false,
232  differences,
233  maxCount))
234  return false;
235  }
236  else // The two trees have different non-empty branches
237  nodeStack.push(
238  {descendThrow(ours, i),
239  otherMap.descendThrow(other, i)});
240  }
241  }
242  else
243  assert(false);
244  }
245 
246  return true;
247 }
248 
249 void
250 SHAMap::walkMap(std::vector<SHAMapMissingNode>& missingNodes, int maxMissing)
251  const
252 {
253  if (!root_->isInner()) // root_ is only node, and we have it
254  return;
255 
256  using StackEntry = std::shared_ptr<SHAMapInnerNode>;
258 
259  nodeStack.push(std::static_pointer_cast<SHAMapInnerNode>(root_));
260 
261  while (!nodeStack.empty())
262  {
263  std::shared_ptr<SHAMapInnerNode> node = std::move(nodeStack.top());
264  nodeStack.pop();
265 
266  for (int i = 0; i < 16; ++i)
267  {
268  if (!node->isEmptyBranch(i))
269  {
271  descendNoStore(node, i);
272 
273  if (nextNode)
274  {
275  if (nextNode->isInner())
276  nodeStack.push(
277  std::static_pointer_cast<SHAMapInnerNode>(
278  nextNode));
279  }
280  else
281  {
282  missingNodes.emplace_back(type_, node->getChildHash(i));
283  if (--maxMissing <= 0)
284  return;
285  }
286  }
287  }
288  }
289 }
290 
291 } // namespace ripple
ripple::SHAMap::isValid
bool isValid() const
Definition: SHAMap.h:522
std::shared_ptr
STL class.
ripple::SHAMap::getHash
SHAMapHash getHash() const
Definition: SHAMap.cpp:793
std::pair
ripple::SHAMap::descendThrow
SHAMapAbstractNode * descendThrow(SHAMapInnerNode *, int branch) const
Definition: SHAMap.cpp:256
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:541
ripple::base_uint< 256 >
ripple::SHAMapAbstractNode::isInner
bool isInner() const
Definition: SHAMapTreeNode.h:364
ripple::SHAMap::DeltaRef
std::pair< std::shared_ptr< SHAMapItem const > const &, std::shared_ptr< SHAMapItem const > const & > DeltaRef
Definition: SHAMap.h:304
ripple::SHAMapInnerNode
Definition: SHAMapTreeNode.h:207
ripple::SHAMap
A SHAMap is both a radix tree with a fan-out of 16 and a Merkle tree.
Definition: SHAMap.h:81
std::stack::pop
T pop(T... args)
std::stack::top
T top(T... args)
ripple::SHAMapTreeNode
Definition: SHAMapTreeNode.h:273
std::map
STL class.
ripple::SHAMap::walkBranch
bool walkBranch(SHAMapAbstractNode *node, std::shared_ptr< SHAMapItem const > const &otherMapItem, bool isFirstMap, Delta &differences, int &maxCount) const
Definition: SHAMapDelta.cpp:34
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)
ripple::SHAMap::root_
std::shared_ptr< SHAMapAbstractNode > root_
Definition: SHAMap.h:88
ripple::SHAMapAbstractNode
Definition: SHAMapTreeNode.h:122
std::stack::empty
T empty(T... args)
std::stack::push
T push(T... args)
std::make_pair
T make_pair(T... args)