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 
34  std::shared_ptr<SHAMapItem const> const& otherMapItem,
35  bool isFirstMap,Delta& differences, int& maxCount) const
36 {
37  // Walk a branch of a SHAMap that's matched by an empty branch or single item in the other map
39  nodeStack.push (node);
40 
41  bool emptyBranch = !otherMapItem;
42 
43  while (!nodeStack.empty ())
44  {
45  node = nodeStack.top ();
46  nodeStack.pop ();
47 
48  if (node->isInner ())
49  {
50  // This is an inner node, add all non-empty branches
51  auto inner = static_cast<SHAMapInnerNode*>(node);
52  for (int i = 0; i < 16; ++i)
53  if (!inner->isEmptyBranch (i))
54  nodeStack.push ({descendThrow (inner, i)});
55  }
56  else
57  {
58  // This is a leaf node, process its item
59  auto item = static_cast<SHAMapTreeNode*>(node)->peekItem();
60 
61  if (emptyBranch || (item->key() != otherMapItem->key()))
62  {
63  // unmatched
64  if (isFirstMap)
65  differences.insert (std::make_pair (item->key(),
67  else
68  differences.insert (std::make_pair (item->key(),
70 
71  if (--maxCount <= 0)
72  return false;
73  }
74  else if (item->peekData () != otherMapItem->peekData ())
75  {
76  // non-matching items with same tag
77  if (isFirstMap)
78  differences.insert (std::make_pair (item->key(),
79  DeltaRef (item, otherMapItem)));
80  else
81  differences.insert (std::make_pair (item->key(),
82  DeltaRef (otherMapItem, item)));
83 
84  if (--maxCount <= 0)
85  return false;
86 
87  emptyBranch = true;
88  }
89  else
90  {
91  // exact match
92  emptyBranch = true;
93  }
94  }
95  }
96 
97  if (!emptyBranch)
98  {
99  // otherMapItem was unmatched, must add
100  if (isFirstMap) // this is first map, so other item is from second
101  differences.insert(std::make_pair(otherMapItem->key(),
103  otherMapItem)));
104  else
105  differences.insert(std::make_pair(otherMapItem->key(),
106  DeltaRef(otherMapItem, std::shared_ptr<SHAMapItem const>())));
107 
108  if (--maxCount <= 0)
109  return false;
110  }
111 
112  return true;
113 }
114 
115 bool
116 SHAMap::compare (SHAMap const& otherMap,
117  Delta& differences, int maxCount) const
118 {
119  // compare two hash trees, add up to maxCount differences to the difference table
120  // return value: true=complete table of differences given, false=too many differences
121  // throws on corrupt tables or missing nodes
122  // CAUTION: otherMap is not locked and must be immutable
123 
124  assert (isValid () && otherMap.isValid ());
125 
126  if (getHash () == otherMap.getHash ())
127  return true;
128 
130  std::stack <StackEntry, std::vector<StackEntry>> nodeStack; // track nodes we've pushed
131 
132  nodeStack.push ({root_.get(), otherMap.root_.get()});
133  while (!nodeStack.empty ())
134  {
135  auto [ourNode, otherNode] = nodeStack.top();
136  nodeStack.pop ();
137 
138  if (!ourNode || !otherNode)
139  {
140  assert (false);
141  Throw<SHAMapMissingNode> (type_, uint256 ());
142  }
143 
144  if (ourNode->isLeaf () && otherNode->isLeaf ())
145  {
146  // two leaves
147  auto ours = static_cast<SHAMapTreeNode*>(ourNode);
148  auto other = static_cast<SHAMapTreeNode*>(otherNode);
149  if (ours->peekItem()->key() == other->peekItem()->key())
150  {
151  if (ours->peekItem()->peekData () != other->peekItem()->peekData ())
152  {
153  differences.insert (std::make_pair (ours->peekItem()->key(),
154  DeltaRef (ours->peekItem (),
155  other->peekItem ())));
156  if (--maxCount <= 0)
157  return false;
158  }
159  }
160  else
161  {
162  differences.insert (std::make_pair(ours->peekItem()->key(),
163  DeltaRef(ours->peekItem(),
165  if (--maxCount <= 0)
166  return false;
167 
168  differences.insert(std::make_pair(other->peekItem()->key(),
169  DeltaRef(std::shared_ptr<SHAMapItem const>(), other->peekItem ())));
170  if (--maxCount <= 0)
171  return false;
172  }
173  }
174  else if (ourNode->isInner () && otherNode->isLeaf ())
175  {
176  auto ours = static_cast<SHAMapInnerNode*>(ourNode);
177  auto other = static_cast<SHAMapTreeNode*>(otherNode);
178  if (!walkBranch (ours, other->peekItem (),
179  true, differences, maxCount))
180  return false;
181  }
182  else if (ourNode->isLeaf () && otherNode->isInner ())
183  {
184  auto ours = static_cast<SHAMapTreeNode*>(ourNode);
185  auto other = static_cast<SHAMapInnerNode*>(otherNode);
186  if (!otherMap.walkBranch (other, ours->peekItem (),
187  false, differences, maxCount))
188  return false;
189  }
190  else if (ourNode->isInner () && otherNode->isInner ())
191  {
192  auto ours = static_cast<SHAMapInnerNode*>(ourNode);
193  auto other = static_cast<SHAMapInnerNode*>(otherNode);
194  for (int i = 0; i < 16; ++i)
195  if (ours->getChildHash (i) != other->getChildHash (i))
196  {
197  if (other->isEmptyBranch (i))
198  {
199  // We have a branch, the other tree does not
200  SHAMapAbstractNode* iNode = descendThrow (ours, i);
201  if (!walkBranch (iNode,
203  differences, maxCount))
204  return false;
205  }
206  else if (ours->isEmptyBranch (i))
207  {
208  // The other tree has a branch, we do not
209  SHAMapAbstractNode* iNode =
210  otherMap.descendThrow(other, i);
211  if (!otherMap.walkBranch (iNode,
213  false, differences, maxCount))
214  return false;
215  }
216  else // The two trees have different non-empty branches
217  nodeStack.push ({descendThrow (ours, i),
218  otherMap.descendThrow (other, i)});
219  }
220  }
221  else
222  assert (false);
223  }
224 
225  return true;
226 }
227 
228 void SHAMap::walkMap (std::vector<SHAMapMissingNode>& missingNodes, int maxMissing) const
229 {
230  if (!root_->isInner ()) // root_ is only node, and we have it
231  return;
232 
233  using StackEntry = std::shared_ptr<SHAMapInnerNode>;
235 
236  nodeStack.push (std::static_pointer_cast<SHAMapInnerNode>(root_));
237 
238  while (!nodeStack.empty ())
239  {
240  std::shared_ptr<SHAMapInnerNode> node = std::move (nodeStack.top());
241  nodeStack.pop ();
242 
243  for (int i = 0; i < 16; ++i)
244  {
245  if (!node->isEmptyBranch (i))
246  {
247  std::shared_ptr<SHAMapAbstractNode> nextNode = descendNoStore (node, i);
248 
249  if (nextNode)
250  {
251  if (nextNode->isInner ())
252  nodeStack.push(
253  std::static_pointer_cast<SHAMapInnerNode>(nextNode));
254  }
255  else
256  {
257  missingNodes.emplace_back (type_, node->getChildHash (i));
258  if (--maxMissing <= 0)
259  return;
260  }
261  }
262  }
263  }
264 }
265 
266 } // ripple
ripple::SHAMap::isValid
bool isValid() const
Definition: SHAMap.h:436
std::shared_ptr
STL class.
ripple::SHAMap::getHash
SHAMapHash getHash() const
Definition: SHAMap.cpp:751
std::pair
ripple::SHAMap::descendThrow
SHAMapAbstractNode * descendThrow(SHAMapInnerNode *, int branch) const
Definition: SHAMap.cpp:244
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:515
ripple::base_uint< 256 >
ripple::SHAMapAbstractNode::isInner
bool isInner() const
Definition: SHAMapTreeNode.h:273
ripple::SHAMapInnerNode
Definition: SHAMapTreeNode.h:140
ripple::SHAMap
A SHAMap is both a radix tree with a fan-out of 16 and a Merkle tree.
Definition: SHAMap.h:79
std::stack::pop
T pop(T... args)
std::stack::top
T top(T... args)
ripple::SHAMapTreeNode
Definition: SHAMapTreeNode.h:183
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:33
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::DeltaRef
std::pair< std::shared_ptr< SHAMapItem const > const &, std::shared_ptr< SHAMapItem const > const & > DeltaRef
Definition: SHAMap.h:257
std::map::insert
T insert(T... args)
ripple::SHAMap::root_
std::shared_ptr< SHAMapAbstractNode > root_
Definition: SHAMap.h:86
ripple::SHAMapAbstractNode
Definition: SHAMapTreeNode.h:92
std::stack::empty
T empty(T... args)
std::stack::push
T push(T... args)
std::make_pair
T make_pair(T... args)