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->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<SHAMapLeafNode*>(ourNode);
156  auto other = static_cast<SHAMapLeafNode*>(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<SHAMapLeafNode*>(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<SHAMapLeafNode*>(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  SHAMapTreeNode* 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  SHAMapTreeNode* iNode = otherMap.descendThrow(other, i);
227  if (!otherMap.walkBranch(
228  iNode,
230  false,
231  differences,
232  maxCount))
233  return false;
234  }
235  else // The two trees have different non-empty branches
236  nodeStack.push(
237  {descendThrow(ours, i),
238  otherMap.descendThrow(other, i)});
239  }
240  }
241  else
242  assert(false);
243  }
244 
245  return true;
246 }
247 
248 void
249 SHAMap::walkMap(std::vector<SHAMapMissingNode>& missingNodes, int maxMissing)
250  const
251 {
252  if (!root_->isInner()) // root_ is only node, and we have it
253  return;
254 
255  using StackEntry = std::shared_ptr<SHAMapInnerNode>;
257 
258  nodeStack.push(std::static_pointer_cast<SHAMapInnerNode>(root_));
259 
260  while (!nodeStack.empty())
261  {
262  std::shared_ptr<SHAMapInnerNode> node = std::move(nodeStack.top());
263  nodeStack.pop();
264 
265  for (int i = 0; i < 16; ++i)
266  {
267  if (!node->isEmptyBranch(i))
268  {
270  descendNoStore(node, i);
271 
272  if (nextNode)
273  {
274  if (nextNode->isInner())
275  nodeStack.push(
276  std::static_pointer_cast<SHAMapInnerNode>(
277  nextNode));
278  }
279  else
280  {
281  missingNodes.emplace_back(type_, node->getChildHash(i));
282  if (--maxMissing <= 0)
283  return;
284  }
285  }
286  }
287  }
288 }
289 
290 } // namespace ripple
ripple::SHAMap::isValid
bool isValid() const
Definition: SHAMap.h:558
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:325
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:133
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