rippled
SHAMapInnerNode.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/shamap/SHAMapInnerNode.h>
21 
22 #include <ripple/basics/ByteUtilities.h>
23 #include <ripple/basics/Log.h>
24 #include <ripple/basics/Slice.h>
25 #include <ripple/basics/contract.h>
26 #include <ripple/basics/safe_cast.h>
27 #include <ripple/beast/core/LexicalCast.h>
28 #include <ripple/protocol/HashPrefix.h>
29 #include <ripple/protocol/digest.h>
30 #include <ripple/shamap/SHAMapTreeNode.h>
31 #include <ripple/shamap/impl/TaggedPointer.ipp>
32 
33 #include <openssl/sha.h>
34 
35 #include <algorithm>
36 #include <array>
37 #include <iterator>
38 #include <mutex>
39 #include <utility>
40 
41 namespace ripple {
42 
44 
46  std::uint32_t cowid,
47  std::uint8_t numAllocatedChildren)
48  : SHAMapTreeNode(cowid), hashesAndChildren_(numAllocatedChildren)
49 {
50 }
51 
53 
54 template <class F>
55 void
57 {
58  hashesAndChildren_.iterChildren(isBranch_, std::forward<F>(f));
59 }
60 
61 template <class F>
62 void
64 {
66 }
67 
68 void
70 {
72  TaggedPointer(std::move(hashesAndChildren_), isBranch_, toAllocate);
73 }
74 
77 {
79 }
80 
83 {
84  auto const branchCount = getBranchCount();
85  auto const thisIsSparse = !hashesAndChildren_.isDense();
86  auto p = std::make_shared<SHAMapInnerNode>(cowid, branchCount);
87  p->hash_ = hash_;
88  p->isBranch_ = isBranch_;
89  p->fullBelowGen_ = fullBelowGen_;
90  SHAMapHash *cloneHashes, *thisHashes;
91  std::shared_ptr<SHAMapTreeNode>*cloneChildren, *thisChildren;
92  // structured bindings can't be captured in c++ 17; use tie instead
93  std::tie(std::ignore, cloneHashes, cloneChildren) =
94  p->hashesAndChildren_.getHashesAndChildren();
95  std::tie(std::ignore, thisHashes, thisChildren) =
97 
98  if (thisIsSparse)
99  {
100  int cloneChildIndex = 0;
101  iterNonEmptyChildIndexes([&](auto branchNum, auto indexNum) {
102  cloneHashes[cloneChildIndex++] = thisHashes[indexNum];
103  });
104  }
105  else
106  {
107  iterNonEmptyChildIndexes([&](auto branchNum, auto indexNum) {
108  cloneHashes[branchNum] = thisHashes[indexNum];
109  });
110  }
112  if (thisIsSparse)
113  {
114  int cloneChildIndex = 0;
115  iterNonEmptyChildIndexes([&](auto branchNum, auto indexNum) {
116  cloneChildren[cloneChildIndex++] = thisChildren[indexNum];
117  });
118  }
119  else
120  {
121  iterNonEmptyChildIndexes([&](auto branchNum, auto indexNum) {
122  cloneChildren[branchNum] = thisChildren[indexNum];
123  });
124  }
125 
126  return p;
127 }
128 
131  Slice data,
132  SHAMapHash const& hash,
133  bool hashValid)
134 {
135  if (data.size() != 512)
136  Throw<std::runtime_error>("Invalid FI node");
137 
138  auto ret = std::make_shared<SHAMapInnerNode>(0, branchFactor);
139 
140  Serializer s(data.data(), data.size());
141 
142  auto retHashes = ret->hashesAndChildren_.getHashes();
143  for (int i = 0; i < branchFactor; ++i)
144  {
145  s.getBitString(retHashes[i].as_uint256(), i * 32);
146 
147  if (retHashes[i].isNonZero())
148  ret->isBranch_ |= (1 << i);
149  }
150 
151  ret->resizeChildArrays(ret->getBranchCount());
152 
153  if (hashValid)
154  ret->hash_ = hash;
155  else
156  ret->updateHash();
157  return ret;
158 }
159 
162 {
163  Serializer s(data.data(), data.size());
164 
165  int len = s.getLength();
166 
167  auto ret = std::make_shared<SHAMapInnerNode>(0, branchFactor);
168 
169  auto retHashes = ret->hashesAndChildren_.getHashes();
170  for (int i = 0; i < (len / 33); ++i)
171  {
172  int pos;
173 
174  if (!s.get8(pos, 32 + (i * 33)))
175  Throw<std::runtime_error>("short CI node");
176 
177  if ((pos < 0) || (pos >= branchFactor))
178  Throw<std::runtime_error>("invalid CI node");
179 
180  s.getBitString(retHashes[pos].as_uint256(), i * 33);
181 
182  if (retHashes[pos].isNonZero())
183  ret->isBranch_ |= (1 << pos);
184  }
185 
186  ret->resizeChildArrays(ret->getBranchCount());
187 
188  ret->updateHash();
189 
190  return ret;
191 }
192 
193 void
195 {
196  uint256 nh;
197  if (isBranch_ != 0)
198  {
200  using beast::hash_append;
202  iterChildren([&](SHAMapHash const& hh) { hash_append(h, hh); });
203  nh = static_cast<typename sha512_half_hasher::result_type>(h);
204  }
205  hash_ = SHAMapHash{nh};
206 }
207 
208 void
210 {
211  SHAMapHash* hashes;
213  // structured bindings can't be captured in c++ 17; use tie instead
214  std::tie(std::ignore, hashes, children) =
216  iterNonEmptyChildIndexes([&](auto branchNum, auto indexNum) {
217  if (children[indexNum] != nullptr)
218  hashes[indexNum] = children[indexNum]->getHash();
219  });
220  updateHash();
221 }
222 
223 void
225 {
226  assert(!isEmpty());
227 
228  // If the node is sparse, then only send non-empty branches:
229  if (getBranchCount() < 12)
230  {
231  // compressed node
232  auto hashes = hashesAndChildren_.getHashes();
233  iterNonEmptyChildIndexes([&](auto branchNum, auto indexNum) {
234  s.addBitString(hashes[indexNum].as_uint256());
235  s.add8(branchNum);
236  });
238  }
239  else
240  {
241  iterChildren(
242  [&](SHAMapHash const& hh) { s.addBitString(hh.as_uint256()); });
243  s.add8(wireTypeInner);
244  }
245 }
246 
247 void
249 {
250  assert(!isEmpty());
251 
253  iterChildren(
254  [&](SHAMapHash const& hh) { s.addBitString(hh.as_uint256()); });
255 }
256 
257 bool
259 {
260  return isBranch_ == 0;
261 }
262 
263 int
265 {
266  return popcnt16(isBranch_);
267 }
268 
271 {
273  auto hashes = hashesAndChildren_.getHashes();
274  iterNonEmptyChildIndexes([&](auto branchNum, auto indexNum) {
275  ret += "\nb";
276  ret += std::to_string(branchNum);
277  ret += " = ";
278  ret += to_string(hashes[indexNum]);
279  });
280  return ret;
281 }
282 
283 // We are modifying an inner node
284 void
286 {
287  assert((m >= 0) && (m < branchFactor));
288  assert(cowid_ != 0);
289  assert(child.get() != this);
290 
291  auto const dstIsBranch = [&] {
292  if (child)
293  return isBranch_ | (1 << m);
294  else
295  return isBranch_ & ~(1 << m);
296  }();
297 
298  auto const dstToAllocate = popcnt16(dstIsBranch);
299  // change hashesAndChildren to remove the element, or make room for the
300  // added element, if necessary
302  std::move(hashesAndChildren_), isBranch_, dstIsBranch, dstToAllocate);
303 
304  isBranch_ = dstIsBranch;
305 
306  if (child)
307  {
308  auto const childIndex = *getChildIndex(m);
309  auto [_, hashes, children] = hashesAndChildren_.getHashesAndChildren();
310  hashes[childIndex].zero();
311  children[childIndex] = child;
312  }
313 
314  hash_.zero();
315 
317 }
318 
319 // finished modifying, now make shareable
320 void
322 {
323  assert((m >= 0) && (m < branchFactor));
324  assert(cowid_ != 0);
325  assert(child);
326  assert(child.get() != this);
327 
328  assert(!isEmptyBranch(m));
330 }
331 
334 {
335  assert(branch >= 0 && branch < branchFactor);
336  assert(!isEmptyBranch(branch));
337 
339  return hashesAndChildren_.getChildren()[*getChildIndex(branch)].get();
340 }
341 
344 {
345  assert(branch >= 0 && branch < branchFactor);
346  assert(!isEmptyBranch(branch));
347 
349  return hashesAndChildren_.getChildren()[*getChildIndex(branch)];
350 }
351 
352 SHAMapHash const&
354 {
355  assert((m >= 0) && (m < branchFactor));
356  if (auto const i = getChildIndex(m))
357  return hashesAndChildren_.getHashes()[*i];
358 
359  return zeroSHAMapHash;
360 }
361 
364  int branch,
366 {
367  assert(branch >= 0 && branch < branchFactor);
368  assert(node);
369  assert(!isEmptyBranch(branch));
370  auto const childIndex = *getChildIndex(branch);
371  auto [_, hashes, children] = hashesAndChildren_.getHashesAndChildren();
372  assert(node->getHash() == hashes[childIndex]);
373 
375  if (children[childIndex])
376  {
377  // There is already a node hooked up, return it
378  node = children[childIndex];
379  }
380  else
381  {
382  // Hook this node up
383  children[childIndex] = node;
384  }
385  return node;
386 }
387 
388 void
389 SHAMapInnerNode::invariants(bool is_root) const
390 {
391  unsigned count = 0;
392  auto [numAllocated, hashes, children] =
394 
395  if (numAllocated != branchFactor)
396  {
397  auto const branchCount = getBranchCount();
398  for (int i = 0; i < branchCount; ++i)
399  {
400  assert(hashes[i].isNonZero());
401  if (children[i] != nullptr)
402  children[i]->invariants();
403  ++count;
404  }
405  }
406  else
407  {
408  for (int i = 0; i < branchFactor; ++i)
409  {
410  if (hashes[i].isNonZero())
411  {
412  assert((isBranch_ & (1 << i)) != 0);
413  if (children[i] != nullptr)
414  children[i]->invariants();
415  ++count;
416  }
417  else
418  {
419  assert((isBranch_ & (1 << i)) == 0);
420  }
421  }
422  }
423 
424  if (!is_root)
425  {
426  assert(hash_.isNonZero());
427  assert(count >= 1);
428  }
429  assert((count == 0) ? hash_.isZero() : hash_.isNonZero());
430 }
431 
432 } // namespace ripple
ripple::SHAMapTreeNode::cowid
std::uint32_t cowid() const
Returns the SHAMap that owns this node.
Definition: SHAMapTreeNode.h:197
ripple::SHAMapInnerNode::serializeWithPrefix
void serializeWithPrefix(Serializer &) const override
Serialize the node in a format appropriate for hashing.
Definition: SHAMapInnerNode.cpp:248
ripple::SHAMapInnerNode::setChild
void setChild(int m, std::shared_ptr< SHAMapTreeNode > const &child)
Definition: SHAMapInnerNode.cpp:285
ripple::SHAMapInnerNode::clone
std::shared_ptr< SHAMapTreeNode > clone(std::uint32_t cowid) const override
Make a copy of this node, setting the owner.
Definition: SHAMapInnerNode.cpp:82
ripple::TaggedPointer::getChildIndex
std::optional< int > getChildIndex(std::uint16_t isBranch, int i) const
Get the child's index inside the hashes or children array (which may or may not be sparse).
std::string
STL class.
std::shared_ptr
STL class.
utility
ripple::SHAMapInnerNode::hashesAndChildren_
TaggedPointer hashesAndChildren_
Opaque type that contains the hashes array (array of type SHAMapHash) and the children array (array o...
Definition: SHAMapInnerNode.h:51
ripple::detail::basic_sha512_half_hasher
Returns the SHA512-Half digest of a message.
Definition: digest.h:166
ripple::SHAMapInnerNode::getChild
std::shared_ptr< SHAMapTreeNode > getChild(int branch)
Definition: SHAMapInnerNode.cpp:343
ripple::Slice
An immutable linear range of bytes.
Definition: Slice.h:44
ripple::SHAMapInnerNode::makeFullInner
static std::shared_ptr< SHAMapTreeNode > makeFullInner(Slice data, SHAMapHash const &hash, bool hashValid)
Definition: SHAMapInnerNode.cpp:130
ripple::SHAMapInnerNode::canonicalizeChild
virtual std::shared_ptr< SHAMapTreeNode > canonicalizeChild(int branch, std::shared_ptr< SHAMapTreeNode > node)
Definition: SHAMapInnerNode.cpp:363
ripple::SHAMapTreeNode::cowid_
std::uint32_t cowid_
Determines the owning SHAMap, if any.
Definition: SHAMapTreeNode.h:145
ripple::Serializer::add8
int add8(unsigned char i)
Definition: Serializer.cpp:166
ripple::SHAMapInnerNode::updateHash
void updateHash() override
Recalculate the hash of this node.
Definition: SHAMapInnerNode.cpp:194
iterator
std::lock_guard
STL class.
ripple::SHAMapHash::isZero
bool isZero() const
Definition: SHAMapTreeNode.h:69
ripple::SHAMapInnerNode::branchFactor
static constexpr unsigned int branchFactor
Each inner node has 16 children (the 'radix tree' part of the map)
Definition: SHAMapInnerNode.h:44
ripple::SHAMapInnerNode::shareChild
void shareChild(int m, std::shared_ptr< SHAMapTreeNode > const &child)
Definition: SHAMapInnerNode.cpp:321
ripple::wireTypeInner
static constexpr unsigned const char wireTypeInner
Definition: SHAMapTreeNode.h:41
ripple::SHAMapNodeID
Identifies a node inside a SHAMap.
Definition: SHAMapNodeID.h:33
ripple::SHAMapHash::isNonZero
bool isNonZero() const
Definition: SHAMapTreeNode.h:74
ripple::SHAMapTreeNode::getString
virtual std::string getString(SHAMapNodeID const &) const
Definition: SHAMapTreeNode.cpp:184
ripple::SHAMapTreeNode::hash_
SHAMapHash hash_
Definition: SHAMapTreeNode.h:137
ripple::SHAMapInnerNode::iterChildren
void iterChildren(F &&f) const
Call the f callback for all 16 (branchFactor) branches - even if the branch is empty.
Definition: SHAMapInnerNode.cpp:56
ripple::TaggedPointer
TaggedPointer is a combination of a pointer and a mask stored in the lowest two bits.
Definition: TaggedPointer.h:57
ripple::SHAMapHash
Definition: SHAMapTreeNode.h:48
algorithm
std::tie
T tie(T... args)
ripple::SHAMapInnerNode::isEmptyBranch
bool isEmptyBranch(int m) const
Definition: SHAMapInnerNode.h:195
ripple::SHAMapInnerNode::iterNonEmptyChildIndexes
void iterNonEmptyChildIndexes(F &&f) const
Call the f callback for all non-empty branches.
Definition: SHAMapInnerNode.cpp:63
ripple::base_uint< 256 >
ripple::SHAMapInnerNode::getString
std::string getString(SHAMapNodeID const &) const override
Definition: SHAMapInnerNode.cpp:270
ripple::HashPrefix::innerNode
@ innerNode
inner node in V1 tree
ripple::SHAMapInnerNode::getChildHash
SHAMapHash const & getChildHash(int m) const
Definition: SHAMapInnerNode.cpp:353
ripple::SHAMapInnerNode::resizeChildArrays
void resizeChildArrays(std::uint8_t toAllocate)
Convert arrays stored in hashesAndChildren_ so they can store the requested number of children.
Definition: SHAMapInnerNode.cpp:69
ripple::TaggedPointer::getHashes
SHAMapHash * getHashes() const
Get the hashes array.
ripple::TaggedPointer::isDense
bool isDense() const
Check if the arrays have a dense format.
ripple::SHAMapTreeNode
Definition: SHAMapTreeNode.h:134
std::to_string
T to_string(T... args)
ripple::SHAMapInnerNode::getChildIndex
std::optional< int > getChildIndex(int i) const
Get the child's index inside the hashes or children array (stored in hashesAndChildren_.
Definition: SHAMapInnerNode.cpp:76
array
ripple::Serializer::get8
bool get8(int &, int offset) const
Definition: Serializer.cpp:174
ripple::SHAMapInnerNode::updateHashDeep
void updateHashDeep()
Recalculate the hash of all children and this node.
Definition: SHAMapInnerNode.cpp:209
ripple::SHAMapInnerNode::SHAMapInnerNode
SHAMapInnerNode(std::uint32_t cowid, std::uint8_t numAllocatedChildren=2)
Definition: SHAMapInnerNode.cpp:45
ripple::SHAMapInnerNode::isBranch_
std::uint16_t isBranch_
Definition: SHAMapInnerNode.h:54
ripple::SHAMapInnerNode::getBranchCount
int getBranchCount() const
Definition: SHAMapInnerNode.cpp:264
std::uint32_t
ripple::SHAMapInnerNode::~SHAMapInnerNode
~SHAMapInnerNode()
ripple::TaggedPointer::iterNonEmptyChildIndexes
void iterNonEmptyChildIndexes(std::uint16_t isBranch, F &&f) const
Call the f callback for all non-empty branches.
ripple::SHAMapTreeNode::getHash
SHAMapHash const & getHash() const
Return the hash of this node.
Definition: SHAMapTreeNode.h:224
ripple::Serializer
Definition: Serializer.h:39
ripple::wireTypeCompressedInner
static constexpr unsigned const char wireTypeCompressedInner
Definition: SHAMapTreeNode.h:42
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
ripple::SHAMapInnerNode::serializeForWire
void serializeForWire(Serializer &) const override
Serialize the node in a format appropriate for sending over the wire.
Definition: SHAMapInnerNode.cpp:224
ripple::Serializer::addBitString
int addBitString(base_uint< Bits, Tag > const &v)
Definition: Serializer.h:97
ripple::Serializer::getBitString
bool getBitString(base_uint< Bits, Tag > &data, int offset) const
Definition: Serializer.h:146
ripple::SHAMapHash::zero
void zero()
Definition: SHAMapTreeNode.h:84
beast::hash_append
std::enable_if_t< is_contiguously_hashable< T, Hasher >::value > hash_append(Hasher &h, T const &t) noexcept
Logically concatenate input data to a Hasher.
Definition: hash_append.h:236
ripple::SHAMapInnerNode::childLock
static std::mutex childLock
Definition: SHAMapInnerNode.h:56
std::optional< int >
mutex
ripple::TaggedPointer::getChildren
std::shared_ptr< SHAMapTreeNode > * getChildren() const
Get the children array.
ripple::to_string
std::string to_string(Manifest const &m)
Format the specified manifest to a string for debugging purposes.
Definition: app/misc/impl/Manifest.cpp:38
ripple::Serializer::add32
int add32(std::uint32_t i)
Definition: Serializer.cpp:38
ripple::TaggedPointer::iterChildren
void iterChildren(std::uint16_t isBranch, F &&f) const
Call the f callback for all 16 (branchFactor) branches - even if the branch is empty.
ripple::SHAMapInnerNode::isEmpty
bool isEmpty() const
Definition: SHAMapInnerNode.cpp:258
ripple::SHAMapInnerNode::makeCompressedInner
static std::shared_ptr< SHAMapTreeNode > makeCompressedInner(Slice data)
Definition: SHAMapInnerNode.cpp:161
ripple::TaggedPointer::getHashesAndChildren
std::tuple< std::uint8_t, SHAMapHash *, std::shared_ptr< SHAMapTreeNode > * > getHashesAndChildren() const
Get the number of elements in each array and a pointer to the start of each array.
ripple::SHAMapHash::as_uint256
uint256 const & as_uint256() const
Definition: SHAMapTreeNode.h:59
ripple::SHAMapInnerNode::getChildPointer
SHAMapTreeNode * getChildPointer(int branch)
Definition: SHAMapInnerNode.cpp:333
ripple::TaggedPointer::capacity
std::uint8_t capacity() const
Get the number of elements allocated for each array.
ripple::Serializer::getLength
int getLength() const
Definition: Serializer.h:199
ripple::hash_append
void hash_append(Hasher &h, ValidatorBlobInfo const &blobInfo)
Definition: ValidatorList.h:897
ripple::SHAMapInnerNode::invariants
void invariants(bool is_root=false) const override
Definition: SHAMapInnerNode.cpp:389
ripple::SHAMapInnerNode::fullBelowGen_
std::uint32_t fullBelowGen_
Definition: SHAMapInnerNode.h:53